0, 'network_id' => get_current_network_id()]) as $site) { $id = (int) $site->blog_id; $out[] = [ 'id' => $id, 'name' => SiteInfo::plain_name((string) get_blog_option($id, 'blogname')), 'url' => untrailingslashit((string) get_blog_option($id, 'home')), 'main' => is_main_site($id), 'available' => !((int) $site->archived || (int) $site->deleted || (int) $site->spam), ]; } usort($out, static fn ($a, $b) => [$b['main'], $a['id']] <=> [$a['main'], $b['id']]); return $out; } /** * Where a new subsite at $address would live: a path under the network's domain on a subdirectory network * ("https://net.test/client1/"), a subdomain or any other domain on a subdomain network. Refuses addresses * that aren't valid there or are taken. * * @return array{domain: string, path: string} */ public static function parse_address(string $address): array { $network = get_network(); $address = trim($address); if (!preg_match('#^https?://#i', $address)) { $address = 'https://' . $address; } $parts = wp_parse_url($address); $domain = strtolower((string) ($parts['host'] ?? '')); if (isset($parts['port'])) { $domain .= ':' . (int) $parts['port']; } $path = '/' . trim(strtolower((string) ($parts['path'] ?? '')), '/') . '/'; $path = $path === '//' ? '/' : $path; $bad = static function (string $why): RelayError { return new RelayError('subsite.bad_address', esc_html($why), esc_html(is_subdomain_install() /* translators: %s: network domain */ ? sprintf(__('Use a subdomain such as client.%s, or a domain of its own.', 'siterelay'), get_network()->domain) /* translators: %s: example address */ : sprintf(__('Use one word after the network\'s address, such as %s.', 'siterelay'), 'https://' . get_network()->domain . get_network()->path . 'client/'))); }; if (!preg_match('/^[a-z0-9.-]+(:[0-9]+)?$/', $domain) || strpos($domain, '.') === false && strpos($domain, 'localhost') !== 0) { /* translators: %s: address */ throw $bad(esc_html(sprintf(__('%s is not a valid address.', 'siterelay'), $address))); } if (is_subdomain_install()) { if ($path !== '/') { throw $bad(esc_html__('On this network a subsite has an address of its own, without a folder.', 'siterelay')); } } else { $slug = trim(substr($path, strlen($network->path)), '/'); if ($domain !== $network->domain || strpos($path, $network->path) !== 0 || !preg_match('/^[a-z0-9-]+$/', $slug)) { /* translators: %s: network address */ throw $bad(esc_html(sprintf(__('On this network a subsite lives in one folder under %s.', 'siterelay'), 'https://' . $network->domain . $network->path))); } if (in_array($slug, get_subdirectory_reserved_names(), true)) { /* translators: %s: folder name */ throw $bad(esc_html(sprintf(__('“%s” is reserved by WordPress.', 'siterelay'), $slug))); } } if (domain_exists($domain, $path, (int) $network->id)) { throw new RelayError( 'subsite.address_taken', /* translators: %s: address */ esc_html(sprintf(__('A subsite already uses %s.', 'siterelay'), $domain . $path)), esc_html__('Choose that subsite to replace it, or another address.', 'siterelay') ); } return ['domain' => $domain, 'path' => $path]; } /** Create the subsite (WordPress fills it with its default content, which the job then replaces). */ public static function create(string $address, string $title, int $user_id): int { $where = self::parse_address($address); $id = wp_insert_site([ 'domain' => $where['domain'], 'path' => $where['path'], 'network_id' => get_current_network_id(), 'title' => $title !== '' ? $title : $where['domain'] . $where['path'], 'user_id' => $user_id > 0 ? $user_id : (int) get_current_user_id(), ]); if (is_wp_error($id)) { /* translators: %s: WordPress' error message */ throw new RelayError('subsite.create_failed', esc_html(sprintf(__('WordPress could not create the subsite: %s', 'siterelay'), $id->get_error_message()))); } return (int) $id; } }