get_var( $wpdb->prepare( "SELECT option_id FROM {$wpdb->options} WHERE option_name LIKE %s AND option_name NOT LIKE %s LIMIT 1", $wpdb->esc_like( 'pagecore_' ) . '%', $wpdb->esc_like( 'pagecore_pro_' ) . '%' ) ); } /** * Remove everything PageCore stored on the current site. * * @return void */ function pagecore_uninstall_site() { global $wpdb; /* * Give every module the chance to remove what it created. A module that * throws must not stop the others from cleaning up, so each call is * guarded. */ foreach ( pagecore()->modules()->all() as $module ) { try { $module->uninstall(); } catch ( Exception $e ) { continue; } catch ( Error $e ) { continue; } delete_option( 'pagecore_' . str_replace( '-', '_', $module->get_id() ) . '_settings' ); } delete_option( 'pagecore_settings' ); delete_option( 'pagecore_enabled_modules' ); /* * A module that has been deleted from disk, or one that threw on the way * through the loop above, leaves its options behind with nothing left to * know about them. This sweeps up anything still carrying the plugin's own * prefix. * * Matched by prefix and escaped, so only options this plugin named can be * reached, and nothing belonging to WordPress or to another plugin is in * range of it. PageCore Pro's options start the same way and are left * alone: they belong to Pro, and go when Pro itself is deleted. */ // phpcs:disable WordPress.DB.DirectDatabaseQuery $leftovers = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s AND option_name NOT LIKE %s", $wpdb->esc_like( 'pagecore_' ) . '%', $wpdb->esc_like( 'pagecore_pro_' ) . '%' ) ); foreach ( (array) $leftovers as $option ) { delete_option( $option ); } // Transients survive in the options table under two names each, so they // are swept the same way rather than guessed at. foreach ( array( '_transient_pagecore_', '_transient_timeout_pagecore_' ) as $prefix ) { $transients = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s AND option_name NOT LIKE %s", $wpdb->esc_like( $prefix ) . '%', $wpdb->esc_like( $prefix . 'pro_' ) . '%' ) ); foreach ( (array) $transients as $option ) { delete_option( $option ); } } /* * Provenance stamps on anything the modules could not reach. These * identify objects as PageCore's; with the plugin gone they identify * nothing. */ // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Runs once, on uninstall. foreach ( array( '_pagecore_run_id', '_pagecore_demo', '_pagecore_source_id', '_pagecore_source_home', '_pagecore_modified_run_id', '_pagecore_modified_demo', '_pagecore_previous_content', '_pagecore_pending_thumbnail', '_pagecore_pending_parent', '_pagecore_source_content' ) as $key ) { $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => $key ) ); $wpdb->delete( $wpdb->termmeta, array( 'meta_key' => $key ) ); } // phpcs:enable } $pagecore_cleaned = false; $pagecore_kept = false; if ( is_multisite() ) { foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0, ) ) as $pagecore_site_id ) { switch_to_blog( $pagecore_site_id ); // Asked before the site is cleaned, since cleaning removes the very // options both questions read. if ( pagecore_uninstall_requested() ) { pagecore_uninstall_site(); $pagecore_cleaned = true; } elseif ( pagecore_uninstall_site_used() ) { $pagecore_kept = true; } restore_current_blog(); } } elseif ( pagecore_uninstall_requested() ) { pagecore_uninstall_site(); $pagecore_cleaned = true; } if ( $pagecore_cleaned ) { /* * Library favorites. User meta is shared by every site on a network, so * it is removed once, here, and only when no site that used the plugin * chose to keep its data: one site removing its own data must not take * another's favorites with it. */ if ( ! $pagecore_kept ) { delete_metadata( 'user', 0, 'pagecore_design_library_favorites', '', true ); } wp_cache_flush(); }