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

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

1,406 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 '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' )
469 );
470 wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' );
471
472 gutenberg_override_style(
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(
481 'wp-block-library-theme',
482 gutenberg_url( 'build/block-library/theme.css' ),
483 array(),
484 filemtime( gutenberg_dir_path() . 'build/block-library/theme.css' )
485 );
486 wp_style_add_data( 'wp-block-library-theme', 'rtl', 'replace' );
487
488 gutenberg_override_style(
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' )
493 );
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 );
522 }
523 add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
524 add_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
525
526 /**
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`.
530 *
531 * @param array $memo Reduce accumulator.
532 * @param string $path REST API path to preload.
533 * @return array Modified reduce accumulator.
534 */
535 function gutenberg_preload_api_request( $memo, $path ) {
536
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();
541 }
542
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';
554 }
555 }
556
557 $path_parts = parse_url( $path );
558 if ( false === $path_parts ) {
559 return $memo;
560 }
561
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 );
566 }
567
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;
575 }
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 );
589 }
590 }
591
592 return $memo;
593 }
594
595 /**
596 * Registers vendor JavaScript files to be used as dependencies of the editor
597 * and plugins.
598 *
599 * This function is called from a script during the plugin build process, so it
600 * should not call any WordPress PHP functions.
601 *
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.
656 *
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
663 */
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 );
685
686 $prefix = $handle;
687 $suffix = $match ? $filename_pieces['suffix'] : '';
688 }
689
690 $hash = substr( md5( $src ), 0, 8 );
691
692 return "${prefix}${suffix}.${hash}.js";
693 }
694
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;
710 }
711
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;
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 }
754 }
755 gutenberg_override_script(
756 $handle,
757 gutenberg_url( 'vendor/' . $filename ),
758 $deps,
759 null
760 );
761 }
762
763 /**
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.
767 *
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.
795 *
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
801 */
802 function gutenberg_common_scripts_and_styles() {
803 if ( ! is_gutenberg_page() && is_admin() ) {
804 return;
805 }
806
807 // Enqueue basic styles built out of Gutenberg through `npm build`.
808 wp_enqueue_style( 'wp-block-library' );
809
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 );
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 }
863 }
864 }
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' );
867
868 /**
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.
876 */
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 }
898
899 if ( isset( $default_block_name ) ) {
900 $settings['template'] = array( array( $default_block_name ) );
901 }
902 }
903
904 return $settings;
905 }
906 add_filter( 'block_editor_settings', 'gutenberg_default_post_format_template', 10, 2 );
907
908 /**
909 * Retrieve a stored autosave that is newer than the post save.
910 *
911 * Deletes autosaves that are older than the post save.
912 *
913 * @param WP_Post $post Post object.
914 * @return WP_Post|boolean The post autosave. False if none found.
915 */
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;
935 }
936
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 );
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' );
1406