PluginProbe
Gutenberg / 16.2.0
Gutenberg v16.2.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 16.2.0, at lib/client-assets.php

578 lines 19.7 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-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-reusable-blocks',
328 // Until #37466, we can't specifically add them as editor styles yet,
329 // so we must hard-code it here as a dependency.
330 'wp-block-editor-content',
331 );
332
333 // Only load the default layout and margin styles for themes without theme.json file.
334 if ( ! wp_theme_has_theme_json() ) {
335 $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
336 }
337
338 global $editor_styles;
339 if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) {
340 // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken.
341 $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
342 }
343
344 gutenberg_override_style(
345 $styles,
346 'wp-reset-editor-styles',
347 gutenberg_url( 'build/block-library/reset.css' ),
348 array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
349 $version
350 );
351 $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
352
353 gutenberg_override_style(
354 $styles,
355 'wp-editor-classic-layout-styles',
356 gutenberg_url( 'build/edit-post/classic.css' ),
357 array(),
358 $version
359 );
360 $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
361
362 gutenberg_override_style(
363 $styles,
364 'wp-edit-blocks',
365 gutenberg_url( 'build/block-library/editor.css' ),
366 $wp_edit_blocks_dependencies,
367 $version
368 );
369 $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
370
371 gutenberg_override_style(
372 $styles,
373 'wp-block-library-theme',
374 gutenberg_url( 'build/block-library/theme.css' ),
375 array(),
376 $version
377 );
378 $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
379
380 gutenberg_override_style(
381 $styles,
382 'wp-list-reusable-blocks',
383 gutenberg_url( 'build/list-reusable-blocks/style.css' ),
384 array( 'wp-components' ),
385 $version
386 );
387 $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
388
389 gutenberg_override_style(
390 $styles,
391 'wp-commands',
392 gutenberg_url( 'build/commands/style.css' ),
393 array( 'wp-components' ),
394 $version
395 );
396 $styles->add_data( 'wp-commands', 'rtl', 'replace' );
397
398 gutenberg_override_style(
399 $styles,
400 'wp-edit-site',
401 gutenberg_url( 'build/edit-site/style.css' ),
402 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-commands' ),
403 $version
404 );
405 $styles->add_data( 'wp-edit-site', 'rtl', 'replace' );
406
407 gutenberg_override_style(
408 $styles,
409 'wp-edit-widgets',
410 gutenberg_url( 'build/edit-widgets/style.css' ),
411 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-reusable-blocks', 'wp-widgets' ),
412 $version
413 );
414 $styles->add_data( 'wp-edit-widgets', 'rtl', 'replace' );
415
416 gutenberg_override_style(
417 $styles,
418 'wp-block-directory',
419 gutenberg_url( 'build/block-directory/style.css' ),
420 array( 'wp-block-editor', 'wp-components' ),
421 $version
422 );
423 $styles->add_data( 'wp-block-directory', 'rtl', 'replace' );
424
425 gutenberg_override_style(
426 $styles,
427 'wp-customize-widgets',
428 gutenberg_url( 'build/customize-widgets/style.css' ),
429 array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks', 'wp-widgets' ),
430 $version
431 );
432 $styles->add_data( 'wp-customize-widgets', 'rtl', 'replace' );
433
434 gutenberg_override_style(
435 $styles,
436 'wp-reusable-blocks',
437 gutenberg_url( 'build/reusable-blocks/style.css' ),
438 array( 'wp-components' ),
439 $version
440 );
441 $styles->add_data( 'wp-reusable-blocks', 'rtl', 'replace' );
442
443 gutenberg_override_style(
444 $styles,
445 'wp-widgets',
446 gutenberg_url( 'build/widgets/style.css' ),
447 array( 'wp-components' )
448 );
449 $styles->add_data( 'wp-widgets', 'rtl', 'replace' );
450 }
451 add_action( 'wp_default_styles', 'gutenberg_register_packages_styles' );
452
453 /**
454 * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
455 * Styles are stored via the Style Engine API.
456 *
457 * This hook also exists, and should be backported to Core in future versions.
458 * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
459 *
460 * See: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
461 *
462 * @param array $options {
463 * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
464 *
465 * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
466 * @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.
467 * }
468 *
469 * @since 6.1
470 *
471 * @return void
472 */
473 function gutenberg_enqueue_stored_styles( $options = array() ) {
474 $is_block_theme = wp_is_block_theme();
475 $is_classic_theme = ! $is_block_theme;
476
477 /*
478 * For block themes, print stored styles in the header.
479 * For classic themes, in the footer.
480 */
481 if (
482 ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
483 ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
484 ) {
485 return;
486 }
487
488 $core_styles_keys = array( 'block-supports' );
489 $compiled_core_stylesheet = '';
490 $style_tag_id = 'core';
491 foreach ( $core_styles_keys as $style_key ) {
492 // Adds comment if code is prettified to identify core styles sections in debugging.
493 $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
494 if ( $should_prettify ) {
495 $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
496 }
497 // Chains core store ids to signify what the styles contain.
498 $style_tag_id .= '-' . $style_key;
499 $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
500 }
501
502 // Combines Core styles.
503 if ( ! empty( $compiled_core_stylesheet ) ) {
504 wp_register_style( $style_tag_id, false, array(), true, true );
505 wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
506 wp_enqueue_style( $style_tag_id );
507 }
508
509 // If there are any other stores registered by themes etc., print them out.
510 $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
511
512 /*
513 * Since the corresponding action hook in Core is removed below,
514 * this function should still honour any styles stored using the Core Style Engine store.
515 */
516 if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
517 $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
518 }
519
520 foreach ( array_keys( $additional_stores ) as $store_name ) {
521 if ( in_array( $store_name, $core_styles_keys, true ) ) {
522 continue;
523 }
524 $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
525 if ( ! empty( $styles ) ) {
526 $key = "wp-style-engine-$store_name";
527 wp_register_style( $key, false, array(), true, true );
528 wp_add_inline_style( $key, $styles );
529 wp_enqueue_style( $key );
530 }
531 }
532 }
533
534 /**
535 * Registers vendor JavaScript files to be used as dependencies of the editor
536 * and plugins.
537 *
538 * This function is called from a script during the plugin build process, so it
539 * should not call any WordPress PHP functions.
540 *
541 * @since 13.0
542 *
543 * @param WP_Scripts $scripts WP_Scripts instance.
544 */
545 function gutenberg_register_vendor_scripts( $scripts ) {
546 $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
547
548 gutenberg_override_script(
549 $scripts,
550 'react',
551 gutenberg_url( 'build/vendors/react' . $extension ),
552 // See https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#externalising-react.
553 SCRIPT_DEBUG ? array( 'wp-react-refresh-entry', 'wp-polyfill' ) : array( 'wp-polyfill' ),
554 '18'
555 );
556 gutenberg_override_script(
557 $scripts,
558 'react-dom',
559 gutenberg_url( 'build/vendors/react-dom' . $extension ),
560 array( 'react' ),
561 '18'
562 );
563 }
564 add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
565
566
567 /*
568 * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
569 * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
570 * which are in continuous development and generally ahead of Core.
571 */
572 remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
573 remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
574
575 // Enqueue stored styles.
576 add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
577 add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
578