PluginProbe
Gutenberg / 4.2.0
Gutenberg v4.2.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 7.4.0 All 402 releases
gutenberg / lib / blocks.php

blocks.php in Gutenberg 4.2.0, at lib/blocks.php

322 lines 9.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Functions related to editor blocks for the Gutenberg editor plugin.
4 *
5 * @package gutenberg
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 die( 'Silence is golden.' );
10 }
11
12 if ( ! function_exists( 'register_block_type' ) ) {
13 /**
14 * Registers a block type.
15 *
16 * @since 0.1.0
17 * @since 0.6.0 Now also accepts a WP_Block_Type instance as first parameter.
18 *
19 * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
20 * complete WP_Block_Type instance. In case a WP_Block_Type
21 * is provided, the $args parameter will be ignored.
22 * @param array $args {
23 * Optional. Array of block type arguments. Any arguments may be defined, however the
24 * ones described below are supported by default. Default empty array.
25 *
26 * @type callable $render_callback Callback used to render blocks of this block type.
27 * }
28 * @return WP_Block_Type|false The registered block type on success, or false on failure.
29 */
30 function register_block_type( $name, $args = array() ) {
31 return WP_Block_Type_Registry::get_instance()->register( $name, $args );
32 }
33 }
34
35 if ( ! function_exists( 'unregister_block_type' ) ) {
36 /**
37 * Unregisters a block type.
38 *
39 * @since 0.1.0
40 * @since 0.6.0 Now also accepts a WP_Block_Type instance as first parameter.
41 *
42 * @param string|WP_Block_Type $name Block type name including namespace, or alternatively a
43 * complete WP_Block_Type instance.
44 * @return WP_Block_Type|false The unregistered block type on success, or false on failure.
45 */
46 function unregister_block_type( $name ) {
47 return WP_Block_Type_Registry::get_instance()->unregister( $name );
48 }
49 }
50
51 if ( ! function_exists( 'gutenberg_parse_blocks' ) ) {
52 /**
53 * Parses blocks out of a content string.
54 *
55 * @since 0.5.0
56 *
57 * @param string $content Post content.
58 * @return array Array of parsed block objects.
59 */
60 function gutenberg_parse_blocks( $content ) {
61 /*
62 * If there are no blocks in the content, return a single block, rather
63 * than wasting time trying to parse the string.
64 */
65 if ( ! has_blocks( $content ) ) {
66 return array(
67 array(
68 'blockName' => null,
69 'attrs' => array(),
70 'innerBlocks' => array(),
71 'innerHTML' => $content,
72 ),
73 );
74 }
75
76 /**
77 * Filter to allow plugins to replace the server-side block parser
78 *
79 * @since 3.8.0
80 *
81 * @param string $parser_class Name of block parser class
82 */
83 $parser_class = apply_filters( 'block_parser_class', 'WP_Block_Parser' );
84 // Load default block parser for server-side parsing if the default parser class is being used.
85 if ( 'WP_Block_Parser' === $parser_class ) {
86 require_once dirname( __FILE__ ) . '/../packages/block-serialization-default-parser/parser.php';
87 }
88 $parser = new $parser_class();
89 return $parser->parse( $content );
90 }
91 }
92
93 if ( ! function_exists( 'get_dynamic_block_names' ) ) {
94 /**
95 * Returns an array of the names of all registered dynamic block types.
96 *
97 * @return array Array of dynamic block names.
98 */
99 function get_dynamic_block_names() {
100 $dynamic_block_names = array();
101
102 $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
103 foreach ( $block_types as $block_type ) {
104 if ( $block_type->is_dynamic() ) {
105 $dynamic_block_names[] = $block_type->name;
106 }
107 }
108
109 return $dynamic_block_names;
110 }
111 }
112
113 if ( ! function_exists( 'get_dynamic_blocks_regex' ) ) {
114 /**
115 * Retrieve the dynamic blocks regular expression for searching.
116 *
117 * @since 3.6.0
118 *
119 * @return string
120 */
121 function get_dynamic_blocks_regex() {
122 $dynamic_block_names = get_dynamic_block_names();
123 $dynamic_block_pattern = (
124 '/<!--\s+wp:(' .
125 str_replace(
126 '/',
127 '\/', // Escape namespace, not handled by preg_quote.
128 str_replace(
129 'core/',
130 '(?:core/)?', // Allow implicit core namespace, but don't capture.
131 implode(
132 '|', // Join block names into capture group alternation.
133 array_map(
134 'preg_quote', // Escape block name for regular expression.
135 $dynamic_block_names
136 )
137 )
138 )
139 ) .
140 ')(\s+(\{.*?\}))?\s+(\/)?-->/'
141 );
142
143 return $dynamic_block_pattern;
144 }
145 }
146
147 /**
148 * Renders a single block into a HTML string.
149 *
150 * @since 1.9.0
151 *
152 * @param array $block A single parsed block object.
153 * @return string String of rendered HTML.
154 */
155 function gutenberg_render_block( $block ) {
156 $block_name = isset( $block['blockName'] ) ? $block['blockName'] : null;
157 $attributes = is_array( $block['attrs'] ) ? $block['attrs'] : array();
158 $raw_content = isset( $block['innerHTML'] ) ? $block['innerHTML'] : null;
159
160 if ( $block_name ) {
161 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
162 if ( null !== $block_type && $block_type->is_dynamic() ) {
163 return $block_type->render( $attributes );
164 }
165 }
166
167 if ( $raw_content ) {
168 return $raw_content;
169 }
170
171 return '';
172 }
173
174 if ( ! function_exists( 'do_blocks' ) ) {
175 /**
176 * Parses dynamic blocks out of `post_content` and re-renders them.
177 *
178 * @since 0.1.0
179 * @global WP_Post $post The post to edit.
180 *
181 * @param string $content Post content.
182 * @return string Updated post content.
183 */
184 function do_blocks( $content ) {
185 global $post;
186
187 $rendered_content = '';
188 $dynamic_block_pattern = get_dynamic_blocks_regex();
189
190 /*
191 * Back up global post, to restore after render callback.
192 * Allows callbacks to run new WP_Query instances without breaking the global post.
193 */
194 $global_post = $post;
195
196 while ( preg_match( $dynamic_block_pattern, $content, $block_match, PREG_OFFSET_CAPTURE ) ) {
197 $opening_tag = $block_match[0][0];
198 $offset = $block_match[0][1];
199 $block_name = $block_match[1][0];
200 $is_self_closing = isset( $block_match[4] );
201
202 // Reset attributes JSON to prevent scope bleed from last iteration.
203 $block_attributes_json = null;
204 if ( isset( $block_match[3] ) ) {
205 $block_attributes_json = $block_match[3][0];
206 }
207
208 // Since content is a working copy since the last match, append to
209 // rendered content up to the matched offset...
210 $rendered_content .= substr( $content, 0, $offset );
211
212 // ...then update the working copy of content.
213 $content = substr( $content, $offset + strlen( $opening_tag ) );
214
215 // Make implicit core namespace explicit.
216 $is_implicit_core_namespace = ( false === strpos( $block_name, '/' ) );
217 $normalized_block_name = $is_implicit_core_namespace ? 'core/' . $block_name : $block_name;
218
219 // Find registered block type. We can assume it exists since we use the
220 // `get_dynamic_block_names` function as a source for pattern matching.
221 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $normalized_block_name );
222
223 // Attempt to parse attributes JSON, if available.
224 $attributes = array();
225 if ( ! empty( $block_attributes_json ) ) {
226 $decoded_attributes = json_decode( $block_attributes_json, true );
227 if ( ! is_null( $decoded_attributes ) ) {
228 $attributes = $decoded_attributes;
229 }
230 }
231
232 $inner_content = '';
233
234 if ( ! $is_self_closing ) {
235 $end_tag_pattern = '/<!--\s+\/wp:' . preg_quote( $block_name, '/' ) . '\s+-->/';
236 if ( ! preg_match( $end_tag_pattern, $content, $block_match_end, PREG_OFFSET_CAPTURE ) ) {
237 // If no closing tag is found, abort all matching, and continue
238 // to append remainder of content to rendered output.
239 break;
240 }
241
242 // Update content to omit text up to and including closing tag.
243 $end_tag = $block_match_end[0][0];
244 $end_offset = $block_match_end[0][1];
245
246 $inner_content = substr( $content, 0, $end_offset );
247 $content = substr( $content, $end_offset + strlen( $end_tag ) );
248 }
249
250 // Replace dynamic block with server-rendered output.
251 $rendered_content .= $block_type->render( $attributes, $inner_content );
252
253 // Restore global $post.
254 $post = $global_post;
255 }
256
257 // Append remaining unmatched content.
258 $rendered_content .= $content;
259
260 // Strip remaining block comment demarcations.
261 $rendered_content = preg_replace( '/<!--\s+\/?wp:.*?-->\r?\n?/m', '', $rendered_content );
262
263 return $rendered_content;
264 }
265
266 add_filter( 'the_content', 'do_blocks', 7 ); // BEFORE do_shortcode() and oembed.
267 }
268
269 if ( ! function_exists( 'strip_dynamic_blocks' ) ) {
270 /**
271 * Remove all dynamic blocks from the given content.
272 *
273 * @since 3.6.0
274 *
275 * @param string $content Content of the current post.
276 * @return string
277 */
278 function strip_dynamic_blocks( $content ) {
279 return preg_replace( get_dynamic_blocks_regex(), '', $content );
280 }
281 }
282
283 if ( ! function_exists( 'strip_dynamic_blocks_add_filter' ) ) {
284 /**
285 * Adds the content filter to strip dynamic blocks from excerpts.
286 *
287 * It's a bit hacky for now, but once this gets merged into core the function
288 * can just be called in `wp_trim_excerpt()`.
289 *
290 * @since 3.6.0
291 *
292 * @param string $text Excerpt.
293 * @return string
294 */
295 function strip_dynamic_blocks_add_filter( $text ) {
296 add_filter( 'the_content', 'strip_dynamic_blocks', 6 );
297
298 return $text;
299 }
300 add_filter( 'get_the_excerpt', 'strip_dynamic_blocks_add_filter', 9 ); // Before wp_trim_excerpt().
301 }
302
303 if ( ! function_exists( 'strip_dynamic_blocks_remove_filter' ) ) {
304 /**
305 * Removes the content filter to strip dynamic blocks from excerpts.
306 *
307 * It's a bit hacky for now, but once this gets merged into core the function
308 * can just be called in `wp_trim_excerpt()`.
309 *
310 * @since 3.6.0
311 *
312 * @param string $text Excerpt.
313 * @return string
314 */
315 function strip_dynamic_blocks_remove_filter( $text ) {
316 remove_filter( 'the_content', 'strip_dynamic_blocks', 6 );
317
318 return $text;
319 }
320 add_filter( 'wp_trim_excerpt', 'strip_dynamic_blocks_remove_filter', 0 ); // Before all other.
321 }
322