From 42caa586b445fa154c835802e3dbdee52c8b72d7 Mon Sep 17 00:00:00 2001 From: Bertrand Bordage Date: Mon, 30 Apr 2018 12:40:42 +0200 Subject: [PATCH] Validates EmbedBlock URLs against providers. --- CHANGELOG.txt | 1 + docs/releases/2.3.rst | 1 + wagtail/embeds/blocks.py | 15 ++++++++++++++- wagtail/embeds/tests.py | 41 ++++++++++++++++++++++++++++------------ 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 87596ed1a..6bf703853 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -7,6 +7,7 @@ Changelog * Added support for Django 2.1 (Ryan Verner, Matt Westcott) * Added 'scale' image filter (Oliver Wilkerson) * Added meta tag to prevent search engines from indexing admin pages (Karl Hobley) + * EmbedBlock now validates against recognised embed providers on save (Bertrand Bordage) * Fix: Respect next param on login (Loic Teixeira) * Fix: InlinePanel now handles relations that specify a related_query_name (Aram Dulyan) diff --git a/docs/releases/2.3.rst b/docs/releases/2.3.rst index 251f10628..8634aa6ff 100644 --- a/docs/releases/2.3.rst +++ b/docs/releases/2.3.rst @@ -24,6 +24,7 @@ Other features * Added 'scale' image filter (Oliver Wilkerson) * Added meta tag to prevent search engines from indexing admin pages (Karl Hobley) + * EmbedBlock now validates against recognised embed providers on save (Bertrand Bordage) Bug fixes diff --git a/wagtail/embeds/blocks.py b/wagtail/embeds/blocks.py index 1f171addf..1d6202d90 100644 --- a/wagtail/embeds/blocks.py +++ b/wagtail/embeds/blocks.py @@ -1,3 +1,7 @@ +from django.core.exceptions import ValidationError +from django.utils.functional import cached_property +from django.utils.translation import ugettext_lazy as _ + from wagtail.core import blocks from wagtail.embeds.format import embed_to_frontend_html @@ -13,9 +17,13 @@ class EmbedValue: def __init__(self, url): self.url = url - def __str__(self): + @cached_property + def html(self): return embed_to_frontend_html(self.url) + def __str__(self): + return self.html + class EmbedBlock(blocks.URLBlock): def get_default(self): @@ -57,5 +65,10 @@ class EmbedBlock(blocks.URLBlock): else: return EmbedValue(value) + def clean(self, value): + if isinstance(value, EmbedValue) and not value.html: + raise ValidationError(_("Cannot find an embed for this URL.")) + return super().clean(value) + class Meta: icon = "media" diff --git a/wagtail/embeds/tests.py b/wagtail/embeds/tests.py index 19ae1443d..04d8fe113 100644 --- a/wagtail/embeds/tests.py +++ b/wagtail/embeds/tests.py @@ -518,25 +518,42 @@ class TestEmbedBlock(TestCase): self.assertIsInstance(block5.get_default(), EmbedValue) self.assertEqual(block5.get_default().url, 'http://www.example.com/foo') - def test_clean(self): - required_block = EmbedBlock() - nonrequired_block = EmbedBlock(required=False) + def test_clean_required(self): + block = EmbedBlock() - # a valid EmbedValue should return the same value on clean - cleaned_value = required_block.clean(EmbedValue('http://www.example.com/foo')) + cleaned_value = block.clean( + EmbedValue('https://www.youtube.com/watch?v=_U79Wc965vw')) self.assertIsInstance(cleaned_value, EmbedValue) - self.assertEqual(cleaned_value.url, 'http://www.example.com/foo') + self.assertEqual(cleaned_value.url, + 'https://www.youtube.com/watch?v=_U79Wc965vw') - cleaned_value = nonrequired_block.clean(EmbedValue('http://www.example.com/foo')) + with self.assertRaisesMessage(ValidationError, ''): + block.clean(None) + + def test_clean_non_required(self): + block = EmbedBlock(required=False) + + cleaned_value = block.clean( + EmbedValue('https://www.youtube.com/watch?v=_U79Wc965vw')) self.assertIsInstance(cleaned_value, EmbedValue) - self.assertEqual(cleaned_value.url, 'http://www.example.com/foo') + self.assertEqual(cleaned_value.url, + 'https://www.youtube.com/watch?v=_U79Wc965vw') - # None should only be accepted for nonrequired blocks - cleaned_value = nonrequired_block.clean(None) - self.assertEqual(cleaned_value, None) + cleaned_value = block.clean(None) + self.assertIsNone(cleaned_value) + + def test_clean_invalid_url(self): + non_required_block = EmbedBlock(required=False) with self.assertRaises(ValidationError): - required_block.clean(None) + non_required_block.clean( + EmbedValue('http://no-oembed-here.com/something')) + + required_block = EmbedBlock() + + with self.assertRaises(ValidationError): + required_block.clean( + EmbedValue('http://no-oembed-here.com/something')) class TestMediaEmbedHandler(TestCase):