1 | <?php |
||||
2 | |||||
3 | namespace SilverStripe\View; |
||||
4 | |||||
5 | use InvalidArgumentException; |
||||
6 | use SilverStripe\Control\HTTPResponse; |
||||
7 | use SilverStripe\Core\Config\Config; |
||||
8 | use SilverStripe\Core\Flushable; |
||||
9 | use SilverStripe\Dev\Deprecation; |
||||
10 | |||||
11 | /** |
||||
12 | * Requirements tracker for JavaScript and CSS. |
||||
13 | */ |
||||
14 | class Requirements implements Flushable |
||||
15 | { |
||||
16 | |||||
17 | /** |
||||
18 | * Flag whether combined files should be deleted on flush. |
||||
19 | * |
||||
20 | * By default all combined files are deleted on flush. If combined files are stored in source control, |
||||
21 | * and thus updated manually, you might want to turn this on to disable this behaviour. |
||||
22 | * |
||||
23 | * @config |
||||
24 | * @var bool |
||||
25 | */ |
||||
26 | private static $disable_flush_combined = false; |
||||
0 ignored issues
–
show
introduced
by
![]() |
|||||
27 | |||||
28 | /** |
||||
29 | * Triggered early in the request when a flush is requested |
||||
30 | */ |
||||
31 | public static function flush() |
||||
32 | { |
||||
33 | $disabled = Config::inst()->get(static::class, 'disable_flush_combined'); |
||||
34 | if (!$disabled) { |
||||
35 | self::delete_all_combined_files(); |
||||
36 | } |
||||
37 | } |
||||
38 | |||||
39 | /** |
||||
40 | * Enable combining of css/javascript files. |
||||
41 | * |
||||
42 | * @param bool $enable |
||||
43 | */ |
||||
44 | public static function set_combined_files_enabled($enable) |
||||
45 | { |
||||
46 | self::backend()->setCombinedFilesEnabled($enable); |
||||
47 | } |
||||
48 | |||||
49 | /** |
||||
50 | * Checks whether combining of css/javascript files is enabled. |
||||
51 | * |
||||
52 | * @return bool |
||||
53 | */ |
||||
54 | public static function get_combined_files_enabled() |
||||
55 | { |
||||
56 | return self::backend()->getCombinedFilesEnabled(); |
||||
57 | } |
||||
58 | |||||
59 | /** |
||||
60 | * Set the relative folder e.g. 'assets' for where to store combined files |
||||
61 | * |
||||
62 | * @param string $folder Path to folder |
||||
63 | */ |
||||
64 | public static function set_combined_files_folder($folder) |
||||
65 | { |
||||
66 | self::backend()->setCombinedFilesFolder($folder); |
||||
67 | } |
||||
68 | |||||
69 | /** |
||||
70 | * Set whether to add caching query params to the requests for file-based requirements. |
||||
71 | * Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by |
||||
72 | * filemtime. This has the benefit of allowing the browser to cache the URL infinitely, |
||||
73 | * while automatically busting this cache every time the file is changed. |
||||
74 | * |
||||
75 | * @param bool |
||||
76 | */ |
||||
77 | public static function set_suffix_requirements($var) |
||||
78 | { |
||||
79 | self::backend()->setSuffixRequirements($var); |
||||
80 | } |
||||
81 | |||||
82 | /** |
||||
83 | * Check whether we want to suffix requirements |
||||
84 | * |
||||
85 | * @return bool |
||||
86 | */ |
||||
87 | public static function get_suffix_requirements() |
||||
88 | { |
||||
89 | return self::backend()->getSuffixRequirements(); |
||||
90 | } |
||||
91 | |||||
92 | /** |
||||
93 | * Instance of the requirements for storage. You can create your own backend to change the |
||||
94 | * default JS and CSS inclusion behaviour. |
||||
95 | * |
||||
96 | * @var Requirements_Backend |
||||
97 | */ |
||||
98 | private static $backend = null; |
||||
99 | |||||
100 | /** |
||||
101 | * @return Requirements_Backend |
||||
102 | */ |
||||
103 | public static function backend() |
||||
104 | { |
||||
105 | if (!self::$backend) { |
||||
106 | self::$backend = Requirements_Backend::create(); |
||||
107 | } |
||||
108 | return self::$backend; |
||||
109 | } |
||||
110 | |||||
111 | /** |
||||
112 | * Setter method for changing the Requirements backend |
||||
113 | * |
||||
114 | * @param Requirements_Backend $backend |
||||
115 | */ |
||||
116 | public static function set_backend(Requirements_Backend $backend) |
||||
117 | { |
||||
118 | self::$backend = $backend; |
||||
119 | } |
||||
120 | |||||
121 | /** |
||||
122 | * Register the given JavaScript file as required. |
||||
123 | * |
||||
124 | * @param string $file Relative to docroot |
||||
125 | * @param array $options List of options. Available options include: |
||||
126 | * - 'provides' : List of scripts files included in this file |
||||
127 | * - 'async' : Boolean value to set async attribute to script tag |
||||
128 | * - 'defer' : Boolean value to set defer attribute to script tag |
||||
129 | */ |
||||
130 | public static function javascript($file, $options = array()) |
||||
131 | { |
||||
132 | self::backend()->javascript($file, $options); |
||||
133 | } |
||||
134 | |||||
135 | /** |
||||
136 | * Register the given JavaScript code into the list of requirements |
||||
137 | * |
||||
138 | * @param string $script The script content as a string (without enclosing `<script>` tag) |
||||
139 | * @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once |
||||
140 | */ |
||||
141 | public static function customScript($script, $uniquenessID = null) |
||||
142 | { |
||||
143 | self::backend()->customScript($script, $uniquenessID); |
||||
144 | } |
||||
145 | |||||
146 | /** |
||||
147 | * Return all registered custom scripts |
||||
148 | * |
||||
149 | * @return array |
||||
150 | */ |
||||
151 | public static function get_custom_scripts() |
||||
152 | { |
||||
153 | return self::backend()->getCustomScripts(); |
||||
154 | } |
||||
155 | |||||
156 | /** |
||||
157 | * Register the given CSS styles into the list of requirements |
||||
158 | * |
||||
159 | * @param string $script CSS selectors as a string (without enclosing `<style>` tag) |
||||
160 | * @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once |
||||
161 | */ |
||||
162 | public static function customCSS($script, $uniquenessID = null) |
||||
163 | { |
||||
164 | self::backend()->customCSS($script, $uniquenessID); |
||||
165 | } |
||||
166 | |||||
167 | /** |
||||
168 | * Add the following custom HTML code to the `<head>` section of the page |
||||
169 | * |
||||
170 | * @param string $html Custom HTML code |
||||
171 | * @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once |
||||
172 | */ |
||||
173 | public static function insertHeadTags($html, $uniquenessID = null) |
||||
174 | { |
||||
175 | self::backend()->insertHeadTags($html, $uniquenessID); |
||||
176 | } |
||||
177 | |||||
178 | /** |
||||
179 | * Include the content of the given JavaScript file in the list of requirements. Dollar-sign |
||||
180 | * variables will be interpolated with values from $vars similar to a .ss template. |
||||
181 | * |
||||
182 | * @param string $file The template file to load, relative to docroot |
||||
183 | * @param string[]|int[] $vars The array of variables to interpolate. |
||||
184 | * @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once |
||||
185 | */ |
||||
186 | public static function javascriptTemplate($file, $vars, $uniquenessID = null) |
||||
187 | { |
||||
188 | self::backend()->javascriptTemplate($file, $vars, $uniquenessID); |
||||
189 | } |
||||
190 | |||||
191 | /** |
||||
192 | * Register the given stylesheet into the list of requirements. |
||||
193 | * |
||||
194 | * @param string $file The CSS file to load, relative to site root |
||||
195 | * @param string $media Comma-separated list of media types to use in the link tag |
||||
196 | * (e.g. 'screen,projector') |
||||
197 | * @param array $options List of options. Available options include: |
||||
198 | * - 'integrity' : SubResource Integrity hash |
||||
199 | * - 'crossorigin' : Cross-origin policy for the resource |
||||
200 | */ |
||||
201 | public static function css($file, $media = null, $options = []) |
||||
202 | { |
||||
203 | self::backend()->css($file, $media, $options); |
||||
204 | } |
||||
205 | |||||
206 | /** |
||||
207 | * Registers the given themeable stylesheet as required. |
||||
208 | * |
||||
209 | * A CSS file in the current theme path name 'themename/css/$name.css' is first searched for, |
||||
210 | * and it that doesn't exist and the module parameter is set then a CSS file with that name in |
||||
211 | * the module is used. |
||||
212 | * |
||||
213 | * @param string $name The name of the file - eg '/css/File.css' would have the name 'File' |
||||
214 | * @param string $media Comma-separated list of media types to use in the link tag |
||||
215 | * (e.g. 'screen,projector') |
||||
216 | */ |
||||
217 | public static function themedCSS($name, $media = null) |
||||
218 | { |
||||
219 | self::backend()->themedCSS($name, $media); |
||||
220 | } |
||||
221 | |||||
222 | /** |
||||
223 | * Registers the given themeable javascript as required. |
||||
224 | * |
||||
225 | * A javascript file in the current theme path name 'themename/javascript/$name.js' is first searched for, |
||||
226 | * and it that doesn't exist and the module parameter is set then a javascript file with that name in |
||||
227 | * the module is used. |
||||
228 | * |
||||
229 | * @param string $name The name of the file - eg '/javascript/File.js' would have the name 'File' |
||||
230 | * @param string $type Comma-separated list of types to use in the script tag |
||||
231 | * (e.g. 'text/javascript,text/ecmascript') |
||||
232 | */ |
||||
233 | public static function themedJavascript($name, $type = null) |
||||
234 | { |
||||
235 | self::backend()->themedJavascript($name, $type); |
||||
236 | } |
||||
237 | |||||
238 | /** |
||||
239 | * Clear either a single or all requirements |
||||
240 | * |
||||
241 | * Caution: Clearing single rules added via customCSS and customScript only works if you |
||||
242 | * originally specified a $uniquenessID. |
||||
243 | * |
||||
244 | * @param string|int $fileOrID |
||||
245 | */ |
||||
246 | public static function clear($fileOrID = null) |
||||
247 | { |
||||
248 | self::backend()->clear($fileOrID); |
||||
249 | } |
||||
250 | |||||
251 | /** |
||||
252 | * Restore requirements cleared by call to Requirements::clear |
||||
253 | */ |
||||
254 | public static function restore() |
||||
255 | { |
||||
256 | self::backend()->restore(); |
||||
257 | } |
||||
258 | |||||
259 | /** |
||||
260 | * Block inclusion of a specific file |
||||
261 | * |
||||
262 | * The difference between this and {@link clear} is that the calling order does not matter; |
||||
263 | * {@link clear} must be called after the initial registration, whereas {@link block} can be |
||||
264 | * used in advance. This is useful, for example, to block scripts included by a superclass |
||||
265 | * without having to override entire functions and duplicate a lot of code. |
||||
266 | * |
||||
267 | * Note that blocking should be used sparingly because it's hard to trace where an file is |
||||
268 | * being blocked from. |
||||
269 | * |
||||
270 | * @param string|int $fileOrID |
||||
271 | */ |
||||
272 | public static function block($fileOrID) |
||||
273 | { |
||||
274 | self::backend()->block($fileOrID); |
||||
275 | } |
||||
276 | |||||
277 | /** |
||||
278 | * Remove an item from the block list |
||||
279 | * |
||||
280 | * @param string|int $fileOrID |
||||
281 | */ |
||||
282 | public static function unblock($fileOrID) |
||||
283 | { |
||||
284 | self::backend()->unblock($fileOrID); |
||||
285 | } |
||||
286 | |||||
287 | /** |
||||
288 | * Removes all items from the block list |
||||
289 | */ |
||||
290 | public static function unblock_all() |
||||
291 | { |
||||
292 | self::backend()->unblockAll(); |
||||
293 | } |
||||
294 | |||||
295 | /** |
||||
296 | * Update the given HTML content with the appropriate include tags for the registered |
||||
297 | * requirements. Needs to receive a valid HTML/XHTML template in the $content parameter, |
||||
298 | * including a head and body tag. |
||||
299 | * |
||||
300 | * @param string $content HTML content that has already been parsed from the $templateFile |
||||
301 | * through {@link SSViewer} |
||||
302 | * @return string HTML content augmented with the requirements tags |
||||
303 | */ |
||||
304 | public static function includeInHTML($content) |
||||
305 | { |
||||
306 | if (func_num_args() > 1) { |
||||
307 | Deprecation::notice( |
||||
308 | '5.0', |
||||
309 | '$templateFile argument is deprecated. includeInHTML takes a sole $content parameter now.' |
||||
310 | ); |
||||
311 | $content = func_get_arg(1); |
||||
312 | } |
||||
313 | |||||
314 | return self::backend()->includeInHTML($content); |
||||
315 | } |
||||
316 | |||||
317 | /** |
||||
318 | * Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the given |
||||
319 | * HTTP Response |
||||
320 | * |
||||
321 | * @param HTTPResponse $response |
||||
322 | */ |
||||
323 | public static function include_in_response(HTTPResponse $response) |
||||
324 | { |
||||
325 | self::backend()->includeInResponse($response); |
||||
326 | } |
||||
327 | |||||
328 | /** |
||||
329 | * Add i18n files from the given javascript directory. SilverStripe expects that the given |
||||
330 | * directory will contain a number of JavaScript files named by language: en_US.js, de_DE.js, |
||||
331 | * etc. |
||||
332 | * |
||||
333 | * @param string $langDir The JavaScript lang directory, relative to the site root, e.g., |
||||
334 | * 'framework/javascript/lang' |
||||
335 | * @param bool $return Return all relative file paths rather than including them in |
||||
336 | * requirements |
||||
337 | * @param bool $langOnly @deprecated 4.1.0:5.0.0 as i18n.js should be included manually in your project |
||||
338 | * |
||||
339 | * @return array |
||||
340 | */ |
||||
341 | public static function add_i18n_javascript($langDir, $return = false, $langOnly = false) |
||||
0 ignored issues
–
show
The parameter
$langOnly is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||
342 | { |
||||
343 | return self::backend()->add_i18n_javascript($langDir, $return); |
||||
344 | } |
||||
345 | |||||
346 | /** |
||||
347 | * Concatenate several css or javascript files into a single dynamically generated file. This |
||||
348 | * increases performance by fewer HTTP requests. |
||||
349 | * |
||||
350 | * The combined file is regenerated based on every file modification time. Optionally a |
||||
351 | * rebuild can be triggered by appending ?flush=1 to the URL. |
||||
352 | * |
||||
353 | * All combined files will have a comment on the start of each concatenated file denoting their |
||||
354 | * original position. |
||||
355 | * |
||||
356 | * CAUTION: You're responsible for ensuring that the load order for combined files is |
||||
357 | * retained - otherwise combining JavaScript files can lead to functional errors in the |
||||
358 | * JavaScript logic, and combining CSS can lead to incorrect inheritance. You can also |
||||
359 | * only include each file once across all includes and comibinations in a single page load. |
||||
360 | * |
||||
361 | * CAUTION: Combining CSS Files discards any "media" information. |
||||
362 | * |
||||
363 | * Example for combined JavaScript: |
||||
364 | * <code> |
||||
365 | * Requirements::combine_files( |
||||
366 | * 'foobar.js', |
||||
367 | * array( |
||||
368 | * 'mysite/javascript/foo.js', |
||||
369 | * 'mysite/javascript/bar.js', |
||||
370 | * ) |
||||
371 | * ); |
||||
372 | * </code> |
||||
373 | * |
||||
374 | * Example for combined CSS: |
||||
375 | * <code> |
||||
376 | * Requirements::combine_files( |
||||
377 | * 'foobar.css', |
||||
378 | * array( |
||||
379 | * 'mysite/javascript/foo.css', |
||||
380 | * 'mysite/javascript/bar.css', |
||||
381 | * ) |
||||
382 | * ); |
||||
383 | * </code> |
||||
384 | * |
||||
385 | * @param string $combinedFileName Filename of the combined file relative to docroot |
||||
386 | * @param array $files Array of filenames relative to docroot |
||||
387 | * @param array $options Array of options for combining files. Available options are: |
||||
388 | * - 'media' : If including CSS Files, you can specify a media type |
||||
389 | * - 'async' : If including JavaScript Files, boolean value to set async attribute to script tag |
||||
390 | * - 'defer' : If including JavaScript Files, boolean value to set defer attribute to script tag |
||||
391 | */ |
||||
392 | public static function combine_files($combinedFileName, $files, $options = array()) |
||||
393 | { |
||||
394 | if (is_string($options)) { |
||||
0 ignored issues
–
show
|
|||||
395 | throw new InvalidArgumentException("Invalid $options"); |
||||
396 | } |
||||
397 | self::backend()->combineFiles($combinedFileName, $files, $options); |
||||
398 | } |
||||
399 | |||||
400 | /** |
||||
401 | * Return all combined files; keys are the combined file names, values are lists of |
||||
402 | * associative arrays with 'files', 'type', and 'media' keys for details about this |
||||
403 | * combined file. |
||||
404 | * |
||||
405 | * @return array |
||||
406 | */ |
||||
407 | public static function get_combine_files() |
||||
408 | { |
||||
409 | return self::backend()->getCombinedFiles(); |
||||
410 | } |
||||
411 | |||||
412 | /** |
||||
413 | * Deletes all generated combined files in the configured combined files directory, |
||||
414 | * but doesn't delete the directory itself |
||||
415 | */ |
||||
416 | public static function delete_all_combined_files() |
||||
417 | { |
||||
418 | self::backend()->deleteAllCombinedFiles(); |
||||
419 | } |
||||
420 | |||||
421 | /** |
||||
422 | * Re-sets the combined files definition. See {@link Requirements_Backend::clear_combined_files()} |
||||
423 | */ |
||||
424 | public static function clear_combined_files() |
||||
425 | { |
||||
426 | self::backend()->clearCombinedFiles(); |
||||
427 | } |
||||
428 | |||||
429 | /** |
||||
430 | * Do the heavy lifting involved in combining the combined files. |
||||
431 | */ |
||||
432 | public static function process_combined_files() |
||||
433 | { |
||||
434 | self::backend()->processCombinedFiles(); |
||||
435 | } |
||||
436 | |||||
437 | /** |
||||
438 | * Set whether you want to write the JS to the body of the page rather than at the end of the |
||||
439 | * head tag. |
||||
440 | * |
||||
441 | * @return bool |
||||
442 | */ |
||||
443 | public static function get_write_js_to_body() |
||||
444 | { |
||||
445 | return self::backend()->getWriteJavascriptToBody(); |
||||
446 | } |
||||
447 | |||||
448 | /** |
||||
449 | * Set whether you want to write the JS to the body of the page rather than at the end of the |
||||
450 | * head tag. |
||||
451 | * |
||||
452 | * @param bool |
||||
453 | */ |
||||
454 | public static function set_write_js_to_body($var) |
||||
455 | { |
||||
456 | self::backend()->setWriteJavascriptToBody($var); |
||||
457 | } |
||||
458 | |||||
459 | /** |
||||
460 | * Get whether to force the JavaScript to end of the body. Useful if you use inline script tags |
||||
461 | * that don't rely on scripts included via {@link Requirements::javascript()). |
||||
462 | * |
||||
463 | * @return bool |
||||
464 | */ |
||||
465 | public static function get_force_js_to_bottom() |
||||
466 | { |
||||
467 | return self::backend()->getForceJSToBottom(); |
||||
468 | } |
||||
469 | |||||
470 | /** |
||||
471 | * Set whether to force the JavaScript to end of the body. Useful if you use inline script tags |
||||
472 | * that don't rely on scripts included via {@link Requirements::javascript()). |
||||
473 | * |
||||
474 | * @param bool $var If true, force the JavaScript to be included at the bottom of the page |
||||
475 | */ |
||||
476 | public static function set_force_js_to_bottom($var) |
||||
477 | { |
||||
478 | self::backend()->setForceJSToBottom($var); |
||||
479 | } |
||||
480 | |||||
481 | /** |
||||
482 | * Check if JS minification is enabled |
||||
483 | * |
||||
484 | * @return bool |
||||
485 | */ |
||||
486 | public static function get_minify_combined_js_files() |
||||
487 | { |
||||
488 | return self::backend()->getMinifyCombinedJSFiles(); |
||||
0 ignored issues
–
show
The method
getMinifyCombinedJSFiles() does not exist on SilverStripe\View\Requirements_Backend . Did you maybe mean getMinifyCombinedFiles() ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||
489 | } |
||||
490 | |||||
491 | /** |
||||
492 | * Enable or disable js minification |
||||
493 | * |
||||
494 | * @param bool $minify |
||||
495 | */ |
||||
496 | public static function set_minify_combined_js_files($minify) |
||||
497 | { |
||||
498 | self::backend()->setMinifyCombinedJSFiles($minify); |
||||
0 ignored issues
–
show
The method
setMinifyCombinedJSFiles() does not exist on SilverStripe\View\Requirements_Backend . Did you maybe mean setMinifyCombinedFiles() ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||
499 | } |
||||
500 | |||||
501 | /** |
||||
502 | * Check if header comments are written |
||||
503 | * |
||||
504 | * @return bool |
||||
505 | */ |
||||
506 | public static function get_write_header_comments() |
||||
507 | { |
||||
508 | return self::backend()->getWriteHeaderComment(); |
||||
509 | } |
||||
510 | |||||
511 | /** |
||||
512 | * Flag whether header comments should be written for each combined file |
||||
513 | * |
||||
514 | * @param bool $write |
||||
515 | */ |
||||
516 | public function set_write_header_comments($write) |
||||
517 | { |
||||
518 | self::backend()->setWriteHeaderComment($write); |
||||
519 | } |
||||
520 | |||||
521 | |||||
522 | /** |
||||
523 | * Output debugging information |
||||
524 | */ |
||||
525 | public static function debug() |
||||
526 | { |
||||
527 | self::backend()->debug(); |
||||
528 | } |
||||
529 | } |
||||
530 |