is_aafe_page() ) { return; } // Remove Elementor's aggressive footer text filter. remove_all_filters( 'admin_footer_text' ); remove_all_filters( 'update_footer' ); // Re-add our own filters after clearing. add_filter( 'admin_footer_text', array( $this, 'custom_admin_footer_text' ), 999 ); add_filter( 'update_footer', array( $this, 'custom_admin_footer_version' ), 999 ); } /** * Check if current page belongs to AAFE plugin. */ private function is_aafe_page() { $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore if ( strpos( $page, 'addronix-addons-for-elementor' ) !== false ) { return true; } if ( function_exists( 'get_current_screen' ) ) { $screen = get_current_screen(); if ( $screen && 'aafe_item' === $screen->post_type ) { return true; } } return false; } /** * Hide WordPress footer text on AAFE pages (we have our own custom footer in the view files). */ public function custom_admin_footer_text( $text ) { if ( ! $this->is_aafe_page() ) { return $text; } return ''; } /** * Hide WordPress version text in footer on AAFE pages. */ public function custom_admin_footer_version( $text ) { if ( ! $this->is_aafe_page() ) { return $text; } return ''; } /** * Check if the current user has permission to access a specific area * based on Role Manager settings in aafe_settings. * Administrators always have full access. * * @param string $area Area identifier: 'dashboard', 'theme_builder', 'settings'. * @return bool */ public static function current_user_can_access( string $area = 'dashboard' ): bool { if ( current_user_can( 'administrator' ) || current_user_can( 'manage_options' ) ) { return true; } $settings = get_option( self::OPTION_KEY, array() ); $role_manager = isset( $settings['role_manager'] ) && is_array( $settings['role_manager'] ) ? $settings['role_manager'] : array(); $current_user = wp_get_current_user(); if ( ! $current_user || empty( $current_user->roles ) ) { return false; } foreach ( $current_user->roles as $role ) { if ( ! empty( $role_manager[ $role ][ $area ] ) ) { return true; } } return false; } public function register_admin_menu() { $can_dashboard = self::current_user_can_access( 'dashboard' ); $can_theme_builder = self::current_user_can_access( 'theme_builder' ); $can_settings = self::current_user_can_access( 'settings' ); // User must have access to at least one area to see the main menu if ( ! $can_dashboard && ! $can_theme_builder && ! $can_settings ) { return; } // Use 'read' capability for users who have explicit permission from Role Manager $cap_dashboard = $can_dashboard ? 'read' : 'manage_options'; $cap_theme_builder = $can_theme_builder ? 'read' : 'manage_options'; $cap_settings = $can_settings ? 'read' : 'manage_options'; // Determine landing page based on available permissions $main_page_slug = $can_dashboard ? 'addronix-addons-for-elementor' : ( $can_theme_builder ? 'addronix-addons-for-elementor-theme-builder' : 'addronix-addons-for-elementor-settings' ); $main_callback = $can_dashboard ? array( $this, 'render_dashboard_page' ) : ( $can_theme_builder ? array( $this, 'render_theme_builder_page' ) : array( $this, 'render_settings_page' ) ); add_menu_page( esc_html__( 'Addronix Addons for Elementor', 'addronix-addons-for-elementor' ), esc_html__( 'Addronix Addons', 'addronix-addons-for-elementor' ), 'read', $main_page_slug, $main_callback, $this->get_menu_icon(), 58 ); if ( $can_dashboard ) { add_submenu_page( $main_page_slug, esc_html__( 'Dashboard — Addronix Addons for Elementor', 'addronix-addons-for-elementor' ), esc_html__( 'Dashboard', 'addronix-addons-for-elementor' ), $cap_dashboard, 'addronix-addons-for-elementor', array( $this, 'render_dashboard_page' ) ); } if ( $can_theme_builder ) { add_submenu_page( $main_page_slug, esc_html__( 'Theme Builder', 'addronix-addons-for-elementor' ), esc_html__( 'Theme Builder', 'addronix-addons-for-elementor' ), $cap_theme_builder, 'addronix-addons-for-elementor-theme-builder', array( $this, 'render_theme_builder_page' ) ); } if ( $can_settings ) { add_submenu_page( $main_page_slug, esc_html__( 'Settings — Addronix Addons for Elementor', 'addronix-addons-for-elementor' ), esc_html__( 'Settings', 'addronix-addons-for-elementor' ), $cap_settings, 'addronix-addons-for-elementor-settings', array( $this, 'render_settings_page' ) ); } if ( current_user_can( 'manage_options' ) ) { add_submenu_page( $main_page_slug, esc_html__( 'About — Addronix Addons for Elementor', 'addronix-addons-for-elementor' ), esc_html__( 'About', 'addronix-addons-for-elementor' ), 'manage_options', 'addronix-addons-for-elementor-about', array( $this, 'render_about_page' ) ); } } private function get_menu_icon(): string { $svg = ''; return 'data:image/svg+xml;base64,' . base64_encode( $svg ); } /** * Get our registered admin page hook suffixes. */ private function get_admin_hooks(): array { return array( 'toplevel_page_addronix-addons-for-elementor', 'addronix-addons_page_addronix-addons-for-elementor-settings', 'addronix-addons_page_addronix-addons-for-elementor-about', ); } public function enqueue_admin_assets( $hook_suffix ) { // Load on any page that belongs to AAFE if ( ! $this->is_aafe_page() ) { return; } $settings = get_option( self::OPTION_KEY, array() ); $load_fonts = isset( $settings['load_fonts'] ) ? $settings['load_fonts'] : 'yes'; if ( 'yes' === $load_fonts ) { // Load Inter Tight & Inter from Google Fonts. wp_enqueue_style( 'aafe-google-fonts', 'https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600;700;800&family=Inter:wght@400;500;600;700&display=swap', array(), null // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion ); } wp_enqueue_style( 'aafe-admin-style', AAFE_URL . 'admin/assets/css/admin-style.css', 'yes' === $load_fonts ? array( 'aafe-google-fonts' ) : array(), filemtime( AAFE_PATH . 'admin/assets/css/admin-style.css' ) ); wp_enqueue_style( 'dashicons' ); wp_enqueue_script( 'aafe-admin-script', AAFE_URL . 'admin/assets/js/admin-script.js', array( 'jquery' ), filemtime( AAFE_PATH . 'admin/assets/js/admin-script.js' ), array( 'in_footer' => true, 'strategy' => 'defer' ) ); wp_localize_script( 'aafe-admin-script', 'aafeAdmin', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonces' => array( 'toggleWidget' => wp_create_nonce( self::NONCE_TOGGLE ), 'resetSettings' => wp_create_nonce( self::NONCE_ACTION ), 'usageStats' => wp_create_nonce( 'aafe_usage_stats_nonce' ), ), 'strings' => array( 'enabled' => esc_html__( 'Enabled', 'addronix-addons-for-elementor' ), 'disabled' => esc_html__( 'Disabled', 'addronix-addons-for-elementor' ), 'saving' => esc_html__( 'Saving…', 'addronix-addons-for-elementor' ), 'saved' => esc_html__( 'Saved!', 'addronix-addons-for-elementor' ), 'error' => esc_html__( 'Error — please try again.', 'addronix-addons-for-elementor' ), 'extEnabled' => esc_html__( 'Active', 'addronix-addons-for-elementor' ), 'extDisabled' => esc_html__( 'Inactive', 'addronix-addons-for-elementor' ), 'extSaveError' => esc_html__( 'Error saving extension.', 'addronix-addons-for-elementor' ), 'allEnabled' => esc_html__( 'All widgets enabled!', 'addronix-addons-for-elementor' ), 'allDisabled' => esc_html__( 'All widgets disabled!', 'addronix-addons-for-elementor' ), 'scanning' => esc_html__( 'Scanning pages…', 'addronix-addons-for-elementor' ), 'scanSuccess' => esc_html__( 'Scan complete!', 'addronix-addons-for-elementor' ), 'resetting' => esc_html__( 'Resetting to defaults…', 'addronix-addons-for-elementor' ), 'resetSuccess' => esc_html__( 'All settings reset to defaults! Reloading…', 'addronix-addons-for-elementor' ), 'confirmReset' => esc_html__( 'Are you sure you want to reset all plugin settings to factory defaults? Your Elementor pages and templates will NOT be deleted.', 'addronix-addons-for-elementor' ), 'confirmDisableUnused' => esc_html__( 'Are you sure you want to disable all unused widgets to optimize Elementor loading speed?', 'addronix-addons-for-elementor' ), ), ) ); } public function enforce_theme_builder_redirect( $screen ) { // AAFE FIX: Do not redirect if we are launching the Elementor editor // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['action'] ) && 'elementor' === $_GET['action'] ) { return; } if ( 'edit-aafe_item' === $screen->id || 'add-aafe_item' === $screen->id || 'aafe_item' === $screen->id ) { // If trying to access post-new.php, pass a flag to open the modal $url = admin_url( 'admin.php?page=addronix-addons-for-elementor-theme-builder' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( 'add-aafe_item' === $screen->id || ( isset( $_GET['post_type'] ) && 'aafe_item' === $_GET['post_type'] && 'post-new.php' === $GLOBALS['pagenow'] ) ) { $url = add_query_arg( 'add_new', '1', $url ); } wp_safe_redirect( $url ); exit; } } /** * AAFE FIX (security): the Theme Builder AJAX actions accepted ANY post ID, so a user who was * given Theme Builder access in the Role Manager could trash / delete / unpublish any page or * post on the site. Every action now verifies the ID belongs to an AAFE template and that the * user has the matching WordPress capability for it. * * @param string $cap Capability to check per post (edit_post / delete_post). * @return \WP_Post */ private function get_template_from_request( $cap = 'edit_post' ) { $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce checked by the caller. $post = $post_id ? get_post( $post_id ) : null; if ( ! $post || \AddronixAddons\AAFE_Template::CPT !== $post->post_type ) { wp_send_json_error( 'Invalid template.' ); } if ( ! current_user_can( $cap, $post->ID ) ) { wp_send_json_error( 'Permission denied.' ); } return $post; } public function ajax_create_template() { check_ajax_referer( 'aafe_tb_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'theme_builder' ) || ! current_user_can( 'publish_pages' ) ) { wp_send_json_error( 'Permission denied.' ); } $title = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : ''; $type = isset( $_POST['type'] ) ? sanitize_key( wp_unslash( $_POST['type'] ) ) : ''; if ( empty( $title ) || empty( $type ) ) { wp_send_json_error( 'Title and type are required.' ); } $allowed_types = [ \AddronixAddons\AAFE_Template::TYPE_HEADER, \AddronixAddons\AAFE_Template::TYPE_FOOTER, \AddronixAddons\AAFE_Template::TYPE_LOOP, \AddronixAddons\AAFE_Template::TYPE_MEGA_MENU, ]; if ( ! in_array( $type, $allowed_types, true ) ) { wp_send_json_error( 'Invalid template type.' ); } $post_id = wp_insert_post( [ 'post_title' => $title, 'post_type' => \AddronixAddons\AAFE_Template::CPT, 'post_status' => 'publish', ] ); if ( is_wp_error( $post_id ) ) { wp_send_json_error( $post_id->get_error_message() ); } // Set template type - save to both meta keys for full compatibility update_post_meta( $post_id, \AddronixAddons\AAFE_Template::META_TYPE, $type ); if ( in_array( $type, [ 'header', 'footer' ], true ) ) { update_post_meta( $post_id, '_aafe_hf_type', $type ); // Default display condition to 'all' so it shows immediately update_post_meta( $post_id, '_aafe_hf_display_on', 'all' ); update_post_meta( $post_id, '_aafe_hf_singular_type', 'all' ); update_post_meta( $post_id, '_aafe_hf_conditions', [ [ 'type' => 'include', 'display_on' => 'all', 'singular_type' => 'all' ] ] ); } // Set to Elementor Canvas update_post_meta( $post_id, '_wp_page_template', 'elementor_canvas' ); // Enable Elementor editor for this post automatically update_post_meta( $post_id, '_elementor_edit_mode', 'builder' ); // Generate Elementor Edit URL $edit_url = add_query_arg( [ 'post' => $post_id, 'action' => 'elementor' ], admin_url( 'post.php' ) ); wp_send_json_success( [ 'url' => $edit_url ] ); } public function ajax_save_conditions() { check_ajax_referer( 'aafe_tb_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'theme_builder' ) ) { wp_send_json_error( 'Permission denied.' ); } $post_id = $this->get_template_from_request( 'edit_post' )->ID; $raw = isset( $_POST['conditions'] ) ? wp_unslash( $_POST['conditions'] ) : '[]'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized below after json_decode. $conditions_array = json_decode( $raw, true ); if ( ! is_array( $conditions_array ) ) { $conditions_array = []; } // Recursively sanitize each decoded string value. array_walk_recursive( $conditions_array, function ( &$value ) { if ( is_string( $value ) ) { $value = sanitize_text_field( $value ); } } ); update_post_meta( $post_id, '_aafe_hf_conditions', $conditions_array ); // Keep legacy display_on / singular_type keys in sync for backward compatibility if ( ! empty( $conditions_array ) && isset( $conditions_array[0]['display_on'] ) ) { update_post_meta( $post_id, '_aafe_hf_display_on', $conditions_array[0]['display_on'] ); update_post_meta( $post_id, '_aafe_hf_singular_type', isset( $conditions_array[0]['singular_type'] ) ? $conditions_array[0]['singular_type'] : 'all' ); } // If status was explicitly passed from the modal, update post status atomically if ( isset( $_POST['status'] ) ) { $status = sanitize_key( wp_unslash( $_POST['status'] ) ); if ( in_array( $status, [ 'publish', 'draft' ], true ) && ( 'publish' !== $status || current_user_can( 'publish_pages' ) ) ) { wp_update_post( [ 'ID' => $post_id, 'post_status' => $status ] ); } } wp_send_json_success( 'Conditions saved.' ); } public function ajax_trash_template() { check_ajax_referer( 'aafe_tb_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'theme_builder' ) ) { wp_send_json_error( 'Permission denied.' ); } $post_id = $this->get_template_from_request( 'delete_post' )->ID; $trashed = wp_trash_post( $post_id ); if ( ! $trashed ) { wp_send_json_error( 'Failed to move template to trash.' ); } wp_send_json_success( 'Template moved to trash.' ); } public function ajax_restore_template() { check_ajax_referer( 'aafe_tb_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'theme_builder' ) ) { wp_send_json_error( 'Permission denied.' ); } $post_id = $this->get_template_from_request( 'delete_post' )->ID; $restored = wp_untrash_post( $post_id ); if ( ! $restored ) { wp_send_json_error( 'Failed to restore template.' ); } wp_send_json_success( 'Template restored.' ); } public function ajax_delete_template() { check_ajax_referer( 'aafe_tb_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'theme_builder' ) ) { wp_send_json_error( 'Permission denied.' ); } $post_id = $this->get_template_from_request( 'delete_post' )->ID; $deleted = wp_delete_post( $post_id, true ); if ( ! $deleted ) { wp_send_json_error( 'Failed to permanently delete template.' ); } wp_send_json_success( 'Template permanently deleted.' ); } public function ajax_toggle_template_status() { check_ajax_referer( 'aafe_tb_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'theme_builder' ) ) { wp_send_json_error( 'Permission denied.' ); } $post = $this->get_template_from_request( 'edit_post' ); $post_id = $post->ID; $requested_status = isset( $_POST['status'] ) ? sanitize_key( wp_unslash( $_POST['status'] ) ) : ''; if ( in_array( $requested_status, [ 'publish', 'draft' ], true ) ) { $new_status = $requested_status; } else { $new_status = 'publish' === $post->post_status ? 'draft' : 'publish'; } if ( 'publish' === $new_status && ! current_user_can( 'publish_pages' ) ) { wp_send_json_error( 'Permission denied.' ); } $updated = wp_update_post( [ 'ID' => $post_id, 'post_status' => $new_status ] ); if ( is_wp_error( $updated ) || ! $updated ) { wp_send_json_error( 'Failed to update status.' ); } wp_send_json_success( [ 'status' => $new_status, 'label' => 'publish' === $new_status ? __( 'Published', 'addronix-addons-for-elementor' ) : __( 'Disabled', 'addronix-addons-for-elementor' ) ] ); } public function ajax_search_posts() { check_ajax_referer( 'aafe_tb_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'theme_builder' ) ) { wp_send_json_error( 'Permission denied.' ); } // If a specific ID is requested for title restoration, return it directly $include_id = isset( $_POST['include_id'] ) ? absint( $_POST['include_id'] ) : 0; if ( $include_id > 0 ) { $single_post = get_post( $include_id ); // Only reveal titles of content the user is allowed to read (no private/draft leaks). if ( $single_post && ( 'publish' === $single_post->post_status || current_user_can( 'read_post', $single_post->ID ) ) ) { wp_send_json_success( [ [ 'id' => $single_post->ID, 'title' => $single_post->post_title ?: __( '(no title)', 'addronix-addons-for-elementor' ), 'type' => $single_post->post_type, 'url' => get_permalink( $single_post->ID ), ] ] ); return; } } $search = isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : ''; $post_type = isset( $_POST['post_type'] ) ? sanitize_key( wp_unslash( $_POST['post_type'] ) ) : ''; $public_types = get_post_types( [ 'public' => true ] ); unset( $public_types[ \AddronixAddons\AAFE_Template::CPT ], $public_types['attachment'] ); $post_types = ! empty( $post_type ) && 'any' !== $post_type && isset( $public_types[ $post_type ] ) ? [ $post_type ] : array_values( $public_types ); $posts = get_posts( [ 's' => $search, 'post_type' => $post_types, 'post_status' => 'publish', 'posts_per_page' => 20, ] ); $results = array_map( function( $p ) { return [ 'id' => $p->ID, 'title' => $p->post_title ?: __( '(no title)', 'addronix-addons-for-elementor' ), 'type' => $p->post_type, 'url' => get_permalink( $p->ID ), ]; }, $posts ); wp_send_json_success( $results ); } public function render_theme_builder_page() { if ( ! self::current_user_can_access( 'theme_builder' ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', 'addronix-addons-for-elementor' ) ); } require_once AAFE_PATH . 'admin/views/theme-builder.php'; } public function render_dashboard_page() { if ( ! self::current_user_can_access( 'dashboard' ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', 'addronix-addons-for-elementor' ) ); } $all_widgets = \AddronixAddons\Widgets_Manager::get_all_widget_metadata(); $settings = get_option( self::OPTION_KEY, array() ); $usage_data = self::get_widget_usage_stats( false ); $usage_stats = isset( $usage_data['stats'] ) ? $usage_data['stats'] : array(); require_once AAFE_PATH . 'admin/views/dashboard.php'; } public function render_settings_page() { if ( ! self::current_user_can_access( 'settings' ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', 'addronix-addons-for-elementor' ) ); } $all_widgets = \AddronixAddons\Widgets_Manager::get_all_widget_metadata(); $settings = get_option( self::OPTION_KEY, array() ); require_once AAFE_PATH . 'admin/views/settings.php'; } public function render_about_page() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', 'addronix-addons-for-elementor' ) ); } require_once AAFE_PATH . 'admin/views/about.php'; } public function handle_save_settings() { if ( ! current_user_can( 'manage_options' ) && ! self::current_user_can_access( 'settings' ) ) { wp_die( esc_html__( 'Permission denied.', 'addronix-addons-for-elementor' ) ); } check_admin_referer( self::NONCE_ACTION ); $current = get_option( self::OPTION_KEY, array() ); $current['plugin_enabled'] = isset( $_POST['plugin_enabled'] ) ? 'yes' : 'no'; // phpcs:ignore $current['performance_mode'] = isset( $_POST['performance_mode'] ) ? 'yes' : 'no'; // phpcs:ignore $current['load_fonts'] = isset( $_POST['load_fonts'] ) ? 'yes' : 'no'; // phpcs:ignore // Process Role Manager settings if user is administrator if ( current_user_can( 'manage_options' ) ) { $role_manager = array(); if ( isset( $_POST['role_manager'] ) && is_array( $_POST['role_manager'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized foreach ( $_POST['role_manager'] as $role_key => $permissions ) { $clean_role = sanitize_key( $role_key ); if ( is_array( $permissions ) ) { foreach ( $permissions as $perm_key => $perm_val ) { $clean_perm = sanitize_key( $perm_key ); $role_manager[ $clean_role ][ $clean_perm ] = ! empty( $perm_val ) ? 1 : 0; } } } } $current['role_manager'] = $role_manager; } update_option( self::OPTION_KEY, $current ); $redirect_args = array( 'page' => 'addronix-addons-for-elementor-settings', 'updated' => '1', ); if ( ! empty( $_POST['active_tab'] ) ) { $redirect_args['tab'] = sanitize_key( wp_unslash( $_POST['active_tab'] ) ); } wp_safe_redirect( add_query_arg( $redirect_args, admin_url( 'admin.php' ) ) ); exit; } public function ajax_toggle_widget() { check_ajax_referer( self::NONCE_TOGGLE, 'nonce' ); if ( ! self::current_user_can_access( 'dashboard' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Permission denied.', 'addronix-addons-for-elementor' ) ) ); } $widget_slug = isset( $_POST['widget_slug'] ) ? sanitize_key( wp_unslash( $_POST['widget_slug'] ) ) : ''; $enabled = isset( $_POST['enabled'] ) ? (bool) intval( $_POST['enabled'] ) : false; if ( empty( $widget_slug ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid widget slug.', 'addronix-addons-for-elementor' ) ) ); } $settings = get_option( self::OPTION_KEY, array() ); $disabled_widgets = isset( $settings['disabled_widgets'] ) && is_array( $settings['disabled_widgets'] ) ? $settings['disabled_widgets'] : array(); if ( $enabled ) { $disabled_widgets = array_values( array_filter( $disabled_widgets, function( $s ) use ( $widget_slug ) { return $s !== $widget_slug; } ) ); } else { if ( ! in_array( $widget_slug, $disabled_widgets, true ) ) { $disabled_widgets[] = $widget_slug; } } $settings['disabled_widgets'] = $disabled_widgets; update_option( self::OPTION_KEY, $settings ); wp_send_json_success( array( 'message' => $enabled ? esc_html__( 'Widget enabled.', 'addronix-addons-for-elementor' ) : esc_html__( 'Widget disabled.', 'addronix-addons-for-elementor' ), 'enabled' => $enabled, ) ); } public function ajax_bulk_toggle_widgets() { check_ajax_referer( self::NONCE_TOGGLE, 'nonce' ); if ( ! self::current_user_can_access( 'dashboard' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Permission denied.', 'addronix-addons-for-elementor' ) ) ); } $enable_all = isset( $_POST['enable_all'] ) ? (bool) intval( $_POST['enable_all'] ) : true; $settings = get_option( self::OPTION_KEY, array() ); if ( $enable_all ) { $settings['disabled_widgets'] = array(); } else { $all_widgets = \AddronixAddons\Widgets_Manager::get_all_widget_metadata(); $settings['disabled_widgets'] = array_column( $all_widgets, 'slug' ); } update_option( self::OPTION_KEY, $settings ); wp_send_json_success( array( 'message' => $enable_all ? esc_html__( 'All widgets enabled.', 'addronix-addons-for-elementor' ) : esc_html__( 'All widgets disabled.', 'addronix-addons-for-elementor' ), 'enable_all' => $enable_all, ) ); } public function ajax_toggle_extension() { check_ajax_referer( self::NONCE_TOGGLE, 'nonce' ); if ( ! self::current_user_can_access( 'dashboard' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Permission denied.', 'addronix-addons-for-elementor' ) ) ); } $ext_slug = isset( $_POST['ext_slug'] ) ? sanitize_key( wp_unslash( $_POST['ext_slug'] ) ) : ''; $enabled = isset( $_POST['enabled'] ) ? (bool) intval( $_POST['enabled'] ) : false; if ( empty( $ext_slug ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Invalid extension slug.', 'addronix-addons-for-elementor' ) ) ); } $settings = get_option( self::OPTION_KEY, array() ); $disabled_extensions = isset( $settings['disabled_extensions'] ) && is_array( $settings['disabled_extensions'] ) ? $settings['disabled_extensions'] : array(); if ( $enabled ) { $disabled_extensions = array_values( array_filter( $disabled_extensions, function( $s ) use ( $ext_slug ) { return $s !== $ext_slug; } ) ); } else { if ( ! in_array( $ext_slug, $disabled_extensions, true ) ) { $disabled_extensions[] = $ext_slug; } } $settings['disabled_extensions'] = $disabled_extensions; update_option( self::OPTION_KEY, $settings ); wp_send_json_success( array( 'message' => $enabled ? esc_html__( 'Extension enabled.', 'addronix-addons-for-elementor' ) : esc_html__( 'Extension disabled.', 'addronix-addons-for-elementor' ), 'enabled' => $enabled, ) ); } /** * Reset all plugin settings, widget toggles, and module configurations back to defaults. */ public function ajax_reset_settings() { check_ajax_referer( self::NONCE_ACTION, 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Permission denied. Only administrators can reset settings.', 'addronix-addons-for-elementor' ) ) ); } $default_settings = array( 'plugin_enabled' => 'yes', 'performance_mode' => 'no', 'load_fonts' => 'yes', 'disabled_widgets' => array(), 'disabled_extensions' => array(), 'role_manager' => array(), ); update_option( self::OPTION_KEY, $default_settings ); delete_transient( 'aafe_widget_usage_stats' ); delete_transient( 'aafe_widget_metadata_v' . AAFE_VERSION ); wp_send_json_success( array( 'message' => esc_html__( 'All settings, widget statuses, and module configurations have been successfully reset to factory defaults.', 'addronix-addons-for-elementor' ), ) ); } /** * Get widget usage statistics across all published posts and templates. * * @param bool $force_refresh Whether to bypass transient cache. * @return array */ public static function get_widget_usage_stats( bool $force_refresh = false ): array { $transient_key = 'aafe_widget_usage_stats'; $cached = get_transient( $transient_key ); if ( ! $force_refresh && false !== $cached && is_array( $cached ) ) { return $cached; } global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $rows = $wpdb->get_results( "SELECT pm.post_id, pm.meta_value FROM {$wpdb->postmeta} pm INNER JOIN {$wpdb->posts} p ON pm.post_id = p.ID WHERE pm.meta_key = '_elementor_data' AND p.post_status = 'publish' AND pm.meta_value LIKE '%widgetType%'", ARRAY_A ); $all_metadata = \AddronixAddons\Widgets_Manager::get_all_widget_metadata(); $stats = array(); foreach ( $all_metadata as $slug => $meta ) { $stats[ $slug ] = array( 'count' => 0, 'post_ids' => array(), ); } if ( ! empty( $rows ) ) { // Precompute alias patterns for every widget slug $slug_aliases = array(); foreach ( array_keys( $all_metadata ) as $slug ) { $slug_aliases[ $slug ] = array_unique( array( $slug, 'aafe_' . $slug, 'aafe_' . str_replace( '-', '_', $slug ), str_replace( '-', '_', $slug ), ) ); } foreach ( $rows as $row ) { $post_id = (int) $row['post_id']; $raw_data = $row['meta_value']; if ( preg_match_all( '/"widgetType"\s*:\s*"([^"]+)"/', $raw_data, $matches ) ) { $found_widgets = array_unique( $matches[1] ); foreach ( $slug_aliases as $slug => $aliases ) { foreach ( $aliases as $alias ) { if ( in_array( $alias, $found_widgets, true ) ) { $stats[ $slug ]['count']++; $stats[ $slug ]['post_ids'][] = $post_id; break; } } } } } } $unused_count = 0; $used_count = 0; foreach ( $stats as $s ) { if ( $s['count'] > 0 ) { $used_count++; } else { $unused_count++; } } $result = array( 'stats' => $stats, 'scanned_at' => current_time( 'timestamp' ), 'scanned_date' => current_time( 'M j, Y g:i a' ), 'scanned_pages' => count( $rows ), 'used_count' => $used_count, 'unused_count' => $unused_count, ); set_transient( $transient_key, $result, 3 * DAY_IN_SECONDS ); return $result; } public function ajax_scan_widget_usage() { check_ajax_referer( 'aafe_usage_stats_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'dashboard' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Permission denied.', 'addronix-addons-for-elementor' ) ) ); } $usage_data = self::get_widget_usage_stats( true ); wp_send_json_success( array( 'message' => sprintf( /* translators: 1: scanned pages count, 2: used widgets count */ esc_html__( 'Scan complete: %1$d Elementor pages analyzed. %2$d widgets in use.', 'addronix-addons-for-elementor' ), $usage_data['scanned_pages'], $usage_data['used_count'] ), 'usage' => $usage_data['stats'], 'scanned_date' => $usage_data['scanned_date'], 'scanned_pages' => $usage_data['scanned_pages'], 'used_count' => $usage_data['used_count'], 'unused_count' => $usage_data['unused_count'], ) ); } public function ajax_disable_unused_widgets() { check_ajax_referer( 'aafe_usage_stats_nonce', 'nonce' ); if ( ! self::current_user_can_access( 'dashboard' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Permission denied.', 'addronix-addons-for-elementor' ) ) ); } $usage_data = self::get_widget_usage_stats( false ); $stats = isset( $usage_data['stats'] ) ? $usage_data['stats'] : array(); $settings = get_option( self::OPTION_KEY, array() ); $disabled_widgets = isset( $settings['disabled_widgets'] ) && is_array( $settings['disabled_widgets'] ) ? $settings['disabled_widgets'] : array(); $disabled_count = 0; foreach ( $stats as $slug => $info ) { if ( 0 === $info['count'] && ! in_array( $slug, $disabled_widgets, true ) ) { $disabled_widgets[] = $slug; $disabled_count++; } } $settings['disabled_widgets'] = array_values( array_unique( $disabled_widgets ) ); update_option( self::OPTION_KEY, $settings ); wp_send_json_success( array( 'message' => sprintf( /* translators: %d: count of disabled widgets */ esc_html__( '%d unused widgets have been disabled to optimize site performance.', 'addronix-addons-for-elementor' ), $disabled_count ), 'disabled_slugs' => $settings['disabled_widgets'], 'new_active_count' => count( \AddronixAddons\Widgets_Manager::get_all_widget_metadata() ) - count( $settings['disabled_widgets'] ), ) ); } }