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