Found that django's urlize() does not deal well with adjacent parantheses. Replaced it with a filter based on bleach.linkify, which does better.

- Legacy-Id: 14752
This commit is contained in:
Henrik Levkowetz 2018-03-09 15:07:04 +00:00
parent 1df3c715d0
commit 2c0348cc7a
13 changed files with 51 additions and 22 deletions

View file

@ -370,8 +370,9 @@ def format_history_text(text, trunc_words=25):
def format_snippet(text, trunc_words=25):
# urlize if there aren't already links present
if not 'href=' in text:
# django's urlize() is buggy in at least Django 1.11; use
# bleach.linkify instead
# django's urlize() cannot handle adjacent parentheszised
# expressions, for instance [REF](http://example.com/foo)
# Use bleach.linkify instead
text = bleach.linkify(text)
full = keep_spacing(collapsebr(linebreaksbr(mark_safe(sanitize_html(text)))))
snippet = truncatewords_html(full, trunc_words)
@ -520,3 +521,9 @@ def zaptmp(s):
def rfcbis(s):
m = re.search('^.*-rfc(\d+)-?bis(-.*)?$', s)
return None if m is None else 'rfc' + m.group(1)
@register.filter
@stringfilter
def urlize(value):
raise RuntimeError("Use linkify from textfilters instead of urlize")

View file

@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
import os
import shutil
import calendar
import datetime
import json
import StringIO
import bleach
import six
from pyquery import PyQuery
from tempfile import NamedTemporaryFile
@ -17,7 +20,6 @@ from django.urls import NoReverseMatch
from django.contrib.auth.models import User
from django.utils.html import escape
from django.template.defaultfilters import urlize
from ietf.community.models import CommunityList
from ietf.community.utils import reset_name_contains_index_for_rule
@ -1273,7 +1275,7 @@ class StatusUpdateTests(TestCase):
response = self.client.get(url)
self.assertEqual(response.status_code,200)
q=PyQuery(response.content)
self.assertTrue(urlize(escape(event.desc) in q('pre')))
self.assertTrue(bleach.linkify(escape(event.desc)) in six.text_type(q('pre')))
self.assertFalse(q('a#edit_button'))
self.client.login(username=chair.person.user.username,password='%s+password'%chair.person.user.username)
response = self.client.get(url)

View file

@ -1,8 +1,9 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load bootstrap3 %}
{% load textfilters %}
{% block title %}
Charter submission for {{ group.acronym }} {{ group.type.name }}
@ -72,10 +73,10 @@
<tbody class="meta">
<tr>
<th>Mailing list</th>
<th>Address</th><td>{{ group.list_email|urlize }}</td>
<th>Address</th><td>{{ group.list_email|linkify }}</td>
</tr>
<tr><td></td><th>To subscribe</th><td>{{ group.list_subscribe|urlize }}</td></tr>
<tr><td></td><th>Archive</th><td>{{ group.list_archive|urlize }}</td></tr>
<tr><td></td><th>To subscribe</th><td>{{ group.list_subscribe|linkify }}</td></tr>
<tr><td></td><th>Archive</th><td>{{ group.list_archive|linkify }}</td></tr>
</tbody>
{% endif %}
</table>

View file

@ -1,5 +1,7 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}{% load origin %}{% origin %}
{% load ietf_filters %}
{% load textfilters %}
<div class="row">
<div class="col-md-2 hidden-sm hidden-xs">
@ -91,7 +93,7 @@
<div class="panel-heading">
<h5 class="panel-title"><b>{{ p.pos.name }}</b> ({{ p.discuss_time|date:"Y-m-d" }}{% if not p.for_current_revision %} for -{{ p.get_dochistory.rev}}{% endif %})</h5>
</div>
<div class="panel-body"><pre class="ballot pasted">{{ p.discuss|escape|urlize }}</pre></div>
<div class="panel-body"><pre class="ballot pasted">{{ p.discuss|escape|linkify }}</pre></div>
</div>
{% endif %}
@ -100,7 +102,7 @@
<div class="panel-heading">
<h5 class="panel-title"><b>Comment</b> ({{ p.comment_time|date:"Y-m-d" }}{% if not p.for_current_revision %} for -{{ p.get_dochistory.rev}}{% endif %})</h5>
</div>
<div class="panel-body"><pre class="ballot pasted">{{ p.comment|escape|urlize }}</pre></div>
<div class="panel-body"><pre class="ballot pasted">{{ p.comment|escape|linkify }}</pre></div>
</div>
{% endif %}
{% endfor %}

View file

@ -3,6 +3,7 @@
{% load origin %}
{% load staticfiles %}
{% load ietf_filters %}
{% load textfilters %}
{% block title %}{{ doc.title }}{% endblock %}
@ -54,6 +55,6 @@
<h2>{{ doc.type.name }}<br><small>{{ doc.name }}</small></h2>
{% if doc.rev and content != None %}
<pre class="pasted">{{ content|urlize|safe|sanitize_html|safe }}</pre>
<pre class="pasted">{{ content|linkify|safe|sanitize_html|safe }}</pre>
{% endif %}
{% endblock %}

View file

@ -3,6 +3,7 @@
{% load origin %}
{% load ietf_filters %}
{% load textfilters %}
{% block title %}Writeups for {{ doc.name }}-{{ doc.rev }}{% endblock %}
@ -20,7 +21,7 @@
{% for name, text, url in writeups %}
{% if text %}
<pre>{{ text|urlize }}</pre>
<pre>{{ text|linkify }}</pre>
{% endif %}
<p>

View file

