*/ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } if ( ! class_exists( 'Sp_Smart_Post_Block_Admin_Menu_Page' ) ) { /** * Sp_Smart_Post_Block_Admin_Menu_Page Class. */ class Sp_Smart_Post_Block_Admin_Menu_Page { /** * Default Modules variable * * @var array */ private $default_modules = array( array( 'module_name' => 'post-badges', 'options' => array( 'post_type' => 'post', ), ), array( 'module_name' => 'back-to-top', 'options' => array( 'display_position' => 'button-right', 'horizontal_position' => array( 'value' => 30, 'unit' => 'px', ), 'vertical_position' => array( 'value' => 30, 'unit' => 'px', ), 'back_to_top_icon' => true, 'top_icon_source' => 'top-arrow-one', 'icon_size' => array( 'value' => 24, 'unit' => 'px', ), 'back_top_label' => '', 'display_on' => 'all-pages', 'exclude_page' => array(), 'include_only' => array(), 'smooth_scroll' => true, 'transition_delay' => array( 'value' => 0.3, 'unit' => 's', ), 'entrance_animation' => 'fade-in', 'go_to_bottom' => false, 'back_bottom_label' => '', 'typography' => array( 'googleFont' => array( 'family' => 'Default', 'variants' => array( '300', '400', '500', '600', '700', '800', '900' ), ), 'typography' => array( 'family' => '', 'fontWeight' => '', 'style' => '', 'transform' => '', 'decoration' => '', ), ), 'font_size' => array( 'value' => 16, 'unit' => 'px', ), 'line_height' => array( 'value' => 1, ), 'letter_spacing' => array( 'value' => '', 'unit' => 'px', ), 'word_spacing' => array( 'value' => '', 'unit' => 'px', ), 'color' => array( 'color' => '#FFF', 'hoverColor' => '#FFF', ), 'background_color' => array( 'color' => array( 'style' => 'bgColor', 'transparent' => '', 'solidColor' => 'var(--smart-post-secondary)', 'gradient' => 'linear-gradient(135deg,rgb(181, 51, 97) 0%,rgb(226, 87, 44) 100%)', ), 'hover' => array( 'style' => 'bgColor', 'transparent' => '', 'solidColor' => 'var(--smart-post-secondary)', 'gradient' => 'linear-gradient(135deg,rgb(181, 51, 97) 0%,rgb(226, 87, 44) 100%)', ), ), 'border' => array( 'style' => 'none', 'color' => '#DDD', 'hoverColor' => '#DDD', ), 'border_width' => array( 'value' => array( 'top' => 1, 'right' => 1, 'bottom' => 1, 'left' => 1, ), 'unit' => 'px', 'allChange' => true, ), 'border_radius' => array( 'value' => array( 'top' => 4, 'right' => 4, 'bottom' => 4, 'left' => 4, ), 'unit' => 'px', 'allChange' => true, ), 'box_shadow_enable' => false, 'box_shadow' => array( 'value' => array( 'top' => 0, 'right' => 4, 'bottom' => 8, 'left' => 0, ), 'unit' => 'outset', 'color' => '#4E4F521A', 'selectDefault' => 'var(--smart-post-shadow-medium-4dp)', ), 'box_shadow_enable_hover' => false, 'box_shadow_hover' => array( 'value' => array( 'top' => 0, 'right' => 4, 'bottom' => 8, 'left' => 0, ), 'unit' => 'outset', 'color' => '#3434361a', 'selectDefault' => 'var(--smart-post-shadow-medium-4dp)', ), 'padding' => array( 'value' => array( 'top' => 8, 'right' => 8, 'bottom' => 8, 'left' => 8, ), 'unit' => 'px', 'allChange' => true, ), 'fontListsEditPage' => '', 'dynamic_style' => '', ), ), ); /** * Block constructor. */ public function __construct() { $this->default_modules; $this->register_module_settings_option(); // update block show/hide option. add_action( 'wp_ajax_sp_smart_post_block_option', array( $this, 'sp_smart_post_block_option' ) ); // Get plugin changelog data. add_action( 'wp_ajax_sp_smart_post_block_changelog', array( $this, 'sp_smart_post_block_changelog' ) ); // Script file enqueue for block settings page. add_action( 'admin_enqueue_scripts', array( $this, 'sp_smart_block_settings_enqueue' ) ); add_action( 'rest_api_init', array( $this, 'sp_smart_post_rest_module_settings' ) ); // Update setup wizard data. add_action( 'wp_ajax_sp_smart_post_get_user_consent', array( $this, 'sp_smart_post_get_user_consent' ) ); // Update settings options. add_action( 'wp_ajax_sp_pcp_update_setting_options', array( $this, 'sp_pcp_update_setting_options' ) ); // Permanently dismiss the "Upgrade to Pro" header notice. add_action( 'wp_ajax_sp_pcp_dismiss_upgrade_notice', array( $this, 'sp_pcp_dismiss_upgrade_notice' ) ); // Weekly schedules events. add_action( 'smart_post_show_weekly_scheduled_events', array( $this, 'send_usage_data_weekly' ) ); add_action( 'init', array( $this, 'sp_pcp_submit_user_consent' ) ); add_action( 'load-post-new.php', array( $this, 'restrict_new_template_creation' ) ); add_action( 'admin_notices', array( $this, 'maybe_show_user_consent_notice' ) ); } /** * Send user data weekly. * * @access public * @return void */ public function send_usage_data_weekly() { $user_consent = get_option( 'sp_pcp_allow_anonymous_data', false ); if ( $user_consent ) { $website_type = get_option( 'sp_ua_site_type', '' ); $this->sp_pc_collect_and_send_usage_data( $website_type ); } } /** * Restrict New Template Creation * * This method checks the total posts (all statuses) * of the `sp_post_template` post type. * If the count is 2 or more, it blocks the * new post creation page. * * @return void */ public function restrict_new_template_creation() { // Check if the request is for our custom post type. if ( isset( $_GET['post_type'] ) && 'sp_post_template' === $_GET['post_type'] ) { // Get all posts count (all statuses). $args = array( 'post_type' => 'sp_post_template', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', ); $posts = get_posts( $args ); $count = count( $posts ); // Prevent creating more than 2 templates. if ( $count >= 2 ) { $redirect_url = admin_url( 'edit.php?post_type=sp_post_carousel&page=pcp_help&show_pro_modal=1#savedTemplate' ); wp_safe_redirect( $redirect_url ); exit; } } } /** * Blocks settings option update function * * @return void */ public function sp_smart_post_block_option() { // Check user capability. if ( ! current_user_can( apply_filters( 'sp_pc_dashboard_capability', 'manage_options' ) ) ) { wp_send_json_error( __( 'Unauthorized access.', 'post-carousel' ), 403 ); } $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'sp-update-block-settings-nonce' ) ) { wp_send_json_error( __( 'Invalid nonce.', 'post-carousel' ), 403 ); } // Process options data. if ( isset( $_POST['optionData'] ) ) { $options_json = wp_unslash( $_POST['optionData'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized // Validate and decode JSON. $options = json_decode( $options_json, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { wp_send_json_error( __( 'Invalid JSON in options data.', 'post-carousel' ), 400 ); } // Validate it's an array and sanitize. if ( ! is_array( $options ) ) { $options = array(); } $options = $this->sanitize_array_recursive( $options ); update_option( 'sp-pcp-blocks-setting-options', $options ); } // Process modules data. if ( isset( $_POST['modulesData'] ) ) { $modules_json = wp_unslash( $_POST['modulesData'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized // Validate and decode JSON. $modules = json_decode( $modules_json, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { wp_send_json_error( __( 'Invalid JSON in modules data.', 'post-carousel' ), 400 ); } // Validate it's an array and sanitize. if ( ! is_array( $modules ) ) { $modules = array(); } $modules = $this->sanitize_array_recursive( $modules ); update_option( 'sp-pcp-blocks-modules-options', $modules ); } // Process integrations data. if ( isset( $_POST['integrations'] ) ) { $integrations_json = wp_unslash( $_POST['integrations'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized // Validate and decode JSON. $integrations = json_decode( $integrations_json, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { wp_send_json_error( __( 'Invalid JSON in integrations data.', 'post-carousel' ), 400 ); } // Validate it's an array and sanitize. if ( ! is_array( $integrations ) ) { $integrations = array(); } $integrations = $this->sanitize_array_recursive( $integrations ); update_option( 'sp-pcp-integration-options', $integrations ); } // Send success response. wp_send_json_success( array( 'message' => __( 'Settings updated successfully.', 'post-carousel' ), ) ); } /** * Blocks settings option update function * * @return void */ public function sp_smart_post_block_changelog() { $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'sp-update-block-settings-nonce' ) ) { return; } $changelog = get_transient( 'sp_smart_post_changelogs' ); if ( empty( $changelog ) ) { $changelog = $this->fetch_changelog_from_api(); } wp_send_json( array( 'changelog' => $changelog, ) ); } /** * Fetches changelog from the remote API. * * @return string The changelog content or empty string on failure. */ protected function fetch_changelog_from_api() { $api_url = 'https://api.wordpress.org/plugins/info/1.0/post-carousel.json'; $response = wp_safe_remote_get( esc_url_raw( $api_url ), array( 'timeout' => 15, ) ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return ''; } $api_data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! isset( $api_data['sections']['changelog'] ) ) { return ''; } $changelog = wp_kses_post( $api_data['sections']['changelog'] ); set_transient( 'sp_smart_post_changelogs', $changelog, 1 * DAY_IN_SECONDS ); return $changelog; } /** * Sanitize array recursively. * * @since 4.0.0 * @param mixed $data The data to sanitize. * @return mixed Sanitized data. */ private function sanitize_array_recursive( $data ) { if ( ! is_array( $data ) ) { return $data; } $sanitized = array(); foreach ( $data as $key => $value ) { if ( is_array( $value ) ) { $sanitized[ $key ] = $this->sanitize_array_recursive( $value ); } elseif ( is_string( $value ) ) { $sanitized[ $key ] = sanitize_text_field( $value ); } elseif ( is_int( $value ) ) { $sanitized[ $key ] = intval( $value ); } elseif ( is_float( $value ) ) { $sanitized[ $key ] = floatval( $value ); } elseif ( is_bool( $value ) ) { $sanitized[ $key ] = (bool) $value; } else { $sanitized[ $key ] = $value; } } return $sanitized; } /** * Duplicates a post & its meta and returns the new duplicated Post ID. * * @param int $post_id The Post ID you want to clone. * @return int The duplicated Post ID. */ public function duplicate_post( int $post_id ): int { $old_post = get_post( $post_id ); if ( ! $old_post ) { // Invalid post ID, return early. return 0; } $title = $old_post->post_title; $content = $old_post->post_content; // Create new post array. $new_post = array( 'post_title' => $title, 'post_content' => $content, 'post_status' => 'draft', 'post_type' => $old_post->post_type, ); // Insert new post. $new_post_id = wp_insert_post( $new_post ); // Copy post meta. $post_meta = get_post_custom( $post_id ); foreach ( $post_meta as $key => $values ) { foreach ( $values as $value ) { add_post_meta( $new_post_id, $key, maybe_unserialize( $value ) ); } } // Copy post taxonomies. $taxonomies = get_post_taxonomies( $post_id ); foreach ( $taxonomies as $taxonomy ) { $term_ids = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'ids' ) ); wp_set_object_terms( $new_post_id, $term_ids, $taxonomy ); } // Return new post ID. return $new_post_id; } /** * Css and Js file enqueue for admin block settings page. * * @return void */ public function sp_smart_block_settings_enqueue() { $global_style = get_option( 'sp_smart_post_global_settings', array() ); $shadow_css = $global_style['shadow']['shadowRootCSS'] ?? ':root { --smart-post-shadow-subtle-1dp: 0px 1px 2px 0px rgba(0, 0, 0, 0.12); --smart-post-shadow-light-2dp: 0px 2px 4px 0px rgba(0, 0, 0, 0.14); --smart-post-shadow-medium-4dp: 0px 4px 6px 0px rgba(0, 0, 0, 0.16); --smart-post-shadow-strong-8dp: 0px 8px 18px 0px rgba(0, 0, 0, 0.18); --smart-post-shadow-deep-12dp: 0px 12px 17px 0px rgba(0, 0, 0, 0.20); --smart-post-shadow-sharp-4dp: 4px 4px 0px 0px rgba(0, 0, 0, 0.25);}'; $root_css = ! empty( $global_style['rootcss'] ) ? $global_style['rootcss'] : ':root{ --sp-smart-breakpoint-tablet: 1023px; --sp-smart-breakpoint-mobile: 767px; --smart-post-light-text: #FAFAFA; --smart-post-background: #FFFFFF; --smart-post-primary-light: #EBEBEB; --smart-post-primary: #999999; --smart-post-primary-dark: #1D1D1D; --smart-post-secondary: #0054FB; --smart-post-dark-2-text: #3E3E3E; --smart-post-dark-text: #0A0A0A; --smart-post-black: #000000;} :root { --smart-post-shadow-subtle-1dp: 0px 1px 2px 0px rgba(0, 0, 0, 0.12); --smart-post-shadow-light-2dp: 0px 2px 4px 0px rgba(0, 0, 0, 0.14); --smart-post-shadow-medium-4dp: 0px 4px 6px 0px rgba(0, 0, 0, 0.16); --smart-post-shadow-strong-8dp: 0px 8px 18px 0px rgba(0, 0, 0, 0.18); --smart-post-shadow-deep-12dp: 0px 12px 17px 0px rgba(0, 0, 0, 0.20); --smart-post-shadow-sharp-4dp: 4px 4px 0px 0px rgba(0, 0, 0, 0.25);}'; $inline_css = wp_strip_all_tags( $shadow_css . $root_css ); wp_register_style( 'sp-smart-post-global-root', false, array(), SMART_POST_SHOW_VERSION ); wp_add_inline_style( 'sp-smart-post-global-root', $inline_css ); wp_enqueue_style( 'sp-smart-post-global-root' ); $current_screen = get_current_screen(); $the_current_page_id = $current_screen->id; if ( 'sp_post_carousel_page_pcp_help' === $the_current_page_id ) { // css file. wp_enqueue_style( 'sp-pcp-block-setting-page', SP_PC_URL . 'blocks/build/dashboard/style-index.css', array(), SP_PC_VERSION, 'all' ); wp_enqueue_style( 'sp-pcp-block-setting-page-editor', SP_PC_URL . 'blocks/build/dashboard/index.css', array( 'wp-components' ), SP_PC_VERSION, 'all' ); wp_enqueue_style( 'sp-post-carousel-help', SP_PC_URL . 'admin/help-page/css/help-page.min.css', array(), SP_PC_VERSION ); // WordPress version check. global $wp_version; if ( version_compare( $wp_version, '6.6', '<' ) ) { // Enqueue react-jsx-runtime.js for lower versions of WordPress. wp_enqueue_script( 'sp-jsx-runtime', SP_PC_URL . 'public/assets/js/react-jsx-runtime.min.js', array( 'react', 'react-dom' ), SP_PC_VERSION, true ); } // Js file. wp_enqueue_script( 'sp-pcp-block-setting-page', SP_PC_URL . 'blocks/build/dashboard/index.js', array( 'wp-plugins', 'wp-element', 'wp-components', 'wp-core-data' ), SP_PC_VERSION, true ); remove_all_actions( 'admin_notices' ); $current_user = wp_get_current_user(); wp_localize_script( 'sp-pcp-block-setting-page', 'sp_pcp_block_settings', array( 'homeUrl' => home_url( '/' ), 'pluginUrl' => SP_PC_URL, 'getOptions' => get_option( 'sp-pcp-blocks-setting-options' ), 'userName' => $current_user->display_name, 'modulesOptions' => get_option( 'sp-pcp-blocks-modules-options' ), 'integrationOptions' => get_option( 'sp-pcp-integration-options' ), 'pluginVersion' => SP_PC_VERSION, 'nonce' => wp_create_nonce( 'sp-update-block-settings-nonce' ), 'adminUrl' => admin_url(), 'settings' => get_option( 'sp_post_carousel_settings', array() ), 'restNonce' => wp_create_nonce( 'wp_rest' ), 'restUrl' => get_rest_url(), 'wizardImages' => SP_PC_URL . 'admin/img/wizard/', 'sp_pcp_user_consent' => get_option( 'sp_pcp_allow_anonymous_data', 'undefined' ), 'sp_ua_site_type' => get_option( 'sp_ua_site_type' ) ?? '', 'pcp_editor_preference' => get_option( 'spsp_blocks_promo_modal_choice', '' ), 'savedTemplatesUrl' => admin_url( 'edit.php?post_type=sp_post_carousel&page=pcp_help#savedTemplate' ), 'upgradeNoticeVisible' => $this->is_upgrade_notice_visible(), ) ); } } /** * Register block Modules Settings Option. * * @return void */ public function register_module_settings_option() { foreach ( $this->default_modules as $module ) { // Build the data you want to save. $data = array( 'module_name' => $module['module_name'], 'options' => $module['options'], ); // Save it in wp_options. add_option( 'sp_smart_post_module_' . $module['module_name'], $data ); } } /** * Modules Settings for Your Plugin * Add this to your main plugin file or create a separate * REST API Endpoints - Super Easy Setup. * * @return void */ public function sp_smart_post_rest_module_settings() { // Register GET endpoint. register_rest_route( 'sp-smart-post/v2', '/get-module-settings', array( 'methods' => 'GET', 'callback' => array( $this, 'get_module_settings' ), 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ) ); // Register POST endpoint. register_rest_route( 'sp-smart-post/v2', '/set-module-settings', array( 'methods' => 'POST', 'callback' => array( $this, 'smart_post_save_module_settings' ), 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ) ); // Get total save template. register_rest_route( 'sp-smart-post/v2', '/get-total-save-template', array( 'methods' => 'get', 'callback' => array( $this, 'sp_get_save_total_template' ), 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ) ); } /** * Get all save template function * * @return int */ public function sp_get_save_total_template() { $args = array( 'post_type' => 'sp_post_template', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', ); $ids = get_posts( $args ); return count( $ids ); } /** * Get Modules Settings function * * @return rest_response */ public function get_module_settings() { $settings['post-badges'] = get_option( 'sp_smart_post_module_post-badges', array() ); $settings['back-to-top'] = get_option( 'sp_smart_post_module_back-to-top', array() ); return rest_ensure_response( $settings ); } /** * Update Modules Settings function * * @param rest_response $request update module options. * * @return rest_response */ public function smart_post_save_module_settings( $request ) { $settings = $request->get_json_params(); // Basic validation( optional but recommended ) . if ( ! is_array( $settings ) ) { return new WP_Error( 'invalid_data', 'Settings must be an array', array( 'status' => 400 ) ); } update_option( 'sp_smart_post_module_' . sanitize_key( $settings['module_name'] ), $settings ); return rest_ensure_response( $settings ); } /** * Handle AJAX request to update block settings. */ public function sp_pcp_update_setting_options() { // Check user capability. if ( ! current_user_can( apply_filters( 'sp_pc_dashboard_capability', 'manage_options' ) ) ) { wp_send_json_error( __( 'Unauthorized access.', 'post-carousel' ), 403 ); } $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'sp-update-block-settings-nonce' ) ) { wp_send_json_error( __( 'Invalid nonce.', 'post-carousel' ) ); } $options_json = isset( $_POST['optionData'] ) ? wp_unslash( $_POST['optionData'] ) : '';// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON validated via json_decode() and json_last_error(). $options = (array) json_decode( $options_json, true ); // Validate JSON was properly decoded. if ( json_last_error() !== JSON_ERROR_NONE ) { wp_send_json_error( __( 'Invalid JSON data.', 'post-carousel' ) ); } if ( isset( $_POST['shareData'] ) ) { $share_data = wp_unslash( $_POST['shareData'] );// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON validated via json_decode() and json_last_error(). $consent = filter_var( $share_data, FILTER_VALIDATE_BOOLEAN ); update_option( 'sp_pcp_allow_anonymous_data', $consent ); } if ( is_array( $options ) ) { // Get existing settings. $existing_options = get_option( 'sp_post_carousel_settings', array() ); // Safety check. if ( ! is_array( $existing_options ) ) { $existing_options = array(); } // Merge new values over old ones. $merged_options = array_merge( $existing_options, $options ); // Save merged result. update_option( 'sp_post_carousel_settings', $merged_options ); } // Handle editor preference for promo modal. if ( isset( $_POST['editorPreference'] ) ) { $editor_preference = sanitize_key( wp_unslash( $_POST['editorPreference'] ) ); $allowed_editors = array( 'block_editor', 'classic_shortcode' ); if ( in_array( $editor_preference, $allowed_editors, true ) ) { update_option( 'spsp_blocks_promo_modal_choice', $editor_preference, false ); } else { delete_option( 'spsp_blocks_promo_modal_choice' ); } } wp_send_json_success( array( 'options' => get_option( 'sp_post_carousel_settings' ), ) ); } /** * Determine whether the "Upgrade to Pro" header notice should be shown. * * The notice appears only after 3 days from plugin activation and only * until the user dismisses it. The activation timestamp is read from the * existing `smart_post_show_activation_date` option (set once on install * in class-smart-post-show-updates.php); the dismissal flag from the * `sp_pcp_upgrade_notice_dismissed` option (default false). * * @return bool True when the notice should be displayed. */ private function is_upgrade_notice_visible() { $activation_date = (int) get_option( 'smart_post_show_activation_date' ); // Hide until 3 days have passed since activation (or if unknown). if ( $activation_date <= 0 || ( $activation_date + ( DAY_IN_SECONDS * 3 ) ) > current_time( 'timestamp' ) ) { return false; } // Hide once the user has dismissed it. if ( (bool) get_option( 'sp_pcp_upgrade_notice_dismissed', false ) ) { return false; } return true; } /** * Permanently dismiss the "Upgrade to Pro" header notice. * * Stores the dismissal in the `sp_pcp_upgrade_notice_dismissed` option * (default false) so the notice is never shown again once closed. * * @return void */ public function sp_pcp_dismiss_upgrade_notice() { // Check user capability. if ( ! current_user_can( apply_filters( 'sp_pc_dashboard_capability', 'manage_options' ) ) ) { wp_send_json_error( __( 'Unauthorized access.', 'post-carousel' ), 403 ); } // Verify nonce. $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'sp-update-block-settings-nonce' ) ) { wp_send_json_error( __( 'Invalid nonce.', 'post-carousel' ) ); } // Sanitize the flag and persist the dismissal. $dismissed = isset( $_POST['dismissed'] ) ? filter_var( wp_unslash( $_POST['dismissed'] ), FILTER_VALIDATE_BOOLEAN ) : true; update_option( 'sp_pcp_upgrade_notice_dismissed', $dismissed ); wp_send_json_success( array( 'dismissed' => (bool) get_option( 'sp_pcp_upgrade_notice_dismissed', false ), ) ); } /** * Find blocks by name recursively in a parsed block structure. * * @param array $blocks The array of blocks to search. * @param array $used_blocks The array to store the found block names. * @return void */ private function sp_pcp_find_blocks_recursive( $blocks, &$used_blocks ) { foreach ( $blocks as $block ) { if ( ! empty( $block['blockName'] ) && strpos( $block['blockName'], 'sp-smart-post-show/' ) === 0 ) { $used_blocks[] = $block['blockName']; } if ( ! empty( $block['innerBlocks'] ) ) { $this->sp_pcp_find_blocks_recursive( $block['innerBlocks'], $used_blocks ); } } } /** * Collect and send site usage data to the ShapedPlugin SDR endpoint. * * This function gathers non-sensitive site information such as * WordPress version, PHP version, active plugins, used blocks, * and basic site metadata, then sends it to a remote API endpoint * for analytics purposes. * * Data collection is skipped for local and development environments. * * @param string $website_type Type of the website (e.g. blog, business, portfolio). * * @return void */ public function sp_pc_collect_and_send_usage_data( $website_type ) { // ---- Prevent sending data from local/dev sites ---- $site_url = home_url(); $host = wp_parse_url( $site_url, PHP_URL_HOST ); // Check for local sites. if ( 'localhost' === $host || '127.0.0.1' === $host || '::1' === $host || str_ends_with( $host, '.local' ) || str_ends_with( $host, '.test' ) || str_ends_with( $host, '.dev' ) || 1 === preg_match( '/^192\.168\./', $host ) || 1 === preg_match( '/^10\./', $host ) || 1 === preg_match( '/^172\.(1[6-9]|2[0-9]|3[0-1])\./', $host ) ) { return; // Stop here, do not collect or send data. } $theme = wp_get_theme(); // PHP version. $php_version = phpversion(); // Database version. $db_version = get_option( 'smart_post_show_db_version' ); $site_language = get_locale(); // Active plugins list. $active_plugins = array(); $plugins = get_plugins(); foreach ( (array) get_option( 'active_plugins', array() ) as $plugin_path ) { if ( isset( $plugins[ $plugin_path ] ) ) { $active_plugins[] = array( 'name' => $plugins[ $plugin_path ]['Name'], 'version' => $plugins[ $plugin_path ]['Version'], ); } } // Get used blocks. $used_blocks = array(); global $wpdb; // Search in posts, pages, custom post types, templates, etc. $post_contents = $wpdb->get_col( $wpdb->prepare( "SELECT post_content FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_content LIKE %s", '%