Skip to content

Commit af57119

Browse files
committed
WP_Theme_JSON: preserve valid non-preset settings for lightbox when KSES filters are active
Extends `VALID_SETTINGS` entries to also do type validation (as well as schema key validation) for the lightbox settings only. Previously, when KSES filters were active (via `add_action( 'init', 'kses_init_filters' )`), valid non-preset settings in Global Styles were being incorrectly filtered out. For example, `lightbox.enabled` and `lightbox.allowEditing` for Image blocks. The idea is that `VALID_SETTINGS` values can act as type validation for further valid settings. Props ramonopoly, mmtr86, oandregal, wildworks, westonruter, andrewserong. Fixes #64280. git-svn-id: https://develop.svn.wordpress.org/trunk@61618 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 4cedbfc commit af57119

2 files changed

Lines changed: 175 additions & 2 deletions

File tree

src/wp-includes/class-wp-theme-json.php

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ class WP_Theme_JSON {
395395
* @since 6.6.0 Added support for 'dimensions.aspectRatios', 'dimensions.defaultAspectRatios',
396396
* 'typography.defaultFontSizes', and 'spacing.defaultSpacingSizes'.
397397
* @since 6.9.0 Added support for `border.radiusSizes`.
398+
* @since 7.0.0 Added type markers to the schema for boolean values.
398399
* @var array
399400
*/
400401
const VALID_SETTINGS = array(
@@ -442,8 +443,8 @@ class WP_Theme_JSON {
442443
'allowCustomContentAndWideSize' => null,
443444
),
444445
'lightbox' => array(
445-
'enabled' => null,
446-
'allowEditing' => null,
446+
'enabled' => true,
447+
'allowEditing' => true,
447448
),
448449
'position' => array(
449450
'fixed' => null,
@@ -1302,6 +1303,7 @@ protected static function get_blocks_metadata() {
13021303
* It is recursive and modifies the input in-place.
13031304
*
13041305
* @since 5.8.0
1306+
* @since 7.0.0 Added type validation for boolean values.
13051307
*
13061308
* @param array $tree Input to process.
13071309
* @param array $schema Schema to adhere to.
@@ -1319,6 +1321,17 @@ protected static function remove_keys_not_in_schema( $tree, $schema ) {
13191321
continue;
13201322
}
13211323

1324+
// Validate type if schema specifies a boolean marker.
1325+
if ( is_bool( $schema[ $key ] ) ) {
1326+
// Schema expects a boolean value - validate the input matches.
1327+
if ( ! is_bool( $value ) ) {
1328+
unset( $tree[ $key ] );
1329+
continue;
1330+
}
1331+
// Type matches, keep the value and continue to next key.
1332+
continue;
1333+
}
1334+
13221335
if ( is_array( $schema[ $key ] ) ) {
13231336
if ( ! is_array( $value ) ) {
13241337
unset( $tree[ $key ] );
@@ -3752,6 +3765,35 @@ protected static function remove_insecure_inner_block_styles( $blocks ) {
37523765
return $sanitized;
37533766
}
37543767

3768+
/**
3769+
* Preserves valid typed settings from input to output based on type markers in schema.
3770+
*
3771+
* Recursively iterates through the schema and validates/preserves settings
3772+
* that have type markers (e.g., boolean) in VALID_SETTINGS.
3773+
*
3774+
* @since 7.0.0
3775+
*
3776+
* @param array $input Input settings to process.
3777+
* @param array $output Output settings array (passed by reference).
3778+
* @param array $schema Schema to validate against (typically VALID_SETTINGS).
3779+
* @param array<string|int> $path Current path in the schema (for recursive calls).
3780+
*/
3781+
private static function preserve_valid_typed_settings( $input, &$output, $schema, $path = array() ) {
3782+
foreach ( $schema as $key => $schema_value ) {
3783+
$current_path = array_merge( $path, array( $key ) );
3784+
3785+
// Validate boolean type markers.
3786+
if ( is_bool( $schema_value ) ) {
3787+
$value = _wp_array_get( $input, $current_path, null );
3788+
if ( is_bool( $value ) ) {
3789+
_wp_array_set( $output, $current_path, $value ); // Preserve boolean value.
3790+
}
3791+
} elseif ( is_array( $schema_value ) ) {
3792+
self::preserve_valid_typed_settings( $input, $output, $schema_value, $current_path ); // Recurse into nested structure.
3793+
}
3794+
}
3795+
}
3796+
37553797
/**
37563798
* Processes a setting node and returns the same node
37573799
* without the insecure settings.
@@ -3811,6 +3853,9 @@ protected static function remove_insecure_settings( $input ) {
38113853
// Ensure indirect properties not included in any `PRESETS_METADATA` value are allowed.
38123854
static::remove_indirect_properties( $input, $output );
38133855

3856+
// Preserve all valid settings that have type markers in VALID_SETTINGS.
3857+
self::preserve_valid_typed_settings( $input, $output, static::VALID_SETTINGS );
3858+
38143859
return $output;
38153860
}
38163861

tests/phpunit/tests/theme/wpThemeJson.php

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6905,4 +6905,132 @@ public function test_block_pseudo_selectors_with_elements() {
69056905
$expected = ':root :where(.wp-block-button .wp-block-button__link){background-color: blue;color: white;}:root :where(.wp-block-button .wp-block-button__link:hover){background-color: white;color: blue;}:root :where(.wp-block-button .wp-block-button__link .wp-element-button,.wp-block-button .wp-block-button__link .wp-block-button__link){color: green;}:root :where(.wp-block-button .wp-block-button__link .wp-element-button:hover,.wp-block-button .wp-block-button__link .wp-block-button__link:hover){color: orange;}';
69066906
$this->assertSame( $expected, $theme_json->get_stylesheet( array( 'styles' ), null, array( 'skip_root_layout_styles' => true ) ) );
69076907
}
6908+
6909+
/**
6910+
* @covers WP_Theme_JSON::sanitize
6911+
* @covers WP_Theme_JSON::remove_keys_not_in_schema
6912+
*
6913+
* @ticket 64280
6914+
*/
6915+
public function test_sanitize_preserves_boolean_values_when_schema_expects_boolean() {
6916+
$theme_json = new WP_Theme_JSON(
6917+
array(
6918+
'version' => WP_Theme_JSON::LATEST_SCHEMA,
6919+
'settings' => array(
6920+
'lightbox' => array(
6921+
'enabled' => true,
6922+
'allowEditing' => false,
6923+
),
6924+
),
6925+
)
6926+
);
6927+
6928+
$settings = $theme_json->get_settings();
6929+
$this->assertTrue( $settings['lightbox']['enabled'], 'Enabled should be true' );
6930+
$this->assertFalse( $settings['lightbox']['allowEditing'], 'Allow editing should be false' );
6931+
}
6932+
6933+
/**
6934+
* @covers WP_Theme_JSON::sanitize
6935+
* @covers WP_Theme_JSON::remove_keys_not_in_schema
6936+
*
6937+
* @ticket 64280
6938+
*/
6939+
public function test_sanitize_removes_non_boolean_values_when_schema_expects_boolean() {
6940+
$theme_json = new WP_Theme_JSON(
6941+
array(
6942+
'version' => WP_Theme_JSON::LATEST_SCHEMA,
6943+
'settings' => array(
6944+
'lightbox' => array(
6945+
'enabled' => 'not-a-boolean',
6946+
'allowEditing' => 123,
6947+
),
6948+
),
6949+
)
6950+
);
6951+
6952+
$settings = $theme_json->get_settings();
6953+
$this->assertArrayNotHasKey( 'enabled', $settings['lightbox'] ?? array(), 'Enabled should be removed' );
6954+
$this->assertArrayNotHasKey( 'allowEditing', $settings['lightbox'] ?? array(), 'Allow editing should be removed' );
6955+
}
6956+
6957+
/**
6958+
* @covers WP_Theme_JSON::sanitize
6959+
* @covers WP_Theme_JSON::remove_keys_not_in_schema
6960+
*
6961+
* @ticket 64280
6962+
*/
6963+
public function test_sanitize_preserves_boolean_values_in_block_settings() {
6964+
$theme_json = new WP_Theme_JSON(
6965+
array(
6966+
'version' => WP_Theme_JSON::LATEST_SCHEMA,
6967+
'settings' => array(
6968+
'blocks' => array(
6969+
'core/image' => array(
6970+
'lightbox' => array(
6971+
'enabled' => true,
6972+
'allowEditing' => false,
6973+
),
6974+
),
6975+
),
6976+
),
6977+
)
6978+
);
6979+
6980+
$settings = $theme_json->get_settings();
6981+
$this->assertTrue( $settings['blocks']['core/image']['lightbox']['enabled'], 'Enabled should be true' );
6982+
$this->assertFalse( $settings['blocks']['core/image']['lightbox']['allowEditing'], 'Allow editing should be false' );
6983+
}
6984+
6985+
/**
6986+
* @covers WP_Theme_JSON::sanitize
6987+
* @covers WP_Theme_JSON::remove_keys_not_in_schema
6988+
*
6989+
* @ticket 64280
6990+
*/
6991+
public function test_sanitize_removes_non_boolean_values_in_block_settings() {
6992+
$theme_json = new WP_Theme_JSON(
6993+
array(
6994+
'version' => WP_Theme_JSON::LATEST_SCHEMA,
6995+
'settings' => array(
6996+
'blocks' => array(
6997+
'core/image' => array(
6998+
'lightbox' => array(
6999+
'enabled' => 'string-value',
7000+
'allowEditing' => array( 'not', 'a', 'boolean' ),
7001+
),
7002+
),
7003+
),
7004+
),
7005+
)
7006+
);
7007+
7008+
$settings = $theme_json->get_settings();
7009+
$lightbox = $settings['blocks']['core/image']['lightbox'] ?? array();
7010+
$this->assertArrayNotHasKey( 'enabled', $lightbox, 'Enabled should be removed' );
7011+
$this->assertArrayNotHasKey( 'allowEditing', $lightbox, 'Allow editing should be removed' );
7012+
}
7013+
7014+
/**
7015+
* @covers WP_Theme_JSON::sanitize
7016+
* @covers WP_Theme_JSON::remove_keys_not_in_schema
7017+
*
7018+
* @ticket 64280
7019+
*/
7020+
public function test_sanitize_preserves_null_schema_behavior() {
7021+
// Test that settings with null in schema (no type validation) still accept any type.
7022+
$theme_json = new WP_Theme_JSON(
7023+
array(
7024+
'version' => WP_Theme_JSON::LATEST_SCHEMA,
7025+
'settings' => array(
7026+
'appearanceTools' => 'string-value', // null in schema, should accept any type.
7027+
'custom' => array( 'nested' => 'value' ), // null in schema, should accept any type.
7028+
),
7029+
)
7030+
);
7031+
7032+
$settings = $theme_json->get_settings();
7033+
$this->assertSame( 'string-value', $settings['appearanceTools'], 'Appearance tools should be string value' );
7034+
$this->assertSame( array( 'nested' => 'value' ), $settings['custom'], 'Custom should be array value' );
7035+
}
69087036
}

0 commit comments

Comments
 (0)