@ -1,8 +1,8 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load ietf_filters staticfiles %}
{% load textfilters %}
{% block pagehead %}
{% if last_call_only %}
@ -47,7 +47,7 @@
<a href="{% url "ietf.doc.views_doc.document_main" doc.name %}">{{ doc.name }}</a>
<br><b>{{ doc.title }}</b>
{% if doc.note %}
<br><i>Note: {{ doc.note|linebreaksbr|urlize }}</i>
<br><i>Note: {{ doc.note|linebreaksbr|linkify }}</i>
{% endif %}
</td>
<td>{{ doc.intended_std_level.name }}</td>

View file

@ -2,6 +2,7 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load ietf_filters %}
{% load textfilters %}
{% block title %}
Shepherd writeup for {{ doc.canonical_name }}-{{ doc.rev }}
@ -11,7 +12,7 @@
{% origin %}
<h1>Shepherd writeup<br><small>{{ doc.canonical_name }}-{{ doc.rev }}</small></h1>
<pre class="pasted">{{writeup|escape|urlize}}</pre>
<pre class="pasted">{{writeup|escape|linkify}}</pre>
{% if can_edit %}
<a class="btn btn-primary" href="{% url 'ietf.doc.views_draft.edit_shepherd_writeup' name=doc.name %}">Edit</a>

View file

@ -4,6 +4,8 @@
{% load staticfiles %}
{% load bootstrap3 %}
{% load ietf_filters %}
{% load textfilters %}
{% block title %}
Status updates
@ -27,7 +29,7 @@
<span class="label label-success">{{ rpt.group.state.slug|upper }}</span>
{% endif %}
<br> {{rpt.time|date:"Y-m-d"}}</td>
<td><pre class="pasted">{{ rpt.desc|default:"(none)"|escape|urlize }}</pre></td>
<td><pre class="pasted">{{ rpt.desc|default:"(none)"|escape|linkify }}</pre></td>
</tr>
{% endfor %}
</table>
@ -42,7 +44,7 @@
<span class="label label-success">{{ rpt.group.state.slug|upper }}</span>
{% endif %}
<br> {{rpt.time|date:"Y-m-d"}}</td>
<td><pre class="pasted">{{ rpt.desc|default:"(none)"|escape|urlize }}</pre></td>
<td><pre class="pasted">{{ rpt.desc|default:"(none)"|escape|linkify }}</pre></td>
</tr>
{% endfor %}
</table>

View file

@ -3,6 +3,7 @@
{% load origin %}
{% load ietf_filters %}
{% load markup_tags %}
{% load textfilters %}
{% block group_content %}
{% origin %}
@ -176,7 +177,7 @@
<a class="btn btn-default btn-xs" href="{% url 'ietf.group.views.edit' acronym=group.acronym field='list_email' %}">Edit</a>
{% endif %}
</td>
<td>{{ group.list_email|urlize }}</td>
<td>{{ group.list_email|linkify }}</td>
</tr>
<tr>
<td></td>
@ -186,7 +187,7 @@
<a class="btn btn-default btn-xs" href="{% url 'ietf.group.views.edit' acronym=group.acronym field='list_subscribe' %}">Edit</a>
{% endif %}
</td>
<td>{{ group.list_subscribe|urlize }}</td>
<td>{{ group.list_subscribe|linkify }}</td>
</tr>
<tr>
<td></td>
@ -196,7 +197,7 @@
<a class="btn btn-default btn-xs" href="{% url 'ietf.group.views.edit' acronym=group.acronym field='list_archive' %}">Edit</a>
{% endif %}
</td>
<td>{{ group.list_archive|urlize }}</td>
<td>{{ group.list_archive|linkify }}</td>
</tr>
</tbody>
{% endif %}

View file

@ -4,6 +4,7 @@
{% load staticfiles %}
{% load bootstrap3 %}
{% load ietf_filters %}
{% load textfilters %}
{% block title %}
Status update for {{ group.type.name }} {{ group.acronym }}
@ -15,7 +16,7 @@
Status update for {{ group.type.name }} {{ group.acronym }}
</h1>
<pre class="pasted">{{ status_update.desc|default:"(none)"|escape|urlize }}</pre>
<pre class="pasted">{{ status_update.desc|default:"(none)"|escape|linkify }}</pre>
{% if can_provide_status_update %}
<a id="edit_button" class="btn btn-primary" href="{% url "ietf.group.views.group_about_status_edit" acronym=group.acronym %}">Edit</a>

View file

@ -4,6 +4,7 @@
{% load staticfiles %}
{% load bootstrap3 %}
{% load ietf_filters %}
{% load textfilters %}
{% block title %}
Status update for {{ group.type.name }} {{ group.acronym }} at {{meeting}}
@ -15,7 +16,7 @@
Status update for {{ group.type.name }} {{ group.acronym }} at {{meeting}}
</h1>
<pre class="pasted">{{ status_update.desc|default:"(none)"|escape|urlize }}</pre>
<pre class="pasted">{{ status_update.desc|default:"(none)"|escape|linkify }}</pre>
<a class="btn btn-default pull-right" href="{% url "ietf.meeting.views.proceedings" num=meeting.number %}">Back</a>

View file

@ -1,7 +1,10 @@
from __future__ import unicode_literals
import bleach
from django import template
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe
import debug # pyflakes:ignore
@ -65,3 +68,9 @@ def texescape_filter(value):
"A TeX escape filter"
return texescape(value)
@register.filter
@stringfilter
def linkify(value):
text = mark_safe(bleach.linkify(value))
debug.show('text[:240]')
return text