kopia lustrzana https://github.com/wagtail/wagtail
Merge branch 'master' into scheduled-publishing
Conflicts: wagtail/wagtailcore/models.pypull/386/head
commit
1cc68807ea
|
@ -209,7 +209,6 @@ Methods:
|
|||
* get_context
|
||||
* get_template
|
||||
* is_navigable
|
||||
* get_other_siblings
|
||||
* get_ancestors
|
||||
* get_descendants
|
||||
* get_siblings
|
||||
|
|
|
@ -91,6 +91,9 @@ In addition to Django's standard tags and filters, Wagtail provides some of its
|
|||
Images (tag)
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. versionchanged:: 0.4
|
||||
The 'image_tags' tags library was renamed to 'wagtailimages_tags'
|
||||
|
||||
The ``image`` tag inserts an XHTML-compatible ``img`` element into the page, setting its ``src``, ``width``, ``height`` and ``alt``. See also :ref:`image_tag_alt`.
|
||||
|
||||
The syntax for the tag is thus::
|
||||
|
@ -101,7 +104,7 @@ For example:
|
|||
|
||||
.. code-block:: django
|
||||
|
||||
{% load image %}
|
||||
{% load wagtailimages_tags %}
|
||||
...
|
||||
|
||||
{% image self.photo width-400 %}
|
||||
|
@ -206,7 +209,7 @@ No validation is performed on attributes add in this way by the developer. It's
|
|||
Wagtail can assign the image data to another object using Django's ``as`` syntax:
|
||||
|
||||
.. code-block:: django
|
||||
|
||||
|
||||
{% image self.photo width-400 as tmp_photo %}
|
||||
|
||||
<img src="{{ tmp_photo.src }}" width="{{ tmp_photo.width }}"
|
||||
|
@ -228,13 +231,16 @@ You can also use the ``attrs`` property as a shorthand to output the ``src``, ``
|
|||
Rich text (filter)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionchanged:: 0.4
|
||||
The 'rich_text' tags library was renamed to 'wagtailcore_tags'
|
||||
|
||||
This filter takes a chunk of HTML content and renders it as safe HTML in the page. Importantly it also expands internal shorthand references to embedded images and links made in the Wagtail editor into fully-baked HTML ready for display.
|
||||
|
||||
Only fields using ``RichTextField`` need this applied in the template.
|
||||
|
||||
.. code-block:: django
|
||||
|
||||
{% load rich_text %}
|
||||
{% load wagtailcore_tags %}
|
||||
...
|
||||
{{ self.body|richtext }}
|
||||
|
||||
|
@ -270,6 +276,9 @@ Wagtail embeds and images are included at their full width, which may overflow t
|
|||
Internal links (tag)
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionchanged:: 0.4
|
||||
The 'pageurl' tags library was renamed to 'wagtailcore_tags'
|
||||
|
||||
pageurl
|
||||
--------
|
||||
|
||||
|
@ -277,7 +286,7 @@ Takes a Page object and returns a relative URL (``/foo/bar/``) if within the sam
|
|||
|
||||
.. code-block:: django
|
||||
|
||||
{% load pageurl %}
|
||||
{% load wagtailcore_tags %}
|
||||
...
|
||||
<a href="{% pageurl self.blog_page %}">
|
||||
|
||||
|
@ -288,7 +297,7 @@ Takes any ``slug`` as defined in a page's "Promote" tab and returns the URL for
|
|||
|
||||
.. code-block:: django
|
||||
|
||||
{% load slugurl %}
|
||||
{% load wagtailcore_tags %}
|
||||
...
|
||||
<a href="{% slugurl self.your_slug %}">
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
border-color: rgba(255,255,255,0.2);
|
||||
border-style: solid;
|
||||
border-width:1px 0 0 0;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
a{
|
||||
|
|
|
@ -18,6 +18,7 @@ from django.template.response import TemplateResponse
|
|||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from treebeard.mp_tree import MP_Node
|
||||
|
||||
|
@ -336,7 +337,7 @@ class Page(MP_Node, ClusterableModel, Indexed):
|
|||
cursor.execute(update_statement,
|
||||
[new_url_path, len(old_url_path) + 1, self.path + '%', self.id])
|
||||
|
||||
@property
|
||||
@cached_property
|
||||
def specific(self):
|
||||
"""
|
||||
Return this page in its most specific subclassed form.
|
||||
|
@ -350,7 +351,7 @@ class Page(MP_Node, ClusterableModel, Indexed):
|
|||
else:
|
||||
return content_type.get_object_for_this_type(id=self.id)
|
||||
|
||||
@property
|
||||
@cached_property
|
||||
def specific_class(self):
|
||||
"""
|
||||
return the class that this page would be if instantiated in its
|
||||
|
@ -380,7 +381,7 @@ class Page(MP_Node, ClusterableModel, Indexed):
|
|||
raise Http404
|
||||
|
||||
def save_revision(self, user=None, submitted_for_moderation=False, approved_go_live_at=None):
|
||||
self.revisions.create(
|
||||
return self.revisions.create(
|
||||
content_json=self.to_json(),
|
||||
user=user,
|
||||
submitted_for_moderation=submitted_for_moderation,
|
||||
|
@ -388,20 +389,15 @@ class Page(MP_Node, ClusterableModel, Indexed):
|
|||
)
|
||||
|
||||
def get_latest_revision(self):
|
||||
try:
|
||||
revision = self.revisions.order_by('-created_at')[0]
|
||||
except IndexError:
|
||||
return False
|
||||
|
||||
return revision
|
||||
return self.revisions.order_by('-created_at').first()
|
||||
|
||||
def get_latest_revision_as_page(self):
|
||||
try:
|
||||
revision = self.revisions.order_by('-created_at')[0]
|
||||
except IndexError:
|
||||
return self.specific
|
||||
latest_revision = self.get_latest_revision()
|
||||
|
||||
return revision.as_page_object()
|
||||
if latest_revision:
|
||||
return latest_revision.as_page_object()
|
||||
else:
|
||||
return self.specific
|
||||
|
||||
def get_context(self, request, *args, **kwargs):
|
||||
return {
|
||||
|
@ -431,6 +427,10 @@ class Page(MP_Node, ClusterableModel, Indexed):
|
|||
return (not self.is_leaf()) or self.depth == 2
|
||||
|
||||
def get_other_siblings(self):
|
||||
warnings.warn(
|
||||
"The 'Page.get_other_siblings()' method has been replaced. "
|
||||
"Use 'Page.get_siblings(inclusive=False)' instead.", DeprecationWarning)
|
||||
|
||||
# get sibling pages excluding self
|
||||
return self.get_siblings().exclude(id=self.id)
|
||||
|
||||
|
@ -801,6 +801,9 @@ class PageRevision(models.Model):
|
|||
self.submitted_for_moderation = False
|
||||
page.revisions.update(submitted_for_moderation=False)
|
||||
|
||||
def __unicode__(self):
|
||||
return '"' + unicode(self.page) + '" at ' + unicode(self.created_at)
|
||||
|
||||
|
||||
PAGE_PERMISSION_TYPE_CHOICES = [
|
||||
('add', 'Add'),
|
||||
|
@ -860,29 +863,20 @@ class UserPagePermissionsProxy(object):
|
|||
if self.user.is_superuser:
|
||||
return Page.objects.all()
|
||||
|
||||
# Translate each of the user's permission rules into a Q-expression
|
||||
q_expressions = []
|
||||
for perm in self.permissions:
|
||||
if perm.permission_type == 'add':
|
||||
# user has edit permission on any subpage of perm.page
|
||||
# (including perm.page itself) that is owned by them
|
||||
q_expressions.append(
|
||||
Q(path__startswith=perm.page.path, owner=self.user)
|
||||
)
|
||||
elif perm.permission_type == 'edit':
|
||||
# user has edit permission on any subpage of perm.page
|
||||
# (including perm.page itself) regardless of owner
|
||||
q_expressions.append(
|
||||
Q(path__startswith=perm.page.path)
|
||||
)
|
||||
editable_pages = Page.objects.none()
|
||||
|
||||
for perm in self.permissions.filter(permission_type='add'):
|
||||
# user has edit permission on any subpage of perm.page
|
||||
# (including perm.page itself) that is owned by them
|
||||
editable_pages |= Page.objects.descendant_of(perm.page, inclusive=True).filter(owner=self.user)
|
||||
|
||||
for perm in self.permissions.filter(permission_type='edit'):
|
||||
# user has edit permission on any subpage of perm.page
|
||||
# (including perm.page itself) regardless of owner
|
||||
editable_pages |= Page.objects.descendant_of(perm.page, inclusive=True)
|
||||
|
||||
return editable_pages
|
||||
|
||||
if q_expressions:
|
||||
all_rules = q_expressions[0]
|
||||
for expr in q_expressions[1:]:
|
||||
all_rules = all_rules | expr
|
||||
return Page.objects.filter(all_rules)
|
||||
else:
|
||||
return Page.objects.none()
|
||||
|
||||
def can_edit_pages(self):
|
||||
"""Return True if the user has permission to edit any pages"""
|
||||
|
|
|
@ -1,24 +1,8 @@
|
|||
from django import template
|
||||
import warnings
|
||||
|
||||
from wagtail.wagtailcore.models import Page
|
||||
|
||||
register = template.Library()
|
||||
warnings.warn(
|
||||
"The pageurl tag library has been moved to wagtailcore_tags. "
|
||||
"Use {% load wagtailcore_tags %} instead.", DeprecationWarning)
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def pageurl(context, page):
|
||||
"""
|
||||
Outputs a page's URL as relative (/foo/bar/) if it's within the same site as the
|
||||
current page, or absolute (http://example.com/foo/bar/) if not.
|
||||
"""
|
||||
return page.relative_url(context['request'].site)
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def slugurl(context, slug):
|
||||
"""Returns the URL for the page that has the given slug."""
|
||||
page = Page.objects.filter(slug=slug).first()
|
||||
|
||||
if page:
|
||||
return page.relative_url(context['request'].site)
|
||||
else:
|
||||
return None
|
||||
from wagtail.wagtailcore.templatetags.wagtailcore_tags import register, pageurl
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
import warnings
|
||||
|
||||
from wagtail.wagtailcore.rich_text import expand_db_html
|
||||
|
||||
register = template.Library()
|
||||
warnings.warn(
|
||||
"The rich_text tag library has been moved to wagtailcore_tags. "
|
||||
"Use {% load wagtailcore_tags %} instead.", DeprecationWarning)
|
||||
|
||||
|
||||
@register.filter
|
||||
def richtext(value):
|
||||
return mark_safe('<div class="rich-text">' + expand_db_html(value) + '</div>')
|
||||
from wagtail.wagtailcore.templatetags.wagtailcore_tags import register, richtext
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from wagtail.wagtailcore.rich_text import expand_db_html
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def pageurl(context, page):
|
||||
"""
|
||||
Outputs a page's URL as relative (/foo/bar/) if it's within the same site as the
|
||||
current page, or absolute (http://example.com/foo/bar/) if not.
|
||||
"""
|
||||
return page.relative_url(context['request'].site)
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def slugurl(context, slug):
|
||||
"""Returns the URL for the page that has the given slug."""
|
||||
page = Page.objects.filter(slug=slug).first()
|
||||
|
||||
if page:
|
||||
return page.relative_url(context['request'].site)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@register.filter
|
||||
def richtext(value):
|
||||
return mark_safe('<div class="rich-text">' + expand_db_html(value) + '</div>')
|
|
@ -1,24 +1,8 @@
|
|||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
import warnings
|
||||
|
||||
from wagtail.wagtailembeds import get_embed
|
||||
warnings.warn(
|
||||
"The embed_filters tag library has been moved to wagtailcore_tags. "
|
||||
"Use {% load wagtailembeds_tags %} instead.", DeprecationWarning)
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@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:
|
||||
return ''
|
||||
|
||||
|
||||
@register.filter
|
||||
def embedly(url, max_width=None):
|
||||
return embed(url, max_width)
|
||||
from wagtail.wagtailembeds.templatetags.wagtailembeds_tags import register, embed, embedly
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import warnings
|
||||
|
||||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from wagtail.wagtailembeds import get_embed
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@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:
|
||||
return ''
|
||||
|
||||
|
||||
@register.filter
|
||||
def embedly(url, max_width=None):
|
||||
warnings.warn(
|
||||
"The 'embedly' filter has been renamed. "
|
||||
"Use 'embed' instead.", DeprecationWarning)
|
||||
|
||||
return embed(url, max_width)
|
|
@ -7,6 +7,7 @@ try:
|
|||
except ImportError:
|
||||
no_embedly = True
|
||||
|
||||
from django import template
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.tests.utils import WagtailTestUtils, unittest
|
||||
|
@ -18,7 +19,8 @@ from wagtail.wagtailembeds.embeds import (
|
|||
AccessDeniedEmbedlyException,
|
||||
)
|
||||
from wagtail.wagtailembeds.embeds import embedly as wagtail_embedly, oembed as wagtail_oembed
|
||||
|
||||
from wagtail.wagtailembeds.templatetags.embed_filters import embed as embed_filter
|
||||
from wagtail.wagtailembeds.templatetags.embed_filters import embedly as embedly_filter
|
||||
|
||||
|
||||
class TestEmbeds(TestCase):
|
||||
|
@ -258,3 +260,81 @@ class TestOembed(TestCase):
|
|||
'height': 'test_height',
|
||||
'html': 'test_html'
|
||||
})
|
||||
|
||||
|
||||
class TestEmbedFilter(TestCase):
|
||||
def setUp(self):
|
||||
class DummyResponse(object):
|
||||
def read(self):
|
||||
return "foo"
|
||||
self.dummy_response = DummyResponse()
|
||||
|
||||
@patch('urllib2.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('urllib2.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'}
|
||||
temp = template.Template('{% load embed_filters %}{{ "http://www.youtube.com/watch/"|embed }}')
|
||||
context = template.Context()
|
||||
result = temp.render(context)
|
||||
self.assertEqual(result, '<img src="http://www.example.com" />')
|
||||
|
||||
@patch('urllib2.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'}
|
||||
temp = template.Template('{% load embed_filters %}{{ "http://www.youtube.com/watch/"|embed }}')
|
||||
context = template.Context()
|
||||
result = temp.render(context)
|
||||
self.assertEqual(result, '')
|
||||
|
||||
|
||||
class TestEmbedlyFilter(TestEmbedFilter):
|
||||
def setUp(self):
|
||||
class DummyResponse(object):
|
||||
def read(self):
|
||||
return "foo"
|
||||
self.dummy_response = DummyResponse()
|
||||
|
||||
@patch('urllib2.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 = embedly_filter('http://www.youtube.com/watch/')
|
||||
self.assertEqual(result, '<img src="http://www.example.com" />')
|
||||
|
||||
@patch('urllib2.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'}
|
||||
temp = template.Template('{% load embed_filters %}{{ "http://www.youtube.com/watch/"|embedly }}')
|
||||
context = template.Context()
|
||||
result = temp.render(context)
|
||||
self.assertEqual(result, '<img src="http://www.example.com" />')
|
||||
|
||||
@patch('urllib2.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'}
|
||||
temp = template.Template('{% load embed_filters %}{{ "http://www.youtube.com/watch/"|embedly }}')
|
||||
context = template.Context()
|
||||
result = temp.render(context)
|
||||
self.assertEqual(result, '')
|
||||
|
|
|
@ -1,76 +1,8 @@
|
|||
from django import template
|
||||
import warnings
|
||||
|
||||
from wagtail.wagtailimages.models import Filter
|
||||
|
||||
register = template.Library()
|
||||
|
||||
# Local cache of filters, avoid hitting the DB
|
||||
filters = {}
|
||||
warnings.warn(
|
||||
"The image_tags tag library has been moved to wagtailcore_tags. "
|
||||
"Use {% load wagtailimages_tags %} instead.", DeprecationWarning)
|
||||
|
||||
|
||||
@register.tag(name="image")
|
||||
def image(parser, token):
|
||||
bits = token.split_contents()[1:]
|
||||
image_var = bits[0]
|
||||
filter_spec = bits[1]
|
||||
bits = bits[2:]
|
||||
|
||||
if len(bits) == 2 and bits[0] == 'as':
|
||||
# token is of the form {% image self.photo max-320x200 as img %}
|
||||
return ImageNode(image_var, filter_spec, output_var_name=bits[1])
|
||||
else:
|
||||
# token is of the form {% image self.photo max-320x200 %} - all additional tokens
|
||||
# should be kwargs, which become attributes
|
||||
attrs = {}
|
||||
for bit in bits:
|
||||
try:
|
||||
name, value = bit.split('=')
|
||||
except ValueError:
|
||||
raise template.TemplateSyntaxError("'image' tag should be of the form {% image self.photo max-320x200 [ custom-attr=\"value\" ... ] %} or {% image self.photo max-320x200 as img %}")
|
||||
attrs[name] = parser.compile_filter(value) # setup to resolve context variables as value
|
||||
|
||||
return ImageNode(image_var, filter_spec, attrs=attrs)
|
||||
|
||||
|
||||
class ImageNode(template.Node):
|
||||
def __init__(self, image_var_name, filter_spec, output_var_name=None, attrs={}):
|
||||
self.image_var = template.Variable(image_var_name)
|
||||
self.output_var_name = output_var_name
|
||||
self.attrs = attrs
|
||||
|
||||
if filter_spec not in filters:
|
||||
filters[filter_spec], _ = Filter.objects.get_or_create(spec=filter_spec)
|
||||
self.filter = filters[filter_spec]
|
||||
|
||||
def render(self, context):
|
||||
try:
|
||||
image = self.image_var.resolve(context)
|
||||
except template.VariableDoesNotExist:
|
||||
return ''
|
||||
|
||||
if not image:
|
||||
return ''
|
||||
|
||||
try:
|
||||
rendition = image.get_rendition(self.filter)
|
||||
except IOError:
|
||||
# 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
|
||||
# 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:
|
||||
# return the rendition object in the given variable
|
||||
context[self.output_var_name] = rendition
|
||||
return ''
|
||||
else:
|
||||
# render the rendition's image tag now
|
||||
resolved_attrs = {}
|
||||
for key in self.attrs:
|
||||
resolved_attrs[key] = self.attrs[key].resolve(context)
|
||||
return rendition.img_tag(resolved_attrs)
|
||||
from wagtail.wagtailimages.templatetags.wagtailimages_tags import register, image
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
from django import template
|
||||
|
||||
from wagtail.wagtailimages.models import Filter
|
||||
|
||||
register = template.Library()
|
||||
|
||||
# Local cache of filters, avoid hitting the DB
|
||||
filters = {}
|
||||
|
||||
|
||||
@register.tag(name="image")
|
||||
def image(parser, token):
|
||||
bits = token.split_contents()[1:]
|
||||
image_var = bits[0]
|
||||
filter_spec = bits[1]
|
||||
bits = bits[2:]
|
||||
|
||||
if len(bits) == 2 and bits[0] == 'as':
|
||||
# token is of the form {% image self.photo max-320x200 as img %}
|
||||
return ImageNode(image_var, filter_spec, output_var_name=bits[1])
|
||||
else:
|
||||
# token is of the form {% image self.photo max-320x200 %} - all additional tokens
|
||||
# should be kwargs, which become attributes
|
||||
attrs = {}
|
||||
for bit in bits:
|
||||
try:
|
||||
name, value = bit.split('=')
|
||||
except ValueError:
|
||||
raise template.TemplateSyntaxError("'image' tag should be of the form {% image self.photo max-320x200 [ custom-attr=\"value\" ... ] %} or {% image self.photo max-320x200 as img %}")
|
||||
attrs[name] = parser.compile_filter(value) # setup to resolve context variables as value
|
||||
|
||||
return ImageNode(image_var, filter_spec, attrs=attrs)
|
||||
|
||||
|
||||
class ImageNode(template.Node):
|
||||
def __init__(self, image_var_name, filter_spec, output_var_name=None, attrs={}):
|
||||
self.image_var = template.Variable(image_var_name)
|
||||
self.output_var_name = output_var_name
|
||||
self.attrs = attrs
|
||||
|
||||
if filter_spec not in filters:
|
||||
filters[filter_spec], _ = Filter.objects.get_or_create(spec=filter_spec)
|
||||
self.filter = filters[filter_spec]
|
||||
|
||||
def render(self, context):
|
||||
try:
|
||||
image = self.image_var.resolve(context)
|
||||
except template.VariableDoesNotExist:
|
||||
return ''
|
||||
|
||||
if not image:
|
||||
return ''
|
||||
|
||||
try:
|
||||
rendition = image.get_rendition(self.filter)
|
||||
except IOError:
|
||||
# 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
|
||||
# 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:
|
||||
# return the rendition object in the given variable
|
||||
context[self.output_var_name] = rendition
|
||||
return ''
|
||||
else:
|
||||
# render the rendition's image tag now
|
||||
resolved_attrs = {}
|
||||
for key in self.attrs:
|
||||
resolved_attrs[key] = self.attrs[key].resolve(context)
|
||||
return rendition.img_tag(resolved_attrs)
|
|
@ -1,3 +1,5 @@
|
|||
from mock import MagicMock
|
||||
|
||||
from django.test import TestCase
|
||||
from django import template
|
||||
from django.contrib.auth.models import User, Group, Permission
|
||||
|
@ -6,7 +8,11 @@ from django.core.files.uploadedfile import SimpleUploadedFile
|
|||
|
||||
from wagtail.tests.utils import unittest, WagtailTestUtils
|
||||
from wagtail.wagtailimages.models import get_image_model
|
||||
from wagtail.wagtailimages.templatetags import image_tags
|
||||
from wagtail.wagtailimages.formats import (
|
||||
Format,
|
||||
get_image_format,
|
||||
register_image_format
|
||||
)
|
||||
|
||||
from wagtail.wagtailimages.backends import get_image_backend
|
||||
from wagtail.wagtailimages.backends.pillow import PillowBackend
|
||||
|
@ -419,3 +425,49 @@ class TestImageChooserUploadView(TestCase, WagtailTestUtils):
|
|||
self.assertTemplateUsed(response, 'wagtailimages/chooser/chooser.js')
|
||||
|
||||
# TODO: Test uploading through chooser
|
||||
|
||||
|
||||
class TestFormat(TestCase):
|
||||
def setUp(self):
|
||||
# test format
|
||||
self.format = Format(
|
||||
'test name',
|
||||
'test label',
|
||||
'test classnames',
|
||||
'test filter spec'
|
||||
)
|
||||
# test image
|
||||
self.image = MagicMock()
|
||||
self.image.id = 0
|
||||
|
||||
def test_editor_attributes(self):
|
||||
result = self.format.editor_attributes(
|
||||
self.image,
|
||||
'test alt text'
|
||||
)
|
||||
self.assertEqual(result,
|
||||
'data-embedtype="image" data-id="0" data-format="test name" data-alt="test alt text" ')
|
||||
|
||||
def test_image_to_editor_html(self):
|
||||
result = self.format.image_to_editor_html(
|
||||
self.image,
|
||||
'test alt text'
|
||||
)
|
||||
self.assertRegexpMatches(
|
||||
result,
|
||||
'<img data-embedtype="image" data-id="0" data-format="test name" data-alt="test alt text" class="test classnames" src="[^"]+" width="1" height="1" alt="test alt text">',
|
||||
)
|
||||
|
||||
def test_image_to_html_no_classnames(self):
|
||||
self.format.classnames = None
|
||||
result = self.format.image_to_html(self.image, 'test alt text')
|
||||
self.assertRegexpMatches(
|
||||
result,
|
||||
'<img src="[^"]+" width="1" height="1" alt="test alt text">'
|
||||
)
|
||||
self.format.classnames = 'test classnames'
|
||||
|
||||
def test_get_image_format(self):
|
||||
register_image_format(self.format)
|
||||
result = get_image_format('test name')
|
||||
self.assertEqual(result, self.format)
|
||||
|
|
Ładowanie…
Reference in New Issue