Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editor: Fix missing frontend section presets output. #3322

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions src/wp-includes/block-supports/settings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php
/**
* Block level presets support.
*
* @package WordPress
* @since 6.1.0
*/

/**
* Get the class name used on block level presets.
*
* @access private
*
* @param array $block Block object.
* @return string The unique class name.
*/
function _wp_get_presets_class_name( $block ) {
return 'wp-settings-' . md5( serialize( $block ) );
}

/**
* Update the block content with block level presets class name.
*
* @access private
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
*/
function _wp_add_block_level_presets_class( $block_content, $block ) {
if ( ! $block_content ) {
return $block_content;
}

// return early if the block doesn't have support for settings.
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
if ( ! block_has_support( $block_type, array( '__experimentalSettings' ), false ) ) {
return $block_content;
}

// return early if no settings are found on the block attributes.
$block_settings = _wp_array_get( $block, array( 'attrs', 'settings' ), null );
if ( empty( $block_settings ) ) {
return $block_content;
}

$class_name = _wp_get_presets_class_name( $block );

// Like the layout hook this assumes the hook only applies to blocks with a single wrapper.
// Retrieve the opening tag of the first HTML element.
$html_element_matches = array();
preg_match( '/<[^>]+>/', $block_content, $html_element_matches, PREG_OFFSET_CAPTURE );
$first_element = $html_element_matches[0][0];
// If the first HTML element has a class attribute just add the new class
// as we do on layout and duotone.
if ( strpos( $first_element, 'class="' ) !== false ) {
$content = preg_replace(
'/' . preg_quote( 'class="', '/' ) . '/',
'class="' . $class_name . ' ',
$block_content,
1
);
} else {
// If the first HTML element has no class attribute we should inject the attribute before the attribute at the end.
$first_element_offset = $html_element_matches[0][1];
$content = substr_replace( $block_content, ' class="' . $class_name . '"', $first_element_offset + strlen( $first_element ) - 1, 0 );
}

return $content;
}

/**
* Render the block level presets stylesheet.
*
* @access private
*
* @param string|null $pre_render The pre-rendered content. Default null.
* @param array $block The block being rendered.
*
* @return null
*/
function _wp_add_block_level_preset_styles( $pre_render, $block ) {
// Return early if the block has not support for descendent block styles.
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
if ( ! block_has_support( $block_type, array( '__experimentalSettings' ), false ) ) {
return null;
}

// return early if no settings are found on the block attributes.
$block_settings = _wp_array_get( $block, array( 'attrs', 'settings' ), null );
if ( empty( $block_settings ) ) {
return null;
}

$class_name = '.' . _wp_get_presets_class_name( $block );

// the root selector for preset variables needs to target every possible block selector
// in order for the general setting to override any bock specific setting of a parent block or
// the site root.
$variables_root_selector = '*,[class*="wp-block"]';
$registry = WP_Block_Type_Registry::get_instance();
$blocks = $registry->get_all_registered();
foreach ( $blocks as $block_type ) {
if (
isset( $block_type->supports['__experimentalSelector'] ) &&
is_string( $block_type->supports['__experimentalSelector'] )
) {
$variables_root_selector .= ',' . $block_type->supports['__experimentalSelector'];
}
}
$variables_root_selector = WP_Theme_JSON::scope_selector( $class_name, $variables_root_selector );

// Remove any potentially unsafe styles.
$theme_json_shape = WP_Theme_JSON::remove_insecure_properties(
array(
'version' => WP_Theme_JSON::LATEST_SCHEMA,
'settings' => $block_settings,
)
);
$theme_json_object = new WP_Theme_JSON( $theme_json_shape );

$styles = '';

// include preset css variables declaration on the stylesheet.
$styles .= $theme_json_object->get_stylesheet(
array( 'variables' ),
null,
array(
'root_selector' => $variables_root_selector,
'scope' => $class_name,
)
);

// include preset css classes on the the stylesheet.
$styles .= $theme_json_object->get_stylesheet(
array( 'presets' ),
null,
array(
'root_selector' => $class_name . ',' . $class_name . ' *',
'scope' => $class_name,
)
);

if ( ! empty( $styles ) ) {
wp_enqueue_block_support_styles( $styles );
}

return null;
}

add_filter( 'render_block', '_wp_add_block_level_presets_class', 10, 2 );
add_filter( 'pre_render_block', '_wp_add_block_level_preset_styles', 10, 2 );
65 changes: 52 additions & 13 deletions src/wp-includes/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ protected static function get_blocks_metadata() {
static::$blocks_metadata[ $block_name ]['features'] = $features;
}

