PluginProbe
Gutenberg / 12.3.2
Gutenberg v12.3.2
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.3.2, at lib/blocks.php

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