From b8f18c94e5531ce3e0e3652bc0bd7c18b6c816b0 Mon Sep 17 00:00:00 2001 From: Maciej Pienczyn Date: Thu, 19 Mar 2026 12:25:43 +0100 Subject: [PATCH] feat(messages): auto-linkify URLs in message content URLs in messages are now automatically converted to clickable links opening in a new tab. Works for both old plain-text and new Quill HTML messages. Uses linkify Jinja2 filter that only processes text nodes outside existing / tags. Co-Authored-By: Claude Opus 4.6 (1M context) --- app.py | 4 +++ templates/messages/view.html | 4 +-- utils/helpers.py | 53 ++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 29dcbfd..679c61c 100644 --- a/app.py +++ b/app.py @@ -225,6 +225,10 @@ def ensure_url_filter(url): return f'https://{url}' return url +# Register linkify filter for messages +from utils.helpers import linkify_urls +app.jinja_env.filters['linkify'] = linkify_urls + # Register forum markdown filter from utils.markdown import register_markdown_filter register_markdown_filter(app) diff --git a/templates/messages/view.html b/templates/messages/view.html index c6cc3a1..57ee418 100755 --- a/templates/messages/view.html +++ b/templates/messages/view.html @@ -450,7 +450,7 @@ {% endif %} -
{{ msg.content|safe }}
+
{{ msg.content|linkify }}
{% if msg.attachments %}
{% for att in msg.attachments %} @@ -510,7 +510,7 @@
-
{{ message.content|safe }}
+
{{ message.content|linkify }}
{% if message.attachments %}
diff --git a/utils/helpers.py b/utils/helpers.py index 29b6475..d3b42a5 100644 --- a/utils/helpers.py +++ b/utils/helpers.py @@ -33,6 +33,59 @@ def sanitize_html(content): return bleach.clean(content, tags=_ALLOWED_TAGS, attributes=_ALLOWED_ATTRS, strip=True) +def linkify_urls(html): + """ + Auto-link URLs in HTML content that are not already inside or tags. + Links to nordabiznes.pl open in new tab as trusted internal links. + """ + if not html: + return html + + from markupsafe import Markup + + # Split HTML into tags and text, only process text outside / tags + url_pattern = re.compile(r'(https?://[^\s<>"\']+)') + tag_pattern = re.compile(r'<(/?)(\w+)([^>]*)>') + + result = [] + pos = 0 + in_a_tag = False + + for match in tag_pattern.finditer(html): + start, end = match.start(), match.end() + is_closing = match.group(1) == '/' + tag_name = match.group(2).lower() + + # Process text before this tag + if start > pos: + text_chunk = html[pos:start] + if in_a_tag: + result.append(text_chunk) + else: + result.append(url_pattern.sub( + lambda m: '{0}'.format(m.group(0)), + text_chunk + )) + + result.append(match.group(0)) + pos = end + + if tag_name in ('a', 'img'): + in_a_tag = not is_closing + + # Process remaining text + if pos < len(html): + text_chunk = html[pos:] + if not in_a_tag: + text_chunk = url_pattern.sub( + lambda m: '{0}'.format(m.group(0)), + text_chunk + ) + result.append(text_chunk) + + return Markup(''.join(result)) + + def sanitize_input(text, max_length=1000): """ Sanitize user input - remove potentially dangerous characters.