init_hooks(); } // Get instance public static function get_instance() { if ( self::$instance == null ) { self::$instance = new self(); } return self::$instance; } // Initialize hooks private function init_hooks() { add_action( 'wp_ajax_ultimate_post_kit_setup_wizard_install_plugins', array( $this, 'install_plugins' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_action( 'admin_init', array( $this, 'activate_default_widgets' ) ); add_action( 'admin_init', array( $this, 'maybe_display_setup_wizard' ) ); add_action( 'admin_init', array( $this, 'check_manual_wizard_request' ) ); // NOTE: WordPress manages plugin/translation updates. Do not add filters // that interfere with the built-in update pipeline (wp.org Guideline). } // Check for manual wizard requests public function check_manual_wizard_request() { // This runs on admin_init, which also fires on admin-ajax.php before any // authentication, and on every admin screen for every logged-in role. The setup // wizard is an administrator-only flow, so gate it explicitly. if ( wp_doing_ajax() || ! current_user_can( 'manage_options' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check of a GET flag to decide whether to render the setup wizard screen, no form data processed. $is_setup_wizard_request = isset($_GET['upk_setup_wizard']) && $_GET['upk_setup_wizard'] === 'show'; if ( $is_setup_wizard_request ) { // Use the same approach as first activation - completely override the page add_action('admin_head', function() { ?> display_page(); }); } } // Display wizard in fullscreen mode public function display_wizard_fullscreen() { ?> '; $this->display_page(); echo ''; ?>
display_page(); }); } } // Keep the admin_menu method for reference but not hooked public function admin_menu() { add_submenu_page( 'ultimate_post_kit_options', esc_html__( 'Setup Wizard', 'ultimate-post-kit' ), esc_html__( 'Setup Wizard', 'ultimate-post-kit' ), 'manage_options', 'ultimate-post-kit-setup-wizard', array( $this, 'display_page' ) ); } public function display_page() { ?> admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'ultimate_post_kit_setup_wizard_nonce' ), 'is_fullscreen' => true ) ); } public static function get_widget_map() { $arr_obj = ModuleService::get_widget_settings( function ( $settings ) { $core_widgets = $settings['settings_fields']['ultimate_post_kit_active_modules']; return $core_widgets; } ); return $arr_obj; } // Install plugins public function install_plugins() { check_ajax_referer( 'ultimate_post_kit_setup_wizard_nonce', 'nonce' ); $plugin_slugs = isset( $_POST['plugins'] ) ? map_deep( wp_unslash( $_POST['plugins'] ), 'sanitize_text_field' ) : array(); if ( empty( $plugin_slugs ) || ! is_array( $plugin_slugs ) ) { wp_send_json_error( array( 'message' => 'Invalid plugins array' ) ); } if ( ! current_user_can( 'install_plugins' ) ) { wp_send_json_error( array( 'message' => 'Unauthorized' ) ); } include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; include_once ABSPATH . 'wp-admin/includes/plugin-install.php'; include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php'; include_once ABSPATH . 'wp-admin/includes/plugin.php'; // Replace new \Plugin_Installer_Skin with new Quiet_Upgrader_Skin when output needs to be suppressed. $skin = new Quiet_Upgrader_Skin(); // $skin = new \Plugin_Installer_Skin( array( 'api' => $api ) ); $upgrader = new \Plugin_Upgrader( $skin ); // $upgrader = new \Plugin_Upgrader(); $installedPlugins = get_plugins(); $results = array(); foreach ( $plugin_slugs as $plugin_slug ) { // skip when the plugin is already active if (is_plugin_active($plugin_slug)) { $results[] = array( 'slug' => $plugin_slug, 'success' => true, 'message' => 'Installed and activated successfully', ); continue; } // Download the plugin if the plugin is not installed if (!isset($installedPlugins[$plugin_slug])) { $slug = explode('/', $plugin_slug)[0]; $api = plugins_api( 'plugin_information', array( 'slug' => $slug ) ); if ( is_wp_error( $api ) ) { $results[] = array( 'slug' => $plugin_slug, 'success' => false, 'message' => $api->get_error_message(), ); continue; } $result = $upgrader->install( $api->download_link ); if ( is_wp_error( $result ) ) { $results[] = array( 'slug' => $plugin_slug, 'success' => false, 'message' => $result->get_error_message(), ); continue; } } // Activating a plugin is a separate capability from installing one, so it is // checked on its own rather than being implied by 'install_plugins' above. if ( ! current_user_can( 'activate_plugins' ) ) { $results[] = array( 'slug' => $plugin_slug, 'success' => false, 'message' => esc_html__( 'You do not have permission to activate plugins on this site.', 'ultimate-post-kit' ), ); continue; } // active the plugin if ( is_plugin_inactive( $plugin_slug ) ) { $activation_result = activate_plugin( $plugin_slug ); if ( is_wp_error( $activation_result ) ) { $results[] = array( 'slug' => $plugin_slug, 'success' => false, 'message' => $activation_result->get_error_message(), ); continue; } $results[] = array( 'slug' => $plugin_slug, 'success' => true, 'message' => 'Installed and activated successfully', ); } } ob_clean(); wp_send_json_success( array( 'results' => $results ) ); wp_die(); } /** * Get the main plugin file path for a given slug. * * @param string $slug Plugin slug. * @return string|false Plugin file path or false if not found. */ private function get_plugin_file( $slug ) { $plugins = get_plugins(); foreach ( $plugins as $file => $plugin ) { if ( strpos( $file, $slug ) !== false ) { return $file; } } return false; } /** * Activate default widgets in setup wizard */ public function activate_default_widgets() { // Also reached anonymously via admin-ajax.php, which fires admin_init before // authentication. Enabling widget modules is an administrator action. if ( wp_doing_ajax() || ! current_user_can( 'manage_options' ) ) { return; } // List of widgets to activate by default $default_active_widgets = array( 'alex-grid', 'alice-grid', 'alter-carousel', 'banner', 'buzz-list', 'carbon-slider', 'featured-list', 'news-ticker', 'pholox-slider', 'timeline', 'category', 'social-count', 'tag-cloud' ); // Get current active modules $active_modules = get_option('ultimate_post_kit_active_modules', array()); // Make sure $active_modules is an array if (!is_array($active_modules)) { $active_modules = array(); } // Check if active_modules option exists and is not empty // If it's a new installation or option doesn't exist, we'll set our defaults $modified = false; foreach ($default_active_widgets as $widget) { // Only set if not already defined (prevents overriding user settings on existing installations) if (!isset($active_modules[$widget])) { $active_modules[$widget] = 'on'; $modified = true; } } // Update the option if changes were made if ($modified) { update_option('ultimate_post_kit_active_modules', $active_modules); } } } // Initialize the Setup Wizard Setup_Wizard::get_instance(); use Elementor\TemplateLibrary\Source_Local; add_action('wp_ajax_ultimate_post_kit_import_elementor_template', function () { check_ajax_referer( 'ultimate_post_kit_setup_wizard_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) ); wp_die(); } $json_url = isset( $_POST['import_url'] ) ? esc_url_raw( wp_unslash( $_POST['import_url'] ) ) : ''; $response = wp_safe_remote_get($json_url, array( 'timeout' => 60, )); if (is_wp_error($response)) { wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]); wp_die(); } $sourceData = wp_remote_retrieve_body($response); $sourceData2 = json_decode($sourceData, true); if (!$sourceData2 || !is_array($sourceData2)) { wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]); wp_die(); } $temp_file = wp_upload_dir()['path'] . '/elementor_import_' . time() . '.json'; file_put_contents($temp_file, $sourceData); // Initialize Elementor's Template Importer if (!class_exists('\Elementor\TemplateLibrary\Source_Local')) { wp_delete_file($temp_file); wp_send_json_error(['message' => esc_html__('Elementor is not installed or activated!', 'ultimate-post-kit')]); wp_die(); } $manager = new Source_Local(); $templateData = $manager->import_template('elementor_template', $temp_file); wp_delete_file($temp_file); // Delete temp file after import if (is_wp_error($templateData) || !is_array($templateData) || empty($templateData[0]['template_id'])) { wp_send_json_error(['message' => esc_html__('Failed to import template!', 'ultimate-post-kit')]); wp_die(); } $template_id = $templateData[0]['template_id']; $metaData = get_post_meta($template_id); $page_title = isset($_POST['title']) ? sanitize_text_field(wp_unslash($_POST['title'])) : esc_html__("No Title", 'ultimate-post-kit'); // Validate Elementor Data if (!isset($metaData['_elementor_data'][0])) { wp_send_json_error(['message' => esc_html__('Elementor data not found in template.', 'ultimate-post-kit')]); wp_die(); } $_elementor_data = wp_slash($metaData['_elementor_data'][0]); // Create New Page $new_post_id = wp_insert_post([ 'post_type' => 'page', 'post_status' => empty($page_title) ? 'draft' : 'publish', 'post_title' => $page_title, 'post_content' => '', ]); if (is_wp_error($new_post_id)) { wp_send_json_error(['message' => esc_html__('Failed to create page!', 'ultimate-post-kit')]); wp_die(); } // Assign Elementor Template Data update_post_meta($new_post_id, '_elementor_data', $_elementor_data); // Import Page Settings if available if (isset($metaData['_elementor_page_settings'][0])) { $_elementor_page_settings = is_serialized($metaData['_elementor_page_settings'][0]) ? unserialize($metaData['_elementor_page_settings'][0], ['allowed_classes' => false]) : $metaData['_elementor_page_settings'][0]; update_post_meta($new_post_id, '_elementor_page_settings', $_elementor_page_settings); } update_post_meta($new_post_id, '_elementor_template_type', $sourceData2['type'] ?? ''); update_post_meta($new_post_id, '_elementor_edit_mode', 'builder'); // update_post_meta($new_post_id, '_wp_page_template', !empty($pageTemplate) ? $pageTemplate : 'elementor_header_footer'); wp_send_json_success([ 'message' => esc_html__('The template was imported successfully.', 'ultimate-post-kit'), 'ids' => $new_post_id, 'edit_link' => admin_url('post.php?post=' . $new_post_id . '&action=elementor'), ]); } ); add_action('wp_ajax_ultimate_post_kit_import_elementor_bundle_template', function () { check_ajax_referer('ultimate_post_kit_setup_wizard_nonce', 'nonce'); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) ); wp_die(); } $file_url = isset($_POST['import_url']) ? esc_url_raw(wp_unslash($_POST['import_url'])) : ''; if (!filter_var($file_url, FILTER_VALIDATE_URL) || 0 !== strpos($file_url, 'http')) { wp_send_json_error(['message' => esc_html__('Invalid import URL', 'ultimate-post-kit')]); } $remote_zip_request = wp_safe_remote_get($file_url, array( 'timeout' => 60, )); if (is_wp_error($remote_zip_request)) { wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]); } if (200 !== $remote_zip_request['response']['code']) { wp_send_json_error(['message' => esc_html__('Failed to fetch template from URL.', 'ultimate-post-kit')]); } $kit_zip_path = Plugin::$instance->uploads_manager->create_temp_file($remote_zip_request['body'], 'kit.zip'); $app = Plugin::$instance->app; if (!$app) { wp_send_json_error(['message' => esc_html__('Elementor app not available', 'ultimate-post-kit')]); } $import_export_module = $app->get_component('import-export'); try { $result = $import_export_module->upload_kit($kit_zip_path, 'local'); $manifest = $result['manifest'] ?? []; $plugins = $manifest['plugins']; $missingPlugins = []; foreach ($plugins as $plugin) { $pluginSlug = $plugin['plugin'].".php"; if (is_plugin_inactive($pluginSlug)) { $missingPlugins[] = $plugin; } } if (count($missingPlugins)) { wp_send_json_error([ 'plugins' => $missingPlugins, 'message' => esc_html__('Missing plugins', 'ultimate-post-kit'), ]); } $tmp_folder_id = $result['session']; $includes = []; $selectedCustomPostTypes = []; if (isset($manifest['templates'])) { $includes[] = 'templates'; } if (isset($manifest['content'])) { $includes[] = 'content'; } if (isset($manifest['site-settings'])) { $includes[] = 'settings'; } if (isset($manifest['custom-post-type-title'])) { $selectedCustomPostTypes = array_keys($manifest['custom-post-type-title']); } $settings = [ 'id' => '', 'session' => $tmp_folder_id, 'include' => $includes, 'overrideConditions' => [], 'selectedCustomPostTypes' => $selectedCustomPostTypes, ]; $import = $import_export_module->import_kit($tmp_folder_id, $settings, true); // Deliberately NOT calling // Plugin::$instance->uploads_manager->enable_unfiltered_files_upload() here. // That permanently sets Elementor's `elementor_unfiltered_files_upload` option, // which Elementor itself surfaces behind an explicit security warning and an // opt-in confirmation. Importing a template must not silently relax another // plugin's upload filtering for the whole site. wp_send_json_success($import); } catch (\Throwable $e) { wp_send_json_error(['message' => esc_html__('Import failed: ', 'ultimate-post-kit') . esc_html($e->getMessage())]); } }); add_action('wp_ajax_ultimate_post_kit_import_elementor_bundle_runner_template', function () { check_ajax_referer('ultimate_post_kit_setup_wizard_nonce', 'nonce'); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'ultimate-post-kit' ) ) ); wp_die(); } $runner = isset($_POST['runner']) ? sanitize_text_field(wp_unslash($_POST['runner'])) : ''; $sessionId = isset($_POST['sessionId']) ? sanitize_text_field(wp_unslash($_POST['sessionId'])) : ''; if (!$runner || !$sessionId) { wp_send_json_error(['message' => esc_html__('Required Param Is Missing.', 'ultimate-post-kit')]); } $app = Plugin::$instance->app; if (!$app) { wp_send_json_error(['message' => esc_html__('Elementor app not available.', 'ultimate-post-kit')]); } try { // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged, WordPress.PHP.IniSet.max_execution_time_Disallowed -- raise the limit only for this admin-triggered template import, which can exceed the default. @ini_set('max_execution_time', 60 * 5); $import_export_module = $app->get_component('import-export'); $import = $import_export_module->import_kit_by_runner($sessionId, $runner); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- hooking into Elementor's own action, not a plugin-defined hook. do_action('elementor/import-export/import-kit/runner/after-run', $import); wp_send_json_success($import); } catch (\Throwable $throwable) { wp_send_json_error(['message' => $throwable->getMessage()]); } });