Repository navigation
Expand file tree
/
Copy pathuninstall.php
More file actions
105 lines (99 loc) · 5.86 KB
/
Copy pathuninstall.php
File metadata and controls
105 lines (99 loc) · 5.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<?php
/**
* Uninstall — only deletes settings if the user opted in.
*
* Scope: this plugin's OWN `devdalyt_*` rows plus a coordinated shared-core cleanup.
* The suite-shared connection state (`devdcorev1_site_id` / `_site_token` / `_account_id` /
* `_account_email` / `_connected_at`) is deliberately NOT deleted here — other DevDome
* plugins on the same site read those rows, and the shared-core helper below removes the
* shared artifacts only when this is the LAST DevDome plugin installed.
*/
defined( 'WP_UNINSTALL_PLUGIN' ) || exit;
/** Per-site cleanup: every site of a network gets it (DeepSeek round 4), the opt-in is read per site. */
function devdalyt_uninstall_site() {
// Housekeeping that runs on EVERY uninstall, before the opt-in guard: unscheduling our cron
// event and removing the generated static-script cache file are not "user data" — leaving
// them behind would orphan a daily cron with no handler and a stray file in uploads/ forever
// (audit 2026-08-05; wp.org reviewers check for orphaned cron events).
wp_clear_scheduled_hook( 'devdalyt_fp_refresh' );
$devdalyt_fp = get_option( 'devdalyt_fp_paths', null );
if ( is_array( $devdalyt_fp ) && ! empty( $devdalyt_fp['dir'] ) && preg_match( '/^[a-f0-9]{10}$/', (string) $devdalyt_fp['dir'] ) ) {
$devdalyt_up = wp_get_upload_dir();
$devdalyt_dir = trailingslashit( $devdalyt_up['basedir'] ) . $devdalyt_fp['dir'];
$devdalyt_file = ! empty( $devdalyt_fp['file'] ) && preg_match( '/^[a-f0-9]{8}\.js$/', (string) $devdalyt_fp['file'] ) ? trailingslashit( $devdalyt_dir ) . $devdalyt_fp['file'] : '';
// never through a link (review 2026-09-11): a linked folder or file would delete outside uploads;
// and the RESOLVED folder must still sit inside the resolved uploads folder (linked ancestors).
$devdalyt_real = realpath( $devdalyt_dir );
$devdalyt_base = realpath( $devdalyt_up['basedir'] );
$devdalyt_inside = $devdalyt_real && $devdalyt_base && 0 === strpos( trailingslashit( $devdalyt_real ), trailingslashit( $devdalyt_base ) );
if ( $devdalyt_inside && '' !== $devdalyt_file && ! is_link( $devdalyt_dir ) && ! is_link( $devdalyt_file ) && is_file( $devdalyt_file ) ) {
wp_delete_file( $devdalyt_file );
}
$devdalyt_botd = trailingslashit( $devdalyt_dir ) . 'botd.js'; // the bot-detector copy next to the tracker (1.1.0)
if ( $devdalyt_inside && ! is_link( $devdalyt_dir ) && ! is_link( $devdalyt_botd ) && is_file( $devdalyt_botd ) ) {
wp_delete_file( $devdalyt_botd );
}
if ( $devdalyt_inside && is_dir( $devdalyt_dir ) && ! is_link( $devdalyt_dir ) ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- removing the plugin's own empty random dir at uninstall
@rmdir( $devdalyt_dir );
}
}
if ( ! get_option( 'devdalyt_delete_data_on_uninstall', false ) ) {
return;
}
delete_option( 'devdalyt_tracking_enabled' );
delete_option( 'devdalyt_click_tracking_enabled' );
delete_option( 'devdalyt_outbound_tracking_enabled' );
delete_option( 'devdalyt_ai_tracking_enabled' );
delete_option( 'devdalyt_bot_tracking_enabled' );
delete_option( 'devdalyt_dnt_admins' );
delete_option( 'devdalyt_excluded_roles' );
delete_option( 'devdalyt_debug_mode' );
delete_option( 'devdalyt_respect_dnt' );
delete_option( 'devdalyt_returning_visitors' );
delete_option( 'devdalyt_api_endpoint' );
delete_option( 'devdalyt_status_endpoint' );
delete_option( 'devdalyt_stats_endpoint' );
delete_option( 'devdalyt_script_url' );
delete_option( 'devdalyt_dashboard_url' );
delete_option( 'devdalyt_plugin_version' );
delete_option( 'devdalyt_last_connection_test' );
delete_option( 'devdalyt_last_event_sent_at' );
delete_option( 'devdalyt_migrated_ids' );
// First-party delivery (1.0.3+): the switch and the random resource names. The cron event
// and the uploads/ file were already removed above, before the opt-in guard.
delete_option( 'devdalyt_first_party' );
delete_option( 'devdalyt_fp_auth_failed' );
delete_option( 'devdalyt_user_disconnected' );
delete_option( 'devdalyt_remote_disconnected' );
delete_option( 'devdalyt_fp_paths' );
delete_option( 'devdalyt_delete_data_on_uninstall' );
delete_option( 'devdalyt_init_pending' ); // fresh-defaults marker (round 3)
delete_option( 'devdalyt_relay_secret' ); // the fleet relay credential this plugin copied (review 2026-09-11)
}
// This plugin's transients use dynamic keys (per-request-token connect handles, per-IP
// beacon rate buckets, the update-metadata cache), so they can't be listed one by one —
// sweep them by the plugin's own prefix. `_` is a LIKE wildcard and must be escaped.
// Per blog (Codex round 5): $wpdb->options follows switch_to_blog(), so the sweep and the shared-core
// cleanup run INSIDE each site's context on a network, not once on the main site after the loop.
function devdalyt_uninstall_sweep() {
global $wpdb;
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $wpdb->esc_like( '_transient_devdalyt_' ) . '%', $wpdb->esc_like( '_transient_timeout_devdalyt_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- uninstall-time cleanup of dynamic transient keys
}
// Coordinate shared-core cleanup: the daily feed cron, the cached feed option and the shared
// hub/account rows are removed only if this is the LAST DevDome plugin still installed, so
// uninstalling this one never orphans the core for its siblings.
require_once __DIR__ . '/lib/devdome-core/uninstall.php';
if ( is_multisite() ) {
foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0 ) ) as $devdalyt_blog_id ) {
switch_to_blog( (int) $devdalyt_blog_id );
devdalyt_uninstall_site();
devdalyt_uninstall_sweep();
devdcorev1_uninstall_cleanup( 'devdome-analytics/devdome-analytics.php' );
restore_current_blog();
}
} else {
devdalyt_uninstall_site();
devdalyt_uninstall_sweep();
devdcorev1_uninstall_cleanup( 'devdome-analytics/devdome-analytics.php' );
}