kopia lustrzana https://github.com/wagtail/wagtail
catch errors on non existing images
rodzic
ab45915a90
commit
2dd3f42e25
|
@ -38,6 +38,7 @@ Changelog
|
||||||
* Fix: `MenuItem` `url` parameter can now take a lazy URL (Adon Metcalfe, rayrayndwiga)
|
* Fix: `MenuItem` `url` parameter can now take a lazy URL (Adon Metcalfe, rayrayndwiga)
|
||||||
* Fix: Added missing translation tag to InlinePanel 'Add' button (jnns)
|
* Fix: Added missing translation tag to InlinePanel 'Add' button (jnns)
|
||||||
* Fix: Restored correct highlighting behaviour of rich text toolbar buttons
|
* Fix: Restored correct highlighting behaviour of rich text toolbar buttons
|
||||||
|
* Fix: Rendering a missing image through ImageChooserBlock no longer breaks the whole page (Christian Peters)
|
||||||
|
|
||||||
1.2 (12.11.2015)
|
1.2 (12.11.2015)
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -88,6 +88,7 @@ Bug fixes
|
||||||
* ``MenuItem`` ``url`` parameter can now take a lazy URL (Adon Metcalfe, rayrayndwiga)
|
* ``MenuItem`` ``url`` parameter can now take a lazy URL (Adon Metcalfe, rayrayndwiga)
|
||||||
* Added missing translation tag to InlinePanel 'Add' button (jnns)
|
* Added missing translation tag to InlinePanel 'Add' button (jnns)
|
||||||
* Restored correct highlighting behaviour of rich text toolbar buttons
|
* Restored correct highlighting behaviour of rich text toolbar buttons
|
||||||
|
* Rendering a missing image through ImageChooserBlock no longer breaks the whole page (Christian Peters)
|
||||||
|
|
||||||
|
|
||||||
Upgrade considerations
|
Upgrade considerations
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from wagtail.wagtailcore.blocks import ChooserBlock
|
from wagtail.wagtailcore.blocks import ChooserBlock
|
||||||
|
from .shortcuts import get_rendition_or_not_found
|
||||||
|
|
||||||
|
|
||||||
class ImageChooserBlock(ChooserBlock):
|
class ImageChooserBlock(ChooserBlock):
|
||||||
|
@ -16,6 +17,6 @@ class ImageChooserBlock(ChooserBlock):
|
||||||
|
|
||||||
def render_basic(self, value):
|
def render_basic(self, value):
|
||||||
if value:
|
if value:
|
||||||
return value.get_rendition('original').img_tag()
|
return get_rendition_or_not_found(value, 'original').img_tag()
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
|
||||||
from wagtail.utils.apps import get_app_submodules
|
from wagtail.utils.apps import get_app_submodules
|
||||||
from wagtail.wagtailimages.models import SourceImageIOError
|
from .shortcuts import get_rendition_or_not_found
|
||||||
|
|
||||||
|
|
||||||
class Format(object):
|
class Format(object):
|
||||||
|
@ -26,15 +26,7 @@ class Format(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
def image_to_html(self, image, alt_text, extra_attributes=''):
|
def image_to_html(self, image, alt_text, extra_attributes=''):
|
||||||
try:
|
rendition = get_rendition_or_not_found(image, self.filter_spec)
|
||||||
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:
|
if self.classnames:
|
||||||
class_attr = 'class="%s" ' % escape(self.classnames)
|
class_attr = 'class="%s" ' % escape(self.classnames)
|
||||||
|
|
|
@ -2,25 +2,14 @@ from __future__ import absolute_import
|
||||||
|
|
||||||
from jinja2.ext import Extension
|
from jinja2.ext import Extension
|
||||||
|
|
||||||
from wagtail.wagtailimages.models import SourceImageIOError
|
from .shortcuts import get_rendition_or_not_found
|
||||||
|
|
||||||
|
|
||||||
def image(image, filterspec, **attrs):
|
def image(image, filterspec, **attrs):
|
||||||
if not image:
|
if not image:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
try:
|
rendition = get_rendition_or_not_found(image, filterspec)
|
||||||
rendition = image.get_rendition(filterspec)
|
|
||||||
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 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.
|
|
||||||
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 attrs:
|
if attrs:
|
||||||
return rendition.img_tag(attrs)
|
return rendition.img_tag(attrs)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# coding=utf-8
|
||||||
|
from wagtail.wagtailimages.models import SourceImageIOError
|
||||||
|
|
||||||
|
|
||||||
|
def get_rendition_or_not_found(image, specs):
|
||||||
|
"""
|
||||||
|
Tries to get / create the rendition for the image or renders a not-found image if it does not exist.
|
||||||
|
|
||||||
|
:param image: AbstractImage
|
||||||
|
:param specs: str or Filter
|
||||||
|
:return: Rendition
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return image.get_rendition(specs)
|
||||||
|
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'
|
||||||
|
return rendition
|
|
@ -1,7 +1,8 @@
|
||||||
from django import template
|
from django import template
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from wagtail.wagtailimages.models import Filter, SourceImageIOError
|
from wagtail.wagtailimages.models import Filter
|
||||||
|
from wagtail.wagtailimages.shortcuts import get_rendition_or_not_found
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
@ -54,18 +55,7 @@ class ImageNode(template.Node):
|
||||||
if not image:
|
if not image:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
try:
|
rendition = get_rendition_or_not_found(image, self.filter)
|
||||||
rendition = image.get_rendition(self.filter)
|
|
||||||
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 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.
|
|
||||||
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.output_var_name:
|
if self.output_var_name:
|
||||||
# return the rendition object in the given variable
|
# return the rendition object in the given variable
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
# -*- coding: utf-8 -*
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.core import serializers
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from wagtail.wagtailimages.blocks import ImageChooserBlock
|
||||||
|
|
||||||
|
from .utils import get_test_image_file, Image
|
||||||
|
|
||||||
|
|
||||||
|
class TestImageChooserBlock(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.image = Image.objects.create(
|
||||||
|
title="Test image",
|
||||||
|
file=get_test_image_file(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an image with a missing file, by deserializing fom a python object
|
||||||
|
# (which bypasses FileField's attempt to read the file)
|
||||||
|
self.bad_image = list(serializers.deserialize('python', [{
|
||||||
|
'fields': {
|
||||||
|
'title': 'missing image',
|
||||||
|
'height': 100,
|
||||||
|
'file': 'original_images/missing-image.jpg',
|
||||||
|
'width': 100,
|
||||||
|
},
|
||||||
|
'model': 'wagtailimages.image'
|
||||||
|
}]))[0].object
|
||||||
|
self.bad_image.save()
|
||||||
|
|
||||||
|
def get_image_filename(self, image, filterspec):
|
||||||
|
"""
|
||||||
|
Get the generated filename for a resized image
|
||||||
|
"""
|
||||||
|
name, ext = os.path.splitext(os.path.basename(image.file.name))
|
||||||
|
return '{}images/{}.{}{}'.format(
|
||||||
|
settings.MEDIA_URL, name, filterspec, ext)
|
||||||
|
|
||||||
|
def test_render(self):
|
||||||
|
block = ImageChooserBlock()
|
||||||
|
html = block.render(self.image)
|
||||||
|
expected_html = '<img alt="Test image" src="{}" width="640" height="480">'.format(
|
||||||
|
self.get_image_filename(self.image, "original")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertHTMLEqual(html, expected_html)
|
||||||
|
|
||||||
|
def test_render_missing(self):
|
||||||
|
block = ImageChooserBlock()
|
||||||
|
html = block.render(self.bad_image)
|
||||||
|
expected_html = '<img alt="missing image" src="/media/not-found" width="0" height="0">'
|
||||||
|
|
||||||
|
self.assertHTMLEqual(html, expected_html)
|
|
@ -5,6 +5,7 @@ import unittest
|
||||||
|
|
||||||
import django
|
import django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core import serializers
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from wagtail.wagtailcore.models import Site
|
from wagtail.wagtailcore.models import Site
|
||||||
|
@ -25,6 +26,19 @@ class TestImagesJinja(TestCase):
|
||||||
file=get_test_image_file(),
|
file=get_test_image_file(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Create an image with a missing file, by deserializing fom a python object
|
||||||
|
# (which bypasses FileField's attempt to read the file)
|
||||||
|
self.bad_image = list(serializers.deserialize('python', [{
|
||||||
|
'fields': {
|
||||||
|
'title': 'missing image',
|
||||||
|
'height': 100,
|
||||||
|
'file': 'original_images/missing-image.jpg',
|
||||||
|
'width': 100,
|
||||||
|
},
|
||||||
|
'model': 'wagtailimages.image'
|
||||||
|
}]))[0].object
|
||||||
|
self.bad_image.save()
|
||||||
|
|
||||||
def render(self, string, context=None, request_context=True):
|
def render(self, string, context=None, request_context=True):
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
|
@ -64,3 +78,9 @@ class TestImagesJinja(TestCase):
|
||||||
'width: {{ background.width }}, url: {{ background.url }}')
|
'width: {{ background.width }}, url: {{ background.url }}')
|
||||||
output = ('width: 200, url: ' + self.get_image_filename(self.image, "width-200"))
|
output = ('width: 200, url: ' + self.get_image_filename(self.image, "width-200"))
|
||||||
self.assertHTMLEqual(self.render(template, {'myimage': self.image}), output)
|
self.assertHTMLEqual(self.render(template, {'myimage': self.image}), output)
|
||||||
|
|
||||||
|
def test_missing_image(self):
|
||||||
|
self.assertHTMLEqual(
|
||||||
|
self.render('{{ image(myimage, "width-200") }}', {'myimage': self.bad_image}),
|
||||||
|
'<img alt="missing image" src="/media/not-found" width="0" height="0">'
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# coding=utf-8
|
||||||
|
from django.test import TestCase
|
||||||
|
from wagtail.wagtailimages.shortcuts import get_rendition_or_not_found
|
||||||
|
from .utils import Image, get_test_image_file
|
||||||
|
|
||||||
|
|
||||||
|
class TestShortcuts(TestCase):
|
||||||
|
|
||||||
|
fixtures = ['test.json']
|
||||||
|
|
||||||
|
def test_fallback_to_not_found(self):
|
||||||
|
bad_image = Image.objects.get(id=1)
|
||||||
|
good_image = Image.objects.create(
|
||||||
|
title="Test image",
|
||||||
|
file=get_test_image_file(),
|
||||||
|
)
|
||||||
|
|
||||||
|
rendition = get_rendition_or_not_found(good_image, 'width-400')
|
||||||
|
self.assertEqual(rendition.width, 400)
|
||||||
|
|
||||||
|
rendition = get_rendition_or_not_found(bad_image, 'width-400')
|
||||||
|
self.assertEqual(rendition.file.name, 'not-found')
|
Ładowanie…
Reference in New Issue