Repository: wagtail/wagtail

Issue:
Whole page crashes with EmbedNotFoundException when an embed on the page cannot be found
Embeds in rich text don't get affected by this issue usually as embeds they have to be selected with the chooser which checks that the embed exists first (and if it does, the embed would get cached in the Embed table in case it does get deleted).

However, this check is not done when an embed is being made from a URL field. If the embed at the URL cannot be found, the frontend will crash.

I think we could do two things to help with this:
1) Make the `|embed` filter fail gracefully
2) Add an `EmbedChooserPanel` which allows the embed chooser to be used on any URL field (#634)



Discussion:

Resolving patch:
diff --git a/wagtail/wagtailembeds/embeds.py b/wagtail/wagtailembeds/embeds.py
--- a/wagtail/wagtailembeds/embeds.py
+++ b/wagtail/wagtailembeds/embeds.py
@@ -15,10 +15,13 @@
 from wagtail.wagtailembeds.models import Embed
 
 
-class EmbedNotFoundException(Exception):
+class EmbedException(Exception):
     pass
 
-class EmbedlyException(Exception):
+class EmbedNotFoundException(EmbedException):
+    pass
+
+class EmbedlyException(EmbedException):
     pass
 
 class AccessDeniedEmbedlyException(EmbedlyException):
diff --git a/wagtail/wagtailembeds/format.py b/wagtail/wagtailembeds/format.py
--- a/wagtail/wagtailembeds/format.py
+++ b/wagtail/wagtailembeds/format.py
@@ -2,36 +2,36 @@
 
 from django.template.loader import render_to_string
 
-from wagtail.wagtailembeds.embeds import get_embed
+from wagtail.wagtailembeds import embeds
 
 
 def embed_to_frontend_html(url):
     try:
-        embed = get_embed(url)
-        if embed is not None:
-            # Work out ratio
-            if embed.width and embed.height:
-                ratio = str(embed.height / embed.width * 100) + "%"
-            else:
-                ratio = "0"
-
-            # Render template
-            return render_to_string('wagtailembeds/embed_frontend.html', {
-                'embed': embed,
-                'ratio': ratio,
-            })
+        embed = embeds.get_embed(url)
+
+        # Work out ratio
+        if embed.width and embed.height:
+            ratio = str(embed.height / embed.width * 100) + "%"
         else:
-            return ''
-    except:
+            ratio = "0"
+
+        # Render template
+        return render_to_string('wagtailembeds/embed_frontend.html', {
+            'embed': embed,
+            'ratio': ratio,
+        })
+    except embeds.EmbedException:
         return ''
 
 
 def embed_to_editor_html(url):
-    embed = get_embed(url)
-    if embed is None:
-        return
-
-    # Render template
-    return render_to_string('wagtailembeds/embed_editor.html', {
-        'embed': embed,
-    })
+    try:
+        embed = embeds.get_embed(url)
+
+        # Render template
+        return render_to_string('wagtailembeds/embed_editor.html', {
+            'embed': embed,
+        })
+    except embeds.EmbedException:
+        # Could be replaced with a nice error message
+        return ''
diff --git a/wagtail/wagtailembeds/templatetags/wagtailembeds_tags.py b/wagtail/wagtailembeds/templatetags/wagtailembeds_tags.py
--- a/wagtail/wagtailembeds/templatetags/wagtailembeds_tags.py
+++ b/wagtail/wagtailembeds/templatetags/wagtailembeds_tags.py
@@ -1,7 +1,7 @@
 from django import template
 from django.utils.safestring import mark_safe
 
-from wagtail.wagtailembeds.embeds import get_embed
+from wagtail.wagtailembeds import embeds
 
 
 register = template.Library()
@@ -9,11 +9,8 @@
 
 @register.filter
 def embed(url, max_width=None):
-    embed = get_embed(url, max_width=max_width)
     try:
-        if embed is not None:
-            return mark_safe(embed.html)
-        else:
-            return ''
-    except:
+        embed = embeds.get_embed(url, max_width=max_width)
+        return mark_safe(embed.html)
+    except embeds.EmbedException:
         return ''


Test patch:
diff --git a/wagtail/wagtailembeds/tests.py b/wagtail/wagtailembeds/tests.py
--- a/wagtail/wagtailembeds/tests.py
+++ b/wagtail/wagtailembeds/tests.py
@@ -273,41 +273,30 @@ def test_oembed_return_values(self, loads, urlopen):
 
 
 class TestEmbedFilter(TestCase):
-    def setUp(self):
-        class DummyResponse(object):
-            def read(self):
-                return b"foo"
-        self.dummy_response = DummyResponse()
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
+    def test_direct_call(self, get_embed):
+        get_embed.return_value = Embed(html='<img src="http://www.example.com" />')
 
-    @patch('six.moves.urllib.request.urlopen')
-    @patch('json.loads')
-    def test_valid_embed(self, loads, urlopen):
-        urlopen.return_value = self.dummy_response
-        loads.return_value = {'type': 'photo',
-                              'url': 'http://www.example.com'}
         result = embed_filter('http://www.youtube.com/watch/')
+
         self.assertEqual(result, '<img src="http://www.example.com" />')
 
-    @patch('six.moves.urllib.request.urlopen')
-    @patch('json.loads')
-    def test_render_filter(self, loads, urlopen):
-        urlopen.return_value = self.dummy_response
-        loads.return_value = {'type': 'photo',
-                              'url': 'http://www.example.com'}
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
+    def test_call_from_template(self, get_embed):
+        get_embed.return_value = Embed(html='<img src="http://www.example.com" />')
+
         temp = template.Template('{% load wagtailembeds_tags %}{{ "http://www.youtube.com/watch/"|embed }}')
-        context = template.Context()
-        result = temp.render(context)
+        result = temp.render(template.Context())
+
         self.assertEqual(result, '<img src="http://www.example.com" />')
 
-    @patch('six.moves.urllib.request.urlopen')
-    @patch('json.loads')
-    def test_render_filter_nonexistent_type(self, loads, urlopen):
-        urlopen.return_value = self.dummy_response
-        loads.return_value = {'type': 'foo',
-                              'url': 'http://www.example.com'}
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
+    def test_catches_embed_not_found(self, get_embed):
+        get_embed.side_effect = EmbedNotFoundException
+
         temp = template.Template('{% load wagtailembeds_tags %}{{ "http://www.youtube.com/watch/"|embed }}')
-        context = template.Context()
-        result = temp.render(context)
+        result = temp.render(template.Context())
+
         self.assertEqual(result, '')
 
 
@@ -337,7 +326,7 @@ def test_serialize(self):
         serialized_empty_val = block.get_prep_value(None)
         self.assertEqual(serialized_empty_val, '')
 
-    @patch('wagtail.wagtailembeds.format.get_embed')
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
     def test_render(self, get_embed):
         get_embed.return_value = Embed(html='<h1>Hello world!</h1>')
 
@@ -354,7 +343,7 @@ def test_render(self, get_embed):
         # Check that get_embed was called correctly
         get_embed.assert_any_call('http://www.example.com/foo')
 
-    @patch('wagtail.wagtailembeds.format.get_embed')
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
     def test_render_within_structblock(self, get_embed):
         """
         When rendering the value of an EmbedBlock directly in a template
@@ -454,18 +443,21 @@ def test_get_db_attributes(self):
         self.assertEqual(result,
                          {'url': 'test-url'})
 
-    @patch('wagtail.wagtailembeds.embeds.oembed')
-    def test_expand_db_attributes_for_editor(self, oembed):
-        oembed.return_value = {
-            'title': 'test title',
-            'author_name': 'test author name',
-            'provider_name': 'test provider name',
-            'type': 'test type',
-            'thumbnail_url': 'test thumbnail url',
-            'width': 'test width',
-            'height': 'test height',
-            'html': 'test html'
-        }
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
+    def test_expand_db_attributes_for_editor(self, get_embed):
+        get_embed.return_value = Embed(
+            url='http://www.youtube.com/watch/',
+            max_width=None,
+            type='video',
+            html='test html',
+            title='test title',
+            author_name='test author name',
+            provider_name='test provider name',
+            thumbnail_url='http://test/thumbnail.url',
+            width=1000,
+            height=1000,
+        )
+
         result = MediaEmbedHandler.expand_db_attributes(
             {'url': 'http://www.youtube.com/watch/'},
             True
@@ -475,22 +467,47 @@ def test_expand_db_attributes_for_editor(self, oembed):
         self.assertIn('<p>URL: http://www.youtube.com/watch/</p>', result)
         self.assertIn('<p>Provider: test provider name</p>', result)
         self.assertIn('<p>Author: test author name</p>', result)
-        self.assertIn('<img src="test thumbnail url" alt="test title">', result)
-
-    @patch('wagtail.wagtailembeds.embeds.oembed')
-    def test_expand_db_attributes_not_for_editor(self, oembed):
-        oembed.return_value = {
-            'title': 'test title',
-            'author_name': 'test author name',
-            'provider_name': 'test provider name',
-            'type': 'test type',
-            'thumbnail_url': 'test thumbnail url',
-            'width': 'test width',
-            'height': 'test height',
-            'html': 'test html'
-        }
+        self.assertIn('<img src="http://test/thumbnail.url" alt="test title">', result)
+
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
+    def test_test_expand_db_attributes_for_editor_catches_embed_not_found(self, get_embed):
+        get_embed.side_effect = EmbedNotFoundException
+
+        result = MediaEmbedHandler.expand_db_attributes(
+            {'url': 'http://www.youtube.com/watch/'},
+            True
+        )
+
+        self.assertEqual(result, '')
+
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
+    def test_expand_db_attributes(self, get_embed):
+        get_embed.return_value = Embed(
+            url='http://www.youtube.com/watch/',
+            max_width=None,
+            type='video',
+            html='test html',
+            title='test title',
+            author_name='test author name',
+            provider_name='test provider name',
+            thumbnail_url='htto://test/thumbnail.url',
+            width=1000,
+            height=1000,
+        )
+
         result = MediaEmbedHandler.expand_db_attributes(
             {'url': 'http://www.youtube.com/watch/'},
             False
         )
         self.assertIn('test html', result)
+
+    @patch('wagtail.wagtailembeds.embeds.get_embed')
+    def test_expand_db_attributes_catches_embed_not_found(self, get_embed):
+        get_embed.side_effect = EmbedNotFoundException
+
+        result = MediaEmbedHandler.expand_db_attributes(
+            {'url': 'http://www.youtube.com/watch/'},
+            False
+        )
+
+        self.assertEqual(result, '')