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