| @@ -10,9 +10,9 @@ | ||
| 10 | 10 | * |
| 11 | 11 | * @param WP_Block_Type $block_type Block Type. |
| 12 | 12 | */ |
| 13 | 13 | function gutenberg_register_layout_support( $block_type ) { |
| 14 | - $support_layout = gutenberg_block_has_support( $block_type, array( '__experimentalLayout' ), false ); | |
| 14 | + $support_layout = block_has_support( $block_type, array( '__experimentalLayout' ), false ); | |
| 15 | 15 | if ( $support_layout ) { |
| 16 | 16 | if ( ! $block_type->attributes ) { |
| 17 | 17 | $block_type->attributes = array(); |
| 18 | 18 | } |
| @@ -27,23 +27,59 @@ | ||
| 27 | 27 | |
| 28 | 28 | /** |
| 29 | 29 | * Generates the CSS corresponding to the provided layout. |
| 30 | 30 | * |
| 31 | - * @param string $selector CSS selector. | |
| 32 | - * @param array $layout Layout object. The one that is passed has already checked the existence of default block layout. | |
| 33 | - * @param boolean $has_block_gap_support Whether the theme has support for the block gap. | |
| 34 | - * @param string $gap_value The block gap value to apply. | |
| 35 | - * | |
| 36 | - * @return string CSS style. | |
| 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. | |
| 37 | 40 | */ |
| 38 | -function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null ) { | |
| 39 | - $layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default'; | |
| 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(); | |
| 40 | 44 | |
| 41 | - $style = ''; | |
| 42 | 45 | if ( 'default' === $layout_type ) { |
| 43 | - $content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : ''; | |
| 44 | - $wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : ''; | |
| 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 | + } | |
| 45 | 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 | + | |
| 46 | 82 | $all_max_width_value = $content_size ? $content_size : $wide_size; |
| 47 | 83 | $wide_max_width_value = $wide_size ? $wide_size : $content_size; |
| 48 | 84 | |
| 49 | 85 | // Make sure there is a single CSS rule, and all tags are stripped for security. |
| @@ -50,26 +86,104 @@ | ||
| 50 | 86 | // TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched. |
| 51 | 87 | $all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] ); |
| 52 | 88 | $wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] ); |
| 53 | 89 | |
| 54 | - $style = ''; | |
| 90 | + $margin_left = 'left' === $justify_content ? '0 !important' : 'auto !important'; | |
| 91 | + $margin_right = 'right' === $justify_content ? '0 !important' : 'auto !important'; | |
| 92 | + | |
| 55 | 93 | if ( $content_size || $wide_size ) { |
| 56 | - $style = "$selector > * {"; | |
| 57 | - $style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';'; | |
| 58 | - $style .= 'margin-left: auto !important;'; | |
| 59 | - $style .= 'margin-right: auto !important;'; | |
| 60 | - $style .= '}'; | |
| 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 | + ); | |
| 61 | 113 | |
| 62 | - $style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}'; | |
| 63 | - $style .= "$selector .alignfull { max-width: none; }"; | |
| 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 | + } | |
| 64 | 140 | } |
| 65 | 141 | |
| 66 | - $style .= "$selector .alignleft { float: left; margin-right: 2em; }"; | |
| 67 | - $style .= "$selector .alignright { float: right; margin-left: 2em; }"; | |
| 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 | + | |
| 68 | 156 | if ( $has_block_gap_support ) { |
| 69 | - $gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap )'; | |
| 70 | - $style .= "$selector > * { margin-top: 0; margin-bottom: 0; }"; | |
| 71 | - $style .= "$selector > * + * { margin-top: $gap_style; margin-bottom: 0; }"; | |
| 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 | + } | |
| 72 | 186 | } |
| 73 | 187 | } elseif ( 'flex' === $layout_type ) { |
| 74 | 188 | $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal'; |
| 75 | 189 | |
| @@ -78,53 +192,124 @@ | ||
| 78 | 192 | 'right' => 'flex-end', |
| 79 | 193 | 'center' => 'center', |
| 80 | 194 | ); |
| 81 | 195 | |
| 196 | + $vertical_alignment_options = array( | |
| 197 | + 'top' => 'flex-start', | |
| 198 | + 'center' => 'center', | |
| 199 | + 'bottom' => 'flex-end', | |
| 200 | + ); | |
| 201 | + | |
| 82 | 202 | if ( 'horizontal' === $layout_orientation ) { |
| 83 | 203 | $justify_content_options += array( 'space-between' => 'space-between' ); |
| 84 | 204 | } |
| 85 | 205 | |
| 86 | - $flex_wrap_options = array( 'wrap', 'nowrap' ); | |
| 87 | - $flex_wrap = ! empty( $layout['flexWrap'] ) && in_array( $layout['flexWrap'], $flex_wrap_options, true ) ? | |
| 88 | - $layout['flexWrap'] : | |
| 89 | - 'wrap'; | |
| 206 | + if ( ! empty( $layout['flexWrap'] ) && 'nowrap' === $layout['flexWrap'] ) { | |
| 207 | + $layout_styles[] = array( | |
| 208 | + 'selector' => $selector, | |
| 209 | + 'declarations' => array( 'flex-wrap' => 'nowrap' ), | |
| 210 | + ); | |
| 211 | + } | |
| 90 | 212 | |
| 91 | - $style = "$selector {"; | |
| 92 | - $style .= 'display: flex;'; | |
| 93 | - if ( $has_block_gap_support ) { | |
| 94 | - $gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap, 0.5em )'; | |
| 95 | - $style .= "gap: $gap_style;"; | |
| 96 | - } else { | |
| 97 | - $style .= 'gap: 0.5em;'; | |
| 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 | + } | |
| 98 | 235 | } |
| 99 | - $style .= "flex-wrap: $flex_wrap;"; | |
| 236 | + | |
| 100 | 237 | if ( 'horizontal' === $layout_orientation ) { |
| 101 | - $style .= 'align-items: center;'; | |
| 102 | - /** | |
| 238 | + /* | |
| 103 | 239 | * Add this style only if is not empty for backwards compatibility, |
| 104 | 240 | * since we intend to convert blocks that had flex layout implemented |
| 105 | 241 | * by custom css. |
| 106 | 242 | */ |
| 107 | 243 | if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { |
| 108 | - $style .= "justify-content: {$justify_content_options[ $layout['justifyContent'] ]};"; | |
| 244 | + $layout_styles[] = array( | |
| 245 | + 'selector' => $selector, | |
| 246 | + 'declarations' => array( 'justify-content' => $justify_content_options[ $layout['justifyContent'] ] ), | |
| 247 | + ); | |
| 109 | 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 | + } | |
| 110 | 256 | } else { |
| 111 | - $style .= 'flex-direction: column;'; | |
| 257 | + $layout_styles[] = array( | |
| 258 | + 'selector' => $selector, | |
| 259 | + 'declarations' => array( 'flex-direction' => 'column' ), | |
| 260 | + ); | |
| 112 | 261 | if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) { |
| 113 | - $style .= "align-items: {$justify_content_options[ $layout['justifyContent'] ]};"; | |
| 262 | + $layout_styles[] = array( | |
| 263 | + 'selector' => $selector, | |
| 264 | + 'declarations' => array( 'align-items' => $justify_content_options[ $layout['justifyContent'] ] ), | |
| 265 | + ); | |
| 114 | 266 | } else { |
| 115 | - $style .= 'align-items: flex-start;'; | |
| 267 | + $layout_styles[] = array( | |
| 268 | + 'selector' => $selector, | |
| 269 | + 'declarations' => array( 'align-items' => 'flex-start' ), | |
| 270 | + ); | |
| 116 | 271 | } |
| 117 | 272 | } |
| 118 | - $style .= '}'; | |
| 273 | + } | |
| 119 | 274 | |
| 120 | - $style .= "$selector > * { margin: 0; }"; | |
| 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 | + ); | |
| 121 | 289 | } |
| 122 | 290 | |
| 123 | - return $style; | |
| 291 | + return ''; | |
| 124 | 292 | } |
| 125 | 293 | |
| 126 | 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 | +/** | |
| 127 | 312 | * Renders the layout config to the block wrapper. |
| 128 | 313 | * |
| 129 | 314 | * @param string $block_content Rendered block content. |
| 130 | 315 | * @param array $block Block object. |
| @@ -131,53 +316,137 @@ | ||
| 131 | 316 | * @return string Filtered block content. |
| 132 | 317 | */ |
| 133 | 318 | function gutenberg_render_layout_support_flag( $block_content, $block ) { |
| 134 | 319 | $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); |
| 135 | - $support_layout = gutenberg_block_has_support( $block_type, array( '__experimentalLayout' ), false ); | |
| 320 | + $support_layout = block_has_support( $block_type, array( '__experimentalLayout' ), false ); | |
| 136 | 321 | |
| 137 | 322 | if ( ! $support_layout ) { |
| 138 | 323 | return $block_content; |
| 139 | 324 | } |
| 140 | 325 | |
| 141 | - $block_gap = wp_get_global_settings( array( 'spacing', 'blockGap' ) ); | |
| 142 | - $default_layout = wp_get_global_settings( array( 'layout' ) ); | |
| 143 | - $has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false; | |
| 144 | - $default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() ); | |
| 145 | - $used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout; | |
| 326 | + $block_gap = gutenberg_get_global_settings( array( 'spacing', 'blockGap' ) ); | |
| 327 | + $global_layout_settings = gutenberg_get_global_settings( array( 'layout' ) ); | |
| 328 | + $has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false; | |
| 329 | + $default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() ); | |
| 330 | + $used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout; | |
| 331 | + | |
| 146 | 332 | if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] ) { |
| 147 | - if ( ! $default_layout ) { | |
| 333 | + if ( ! $global_layout_settings ) { | |
| 148 | 334 | return $block_content; |
| 149 | 335 | } |
| 150 | - $used_layout = $default_layout; | |
| 151 | 336 | } |
| 152 | 337 | |
| 153 | - $id = uniqid(); | |
| 154 | - $gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) ); | |
| 155 | - // Skip if gap value contains unsupported characters. | |
| 156 | - // Regex for CSS value borrowed from `safecss_filter_attr`, and used here | |
| 157 | - // because we only want to match against the value, not the CSS attribute. | |
| 158 | - $gap_value = preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value; | |
| 159 | - $style = gutenberg_get_layout_style( ".wp-container-$id", $used_layout, $has_block_gap_support, $gap_value ); | |
| 160 | - // This assumes the hook only applies to blocks with a single wrapper. | |
| 161 | - // I think this is a reasonable limitation for that particular hook. | |
| 162 | - $content = preg_replace( | |
| 163 | - '/' . preg_quote( 'class="', '/' ) . '/', | |
| 164 | - 'class="wp-container-' . $id . ' ', | |
| 165 | - $block_content, | |
| 166 | - 1 | |
| 167 | - ); | |
| 338 | + $class_names = array(); | |
| 339 | + $layout_definitions = _wp_array_get( $global_layout_settings, array( 'definitions' ), array() ); | |
| 340 | + $container_class = wp_unique_id( 'wp-container-' ); | |
| 341 | + $layout_classname = ''; | |
| 168 | 342 | |
| 169 | - // Ideally styles should be loaded in the head, but blocks may be parsed | |
| 170 | - // after that, so loading in the footer for now. | |
| 171 | - // See https://core.trac.wordpress.org/ticket/53494. | |
| 172 | - add_action( | |
| 173 | - 'wp_footer', | |
| 174 | - function () use ( $style ) { | |
| 175 | - echo '<style>' . $style . '</style>'; | |
| 343 | + // Set the correct layout type for blocks using legacy content width. | |
| 344 | + if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ) { | |
| 345 | + $used_layout['type'] = 'constrained'; | |
| 346 | + } | |
| 347 | + | |
| 348 | + if ( | |
| 349 | + gutenberg_get_global_settings( array( 'useRootPaddingAwareAlignments' ) ) && | |
| 350 | + isset( $used_layout['type'] ) && | |
| 351 | + 'constrained' === $used_layout['type'] | |
| 352 | + ) { | |
| 353 | + $class_names[] = 'has-global-padding'; | |
| 354 | + } | |
| 355 | + | |
| 356 | + /* | |
| 357 | + * The following section was added to reintroduce a small set of layout classnames that were | |
| 358 | + * removed in the 5.9 release (https://github.com/WordPress/gutenberg/issues/38719). It is | |
| 359 | + * not intended to provide an extended set of classes to match all block layout attributes | |
| 360 | + * here. | |
| 361 | + */ | |
| 362 | + if ( ! empty( $block['attrs']['layout']['orientation'] ) ) { | |
| 363 | + $class_names[] = 'is-' . sanitize_title( $block['attrs']['layout']['orientation'] ); | |
| 364 | + } | |
| 365 | + | |
| 366 | + if ( ! empty( $block['attrs']['layout']['justifyContent'] ) ) { | |
| 367 | + $class_names[] = 'is-content-justification-' . sanitize_title( $block['attrs']['layout']['justifyContent'] ); | |
| 368 | + } | |
| 369 | + | |
| 370 | + if ( ! empty( $block['attrs']['layout']['flexWrap'] ) && 'nowrap' === $block['attrs']['layout']['flexWrap'] ) { | |
| 371 | + $class_names[] = 'is-nowrap'; | |
| 372 | + } | |
| 373 | + | |
| 374 | + // Get classname for layout type. | |
| 375 | + if ( isset( $used_layout['type'] ) ) { | |
| 376 | + $layout_classname = _wp_array_get( $layout_definitions, array( $used_layout['type'], 'className' ), '' ); | |
| 377 | + } else { | |
| 378 | + $layout_classname = _wp_array_get( $layout_definitions, array( 'default', 'className' ), '' ); | |
| 379 | + } | |
| 380 | + | |
| 381 | + if ( $layout_classname && is_string( $layout_classname ) ) { | |
| 382 | + $class_names[] = sanitize_title( $layout_classname ); | |
| 383 | + } | |
| 384 | + | |
| 385 | + /* | |
| 386 | + * Only generate Layout styles if the theme has not opted-out. | |
| 387 | + * Attribute-based Layout classnames are output in all cases. | |
| 388 | + */ | |
| 389 | + if ( ! current_theme_supports( 'disable-layout-styles' ) ) { | |
| 390 | + | |
| 391 | + $gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) ); | |
| 392 | + | |
| 393 | + /* | |
| 394 | + * Skip if gap value contains unsupported characters. | |
| 395 | + * Regex for CSS value borrowed from `safecss_filter_attr`, and used here | |
| 396 | + * to only match against the value, not the CSS attribute. | |
| 397 | + */ | |
| 398 | + if ( is_array( $gap_value ) ) { | |
| 399 | + foreach ( $gap_value as $key => $value ) { | |
| 400 | + $gap_value[ $key ] = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value; | |
| 401 | + } | |
| 402 | + } else { | |
| 403 | + $gap_value = $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value; | |
| 176 | 404 | } |
| 177 | - ); | |
| 178 | 405 | |
| 179 | - return $content; | |
| 406 | + $fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' ); | |
| 407 | + $block_spacing = _wp_array_get( $block, array( 'attrs', 'style', 'spacing' ), null ); | |
| 408 | + | |
| 409 | + /* | |
| 410 | + * If a block's block.json skips serialization for spacing or spacing.blockGap, | |
| 411 | + * don't apply the user-defined value to the styles. | |
| 412 | + */ | |
| 413 | + $should_skip_gap_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' ); | |
| 414 | + | |
| 415 | + $style = gutenberg_get_layout_style( | |
| 416 | + ".$container_class.$container_class", | |
| 417 | + $used_layout, | |
| 418 | + $has_block_gap_support, | |
| 419 | + $gap_value, | |
| 420 | + $should_skip_gap_serialization, | |
| 421 | + $fallback_gap_value, | |
| 422 | + $block_spacing | |
| 423 | + ); | |
| 424 | + | |
| 425 | + // Only add container class and enqueue block support styles if unique styles were generated. | |
| 426 | + if ( ! empty( $style ) ) { | |
| 427 | + $class_names[] = $container_class; | |
| 428 | + } | |
| 429 | + } | |
| 430 | + | |
| 431 | + /** | |
| 432 | + * The first chunk of innerContent contains the block markup up until the inner blocks start. | |
| 433 | + * We want to target the opening tag of the inner blocks wrapper, which is the last tag in that chunk. | |
| 434 | + */ | |
| 435 | + $inner_content_classnames = isset( $block['innerContent'][0] ) && 'string' === gettype( $block['innerContent'][0] ) ? gutenberg_get_classnames_from_last_tag( $block['innerContent'][0] ) : ''; | |
| 436 | + | |
| 437 | + $content = new WP_HTML_Tag_Processor( $block_content ); | |
| 438 | + if ( $inner_content_classnames ) { | |
| 439 | + $content->next_tag( array( 'class_name' => $inner_content_classnames ) ); | |
| 440 | + foreach ( $class_names as $class_name ) { | |
| 441 | + $content->add_class( $class_name ); | |
| 442 | + } | |
| 443 | + } else { | |
| 444 | + $content->next_tag(); | |
| 445 | + $content->add_class( implode( ' ', $class_names ) ); | |
| 446 | + } | |
| 447 | + | |
| 448 | + return (string) $content; | |
| 180 | 449 | } |
| 181 | 450 | |
| 182 | 451 | // Register the block support. (overrides core one). |
| 183 | 452 | WP_Block_Supports::get_instance()->register( |
| @@ -185,8 +454,9 @@ | ||
| 185 | 454 | array( |
| 186 | 455 | 'register_attribute' => 'gutenberg_register_layout_support', |
| 187 | 456 | ) |
| 188 | 457 | ); |
| 458 | + | |
| 189 | 459 | if ( function_exists( 'wp_render_layout_support_flag' ) ) { |
| 190 | 460 | remove_filter( 'render_block', 'wp_render_layout_support_flag' ); |
| 191 | 461 | } |
| 192 | 462 | add_filter( 'render_block', 'gutenberg_render_layout_support_flag', 10, 2 ); |
| @@ -206,12 +476,11 @@ | ||
| 206 | 476 | '/(^\s*<%1$s\b[^>]*wp-block-group(\s|")[^>]*>)(\s*<div\b[^>]*wp-block-group__inner-container(\s|")[^>]*>)((.|\S|\s)*)/U', |
| 207 | 477 | preg_quote( $tag_name, '/' ) |
| 208 | 478 | ); |
| 209 | 479 | if ( |
| 210 | - 'core/group' !== $block['blockName'] || | |
| 211 | - WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() || | |
| 480 | + wp_theme_has_theme_json() || | |
| 212 | 481 | 1 === preg_match( $group_with_inner_container_regex, $block_content ) || |
| 213 | - ( isset( $block['attrs']['layout']['type'] ) && 'default' !== $block['attrs']['layout']['type'] ) | |
| 482 | + ( isset( $block['attrs']['layout']['type'] ) && 'flex' === $block['attrs']['layout']['type'] ) | |
| 214 | 483 | ) { |
| 215 | 484 | return $block_content; |
| 216 | 485 | } |
| 217 | 486 | |
| @@ -230,6 +499,69 @@ | ||
| 230 | 499 | } |
| 231 | 500 | |
| 232 | 501 | if ( function_exists( 'wp_restore_group_inner_container' ) ) { |
| 233 | 502 | remove_filter( 'render_block', 'wp_restore_group_inner_container', 10, 2 ); |
| 503 | + remove_filter( 'render_block_core/group', 'wp_restore_group_inner_container', 10, 2 ); | |
| 234 | 504 | } |
| 235 | -add_filter( 'render_block', 'gutenberg_restore_group_inner_container', 10, 2 ); | |
| 505 | +add_filter( 'render_block_core/group', 'gutenberg_restore_group_inner_container', 10, 2 ); | |
| 506 | + | |
| 507 | + | |
| 508 | +/** | |
| 509 | + * For themes without theme.json file, make sure | |
| 510 | + * to restore the outer div for the aligned image block | |
| 511 | + * to avoid breaking styles relying on that div. | |
| 512 | + * | |
| 513 | + * @param string $block_content Rendered block content. | |
| 514 | + * @param array $block Block object. | |
| 515 | + * @return string Filtered block content. | |
| 516 | + */ | |
| 517 | +function gutenberg_restore_image_outer_container( $block_content, $block ) { | |
| 518 | + $image_with_align = " | |
| 519 | +/# 1) everything up to the class attribute contents | |
| 520 | +( | |
| 521 | + ^\s* | |
| 522 | + <figure\b | |
| 523 | + [^>]* | |
| 524 | + \bclass= | |
| 525 | + [\"'] | |
| 526 | +) | |
| 527 | +# 2) the class attribute contents | |
| 528 | +( | |
| 529 | + [^\"']* | |
| 530 | + \bwp-block-image\b | |
| 531 | + [^\"']* | |
| 532 | + \b(?:alignleft|alignright|aligncenter)\b | |
| 533 | + [^\"']* | |
| 534 | +) | |
| 535 | +# 3) everything after the class attribute contents | |
| 536 | +( | |
| 537 | + [\"'] | |
| 538 | + [^>]* | |
| 539 | + > | |
| 540 | + .* | |
| 541 | + <\/figure> | |
| 542 | +)/iUx"; | |
| 543 | + | |
| 544 | + if ( | |
| 545 | + wp_theme_has_theme_json() || | |
| 546 | + 0 === preg_match( $image_with_align, $block_content, $matches ) | |
| 547 | + ) { | |
| 548 | + return $block_content; | |
| 549 | + } | |
| 550 | + | |
| 551 | + $wrapper_classnames = array( 'wp-block-image' ); | |
| 552 | + | |
| 553 | + // If the block has a classNames attribute these classnames need to be removed from the content and added back | |
| 554 | + // to the new wrapper div also. | |
| 555 | + if ( ! empty( $block['attrs']['className'] ) ) { | |
| 556 | + $wrapper_classnames = array_merge( $wrapper_classnames, explode( ' ', $block['attrs']['className'] ) ); | |
| 557 | + } | |
| 558 | + $content_classnames = explode( ' ', $matches[2] ); | |
| 559 | + $filtered_content_classnames = array_diff( $content_classnames, $wrapper_classnames ); | |
| 560 | + | |
| 561 | + return '<div class="' . implode( ' ', $wrapper_classnames ) . '">' . $matches[1] . implode( ' ', $filtered_content_classnames ) . $matches[3] . '</div>'; | |
| 562 | +} | |
| 563 | + | |
| 564 | +if ( function_exists( 'wp_restore_image_outer_container' ) ) { | |
| 565 | + remove_filter( 'render_block_core/image', 'wp_restore_image_outer_container', 10, 2 ); | |
| 566 | +} | |
| 567 | +add_filter( 'render_block_core/image', 'gutenberg_restore_image_outer_container', 10, 2 ); | |