PluginProbe
Gutenberg / 12.9.0
Gutenberg v12.9.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.9.0, at lib/blocks.php

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