diff --git a/weblate/accounts/models.py b/weblate/accounts/models.py index 5a8a77512f48..41aff75186ae 100644 --- a/weblate/accounts/models.py +++ b/weblate/accounts/models.py @@ -46,6 +46,7 @@ from weblate.utils import messages from weblate.utils.decorators import disable_for_loaddata from weblate.utils.fields import EmailField +from weblate.utils.html import mail_quote_value from weblate.utils.render import validate_editor from weblate.utils.request import get_ip_address, get_user_agent from weblate.utils.token import get_token @@ -352,9 +353,9 @@ def get_params(self): } for name, value in self.params.items(): if name in {"old", "new", "name", "email", "username"}: - value = format_html("{}", value) + value = format_html("{}", mail_quote_value(value)) elif name in {"device", "project", "site_title", "method"}: - value = format_html("{}", value) + value = format_html("{}", mail_quote_value(value)) result[name] = value diff --git a/weblate/utils/html.py b/weblate/utils/html.py index 4f4eae3e8eeb..9c7b0ebd547f 100644 --- a/weblate/utils/html.py +++ b/weblate/utils/html.py @@ -9,10 +9,12 @@ from typing import TYPE_CHECKING import nh3 +from django.utils.html import format_html, format_html_join from html2text import HTML2Text as _HTML2Text from lxml.etree import HTMLParser if TYPE_CHECKING: + from django.utils.safestring import SafeString from lxml.etree import ParserTarget from weblate.checks.flags import Flags @@ -164,3 +166,25 @@ def handle_tag(self, tag: str, attrs: dict[str, str | None], start: bool) -> Non self.o(WEBLATE_TAGS[tag][not start]) return super().handle_tag(tag, attrs, start) + + +def mail_quote_char(text: str) -> str | SafeString: + if text in {":", "."}: + return format_html("{}", text) + return text + + +def mail_quote_value(text: str) -> str | SafeString: + """ + Quote value to be used in e-mail notifications. + + This tries to avoid automatic conversion to links by Gmail + and similar services. + + Solution based on https://stackoverflow.com/a/23404042/225718 + """ + return format_html_join( + "", + "{}", + ((mail_quote_char(part),) for part in re.split("([.:])", text)), + ) diff --git a/weblate/utils/tests/test_html.py b/weblate/utils/tests/test_html.py index 8dd276ea9a7e..69deed6020e1 100644 --- a/weblate/utils/tests/test_html.py +++ b/weblate/utils/tests/test_html.py @@ -7,7 +7,12 @@ from django.test import SimpleTestCase from weblate.checks.flags import Flags -from weblate.utils.html import HTML2Text, HTMLSanitizer, extract_html_tags +from weblate.utils.html import ( + HTML2Text, + HTMLSanitizer, + extract_html_tags, + mail_quote_value, +) class HTMLSanitizerTestCase(SimpleTestCase): @@ -104,3 +109,23 @@ def test_html2text_diff(self) -> None: html2text.handle("text "), "text{+ +}\n\n", ) + + +class MailQuoteTestCase(SimpleTestCase): + def test_plain(self): + self.assertEqual( + mail_quote_value("text"), + "text", + ) + + def test_dot(self): + self.assertEqual( + mail_quote_value("example.com"), + "example.com", + ) + + def test_url(self): + self.assertEqual( + mail_quote_value("https://test.example.com"), + "https://test.example.com", + )