Merge pull request #14170 from mlutfy/cart-emails
[civicrm-core.git] / CRM / Core / Component.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 * Component stores all the static and dynamic information of the various
30 * CiviCRM components
31 *
32 * @package CRM
33 * @copyright CiviCRM LLC (c) 2004-2019
34 * $Id$
35 *
36 */
37 class CRM_Core_Component {
38
39 /**
40 * End part (filename) of the component information class'es name
41 * that needs to be present in components main directory.
42 */
43 const COMPONENT_INFO_CLASS = 'Info';
44
45 /**
46 * @var array
47 */
48 public static $_contactSubTypes = NULL;
49
50 /**
51 * @param bool $force
52 *
53 * @return CRM_Core_Component_Info[]
54 */
55 private static function &_info($force = FALSE) {
56 if (!isset(Civi::$statics[__CLASS__]['info'])|| $force) {
57 Civi::$statics[__CLASS__]['info'] = [];
58 $c = [];
59
60 $config = CRM_Core_Config::singleton();
61 $c = self::getComponents();
62
63 foreach ($c as $name => $comp) {
64 if (in_array($name, $config->enableComponents)) {
65 Civi::$statics[__CLASS__]['info'][$name] = $comp;
66 }
67 }
68 }
69
70 return Civi::$statics[__CLASS__]['info'];
71 }
72
73 /**
74 * @param string $name
75 * @param null $attribute
76 *
77 * @return mixed
78 */
79 public static function get($name, $attribute = NULL) {
80 $comp = CRM_Utils_Array::value($name, self::_info());
81 if ($attribute) {
82 return CRM_Utils_Array::value($attribute, $comp->info);
83 }
84 return $comp;
85 }
86
87 /**
88 * @param bool $force
89 *
90 * @return CRM_Core_Component_Info[]
91 * @throws Exception
92 */
93 public static function &getComponents($force = FALSE) {
94 if (!isset(Civi::$statics[__CLASS__]['all']) || $force) {
95 Civi::$statics[__CLASS__]['all'] = [];
96
97 $cr = new CRM_Core_DAO_Component();
98 $cr->find(FALSE);
99 while ($cr->fetch()) {
100 $infoClass = $cr->namespace . '_' . self::COMPONENT_INFO_CLASS;
101 $infoClassFile = str_replace('_', DIRECTORY_SEPARATOR, $infoClass) . '.php';
102 if (!CRM_Utils_File::isIncludable($infoClassFile)) {
103 continue;
104 }
105 require_once $infoClassFile;
106 $infoObject = new $infoClass($cr->name, $cr->namespace, $cr->id);
107 if ($infoObject->info['name'] !== $cr->name) {
108 CRM_Core_Error::fatal("There is a discrepancy between name in component registry and in info file ({$cr->name}).");
109 }
110 Civi::$statics[__CLASS__]['all'][$cr->name] = $infoObject;
111 unset($infoObject);
112 }
113 }
114
115 return Civi::$statics[__CLASS__]['all'];
116 }
117
118 /**
119 * @return array
120 * Array(string $name => int $id).
121 */
122 public static function &getComponentIDs() {
123 $componentIDs = [];
124
125 $cr = new CRM_Core_DAO_Component();
126 $cr->find(FALSE);
127 while ($cr->fetch()) {
128 $componentIDs[$cr->name] = $cr->id;
129 }
130
131 return $componentIDs;
132 }
133
134 /**
135 * @param bool $force
136 *
137 * @return CRM_Core_Component_Info[]
138 */
139 public static function &getEnabledComponents($force = FALSE) {
140 return self::_info($force);
141 }
142
143 public static function flushEnabledComponents() {
144 unset(Civi::$statics[__CLASS__]);
145 }
146
147 /**
148 * @param bool $translated
149 *
150 * @return array
151 */
152 public static function &getNames($translated = FALSE) {
153 $allComponents = self::getComponents();
154
155 $names = [];
156 foreach ($allComponents as $name => $comp) {
157 if ($translated) {
158 $names[$comp->componentID] = $comp->info['translatedName'];
159 }
160 else {
161 $names[$comp->componentID] = $name;
162 }
163 }
164 return $names;
165 }
166
167 /**
168 * @param $args
169 * @param $type
170 *
171 * @return bool
172 */
173 public static function invoke(&$args, $type) {
174 $info = self::_info();
175 $config = CRM_Core_Config::singleton();
176
177 $firstArg = CRM_Utils_Array::value(1, $args, '');
178 $secondArg = CRM_Utils_Array::value(2, $args, '');
179 foreach ($info as $name => $comp) {
180 if (in_array($name, $config->enableComponents) &&
181 (($comp->info['url'] === $firstArg && $type == 'main') ||
182 ($comp->info['url'] === $secondArg && $type == 'admin')
183 )
184 ) {
185 if ($type == 'main') {
186 // also set the smarty variables to the current component
187 $template = CRM_Core_Smarty::singleton();
188 $template->assign('activeComponent', $name);
189 if (!empty($comp->info[$name]['formTpl'])) {
190 $template->assign('formTpl', $comp->info[$name]['formTpl']);
191 }
192 if (!empty($comp->info[$name]['css'])) {
193 $styleSheets = '<style type="text/css">@import url(' . "{$config->resourceBase}css/{$comp->info[$name]['css']});</style>";
194 CRM_Utils_System::addHTMLHead($styleSheet);
195 }
196 }
197 $inv = $comp->getInvokeObject();
198 $inv->$type($args);
199 return TRUE;
200 }
201 }
202 return FALSE;
203 }
204
205 /**
206 * @return array
207 */
208 public static function xmlMenu() {
209
210 // lets build the menu for all components
211 $info = self::getComponents(TRUE);
212
213 $files = [];
214 foreach ($info as $name => $comp) {
215 $files = array_merge($files,
216 $comp->menuFiles()
217 );
218 }
219
220 return $files;
221 }
222
223 /**
224 * @return array
225 */
226 public static function &menu() {
227 $info = self::_info();
228 $items = [];
229 foreach ($info as $name => $comp) {
230 $mnu = $comp->getMenuObject();
231
232 $ret = $mnu->permissioned();
233 $items = array_merge($items, $ret);
234
235 $ret = $mnu->main($task);
236 $items = array_merge($items, $ret);
237 }
238 return $items;
239 }
240
241 /**
242 * @param string $componentName
243 *
244 * @return mixed
245 */
246 public static function getComponentID($componentName) {
247 $info = self::_info();
248 if (!empty($info[$componentName])) {
249 return $info[$componentName]->componentID;
250 }
251 else {
252 return;
253 }
254 }
255
256 /**
257 * @param int $componentID
258 *
259 * @return int|null|string
260 */
261 public static function getComponentName($componentID) {
262 $info = self::_info();
263
264 $componentName = NULL;
265 foreach ($info as $compName => $component) {
266 if ($component->componentID == $componentID) {
267 $componentName = $compName;
268 break;
269 }
270 }
271
272 return $componentName;
273 }
274
275 /**
276 * @return array
277 */
278 public static function &getQueryFields($checkPermission = TRUE) {
279 $info = self::_info();
280 $fields = [];
281 foreach ($info as $name => $comp) {
282 if ($comp->usesSearch()) {
283 $bqr = $comp->getBAOQueryObject();
284 $flds = $bqr->getFields($checkPermission);
285 $fields = array_merge($fields, $flds);
286 }
287 }
288 return $fields;
289 }
290
291 /**
292 * @param $query
293 * @param string $fnName
294 */
295 public static function alterQuery(&$query, $fnName) {
296 $info = self::_info();
297
298 foreach ($info as $name => $comp) {
299 if ($comp->usesSearch()) {
300 $bqr = $comp->getBAOQueryObject();
301 $bqr->$fnName($query);
302 }
303 }
304 }
305
306 /**
307 * @param string $fieldName
308 * @param $mode
309 * @param $side
310 *
311 * @return null
312 */
313 public static function from($fieldName, $mode, $side) {
314 $info = self::_info();
315
316 $from = NULL;
317 foreach ($info as $name => $comp) {
318 if ($comp->usesSearch()) {
319 $bqr = $comp->getBAOQueryObject();
320 $from = $bqr->from($fieldName, $mode, $side);
321 if ($from) {
322 return $from;
323 }
324 }
325 }
326 return $from;
327 }
328
329 /**
330 * @param $mode
331 * @param bool $includeCustomFields
332 *
333 * @return null
334 */
335 public static function &defaultReturnProperties(
336 $mode,
337 $includeCustomFields = TRUE
338 ) {
339 $info = self::_info();
340
341 $properties = NULL;
342 foreach ($info as $name => $comp) {
343 if ($comp->usesSearch()) {
344 $bqr = $comp->getBAOQueryObject();
345 $properties = $bqr->defaultReturnProperties($mode, $includeCustomFields);
346 if ($properties) {
347 return $properties;
348 }
349 }
350 }
351 return $properties;
352 }
353
354 /**
355 * @param CRM_Core_Form $form
356 */
357 public static function &buildSearchForm(&$form) {
358 $info = self::_info();
359
360 foreach ($info as $name => $comp) {
361 if ($comp->usesSearch()) {
362 $bqr = $comp->getBAOQueryObject();
363 $bqr->buildSearchForm($form);
364 }
365 }
366 }
367
368 /**
369 * @param $row
370 * @param int $id
371 */
372 public static function searchAction(&$row, $id) {
373 $info = self::_info();
374
375 foreach ($info as $name => $comp) {
376 if ($comp->usesSearch()) {
377 $bqr = $comp->getBAOQueryObject();
378 $bqr->searchAction($row, $id);
379 }
380 }
381 }
382
383 /**
384 * @return array|null
385 */
386 public static function &contactSubTypes() {
387 if (self::$_contactSubTypes == NULL) {
388 self::$_contactSubTypes = [];
389 }
390 return self::$_contactSubTypes;
391 }
392
393 /**
394 * @param $subType
395 * @param $op
396 *
397 * @return null
398 */
399 public static function &contactSubTypeProperties($subType, $op) {
400 $properties = self::contactSubTypes();
401 if (array_key_exists($subType, $properties) &&
402 array_key_exists($op, $properties[$subType])
403 ) {
404 return $properties[$subType][$op];
405 }
406 return CRM_Core_DAO::$_nullObject;
407 }
408
409 /**
410 * Handle table dependencies of components.
411 *
412 * @param array $tables
413 * Array of tables.
414 *
415 */
416 public static function tableNames(&$tables) {
417 $info = self::_info();
418
419 foreach ($info as $name => $comp) {
420 if ($comp->usesSearch()) {
421 $bqr = $comp->getBAOQueryObject();
422 $bqr->tableNames($tables);
423 }
424 }
425 }
426
427 /**
428 * Get components info from info file.
429 *
430 * @param string $crmFolderDir
431 *
432 * @return array
433 */
434 public static function getComponentsFromFile($crmFolderDir) {
435 $components = [];
436 //traverse CRM folder and check for Info file
437 if (is_dir($crmFolderDir) && $dir = opendir($crmFolderDir)) {
438 while ($subDir = readdir($dir)) {
439 // skip the extensions diretory since it has an Info.php file also
440 if ($subDir == 'Extension') {
441 continue;
442 }
443
444 $infoFile = $crmFolderDir . "/{$subDir}/" . self::COMPONENT_INFO_CLASS . '.php';
445 if (file_exists($infoFile)) {
446 $infoClass = 'CRM_' . $subDir . '_' . self::COMPONENT_INFO_CLASS;
447 require_once str_replace('_', DIRECTORY_SEPARATOR, $infoClass) . '.php';
448 $infoObject = new $infoClass(NULL, NULL, NULL);
449 $components[$infoObject->info['name']] = $infoObject;
450 unset($infoObject);
451 }
452 }
453 }
454
455 return $components;
456 }
457
458 }