PluginProbe
Gutenberg / 12.0.0
Gutenberg v12.0.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 / blocks.php

blocks.php in Gutenberg 12.0.0, at lib/blocks.php

679 lines 21.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Block and style registration functions.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Substitutes the implementation of a core-registered block type, if exists,
10 * with the built result from the plugin.
11 */
12 function gutenberg_reregister_core_block_types() {
13 // Blocks directory may not exist if working from a fresh clone.
14 $blocks_dirs = array(
15 __DIR__ . '/../build/block-library/blocks/' => array(
16 'block_folders' => array(
17 'audio',
18 'button',
19 'buttons',
20 'freeform',
21 'code',
22 'column',
23 'columns',
24 'comments-query-loop',
25 'cover',
26 'gallery',
27 'group',
28 'heading',
29 'html',
30 'home-link',
31 'image',
32 'list',
33 'media-text',
34 'missing',
35 'more',
36 'navigation-area',
37 'navigation-link',
38 'navigation-submenu',
39 'nextpage',
40 'pattern',
41 'paragraph',
42 'preformatted',
43 'pullquote',
44 'quote',
45 'separator',
46 'social-links',
47 'spacer',
48 'table',
49 // 'table-of-contents',
50 'text-columns',
51 'verse',
52 'video',
53 'embed',
54 ),
55 'block_names' => array(
56 'archives.php' => 'core/archives',
57 'block.php' => 'core/block',
58 'calendar.php' => 'core/calendar',
59 'categories.php' => 'core/categories',
60 'comment-author-avatar.php' => 'core/comment-author-avatar',
61 'comment-author-name.php' => 'core/comment-author-name',
62 'comment-content.php' => 'core/comment-content',
63 'comment-date.php' => 'core/comment-date',
64 'comment-edit-link.php' => 'core/comment-edit-link',
65 'comment-reply-link.php' => 'core/comment-reply-link',
66 'comment-template.php' => 'core/comment-template',
67 'file.php' => 'core/file',
68 'home-link.php' => 'core/home-link',
69 'image.php' => 'core/image',
70 'gallery.php' => 'core/gallery',
71 'latest-comments.php' => 'core/latest-comments',
72 'latest-posts.php' => 'core/latest-posts',
73 'loginout.php' => 'core/loginout',
74 'navigation.php' => 'core/navigation',
75 'navigation-area.php' => 'core/navigation-area',
76 'navigation-link.php' => 'core/navigation-link',
77 'navigation-submenu.php' => 'core/navigation-submenu',
78 'page-list.php' => 'core/page-list',
79 'pattern.php' => 'core/pattern',
80 'post-author.php' => 'core/post-author',
81 'post-comment.php' => 'core/post-comment',
82 'post-comments.php' => 'core/post-comments',
83 'post-comments-count.php' => 'core/post-comments-count',
84 'post-comments-form.php' => 'core/post-comments-form',
85 'post-comments-link.php' => 'core/post-comments-link',
86 'post-content.php' => 'core/post-content',
87 'post-date.php' => 'core/post-date',
88 'post-excerpt.php' => 'core/post-excerpt',
89 'post-featured-image.php' => 'core/post-featured-image',
90 'post-navigation-link.php' => 'core/post-navigation-link',
91 'post-terms.php' => 'core/post-terms',
92 'post-title.php' => 'core/post-title',
93 'query.php' => 'core/query',
94 'post-template.php' => 'core/post-template',
95 'query-pagination.php' => 'core/query-pagination',
96 'query-pagination-next.php' => 'core/query-pagination-next',
97 'query-pagination-numbers.php' => 'core/query-pagination-numbers',
98 'query-pagination-previous.php' => 'core/query-pagination-previous',
99 'query-title.php' => 'core/query-title',
100 'rss.php' => 'core/rss',
101 'search.php' => 'core/search',
102 'shortcode.php' => 'core/shortcode',
103 'social-link.php' => 'core/social-link',
104 'site-logo.php' => 'core/site-logo',
105 'site-tagline.php' => 'core/site-tagline',
106 'site-title.php' => 'core/site-title',
107 // 'table-of-contents.php' => 'core/table-of-contents',
108 'tag-cloud.php' => 'core/tag-cloud',
109 'template-part.php' => 'core/template-part',
110 'term-description.php' => 'core/term-description',
111 ),
112 ),
113 __DIR__ . '/../build/edit-widgets/blocks/' => array(
114 'block_folders' => array(
115 'widget-area',
116 ),
117 'block_names' => array(),
118 ),
119 __DIR__ . '/../build/widgets/blocks/' => array(
120 'block_folders' => array(
121 'legacy-widget',
122 'widget-group',
123 ),
124 'block_names' => array(
125 'legacy-widget.php' => 'core/legacy-widget',
126 'widget-group.php' => 'core/widget-group',
127 ),
128 ),
129 );
130 foreach ( $blocks_dirs as $blocks_dir => $details ) {
131 $block_folders = $details['block_folders'];
132 $block_names = $details['block_names'];
133
134 $registry = WP_Block_Type_Registry::get_instance();
135
136 foreach ( $block_folders as $folder_name ) {
137 $block_json_file = $blocks_dir . $folder_name . '/block.json';
138
139 // Ideally, all paths to block metadata files should be listed in
140 // WordPress core. In this place we should rather use filter
141 // to replace paths with overrides defined by the plugin.
142 $metadata = json_decode( file_get_contents( $block_json_file ), true );
143 if ( ! is_array( $metadata ) || ! $metadata['name'] ) {
144 return false;
145 }
146
147 if ( $registry->is_registered( $metadata['name'] ) ) {
148 $registry->unregister( $metadata['name'] );
149 }
150
151 gutenberg_register_core_block_assets( $folder_name );
152 register_block_type_from_metadata( $block_json_file );
153 }
154
155 foreach ( $block_names as $file => $sub_block_names ) {
156 if ( ! file_exists( $blocks_dir . $file ) ) {
157 return;
158 }
159
160 $sub_block_names_normalized = is_string( $sub_block_names ) ? array( $sub_block_names ) : $sub_block_names;
161 foreach ( $sub_block_names_normalized as $block_name ) {
162 if ( $registry->is_registered( $block_name ) ) {
163 $registry->unregister( $block_name );
164 }
165 gutenberg_register_core_block_assets( $block_name );
166 }
167
168 require_once $blocks_dir . $file;
169 }
170 }
171 }
172
173 add_action( 'init', 'gutenberg_reregister_core_block_types' );
174
175 /**
176 * Registers block styles for a core block.
177 *
178 * @param string $block_name The block-name.
179 *
180 * @return void
181 */
182 function gutenberg_register_core_block_assets( $block_name ) {
183 if ( ! wp_should_load_separate_core_block_assets() ) {
184 return;
185 }
186
187 $block_name = str_replace( 'core/', '', $block_name );
188
189 // When in production, use the plugin's version as the default asset version;
190 // else (for development or test) default to use the current time.
191 $default_version = defined( 'GUTENBERG_VERSION' ) && ! ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? GUTENBERG_VERSION : time();
192
193 $style_path = "build/block-library/blocks/$block_name/style.css";
194 $editor_style_path = "build/block-library/blocks/$block_name/style-editor.css";
195
196 if ( file_exists( gutenberg_dir_path() . $style_path ) ) {
197 wp_deregister_style( "wp-block-{$block_name}" );
198 wp_register_style(
199 "wp-block-{$block_name}",
200 gutenberg_url( $style_path ),
201 array(),
202 $default_version
203 );
204 wp_style_add_data( "wp-block-{$block_name}", 'rtl', 'replace' );
205
206 // Add a reference to the stylesheet's path to allow calculations for inlining styles in `wp_head`.
207 wp_style_add_data( "wp-block-{$block_name}", 'path', gutenberg_dir_path() . $style_path );
208 } else {
209 wp_register_style( "wp-block-{$block_name}", false );
210 }
211
212 // If the current theme supports wp-block-styles, dequeue the full stylesheet
213 // and instead attach each block's theme-styles to their block styles stylesheet.
214 if ( current_theme_supports( 'wp-block-styles' ) ) {
215
216 // Dequeue the full stylesheet.
217 // Make sure this only runs once, it doesn't need to run for every block.
218 static $stylesheet_removed;
219 if ( ! $stylesheet_removed ) {
220 add_action(
221 'wp_enqueue_scripts',
222 function() {
223 wp_dequeue_style( 'wp-block-library-theme' );
224 }
225 );
226 $stylesheet_removed = true;
227 }
228
229 // Get the path to the block's stylesheet.
230 $theme_style_path = is_rtl()
231 ? "build/block-library/blocks/$block_name/theme-rtl.css"
232 : "build/block-library/blocks/$block_name/theme.css";
233
234 // If the file exists, enqueue it.
235 if ( file_exists( gutenberg_dir_path() . $theme_style_path ) ) {
236
237 if ( file_exists( gutenberg_dir_path() . $style_path ) ) {
238 // If there is a main stylesheet for this block, append the theme styles to main styles.
239 wp_add_inline_style(
240 "wp-block-{$block_name}",
241 file_get_contents( gutenberg_dir_path() . $theme_style_path )
242 );
243 } else {
244 // If there is no main stylesheet for this block, register theme style.
245 wp_register_style(
246 "wp-block-{$block_name}",
247 gutenberg_url( $theme_style_path ),
248 array(),
249 $default_version
250 );
251 wp_style_add_data( "wp-block-{$block_name}", 'path', gutenberg_dir_path() . $theme_style_path );
252 }
253 }
254 }
255
256 if ( file_exists( gutenberg_dir_path() . $editor_style_path ) ) {
257 wp_deregister_style( "wp-block-{$block_name}-editor" );
258 wp_register_style(
259 "wp-block-{$block_name}-editor",
260 gutenberg_url( $editor_style_path ),
261 array(),
262 $default_version
263 );
264 wp_style_add_data( "wp-block-{$block_name}-editor", 'rtl', 'replace' );
265 } else {
266 wp_register_style( "wp-block-{$block_name}-editor", false );
267 }
268 }
269
270 /**
271 * Change the way styles get loaded depending on their size.
272 *
273 * Optimizes performance and sustainability of styles by inlining smaller stylesheets.
274 *
275 * @todo Remove this function when the minimum supported version is WordPress 5.8.
276 *
277 * @return void
278 */
279 function gutenberg_maybe_inline_styles() {
280
281 // Early exit if the "wp_maybe_inline_styles" function exists.
282 if ( function_exists( 'wp_maybe_inline_styles' ) ) {
283 return;
284 }
285
286 $total_inline_limit = 20000;
287 /**
288 * The maximum size of inlined styles in bytes.
289 *
290 * @param int $total_inline_limit The file-size threshold, in bytes. Defaults to 20000.
291 * @return int The file-size threshold, in bytes.
292 */
293 $total_inline_limit = apply_filters( 'styles_inline_size_limit', $total_inline_limit );
294
295 global $wp_styles;
296 $styles = array();
297
298 // Build an array of styles that have a path defined.
299 foreach ( $wp_styles->queue as $handle ) {
300 if ( wp_styles()->get_data( $handle, 'path' ) && file_exists( $wp_styles->registered[ $handle ]->extra['path'] ) ) {
301 $styles[] = array(
302 'handle' => $handle,
303 'src' => $wp_styles->registered[ $handle ]->src,
304 'path' => $wp_styles->registered[ $handle ]->extra['path'],
305 'size' => filesize( $wp_styles->registered[ $handle ]->extra['path'] ),
306 );
307 }
308 }
309
310 if ( ! empty( $styles ) ) {
311 // Reorder styles array based on size.
312 usort(
313 $styles,
314 function( $a, $b ) {
315 return ( $a['size'] <= $b['size'] ) ? -1 : 1;
316 }
317 );
318
319 /**
320 * The total inlined size.
321 *
322 * On each iteration of the loop, if a style gets added inline the value of this var increases
323 * to reflect the total size of inlined styles.
324 */
325 $total_inline_size = 0;
326
327 // Loop styles.
328 foreach ( $styles as $style ) {
329
330 // Size check. Since styles are ordered by size, we can break the loop.
331 if ( $total_inline_size + $style['size'] > $total_inline_limit ) {
332 break;
333 }
334
335 // Get the styles if we don't already have them.
336 $style['css'] = file_get_contents( $style['path'] );
337
338 // Check if the style contains relative URLs that need to be modified.
339 // URLs relative to the stylesheet's path should be converted to relative to the site's root.
340 $style['css'] = _wp_normalize_relative_css_links( $style['css'], $style['src'] );
341
342 // Set `src` to `false` and add styles inline.
343 $wp_styles->registered[ $style['handle'] ]->src = false;
344 if ( empty( $wp_styles->registered[ $style['handle'] ]->extra['after'] ) ) {
345 $wp_styles->registered[ $style['handle'] ]->extra['after'] = array();
346 }
347 array_unshift( $wp_styles->registered[ $style['handle'] ]->extra['after'], $style['css'] );
348
349 // Add the styles size to the $total_inline_size var.
350 $total_inline_size += (int) $style['size'];
351 }
352 }
353 }
354 // Run for styles enqueued in <head>.
355 add_action( 'wp_head', 'gutenberg_maybe_inline_styles', 1 );
356 // Run for late-loaded styles in the footer.
357 add_action( 'wp_footer', 'gutenberg_maybe_inline_styles', 1 );
358
359 if ( ! function_exists( '_wp_normalize_relative_css_links' ) ) {
360 /**
361 * Make URLs relative to the WordPress installation.
362 *
363 * @since 5.8.2
364 *
365 * @param string $css The CSS to make URLs relative to the WordPress installation.
366 * @param string $stylesheet_url The URL to the stylesheet.
367 *
368 * @return string The CSS with URLs made relative to the WordPress installation.
369 */
370 function _wp_normalize_relative_css_links( $css, $stylesheet_url ) {
371 $has_src_results = preg_match_all( '#url\s*\(\s*[\'"]?\s*([^\'"\)]+)#', $css, $src_results );
372 if ( $has_src_results ) {
373 // Loop through the URLs to find relative ones.
374 foreach ( $src_results[1] as $src_index => $src_result ) {
375 // Skip if this is an absolute URL.
376 if ( 0 === strpos( $src_result, 'http' ) || 0 === strpos( $src_result, '//' ) ) {
377 continue;
378 }
379
380 // Build the absolute URL.
381 $absolute_url = dirname( $stylesheet_url ) . '/' . $src_result;
382 $absolute_url = str_replace( '/./', '/', $absolute_url );
383 // Convert to URL related to the site root.
384 $relative_url = wp_make_link_relative( $absolute_url );
385
386 // Replace the URL in the CSS.
387 $css = str_replace(
388 $src_results[0][ $src_index ],
389 str_replace( $src_result, $relative_url, $src_results[0][ $src_index ] ),
390 $css
391 );
392 }
393 }
394
395 return $css;
396 }
397 }
398
399 /**
400 * Complements the implementation of block type `core/social-icon`, whether it
401 * be provided by core or the plugin, with derived block types for each
402 * "service" (WordPress, Twitter, etc.) supported by Social Links.
403 *
404 * This ensures backwards compatibility for any users running the Gutenberg
405 * plugin who have used Social Links prior to their conversion to block
406 * variations.
407 *
408 * This shim is INTENTIONALLY left out of core, as Social Links haven't yet
409 * landed there.
410 *
411 * @see https://github.com/WordPress/gutenberg/pull/19887
412 */
413 function gutenberg_register_legacy_social_link_blocks() {
414 $services = array(
415 'amazon',
416 'bandcamp',
417 'behance',
418 'chain',
419 'codepen',
420 'deviantart',
421 'dribbble',
422 'dropbox',
423 'etsy',
424 'facebook',
425 'feed',
426 'fivehundredpx',
427 'flickr',
428 'foursquare',
429 'goodreads',
430 'google',
431 'github',
432 'instagram',
433 'lastfm',
434 'linkedin',
435 'mail',
436 'mastodon',
437 'meetup',
438 'medium',
439 'pinterest',
440 'pocket',
441 'reddit',
442 'skype',
443 'snapchat',
444 'soundcloud',
445 'spotify',
446 'tumblr',
447 'twitch',
448 'twitter',
449 'vimeo',
450 'vk',
451 'wordpress',
452 'yelp',
453 'youtube',
454 );
455
456 foreach ( $services as $service ) {
457 register_block_type(
458 'core/social-link-' . $service,
459 array(
460 'category' => 'widgets',
461 'attributes' => array(
462 'url' => array(
463 'type' => 'string',
464 ),
465 'service' => array(
466 'type' => 'string',
467 'default' => $service,
468 ),
469 'label' => array(
470 'type' => 'string',
471 ),
472 ),
473 'render_callback' => 'gutenberg_render_block_core_social_link',
474 )
475 );
476 }
477 }
478
479 add_action( 'init', 'gutenberg_register_legacy_social_link_blocks' );
480
481 /**
482 * Filters the default block categories array to add a new one for themes.
483 *
484 * This can be removed when plugin support requires WordPress 5.8.0+.
485 *
486 * @see https://core.trac.wordpress.org/ticket/52883
487 *
488 * @param array[] $categories The list of default block categories.
489 *
490 * @return array[] Filtered block categories.
491 */
492 function gutenberg_register_theme_block_category( $categories ) {
493 foreach ( $categories as $category ) {
494 // Skip when the category is already set in WordPress core.
495 if (
496 isset( $category['slug'] ) &&
497 'theme' === $category['slug']
498 ) {
499 return $categories;
500 }
501 }
502
503 $categories[] = array(
504 'slug' => 'theme',
505 'title' => _x( 'Theme', 'block category', 'gutenberg' ),
506 'icon' => null,
507 );
508 return $categories;
509 }
510 // This can be removed when plugin support requires WordPress 5.8.0+.
511 if ( ! function_exists( 'get_default_block_categories' ) ) {
512 add_filter( 'block_categories', 'gutenberg_register_theme_block_category' );
513 }
514
515 /**
516 * Checks whether the current block type supports the feature requested.
517 *
518 * @param WP_Block_Type $block_type Block type to check for support.
519 * @param array $feature Path of the feature to check support for.
520 * @param mixed $default Fallback value for feature support, defaults to false.
521 *
522 * @return boolean Whether or not the feature is supported.
523 */
524 function gutenberg_block_has_support( $block_type, $feature, $default = false ) {
525 $block_support = $default;
526 if ( $block_type && property_exists( $block_type, 'supports' ) ) {
527 $block_support = _wp_array_get( $block_type->supports, $feature, $default );
528 }
529
530 return true === $block_support || is_array( $block_support );
531 }
532
533 /**
534 * Updates the shape of supports for declaring fontSize and lineHeight.
535 *
536 * @param array $metadata Metadata for registering a block type.
537 * @return array Metadata for registering a block type with the supports shape updated.
538 */
539 function gutenberg_migrate_old_typography_shape( $metadata ) {
540 // Temporarily disable migrations from core blocks to avoid warnings on versions older than 5.8.
541 if ( isset( $metadata['supports'] ) && false === strpos( $metadata['file'], '/wp-includes/blocks/' ) ) {
542 $typography_keys = array(
543 '__experimentalFontFamily',
544 '__experimentalFontStyle',
545 '__experimentalFontWeight',
546 '__experimentalLetterSpacing',
547 '__experimentalTextDecoration',
548 '__experimentalTextTransform',
549 'fontSize',
550 'lineHeight',
551 );
552 foreach ( $typography_keys as $typography_key ) {
553 $support_for_key = _wp_array_get( $metadata['supports'], array( $typography_key ), null );
554 if ( null !== $support_for_key ) {
555 trigger_error(
556 /* translators: %1$s: Block type, %2$s: typography supports key e.g: fontSize, lineHeight etc... */
557 sprintf( __( 'Block %1$s is declaring %2$s support on block.json under supports.%2$s. %2$s support is now declared under supports.typography.%2$s.', 'gutenberg' ), $metadata['name'], $typography_key ),
558 headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
559 );
560 _wp_array_set( $metadata['supports'], array( 'typography', $typography_key ), $support_for_key );
561 unset( $metadata['supports'][ $typography_key ] );
562 }
563 }
564 }
565 return $metadata;
566 }
567
568 if ( ! function_exists( 'wp_migrate_old_typography_shape' ) ) {
569 add_filter( 'block_type_metadata', 'gutenberg_migrate_old_typography_shape' );
570 }
571
572 if ( ! function_exists( 'wp_enqueue_block_style' ) ) {
573 /**
574 * Enqueue a stylesheet for a specific block.
575 *
576 * If the theme has opted-in to separate-styles loading,
577 * then the stylesheet will be enqueued on-render,
578 * otherwise when the block inits.
579 *
580 * @param string $block_name The block-name, including namespace.
581 * @param array $args An array of arguments [handle,src,deps,ver,media].
582 *
583 * @return void
584 */
585 function wp_enqueue_block_style( $block_name, $args ) {
586 $args = wp_parse_args(
587 $args,
588 array(
589 'handle' => '',
590 'src' => '',
591 'deps' => array(),
592 'ver' => false,
593 'media' => 'all',
594 )
595 );
596
597 /**
598 * Callback function to register and enqueue styles.
599 *
600 * @param string $content When the callback is used for the render_block filter,
601 * the content needs to be returned so the function parameter
602 * is to ensure the content exists.
603 *
604 * @return string
605 */
606 $callback = function( $content ) use ( $args ) {
607 // Register the stylesheet.
608 if ( ! empty( $args['src'] ) ) {
609 wp_register_style( $args['handle'], $args['src'], $args['deps'], $args['ver'], $args['media'] );
610 }
611
612 // Add `path` data if provided.
613 if ( isset( $args['path'] ) ) {
614 wp_style_add_data( $args['handle'], 'path', $args['path'] );
615
616 // Get the RTL file path.
617 $rtl_file_path = str_replace( '.css', '-rtl.css', $args['path'] );
618
619 // Add RTL stylesheet.
620 if ( file_exists( $rtl_file_path ) ) {
621 wp_style_add_data( $args['hanle'], 'rtl', 'replace' );
622
623 if ( is_rtl() ) {
624 wp_style_add_data( $args['handle'], 'path', $rtl_file_path );
625 }
626 }
627 }
628
629 // Enqueue the stylesheet.
630 wp_enqueue_style( $args['handle'] );
631
632 return $content;
633 };
634
635 $hook = did_action( 'wp_enqueue_scripts' ) ? 'wp_footer' : 'wp_enqueue_scripts';
636 if ( wp_should_load_separate_core_block_assets() ) {
637 $hook = "render_block_$block_name";
638 }
639
640 // Enqueue assets in the frontend.
641 add_filter( $hook, $callback );
642
643 // Enqueue assets in the editor.
644 add_action( 'enqueue_block_assets', $callback );
645 }
646 }
647
648 /**
649 * Allow multiple block styles.
650 *
651 * @param array $metadata Metadata for registering a block type.
652 *
653 * @return array
654 */
655 function gutenberg_multiple_block_styles( $metadata ) {
656 foreach ( array( 'style', 'editorStyle' ) as $key ) {
657 if ( ! empty( $metadata[ $key ] ) && is_array( $metadata[ $key ] ) ) {
658 $default_style = array_shift( $metadata[ $key ] );
659 foreach ( $metadata[ $key ] as $handle ) {
660 $args = array( 'handle' => $handle );
661 if ( 0 === strpos( $handle, 'file:' ) && isset( $metadata['file'] ) ) {
662 $style_path = remove_block_asset_path_prefix( $handle );
663 $args = array(
664 'handle' => sanitize_key( "{$metadata['name']}-{$style_path}" ),
665 'src' => plugins_url( $style_path, $metadata['file'] ),
666 );
667 }
668
669 wp_enqueue_block_style( $metadata['name'], $args );
670 }
671
672 // Only return the 1st item in the array.
673 $metadata[ $key ] = $default_style;
674 }
675 }
676 return $metadata;
677 }
678 add_filter( 'block_type_metadata', 'gutenberg_multiple_block_styles' );
679