From 1eb2c30684505f8e71621e7a066be36eb7c9ca92 Mon Sep 17 00:00:00 2001 From: Jan-CoreBunch Date: Fri, 14 Aug 2026 12:46:35 +0200 Subject: [PATCH] Fix Bricks synchronization for empty class sets Always run Bricks synchronization after the stylesheet is persisted, including when Core Framework generates no utility classes. Normalize empty selector payloads, prevent blank global classes, and calculate removed CF class IDs before filtering previous classes. Remove stale locked-class references while preserving non-CF classes, settings, variables, and categories. Add regression coverage for empty, non-empty, removal, preservation, and idempotent synchronization. Manual validation against Bricks 2.3.11 covered variables-only synchronization and stress testing up to 5,000 synthetic Bricks documents with 100 elements each. Cleanup remained correct and error-free. Removal time scaled approximately linearly, reaching 19.09 seconds in the largest synthetic case. --- .../wp/Tests/BricksSynchronizationTest.php | 425 ++++++++++++++++++ packages/wp/src/functions/wpdb.ts | 2 +- packages/wp/src/hooks/usePush.ts | 27 +- packages/wp/wp/App/Bricks/Functions.php | 161 ++++++- packages/wp/wp/App/Rest/AllPoints.php | 18 +- 5 files changed, 606 insertions(+), 27 deletions(-) create mode 100644 packages/wp/Tests/BricksSynchronizationTest.php diff --git a/packages/wp/Tests/BricksSynchronizationTest.php b/packages/wp/Tests/BricksSynchronizationTest.php new file mode 100644 index 0000000..1875f18 --- /dev/null +++ b/packages/wp/Tests/BricksSynchronizationTest.php @@ -0,0 +1,425 @@ +data = $data; + } + + public function get_data() { + return $this->data; + } + } +} + +if ( ! function_exists( 'get_option' ) ) { + function get_option( $option, $default = false ) { + return array_key_exists( $option, $GLOBALS['cf_test_options'] ) + ? $GLOBALS['cf_test_options'][ $option ] + : $default; + } +} + +if ( ! function_exists( 'update_option' ) ) { + function update_option( $option, $value, $autoload = null ) { + $current = get_option( $option, null ); + if ( $current === $value ) { + return false; + } + + $GLOBALS['cf_test_options'][ $option ] = $value; + ++$GLOBALS['cf_test_mutations']; + return true; + } +} + +if ( ! function_exists( 'get_post_types' ) ) { + function get_post_types( $args = array(), $output = 'names' ) { + return array( + 'page' => 'page', + 'bricks_template' => 'bricks_template', + ); + } +} + +if ( ! function_exists( 'get_posts' ) ) { + function get_posts( $args = array() ) { + $meta_keys = array_column( array_filter( $args['meta_query'] ?? array(), 'is_array' ), 'key' ); + $post_ids = array(); + + foreach ( $GLOBALS['cf_test_post_meta'] as $post_id => $post_meta ) { + if ( array_intersect( $meta_keys, array_keys( $post_meta ) ) ) { + $post_ids[] = $post_id; + } + } + + return $post_ids; + } +} + +if ( ! function_exists( 'get_post_meta' ) ) { + function get_post_meta( $post_id, $key, $single = false ) { + return $GLOBALS['cf_test_post_meta'][ $post_id ][ $key ] ?? ''; + } +} + +if ( ! function_exists( 'update_post_meta' ) ) { + function update_post_meta( $post_id, $key, $value ) { + $current = get_post_meta( $post_id, $key, true ); + if ( $current === $value ) { + return false; + } + + $GLOBALS['cf_test_post_meta'][ $post_id ][ $key ] = $value; + ++$GLOBALS['cf_test_mutations']; + return true; + } +} + +if ( ! function_exists( 'sanitize_title' ) ) { + function sanitize_title( $title ) { + $title = strtolower( trim( (string) $title ) ); + $title = preg_replace( '/[^a-z0-9_-]+/', '-', $title ); + return trim( $title, '-' ); + } +} + +if ( ! function_exists( 'wp_upload_dir' ) ) { + function wp_upload_dir() { + return array( + 'basedir' => 'C:/tmp/uploads', + 'baseurl' => 'https://example.test/uploads', + ); + } +} + +if ( ! function_exists( 'trailingslashit' ) ) { + function trailingslashit( $value ) { + return rtrim( $value, '/\\' ) . '/'; + } +} + +if ( ! function_exists( 'WP_Filesystem' ) ) { + function WP_Filesystem() { + return true; + } +} + +final class CoreFrameworkTestFilesystem { + public function get_contents( $path ) { + return $GLOBALS['cf_test_stylesheet']; + } +} + +final class CoreFrameworkTestBuilder { + public $active; + public $selectors = array(); + public $variable_refreshes = 0; + + public function __construct( bool $active ) { + $this->active = $active; + } + + public function is_bricks(): bool { + return $this->active; + } + + public function is_oxygen(): bool { + return $this->active; + } + + public function refresh_selectors( $selectors ): void { + $this->selectors[] = $selectors; + } + + public function refresh_variables(): void { + ++$this->variable_refreshes; + } +} + +if ( ! function_exists( 'CoreFrameworkBricks' ) ) { + function CoreFrameworkBricks() { + return $GLOBALS['cf_test_bricks_builder']; + } +} + +if ( ! function_exists( 'CoreFrameworkOxygen' ) ) { + function CoreFrameworkOxygen() { + return $GLOBALS['cf_test_oxygen_builder']; + } +} + +final class BricksSynchronizationTest extends TestCase { + protected function setUp(): void { + global $wp_filesystem; + + $GLOBALS['cf_test_options'] = array( + 'core_framework_main' => array( + 'bricks' => true, + 'oxygen' => false, + ), + ); + $GLOBALS['cf_test_post_meta'] = array(); + $GLOBALS['cf_test_mutations'] = 0; + $GLOBALS['cf_test_stylesheet'] = ''; + $GLOBALS['cf_test_bricks_builder'] = new CoreFrameworkTestBuilder( true ); + $GLOBALS['cf_test_oxygen_builder'] = new CoreFrameworkTestBuilder( false ); + $wp_filesystem = new CoreFrameworkTestFilesystem(); + } + + private function createBricksFunctions(): BricksFunctions { + $reflection = new ReflectionClass( BricksFunctions::class ); + return $reflection->newInstanceWithoutConstructor(); + } + + private function createRestController(): AllPoints { + $reflection = new ReflectionClass( AllPoints::class ); + return $reflection->newInstanceWithoutConstructor(); + } + + private function createRequest( array $params ) { + return new class( $params ) { + private $params; + + public function __construct( array $params ) { + $this->params = $params; + } + + public function get_param( $key ) { + return $this->params[ $key ] ?? null; + } + }; + } + + public function testEmptySelectorPayloadStillRefreshesBricksVariables(): void { + $request = $this->createRequest( + array( + 'classes' => '', + 'addonEnableArray' => array( + array( + 'addon' => 'bricks', + 'enabled' => true, + ), + ), + ) + ); + + $response = $this->createRestController()->update_classes( $request ); + + $this->assertSame( array( array() ), $GLOBALS['cf_test_bricks_builder']->selectors ); + $this->assertSame( 1, $GLOBALS['cf_test_bricks_builder']->variable_refreshes ); + $this->assertSame( + array( + 'success' => true, + 'active_builders' => array( 'bricks' ), + ), + $response->get_data() + ); + } + + public function testSelectorPayloadIsTrimmedFilteredAndDeduplicated(): void { + $request = $this->createRequest( + array( + 'classes' => array( ' padding ', '', null, 'padding', 'margin' ), + 'addonEnableArray' => array( + array( + 'addon' => 'bricks', + 'enabled' => true, + ), + ), + ) + ); + + $this->createRestController()->update_classes( $request ); + + $this->assertSame( array( array( 'padding', 'margin' ) ), $GLOBALS['cf_test_bricks_builder']->selectors ); + } + + public function testClassSynchronizationRemovesFinalCoreClassAndOnlyItsReferences(): void { + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_CLASSES_OPTION ] = array( + array( + 'id' => 'user-class', + 'name' => 'user-class', + 'settings' => array( 'color' => 'red' ), + 'category' => 'custom', + ), + array( + 'id' => 'user-owned_c', + 'name' => 'user-owned', + 'settings' => array(), + 'category' => 'custom', + ), + ); + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_LOCKED_CLASSES_OPTION ] = array( 'user-class', 'user-owned_c' ); + $GLOBALS['cf_test_post_meta'][10] = array( + '_bricks_page_content_2' => array( + array( + 'id' => 'element-one', + 'settings' => array( + '_cssGlobalClasses' => array( 'padding_c', 'user-class', 'user-owned_c' ), + '_cssClasses' => 'padding_c unrelated-class', + ), + ), + ), + '_bricks_page_settings' => array( + 'container' => array( + '_cssGlobalClasses' => 'padding_c user-class', + 'customValue' => 'padding_c', + ), + ), + 'unrelated_meta' => array( + '_cssGlobalClasses' => array( 'padding_c' ), + ), + ); + + $functions = $this->createBricksFunctions(); + $functions->refresh_selectors( array( 'padding' ) ); + + $classes = $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_CLASSES_OPTION ]; + $this->assertSame( array( 'user-class', 'user-owned', 'padding' ), array_column( $classes, 'name' ) ); + $this->assertSame( + array( 'user-class', 'user-owned_c', 'padding_c' ), + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_LOCKED_CLASSES_OPTION ] + ); + + $state_after_first_sync = array( + 'options' => $GLOBALS['cf_test_options'], + 'post_meta' => $GLOBALS['cf_test_post_meta'], + 'mutations' => $GLOBALS['cf_test_mutations'], + ); + + $functions->refresh_selectors( array( 'padding', 'padding', '' ) ); + + $this->assertSame( $state_after_first_sync['options'], $GLOBALS['cf_test_options'] ); + $this->assertSame( $state_after_first_sync['post_meta'], $GLOBALS['cf_test_post_meta'] ); + $this->assertSame( $state_after_first_sync['mutations'], $GLOBALS['cf_test_mutations'] ); + + $functions->refresh_selectors( array() ); + + $classes = $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_CLASSES_OPTION ]; + $this->assertSame( array( 'user-class', 'user-owned' ), array_column( $classes, 'name' ) ); + $this->assertSame( + array( 'user-class', 'user-owned_c' ), + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_LOCKED_CLASSES_OPTION ] + ); + $this->assertSame( + array( 'user-class', 'user-owned_c' ), + $GLOBALS['cf_test_post_meta'][10]['_bricks_page_content_2'][0]['settings']['_cssGlobalClasses'] + ); + $this->assertSame( + 'padding_c unrelated-class', + $GLOBALS['cf_test_post_meta'][10]['_bricks_page_content_2'][0]['settings']['_cssClasses'] + ); + $this->assertSame( + 'user-class', + $GLOBALS['cf_test_post_meta'][10]['_bricks_page_settings']['container']['_cssGlobalClasses'] + ); + $this->assertSame( + 'padding_c', + $GLOBALS['cf_test_post_meta'][10]['_bricks_page_settings']['container']['customValue'] + ); + $this->assertSame( + array( 'padding_c' ), + $GLOBALS['cf_test_post_meta'][10]['unrelated_meta']['_cssGlobalClasses'] + ); + + $state_after_removal = array( + 'options' => $GLOBALS['cf_test_options'], + 'post_meta' => $GLOBALS['cf_test_post_meta'], + 'mutations' => $GLOBALS['cf_test_mutations'], + ); + + $functions->refresh_selectors( array() ); + + $this->assertSame( $state_after_removal['options'], $GLOBALS['cf_test_options'] ); + $this->assertSame( $state_after_removal['post_meta'], $GLOBALS['cf_test_post_meta'] ); + $this->assertSame( $state_after_removal['mutations'], $GLOBALS['cf_test_mutations'] ); + } + + public function testEmptyInputCannotCreateBlankCoreClass(): void { + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_CLASSES_OPTION ] = array(); + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_LOCKED_CLASSES_OPTION ] = array(); + + $this->createBricksFunctions()->refresh_selectors( array( '', ' ', null, '!!!' ) ); + + $this->assertSame( array(), $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_CLASSES_OPTION ] ); + $this->assertSame( array(), $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_LOCKED_CLASSES_OPTION ] ); + } + + public function testVariableSynchronizationPreservesNonCoreVariablesAndCategories(): void { + $GLOBALS['cf_test_stylesheet'] = ':root{--primary:#ffffff;}'; + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_VARIABLES_OPTION ] = array( + array( + 'id' => 'custom-variable', + 'name' => 'custom-variable', + 'value' => '42px', + 'category' => 'custom-category', + ), + array( + 'id' => 'old-core-variable', + 'name' => 'old-core-variable', + 'value' => 'red', + 'category' => BricksFunctions::CORE_VARIABLE_CATEGORY, + ), + ); + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_VARIABLES_CATEGORY ] = array( + array( + 'id' => 'custom-category', + 'name' => 'Custom', + ), + array( + 'id' => BricksFunctions::CORE_VARIABLE_CATEGORY, + 'name' => 'Core Framework', + ), + ); + + $functions = $this->createBricksFunctions(); + $functions->refresh_variables(); + + $this->assertSame( + array( + array( + 'id' => 'custom-variable', + 'name' => 'custom-variable', + 'value' => '42px', + 'category' => 'custom-category', + ), + array( + 'id' => 'primary', + 'name' => 'primary', + 'value' => '#ffffff', + 'category' => BricksFunctions::CORE_VARIABLE_CATEGORY, + ), + ), + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_VARIABLES_OPTION ] + ); + $this->assertSame( + array( + array( + 'id' => 'custom-category', + 'name' => 'Custom', + ), + array( + 'id' => BricksFunctions::CORE_VARIABLE_CATEGORY, + 'name' => 'Core Framework', + ), + ), + $GLOBALS['cf_test_options'][ BricksFunctions::BRICKS_VARIABLES_CATEGORY ] + ); + + $mutations_after_first_sync = $GLOBALS['cf_test_mutations']; + $functions->refresh_variables(); + + $this->assertSame( $mutations_after_first_sync, $GLOBALS['cf_test_mutations'] ); + } +} diff --git a/packages/wp/src/functions/wpdb.ts b/packages/wp/src/functions/wpdb.ts index c12b870..5716369 100644 --- a/packages/wp/src/functions/wpdb.ts +++ b/packages/wp/src/functions/wpdb.ts @@ -243,7 +243,7 @@ export async function updateColors({ addonEnableArray, colors }: IUpdateColors) } interface IUpdateClasses { - readonly classes: string; + readonly classes: string[]; readonly addonEnableArray: { addon: "bricks" | "oxygen" | "gutenberg" | ""; enabled: boolean; diff --git a/packages/wp/src/hooks/usePush.ts b/packages/wp/src/hooks/usePush.ts index 1a434e5..559d351 100644 --- a/packages/wp/src/hooks/usePush.ts +++ b/packages/wp/src/hooks/usePush.ts @@ -174,13 +174,9 @@ export function usePush() { }); } - if (!classAccumulator.length) { - return; - } - await Promise.allSettled([ updateClasses({ - classes: [...new Set(classAccumulator)].join(","), + classes: [...new Set(classAccumulator)], addonEnableArray, }), ]); @@ -669,13 +665,6 @@ export function usePush() { }), ]); - await withDevTimeLogger(handleClassesRefresh)({ - cssObjects, - classPrefix: latestPreset.classPrefix, - preset: latestPreset, - addonEnableArray, - }); - responses.forEach((response) => { if (response.status === "rejected") { console.warn(response.reason); @@ -683,6 +672,18 @@ export function usePush() { }); const mainTableResponse = responses[1]; + const stylesheetWasPersisted = + mainTableResponse.status === "fulfilled" && mainTableResponse.value.success; + + if (stylesheetWasPersisted) { + await withDevTimeLogger(handleClassesRefresh)({ + cssObjects, + classPrefix: latestPreset.classPrefix, + preset: latestPreset, + addonEnableArray, + }); + } + const t2_total = performance.now(); const bytesSaved = mainTableResponse.status === "fulfilled" ? mainTableResponse.value.bytes_saved : 0; const totalTime = t2_total - t1_total; @@ -692,7 +693,7 @@ export function usePush() { "Saved CSS size": [humanFileSize(bytesSaved)], }); - if (withToast) { + if (withToast && stylesheetWasPersisted) { toast.success( addonEnableArray.some(({ enabled }) => enabled) ? "Successfully pushed and synced." diff --git a/packages/wp/wp/App/Bricks/Functions.php b/packages/wp/wp/App/Bricks/Functions.php index 667a9cd..840b09e 100644 --- a/packages/wp/wp/App/Bricks/Functions.php +++ b/packages/wp/wp/App/Bricks/Functions.php @@ -122,8 +122,24 @@ public function init(): void { * @since 0.0.1 */ public function refresh_selectors( $new_core_selectors_array ): array { - $bricks_classes = get_option( self::BRICKS_CLASSES_OPTION, array() ); - $bricks_locked_classes = get_option( self::BRICKS_LOCKED_CLASSES_OPTION, array() ); + $bricks_classes = get_option( self::BRICKS_CLASSES_OPTION, array() ); + $bricks_locked_classes = get_option( self::BRICKS_LOCKED_CLASSES_OPTION, array() ); + $new_core_selectors_array = is_array( $new_core_selectors_array ) + ? array_values( + array_unique( + array_filter( + array_map( + fn( $class ): string => is_string( $class ) ? trim( $class ) : '', + $new_core_selectors_array + ), + fn( $class ): bool => '' !== $class + ) + ) + ) + : array(); + + $bricks_classes = is_array( $bricks_classes ) ? $bricks_classes : array(); + $bricks_locked_classes = is_array( $bricks_locked_classes ) ? $bricks_locked_classes : array(); $splitted_array = array( 'core' => array(), @@ -131,7 +147,11 @@ public function refresh_selectors( $new_core_selectors_array ): array { ); foreach ( $bricks_classes as $class ) { - if ( str_ends_with( $class['id'], self::CORE_SUFFIX ) ) { + $is_core_class = isset( $class['id'], $class['name'] ) + && str_ends_with( $class['id'], self::CORE_SUFFIX ) + && self::CORE_VARIABLE_CATEGORY === ( $class['category'] ?? '' ); + + if ( $is_core_class ) { $splitted_array['core'][] = $class; continue; } @@ -142,24 +162,46 @@ public function refresh_selectors( $new_core_selectors_array ): array { $core_prev_classes = $splitted_array['core']; $others_prev_classes = $splitted_array['others']; - $core_prev_classes = array_filter( $core_prev_classes, fn( $class ): bool => in_array( $class['name'], $new_core_selectors_array ) ); + $locked_classes_to_remove = array_column( + array_filter( + $core_prev_classes, + fn( $class ): bool => ! in_array( $class['name'], $new_core_selectors_array, true ) + ), + 'id' + ); + $core_prev_classes = array_filter( + $core_prev_classes, + fn( $class ): bool => in_array( $class['name'], $new_core_selectors_array, true ) + ); + $bricks_locked_classes = array_filter( + $bricks_locked_classes, + fn( $class ): bool => ! in_array( $class, $locked_classes_to_remove, true ) + ); - $locked_classes_to_remove = array_column( array_filter( $core_prev_classes, fn( $class ): bool => ! in_array( $class['name'], $new_core_selectors_array ) ), 'id' ); - $bricks_locked_classes = array_filter( $bricks_locked_classes, fn( $class ): bool => ! in_array( $class, $locked_classes_to_remove ) ); + $this->remove_class_references( $locked_classes_to_remove ); foreach ( $new_core_selectors_array as $new_core_selector_array ) { - if ( in_array( $new_core_selector_array, array_column( $core_prev_classes, 'name' ) ) ) { + if ( in_array( $new_core_selector_array, array_column( $core_prev_classes, 'name' ), true ) ) { + continue; + } + + $sanitized_selector = sanitize_title( $new_core_selector_array ); + + if ( '' === $sanitized_selector ) { continue; } - $id = $new_core_selector_array === 'z--1' ? 'z--1_c' : sanitize_title( $new_core_selector_array ) . self::CORE_SUFFIX; - $core_prev_classes[] = array( + $id = $new_core_selector_array === 'z--1' ? 'z--1_c' : $sanitized_selector . self::CORE_SUFFIX; + $core_prev_classes[] = array( 'name' => $new_core_selector_array, 'id' => $id, 'settings' => array(), - 'category' => 'corefrm', + 'category' => self::CORE_VARIABLE_CATEGORY, ); - $bricks_locked_classes[] = $id; + + if ( ! in_array( $id, $bricks_locked_classes, true ) ) { + $bricks_locked_classes[] = $id; + } } $all = array( ...$others_prev_classes, ...$core_prev_classes ); @@ -170,6 +212,101 @@ public function refresh_selectors( $new_core_selectors_array ): array { return array( 'status' => 'success' ); } + /** + * Remove deleted Core Framework class IDs from Bricks element settings. + * + * @param string[] $class_ids Removed Core Framework class IDs. + */ + private function remove_class_references( array $class_ids ): void { + if ( empty( $class_ids ) ) { + return; + } + + $meta_keys = array( + defined( 'BRICKS_DB_PAGE_HEADER' ) ? constant( 'BRICKS_DB_PAGE_HEADER' ) : '_bricks_page_header_2', + defined( 'BRICKS_DB_PAGE_CONTENT' ) ? constant( 'BRICKS_DB_PAGE_CONTENT' ) : '_bricks_page_content_2', + defined( 'BRICKS_DB_PAGE_FOOTER' ) ? constant( 'BRICKS_DB_PAGE_FOOTER' ) : '_bricks_page_footer_2', + defined( 'BRICKS_DB_PAGE_SETTINGS' ) ? constant( 'BRICKS_DB_PAGE_SETTINGS' ) : '_bricks_page_settings', + defined( 'BRICKS_DB_TEMPLATE_SETTINGS' ) ? constant( 'BRICKS_DB_TEMPLATE_SETTINGS' ) : '_bricks_template_settings', + ); + + $meta_query = array( 'relation' => 'OR' ); + foreach ( $meta_keys as $meta_key ) { + $meta_query[] = array( + 'key' => $meta_key, + 'compare' => 'EXISTS', + ); + } + + $post_ids = get_posts( + array( + 'post_type' => array_values( get_post_types( array(), 'names' ) ), + 'post_status' => 'any', + 'posts_per_page' => -1, + 'fields' => 'ids', + 'no_found_rows' => true, + 'suppress_filters' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'meta_query' => $meta_query, + ) + ); + + foreach ( $post_ids as $post_id ) { + foreach ( $meta_keys as $meta_key ) { + $settings = get_post_meta( $post_id, $meta_key, true ); + + if ( ! is_array( $settings ) ) { + continue; + } + + $updated_settings = $this->remove_class_references_from_settings( $settings, $class_ids ); + if ( $updated_settings !== $settings ) { + update_post_meta( $post_id, $meta_key, $updated_settings ); + } + } + } + } + + /** + * Recursively remove class IDs only from Bricks' global-class setting. + * + * @param array $settings Bricks settings or element data. + * @param string[] $class_ids Removed Core Framework class IDs. + * @return array + */ + private function remove_class_references_from_settings( array $settings, array $class_ids ): array { + foreach ( $settings as $key => $value ) { + if ( '_cssGlobalClasses' === $key ) { + if ( is_array( $value ) ) { + $settings[ $key ] = array_values( + array_filter( + $value, + fn( $class_id ): bool => ! in_array( $class_id, $class_ids, true ) + ) + ); + } elseif ( is_string( $value ) ) { + $class_references = preg_split( '/\s+/', trim( $value ) ) ?: array(); + $settings[ $key ] = implode( + ' ', + array_filter( + $class_references, + fn( $class_id ): bool => ! in_array( $class_id, $class_ids, true ) + ) + ); + } + + continue; + } + + if ( is_array( $value ) ) { + $settings[ $key ] = $this->remove_class_references_from_settings( $value, $class_ids ); + } + } + + return $settings; + } + public function refresh_variables(): array { $bricks_variables = get_option( self::BRICKS_VARIABLES_OPTION, array() ); @@ -207,7 +344,7 @@ public function refresh_variables(): array { $current_variables ); - $others_variables = array_filter( $bricks_variables, fn( $variable ): bool => $variable['category'] !== self::CORE_VARIABLE_CATEGORY ); + $others_variables = array_filter( $bricks_variables, fn( $variable ): bool => ( $variable['category'] ?? '' ) !== self::CORE_VARIABLE_CATEGORY ); $all_new_variables = array( ...$others_variables, ...$new_core_variables ); update_option( self::BRICKS_VARIABLES_OPTION, array_values( $all_new_variables ), false ); diff --git a/packages/wp/wp/App/Rest/AllPoints.php b/packages/wp/wp/App/Rest/AllPoints.php index d3694ae..c1eb86b 100644 --- a/packages/wp/wp/App/Rest/AllPoints.php +++ b/packages/wp/wp/App/Rest/AllPoints.php @@ -968,7 +968,23 @@ public function update_classes( $request ) { exit(); } - $new_selectors_array = explode( ',', $classes ) ?? array(); + if ( is_string( $classes ) ) { + $classes = explode( ',', $classes ); + } + + $new_selectors_array = is_array( $classes ) + ? array_values( + array_unique( + array_filter( + array_map( + fn( $class ): string => is_string( $class ) ? trim( $class ) : '', + $classes + ), + fn( $class ): bool => '' !== $class + ) + ) + ) + : array(); $builder_array = array( 'oxygen' => array( 'is_active' => CoreFrameworkOxygen()->is_oxygen(),