rmdir( $dir ); } /** * Per-site cleanup: delete options, upload files, and tables. * On multisite this runs for every subsite in the network. * * @param array $option_keys List of option keys to delete. */ function _ab_uninstall_site( array $option_keys ): void { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound -- admbud_ is the plugin prefix. global $wpdb; // Delete all per-site options. foreach ( $option_keys as $key ) { delete_option( $key ); } // Delete per-user notice dismissal flags for this site. $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'admbud_ms_notice_dismissed_%'" ); // Delete Admin Buddy uploads directory (snippets + blueprints). $_admbud_uploads = trailingslashit( wp_upload_dir()['basedir'] ) . 'admin-buddy'; if ( is_dir( $_admbud_uploads ) ) { _ab_recursive_rmdir( $_admbud_uploads ); } // Legacy: remove old ab-snippets directory if it still exists. $_legacy_snippets = trailingslashit( wp_upload_dir()['basedir'] ) . 'ab-snippets'; if ( is_dir( $_legacy_snippets ) ) { _ab_recursive_rmdir( $_legacy_snippets ); } // Drop custom tables for this site. $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}admbud_svg_icons" ); // phpcs:ignore $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}admbud_activity_log" ); // phpcs:ignore } if ( is_multisite() ) { // Loop through every subsite and clean up. $sites = get_sites( [ 'number' => 0, 'fields' => 'ids' ] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound foreach ( $sites as $site_id ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound switch_to_blog( $site_id ); _ab_uninstall_site( $admbud_option_keys ); restore_current_blog(); } // Remove network-level options (locked keys, network defaults). foreach ( $admbud_option_keys as $key ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound delete_site_option( $key ); } } else { _ab_uninstall_site( $admbud_option_keys ); } // ------------------------------------------------------------------------- // Delete Admin Buddy transients // ------------------------------------------------------------------------- delete_transient( 'admbud_notices_plugin_callbacks' ); delete_transient( 'admbud_dashboard_widget_catalogue' ); delete_transient( 'admbud_snippets_safe_mode' ); delete_transient( 'admbud_activation_redirect' ); delete_transient( 'admbud_colours_css_cache' ); // legacy key // Versioned cache keys (admbud_colours_css_ + version) $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_admbud_colours_css_%' OR option_name LIKE '_transient_timeout_admbud_colours_css_%'" ); // phpcs:ignore WordPress.DB // ------------------------------------------------------------------------- // Option Pages - delete all field values (admbud_op_{slug}_{key} pattern). // The admbud_option_pages definition key is handled above in the loop. // ------------------------------------------------------------------------- // phpcs:ignore WordPress.DB.DirectDatabaseQuery $wpdb->query( $wpdb->prepare( // phpcs:ignore WordPress.DB "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", $wpdb->esc_like( 'admbud_op_' ) . '%' ) ); // ------------------------------------------------------------------------- // Remove Demo Data post meta, term meta, user meta (belt-and-suspenders - // the Remove All button in the module handles this during normal use, but // we clean up here in case someone uninstalls without removing demo data). // ------------------------------------------------------------------------- $wpdb->delete( $wpdb->usermeta, [ 'meta_key' => 'admbud_last_seen' ] ); // phpcs:ignore WordPress.DB,- User Last Seen timestamps // ------------------------------------------------------------------------- // Remove Admin Buddy custom capabilities from all roles // ------------------------------------------------------------------------- global $wp_roles; if ( ! isset( $wp_roles ) ) { $wp_roles = new WP_Roles(); } foreach ( array_keys( $wp_roles->roles ) as $role_name ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound $role = get_role( $role_name ); if ( $role ) { $role->remove_cap( 'admbud_manage_roles' ); $role->remove_cap( 'admbud_manage_snippets' ); } } // ------------------------------------------------------------------------- // Clear scheduled cron events // ------------------------------------------------------------------------- wp_clear_scheduled_hook( 'admbud_activity_log_prune' ); // ------------------------------------------------------------------------- // Allow add-ons to hook in // ------------------------------------------------------------------------- do_action( 'admbud_uninstall' );