From 242b0beb0eff7c97205b0bbe3747118b70ff1ee9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Fr=C3=A9d=C3=A9ric=20Perrin?= Date: Mon, 31 Oct 2016 01:21:18 +0000 Subject: [PATCH] Add HTML validation --- quotes/localmodels.py | 12 ++++++++ quotes/models.py | 44 ++++++++++++++-------------- quotes/templates/quotes/display.html | 2 +- quotes/templates/quotes/tag.html | 1 - quotes/tests.py | 24 ++++++++------- 5 files changed, 48 insertions(+), 35 deletions(-) create mode 100644 quotes/localmodels.py diff --git a/quotes/localmodels.py b/quotes/localmodels.py new file mode 100644 index 0000000..afe874a --- /dev/null +++ b/quotes/localmodels.py @@ -0,0 +1,12 @@ +import tinymce.models +import bleach + +def valid_html(value): + return bleach.clean(value, bleach.ALLOWED_TAGS + ['p']) + +class HTMLField(tinymce.models.HTMLField): + def pre_save(self, model_instance, add): + value = getattr(model_instance, self.attname) + safe_value = valid_html(value) + setattr(model_instance, self.attname, safe_value) + return safe_value diff --git a/quotes/models.py b/quotes/models.py index cdae648..0fa66af 100644 --- a/quotes/models.py +++ b/quotes/models.py @@ -1,5 +1,5 @@ from django.db import models -from tinymce import models as tinymce_models +from localmodels import HTMLField # Create your models here. class Tag(models.Model): @@ -10,12 +10,12 @@ class Tag(models.Model): class Author(models.Model): name = models.CharField(max_length=100, help_text="Name of the author") - notes = tinymce_models.HTMLField(blank=True, help_text= \ - "Notes about the author; may be left blank. Will \ - not be HTML-escaped.",) - pvt_notes = models.TextField(blank=True, help_text= \ - "Notes about the author; not displayed on \ - the public interface",) + notes = HTMLField(blank=True, help_text= \ + "Notes about the author; may be left blank. Will \ + not be HTML-escaped.",) + pvt_notes = HTMLField(blank=True, help_text= \ + "Notes about the author; not displayed on \ + the public interface",) tags = models.ManyToManyField(Tag, blank=True) birth_date = models.DateField(blank=True, null=True, @@ -24,7 +24,7 @@ class Author(models.Model): help_text="Date of death (leave blank \ if still alive!)") - def __unicode__(self): + def __str__(self): return self.name # class DatePrecision(models.CharField): @@ -55,27 +55,27 @@ class Work(models.Model): help_text="Date of the quote") tags = models.ManyToManyField(Tag, blank=True) - notes = models.TextField(blank=True, help_text= \ - "Notes about the work; may be left blank. Will \ - not be HTML-escaped. XXX: offer a WYSIWYG editor") - pvt_notes = models.TextField(blank=True, help_text= \ - "Notes about the work; not displayed on \ - the public interface") + notes = HTMLField(blank=True, help_text= \ + "Notes about the work; may be left blank. Will \ + not be HTML-escaped. XXX: offer a WYSIWYG editor") + pvt_notes = HTMLField(blank=True, help_text= \ + "Notes about the work; not displayed on \ + the public interface") def __str__(self): return "%s: %s (%s)" % (self.author.name, self.name, self.date) class Quote(models.Model): - text = models.TextField() + text = HTMLField() tags = models.ManyToManyField(Tag, blank=True) work = models.ForeignKey(Work) - notes = models.TextField(blank=True, help_text= \ - "Notes about the quote; may be left blank. Will \ - not be HTML-escaped. XXX: offer a WYSIWYG editor") - pvt_notes = models.TextField(blank=True, help_text= \ - "Notes about the quote; not displayed on \ - the public interface") + notes = HTMLField(blank=True, help_text= \ + "Notes about the quote; may be left blank. Will \ + not be HTML-escaped. XXX: offer a WYSIWYG editor") + pvt_notes = HTMLField(blank=True, help_text= \ + "Notes about the quote; not displayed on \ + the public interface") - def __unicode__(self): + def __str__(self): return self.work.author.name + ": " + self.text diff --git a/quotes/templates/quotes/display.html b/quotes/templates/quotes/display.html index f274462..696f754 100644 --- a/quotes/templates/quotes/display.html +++ b/quotes/templates/quotes/display.html @@ -1,6 +1,6 @@

- {{ quote.text }} + {{ quote.text|safe }}

diff --git a/quotes/templates/quotes/tag.html b/quotes/templates/quotes/tag.html index 52497dc..a308d45 100644 --- a/quotes/templates/quotes/tag.html +++ b/quotes/templates/quotes/tag.html @@ -10,5 +10,4 @@ {% include "quotes/display.html" with quote=quote %} {% endfor %} - {% endblock %} diff --git a/quotes/tests.py b/quotes/tests.py index 2d75e32..4fbe477 100644 --- a/quotes/tests.py +++ b/quotes/tests.py @@ -9,11 +9,11 @@ class QuoteTest(TestCase): def setUp(self): a1 = Author.objects.create(name="JFK") w1 = Work.objects.create(name="Berlin speech", author=a1) - q1 = Quote.objects.create(text="Ich bin...", work=w1) + q1 = Quote.objects.create(text="

Ich bin...

", work=w1) self.q1 = q1 def test_one(self): - q = Quote.objects.filter(text__startswith="Ich") + q = Quote.objects.filter(text__startswith="

Ich") self.assertEqual(q.count(), 1) q = q[0] self.assertEqual(q, self.q1) @@ -30,24 +30,25 @@ class ViewsTest(TestCase): lxml.etree.fromstring(document) except lxml.etree.XMLSyntaxError as e: self.assertTrue(False, 'Errors in page at %s: %s' % (url, e)) + self.assertFalse('") w1 = Work.objects.create(name="Context with some notes", author=a1, - notes="Some notes for the work") - q1 = Quote.objects.create(text="Quote01, two tags", + notes="

Some notes for the work

") + q1 = Quote.objects.create(text="

Quote01, two tags

", work=w1, - notes="Some notes for the quote") + notes="

Some notes for the quote

") t1 = Tag.objects.create(tag="tag01-1") t2 = Tag.objects.create(tag="tag01-2") q1.tags.add(t1, t2) a2 = Author.objects.create(name="Author without notes") w2 = Work.objects.create(name="Work without notes", author=a2) - q2 = Quote.objects.create(text="Quote02, no tags", work=w2) + q2 = Quote.objects.create(text="

Quote02, no tags

", work=w2) self.assertSequenceEqual(q2.tags.all(), []) def test_all(self): @@ -81,8 +82,8 @@ class ViewsTest(TestCase): self.assertTrue(seen[q.id]) def test_views_all_data(self): - q = Quote.objects.filter(text__startswith="Quote01") - self.assertEqual(q.count(), 1) + q = Quote.objects.filter(text__startswith="

Quote01") + self.assertEqual(q.count(), 1, "Couldn't find Quote01 after insertion") q = q[0] # check the individual quote page; each of the note type is displayed @@ -117,8 +118,9 @@ class ViewsTest(TestCase): self.assertIn(q.text, tagpage) def test_views_minimal_data(self): - q = Quote.objects.filter(text__startswith="Quote02") - self.assertEqual(q.count(), 1) + q = Quote.objects.filter(text__startswith="

Quote02") + self.assertEqual(q.count(), 1, + "Couldn't find Quote02 after insertion") q = q[0] quotepage = self.getPage('show/%s/' % q.id) -- 2.43.0