PluginProbe
Gutenberg / 4.4.0
Gutenberg v4.4.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.4.0, at lib/client-assets.php

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