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

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

1,405 lines 42.8 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) for the
4 * 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( dirname( __FILE__ ) );
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, dirname( __FILE__ ) );
35 }
36
37 /**
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.
41 *
42 * @param array $tests Features to detect.
43 * @return string Conditional polyfill inline script.
44 */
45 function gutenberg_get_script_polyfill( $tests ) {
46 global $wp_scripts;
47
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 );
64 }
65
66 return $polyfill;
67 }
68
69 if ( ! function_exists( 'register_tinymce_scripts' ) ) {
70 /**
71 * Registers the main TinyMCE scripts.
72 */
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 }
90
91 gutenberg_override_script( 'wp-tinymce-lists', includes_url( 'js/tinymce/' ) . "plugins/lists/plugin{$suffix}.js", array( 'wp-tinymce' ), $tinymce_version );
92 }
93 }
94
95 /**
96 * Registers a script according to `wp_register_script`. Honors this request by
97 * deregistering any script by the same handler before registration.
98 *
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'.
110 */
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 );
114 }
115
116 /**
117 * Registers a style according to `wp_register_style`. Honors this request by
118 * deregistering any style by the same handler before registration.
119 *
120 * @since 4.1.0
121 *
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.
141 *
142 * @since 4.5.0
143 */
144 function gutenberg_register_packages_scripts() {
145 $packages_dependencies = include dirname( __FILE__ ) . '/packages-dependencies.php';
146
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 }
160
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();
169
170 register_tinymce_scripts();
171
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 );
184
185 gutenberg_register_packages_scripts();
186
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'
195 );
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 );
288
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',
308 );
309 $tinymce_plugins = apply_filters( 'tiny_mce_plugins', $tinymce_plugins, 'classic-block' );
310 $tinymce_plugins = array_unique( $tinymce_plugins );
311
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',
328 );
329 $toolbar1 = apply_filters( 'mce_buttons', $toolbar1, 'classic-block' );
330
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',
343 );
344 $toolbar2 = apply_filters( 'mce_buttons_2', $toolbar2, 'classic-block' );
345
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,
359 );
360 $tinymce_settings = apply_filters( 'tiny_mce_before_init', $tinymce_settings, 'classic-block' );
361
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 . '",';
379 }
380
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' ) );
409 }
410
411 gutenberg_override_style(
412 'wp-editor-font',
413 $fonts_url,
414 array(),
415 null
416 );
417
418 gutenberg_override_style(
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' ),
437 array(),
438 filemtime( gutenberg_dir_path() . 'build/components/style.css' )
439 );
440 wp_style_add_data( 'wp-components', 'rtl', 'replace' );
441
442 gutenberg_override_style(
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' ),
453 array(),
454 filemtime( gutenberg_dir_path() . 'build/format-library/style.css' )
455 );
456 wp_style_add_data( 'wp-format-library', 'rtl', 'replace' );
457
458 gutenberg_override_style(
459 'wp-edit-blocks',
460 gutenberg_url( 'build/block-library/editor.css' ),
461 array(
462 'wp-components',
463 'wp-editor',
464 // Always include visual styles so the editor never appears broken.
465 'wp-block-library-theme',
466 ),
467 filemtime( gutenberg_dir_path() . 'build/block-library/editor.css' )
468 );
469 wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' );
470
471 gutenberg_override_style(
472 'wp-nux',
473 gutenberg_url( 'build/nux/style.css' ),
474 array( 'wp-components' ),
475 filemtime( gutenberg_dir_path() . 'build/nux/style.css' )
476 );
477 wp_style_add_data( 'wp-nux', 'rtl', 'replace' );
478
479 gutenberg_override_style(
480 'wp-block-library-theme',
481 gutenberg_url( 'build/block-library/theme.css' ),
482 array(),
483 filemtime( gutenberg_dir_path() . 'build/block-library/theme.css' )
484 );
485 wp_style_add_data( 'wp-block-library-theme', 'rtl', 'replace' );
486
487 gutenberg_override_style(
488 'wp-list-reusable-blocks',
489 gutenberg_url( 'build/list-reusable-blocks/style.css' ),
490 array( 'wp-components' ),
491 filemtime( gutenberg_dir_path() . 'build/list-reusable-blocks/style.css' )
492 );
493 wp_style_add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
494
495 if ( defined( 'GUTENBERG_LIVE_RELOAD' ) && GUTENBERG_LIVE_RELOAD ) {
496 $live_reload_url = ( GUTENBERG_LIVE_RELOAD === true ) ? 'http://localhost:35729/livereload.js' : GUTENBERG_LIVE_RELOAD;
497
498 wp_enqueue_script(
499 'gutenberg-live-reload',
500 $live_reload_url
501 );
502 }
503
504 // Temporary backward compatibility for `wp-polyfill-ecmascript`, which has
505 // since been absorbed into `wp-polyfill`.
506 //
507 // [TODO][REMOVEME] To be removed in Gutenberg v4.5.
508 gutenberg_override_script(
509 'wp-polyfill-ecmascript',
510 null,
511 array(
512 'wp-polyfill',
513 'wp-deprecated',
514 )
515 );
516 wp_script_add_data(
517 'wp-polyfill-ecmascript',
518 'data',
519 'wp.deprecated( "wp-polyfill-ecmascript script handle", { plugin: "Gutenberg", version: "4.5" } );'
520 );
521 }
522 add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
523 add_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
524
525 /**
526 * Append result of internal request to REST API for purpose of preloading
527 * data to be attached to the page. Expected to be called in the context of
528 * `array_reduce`.
529 *
530 * @param array $memo Reduce accumulator.
531 * @param string $path REST API path to preload.
532 * @return array Modified reduce accumulator.
533 */
534 function gutenberg_preload_api_request( $memo, $path ) {
535
536 // array_reduce() doesn't support passing an array in PHP 5.2
537 // so we need to make sure we start with one.
538 if ( ! is_array( $memo ) ) {
539 $memo = array();
540 }
541
542 if ( empty( $path ) ) {
543 return $memo;
544 }
545
546 $method = 'GET';
547 if ( is_array( $path ) && 2 === count( $path ) ) {
548 $method = end( $path );
549 $path = reset( $path );
550
551 if ( ! in_array( $method, array( 'GET', 'OPTIONS' ), true ) ) {
552 $method = 'GET';
553 }
554 }
555
556 $path_parts = parse_url( $path );
557 if ( false === $path_parts ) {
558 return $memo;
559 }
560
561 $request = new WP_REST_Request( $method, $path_parts['path'] );
562 if ( ! empty( $path_parts['query'] ) ) {
563 parse_str( $path_parts['query'], $query_params );
564 $request->set_query_params( $query_params );
565 }
566
567 $response = rest_do_request( $request );
568 if ( 200 === $response->status ) {
569 $server = rest_get_server();
570 $data = (array) $response->get_data();
571 $links = $server->get_compact_response_links( $response );
572 if ( ! empty( $links ) ) {
573 $data['_links'] = $links;
574 }
575
576 if ( 'OPTIONS' === $method ) {
577 $response = rest_send_allow_header( $response, $server, $request );
578
579 $memo[ $method ][ $path ] = array(
580 'body' => $data,
581 'headers' => $response->headers,
582 );
583 } else {
584 $memo[ $path ] = array(
585 'body' => $data,
586 'headers' => $response->headers,
587 );
588 }
589 }
590
591 return $memo;
592 }
593
594 /**
595 * Registers vendor JavaScript files to be used as dependencies of the editor
596 * and plugins.
597 *
598 * This function is called from a script during the plugin build process, so it
599 * should not call any WordPress PHP functions.
600 *
601 * @since 0.1.0
602 */
603 function gutenberg_register_vendor_scripts() {
604 $suffix = SCRIPT_DEBUG ? '' : '.min';
605
606 // Vendor Scripts.
607 $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix;
608
609 gutenberg_register_vendor_script(
610 'react',
611 'https://unpkg.com/react@16.6.3/umd/react' . $react_suffix . '.js',
612 array( 'wp-polyfill' )
613 );
614 gutenberg_register_vendor_script(
615 'react-dom',
616 'https://unpkg.com/react-dom@16.6.3/umd/react-dom' . $react_suffix . '.js',
617 array( 'react' )
618 );
619 $moment_script = SCRIPT_DEBUG ? 'moment.js' : 'min/moment.min.js';
620 gutenberg_register_vendor_script(
621 'moment',
622 'https://unpkg.com/moment@2.22.1/' . $moment_script,
623 array()
624 );
625 gutenberg_register_vendor_script(
626 'lodash',
627 'https://unpkg.com/lodash@4.17.5/lodash' . $suffix . '.js'
628 );
629 wp_add_inline_script( 'lodash', 'window.lodash = _.noConflict();' );
630 gutenberg_register_vendor_script(
631 'wp-polyfill-fetch',
632 'https://unpkg.com/whatwg-fetch@3.0.0/dist/fetch.umd.js'
633 );
634 gutenberg_register_vendor_script(
635 'wp-polyfill-formdata',
636 'https://unpkg.com/formdata-polyfill@3.0.9/formdata.min.js'
637 );
638 gutenberg_register_vendor_script(
639 'wp-polyfill-node-contains',
640 'https://unpkg.com/polyfill-library@3.26.0-0/polyfills/Node/prototype/contains/polyfill.js'
641 );
642 gutenberg_register_vendor_script(
643 'wp-polyfill-element-closest',
644 'https://unpkg.com/element-closest@2.0.2/element-closest.js'
645 );
646 gutenberg_register_vendor_script(
647 'wp-polyfill',
648 'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.0.0/polyfill' . $suffix . '.js'
649 );
650 }
651
652 /**
653 * Retrieves a unique and reasonably short and human-friendly filename for a
654 * vendor script based on a URL and the script handle.
655 *
656 * @param string $handle The name of the script.
657 * @param string $src Full URL of the external script.
658 *
659 * @return string Script filename suitable for local caching.
660 *
661 * @since 0.1.0
662 */
663 function gutenberg_vendor_script_filename( $handle, $src ) {
664 $match_tinymce_plugin = preg_match(
665 '@tinymce.*/plugins/([^/]+)/plugin(\.min)?\.js$@',
666 $src,
667 $tinymce_plugin_pieces
668 );
669 if ( $match_tinymce_plugin ) {
670 $prefix = 'tinymce-plugin-' . $tinymce_plugin_pieces[1];
671 $suffix = isset( $tinymce_plugin_pieces[2] ) ? $tinymce_plugin_pieces[2] : '';
672 } else {
673 $filename = basename( $src );
674 $match = preg_match(
675 '/^'
676 . '(?P<ignore>.*?)'
677 . '(?P<suffix>\.min)?'
678 . '(?P<extension>\.js)'
679 . '(?P<extra>.*)'
680 . '$/',
681 $filename,
682 $filename_pieces
683 );
684
685 $prefix = $handle;
686 $suffix = $match ? $filename_pieces['suffix'] : '';
687 }
688
689 $hash = substr( md5( $src ), 0, 8 );
690
691 return "${prefix}${suffix}.${hash}.js";
692 }
693
694 /**
695 * Registers a vendor script from a URL, preferring a locally cached version if
696 * possible, or downloading it if the cached version is unavailable or
697 * outdated.
698 *
699 * @param string $handle Name of the script.
700 * @param string $src Full URL of the external script.
701 * @param array $deps Optional. An array of registered script handles this
702 * script depends on.
703 *
704 * @since 0.1.0
705 */
706 function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) {
707 if ( defined( 'GUTENBERG_LOAD_VENDOR_SCRIPTS' ) && ! GUTENBERG_LOAD_VENDOR_SCRIPTS ) {
708 return;
709 }
710
711 $filename = gutenberg_vendor_script_filename( $handle, $src );
712
713 if ( defined( 'GUTENBERG_LIST_VENDOR_ASSETS' ) && GUTENBERG_LIST_VENDOR_ASSETS ) {
714 echo "$src|$filename\n";
715 return;
716 }
717
718 $full_path = gutenberg_dir_path() . 'vendor/' . $filename;
719
720 $needs_fetch = (
721 defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && (
722 ! file_exists( $full_path ) ||
723 time() - filemtime( $full_path ) >= DAY_IN_SECONDS
724 )
725 );
726
727 if ( $needs_fetch ) {
728 // Determine whether we can write to this file. If not, don't waste
729 // time doing a network request.
730 // @codingStandardsIgnoreStart
731 $f = @fopen( $full_path, 'a' );
732 // @codingStandardsIgnoreEnd
733 if ( ! $f ) {
734 // Failed to open the file for writing, probably due to server
735 // permissions. Enqueue the script directly from the URL instead.
736 gutenberg_override_script( $handle, $src, $deps, null );
737 return;
738 }
739 fclose( $f );
740 $response = wp_remote_get( $src );
741 if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
742 $f = fopen( $full_path, 'w' );
743 fwrite( $f, wp_remote_retrieve_body( $response ) );
744 fclose( $f );
745 } elseif ( ! filesize( $full_path ) ) {
746 // The request failed. If the file is already cached, continue to
747 // use this file. If not, then unlink the 0 byte file, and enqueue
748 // the script directly from the URL.
749 gutenberg_override_script( $handle, $src, $deps, null );
750 unlink( $full_path );
751 return;
752 }
753 }
754 gutenberg_override_script(
755 $handle,
756 gutenberg_url( 'vendor/' . $filename ),
757 $deps,
758 null
759 );
760 }
761
762 /**
763 * Prepares server-registered blocks for JavaScript, returning an associative
764 * array of registered block data keyed by block name. Data includes properties
765 * of a block relevant for client registration.
766 *
767 * @return array An associative array of registered block data.
768 */
769 function gutenberg_prepare_blocks_for_js() {
770 $block_registry = WP_Block_Type_Registry::get_instance();
771 $blocks = array();
772 $keys_to_pick = array( 'title', 'description', 'icon', 'category', 'keywords', 'supports', 'attributes' );
773
774 foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
775 foreach ( $keys_to_pick as $key ) {
776 if ( ! isset( $block_type->{ $key } ) ) {
777 continue;
778 }
779
780 if ( ! isset( $blocks[ $block_name ] ) ) {
781 $blocks[ $block_name ] = array();
782 }
783
784 $blocks[ $block_name ][ $key ] = $block_type->{ $key };
785 }
786 }
787
788 return $blocks;
789 }
790
791 /**
792 * Handles the enqueueing of block scripts and styles that are common to both
793 * the editor and the front-end.
794 *
795 * Note: This function must remain *before*
796 * `gutenberg_editor_scripts_and_styles` so that editor-specific stylesheets
797 * are loaded last.
798 *
799 * @since 0.4.0
800 */
801 function gutenberg_common_scripts_and_styles() {
802 if ( ! is_gutenberg_page() && is_admin() ) {
803 return;
804 }
805
806 // Enqueue basic styles built out of Gutenberg through `npm build`.
807 wp_enqueue_style( 'wp-block-library' );
808
809 /*
810 * Enqueue block styles built through plugins. This lives in a separate
811 * action for a couple of reasons: (1) we want to load these assets
812 * (usually stylesheets) in *both* frontend and editor contexts, and (2)
813 * one day we may need to be smarter about whether assets are included
814 * based on whether blocks are actually present on the page.
815 */
816
817 /**
818 * Fires after enqueuing block assets for both editor and front-end.
819 *
820 * Call `add_action` on any hook before 'wp_enqueue_scripts'.
821 *
822 * In the function call you supply, simply use `wp_enqueue_script` and
823 * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
824 *
825 * @since 0.4.0
826 */
827 do_action( 'enqueue_block_assets' );
828 }
829 add_action( 'wp_enqueue_scripts', 'gutenberg_common_scripts_and_styles' );
830 add_action( 'admin_enqueue_scripts', 'gutenberg_common_scripts_and_styles' );
831
832 /**
833 * Enqueues registered block scripts and styles, depending on current rendered
834 * context (only enqueuing editor scripts while in context of the editor).
835 *
836 * @since 2.0.0
837 */
838 function gutenberg_enqueue_registered_block_scripts_and_styles() {
839 $is_editor = ( 'enqueue_block_editor_assets' === current_action() );
840
841 $block_registry = WP_Block_Type_Registry::get_instance();
842 foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
843 // Front-end styles.
844 if ( ! empty( $block_type->style ) ) {
845 wp_enqueue_style( $block_type->style );
846 }
847
848 // Front-end script.
849 if ( ! empty( $block_type->script ) ) {
850 wp_enqueue_script( $block_type->script );
851 }
852
853 // Editor styles.
854 if ( $is_editor && ! empty( $block_type->editor_style ) ) {
855 wp_enqueue_style( $block_type->editor_style );
856 }
857
858 // Editor script.
859 if ( $is_editor && ! empty( $block_type->editor_script ) ) {
860 wp_enqueue_script( $block_type->editor_script );
861 }
862 }
863 }
864 add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' );
865 add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' );
866
867 /**
868 * Assigns a default editor template with a default block by post format, if
869 * not otherwise assigned for a new post of type "post".
870 *
871 * @param array $settings Default editor settings.
872 * @param WP_Post $post Post being edited.
873 *
874 * @return array Filtered block editor settings.
875 */
876 function gutenberg_default_post_format_template( $settings, $post ) {
877 // Only assign template for new posts without explicitly assigned template.
878 $is_new_post = 'auto-draft' === $post->post_status;
879 if ( $is_new_post && ! isset( $settings['template'] ) && 'post' === $post->post_type ) {
880 switch ( get_post_format() ) {
881 case 'audio':
882 $default_block_name = 'core/audio';
883 break;
884 case 'gallery':
885 $default_block_name = 'core/gallery';
886 break;
887 case 'image':
888 $default_block_name = 'core/image';
889 break;
890 case 'quote':
891 $default_block_name = 'core/quote';
892 break;
893 case 'video':
894 $default_block_name = 'core/video';
895 break;
896 }
897
898 if ( isset( $default_block_name ) ) {
899 $settings['template'] = array( array( $default_block_name ) );
900 }
901 }
902
903 return $settings;
904 }
905 add_filter( 'block_editor_settings', 'gutenberg_default_post_format_template', 10, 2 );
906
907 /**
908 * Retrieve a stored autosave that is newer than the post save.
909 *
910 * Deletes autosaves that are older than the post save.
911 *
912 * @param WP_Post $post Post object.
913 * @return WP_Post|boolean The post autosave. False if none found.
914 */
915 function gutenberg_get_autosave_newer_than_post_save( $post ) {
916 // Add autosave data if it is newer and changed.
917 $autosave = wp_get_post_autosave( $post->ID );
918
919 if ( ! $autosave ) {
920 return false;
921 }
922
923 // Check if the autosave is newer than the current post.
924 if (
925 mysql2date( 'U', $autosave->post_modified_gmt, false ) > mysql2date( 'U', $post->post_modified_gmt, false )
926 ) {
927 return $autosave;
928 }
929
930 // If the autosave isn't newer, remove it.
931 wp_delete_post_revision( $autosave->ID );
932
933 return false;
934 }
935
936 /**
937 * Returns all the block categories.
938 *
939 * @since 2.2.0
940 *
941 * @param WP_Post $post Post object.
942 * @return Object[] Block categories.
943 */
944 function gutenberg_get_block_categories( $post ) {
945 $default_categories = array(
946 array(
947 'slug' => 'common',
948 'title' => __( 'Common Blocks', 'gutenberg' ),
949 'icon' => null,
950 ),
951 array(
952 'slug' => 'formatting',
953 'title' => __( 'Formatting', 'gutenberg' ),
954 'icon' => null,
955 ),
956 array(
957 'slug' => 'layout',
958 'title' => __( 'Layout Elements', 'gutenberg' ),
959 'icon' => null,
960 ),
961 array(
962 'slug' => 'widgets',
963 'title' => __( 'Widgets', 'gutenberg' ),
964 'icon' => null,
965 ),
966 array(
967 'slug' => 'embed',
968 'title' => __( 'Embeds', 'gutenberg' ),
969 'icon' => null,
970 ),
971 array(
972 'slug' => 'reusable',
973 'title' => __( 'Reusable Blocks', 'gutenberg' ),
974 'icon' => null,
975 ),
976 );
977
978 return apply_filters( 'block_categories', $default_categories, $post );
979 }
980
981 /**
982 * Loads Gutenberg Locale Data.
983 */
984 function gutenberg_load_locale_data() {
985 // Prepare Jed locale data.
986 $locale_data = gutenberg_get_jed_locale_data( 'gutenberg' );
987 wp_add_inline_script(
988 'wp-i18n',
989 'wp.i18n.setLocaleData( ' . json_encode( $locale_data ) . ' );'
990 );
991 }
992
993 /**
994 * Retrieve The available image sizes for a post
995 *
996 * @return array
997 */
998 function gutenberg_get_available_image_sizes() {
999 $size_names = apply_filters(
1000 'image_size_names_choose',
1001 array(
1002 'thumbnail' => __( 'Thumbnail', 'gutenberg' ),
1003 'medium' => __( 'Medium', 'gutenberg' ),
1004 'large' => __( 'Large', 'gutenberg' ),
1005 'full' => __( 'Full Size', 'gutenberg' ),
1006 )
1007 );
1008
1009 $all_sizes = array();
1010 foreach ( $size_names as $size_slug => $size_name ) {
1011 $all_sizes[] = array(
1012 'slug' => $size_slug,
1013 'name' => $size_name,
1014 );
1015 }
1016
1017 return $all_sizes;
1018 }
1019
1020 /**
1021 * Scripts & Styles.
1022 *
1023 * Enqueues the needed scripts and styles when visiting the top-level page of
1024 * the Gutenberg editor.
1025 *
1026 * @since 0.1.0
1027 *
1028 * @param string $hook Screen name.
1029 */
1030 function gutenberg_editor_scripts_and_styles( $hook ) {
1031 $is_demo = isset( $_GET['gutenberg-demo'] );
1032
1033 global $wp_scripts;
1034
1035 // Add "wp-hooks" as dependency of "heartbeat".
1036 $heartbeat_script = $wp_scripts->query( 'heartbeat', 'registered' );
1037 if ( $heartbeat_script && ! in_array( 'wp-hooks', $heartbeat_script->deps ) ) {
1038 $heartbeat_script->deps[] = 'wp-hooks';
1039 }
1040
1041 // Enqueue heartbeat separately as an "optional" dependency of the editor.
1042 // Heartbeat is used for automatic nonce refreshing, but some hosts choose
1043 // to disable it outright.
1044 wp_enqueue_script( 'heartbeat' );
1045
1046 // Transform a "heartbeat-tick" jQuery event into "heartbeat.tick" hook action.
1047 // This removes the need of using jQuery for listening to the event.
1048 wp_add_inline_script(
1049 'heartbeat',
1050 'jQuery( document ).on( "heartbeat-tick", function ( event, response ) { wp.hooks.doAction( "heartbeat.tick", response ) } );',
1051 'after'
1052 );
1053
1054 // Ignore Classic Editor's `rich_editing` user option, aka "Disable visual
1055 // editor". Forcing this to be true guarantees that TinyMCE and its plugins
1056 // are available in Gutenberg. Fixes
1057 // https://github.com/WordPress/gutenberg/issues/5667.
1058 $user_can_richedit = user_can_richedit();
1059 add_filter( 'user_can_richedit', '__return_true' );
1060
1061 wp_enqueue_script( 'wp-edit-post' );
1062 wp_enqueue_script( 'wp-format-library' );
1063 wp_enqueue_style( 'wp-format-library' );
1064
1065 global $post;
1066
1067 // Set initial title to empty string for auto draft for duration of edit.
1068 // Otherwise, title defaults to and displays as "Auto Draft".
1069 $is_new_post = 'auto-draft' === $post->post_status;
1070
1071 // Set the post type name.
1072 $post_type = get_post_type( $post );
1073 $post_type_object = get_post_type_object( $post_type );
1074 $rest_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
1075
1076 $preload_paths = array(
1077 '/',
1078 '/wp/v2/types?context=edit',
1079 '/wp/v2/taxonomies?per_page=-1&context=edit',
1080 '/wp/v2/themes?status=active',
1081 sprintf( '/wp/v2/%s/%s?context=edit', $rest_base, $post->ID ),
1082 sprintf( '/wp/v2/types/%s?context=edit', $post_type ),
1083 sprintf( '/wp/v2/users/me?post_type=%s&context=edit', $post_type ),
1084 array( '/wp/v2/media', 'OPTIONS' ),
1085 );
1086
1087 /**
1088 * Preload common data by specifying an array of REST API paths that will be preloaded.
1089 *
1090 * Filters the array of paths that will be preloaded.
1091 *
1092 * @param array $preload_paths Array of paths to preload
1093 * @param object $post The post resource data.
1094 */
1095 $preload_paths = apply_filters( 'block_editor_preload_paths', $preload_paths, $post );
1096
1097 // Ensure the global $post remains the same after
1098 // API data is preloaded. Because API preloading
1099 // can call the_content and other filters, callbacks
1100 // can unexpectedly modify $post resulting in issues
1101 // like https://github.com/WordPress/gutenberg/issues/7468.
1102 $backup_global_post = $post;
1103
1104 $preload_data = array_reduce(
1105 $preload_paths,
1106 'gutenberg_preload_api_request',
1107 array()
1108 );
1109
1110 // Restore the global $post as it was before API preloading.
1111 $post = $backup_global_post;
1112
1113 wp_add_inline_script(
1114 'wp-api-fetch',
1115 sprintf( 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', wp_json_encode( $preload_data ) ),
1116 'after'
1117 );
1118
1119 wp_add_inline_script(
1120 'wp-blocks',
1121 sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( gutenberg_get_block_categories( $post ) ) ),
1122 'after'
1123 );
1124
1125 // Assign initial edits, if applicable. These are not initially assigned
1126 // to the persisted post, but should be included in its save payload.
1127 if ( $is_new_post && $is_demo ) {
1128 // Prepopulate with some test content in demo.
1129 ob_start();
1130 include gutenberg_dir_path() . 'post-content.php';
1131 $demo_content = ob_get_clean();
1132
1133 $initial_edits = array(
1134 'title' => __( 'Welcome to the Gutenberg Editor', 'gutenberg' ),
1135 'content' => $demo_content,
1136 );
1137 } elseif ( $is_new_post ) {
1138 // Override "(Auto Draft)" new post default title with empty string,
1139 // or filtered value.
1140 $initial_edits = array(
1141 'title' => $post->post_title,
1142 'content' => $post->post_content,
1143 'excerpt' => $post->post_excerpt,
1144 );
1145 } else {
1146 $initial_edits = null;
1147 }
1148
1149 gutenberg_load_locale_data();
1150
1151 // Preload server-registered block schemas.
1152 wp_add_inline_script(
1153 'wp-blocks',
1154 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . json_encode( gutenberg_prepare_blocks_for_js() ) . ');'
1155 );
1156
1157 // Get admin url for handling meta boxes.
1158 $meta_box_url = admin_url( 'post.php' );
1159 $meta_box_url = add_query_arg(
1160 array(
1161 'post' => $post->ID,
1162 'action' => 'edit',
1163 'classic-editor' => true,
1164 'meta_box' => true,
1165 ),
1166 $meta_box_url
1167 );
1168 wp_localize_script( 'wp-editor', '_wpMetaBoxUrl', $meta_box_url );
1169
1170 // Initialize the editor.
1171 $gutenberg_theme_support = get_theme_support( 'gutenberg' );
1172 $align_wide = get_theme_support( 'align-wide' );
1173 $color_palette = current( (array) get_theme_support( 'editor-color-palette' ) );
1174 $font_sizes = current( (array) get_theme_support( 'editor-font-sizes' ) );
1175
1176 /**
1177 * Filters the allowed block types for the editor, defaulting to true (all
1178 * block types supported).
1179 *
1180 * @param bool|array $allowed_block_types Array of block type slugs, or
1181 * boolean to enable/disable all.
1182 * @param object $post The post resource data.
1183 */
1184 $allowed_block_types = apply_filters( 'allowed_block_types', true, $post );
1185
1186 // Get all available templates for the post/page attributes meta-box.
1187 // The "Default template" array element should only be added if the array is
1188 // not empty so we do not trigger the template select element without any options
1189 // besides the default value.
1190 $available_templates = wp_get_theme()->get_page_templates( get_post( $post->ID ) );
1191 $available_templates = ! empty( $available_templates ) ? array_merge(
1192 array(
1193 '' => apply_filters( 'default_page_template_title', __( 'Default template', 'gutenberg' ), 'rest-api' ),
1194 ),
1195 $available_templates
1196 ) : $available_templates;
1197
1198 // Media settings.
1199 $max_upload_size = wp_max_upload_size();
1200 if ( ! $max_upload_size ) {
1201 $max_upload_size = 0;
1202 }
1203
1204 // Editor Styles.
1205 global $editor_styles;
1206 $styles = array(
1207 array(
1208 'css' => file_get_contents(
1209 gutenberg_dir_path() . 'build/editor/editor-styles.css'
1210 ),
1211 ),
1212 );
1213
1214 /* Translators: Use this to specify the CSS font family for the default font */
1215 $locale_font_family = esc_html_x( 'Noto Serif', 'CSS Font Family for Editor Font', 'gutenberg' );
1216 $styles[] = array(
1217 'css' => "body { font-family: '$locale_font_family' }",
1218 );
1219
1220 if ( $editor_styles && current_theme_supports( 'editor-styles' ) ) {
1221 foreach ( $editor_styles as $style ) {
1222 if ( filter_var( $style, FILTER_VALIDATE_URL ) ) {
1223 $styles[] = array(
1224 'css' => file_get_contents( $style ),
1225 );
1226 } else {
1227 $file = get_theme_file_path( $style );
1228 if ( file_exists( $file ) ) {
1229 $styles[] = array(
1230 'css' => file_get_contents( $file ),
1231 'baseURL' => get_theme_file_uri( $style ),
1232 );
1233 }
1234 }
1235 }
1236 }
1237
1238 // Lock settings.
1239 $user_id = wp_check_post_lock( $post->ID );
1240 if ( $user_id ) {
1241 /**
1242 * Filters whether to show the post locked dialog.
1243 *
1244 * Returning a falsey value to the filter will short-circuit displaying the dialog.
1245 *
1246 * @since 3.6.0
1247 *
1248 * @param bool $display Whether to display the dialog. Default true.
1249 * @param WP_Post $post Post object.
1250 * @param WP_User|bool $user The user id currently editing the post.
1251 */
1252 if ( apply_filters( 'show_post_locked_dialog', true, $post, $user_id ) ) {
1253 $locked = true;
1254 }
1255
1256 $user_details = null;
1257 if ( $locked ) {
1258 $user = get_userdata( $user_id );
1259 $user_details = array(
1260 'name' => $user->display_name,
1261 );
1262 $avatar = get_avatar( $user_id, 64 );
1263 if ( $avatar ) {
1264 if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) {
1265 $user_details['avatar'] = $matches[1];
1266 }
1267 }
1268 }
1269
1270 $lock_details = array(
1271 'isLocked' => $locked,
1272 'user' => $user_details,
1273 );
1274 } else {
1275
1276 // Lock the post.
1277 $active_post_lock = wp_set_post_lock( $post->ID );
1278 $lock_details = array(
1279 'isLocked' => false,
1280 'activePostLock' => esc_attr( implode( ':', $active_post_lock ) ),
1281 );
1282 }
1283
1284 $editor_settings = array(
1285 'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), // Backcompat. Use `align-wide` outside of `gutenberg` array.
1286 'availableTemplates' => $available_templates,
1287 'allowedBlockTypes' => $allowed_block_types,
1288 'disableCustomColors' => get_theme_support( 'disable-custom-colors' ),
1289 'disableCustomFontSizes' => get_theme_support( 'disable-custom-font-sizes' ),
1290 'disablePostFormats' => ! current_theme_supports( 'post-formats' ),
1291 'titlePlaceholder' => apply_filters( 'enter_title_here', __( 'Add title', 'gutenberg' ), $post ),
1292 'bodyPlaceholder' => apply_filters( 'write_your_story', __( 'Start writing or type / to choose a block', 'gutenberg' ), $post ),
1293 'isRTL' => is_rtl(),
1294 'autosaveInterval' => 10,
1295 'maxUploadFileSize' => $max_upload_size,
1296 'allowedMimeTypes' => get_allowed_mime_types(),
1297 'styles' => $styles,
1298 'imageSizes' => gutenberg_get_available_image_sizes(),
1299 'richEditingEnabled' => $user_can_richedit,
1300
1301 // Ideally, we'd remove this and rely on a REST API endpoint.
1302 'postLock' => $lock_details,
1303 'postLockUtils' => array(
1304 'nonce' => wp_create_nonce( 'lock-post_' . $post->ID ),
1305 'unlockNonce' => wp_create_nonce( 'update-post_' . $post->ID ),
1306 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
1307 ),
1308
1309 // Whether or not to load the 'postcustom' meta box is stored as a user meta
1310 // field so that we're not always loading its assets.
1311 'enableCustomFields' => (bool) get_user_meta( get_current_user_id(), 'enable_custom_fields', true ),
1312 );
1313
1314 $post_autosave = gutenberg_get_autosave_newer_than_post_save( $post );
1315 if ( $post_autosave ) {
1316 $editor_settings['autosave'] = array(
1317 'editLink' => add_query_arg( 'gutenberg', true, get_edit_post_link( $post_autosave->ID ) ),
1318 );
1319 }
1320
1321 if ( false !== $color_palette ) {
1322 $editor_settings['colors'] = $color_palette;
1323 }
1324
1325 if ( ! empty( $font_sizes ) ) {
1326 $editor_settings['fontSizes'] = $font_sizes;
1327 }
1328
1329 if ( ! empty( $post_type_object->template ) ) {
1330 $editor_settings['template'] = $post_type_object->template;
1331 $editor_settings['templateLock'] = ! empty( $post_type_object->template_lock ) ? $post_type_object->template_lock : false;
1332 }
1333
1334 $init_script = <<<JS
1335 ( function() {
1336 window._wpLoadGutenbergEditor = new Promise( function( resolve ) {
1337 wp.domReady( function() {
1338 resolve( wp.editPost.initializeEditor( 'editor', "%s", %d, %s, %s ) );
1339 } );
1340 } );
1341 } )();
1342 JS;
1343
1344 /**
1345 * Filters the settings to pass to the block editor.
1346 *
1347 * @since 3.7.0
1348 *
1349 * @param array $editor_settings Default editor settings.
1350 * @param WP_Post $post Post being edited.
1351 */
1352 $editor_settings = apply_filters( 'block_editor_settings', $editor_settings, $post );
1353
1354 $script = sprintf(
1355 $init_script,
1356 $post->post_type,
1357 $post->ID,
1358 wp_json_encode( $editor_settings ),
1359 wp_json_encode( $initial_edits )
1360 );
1361 wp_add_inline_script( 'wp-edit-post', $script );
1362
1363 /**
1364 * Scripts
1365 */
1366 wp_enqueue_media(
1367 array(
1368 'post' => $post->ID,
1369 )
1370 );
1371 wp_enqueue_editor();
1372
1373 /**
1374 * Styles
1375 */
1376 wp_enqueue_style( 'wp-edit-post' );
1377
1378 /**
1379 * Fires after block assets have been enqueued for the editing interface.
1380 *
1381 * Call `add_action` on any hook before 'admin_enqueue_scripts'.
1382 *
1383 * In the function call you supply, simply use `wp_enqueue_script` and
1384 * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
1385 *
1386 * @since 0.4.0
1387 */
1388 do_action( 'enqueue_block_editor_assets' );
1389 }
1390
1391 /**
1392 * Enqueue the reusable blocks listing page's script
1393 *
1394 * @param string $hook Screen name.
1395 */
1396 function gutenberg_load_list_reusable_blocks( $hook ) {
1397 $is_reusable_blocks_list_page = 'edit.php' === $hook && isset( $_GET['post_type'] ) && 'wp_block' === $_GET['post_type'];
1398 if ( $is_reusable_blocks_list_page ) {
1399 gutenberg_load_locale_data();
1400 wp_enqueue_script( 'wp-list-reusable-blocks' );
1401 wp_enqueue_style( 'wp-list-reusable-blocks' );
1402 }
1403 }
1404 add_action( 'admin_enqueue_scripts', 'gutenberg_load_list_reusable_blocks' );
1405