From 515aa619438e5cc6d0561e82364c0839cec9b67d Mon Sep 17 00:00:00 2001 From: Yannick Chabbert Date: Thu, 7 Apr 2016 16:40:30 +0200 Subject: [PATCH] Add template tag for dynamic image url Update documentation Co-Authored-By: zerolab --- .../images/image_serve_view.rst | 35 +++++++++++-------- wagtail/images/jinja2tags.py | 2 ++ .../images/templatetags/wagtailimages_tags.py | 15 ++++++++ wagtail/images/tests/test_jinja2.py | 12 +++++++ wagtail/images/tests/tests.py | 32 +++++++++++++++++ wagtail/images/tests/urls.py | 1 + wagtail/images/views/serve.py | 8 +++++ 7 files changed, 91 insertions(+), 14 deletions(-) diff --git a/docs/advanced_topics/images/image_serve_view.rst b/docs/advanced_topics/images/image_serve_view.rst index fbd6fb86d..7f47fbb1a 100644 --- a/docs/advanced_topics/images/image_serve_view.rst +++ b/docs/advanced_topics/images/image_serve_view.rst @@ -57,21 +57,10 @@ client over an API or used directly in the template. One advantage of using dynamic image URLs in the template is that they do not block the initial response while rendering like the ``{% image %}`` tag does. -.. code-block:: python +The ``generate_image_url`` function in ``wagtail.images.views.serve`` is a convenience +method to generate a dynamic image URL. - from django.urls import reverse - from wagtail.images.views.serve import generate_signature - - def generate_image_url(image, filter_spec): - signature = generate_signature(image.id, filter_spec) - url = reverse('wagtailimages_serve', args=(signature, image.id, filter_spec)) - - # Append image's original filename to the URL (optional) - url += image.file.name[len('original_images/'):] - - return url - -And here's an example of this being used in a view: +Here's an example of this being used in a view: .. code-block:: python @@ -92,6 +81,24 @@ Image operations can be chained by joining them with a ``|`` character: }) +In your templates: + +.. code-block:: html+django + + {% load wagtailimages_tags %} + ... + + + {% image_url page.photo "width-400" %} + + + {% image_url page.photo "fill-100x100|jpegquality-40" %} + + + {% image_url page.photo "width-400" "mycustomview_serve" %} + +You can pass an optional view name that will be used to serve the image through. The default is ``wagtailimages_serve`` + Advanced configuration ====================== diff --git a/wagtail/images/jinja2tags.py b/wagtail/images/jinja2tags.py index 28e6cc56b..a65b474a3 100644 --- a/wagtail/images/jinja2tags.py +++ b/wagtail/images/jinja2tags.py @@ -1,6 +1,7 @@ from jinja2.ext import Extension from .shortcuts import get_rendition_or_not_found +from .templatetags.wagtailimages_tags import image_url def image(image, filterspec, **attrs): @@ -21,6 +22,7 @@ class WagtailImagesExtension(Extension): self.environment.globals.update({ 'image': image, + 'image_url': image_url, }) diff --git a/wagtail/images/templatetags/wagtailimages_tags.py b/wagtail/images/templatetags/wagtailimages_tags.py index 5887025ce..7ffee228f 100644 --- a/wagtail/images/templatetags/wagtailimages_tags.py +++ b/wagtail/images/templatetags/wagtailimages_tags.py @@ -1,10 +1,14 @@ import re from django import template +from django.core.exceptions import ImproperlyConfigured +from django.urls import NoReverseMatch from django.utils.functional import cached_property from wagtail.images.models import Filter from wagtail.images.shortcuts import get_rendition_or_not_found +from wagtail.images.views.serve import generate_image_url + register = template.Library() allowed_filter_pattern = re.compile(r"^[A-Za-z0-9_\-\.]+$") @@ -107,3 +111,14 @@ class ImageNode(template.Node): for key in self.attrs: resolved_attrs[key] = self.attrs[key].resolve(context) return rendition.img_tag(resolved_attrs) + + +@register.simple_tag() +def image_url(image, filter_spec, viewname='wagtailimages_serve'): + try: + return generate_image_url(image, filter_spec, viewname) + except NoReverseMatch: + raise ImproperlyConfigured( + "'image_url' tag requires the " + viewname + " view to be configured. Please see " + "https://docs.wagtail.io/en/stable/advanced_topics/images/image_serve_view.html#setup for instructions." + ) diff --git a/wagtail/images/tests/test_jinja2.py b/wagtail/images/tests/test_jinja2.py index 31c254220..1b0559f37 100644 --- a/wagtail/images/tests/test_jinja2.py +++ b/wagtail/images/tests/test_jinja2.py @@ -78,3 +78,15 @@ class TestImagesJinja(TestCase): self.render('{{ image(myimage, "width-200") }}', {'myimage': self.bad_image}), 'missing image' ) + + def test_image_url(self): + self.assertRegex( + self.render('{{ image_url(myimage, "width-200") }}', {'myimage': self.image}), + '/images/.*/width-200/{}'.format(self.image.file.name.split('/')[-1]), + ) + + def test_image_url_custom_view(self): + self.assertRegex( + self.render('{{ image_url(myimage, "width-200", "wagtailimages_serve_custom_view") }}', {'myimage': self.image}), + '/testimages/custom_view/.*/width-200/{}'.format(self.image.file.name.split('/')[-1]), + ) diff --git a/wagtail/images/tests/tests.py b/wagtail/images/tests/tests.py index 6c3a3627a..512804728 100644 --- a/wagtail/images/tests/tests.py +++ b/wagtail/images/tests/tests.py @@ -146,6 +146,38 @@ class TestImageTag(TestCase): context = template.Context({'image_obj': self.image}) temp.render(context) + def render_image_url_tag(self, image, view_name): + temp = template.Template( + '{% load wagtailimages_tags %}{% image_url image_obj "width-400" "' + view_name + '" %}' + ) + context = template.Context({'image_obj': image}) + return temp.render(context) + + def test_image_url(self): + result = self.render_image_url_tag(self.image, 'wagtailimages_serve') + self.assertRegex( + result, + '/images/.*/width-400/{}'.format(self.image.file.name.split('/')[-1]), + ) + + def test_image_url_custom_view(self): + result = self.render_image_url_tag(self.image, 'wagtailimages_serve_custom_view') + + self.assertRegex( + result, + '/testimages/custom_view/.*/width-400/{}'.format(self.image.file.name.split('/')[-1]), + ) + + + def test_image_url_no_imageserve_view_added(self): + # if image_url tag is used, but the image serve view was not defined. + with self.assertRaises(ImproperlyConfigured): + temp = template.Template( + '{% load wagtailimages_tags %}{% image_url image_obj "width-400" "mynonexistingimageserve_view" %}' + ) + context = template.Context({'image_obj': self.image}) + temp.render(context) + class TestMissingImage(TestCase): """ diff --git a/wagtail/images/tests/urls.py b/wagtail/images/tests/urls.py index 7df356568..bab50aff8 100644 --- a/wagtail/images/tests/urls.py +++ b/wagtail/images/tests/urls.py @@ -7,6 +7,7 @@ urlpatterns = [ url(r'^actions/serve/(.*)/(\d*)/(.*)/[^/]*', ServeView.as_view(action='serve'), name='wagtailimages_serve_action_serve'), url(r'^actions/redirect/(.*)/(\d*)/(.*)/[^/]*', ServeView.as_view(action='redirect'), name='wagtailimages_serve_action_redirect'), url(r'^custom_key/(.*)/(\d*)/(.*)/[^/]*', ServeView.as_view(key='custom'), name='wagtailimages_serve_custom_key'), + url(r'^custom_view/([^/]*)/(\d*)/([^/]*)/[^/]*$', ServeView.as_view(), name='wagtailimages_serve_custom_view'), url(r'^sendfile/(.*)/(\d*)/(.*)/[^/]*', SendFileView.as_view(), name='wagtailimages_sendfile'), url(r'^sendfile-dummy/(.*)/(\d*)/(.*)/[^/]*', SendFileView.as_view(backend=dummy_sendfile_backend.sendfile), name='wagtailimages_sendfile_dummy'), ] diff --git a/wagtail/images/views/serve.py b/wagtail/images/views/serve.py index fa72752c4..9be492f43 100644 --- a/wagtail/images/views/serve.py +++ b/wagtail/images/views/serve.py @@ -8,6 +8,7 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.http import HttpResponse, HttpResponsePermanentRedirect, StreamingHttpResponse from django.shortcuts import get_object_or_404 +from django.urls import reverse from django.utils.decorators import classonlymethod from django.utils.encoding import force_text from django.views.generic import View @@ -36,6 +37,13 @@ def verify_signature(signature, image_id, filter_spec, key=None): return force_text(signature) == generate_signature(image_id, filter_spec, key=key) +def generate_image_url(image, filter_spec, viewname='wagtailimages_serve', key=None): + signature = generate_signature(image.id, filter_spec, key) + url = reverse(viewname, args=(signature, image.id, filter_spec)) + url += image.file.name[len('original_images/'):] + return url + + class ServeView(View): model = get_image_model() action = 'serve'