9
|
1 |
<?php |
|
2 |
/** |
|
3 |
* Functions related to registering and parsing blocks. |
|
4 |
* |
|
5 |
* @package WordPress |
|
6 |
* @subpackage Blocks |
|
7 |
* @since 5.0.0 |
|
8 |
*/ |
|
9 |
|
|
10 |
/** |
|
11 |
* Registers a block type. |
|
12 |
* |
|
13 |
* @since 5.0.0 |
|
14 |
* |
|
15 |
* @param string|WP_Block_Type $name Block type name including namespace, or alternatively a |
|
16 |
* complete WP_Block_Type instance. In case a WP_Block_Type |
|
17 |
* is provided, the $args parameter will be ignored. |
|
18 |
* @param array $args { |
|
19 |
* Optional. Array of block type arguments. Any arguments may be defined, however the |
|
20 |
* ones described below are supported by default. Default empty array. |
|
21 |
* |
|
22 |
* @type callable $render_callback Callback used to render blocks of this block type. |
|
23 |
* } |
|
24 |
* @return WP_Block_Type|false The registered block type on success, or false on failure. |
|
25 |
*/ |
|
26 |
function register_block_type( $name, $args = array() ) { |
|
27 |
return WP_Block_Type_Registry::get_instance()->register( $name, $args ); |
|
28 |
} |
|
29 |
|
|
30 |
/** |
|
31 |
* Unregisters a block type. |
|
32 |
* |
|
33 |
* @since 5.0.0 |
|
34 |
* |
|
35 |
* @param string|WP_Block_Type $name Block type name including namespace, or alternatively a |
|
36 |
* complete WP_Block_Type instance. |
|
37 |
* @return WP_Block_Type|false The unregistered block type on success, or false on failure. |
|
38 |
*/ |
|
39 |
function unregister_block_type( $name ) { |
|
40 |
return WP_Block_Type_Registry::get_instance()->unregister( $name ); |
|
41 |
} |
|
42 |
|
|
43 |
/** |
|
44 |
* Determine whether a post or content string has blocks. |
|
45 |
* |
|
46 |
* This test optimizes for performance rather than strict accuracy, detecting |
|
47 |
* the pattern of a block but not validating its structure. For strict accuracy, |
|
48 |
* you should use the block parser on post content. |
|
49 |
* |
|
50 |
* @since 5.0.0 |
|
51 |
* @see parse_blocks() |
|
52 |
* |
|
53 |
* @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post. |
|
54 |
* @return bool Whether the post has blocks. |
|
55 |
*/ |
|
56 |
function has_blocks( $post = null ) { |
|
57 |
if ( ! is_string( $post ) ) { |
|
58 |
$wp_post = get_post( $post ); |
|
59 |
if ( $wp_post instanceof WP_Post ) { |
|
60 |
$post = $wp_post->post_content; |
|
61 |
} |
|
62 |
} |
|
63 |
|
|
64 |
return false !== strpos( (string) $post, '<!-- wp:' ); |
|
65 |
} |
|
66 |
|
|
67 |
/** |
|
68 |
* Determine whether a $post or a string contains a specific block type. |
|
69 |
* |
|
70 |
* This test optimizes for performance rather than strict accuracy, detecting |
|
71 |
* the block type exists but not validating its structure. For strict accuracy, |
|
72 |
* you should use the block parser on post content. |
|
73 |
* |
|
74 |
* @since 5.0.0 |
|
75 |
* @see parse_blocks() |
|
76 |
* |
|
77 |
* @param string $block_type Full Block type to look for. |
|
78 |
* @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post. |
|
79 |
* @return bool Whether the post content contains the specified block. |
|
80 |
*/ |
|
81 |
function has_block( $block_type, $post = null ) { |
|
82 |
if ( ! has_blocks( $post ) ) { |
|
83 |
return false; |
|
84 |
} |
|
85 |
|
|
86 |
if ( ! is_string( $post ) ) { |
|
87 |
$wp_post = get_post( $post ); |
|
88 |
if ( $wp_post instanceof WP_Post ) { |
|
89 |
$post = $wp_post->post_content; |
|
90 |
} |
|
91 |
} |
|
92 |
|
|
93 |
return false !== strpos( $post, '<!-- wp:' . $block_type . ' ' ); |
|
94 |
} |
|
95 |
|
|
96 |
/** |
|
97 |
* Returns an array of the names of all registered dynamic block types. |
|
98 |
* |
|
99 |
* @since 5.0.0 |
|
100 |
* |
|
101 |
* @return array Array of dynamic block names. |
|
102 |
*/ |
|
103 |
function get_dynamic_block_names() { |
|
104 |
$dynamic_block_names = array(); |
|
105 |
|
|
106 |
$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered(); |
|
107 |
foreach ( $block_types as $block_type ) { |
|
108 |
if ( $block_type->is_dynamic() ) { |
|
109 |
$dynamic_block_names[] = $block_type->name; |
|
110 |
} |
|
111 |
} |
|
112 |
|
|
113 |
return $dynamic_block_names; |
|
114 |
} |
|
115 |
|
|
116 |
/** |
|
117 |
* Parses blocks out of a content string, and renders those appropriate for the excerpt. |
|
118 |
* |
|
119 |
* As the excerpt should be a small string of text relevant to the full post content, |
|
120 |
* this function renders the blocks that are most likely to contain such text. |
|
121 |
* |
|
122 |
* @since 5.0.0 |
|
123 |
* |
|
124 |
* @param string $content The content to parse. |
|
125 |
* @return string The parsed and filtered content. |
|
126 |
*/ |
|
127 |
function excerpt_remove_blocks( $content ) { |
|
128 |
$allowed_inner_blocks = array( |
|
129 |
// Classic blocks have their blockName set to null. |
|
130 |
null, |
|
131 |
'core/freeform', |
|
132 |
'core/heading', |
|
133 |
'core/html', |
|
134 |
'core/list', |
|
135 |
'core/media-text', |
|
136 |
'core/paragraph', |
|
137 |
'core/preformatted', |
|
138 |
'core/pullquote', |
|
139 |
'core/quote', |
|
140 |
'core/table', |
|
141 |
'core/verse', |
|
142 |
); |
|
143 |
|
|
144 |
$allowed_blocks = array_merge( $allowed_inner_blocks, array( 'core/columns' ) ); |
|
145 |
|
|
146 |
/** |
|
147 |
* Filters the list of blocks that can contribute to the excerpt. |
|
148 |
* |
|
149 |
* If a dynamic block is added to this list, it must not generate another |
|
150 |
* excerpt, as this will cause an infinite loop to occur. |
|
151 |
* |
|
152 |
* @since 5.0.0 |
|
153 |
* |
|
154 |
* @param array $allowed_blocks The list of allowed blocks. |
|
155 |
*/ |
|
156 |
$allowed_blocks = apply_filters( 'excerpt_allowed_blocks', $allowed_blocks ); |
|
157 |
$blocks = parse_blocks( $content ); |
|
158 |
$output = ''; |
|
159 |
|
|
160 |
foreach ( $blocks as $block ) { |
|
161 |
if ( in_array( $block['blockName'], $allowed_blocks, true ) ) { |
|
162 |
if ( ! empty( $block['innerBlocks'] ) ) { |
|
163 |
if ( 'core/columns' === $block['blockName'] ) { |
|
164 |
$output .= _excerpt_render_inner_columns_blocks( $block, $allowed_inner_blocks ); |
|
165 |
continue; |
|
166 |
} |
|
167 |
|
|
168 |
// Skip the block if it has disallowed or nested inner blocks. |
|
169 |
foreach ( $block['innerBlocks'] as $inner_block ) { |
|
170 |
if ( |
|
171 |
! in_array( $inner_block['blockName'], $allowed_inner_blocks, true ) || |
|
172 |
! empty( $inner_block['innerBlocks'] ) |
|
173 |
) { |
|
174 |
continue 2; |
|
175 |
} |
|
176 |
} |
|
177 |
} |
|
178 |
|
|
179 |
$output .= render_block( $block ); |
|
180 |
} |
|
181 |
} |
|
182 |
|
|
183 |
return $output; |
|
184 |
} |
|
185 |
|
|
186 |
/** |
|
187 |
* Render inner blocks from the `core/columns` block for generating an excerpt. |
|
188 |
* |
|
189 |
* @since 5.2.0 |
|
190 |
* @access private |
|
191 |
* |
|
192 |
* @param array $columns The parsed columns block. |
|
193 |
* @param array $allowed_blocks The list of allowed inner blocks. |
|
194 |
* @return string The rendered inner blocks. |
|
195 |
*/ |
|
196 |
function _excerpt_render_inner_columns_blocks( $columns, $allowed_blocks ) { |
|
197 |
$output = ''; |
|
198 |
|
|
199 |
foreach ( $columns['innerBlocks'] as $column ) { |
|
200 |
foreach ( $column['innerBlocks'] as $inner_block ) { |
|
201 |
if ( in_array( $inner_block['blockName'], $allowed_blocks, true ) && empty( $inner_block['innerBlocks'] ) ) { |
|
202 |
$output .= render_block( $inner_block ); |
|
203 |
} |
|
204 |
} |
|
205 |
} |
|
206 |
|
|
207 |
return $output; |
|
208 |
} |
|
209 |
|
|
210 |
/** |
|
211 |
* Renders a single block into a HTML string. |
|
212 |
* |
|
213 |
* @since 5.0.0 |
|
214 |
* |
|
215 |
* @global WP_Post $post The post to edit. |
|
216 |
* |
|
217 |
* @param array $block A single parsed block object. |
|
218 |
* @return string String of rendered HTML. |
|
219 |
*/ |
|
220 |
function render_block( $block ) { |
|
221 |
global $post; |
|
222 |
|
|
223 |
/** |
|
224 |
* Allows render_block() to be shortcircuited, by returning a non-null value. |
|
225 |
* |
|
226 |
* @since 5.1.0 |
|
227 |
* |
|
228 |
* @param string $pre_render The pre-rendered content. Default null. |
|
229 |
* @param array $block The block being rendered. |
|
230 |
*/ |
|
231 |
$pre_render = apply_filters( 'pre_render_block', null, $block ); |
|
232 |
if ( ! is_null( $pre_render ) ) { |
|
233 |
return $pre_render; |
|
234 |
} |
|
235 |
|
|
236 |
$source_block = $block; |
|
237 |
|
|
238 |
/** |
|
239 |
* Filters the block being rendered in render_block(), before it's processed. |
|
240 |
* |
|
241 |
* @since 5.1.0 |
|
242 |
* |
|
243 |
* @param array $block The block being rendered. |
|
244 |
* @param array $source_block An un-modified copy of $block, as it appeared in the source content. |
|
245 |
*/ |
|
246 |
$block = apply_filters( 'render_block_data', $block, $source_block ); |
|
247 |
|
|
248 |
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); |
|
249 |
$is_dynamic = $block['blockName'] && null !== $block_type && $block_type->is_dynamic(); |
|
250 |
$block_content = ''; |
|
251 |
$index = 0; |
|
252 |
|
|
253 |
foreach ( $block['innerContent'] as $chunk ) { |
|
254 |
$block_content .= is_string( $chunk ) ? $chunk : render_block( $block['innerBlocks'][ $index++ ] ); |
|
255 |
} |
|
256 |
|
|
257 |
if ( ! is_array( $block['attrs'] ) ) { |
|
258 |
$block['attrs'] = array(); |
|
259 |
} |
|
260 |
|
|
261 |
if ( $is_dynamic ) { |
|
262 |
$global_post = $post; |
|
263 |
$block_content = $block_type->render( $block['attrs'], $block_content ); |
|
264 |
$post = $global_post; |
|
265 |
} |
|
266 |
|
|
267 |
/** |
|
268 |
* Filters the content of a single block. |
|
269 |
* |
|
270 |
* @since 5.0.0 |
|
271 |
* |
|
272 |
* @param string $block_content The block content about to be appended. |
|
273 |
* @param array $block The full block, including name and attributes. |
|
274 |
*/ |
|
275 |
return apply_filters( 'render_block', $block_content, $block ); |
|
276 |
} |
|
277 |
|
|
278 |
/** |
|
279 |
* Parses blocks out of a content string. |
|
280 |
* |
|
281 |
* @since 5.0.0 |
|
282 |
* |
|
283 |
* @param string $content Post content. |
|
284 |
* @return array Array of parsed block objects. |
|
285 |
*/ |
|
286 |
function parse_blocks( $content ) { |
|
287 |
/** |
|
288 |
* Filter to allow plugins to replace the server-side block parser |
|
289 |
* |
|
290 |
* @since 5.0.0 |
|
291 |
* |
|
292 |
* @param string $parser_class Name of block parser class. |
|
293 |
*/ |
|
294 |
$parser_class = apply_filters( 'block_parser_class', 'WP_Block_Parser' ); |
|
295 |
|
|
296 |
$parser = new $parser_class(); |
|
297 |
return $parser->parse( $content ); |
|
298 |
} |
|
299 |
|
|
300 |
/** |
|
301 |
* Parses dynamic blocks out of `post_content` and re-renders them. |
|
302 |
* |
|
303 |
* @since 5.0.0 |
|
304 |
* @global WP_Post $post The post to edit. |
|
305 |
* |
|
306 |
* @param string $content Post content. |
|
307 |
* @return string Updated post content. |
|
308 |
*/ |
|
309 |
function do_blocks( $content ) { |
|
310 |
$blocks = parse_blocks( $content ); |
|
311 |
$output = ''; |
|
312 |
|
|
313 |
foreach ( $blocks as $block ) { |
|
314 |
$output .= render_block( $block ); |
|
315 |
} |
|
316 |
|
|
317 |
// If there are blocks in this content, we shouldn't run wpautop() on it later. |
|
318 |
$priority = has_filter( 'the_content', 'wpautop' ); |
|
319 |
if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) { |
|
320 |
remove_filter( 'the_content', 'wpautop', $priority ); |
|
321 |
add_filter( 'the_content', '_restore_wpautop_hook', $priority + 1 ); |
|
322 |
} |
|
323 |
|
|
324 |
return $output; |
|
325 |
} |
|
326 |
|
|
327 |
/** |
|
328 |
* If do_blocks() needs to remove wpautop() from the `the_content` filter, this re-adds it afterwards, |
|
329 |
* for subsequent `the_content` usage. |
|
330 |
* |
|
331 |
* @access private |
|
332 |
* |
|
333 |
* @since 5.0.0 |
|
334 |
* |
|
335 |
* @param string $content The post content running through this filter. |
|
336 |
* @return string The unmodified content. |
|
337 |
*/ |
|
338 |
function _restore_wpautop_hook( $content ) { |
|
339 |
$current_priority = has_filter( 'the_content', '_restore_wpautop_hook' ); |
|
340 |
|
|
341 |
add_filter( 'the_content', 'wpautop', $current_priority - 1 ); |
|
342 |
remove_filter( 'the_content', '_restore_wpautop_hook', $current_priority ); |
|
343 |
|
|
344 |
return $content; |
|
345 |
} |
|
346 |
|
|
347 |
/** |
|
348 |
* Returns the current version of the block format that the content string is using. |
|
349 |
* |
|
350 |
* If the string doesn't contain blocks, it returns 0. |
|
351 |
* |
|
352 |
* @since 5.0.0 |
|
353 |
* |
|
354 |
* @param string $content Content to test. |
|
355 |
* @return int The block format version is 1 if the content contains one or more blocks, 0 otherwise. |
|
356 |
*/ |
|
357 |
function block_version( $content ) { |
|
358 |
return has_blocks( $content ) ? 1 : 0; |
|
359 |
} |