PluginProbe
Gutenberg / 18.3.0
Gutenberg v18.3.0
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 18.3.0, at lib/client-assets.php

608 lines 20.4 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 * @since 0.1.0
17 *
18 * @return string Root path to the gutenberg plugin.
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 * @since 0.1.0
28 *
29 * @param string $path Relative path of the desired file.
30 *
31 * @return string Fully qualified URL pointing to the desired file.
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 * @global array $editor_styles
249 *
250 * @param WP_Styles $styles WP_Styles instance.
251 */
252 function gutenberg_register_packages_styles( $styles ) {
253 // When in production, use the plugin's version as the asset version;
254 // else (for development or test) default to use the current time.
255 $version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time();
256
257 gutenberg_override_style(
258 $styles,
259 'wp-block-editor-content',
260 gutenberg_url( 'build/block-editor/content.css' ),
261 array( 'wp-components' ),
262 $version
263 );
264 $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' );
265
266 // Editor Styles.
267 gutenberg_override_style(
268 $styles,
269 'wp-block-editor',
270 gutenberg_url( 'build/block-editor/style.css' ),
271 array( 'wp-components', 'wp-preferences' ),
272 $version
273 );
274 $styles->add_data( 'wp-block-editor', 'rtl', 'replace' );
275
276 gutenberg_override_style(
277 $styles,
278 'wp-editor',
279 gutenberg_url( 'build/editor/style.css' ),
280 array( 'wp-components', 'wp-block-editor', 'wp-patterns', 'wp-reusable-blocks', 'wp-preferences' ),
281 $version
282 );
283 $styles->add_data( 'wp-editor', 'rtl', 'replace' );
284
285 gutenberg_override_style(
286 $styles,
287 'wp-edit-post',
288 gutenberg_url( 'build/edit-post/style.css' ),
289 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-commands', 'wp-preferences' ),
290 $version
291 );
292 $styles->add_data( 'wp-edit-post', 'rtl', 'replace' );
293
294 gutenberg_override_style(
295 $styles,
296 'wp-components',
297 gutenberg_url( 'build/components/style.css' ),
298 array( 'dashicons' ),
299 $version
300 );
301 $styles->add_data( 'wp-components', 'rtl', 'replace' );
302
303 $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style';
304 gutenberg_override_style(
305 $styles,
306 'wp-block-library',
307 gutenberg_url( 'build/block-library/' . $block_library_filename . '.css' ),
308 array(),
309 $version
310 );
311 $styles->add_data( 'wp-block-library', 'rtl', 'replace' );
312 $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/block-library/' . $block_library_filename . '.css' );
313
314 gutenberg_override_style(
315 $styles,
316 'wp-format-library',
317 gutenberg_url( 'build/format-library/style.css' ),
318 array( 'wp-block-editor', 'wp-components' ),
319 $version
320 );
321 $styles->add_data( 'wp-format-library', 'rtl', 'replace' );
322
323 $wp_edit_blocks_dependencies = array(
324 'wp-components',
325 // This need to be added before the block library styles,
326 // The block library styles override the "reset" styles.
327 'wp-reset-editor-styles',
328 'wp-block-library',
329 'wp-patterns',
330 'wp-reusable-blocks',
331 // Until #37466, we can't specifically add them as editor styles yet,
332 // so we must hard-code it here as a dependency.
333 'wp-block-editor-content',
334 );
335
336 // Only load the default layout and margin styles for themes without theme.json file.
337 if ( ! wp_theme_has_theme_json() ) {
338 $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
339 }
340
341 global $editor_styles;
342 if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) {
343 // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken.
344 $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
345 }
346
347 gutenberg_override_style(
348 $styles,
349 'wp-reset-editor-styles',
350 gutenberg_url( 'build/block-library/reset.css' ),
351 array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
352 $version
353 );
354 $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
355
356 gutenberg_override_style(
357 $styles,
358 'wp-editor-classic-layout-styles',
359 gutenberg_url( 'build/edit-post/classic.css' ),
360 array(),
361 $version
362 );
363 $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
364
365 gutenberg_override_style(
366 $styles,
367 'wp-edit-blocks',
368 gutenberg_url( 'build/block-library/editor.css' ),
369 $wp_edit_blocks_dependencies,
370 $version
371 );
372 $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
373
374 gutenberg_override_style(
375 $styles,
376 'wp-nux',
377 gutenberg_url( 'build/nux/style.css' ),
378 array( 'wp-components' ),
379 $version
380 );
381 $styles->add_data( 'wp-nux', 'rtl', 'replace' );
382
383 gutenberg_override_style(
384 $styles,
385 'wp-block-library-theme',
386 gutenberg_url( 'build/block-library/theme.css' ),
387 array(),
388 $version
389 );
390 $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
391
392 gutenberg_override_style(
393 $styles,
394 'wp-list-reusable-blocks',
395 gutenberg_url( 'build/list-reusable-blocks/style.css' ),
396 array( 'wp-components' ),
397 $version
398 );
399 $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
400
401 gutenberg_override_style(
402 $styles,
403 'wp-commands',
404 gutenberg_url( 'build/commands/style.css' ),
405 array( 'wp-components' ),
406 $version
407 );
408 $styles->add_data( 'wp-commands', 'rtl', 'replace' );
409
410 gutenberg_override_style(
411 $styles,
412 'wp-edit-site',
413 gutenberg_url( 'build/edit-site/style.css' ),
414 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-commands', 'wp-preferences' ),
415 $version
416 );
417 $styles->add_data( 'wp-edit-site', 'rtl', 'replace' );
418
419 gutenberg_override_style(
420 $styles,
421 'wp-edit-widgets',
422 gutenberg_url( 'build/edit-widgets/style.css' ),
423 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-patterns', 'wp-reusable-blocks', 'wp-widgets', 'wp-preferences' ),
424 $version
425 );
426 $styles->add_data( 'wp-edit-widgets', 'rtl', 'replace' );
427
428 gutenberg_override_style(
429 $styles,
430 'wp-block-directory',
431 gutenberg_url( 'build/block-directory/style.css' ),
432 array( 'wp-block-editor', 'wp-components' ),
433 $version
434 );
435 $styles->add_data( 'wp-block-directory', 'rtl', 'replace' );
436
437 gutenberg_override_style(
438 $styles,
439 'wp-customize-widgets',
440 gutenberg_url( 'build/customize-widgets/style.css' ),
441 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-widgets', 'wp-preferences' ),
442 $version
443 );
444 $styles->add_data( 'wp-customize-widgets', 'rtl', 'replace' );
445
446 gutenberg_override_style(
447 $styles,
448 'wp-patterns',
449 gutenberg_url( 'build/patterns/style.css' ),
450 array( 'wp-components' ),
451 $version
452 );
453 $styles->add_data( 'wp-patterns', 'rtl', 'replace' );
454
455 gutenberg_override_style(
456 $styles,
457 'wp-reusable-blocks',
458 gutenberg_url( 'build/reusable-blocks/style.css' ),
459 array( 'wp-components' ),
460 $version
461 );
462 $styles->add_data( 'wp-reusable-blocks', 'rtl', 'replace' );
463
464 gutenberg_override_style(
465 $styles,
466 'wp-widgets',
467 gutenberg_url( 'build/widgets/style.css' ),
468 array( 'wp-components' )
469 );
470 $styles->add_data( 'wp-widgets', 'rtl', 'replace' );
471
472 gutenberg_override_style(
473 $styles,
474 'wp-preferences',
475 gutenberg_url( 'build/preferences/style.css' ),
476 array( 'wp-components' ),
477 $version
478 );
479 $styles->add_data( 'wp-preferences', 'rtl', 'replace' );
480 }
481 add_action( 'wp_default_styles', 'gutenberg_register_packages_styles' );
482
483 /**
484 * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
485 * Styles are stored via the Style Engine API.
486 *
487 * This hook also exists, and should be backported to Core in future versions.
488 * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
489 *
490 * See: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
491 *
492 * @param array $options {
493 * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
494 *
495 * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
496 * @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.
497 * }
498 *
499 * @since 6.1
500 *
501 * @return void
502 */
503 function gutenberg_enqueue_stored_styles( $options = array() ) {
504 $is_block_theme = wp_is_block_theme();
505 $is_classic_theme = ! $is_block_theme;
506
507 /*
508 * For block themes, print stored styles in the header.
509 * For classic themes, in the footer.
510 */
511 if (
512 ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
513 ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
514 ) {
515 return;
516 }
517
518 $core_styles_keys = array( 'block-supports' );
519 $compiled_core_stylesheet = '';
520 $style_tag_id = 'core';
521 foreach ( $core_styles_keys as $style_key ) {
522 // Adds comment if code is prettified to identify core styles sections in debugging.
523 $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
524 if ( $should_prettify ) {
525 $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
526 }
527 // Chains core store ids to signify what the styles contain.
528 $style_tag_id .= '-' . $style_key;
529 $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
530 }
531
532 // Combines Core styles.
533 if ( ! empty( $compiled_core_stylesheet ) ) {
534 wp_register_style( $style_tag_id, false, array(), true, true );
535 wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
536 wp_enqueue_style( $style_tag_id );
537 }
538
539 // If there are any other stores registered by themes etc., print them out.
540 $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
541
542 /*
543 * Since the corresponding action hook in Core is removed below,
544 * this function should still honour any styles stored using the Core Style Engine store.
545 */
546 if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
547 $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
548 }
549
550 foreach ( array_keys( $additional_stores ) as $store_name ) {
551 if ( in_array( $store_name, $core_styles_keys, true ) ) {
552 continue;
553 }
554 $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
555 if ( ! empty( $styles ) ) {
556 $key = "wp-style-engine-$store_name";
557 wp_register_style( $key, false, array(), true, true );
558 wp_add_inline_style( $key, $styles );
559 wp_enqueue_style( $key );
560 }
561 }
562 }
563
564 /**
565 * Registers vendor JavaScript files to be used as dependencies of the editor
566 * and plugins.
567 *
568 * This function is called from a script during the plugin build process, so it
569 * should not call any WordPress PHP functions.
570 *
571 * @since 13.0
572 *
573 * @param WP_Scripts $scripts WP_Scripts instance.
574 */
575 function gutenberg_register_vendor_scripts( $scripts ) {
576 $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
577
578 gutenberg_override_script(
579 $scripts,
580 'react',
581 gutenberg_url( 'build/vendors/react' . $extension ),
582 // See https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#externalising-react.
583 SCRIPT_DEBUG ? array( 'wp-react-refresh-entry', 'wp-polyfill' ) : array( 'wp-polyfill' ),
584 '18'
585 );
586 gutenberg_override_script(
587 $scripts,
588 'react-dom',
589 gutenberg_url( 'build/vendors/react-dom' . $extension ),
590 array( 'react' ),
591 '18'
592 );
593 }
594 add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
595
596
597 /*
598 * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
599 * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
600 * which are in continuous development and generally ahead of Core.
601 */
602 remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
603 remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
604
605 // Enqueue stored styles.
606 add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
607 add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
608