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 / block-supports / layout.php

layout.php in Gutenberg 14.7.0, at lib/block-supports/layout.php

631 lines 22.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Layout block support flag.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Registers the layout block attribute for block types that support it.
10 *
11 * @param WP_Block_Type $block_type Block Type.
12 */
13 function gutenberg_register_layout_support( $block_type ) {
14 $support_layout = block_has_support( $block_type, array( '__experimentalLayout' ), false );
15 if ( $support_layout ) {
16 if ( ! $block_type->attributes ) {
17 $block_type->attributes = array();
18 }
19
20 if ( ! array_key_exists( 'layout', $block_type->attributes ) ) {
21 $block_type->attributes['layout'] = array(
22 'type' => 'object',
23 );
24 }
25 }
26 }
27
28 /**
29 * Generates the CSS corresponding to the provided layout.
30 *
31 * @param string $selector CSS selector.
32 * @param array $layout Layout object. The one that is passed has already checked
33 * the existence of default block layout.
34 * @param bool $has_block_gap_support Optional. Whether the theme has support for the block gap. Default false.
35 * @param string|string[]|null $gap_value Optional. The block gap value to apply. Default null.
36 * @param bool $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the editor. Default false.
37 * @param string $fallback_gap_value Optional. The block gap value to apply. Default '0.5em'.
38 * @param array|null $block_spacing Optional. Custom spacing set on the block. Default null.
39 * @return string CSS styles on success. Else, empty string.
40 */
41 function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em', $block_spacing = null ) {
42 $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default';
43 $layout_styles = array();
44
45 if ( 'default' === $layout_type ) {
46 if ( $has_block_gap_support ) {
47 if ( is_array( $gap_value ) ) {
48 $gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null;
49 }
50 if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
51 // Get spacing CSS variable from preset value if provided.
52 if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) {
53 $index_to_splice = strrpos( $gap_value, '|' ) + 1;
54 $slug = _wp_to_kebab_case( substr( $gap_value, $index_to_splice ) );
55 $gap_value = "var(--wp--preset--spacing--$slug)";
56 }
57
58 array_push(
59 $layout_styles,
60 array(
61 'selector' => "$selector > *",
62 'declarations' => array(
63 'margin-block-start' => '0',
64 'margin-block-end' => '0',
65 ),
66 ),
67 array(
68 'selector' => "$selector$selector > * + *",
69 'declarations' => array(
70 'margin-block-start' => $gap_value,
71 'margin-block-end' => '0',
72 ),
73 )
74 );
75 }
76 }
77 } elseif ( 'constrained' === $layout_type ) {
78 $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : '';
79 $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : '';
80 $justify_content = isset( $layout['justifyContent'] ) ? $layout['justifyContent'] : 'center';
81
82 $all_max_width_value = $content_size ? $content_size : $wide_size;
83 $wide_max_width_value = $wide_size ? $wide_size : $content_size;
84
85 // Make sure there is a single CSS rule, and all tags are stripped for security.
86 // TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched.
87 $all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] );
88 $wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] );
89
90 $margin_left = 'left' === $justify_content ? '0 !important' : 'auto !important';
91 $margin_right = 'right' === $justify_content ? '0 !important' : 'auto !important';
92
93 if ( $content_size || $wide_size ) {
94 array_push(
95 $layout_styles,
96 array(
97 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
98 'declarations' => array(
99 'max-width' => $all_max_width_value,
100 'margin-left' => $margin_left,
101 'margin-right' => $margin_right,
102 ),
103 ),
104 array(
105 'selector' => "$selector > .alignwide",
106 'declarations' => array( 'max-width' => $wide_max_width_value ),
107 ),
108 array(
109 'selector' => "$selector .alignfull",
110 'declarations' => array( 'max-width' => 'none' ),
111 )
112 );
113
114 if ( isset( $block_spacing ) ) {
115 $block_spacing_values = gutenberg_style_engine_get_styles(
116 array(
117 'spacing' => $block_spacing,
118 )
119 );
120
121 /*
122 * Handle negative margins for alignfull children of blocks with custom padding set.
123 * They're added separately because padding might only be set on one side.
124 */
125 if ( isset( $block_spacing_values['declarations']['padding-right'] ) ) {
126 $padding_right = $block_spacing_values['declarations']['padding-right'];
127 $layout_styles[] = array(
128 'selector' => "$selector > .alignfull",
129 'declarations' => array( 'margin-right' => "calc($padding_right * -1)" ),
130 );
131 }
132 if ( isset( $block_spacing_values['declarations']['padding-left'] ) ) {
133 $padding_left = $block_spacing_values['declarations']['padding-left'];
134 $layout_styles[] = array(
135 'selector' => "$selector > .alignfull",
136 'declarations' => array( 'margin-left' => "calc($padding_left * -1)" ),
137 );
138 }
139 }
140 }
141
142 if ( 'left' === $justify_content ) {
143 $layout_styles[] = array(
144 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
145 'declarations' => array( 'margin-left' => '0 !important' ),
146 );
147 }
148
149 if ( 'right' === $justify_content ) {
150 $layout_styles[] = array(
151 'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
152 'declarations' => array( 'margin-right' => '0 !important' ),
153 );
154 }
155
156 if ( $has_block_gap_support ) {
157 if ( is_array( $gap_value ) ) {
158 $gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null;
159 }
160 if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
161 // Get spacing CSS variable from preset value if provided.
162 if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) {
163 $index_to_splice = strrpos( $gap_value, '|' ) + 1;
164 $slug = _wp_to_kebab_case( substr( $gap_value, $index_to_splice ) );
165 $gap_value = "var(--wp--preset--spacing--$slug)";
166 }
167
168 array_push(
169 $layout_styles,
170 array(
171 'selector' => "$selector > *",
172 'declarations' => array(
173 'margin-block-start' => '0',
174 'margin-block-end' => '0',
175 ),
176 ),
177 array(
178 'selector' => "$selector$selector > * + *",
179 'declarations' => array(
180 'margin-block-start' => $gap_value,
181 'margin-block-end' => '0',
182 ),
183 )
184 );
185 }
186 }
187 } elseif ( 'flex' === $layout_type ) {
188 $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal';
189
190 $justify_content_options = array(
191 'left' => 'flex-start',
192 'right' => 'flex-end',
193 'center' => 'center',
194 );
195
196 $vertical_alignment_options = array(
197 'top' => 'flex-start',
198 'center' => 'center',
199 'bottom' => 'flex-end',
200 );
201
202 if ( 'horizontal' === $layout_orientation ) {
203 $justify_content_options += array( 'space-between' => 'space-between' );
204 }
205
206 if ( ! empty( $layout['flexWrap'] ) && 'nowrap' === $layout['flexWrap'] ) {
207 $layout_styles[] = array(
208 'selector' => $selector,
209 'declarations' => array( 'flex-wrap' => 'nowrap' ),
210 );
211 }
212
213 if ( $has_block_gap_support && isset( $gap_value ) ) {
214 $combined_gap_value = '';
215 $gap_sides = is_array( $gap_value ) ? array( 'top', 'left' ) : array( 'top' );
216
217 foreach ( $gap_sides as $gap_side ) {
218 $process_value = is_string( $gap_value ) ? $gap_value : _wp_array_get( $gap_value, array( $gap_side ), $fallback_gap_value );
219 // Get spacing CSS variable from preset value if provided.
220 if ( is_string( $process_value ) && str_contains( $process_value, 'var:preset|spacing|' ) ) {
221 $index_to_splice = strrpos( $process_value, '|' ) + 1;
222 $slug = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) );
223 $process_value = "var(--wp--preset--spacing--$slug)";
224 }
225 $combined_gap_value .= "$process_value ";
226 }
227 $gap_value = trim( $combined_gap_value );
228
229 if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
230 $layout_styles[] = array(
231 'selector' => $selector,
232 'declarations' => array( 'gap' => $gap_value ),
233 );
234 }
235 }
236
237 if ( 'horizontal' === $layout_orientation ) {
238 /*
239 * Add this style only if is not empty for backwards compatibility,
240 * since we intend to convert blocks that had flex layout implemented
241 * by custom css.
242 */
243 if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
244 $layout_styles[] = array(
245 'selector' => $selector,
246 'declarations' => array( 'justify-content' => $justify_content_options[ $layout['justifyContent'] ] ),
247 );
248 }
249
250 if ( ! empty( $layout['verticalAlignment'] ) && array_key_exists( $layout['verticalAlignment'], $vertical_alignment_options ) ) {
251 $layout_styles[] = array(
252 'selector' => $selector,
253 'declarations' => array( 'align-items' => $vertical_alignment_options[ $layout['verticalAlignment'] ] ),
254 );
255 }
256 } else {
257 $layout_styles[] = array(
258 'selector' => $selector,
259 'declarations' => array( 'flex-direction' => 'column' ),
260 );
261 if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
262 $layout_styles[] = array(
263 'selector' => $selector,
264 'declarations' => array( 'align-items' => $justify_content_options[ $layout['justifyContent'] ] ),
265 );
266 } else {
267 $layout_styles[] = array(
268 'selector' => $selector,
269 'declarations' => array( 'align-items' => 'flex-start' ),
270 );
271 }
272 }
273 }
274
275 if ( ! empty( $layout_styles ) ) {
276 /*
277 * Add to the style engine store to enqueue and render layout styles.
278 * Return compiled layout styles to retain backwards compatibility.
279 * Since https://github.com/WordPress/gutenberg/pull/42452,
280 * wp_enqueue_block_support_styles is no longer called in this block supports file.
281 */
282 return gutenberg_style_engine_get_stylesheet_from_css_rules(
283 $layout_styles,
284 array(
285 'context' => 'block-supports',
286 'prettify' => false,
287 )
288 );
289 }
290
291 return '';
292 }
293
294 /**
295 * Gets classname from last tag in a string of HTML.
296 *
297 * @param string $html markup to be processed.
298 * @return string String of inner wrapper classnames.
299 */
300 function gutenberg_get_classnames_from_last_tag( $html ) {
301 $tags = new WP_HTML_Tag_Processor( $html );
302 $last_classnames = '';
303
304 while ( $tags->next_tag() ) {
305 $last_classnames = $tags->get_attribute( 'class' );
306 }
307
308 return (string) $last_classnames;
309 }
310
311 /**
312 * Renders the layout config to the block wrapper.
313 *
314 * @param string $block_content Rendered block content.
315 * @param array $block Block object.
316 * @return string Filtered block content.
317 */
318 function gutenberg_render_layout_support_flag( $block_content, $block ) {
319 $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
320 $support_layout = block_has_support( $block_type, array( '__experimentalLayout' ), false );
321 $has_child_layout = isset( $block['attrs']['style']['layout']['selfStretch'] );
322
323 if ( ! $support_layout
324 && ! $has_child_layout ) {
325 return $block_content;
326 }
327
328 $outer_class_names = array();
329
330 if ( $has_child_layout && ( 'fixed' === $block['attrs']['style']['layout']['selfStretch'] || 'fill' === $block['attrs']['style']['layout']['selfStretch'] ) ) {
331
332 $container_content_class = wp_unique_id( 'wp-container-content-' );
333
334 $child_layout_styles = array();
335
336 if ( 'fixed' === $block['attrs']['style']['layout']['selfStretch'] && isset( $block['attrs']['style']['layout']['flexSize'] ) ) {
337 $child_layout_styles[] = array(
338 'selector' => ".$container_content_class",
339 'declarations' => array(
340 'flex-shrink' => '0',
341 'flex-basis' => $block['attrs']['style']['layout']['flexSize'],
342 'box-sizing' => 'border-box',
343 ),
344 );
345 } elseif ( 'fill' === $block['attrs']['style']['layout']['selfStretch'] ) {
346 $child_layout_styles[] = array(
347 'selector' => ".$container_content_class",
348 'declarations' => array(
349 'flex-grow' => '1',
350 ),
351 );
352 }
353
354 gutenberg_style_engine_get_stylesheet_from_css_rules(
355 $child_layout_styles,
356 array(
357 'context' => 'block-supports',
358 'prettify' => false,
359 )
360 );
361
362 $outer_class_names[] = $container_content_class;
363
364 }
365
366 // Return early if only child layout exists.
367 if ( ! $support_layout && ! empty( $outer_class_names ) ) {
368 $content = new WP_HTML_Tag_Processor( $block_content );
369 $content->next_tag();
370 $content->add_class( implode( ' ', $outer_class_names ) );
371 return (string) $content;
372 }
373
374 $block_gap = gutenberg_get_global_settings( array( 'spacing', 'blockGap' ) );
375 $global_layout_settings = gutenberg_get_global_settings( array( 'layout' ) );
376 $has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false;
377 $default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() );
378 $used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout;
379
380 if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] ) {
381 if ( ! $global_layout_settings ) {
382 return $block_content;
383 }
384 }
385
386 $class_names = array();
387 $layout_definitions = _wp_array_get( $global_layout_settings, array( 'definitions' ), array() );
388 $container_class = wp_unique_id( 'wp-container-' );
389 $layout_classname = '';
390
391 // Set the correct layout type for blocks using legacy content width.
392 if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ) {
393 $used_layout['type'] = 'constrained';
394 }
395
396 if (
397 gutenberg_get_global_settings( array( 'useRootPaddingAwareAlignments' ) ) &&
398 isset( $used_layout['type'] ) &&
399 'constrained' === $used_layout['type']
400 ) {
401 $class_names[] = 'has-global-padding';
402 }
403
404 /*
405 * The following section was added to reintroduce a small set of layout classnames that were
406 * removed in the 5.9 release (https://github.com/WordPress/gutenberg/issues/38719). It is
407 * not intended to provide an extended set of classes to match all block layout attributes
408 * here.
409 */
410 if ( ! empty( $block['attrs']['layout']['orientation'] ) ) {
411 $class_names[] = 'is-' . sanitize_title( $block['attrs']['layout']['orientation'] );
412 }
413
414 if ( ! empty( $block['attrs']['layout']['justifyContent'] ) ) {
415 $class_names[] = 'is-content-justification-' . sanitize_title( $block['attrs']['layout']['justifyContent'] );
416 }
417
418 if ( ! empty( $block['attrs']['layout']['flexWrap'] ) && 'nowrap' === $block['attrs']['layout']['flexWrap'] ) {
419 $class_names[] = 'is-nowrap';
420 }
421
422 // Get classname for layout type.
423 if ( isset( $used_layout['type'] ) ) {
424 $layout_classname = _wp_array_get( $layout_definitions, array( $used_layout['type'], 'className' ), '' );
425 } else {
426 $layout_classname = _wp_array_get( $layout_definitions, array( 'default', 'className' ), '' );
427 }
428
429 if ( $layout_classname && is_string( $layout_classname ) ) {
430 $class_names[] = sanitize_title( $layout_classname );
431 }
432
433 /*
434 * Only generate Layout styles if the theme has not opted-out.
435 * Attribute-based Layout classnames are output in all cases.
436 */
437 if ( ! current_theme_supports( 'disable-layout-styles' ) ) {
438
439 $gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) );
440
441 /*
442 * Skip if gap value contains unsupported characters.
443 * Regex for CSS value borrowed from `safecss_filter_attr`, and used here
444 * to only match against the value, not the CSS attribute.
445 */
446 if ( is_array( $gap_value ) ) {
447 foreach ( $gap_value as $key => $value ) {
448 $gap_value[ $key ] = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value;
449 }
450 } else {
451 $gap_value = $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
452 }
453
454 $fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' );
455 $block_spacing = _wp_array_get( $block, array( 'attrs', 'style', 'spacing' ), null );
456
457 /*
458 * If a block's block.json skips serialization for spacing or spacing.blockGap,
459 * don't apply the user-defined value to the styles.
460 */
461 $should_skip_gap_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' );
462
463 $style = gutenberg_get_layout_style(
464 ".$container_class.$container_class",
465 $used_layout,
466 $has_block_gap_support,
467 $gap_value,
468 $should_skip_gap_serialization,
469 $fallback_gap_value,
470 $block_spacing
471 );
472
473 // Only add container class and enqueue block support styles if unique styles were generated.
474 if ( ! empty( $style ) ) {
475 $class_names[] = $container_class;
476 }
477 }
478
479 $content_with_outer_classnames = '';
480
481 if ( ! empty( $outer_class_names ) ) {
482 $content_with_outer_classnames = new WP_HTML_Tag_Processor( $block_content );
483 $content_with_outer_classnames->next_tag();
484 foreach ( $outer_class_names as $outer_class_name ) {
485 $content_with_outer_classnames->add_class( $outer_class_name );
486 }
487
488 $content_with_outer_classnames = (string) $content_with_outer_classnames;
489 }
490
491 /**
492 * The first chunk of innerContent contains the block markup up until the inner blocks start.
493 * We want to target the opening tag of the inner blocks wrapper, which is the last tag in that chunk.
494 */
495 $inner_content_classnames = isset( $block['innerContent'][0] ) && 'string' === gettype( $block['innerContent'][0] ) ? gutenberg_get_classnames_from_last_tag( $block['innerContent'][0] ) : '';
496
497 $content = $content_with_outer_classnames ? new WP_HTML_Tag_Processor( $content_with_outer_classnames ) : new WP_HTML_Tag_Processor( $block_content );
498
499 if ( $inner_content_classnames ) {
500 $content->next_tag( array( 'class_name' => $inner_content_classnames ) );
501 foreach ( $class_names as $class_name ) {
502 $content->add_class( $class_name );
503 }
504 } else {
505 $content->next_tag();
506 foreach ( $class_names as $class_name ) {
507 $content->add_class( $class_name );
508 }
509 }
510
511 return (string) $content;
512 }
513
514 // Register the block support. (overrides core one).
515 WP_Block_Supports::get_instance()->register(
516 'layout',
517 array(
518 'register_attribute' => 'gutenberg_register_layout_support',
519 )
520 );
521
522 if ( function_exists( 'wp_render_layout_support_flag' ) ) {
523 remove_filter( 'render_block', 'wp_render_layout_support_flag' );
524 }
525 add_filter( 'render_block', 'gutenberg_render_layout_support_flag', 10, 2 );
526
527 /**
528 * For themes without theme.json file, make sure
529 * to restore the inner div for the group block
530 * to avoid breaking styles relying on that div.
531 *
532 * @param string $block_content Rendered block content.
533 * @param array $block Block object.
534 * @return string Filtered block content.
535 */
536 function gutenberg_restore_group_inner_container( $block_content, $block ) {
537 $tag_name = isset( $block['attrs']['tagName'] ) ? $block['attrs']['tagName'] : 'div';
538 $group_with_inner_container_regex = sprintf(
539 '/(^\s*<%1$s\b[^>]*wp-block-group(\s|")[^>]*>)(\s*<div\b[^>]*wp-block-group__inner-container(\s|")[^>]*>)((.|\S|\s)*)/U',
540 preg_quote( $tag_name, '/' )
541 );
542 if (
543 wp_theme_has_theme_json() ||
544 1 === preg_match( $group_with_inner_container_regex, $block_content ) ||
545 ( isset( $block['attrs']['layout']['type'] ) && 'flex' === $block['attrs']['layout']['type'] )
546 ) {
547 return $block_content;
548 }
549
550 $replace_regex = sprintf(
551 '/(^\s*<%1$s\b[^>]*wp-block-group[^>]*>)(.*)(<\/%1$s>\s*$)/ms',
552 preg_quote( $tag_name, '/' )
553 );
554 $updated_content = preg_replace_callback(
555 $replace_regex,
556 function( $matches ) {
557 return $matches[1] . '<div class="wp-block-group__inner-container">' . $matches[2] . '</div>' . $matches[3];
558 },
559 $block_content
560 );
561 return $updated_content;
562 }
563
564 if ( function_exists( 'wp_restore_group_inner_container' ) ) {
565 remove_filter( 'render_block', 'wp_restore_group_inner_container', 10, 2 );
566 remove_filter( 'render_block_core/group', 'wp_restore_group_inner_container', 10, 2 );
567 }
568 add_filter( 'render_block_core/group', 'gutenberg_restore_group_inner_container', 10, 2 );
569
570
571 /**
572 * For themes without theme.json file, make sure
573 * to restore the outer div for the aligned image block
574 * to avoid breaking styles relying on that div.
575 *
576 * @param string $block_content Rendered block content.
577 * @param array $block Block object.
578 * @return string Filtered block content.
579 */
580 function gutenberg_restore_image_outer_container( $block_content, $block ) {
581 $image_with_align = "
582 /# 1) everything up to the class attribute contents
583 (
584 ^\s*
585 <figure\b
586 [^>]*
587 \bclass=
588 [\"']
589 )
590 # 2) the class attribute contents
591 (
592 [^\"']*
593 \bwp-block-image\b
594 [^\"']*
595 \b(?:alignleft|alignright|aligncenter)\b
596 [^\"']*
597 )
598 # 3) everything after the class attribute contents
599 (
600 [\"']
601 [^>]*
602 >
603 .*
604 <\/figure>
605 )/iUx";
606
607 if (
608 wp_theme_has_theme_json() ||
609 0 === preg_match( $image_with_align, $block_content, $matches )
610 ) {
611 return $block_content;
612 }
613
614 $wrapper_classnames = array( 'wp-block-image' );
615
616 // If the block has a classNames attribute these classnames need to be removed from the content and added back
617 // to the new wrapper div also.
618 if ( ! empty( $block['attrs']['className'] ) ) {
619 $wrapper_classnames = array_merge( $wrapper_classnames, explode( ' ', $block['attrs']['className'] ) );
620 }
621 $content_classnames = explode( ' ', $matches[2] );
622 $filtered_content_classnames = array_diff( $content_classnames, $wrapper_classnames );
623
624 return '<div class="' . implode( ' ', $wrapper_classnames ) . '">' . $matches[1] . implode( ' ', $filtered_content_classnames ) . $matches[3] . '</div>';
625 }
626
627 if ( function_exists( 'wp_restore_image_outer_container' ) ) {
628 remove_filter( 'render_block_core/image', 'wp_restore_image_outer_container', 10, 2 );
629 }
630 add_filter( 'render_block_core/image', 'gutenberg_restore_image_outer_container', 10, 2 );
631