PluginProbe
Gutenberg / 4.9.0
Gutenberg v4.9.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.9.0, at lib/client-assets.php

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