diff --git a/tests/behat/features/links.feature b/tests/behat/features/links.feature
index b622f2f10..8232ec01b 100644
--- a/tests/behat/features/links.feature
+++ b/tests/behat/features/links.feature
@@ -38,6 +38,10 @@ Feature: Content links processing
| field_c_p_content:value | External dark link from overridden domain |
| field_c_p_content:format | civictheme_rich_text |
| field_c_p_theme | dark |
+ And "field_c_n_components" in "civictheme_page" "node" with "title" of "[TEST] Page 1" has "civictheme_content" paragraph:
+ | field_c_p_content:value | Invalid link |
+ | field_c_p_content:format | civictheme_rich_text |
+ | field_c_p_theme | dark |
And "field_c_n_components" in "civictheme_page" "node" with "title" of "[TEST] Page 1" has "civictheme_content" paragraph:
| field_c_p_content:value | Telephone link |
| field_c_p_content:format | civictheme_rich_text |
@@ -108,4 +112,9 @@ Feature: Content links processing
And I should see an ".ct-basic-content a[href='http://exampleoverridden.com/external-dark-link'][target='_blank'].ct-content-link" element
And I should not see an ".ct-basic-content a[href='http://exampleoverridden.com/external-dark-link'].ct-content-link.ct-content-link--external" element
+ And I should see an ".ct-basic-content a[href='///C:/Users/civictheme/invalid'].ct-content-link" element
+ And I should see an ".ct-basic-content a[href='///C:/Users/civictheme/invalid'].ct-theme-dark" element
+ And I should see an ".ct-basic-content a[href='///C:/Users/civictheme/invalid'][target='_blank'].ct-content-link" element
+ And I should not see an ".ct-basic-content a[href='///C:/Users/civictheme/invalid'].ct-content-link.ct-content-link--external" element
+
And I should see an ".ct-basic-content a[href='mailto:person@test.com'].ct-content-link.ct-theme-dark" element
diff --git a/web/themes/contrib/civictheme/includes/utilities.inc b/web/themes/contrib/civictheme/includes/utilities.inc
index 79ab667f8..81e7aa3f2 100644
--- a/web/themes/contrib/civictheme/includes/utilities.inc
+++ b/web/themes/contrib/civictheme/includes/utilities.inc
@@ -653,33 +653,56 @@ function civictheme_get_icon_from_file(FileInterface $file): string {
* @SuppressWarnings(PHPMD.StaticAccess)
*/
function civictheme_url_is_external(string $url, string $base_url, array $override_domains = []): bool {
- if (empty($url) || !UrlHelper::isValid($url)) {
+ if (!civictheme_url_is_valid($url)) {
return FALSE;
}
- $url_parts = parse_url($url);
- if (!empty($url_parts['scheme']) && empty($url_parts['host'])) {
+ if (!UrlHelper::isExternal($url)) {
return FALSE;
}
- if (UrlHelper::isExternal($url)) {
- $override_domains[] = $base_url;
- foreach ($override_domains as $override_domain) {
- $override_domain = civictheme_external_link_normalize_domain($override_domain);
+ $override_domains[] = $base_url;
+ foreach ($override_domains as $override_domain) {
+ $override_domain = civictheme_external_link_normalize_domain($override_domain);
- if (!UrlHelper::isValid($override_domain, TRUE)) {
- continue;
- }
+ if (!civictheme_url_is_valid($override_domain, TRUE)) {
+ continue;
+ }
+ try {
if (UrlHelper::externalIsLocal($url, $override_domain)) {
return FALSE;
}
}
+ catch (\InvalidArgumentException $exception) {
+ return FALSE;
+ }
+ }
- return TRUE;
+ return TRUE;
+}
+
+/**
+ * Check if a URL is valid.
+ *
+ * @param string $url
+ * The URL to be checked.
+ * @param bool $absolute
+ * Whether the URL is absolute (beginning with a scheme such as "http:").
+ *
+ * @return bool
+ * TRUE if the provided URL is valid.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
+ */
+function civictheme_url_is_valid(string $url, bool $absolute = FALSE) : bool {
+ if (empty($url) || !UrlHelper::isValid($url, $absolute)) {
+ return FALSE;
}
- return FALSE;
+ $url_parts = parse_url($url);
+ return !(!empty($url_parts['scheme']) && empty($url_parts['host']));
}
/**