| @@ -69,8 +69,26 @@ | ||
| 69 | 69 | // Settings class default and the documented UI behavior — only nuke data when |
| 70 | 70 | // the user has explicitly enabled "Delete all data on uninstall". |
| 71 | 71 | $keep_data = (bool) get_option('thinkrank_keep_data_on_uninstall', true); |
| 72 | 72 | |
| 73 | + // Files first, and regardless of $keep_data. | |
| 74 | + // | |
| 75 | + // Ordering: every ownership test in there reads state the database | |
| 76 | + // cleanup below is about to destroy — sitemap filenames come from the | |
| 77 | + // settings table, the IndexNow key file's name from an option. Run it | |
| 78 | + // after, and there is nothing left to identify our files by. | |
| 79 | + // | |
| 80 | + // Not gated on $keep_data because these are not the user's data. They | |
| 81 | + // are artifacts of a plugin that is being removed, and leaving them is | |
| 82 | + // actively harmful: a real file at /sitemap_index.xml is served by the | |
| 83 | + // web server before WordPress boots, so it goes on shadowing RankMath, | |
| 84 | + // Squirrly or core's own sitemap indefinitely — while rendering blank, | |
| 85 | + // since the XSL stylesheet it points at leaves with the plugin (#510). | |
| 86 | + // "Keep my data" means keep the settings for a reinstall, not keep a | |
| 87 | + // dead file breaking the next plugin. On reinstall they are republished | |
| 88 | + // from the settings that were kept. | |
| 89 | + self::remove_webroot_files(); | |
| 90 | + | |
| 73 | 91 | if (!$keep_data) { |
| 74 | 92 | self::remove_database_tables(); |
| 75 | 93 | self::remove_options(); |
| 76 | 94 | self::remove_user_meta(); |
| @@ -84,8 +102,38 @@ | ||
| 84 | 102 | self::mark_uninstalled(); |
| 85 | 103 | } |
| 86 | 104 | |
| 87 | 105 | /** |
| 106 | + * Delete every file ThinkRank published into the WordPress web root. | |
| 107 | + * | |
| 108 | + * The sitemap, robots.txt, llms.txt and the IndexNow key file are real files | |
| 109 | + * in `ABSPATH`, not routes, so removing the plugin has to remove them too — | |
| 110 | + * otherwise the web server keeps serving a sitemap belonging to a plugin | |
| 111 | + * that is no longer installed, and the next SEO plugin's own sitemap can | |
| 112 | + * never answer (#510). | |
| 113 | + * | |
| 114 | + * @since 2.1.0 | |
| 115 | + * | |
| 116 | + * @return void | |
| 117 | + */ | |
| 118 | + private static function remove_webroot_files(): void { | |
| 119 | + // No autoloader here — WP_UNINSTALL_PLUGIN loads this file without the | |
| 120 | + // plugin — so the removal logic comes from the same plain function file | |
| 121 | + // the deactivator and the sitemap generator use. One copy, not three. | |
| 122 | + $shared = __DIR__ . '/includes/cleanup-webroot.php'; | |
| 123 | + | |
| 124 | + if (!is_readable($shared)) { | |
| 125 | + return; | |
| 126 | + } | |
| 127 | + | |
| 128 | + require_once $shared; | |
| 129 | + | |
| 130 | + if (function_exists('thinkrank_webroot_cleanup')) { | |
| 131 | + thinkrank_webroot_cleanup(); | |
| 132 | + } | |
| 133 | + } | |
| 134 | + | |
| 135 | + /** | |
| 88 | 136 | * The shared cleanup manifest. |
| 89 | 137 | * |
| 90 | 138 | * There is no autoloader here — WP_UNINSTALL_PLUGIN loads this file without |
| 91 | 139 | * the plugin — so the cron-hook and capability lists come from a plain |
| @@ -141,9 +189,11 @@ | ||
| 141 | 189 | $wpdb->prefix . 'thinkrank_seo_local', |
| 142 | 190 | $wpdb->prefix . 'thinkrank_email_report_logs', |
| 143 | 191 | // AI Visibility Tables. These were registered in Database_Schema but |
| 144 | 192 | // never listed here, so an uninstall left them behind — bv_tasks in |
| 145 | - // particular holds the full text of every AI answer (#302). | |
| 193 | + // particular holds the full text of every AI answer (#302). The | |
| 194 | + // three Brand Visibility tables are no longer created, but sites | |
| 195 | + // that used the feature before it was removed still have them. | |
| 146 | 196 | $wpdb->prefix . 'thinkrank_ai_traffic', |
| 147 | 197 | $wpdb->prefix . 'thinkrank_brand_visibility_checks', |
| 148 | 198 | $wpdb->prefix . 'thinkrank_bv_runs', |
| 149 | 199 | $wpdb->prefix . 'thinkrank_bv_tasks', |