assertAllowed( self::ACTION_DEACTIVATE_LEGACY ); $deactivated = $this->deactivateLegacy(); $this->finish( empty( $deactivated ) ? 'nothing' : 'deactivated' ); } /** * Request handler: clear the markers, then report back. */ public function handleResetMarkers(): void { $this->assertAllowed( self::ACTION_RESET_MARKERS ); $this->resetMarkers(); $this->finish( 'markers-reset' ); } /** * Deactivate every legacy monolith that is currently active. * * Separate from the request handler so the repair itself can be * exercised without a nonce, a capability and a redirect that would * end the process. * * @return array The plugin files that were deactivated. */ public function deactivateLegacy(): array { $active = LegacyProEnvironment::activeLegacyInstallations(); if ( empty( $active ) ) { return array(); } if ( ! function_exists( 'deactivate_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $files = array(); foreach ( $active as $entry ) { $files[] = $entry['file']; } // $silent = true: do not run the old plugin's deactivation hook. deactivate_plugins( $files, true ); LegacyMonolithDetector::flushCache(); return $files; } /** * Clear the one-shot markers so migration and addon install can run * again. Leaves the licence key and every form setting untouched. */ public function resetMarkers(): void { foreach ( LegacyProEnvironment::ONE_SHOT_MARKERS as $marker ) { delete_option( $marker ); if ( function_exists( 'delete_site_option' ) ) { delete_site_option( $marker ); } } foreach ( LegacyProEnvironment::STALE_TRANSIENTS as $transient ) { delete_transient( $transient ); if ( function_exists( 'delete_site_transient' ) ) { delete_site_transient( $transient ); } } } /** * Capability + nonce, in that order, before anything is touched. */ private function assertAllowed( string $action ): void { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You are not allowed to run this repair.', 'double-opt-in' ) ); } check_admin_referer( $action ); } /** * Back to where the operator came from, carrying the outcome. */ private function finish( string $result ): void { $target = wp_get_referer(); if ( ! is_string( $target ) || $target === '' ) { $target = admin_url( 'site-health.php' ); } wp_safe_redirect( add_query_arg( self::RESULT_ARG, $result, $target ) ); exit; } /** * Tell the operator what happened. Deliberately dismissible — unlike * the problem notices, this one is a receipt, not a warning. */ public function renderResultNotice(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $result = isset( $_GET[ self::RESULT_ARG ] ) ? sanitize_key( wp_unslash( $_GET[ self::RESULT_ARG ] ) ) : ''; if ( $result === '' ) { return; } $messages = array( 'deactivated' => __( 'The old Double Opt-In Pro plugin has been deactivated. You can now activate Double Opt-In Pro 4 and install the modules you need. To remove the old plugin for good, delete its folder over FTP — do not use the WordPress delete button, which would drop your opt-out table.', 'double-opt-in' ), 'markers-reset' => __( 'The Pro setup markers have been cleared. Licence migration and module installation can run again. Your licence key was not touched.', 'double-opt-in' ), 'nothing' => __( 'Nothing to repair — no active copy of the old Double Opt-In Pro plugin was found.', 'double-opt-in' ), ); if ( ! isset( $messages[ $result ] ) ) { return; } printf( '

%s

', esc_html( $messages[ $result ] ) ); } }