PluginProbe
Gutenberg / 4.0.1
Gutenberg v4.0.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.0.1, at lib/client-assets.php

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