Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 21 additions & 25 deletions integreat_cms/cms/forms/linkcheck/edit_url_form.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,24 @@
from __future__ import annotations

import logging
import re

from django import forms
from django.core.validators import EmailValidator, URLValidator
from django.utils.translation import gettext_lazy as _

from ...utils.link_utils import format_phone_number

logger = logging.getLogger(__name__)


class LinkField(forms.URLField):
class LinkField(forms.CharField):
"""
A field for links that might be URLs but could also be mailto: or tel: links
"""

#: Disable the default URL validator
default_validators: list[URLValidator | EmailValidator] = []
#: Whether to skip the validation URL fragments in URLField.to_python()
skip_url_fragment_validation: bool = True

def to_python(self, value: str) -> str:
"""
Convert the string value to the appropriate Python data structure for this field

:param value: The value that was input into the form
:returns: The Python value
"""
if self.skip_url_fragment_validation:
# Skip the URL field to_python for email and phone links
logger.debug(
"Value %r is a mailto or tel link, skipping to_python() of URLField.",
value,
)
return super(forms.URLField, self).to_python(value)
return super().to_python(value)

def clean(self, value: str) -> str:
"""
Expand All @@ -43,8 +28,11 @@ def clean(self, value: str) -> str:
:param value: The value that was input into the form
:returns: The cleaned value
"""
if value.startswith("mailto:"):
email = value[7:]
if "@" in value:
email = value
if value.startswith("mailto:"):
email = value[7:]

