PluginProbe
Gutenberg / 24.0.0
Gutenberg v24.0.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 12.6.0 All 403 releases
← All changes | lib/blocks.php +208 -406 12.6.024.0.0 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Block and style registration functions.
3 + * Block functions specific for the Gutenberg editor plugin.
4 4 *
5 5 * @package gutenberg
6 6 */
7 7
@@ -9,177 +9,101 @@
9 9 * Substitutes the implementation of a core-registered block type, if exists,
10 10 * with the built result from the plugin.
11 11 */
12 12 function gutenberg_reregister_core_block_types() {
13 - // Blocks directory may not exist if working from a fresh clone.
14 13 $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 - ),
14 + __DIR__ . '/../build/scripts/block-library/',
15 + __DIR__ . '/../build/scripts/edit-widgets/blocks/',
16 + __DIR__ . '/../build/scripts/widgets/blocks/',
136 17 );
137 - foreach ( $blocks_dirs as $blocks_dir => $details ) {
138 - $block_folders = $details['block_folders'];
139 - $block_names = $details['block_names'];
140 18
141 - $registry = WP_Block_Type_Registry::get_instance();
19 + foreach ( $blocks_dirs as $blocks_dir ) {
20 + $manifest_path = $blocks_dir . 'blocks-manifest.php';
21 + $blocks = require $manifest_path;
142 22
143 - foreach ( $block_folders as $folder_name ) {
144 - $block_json_file = $blocks_dir . $folder_name . '/block.json';
23 + foreach ( $blocks as $block_name_folder => $metadata ) {
24 + if ( ! is_array( $metadata ) || ! isset( $metadata['name'] ) ) {
25 + continue;
26 + }
145 27
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;
28 + gutenberg_deregister_core_block_and_assets( $metadata['name'] );
29 + gutenberg_register_core_block_assets( $block_name_folder );
30 +
31 + $php_file = $blocks_dir . $block_name_folder . '.php';
32 + if ( file_exists( $php_file ) ) {
33 + require_once $php_file;
34 + } else {
35 + register_block_type_from_metadata( $blocks_dir . $block_name_folder );
152 36 }
37 + }
38 + }
39 +}
153 40
154 - if ( $registry->is_registered( $metadata['name'] ) ) {
155 - $registry->unregister( $metadata['name'] );
156 - }
41 +add_action( 'init', 'gutenberg_reregister_core_block_types' );
157 42
158 - gutenberg_register_core_block_assets( $folder_name );
159 - register_block_type_from_metadata( $block_json_file );
43 +/*
44 + * Remove the WordPress core filter to avoid generating a second tabs ID for
45 + * every tabs block: `wp_unique_prefixed_id()` advances a counter, so running
46 + * both copies makes the numbering skip.
47 + */
48 +remove_filter( 'render_block_context', 'block_core_tabs_provide_context', 10 );
49 +
50 +/**
51 + * Adds the defer loading strategy to all registered blocks.
52 + *
53 + * This function would not be part of core merge. Instead, the register_block_script_handle() function would be patched
54 + * as follows.
55 + *
56 + * ```
57 + * --- a/wp-includes/blocks.php
58 + * +++ b/wp-includes/blocks.php
59 + * @ @ -153,7 +153,8 @ @ function register_block_script_handle( $metadata, $field_name, $index = 0 ) {
60 + * $script_handle,
61 + * $script_uri,
62 + * $script_dependencies,
63 + * - $script_asset['version'] ?? false
64 + * + $script_asset['version'] ?? false,
65 + * + array( 'strategy' => 'defer' )
66 + * );
67 + * if ( ! $result ) {
68 + * return false;
69 + * ```
70 + *
71 + * @see register_block_script_handle()
72 + */
73 +function gutenberg_defer_block_view_scripts() {
74 + $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
75 + foreach ( $block_types as $block_type ) {
76 + foreach ( $block_type->view_script_handles as $view_script_handle ) {
77 + wp_script_add_data( $view_script_handle, 'strategy', 'defer' );
160 78 }
79 + }
80 +}
161 81
162 - foreach ( $block_names as $file => $sub_block_names ) {
163 - if ( ! file_exists( $blocks_dir . $file ) ) {
164 - return;
165 - }
82 +add_action( 'init', 'gutenberg_defer_block_view_scripts', 100 );
166 83
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 );
84 +/**
85 + * Deregisters the existing core block type and its assets.
86 + *
87 + * @param string $block_name The name of the block.
88 + *
89 + * @return void
90 + */
91 +function gutenberg_deregister_core_block_and_assets( $block_name ) {
92 + $registry = WP_Block_Type_Registry::get_instance();
93 + if ( $registry->is_registered( $block_name ) ) {
94 + $block_type = $registry->get_registered( $block_name );
95 + if ( ! empty( $block_type->view_script_handles ) ) {
96 + foreach ( $block_type->view_script_handles as $view_script_handle ) {
97 + if ( str_starts_with( $view_script_handle, 'wp-block-' ) ) {
98 + wp_deregister_script( $view_script_handle );
171 99 }
172 - gutenberg_register_core_block_assets( $block_name );
173 100 }
174 -
175 - require_once $blocks_dir . $file;
176 101 }
102 + $registry->unregister( $block_name );
177 103 }
178 104 }
179 105
180 -add_action( 'init', 'gutenberg_reregister_core_block_types' );
181 -
182 106 /**
183 107 * Registers block styles for a core block.
184 108 *
185 109 * @param string $block_name The block-name.
@@ -186,8 +110,17 @@
186 110 *
187 111 * @return void
188 112 */
189 113 function gutenberg_register_core_block_assets( $block_name ) {
114 + static $gutenberg_url_root = null;
115 + // Running `gutenberg_url` inside of a loop can be expensive in systems with
116 + // many callbacks attached to the `plugins_url` hook.
117 + // Since all of the paths have the same root, we can instead retrieve the
118 + // corresponding URL root once, and manually concatenate the URL below.
119 + if ( is_null( $gutenberg_url_root ) ) {
120 + $gutenberg_url_root = gutenberg_url( '/' );
121 + }
122 +
190 123 if ( ! wp_should_load_separate_core_block_assets() ) {
191 124 return;
192 125 }
193 126
@@ -194,127 +127,73 @@
194 127 $block_name = str_replace( 'core/', '', $block_name );
195 128
196 129 // When in production, use the plugin's version as the default asset version;
197 130 // 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();
131 + $default_version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time();
132 + $suffix = SCRIPT_DEBUG ? '' : '.min';
199 133
200 - $style_path = "build/block-library/blocks/$block_name/style.css";
201 - $editor_style_path = "build/block-library/blocks/$block_name/style-editor.css";
134 + $style_path = "build/styles/block-library/$block_name/";
135 + $stylesheet_url = $gutenberg_url_root . $style_path . 'style' . $suffix . '.css';
136 + $stylesheet_path = gutenberg_dir_path() . $style_path . ( is_rtl() ? 'style-rtl' . $suffix . '.css' : 'style' . $suffix . '.css' );
202 137
203 - if ( file_exists( gutenberg_dir_path() . $style_path ) ) {
138 + if ( file_exists( $stylesheet_path ) ) {
139 +
204 140 wp_deregister_style( "wp-block-{$block_name}" );
205 141 wp_register_style(
206 142 "wp-block-{$block_name}",
207 - gutenberg_url( $style_path ),
143 + $stylesheet_url,
208 144 array(),
209 145 $default_version
210 146 );
211 147 wp_style_add_data( "wp-block-{$block_name}", 'rtl', 'replace' );
212 -
148 + wp_style_add_data( "wp-block-{$block_name}", 'suffix', $suffix );
213 149 // 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 );
150 + wp_style_add_data( "wp-block-{$block_name}", 'path', $stylesheet_path );
215 151 } else {
216 - wp_register_style( "wp-block-{$block_name}", false );
152 + wp_register_style( "wp-block-{$block_name}", false, array() );
217 153 }
218 154
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.
155 + /*
156 + * If the current theme supports wp-block-styles, dequeue the core styles
157 + * and enqueue the plugin ones instead.
158 + */
221 159 if ( current_theme_supports( 'wp-block-styles' ) ) {
222 160
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 161 // Get the path to the block's stylesheet.
237 162 $theme_style_path = is_rtl()
238 - ? "build/block-library/blocks/$block_name/theme-rtl.css"
239 - : "build/block-library/blocks/$block_name/theme.css";
163 + ? "build/styles/block-library/$block_name/theme-rtl{$suffix}.css"
164 + : "build/styles/block-library/$block_name/theme{$suffix}.css";
240 165
241 166 // If the file exists, enqueue it.
242 167 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 - }
168 + wp_deregister_style( "wp-block-{$block_name}-theme" );
169 + wp_register_style(
170 + "wp-block-{$block_name}-theme",
171 + $gutenberg_url_root . $theme_style_path,
172 + array(),
173 + $default_version
174 + );
175 + wp_style_add_data( "wp-block-{$block_name}-theme", 'path', gutenberg_dir_path() . $theme_style_path );
176 + wp_style_add_data( "wp-block-{$block_name}-theme", 'suffix', $suffix );
260 177 }
261 178 }
262 179
180 + $editor_style_path = "build/styles/block-library/$block_name/style-editor{$suffix}.css";
263 181 if ( file_exists( gutenberg_dir_path() . $editor_style_path ) ) {
264 182 wp_deregister_style( "wp-block-{$block_name}-editor" );
265 183 wp_register_style(
266 184 "wp-block-{$block_name}-editor",
267 - gutenberg_url( $editor_style_path ),
185 + $gutenberg_url_root . $editor_style_path,
268 186 array(),
269 187 $default_version
270 188 );
271 189 wp_style_add_data( "wp-block-{$block_name}-editor", 'rtl', 'replace' );
190 + wp_style_add_data( "wp-block-{$block_name}-editor", 'suffix', $suffix );
272 191 } else {
273 192 wp_register_style( "wp-block-{$block_name}-editor", false );
274 193 }
275 194 }
276 195
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 196 /**
318 197 * Complements the implementation of block type `core/social-icon`, whether it
319 198 * be provided by core or the plugin, with derived block types for each
320 199 * "service" (WordPress, Twitter, etc.) supported by Social Links.
@@ -322,12 +201,12 @@
322 201 * This ensures backwards compatibility for any users running the Gutenberg
323 202 * plugin who have used Social Links prior to their conversion to block
324 203 * variations.
325 204 *
326 - * This shim is INTENTIONALLY left out of core, as Social Links haven't yet
205 + * This shim is INTENTIONALLY left out of core, as Social Links have never
327 206 * landed there.
328 207 *
329 - * @see https://github.com/WordPress/gutenberg/pull/19887
208 + * @link https://github.com/WordPress/gutenberg/pull/19887
330 209 */
331 210 function gutenberg_register_legacy_social_link_blocks() {
332 211 $services = array(
333 212 'amazon',
@@ -396,198 +275,121 @@
396 275
397 276 add_action( 'init', 'gutenberg_register_legacy_social_link_blocks' );
398 277
399 278 /**
400 - * Checks whether the current block type supports the feature requested.
279 + * Migrate the legacy `sync_status` meta key (added 16.1) to the new `wp_pattern_sync_status` meta key (16.1.1).
401 280 *
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.
281 + * This filter is INTENTIONALLY left out of core as the meta key was fist introduced to core in 6.3 as `wp_pattern_sync_status`.
282 + * see https://github.com/WordPress/gutenberg/pull/52232
405 283 *
406 - * @return boolean Whether or not the feature is supported.
284 + * @param mixed $value The value to return, either a single metadata value or an array of values depending on the value of $single.
285 + * @param int $object_id ID of the object metadata is for.
286 + * @param string $meta_key Metadata key.
287 + * @param bool $single Whether to return only the first value of the specified $meta_key.
407 288 */
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 );
289 +function gutenberg_legacy_wp_block_post_meta( $value, $object_id, $meta_key, $single ) {
290 + if ( 'wp_pattern_sync_status' !== $meta_key ) {
291 + return $value;
412 292 }
413 293
414 - return true === $block_support || is_array( $block_support );
294 + $sync_status = get_post_meta( $object_id, 'sync_status', $single );
295 +
296 + if ( $single && 'unsynced' === $sync_status ) {
297 + return $sync_status;
298 + } elseif ( isset( $sync_status[0] ) && 'unsynced' === $sync_status[0] ) {
299 + return $sync_status;
300 + }
301 +
302 + return $value;
415 303 }
416 304
305 +add_filter( 'default_post_metadata', 'gutenberg_legacy_wp_block_post_meta', 10, 4 );
306 +
307 +
308 +
417 309 /**
418 - * Updates the shape of supports for declaring fontSize and lineHeight.
310 + * Strips all HTML from the content of footnotes, and sanitizes the ID.
419 311 *
420 - * @param array $metadata Metadata for registering a block type.
421 - * @return array Metadata for registering a block type with the supports shape updated.
312 + * This function expects slashed data on the footnotes content.
313 + *
314 + * @access private
315 + *
316 + * @param string $footnotes JSON encoded string of an array containing the content and ID of each footnote.
317 + * @return string Filtered content without any HTML on the footnote content and with the sanitized id.
422 318 */
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 - }
319 +function _gutenberg_filter_post_meta_footnotes( $footnotes ) {
320 + $footnotes_decoded = json_decode( $footnotes, true );
321 + if ( ! is_array( $footnotes_decoded ) ) {
322 + return '';
323 + }
324 + $footnotes_sanitized = array();
325 + foreach ( $footnotes_decoded as $footnote ) {
326 + if ( ! empty( $footnote['content'] ) && ! empty( $footnote['id'] ) ) {
327 + $footnotes_sanitized[] = array(
328 + 'id' => sanitize_key( $footnote['id'] ),
329 + 'content' => wp_unslash( wp_filter_post_kses( wp_slash( $footnote['content'] ) ) ),
330 + );
447 331 }
448 332 }
449 - return $metadata;
333 + return wp_json_encode( $footnotes_sanitized );
450 334 }
451 335
452 -if ( ! function_exists( 'wp_migrate_old_typography_shape' ) ) {
453 - add_filter( 'block_type_metadata', 'gutenberg_migrate_old_typography_shape' );
336 +/**
337 + * Adds the filters to filter footnotes meta field.
338 + *
339 + * @access private
340 + */
341 +function _gutenberg_footnotes_kses_init_filters() {
342 + add_filter( 'sanitize_post_meta_footnotes', '_gutenberg_filter_post_meta_footnotes' );
454 343 }
455 344
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 - );
345 +/**
346 + * Removes the filters that filter footnotes meta field.
347 + *
348 + * @access private
349 + */
350 +function _gutenberg_footnotes_remove_filters() {
351 + remove_filter( 'sanitize_post_meta_footnotes', '_gutenberg_filter_post_meta_footnotes' );
352 +}
480 353
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 );
354 +/**
355 + * Registers the filter of footnotes meta field if the user does not have unfiltered_html capability.
356 + *
357 + * @access private
358 + */
359 +function _gutenberg_footnotes_kses_init() {
360 + if ( function_exists( '_wp_filter_post_meta_footnotes' ) ) {
361 + return;
560 362 }
363 + _gutenberg_footnotes_remove_filters();
364 + if ( ! current_user_can( 'unfiltered_html' ) ) {
365 + _gutenberg_footnotes_kses_init_filters();
366 + }
561 367 }
562 368
563 369 /**
564 - * Allow multiple block styles.
370 + * Initializes footnotes meta field filters when imported data should be filtered.
565 371 *
566 - * @param array $metadata Metadata for registering a block type.
372 + * This filter is the last being executed on force_filtered_html_on_import.
373 + * If the input of the filter is true it means we are in an import situation and should
374 + * enable kses, independently of the user capabilities.
375 + * So in that case we call _gutenberg_footnotes_kses_init_filters;
567 376 *
568 - * @return array
377 + * @access private
378 + *
379 + * @param string $arg Input argument of the filter.
380 + * @return string Input argument of the filter.
569 381 */
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 - }
382 +function _gutenberg_footnotes_force_filtered_html_on_import_filter( $arg ) {
383 + if ( function_exists( '_wp_filter_post_meta_footnotes' ) ) {
384 + return $arg;
590 385 }
591 - return $metadata;
386 + // force_filtered_html_on_import is true we need to init the global styles kses filters.
387 + if ( $arg ) {
388 + _gutenberg_footnotes_kses_init_filters();
389 + }
390 + return $arg;
592 391 }
593 -add_filter( 'block_type_metadata', 'gutenberg_multiple_block_styles' );
392 +
393 +add_action( 'init', '_gutenberg_footnotes_kses_init' );
394 +add_action( 'set_current_user', '_gutenberg_footnotes_kses_init' );
395 +add_filter( 'force_filtered_html_on_import', '_gutenberg_footnotes_force_filtered_html_on_import_filter', 999 );