PluginProbe
Gutenberg / 4.7.0
Gutenberg v4.7.0
24.0.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 All 403 releases
← All changes | lib/client-assets.php +1250 -321 23.5.24.7.0 View file →
@@ -1,8 +1,8 @@
1 1 <?php
2 2 /**
3 - * Functions to register client-side assets (scripts and stylesheets) specific
4 - * for the Gutenberg editor plugin.
3 + * Functions to register client-side assets (scripts and stylesheets) for the
4 + * Gutenberg editor plugin.
5 5 *
6 6 * @package gutenberg
7 7 */
8 8
@@ -12,358 +12,585 @@
12 12
13 13 /**
14 14 * Retrieves the root plugin path.
15 15 *
16 + * @return string Root path to the gutenberg plugin.
17 + *
16 18 * @since 0.1.0
17 - *
18 - * @return string Root path to the gutenberg plugin.
19 19 */
20 20 function gutenberg_dir_path() {
21 - return plugin_dir_path( __DIR__ );
21 + return plugin_dir_path( dirname( __FILE__ ) );
22 22 }
23 23
24 24 /**
25 25 * Retrieves a URL to a file in the gutenberg plugin.
26 26 *
27 - * @since 0.1.0
28 - *
29 27 * @param string $path Relative path of the desired file.
30 28 *
31 29 * @return string Fully qualified URL pointing to the desired file.
30 + *
31 + * @since 0.1.0
32 32 */
33 33 function gutenberg_url( $path ) {
34 - return plugins_url( $path, __DIR__ );
34 + return plugins_url( $path, dirname( __FILE__ ) );
35 35 }
36 36
37 37 /**
38 - * Filters the default translation file load behavior to load the Gutenberg
39 - * plugin translation file, if available.
38 + * Returns contents of an inline script used in appending polyfill scripts for
39 + * browsers which fail the provided tests. The provided array is a mapping from
40 + * a condition to verify feature support to its polyfill script handle.
40 41 *
41 - * @param string|false $file Path to the translation file to load. False if
42 - * there isn't one.
43 - * @param string $handle Name of the script to register a translation
44 - * domain to.
45 - *
46 - * @return string|false Filtered path to the Gutenberg translation file, if
47 - * available.
42 + * @param array $tests Features to detect.
43 + * @return string Conditional polyfill inline script.
48 44 */
49 -function gutenberg_override_translation_file( $file, $handle ) {
50 - if ( ! $file ) {
51 - return $file;
52 - }
45 +function gutenberg_get_script_polyfill( $tests ) {
46 + global $wp_scripts;
53 47
54 - // Ignore scripts whose handle does not have the "wp-" prefix.
55 - if ( ! str_starts_with( $handle, 'wp-' ) ) {
56 - return $file;
48 + $polyfill = '';
49 + foreach ( $tests as $test => $handle ) {
50 + if ( ! array_key_exists( $handle, $wp_scripts->registered ) ) {
51 + continue;
52 + }
53 +
54 + $polyfill .= (
55 + // Test presence of feature...
56 + '( ' . $test . ' ) || ' .
57 + // ...appending polyfill on any failures. Cautious viewers may balk
58 + // at the `document.write`. Its caveat of synchronous mid-stream
59 + // blocking write is exactly the behavior we need though.
60 + 'document.write( \'<script src="' .
61 + esc_url( $wp_scripts->registered[ $handle ]->src ) .
62 + '"></scr\' + \'ipt>\' );'
63 + );
57 64 }
58 65
59 - // Ignore scripts that are not found in the expected `build/scripts/` location.
60 - $script_path = gutenberg_dir_path() . 'build/scripts/' . substr( $handle, 3 ) . '/index.min.js';
61 - if ( ! file_exists( $script_path ) ) {
62 - return $file;
63 - }
66 + return $polyfill;
67 +}
64 68
65 - /*
66 - * The default file will be in the plugins language directory, omitting the
67 - * domain since Gutenberg assigns the script translations as the default.
68 - *
69 - * Example: /www/wp-content/languages/plugins/de_DE-07d88e6a803e01276b9bfcc1203e862e.json
70 - *
71 - * The logic of `load_script_textdomain` is such that it will assume to
72 - * search in the plugins language directory, since the assigned source of
73 - * the overridden Gutenberg script originates in the plugins directory.
74 - *
75 - * The plugin translation files each begin with the slug of the plugin, so
76 - * it's a simple matter of prepending the Gutenberg plugin slug.
69 +if ( ! function_exists( 'register_tinymce_scripts' ) ) {
70 + /**
71 + * Registers the main TinyMCE scripts.
77 72 */
78 - $path_parts = pathinfo( $file );
79 - $plugin_translation_file = (
80 - $path_parts['dirname'] .
81 - '/gutenberg-' .
82 - $path_parts['basename']
83 - );
73 + function register_tinymce_scripts() {
74 + global $tinymce_version, $concatenate_scripts, $compress_scripts;
75 + if ( ! isset( $concatenate_scripts ) ) {
76 + script_concat_settings();
77 + }
78 + $suffix = SCRIPT_DEBUG ? '' : '.min';
79 + $compressed = $compress_scripts && $concatenate_scripts && isset( $_SERVER['HTTP_ACCEPT_ENCODING'] )
80 + && false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' );
81 + // Load tinymce.js when running from /src, otherwise load wp-tinymce.js.gz (in production) or
82 + // tinymce.min.js (when SCRIPT_DEBUG is true).
83 + $mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min';
84 + if ( $compressed ) {
85 + gutenberg_override_script( 'wp-tinymce', includes_url( 'js/tinymce/' ) . 'wp-tinymce.php', array(), $tinymce_version );
86 + } else {
87 + gutenberg_override_script( 'wp-tinymce-root', includes_url( 'js/tinymce/' ) . "tinymce{$mce_suffix}.js", array(), $tinymce_version );
88 + gutenberg_override_script( 'wp-tinymce', includes_url( 'js/tinymce/' ) . "plugins/compat3x/plugin{$suffix}.js", array( 'wp-tinymce-root' ), $tinymce_version );
89 + }
84 90
85 - return $plugin_translation_file;
91 + gutenberg_override_script( 'wp-tinymce-lists', includes_url( 'js/tinymce/' ) . "plugins/lists/plugin{$suffix}.js", array( 'wp-tinymce' ), $tinymce_version );
92 + }
86 93 }
87 -add_filter( 'load_script_translation_file', 'gutenberg_override_translation_file', 10, 2 );
88 94
89 95 /**
90 - * Adds the 'editor' dependency to wp-block-library, required by the Classic block.
96 + * Registers a script according to `wp_register_script`. Honors this request by
97 + * deregistering any script by the same handler before registration.
91 98 *
92 - * @param WP_Scripts $scripts WP_Scripts instance.
99 + * @since 4.1.0
100 + *
101 + * @param string $handle Name of the script. Should be unique.
102 + * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory.
103 + * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array.
104 + * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
105 + * as a query string for cache busting purposes. If version is set to false, a version
106 + * number is automatically added equal to current installed WordPress version.
107 + * If set to null, no version is added.
108 + * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
109 + * Default 'false'.
93 110 */
94 -function gutenberg_register_block_library_script_special_case( $scripts ) {
95 - $handle = 'wp-block-library';
96 - $script = $scripts->query( $handle, 'registered' );
97 - if ( ! in_array( 'editor', $script->deps, true ) ) {
98 - $script->deps[] = 'editor';
99 - }
111 +function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
112 + wp_deregister_script( $handle );
113 + wp_register_script( $handle, $src, $deps, $ver, $in_footer );
100 114 }
101 -add_action( 'wp_default_scripts', 'gutenberg_register_block_library_script_special_case', 11 );
102 115
103 116 /**
104 - * Registers WordPress package styles with complex requirements.
117 + * Registers a style according to `wp_register_style`. Honors this request by
118 + * deregistering any style by the same handler before registration.
105 119 *
106 - * Simple styles (main style.css with inferred dependencies) are auto-registered
107 - * via build/styles.php at default priority (10). This function runs at priority 15 to handle:
108 - * - Multiple style files per package (content.css, classic.css, etc.)
109 - * - Non-WordPress dependencies (dashicons, common, forms)
110 - * - Custom handles that don't match wp-{package} pattern
111 - * - Conditional dependencies based on theme/settings
112 - * - Dynamic filename logic
120 + * @since 4.1.0
113 121 *
114 - * These override calls will replace the auto-registered versions as needed.
122 + * @param string $handle Name of the stylesheet. Should be unique.
123 + * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
124 + * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
125 + * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL
126 + * as a query string for cache busting purposes. If version is set to false, a version
127 + * number is automatically added equal to current installed WordPress version.
128 + * If set to null, no version is added.
129 + * @param string $media Optional. The media for which this stylesheet has been defined.
130 + * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like
131 + * '(orientation: portrait)' and '(max-width: 640px)'.
132 + */
133 +function gutenberg_override_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
134 + wp_deregister_style( $handle );
135 + wp_register_style( $handle, $src, $deps, $ver, $media );
136 +}
137 +
138 +/**
139 + * Registers all the WordPress packages scripts that are in the standardized
140 + * `build/` location.
115 141 *
116 - * @since 6.7.0
117 - *
118 - * @global array $editor_styles
119 - *
120 - * @param WP_Styles $styles WP_Styles instance.
142 + * @since 4.5.0
121 143 */
122 -function gutenberg_register_packages_styles( $styles ) {
123 - // When in production, use the plugin's version as the asset version;
124 - // else (for development or test) default to use the current time.
125 - $version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time();
126 - $suffix = SCRIPT_DEBUG ? '' : '.min';
144 +function gutenberg_register_packages_scripts() {
145 + $packages_dependencies = include dirname( __FILE__ ) . '/packages-dependencies.php';
127 146
128 - // wp-components: add dashicons (icon font dependency)
129 - $styles->query( 'wp-components', 'registered' )->deps[] = 'dashicons';
147 + foreach ( $packages_dependencies as $handle => $dependencies ) {
148 + // Remove `wp-` prefix from the handle to get the package's name.
149 + $package_name = strpos( $handle, 'wp-' ) === 0 ? substr( $handle, 3 ) : $handle;
150 + $path = "build/$package_name/index.js";
151 + gutenberg_override_script(
152 + $handle,
153 + gutenberg_url( $path ),
154 + array_merge( $dependencies, array( 'wp-polyfill' ) ),
155 + filemtime( gutenberg_dir_path() . $path ),
156 + true
157 + );
158 + }
159 +}
130 160
131 - // wp-edit-post: add wp-edit-blocks (custom handle not auto-inferred)
132 - $styles->query( 'wp-edit-post', 'registered' )->deps[] = 'wp-edit-blocks';
161 +/**
162 + * Registers common scripts and styles to be used as dependencies of the editor
163 + * and plugins.
164 + *
165 + * @since 0.1.0
166 + */
167 +function gutenberg_register_scripts_and_styles() {
168 + gutenberg_register_vendor_scripts();
133 169
134 - // wp-edit-site: add core WP styles and custom handles
135 - $edit_site_style = $styles->query( 'wp-edit-site', 'registered' );
136 - $edit_site_style->deps[] = 'common';
137 - $edit_site_style->deps[] = 'forms';
138 - $edit_site_style->deps[] = 'wp-block-library-editor';
170 + register_tinymce_scripts();
139 171
140 - // wp-edit-widgets: add wp-edit-blocks (custom handle not auto-inferred)
141 - $styles->query( 'wp-edit-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
172 + wp_add_inline_script(
173 + 'wp-polyfill',
174 + gutenberg_get_script_polyfill(
175 + array(
176 + '\'fetch\' in window' => 'wp-polyfill-fetch',
177 + 'document.contains' => 'wp-polyfill-node-contains',
178 + 'window.FormData && window.FormData.prototype.keys' => 'wp-polyfill-formdata',
179 + 'Element.prototype.matches && Element.prototype.closest' => 'wp-polyfill-element-closest',
180 + ),
181 + 'after'
182 + )
183 + );
142 184
143 - // wp-customize-widgets: add wp-edit-blocks (custom handle not auto-inferred)
144 - $styles->query( 'wp-customize-widgets', 'registered' )->deps[] = 'wp-edit-blocks';
185 + gutenberg_register_packages_scripts();
145 186
146 - // Register wp-theme (Design System tokens from @wordpress/theme) as a
147 - // dependency of wp-base-styles so its `:root` token block loads
148 - // everywhere wp-base-styles does, including the editor iframe via
149 - // $wp_edit_blocks_dependencies below.
150 - gutenberg_override_style(
151 - $styles,
152 - 'wp-theme',
153 - gutenberg_url( 'build/styles/theme/design-tokens' . $suffix . '.css' ),
154 - array(),
155 - $version
187 + // Inline scripts.
188 + wp_add_inline_script(
189 + 'wp-api-fetch',
190 + sprintf(
191 + 'wp.apiFetch.use( wp.apiFetch.createNonceMiddleware( "%s" ) );',
192 + ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' )
193 + ),
194 + 'after'
156 195 );
157 - $styles->add_data( 'wp-theme', 'rtl', 'replace' );
158 - $styles->add_data( 'wp-theme', 'suffix', $suffix );
159 - $styles->add_data( 'wp-theme', 'path', gutenberg_dir_path() . 'build/styles/theme/design-tokens' . $suffix . '.css' );
196 + wp_add_inline_script(
197 + 'wp-api-fetch',
198 + sprintf(
199 + 'wp.apiFetch.use( wp.apiFetch.createRootURLMiddleware( "%s" ) );',
200 + esc_url_raw( get_rest_url() )
201 + ),
202 + 'after'
203 + );
204 + wp_add_inline_script(
205 + 'wp-data',
206 + implode(
207 + "\n",
208 + array(
209 + '( function() {',
210 + ' var userId = ' . get_current_user_ID() . ';',
211 + ' var storageKey = "WP_DATA_USER_" + userId;',
212 + ' wp.data',
213 + ' .use( wp.data.plugins.persistence, { storageKey: storageKey } )',
214 + ' .use( wp.data.plugins.controls );',
215 + '} )()',
216 + )
217 + )
218 + );
219 + global $wp_locale;
220 + wp_add_inline_script(
221 + 'wp-date',
222 + sprintf(
223 + 'wp.date.setSettings( %s );',
224 + wp_json_encode(
225 + array(
226 + 'l10n' => array(
227 + 'locale' => get_user_locale(),
228 + 'months' => array_values( $wp_locale->month ),
229 + 'monthsShort' => array_values( $wp_locale->month_abbrev ),
230 + 'weekdays' => array_values( $wp_locale->weekday ),
231 + 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
232 + 'meridiem' => (object) $wp_locale->meridiem,
233 + 'relative' => array(
234 + /* translators: %s: duration */
235 + 'future' => __( '%s from now', 'default' ),
236 + /* translators: %s: duration */
237 + 'past' => __( '%s ago', 'default' ),
238 + ),
239 + ),
240 + 'formats' => array(
241 + 'time' => get_option( 'time_format', __( 'g:i a', 'default' ) ),
242 + 'date' => get_option( 'date_format', __( 'F j, Y', 'default' ) ),
243 + 'datetime' => __( 'F j, Y g:i a', 'default' ),
244 + 'datetimeAbbreviated' => __( 'M j, Y g:i a', 'default' ),
245 + ),
246 + 'timezone' => array(
247 + 'offset' => get_option( 'gmt_offset', 0 ),
248 + 'string' => get_option( 'timezone_string', 'UTC' ),
249 + ),
250 + )
251 + )
252 + ),
253 + 'after'
254 + );
255 + wp_add_inline_script(
256 + 'moment',
257 + sprintf(
258 + "moment.locale( '%s', %s );",
259 + get_user_locale(),
260 + wp_json_encode(
261 + array(
262 + 'months' => array_values( $wp_locale->month ),
263 + 'monthsShort' => array_values( $wp_locale->month_abbrev ),
264 + 'weekdays' => array_values( $wp_locale->weekday ),
265 + 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
266 + 'week' => array(
267 + 'dow' => (int) get_option( 'start_of_week', 0 ),
268 + ),
269 + 'longDateFormat' => array(
270 + 'LT' => get_option( 'time_format', __( 'g:i a', 'default' ) ),
271 + 'LTS' => null,
272 + 'L' => null,
273 + 'LL' => get_option( 'date_format', __( 'F j, Y', 'default' ) ),
274 + 'LLL' => __( 'F j, Y g:i a', 'default' ),
275 + 'LLLL' => null,
276 + ),
277 + )
278 + )
279 + ),
280 + 'after'
281 + );
282 + // Loading the old editor and its config to ensure the classic block works as expected.
283 + wp_add_inline_script(
284 + 'editor',
285 + 'window.wp.oldEditor = window.wp.editor;',
286 + 'after'
287 + );
160 288
161 - // Register wp-base-styles and add it to the already registered wp-admin stylesheet
162 - gutenberg_override_style(
163 - $styles,
164 - 'wp-base-styles',
165 - gutenberg_url( 'build/styles/base-styles/admin-schemes' . $suffix . '.css' ),
166 - array( 'wp-theme' ),
167 - $version
289 + $tinymce_plugins = array(
290 + 'charmap',
291 + 'colorpicker',
292 + 'hr',
293 + 'lists',
294 + 'media',
295 + 'paste',
296 + 'tabfocus',
297 + 'textcolor',
298 + 'fullscreen',
299 + 'wordpress',
300 + 'wpautoresize',
301 + 'wpeditimage',
302 + 'wpemoji',
303 + 'wpgallery',
304 + 'wplink',
305 + 'wpdialogs',
306 + 'wptextpattern',
307 + 'wpview',
168 308 );
169 - $styles->add_data( 'wp-base-styles', 'rtl', 'replace' );
170 - $styles->add_data( 'wp-base-styles', 'suffix', $suffix );
171 - $styles->add_data( 'wp-base-styles', 'path', gutenberg_dir_path() . 'build/styles/base-styles/admin-schemes' . $suffix . '.css' );
172 - $styles->query( 'wp-admin', 'registered' )->deps[] = 'wp-base-styles';
309 + $tinymce_plugins = apply_filters( 'tiny_mce_plugins', $tinymce_plugins, 'classic-block' );
310 + $tinymce_plugins = array_unique( $tinymce_plugins );
173 311
174 - gutenberg_override_style(
175 - $styles,
176 - 'wp-block-editor-content',
177 - gutenberg_url( 'build/styles/block-editor/content' . $suffix . '.css' ),
178 - array( 'wp-components' ),
179 - $version
312 + $toolbar1 = array(
313 + 'formatselect',
314 + 'bold',
315 + 'italic',
316 + 'bullist',
317 + 'numlist',
318 + 'blockquote',
319 + 'alignleft',
320 + 'aligncenter',
321 + 'alignright',
322 + 'link',
323 + 'unlink',
324 + 'wp_more',
325 + 'spellchecker',
326 + 'wp_add_media',
327 + 'kitchensink',
180 328 );
181 - $styles->add_data( 'wp-block-editor-content', 'rtl', 'replace' );
182 - $styles->add_data( 'wp-block-editor-content', 'suffix', $suffix );
329 + $toolbar1 = apply_filters( 'mce_buttons', $toolbar1, 'classic-block' );
183 330
184 - $block_library_filename = wp_should_load_separate_core_block_assets() ? 'common' : 'style';
185 - gutenberg_override_style(
186 - $styles,
187 - 'wp-block-library',
188 - gutenberg_url( 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' ),
189 - array(),
190 - $version
331 + $toolbar2 = array(
332 + 'strikethrough',
333 + 'hr',
334 + 'forecolor',
335 + 'pastetext',
336 + 'removeformat',
337 + 'charmap',
338 + 'outdent',
339 + 'indent',
340 + 'undo',
341 + 'redo',
342 + 'wp_help',
191 343 );
192 - $styles->add_data( 'wp-block-library', 'rtl', 'replace' );
193 - $styles->add_data( 'wp-block-library', 'suffix', $suffix );
194 - $styles->add_data( 'wp-block-library', 'path', gutenberg_dir_path() . 'build/styles/block-library/' . $block_library_filename . $suffix . '.css' );
344 + $toolbar2 = apply_filters( 'mce_buttons_2', $toolbar2, 'classic-block' );
195 345
196 - // Only add CONTENT styles here that should be enqueued in the iframe!
197 - $wp_edit_blocks_dependencies = array(
198 - // Design System tokens load first so the `:root` CSS custom
199 - // properties are defined before any consuming stylesheet reads
200 - // them inside the editor iframe.
201 - 'wp-theme',
202 - 'wp-components',
203 - // This need to be added before the block library styles,
204 - // The block library styles override the "reset" styles.
205 - 'wp-reset-editor-styles',
206 - 'wp-block-library',
207 - // Until #37466, we can't specifically add them as editor styles yet,
208 - // so we must hard-code it here as a dependency.
209 - 'wp-block-editor-content',
210 - 'wp-base-styles',
346 + $toolbar3 = apply_filters( 'mce_buttons_3', array(), 'classic-block' );
347 + $toolbar4 = apply_filters( 'mce_buttons_4', array(), 'classic-block' );
348 +
349 + $external_plugins = apply_filters( 'mce_external_plugins', array(), 'classic-block' );
350 +
351 + $tinymce_settings = array(
352 + 'plugins' => implode( ',', $tinymce_plugins ),
353 + 'toolbar1' => implode( ',', $toolbar1 ),
354 + 'toolbar2' => implode( ',', $toolbar2 ),
355 + 'toolbar3' => implode( ',', $toolbar3 ),
356 + 'toolbar4' => implode( ',', $toolbar4 ),
357 + 'external_plugins' => wp_json_encode( $external_plugins ),
358 + 'classic_block_editor' => true,
211 359 );
360 + $tinymce_settings = apply_filters( 'tiny_mce_before_init', $tinymce_settings, 'classic-block' );
212 361
213 - // Only load the default layout and margin styles for themes without theme.json file.
214 - if ( ! wp_theme_has_theme_json() ) {
215 - $wp_edit_blocks_dependencies[] = 'wp-editor-classic-layout-styles';
362 + // Do "by hand" translation from PHP array to js object.
363 + // Prevents breakage in some custom settings.
364 + $init_obj = '';
365 + foreach ( $tinymce_settings as $key => $value ) {
366 + if ( is_bool( $value ) ) {
367 + $val = $value ? 'true' : 'false';
368 + $init_obj .= $key . ':' . $val . ',';
369 + continue;
370 + } elseif ( ! empty( $value ) && is_string( $value ) && (
371 + ( '{' == $value{0} && '}' == $value{strlen( $value ) - 1} ) ||
372 + ( '[' == $value{0} && ']' == $value{strlen( $value ) - 1} ) ||
373 + preg_match( '/^\(?function ?\(/', $value ) ) ) {
374 +
375 + $init_obj .= $key . ':' . $value . ',';
376 + continue;
377 + }
378 + $init_obj .= $key . ':"' . $value . '",';
216 379 }
217 380
218 - global $editor_styles;
219 - if ( current_theme_supports( 'wp-block-styles' ) && ( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 ) ) {
220 - // Include opinionated block styles if the theme supports block styles and no $editor_styles are declared, so the editor never appears broken.
221 - $wp_edit_blocks_dependencies[] = 'wp-block-library-theme';
381 + $init_obj = '{' . trim( $init_obj, ' ,' ) . '}';
382 +
383 + $script = 'window.wpEditorL10n = {
384 + tinymce: {
385 + baseURL: ' . wp_json_encode( includes_url( 'js/tinymce' ) ) . ',
386 + suffix: ' . ( SCRIPT_DEBUG ? '""' : '".min"' ) . ',
387 + settings: ' . $init_obj . ',
388 + }
389 + }';
390 +
391 + wp_add_inline_script( 'wp-block-library', $script, 'before' );
392 +
393 + // Editor Styles.
394 + // This empty stylesheet is defined to ensure backwards compatibility.
395 + gutenberg_override_style( 'wp-blocks', false );
396 + $fonts_url = '';
397 +
398 + /*
399 + * Translators: Use this to specify the proper Google Font name and variants
400 + * to load that is supported by your language. Do not translate.
401 + * Set to 'off' to disable loading.
402 + */
403 + $font_family = _x( 'Noto Serif:400,400i,700,700i', 'Google Font Name and Variants', 'gutenberg' );
404 + if ( 'off' !== $font_family ) {
405 + $query_args = array(
406 + 'family' => urlencode( $font_family ),
407 + );
408 + $fonts_url = esc_url_raw( add_query_arg( $query_args, 'https://fonts.googleapis.com/css' ) );
222 409 }
223 410
224 411 gutenberg_override_style(
225 - $styles,
226 - 'wp-reset-editor-styles',
227 - gutenberg_url( 'build/styles/block-library/reset' . $suffix . '.css' ),
228 - array( 'common', 'forms' ), // Make sure the reset is loaded after the default WP Admin styles.
229 - $version
412 + 'wp-editor-font',
413 + $fonts_url,
414 + array(),
415 + null
230 416 );
231 - $styles->add_data( 'wp-reset-editor-styles', 'rtl', 'replace' );
232 - $styles->add_data( 'wp-reset-editor-styles', 'suffix', $suffix );
233 417
234 418 gutenberg_override_style(
235 - $styles,
236 - 'wp-editor-classic-layout-styles',
237 - gutenberg_url( 'build/styles/edit-post/classic' . $suffix . '.css' ),
419 + 'wp-editor',
420 + gutenberg_url( 'build/editor/style.css' ),
421 + array( 'wp-components', 'wp-editor-font', 'wp-nux' ),
422 + filemtime( gutenberg_dir_path() . 'build/editor/style.css' )
423 + );
424 + wp_style_add_data( 'wp-editor', 'rtl', 'replace' );
425 +
426 + gutenberg_override_style(
427 + 'wp-edit-post',
428 + gutenberg_url( 'build/edit-post/style.css' ),
429 + array( 'wp-components', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ),
430 + filemtime( gutenberg_dir_path() . 'build/edit-post/style.css' )
431 + );
432 + wp_style_add_data( 'wp-edit-post', 'rtl', 'replace' );
433 +
434 + gutenberg_override_style(
435 + 'wp-components',
436 + gutenberg_url( 'build/components/style.css' ),
238 437 array(),
239 - $version
438 + filemtime( gutenberg_dir_path() . 'build/components/style.css' )
240 439 );
241 - $styles->add_data( 'wp-editor-classic-layout-styles', 'rtl', 'replace' );
242 - $styles->add_data( 'wp-editor-classic-layout-styles', 'suffix', $suffix );
440 + wp_style_add_data( 'wp-components', 'rtl', 'replace' );
243 441
244 442 gutenberg_override_style(
245 - $styles,
246 - 'wp-block-library-editor',
247 - gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
443 + 'wp-block-library',
444 + gutenberg_url( 'build/block-library/style.css' ),
445 + current_theme_supports( 'wp-block-styles' ) ? array( 'wp-block-library-theme' ) : array(),
446 + filemtime( gutenberg_dir_path() . 'build/block-library/style.css' )
447 + );
448 + wp_style_add_data( 'wp-block-library', 'rtl', 'replace' );
449 +
450 + gutenberg_override_style(
451 + 'wp-format-library',
452 + gutenberg_url( 'build/format-library/style.css' ),
248 453 array(),
249 - $version
454 + filemtime( gutenberg_dir_path() . 'build/format-library/style.css' )
250 455 );
251 - $styles->add_data( 'wp-block-library-editor', 'rtl', 'replace' );
252 - $styles->add_data( 'wp-block-library-editor', 'suffix', $suffix );
456 + wp_style_add_data( 'wp-format-library', 'rtl', 'replace' );
253 457
254 458 gutenberg_override_style(
255 - $styles,
256 459 'wp-edit-blocks',
257 - gutenberg_url( 'build/styles/block-library/editor' . $suffix . '.css' ),
258 - $wp_edit_blocks_dependencies,
259 - $version
460 + gutenberg_url( 'build/block-library/editor.css' ),
461 + array(
462 + 'wp-components',
463 + 'wp-editor',
464 + 'wp-block-library',
465 + // Always include visual styles so the editor never appears broken.
466 + 'wp-block-library-theme',
467 + ),
468 + filemtime( gutenberg_dir_path() . 'build/block-library/editor.css' )
260 469 );
261 - $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
262 - $styles->add_data( 'wp-edit-blocks', 'suffix', $suffix );
470 + wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' );
263 471
264 472 gutenberg_override_style(
265 - $styles,
473 + 'wp-nux',
474 + gutenberg_url( 'build/nux/style.css' ),
475 + array( 'wp-components' ),
476 + filemtime( gutenberg_dir_path() . 'build/nux/style.css' )
477 + );
478 + wp_style_add_data( 'wp-nux', 'rtl', 'replace' );
479 +
480 + gutenberg_override_style(
266 481 'wp-block-library-theme',
267 - gutenberg_url( 'build/styles/block-library/theme' . $suffix . '.css' ),
482 + gutenberg_url( 'build/block-library/theme.css' ),
268 483 array(),
269 - $version
484 + filemtime( gutenberg_dir_path() . 'build/block-library/theme.css' )
270 485 );
271 - $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
272 - $styles->add_data( 'wp-block-library-theme', 'suffix', $suffix );
486 + wp_style_add_data( 'wp-block-library-theme', 'rtl', 'replace' );
273 487
274 488 gutenberg_override_style(
275 - $styles,
276 - 'classic-theme-styles',
277 - gutenberg_url( 'build/styles/block-library/classic' . $suffix . '.css' ),
278 - array(),
279 - $version
489 + 'wp-list-reusable-blocks',
490 + gutenberg_url( 'build/list-reusable-blocks/style.css' ),
491 + array( 'wp-components' ),
492 + filemtime( gutenberg_dir_path() . 'build/list-reusable-blocks/style.css' )
280 493 );
281 - $styles->add_data( 'classic-theme-styles', 'rtl', 'replace' );
282 - $styles->add_data( 'classic-theme-styles', 'suffix', $suffix );
283 - $styles->add_data( 'classic-theme-styles', 'path', gutenberg_dir_path() . 'build/styles/block-library/classic' . $suffix . '.css' );
494 + wp_style_add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
495 +
496 + if ( defined( 'GUTENBERG_LIVE_RELOAD' ) && GUTENBERG_LIVE_RELOAD ) {
497 + $live_reload_url = ( GUTENBERG_LIVE_RELOAD === true ) ? 'http://localhost:35729/livereload.js' : GUTENBERG_LIVE_RELOAD;
498 +
499 + wp_enqueue_script(
500 + 'gutenberg-live-reload',
501 + $live_reload_url
502 + );
503 + }
504 +
505 + // Temporary backward compatibility for `wp-polyfill-ecmascript`, which has
506 + // since been absorbed into `wp-polyfill`.
507 + //
508 + // [TODO][REMOVEME] To be removed in Gutenberg v4.5.
509 + gutenberg_override_script(
510 + 'wp-polyfill-ecmascript',
511 + null,
512 + array(
513 + 'wp-polyfill',
514 + 'wp-deprecated',
515 + )
516 + );
517 + wp_script_add_data(
518 + 'wp-polyfill-ecmascript',
519 + 'data',
520 + 'wp.deprecated( "wp-polyfill-ecmascript script handle", { plugin: "Gutenberg", version: "4.5" } );'
521 + );
284 522 }
285 -add_action( 'wp_default_styles', 'gutenberg_register_packages_styles', 15 );
523 +add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
524 +add_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
286 525
287 526 /**
288 - * Fetches, processes and compiles stored core styles, then combines and renders them to the page.
289 - * Styles are stored via the Style Engine API.
527 + * Append result of internal request to REST API for purpose of preloading
528 + * data to be attached to the page. Expected to be called in the context of
529 + * `array_reduce`.
290 530 *
291 - * This hook also exists, and should be backported to Core in future versions.
292 - * However, it is envisaged that Gutenberg will continue to use the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes to aid continuous development.
293 - *
294 - * @since 6.1
295 - *
296 - * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-style-engine/
297 - *
298 - * @param array $options {
299 - * Optional. An array of options to pass to gutenberg_style_engine_get_stylesheet_from_context(). Default empty array.
300 - *
301 - * @type bool $optimize Whether to optimize the CSS output, e.g., combine rules. Default is `false`.
302 - * @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.
303 - * }
304 - *
305 - * @return void
531 + * @param array $memo Reduce accumulator.
532 + * @param string $path REST API path to preload.
533 + * @return array Modified reduce accumulator.
306 534 */
307 -function gutenberg_enqueue_stored_styles( $options = array() ) {
308 - $is_block_theme = wp_is_block_theme();
309 - $is_classic_theme = ! $is_block_theme;
535 +function gutenberg_preload_api_request( $memo, $path ) {
310 536
311 - /*
312 - * For block themes, print stored styles in the header.
313 - * For classic themes, in the footer.
314 - */
315 - if (
316 - ( $is_block_theme && doing_action( 'wp_footer' ) ) ||
317 - ( $is_classic_theme && doing_action( 'wp_enqueue_scripts' ) )
318 - ) {
319 - return;
537 + // array_reduce() doesn't support passing an array in PHP 5.2
538 + // so we need to make sure we start with one.
539 + if ( ! is_array( $memo ) ) {
540 + $memo = array();
320 541 }
321 542
322 - $core_styles_keys = array( 'block-supports' );
323 - $compiled_core_stylesheet = '';
324 - $style_tag_id = 'core';
325 - foreach ( $core_styles_keys as $style_key ) {
326 - // Adds comment if code is prettified to identify core styles sections in debugging.
327 - $should_prettify = isset( $options['prettify'] ) ? true === $options['prettify'] : SCRIPT_DEBUG;
328 - if ( $should_prettify ) {
329 - $compiled_core_stylesheet .= "/**\n * Core styles: $style_key\n */\n";
543 + if ( empty( $path ) ) {
544 + return $memo;
545 + }
546 +
547 + $method = 'GET';
548 + if ( is_array( $path ) && 2 === count( $path ) ) {
549 + $method = end( $path );
550 + $path = reset( $path );
551 +
552 + if ( ! in_array( $method, array( 'GET', 'OPTIONS' ), true ) ) {
553 + $method = 'GET';
330 554 }
331 - // Chains core store ids to signify what the styles contain.
332 - $style_tag_id .= '-' . $style_key;
333 - $compiled_core_stylesheet .= gutenberg_style_engine_get_stylesheet_from_context( $style_key, $options );
334 555 }
335 556
336 - // Combines Core styles.
337 - if ( ! empty( $compiled_core_stylesheet ) ) {
338 - wp_register_style( $style_tag_id, false, array(), true );
339 - wp_add_inline_style( $style_tag_id, $compiled_core_stylesheet );
340 - wp_enqueue_style( $style_tag_id );
557 + $path_parts = parse_url( $path );
558 + if ( false === $path_parts ) {
559 + return $memo;
341 560 }
342 561
343 - // If there are any other stores registered by themes etc., print them out.
344 - $additional_stores = WP_Style_Engine_CSS_Rules_Store_Gutenberg::get_stores();
345 -
346 - /*
347 - * Since the corresponding action hook in Core is removed below,
348 - * this function should still honour any styles stored using the Core Style Engine store.
349 - */
350 - if ( class_exists( 'WP_Style_Engine_CSS_Rules_Store' ) ) {
351 - $additional_stores = array_merge( $additional_stores, WP_Style_Engine_CSS_Rules_Store::get_stores() );
562 + $request = new WP_REST_Request( $method, $path_parts['path'] );
563 + if ( ! empty( $path_parts['query'] ) ) {
564 + parse_str( $path_parts['query'], $query_params );
565 + $request->set_query_params( $query_params );
352 566 }
353 567
354 - foreach ( array_keys( $additional_stores ) as $store_name ) {
355 - if ( in_array( $store_name, $core_styles_keys, true ) ) {
356 - continue;
568 + $response = rest_do_request( $request );
569 + if ( 200 === $response->status ) {
570 + $server = rest_get_server();
571 + $data = (array) $response->get_data();
572 + $links = $server->get_compact_response_links( $response );
573 + if ( ! empty( $links ) ) {
574 + $data['_links'] = $links;
357 575 }
358 - $styles = gutenberg_style_engine_get_stylesheet_from_context( $store_name, $options );
359 - if ( ! empty( $styles ) ) {
360 - $key = "wp-style-engine-$store_name";
361 - wp_register_style( $key, false, array(), true );
362 - wp_add_inline_style( $key, $styles );
363 - wp_enqueue_style( $key );
576 +
577 + if ( 'OPTIONS' === $method ) {
578 + $response = rest_send_allow_header( $response, $server, $request );
579 +
580 + $memo[ $method ][ $path ] = array(
581 + 'body' => $data,
582 + 'headers' => $response->headers,
583 + );
584 + } else {
585 + $memo[ $path ] = array(
586 + 'body' => $data,
587 + 'headers' => $response->headers,
588 + );
364 589 }
365 590 }
591 +
592 + return $memo;
366 593 }
367 594
368 595 /**
369 596 * Registers vendor JavaScript files to be used as dependencies of the editor
@@ -371,106 +598,808 @@
371 598 *
372 599 * This function is called from a script during the plugin build process, so it
373 600 * should not call any WordPress PHP functions.
374 601 *
375 - * @since 13.0
602 + * @since 0.1.0
603 + */
604 +function gutenberg_register_vendor_scripts() {
605 + $suffix = SCRIPT_DEBUG ? '' : '.min';
606 +
607 + // Vendor Scripts.
608 + $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix;
609 +
610 + gutenberg_register_vendor_script(
611 + 'react',
612 + 'https://unpkg.com/react@16.6.3/umd/react' . $react_suffix . '.js',
613 + array( 'wp-polyfill' )
614 + );
615 + gutenberg_register_vendor_script(
616 + 'react-dom',
617 + 'https://unpkg.com/react-dom@16.6.3/umd/react-dom' . $react_suffix . '.js',
618 + array( 'react' )
619 + );
620 + $moment_script = SCRIPT_DEBUG ? 'moment.js' : 'min/moment.min.js';
621 + gutenberg_register_vendor_script(
622 + 'moment',
623 + 'https://unpkg.com/moment@2.22.1/' . $moment_script,
624 + array()
625 + );
626 + gutenberg_register_vendor_script(
627 + 'lodash',
628 + 'https://unpkg.com/lodash@4.17.5/lodash' . $suffix . '.js'
629 + );
630 + wp_add_inline_script( 'lodash', 'window.lodash = _.noConflict();' );
631 + gutenberg_register_vendor_script(
632 + 'wp-polyfill-fetch',
633 + 'https://unpkg.com/whatwg-fetch@3.0.0/dist/fetch.umd.js'
634 + );
635 + gutenberg_register_vendor_script(
636 + 'wp-polyfill-formdata',
637 + 'https://unpkg.com/formdata-polyfill@3.0.9/formdata.min.js'
638 + );
639 + gutenberg_register_vendor_script(
640 + 'wp-polyfill-node-contains',
641 + 'https://unpkg.com/polyfill-library@3.26.0-0/polyfills/Node/prototype/contains/polyfill.js'
642 + );
643 + gutenberg_register_vendor_script(
644 + 'wp-polyfill-element-closest',
645 + 'https://unpkg.com/element-closest@2.0.2/element-closest.js'
646 + );
647 + gutenberg_register_vendor_script(
648 + 'wp-polyfill',
649 + 'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.0.0/polyfill' . $suffix . '.js'
650 + );
651 +}
652 +
653 +/**
654 + * Retrieves a unique and reasonably short and human-friendly filename for a
655 + * vendor script based on a URL and the script handle.
376 656 *
377 - * @param WP_Scripts $scripts WP_Scripts instance.
657 + * @param string $handle The name of the script.
658 + * @param string $src Full URL of the external script.
659 + *
660 + * @return string Script filename suitable for local caching.
661 + *
662 + * @since 0.1.0
378 663 */
379 -function gutenberg_register_vendor_scripts( $scripts ) {
380 - $extension = SCRIPT_DEBUG ? '.js' : '.min.js';
381 - $vendors_dir = gutenberg_dir_path() . 'build/scripts/vendors/';
664 +function gutenberg_vendor_script_filename( $handle, $src ) {
665 + $match_tinymce_plugin = preg_match(
666 + '@tinymce.*/plugins/([^/]+)/plugin(\.min)?\.js$@',
667 + $src,
668 + $tinymce_plugin_pieces
669 + );
670 + if ( $match_tinymce_plugin ) {
671 + $prefix = 'tinymce-plugin-' . $tinymce_plugin_pieces[1];
672 + $suffix = isset( $tinymce_plugin_pieces[2] ) ? $tinymce_plugin_pieces[2] : '';
673 + } else {
674 + $filename = basename( $src );
675 + $match = preg_match(
676 + '/^'
677 + . '(?P<ignore>.*?)'
678 + . '(?P<suffix>\.min)?'
679 + . '(?P<extension>\.js)'
680 + . '(?P<extra>.*)'
681 + . '$/',
682 + $filename,
683 + $filename_pieces
684 + );
382 685
383 - // When the React 19 experiment is enabled, register React 19 vendor
384 - // scripts under the `react`, `react-dom`, and `react-jsx-runtime` handles.
385 - $use_react_19 = gutenberg_is_experiment_enabled( 'gutenberg-react-19' );
686 + $prefix = $handle;
687 + $suffix = $match ? $filename_pieces['suffix'] : '';
688 + }
386 689
387 - $vendor_handles = array( 'react', 'react-dom', 'react-jsx-runtime' );
690 + $hash = substr( md5( $src ), 0, 8 );
388 691
389 - foreach ( $vendor_handles as $handle ) {
390 - $source = $use_react_19 ? $handle . '-19' : $handle;
391 - $asset_file = $vendors_dir . $source . '.min.asset.php';
392 - $asset = file_exists( $asset_file ) ? require $asset_file : array();
393 - $dependencies = $asset['dependencies'] ?? array();
394 - $version = $asset['version'] ?? '0';
692 + return "${prefix}${suffix}.${hash}.js";
693 +}
395 694
396 - gutenberg_override_script(
397 - $scripts,
398 - $handle,
399 - gutenberg_url( 'build/scripts/vendors/' . $source . $extension ),
400 - $dependencies,
401 - $version
402 - );
695 +/**
696 + * Registers a vendor script from a URL, preferring a locally cached version if
697 + * possible, or downloading it if the cached version is unavailable or
698 + * outdated.
699 + *
700 + * @param string $handle Name of the script.
701 + * @param string $src Full URL of the external script.
702 + * @param array $deps Optional. An array of registered script handles this
703 + * script depends on.
704 + *
705 + * @since 0.1.0
706 + */
707 +function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) {
708 + if ( defined( 'GUTENBERG_LOAD_VENDOR_SCRIPTS' ) && ! GUTENBERG_LOAD_VENDOR_SCRIPTS ) {
709 + return;
403 710 }
404 711
405 - // WordPress Core in `wp_register_development_scripts` sets `wp-react-refresh-entry`
406 - // as a dependency to `react` when `SCRIPT_DEBUG` is true. Preserve that here.
407 - if ( SCRIPT_DEBUG ) {
408 - $react = $scripts->query( 'react', 'registered' );
409 - if ( $react && ! in_array( 'wp-react-refresh-entry', $react->deps, true ) ) {
410 - $react->deps[] = 'wp-react-refresh-entry';
712 + $filename = gutenberg_vendor_script_filename( $handle, $src );
713 +
714 + if ( defined( 'GUTENBERG_LIST_VENDOR_ASSETS' ) && GUTENBERG_LIST_VENDOR_ASSETS ) {
715 + echo "$src|$filename\n";
716 + return;
717 + }
718 +
719 + $full_path = gutenberg_dir_path() . 'vendor/' . $filename;
720 +
721 + $needs_fetch = (
722 + defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && (
723 + ! file_exists( $full_path ) ||
724 + time() - filemtime( $full_path ) >= DAY_IN_SECONDS
725 + )
726 + );
727 +
728 + if ( $needs_fetch ) {
729 + // Determine whether we can write to this file. If not, don't waste
730 + // time doing a network request.
731 + // @codingStandardsIgnoreStart
732 + $f = @fopen( $full_path, 'a' );
733 + // @codingStandardsIgnoreEnd
734 + if ( ! $f ) {
735 + // Failed to open the file for writing, probably due to server
736 + // permissions. Enqueue the script directly from the URL instead.
737 + gutenberg_override_script( $handle, $src, $deps, null );
738 + return;
411 739 }
740 + fclose( $f );
741 + $response = wp_remote_get( $src );
742 + if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
743 + $f = fopen( $full_path, 'w' );
744 + fwrite( $f, wp_remote_retrieve_body( $response ) );
745 + fclose( $f );
746 + } elseif ( ! filesize( $full_path ) ) {
747 + // The request failed. If the file is already cached, continue to
748 + // use this file. If not, then unlink the 0 byte file, and enqueue
749 + // the script directly from the URL.
750 + gutenberg_override_script( $handle, $src, $deps, null );
751 + unlink( $full_path );
752 + return;
753 + }
412 754 }
755 + gutenberg_override_script(
756 + $handle,
757 + gutenberg_url( 'vendor/' . $filename ),
758 + $deps,
759 + null
760 + );
413 761 }
414 -add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' );
415 762
416 763 /**
417 - * Registers or re-registers Gutenberg Script Modules.
764 + * Prepares server-registered blocks for JavaScript, returning an associative
765 + * array of registered block data keyed by block name. Data includes properties
766 + * of a block relevant for client registration.
418 767 *
419 - * Script modules that are registered by Core will be re-registered by Gutenberg.
768 + * @return array An associative array of registered block data.
769 + */
770 +function gutenberg_prepare_blocks_for_js() {
771 + $block_registry = WP_Block_Type_Registry::get_instance();
772 + $blocks = array();
773 + $keys_to_pick = array( 'title', 'description', 'icon', 'category', 'keywords', 'supports', 'attributes' );
774 +
775 + foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
776 + foreach ( $keys_to_pick as $key ) {
777 + if ( ! isset( $block_type->{ $key } ) ) {
778 + continue;
779 + }
780 +
781 + if ( ! isset( $blocks[ $block_name ] ) ) {
782 + $blocks[ $block_name ] = array();
783 + }
784 +
785 + $blocks[ $block_name ][ $key ] = $block_type->{ $key };
786 + }
787 + }
788 +
789 + return $blocks;
790 +}
791 +
792 +/**
793 + * Handles the enqueueing of block scripts and styles that are common to both
794 + * the editor and the front-end.
420 795 *
421 - * @since 19.3.0
796 + * Note: This function must remain *before*
797 + * `gutenberg_editor_scripts_and_styles` so that editor-specific stylesheets
798 + * are loaded last.
799 + *
800 + * @since 0.4.0
422 801 */
423 -function gutenberg_define_interactivity_modules_support() {
424 - // Load the auto-generated module registry.
425 - $modules_registry_file = gutenberg_dir_path() . 'build/modules/index.php';
426 - if ( ! file_exists( $modules_registry_file ) ) {
802 +function gutenberg_common_scripts_and_styles() {
803 + if ( ! is_gutenberg_page() && is_admin() ) {
427 804 return;
428 805 }
429 806
430 - $modules = require $modules_registry_file;
807 + // Enqueue basic styles built out of Gutenberg through `npm build`.
808 + wp_enqueue_style( 'wp-block-library' );
431 809
432 - // Add client navigation support to block library modules.
433 - foreach ( $modules as $module ) {
434 - if ( str_starts_with( $module['id'], '@wordpress/block-library' ) && method_exists( 'WP_Interactivity_API', 'add_client_navigation_support_to_script_module' ) ) {
435 - wp_interactivity()->add_client_navigation_support_to_script_module( $module['id'] );
810 + /*
811 + * Enqueue block styles built through plugins. This lives in a separate
812 + * action for a couple of reasons: (1) we want to load these assets
813 + * (usually stylesheets) in *both* frontend and editor contexts, and (2)
814 + * one day we may need to be smarter about whether assets are included
815 + * based on whether blocks are actually present on the page.
816 + */
817 +
818 + /**
819 + * Fires after enqueuing block assets for both editor and front-end.
820 + *
821 + * Call `add_action` on any hook before 'wp_enqueue_scripts'.
822 + *
823 + * In the function call you supply, simply use `wp_enqueue_script` and
824 + * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
825 + *
826 + * @since 0.4.0
827 + */
828 + do_action( 'enqueue_block_assets' );
829 +}
830 +add_action( 'wp_enqueue_scripts', 'gutenberg_common_scripts_and_styles' );
831 +add_action( 'admin_enqueue_scripts', 'gutenberg_common_scripts_and_styles' );
832 +
833 +/**
834 + * Enqueues registered block scripts and styles, depending on current rendered
835 + * context (only enqueuing editor scripts while in context of the editor).
836 + *
837 + * @since 2.0.0
838 + */
839 +function gutenberg_enqueue_registered_block_scripts_and_styles() {
840 + $is_editor = ( 'enqueue_block_editor_assets' === current_action() );
841 +
842 + $block_registry = WP_Block_Type_Registry::get_instance();
843 + foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
844 + // Front-end styles.
845 + if ( ! empty( $block_type->style ) ) {
846 + wp_enqueue_style( $block_type->style );
436 847 }
848 +
849 + // Front-end script.
850 + if ( ! empty( $block_type->script ) ) {
851 + wp_enqueue_script( $block_type->script );
852 + }
853 +
854 + // Editor styles.
855 + if ( $is_editor && ! empty( $block_type->editor_style ) ) {
856 + wp_enqueue_style( $block_type->editor_style );
857 + }
858 +
859 + // Editor script.
860 + if ( $is_editor && ! empty( $block_type->editor_script ) ) {
861 + wp_enqueue_script( $block_type->editor_script );
862 + }
437 863 }
438 864 }
439 -remove_action( 'wp_default_scripts', 'wp_define_interactivity_modules_support' );
440 -add_action( 'wp_default_scripts', 'gutenberg_define_interactivity_modules_support' );
865 +add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' );
866 +add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' );
441 867
442 868 /**
443 - * Always remove the Core action hook while gutenberg_enqueue_stored_styles() exists to avoid styles being printed twice.
444 - * This is also because gutenberg_enqueue_stored_styles uses the Style Engine's `gutenberg_*` functions and `_Gutenberg` classes,
445 - * which are in continuous development and generally ahead of Core.
869 + * Assigns a default editor template with a default block by post format, if
870 + * not otherwise assigned for a new post of type "post".
871 + *
872 + * @param array $settings Default editor settings.
873 + * @param WP_Post $post Post being edited.
874 + *
875 + * @return array Filtered block editor settings.
446 876 */
447 -remove_action( 'wp_enqueue_scripts', 'wp_enqueue_stored_styles' );
448 -remove_action( 'wp_footer', 'wp_enqueue_stored_styles', 1 );
877 +function gutenberg_default_post_format_template( $settings, $post ) {
878 + // Only assign template for new posts without explicitly assigned template.
879 + $is_new_post = 'auto-draft' === $post->post_status;
880 + if ( $is_new_post && ! isset( $settings['template'] ) && 'post' === $post->post_type ) {
881 + switch ( get_post_format() ) {
882 + case 'audio':
883 + $default_block_name = 'core/audio';
884 + break;
885 + case 'gallery':
886 + $default_block_name = 'core/gallery';
887 + break;
888 + case 'image':
889 + $default_block_name = 'core/image';
890 + break;
891 + case 'quote':
892 + $default_block_name = 'core/quote';
893 + break;
894 + case 'video':
895 + $default_block_name = 'core/video';
896 + break;
897 + }
449 898
450 -// Enqueue stored styles.
451 -add_action( 'wp_enqueue_scripts', 'gutenberg_enqueue_stored_styles' );
452 -add_action( 'wp_footer', 'gutenberg_enqueue_stored_styles', 1 );
899 + if ( isset( $default_block_name ) ) {
900 + $settings['template'] = array( array( $default_block_name ) );
901 + }
902 + }
453 903
454 -add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_latex_to_mathml_loader' );
455 -function gutenberg_enqueue_latex_to_mathml_loader() {
456 - wp_enqueue_script_module( '@wordpress/latex-to-mathml/loader' );
904 + return $settings;
457 905 }
906 +add_filter( 'block_editor_settings', 'gutenberg_default_post_format_template', 10, 2 );
458 907
459 908 /**
460 - * Enqueue the vips loader script module in the block editor.
909 + * Retrieve a stored autosave that is newer than the post save.
461 910 *
462 - * This registers @wordpress/vips/worker as a dynamic dependency in the import map,
463 - * enabling on-demand loading of the ~3.8MB WASM-based image processing module
464 - * when client-side media processing is triggered via @wordpress/upload-media.
911 + * Deletes autosaves that are older than the post save.
465 912 *
466 - * @see packages/vips/src/loader.ts
913 + * @param WP_Post $post Post object.
914 + * @return WP_Post|boolean The post autosave. False if none found.
467 915 */
468 -add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_vips_loader' );
469 -function gutenberg_enqueue_vips_loader() {
470 - wp_enqueue_script_module( '@wordpress/vips/loader' );
916 +function gutenberg_get_autosave_newer_than_post_save( $post ) {
917 + // Add autosave data if it is newer and changed.
918 + $autosave = wp_get_post_autosave( $post->ID );
919 +
920 + if ( ! $autosave ) {
921 + return false;
922 + }
923 +
924 + // Check if the autosave is newer than the current post.
925 + if (
926 + mysql2date( 'U', $autosave->post_modified_gmt, false ) > mysql2date( 'U', $post->post_modified_gmt, false )
927 + ) {
928 + return $autosave;
929 + }
930 +
931 + // If the autosave isn't newer, remove it.
932 + wp_delete_post_revision( $autosave->ID );
933 +
934 + return false;
471 935 }
472 936
473 -add_action( 'admin_enqueue_scripts', 'gutenberg_enqueue_core_abilities' );
474 -function gutenberg_enqueue_core_abilities() {
475 - wp_enqueue_script_module( '@wordpress/core-abilities' );
937 +/**
938 + * Returns all the block categories.
939 + *
940 + * @since 2.2.0
941 + *
942 + * @param WP_Post $post Post object.
943 + * @return Object[] Block categories.
944 + */
945 +function gutenberg_get_block_categories( $post ) {
946 + $default_categories = array(
947 + array(
948 + 'slug' => 'common',
949 + 'title' => __( 'Common Blocks', 'gutenberg' ),
950 + 'icon' => null,
951 + ),
952 + array(
953 + 'slug' => 'formatting',
954 + 'title' => __( 'Formatting', 'gutenberg' ),
955 + 'icon' => null,
956 + ),
957 + array(
958 + 'slug' => 'layout',
959 + 'title' => __( 'Layout Elements', 'gutenberg' ),
960 + 'icon' => null,
961 + ),
962 + array(
963 + 'slug' => 'widgets',
964 + 'title' => __( 'Widgets', 'gutenberg' ),
965 + 'icon' => null,
966 + ),
967 + array(
968 + 'slug' => 'embed',
969 + 'title' => __( 'Embeds', 'gutenberg' ),
970 + 'icon' => null,
971 + ),
972 + array(
973 + 'slug' => 'reusable',
974 + 'title' => __( 'Reusable Blocks', 'gutenberg' ),
975 + 'icon' => null,
976 + ),
977 + );
978 +
979 + return apply_filters( 'block_categories', $default_categories, $post );
476 980 }
981 +
982 +/**
983 + * Loads Gutenberg Locale Data.
984 + */
985 +function gutenberg_load_locale_data() {
986 + // Prepare Jed locale data.
987 + $locale_data = gutenberg_get_jed_locale_data( 'gutenberg' );
988 + wp_add_inline_script(
989 + 'wp-i18n',
990 + 'wp.i18n.setLocaleData( ' . json_encode( $locale_data ) . ' );'
991 + );
992 +}
993 +
994 +/**
995 + * Retrieve The available image sizes for a post
996 + *
997 + * @return array
998 + */
999 +function gutenberg_get_available_image_sizes() {
1000 + $size_names = apply_filters(
1001 + 'image_size_names_choose',
1002 + array(
1003 + 'thumbnail' => __( 'Thumbnail', 'gutenberg' ),
1004 + 'medium' => __( 'Medium', 'gutenberg' ),
1005 + 'large' => __( 'Large', 'gutenberg' ),
1006 + 'full' => __( 'Full Size', 'gutenberg' ),
1007 + )
1008 + );
1009 +
1010 + $all_sizes = array();
1011 + foreach ( $size_names as $size_slug => $size_name ) {
1012 + $all_sizes[] = array(
1013 + 'slug' => $size_slug,
1014 + 'name' => $size_name,
1015 + );
1016 + }
1017 +
1018 + return $all_sizes;
1019 +}
1020 +
1021 +/**
1022 + * Scripts & Styles.
1023 + *
1024 + * Enqueues the needed scripts and styles when visiting the top-level page of
1025 + * the Gutenberg editor.
1026 + *
1027 + * @since 0.1.0
1028 + *
1029 + * @param string $hook Screen name.
1030 + */
1031 +function gutenberg_editor_scripts_and_styles( $hook ) {
1032 + $is_demo = isset( $_GET['gutenberg-demo'] );
1033 +
1034 + global $wp_scripts;
1035 +
1036 + // Add "wp-hooks" as dependency of "heartbeat".
1037 + $heartbeat_script = $wp_scripts->query( 'heartbeat', 'registered' );
1038 + if ( $heartbeat_script && ! in_array( 'wp-hooks', $heartbeat_script->deps ) ) {
1039 + $heartbeat_script->deps[] = 'wp-hooks';
1040 + }
1041 +
1042 + // Enqueue heartbeat separately as an "optional" dependency of the editor.
1043 + // Heartbeat is used for automatic nonce refreshing, but some hosts choose
1044 + // to disable it outright.
1045 + wp_enqueue_script( 'heartbeat' );
1046 +
1047 + // Transform a "heartbeat-tick" jQuery event into "heartbeat.tick" hook action.
1048 + // This removes the need of using jQuery for listening to the event.
1049 + wp_add_inline_script(
1050 + 'heartbeat',
1051 + 'jQuery( document ).on( "heartbeat-tick", function ( event, response ) { wp.hooks.doAction( "heartbeat.tick", response ) } );',
1052 + 'after'
1053 + );
1054 +
1055 + // Ignore Classic Editor's `rich_editing` user option, aka "Disable visual
1056 + // editor". Forcing this to be true guarantees that TinyMCE and its plugins
1057 + // are available in Gutenberg. Fixes
1058 + // https://github.com/WordPress/gutenberg/issues/5667.
1059 + $user_can_richedit = user_can_richedit();
1060 + add_filter( 'user_can_richedit', '__return_true' );
1061 +
1062 + wp_enqueue_script( 'wp-edit-post' );
1063 + wp_enqueue_script( 'wp-format-library' );
1064 + wp_enqueue_style( 'wp-format-library' );
1065 +
1066 + global $post;
1067 +
1068 + // Set initial title to empty string for auto draft for duration of edit.
1069 + // Otherwise, title defaults to and displays as "Auto Draft".
1070 + $is_new_post = 'auto-draft' === $post->post_status;
1071 +
1072 + // Set the post type name.
1073 + $post_type = get_post_type( $post );
1074 + $post_type_object = get_post_type_object( $post_type );
1075 + $rest_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
1076 +
1077 + $preload_paths = array(
1078 + '/',
1079 + '/wp/v2/types?context=edit',
1080 + '/wp/v2/taxonomies?per_page=-1&context=edit',
1081 + '/wp/v2/themes?status=active',
1082 + sprintf( '/wp/v2/%s/%s?context=edit', $rest_base, $post->ID ),
1083 + sprintf( '/wp/v2/types/%s?context=edit', $post_type ),
1084 + sprintf( '/wp/v2/users/me?post_type=%s&context=edit', $post_type ),
1085 + array( '/wp/v2/media', 'OPTIONS' ),
1086 + );
1087 +
1088 + /**
1089 + * Preload common data by specifying an array of REST API paths that will be preloaded.
1090 + *
1091 + * Filters the array of paths that will be preloaded.
1092 + *
1093 + * @param array $preload_paths Array of paths to preload
1094 + * @param object $post The post resource data.
1095 + */
1096 + $preload_paths = apply_filters( 'block_editor_preload_paths', $preload_paths, $post );
1097 +
1098 + // Ensure the global $post remains the same after
1099 + // API data is preloaded. Because API preloading
1100 + // can call the_content and other filters, callbacks
1101 + // can unexpectedly modify $post resulting in issues
1102 + // like https://github.com/WordPress/gutenberg/issues/7468.
1103 + $backup_global_post = $post;
1104 +
1105 + $preload_data = array_reduce(
1106 + $preload_paths,
1107 + 'gutenberg_preload_api_request',
1108 + array()
1109 + );
1110 +
1111 + // Restore the global $post as it was before API preloading.
1112 + $post = $backup_global_post;
1113 +
1114 + wp_add_inline_script(
1115 + 'wp-api-fetch',
1116 + sprintf( 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', wp_json_encode( $preload_data ) ),
1117 + 'after'
1118 + );
1119 +
1120 + wp_add_inline_script(
1121 + 'wp-blocks',
1122 + sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( gutenberg_get_block_categories( $post ) ) ),
1123 + 'after'
1124 + );
1125 +
1126 + // Assign initial edits, if applicable. These are not initially assigned
1127 + // to the persisted post, but should be included in its save payload.
1128 + if ( $is_new_post && $is_demo ) {
1129 + // Prepopulate with some test content in demo.
1130 + ob_start();
1131 + include gutenberg_dir_path() . 'post-content.php';
1132 + $demo_content = ob_get_clean();
1133 +
1134 + $initial_edits = array(
1135 + 'title' => __( 'Welcome to the Gutenberg Editor', 'gutenberg' ),
1136 + 'content' => $demo_content,
1137 + );
1138 + } elseif ( $is_new_post ) {
1139 + // Override "(Auto Draft)" new post default title with empty string,
1140 + // or filtered value.
1141 + $initial_edits = array(
1142 + 'title' => $post->post_title,
1143 + 'content' => $post->post_content,
1144 + 'excerpt' => $post->post_excerpt,
1145 + );
1146 + } else {
1147 + $initial_edits = null;
1148 + }
1149 +
1150 + gutenberg_load_locale_data();
1151 +
1152 + // Preload server-registered block schemas.
1153 + wp_add_inline_script(
1154 + 'wp-blocks',
1155 + 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . json_encode( gutenberg_prepare_blocks_for_js() ) . ');'
1156 + );
1157 +
1158 + // Get admin url for handling meta boxes.
1159 + $meta_box_url = admin_url( 'post.php' );
1160 + $meta_box_url = add_query_arg(
1161 + array(
1162 + 'post' => $post->ID,
1163 + 'action' => 'edit',
1164 + 'classic-editor' => true,
1165 + 'meta_box' => true,
1166 + ),
1167 + $meta_box_url
1168 + );
1169 + wp_localize_script( 'wp-editor', '_wpMetaBoxUrl', $meta_box_url );
1170 +
1171 + // Initialize the editor.
1172 + $gutenberg_theme_support = get_theme_support( 'gutenberg' );
1173 + $align_wide = get_theme_support( 'align-wide' );
1174 + $color_palette = current( (array) get_theme_support( 'editor-color-palette' ) );
1175 + $font_sizes = current( (array) get_theme_support( 'editor-font-sizes' ) );
1176 +
1177 + /**
1178 + * Filters the allowed block types for the editor, defaulting to true (all
1179 + * block types supported).
1180 + *
1181 + * @param bool|array $allowed_block_types Array of block type slugs, or
1182 + * boolean to enable/disable all.
1183 + * @param object $post The post resource data.
1184 + */
1185 + $allowed_block_types = apply_filters( 'allowed_block_types', true, $post );
1186 +
1187 + // Get all available templates for the post/page attributes meta-box.
1188 + // The "Default template" array element should only be added if the array is
1189 + // not empty so we do not trigger the template select element without any options
1190 + // besides the default value.
1191 + $available_templates = wp_get_theme()->get_page_templates( get_post( $post->ID ) );
1192 + $available_templates = ! empty( $available_templates ) ? array_merge(
1193 + array(
1194 + '' => apply_filters( 'default_page_template_title', __( 'Default template', 'gutenberg' ), 'rest-api' ),
1195 + ),
1196 + $available_templates
1197 + ) : $available_templates;
1198 +
1199 + // Media settings.
1200 + $max_upload_size = wp_max_upload_size();
1201 + if ( ! $max_upload_size ) {
1202 + $max_upload_size = 0;
1203 + }
1204 +
1205 + // Editor Styles.
1206 + global $editor_styles;
1207 + $styles = array(
1208 + array(
1209 + 'css' => file_get_contents(
1210 + gutenberg_dir_path() . 'build/editor/editor-styles.css'
1211 + ),
1212 + ),
1213 + );
1214 +
1215 + /* Translators: Use this to specify the CSS font family for the default font */
1216 + $locale_font_family = esc_html_x( 'Noto Serif', 'CSS Font Family for Editor Font', 'gutenberg' );
1217 + $styles[] = array(
1218 + 'css' => "body { font-family: '$locale_font_family' }",
1219 + );
1220 +
1221 + if ( $editor_styles && current_theme_supports( 'editor-styles' ) ) {
1222 + foreach ( $editor_styles as $style ) {
1223 + if ( filter_var( $style, FILTER_VALIDATE_URL ) ) {
1224 + $styles[] = array(
1225 + 'css' => file_get_contents( $style ),
1226 + );
1227 + } else {
1228 + $file = get_theme_file_path( $style );
1229 + if ( file_exists( $file ) ) {
1230 + $styles[] = array(
1231 + 'css' => file_get_contents( $file ),
1232 + 'baseURL' => get_theme_file_uri( $style ),
1233 + );
1234 + }
1235 + }
1236 + }
1237 + }
1238 +
1239 + // Lock settings.
1240 + $user_id = wp_check_post_lock( $post->ID );
1241 + if ( $user_id ) {
1242 + /**
1243 + * Filters whether to show the post locked dialog.
1244 + *
1245 + * Returning a falsey value to the filter will short-circuit displaying the dialog.
1246 + *
1247 + * @since 3.6.0
1248 + *
1249 + * @param bool $display Whether to display the dialog. Default true.
1250 + * @param WP_Post $post Post object.
1251 + * @param WP_User|bool $user The user id currently editing the post.
1252 + */
1253 + if ( apply_filters( 'show_post_locked_dialog', true, $post, $user_id ) ) {
1254 + $locked = true;
1255 + }
1256 +
1257 + $user_details = null;
1258 + if ( $locked ) {
1259 + $user = get_userdata( $user_id );
1260 + $user_details = array(
1261 + 'name' => $user->display_name,
1262 + );
1263 + $avatar = get_avatar( $user_id, 64 );
1264 + if ( $avatar ) {
1265 + if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) {
1266 + $user_details['avatar'] = $matches[1];
1267 + }
1268 + }
1269 + }
1270 +
1271 + $lock_details = array(
1272 + 'isLocked' => $locked,
1273 + 'user' => $user_details,
1274 + );
1275 + } else {
1276 +
1277 + // Lock the post.
1278 + $active_post_lock = wp_set_post_lock( $post->ID );
1279 + $lock_details = array(
1280 + 'isLocked' => false,
1281 + 'activePostLock' => esc_attr( implode( ':', $active_post_lock ) ),
1282 + );
1283 + }
1284 +
1285 + $editor_settings = array(
1286 + 'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), // Backcompat. Use `align-wide` outside of `gutenberg` array.
1287 + 'availableTemplates' => $available_templates,
1288 + 'allowedBlockTypes' => $allowed_block_types,
1289 + 'disableCustomColors' => get_theme_support( 'disable-custom-colors' ),
1290 + 'disableCustomFontSizes' => get_theme_support( 'disable-custom-font-sizes' ),
1291 + 'disablePostFormats' => ! current_theme_supports( 'post-formats' ),
1292 + 'titlePlaceholder' => apply_filters( 'enter_title_here', __( 'Add title', 'gutenberg' ), $post ),
1293 + 'bodyPlaceholder' => apply_filters( 'write_your_story', __( 'Start writing or type / to choose a block', 'gutenberg' ), $post ),
1294 + 'isRTL' => is_rtl(),
1295 + 'autosaveInterval' => 10,
1296 + 'maxUploadFileSize' => $max_upload_size,
1297 + 'allowedMimeTypes' => get_allowed_mime_types(),
1298 + 'styles' => $styles,
1299 + 'imageSizes' => gutenberg_get_available_image_sizes(),
1300 + 'richEditingEnabled' => $user_can_richedit,
1301 +
1302 + // Ideally, we'd remove this and rely on a REST API endpoint.
1303 + 'postLock' => $lock_details,
1304 + 'postLockUtils' => array(
1305 + 'nonce' => wp_create_nonce( 'lock-post_' . $post->ID ),
1306 + 'unlockNonce' => wp_create_nonce( 'update-post_' . $post->ID ),
1307 + 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
1308 + ),
1309 +
1310 + // Whether or not to load the 'postcustom' meta box is stored as a user meta
1311 + // field so that we're not always loading its assets.
1312 + 'enableCustomFields' => (bool) get_user_meta( get_current_user_id(), 'enable_custom_fields', true ),
1313 + );
1314 +
1315 + $post_autosave = gutenberg_get_autosave_newer_than_post_save( $post );
1316 + if ( $post_autosave ) {
1317 + $editor_settings['autosave'] = array(
1318 + 'editLink' => add_query_arg( 'gutenberg', true, get_edit_post_link( $post_autosave->ID ) ),
1319 + );
1320 + }
1321 +
1322 + if ( false !== $color_palette ) {
1323 + $editor_settings['colors'] = $color_palette;
1324 + }
1325 +
1326 + if ( ! empty( $font_sizes ) ) {
1327 + $editor_settings['fontSizes'] = $font_sizes;
1328 + }
1329 +
1330 + if ( ! empty( $post_type_object->template ) ) {
1331 + $editor_settings['template'] = $post_type_object->template;
1332 + $editor_settings['templateLock'] = ! empty( $post_type_object->template_lock ) ? $post_type_object->template_lock : false;
1333 + }
1334 +
1335 + $init_script = <<<JS
1336 + ( function() {
1337 + window._wpLoadGutenbergEditor = new Promise( function( resolve ) {
1338 + wp.domReady( function() {
1339 + resolve( wp.editPost.initializeEditor( 'editor', "%s", %d, %s, %s ) );
1340 + } );
1341 + } );
1342 +} )();
1343 +JS;
1344 +
1345 + /**
1346 + * Filters the settings to pass to the block editor.
1347 + *
1348 + * @since 3.7.0
1349 + *
1350 + * @param array $editor_settings Default editor settings.
1351 + * @param WP_Post $post Post being edited.
1352 + */
1353 + $editor_settings = apply_filters( 'block_editor_settings', $editor_settings, $post );
1354 +
1355 + $script = sprintf(
1356 + $init_script,
1357 + $post->post_type,
1358 + $post->ID,
1359 + wp_json_encode( $editor_settings ),
1360 + wp_json_encode( $initial_edits )
1361 + );
1362 + wp_add_inline_script( 'wp-edit-post', $script );
1363 +
1364 + /**
1365 + * Scripts
1366 + */
1367 + wp_enqueue_media(
1368 + array(
1369 + 'post' => $post->ID,
1370 + )
1371 + );
1372 + wp_enqueue_editor();
1373 +
1374 + /**
1375 + * Styles
1376 + */
1377 + wp_enqueue_style( 'wp-edit-post' );
1378 +
1379 + /**
1380 + * Fires after block assets have been enqueued for the editing interface.
1381 + *
1382 + * Call `add_action` on any hook before 'admin_enqueue_scripts'.
1383 + *
1384 + * In the function call you supply, simply use `wp_enqueue_script` and
1385 + * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
1386 + *
1387 + * @since 0.4.0
1388 + */
1389 + do_action( 'enqueue_block_editor_assets' );
1390 +}
1391 +
1392 +/**
1393 + * Enqueue the reusable blocks listing page's script
1394 + *
1395 + * @param string $hook Screen name.
1396 + */
1397 +function gutenberg_load_list_reusable_blocks( $hook ) {
1398 + $is_reusable_blocks_list_page = 'edit.php' === $hook && isset( $_GET['post_type'] ) && 'wp_block' === $_GET['post_type'];
1399 + if ( $is_reusable_blocks_list_page ) {
1400 + gutenberg_load_locale_data();
1401 + wp_enqueue_script( 'wp-list-reusable-blocks' );
1402 + wp_enqueue_style( 'wp-list-reusable-blocks' );
1403 + }
1404 +}
1405 +add_action( 'admin_enqueue_scripts', 'gutenberg_load_list_reusable_blocks' );