Comment thread
MizukiTemma marked this conversation as resolved.
logger.debug(
"Value %r is an email link, enforcing EmailValidator on %r",
value,
Expand All @@ -53,10 +41,18 @@ def clean(self, value: str) -> str:
self.validators.append(EmailValidator())
self.error_messages["invalid"] = _("Enter a valid email address.")
return f"mailto:{super().clean(email)}"
if not value.startswith("tel:"):
logger.debug("Value %r is a normal link, enforcing URLValidator", value)
self.validators.append(URLValidator(schemes=["http", "https"]))
self.skip_url_fragment_validation = False

if not value.startswith("tel:") and re.fullmatch(r"[+\d][\d ]*", value):
formatted_phone_number = format_phone_number(value)
logger.debug(
"Value %r looks like an phone link, formatting to %r",
value,
formatted_phone_number,
Comment thread
MizukiTemma marked this conversation as resolved.
Dismissed
)
return f"tel:{formatted_phone_number}"

logger.debug("Value %r is a normal link, enforcing URLValidator", value)
self.validators.append(URLValidator(schemes=["http", "https"]))
return super().clean(value)


Expand Down
21 changes: 15 additions & 6 deletions integreat_cms/cms/templates/linkcheck/link_list_row.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
rel="noopener noreferrer"
class="text-blue-500 hover:underline"
title="{{ url.url }}">
{{ url.url }}
{% remove_url_prefix url as plain_url %}
{{ plain_url }}
</a>
{% if url.redirect_to %}
<br />
Expand All @@ -39,9 +40,11 @@
</span>
</div>
</td>
<td class="py-3 pr-2 align-top border-t border-solid border-gray-200 break-words {% if view.kwargs.url_filter == 'invalid' %} text-red-500 {% elif view.kwargs.url_filter == 'valid' %} text-green-500 {% endif %} max-w-[75px] sm:max-w-[100px] md:max-w-[100px] lg:max-w-[100px] xl:max-w-[150px] 2xl:max-w-[200px] 3xl:max-w-[260px] 4xl:max-w-[400px]">
{% translate url.get_message %}
</td>
{% if view.kwargs.url_filter == 'valid' or view.kwargs.url_filter == 'invalid' %}
<td class="py-3 pr-2 align-top border-t border-solid border-gray-200 break-words {% if view.kwargs.url_filter == 'invalid' %} text-red-500 {% elif view.kwargs.url_filter == 'valid' %} text-green-500 {% endif %} max-w-[75px] sm:max-w-[100px] md:max-w-[100px] lg:max-w-[100px] xl:max-w-[150px] 2xl:max-w-[200px] 3xl:max-w-[260px] 4xl:max-w-[400px]">
{% translate url.get_message %}
</td>
{% endif %}
{% with link_text=url.regions_links.0.text %}
<td class="py-3 pr-2 align-top border-t border-solid border-gray-200 max-w-[75px] sm:max-w-[100px] md:max-w-[100px] lg:max-w-[100px] xl:max-w-[150px] 2xl:max-w-[200px] 3xl:max-w-[260px] 4xl:max-w-[400px]"
title="{{ link_text }}">
Expand Down Expand Up @@ -79,7 +82,13 @@
<a title="{% if request.region %}{% translate "Replace URL centrally in the current region" %}{% else %}{% translate "Replace URL globally in all the regions" %}{% endif %}"
href="{% url_for_current_region 'edit_url' request url_id=url.id url_filter=view.kwargs.url_filter %}{{ pagination_params }}#replace-url"
class="flex items-center justify-end gap-1 text-blue-500 hover:underline whitespace-nowrap">
{% translate "Change links on all pages" %}
{% if view.kwargs.url_filter == 'invalid' or view.kwargs.url_filter == 'valid' %}
{% translate "Change links on all pages" %}
{% elif LINKCHECK_EMAIL_ENABLED and view.kwargs.url_filter == 'email' %}
{% translate "Adjust email on all pages" %}
{% elif LINKCHECK_PHONE_ENABLED and view.kwargs.url_filter == 'phone' %}
{% translate "Adjust phone number on all pages" %}
{% endif %}
<i icon-name="pen-square" class="shrink-0"></i>
</a>
{% if view.kwargs.url_filter == 'invalid' %}
Expand All @@ -101,7 +110,7 @@
<tr>
<td colspan="6">
<div class="flex gap-2 p-2">
{% render_field edit_url_form.url|add_error_class:"border-red-500" type="url" form="edit-url-form" %}
{% render_field edit_url_form.url|add_error_class:"border-red-500" form="edit-url-form" %}
<a href="{% url_for_current_region 'linkcheck' request url_filter=view.kwargs.url_filter %}{{ pagination_params }}"
class="btn btn-ghost">{% translate "Cancel" %}</a>
<button type="submit" form="edit-url-form" class="btn">
Expand Down
30 changes: 20 additions & 10 deletions integreat_cms/cms/templates/linkcheck/links_by_filter.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,16 @@ <h1 class="heading">
</a>
{% endif %}
{% endif %}
<div class="flex flex-wrap items-center justify-between gap-4 mt-4">
<div class="flex flex-wrap items-center justify-between gap-4 py-4">
<p>
{% if view.kwargs.url_filter == 'invalid' %}
{% translate "The following links are reported by the system as potentially faulty." %}
<br />
{% translate "Click the links to check them and adjust them if necessary." %}
{% elif LINKCHECK_EMAIL_ENABLED and view.kwargs.url_filter == 'email' %}
{% translate "The following email addresses are used and linked in the system." %}
{% elif LINKCHECK_PHONE_ENABLED and view.kwargs.url_filter == 'phone' %}
{% translate "The following telephone numbers are used and linked in the system." %}
{% endif %}
</p>
<a href="{{ wiki_url }}"
Expand All @@ -90,7 +94,7 @@ <h1 class="heading">
class="table-listing"
data-js-bulk-actions>
{% csrf_token %}
<table class="w-full mt-4 rounded border border-solid border-gray-200 shadow bg-white">
<table class="w-full rounded border border-solid border-gray-200 shadow bg-white">
<thead>
<tr class="border-b border-solid border-gray-200">
<th class="text-sm text-left uppercase py-3 pl-4 min">
Expand All @@ -99,17 +103,23 @@ <h1 class="heading">
<th class="text-sm text-left uppercase py-3 pr-2">
{% if view.kwargs.url_filter == 'invalid' %}
{% translate "Link to be checked" %}
{% elif LINKCHECK_EMAIL_ENABLED and view.kwargs.url_filter == 'email' %}
{% translate "Email linking" %}
{% elif LINKCHECK_PHONE_ENABLED and view.kwargs.url_filter == 'phone' %}
{% translate "Phone linking" %}
{% else %}
{% translate "URL" %}
{% endif %}
</th>
<th class="text-sm text-left uppercase py-3 pr-2">
{% if view.kwargs.url_filter == 'invalid' %}
{% translate "Error message" %}
{% else %}
{% translate "Status" %}
{% endif %}
</th>
{% if view.kwargs.url_filter == 'valid' or view.kwargs.url_filter == 'invalid' %}
<th class="text-sm text-left uppercase py-3 pr-2">
{% if view.kwargs.url_filter == 'invalid' %}
{% translate "Error message" %}
{% elif view.kwargs.url_filter == 'valid' %}
{% translate "Status" %}
{% endif %}
</th>
{% endif %}
<th class="text-sm text-left uppercase py-3 pr-2"
title="{% translate "The link text of the first usage" %}">
{% translate "Link text" %}
Expand All @@ -118,7 +128,7 @@ <h1 class="heading">
title="{% translate "The source translation of the first usage" %}">
{% translate "Source" %}
</th>
<th class="text-sm text-right uppercase py-3 pr-4 min">
<th class="text-sm text-right uppercase py-3 pr-4">
{% translate "Options" %}
</th>
</tr>
Expand Down
13 changes: 13 additions & 0 deletions integreat_cms/cms/templatetags/url_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
from django.urls import reverse
from django.utils.safestring import mark_safe

from ..views.contacts.contact_from_existing_data import clean_url

if TYPE_CHECKING:
from typing import Any
from urllib.parse import ParseResult

from django.http import HttpRequest
from linkcheck import Url

register = template.Library()

Expand Down Expand Up @@ -52,3 +55,13 @@ def url_for_current_region(target: str, request: HttpRequest, **kwargs: Any) ->
if request.region:
kwargs["region_slug"] = request.region.slug
return reverse(target, kwargs=kwargs)


@register.simple_tag
def remove_url_prefix(url: Url) -> str:
"""
Return the url without prefix "mailto:" and "tel:"

:param target: Target url
"""
return clean_url(url)
23 changes: 20 additions & 3 deletions integreat_cms/cms/views/linkcheck/linkcheck_list_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,17 @@ def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpRespo
if request.POST:
form_kwargs = {"data": request.POST}
else:
form_kwargs = {"initial": {"url": self.instance}}
form_kwargs = {"initial": {"url": None}}
self.form = EditUrlForm(**form_kwargs)

# Potential conflicts with #4458
placeholder_text = _("Enter new link here")
if self.instance.type == "mailto":
placeholder_text = _("Enter new E-mail here")
elif self.instance.type == "phone":
placeholder_text = _("Enter new phone number here")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest also adding
´´´
self.form.fields["url"].widget.input_type = input_type
´´´

And to determine input_type based on the self.instance.type first. This will give us type="email" on the input field and so we can use the browsers own validation

self.form.fields["url"].widget.attrs["placeholder"] = placeholder_text
return super().dispatch(request, *args, **kwargs)

def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> TemplateResponse:
Expand Down Expand Up @@ -190,12 +199,20 @@ def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
).format(len(failed_replacements)),
)

# Messages for E-mail and phone number links are adjusted in #4518. Do not overwrite or throw away in other PRs of #2837's children.
if new_url.startswith("mailto:"):
messages.success(request, _("Email link was successfully replaced"))
messages.success(
request,
_(
"The email link has been successfully updated on all pages and in all translations."
),
)
elif new_url.startswith("tel:"):
messages.success(
request,
_("Phone number link was successfully replaced"),
_(
"The telephone link has been successfully updated on all pages and in all translations."
),
)
else:
messages.success(request, _("URL was successfully replaced"))
Expand Down
Loading