run( false ); } public static function install_site_defaults() { $repository = new SettingsRepository( new SettingsSchema() ); if ( false === get_option( SettingsRepository::OPTION, false ) ) { $repository->replace( $repository->defaults() ); } if ( false === get_option( 'artbees_qv_onboarding', false ) ) { update_option( 'artbees_qv_onboarding', 'new', true ); } } /** Continues network activation in bounded, resumable batches. */ public static function process_network_setup() { $offset = (int) get_site_option( self::NETWORK_CURSOR_OPTION, 0 ); $site_ids = get_sites( array( 'fields' => 'ids', 'number' => self::SITE_BATCH_SIZE, 'offset' => $offset, ) ); foreach ( $site_ids as $site_id ) { switch_to_blog( (int) $site_id ); ( new MigrationRunner() )->run( true ); restore_current_blog(); } if ( count( $site_ids ) === self::SITE_BATCH_SIZE ) { update_site_option( self::NETWORK_CURSOR_OPTION, $offset + count( $site_ids ) ); if ( ! wp_next_scheduled( self::NETWORK_SETUP_HOOK ) ) { wp_schedule_single_event( time() + 1, self::NETWORK_SETUP_HOOK ); } return; } delete_site_option( self::NETWORK_CURSOR_OPTION ); } public static function deactivate() { wp_clear_scheduled_hook( 'artbees_qv/maintenance' ); wp_clear_scheduled_hook( self::NETWORK_SETUP_HOOK ); delete_option( MigrationRunner::LOCK ); delete_site_option( self::NETWORK_CURSOR_OPTION ); } public static function new_site( $site ) { if ( ! is_object( $site ) || empty( $site->blog_id ) ) { return; } switch_to_blog( (int) $site->blog_id ); ( new MigrationRunner() )->run( false ); restore_current_blog(); } public static function uninstall( $network_wide = false ) { if ( is_multisite() && $network_wide && is_super_admin() ) { self::for_sites( static function () { if ( self::remove_data_enabled() ) { self::delete_site_data(); } } ); } elseif ( self::remove_data_enabled() ) { self::delete_site_data(); } do_action( 'artbees_qv/lifecycle/uninstall', (bool) $network_wide ); } /** Deletes in 100-site queries while preserving each site's own remove-data choice. */ public static function for_sites( callable $callback ) { $offset = 0; do { $site_ids = get_sites( array( 'fields' => 'ids', 'number' => self::SITE_BATCH_SIZE, 'offset' => $offset ) ); foreach ( $site_ids as $site_id ) { switch_to_blog( (int) $site_id ); $callback(); restore_current_blog(); } $offset += count( $site_ids ); } while ( count( $site_ids ) === self::SITE_BATCH_SIZE ); } private static function remove_data_enabled() { $settings = get_option( SettingsRepository::OPTION, array() ); return is_array( $settings ) && ! empty( $settings['lifecycle']['remove_data'] ); } private static function delete_site_data() { global $wpdb; delete_option( SettingsRepository::OPTION ); delete_option( SettingsRepository::VERSION_OPTION ); delete_option( MigrationRunner::VERSION_OPTION ); delete_option( MigrationRunner::LOCK ); delete_option( 'artbees_qv_onboarding' ); wp_clear_scheduled_hook( 'artbees_qv/maintenance' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Plugin-owned metadata is removed only after the merchant explicitly chooses removal. $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_artbees_qv_visibility' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Plugin-owned metadata is removed only after the merchant explicitly chooses removal. $wpdb->delete( $wpdb->termmeta, array( 'meta_key' => '_artbees_qv_visibility' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Plugin-owned metadata is removed only after the merchant explicitly chooses removal. $wpdb->delete( $wpdb->termmeta, array( 'meta_key' => '_artbees_qv_include_descendants' ) ); } }