PluginProbe
Gutenberg / 12.6.0
Gutenberg v12.6.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.6.0, at lib/blocks.php

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