KOKINIO - MANAGER
Edit File: st-resetter.php
<?php /** * Zip AI - Module. * * This file is used to register and manage the Zip AI Modules. * * @package zip-ai */ namespace STImporter\Resetter; use Throwable; use Exception; use STImporter\Importer\ST_Importer_Log; // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } /** * The Module Class. */ class ST_Resetter { /** * Instance of this class. * * @since 1.0.0 * @var self Class object. */ private static $instance = null; /** * Initiator of this class. * * @since 1.0.0 * @return self initialized object of this class. */ public static function get_instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Backup our existing settings. * * @return string */ public static function backup_settings() { ST_Importer_Log::add( 'Starting backup of site settings.' ); $file_name = 'astra-sites-backup-' . gmdate( 'd-M-Y-h-i-s' ) . '.json'; $old_settings = get_option( 'astra-settings', array() ); $upload_dir = self::log_dir(); $upload_path = trailingslashit( $upload_dir['path'] ); $log_file = $upload_path . $file_name; $file_system = self::get_filesystem(); ST_Importer_Log::add( sprintf( 'Backup Settings - Backup file path: %s', $log_file ), 'info', array( 'file_name' => $file_name, 'settings_count' => count( $old_settings ), ) ); // If file system fails? Then take a backup in site option. $write_failed = ! $file_system || ! method_exists( $file_system, 'put_contents' ) || false === $file_system->put_contents( $log_file, (string) wp_json_encode( $old_settings ), FS_CHMOD_FILE ); if ( $write_failed ) { update_option( 'astra_sites_' . $file_name, $old_settings, false ); ST_Importer_Log::add( 'Backup Settings - Backup file could not be written to filesystem. Using site option as fallback.', 'warning', array( 'fallback_option_key' => 'astra_sites_' . $file_name, ) ); } else { ST_Importer_Log::add( sprintf( 'Backup Settings - Settings backup created successfully at %s', $log_file ), 'success', array( 'file_path' => $log_file, 'settings_count' => count( $old_settings ), ) ); } return $log_file; } /** * Log file directory * * @since 1.0.0 * @param string $dir_name Directory Name. * @return array<string, string> Uploads directory array. */ public static function log_dir( $dir_name = 'st-importer' ) { $upload_dir = wp_upload_dir(); // Build the paths. $dir_info = array( 'path' => $upload_dir['basedir'] . '/' . $dir_name . '/', 'url' => $upload_dir['baseurl'] . '/' . $dir_name . '/', ); // Create the upload dir if it doesn't exist. if ( ! file_exists( $dir_info['path'] ) ) { // Create the directory. wp_mkdir_p( $dir_info['path'] ); $filesystem = self::get_filesystem(); if ( $filesystem && method_exists( $filesystem, 'put_contents' ) ) { // Add an index file for security. $filesystem->put_contents( $dir_info['path'] . 'index.html', '' ); // Add an .htaccess for security. $filesystem->put_contents( $dir_info['path'] . '.htaccess', 'deny from all' ); } } return $dir_info; } /** * Get an instance of WP_Filesystem. * * Returns null when WP_Filesystem() fails to initialise (e.g. FTP method * selected without valid credentials), preventing fatal errors when callers * invoke filesystem methods on a partially initialised global. * * @since 1.0.0 * @return \WP_Filesystem_Base|null Filesystem instance, or null on failure. */ public static function get_filesystem() { global $wp_filesystem; require_once ABSPATH . '/wp-admin/includes/file.php'; if ( ! WP_Filesystem() ) { return null; } return $wp_filesystem; } /** * Reset site options * * @since 1.0.0 * * @param array<string, mixed> $options option aray. * @return void */ public static function reset_site_options( $options = array() ) { if ( ! is_array( $options ) ) { ST_Importer_Log::add( 'Reset Site Options - No options provided or invalid format.', 'warning' ); return; } if ( empty( $options ) ) { ST_Importer_Log::add( 'Reset Site Options - No site options to reset.', 'warning' ); return; } ST_Importer_Log::add( sprintf( 'Reset Site Options - Starting reset of %d site options.', count( $options ) ), 'info', array( 'total_options' => count( $options ), ) ); // Check if SureCart is required plugin, then clear API token and account cache. $required_plugins = (array) astra_get_site_data( 'required-plugins' ); $plugins_slug = array_column( $required_plugins, 'slug' ); if ( in_array( 'surecart', $plugins_slug, true ) ) { ST_Importer_Log::add( 'Reset Site Options - SureCart detected as required plugin. Clearing API token and account cache.', 'info' ); if ( class_exists( '\SureCart' ) && class_exists( '\SureCart\Models\ApiToken' ) ) { // 1. Clear the API token \SureCart\Models\ApiToken::clear(); // 2. Clear the account cache. \SureCart::account()->clearCache(); } // Optionally, clear the option value to ensure it's reset. $options['sc_api_token'] = ''; } $deleted_count = 0; $failed_count = 0; foreach ( $options as $option_key => $option_value ) { if ( delete_option( $option_key ) ) { $deleted_count++; } else { $failed_count++; } } ST_Importer_Log::add( sprintf( 'Reset Site Options - Site options reset completed. Deleted: %d, Failed or not found: %d', $deleted_count, $failed_count ), 'success', array( 'total_options' => count( $options ), 'deleted_count' => $deleted_count, 'failed_count' => $failed_count, 'option_keys' => array_keys( $options ), ) ); } /** * Reset customizer data * * @since 1.0.0 * @return void */ public static function reset_customizer_data() { ST_Importer_Log::add( 'Reset Customizer Data - Starting reset of customizer settings.', 'info' ); $settings = get_option( 'astra-settings', array() ); $settings_count = count( $settings ); ST_Importer_Log::add( sprintf( 'Reset Customizer Data - Retrieved customizer data with %d settings entries.', $settings_count ), 'info', array( 'settings_count' => $settings_count, ) ); if ( delete_option( 'astra-settings' ) ) { ST_Importer_Log::add( sprintf( 'Reset Customizer Data - Customizer settings successfully reset. Deleted %d settings entries.', $settings_count ), 'success', array( 'settings_count' => $settings_count, 'option_name' => 'astra-settings', ) ); } else { ST_Importer_Log::add( 'Reset Customizer Data - Customizer settings reset completed, but option may not have existed or was already empty.', 'warning', array( 'settings_count' => $settings_count, 'option_name' => 'astra-settings', ) ); } } /** * Reset widgets data * * @since 1.0.0 * * @param array<int, array<string, mixed>> $old_widgets_data widget data. * @return void */ public static function reset_widgets_data( $old_widgets_data = array() ) { ST_Importer_Log::add( 'Reset Widgets Data - Starting reset of widgets data.', 'info' ); if ( empty( $old_widgets_data ) ) { ST_Importer_Log::add( 'Reset Widgets Data - No old widgets data provided for reset.', 'warning' ); return; } $old_widget_ids = array(); foreach ( $old_widgets_data as $old_sidebar_key => $old_widgets ) { if ( ! empty( $old_widgets ) && is_array( $old_widgets ) ) { $old_widget_ids = array_merge( $old_widget_ids, $old_widgets ); } } ST_Importer_Log::add( sprintf( 'Reset Widgets Data - Retrieved %d widgets from %d sidebars.', count( $old_widget_ids ), count( $old_widgets_data ) ), 'info', array( 'total_widgets' => count( $old_widget_ids ), 'total_sidebars' => count( $old_widgets_data ), ) ); // Process if not empty. $sidebars_widgets = get_option( 'sidebars_widgets', array() ); if ( empty( $old_widget_ids ) || empty( $sidebars_widgets ) ) { ST_Importer_Log::add( 'Reset Widgets Data - No widgets or sidebars found to reset.', 'warning', array( 'old_widget_ids' => count( $old_widget_ids ), 'sidebars_widgets' => count( $sidebars_widgets ), ) ); return; } $moved_to_inactive = 0; $removed_from_sidebars = 0; foreach ( $sidebars_widgets as $sidebar_id => $widgets ) { $widgets = (array) $widgets; if ( ! empty( $widgets ) && is_array( $widgets ) ) { foreach ( $widgets as $widget_id ) { if ( in_array( $widget_id, $old_widget_ids, true ) ) { // Move old widget to inacitve list. $sidebars_widgets['wp_inactive_widgets'][] = $widget_id; $moved_to_inactive++; // Remove old widget from sidebar. $sidebars_widgets[ $sidebar_id ] = array_diff( $sidebars_widgets[ $sidebar_id ], array( $widget_id ) ); $removed_from_sidebars++; } } } } update_option( 'sidebars_widgets', $sidebars_widgets ); ST_Importer_Log::add( sprintf( 'Reset Widgets Data - Widgets reset completed. Moved %d widgets to inactive, removed %d from sidebars.', $moved_to_inactive, $removed_from_sidebars ), 'success', array( 'total_widgets' => count( $old_widget_ids ), 'moved_to_inactive' => $moved_to_inactive, 'removed_from_sidebars' => $removed_from_sidebars, ) ); } /** * Reset posts in chunks. * * @since 1.0.0 * * @return void */ public static function reset_posts() { ST_Importer_Log::add( 'Reset Posts - Starting post deletion process.', 'info' ); ST_Resetter::get_instance()->start_error_handler(); // Suspend bunches of stuff in WP core. wp_defer_term_counting( true ); wp_defer_comment_counting( true ); wp_suspend_cache_invalidation( true ); $posts = json_decode( stripslashes( sanitize_text_field( $_POST['ids'] ) ), true ); //phpcs:ignore WordPress.Security.NonceVerification.Missing if ( empty( $posts ) ) { ST_Importer_Log::add( 'Reset Posts - No posts provided for deletion.', 'warning' ); wp_suspend_cache_invalidation( false ); wp_defer_term_counting( false ); wp_defer_comment_counting( false ); self::stop_error_handler(); return; } ST_Importer_Log::add( sprintf( 'Reset Posts - Retrieved %d posts for deletion.', count( $posts ) ), 'info', array( 'total_posts' => count( $posts ), ) ); $deleted_count = 0; $post_types = array(); foreach ( $posts as $key => $post_id ) { $post_id = absint( $post_id ); if ( $post_id ) { $post_type = get_post_type( $post_id ); if ( ! isset( $post_types[ $post_type ] ) ) { $post_types[ $post_type ] = 0; } $post_types[ $post_type ]++; do_action( 'astra_sites_before_delete_imported_posts', $post_id, $post_type ); if ( wp_delete_post( $post_id, true ) ) { $deleted_count++; } } } // Re-enable stuff in core. wp_suspend_cache_invalidation( false ); wp_cache_flush(); foreach ( get_taxonomies() as $tax ) { delete_option( "{$tax}_children" ); _get_term_hierarchy( $tax ); } wp_defer_term_counting( false ); wp_defer_comment_counting( false ); ST_Importer_Log::add( sprintf( 'Reset Posts - Post deletion completed. Deleted %d of %d posts.', $deleted_count, count( $posts ) ), 'success', array( 'total_posts' => count( $posts ), 'deleted_count' => $deleted_count, 'post_types' => $post_types, ) ); self::stop_error_handler(); } /** * Start the error handling. * * @return void */ public function start_error_handler() { if ( ! interface_exists( 'Throwable' ) ) { // Fatal error handler for PHP < 7. register_shutdown_function( array( $this, 'shutdown_handler' ) ); } // Fatal error handler for PHP >= 7, and uncaught exception handler for all PHP versions. set_exception_handler( array( $this, 'exception_handler' ) ); } /** * Stop and restore the error handlers. * * @return void */ public static function stop_error_handler() { // Restore the error handlers. restore_error_handler(); restore_exception_handler(); } /** * Displays fatal error output for sites running PHP < 7. * * @return void */ public function shutdown_handler() { $e = error_get_last(); if ( empty( $e ) || ! ( $e['type'] & ST_ERROR_FATALS ) ) { // @phpstan-ignore-line return; } if ( $e['type'] & E_RECOVERABLE_ERROR ) { $error = 'Catchable fatal error'; } else { $error = 'Fatal error'; } if ( wp_doing_ajax() ) { wp_send_json_error( array( 'message' => __( 'There was an error your website.', 'astra-sites' ), 'stack' => array( 'error-message' => $error, 'error' => $e, ), ) ); } } /** * Uncaught exception handler. * * In PHP >= 7 this will receive a Throwable object. * In PHP < 7 it will receive an Exception object. * * @throws Exception Exception that is catched. * @param Throwable|Exception $e The error or exception. * * @return void */ public function exception_handler( $e ) { if ( is_a( $e, 'Exception' ) ) { $error = 'Uncaught Exception'; } else { $error = 'Uncaught Error'; } if ( wp_doing_ajax() ) { wp_send_json_error( array( 'message' => __( 'There was an error on your website.', 'astra-sites' ), 'stack' => array( 'error-message' => sprintf( '%s: %s', $error, $e->getMessage() ), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTrace(), ), ) ); } throw $e; } /** * Reset terms and forms. * * @since 1.0.0 * * @return void */ public static function reset_terms_and_forms() { ST_Importer_Log::add( 'Reset Terms and Forms - Starting reset of terms and forms.', 'info' ); ST_Resetter::get_instance()->start_error_handler(); $terms = self::astra_sites_get_reset_term_data(); if ( empty( $terms ) ) { ST_Importer_Log::add( 'Reset Terms and Forms - No terms found for deletion.', 'warning' ); } else { ST_Importer_Log::add( sprintf( 'Reset Terms and Forms - Retrieved %d terms for deletion.', count( $terms ) ), 'info', array( 'total_terms' => count( $terms ), ) ); $deleted_terms = 0; $failed_terms = 0; $term_taxonomies = array(); foreach ( $terms as $key => $term_id ) { $term_id = absint( $term_id ); if ( $term_id ) { $term = get_term( $term_id ); if ( ! is_wp_error( $term ) && ! empty( $term ) && is_object( $term ) ) { if ( ! isset( $term_taxonomies[ $term->taxonomy ] ) ) { $term_taxonomies[ $term->taxonomy ] = 0; } $term_taxonomies[ $term->taxonomy ]++; do_action( 'astra_sites_before_delete_imported_terms', $term_id, $term ); if ( wp_delete_term( $term_id, $term->taxonomy ) ) { $deleted_terms++; } else { $failed_terms++; } } } } ST_Importer_Log::add( sprintf( 'Reset Terms and Forms - Term deletion completed. Deleted %d of %d terms.', $deleted_terms, count( $terms ) ), 'success', array( 'total_terms' => count( $terms ), 'deleted_count' => $deleted_terms, 'failed_count' => $failed_terms, 'term_taxonomies' => $term_taxonomies, ) ); } $forms = self::astra_sites_get_reset_form_data(); if ( empty( $forms ) ) { ST_Importer_Log::add( 'Reset Terms and Forms - No forms found for deletion.', 'warning' ); } else { ST_Importer_Log::add( sprintf( 'Reset Terms and Forms - Retrieved %d forms for deletion.', count( $forms ) ), 'info', array( 'total_forms' => count( $forms ), ) ); $deleted_forms = 0; $failed_forms = 0; foreach ( $forms as $key => $post_id ) { $post_id = absint( $post_id ); if ( $post_id ) { do_action( 'astra_sites_before_delete_imported_wp_forms', $post_id ); if ( wp_delete_post( $post_id, true ) ) { $deleted_forms++; } else { $failed_forms++; } } } ST_Importer_Log::add( sprintf( 'Reset Terms and Forms - Form deletion completed. Deleted %d of %d forms.', $deleted_forms, count( $forms ) ), 'success', array( 'total_forms' => count( $forms ), 'deleted_count' => $deleted_forms, 'failed_count' => $failed_forms, ) ); } self::stop_error_handler(); } /** * Get all the forms to be reset. * * @since 1.0.0 * @return array<int, int> */ public static function astra_sites_get_reset_form_data() { global $wpdb; $form_ids = $wpdb->get_col( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_astra_sites_imported_wp_forms'" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the WP forms. Traditional WP_Query would have been expensive here. return $form_ids; } /** * Get all the terms to be reset. * * @since 1.0.0 * @return array<int, int> */ public static function astra_sites_get_reset_term_data() { global $wpdb; $term_ids = $wpdb->get_col( "SELECT term_id FROM {$wpdb->termmeta} WHERE meta_key='_astra_sites_imported_term'" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- We need this to get all the terms and taxonomy. Traditional WP_Query would have been expensive here. return $term_ids; } }