0, ]; $opt = get_option( abdielmskef_settings_key(), [] ); if ( ! is_array( $opt ) ) { $opt = []; } return array_merge( $defaults, $opt ); } function abdielmskef_update_settings( array $new ): void { $cur = abdielmskef_get_settings(); update_option( abdielmskef_settings_key(), array_merge( $cur, $new ), false ); } /** * Elementor kit query helpers. */ function abdielmskef_query_kits(): array { $args = [ 'post_type' => 'elementor_library', 'posts_per_page' => 200, 'post_status' => [ 'publish', 'draft', 'private' ], 'meta_query' => [ [ 'key' => '_elementor_template_type', 'value' => 'kit', ], ], 'orderby' => 'title', 'order' => 'ASC', 'fields' => 'ids', ]; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Essential to find Elementor Kits $q = new WP_Query( $args ); return $q->posts ?: []; } function abdielmskef_is_kit( int $post_id ): bool { return ( get_post_type( $post_id ) === 'elementor_library' ) && ( get_post_meta( $post_id, '_elementor_template_type', true ) === 'kit' ); } function abdielmskef_get_active_kit_id(): int { return (int) get_option( 'elementor_active_kit', 0 ); } function abdielmskef_set_active_kit_id( int $kit_id ): bool { if ( $kit_id && ! abdielmskef_is_kit( $kit_id ) ) { return false; } update_option( 'elementor_active_kit', $kit_id, false ); return true; } /** * JSON export/import (simple, no remote). */ function abdielmskef_export_kit_payload( int $kit_id ): array { $post = get_post( $kit_id ); if ( ! $post ) { return []; } $meta_keys = [ '_elementor_data', '_elementor_page_settings', '_elementor_edit_mode', '_elementor_template_type', '_elementor_version', '_elementor_css', ]; $meta = []; foreach ( $meta_keys as $k ) { $meta[ $k ] = get_post_meta( $kit_id, $k, true ); } return [ 'format' => 'mskfe-kit-v1', 'exported_at' => gmdate( 'c' ), 'title' => $post->post_title, 'status' => $post->post_status, 'meta' => $meta, ]; } function abdielmskef_import_kit_payload( array $payload ): int { if ( ! isset( $payload['format'] ) || $payload['format'] !== 'mskfe-kit-v1' ) { return 0; } $title = sanitize_text_field( $payload['title'] ?? 'Imported Kit' ); $status = in_array( $payload['status'] ?? 'publish', [ 'publish', 'draft', 'private' ], true ) ? $payload['status'] : 'publish'; $new_id = wp_insert_post( [ 'post_type' => 'elementor_library', 'post_title' => $title, 'post_status' => $status, ], true ); if ( is_wp_error( $new_id ) ) { return 0; } // Ensure template type is kit update_post_meta( $new_id, '_elementor_template_type', 'kit' ); $meta = $payload['meta'] ?? []; if ( is_array( $meta ) ) { foreach ( $meta as $k => $v ) { if ( $k === '_elementor_template_type' ) { continue; } update_post_meta( $new_id, $k, $v ); } } return (int) $new_id; } /** * Clone kit. */ function abdielmskef_clone_kit( int $kit_id ): int { $post = get_post( $kit_id ); if ( ! $post ) { return 0; } $new_id = wp_insert_post( [ 'post_type' => 'elementor_library', 'post_title' => $post->post_title . ' (Clone)', 'post_status' => $post->post_status, ], true ); if ( is_wp_error( $new_id ) ) { return 0; } $all_meta = get_post_meta( $kit_id ); foreach ( $all_meta as $key => $values ) { if ( $key === '_edit_lock' || $key === '_edit_last' ) { continue; } if ( ! is_array( $values ) ) { continue; } update_post_meta( $new_id, $key, maybe_unserialize( $values[0] ) ); } update_post_meta( $new_id, '_elementor_template_type', 'kit' ); return (int) $new_id; } /** * Normalize kit colors. */ function abdielmskef_normalize_kit_colors( int $kit_id ): bool { if ( ! abdielmskef_is_kit( $kit_id ) ) { return false; } $settings = get_post_meta( $kit_id, '_elementor_page_settings', true ); if ( ! is_array( $settings ) ) { $settings = []; } $sys = $settings['system_colors'] ?? []; if ( ! is_array( $sys ) ) { $sys = []; } $existing_ids = []; foreach ( $sys as $item ) { if ( is_array( $item ) && isset( $item['_id'] ) ) { $existing_ids[ (string) $item['_id'] ] = true; } } $to_add = [ [ '_id' => 'ksa_dark', 'title' => 'Dark', 'color' => '#141414' ], [ '_id' => 'ksa_light', 'title' => 'Light', 'color' => '#f4f4f4' ], [ '_id' => 'ksa_text_dark', 'title' => 'Text Dark', 'color' => '#0f172a' ], [ '_id' => 'ksa_text_light', 'title' => 'Text Light', 'color' => '#f8fafc' ], [ '_id' => 'ksa_accent_1', 'title' => 'Accent 1', 'color' => '#2563eb' ], [ '_id' => 'ksa_accent_2', 'title' => 'Accent 2', 'color' => '#ff3bd4' ], [ '_id' => 'ksa_accent_3', 'title' => 'Accent 3', 'color' => '#7c3aed' ], ]; $changed = false; foreach ( $to_add as $item ) { if ( ! isset( $existing_ids[ $item['_id'] ] ) ) { $sys[] = $item; $changed = true; } } if ( $changed ) { $settings['system_colors'] = $sys; update_post_meta( $kit_id, '_elementor_page_settings', $settings ); } return true; } // AJAX: search pages/posts by title add_action( 'wp_ajax_abdielmskef_search_posts', function () { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => 'forbidden' ], 403 ); } // SECURITY FIX: Remove third param 'false' to enforce strict check if ( ! check_ajax_referer( 'abdielmskef_admin', 'nonce', false ) ) { wp_send_json_error( [ 'message' => 'bad_nonce' ], 403 ); } $term = isset( $_GET['term'] ) ? sanitize_text_field( wp_unslash( $_GET['term'] ) ) : ''; $scope = isset( $_GET['scope'] ) ? sanitize_key( $_GET['scope'] ) : 'pages'; $post_types = ( $scope === 'pages_posts' ) ? [ 'page', 'post' ] : [ 'page' ]; $args = [ 'post_type' => $post_types, 'post_status' => [ 'publish', 'draft', 'private' ], 'posts_per_page' => 20, 's' => $term, 'fields' => 'ids', ]; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_s $q = new WP_Query( $args ); $items = []; foreach ( $q->posts as $id ) { $items[] = [ 'id' => (int) $id, 'title' => get_the_title( $id ), 'type' => get_post_type( $id ), 'status' => get_post_status( $id ), ]; } wp_send_json_success( [ 'items' => $items ] ); } ); // Flash messages function abdielmskef_set_flash( string $type, string $message ): void { set_transient( 'abdielmskef_flash_' . get_current_user_id(), [ 'type' => $type, 'message' => $message, ], 60 ); } function abdielmskef_get_flash(): array { $key = 'abdielmskef_flash_' . get_current_user_id(); $data = get_transient( $key ); if ( $data ) { delete_transient( $key ); } return is_array( $data ) ? $data : []; } /** * Build tracked external URLs. */ function abdielmskef_tracked_url( string $url, string $placement = 'unknown', string $context = 'admin' ): string { $site = wp_parse_url( home_url(), PHP_URL_HOST ); $params = [ 'utm_source' => 'wp_plugin', 'utm_medium' => 'plugin', 'utm_campaign' => 'multi-style-kits-for-elementor', 'utm_content' => $placement, 'site' => $site ?: '', 'plugin' => ABDIELMSKEF_SLUG, 'version' => ABDIELMSKEF_VERSION, 'context' => $context, ]; return add_query_arg( array_filter( $params, function( $v ) { return $v !== ''; } ), $url ); } /** * Common admin footer. */ function abdielmskef_admin_footer(): void { $support_url = abdielmskef_tracked_url( 'https://joseabdiel.com/', 'footer_support', 'admin' ); $resources_url = abdielmskef_tracked_url( 'https://joseabdiel.com/', 'footer_resources', 'admin' ); echo ''; } // AJAX: set kit as global add_action( 'wp_ajax_abdielmskef_set_global_kit', function () { try { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => 'forbidden' ], 403 ); } // SECURITY FIX: Strict check if ( ! check_ajax_referer( 'abdielmskef_admin', 'nonce', false ) ) { wp_send_json_error( [ 'message' => 'bad_nonce' ], 403 ); } $kit_id = isset( $_POST['kit_id'] ) ? (int) $_POST['kit_id'] : 0; if ( ! $kit_id || ! function_exists( 'abdielmskef_is_kit' ) || ! abdielmskef_is_kit( $kit_id ) ) { wp_send_json_error( [ 'message' => 'invalid_kit' ], 400 ); } if ( function_exists( 'abdielmskef_set_active_kit_id' ) ) { abdielmskef_set_active_kit_id( $kit_id ); } else { update_option( 'elementor_active_kit', $kit_id, false ); } update_option( 'abdielmskef_base_kit_id', $kit_id, false ); if ( class_exists( 'Elementor\\Plugin' ) ) { try { $plugin = \Elementor\Plugin::instance(); if ( isset( $plugin->files_manager ) && method_exists( $plugin->files_manager, 'clear_cache' ) ) { $plugin->files_manager->clear_cache(); } } catch ( \Throwable $e ) {} } wp_send_json_success( [ 'kit_id' => $kit_id, 'title' => get_the_title( $kit_id ), ] ); } catch ( \Throwable $e ) { wp_send_json_error( [ 'message' => $e->getMessage() ], 500 ); } } ); /** * Delete a kit safely. */ function abdielmskef_delete_kit_safely( int $kit_id, array &$out, string &$err ): bool { $out = []; $err = ''; if ( ! $kit_id || ! function_exists( 'abdielmskef_is_kit' ) || ! abdielmskef_is_kit( $kit_id ) ) { $err = 'invalid_kit'; return false; } $kits = function_exists( 'abdielmskef_query_kits' ) ? abdielmskef_query_kits() : []; $kits = is_array( $kits ) ? $kits : []; $remaining = array_values( array_filter( $kits, function ( $id ) use ( $kit_id ) { return (int) $id !== (int) $kit_id; } ) ); if ( count( $remaining ) < 1 ) { $err = __( 'Cannot delete because it is the only kit in the library.', 'abdiel-multi-style-kits-for-elementor' ); return false; } $current_global = (int) get_option( 'elementor_active_kit', 0 ); $current_base = (int) get_option( ABDIELMSKEF_OPT_BASE_KIT_ID, 0 ); $replacement = (int) ( $remaining[0] ?? 0 ); $reassigned = 0; if ( $kit_id === $current_global && $replacement ) { update_option( 'elementor_active_kit', $replacement, false ); $reassigned = 1; } if ( $kit_id === $current_base && $replacement ) { update_option( ABDIELMSKEF_OPT_BASE_KIT_ID, $replacement, false ); } $deleted = wp_delete_post( $kit_id, true ); if ( is_wp_error( $deleted ) ) { $err = $deleted->get_error_message(); return false; } if ( ! $deleted ) { $err = __( 'Could not delete the kit.', 'abdiel-multi-style-kits-for-elementor' ); return false; } $reassigned_posts = 0; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query $q = new WP_Query( [ 'post_type' => 'any', 'posts_per_page' => 500, 'post_status' => [ 'publish', 'draft', 'private' ], 'meta_query' => [ [ 'key' => ABDIELMSKEF_META_LOCAL_KIT_ID, 'value' => (string) $kit_id, 'compare' => '=', ], ], 'fields' => 'ids', ] ); if ( $q->have_posts() ) { foreach ( $q->posts as $pid ) { delete_post_meta( $pid, ABDIELMSKEF_META_LOCAL_KIT_ID ); $reassigned_posts++; $settings = get_post_meta( $pid, '_elementor_page_settings', true ); if ( is_array( $settings ) ) { $new_key = ABDIELMSKEF_META_LOCAL_KIT_ID; if ( isset( $settings[ $new_key ] ) && (int) $settings[ $new_key ] === (int) $kit_id ) { unset( $settings[ $new_key ] ); update_post_meta( $pid, '_elementor_page_settings', $settings ); $reassigned_posts++; } } elseif ( is_string( $settings ) && strpos( $settings, ABDIELMSKEF_META_LOCAL_KIT_ID ) !== false ) { $maybe = @unserialize( $settings ); if ( is_array( $maybe ) ) { if ( isset( $maybe[ ABDIELMSKEF_META_LOCAL_KIT_ID ] ) && (int) $maybe[ ABDIELMSKEF_META_LOCAL_KIT_ID ] === (int) $kit_id ) { unset( $maybe[ ABDIELMSKEF_META_LOCAL_KIT_ID ] ); update_post_meta( $pid, '_elementor_page_settings', $maybe ); $reassigned_posts++; } } } } } wp_reset_postdata(); if ( class_exists( '\Elementor\Plugin' ) ) { try { $plugin = \Elementor\Plugin::instance(); if ( $plugin && isset( $plugin->files_manager ) && method_exists( $plugin->files_manager, 'clear_cache' ) ) { $plugin->files_manager->clear_cache(); } } catch ( \Throwable $e ) {} } $out['replacement'] = $replacement; $out['reassigned'] = $reassigned; $out['reassigned_posts'] = $reassigned_posts; $out['deleted'] = 1; return true; } // AJAX: delete kit add_action( 'wp_ajax_abdielmskef_delete_kit', function () { try { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => 'forbidden' ], 403 ); } // SECURITY FIX: Strict check if ( ! check_ajax_referer( 'abdielmskef_admin', 'nonce', false ) ) { wp_send_json_error( [ 'message' => 'bad_nonce' ], 403 ); } $kit_id = isset( $_POST['kit_id'] ) ? (int) $_POST['kit_id'] : 0; if ( ! $kit_id ) { wp_send_json_error( [ 'message' => 'invalid_kit' ], 400 ); } $out = []; $err = ''; if ( ! abdielmskef_delete_kit_safely( (int) $kit_id, $out, $err ) ) { $msg = $err ? $err : 'error'; wp_send_json_error( [ 'message' => $msg ], 400 ); } wp_send_json_success( [ 'deleted_id' => (int) $kit_id, 'replacement' => isset( $out['replacement'] ) ? (int) $out['replacement'] : 0, 'reassigned' => isset( $out['reassigned'] ) ? (int) $out['reassigned'] : 0, ] ); } catch ( \Throwable $e ) { wp_send_json_error( [ 'message' => $e->getMessage() ], 500 ); } } );