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