PluginProbe
Gutenberg / 8.9.2
Gutenberg v8.9.2
24.1.0 24.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 All 404 releases
← All changes | lib/compat.php +463 -23 7.4.0 → 8.9.2 View file →
@@ -8,42 +8,482 @@
8 8 * @package gutenberg
9 9 */
10 10
11 11 /**
12 - * Filters allowed CSS attributes to include `flex-basis`, included in saved
13 - * markup of the Column block.
12 + * These functions can be removed when plugin support requires WordPress 5.5.0+.
14 13 *
15 - * This can be removed when plugin support requires WordPress 5.3.0+.
14 + * @see https://core.trac.wordpress.org/ticket/50263
15 + * @see https://core.trac.wordpress.org/changeset/48141
16 + */
17 +if ( ! function_exists( 'register_block_type_from_metadata' ) ) {
18 + /**
19 + * Removes the block asset's path prefix if provided.
20 + *
21 + * @since 5.5.0
22 + *
23 + * @param string $asset_handle_or_path Asset handle or prefixed path.
24 + *
25 + * @return string Path without the prefix or the original value.
26 + */
27 + function remove_block_asset_path_prefix( $asset_handle_or_path ) {
28 + $path_prefix = 'file:';
29 + if ( strpos( $asset_handle_or_path, $path_prefix ) !== 0 ) {
30 + return $asset_handle_or_path;
31 + }
32 + return substr(
33 + $asset_handle_or_path,
34 + strlen( $path_prefix )
35 + );
36 + }
37 +
38 + /**
39 + * Generates the name for an asset based on the name of the block
40 + * and the field name provided.
41 + *
42 + * @since 5.5.0
43 + *
44 + * @param string $block_name Name of the block.
45 + * @param string $field_name Name of the metadata field.
46 + *
47 + * @return string Generated asset name for the block's field.
48 + */
49 + function generate_block_asset_handle( $block_name, $field_name ) {
50 + $field_mappings = array(
51 + 'editorScript' => 'editor-script',
52 + 'script' => 'script',
53 + 'editorStyle' => 'editor-style',
54 + 'style' => 'style',
55 + );
56 + return str_replace( '/', '-', $block_name ) .
57 + '-' . $field_mappings[ $field_name ];
58 + }
59 +
60 + /**
61 + * Finds a script handle for the selected block metadata field. It detects
62 + * when a path to file was provided and finds a corresponding
63 + * asset file with details necessary to register the script under
64 + * automatically generated handle name. It returns unprocessed script handle
65 + * otherwise.
66 + *
67 + * @since 5.5.0
68 + *
69 + * @param array $metadata Block metadata.
70 + * @param string $field_name Field name to pick from metadata.
71 + *
72 + * @return string|boolean Script handle provided directly or created through
73 + * script's registration, or false on failure.
74 + */
75 + function register_block_script_handle( $metadata, $field_name ) {
76 + if ( empty( $metadata[ $field_name ] ) ) {
77 + return false;
78 + }
79 + $script_handle = $metadata[ $field_name ];
80 + $script_path = remove_block_asset_path_prefix( $metadata[ $field_name ] );
81 + if ( $script_handle === $script_path ) {
82 + return $script_handle;
83 + }
84 +
85 + $script_handle = generate_block_asset_handle( $metadata['name'], $field_name );
86 + $script_asset_path = realpath(
87 + dirname( $metadata['file'] ) . '/' .
88 + substr_replace( $script_path, '.asset.php', - strlen( '.js' ) )
89 + );
90 + if ( ! file_exists( $script_asset_path ) ) {
91 + $message = sprintf(
92 + /* translators: %1: field name. %2: block name */
93 + __( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.', 'default' ),
94 + $field_name,
95 + $metadata['name']
96 + );
97 + _doing_it_wrong( __FUNCTION__, $message, '5.5.0' );
98 + return false;
99 + }
100 + $script_asset = require( $script_asset_path );
101 + $result = wp_register_script(
102 + $script_handle,
103 + plugins_url( $script_path, $metadata['file'] ),
104 + $script_asset['dependencies'],
105 + $script_asset['version']
106 + );
107 + return $result ? $script_handle : false;
108 + }
109 +
110 + /**
111 + * Finds a style handle for the block metadata field. It detects when a path
112 + * to file was provided and registers the style under automatically
113 + * generated handle name. It returns unprocessed style handle otherwise.
114 + *
115 + * @since 5.5.0
116 + *
117 + * @param array $metadata Block metadata.
118 + * @param string $field_name Field name to pick from metadata.
119 + *
120 + * @return string|boolean Style handle provided directly or created through
121 + * style's registration, or false on failure.
122 + */
123 + function register_block_style_handle( $metadata, $field_name ) {
124 + if ( empty( $metadata[ $field_name ] ) ) {
125 + return false;
126 + }
127 + $style_handle = $metadata[ $field_name ];
128 + $style_path = remove_block_asset_path_prefix( $metadata[ $field_name ] );
129 + if ( $style_handle === $style_path ) {
130 + return $style_handle;
131 + }
132 +
133 + $style_handle = generate_block_asset_handle( $metadata['name'], $field_name );
134 + $block_dir = dirname( $metadata['file'] );
135 + $result = wp_register_style(
136 + $style_handle,
137 + plugins_url( $style_path, $metadata['file'] ),
138 + array(),
139 + filemtime( realpath( "$block_dir/$style_path" ) )
140 + );
141 + return $result ? $style_handle : false;
142 + }
143 +
144 + /**
145 + * Registers a block type from metadata stored in the `block.json` file.
146 + *
147 + * @since 7.9.0
148 + *
149 + * @param string $file_or_folder Path to the JSON file with metadata definition for
150 + * the block or path to the folder where the `block.json` file is located.
151 + * @param array $args {
152 + * Optional. Array of block type arguments. Any arguments may be defined, however the
153 + * ones described below are supported by default. Default empty array.
154 + *
155 + * @type callable $render_callback Callback used to render blocks of this block type.
156 + * }
157 + * @return WP_Block_Type|false The registered block type on success, or false on failure.
158 + */
159 + function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
160 + $filename = 'block.json';
161 + $metadata_file = ( substr( $file_or_folder, -strlen( $filename ) ) !== $filename ) ?
162 + trailingslashit( $file_or_folder ) . $filename :
163 + $file_or_folder;
164 + if ( ! file_exists( $metadata_file ) ) {
165 + return false;
166 + }
167 +
168 + $metadata = json_decode( file_get_contents( $metadata_file ), true );
169 + if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) {
170 + return false;
171 + }
172 + $metadata['file'] = $metadata_file;
173 +
174 + $settings = array();
175 + $property_mappings = array(
176 + 'title' => 'title',
177 + 'category' => 'category',
178 + 'parent' => 'parent',
179 + 'icon' => 'icon',
180 + 'description' => 'description',
181 + 'keywords' => 'keywords',
182 + 'attributes' => 'attributes',
183 + 'providesContext' => 'provides_context',
184 + 'usesContext' => 'uses_context',
185 + // Deprecated: remove with Gutenberg 8.6 release.
186 + 'context' => 'context',
187 + 'supports' => 'supports',
188 + 'styles' => 'styles',
189 + 'example' => 'example',
190 + );
191 +
192 + foreach ( $property_mappings as $key => $mapped_key ) {
193 + if ( isset( $metadata[ $key ] ) ) {
194 + $settings[ $mapped_key ] = $metadata[ $key ];
195 + }
196 + }
197 +
198 + if ( ! empty( $metadata['editorScript'] ) ) {
199 + $settings['editor_script'] = register_block_script_handle(
200 + $metadata,
201 + 'editorScript'
202 + );
203 + }
204 +
205 + if ( ! empty( $metadata['script'] ) ) {
206 + $settings['script'] = register_block_script_handle(
207 + $metadata,
208 + 'script'
209 + );
210 + }
211 +
212 + if ( ! empty( $metadata['editorStyle'] ) ) {
213 + $settings['editor_style'] = register_block_style_handle(
214 + $metadata,
215 + 'editorStyle'
216 + );
217 + }
218 +
219 + if ( ! empty( $metadata['style'] ) ) {
220 + $settings['style'] = register_block_style_handle(
221 + $metadata,
222 + 'style'
223 + );
224 + }
225 +
226 + return register_block_type(
227 + $metadata['name'],
228 + array_merge(
229 + $settings,
230 + $args
231 + )
232 + );
233 + }
234 +}
235 +
236 +/**
237 + * Adds a wp.date.setSettings with timezone abbr parameter
16 238 *
17 - * @see https://core.trac.wordpress.org/ticket/47281
18 - * @see https://core.trac.wordpress.org/changeset/45363
239 + * This can be removed when plugin support requires WordPress 5.6.0+.
19 240 *
20 - * @since 5.7.0
241 + * The script registration occurs in core wp-includes/script-loader.php
242 + * wp_default_packages_inline_scripts()
21 243 *
22 - * @param string[] $attr Array of allowed CSS attributes.
244 + * @since 8.6.0
23 245 *
24 - * @return string[] Filtered array of allowed CSS attributes.
246 + * @param WP_Scripts $scripts WP_Scripts object.
25 247 */
26 -function gutenberg_safe_style_css_column_flex_basis( $attr ) {
27 - $attr[] = 'flex-basis';
248 +function gutenberg_add_date_settings_timezone( $scripts ) {
249 + if ( ! did_action( 'init' ) ) {
250 + return;
251 + }
28 252
29 - return $attr;
253 + global $wp_locale;
254 +
255 + // Calculate the timezone abbr (EDT, PST) if possible.
256 + $timezone_string = get_option( 'timezone_string', 'UTC' );
257 + $timezone_abbr = '';
258 +
259 + if ( ! empty( $timezone_string ) ) {
260 + $timezone_date = new DateTime( null, new DateTimeZone( $timezone_string ) );
261 + $timezone_abbr = $timezone_date->format( 'T' );
262 + }
263 +
264 + $scripts->add_inline_script(
265 + 'wp-date',
266 + sprintf(
267 + 'wp.date.setSettings( %s );',
268 + wp_json_encode(
269 + array(
270 + 'l10n' => array(
271 + 'locale' => get_user_locale(),
272 + 'months' => array_values( $wp_locale->month ),
273 + 'monthsShort' => array_values( $wp_locale->month_abbrev ),
274 + 'weekdays' => array_values( $wp_locale->weekday ),
275 + 'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
276 + 'meridiem' => (object) $wp_locale->meridiem,
277 + 'relative' => array(
278 + /* translators: %s: Duration. */
279 + 'future' => __( '%s from now', 'default' ),
280 + /* translators: %s: Duration. */
281 + 'past' => __( '%s ago', 'default' ),
282 + ),
283 + ),
284 + 'formats' => array(
285 + /* translators: Time format, see https://www.php.net/date */
286 + 'time' => get_option( 'time_format', __( 'g:i a', 'default' ) ),
287 + /* translators: Date format, see https://www.php.net/date */
288 + 'date' => get_option( 'date_format', __( 'F j, Y', 'default' ) ),
289 + /* translators: Date/Time format, see https://www.php.net/date */
290 + 'datetime' => __( 'F j, Y g:i a', 'default' ),
291 + /* translators: Abbreviated date/time format, see https://www.php.net/date */
292 + 'datetimeAbbreviated' => __( 'M j, Y g:i a', 'default' ),
293 + ),
294 + 'timezone' => array(
295 + 'offset' => get_option( 'gmt_offset', 0 ),
296 + 'string' => $timezone_string,
297 + 'abbr' => $timezone_abbr,
298 + ),
299 + )
300 + )
301 + ),
302 + 'after'
303 + );
30 304 }
31 -add_filter( 'safe_style_css', 'gutenberg_safe_style_css_column_flex_basis' );
305 +add_action( 'wp_default_scripts', 'gutenberg_add_date_settings_timezone', 20 );
32 306
33 307 /**
34 - * Sets the current post for usage in template blocks.
308 + * Filters default block categories to substitute legacy category names with new
309 + * block categories.
35 310 *
36 - * @return WP_Post|null The post if any, or null otherwise.
311 + * This can be removed when plugin support requires WordPress 5.5.0+.
312 + *
313 + * @see https://core.trac.wordpress.org/ticket/50278
314 + * @see https://core.trac.wordpress.org/changeset/48177
315 + *
316 + * @param array[] $default_categories Array of block categories.
317 + *
318 + * @return array[] Filtered block categories.
37 319 */
38 -function gutenberg_get_post_from_context() {
39 - // TODO: Without this temporary fix, an infinite loop can occur where
40 - // posts with post content blocks render themselves recursively.
41 - if ( is_admin() || defined( 'REST_REQUEST' ) ) {
42 - return null;
320 +function gutenberg_replace_default_block_categories( $default_categories ) {
321 + $substitution = array(
322 + 'common' => array(
323 + 'slug' => 'text',
324 + 'title' => __( 'Text', 'gutenberg' ),
325 + 'icon' => null,
326 + ),
327 + 'formatting' => array(
328 + 'slug' => 'media',
329 + 'title' => __( 'Media', 'gutenberg' ),
330 + 'icon' => null,
331 + ),
332 + 'layout' => array(
333 + 'slug' => 'design',
334 + 'title' => __( 'Design', 'gutenberg' ),
335 + 'icon' => null,
336 + ),
337 + );
338 +
339 + // Loop default categories to perform in-place substitution by legacy slug.
340 + foreach ( $default_categories as $i => $default_category ) {
341 + $slug = $default_category['slug'];
342 + if ( isset( $substitution[ $slug ] ) ) {
343 + $default_categories[ $i ] = $substitution[ $slug ];
344 + unset( $substitution[ $slug ] );
345 + }
43 346 }
44 - if ( ! in_the_loop() ) {
45 - rewind_posts();
46 - the_post();
347 +
348 + /*
349 + * At this point, `$substitution` should contain only the categories which
350 + * could not be in-place substituted with a default category, likely in the
351 + * case that core has since been updated to use the default categories.
352 + * Check to verify they exist.
353 + */
354 + $default_category_slugs = wp_list_pluck( $default_categories, 'slug' );
355 + foreach ( $substitution as $i => $substitute_category ) {
356 + if ( in_array( $substitute_category['slug'], $default_category_slugs, true ) ) {
357 + unset( $substitution[ $i ] );
358 + }
47 359 }
48 - return get_post();
360 +
361 + /*
362 + * Any substitutes remaining should be appended, as they are not yet
363 + * assigned in the default categories array.
364 + */
365 + return array_merge( $default_categories, array_values( $substitution ) );
49 366 }
367 +add_filter( 'block_categories', 'gutenberg_replace_default_block_categories' );
368 +
369 +/**
370 + * Shim that hooks into `pre_render_block` so as to override `render_block` with
371 + * a function that assigns block context.
372 + *
373 + * This can be removed when plugin support requires WordPress 5.5.0+.
374 + *
375 + * @see https://core.trac.wordpress.org/ticket/49927
376 + * @see https://core.trac.wordpress.org/changeset/48243
377 + *
378 + * @param string|null $pre_render The pre-rendered content. Defaults to null.
379 + * @param array $parsed_block The parsed block being rendered.
380 + *
381 + * @return string String of rendered HTML.
382 + */
383 +function gutenberg_render_block_with_assigned_block_context( $pre_render, $parsed_block ) {
384 + global $post, $wp_query;
385 +
386 + /*
387 + * If a non-null value is provided, a filter has run at an earlier priority
388 + * and has already handled custom rendering and should take precedence.
389 + */
390 + if ( null !== $pre_render ) {
391 + return $pre_render;
392 + }
393 +
394 + $source_block = $parsed_block;
395 +
396 + /** This filter is documented in src/wp-includes/blocks.php */
397 + $parsed_block = apply_filters( 'render_block_data', $parsed_block, $source_block );
398 +
399 + $context = array();
400 +
401 + if ( $post instanceof WP_Post ) {
402 + $context['postId'] = $post->ID;
403 +
404 + /*
405 + * The `postType` context is largely unnecessary server-side, since the
406 + * ID is usually sufficient on its own. That being said, since a block's
407 + * manifest is expected to be shared between the server and the client,
408 + * it should be included to consistently fulfill the expectation.
409 + */
410 + $context['postType'] = $post->post_type;
411 + }
412 +
413 + if ( isset( $wp_query->tax_query->queried_terms['category'] ) ) {
414 + $context['query'] = array( 'categoryIds' => array() );
415 +
416 + foreach ( $wp_query->tax_query->queried_terms['category']['terms'] as $category_slug_or_id ) {
417 + $context['query']['categoryIds'][] = 'slug' === $wp_query->tax_query->queried_terms['category']['field'] ? get_cat_ID( $category_slug_or_id ) : $category_slug_or_id;
418 + }
419 + }
420 +
421 + /**
422 + * Filters the default context provided to a rendered block.
423 + *
424 + * @param array $context Default context.
425 + * @param array $parsed_block Block being rendered, filtered by `render_block_data`.
426 + */
427 + $context = apply_filters( 'render_block_context', $context, $parsed_block );
428 +
429 + $block = new WP_Block( $parsed_block, $context );
430 +
431 + return $block->render();
432 +}
433 +add_filter( 'pre_render_block', 'gutenberg_render_block_with_assigned_block_context', 9, 2 );
434 +
435 +/**
436 + * Amends the paths to preload when initializing edit post.
437 + *
438 + * @see https://core.trac.wordpress.org/ticket/50606
439 + *
440 + * @since 8.4.0
441 + *
442 + * @param array $preload_paths Default path list that will be preloaded.
443 + * @return array Modified path list to preload.
444 + */
445 +function gutenberg_preload_edit_post( $preload_paths ) {
446 + $additional_paths = array( '/?context=edit' );
447 + return array_merge( $preload_paths, $additional_paths );
448 +}
449 +
450 +add_filter( 'block_editor_preload_paths', 'gutenberg_preload_edit_post' );
451 +
452 +/**
453 + * Override post type labels for Reusable Block custom post type.
454 + *
455 + * This shim can be removed when the Gutenberg plugin requires a WordPress
456 + * version that has the ticket below.
457 + *
458 + * @see https://core.trac.wordpress.org/ticket/50755
459 + *
460 + * @since 8.6.0
461 + *
462 + * @return array Array of new labels for Reusable Block post type.
463 + */
464 +function gutenberg_override_reusable_block_post_type_labels() {
465 + return array(
466 + 'name' => _x( 'Reusable Blocks', 'post type general name', 'gutenberg' ),
467 + 'singular_name' => _x( 'Reusable Block', 'post type singular name', 'gutenberg' ),
468 + 'menu_name' => _x( 'Reusable Blocks', 'admin menu', 'gutenberg' ),
469 + 'name_admin_bar' => _x( 'Reusable Block', 'add new on admin bar', 'gutenberg' ),
470 + 'add_new' => _x( 'Add New', 'Reusable Block', 'gutenberg' ),
471 + 'add_new_item' => __( 'Add New Reusable Block', 'gutenberg' ),
472 + 'new_item' => __( 'New Reusable Block', 'gutenberg' ),
473 + 'edit_item' => __( 'Edit Reusable Block', 'gutenberg' ),
474 + 'view_item' => __( 'View Reusable Block', 'gutenberg' ),
475 + 'all_items' => __( 'All Reusable Blocks', 'gutenberg' ),
476 + 'search_items' => __( 'Search Reusable Blocks', 'gutenberg' ),
477 + 'not_found' => __( 'No reusable blocks found.', 'gutenberg' ),
478 + 'not_found_in_trash' => __( 'No reusable blocks found in Trash.', 'gutenberg' ),
479 + 'filter_items_list' => __( 'Filter reusable blocks list', 'gutenberg' ),
480 + 'items_list_navigation' => __( 'Reusable Blocks list navigation', 'gutenberg' ),
481 + 'items_list' => __( 'Reusable Blocks list', 'gutenberg' ),
482 + 'item_published' => __( 'Reusable Block published.', 'gutenberg' ),
483 + 'item_published_privately' => __( 'Reusable Block published privately.', 'gutenberg' ),
484 + 'item_reverted_to_draft' => __( 'Reusable Block reverted to draft.', 'gutenberg' ),
485 + 'item_scheduled' => __( 'Reusable Block scheduled.', 'gutenberg' ),
486 + 'item_updated' => __( 'Reusable Block updated.', 'gutenberg' ),
487 + );
488 +}
489 +add_filter( 'post_type_labels_wp_block', 'gutenberg_override_reusable_block_post_type_labels', 10, 0 );