PluginProbe
Gutenberg / 14.7.0
Gutenberg v14.7.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
gutenberg / lib / compat / wordpress-6.1 / blocks.php

blocks.php in Gutenberg 14.7.0, at lib/compat/wordpress-6.1/blocks.php

351 lines 12.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Temporary compatibility shims for block APIs present in Gutenberg.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Update allowed inline style attributes list.
10 *
11 * Note: This should be removed when the minimum required WP version is >= 6.1.
12 *
13 * @param string[] $attrs Array of allowed CSS attributes.
14 * @return string[] CSS attributes.
15 */
16 function gutenberg_safe_style_attrs_6_1( $attrs ) {
17 $attrs[] = 'flex-wrap';
18 $attrs[] = 'gap';
19 $attrs[] = 'margin-block-start';
20 $attrs[] = 'margin-block-end';
21 $attrs[] = 'margin-inline-start';
22 $attrs[] = 'margin-inline-end';
23
24 return $attrs;
25 }
26 add_filter( 'safe_style_css', 'gutenberg_safe_style_attrs_6_1' );
27
28 /**
29 * Update allowed CSS values to match WordPress 6.1.
30 *
31 * Note: This should be removed when the minimum required WP version is >= 6.1.
32 *
33 * The logic in this function follows that provided in: https://core.trac.wordpress.org/ticket/55966.
34 *
35 * @param boolean $allow_css Whether or not the current test string is allowed.
36 * @param string $css_test_string The CSS string to be tested.
37 * @return boolean
38 */
39 function gutenberg_safecss_filter_attr_allow_css_6_1( $allow_css, $css_test_string ) {
40 if ( false === $allow_css ) {
41 /*
42 * Allow CSS functions like var(), calc(), etc. by removing them from the test string.
43 * Nested functions and parentheses are also removed, so long as the parentheses are balanced.
44 */
45 $css_test_string = preg_replace(
46 '/\b(?:var|calc|min|max|minmax|clamp)(\((?:[^()]|(?1))*\))/',
47 '',
48 $css_test_string
49 );
50
51 // Check for any CSS containing \ ( & } = or comments,
52 // except for url(), calc(), or var() usage checked above.
53 $allow_css = ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string );
54 }
55 return $allow_css;
56 }
57 add_filter( 'safecss_filter_attr_allow_css', 'gutenberg_safecss_filter_attr_allow_css_6_1', 10, 2 );
58
59 /**
60 * Registers view scripts for core blocks if handling is missing in WordPress core.
61 *
62 * @since 6.1.0
63 *
64 * @param array $settings Array of determined settings for registering a block type.
65 * @param array $metadata Metadata provided for registering a block type.
66 *
67 * @return array Array of settings for registering a block type.
68 */
69 function gutenberg_block_type_metadata_view_script( $settings, $metadata ) {
70 if (
71 ! isset( $metadata['viewScript'] ) ||
72 ! empty( $settings['view_script'] ) ||
73 ! isset( $metadata['file'] ) ||
74 ! str_starts_with( $metadata['file'], wp_normalize_path( gutenberg_dir_path() ) )
75 ) {
76 return $settings;
77 }
78
79 $view_script_path = wp_normalize_path( realpath( dirname( $metadata['file'] ) . '/' . remove_block_asset_path_prefix( $metadata['viewScript'] ) ) );
80
81 if ( file_exists( $view_script_path ) ) {
82 $view_script_id = str_replace( array( '.min.js', '.js' ), '', basename( remove_block_asset_path_prefix( $metadata['viewScript'] ) ) );
83 $view_script_handle = str_replace( 'core/', 'wp-block-', $metadata['name'] ) . '-' . $view_script_id;
84 wp_deregister_script( $view_script_handle );
85
86 // Replace suffix and extension with `.asset.php` to find the generated dependencies file.
87 $view_asset_file = substr( $view_script_path, 0, -( strlen( '.js' ) ) ) . '.asset.php';
88 $view_asset = file_exists( $view_asset_file ) ? require $view_asset_file : null;
89 $view_script_dependencies = isset( $view_asset['dependencies'] ) ? $view_asset['dependencies'] : array();
90 $view_script_version = isset( $view_asset['version'] ) ? $view_asset['version'] : false;
91 $result = wp_register_script(
92 $view_script_handle,
93 gutenberg_url( str_replace( wp_normalize_path( gutenberg_dir_path() ), '', $view_script_path ) ),
94 $view_script_dependencies,
95 $view_script_version
96 );
97 if ( $result ) {
98 $settings['view_script'] = $view_script_handle;
99
100 if ( ! empty( $metadata['textdomain'] ) && in_array( 'wp-i18n', $view_script_dependencies, true ) ) {
101 wp_set_script_translations( $view_script_handle, $metadata['textdomain'] );
102 }
103 }
104 }
105 return $settings;
106 }
107 add_filter( 'block_type_metadata_settings', 'gutenberg_block_type_metadata_view_script', 10, 2 );
108
109 /**
110 * Allow multiple view scripts per block.
111 *
112 * Filters the metadata provided for registering a block type.
113 *
114 * @since 6.1.0
115 *
116 * @param array $metadata Metadata for registering a block type.
117 *
118 * @return array
119 */
120 function gutenberg_block_type_metadata_multiple_view_scripts( $metadata ) {
121
122 // Early return if viewScript is empty, or not an array.
123 if ( ! isset( $metadata['viewScript'] ) || ! is_array( $metadata['viewScript'] ) ) {
124 return $metadata;
125 }
126
127 // Register all viewScript items.
128 foreach ( $metadata['viewScript'] as $view_script ) {
129 $item_metadata = $metadata;
130 $item_metadata['viewScript'] = $view_script;
131 gutenberg_block_type_metadata_view_script( array(), $item_metadata );
132 }
133
134 // Proceed with the default behavior.
135 $metadata['viewScript'] = $metadata['viewScript'][0];
136 return $metadata;
137 }
138 add_filter( 'block_type_metadata', 'gutenberg_block_type_metadata_multiple_view_scripts' );
139
140 /**
141 * Helper function that constructs a WP_Query args array from
142 * a `Query` block properties.
143 *
144 * It's used in QueryLoop, QueryPaginationNumbers and QueryPaginationNext blocks.
145 *
146 * `build_query_vars_from_query_block` was introduced in 5.8, for 6.1 we just need
147 * to update that function and not create a new one.
148 *
149 * @param WP_Block $block Block instance.
150 * @param int $page Current query's page.
151 *
152 * @return array Returns the constructed WP_Query arguments.
153 */
154 function gutenberg_build_query_vars_from_query_block( $block, $page ) {
155 $query = array(
156 'post_type' => 'post',
157 'order' => 'DESC',
158 'orderby' => 'date',
159 'post__not_in' => array(),
160 );
161
162 if ( isset( $block->context['query'] ) ) {
163 if ( ! empty( $block->context['query']['postType'] ) ) {
164 $post_type_param = $block->context['query']['postType'];
165 if ( is_post_type_viewable( $post_type_param ) ) {
166 $query['post_type'] = $post_type_param;
167 }
168 }
169 if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) {
170 $sticky = get_option( 'sticky_posts' );
171 if ( 'only' === $block->context['query']['sticky'] ) {
172 /**
173 * Passing an empty array to post__in will return have_posts() as true (and all posts will be returned).
174 * Logic should be used before hand to determine if WP_Query should be used in the event that the array
175 * being passed to post__in is empty.
176 *
177 * @see https://core.trac.wordpress.org/ticket/28099
178 */
179 $query['post__in'] = ! empty( $sticky ) ? $sticky : array( 0 );
180 $query['ignore_sticky_posts'] = 1;
181 } else {
182 $query['post__not_in'] = array_merge( $query['post__not_in'], $sticky );
183 }
184 }
185 if ( ! empty( $block->context['query']['exclude'] ) ) {
186 $excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] );
187 $excluded_post_ids = array_filter( $excluded_post_ids );
188 $query['post__not_in'] = array_merge( $query['post__not_in'], $excluded_post_ids );
189 }
190 if (
191 isset( $block->context['query']['perPage'] ) &&
192 is_numeric( $block->context['query']['perPage'] )
193 ) {
194 $per_page = absint( $block->context['query']['perPage'] );
195 $offset = 0;
196
197 if (
198 isset( $block->context['query']['offset'] ) &&
199 is_numeric( $block->context['query']['offset'] )
200 ) {
201 $offset = absint( $block->context['query']['offset'] );
202 }
203
204 $query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset;
205 $query['posts_per_page'] = $per_page;
206 }
207
208 // We need to migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility.
209 if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) {
210 $tax_query = array();
211 if ( ! empty( $block->context['query']['categoryIds'] ) ) {
212 $tax_query[] = array(
213 'taxonomy' => 'category',
214 'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ),
215 'include_children' => false,
216 );
217 }
218 if ( ! empty( $block->context['query']['tagIds'] ) ) {
219 $tax_query[] = array(
220 'taxonomy' => 'post_tag',
221 'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ),
222 'include_children' => false,
223 );
224 }
225 $query['tax_query'] = $tax_query;
226 }
227 if ( ! empty( $block->context['query']['taxQuery'] ) ) {
228 $query['tax_query'] = array();
229 foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) {
230 if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) {
231 $query['tax_query'][] = array(
232 'taxonomy' => $taxonomy,
233 'terms' => array_filter( array_map( 'intval', $terms ) ),
234 'include_children' => false,
235 );
236 }
237 }
238 }
239 if (
240 isset( $block->context['query']['order'] ) &&
241 in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true )
242 ) {
243 $query['order'] = strtoupper( $block->context['query']['order'] );
244 }
245 if ( isset( $block->context['query']['orderBy'] ) ) {
246 $query['orderby'] = $block->context['query']['orderBy'];
247 }
248 if ( ! empty( $block->context['query']['author'] ) ) {
249 $query['author'] = $block->context['query']['author'];
250 }
251 if ( ! empty( $block->context['query']['search'] ) ) {
252 $query['s'] = $block->context['query']['search'];
253 }
254 if ( ! empty( $block->context['query']['parents'] ) && is_post_type_hierarchical( $query['post_type'] ) ) {
255 $query['post_parent__in'] = array_filter( array_map( 'intval', $block->context['query']['parents'] ) );
256 }
257 }
258
259 /**
260 * Filters the arguments which will be passed to `WP_Query` for the Query Loop Block.
261 *
262 * Anything to this filter should be compatible with the `WP_Query` API to form
263 * the query context which will be passed down to the Query Loop Block's children.
264 * This can help, for example, to include additional settings or meta queries not
265 * directly supported by the core Query Loop Block, and extend its capabilities.
266 *
267 * Please note that this will only influence the query that will be rendered on the
268 * front-end. The editor preview is not affected by this filter. Also, worth noting
269 * that the editor preview uses the REST API, so, ideally, one should aim to provide
270 * attributes which are also compatible with the REST API, in order to be able to
271 * implement identical queries on both sides.
272 *
273 * @since 6.1.0
274 *
275 * @param array $query Array containing parameters for `WP_Query` as parsed by the block context.
276 * @param WP_Block $block Block instance.
277 * @param int $page Current query's page.
278 */
279 return apply_filters( 'query_loop_block_query_vars', $query, $block, $page );
280 }
281
282 /**
283 * Register render template for core blocks if handling is missing in WordPress core.
284 *
285 * @since 6.1.0
286 *
287 * @param array $settings Array of determined settings for registering a block type.
288 * @param array $metadata Metadata provided for registering a block type.
289 *
290 * @return array Array of settings for registering a block type.
291 */
292 function gutenberg_block_type_metadata_render_template( $settings, $metadata ) {
293 if ( empty( $metadata['render'] ) || isset( $settings['render_callback'] ) ) {
294 return $settings;
295 }
296
297 $template_path = wp_normalize_path(
298 realpath(
299 dirname( $metadata['file'] ) . '/' .
300 remove_block_asset_path_prefix( $metadata['render'] )
301 )
302 );
303
304 // Bail if the file does not exist.
305 if ( ! file_exists( $template_path ) ) {
306 return $settings;
307 }
308 /**
309 * Renders the block on the server.
310 *
311 * @param array $attributes Block attributes.
312 * @param string $content Block default content.
313 * @param WP_Block $block Block instance.
314 *
315 * @return string Returns the block content.
316 */
317 $settings['render_callback'] = function( $attributes, $content, $block ) use ( $template_path ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
318 ob_start();
319 require $template_path;
320 return ob_get_clean();
321 };
322
323 return $settings;
324 }
325 add_filter( 'block_type_metadata_settings', 'gutenberg_block_type_metadata_render_template', 10, 2 );
326
327 /**
328 * Registers the metadata block attribute for block types.
329 *
330 * Once 6.1 is the minimum supported WordPress version for the Gutenberg
331 * plugin, this shim can be removed
332 *
333 * @param array $args Array of arguments for registering a block type.
334 * @return array $args
335 */
336 function gutenberg_register_metadata_attribute( $args ) {
337 // Setup attributes if needed.
338 if ( ! isset( $args['attributes'] ) || ! is_array( $args['attributes'] ) ) {
339 $args['attributes'] = array();
340 }
341
342 if ( ! array_key_exists( 'metadata', $args['attributes'] ) ) {
343 $args['attributes']['metadata'] = array(
344 'type' => 'object',
345 );
346 }
347
348 return $args;
349 }
350 add_filter( 'register_block_type_args', 'gutenberg_register_metadata_attribute' );
351