PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.2
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.2
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
metasync / uninstall.php

uninstall.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.7.2, at uninstall.php

161 lines 6.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Fired when the plugin is uninstalled.
5 *
6 * Cleanup policy:
7 *
8 * - Credentials (the Google Indexing service account — a plaintext private
9 * key — and the IndexNow API key) are ALWAYS deleted. They must never be
10 * left behind in wp_options by a plugin that is no longer installed.
11 * - Everything else (custom tables, options, transients, per-post SEO meta,
12 * log files) is only removed when the site owner opted in via the
13 * "Delete all plugin data when the plugin is uninstalled" toggle in
14 * Advanced Settings. The default is OFF, per WordPress uninstall guidelines.
15 *
16 * The opt-in option is read BEFORE any options are deleted, because the
17 * option itself is plugin data.
18 *
19 * For multisite, the cleanup runs once per site when the plugin is
20 * network-activated and removed.
21 *
22 * @link https://searchatlas.com
23 * @since 1.0.0
24 *
25 * @package Metasync
26 */
27
28 // If uninstall not called from WordPress, then exit.
29 if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
30 exit;
31 }
32
33 // Consent-gated churn signal. Sent first, while the consent option still
34 // exists below; sends nothing when the site never opted in. The plugin main
35 // file is not loaded during uninstall, so the key constant it normally
36 // defines must be defined here or track() is a silent no-op.
37 if ( ! defined( 'METASYNC_POSTHOG_API_KEY' ) ) {
38 define( 'METASYNC_POSTHOG_API_KEY', getenv('METASYNC_POSTHOG_API_KEY') !== false ? getenv('METASYNC_POSTHOG_API_KEY') : 'phc_ntSayCJE7Ad9eBHRG6ygBaY4BJMa8rFjXcXESun9ftkh' ); // gitleaks:allow -- public write-only PostHog project key
39 }
40 require_once plugin_dir_path( __FILE__ ) . 'includes/class-metasync-posthog.php';
41 Metasync_PostHog::get_instance()->track( 'plugin_uninstalled', array() );
42
43 /**
44 * Remove plugin data for the current site.
45 *
46 * @return void
47 */
48 function metasync_uninstall_cleanup() {
49 global $wpdb;
50
51 // Read the opt-in first: the option itself is plugin data and is removed
52 // by the prefix sweep below.
53 $delete_all_data = ( 'yes' === get_option( 'metasync_delete_data_on_uninstall', 'no' ) );
54
55 // Credentials are always removed, regardless of the opt-in.
56 delete_option( 'google_index_service_account' );
57 delete_option( 'metasync_options_bing_instant_indexing' );
58 // Legacy instant-indexing option: holds the Google service-account JSON key.
59 delete_option( 'metasync_options_instant_indexing' );
60
61 if ( ! $delete_all_data ) {
62 return;
63 }
64
65 // Scheduled events.
66 $cron_hooks = array(
67 'metasync_daily_cleanup',
68 'metasync_otto_js_check_event',
69 'metasync_process_otto_batch_cache_job',
70 'metasync_process_otto_crawl_url_job',
71 'metasync_sitemap_async_warmup_event',
72 'metasync_media_batch_optimize_cron',
73 'metasync_check_debug_limits',
74 'metasync_speed_cache_cleanup',
75 'metasync_bing_indexnow_submit_event',
76 'metasync_ph_flush_rollups',
77 );
78 foreach ( $cron_hooks as $cron_hook ) {
79 wp_clear_scheduled_hook( $cron_hook );
80 }
81
82 // Custom tables.
83 $tables = array(
84 'metasync_404_logs',
85 'metasync_redirections',
86 'metasync_heartbeat_error_logs',
87 'metasync_sync_history',
88 'metasync_otto_excluded_urls',
89 'metasync_robots_txt_backups',
90 'metasync_otto_bot_stats',
91 'metasync_otto_bot_logs',
92 );
93 foreach ( $tables as $table ) {
94 $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}{$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- static table names, no user input.
95 }
96
97 // Plugin options and transients (transients live in the options table
98 // under _transient_/_transient_timeout_ prefixes when no object cache
99 // persists them; object-cache-backed transients age out on their own).
100 $option_patterns = array(
101 'metasync\_%',
102 '\_transient\_metasync\_%',
103 '\_transient\_timeout\_metasync\_%',
104 '\_site\_transient\_metasync\_%',
105 '\_site\_transient\_timeout\_metasync\_%',
106 );
107 foreach ( $option_patterns as $option_pattern ) {
108 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", $option_pattern ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- table name from core.
109 }
110
111 // Per-post SEO values saved by the plugin (custom titles, meta
112 // descriptions, robots directives, schema, Open Graph, canonical URLs).
113 // Several metaboxes store their values without the leading underscore
114 // (metasync_common_robots, metasync_schema_markup, metasync_advance_robots,
115 // metasync_post, metasync_post_redirection_meta), so both prefixes are
116 // swept.
117 $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '\_metasync\_%' OR meta_key LIKE 'metasync\_%'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- literal patterns, no user input.
118
119 // On-disk data directory (error logs, zipped logs, cache artifacts).
120 $metasync_data_dir = WP_CONTENT_DIR . '/metasync_data';
121 if ( is_dir( $metasync_data_dir ) ) {
122 metasync_uninstall_rrmdir( $metasync_data_dir );
123 }
124 }
125
126 /**
127 * Recursively delete a directory.
128 *
129 * @param string $dir Absolute directory path.
130 * @return void
131 */
132 function metasync_uninstall_rrmdir( $dir ) {
133 if ( ! is_dir( $dir ) ) {
134 return;
135 }
136 foreach ( scandir( $dir ) as $entry ) {
137 if ( '.' === $entry || '..' === $entry ) {
138 continue;
139 }
140 $path = $dir . '/' . $entry;
141 if ( is_dir( $path ) ) {
142 metasync_uninstall_rrmdir( $path );
143 } else {
144 @wp_delete_file( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- best-effort cleanup on uninstall.
145 }
146 }
147 @rmdir( $dir ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- best-effort cleanup on uninstall; WP_Filesystem would prompt for FTP credentials on non-direct hosts.
148 }
149
150 if ( is_multisite() ) {
151 $metasync_uninstall_site_ids = get_sites( array( 'fields' => 'ids', 'number' => 0 ) );
152 foreach ( $metasync_uninstall_site_ids as $metasync_uninstall_site_id ) {
153 switch_to_blog( $metasync_uninstall_site_id );
154 metasync_uninstall_cleanup();
155 }
156 restore_current_blog();
157 unset( $metasync_uninstall_site_ids, $metasync_uninstall_site_id );
158 } else {
159 metasync_uninstall_cleanup();
160 }
161