1from django.core.exceptions import ValidationError
2from django.utils.functional import cached_property
3from django.utils.translation import gettext_lazy as _
4
5from wagtail.core import blocks
6from wagtail.embeds.format import embed_to_frontend_html
7
8
9class EmbedValue:
10    """
11    Native value of an EmbedBlock. Should, at minimum, have a 'url' property
12    and render as the embed HTML when rendered in a template.
13    NB We don't use a wagtailembeds.model.Embed object for this, because
14    we want to be able to do {% embed value.url 500 %} without
15    doing a redundant fetch of the embed at the default width.
16    """
17    def __init__(self, url, max_width=None, max_height=None):
18        self.url = url
19        self.max_width = max_width
20        self.max_height = max_height
21
22    @cached_property
23    def html(self):
24        return embed_to_frontend_html(self.url, self.max_width, self.max_height)
25
26    def __str__(self):
27        return self.html
28
29
30class EmbedBlock(blocks.URLBlock):
31    def get_default(self):
32        # Allow specifying the default for an EmbedBlock as either an EmbedValue or a string (or None).
33        if not self.meta.default:
34            return None
35        elif isinstance(self.meta.default, EmbedValue):
36            return self.meta.default
37        else:
38            # assume default has been passed as a string
39            return EmbedValue(self.meta.default, getattr(self.meta, 'max_width', None), getattr(self.meta, 'max_height', None))
40
41    def to_python(self, value):
42        # The JSON representation of an EmbedBlock's value is a URL string;
43        # this should be converted to an EmbedValue (or None).
44        if not value:
45            return None
46        else:
47            return EmbedValue(value, getattr(self.meta, 'max_width', None), getattr(self.meta, 'max_height', None))
48
49    def get_prep_value(self, value):
50        # serialisable value should be a URL string
51        if value is None:
52            return ''
53        else:
54            return value.url
55
56    def value_for_form(self, value):
57        # the value to be handled by the URLField is a plain URL string (or the empty string)
58        if value is None:
59            return ''
60        else:
61            return value.url
62
63    def value_from_form(self, value):
64        # convert the value returned from the form (a URL string) to an EmbedValue (or None)
65        if not value:
66            return None
67        else:
68            return EmbedValue(value, getattr(self.meta, 'max_width', None), getattr(self.meta, 'max_height', None))
69
70    def clean(self, value):
71        if isinstance(value, EmbedValue) and not value.html:
72            raise ValidationError(_("Cannot find an embed for this URL."))
73        return super().clean(value)
74
75    class Meta:
76        icon = "media"
77