feat(messages): auto-linkify URLs in message content
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

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 <a>/<img> tags.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-19 12:25:43 +01:00
parent 73d9de8c9c
commit b8f18c94e5
3 changed files with 59 additions and 2 deletions

4
app.py
View File

@ -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)

View File

@ -450,7 +450,7 @@
{% endif %}
</div>
</div>
<div class="thread-msg-body">{{ msg.content|safe }}</div>
<div class="thread-msg-body">{{ msg.content|linkify }}</div>
{% if msg.attachments %}
<div class="attachments-section" style="margin: 0 var(--spacing-md) var(--spacing-sm); padding-top: var(--spacing-sm);">
{% for att in msg.attachments %}
@ -510,7 +510,7 @@
</div>
<div class="message-card-body">
<div class="message-body">{{ message.content|safe }}</div>
<div class="message-body">{{ message.content|linkify }}</div>
{% if message.attachments %}
<div class="attachments-section">

View File

@ -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 <a> or <img> 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 <a>/<img> 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: '<a href="{0}" target="_blank" style="color:var(--primary);word-break:break-all;">{0}</a>'.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: '<a href="{0}" target="_blank" style="color:var(--primary);word-break:break-all;">{0}</a>'.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.