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

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

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