'ids', 'number' => self::NETWORK_ACTIVATE_LIMIT, 'archived' => 0, 'deleted' => 0, 'spam' => 0, ] ); foreach ( $site_ids as $site_id ) { switch_to_blog( (int) $site_id ); Installer::activate(); restore_current_blog(); } } /** * Deactivation hook handler. Network deactivation used to unschedule the * daily snapshot on the main site only — the other sites kept the event * until uninstall (0.98.175). Data is kept either way. */ public static function deactivate( bool $network_wide ): void { require_once __DIR__ . '/class-installer.php'; if ( ! is_multisite() || ! $network_wide ) { Installer::deactivate(); return; } foreach ( get_sites( [ 'fields' => 'ids', 'number' => 0 ] ) as $site_id ) { switch_to_blog( (int) $site_id ); Installer::deactivate(); restore_current_blog(); } } public static function on_new_site( \WP_Site $site ): void { if ( ! self::is_network_active() ) return; require_once __DIR__ . '/class-installer.php'; switch_to_blog( (int) $site->blog_id ); Installer::activate(); restore_current_blog(); } /** * @param string[] $tables * @return string[] */ public static function drop_tables( array $tables, int $site_id ): array { global $wpdb; $prefix = $wpdb->get_blog_prefix( $site_id ); foreach ( self::TABLES as $bare ) { $tables[] = $prefix . $bare; } return $tables; } /* ------------------------------------------------------------------ URL scoping ------------------------------------------------------------------ */ /** * True if $url points at the current site. * Single-site / subdomain networks: unchanged behaviour (starts with * home_url(), or is root-relative). */ public static function is_internal_url( string $url ): bool { $url = self::normalize_url( $url ); if ( strpos( $url, '//' ) === 0 ) return false; // protocol-relative to another host (ours was rewritten above) $internal = strpos( $url, home_url() ) === 0 || strpos( $url, '/' ) === 0; if ( ! $internal ) return false; if ( ! is_multisite() || is_subdomain_install() ) return true; $path = strpos( $url, '/' ) === 0 ? $url : (string) wp_parse_url( $url, PHP_URL_PATH ); $path = trailingslashit( '/' . ltrim( (string) strtok( $path, '?#' ), '/' ) ); $own = self::current_site_path(); // Root-relative link on a sub-site that doesn't include our own path // points at the main site (or another sub-site), not at us. if ( $own !== '/' && strpos( $path, $own ) !== 0 ) return false; // Deeper path owned by another site on this domain (e.g. /site-b/). foreach ( self::other_site_paths() as $other ) { if ( strlen( $other ) > strlen( $own ) && strpos( $path, $other ) === 0 ) { return false; } } return true; } /** * A link to this site written another way — http instead of https, with or * without "www.", protocol-relative "//host/…", host in capitals — rewritten * to home_url()'s own scheme and host, so it counts as the internal link it is * (0.98.167; such links used to be skipped, making their targets look unlinked). * Links to other hosts come back unchanged. */ public static function normalize_url( string $url ): string { $url = trim( $url ); if ( ! preg_match( '#^(?:https?:)?//([^/?\#:]+)(:\d+)?(.*)$#i', $url, $m ) ) return $url; static $home = null; if ( $home === null ) { $h = wp_parse_url( home_url() ); $home = [ 'host' => strtolower( (string) ( $h['host'] ?? '' ) ), 'base' => ( $h['scheme'] ?? 'https' ) . '://' . ( $h['host'] ?? '' ) . ( isset( $h['port'] ) ? ':' . $h['port'] : '' ), ]; } $bare = fn( string $host ) => preg_replace( '/^www\./', '', strtolower( $host ) ); if ( $home['host'] === '' || $bare( $m[1] ) !== $bare( $home['host'] ) ) return $url; return $home['base'] . $m[3]; } /** * Root-relative "/foo" → "https://host/foo". Root-relative URLs are * relative to the domain, not to home_url() — prefixing home_url() * doubled the path on sub-sites and subdirectory installs. */ public static function absolutize( string $url ): string { if ( strpos( $url, '//' ) === 0 ) return set_url_scheme( $url ); if ( strpos( $url, '/' ) !== 0 ) return $url; $parts = wp_parse_url( home_url() ); $base = ( $parts['scheme'] ?? 'https' ) . '://' . ( $parts['host'] ?? '' ) . ( isset( $parts['port'] ) ? ':' . $parts['port'] : '' ); return $base . $url; } private static function current_site_path(): string { $details = get_site( get_current_blog_id() ); return $details ? trailingslashit( $details->path ) : '/'; } /** @return string[] paths of the other sites on the current domain */ private static function other_site_paths(): array { static $cache = []; $blog_id = get_current_blog_id(); if ( isset( $cache[ $blog_id ] ) ) return $cache[ $blog_id ]; $current = get_site( $blog_id ); $paths = []; if ( $current ) { $sites = get_sites( [ 'domain' => $current->domain, 'site__not_in' => [ $blog_id ], 'number' => 0, 'fields' => '', 'update_site_meta_cache' => false, ] ); foreach ( $sites as $site ) { $paths[] = trailingslashit( $site->path ); } } return $cache[ $blog_id ] = $paths; } }