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

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

1,680 lines 48.5 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 }
92
93 /**
94 * Registers a script according to `wp_register_script`. Honors this request by
95 * deregistering any script by the same handler before registration.
96 *
97 * @since 4.1.0
98 *
99 * @param string $handle Name of the script. Should be unique.
100 * @param string $src Full URL of the script, or path of the script relative to the WordPress root directory.
101 * @param array $deps Optional. An array of registered script handles this script depends on. Default empty array.
102 * @param string|bool|null $ver Optional. String specifying script version number, if it has one, which is added to the URL
103 * as a query string for cache busting purposes. If version is set to false, a version
104 * number is automatically added equal to current installed WordPress version.
105 * If set to null, no version is added.
106 * @param bool $in_footer Optional. Whether to enqueue the script before </body> instead of in the <head>.
107 * Default 'false'.
108 */
109 function gutenberg_override_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) {
110 wp_deregister_script( $handle );
111 wp_register_script( $handle, $src, $deps, $ver, $in_footer );
112 }
113
114 /**
115 * Registers a style according to `wp_register_style`. Honors this request by
116 * deregistering any style by the same handler before registration.
117 *
118 * @since 4.1.0
119 *
120 * @param string $handle Name of the stylesheet. Should be unique.
121 * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory.
122 * @param array $deps Optional. An array of registered stylesheet handles this stylesheet depends on. Default empty array.
123 * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL
124 * as a query string for cache busting purposes. If version is set to false, a version
125 * number is automatically added equal to current installed WordPress version.
126 * If set to null, no version is added.
127 * @param string $media Optional. The media for which this stylesheet has been defined.
128 * Default 'all'. Accepts media types like 'all', 'print' and 'screen', or media queries like
129 * '(orientation: portrait)' and '(max-width: 640px)'.
130 */
131 function gutenberg_override_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) {
132 wp_deregister_style( $handle );
133 wp_register_style( $handle, $src, $deps, $ver, $media );
134 }
135
136 /**
137 * Registers common scripts and styles to be used as dependencies of the editor
138 * and plugins.
139 *
140 * @since 0.1.0
141 */
142 function gutenberg_register_scripts_and_styles() {
143 gutenberg_register_vendor_scripts();
144
145 register_tinymce_scripts();
146
147 gutenberg_override_script(
148 'wp-polyfill',
149 null,
150 array(
151 'wp-polyfill-ecmascript',
152 )
153 );
154 wp_script_add_data(
155 'wp-polyfill',
156 'data',
157 gutenberg_get_script_polyfill(
158 array(
159 '\'fetch\' in window' => 'wp-polyfill-fetch',
160 'document.contains' => 'wp-polyfill-node-contains',
161 'window.FormData && window.FormData.prototype.keys' => 'wp-polyfill-formdata',
162 'Element.prototype.matches && Element.prototype.closest' => 'wp-polyfill-element-closest',
163 )
164 )
165 );
166 gutenberg_override_script(
167 'wp-url',
168 gutenberg_url( 'build/url/index.js' ),
169 array( 'wp-polyfill' ),
170 filemtime( gutenberg_dir_path() . 'build/url/index.js' ),
171 true
172 );
173 gutenberg_override_script(
174 'wp-autop',
175 gutenberg_url( 'build/autop/index.js' ),
176 array( 'wp-polyfill' ),
177 filemtime( gutenberg_dir_path() . 'build/autop/index.js' ),
178 true
179 );
180 gutenberg_override_script(
181 'wp-wordcount',
182 gutenberg_url( 'build/wordcount/index.js' ),
183 array( 'wp-polyfill' ),
184 filemtime( gutenberg_dir_path() . 'build/wordcount/index.js' ),
185 true
186 );
187 gutenberg_override_script(
188 'wp-dom-ready',
189 gutenberg_url( 'build/dom-ready/index.js' ),
190 array( 'wp-polyfill' ),
191 filemtime( gutenberg_dir_path() . 'build/dom-ready/index.js' ),
192 true
193 );
194 gutenberg_override_script(
195 'wp-a11y',
196 gutenberg_url( 'build/a11y/index.js' ),
197 array( 'wp-dom-ready', 'wp-polyfill' ),
198 filemtime( gutenberg_dir_path() . 'build/a11y/index.js' ),
199 true
200 );
201 gutenberg_override_script(
202 'wp-hooks',
203 gutenberg_url( 'build/hooks/index.js' ),
204 array( 'wp-polyfill' ),
205 filemtime( gutenberg_dir_path() . 'build/hooks/index.js' ),
206 true
207 );
208 gutenberg_override_script(
209 'wp-i18n',
210 gutenberg_url( 'build/i18n/index.js' ),
211 array( 'wp-polyfill' ),
212 filemtime( gutenberg_dir_path() . 'build/i18n/index.js' ),
213 true
214 );
215 gutenberg_override_script(
216 'wp-is-shallow-equal',
217 gutenberg_url( 'build/is-shallow-equal/index.js' ),
218 array( 'wp-polyfill' ),
219 filemtime( gutenberg_dir_path() . 'build/is-shallow-equal/index.js' ),
220 true
221 );
222 gutenberg_override_script(
223 'wp-token-list',
224 gutenberg_url( 'build/token-list/index.js' ),
225 array( 'lodash', 'wp-polyfill' ),
226 filemtime( gutenberg_dir_path() . 'build/token-list/index.js' ),
227 true
228 );
229
230 // Editor Scripts.
231 gutenberg_override_script(
232 'wp-api-fetch',
233 gutenberg_url( 'build/api-fetch/index.js' ),
234 array( 'wp-polyfill', 'wp-hooks', 'wp-i18n', 'wp-url' ),
235 filemtime( gutenberg_dir_path() . 'build/api-fetch/index.js' ),
236 true
237 );
238 wp_add_inline_script(
239 'wp-api-fetch',
240 sprintf(
241 'wp.apiFetch.use( wp.apiFetch.createNonceMiddleware( "%s" ) );',
242 ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' )
243 ),
244 'after'
245 );
246 wp_add_inline_script(
247 'wp-api-fetch',
248 sprintf(
249 'wp.apiFetch.use( wp.apiFetch.createRootURLMiddleware( "%s" ) );',
250 esc_url_raw( get_rest_url() )
251 ),
252 'after'
253 );
254
255 gutenberg_override_script(
256 'wp-deprecated',
257 gutenberg_url( 'build/deprecated/index.js' ),
258 array( 'wp-polyfill', 'wp-hooks' ),
259 filemtime( gutenberg_dir_path() . 'build/deprecated/index.js' ),
260 true
261 );
262 gutenberg_override_script(
263 'wp-blob',
264 gutenberg_url( 'build/blob/index.js' ),
265 array( 'wp-polyfill' ),
266 filemtime( gutenberg_dir_path() . 'build/blob/index.js' ),
267 true
268 );
269 gutenberg_override_script(
270 'wp-compose',
271 gutenberg_url( 'build/compose/index.js' ),
272 array( 'lodash', 'wp-element', 'wp-is-shallow-equal', 'wp-polyfill' ),
273 filemtime( gutenberg_dir_path() . 'build/compose/index.js' ),
274 true
275 );
276 gutenberg_override_script(
277 'wp-keycodes',
278 gutenberg_url( 'build/keycodes/index.js' ),
279 array( 'lodash', 'wp-polyfill' ),
280 filemtime( gutenberg_dir_path() . 'build/keycodes/index.js' ),
281 true
282 );
283 gutenberg_override_script(
284 'wp-html-entities',
285 gutenberg_url( 'build/html-entities/index.js' ),
286 array( 'wp-polyfill' ),
287 filemtime( gutenberg_dir_path() . 'build/html-entities/index.js' ),
288 true
289 );
290 gutenberg_override_script(
291 'wp-data',
292 gutenberg_url( 'build/data/index.js' ),
293 array(
294 'lodash',
295 'wp-compose',
296 'wp-deprecated',
297 'wp-element',
298 'wp-is-shallow-equal',
299 'wp-polyfill',
300 'wp-redux-routine',
301 ),
302 filemtime( gutenberg_dir_path() . 'build/data/index.js' ),
303 true
304 );
305 wp_add_inline_script(
306 'wp-data',
307 implode(
308 "\n",
309 array(
310 '( function() {',
311 ' var userId = ' . get_current_user_ID() . ';',
312 ' var storageKey = "WP_DATA_USER_" + userId;',
313 ' wp.data',
314 ' .use( wp.data.plugins.persistence, { storageKey: storageKey } )',
315 ' .use( wp.data.plugins.asyncGenerator )',
316 ' .use( wp.data.plugins.controls );',
317 '} )()',
318 )
319 )
320 );
321 gutenberg_override_script(
322 'wp-core-data',
323 gutenberg_url( 'build/core-data/index.js' ),
324 array( 'wp-data', 'wp-api-fetch', 'wp-polyfill', 'wp-url', 'lodash' ),
325 filemtime( gutenberg_dir_path() . 'build/core-data/index.js' ),
326 true
327 );
328 gutenberg_override_script(
329 'wp-dom',
330 gutenberg_url( 'build/dom/index.js' ),
331 array( 'lodash', 'wp-polyfill', 'wp-tinymce' ),
332 filemtime( gutenberg_dir_path() . 'build/dom/index.js' ),
333 true
334 );
335 gutenberg_override_script(
336 'wp-block-serialization-default-parser',
337 gutenberg_url( 'build/block-serialization-default-parser/index.js' ),
338 array(),
339 filemtime( gutenberg_dir_path() . 'build/block-serialization-default-parser/index.js' ),
340 true
341 );
342 gutenberg_override_script(
343 'wp-block-serialization-spec-parser',
344 gutenberg_url( 'build/block-serialization-spec-parser/index.js' ),
345 array( 'wp-polyfill' ),
346 filemtime( gutenberg_dir_path() . 'build/block-serialization-spec-parser/index.js' ),
347 true
348 );
349 gutenberg_override_script(
350 'wp-shortcode',
351 gutenberg_url( 'build/shortcode/index.js' ),
352 array( 'wp-polyfill', 'lodash' ),
353 filemtime( gutenberg_dir_path() . 'build/shortcode/index.js' ),
354 true
355 );
356 gutenberg_override_script(
357 'wp-redux-routine',
358 gutenberg_url( 'build/redux-routine/index.js' ),
359 array( 'wp-polyfill' ),
360 filemtime( gutenberg_dir_path() . 'build/redux-routine/index.js' ),
361 true
362 );
363 gutenberg_override_script(
364 'wp-date',
365 gutenberg_url( 'build/date/index.js' ),
366 array( 'moment', 'wp-polyfill' ),
367 filemtime( gutenberg_dir_path() . 'build/date/index.js' ),
368 true
369 );
370 global $wp_locale;
371 wp_add_inline_script(
372 'wp-date',
373 sprintf(
374 'wp.date.setSettings( %s );',
375 wp_json_encode(
376 array(
377 'l10n' => array(
378 'locale' => get_user_locale(),
379 'months' => array_values( $wp_locale->month ),
380 'monthsShort' => array_values( $wp_locale->month_abbrev ),
381 'weekdays' => array_values( $wp_locale->weekday ),
382 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
383 'meridiem' => (object) $wp_locale->meridiem,
384 'relative' => array(
385 /* translators: %s: duration */
386 'future' => __( '%s from now', 'default' ),
387 /* translators: %s: duration */
388 'past' => __( '%s ago', 'default' ),
389 ),
390 ),
391 'formats' => array(
392 'time' => get_option( 'time_format', __( 'g:i a', 'default' ) ),
393 'date' => get_option( 'date_format', __( 'F j, Y', 'default' ) ),
394 'datetime' => __( 'F j, Y g:i a', 'default' ),
395 'datetimeAbbreviated' => __( 'M j, Y g:i a', 'default' ),
396 ),
397 'timezone' => array(
398 'offset' => get_option( 'gmt_offset', 0 ),
399 'string' => get_option( 'timezone_string', 'UTC' ),
400 ),
401 )
402 )
403 ),
404 'after'
405 );
406 gutenberg_override_script(
407 'wp-element',
408 gutenberg_url( 'build/element/index.js' ),
409 array( 'wp-polyfill', 'react', 'react-dom', 'lodash', 'wp-escape-html' ),
410 filemtime( gutenberg_dir_path() . 'build/element/index.js' ),
411 true
412 );
413 gutenberg_override_script(
414 'wp-escape-html',
415 gutenberg_url( 'build/escape-html/index.js' ),
416 array( 'wp-polyfill' ),
417 filemtime( gutenberg_dir_path() . 'build/element/index.js' ),
418 true
419 );
420 gutenberg_override_script(
421 'wp-rich-text',
422 gutenberg_url( 'build/rich-text/index.js' ),
423 array( 'wp-polyfill', 'wp-escape-html', 'lodash' ),
424 filemtime( gutenberg_dir_path() . 'build/rich-text/index.js' ),
425 true
426 );
427 gutenberg_override_script(
428 'wp-components',
429 gutenberg_url( 'build/components/index.js' ),
430 array(
431 'lodash',
432 'moment',
433 'wp-a11y',
434 'wp-api-fetch',
435 'wp-compose',
436 'wp-deprecated',
437 'wp-dom',
438 'wp-element',
439 'wp-hooks',
440 'wp-html-entities',
441 'wp-i18n',
442 'wp-is-shallow-equal',
443 'wp-keycodes',
444 'wp-polyfill',
445 'wp-url',
446 'wp-rich-text',
447 ),
448 filemtime( gutenberg_dir_path() . 'build/components/index.js' ),
449 true
450 );
451 wp_add_inline_script(
452 'wp-components',
453 sprintf( 'wp.components.unstable__setSiteURL(%s);', json_encode( site_url() ) )
454 );
455 gutenberg_override_script(
456 'wp-blocks',
457 gutenberg_url( 'build/blocks/index.js' ),
458 array(
459 'wp-autop',
460 'wp-blob',
461 'wp-block-serialization-default-parser',
462 'wp-data',
463 'wp-deprecated',
464 'wp-dom',
465 'wp-element',
466 'wp-hooks',
467 'wp-i18n',
468 'wp-is-shallow-equal',
469 'wp-polyfill',
470 'wp-shortcode',
471 'lodash',
472 ),
473 filemtime( gutenberg_dir_path() . 'build/blocks/index.js' ),
474 true
475 );
476 gutenberg_override_script(
477 'wp-viewport',
478 gutenberg_url( 'build/viewport/index.js' ),
479 array( 'wp-polyfill', 'wp-element', 'wp-data', 'wp-compose', 'lodash' ),
480 filemtime( gutenberg_dir_path() . 'build/viewport/index.js' ),
481 true
482 );
483 gutenberg_override_script(
484 'wp-block-library',
485 gutenberg_url( 'build/block-library/index.js' ),
486 array(
487 'editor',
488 'lodash',
489 'moment',
490 'wp-api-fetch',
491 'wp-autop',
492 'wp-blob',
493 'wp-blocks',
494 'wp-components',
495 'wp-compose',
496 'wp-core-data',
497 'wp-data',
498 'wp-date',
499 'wp-editor',
500 'wp-element',
501 'wp-html-entities',
502 'wp-i18n',
503 'wp-keycodes',
504 'wp-polyfill',
505 'wp-url',
506 'wp-viewport',
507 'wp-rich-text',
508 ),
509 filemtime( gutenberg_dir_path() . 'build/block-library/index.js' ),
510 true
511 );
512 gutenberg_override_script(
513 'wp-nux',
514 gutenberg_url( 'build/nux/index.js' ),
515 array(
516 'wp-element',
517 'wp-components',
518 'wp-compose',
519 'wp-data',
520 'wp-i18n',
521 'wp-polyfill',
522 'lodash',
523 ),
524 filemtime( gutenberg_dir_path() . 'build/nux/index.js' ),
525 true
526 );
527 gutenberg_override_script(
528 'wp-plugins',
529 gutenberg_url( 'build/plugins/index.js' ),
530 array( 'lodash', 'wp-compose', 'wp-element', 'wp-hooks', 'wp-polyfill' ),
531 filemtime( gutenberg_dir_path() . 'build/plugins/index.js' )
532 );
533 // Loading the old editor and its config to ensure the classic block works as expected.
534 wp_add_inline_script(
535 'editor',
536 'window.wp.oldEditor = window.wp.editor;',
537 'after'
538 );
539 $tinymce_settings = apply_filters(
540 'tiny_mce_before_init',
541 array(
542 'plugins' => implode(
543 ',',
544 array_unique(
545 apply_filters(
546 'tiny_mce_plugins',
547 array(
548 'charmap',
549 'colorpicker',
550 'hr',
551 'lists',
552 'media',
553 'paste',
554 'tabfocus',
555 'textcolor',
556 'fullscreen',
557 'wordpress',
558 'wpautoresize',
559 'wpeditimage',
560 'wpemoji',
561 'wpgallery',
562 'wplink',
563 'wpdialogs',
564 'wptextpattern',
565 'wpview',
566 )
567 )
568 )
569 ),
570 'toolbar1' => implode(
571 ',',
572 array_merge(
573 apply_filters(
574 'mce_buttons',
575 array(
576 'formatselect',
577 'bold',
578 'italic',
579 'bullist',
580 'numlist',
581 'blockquote',
582 'alignleft',
583 'aligncenter',
584 'alignright',
585 'link',
586 'unlink',
587 'wp_more',
588 'spellchecker',
589 'wp_add_media',
590 ),
591 'editor'
592 ),
593 array( 'kitchensink' )
594 )
595 ),
596 'toolbar2' => implode(
597 ',',
598 apply_filters(
599 'mce_buttons_2',
600 array(
601 'strikethrough',
602 'hr',
603 'forecolor',
604 'pastetext',
605 'removeformat',
606 'charmap',
607 'outdent',
608 'indent',
609 'undo',
610 'redo',
611 'wp_help',
612 ),
613 'editor'
614 )
615 ),
616 'toolbar3' => implode( ',', apply_filters( 'mce_buttons_3', array(), 'editor' ) ),
617 'toolbar4' => implode( ',', apply_filters( 'mce_buttons_4', array(), 'editor' ) ),
618 'external_plugins' => apply_filters( 'mce_external_plugins', array() ),
619 ),
620 'editor'
621 );
622 if ( isset( $tinymce_settings['style_formats'] ) && is_string( $tinymce_settings['style_formats'] ) ) {
623 // Decode the options as we used to recommende json_encoding the TinyMCE settings.
624 $tinymce_settings['style_formats'] = json_decode( $tinymce_settings['style_formats'] );
625 }
626 wp_localize_script(
627 'wp-block-library',
628 'wpEditorL10n',
629 array(
630 'tinymce' => array(
631 'baseURL' => includes_url( 'js/tinymce' ),
632 'suffix' => SCRIPT_DEBUG ? '' : '.min',
633 'settings' => $tinymce_settings,
634 ),
635 )
636 );
637
638 gutenberg_override_script(
639 'wp-editor',
640 gutenberg_url( 'build/editor/index.js' ),
641 array(
642 'jquery',
643 'lodash',
644 'tinymce-latest-lists',
645 'wp-a11y',
646 'wp-api-fetch',
647 'wp-blob',
648 'wp-blocks',
649 'wp-components',
650 'wp-compose',
651 'wp-core-data',
652 'wp-data',
653 'wp-date',
654 'wp-deprecated',
655 'wp-dom',
656 'wp-element',
657 'wp-hooks',
658 'wp-html-entities',
659 'wp-i18n',
660 'wp-is-shallow-equal',
661 'wp-keycodes',
662 'wp-nux',
663 'wp-polyfill',
664 'wp-tinymce',
665 'wp-token-list',
666 'wp-url',
667 'wp-viewport',
668 'wp-wordcount',
669 'wp-rich-text',
670 ),
671 filemtime( gutenberg_dir_path() . 'build/editor/index.js' )
672 );
673
674 gutenberg_override_script(
675 'wp-edit-post',
676 gutenberg_url( 'build/edit-post/index.js' ),
677 array(
678 'jquery',
679 'lodash',
680 'postbox',
681 'media-models',
682 'media-views',
683 'wp-a11y',
684 'wp-api-fetch',
685 'wp-block-library',
686 'wp-blocks',
687 'wp-components',
688 'wp-compose',
689 'wp-core-data',
690 'wp-data',
691 'wp-deprecated',
692 'wp-dom-ready',
693 'wp-editor',
694 'wp-element',
695 'wp-embed',
696 'wp-i18n',
697 'wp-keycodes',
698 'wp-nux',
699 'wp-plugins',
700 'wp-polyfill',
701 'wp-url',
702 'wp-viewport',
703 ),
704 filemtime( gutenberg_dir_path() . 'build/edit-post/index.js' ),
705 true
706 );
707
708 gutenberg_override_script(
709 'wp-list-reusable-blocks',
710 gutenberg_url( 'build/list-reusable-blocks/index.js' ),
711 array(
712 'lodash',
713 'wp-api-fetch',
714 'wp-components',
715 'wp-compose',
716 'wp-element',
717 'wp-i18n',
718 'wp-polyfill-ecmascript',
719 ),
720 filemtime( gutenberg_dir_path() . 'build/list-reusable-blocks/index.js' ),
721 true
722 );
723
724 // Editor Styles.
725 // This empty stylesheet is defined to ensure backwards compatibility.
726 gutenberg_override_style( 'wp-blocks', false );
727
728 $fonts_url = '';
729
730 /*
731 * Translators: If there are characters in your language that are not supported
732 * by Noto Serif, translate this to 'off'. Do not translate into your own language.
733 */
734 if ( 'off' !== _x( 'on', 'Noto Serif font: on or off', 'gutenberg' ) ) {
735 $query_args = array(
736 'family' => urlencode( 'Noto Serif:400,400i,700,700i' ),
737 );
738
739 $fonts_url = esc_url_raw( add_query_arg( $query_args, 'https://fonts.googleapis.com/css' ) );
740 }
741
742 gutenberg_override_style(
743 'wp-editor-font',
744 $fonts_url,
745 array(),
746 null
747 );
748
749 gutenberg_override_style(
750 'wp-editor',
751 gutenberg_url( 'build/editor/style.css' ),
752 array( 'wp-components', 'wp-editor-font', 'wp-nux' ),
753 filemtime( gutenberg_dir_path() . 'build/editor/style.css' )
754 );
755 wp_style_add_data( 'wp-editor', 'rtl', 'replace' );
756
757 gutenberg_override_style(
758 'wp-edit-post',
759 gutenberg_url( 'build/edit-post/style.css' ),
760 array( 'wp-components', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ),
761 filemtime( gutenberg_dir_path() . 'build/edit-post/style.css' )
762 );
763 wp_style_add_data( 'wp-edit-post', 'rtl', 'replace' );
764
765 gutenberg_override_style(
766 'wp-components',
767 gutenberg_url( 'build/components/style.css' ),
768 array(),
769 filemtime( gutenberg_dir_path() . 'build/components/style.css' )
770 );
771 wp_style_add_data( 'wp-components', 'rtl', 'replace' );
772
773 gutenberg_override_style(
774 'wp-block-library',
775 gutenberg_url( 'build/block-library/style.css' ),
776 current_theme_supports( 'wp-block-styles' ) ? array( 'wp-block-library-theme' ) : array(),
777 filemtime( gutenberg_dir_path() . 'build/block-library/style.css' )
778 );
779 wp_style_add_data( 'wp-block-library', 'rtl', 'replace' );
780
781 gutenberg_override_style(
782 'wp-edit-blocks',
783 gutenberg_url( 'build/block-library/editor.css' ),
784 array(
785 'wp-components',
786 'wp-editor',
787 // Always include visual styles so the editor never appears broken.
788 'wp-block-library-theme',
789 ),
790 filemtime( gutenberg_dir_path() . 'build/block-library/editor.css' )
791 );
792 wp_style_add_data( 'wp-edit-blocks', 'rtl', 'replace' );
793
794 gutenberg_override_style(
795 'wp-nux',
796 gutenberg_url( 'build/nux/style.css' ),
797 array( 'wp-components' ),
798 filemtime( gutenberg_dir_path() . 'build/nux/style.css' )
799 );
800 wp_style_add_data( 'wp-nux', 'rtl', 'replace' );
801
802 gutenberg_override_style(
803 'wp-block-library-theme',
804 gutenberg_url( 'build/block-library/theme.css' ),
805 array(),
806 filemtime( gutenberg_dir_path() . 'build/block-library/theme.css' )
807 );
808 wp_style_add_data( 'wp-block-library-theme', 'rtl', 'replace' );
809
810 gutenberg_override_style(
811 'wp-list-reusable-blocks',
812 gutenberg_url( 'build/list-reusable-blocks/style.css' ),
813 array( 'wp-components' ),
814 filemtime( gutenberg_dir_path() . 'build/list-reusable-blocks/style.css' )
815 );
816 wp_style_add_data( 'wp-list-reusable-block', 'rtl', 'replace' );
817
818 if ( defined( 'GUTENBERG_LIVE_RELOAD' ) && GUTENBERG_LIVE_RELOAD ) {
819 $live_reload_url = ( GUTENBERG_LIVE_RELOAD === true ) ? 'http://localhost:35729/livereload.js' : GUTENBERG_LIVE_RELOAD;
820
821 wp_enqueue_script(
822 'gutenberg-live-reload',
823 $live_reload_url
824 );
825 }
826 }
827 add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
828 add_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
829
830 /**
831 * Append result of internal request to REST API for purpose of preloading
832 * data to be attached to the page. Expected to be called in the context of
833 * `array_reduce`.
834 *
835 * @param array $memo Reduce accumulator.
836 * @param string $path REST API path to preload.
837 * @return array Modified reduce accumulator.
838 */
839 function gutenberg_preload_api_request( $memo, $path ) {
840
841 // array_reduce() doesn't support passing an array in PHP 5.2
842 // so we need to make sure we start with one.
843 if ( ! is_array( $memo ) ) {
844 $memo = array();
845 }
846
847 if ( empty( $path ) ) {
848 return $memo;
849 }
850
851 $path_parts = parse_url( $path );
852 if ( false === $path_parts ) {
853 return $memo;
854 }
855
856 $request = new WP_REST_Request( 'GET', $path_parts['path'] );
857 if ( ! empty( $path_parts['query'] ) ) {
858 parse_str( $path_parts['query'], $query_params );
859 $request->set_query_params( $query_params );
860 }
861
862 $response = rest_do_request( $request );
863 if ( 200 === $response->status ) {
864 $server = rest_get_server();
865 $data = (array) $response->get_data();
866 if ( method_exists( $server, 'get_compact_response_links' ) ) {
867 $links = call_user_func( array( $server, 'get_compact_response_links' ), $response );
868 } else {
869 $links = call_user_func( array( $server, 'get_response_links' ), $response );
870 }
871 if ( ! empty( $links ) ) {
872 $data['_links'] = $links;
873 }
874
875 $memo[ $path ] = array(
876 'body' => $data,
877 'headers' => $response->headers,
878 );
879 }
880
881 return $memo;
882 }
883
884 /**
885 * Registers vendor JavaScript files to be used as dependencies of the editor
886 * and plugins.
887 *
888 * This function is called from a script during the plugin build process, so it
889 * should not call any WordPress PHP functions.
890 *
891 * @since 0.1.0
892 */
893 function gutenberg_register_vendor_scripts() {
894 $suffix = SCRIPT_DEBUG ? '' : '.min';
895
896 // Vendor Scripts.
897 $react_suffix = ( SCRIPT_DEBUG ? '.development' : '.production' ) . $suffix;
898
899 gutenberg_register_vendor_script(
900 'react',
901 'https://unpkg.com/react@16.4.1/umd/react' . $react_suffix . '.js'
902 );
903 gutenberg_register_vendor_script(
904 'react-dom',
905 'https://unpkg.com/react-dom@16.4.1/umd/react-dom' . $react_suffix . '.js',
906 array( 'react' )
907 );
908 $moment_script = SCRIPT_DEBUG ? 'moment.js' : 'min/moment.min.js';
909 gutenberg_register_vendor_script(
910 'moment',
911 'https://unpkg.com/moment@2.22.1/' . $moment_script,
912 array()
913 );
914 $tinymce_version = '4.7.11';
915 gutenberg_register_vendor_script(
916 'tinymce-latest-lists',
917 'https://unpkg.com/tinymce@' . $tinymce_version . '/plugins/lists/plugin' . $suffix . '.js',
918 array( 'wp-tinymce' )
919 );
920 gutenberg_register_vendor_script(
921 'lodash',
922 'https://unpkg.com/lodash@4.17.5/lodash' . $suffix . '.js'
923 );
924 wp_add_inline_script( 'lodash', 'window.lodash = _.noConflict();' );
925 gutenberg_register_vendor_script(
926 'wp-polyfill-fetch',
927 'https://unpkg.com/whatwg-fetch@3.0.0/dist/fetch.umd.js'
928 );
929 gutenberg_register_vendor_script(
930 'wp-polyfill-formdata',
931 'https://unpkg.com/formdata-polyfill@3.0.9/formdata.min.js'
932 );
933 gutenberg_register_vendor_script(
934 'wp-polyfill-node-contains',
935 'https://unpkg.com/polyfill-library@3.26.0-0/polyfills/Node/prototype/contains/polyfill.js'
936 );
937 gutenberg_register_vendor_script(
938 'wp-polyfill-element-closest',
939 'https://unpkg.com/element-closest@2.0.2/element-closest.js'
940 );
941 gutenberg_register_vendor_script(
942 'wp-polyfill-ecmascript',
943 'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.0.0/polyfill' . $suffix . '.js'
944 );
945 }
946
947 /**
948 * Retrieves a unique and reasonably short and human-friendly filename for a
949 * vendor script based on a URL and the script handle.
950 *
951 * @param string $handle The name of the script.
952 * @param string $src Full URL of the external script.
953 *
954 * @return string Script filename suitable for local caching.
955 *
956 * @since 0.1.0
957 */
958 function gutenberg_vendor_script_filename( $handle, $src ) {
959 $match_tinymce_plugin = preg_match(
960 '@tinymce.*/plugins/([^/]+)/plugin(\.min)?\.js$@',
961 $src,
962 $tinymce_plugin_pieces
963 );
964 if ( $match_tinymce_plugin ) {
965 $prefix = 'tinymce-plugin-' . $tinymce_plugin_pieces[1];
966 $suffix = isset( $tinymce_plugin_pieces[2] ) ? $tinymce_plugin_pieces[2] : '';
967 } else {
968 $filename = basename( $src );
969 $match = preg_match(
970 '/^'
971 . '(?P<ignore>.*?)'
972 . '(?P<suffix>\.min)?'
973 . '(?P<extension>\.js)'
974 . '(?P<extra>.*)'
975 . '$/',
976 $filename,
977 $filename_pieces
978 );
979
980 $prefix = $handle;
981 $suffix = $match ? $filename_pieces['suffix'] : '';
982 }
983
984 $hash = substr( md5( $src ), 0, 8 );
985
986 return "${prefix}${suffix}.${hash}.js";
987 }
988
989 /**
990 * Registers a vendor script from a URL, preferring a locally cached version if
991 * possible, or downloading it if the cached version is unavailable or
992 * outdated.
993 *
994 * @param string $handle Name of the script.
995 * @param string $src Full URL of the external script.
996 * @param array $deps Optional. An array of registered script handles this
997 * script depends on.
998 *
999 * @since 0.1.0
1000 */
1001 function gutenberg_register_vendor_script( $handle, $src, $deps = array() ) {
1002 if ( defined( 'GUTENBERG_LOAD_VENDOR_SCRIPTS' ) && ! GUTENBERG_LOAD_VENDOR_SCRIPTS ) {
1003 return;
1004 }
1005
1006 $filename = gutenberg_vendor_script_filename( $handle, $src );
1007
1008 if ( defined( 'GUTENBERG_LIST_VENDOR_ASSETS' ) && GUTENBERG_LIST_VENDOR_ASSETS ) {
1009 echo "$src|$filename\n";
1010 return;
1011 }
1012
1013 $full_path = gutenberg_dir_path() . 'vendor/' . $filename;
1014
1015 $needs_fetch = (
1016 defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE && (
1017 ! file_exists( $full_path ) ||
1018 time() - filemtime( $full_path ) >= DAY_IN_SECONDS
1019 )
1020 );
1021
1022 if ( $needs_fetch ) {
1023 // Determine whether we can write to this file. If not, don't waste
1024 // time doing a network request.
1025 // @codingStandardsIgnoreStart
1026 $f = @fopen( $full_path, 'a' );
1027 // @codingStandardsIgnoreEnd
1028 if ( ! $f ) {
1029 // Failed to open the file for writing, probably due to server
1030 // permissions. Enqueue the script directly from the URL instead.
1031 gutenberg_override_script( $handle, $src, $deps, null );
1032 return;
1033 }
1034 fclose( $f );
1035 $response = wp_remote_get( $src );
1036 if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
1037 $f = fopen( $full_path, 'w' );
1038 fwrite( $f, wp_remote_retrieve_body( $response ) );
1039 fclose( $f );
1040 } elseif ( ! filesize( $full_path ) ) {
1041 // The request failed. If the file is already cached, continue to
1042 // use this file. If not, then unlink the 0 byte file, and enqueue
1043 // the script directly from the URL.
1044 gutenberg_override_script( $handle, $src, $deps, null );
1045 unlink( $full_path );
1046 return;
1047 }
1048 }
1049 gutenberg_override_script(
1050 $handle,
1051 gutenberg_url( 'vendor/' . $filename ),
1052 $deps,
1053 null
1054 );
1055 }
1056
1057 /**
1058 * Prepares server-registered blocks for JavaScript, returning an associative
1059 * array of registered block data keyed by block name. Data includes properties
1060 * of a block relevant for client registration.
1061 *
1062 * @return array An associative array of registered block data.
1063 */
1064 function gutenberg_prepare_blocks_for_js() {
1065 $block_registry = WP_Block_Type_Registry::get_instance();
1066 $blocks = array();
1067 $keys_to_pick = array( 'title', 'description', 'icon', 'category', 'keywords', 'supports', 'attributes' );
1068
1069 foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
1070 foreach ( $keys_to_pick as $key ) {
1071 if ( ! isset( $block_type->{ $key } ) ) {
1072 continue;
1073 }
1074
1075 if ( ! isset( $blocks[ $block_name ] ) ) {
1076 $blocks[ $block_name ] = array();
1077 }
1078
1079 $blocks[ $block_name ][ $key ] = $block_type->{ $key };
1080 }
1081 }
1082
1083 return $blocks;
1084 }
1085
1086 /**
1087 * Handles the enqueueing of block scripts and styles that are common to both
1088 * the editor and the front-end.
1089 *
1090 * Note: This function must remain *before*
1091 * `gutenberg_editor_scripts_and_styles` so that editor-specific stylesheets
1092 * are loaded last.
1093 *
1094 * @since 0.4.0
1095 */
1096 function gutenberg_common_scripts_and_styles() {
1097 if ( ! is_gutenberg_page() && is_admin() ) {
1098 return;
1099 }
1100
1101 // Enqueue basic styles built out of Gutenberg through `npm build`.
1102 wp_enqueue_style( 'wp-block-library' );
1103
1104 /*
1105 * Enqueue block styles built through plugins. This lives in a separate
1106 * action for a couple of reasons: (1) we want to load these assets
1107 * (usually stylesheets) in *both* frontend and editor contexts, and (2)
1108 * one day we may need to be smarter about whether assets are included
1109 * based on whether blocks are actually present on the page.
1110 */
1111
1112 /**
1113 * Fires after enqueuing block assets for both editor and front-end.
1114 *
1115 * Call `add_action` on any hook before 'wp_enqueue_scripts'.
1116 *
1117 * In the function call you supply, simply use `wp_enqueue_script` and
1118 * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
1119 *
1120 * @since 0.4.0
1121 */
1122 do_action( 'enqueue_block_assets' );
1123 }
1124 add_action( 'wp_enqueue_scripts', 'gutenberg_common_scripts_and_styles' );
1125 add_action( 'admin_enqueue_scripts', 'gutenberg_common_scripts_and_styles' );
1126
1127 /**
1128 * Enqueues registered block scripts and styles, depending on current rendered
1129 * context (only enqueuing editor scripts while in context of the editor).
1130 *
1131 * @since 2.0.0
1132 */
1133 function gutenberg_enqueue_registered_block_scripts_and_styles() {
1134 $is_editor = ( 'enqueue_block_editor_assets' === current_action() );
1135
1136 $block_registry = WP_Block_Type_Registry::get_instance();
1137 foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
1138 // Front-end styles.
1139 if ( ! empty( $block_type->style ) ) {
1140 wp_enqueue_style( $block_type->style );
1141 }
1142
1143 // Front-end script.
1144 if ( ! empty( $block_type->script ) ) {
1145 wp_enqueue_script( $block_type->script );
1146 }
1147
1148 // Editor styles.
1149 if ( $is_editor && ! empty( $block_type->editor_style ) ) {
1150 wp_enqueue_style( $block_type->editor_style );
1151 }
1152
1153 // Editor script.
1154 if ( $is_editor && ! empty( $block_type->editor_script ) ) {
1155 wp_enqueue_script( $block_type->editor_script );
1156 }
1157 }
1158 }
1159 add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' );
1160 add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' );
1161
1162 /**
1163 * Assigns a default editor template with a default block by post format, if
1164 * not otherwise assigned for a new post of type "post".
1165 *
1166 * @param array $settings Default editor settings.
1167 * @param WP_Post $post Post being edited.
1168 *
1169 * @return array Filtered block editor settings.
1170 */
1171 function gutenberg_default_post_format_template( $settings, $post ) {
1172 // Only assign template for new posts without explicitly assigned template.
1173 $is_new_post = 'auto-draft' === $post->post_status;
1174 if ( $is_new_post && ! isset( $settings['template'] ) && 'post' === $post->post_type ) {
1175 switch ( get_post_format() ) {
1176 case 'audio':
1177 $default_block_name = 'core/audio';
1178 break;
1179 case 'gallery':
1180 $default_block_name = 'core/gallery';
1181 break;
1182 case 'image':
1183 $default_block_name = 'core/image';
1184 break;
1185 case 'quote':
1186 $default_block_name = 'core/quote';
1187 break;
1188 case 'video':
1189 $default_block_name = 'core/video';
1190 break;
1191 }
1192
1193 if ( isset( $default_block_name ) ) {
1194 $settings['template'] = array( array( $default_block_name ) );
1195 }
1196 }
1197
1198 return $settings;
1199 }
1200 add_filter( 'block_editor_settings', 'gutenberg_default_post_format_template', 10, 2 );
1201
1202 /**
1203 * The code editor settings that were last captured by
1204 * gutenberg_capture_code_editor_settings().
1205 *
1206 * @var array|false
1207 */
1208 $gutenberg_captured_code_editor_settings = false;
1209
1210 /**
1211 * When passed to the wp_code_editor_settings filter, this function captures
1212 * the code editor settings given to it and then prevents
1213 * wp_enqueue_code_editor() from enqueuing any assets.
1214 *
1215 * This is a workaround until e.g. code_editor_settings() is added to Core.
1216 *
1217 * @since 2.1.0
1218 *
1219 * @param array $settings Code editor settings.
1220 * @return false
1221 */
1222 function gutenberg_capture_code_editor_settings( $settings ) {
1223 global $gutenberg_captured_code_editor_settings;
1224 $gutenberg_captured_code_editor_settings = $settings;
1225 return false;
1226 }
1227
1228 /**
1229 * Retrieve a stored autosave that is newer than the post save.
1230 *
1231 * Deletes autosaves that are older than the post save.
1232 *
1233 * @param WP_Post $post Post object.
1234 * @return WP_Post|boolean The post autosave. False if none found.
1235 */
1236 function gutenberg_get_autosave_newer_than_post_save( $post ) {
1237 // Add autosave data if it is newer and changed.
1238 $autosave = wp_get_post_autosave( $post->ID );
1239
1240 if ( ! $autosave ) {
1241 return false;
1242 }
1243
1244 // Check if the autosave is newer than the current post.
1245 if (
1246 mysql2date( 'U', $autosave->post_modified_gmt, false ) > mysql2date( 'U', $post->post_modified_gmt, false )
1247 ) {
1248 return $autosave;
1249 }
1250
1251 // If the autosave isn't newer, remove it.
1252 wp_delete_post_revision( $autosave->ID );
1253
1254 return false;
1255 }
1256
1257 /**
1258 * Returns all the block categories.
1259 *
1260 * @since 2.2.0
1261 *
1262 * @param WP_Post $post Post object.
1263 * @return Object[] Block categories.
1264 */
1265 function gutenberg_get_block_categories( $post ) {
1266 $default_categories = array(
1267 array(
1268 'slug' => 'common',
1269 'title' => __( 'Common Blocks', 'gutenberg' ),
1270 ),
1271 array(
1272 'slug' => 'formatting',
1273 'title' => __( 'Formatting', 'gutenberg' ),
1274 ),
1275 array(
1276 'slug' => 'layout',
1277 'title' => __( 'Layout Elements', 'gutenberg' ),
1278 ),
1279 array(
1280 'slug' => 'widgets',
1281 'title' => __( 'Widgets', 'gutenberg' ),
1282 ),
1283 array(
1284 'slug' => 'embed',
1285 'title' => __( 'Embeds', 'gutenberg' ),
1286 ),
1287 array(
1288 'slug' => 'reusable',
1289 'title' => __( 'Reusable Blocks', 'gutenberg' ),
1290 ),
1291 );
1292
1293 return apply_filters( 'block_categories', $default_categories, $post );
1294 }
1295
1296 /**
1297 * Scripts & Styles.
1298 *
1299 * Enqueues the needed scripts and styles when visiting the top-level page of
1300 * the Gutenberg editor.
1301 *
1302 * @since 0.1.0
1303 *
1304 * @param string $hook Screen name.
1305 */
1306 function gutenberg_editor_scripts_and_styles( $hook ) {
1307 $is_demo = isset( $_GET['gutenberg-demo'] );
1308
1309 global $wp_scripts;
1310
1311 // Add "wp-hooks" as dependency of "heartbeat".
1312 $heartbeat_script = $wp_scripts->query( 'heartbeat', 'registered' );
1313 if ( $heartbeat_script && ! in_array( 'wp-hooks', $heartbeat_script->deps ) ) {
1314 $heartbeat_script->deps[] = 'wp-hooks';
1315 }
1316
1317 // Enqueue heartbeat separately as an "optional" dependency of the editor.
1318 // Heartbeat is used for automatic nonce refreshing, but some hosts choose
1319 // to disable it outright.
1320 wp_enqueue_script( 'heartbeat' );
1321
1322 // Transform a "heartbeat-tick" jQuery event into "heartbeat.tick" hook action.
1323 // This removes the need of using jQuery for listening to the event.
1324 wp_add_inline_script(
1325 'heartbeat',
1326 'jQuery( document ).on( "heartbeat-tick", function ( event, response ) { wp.hooks.doAction( "heartbeat.tick", response ) } );',
1327 'after'
1328 );
1329
1330 // Ignore Classic Editor's `rich_editing` user option, aka "Disable visual
1331 // editor". Forcing this to be true guarantees that TinyMCE and its plugins
1332 // are available in Gutenberg. Fixes
1333 // https://github.com/WordPress/gutenberg/issues/5667.
1334 add_filter( 'user_can_richedit', '__return_true' );
1335
1336 wp_enqueue_script( 'wp-edit-post' );
1337
1338 global $post;
1339
1340 // Set initial title to empty string for auto draft for duration of edit.
1341 // Otherwise, title defaults to and displays as "Auto Draft".
1342 $is_new_post = 'auto-draft' === $post->post_status;
1343
1344 // Set the post type name.
1345 $post_type = get_post_type( $post );
1346 $post_type_object = get_post_type_object( $post_type );
1347 $rest_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name;
1348
1349 // Preload common data.
1350 $preload_paths = array(
1351 '/',
1352 '/wp/v2/types?context=edit',
1353 '/wp/v2/taxonomies?per_page=-1&context=edit',
1354 '/wp/v2/themes?status=active',
1355 sprintf( '/wp/v2/%s/%s?context=edit', $rest_base, $post->ID ),
1356 sprintf( '/wp/v2/types/%s?context=edit', $post_type ),
1357 sprintf( '/wp/v2/users/me?post_type=%s&context=edit', $post_type ),
1358 );
1359
1360 // Ensure the global $post remains the same after
1361 // API data is preloaded. Because API preloading
1362 // can call the_content and other filters, callbacks
1363 // can unexpectedly modify $post resulting in issues
1364 // like https://github.com/WordPress/gutenberg/issues/7468.
1365 $backup_global_post = $post;
1366
1367 $preload_data = array_reduce(
1368 $preload_paths,
1369 'gutenberg_preload_api_request',
1370 array()
1371 );
1372
1373 // Restore the global $post as it was before API preloading.
1374 $post = $backup_global_post;
1375
1376 wp_add_inline_script(
1377 'wp-api-fetch',
1378 sprintf( 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', wp_json_encode( $preload_data ) ),
1379 'after'
1380 );
1381
1382 wp_add_inline_script(
1383 'wp-blocks',
1384 sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( gutenberg_get_block_categories( $post ) ) ),
1385 'after'
1386 );
1387
1388 // Assign initial edits, if applicable. These are not initially assigned
1389 // to the persisted post, but should be included in its save payload.
1390 if ( $is_new_post && $is_demo ) {
1391 // Prepopulate with some test content in demo.
1392 ob_start();
1393 include gutenberg_dir_path() . 'post-content.php';
1394 $demo_content = ob_get_clean();
1395
1396 $initial_edits = array(
1397 'title' => array(
1398 'raw' => __( 'Welcome to the Gutenberg Editor', 'gutenberg' ),
1399 ),
1400 'content' => array(
1401 'raw' => $demo_content,
1402 ),
1403 );
1404 } elseif ( $is_new_post ) {
1405 // Override "(Auto Draft)" new post default title with empty string,
1406 // or filtered value.
1407 $initial_edits = array(
1408 'title' => array(
1409 'raw' => $post->post_title,
1410 ),
1411 'content' => array(
1412 'raw' => $post->post_content,
1413 ),
1414 'excerpt' => array(
1415 'raw' => $post->post_excerpt,
1416 ),
1417 );
1418 } else {
1419 $initial_edits = null;
1420 }
1421
1422 // Prepare Jed locale data.
1423 $locale_data = gutenberg_get_jed_locale_data( 'gutenberg' );
1424 wp_add_inline_script(
1425 'wp-i18n',
1426 'wp.i18n.setLocaleData( ' . json_encode( $locale_data ) . ' );'
1427 );
1428
1429 // Preload server-registered block schemas.
1430 wp_add_inline_script(
1431 'wp-blocks',
1432 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . json_encode( gutenberg_prepare_blocks_for_js() ) . ');'
1433 );
1434
1435 // Get admin url for handling meta boxes.
1436 $meta_box_url = admin_url( 'post.php' );
1437 $meta_box_url = add_query_arg(
1438 array(
1439 'post' => $post->ID,
1440 'action' => 'edit',
1441 'classic-editor' => true,
1442 'meta_box' => true,
1443 ),
1444 $meta_box_url
1445 );
1446 wp_localize_script( 'wp-editor', '_wpMetaBoxUrl', $meta_box_url );
1447
1448 // Populate default code editor settings by short-circuiting wp_enqueue_code_editor.
1449 global $gutenberg_captured_code_editor_settings;
1450 add_filter( 'wp_code_editor_settings', 'gutenberg_capture_code_editor_settings' );
1451 wp_enqueue_code_editor( array( 'type' => 'text/html' ) );
1452 remove_filter( 'wp_code_editor_settings', 'gutenberg_capture_code_editor_settings' );
1453 wp_add_inline_script(
1454 'wp-editor',
1455 sprintf(
1456 'window._wpGutenbergCodeEditorSettings = %s;',
1457 wp_json_encode( $gutenberg_captured_code_editor_settings )
1458 )
1459 );
1460
1461 // Initialize the editor.
1462 $gutenberg_theme_support = get_theme_support( 'gutenberg' );
1463 $align_wide = get_theme_support( 'align-wide' );
1464 $color_palette = current( (array) get_theme_support( 'editor-color-palette' ) );
1465 $font_sizes = current( (array) get_theme_support( 'editor-font-sizes' ) );
1466
1467 /**
1468 * Filters the allowed block types for the editor, defaulting to true (all
1469 * block types supported).
1470 *
1471 * @param bool|array $allowed_block_types Array of block type slugs, or
1472 * boolean to enable/disable all.
1473 * @param object $post The post resource data.
1474 */
1475 $allowed_block_types = apply_filters( 'allowed_block_types', true, $post );
1476
1477 // Get all available templates for the post/page attributes meta-box.
1478 // The "Default template" array element should only be added if the array is
1479 // not empty so we do not trigger the template select element without any options
1480 // besides the default value.
1481 $available_templates = wp_get_theme()->get_page_templates( get_post( $post->ID ) );
1482 $available_templates = ! empty( $available_templates ) ? array_merge(
1483 array(
1484 '' => apply_filters( 'default_page_template_title', __( 'Default template', 'gutenberg' ), 'rest-api' ),
1485 ),
1486 $available_templates
1487 ) : $available_templates;
1488
1489 // Media settings.
1490 $max_upload_size = wp_max_upload_size();
1491 if ( ! $max_upload_size ) {
1492 $max_upload_size = 0;
1493 }
1494
1495 // Editor Styles.
1496 global $editor_styles;
1497 $styles = array(
1498 array(
1499 'css' => file_get_contents(
1500 gutenberg_dir_path() . 'build/editor/editor-styles.css'
1501 ),
1502 ),
1503 );
1504 if ( $editor_styles && current_theme_supports( 'editor-styles' ) ) {
1505 foreach ( $editor_styles as $style ) {
1506 if ( filter_var( $style, FILTER_VALIDATE_URL ) ) {
1507 $styles[] = array(
1508 'css' => file_get_contents( $style ),
1509 );
1510 } else {
1511 $file = get_theme_file_path( $style );
1512 $styles[] = array(
1513 'css' => file_get_contents( get_theme_file_path( $style ) ),
1514 'baseURL' => get_theme_file_uri( $style ),
1515 );
1516 }
1517 }
1518 }
1519
1520 // Lock settings.
1521 $user_id = wp_check_post_lock( $post->ID );
1522 if ( $user_id ) {
1523 /**
1524 * Filters whether to show the post locked dialog.
1525 *
1526 * Returning a falsey value to the filter will short-circuit displaying the dialog.
1527 *
1528 * @since 3.6.0
1529 *
1530 * @param bool $display Whether to display the dialog. Default true.
1531 * @param WP_Post $post Post object.
1532 * @param WP_User|bool $user The user id currently editing the post.
1533 */
1534 if ( apply_filters( 'show_post_locked_dialog', true, $post, $user_id ) ) {
1535 $locked = true;
1536 }
1537
1538 $user_details = null;
1539 if ( $locked ) {
1540 $user = get_userdata( $user_id );
1541 $user_details = array(
1542 'name' => $user->display_name,
1543 );
1544 $avatar = get_avatar( $user_id, 64 );
1545 if ( $avatar ) {
1546 if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) {
1547 $user_details['avatar'] = $matches[1];
1548 }
1549 }
1550 }
1551
1552 $lock_details = array(
1553 'isLocked' => $locked,
1554 'user' => $user_details,
1555 );
1556 } else {
1557
1558 // Lock the post.
1559 $active_post_lock = wp_set_post_lock( $post->ID );
1560 $lock_details = array(
1561 'isLocked' => false,
1562 'activePostLock' => esc_attr( implode( ':', $active_post_lock ) ),
1563 );
1564 }
1565
1566 $editor_settings = array(
1567 'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), // Backcompat. Use `align-wide` outside of `gutenberg` array.
1568 'availableTemplates' => $available_templates,
1569 'allowedBlockTypes' => $allowed_block_types,
1570 'disableCustomColors' => get_theme_support( 'disable-custom-colors' ),
1571 'disableCustomFontSizes' => get_theme_support( 'disable-custom-font-sizes' ),
1572 'disablePostFormats' => ! current_theme_supports( 'post-formats' ),
1573 'titlePlaceholder' => apply_filters( 'enter_title_here', __( 'Add title', 'gutenberg' ), $post ),
1574 'bodyPlaceholder' => apply_filters( 'write_your_story', __( 'Write your story', 'gutenberg' ), $post ),
1575 'isRTL' => is_rtl(),
1576 'autosaveInterval' => 10,
1577 'maxUploadFileSize' => $max_upload_size,
1578 'allowedMimeTypes' => get_allowed_mime_types(),
1579 'styles' => $styles,
1580 'postLock' => $lock_details,
1581
1582 // Ideally, we'd remove this and rely on a REST API endpoint.
1583 'postLockUtils' => array(
1584 'nonce' => wp_create_nonce( 'lock-post_' . $post->ID ),
1585 'unlockNonce' => wp_create_nonce( 'update-post_' . $post->ID ),
1586 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
1587 ),
1588 );
1589
1590 $post_autosave = gutenberg_get_autosave_newer_than_post_save( $post );
1591 if ( $post_autosave ) {
1592 $editor_settings['autosave'] = array(
1593 'editLink' => add_query_arg( 'gutenberg', true, get_edit_post_link( $post_autosave->ID ) ),
1594 );
1595 }
1596
1597 if ( false !== $color_palette ) {
1598 $editor_settings['colors'] = $color_palette;
1599 }
1600
1601 if ( ! empty( $font_sizes ) ) {
1602 $editor_settings['fontSizes'] = $font_sizes;
1603 }
1604
1605 if ( ! empty( $post_type_object->template ) ) {
1606 $editor_settings['template'] = $post_type_object->template;
1607 $editor_settings['templateLock'] = ! empty( $post_type_object->template_lock ) ? $post_type_object->template_lock : false;
1608 }
1609
1610 $init_script = <<<JS
1611 ( function() {
1612 window._wpLoadGutenbergEditor = new Promise( function( resolve ) {
1613 wp.domReady( function() {
1614 resolve( wp.editPost.initializeEditor( 'editor', "%s", %d, %s, %s ) );
1615 } );
1616 } );
1617 } )();
1618 JS;
1619
1620 /**
1621 * Filters the settings to pass to the block editor.
1622 *
1623 * @since 3.7.0
1624 *
1625 * @param array $editor_settings Default editor settings.
1626 * @param WP_Post $post Post being edited.
1627 */
1628 $editor_settings = apply_filters( 'block_editor_settings', $editor_settings, $post );
1629
1630 $script = sprintf(
1631 $init_script,
1632 $post->post_type,
1633 $post->ID,
1634 wp_json_encode( $editor_settings ),
1635 wp_json_encode( $initial_edits )
1636 );
1637 wp_add_inline_script( 'wp-edit-post', $script );
1638
1639 /**
1640 * Scripts
1641 */
1642 wp_enqueue_media(
1643 array(
1644 'post' => $post->ID,
1645 )
1646 );
1647 wp_enqueue_editor();
1648
1649 /**
1650 * Styles
1651 */
1652 wp_enqueue_style( 'wp-edit-post' );
1653
1654 /**
1655 * Fires after block assets have been enqueued for the editing interface.
1656 *
1657 * Call `add_action` on any hook before 'admin_enqueue_scripts'.
1658 *
1659 * In the function call you supply, simply use `wp_enqueue_script` and
1660 * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
1661 *
1662 * @since 0.4.0
1663 */
1664 do_action( 'enqueue_block_editor_assets' );
1665 }
1666
1667 /**
1668 * Enqueue the reusable blocks listing page's script
1669 *
1670 * @param string $hook Screen name.
1671 */
1672 function gutenberg_load_list_reusable_blocks( $hook ) {
1673 $is_reusable_blocks_list_page = 'edit.php' === $hook && isset( $_GET['post_type'] ) && 'wp_block' === $_GET['post_type'];
1674 if ( $is_reusable_blocks_list_page ) {
1675 wp_enqueue_script( 'wp-list-reusable-blocks' );
1676 wp_enqueue_style( 'wp-list-reusable-blocks' );
1677 }
1678 }
1679 add_action( 'admin_enqueue_scripts', 'gutenberg_load_list_reusable_blocks' );
1680