diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4e0f5be248..32ba543ea8 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -10,6 +10,7 @@ Changelog * Added `full_url` property to image renditions (Shreyash Srivastava) * Added locale selector when choosing translatable snippets (Karl Hobley) * Added `WAGTAIL_WORKFLOW_ENABLED` setting for enabling / disabling moderation workflows globally (Matt Westcott) + * Allow specifying `max_width` and `max_height` on EmbedBlock (Petr DlouhĂ˝) * Fix: Invalid filter values for foreign key fields in the API now give an error instead of crashing (Tidjani Dia) * Fix: Ordering specified in `construct_explorer_page_queryset` hook is now taken into account again by the page explorer API (Andre Fonseca) * Fix: Deleting a page from its listing view no longer results in a 404 error (Tidjani Dia) diff --git a/docs/advanced_topics/embeds.rst b/docs/advanced_topics/embeds.rst index b6f9e3e249..5c43ca6376 100644 --- a/docs/advanced_topics/embeds.rst +++ b/docs/advanced_topics/embeds.rst @@ -33,6 +33,8 @@ nest the embed code. The :class:`~wagtail.embeds.block.EmbedBlock` block type allows embeds to be placed into a ``StreamField``. +The ``max_width`` and ``max_height`` arguments are sent to the provider when fetching the embed code. + For example: .. code-block:: python @@ -42,7 +44,7 @@ For example: class MyStreamField(blocks.StreamBlock): ... - embed = EmbedBlock() + embed = EmbedBlock(max_width=800, max_height=400) ``{% embed %}`` tag ------------------- diff --git a/docs/reference/streamfield/blocks.rst b/docs/reference/streamfield/blocks.rst index a1073bea9e..45971e7103 100644 --- a/docs/reference/streamfield/blocks.rst +++ b/docs/reference/streamfield/blocks.rst @@ -332,6 +332,8 @@ Field block types A field for the editor to enter a URL to a media item (such as a YouTube video) to appear as embedded media on the page. The following keyword arguments are accepted in addition to the standard ones: :param required: If true (the default), the field cannot be left blank. + :param max_width: The maximum width of the embed, in pixels; this will be passed to the provider when requesting the embed. + :param max_height: The maximum height of the embed, in pixels; this will be passed to the provider when requesting the embed. :param max_length: The maximum allowed length of the field. :param min_length: The minimum allowed length of the field. :param help_text: Help text to display alongside the field. diff --git a/docs/releases/2.14.rst b/docs/releases/2.14.rst index 59a69c5d47..adafcf7d2b 100644 --- a/docs/releases/2.14.rst +++ b/docs/releases/2.14.rst @@ -18,6 +18,7 @@ Other features * Added ``full_url`` property to image renditions (Shreyash Srivastava) * Added locale selector when choosing translatable snippets (Karl Hobley) * Added ``WAGTAIL_WORKFLOW_ENABLED`` setting for enabling / disabling moderation workflows globally (Matt Westcott) + * Allow specifying ``max_width`` and ``max_height`` on EmbedBlock (Petr DlouhĂ˝) Bug fixes ~~~~~~~~~ diff --git a/wagtail/embeds/blocks.py b/wagtail/embeds/blocks.py index 5a1e4d0121..8a93e66444 100644 --- a/wagtail/embeds/blocks.py +++ b/wagtail/embeds/blocks.py @@ -14,12 +14,14 @@ class EmbedValue: we want to be able to do {% embed value.url 500 %} without doing a redundant fetch of the embed at the default width. """ - def __init__(self, url): + def __init__(self, url, max_width=None, max_height=None): self.url = url + self.max_width = max_width + self.max_height = max_height @cached_property def html(self): - return embed_to_frontend_html(self.url) + return embed_to_frontend_html(self.url, self.max_width, self.max_height) def __str__(self): return self.html @@ -34,7 +36,7 @@ class EmbedBlock(blocks.URLBlock): return self.meta.default else: # assume default has been passed as a string - return EmbedValue(self.meta.default) + return EmbedValue(self.meta.default, getattr(self.meta, 'max_width', None), getattr(self.meta, 'max_height', None)) def to_python(self, value): # The JSON representation of an EmbedBlock's value is a URL string; @@ -42,7 +44,7 @@ class EmbedBlock(blocks.URLBlock): if not value: return None else: - return EmbedValue(value) + return EmbedValue(value, getattr(self.meta, 'max_width', None), getattr(self.meta, 'max_height', None)) def get_prep_value(self, value): # serialisable value should be a URL string @@ -63,7 +65,7 @@ class EmbedBlock(blocks.URLBlock): if not value: return None else: - return EmbedValue(value) + return EmbedValue(value, getattr(self.meta, 'max_width', None), getattr(self.meta, 'max_height', None)) def clean(self, value): if isinstance(value, EmbedValue) and not value.html: diff --git a/wagtail/embeds/embeds.py b/wagtail/embeds/embeds.py index c42f716b18..c9319feb31 100644 --- a/wagtail/embeds/embeds.py +++ b/wagtail/embeds/embeds.py @@ -3,13 +3,14 @@ from hashlib import md5 from django.utils.timezone import now +from ..core.utils import accepts_kwarg from .exceptions import EmbedUnsupportedProviderException from .finders import get_finders from .models import Embed -def get_embed(url, max_width=None, finder=None): - embed_hash = get_embed_hash(url, max_width) +def get_embed(url, max_width=None, max_height=None, finder=None): + embed_hash = get_embed_hash(url, max_width, max_height) # Check database try: @@ -20,14 +21,17 @@ def get_embed(url, max_width=None, finder=None): # Get/Call finder if not finder: - def finder(url, max_width=None): + def finder(url, max_width=None, max_height=None): for finder in get_finders(): if finder.accept(url): - return finder.find_embed(url, max_width=max_width) + kwargs = {} + if accepts_kwarg(finder.find_embed, 'max_height'): + kwargs['max_height'] = max_height + return finder.find_embed(url, max_width=max_width, **kwargs) raise EmbedUnsupportedProviderException - embed_dict = finder(url, max_width) + embed_dict = finder(url, max_width, max_height) # Make sure width and height are valid integers before inserting into database try: @@ -65,10 +69,13 @@ def get_embed(url, max_width=None, finder=None): return embed -def get_embed_hash(url, max_width=None): +def get_embed_hash(url, max_width=None, max_height=None): h = md5() h.update(url.encode("utf-8")) if max_width is not None: h.update(b"\n") h.update(str(max_width).encode("utf-8")) + if max_height is not None: + h.update(b"\n") + h.update(str(max_height).encode("utf-8")) return h.hexdigest() diff --git a/wagtail/embeds/finders/oembed.py b/wagtail/embeds/finders/oembed.py index 2ae14c3529..d2da0edf50 100644 --- a/wagtail/embeds/finders/oembed.py +++ b/wagtail/embeds/finders/oembed.py @@ -45,7 +45,7 @@ class OEmbedFinder(EmbedFinder): def accept(self, url): return self._get_endpoint(url) is not None - def find_embed(self, url, max_width=None): + def find_embed(self, url, max_width=None, max_height=None): # Find provider endpoint = self._get_endpoint(url) if endpoint is None: @@ -57,6 +57,8 @@ class OEmbedFinder(EmbedFinder): params['format'] = 'json' if max_width: params['maxwidth'] = max_width + if max_height: + params['maxheight'] = max_height # Perform request request = Request(endpoint + '?' + urlencode(params)) diff --git a/wagtail/embeds/format.py b/wagtail/embeds/format.py index 961f545b7d..1edc5289a9 100644 --- a/wagtail/embeds/format.py +++ b/wagtail/embeds/format.py @@ -4,9 +4,9 @@ from wagtail.embeds import embeds from wagtail.embeds.exceptions import EmbedException -def embed_to_frontend_html(url): +def embed_to_frontend_html(url, max_width=None, max_height=None): try: - embed = embeds.get_embed(url) + embed = embeds.get_embed(url, max_width, max_height) # Render template return render_to_string('wagtailembeds/embed_frontend.html', { diff --git a/wagtail/embeds/tests/test_embeds.py b/wagtail/embeds/tests/test_embeds.py index a5a044248e..6d9459d5a2 100644 --- a/wagtail/embeds/tests/test_embeds.py +++ b/wagtail/embeds/tests/test_embeds.py @@ -122,7 +122,7 @@ class TestEmbeds(TestCase): def setUp(self): self.hit_count = 0 - def dummy_finder(self, url, max_width=None): + def dummy_finder(self, url, max_width=None, max_height=None): # Up hit count self.hit_count += 1 @@ -175,7 +175,7 @@ class TestEmbeds(TestCase): self.assertFalse(embed.is_responsive) self.assertIsNone(embed.cache_until) - def dummy_cache_until_finder(self, url, max_width=None): + def dummy_cache_until_finder(self, url, max_width=None, max_height=None): # Up hit count self.hit_count += 1 @@ -210,7 +210,7 @@ class TestEmbeds(TestCase): self.assertEqual(embed, embed_3) self.assertEqual(embed_3.cache_until, future_dt) - def dummy_finder_invalid_width(self, url, max_width=None): + def dummy_finder_invalid_width(self, url, max_width=None, max_height=None): # Return a record with an invalid width return { 'title': "Test: " + url, @@ -228,7 +228,7 @@ class TestEmbeds(TestCase): self.assertEqual(embed.width, None) def test_no_html(self): - def no_html_finder(url, max_width=None): + def no_html_finder(url, max_width=None, max_height=None): """ A finder which returns everything but HTML """ @@ -710,7 +710,7 @@ class TestEmbedBlock(TestCase): self.assertIn('
1 2 3 4
') self.assertIn('test html', result) - get_embed.assert_called_with('https://www.youtube.com/watch?v=O7D-1RG-VRk&t=25') + get_embed.assert_called_with('https://www.youtube.com/watch?v=O7D-1RG-VRk&t=25', None, None)