// Assign defaults, then overwrite those that the block sets by itself.
// Assign defaults, then override those that the block sets by itself.
// If the block selector is compounded, will append the element to each
// individual block selector.
$block_selectors = explode( ',', static::$blocks_metadata[ $block_name ]['selector'] );
Expand Down Expand Up @@ -866,11 +866,13 @@ public function get_settings() {
* @param array $types Types of styles to load. Will load all by default. It accepts:
* - `variables`: only the CSS Custom Properties for presets & custom ones.
* - `styles`: only the styles section in theme.json.
* - `presets`: only the classes for the presets.
* @param array $origins A list of origins to include. By default it includes VALID_ORIGINS.
* - `presets`: only the classes for the presets. @param array $origins A list of origins to include. By default it includes VALID_ORIGINS.
* @param array $options An array of options for now used for internal purposes only (may change without notice).
* The options currently supported are 'scope' that makes sure all style are scoped to a given selector,
* and root_selector which overwrites and forces a given selector to be used on the root node.
* @return string Stylesheet.
*/
public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = null ) {
public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = null, $options = array() ) {
if ( null === $origins ) {
$origins = static::VALID_ORIGINS;
}
Expand All @@ -891,30 +893,58 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets'
$style_nodes = static::get_style_nodes( $this->theme_json, $blocks_metadata );
$setting_nodes = static::get_setting_nodes( $this->theme_json, $blocks_metadata );

$root_style_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true );
$root_settings_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $setting_nodes, 'selector' ), true );

if ( ! empty( $options['scope'] ) ) {
foreach ( $setting_nodes as &$node ) {
$node['selector'] = static::scope_selector( $options['scope'], $node['selector'] );
}
foreach ( $style_nodes as &$node ) {
$node['selector'] = static::scope_selector( $options['scope'], $node['selector'] );
}
}

if ( ! empty( $options['root_selector'] ) ) {
if ( false !== $root_settings_key ) {
$setting_nodes[ $root_settings_key ]['selector'] = $options['root_selector'];
}
if ( false !== $root_style_key ) {
$setting_nodes[ $root_style_key ]['selector'] = $options['root_selector'];
}
}

$stylesheet = '';

if ( in_array( 'variables', $types, true ) ) {
$stylesheet .= $this->get_css_variables( $setting_nodes, $origins );
}

if ( in_array( 'styles', $types, true ) ) {
$root_block_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true );

if ( false !== $root_block_key ) {
$stylesheet .= $this->get_root_layout_rules( static::ROOT_BLOCK_SELECTOR, $style_nodes[ $root_block_key ] );
if ( false !== $root_style_key ) {
$stylesheet .= $this->get_root_layout_rules( $style_nodes[ $root_style_key ]['selector'], $style_nodes[ $root_style_key ] );
}
$stylesheet .= $this->get_block_classes( $style_nodes );
} elseif ( in_array( 'base-layout-styles', $types, true ) ) {
$root_selector = static::ROOT_BLOCK_SELECTOR;
$columns_selector = '.wp-block-columns';
if ( ! empty( $options['scope'] ) ) {
$root_selector = static::scope_selector( $options['scope'], $root_selector );
$columns_selector = static::scope_selector( $options['scope'], $columns_selector );
}
if ( ! empty( $options['root_selector'] ) ) {
$root_selector = $options['root_selector'];
}
// Base layout styles are provided as part of `styles`, so only output separately if explicitly requested.
// For backwards compatibility, the Columns block is explicitly included, to support a different default gap value.
$base_styles_nodes = array(
array(
'path' => array( 'styles' ),
'selector' => static::ROOT_BLOCK_SELECTOR,
'selector' => $root_selector,
),
array(
'path' => array( 'styles', 'blocks', 'core/columns' ),
'selector' => '.wp-block-columns',
'selector' => $columns_selector,
'name' => 'core/columns',
),
);
Expand Down Expand Up @@ -1365,18 +1395,27 @@ protected static function compute_preset_classes( $settings, $selector, $origins
* @param string $selector Original selector.
* @return string Scoped selector.
*/
protected static function scope_selector( $scope, $selector ) {
public static function scope_selector( $scope, $selector ) {
$scopes = explode( ',', $scope );
$selectors = explode( ',', $selector );

$selectors_scoped = array();
foreach ( $scopes as $outer ) {
foreach ( $selectors as $inner ) {
$selectors_scoped[] = trim( $outer ) . ' ' . trim( $inner );
$outer = trim( $outer );
$inner = trim( $inner );
if ( ! empty( $outer ) && ! empty( $inner ) ) {
$selectors_scoped[] = $outer . ' ' . $inner;
} elseif ( empty( $outer ) ) {
$selectors_scoped[] = $inner;
} elseif ( empty( $inner ) ) {
$selectors_scoped[] = $outer;
}
}
}

return implode( ', ', $selectors_scoped );
$result = implode( ', ', $selectors_scoped );
return $result;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/wp-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@
require ABSPATH . WPINC . '/block-patterns.php';
require ABSPATH . WPINC . '/class-wp-block-supports.php';
require ABSPATH . WPINC . '/block-supports/utils.php';
require ABSPATH . WPINC . '/block-supports/settings.php';
require ABSPATH . WPINC . '/block-supports/align.php';
require ABSPATH . WPINC . '/block-supports/border.php';
require ABSPATH . WPINC . '/block-supports/colors.php';
Expand Down