|
@@ -8,6 +8,8 @@ from django.contrib.admin.utils import quote
|
|
|
from django.contrib.humanize.templatetags.humanize import intcomma, naturaltime
|
|
|
from django.contrib.messages.constants import DEFAULT_TAGS as MESSAGE_TAGS
|
|
|
from django.shortcuts import resolve_url as resolve_url_func
|
|
|
+from django.template import Context
|
|
|
+from django.template.base import token_kwargs
|
|
|
from django.template.defaultfilters import stringfilter
|
|
|
from django.templatetags.static import static
|
|
|
from django.urls import reverse
|
|
@@ -859,61 +861,147 @@ def component(context, obj, fallback_render_method=False):
|
|
|
return obj.render_html(context)
|
|
|
|
|
|
|
|
|
-@register.inclusion_tag("wagtailadmin/shared/dialog/dialog.html")
|
|
|
-def dialog(
|
|
|
- id,
|
|
|
- title,
|
|
|
- icon_name=None,
|
|
|
- subtitle=None,
|
|
|
- message_status=None,
|
|
|
- message_heading=None,
|
|
|
- message_description=None,
|
|
|
-):
|
|
|
+class FragmentNode(template.Node):
|
|
|
+ def __init__(self, nodelist, target_var):
|
|
|
+ self.nodelist = nodelist
|
|
|
+ self.target_var = target_var
|
|
|
+
|
|
|
+ def render(self, context):
|
|
|
+ fragment = self.nodelist.render(context) if self.nodelist else ""
|
|
|
+ context[self.target_var] = fragment
|
|
|
+ return ""
|
|
|
+
|
|
|
+
|
|
|
+@register.tag(name="fragment")
|
|
|
+def fragment(parser, token):
|
|
|
"""
|
|
|
- Dialog tag - to be used with its corresponding {% enddialog %} tag with dialog content markup nested between
|
|
|
+ Store a template fragment as a variable.
|
|
|
+
|
|
|
+ Usage:
|
|
|
+ {% fragment as header_title %}
|
|
|
+ {% blocktrans trimmed %}Welcome to the {{ site_name }} Wagtail CMS{% endblocktrans %}
|
|
|
+ {% fragment %}
|
|
|
+
|
|
|
+ Copy-paste of slippers’ fragment template tag.
|
|
|
+ See https://github.com/mixxorz/slippers/blob/254c720e6bb02eb46ae07d104863fce41d4d3164/slippers/templatetags/slippers.py#L173.
|
|
|
"""
|
|
|
- if not title:
|
|
|
- raise ValueError("You must supply a title")
|
|
|
- if not id:
|
|
|
- raise ValueError("You must supply an id")
|
|
|
+ error_message = "The syntax for fragment is {% fragment as variable_name %}"
|
|
|
|
|
|
- # Used for determining which icon the message will use
|
|
|
- message_status_type = {
|
|
|
- "info": {
|
|
|
- "message_icon_name": "info-circle",
|
|
|
- },
|
|
|
- "warning": {
|
|
|
- "message_icon_name": "warning",
|
|
|
- },
|
|
|
- "critical": {
|
|
|
- "message_icon_name": "warning",
|
|
|
- },
|
|
|
- "success": {
|
|
|
- "message_icon_name": "circle-check",
|
|
|
- },
|
|
|
- }
|
|
|
+ try:
|
|
|
+ tag_name, _, target_var = token.split_contents()
|
|
|
+ nodelist = parser.parse(("endfragment",))
|
|
|
+ parser.delete_first_token()
|
|
|
+ except ValueError:
|
|
|
+ if settings.DEBUG:
|
|
|
+ raise template.TemplateSyntaxError(error_message)
|
|
|
+ return ""
|
|
|
|
|
|
- context = {
|
|
|
- "id": id,
|
|
|
- "title": title,
|
|
|
- "icon_name": icon_name,
|
|
|
- "subtitle": subtitle,
|
|
|
- "message_heading": message_heading,
|
|
|
- "message_description": message_description,
|
|
|
- "message_status": message_status,
|
|
|
- }
|
|
|
+ return FragmentNode(nodelist, target_var)
|
|
|
+
|
|
|
+
|
|
|
+class BlockInclusionNode(template.Node):
|
|
|
+ """
|
|
|
+ Create template-driven tags like Django’s inclusion_tag / InclusionNode, but for block-level tags.
|
|
|
+
|
|
|
+ Usage:
|
|
|
+ {% my_tag status="test" label="Alert" %}
|
|
|
+ Proceed with caution.
|
|
|
+ {% endmy_tag %}
|
|
|
+
|
|
|
+ Within `my_tag`’s template, the template fragment will be accessible as the {{ children }} context variable.
|
|
|
+
|
|
|
+ The output can also be stored as a variable in the parent context:
|
|
|
+
|
|
|
+ {% my_tag status="test" label="Alert" as my_variable %}
|
|
|
+ Proceed with caution.
|
|
|
+ {% endmy_tag %}
|
|
|
+
|
|
|
+ Inspired by slippers’ Component Node.
|
|
|
+ See https://github.com/mixxorz/slippers/blob/254c720e6bb02eb46ae07d104863fce41d4d3164/slippers/templatetags/slippers.py#L47.
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self, nodelist, template, extra_context, target_var=None):
|
|
|
+ self.nodelist = nodelist
|
|
|
+ self.template = template
|
|
|
+ self.extra_context = extra_context
|
|
|
+ self.target_var = target_var
|
|
|
+
|
|
|
+ def get_context_data(self, parent_context):
|
|
|
+ return parent_context
|
|
|
+
|
|
|
+ def render(self, context):
|
|
|
+ children = self.nodelist.render(context) if self.nodelist else ""
|
|
|
+
|
|
|
+ values = {
|
|
|
+ # Resolve the tag’s parameters within the current context.
|
|
|
+ key: value.resolve(context)
|
|
|
+ for key, value in self.extra_context.items()
|
|
|
+ }
|
|
|
+
|
|
|
+ t = context.template.engine.get_template(self.template)
|
|
|
+ # Add the `children` variable in the rendered template’s context.
|
|
|
+ context_data = self.get_context_data({**values, "children": children})
|
|
|
+ output = t.render(Context(context_data, autoescape=context.autoescape))
|
|
|
+
|
|
|
+ if self.target_var:
|
|
|
+ context[self.target_var] = output
|
|
|
+ return ""
|
|
|
+
|
|
|
+ return output
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def handle(cls, parser, token):
|
|
|
+ tag_name, *remaining_bits = token.split_contents()
|
|
|
+
|
|
|
+ nodelist = parser.parse((f"end{tag_name}",))
|
|
|
+ parser.delete_first_token()
|
|
|
+
|
|
|
+ extra_context = token_kwargs(remaining_bits, parser)
|
|
|
+
|
|
|
+ # Allow component fragment to be assigned to a variable
|
|
|
+ target_var = None
|
|
|
+ if len(remaining_bits) >= 2 and remaining_bits[-2] == "as":
|
|
|
+ target_var = remaining_bits[-1]
|
|
|
+
|
|
|
+ return cls(nodelist, cls.template, extra_context, target_var)
|
|
|
+
|
|
|
+
|
|
|
+class DialogNode(BlockInclusionNode):
|
|
|
+ template = "wagtailadmin/shared/dialog/dialog.html"
|
|
|
+
|
|
|
+ def get_context_data(self, parent_context):
|
|
|
+ context = super().get_context_data(parent_context)
|
|
|
+
|
|
|
+ if "title" not in context:
|
|
|
+ raise TypeError("You must supply a title")
|
|
|
+ if "id" not in context:
|
|
|
+ raise TypeError("You must supply an id")
|
|
|
+
|
|
|
+ # Used for determining which icon the message will use
|
|
|
+ message_icon_name = {
|
|
|
+ "info": "info-circle",
|
|
|
+ "warning": "warning",
|
|
|
+ "critical": "warning",
|
|
|
+ "success": "circle-check",
|
|
|
+ }
|
|
|
+
|
|
|
+ message_status = context.get("message_status")
|
|
|
+
|
|
|
+ # If there is a message status then determine which icon to use.
|
|
|
+ if message_status:
|
|
|
+ context["message_icon_name"] = message_icon_name[message_status]
|
|
|
+
|
|
|
+ return context
|
|
|
+
|
|
|
+
|
|
|
+register.tag("dialog", DialogNode.handle)
|
|
|
|
|
|
- # If there is a message status then add the context for that message type
|
|
|
- if message_status:
|
|
|
- context.update(**message_status_type[message_status])
|
|
|
|
|
|
- return context
|
|
|
+class HelpBlockNode(BlockInclusionNode):
|
|
|
+ template = "wagtailadmin/shared/help_block.html"
|
|
|
|
|
|
|
|
|
-# Closing tag for dialog tag {% enddialog %}
|
|
|
-@register.inclusion_tag("wagtailadmin/shared/dialog/end_dialog.html")
|
|
|
-def enddialog():
|
|
|
- return
|
|
|
+register.tag("help_block", HelpBlockNode.handle)
|
|
|
|
|
|
|
|
|
# Button used to open dialogs
|