PluginProbe
Gutenberg / 12.7.1
Gutenberg v12.7.1
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / blocks.php

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

593 lines 19.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 'group',
27 'heading',
28 'html',
29 'home-link',
30 'image',
31 'list',
32 'media-text',
33 'missing',
34 'more',
35 'navigation-area',
36 'navigation-link',
37 'navigation-submenu',
38 'nextpage',
39 'pattern',
40 'paragraph',
41 'preformatted',
42 'pullquote',
43 'quote',
44 'separator',
45 'social-links',
46 'spacer',
47 'table',
48 // 'table-of-contents',
49 'text-columns',
50 'verse',
51 'video',
52 'embed',
53 ),
54 'block_names' => array(
55 'archives.php' => 'core/archives',
56 'block.php' => 'core/block',
57 'calendar.php' => 'core/calendar',
58 'categories.php' => 'core/categories',
59 'comment-author-avatar.php' => 'core/comment-author-avatar',
60 'comment-author-name.php' => 'core/comment-author-name',
61 'comment-content.php' => 'core/comment-content',
62 'comment-date.php' => 'core/comment-date',
63 'comment-edit-link.php' => 'core/comment-edit-link',
64 'comment-reply-link.php' => 'core/comment-reply-link',
65 'comment-template.php' => 'core/comment-template',
66 'comments-pagination.php' => 'core/comments-pagination',
67 'comments-pagination-next.php' => 'core/comments-pagination-next',
68 'comments-pagination-numbers.php' => 'core/comments-pagination-numbers',
69 'comments-pagination-previous.php' => 'core/comments-pagination-previous',
70 'file.php' => 'core/file',
71 'home-link.php' => 'core/home-link',
72 'image.php' => 'core/image',
73 'gallery.php' => 'core/gallery',
74 'latest-comments.php' => 'core/latest-comments',
75 'latest-posts.php' => 'core/latest-posts',
76 'loginout.php' => 'core/loginout',
77 'navigation.php' => 'core/navigation',
78 'navigation-area.php' => 'core/navigation-area',
79 'navigation-link.php' => 'core/navigation-link',
80 'navigation-submenu.php' => 'core/navigation-submenu',
81 'page-list.php' => 'core/page-list',
82 'pattern.php' => 'core/pattern',
83 'post-author.php' => 'core/post-author',
84 'post-author-name.php' => 'core/post-author-name',
85 'post-author-biography.php' => 'core/post-author-biography',
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 'read-more.php' => 'core/read-more',
106 'rss.php' => 'core/rss',
107 'search.php' => 'core/search',
108 'shortcode.php' => 'core/shortcode',
109 'social-link.php' => 'core/social-link',
110 'site-logo.php' => 'core/site-logo',
111 'site-tagline.php' => 'core/site-tagline',
112 'site-title.php' => 'core/site-title',
113 // 'table-of-contents.php' => 'core/table-of-contents',
114 'tag-cloud.php' => 'core/tag-cloud',
115 'template-part.php' => 'core/template-part',
116 'term-description.php' => 'core/term-description',
117 ),
118 ),
119 __DIR__ . '/../build/edit-widgets/blocks/' => array(
120 'block_folders' => array(
121 'widget-area',
122 ),
123 'block_names' => array(),
124 ),
125 __DIR__ . '/../build/widgets/blocks/' => array(
126 'block_folders' => array(
127 'legacy-widget',
128 'widget-group',
129 ),
130 'block_names' => array(
131 'legacy-widget.php' => 'core/legacy-widget',
132 'widget-group.php' => 'core/widget-group',
133 ),
134 ),
135 );
136 foreach ( $blocks_dirs as $blocks_dir => $details ) {
137 $block_folders = $details['block_folders'];
138 $block_names = $details['block_names'];
139
140 $registry = WP_Block_Type_Registry::get_instance();
141
142 foreach ( $block_folders as $folder_name ) {
143 $block_json_file = $blocks_dir . $folder_name . '/block.json';
144
145 // Ideally, all paths to block metadata files should be listed in
146 // WordPress core. In this place we should rather use filter
147 // to replace paths with overrides defined by the plugin.
148 $metadata = json_decode( file_get_contents( $block_json_file ), true );
149 if ( ! is_array( $metadata ) || ! $metadata['name'] ) {
150 return false;
151 }
152
153 if ( $registry->is_registered( $metadata['name'] ) ) {
154 $registry->unregister( $metadata['name'] );
155 }
156
157 gutenberg_register_core_block_assets( $folder_name );
158 register_block_type_from_metadata( $block_json_file );
159 }
160
161 foreach ( $block_names as $file => $sub_block_names ) {
162 if ( ! file_exists( $blocks_dir . $file ) ) {
163 return;
164 }
165
166 $sub_block_names_normalized = is_string( $sub_block_names ) ? array( $sub_block_names ) : $sub_block_names;
167 foreach ( $sub_block_names_normalized as $block_name ) {
168 if ( $registry->is_registered( $block_name ) ) {
169 $registry->unregister( $block_name );
170 }
171 gutenberg_register_core_block_assets( $block_name );
172 }
173
174 require_once $blocks_dir . $file;
175 }
176 }
177 }
178
179 add_action( 'init', 'gutenberg_reregister_core_block_types' );
180
181 /**
182 * Registers block styles for a core block.
183 *
184 * @param string $block_name The block-name.
185 *
186 * @return void
187 */
188 function gutenberg_register_core_block_assets( $block_name ) {
189 if ( ! wp_should_load_separate_core_block_assets() ) {
190 return;
191 }
192
193 $block_name = str_replace( 'core/', '', $block_name );
194
195 // When in production, use the plugin's version as the default asset version;
196 // else (for development or test) default to use the current time.
197 $default_version = defined( 'GUTENBERG_VERSION' ) && ! ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? GUTENBERG_VERSION : time();
198
199 $style_path = "build/block-library/blocks/$block_name/style.css";
200 $editor_style_path = "build/block-library/blocks/$block_name/style-editor.css";
201
202 if ( file_exists( gutenberg_dir_path() . $style_path ) ) {
203 wp_deregister_style( "wp-block-{$block_name}" );
204 wp_register_style(
205 "wp-block-{$block_name}",
206 gutenberg_url( $style_path ),
207 array(),
208 $default_version
209 );
210 wp_style_add_data( "wp-block-{$block_name}", 'rtl', 'replace' );
211
212 // Add a reference to the stylesheet's path to allow calculations for inlining styles in `wp_head`.
213 wp_style_add_data( "wp-block-{$block_name}", 'path', gutenberg_dir_path() . $style_path );
214 } else {
215 wp_register_style( "wp-block-{$block_name}", false );
216 }
217
218 // If the current theme supports wp-block-styles, dequeue the full stylesheet
219 // and instead attach each block's theme-styles to their block styles stylesheet.
220 if ( current_theme_supports( 'wp-block-styles' ) ) {
221
222 // Dequeue the full stylesheet.
223 // Make sure this only runs once, it doesn't need to run for every block.
224 static $stylesheet_removed;
225 if ( ! $stylesheet_removed ) {
226 add_action(
227 'wp_enqueue_scripts',
228 function() {
229 wp_dequeue_style( 'wp-block-library-theme' );
230 }
231 );
232 $stylesheet_removed = true;
233 }
234
235 // Get the path to the block's stylesheet.
236 $theme_style_path = is_rtl()
237 ? "build/block-library/blocks/$block_name/theme-rtl.css"
238 : "build/block-library/blocks/$block_name/theme.css";
239
240 // If the file exists, enqueue it.
241 if ( file_exists( gutenberg_dir_path() . $theme_style_path ) ) {
242
243 if ( file_exists( gutenberg_dir_path() . $style_path ) ) {
244 // If there is a main stylesheet for this block, append the theme styles to main styles.
245 wp_add_inline_style(
246 "wp-block-{$block_name}",
247 file_get_contents( gutenberg_dir_path() . $theme_style_path )
248 );
249 } else {
250 // If there is no main stylesheet for this block, register theme style.
251 wp_register_style(
252 "wp-block-{$block_name}",
253 gutenberg_url( $theme_style_path ),
254 array(),
255 $default_version
256 );
257 wp_style_add_data( "wp-block-{$block_name}", 'path', gutenberg_dir_path() . $theme_style_path );
258 }
259 }
260 }
261
262 if ( file_exists( gutenberg_dir_path() . $editor_style_path ) ) {
263 wp_deregister_style( "wp-block-{$block_name}-editor" );
264 wp_register_style(
265 "wp-block-{$block_name}-editor",
266 gutenberg_url( $editor_style_path ),
267 array(),
268 $default_version
269 );
270 wp_style_add_data( "wp-block-{$block_name}-editor", 'rtl', 'replace' );
271 } else {
272 wp_register_style( "wp-block-{$block_name}-editor", false );
273 }
274 }
275
276 if ( ! function_exists( '_wp_normalize_relative_css_links' ) ) {
277 /**
278 * Make URLs relative to the WordPress installation.
279 *
280 * @since 5.8.2
281 *
282 * @param string $css The CSS to make URLs relative to the WordPress installation.
283 * @param string $stylesheet_url The URL to the stylesheet.
284 *
285 * @return string The CSS with URLs made relative to the WordPress installation.
286 */
287 function _wp_normalize_relative_css_links( $css, $stylesheet_url ) {
288 $has_src_results = preg_match_all( '#url\s*\(\s*[\'"]?\s*([^\'"\)]+)#', $css, $src_results );
289 if ( $has_src_results ) {
290 // Loop through the URLs to find relative ones.
291 foreach ( $src_results[1] as $src_index => $src_result ) {
292 // Skip if this is an absolute URL.
293 if ( 0 === strpos( $src_result, 'http' ) || 0 === strpos( $src_result, '//' ) ) {
294 continue;
295 }
296
297 // Build the absolute URL.
298 $absolute_url = dirname( $stylesheet_url ) . '/' . $src_result;
299 $absolute_url = str_replace( '/./', '/', $absolute_url );
300 // Convert to URL related to the site root.
301 $relative_url = wp_make_link_relative( $absolute_url );
302
303 // Replace the URL in the CSS.
304 $css = str_replace(
305 $src_results[0][ $src_index ],
306 str_replace( $src_result, $relative_url, $src_results[0][ $src_index ] ),
307 $css
308 );
309 }
310 }
311
312 return $css;
313 }
314 }
315
316 /**
317 * Complements the implementation of block type `core/social-icon`, whether it
318 * be provided by core or the plugin, with derived block types for each
319 * "service" (WordPress, Twitter, etc.) supported by Social Links.
320 *
321 * This ensures backwards compatibility for any users running the Gutenberg
322 * plugin who have used Social Links prior to their conversion to block
323 * variations.
324 *
325 * This shim is INTENTIONALLY left out of core, as Social Links haven't yet
326 * landed there.
327 *
328 * @see https://github.com/WordPress/gutenberg/pull/19887
329 */
330 function gutenberg_register_legacy_social_link_blocks() {
331 $services = array(
332 'amazon',
333 'bandcamp',
334 'behance',
335 'chain',
336 'codepen',
337 'deviantart',
338 'dribbble',
339 'dropbox',
340 'etsy',
341 'facebook',
342 'feed',
343 'fivehundredpx',
344 'flickr',
345 'foursquare',
346 'goodreads',
347 'google',
348 'github',
349 'instagram',
350 'lastfm',
351 'linkedin',
352 'mail',
353 'mastodon',
354 'meetup',
355 'medium',
356 'pinterest',
357 'pocket',
358 'reddit',
359 'skype',
360 'snapchat',
361 'soundcloud',
362 'spotify',
363 'tumblr',
364 'twitch',
365 'twitter',
366 'vimeo',
367 'vk',
368 'wordpress',
369 'yelp',
370 'youtube',
371 );
372
373 foreach ( $services as $service ) {
374 register_block_type(
375 'core/social-link-' . $service,
376 array(
377 'category' => 'widgets',
378 'attributes' => array(
379 'url' => array(
380 'type' => 'string',
381 ),
382 'service' => array(
383 'type' => 'string',
384 'default' => $service,
385 ),
386 'label' => array(
387 'type' => 'string',
388 ),
389 ),
390 'render_callback' => 'gutenberg_render_block_core_social_link',
391 )
392 );
393 }
394 }
395
396 add_action( 'init', 'gutenberg_register_legacy_social_link_blocks' );
397
398 /**
399 * Checks whether the current block type supports the feature requested.
400 *
401 * @param WP_Block_Type $block_type Block type to check for support.
402 * @param array $feature Path of the feature to check support for.
403 * @param mixed $default Fallback value for feature support, defaults to false.
404 *
405 * @return boolean Whether or not the feature is supported.
406 */
407 function gutenberg_block_has_support( $block_type, $feature, $default = false ) {
408 $block_support = $default;
409 if ( $block_type && property_exists( $block_type, 'supports' ) ) {
410 $block_support = _wp_array_get( $block_type->supports, $feature, $default );
411 }
412
413 return true === $block_support || is_array( $block_support );
414 }
415
416 /**
417 * Updates the shape of supports for declaring fontSize and lineHeight.
418 *
419 * @param array $metadata Metadata for registering a block type.
420 * @return array Metadata for registering a block type with the supports shape updated.
421 */
422 function gutenberg_migrate_old_typography_shape( $metadata ) {
423 // Temporarily disable migrations from core blocks to avoid warnings on versions older than 5.8.
424 if ( isset( $metadata['supports'] ) && false === strpos( $metadata['file'], '/wp-includes/blocks/' ) ) {
425 $typography_keys = array(
426 '__experimentalFontFamily',
427 '__experimentalFontStyle',
428 '__experimentalFontWeight',
429 '__experimentalLetterSpacing',
430 '__experimentalTextDecoration',
431 '__experimentalTextTransform',
432 'fontSize',
433 'lineHeight',
434 );
435 foreach ( $typography_keys as $typography_key ) {
436 $support_for_key = _wp_array_get( $metadata['supports'], array( $typography_key ), null );
437 if ( null !== $support_for_key ) {
438 trigger_error(
439 /* translators: %1$s: Block type, %2$s: typography supports key e.g: fontSize, lineHeight etc... */
440 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 ),
441 headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
442 );
443 _wp_array_set( $metadata['supports'], array( 'typography', $typography_key ), $support_for_key );
444 unset( $metadata['supports'][ $typography_key ] );
445 }
446 }
447 }
448 return $metadata;
449 }
450
451 if ( ! function_exists( 'wp_migrate_old_typography_shape' ) ) {
452 add_filter( 'block_type_metadata', 'gutenberg_migrate_old_typography_shape' );
453 }
454
455 if ( ! function_exists( 'wp_enqueue_block_style' ) ) {
456 /**
457 * Enqueue a stylesheet for a specific block.
458 *
459 * If the theme has opted-in to separate-styles loading,
460 * then the stylesheet will be enqueued on-render,
461 * otherwise when the block inits.
462 *
463 * @param string $block_name The block-name, including namespace.
464 * @param array $args An array of arguments [handle,src,deps,ver,media].
465 *
466 * @return void
467 */
468 function wp_enqueue_block_style( $block_name, $args ) {
469 $args = wp_parse_args(
470 $args,
471 array(
472 'handle' => '',
473 'src' => '',
474 'deps' => array(),
475 'ver' => false,
476 'media' => 'all',
477 )
478 );
479
480 /**
481 * Callback function to register and enqueue styles.
482 *
483 * @param string $content When the callback is used for the render_block filter,
484 * the content needs to be returned so the function parameter
485 * is to ensure the content exists.
486 * @return string Block content.
487 */
488 $callback = static function( $content ) use ( $args ) {
489 // Register the stylesheet.
490 if ( ! empty( $args['src'] ) ) {
491 wp_register_style( $args['handle'], $args['src'], $args['deps'], $args['ver'], $args['media'] );
492 }
493
494 // Add `path` data if provided.
495 if ( isset( $args['path'] ) ) {
496 wp_style_add_data( $args['handle'], 'path', $args['path'] );
497
498 // Get the RTL file path.
499 $rtl_file_path = str_replace( '.css', '-rtl.css', $args['path'] );
500
501 // Add RTL stylesheet.
502 if ( file_exists( $rtl_file_path ) ) {
503 wp_style_add_data( $args['handle'], 'rtl', 'replace' );
504
505 if ( is_rtl() ) {
506 wp_style_add_data( $args['handle'], 'path', $rtl_file_path );
507 }
508 }
509 }
510
511 // Enqueue the stylesheet.
512 wp_enqueue_style( $args['handle'] );
513
514 return $content;
515 };
516
517 $hook = did_action( 'wp_enqueue_scripts' ) ? 'wp_footer' : 'wp_enqueue_scripts';
518 if ( wp_should_load_separate_core_block_assets() ) {
519 /**
520 * Callback function to register and enqueue styles.
521 *
522 * @param string $content The block content.
523 * @param array $block The full block, including name and attributes.
524 * @return string Block content.
525 */
526 $callback_separate = static function( $content, $block ) use ( $block_name, $callback ) {
527 if ( ! empty( $block['blockName'] ) && $block_name === $block['blockName'] ) {
528 return $callback( $content );
529 }
530 return $content;
531 };
532
533 /*
534 * The filter's callback here is an anonymous function because
535 * using a named function in this case is not possible.
536 *
537 * The function cannot be unhooked, however, users are still able
538 * to dequeue the stylesheets registered/enqueued by the callback
539 * which is why in this case, using an anonymous function
540 * was deemed acceptable.
541 */
542 add_filter( 'render_block', $callback_separate, 10, 2 );
543 return;
544 }
545
546 /*
547 * The filter's callback here is an anonymous function because
548 * using a named function in this case is not possible.
549 *
550 * The function cannot be unhooked, however, users are still able
551 * to dequeue the stylesheets registered/enqueued by the callback
552 * which is why in this case, using an anonymous function
553 * was deemed acceptable.
554 */
555 add_filter( $hook, $callback );
556
557 // Enqueue assets in the editor.
558 add_action( 'enqueue_block_assets', $callback );
559 }
560 }
561
562 /**
563 * Allow multiple block styles.
564 *
565 * @param array $metadata Metadata for registering a block type.
566 *
567 * @return array
568 */
569 function gutenberg_multiple_block_styles( $metadata ) {
570 foreach ( array( 'style', 'editorStyle' ) as $key ) {
571 if ( ! empty( $metadata[ $key ] ) && is_array( $metadata[ $key ] ) ) {
572 $default_style = array_shift( $metadata[ $key ] );
573 foreach ( $metadata[ $key ] as $handle ) {
574 $args = array( 'handle' => $handle );
575 if ( 0 === strpos( $handle, 'file:' ) && isset( $metadata['file'] ) ) {
576 $style_path = remove_block_asset_path_prefix( $handle );
577 $args = array(
578 'handle' => sanitize_key( "{$metadata['name']}-{$style_path}" ),
579 'src' => plugins_url( $style_path, $metadata['file'] ),
580 );
581 }
582
583 wp_enqueue_block_style( $metadata['name'], $args );
584 }
585
586 // Only return the 1st item in the array.
587 $metadata[ $key ] = $default_style;
588 }
589 }
590 return $metadata;
591 }
592 add_filter( 'block_type_metadata', 'gutenberg_multiple_block_styles' );
593