wizard
1 month ago
class-dashboard-controller.php
1 month ago
class-license-resolve-controller.php
1 month ago
class-newsletter-signup-controller.php
1 month ago
class-notice-controller.php
1 month ago
class-plugin-reset-controller.php
1 month ago
class-rewrite-check-controller.php
1 month ago
class-settings-controller.php
1 month ago
class-troubleshooting-controller.php
1 month ago
class-plugin-reset-controller.php
162 lines
| 1 | <?php |
| 2 | |
| 3 | namespace SuperbAddons\Admin\Controllers; |
| 4 | |
| 5 | defined('ABSPATH') || exit(); |
| 6 | |
| 7 | use Exception; |
| 8 | use SuperbAddons\Data\Controllers\KeyController; |
| 9 | use SuperbAddons\Data\Controllers\LogController; |
| 10 | use SuperbAddons\Data\Controllers\Option; |
| 11 | use SuperbAddons\Gutenberg\Controllers\GutenbergEnhancementsController; |
| 12 | use SuperbAddons\Library\Controllers\FavoritesController; |
| 13 | use SuperbAddons\Gutenberg\Form\FormGoogleAuth; |
| 14 | use SuperbAddons\Gutenberg\Form\FormRegistry; |
| 15 | use SuperbAddons\Gutenberg\Form\FormSettings; |
| 16 | use SuperbAddons\Gutenberg\Form\FormSubmissionCPT; |
| 17 | use SuperbAddons\Gutenberg\Form\FormSubmissionHandler; |
| 18 | use SuperbAddons\Gutenberg\Popup\PopupRegistry; |
| 19 | use SuperbAddons\Tours\Controllers\TourController; |
| 20 | |
| 21 | class PluginResetController |
| 22 | { |
| 23 | /** |
| 24 | * Completely wipe every trace of plugin data from this site. |
| 25 | * |
| 26 | * Order matters: license removal must run before its option row is deleted |
| 27 | * so the remote server is notified; the form registry must be read before |
| 28 | * its option row is deleted so every dynamic config option gets cleaned up. |
| 29 | * |
| 30 | * @param bool $include_submissions When true, also deletes every stored form submission CPT post. |
| 31 | * @return bool |
| 32 | */ |
| 33 | public static function RemoveAllPluginData($include_submissions = false) |
| 34 | { |
| 35 | // 1. License first — notify remote server before the option row is deleted locally. |
| 36 | // Guarded so we don't make a pointless remote call when no key is registered. |
| 37 | if (KeyController::HasRegisteredKey()) { |
| 38 | try { |
| 39 | KeyController::RemoveKey(); |
| 40 | } catch (Exception $e) { |
| 41 | // RemoveKey already logs internally; swallow so a network failure |
| 42 | // does not block the rest of the local cleanup. |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | // 2. Unschedule cron hooks. |
| 47 | $cron_hooks = array( |
| 48 | 'superbaddons_share_error_logs', |
| 49 | 'superb_blocks_template_restoration_cleanup', |
| 50 | 'spb_form_spam_purge', |
| 51 | 'spb_form_retention_purge', |
| 52 | ); |
| 53 | foreach ($cron_hooks as $hook) { |
| 54 | wp_clear_scheduled_hook($hook); |
| 55 | } |
| 56 | |
| 57 | // 3. Iterate form registry BEFORE deleting it so we can clean up every |
| 58 | // dynamic spb_form_cfg_{form_id} option row. |
| 59 | $form_registry = get_option(FormRegistry::OPTION_KEY, array()); |
| 60 | if (is_array($form_registry)) { |
| 61 | foreach ($form_registry as $form_id => $_unused) { |
| 62 | delete_option(FormRegistry::CONFIG_PREFIX . sanitize_key($form_id)); |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | // 4. Delete every option row the plugin has ever written. |
| 67 | $option_keys = array( |
| 68 | // Core plugin options (with constants where available) |
| 69 | Option::KEY_DOMAIN, |
| 70 | Option::SETTINGS, |
| 71 | Option::COMPATIBILITY_SETTINGS, |
| 72 | Option::DISABLED_BLOCKS, |
| 73 | Option::GLOBAL_ENHANCEMENTS, |
| 74 | // Core plugin options without constants |
| 75 | 'superbaddonslibrary_service_version', |
| 76 | 'superbaddonslibrary_gutenberg_patterns', |
| 77 | 'superbaddonslibrary_gutenberg_pages', |
| 78 | 'superbaddonslibrary_elementor_sections', |
| 79 | 'superbaddons_errorlogs', |
| 80 | 'superbaddons_wizard_completed_themes', |
| 81 | 'superbaddons_wizard_navigation_post_id', |
| 82 | 'superbaddons_elementor_tour_id', |
| 83 | 'superbaddons_pre_activation', |
| 84 | 'superbaddons_css_block_target_valid_labels', |
| 85 | // Form settings |
| 86 | FormSettings::OPTION_HCAPTCHA_SITE_KEY, |
| 87 | FormSettings::OPTION_HCAPTCHA_SECRET_KEY, |
| 88 | FormSettings::OPTION_RECAPTCHA_SITE_KEY, |
| 89 | FormSettings::OPTION_RECAPTCHA_SECRET_KEY, |
| 90 | FormSettings::OPTION_TURNSTILE_SITE_KEY, |
| 91 | FormSettings::OPTION_TURNSTILE_SECRET_KEY, |
| 92 | FormSettings::OPTION_MAILCHIMP_API_KEY, |
| 93 | FormSettings::OPTION_BREVO_API_KEY, |
| 94 | FormSettings::OPTION_GOOGLE_SHEETS_CLIENT_EMAIL, |
| 95 | FormSettings::OPTION_GOOGLE_SHEETS_PRIVATE_KEY, |
| 96 | FormSettings::OPTION_WEBHOOK_SECRETS, |
| 97 | 'superbaddons_form_role_permissions', |
| 98 | 'superbaddons_form_access_control_enabled', |
| 99 | 'superbaddons_form_default_email', |
| 100 | 'superbaddons_form_data_retention', |
| 101 | // Registries (delete AFTER iterating above) |
| 102 | FormRegistry::OPTION_KEY, |
| 103 | PopupRegistry::OPTION_KEY, |
| 104 | ); |
| 105 | foreach ($option_keys as $option_key) { |
| 106 | delete_option($option_key); |
| 107 | } |
| 108 | |
| 109 | // 5. Delete transients. |
| 110 | $transient_keys = array( |
| 111 | 'superbaddons_activation_redirect', |
| 112 | 'superbaddons_wizard_recommender_transient', |
| 113 | 'superbaddons_wizard_woocommerce_transient', |
| 114 | 'superb_blocks_template_restoration', |
| 115 | 'superbaddons_template_part_preview', |
| 116 | FormGoogleAuth::TRANSIENT_KEY, |
| 117 | ); |
| 118 | foreach ($transient_keys as $transient_key) { |
| 119 | delete_transient($transient_key); |
| 120 | } |
| 121 | |
| 122 | // 5b. Purge namespaced commerce transients (product/stock/search caches). |
| 123 | // Keys look like: _transient_superb_commerce_product_123_1700000000 etc. |
| 124 | // Direct query: no WP API exists for bulk-deleting transients by prefix; iterating get_option() to find them would itself bypass caching and is far slower. Pattern is a constant string with literal underscores escaped so they aren't treated as LIKE wildcards. Caching does not apply to a DELETE; this runs once during plugin reset. |
| 125 | global $wpdb; |
| 126 | if (isset($wpdb) && is_object($wpdb)) { |
| 127 | // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching |
| 128 | $wpdb->query( |
| 129 | "DELETE FROM {$wpdb->options} |
| 130 | WHERE option_name LIKE '_transient_superb_commerce\\_%' |
| 131 | OR option_name LIKE '_transient_timeout_superb_commerce\\_%'" |
| 132 | ); |
| 133 | } |
| 134 | |
| 135 | // 6. Delete user meta (site-wide). |
| 136 | AdminNoticeController::Cleanup(); |
| 137 | NewsletterSignupController::Cleanup(); |
| 138 | delete_metadata('user', 0, TourController::TOUR_DASHBOARD_WELCOME_META, false, true); |
| 139 | delete_metadata('user', 0, TourController::TOUR_BLOCK_THEME_META, false, true); |
| 140 | delete_metadata('user', 0, GutenbergEnhancementsController::ENHANCEMENTS_OPTION, false, true); |
| 141 | delete_metadata('user', 0, FavoritesController::FAVORITES_META_KEY, false, true); |
| 142 | |
| 143 | // 7. Optionally wipe every stored form submission. |
| 144 | if ($include_submissions) { |
| 145 | $submission_ids = get_posts(array( |
| 146 | 'post_type' => FormSubmissionCPT::POST_TYPE, |
| 147 | 'post_status' => 'any', |
| 148 | 'posts_per_page' => -1, |
| 149 | 'fields' => 'ids', |
| 150 | 'no_found_rows' => true, |
| 151 | 'update_post_meta_cache' => false, |
| 152 | 'update_post_term_cache' => false, |
| 153 | )); |
| 154 | if (!empty($submission_ids)) { |
| 155 | FormSubmissionHandler::BulkDelete($submission_ids); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | return true; |
| 160 | } |
| 161 | } |
| 162 |