PluginProbe
Gutenberg / 17.3.1
Gutenberg v17.3.1
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / client-assets.php

client-assets.php in Gutenberg 17.3.1, at lib/client-assets.php

597 lines 20.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Functions to register client-side assets (scripts and stylesheets) specific
4 * for the Gutenberg editor plugin.
5 *
6 * @package gutenberg
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 die( 'Silence is golden.' );
11 }
12
13 /**
14 * Retrieves the root plugin path.
15 *
16 * @return string Root path to the gutenberg plugin.
17 *
18 * @since 0.1.0
19 */
20 function gutenberg_dir_path() {
21 return plugin_dir_path( __DIR__ );
22 }
23
24 /**
25 * Retrieves a URL to a file in the gutenberg plugin.
26 *
27 * @param string $path Relative path of the desired file.
28 *
29 * @return string Fully qualified URL pointing to the desired file.
30 *
31 * @since 0.1.0
32 */
33 function gutenberg_url( $path ) {
34 return plugins_url( $path, __DIR__ );
35 }
36
37 /**
38 * Registers a script according to `wp_register_script`. Honors this request by
39 * reassigning internal dependency properties of any script handle already
40 * registered by that name. It does not deregister the original script, to
41 * avoid losing inline scripts which may have been attached.
42 *
43 * @since 4.1.0
44 *
45 * @param WP_Scripts $scripts WP_Scripts instance.
46 * @param string $handle Name of the script. Should be unique.
47 * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory.
48 * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array.
49 * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
50 * as a query string for cache busting purposes. If version is set to false, a version
51 * number is automatically added equal to current installed WordPress version.
52 * If set to null, no version is added.
53 * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
54 * Default 'false'.
55 */
56 function gutenberg_override_script( $scripts, $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
57 /*
58 * Force `wp-i18n` script to be registered in the <head> as a
59 * temporary workaround for https://meta.trac.wordpress.org/ticket/6195.
60 */
61 $in_footer = 'wp-i18n' === $handle ? false : $in_footer;
62
63 $script = $scripts->query( $handle, 'registered' );
64 if ( $script ) {
65 /*
66 * In many ways, this is a reimplementation of `wp_register_script` but
67 * bypassing consideration of whether a script by the given handle had
68 * already been registered.
69 */
70
71 // See: `_WP_Dependency::__construct` .
72 $script->src = $src;
73 $script->deps = $deps;
74 $script->ver = $ver;
75 $script->args = $in_footer ? 1 : null;
76 } else {
77 $scripts->add( $handle, $src, $deps, $ver, ( $in_footer ? 1 : null ) );
78 }
79
80 if ( in_array( 'wp-i18n', $deps, true ) ) {
81 $scripts->set_translations( $handle );
82 }
83
84 /*
85 * Wp-editor module is exposed as window.wp.editor.
86 * Problem: there is quite some code expecting window.wp.oldEditor object available under window.wp.editor.
87 * Solution: fuse the two objects together to maintain backward compatibility.
88 * For more context, see https://github.com/WordPress/gutenberg/issues/33203
89 */
90 if ( 'wp-editor' === $handle ) {
91 $scripts->add_inline_script(
92 'wp-editor',
93 'Object.assign( window.wp.editor, window.wp.oldEditor );',
94 'after'
95 );
96 }
97 }
98
99 /**
100 * Filters the default translation file load behavior to load the Gutenberg
101 * plugin translation file, if available.
102 *
103 * @param string|false $file Path to the translation file to load. False if
104 * there isn't one.
105 * @param string $handle Name of the script to register a translation
106 * domain to.
107 *
108 * @return string|false Filtered path to the Gutenberg translation file, if
109 * available.
110 */
111 function gutenberg_override_translation_file( $file, $handle ) {
112 if ( ! $file ) {
113 return $file;
114 }
115
116 // Ignore scripts whose handle does not have the "wp-" prefix.
117 if ( ! str_starts_with( $handle, 'wp-' ) ) {
118 return $file;
119 }
120
121 // Ignore scripts that are not found in the expected `build/` location.
122 $script_path = gutenberg_dir_path() . 'build/' . substr( $handle, 3 ) . '/index.min.js';
123 if ( ! file_exists( $script_path ) ) {
124 return $file;
125 }
126
127 /*
128 * The default file will be in the plugins language directory, omitting the
129 * domain since Gutenberg assigns the script translations as the default.
130 *
131 * Example: /www/wp-content/languages/plugins/de_DE-07d88e6a803e01276b9bfcc1203e862e.json
132 *
133 * The logic of `load_script_textdomain` is such that it will assume to
134 * search in the plugins language directory, since the assigned source of
135 * the overridden Gutenberg script originates in the plugins directory.
136 *
137 * The plugin translation files each begin with the slug of the plugin, so
138 * it's a simple matter of prepending the Gutenberg plugin slug.
139 */
140 $path_parts = pathinfo( $file );
141 $plugin_translation_file = (
142 $path_parts['dirname'] .
143 '/gutenberg-' .
144 $path_parts['basename']
145 );
146
147 return $plugin_translation_file;
148 }
149 add_filter( 'load_script_translation_file', 'gutenberg_override_translation_file', 10, 2 );
150
151 /**
152 * Registers a style according to `wp_register_style`. Honors this request by
153 * deregistering any style by the same handler before registration.
154 *
155 * @since 4.1.0
156 *
157 * @param WP_Styles $styles WP_Styles instance.
158 * @param string $handle Name of the stylesheet. Should be unique.
159 * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
160 * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
161 * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL
162 * as a query string for cache busting purposes. If version is set to false, a version
163 * number is automatically added equal to current installed WordPress version.
164 * If set to null, no version is added.
165 * @param string $media Optional. The media for which this stylesheet has been defined.
166 * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like
167 * '(orientation: portrait)' and '(max-width: 640px)'.
168 */
169 function gutenberg_override_style( $styles, $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
170 $style = $styles->query( $handle, 'registered' );
171 if ( $style ) {
172 $styles->remove( $handle );
173 }
174 $styles->add( $handle, $src, $deps, $ver, $media );
175 }
176
177 /**
178 * Registers all the WordPress packages scripts that are in the standardized
179 * `build/` location.
180 *
181 * @since 4.5.0
182 *
183 * @param WP_Scripts $scripts WP_Scripts instance.
184 */
185 function gutenberg_register_packages_scripts( $scripts ) {
186 // When in production, use the plugin's version as the default asset version;
187 // else (for development or test) default to use the current time.
188 $default_version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time();
189
190 foreach ( glob( gutenberg_dir_path() . 'build/*/index.min.js' ) as $path ) {
191 // Prefix `wp-` to package directory to get script handle.
192 // For example, `…/build/a11y/index.min.js` becomes `wp-a11y`.
193 $handle = 'wp-' . basename( dirname( $path ) );
194
195 // Replace extension with `.asset.php` to find the generated dependencies file.
196 $asset_file = substr( $path, 0, -( strlen( '.js' ) ) ) . '.asset.php';
197 $asset = file_exists( $asset_file )
198 ? require $asset_file
199 : null;
200 $dependencies = isset( $asset['dependencies'] ) ? $asset['dependencies'] : array();
201 $version = isset( $asset['version'] ) ? $asset['version'] : $default_version;
202
203 // Add dependencies that cannot be detected and generated by build tools.
204 switch ( $handle ) {
205 case 'wp-block-library':
206 if (
207 ! gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ||
208 ! empty( $_GET['requiresTinymce'] ) ||
209 gutenberg_post_being_edited_requires_classic_block()
210 ) {
211 array_push( $dependencies, 'editor' );
212 }
213 break;
214
215 case 'wp-edit-post':
216 array_push( $dependencies, 'media-models', 'media-views', 'postbox' );
217 break;
218
219 case 'wp-edit-site':
220 array_push( $dependencies, 'wp-dom-ready' );
221 break;
222 case 'wp-preferences':
223 array_push( $dependencies, 'wp-preferences-persistence' );
224 break;
225 }
226
227 // Get the path from Gutenberg directory as expected by `gutenberg_url`.
228 $gutenberg_path = substr( $path, strlen( gutenberg_dir_path() ) );
229
230 gutenberg_override_script(
231 $scripts,
232 $handle,
233 gutenberg_url( $gutenberg_path ),
234 $dependencies,
235 $version,
236 true
237 );
238 }
239 }
240 add_action( 'wp_default_scripts', 'gutenberg_register_packages_scripts' );
241
242 /**
243 * Registers all the WordPress packages styles that are in the standardized
244 * `build/` location.
245 *
246 * @since 6.7.0
247
248 * @param WP_Styles $styles WP_Styles instance.
249 */
250 function gutenberg_register_packages_styles( $styles ) {
251 // When in production, use the plugin's version as the asset version;
252 // else (for development or test) default to use the current time.
253 $version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time();
254
255 gutenberg_override_style(
256 $styles,
257 'wp-block-editor-content',
258 gutenberg_url( 'build/block-editor/content.css' ),
259 array( 'wp-components' ),
260 $version
261 );
262 $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' );
263
264 // Editor Styles.
265 gutenberg_override_style(
266 $styles,
267 'wp-block-editor',
268 gutenberg_url( 'build/block-editor/style.css' ),
269 array( 'wp-components' ),
270 $version
271 );
272 $styles->add_data( 'wp-block-editor', 'rtl', 'replace' );
273
274 gutenberg_override_style(
275 $styles,
276 'wp-editor',
277 gutenberg_url( 'build/editor/style.css' ),
278 array( 'wp-components', 'wp-block-editor', 'wp-patterns', 'wp-reusable-blocks' ),
279 $version
280 );
281 $styles->add_data( 'wp-editor', 'rtl', 'replace' );
282
283 gutenberg_override_style(
284 $styles,
285 'wp-edit-post',
286 gutenberg_url( 'build/edit-post/style.css' ),
287 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-commands' ),
288 $version
289 );
290 $styles->add_data( 'wp-edit-post', 'rtl', 'replace' );
291
292 gutenberg_override_style(
293 $styles,
294 'wp-components',
295 gutenberg_url( 'build/components/style.css' ),
296 array( 'dashicons' ),
297 $version
298 );
299 $styles->add_data( 'wp-components', 'rtl', 'replace' );
300
301 $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style';
302 gutenberg_override_style(
303 $styles,
304 'wp-block-library',
305 gutenberg_url( 'build/block-library/' . $block_library_filename . '.css' ),
306 array(),
307 $version
308 );
309 $styles->add_data( 'wp-block-library', 'rtl', 'replace' );
310 $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/block-library/' . $block_library_filename . '.css' );
311
312 gutenberg_override_style(
313 $styles,
314 'wp-format-library',
315 gutenberg_url( 'build/format-library/style.css' ),
316 array( 'wp-block-editor', 'wp-components' ),
317 $version
318 );
319 $styles->add_data( 'wp-format-library', 'rtl', 'replace' );
320
321 $wp_edit_blocks_dependencies = array(
322 'wp-components',
323 // This need to be added before the block library styles,
324 // The block library styles override the "reset" styles.
325 'wp-reset-editor-styles',
326 'wp-block-library',
327 'wp-patterns',
328 'wp-reusable-blocks',
329 // Until #37466, we can't specifically add them as editor styles yet,
330 // so we must hard-code it here as a dependency.
331 'wp-block-editor-content',
332 );
333
334 // Only load the default layout and margin styles for themes without theme.json file.
335 if ( ! wp_theme_has_theme_json() ) {
336 $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
337 }
338
339 global $editor_styles;
340 if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) {
341 // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken.
342 $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
343 }
344
345 gutenberg_override_style(
346 $styles,
347 'wp-reset-editor-styles',
348 gutenberg_url( 'build/block-library/reset.css' ),
349 array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
350 $version
351 );
352 $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
353
354 gutenberg_override_style(
355 $styles,
356 'wp-editor-classic-layout-styles',
357 gutenberg_url( 'build/edit-post/classic.css' ),
358 array(),
359 $version
360 );
361 $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
362
363 gutenberg_override_style(
364 $styles,
365 'wp-edit-blocks',
366 gutenberg_url( 'build/block-library/editor.css' ),
367 $wp_edit_blocks_dependencies,
368 $version
369 );
370 $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
371
372 gutenberg_override_style(
373 $styles,
374 'wp-nux',
375 gutenberg_url( 'build/nux/style.css' ),
376 array( 'wp-components' ),
377 $version
378 );
379 $styles->add_data( 'wp-nux', 'rtl', 'replace' );
380
381 gutenberg_override_style(
382 $styles,
383 'wp-block-library-theme',
384 gutenberg_url( 'build/block-library/theme.css' ),
385 array(),
386 $version
387 );
388 $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
389
390 gutenberg_override_style(
391 $styles,
392 'wp-list-reusable-blocks',
393 gutenberg_url( 'build/list-reusable-blocks/style.css' ),
394 array( 'wp-components' ),
395 $version
396 );
397 $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
398
399 gutenberg_override_style(
400 $styles,
401 'wp-commands',
402 gutenberg_url( 'build/commands/style.css' ),
403 array( 'wp-components' ),
404 $version
405 );
406 $styles->add_data( 'wp-commands', 'rtl', 'replace' );
407
408 gutenberg_override_style(
409 $styles,
410 'wp-edit-site',
411 gutenberg_url( 'build/edit-site/style.css' ),
412 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-commands' ),
413 $version
414 );
415 $styles->add_data( 'wp-edit-site', 'rtl', 'replace' );
416
417 gutenberg_override_style(
418 $styles,
419 'wp-edit-widgets',
420 gutenberg_url( 'build/edit-widgets/style.css' ),
421 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-patterns', 'wp-reusable-blocks', 'wp-widgets' ),
422 $version
423 );
424 $styles->add_data( 'wp-edit-widgets', 'rtl', 'replace' );
425
426 gutenberg_override_style(
427 $styles,
428 'wp-block-directory',
429 gutenberg_url( 'build/block-directory/style.css' ),
430 array( 'wp-block-editor', 'wp-components' ),
431 $version
432 );
433 $styles->add_data( 'wp-block-directory', 'rtl', 'replace' );
434
435 gutenberg_override_style(
436 $styles,
437 'wp-customize-widgets',
438 gutenberg_url( 'build/customize-widgets/style.css' ),
439 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-widgets' ),
440 $version
441 );
442 $styles->add_data( 'wp-customize-widgets', 'rtl', 'replace' );
443
444 gutenberg_override_style(
445 $styles,
446 'wp-patterns',
447 gutenberg_url( 'build/patterns/style.css' ),
448 array( 'wp-components' ),
449 $version
450 );
451 $styles->add_data( 'wp-patterns', 'rtl', 'replace' );
452
453 gutenberg_override_style(
454 $styles,
455 'wp-reusable-blocks',
456 gutenberg_url( 'build/reusable-blocks/style.css' ),
457 array( 'wp-components' ),
458 $version
459 );
460 $styles->add_data( 'wp-reusable-blocks', 'rtl', 'replace' );
461
462 gutenberg_override_style(
463 $styles,
464 'wp-widgets',
465 gutenberg_url( 'build/widgets/style.css' ),
466 array( 'wp-components' )
467 );
468 $styles->add_data( 'wp-widgets', 'rtl', 'replace' );
469 }
470 add_action( 'wp_default_styles', 'gutenberg_register_packages_styles' );
471
472 /**
473 * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
474 * Styles are stored via the Style Engine API.
475 *
476 * This hook also exists, and should be backported to Core in future versions.
477 * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
478 *
479 * See: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
480 *
481 * @param array $options {
482 * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
483 *
484 * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
485 * @type bool $prettify Whether to add new lines and indents to output. Default is the test of whether the global constant `SCRIPT_DEBUG` is defined.
486 * }
487 *
488 * @since 6.1
489 *
490 * @return void
491 */
492 function gutenberg_enqueue_stored_styles( $options = array() ) {
493 $is_block_theme = wp_is_block_theme();
494 $is_classic_theme = ! $is_block_theme;
495
496 /*
497 * For block themes, print stored styles in the header.
498 * For classic themes, in the footer.
499 */
500 if (
501 ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
502 ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
503 ) {
504 return;
505 }
506
507 $core_styles_keys = array( 'block-supports' );
508 $compiled_core_stylesheet = '';
509 $style_tag_id = 'core';
510 foreach ( $core_styles_keys as $style_key ) {
511 // Adds comment if code is prettified to identify core styles sections in debugging.
512 $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
513 if ( $should_prettify ) {
514 $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
515 }
516 // Chains core store ids to signify what the styles contain.
517 $style_tag_id .= '-' . $style_key;
518 $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
519 }
520
521 // Combines Core styles.
522 if ( ! empty( $compiled_core_stylesheet ) ) {
523 wp_register_style( $style_tag_id, false, array(), true, true );
524 wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
525 wp_enqueue_style( $style_tag_id );
526 }
527
528 // If there are any other stores registered by themes etc., print them out.
529 $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
530
531 /*
532 * Since the corresponding action hook in Core is removed below,
533 * this function should still honour any styles stored using the Core Style Engine store.
534 */
535 if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
536 $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
537 }
538
539 foreach ( array_keys( $additional_stores ) as $store_name ) {
540 if ( in_array( $store_name, $core_styles_keys, true ) ) {
541 continue;
542 }
543 $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
544 if ( ! empty( $styles ) ) {
545 $key = "wp-style-engine-$store_name";
546 wp_register_style( $key, false, array(), true, true );
547 wp_add_inline_style( $key, $styles );
548 wp_enqueue_style( $key );
549 }
550 }
551 }
552
553 /**
554 * Registers vendor JavaScript files to be used as dependencies of the editor
555 * and plugins.
556 *
557 * This function is called from a script during the plugin build process, so it
558 * should not call any WordPress PHP functions.
559 *
560 * @since 13.0
561 *
562 * @param WP_Scripts $scripts WP_Scripts instance.
563 */
564 function gutenberg_register_vendor_scripts( $scripts ) {
565 $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
566
567 gutenberg_override_script(
568 $scripts,
569 'react',
570 gutenberg_url( 'build/vendors/react' . $extension ),
571 // See https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#externalising-react.
572 SCRIPT_DEBUG ? array( 'wp-react-refresh-entry', 'wp-polyfill' ) : array( 'wp-polyfill' ),
573 '18'
574 );
575 gutenberg_override_script(
576 $scripts,
577 'react-dom',
578 gutenberg_url( 'build/vendors/react-dom' . $extension ),
579 array( 'react' ),
580 '18'
581 );
582 }
583 add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
584
585
586 /*
587 * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
588 * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
589 * which are in continuous development and generally ahead of Core.
590 */
591 remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
592 remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
593
594 // Enqueue stored styles.
595 add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
596 add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
597