query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $wpdb->esc_like( '_transient_sharelyai_' ) . '%', $wpdb->esc_like( '_transient_timeout_sharelyai_' ) . '%' ) ); // The sync map. Without this every post keeps a knowledge id for a // workspace nothing on this site can reach any more — the plugin's // fingerprints on content that outlives it. foreach ( array( '_sharelyai_knowledge_id', '_sharelyai_hash', '_sharelyai_synced', '_sharelyai_error', '_sharelyai_orphans', ) as $sharelyai_meta ) { delete_post_meta_by_key( $sharelyai_meta ); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- own table; a table name cannot be a placeholder. $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'sharelyai_query_stat' ); // Deactivation cancels the queue, and WordPress deactivates before it // uninstalls — but a site restored from an export may never have run that, // and an orphaned event whose handler is gone is a warning on every cron run. foreach ( array( 'sharelyai_daily', 'sharelyai_sync_roles', 'sharelyai_sync_adopt', 'sharelyai_sync_batch', 'sharelyai_sync_post' ) as $sharelyai_hook ) { wp_unschedule_hook( $sharelyai_hook ); } } // Multisite: each site has its own options, its own table and its own workspace. if ( is_multisite() ) { $sharelyai_sites = get_sites( array( 'fields' => 'ids', 'number' => 0, ) ); foreach ( $sharelyai_sites as $sharelyai_site_id ) { switch_to_blog( (int) $sharelyai_site_id ); sharelyai_uninstall_site(); restore_current_blog(); } } else { sharelyai_uninstall_site(); }