]> gitweb.fperrin.net Git - djsite.git/commitdiff
Add HTML validation
authorFrédéric Perrin <frederic.perrin@resel.fr>
Mon, 31 Oct 2016 01:21:18 +0000 (01:21 +0000)
committerFrédéric Perrin <frederic.perrin@resel.fr>
Mon, 31 Oct 2016 01:21:18 +0000 (01:21 +0000)
quotes/localmodels.py [new file with mode: 0644]
quotes/models.py
quotes/templates/quotes/display.html
quotes/templates/quotes/tag.html
quotes/tests.py

diff --git a/quotes/localmodels.py b/quotes/localmodels.py
new file mode 100644 (file)
index 0000000..afe874a
--- /dev/null
@@ -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
index cdae648e21960c540bc2dbab1b9b8334ffdf9344..0fa66afd0ed91e8e076bd7d674c068246ef09b46 100644 (file)
@@ -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
index f2744621eae07dc71bef8d42d099e458d168399b..696f7549eaef1954871d7eca32cffe39b496b63d 100644 (file)
@@ -1,6 +1,6 @@
 <div class="quote">
   <p class="text">
-    {{ quote.text }}
+    {{ quote.text|safe }}
   </p>
 
   <div class="details">
index 52497dc241e21d4cce91f4274a7100eb849ed385..a308d451f8f348d0338917c74879b103e3c1c725 100644 (file)
@@ -10,5 +10,4 @@
   {% include "quotes/display.html" with quote=quote %}
 {% endfor %}
 
-
 {% endblock %}
index 2d75e321334ae7b07f3894ae964b87a3992e6ef6..4fbe4776757864f14ad078e4e17cb538e422e867 100644 (file)
@@ -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="<p>Ich bin...</p>", work=w1)
         self.q1 = q1
 
     def test_one(self):
-        q = Quote.objects.filter(text__startswith="Ich")
+        q = Quote.objects.filter(text__startswith="<p>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('<script>' in document)
         return document
 
     def setUp(self):
         a1 = Author.objects.create(name="Author with notes",
-                                   notes="Some notes for the author")
+                                   notes="<script>Some notes for the author</script>")
         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="<p>Some notes for the work</p>")
+        q1 = Quote.objects.create(text="<p>Quote01, two tags</p>",
                                   work=w1,
-                                  notes="Some notes for the quote")
+                                  notes="<p>Some notes for the quote</p>")
         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="<p>Quote02, no tags</p>", 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="<p>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="<p>Quote02")
+        self.assertEqual(q.count(), 1,
+                         "Couldn't find Quote02 after insertion")
         q = q[0]
 
         quotepage = self.getPage('show/%s/' % q.id)