kopia lustrzana https://github.com/wagtail/wagtail
Merge pull request #811 from gasman/fix/catch-missing-images
Render missing images within rich text as broken images, rather than throwing IOError during renderingpull/822/head
commit
2eff5e85c9
|
@ -81,7 +81,8 @@
|
|||
"audience": "public",
|
||||
"location": "The North Pole",
|
||||
"body": "<p>Chestnuts roasting on an open fire</p>",
|
||||
"cost": "Free"
|
||||
"cost": "Free",
|
||||
"feed_image": 1
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -223,7 +224,7 @@
|
|||
"date_from": "2015-04-22",
|
||||
"audience": "public",
|
||||
"location": "Ameristralia",
|
||||
"body": "<p>come celebrate the independence of Ameristralia</p>",
|
||||
"body": "<p>come celebrate the independence of Ameristralia <embed embedtype=\"image\" format=\"fullwidth\" id=\"1\" alt=\"where did my image go?\" /></p>",
|
||||
"cost": "Free"
|
||||
}
|
||||
},
|
||||
|
@ -625,5 +626,16 @@
|
|||
"page": 11,
|
||||
"password": "swordfish"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "wagtailimages.image",
|
||||
"fields": {
|
||||
"title": "A missing image",
|
||||
"file": "original_images/missing.jpg",
|
||||
"width": 1000,
|
||||
"height": 1000,
|
||||
"created_at": "2014-01-01T12:00:00.000Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% load wagtailcore_tags %}
|
||||
{% load wagtailcore_tags wagtailimages_tags %}
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
@ -8,6 +8,10 @@
|
|||
<body>
|
||||
<h1>{{ self.title }}</h1>
|
||||
<h2>Event</h2>
|
||||
{% if self.feed_image %}
|
||||
{% image self.feed_image width-200 class="feed-image" %}
|
||||
{% endif %}
|
||||
{{ self.body|richtext }}
|
||||
<p><a href="{% slugurl 'events' %}">Back to events index</a></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.utils.html import escape
|
||||
|
||||
from wagtail.utils.apps import get_app_submodules
|
||||
from wagtail.wagtailimages.models import SourceImageIOError
|
||||
|
||||
|
||||
class Format(object):
|
||||
|
@ -25,7 +26,15 @@ class Format(object):
|
|||
)
|
||||
|
||||
def image_to_html(self, image, alt_text, extra_attributes=''):
|
||||
rendition = image.get_rendition(self.filter_spec)
|
||||
try:
|
||||
rendition = image.get_rendition(self.filter_spec)
|
||||
except SourceImageIOError:
|
||||
# Image file is (probably) missing from /media/original_images - generate a dummy
|
||||
# rendition so that we just output a broken image, rather than crashing out completely
|
||||
# during rendering
|
||||
Rendition = image.renditions.model # pick up any custom Image / Rendition classes that may be in use
|
||||
rendition = Rendition(image=image, width=0, height=0)
|
||||
rendition.file.name = 'not-found'
|
||||
|
||||
if self.classnames:
|
||||
class_attr = 'class="%s" ' % escape(self.classnames)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import os.path
|
||||
import re
|
||||
|
||||
from six import BytesIO
|
||||
from six import BytesIO, text_type
|
||||
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
|
@ -28,6 +28,13 @@ from wagtail.wagtailimages.rect import Rect
|
|||
from wagtail.wagtailadmin.utils import get_object_usage
|
||||
|
||||
|
||||
class SourceImageIOError(IOError):
|
||||
"""
|
||||
Custom exception to distinguish IOErrors that were thrown while opening the source image
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def get_upload_to(instance, filename):
|
||||
folder_name = 'original_images'
|
||||
filename = instance.file.field.storage.get_valid_name(filename)
|
||||
|
@ -178,7 +185,15 @@ class AbstractImage(models.Model, TagSearchable):
|
|||
# If we have a backend attribute then pass it to process
|
||||
# image - else pass 'default'
|
||||
backend_name = getattr(self, 'backend', 'default')
|
||||
generated_image = filter.process_image(file_field.file, backend_name=backend_name, focal_point=self.get_focal_point())
|
||||
|
||||
try:
|
||||
image_file = file_field.file # triggers a call to self.storage.open, so IOErrors from missing files will be raised at this point
|
||||
except IOError as e:
|
||||
# re-throw this as a SourceImageIOError so that calling code can distinguish
|
||||
# these from IOErrors elsewhere in the process
|
||||
raise SourceImageIOError(text_type(e))
|
||||
|
||||
generated_image = filter.process_image(image_file, backend_name=backend_name, focal_point=self.get_focal_point())
|
||||
|
||||
# generate new filename derived from old one, inserting the filter spec and focal point key before the extension
|
||||
if self.has_focal_point():
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django import template
|
||||
|
||||
from wagtail.wagtailimages.models import Filter
|
||||
from wagtail.wagtailimages.models import Filter, SourceImageIOError
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -53,10 +53,10 @@ class ImageNode(template.Node):
|
|||
|
||||
try:
|
||||
rendition = image.get_rendition(self.filter)
|
||||
except IOError:
|
||||
except SourceImageIOError:
|
||||
# It's fairly routine for people to pull down remote databases to their
|
||||
# local dev versions without retrieving the corresponding image files.
|
||||
# In such a case, we would get an IOError at the point where we try to
|
||||
# In such a case, we would get a SourceImageIOError at the point where we try to
|
||||
# create the resized version of a non-existent image. Since this is a
|
||||
# bit catastrophic for a missing image, we'll substitute a dummy
|
||||
# Rendition object so that we just output a broken link instead.
|
||||
|
|
|
@ -63,6 +63,25 @@ class TestImageTag(TestCase):
|
|||
self.assertTrue('title="my wonderful title"' in result)
|
||||
|
||||
|
||||
class TestMissingImage(TestCase):
|
||||
"""
|
||||
Missing image files in media/original_images should be handled gracefully, to cope with
|
||||
pulling live databases to a development instance without copying the corresponding image files.
|
||||
In this case, it's acceptable to render broken images, but not to fail rendering the page outright.
|
||||
"""
|
||||
fixtures = ['test.json']
|
||||
|
||||
def test_image_tag_with_missing_image(self):
|
||||
# the page /events/christmas/ has a missing image as the feed image
|
||||
response = self.client.get('/events/christmas/')
|
||||
self.assertContains(response, '<img src="/media/not-found" width="0" height="0" alt="A missing image" class="feed-image">', html=True)
|
||||
|
||||
def test_rich_text_with_missing_image(self):
|
||||
# the page /events/final-event/ has a missing image in the rich text body
|
||||
response = self.client.get('/events/final-event/')
|
||||
self.assertContains(response, '<img class="richtext-image full-width" src="/media/not-found" width="0" height="0" alt="where did my image go?">', html=True)
|
||||
|
||||
|
||||
class TestFormat(TestCase):
|
||||
def setUp(self):
|
||||
# test format
|
||||
|
|
Ładowanie…
Reference in New Issue