Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
bc77d7c0 TO |
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 | +--------------------------------------------------------------------+ | |
e70a7fc0 | 10 | */ |
1889d803 | 11 | use Civi\Core\Event\GenericHookEvent; |
6a488035 TO |
12 | |
13 | /** | |
14 | * This class facilitates the loading of resources | |
15 | * such as JavaScript files and CSS files. | |
16 | * | |
17 | * Any URLs generated for resources may include a 'cache-code'. By resetting the | |
18 | * cache-code, one may force clients to re-download resource files (regardless of | |
19 | * any HTTP caching rules). | |
20 | * | |
21 | * TODO: This is currently a thin wrapper over CRM_Core_Region. We | |
22 | * should incorporte services for aggregation, minimization, etc. | |
23 | * | |
24 | * @package CRM | |
ca5cec67 | 25 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
6a488035 | 26 | */ |
e9d08c6b | 27 | class CRM_Core_Resources implements CRM_Core_Resources_CollectionAdderInterface { |
6a488035 TO |
28 | const DEFAULT_WEIGHT = 0; |
29 | const DEFAULT_REGION = 'page-footer'; | |
30 | ||
e9d08c6b TO |
31 | use CRM_Core_Resources_CollectionAdderTrait; |
32 | ||
6a488035 TO |
33 | /** |
34 | * We don't have a container or dependency-injection, so use singleton instead | |
35 | * | |
36 | * @var object | |
6a488035 TO |
37 | */ |
38 | private static $_singleton = NULL; | |
39 | ||
40 | /** | |
41 | * @var CRM_Extension_Mapper | |
42 | */ | |
43 | private $extMapper = NULL; | |
44 | ||
45 | /** | |
fd7dc3f3 | 46 | * @var CRM_Core_Resources_Strings |
6a488035 | 47 | */ |
fd7dc3f3 | 48 | private $strings = NULL; |
6a488035 | 49 | |
fcf926ad TO |
50 | /** |
51 | * Any bundles that have been added. | |
52 | * | |
53 | * Format is ($bundleName => bool). | |
54 | * | |
55 | * @var array | |
56 | */ | |
57 | protected $addedBundles = []; | |
58 | ||
6a488035 | 59 | /** |
e97c66ff | 60 | * Added core resources. |
61 | * | |
62 | * Format is ($regionName => bool). | |
63 | * | |
64 | * @var array | |
6a488035 | 65 | */ |
be2fb01f | 66 | protected $addedCoreResources = []; |
6a488035 | 67 | |
2b2878a9 MW |
68 | /** |
69 | * Added settings. | |
70 | * | |
71 | * Format is ($regionName => bool). | |
72 | * | |
73 | * @var array | |
74 | */ | |
75 | protected $addedSettings = []; | |
76 | ||
6a488035 | 77 | /** |
e97c66ff | 78 | * A value to append to JS/CSS URLs to coerce cache resets. |
79 | * | |
80 | * @var string | |
6a488035 TO |
81 | */ |
82 | protected $cacheCode = NULL; | |
83 | ||
84 | /** | |
e97c66ff | 85 | * The name of a setting which persistently stores the cacheCode. |
86 | * | |
87 | * @var string | |
6a488035 TO |
88 | */ |
89 | protected $cacheCodeKey = NULL; | |
90 | ||
53f2643c | 91 | /** |
e97c66ff | 92 | * Are ajax popup screens enabled. |
93 | * | |
53f2643c CW |
94 | * @var bool |
95 | */ | |
96 | public $ajaxPopupsEnabled; | |
97 | ||
b698e2d5 TO |
98 | /** |
99 | * @var \Civi\Core\Paths | |
100 | */ | |
101 | protected $paths; | |
102 | ||
6a488035 | 103 | /** |
fe482240 | 104 | * Get or set the single instance of CRM_Core_Resources. |
6a488035 | 105 | * |
5a4f6742 CW |
106 | * @param CRM_Core_Resources $instance |
107 | * New copy of the manager. | |
e97c66ff | 108 | * |
6a488035 TO |
109 | * @return CRM_Core_Resources |
110 | */ | |
518fa0ee | 111 | public static function singleton(CRM_Core_Resources $instance = NULL) { |
6a488035 TO |
112 | if ($instance !== NULL) { |
113 | self::$_singleton = $instance; | |
114 | } | |
115 | if (self::$_singleton === NULL) { | |
223ba025 | 116 | self::$_singleton = Civi::service('resources'); |
6a488035 TO |
117 | } |
118 | return self::$_singleton; | |
119 | } | |
120 | ||
121 | /** | |
d09edf64 | 122 | * Construct a resource manager. |
6a488035 | 123 | * |
6a0b768e TO |
124 | * @param CRM_Extension_Mapper $extMapper |
125 | * Map extension names to their base path or URLs. | |
bbcf0f46 | 126 | * @param CRM_Core_Resources_Strings $strings |
6a0b768e | 127 | * JS-localization cache. |
3be754d6 | 128 | * @param string|null $cacheCodeKey Random code to append to resource URLs; changing the code forces clients to reload resources |
6a488035 | 129 | */ |
bbcf0f46 | 130 | public function __construct($extMapper, $strings, $cacheCodeKey = NULL) { |
6a488035 | 131 | $this->extMapper = $extMapper; |
bbcf0f46 | 132 | $this->strings = $strings; |
6a488035 TO |
133 | $this->cacheCodeKey = $cacheCodeKey; |
134 | if ($cacheCodeKey !== NULL) { | |
aaffa79f | 135 | $this->cacheCode = Civi::settings()->get($cacheCodeKey); |
6a488035 | 136 | } |
150f50c1 | 137 | if (!$this->cacheCode) { |
6a488035 TO |
138 | $this->resetCacheCode(); |
139 | } | |
84fb7424 | 140 | $this->ajaxPopupsEnabled = (bool) Civi::settings()->get('ajaxPopupsEnabled'); |
b698e2d5 | 141 | $this->paths = Civi::paths(); |
6a488035 TO |
142 | } |
143 | ||
e9d08c6b TO |
144 | /** |
145 | * Add an item to the collection. | |
146 | * | |
147 | * @param array $snippet | |
148 | * @return array | |
149 | * The full/computed snippet (with defaults applied). | |
150 | * @see CRM_Core_Resources_CollectionInterface::add() | |
151 | */ | |
152 | public function add($snippet) { | |
153 | if (!isset($snippet['region'])) { | |
154 | $snippet['region'] = self::DEFAULT_REGION; | |
155 | } | |
156 | if (!isset($snippet['weight'])) { | |
157 | $snippet['weight'] = self::DEFAULT_WEIGHT; | |
158 | } | |
159 | return CRM_Core_Region::instance($snippet['region'])->add($snippet); | |
160 | } | |
161 | ||
162 | /** | |
163 | * Locate the 'settings' snippet. | |
164 | * | |
165 | * @param array $options | |
166 | * @return array | |
167 | * @see CRM_Core_Resources_CollectionTrait::findCreateSettingSnippet() | |
168 | */ | |
169 | public function &findCreateSettingSnippet($options = []): array { | |
170 | $options = CRM_Core_Resources_CollectionAdderTrait::mergeSettingOptions($options, [ | |
171 | 'region' => NULL, | |
172 | ]); | |
173 | return $this->getSettingRegion($options['region'])->findCreateSettingSnippet($options); | |
174 | } | |
175 | ||
fcf926ad TO |
176 | /** |
177 | * Assimilate all the resources listed in a bundle. | |
178 | * | |
179 | * @param iterable|string|\CRM_Core_Resources_Bundle $bundle | |
19d42be6 | 180 | * Either bundle object, or the symbolic name of a bundle, or a list of bundles. |
fcf926ad TO |
181 | * Note: For symbolic names, the bundle must be a container service ('bundle.FOO'). |
182 | * @return static | |
183 | */ | |
184 | public function addBundle($bundle) { | |
19d42be6 TO |
185 | // There are two ways you might write this method: (1) immediately merge |
186 | // resources from the bundle, or (2) store a reference to the bundle and | |
187 | // merge resources later. Both have pros/cons. The implementation does #1. | |
188 | // | |
189 | // The upshot of #1 is *multi-region* support. For example, a bundle might | |
190 | // add some JS to `html-header` and then add some HTML to `page-header`. | |
191 | // Implementing this requires splitting the bundle (ie copying specific | |
192 | // resources to their respective regions). The timing of `addBundle()` is | |
193 | // favorable to splitting. | |
194 | // | |
195 | // The upshot of #2 would be *reduced timing sensitivity for downstream*: | |
196 | // if party A wants to include some bundle, and party B wants to refine | |
197 | // the same bundle, then it wouldn't matter if A or B executed first. | |
198 | // This should make DX generally more forgiving. But we can't split until | |
199 | // everyone has their shot at tweaking the bundle. | |
200 | // | |
201 | // In theory, you could have both characteristics if you figure the right | |
202 | // time at which to perform a split. Or maybe you could have both by tracking | |
203 | // more detailed references+events among the bundles/regions. I haven't | |
204 | // seen a simple way to do get both. | |
205 | ||
fcf926ad TO |
206 | if (is_iterable($bundle)) { |
207 | foreach ($bundle as $b) { | |
208 | $this->addBundle($b); | |
fcf926ad | 209 | } |
87edc8d2 | 210 | return $this; |
fcf926ad TO |
211 | } |
212 | ||
213 | if (is_string($bundle)) { | |
214 | $bundle = Civi::service('bundle.' . $bundle); | |
215 | } | |
216 | ||
217 | if (isset($this->addedBundles[$bundle->name])) { | |
218 | return $this; | |
219 | } | |
220 | $this->addedBundles[$bundle->name] = TRUE; | |
221 | ||
0aded46e TO |
222 | // Ensure that every asset has a region. |
223 | $bundle->filter(function($snippet) { | |
224 | if (empty($snippet['region'])) { | |
225 | $snippet['region'] = isset($snippet['settings']) | |
226 | ? $this->getSettingRegion()->_name | |
227 | : self::DEFAULT_REGION; | |
fcf926ad | 228 | } |
0aded46e TO |
229 | return $snippet; |
230 | }); | |
fcf926ad | 231 | |
0aded46e | 232 | $byRegion = CRM_Utils_Array::index(['region', 'name'], $bundle->getAll()); |
fcf926ad TO |
233 | foreach ($byRegion as $regionName => $snippets) { |
234 | CRM_Core_Region::instance($regionName)->merge($snippets); | |
235 | } | |
236 | return $this; | |
237 | } | |
238 | ||
69847402 | 239 | /** |
d09edf64 | 240 | * Helper fn for addSettingsFactory. |
6a488035 | 241 | */ |
63918d37 TO |
242 | public function getSettings($region = NULL) { |
243 | return $this->getSettingRegion($region)->getSettings(); | |
6a488035 TO |
244 | } |
245 | ||
6a488035 | 246 | /** |
d09edf64 | 247 | * Determine file path of a resource provided by an extension. |
6a488035 | 248 | * |
5a4f6742 CW |
249 | * @param string $ext |
250 | * extension name; use 'civicrm' for core. | |
e97c66ff | 251 | * @param string|null $file |
5a4f6742 | 252 | * file path -- relative to the extension base dir. |
6a488035 | 253 | * |
72b3a70c CW |
254 | * @return bool|string |
255 | * full file path or FALSE if not found | |
6a488035 | 256 | */ |
16cd1eca | 257 | public function getPath($ext, $file = NULL) { |
6a488035 | 258 | // TODO consider caching results |
b698e2d5 TO |
259 | $base = $this->paths->hasVariable($ext) |
260 | ? rtrim($this->paths->getVariable($ext, 'path'), '/') | |
261 | : $this->extMapper->keyToBasePath($ext); | |
16cd1eca | 262 | if ($file === NULL) { |
b698e2d5 | 263 | return $base; |
16cd1eca | 264 | } |
b698e2d5 | 265 | $path = $base . '/' . $file; |
6a488035 TO |
266 | if (is_file($path)) { |
267 | return $path; | |
268 | } | |
269 | return FALSE; | |
270 | } | |
271 | ||
272 | /** | |
d09edf64 | 273 | * Determine public URL of a resource provided by an extension. |
6a488035 | 274 | * |
5a4f6742 CW |
275 | * @param string $ext |
276 | * extension name; use 'civicrm' for core. | |
277 | * @param string $file | |
278 | * file path -- relative to the extension base dir. | |
2a6da8d7 EM |
279 | * @param bool $addCacheCode |
280 | * | |
6a488035 TO |
281 | * @return string, URL |
282 | */ | |
283 | public function getUrl($ext, $file = NULL, $addCacheCode = FALSE) { | |
284 | if ($file === NULL) { | |
285 | $file = ''; | |
286 | } | |
287 | if ($addCacheCode) { | |
6f12c6eb | 288 | $file = $this->addCacheCode($file); |
6a488035 TO |
289 | } |
290 | // TODO consider caching results | |
b698e2d5 TO |
291 | $base = $this->paths->hasVariable($ext) |
292 | ? $this->paths->getVariable($ext, 'url') | |
293 | : ($this->extMapper->keyToUrl($ext) . '/'); | |
294 | return $base . $file; | |
6a488035 TO |
295 | } |
296 | ||
16cd1eca TO |
297 | /** |
298 | * Evaluate a glob pattern in the context of a particular extension. | |
299 | * | |
300 | * @param string $ext | |
301 | * Extension name; use 'civicrm' for core. | |
302 | * @param string|array $patterns | |
303 | * Glob pattern; e.g. "*.html". | |
304 | * @param null|int $flags | |
305 | * See glob(). | |
306 | * @return array | |
307 | * List of matching files, relative to the extension base dir. | |
308 | * @see glob() | |
309 | */ | |
310 | public function glob($ext, $patterns, $flags = NULL) { | |
311 | $path = $this->getPath($ext); | |
312 | $patterns = (array) $patterns; | |
be2fb01f | 313 | $files = []; |
16cd1eca | 314 | foreach ($patterns as $pattern) { |
e5c376e7 TO |
315 | if (preg_match(';^(assetBuilder|ext)://;', $pattern)) { |
316 | $files[] = $pattern; | |
317 | } | |
9f87b14b | 318 | if (CRM_Utils_File::isAbsolute($pattern)) { |
16cd1eca TO |
319 | // Absolute path. |
320 | $files = array_merge($files, (array) glob($pattern, $flags)); | |
321 | } | |
322 | else { | |
323 | // Relative path. | |
324 | $files = array_merge($files, (array) glob("$path/$pattern", $flags)); | |
325 | } | |
326 | } | |
518fa0ee SL |
327 | // Deterministic order. |
328 | sort($files); | |
16cd1eca TO |
329 | $files = array_unique($files); |
330 | return array_map(function ($file) use ($path) { | |
331 | return CRM_Utils_File::relativize($file, "$path/"); | |
332 | }, $files); | |
333 | } | |
334 | ||
a0ee3941 EM |
335 | /** |
336 | * @return string | |
337 | */ | |
6a488035 | 338 | public function getCacheCode() { |
8b547c56 ML |
339 | // Ex: AngularJS json partials are language-specific because they ship with the strings |
340 | // for the current language. | |
341 | return $this->cacheCode . CRM_Core_I18n::getLocale(); | |
6a488035 TO |
342 | } |
343 | ||
a0ee3941 EM |
344 | /** |
345 | * @param $value | |
5badddc3 | 346 | * @return CRM_Core_Resources |
a0ee3941 | 347 | */ |
6a488035 TO |
348 | public function setCacheCode($value) { |
349 | $this->cacheCode = $value; | |
350 | if ($this->cacheCodeKey) { | |
08ef4ddd | 351 | Civi::settings()->set($this->cacheCodeKey, $value); |
6a488035 | 352 | } |
9762f6ff | 353 | return $this; |
6a488035 TO |
354 | } |
355 | ||
5badddc3 CW |
356 | /** |
357 | * @return CRM_Core_Resources | |
358 | */ | |
6a488035 TO |
359 | public function resetCacheCode() { |
360 | $this->setCacheCode(CRM_Utils_String::createRandom(5, CRM_Utils_String::ALPHANUMERIC)); | |
f091327b CW |
361 | // Also flush cms resource cache if needed |
362 | CRM_Core_Config::singleton()->userSystem->clearResourceCache(); | |
9762f6ff | 363 | return $this; |
6a488035 TO |
364 | } |
365 | ||
366 | /** | |
367 | * This adds CiviCRM's standard css and js to the specified region of the document. | |
368 | * It will only run once. | |
369 | * | |
2a6da8d7 | 370 | * @param string $region |
6a488035 | 371 | * @return CRM_Core_Resources |
6a488035 TO |
372 | */ |
373 | public function addCoreResources($region = 'html-header') { | |
8d2c99cf TO |
374 | if ($region !== 'html-header') { |
375 | // The signature of this method allowed different regions. However, this | |
376 | // doesn't appear to be used - based on grepping `universe` generally | |
377 | // and `civicrm-{core,backdrop,drupal,packages,wordpress,joomla}` specifically, | |
378 | // it appears that all callers use 'html-header' (either implicitly or explicitly). | |
379 | throw new \CRM_Core_Exception("Error: addCoreResources only supports html-header"); | |
380 | } | |
8d469336 TO |
381 | if (!self::isAjaxMode()) { |
382 | $this->addBundle('coreResources'); | |
6a488035 TO |
383 | $this->addCoreStyles($region); |
384 | } | |
385 | return $this; | |
386 | } | |
387 | ||
388 | /** | |
389 | * This will add CiviCRM's standard CSS | |
390 | * | |
6a488035 TO |
391 | * @param string $region |
392 | * @return CRM_Core_Resources | |
393 | */ | |
394 | public function addCoreStyles($region = 'html-header') { | |
8d2c99cf TO |
395 | if ($region !== 'html-header') { |
396 | // The signature of this method allowed different regions. However, this | |
397 | // doesn't appear to be used - based on grepping `universe` generally | |
398 | // and `civicrm-{core,backdrop,drupal,packages,wordpress,joomla}` specifically, | |
399 | // it appears that all callers use 'html-header' (either implicitly or explicitly). | |
400 | throw new \CRM_Core_Exception("Error: addCoreResources only supports html-header"); | |
401 | } | |
5526ab4d | 402 | $this->addBundle('coreStyles'); |
6a488035 TO |
403 | return $this; |
404 | } | |
405 | ||
627668e8 | 406 | /** |
d09edf64 | 407 | * Flushes cached translated strings. |
5badddc3 | 408 | * @return CRM_Core_Resources |
627668e8 CW |
409 | */ |
410 | public function flushStrings() { | |
fd7dc3f3 | 411 | $this->strings->flush(); |
9762f6ff CW |
412 | return $this; |
413 | } | |
414 | ||
6a488035 | 415 | /** |
fd7dc3f3 TO |
416 | * @return CRM_Core_Resources_Strings |
417 | */ | |
418 | public function getStrings() { | |
419 | return $this->strings; | |
6a488035 TO |
420 | } |
421 | ||
19f7e35e | 422 | /** |
8d7a9d07 | 423 | * Create dynamic script for localizing js widgets. |
19f7e35e | 424 | */ |
2daeb956 CW |
425 | public static function renderL10nJs(GenericHookEvent $e) { |
426 | if ($e->asset !== 'crm-l10n.js') { | |
427 | return; | |
428 | } | |
429 | $e->mimeType = 'application/javascript'; | |
430 | $params = $e->params; | |
431 | $params += [ | |
432 | 'contactSearch' => json_encode($params['includeEmailInName'] ? ts('Search by name/email or id...') : ts('Search by name or id...')), | |
f13b7faa | 433 | 'otherSearch' => json_encode(ts('Enter search term or id...')), |
e695ee7c | 434 | 'entityRef' => self::getEntityRefMetadata(), |
be2fb01f | 435 | ]; |
2daeb956 | 436 | $e->content = CRM_Core_Smarty::singleton()->fetchWith('CRM/common/l10n.js.tpl', $params); |
c66581f5 CW |
437 | } |
438 | ||
156fd9b9 | 439 | /** |
a6c01b45 CW |
440 | * @return bool |
441 | * is this page request an ajax snippet? | |
156fd9b9 | 442 | */ |
00be9182 | 443 | public static function isAjaxMode() { |
be2fb01f | 444 | if (in_array(CRM_Utils_Array::value('snippet', $_REQUEST), [ |
518fa0ee SL |
445 | CRM_Core_Smarty::PRINT_SNIPPET, |
446 | CRM_Core_Smarty::PRINT_NOFORM, | |
447 | CRM_Core_Smarty::PRINT_JSON, | |
448 | ]) | |
42a40a1c | 449 | ) { |
450 | return TRUE; | |
451 | } | |
f31f885e | 452 | list($arg0, $arg1) = array_pad(explode('/', CRM_Utils_System::currentPath()), 2, ''); |
60c3b6e9 | 453 | return ($arg0 === 'civicrm' && in_array($arg1, ['ajax', 'angularprofiles', 'asset'])); |
156fd9b9 | 454 | } |
b7ceb253 | 455 | |
1889d803 | 456 | /** |
518fa0ee | 457 | * @param \Civi\Core\Event\GenericHookEvent $e |
1889d803 CW |
458 | * @see \CRM_Utils_Hook::buildAsset() |
459 | */ | |
460 | public static function renderMenubarStylesheet(GenericHookEvent $e) { | |
461 | if ($e->asset !== 'crm-menubar.css') { | |
462 | return; | |
463 | } | |
464 | $e->mimeType = 'text/css'; | |
dcaf410f | 465 | $content = ''; |
1889d803 CW |
466 | $config = CRM_Core_Config::singleton(); |
467 | $cms = strtolower($config->userFramework); | |
468 | $cms = $cms === 'drupal' ? 'drupal7' : $cms; | |
469 | $items = [ | |
470 | 'bower_components/smartmenus/dist/css/sm-core-css.css', | |
471 | 'css/crm-menubar.css', | |
472 | "css/menubar-$cms.css", | |
473 | ]; | |
474 | foreach ($items as $item) { | |
dcaf410f | 475 | $content .= file_get_contents(self::singleton()->getPath('civicrm', $item)); |
8a52ae34 | 476 | } |
dcaf410f CW |
477 | $params = $e->params; |
478 | // "color" is deprecated in favor of the more specific "menubarColor" | |
479 | $menubarColor = $params['color'] ?? $params['menubarColor']; | |
1889d803 | 480 | $vars = [ |
dcaf410f CW |
481 | '$resourceBase' => rtrim($config->resourceBase, '/'), |
482 | '$menubarHeight' => $params['height'] . 'px', | |
483 | '$breakMin' => $params['breakpoint'] . 'px', | |
484 | '$breakMax' => ($params['breakpoint'] - 1) . 'px', | |
485 | '$menubarColor' => $menubarColor, | |
63c2508b | 486 | '$menuItemColor' => $params['menuItemColor'] ?? $menubarColor, |
dcaf410f CW |
487 | '$highlightColor' => $params['highlightColor'] ?? CRM_Utils_Color::getHighlight($menubarColor), |
488 | '$textColor' => $params['textColor'] ?? CRM_Utils_Color::getContrast($menubarColor, '#333', '#ddd'), | |
1889d803 | 489 | ]; |
dcaf410f CW |
490 | $vars['$highlightTextColor'] = $params['highlightTextColor'] ?? CRM_Utils_Color::getContrast($vars['$highlightColor'], '#333', '#ddd'); |
491 | $e->content = str_replace(array_keys($vars), array_values($vars), $content); | |
1889d803 CW |
492 | } |
493 | ||
b7ceb253 | 494 | /** |
f9e31d7f | 495 | * Provide a list of available entityRef filters. |
fd7c068f | 496 | * |
b7ceb253 CW |
497 | * @return array |
498 | */ | |
0089ae22 | 499 | protected static function getEntityRefMetadata() { |
e695ee7c CW |
500 | $data = [ |
501 | 'filters' => [], | |
502 | 'links' => [], | |
503 | ]; | |
06606cd1 | 504 | |
1d6f94ab CW |
505 | foreach (CRM_Core_DAO_AllCoreTables::daoToClass() as $entity => $daoName) { |
506 | // Skip DAOs of disabled components | |
cabcfae5 | 507 | if (defined("$daoName::COMPONENT") && !CRM_Core_Component::isEnabled($daoName::COMPONENT)) { |
ac7858bc | 508 | continue; |
1d6f94ab CW |
509 | } |
510 | $baoName = str_replace('_DAO_', '_BAO_', $daoName); | |
511 | if (class_exists($baoName)) { | |
e695ee7c CW |
512 | $filters = $baoName::getEntityRefFilters(); |
513 | if ($filters) { | |
2229cf4f | 514 | $data['filters'][$entity] = $filters; |
e695ee7c CW |
515 | } |
516 | if (is_callable([$baoName, 'getEntityRefCreateLinks'])) { | |
517 | $createLinks = $baoName::getEntityRefCreateLinks(); | |
518 | if ($createLinks) { | |
2229cf4f | 519 | $data['links'][$entity] = $createLinks; |
e695ee7c | 520 | } |
1d6f94ab CW |
521 | } |
522 | } | |
b7b528bc CW |
523 | } |
524 | ||
77d0bf4e | 525 | CRM_Utils_Hook::entityRefFilters($data['filters'], $data['links']); |
fd7c068f | 526 | |
e695ee7c | 527 | return $data; |
b7ceb253 | 528 | } |
96025800 | 529 | |
09a4dcd5 | 530 | /** |
d89d2545 | 531 | * Determine the minified file name. |
09a4dcd5 | 532 | * |
d89d2545 TO |
533 | * @param string $ext |
534 | * @param string $file | |
535 | * @return string | |
536 | * An updated $fileName. If a minified version exists and is supported by | |
537 | * system policy, the minified version will be returned. Otherwise, the original. | |
538 | */ | |
539 | public function filterMinify($ext, $file) { | |
540 | if (CRM_Core_Config::singleton()->debug && strpos($file, '.min.') !== FALSE) { | |
541 | $nonMiniFile = str_replace('.min.', '.', $file); | |
542 | if ($this->getPath($ext, $nonMiniFile)) { | |
543 | $file = $nonMiniFile; | |
09a4dcd5 CW |
544 | } |
545 | } | |
d89d2545 | 546 | return $file; |
09a4dcd5 CW |
547 | } |
548 | ||
6f12c6eb | 549 | /** |
550 | * @param string $url | |
551 | * @return string | |
552 | */ | |
553 | public function addCacheCode($url) { | |
33603e1d | 554 | $hasQuery = strpos($url, '?') !== FALSE; |
03449a5b | 555 | $operator = $hasQuery ? '&' : '?'; |
6f12c6eb | 556 | |
8b547c56 | 557 | return $url . $operator . 'r=' . $this->getCacheCode(); |
6f12c6eb | 558 | } |
33603e1d | 559 | |
adcd4bf7 CW |
560 | /** |
561 | * Checks if the given URL is fully-formed | |
562 | * | |
563 | * @param string $url | |
564 | * | |
565 | * @return bool | |
566 | */ | |
567 | public static function isFullyFormedUrl($url) { | |
568 | return (substr($url, 0, 4) === 'http') || (substr($url, 0, 1) === '/'); | |
569 | } | |
570 | ||
f55f8f17 | 571 | /** |
aec7c57d | 572 | * @param string|null $region |
f55f8f17 TO |
573 | * Optional request for a specific region. If NULL/omitted, use global default. |
574 | * @return \CRM_Core_Region | |
575 | */ | |
576 | private function getSettingRegion($region = NULL) { | |
577 | $region = $region ?: (self::isAjaxMode() ? 'ajax-snippet' : 'html-header'); | |
578 | return CRM_Core_Region::instance($region); | |
579 | } | |
580 | ||
6a488035 | 581 | } |