Merge pull request #22555 from eileenmcnaughton/stricter
[civicrm-core.git] / CRM / Core / Resources / Common.php
CommitLineData
b2d8361e
TO
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 * Define some common, global lists of resources.
14 */
15class CRM_Core_Resources_Common {
16
17 const REGION = 'html-header';
18
8eb00bd1 19 /**
96622c8c
TO
20 * Create a "basic" (generic) bundle.
21 *
22 * The bundle goes through some lifecycle events (like `hook_alterBundle`).
23 *
24 * To define default content for a basic bundle, you may either give an
25 * `$init` function or subscribe to `hook_alterBundle`.
8eb00bd1
TO
26 *
27 * @param string $name
28 * Symbolic name of the bundle.
aec7c57d 29 * @param callable|null $init
8eb00bd1 30 * Optional initialization function. Populate default resources.
96622c8c
TO
31 * Signature: `function($bundle): void`
32 * Example: `function myinit($b) { $b->addScriptFile(...)->addStyleFile(...); }`
8eb00bd1
TO
33 * @param string|string[] $types
34 * List of resource-types to permit in this bundle. NULL for a default list.
96622c8c 35 * Example: ['styleFile', 'styleUrl']
8eb00bd1
TO
36 * The following aliases are allowed: '*all*', '*default*', '*script*', '*style*'
37 * @return CRM_Core_Resources_Bundle
38 */
39 public static function createBasicBundle($name, $init = NULL, $types = NULL) {
40 $bundle = new CRM_Core_Resources_Bundle($name, $types);
41 if ($init !== NULL) {
42 $init($bundle);
43 }
44 CRM_Utils_Hook::alterBundle($bundle);
45 $bundle->fillDefaults();
46 return $bundle;
47 }
48
6e7adedc
TO
49 /**
50 * The 'bundle.bootstrap3' service is a collection of resources which are
51 * loaded when a page needs to support Boostrap CSS v3.
52 *
53 * @param string $name
54 * i.e. 'bootstrap3'
55 * @return \CRM_Core_Resources_Bundle
56 */
57 public static function createBootstrap3Bundle($name) {
d853edea
TO
58 $bundle = new CRM_Core_Resources_Bundle($name, ['script', 'scriptFile', 'scriptUrl', 'settings', 'style', 'styleFile', 'styleUrl', 'markup']);
59 // Leave it to the theme/provider to register specific resources.
60 // $bundle->addStyleFile('civicrm', 'css/bootstrap3.css');
61 // $bundle->addScriptFile('civicrm', 'js/bootstrap3.js', [
62 // 'translate' => FALSE,
63 //]);
64
65 // This warning will show if bootstrap is unavailable. Normally it will be hidden by the bootstrap .collapse class.
66 $bundle->addMarkup('
67 <div id="bootstrap-theme">
68 <div class="messages warning no-popup collapse">
69 <p>
70 <i class="crm-i fa-exclamation-triangle" aria-hidden="true"></i>
71 <strong>' . ts('Bootstrap theme not found.') . '</strong>
72 </p>
73 <p>' . ts('This screen may not work correctly without a bootstrap-based theme such as Shoreditch installed.') . '</p>
74 </div>
75 </div>',
76 ['region' => 'page-header']
77 );
78
6e7adedc 79 CRM_Utils_Hook::alterBundle($bundle);
924d1dc8 80 $bundle->fillDefaults();
6e7adedc
TO
81 return $bundle;
82 }
83
b2d8361e
TO
84 /**
85 * The 'bundle.coreStyles' service is a collection of resources used on some
86 * non-Civi pages (wherein Civi may be mixed-in).
87 *
88 * @param string $name
89 * i.e. 'coreStyles'
90 * @return \CRM_Core_Resources_Bundle
950538ac 91 * @see \Civi\Core\Container::createContainer()
b2d8361e
TO
92 */
93 public static function createStyleBundle($name) {
94 $bundle = new CRM_Core_Resources_Bundle($name);
5526ab4d
TO
95
96 // Load custom or core css
97 $config = CRM_Core_Config::singleton();
98 if (!empty($config->customCSSURL)) {
99 $customCSSURL = Civi::resources()->addCacheCode($config->customCSSURL);
996f54f1 100 $bundle->addStyleUrl($customCSSURL, ['weight' => 99, 'name' => 'civicrm:css/custom.css']);
5526ab4d
TO
101 }
102 if (!Civi::settings()->get('disable_core_css')) {
103 $bundle->addStyleFile('civicrm', 'css/civicrm.css', -99);
104 }
105 // crm-i.css added ahead of other styles so it can be overridden by FA.
106 $bundle->addStyleFile('civicrm', 'css/crm-i.css', -101);
107
b2d8361e 108 CRM_Utils_Hook::alterBundle($bundle);
924d1dc8 109 $bundle->fillDefaults();
b2d8361e
TO
110 return $bundle;
111 }
112
113 /**
114 * The 'bundle.coreResources' service is a collection of resources
115 * shared by Civi pages (ie pages where Civi controls rendering).
116 *
117 * @param string $name
118 * i.e. 'coreResources'
119 * @return \CRM_Core_Resources_Bundle
950538ac 120 * @see \Civi\Core\Container::createContainer()
b2d8361e
TO
121 */
122 public static function createFullBundle($name) {
123 $bundle = new CRM_Core_Resources_Bundle($name);
8d469336
TO
124 $config = CRM_Core_Config::singleton();
125
126 // Add resources from coreResourceList
127 $jsWeight = -9999;
128 foreach (self::coreResourceList(self::REGION) as $item) {
129 if (is_array($item)) {
130 $bundle->addSetting($item);
131 }
132 elseif (strpos($item, '.css')) {
133 Civi::resources()->isFullyFormedUrl($item) ? $bundle->addStyleUrl($item, -100) : $bundle->addStyleFile('civicrm', $item, -100);
134 }
135 elseif (Civi::resources()->isFullyFormedUrl($item)) {
136 $bundle->addScriptUrl($item, $jsWeight++);
137 }
138 else {
139 // Don't bother looking for ts() calls in packages, there aren't any
140 $translate = (substr($item, 0, 3) == 'js/');
141 $bundle->addScriptFile('civicrm', $item, [
142 'weight' => $jsWeight++,
143 'translate' => $translate,
144 ]);
145 }
146 }
147 // Add global settings
148 $settings = [
149 'config' => [
150 'isFrontend' => $config->userFrameworkFrontend,
151 ],
152 ];
153 // Disable profile creation if user lacks permission
154 if (!CRM_Core_Permission::check('edit all contacts') && !CRM_Core_Permission::check('add contacts')) {
155 $settings['config']['entityRef']['contactCreate'] = FALSE;
156 }
157 $bundle->addSetting($settings);
158
159 // Give control of jQuery and _ back to the CMS - this loads last
160 $bundle->addScriptFile('civicrm', 'js/noconflict.js', [
161 'weight' => 9999,
162 'translate' => FALSE,
163 ]);
164
b2d8361e 165 CRM_Utils_Hook::alterBundle($bundle);
924d1dc8 166 $bundle->fillDefaults();
b2d8361e
TO
167 return $bundle;
168 }
169
8d469336
TO
170 /**
171 * List of core resources we add to every CiviCRM page.
172 *
173 * Note: non-compressed versions of .min files will be used in debug mode
174 *
175 * @param string $region
176 * @return array
177 */
178 protected static function coreResourceList($region) {
2daeb956
CW
179 $settings = Civi::settings();
180 $contactID = (int) CRM_Core_Session::getLoggedInContactID();
8d469336
TO
181
182 // Scripts needed by everyone, everywhere
183 // FIXME: This is too long; list needs finer-grained segmentation
184 $items = [
185 "bower_components/jquery/dist/jquery.min.js",
186 "bower_components/jquery-ui/jquery-ui.min.js",
187 "bower_components/jquery-ui/themes/smoothness/jquery-ui.min.css",
188 "bower_components/lodash-compat/lodash.min.js",
189 "packages/jquery/plugins/jquery.mousewheel.min.js",
190 "bower_components/select2/select2.min.js",
191 "bower_components/select2/select2.min.css",
192 "bower_components/font-awesome/css/font-awesome.min.css",
193 "packages/jquery/plugins/jquery.form.min.js",
194 "packages/jquery/plugins/jquery.timeentry.min.js",
195 "packages/jquery/plugins/jquery.blockUI.min.js",
196 "bower_components/datatables/media/js/jquery.dataTables.min.js",
197 "bower_components/datatables/media/css/jquery.dataTables.min.css",
198 "bower_components/jquery-validation/dist/jquery.validate.min.js",
199 "bower_components/jquery-validation/dist/additional-methods.min.js",
200 "packages/jquery/plugins/jquery.ui.datepicker.validation.min.js",
201 "js/Common.js",
202 "js/crm.datepicker.js",
203 "js/crm.ajax.js",
204 "js/wysiwyg/crm.wysiwyg.js",
205 ];
206
207 // Dynamic localization script
89689b37 208 if (!CRM_Core_Config::isUpgradeMode()) {
209 $items[] = Civi::service('asset_builder')->getUrl('crm-l10n.js', [
210 'cid' => $contactID,
211 'includeEmailInName' => (bool) $settings->get('includeEmailInName'),
212 'ajaxPopupsEnabled' => (bool) $settings->get('ajaxPopupsEnabled'),
213 'allowAlertAutodismissal' => (bool) $settings->get('allow_alert_autodismissal'),
214 'resourceCacheCode' => Civi::resources()->getCacheCode(),
215 'locale' => CRM_Core_I18n::getLocale(),
216 'lcMessages' => $settings->get('lcMessages'),
217 'dateInputFormat' => $settings->get('dateInputFormat'),
218 'timeInputFormat' => $settings->get('timeInputFormat'),
219 'moneyFormat' => CRM_Utils_Money::format(1234.56),
220 ]);
221 }
8d469336 222
8d469336
TO
223 // These scripts are only needed by back-office users
224 if (CRM_Core_Permission::check('access CiviCRM')) {
225 $items[] = "packages/jquery/plugins/jquery.tableHeader.js";
226 $items[] = "packages/jquery/plugins/jquery.notify.min.js";
227 }
228
8d469336
TO
229 // Menubar
230 $position = 'none';
231 if (
2daeb956 232 $contactID && !CRM_Core_Config::singleton()->userFrameworkFrontend
8d469336 233 && CRM_Core_Permission::check('access CiviCRM')
704cb100 234 && !CRM_Utils_Constant::value('CIVICRM_DISABLE_DEFAULT_MENU')
8d469336
TO
235 && !CRM_Core_Config::isUpgradeMode()
236 ) {
2daeb956 237 $position = $settings->get('menubar_position') ?: 'over-cms-menu';
8d469336
TO
238 }
239 if ($position !== 'none') {
240 $items[] = 'bower_components/smartmenus/dist/jquery.smartmenus.min.js';
241 $items[] = 'bower_components/smartmenus/dist/addons/keyboard/jquery.smartmenus.keyboard.min.js';
242 $items[] = 'js/crm.menubar.js';
243 // @see CRM_Core_Resources::renderMenubarStylesheet
244 $items[] = Civi::service('asset_builder')->getUrl('crm-menubar.css', [
2daeb956 245 'menubarColor' => $settings->get('menubar_color'),
8d469336
TO
246 'height' => 40,
247 'breakpoint' => 768,
248 ]);
249 // Variables for crm.menubar.js
250 $items[] = [
251 'menubar' => [
252 'position' => $position,
253 'qfKey' => CRM_Core_Key::get('CRM_Contact_Controller_Search', TRUE),
254 'cacheCode' => CRM_Core_BAO_Navigation::getCacheKey($contactID),
255 ],
256 ];
257 }
258
259 // JS for multilingual installations
2daeb956
CW
260 $languageLimit = $settings->get('languageLimit');
261 if (is_array($languageLimit) && count($languageLimit) > 1 && CRM_Core_Permission::check('translate CiviCRM')) {
8d469336
TO
262 $items[] = "js/crm.multilingual.js";
263 }
264
265 // Enable administrators to edit option lists in a dialog
2daeb956 266 if (CRM_Core_Permission::check('administer CiviCRM') && $settings->get('ajaxPopupsEnabled')) {
8d469336
TO
267 $items[] = "js/crm.optionEdit.js";
268 }
269
270 $tsLocale = CRM_Core_I18n::getLocale();
271 // Add localized jQuery UI files
272 if ($tsLocale && $tsLocale != 'en_US') {
273 // Search for i18n file in order of specificity (try fr-CA, then fr)
274 list($lang) = explode('_', $tsLocale);
275 $path = "bower_components/jquery-ui/ui/i18n";
276 foreach ([str_replace('_', '-', $tsLocale), $lang] as $language) {
277 $localizationFile = "$path/datepicker-{$language}.js";
278 if (Civi::resources()->getPath('civicrm', $localizationFile)) {
279 $items[] = $localizationFile;
280 break;
281 }
282 }
283 }
284
285 // Allow hooks to modify this list
286 CRM_Utils_Hook::coreResourceList($items, $region);
287
288 // Oof, existing listeners would expect $items to typically begin with 'bower_components/' or 'packages/'
289 // (using an implicit base of `[civicrm.root]`). We preserve the hook contract and cleanup $items post-hook.
290 $map = [
291 'bower_components' => rtrim(Civi::paths()->getUrl('[civicrm.bower]/.', 'absolute'), '/'),
292 'packages' => rtrim(Civi::paths()->getUrl('[civicrm.packages]/.', 'absolute'), '/'),
293 ];
294 $filter = function($m) use ($map) {
295 return $map[$m[1]] . $m[2];
296 };
297 $items = array_map(function($item) use ($filter) {
298 return is_array($item) ? $item : preg_replace_callback(';^(bower_components|packages)(/.*);', $filter, $item);
299 }, $items);
300
301 return $items;
302 }
303
b2d8361e 304}