|
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 } |