Files
old-saburly-wagtail-web/env/lib/python3.10/site-packages/wagtail/admin/ui/side_panels.py
2024-08-27 20:33:44 +02:00

358 lines
13 KiB
Python

from django.urls import reverse
from django.utils.text import capfirst
from django.utils.translation import gettext_lazy, ngettext
from wagtail import hooks
from wagtail.admin.ui.components import Component
from wagtail.admin.userbar import AccessibilityItem
from wagtail.models import DraftStateMixin, LockableMixin, Page, ReferenceIndex
class BaseSidePanel(Component):
class SidePanelToggle(Component):
template_name = "wagtailadmin/shared/side_panel_toggle.html"
aria_label = ""
icon_name = ""
has_counter = True
counter_classname = ""
keyboard_shortcut = None
def __init__(self, panel):
self.panel = panel
def get_context_data(self, parent_context):
# Inherit classes from fragments defined in slim_header.html
inherit = {
"nav_icon_button_classes",
"nav_icon_classes",
"nav_icon_counter_classes",
}
context = {key: parent_context.get(key) for key in inherit}
context["toggle"] = self
context["panel"] = self.panel
context["count"] = 0
return context
def __init__(self, object, request):
self.object = object
self.request = request
self.model = type(self.object)
self.toggle = self.SidePanelToggle(panel=self)
def get_context_data(self, parent_context):
context = {"panel": self, "object": self.object, "request": self.request}
if issubclass(self.model, Page):
context["page"] = self.object
return context
class StatusSidePanel(BaseSidePanel):
class SidePanelToggle(BaseSidePanel.SidePanelToggle):
aria_label = gettext_lazy("Toggle status")
icon_name = "info-circle"
counter_classname = "w-bg-critical-200"
def get_context_data(self, parent_context):
context = super().get_context_data(parent_context)
form = parent_context.get("form")
context["count"] = form and len(
form.errors.keys() & {"go_live_at", "expire_at"}
)
return context
name = "status"
title = gettext_lazy("Status")
template_name = "wagtailadmin/shared/side_panels/status.html"
order = 100
def __init__(
self,
*args,
show_schedule_publishing_toggle=None,
live_object=None,
scheduled_object=None,
locale=None,
translations=None,
usage_url=None,
history_url=None,
last_updated_info=None,
**kwargs,
):
super().__init__(*args, **kwargs)
self.show_schedule_publishing_toggle = show_schedule_publishing_toggle
self.live_object = live_object
self.scheduled_object = scheduled_object
self.locale = locale
self.translations = translations
self.usage_url = usage_url
self.history_url = history_url
self.last_updated_info = last_updated_info
self.locking_enabled = isinstance(self.object, LockableMixin)
def get_status_templates(self, context):
templates = ["wagtailadmin/shared/side_panels/includes/status/workflow.html"]
if self.locale:
templates.append(
"wagtailadmin/shared/side_panels/includes/status/locale.html"
)
if self.object.pk:
if self.locking_enabled:
templates.append(
"wagtailadmin/shared/side_panels/includes/status/locked.html"
)
if self.usage_url:
templates.append(
"wagtailadmin/shared/side_panels/includes/status/usage.html"
)
return templates
def get_scheduled_publishing_context(self, parent_context):
if not isinstance(self.object, DraftStateMixin):
return {"draftstate_enabled": False}
context = {
# Used for hiding the info completely if the model doesn't extend DraftStateMixin
"draftstate_enabled": True,
# Show error message if any of the scheduled publishing fields has errors
"schedule_has_errors": False,
# The dialog toggle can be hidden (e.g. if PublishingPanel is not present)
# but the scheduled publishing info should still be shown
"show_schedule_publishing_toggle": self.show_schedule_publishing_toggle,
# These are the dates that show up with the unticked calendar icon,
# aka "draft schedule"
"draft_go_live_at": None,
"draft_expire_at": None,
# These are the dates that show up with the ticked calendar icon,
# aka "active schedule"
"scheduled_go_live_at": None,
"scheduled_expire_at": None,
# This is for an edge case where the live object already has an
# expire_at, which can still take effect if the active schedule's
# go_live_at is later than that
"live_expire_at": None,
}
# Reuse logic from the toggle to get the count of errors
if self.toggle.get_context_data(parent_context)["count"]:
context["schedule_has_errors"] = True
# Only consider draft schedule if the object hasn't been created
# or if there are unpublished changes
if not self.object.pk or self.object.has_unpublished_changes:
context["draft_go_live_at"] = self.object.go_live_at
context["draft_expire_at"] = self.object.expire_at
# Get active schedule from the scheduled revision's object (if any)
if self.scheduled_object:
context["scheduled_go_live_at"] = self.scheduled_object.go_live_at
context["scheduled_expire_at"] = self.scheduled_object.expire_at
# Ignore draft schedule if it's the same as the active schedule
if context["draft_go_live_at"] == context["scheduled_go_live_at"]:
context["draft_go_live_at"] = None
if context["draft_expire_at"] == context["scheduled_expire_at"]:
context["draft_expire_at"] = None
# The live object can still have its own active expiry date
# that's separate from the active schedule
if (
self.live_object
and self.live_object.expire_at
and not self.live_object.expired
):
context["live_expire_at"] = self.live_object.expire_at
# Ignore the live object's expiry date if the active schedule has
# an earlier go_live_at, as the active schedule's expiry date will
# override the live object's expiry date when the draft is published
if (
context["scheduled_go_live_at"]
and context["scheduled_go_live_at"] < context["live_expire_at"]
):
context["live_expire_at"] = None
# Only show the box for the live object expire_at edge case
# if it passes the checks above
context["has_live_publishing_schedule"] = bool(context["live_expire_at"])
# Only show the main scheduled publishing box if it has at least one of
# the draft/active schedule dates after passing the checks above
context["has_draft_publishing_schedule"] = any(
(
context["scheduled_go_live_at"],
context["scheduled_expire_at"],
context["draft_go_live_at"],
context["draft_expire_at"],
)
)
return context
def get_lock_context(self, parent_context):
self.lock = None
lock_context = {}
if self.locking_enabled:
self.lock = self.object.get_lock()
if self.lock:
lock_context = self.lock.get_context_for_user(
self.request.user, parent_context
)
return {
"lock": self.lock,
"user_can_lock": parent_context.get("user_can_lock"),
"user_can_unlock": parent_context.get("user_can_unlock"),
"lock_context": lock_context,
}
def get_usage_context(self):
return {
"usage_count": ReferenceIndex.get_grouped_references_to(
self.object
).count(),
"usage_url": self.usage_url,
}
def get_context_data(self, parent_context):
context = super().get_context_data(parent_context)
context["locale"] = self.locale
context["translations"] = self.translations
if self.translations:
context["translations_total"] = len(self.translations) + 1
context["model_name"] = capfirst(self.model._meta.verbose_name)
context["base_model_name"] = context["model_name"]
context["history_url"] = self.history_url
context["status_templates"] = self.get_status_templates(context)
context["last_updated_info"] = self.last_updated_info
context.update(self.get_scheduled_publishing_context(parent_context))
context.update(self.get_lock_context(parent_context))
if self.object.pk and self.usage_url:
context.update(self.get_usage_context())
return context
class PageStatusSidePanel(StatusSidePanel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.object.pk:
self.usage_url = reverse("wagtailadmin_pages:usage", args=(self.object.pk,))
permissions = self.object.permissions_for_user(self.request.user)
if permissions.can_view_revisions():
self.history_url = reverse(
"wagtailadmin_pages:history",
args=(self.object.pk,),
)
def get_status_templates(self, context):
templates = super().get_status_templates(context)
templates.insert(
-1, "wagtailadmin/shared/side_panels/includes/status/privacy.html"
)
return templates
def get_usage_context(self):
context = super().get_usage_context()
context["usage_url_text"] = ngettext(
"Referenced %(count)s time",
"Referenced %(count)s times",
context["usage_count"],
) % {"count": context["usage_count"]}
return context
def get_context_data(self, parent_context):
context = super().get_context_data(parent_context)
page = self.object
if page.id:
context.update(
{
"workflow_history_url": reverse(
"wagtailadmin_pages:workflow_history", args=(page.id,)
),
"revisions_compare_url_name": "wagtailadmin_pages:revisions_compare",
"lock_url": reverse("wagtailadmin_pages:lock", args=(page.id,)),
"unlock_url": reverse("wagtailadmin_pages:unlock", args=(page.id,)),
}
)
context.update(
{
"model_name": self.model.get_verbose_name(),
"base_model_name": Page._meta.verbose_name,
"model_description": self.model.get_page_description(),
"status_templates": self.get_status_templates(context),
}
)
return context
class CommentsSidePanel(BaseSidePanel):
class SidePanelToggle(BaseSidePanel.SidePanelToggle):
aria_label = gettext_lazy("Toggle comments")
icon_name = "comment"
name = "comments"
title = gettext_lazy("Comments")
template_name = "wagtailadmin/shared/side_panels/comments.html"
order = 300
def get_context_data(self, parent_context):
context = super().get_context_data(parent_context)
context["form"] = parent_context.get("form")
return context
class ChecksSidePanel(BaseSidePanel):
class SidePanelToggle(BaseSidePanel.SidePanelToggle):
aria_label = gettext_lazy("Toggle checks")
icon_name = "glasses"
name = "checks"
title = gettext_lazy("Checks")
template_name = "wagtailadmin/shared/side_panels/checks.html"
order = 350
def get_axe_configuration(self):
# Retrieve the Axe configuration from the userbar.
userbar_items = [AccessibilityItem()]
for fn in hooks.get_hooks("construct_wagtail_userbar"):
fn(self.request, userbar_items)
for item in userbar_items:
if isinstance(item, AccessibilityItem):
return item.get_axe_configuration(self.request)
def get_context_data(self, parent_context):
context = super().get_context_data(parent_context)
context["axe_configuration"] = self.get_axe_configuration()
return context
class PreviewSidePanel(BaseSidePanel):
class SidePanelToggle(BaseSidePanel.SidePanelToggle):
aria_label = gettext_lazy("Toggle preview")
icon_name = "mobile-alt"
has_counter = False
keyboard_shortcut = "mod+p"
name = "preview"
title = gettext_lazy("Preview")
template_name = "wagtailadmin/shared/side_panels/preview.html"
order = 400
def __init__(self, object, request, *, preview_url):
super().__init__(object, request)
self.preview_url = preview_url
def get_context_data(self, parent_context):
context = super().get_context_data(parent_context)
context["preview_url"] = self.preview_url
context["has_multiple_modes"] = len(self.object.preview_modes) > 1
return context