diff --git a/assets/js/domain-logs.min.js b/assets/js/domain-logs.min.js index 162af579c..a2955ff15 100644 --- a/assets/js/domain-logs.min.js +++ b/assets/js/domain-logs.min.js @@ -1 +1 @@ -(o=>{o(document).ready(function(){function t(e){o.ajax({url:ajaxurl,method:"GET",data:{action:"wu_handle_view_logs",file:wu_domain_logs.log_file,return_ascii:"no"},success(n){o("#content").html(n.data.contents),void 0!==e&&e()}})}t(),setInterval(t,6e4),o(document).on("click","#refresh-logs",function(n){let e=wu_block_ui("#content");n.preventDefault(),t(function(){e.unblock()})})})})(jQuery); \ No newline at end of file +(o=>{o(document).ready(function(){function t(e){o.ajax({url:ajaxurl,method:"GET",data:{action:"wu_handle_view_logs",file:wu_domain_logs.log_file,return_ascii:"no"},success(n){o("#content").text(n.data.contents),void 0!==e&&e()}})}t(),setInterval(t,6e4),o(document).on("click","#refresh-logs",function(n){let e=wu_block_ui("#content");n.preventDefault(),t(function(){e.unblock()})})})})(jQuery); \ No newline at end of file diff --git a/inc/class-domain-mapping.php b/inc/class-domain-mapping.php index f00dac6c5..41086bc0c 100644 --- a/inc/class-domain-mapping.php +++ b/inc/class-domain-mapping.php @@ -242,6 +242,15 @@ public function get_www_and_nowww_versions($domain) { return [$nowww, $www]; } + /** + * Check if this is a special loopback request. + * + * @param null|false|\WP_Site $current_site Current Site. + * @param string $domain Current domain. + * @param string $path Current Path. + * + * @return void + */ public function verify_dns_mapping($current_site, $domain, $path) { // Nonce functions are unavailable and the wp_hash is basically the same. @@ -274,6 +283,7 @@ public function verify_dns_mapping($current_site, $domain, $path) { */ public function check_domain_mapping($site, $domain) { + $this->verify_dns_mapping($site, $domain, '/'); // Have we already matched? (Allows other plugins to match first) if ( ! empty($site)) { return $site; diff --git a/inc/integrations/host-providers/class-cloudflare-host-provider.php b/inc/integrations/host-providers/class-cloudflare-host-provider.php index 2bdbf91b8..5b3b52b90 100644 --- a/inc/integrations/host-providers/class-cloudflare-host-provider.php +++ b/inc/integrations/host-providers/class-cloudflare-host-provider.php @@ -252,7 +252,14 @@ public function on_add_subdomain($subdomain, $site_id): void { return; } - $should_add_www = apply_filters('wu_cloudflare_should_add_www', true, $subdomain, $site_id); + // Build FQDN so Domain_Manager can classify main vs. subdomain correctly. + $full_domain = $subdomain . '.' . $current_site->domain; + $should_add_www = apply_filters( + 'wu_cloudflare_should_add_www', + \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($full_domain), + $subdomain, + $site_id + ); $domains_to_send = [$subdomain]; diff --git a/inc/integrations/host-providers/class-cloudways-host-provider.php b/inc/integrations/host-providers/class-cloudways-host-provider.php index e21a04e63..a6e0fc5cd 100644 --- a/inc/integrations/host-providers/class-cloudways-host-provider.php +++ b/inc/integrations/host-providers/class-cloudways-host-provider.php @@ -291,7 +291,7 @@ private function get_domains(): array { $domain_list = $this->get_domain_list(); foreach ($domain_list as $naked_domain) { - if (! str_starts_with((string) $naked_domain, 'www.') && ! str_starts_with((string) $naked_domain, '*.')) { + if (! str_starts_with((string) $naked_domain, 'www.') && ! str_starts_with((string) $naked_domain, '*.') && \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($naked_domain)) { $domain_list[] = 'www.' . $naked_domain; } } @@ -378,7 +378,7 @@ public function get_all_mapped_domains() { foreach ($mappings as $domain) { $final_domain_list[] = $domain; - if (! str_starts_with((string) $domain, 'www.')) { + if (! str_starts_with((string) $domain, 'www.') && \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain)) { $final_domain_list[] = "www.$domain"; } } diff --git a/inc/integrations/host-providers/class-runcloud-host-provider.php b/inc/integrations/host-providers/class-runcloud-host-provider.php index 525ac77c3..2b88249cd 100644 --- a/inc/integrations/host-providers/class-runcloud-host-provider.php +++ b/inc/integrations/host-providers/class-runcloud-host-provider.php @@ -124,12 +124,14 @@ public function on_add_domain($domain, $site_id): void { $success = false; + $create_www = \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain); + $response = $this->send_runcloud_request( $this->get_runcloud_base_url('domains'), [ 'name' => $domain, - 'www' => true, - 'redirection' => 'non-www', + 'www' => $create_www, + 'redirection' => $create_www ? 'non-www' : 'none', ], 'POST' ); diff --git a/inc/integrations/host-providers/class-serverpilot-host-provider.php b/inc/integrations/host-providers/class-serverpilot-host-provider.php index d2bf992f8..c36adf405 100644 --- a/inc/integrations/host-providers/class-serverpilot-host-provider.php +++ b/inc/integrations/host-providers/class-serverpilot-host-provider.php @@ -11,7 +11,7 @@ use Psr\Log\LogLevel; -defined( 'ABSPATH' ) || exit; +defined('ABSPATH') || exit; /** * This base class should be extended to implement new host integrations for SSL and domains. @@ -119,10 +119,16 @@ public function on_add_domain($domain, $site_id): void { $current_domain_list = $this->get_server_pilot_domains(); if ($current_domain_list && is_array($current_domain_list)) { + $domains_to_add = [$domain]; + + if (\WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain)) { + $domains_to_add[] = 'www.' . $domain; + } + $this->send_server_pilot_api_request( '', [ - 'domains' => array_merge($current_domain_list, [$domain, 'www.' . $domain]), + 'domains' => array_merge($current_domain_list, $domains_to_add), ] ); diff --git a/inc/integrations/host-providers/class-wpmudev-host-provider.php b/inc/integrations/host-providers/class-wpmudev-host-provider.php index 6e5e41d62..22636a175 100644 --- a/inc/integrations/host-providers/class-wpmudev-host-provider.php +++ b/inc/integrations/host-providers/class-wpmudev-host-provider.php @@ -135,7 +135,7 @@ public function on_add_domain($domain, $site_id): void { $domains = [$domain]; - if (! str_starts_with($domain, 'www.')) { + if (! str_starts_with($domain, 'www.') && \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain)) { $domains[] = "www.$domain"; } diff --git a/inc/managers/class-domain-manager.php b/inc/managers/class-domain-manager.php index bedf40d6f..5fa99498d 100644 --- a/inc/managers/class-domain-manager.php +++ b/inc/managers/class-domain-manager.php @@ -395,6 +395,78 @@ public function add_domain_mapping_settings(): void { ], ] ); + + wu_register_settings_field( + 'domain-mapping', + 'auto_create_www_subdomain', + [ + 'title' => __('Create www Subdomain Automatically?', 'multisite-ultimate'), + 'desc' => __('Control when www subdomains should be automatically created for mapped domains.', 'multisite-ultimate'), + 'tooltip' => __('This setting applies to all hosting integrations and determines when a www version of the domain should be automatically created.', 'multisite-ultimate'), + 'type' => 'select', + 'default' => 'always', + 'options' => [ + 'always' => __('Always - Create www subdomain for all domains', 'multisite-ultimate'), + 'main_only' => __('Only for main domains (e.g., example.com but not subdomain.example.com)', 'multisite-ultimate'), + 'never' => __('Never - Do not automatically create www subdomains', 'multisite-ultimate'), + ], + 'require' => [ + 'enable_domain_mapping' => true, + ], + ] + ); + } + + /** + * Check if a www subdomain should be created for the given domain. + * + * @since 2.0.0 + * @param string $domain The domain to check. + * @return bool True if www subdomain should be created, false otherwise. + */ + public function should_create_www_subdomain($domain) { + + // Normalize incoming domain + $domain = trim(strtolower($domain)); + + // Guard against double-prefixing - return false if already starts with www. + if (strpos($domain, 'www.') === 0) { + return false; + } + + $setting = wu_get_setting('auto_create_www_subdomain', 'always'); + + switch ($setting) { + case 'never': + return false; + + case 'main_only': + // Check if this is a main domain (no subdomain parts) + // A main domain has only 2 parts when split by dots (e.g., example.com) + // or 3 parts if it's a known TLD structure (e.g., example.co.uk) + $parts = explode('.', $domain); + + // Simple heuristic: if domain has only 2 parts, it's definitely a main domain + if (count($parts) <= 2) { + return true; // e.g., example.com + } + + // For 3+ parts, check if it's a main domain with multi-part TLD + $known_multi_part_tlds = apply_filters('wu_multi_part_tlds', ['.co.uk', '.com.au', '.co.nz', '.com.br', '.co.in']); + $last_two_parts = '.' . $parts[ count($parts) - 2 ] . '.' . $parts[ count($parts) - 1 ]; + + // If it has exactly 3 parts and matches a known multi-part TLD, it's a main domain + if (count($parts) === 3 && in_array($last_two_parts, $known_multi_part_tlds, true)) { + return true; // e.g., example.co.uk + } + + // Otherwise, it's a subdomain + return false; + + case 'always': + default: + return true; + } } /** @@ -955,7 +1027,7 @@ public function verify_domain_with_loopback_request(Domain $domain): bool { [ 'timeout' => 10, 'redirection' => 0, - 'sslverify' => $protocol_config['sslverify'], + 'sslverify' => $protocol_config['sslverify'] ?? false, 'body' => ['async_check_dns_nonce' => wp_hash($domain_url)], ] ); diff --git a/readme.txt b/readme.txt index 1887a2912..200b1b68f 100644 --- a/readme.txt +++ b/readme.txt @@ -233,6 +233,10 @@ Version [2.4.4] - Released on 2025-08-XX - Fixed: Saving email templates without stripping html - New: Option to allow site owners to edit users on their site - Fixed: Invoices not loading when logo is not set +- Fixed: Verify DNS settings when using a reverse proxy +- Improved: Lazy load limitations for better performance and compatibility +- New: Add Admin Notice if sunrise.php is not setup +- New: Option to not always create www. subdomains with hosting integrations Version [2.4.3] - Released on 2025-08-15 - Fixed: Bug in Slim SEO plugin