Initial commit
This commit is contained in:
9
env/lib/python3.10/site-packages/wagtail/admin/widgets/__init__.py
vendored
Normal file
9
env/lib/python3.10/site-packages/wagtail/admin/widgets/__init__.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
from wagtail.admin.widgets.auto_height_text import * # NOQA: F403
|
||||
from wagtail.admin.widgets.boolean_radio_select import * # NOQA: F403
|
||||
from wagtail.admin.widgets.button import * # NOQA: F403
|
||||
from wagtail.admin.widgets.chooser import * # NOQA: F403
|
||||
from wagtail.admin.widgets.datetime import * # NOQA: F403
|
||||
from wagtail.admin.widgets.filtered_select import * # NOQA: F403
|
||||
from wagtail.admin.widgets.slug import * # NOQA: F403
|
||||
from wagtail.admin.widgets.switch import * # NOQA: F403
|
||||
from wagtail.admin.widgets.tags import * # NOQA: F403
|
||||
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/auto_height_text.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/auto_height_text.cpython-310.pyc
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/button.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/button.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/chooser.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/chooser.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/datetime.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/datetime.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/filtered_select.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/filtered_select.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/slug.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/slug.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/switch.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/switch.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/tags.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/tags.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/workflows.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/admin/widgets/__pycache__/workflows.cpython-310.pyc
vendored
Normal file
Binary file not shown.
20
env/lib/python3.10/site-packages/wagtail/admin/widgets/auto_height_text.py
vendored
Normal file
20
env/lib/python3.10/site-packages/wagtail/admin/widgets/auto_height_text.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
from django.forms import widgets
|
||||
|
||||
|
||||
class AdminAutoHeightTextInput(widgets.Textarea):
|
||||
def __init__(self, attrs=None):
|
||||
# Use more appropriate rows default, given autosize will alter this anyway
|
||||
default_attrs = {
|
||||
"rows": 1,
|
||||
"data-controller": "w-autosize",
|
||||
}
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
|
||||
# add a w-field__autosize classname
|
||||
try:
|
||||
default_attrs["class"] += " w-field__autosize"
|
||||
except KeyError:
|
||||
default_attrs["class"] = "w-field__autosize"
|
||||
|
||||
super().__init__(default_attrs)
|
||||
40
env/lib/python3.10/site-packages/wagtail/admin/widgets/boolean_radio_select.py
vendored
Normal file
40
env/lib/python3.10/site-packages/wagtail/admin/widgets/boolean_radio_select.py
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class BooleanRadioSelect(forms.RadioSelect):
|
||||
"""
|
||||
A radio select widget for boolean fields. Displays as three options; "All", "Yes" and "No".
|
||||
"""
|
||||
|
||||
input_type = "radio"
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
choices = (
|
||||
("", _("All")),
|
||||
("true", _("Yes")),
|
||||
("false", _("No")),
|
||||
)
|
||||
super().__init__(attrs, choices)
|
||||
|
||||
def format_value(self, value):
|
||||
try:
|
||||
return {
|
||||
True: ["true"],
|
||||
False: ["false"],
|
||||
"true": ["true"],
|
||||
"false": ["false"],
|
||||
}[value]
|
||||
except KeyError:
|
||||
return ""
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
value = data.get(name)
|
||||
return {
|
||||
True: True,
|
||||
"True": True,
|
||||
"False": False,
|
||||
False: False,
|
||||
"true": True,
|
||||
"false": False,
|
||||
}.get(value)
|
||||
252
env/lib/python3.10/site-packages/wagtail/admin/widgets/button.py
vendored
Normal file
252
env/lib/python3.10/site-packages/wagtail/admin/widgets/button.py
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
from warnings import warn
|
||||
|
||||
from django.forms.utils import flatatt
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import urlencode
|
||||
|
||||
from wagtail import hooks
|
||||
from wagtail.admin.ui.components import Component
|
||||
from wagtail.coreutils import accepts_kwarg
|
||||
from wagtail.utils.deprecation import RemovedInWagtail70Warning
|
||||
|
||||
|
||||
class Button(Component):
|
||||
template_name = "wagtailadmin/shared/button.html"
|
||||
show = True
|
||||
label = ""
|
||||
icon_name = None
|
||||
url = None
|
||||
attrs = {}
|
||||
|
||||
def __init__(
|
||||
self, label="", url=None, classname="", icon_name=None, attrs={}, priority=1000
|
||||
):
|
||||
if label:
|
||||
self.label = label
|
||||
|
||||
if url:
|
||||
self.url = url
|
||||
|
||||
self.classname = classname
|
||||
|
||||
if icon_name:
|
||||
self.icon_name = icon_name
|
||||
|
||||
self.attrs = self.attrs.copy()
|
||||
self.attrs.update(attrs)
|
||||
|
||||
# if a 'title' attribute has been passed, correct that to aria-label
|
||||
# as that's what will be picked up in renderings that don't use button.render
|
||||
# directly (e.g. _dropdown_items.html)
|
||||
if "title" in self.attrs and "aria-label" not in self.attrs:
|
||||
self.attrs["aria-label"] = self.attrs.pop("title")
|
||||
self.priority = priority
|
||||
|
||||
def get_context_data(self, parent_context):
|
||||
return {"button": self}
|
||||
|
||||
@property
|
||||
def base_attrs_string(self):
|
||||
# The set of attributes to be included on all renderings of
|
||||
# the button, as a string. Does not include the href or class
|
||||
# attributes (since the classnames intended for the button styling
|
||||
# should not be applied to dropdown items)
|
||||
return flatatt(self.attrs)
|
||||
|
||||
@property
|
||||
def aria_label(self):
|
||||
return self.attrs.get("aria-label", "")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Button: {self.label}>"
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, Button):
|
||||
return NotImplemented
|
||||
return (self.priority, self.label) < (other.priority, other.label)
|
||||
|
||||
def __le__(self, other):
|
||||
if not isinstance(other, Button):
|
||||
return NotImplemented
|
||||
return (self.priority, self.label) <= (other.priority, other.label)
|
||||
|
||||
def __gt__(self, other):
|
||||
if not isinstance(other, Button):
|
||||
return NotImplemented
|
||||
return (self.priority, self.label) > (other.priority, other.label)
|
||||
|
||||
def __ge__(self, other):
|
||||
if not isinstance(other, Button):
|
||||
return NotImplemented
|
||||
return (self.priority, self.label) >= (other.priority, other.label)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Button):
|
||||
return NotImplemented
|
||||
return (
|
||||
self.label == other.label
|
||||
and self.url == other.url
|
||||
and self.classname == other.classname
|
||||
and self.attrs == other.attrs
|
||||
and self.priority == other.priority
|
||||
)
|
||||
|
||||
|
||||
class HeaderButton(Button):
|
||||
"""An icon-only button to be displayed after the breadcrumbs in the header."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
label="",
|
||||
url=None,
|
||||
classname="",
|
||||
icon_name=None,
|
||||
attrs={},
|
||||
icon_only=False,
|
||||
**kwargs,
|
||||
):
|
||||
classname = f"{classname} w-header-button button".strip()
|
||||
attrs = attrs.copy()
|
||||
if icon_only:
|
||||
controller = f"{attrs.get('data-controller', '')} w-tooltip".strip()
|
||||
attrs["data-controller"] = controller
|
||||
attrs["data-w-tooltip-content-value"] = label
|
||||
attrs["aria-label"] = label
|
||||
label = ""
|
||||
|
||||
super().__init__(
|
||||
label=label,
|
||||
url=url,
|
||||
classname=classname,
|
||||
icon_name=icon_name,
|
||||
attrs=attrs,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
# Base class for all listing buttons
|
||||
# This is also used by SnippetListingButton defined in wagtail.snippets.widgets
|
||||
class ListingButton(Button):
|
||||
def __init__(self, label="", url=None, classname="", **kwargs):
|
||||
classname = f"{classname} button button-small button-secondary".strip()
|
||||
super().__init__(label=label, url=url, classname=classname, **kwargs)
|
||||
|
||||
|
||||
class PageListingButton(ListingButton):
|
||||
aria_label_format = None
|
||||
url_name = None
|
||||
|
||||
def __init__(self, *args, page=None, next_url=None, attrs={}, user=None, **kwargs):
|
||||
self.page = page
|
||||
self.user = user
|
||||
self.next_url = next_url
|
||||
|
||||
attrs = attrs.copy()
|
||||
if (
|
||||
self.page
|
||||
and self.aria_label_format is not None
|
||||
and "aria-label" not in attrs
|
||||
):
|
||||
attrs["aria-label"] = self.aria_label_format % {
|
||||
"title": self.page.get_admin_display_title()
|
||||
}
|
||||
super().__init__(*args, attrs=attrs, **kwargs)
|
||||
|
||||
@cached_property
|
||||
def url(self):
|
||||
if self.page and self.url_name is not None:
|
||||
url = reverse(self.url_name, args=[self.page.id])
|
||||
if self.next_url:
|
||||
url += "?" + urlencode({"next": self.next_url})
|
||||
return url
|
||||
|
||||
@cached_property
|
||||
def page_perms(self):
|
||||
if self.page:
|
||||
return self.page.permissions_for_user(self.user)
|
||||
|
||||
|
||||
class BaseDropdownMenuButton(Button):
|
||||
template_name = "wagtailadmin/pages/listing/_button_with_dropdown.html"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, url=None, **kwargs)
|
||||
|
||||
@cached_property
|
||||
def dropdown_buttons(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_context_data(self, parent_context):
|
||||
return {
|
||||
"buttons": sorted(self.dropdown_buttons),
|
||||
"label": self.label,
|
||||
"title": self.aria_label,
|
||||
"toggle_classname": self.classname,
|
||||
"icon_name": self.icon_name,
|
||||
}
|
||||
|
||||
|
||||
class ButtonWithDropdown(BaseDropdownMenuButton):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.dropdown_buttons = kwargs.pop("buttons", [])
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class ButtonWithDropdownFromHook(BaseDropdownMenuButton):
|
||||
def __init__(
|
||||
self,
|
||||
label,
|
||||
hook_name,
|
||||
page,
|
||||
user=None,
|
||||
page_perms=None,
|
||||
next_url=None,
|
||||
**kwargs,
|
||||
):
|
||||
self.hook_name = hook_name
|
||||
self.page = page
|
||||
|
||||
if user is None:
|
||||
if page_perms is not None:
|
||||
warn(
|
||||
"ButtonWithDropdownFromHook should be passed a `user` argument instead of `page_perms`",
|
||||
category=RemovedInWagtail70Warning,
|
||||
stacklevel=2,
|
||||
)
|
||||
self.user = page_perms.user
|
||||
else:
|
||||
raise TypeError("ButtonWithDropdownFromHook requires a `user` argument")
|
||||
else:
|
||||
self.user = user
|
||||
|
||||
self.next_url = next_url
|
||||
|
||||
super().__init__(label, **kwargs)
|
||||
|
||||
@property
|
||||
def show(self):
|
||||
return bool(self.dropdown_buttons)
|
||||
|
||||
@cached_property
|
||||
def dropdown_buttons(self):
|
||||
button_hooks = hooks.get_hooks(self.hook_name)
|
||||
|
||||
buttons = []
|
||||
for hook in button_hooks:
|
||||
if accepts_kwarg(hook, "user"):
|
||||
buttons.extend(
|
||||
hook(page=self.page, user=self.user, next_url=self.next_url)
|
||||
)
|
||||
else:
|
||||
# old-style hook that accepts page_perms instead of user
|
||||
warn(
|
||||
f"`{self.hook_name}` hook functions should accept a `user` argument instead of `page_perms` -"
|
||||
f" {hook.__module__}.{hook.__name__} needs to be updated",
|
||||
category=RemovedInWagtail70Warning,
|
||||
)
|
||||
page_perms = self.page.permissions_for_user(self.user)
|
||||
buttons.extend(hook(self.page, page_perms, self.next_url))
|
||||
|
||||
buttons = [b for b in buttons if b.show]
|
||||
return buttons
|
||||
350
env/lib/python3.10/site-packages/wagtail/admin/widgets/chooser.py
vendored
Normal file
350
env/lib/python3.10/site-packages/wagtail/admin/widgets/chooser.py
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
import json
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.forms import widgets
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from wagtail.admin.admin_url_finder import AdminURLFinder
|
||||
from wagtail.admin.staticfiles import versioned_static
|
||||
from wagtail.coreutils import resolve_model_string
|
||||
from wagtail.models import Page
|
||||
from wagtail.telepath import register
|
||||
from wagtail.widget_adapters import WidgetAdapter
|
||||
|
||||
|
||||
class BaseChooser(widgets.Input):
|
||||
choose_one_text = _("Choose an item")
|
||||
choose_another_text = _("Choose another item")
|
||||
clear_choice_text = _("Clear choice")
|
||||
link_to_chosen_text = _("Edit this item")
|
||||
show_edit_link = True
|
||||
show_clear_link = True
|
||||
template_name = "wagtailadmin/widgets/chooser.html"
|
||||
display_title_key = (
|
||||
"title" # key to use for the display title within the value data dict
|
||||
)
|
||||
icon = None
|
||||
classname = None
|
||||
model = None
|
||||
js_constructor = "Chooser"
|
||||
linked_fields = {}
|
||||
|
||||
# when looping over form fields, this one should appear in visible_fields, not hidden_fields
|
||||
# despite the underlying input being type="hidden"
|
||||
input_type = "hidden"
|
||||
is_hidden = False
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# allow attributes to be overridden by kwargs
|
||||
for var in [
|
||||
"choose_one_text",
|
||||
"choose_another_text",
|
||||
"clear_choice_text",
|
||||
"link_to_chosen_text",
|
||||
"show_edit_link",
|
||||
"show_clear_link",
|
||||
"icon",
|
||||
"linked_fields",
|
||||
]:
|
||||
if var in kwargs:
|
||||
setattr(self, var, kwargs.pop(var))
|
||||
super().__init__(**kwargs)
|
||||
|
||||
@cached_property
|
||||
def model_class(self):
|
||||
return resolve_model_string(self.model)
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
# treat the empty string as None
|
||||
result = super().value_from_datadict(data, files, name)
|
||||
if result == "":
|
||||
return None
|
||||
else:
|
||||
return result
|
||||
|
||||
def get_hidden_input_context(self, name, value, attrs):
|
||||
"""
|
||||
Return the context variables required to render the underlying hidden input element
|
||||
"""
|
||||
return super().get_context(name, value, attrs)
|
||||
|
||||
def render_hidden_input(self, name, value, attrs):
|
||||
"""Render the HTML for the underlying hidden input element"""
|
||||
return self._render(
|
||||
"django/forms/widgets/input.html",
|
||||
self.get_hidden_input_context(name, value, attrs),
|
||||
)
|
||||
|
||||
def get_chooser_modal_url(self):
|
||||
return reverse(self.chooser_modal_url_name)
|
||||
|
||||
def get_context(self, name, value_data, attrs):
|
||||
original_field_html = self.render_hidden_input(
|
||||
name, value_data.get("id"), attrs
|
||||
)
|
||||
return {
|
||||
"widget": self,
|
||||
"original_field_html": original_field_html,
|
||||
"attrs": attrs,
|
||||
"value": bool(
|
||||
value_data
|
||||
), # only used by chooser.html to identify blank values
|
||||
"edit_url": value_data.get("edit_url", ""),
|
||||
"display_title": value_data.get(self.display_title_key, ""),
|
||||
"chooser_url": self.get_chooser_modal_url(),
|
||||
"icon": self.icon,
|
||||
"classname": self.classname,
|
||||
}
|
||||
|
||||
def render_html(self, name, value_data, attrs):
|
||||
return render_to_string(
|
||||
self.template_name,
|
||||
self.get_context(name, value_data or {}, attrs),
|
||||
)
|
||||
|
||||
def get_instance(self, value):
|
||||
"""
|
||||
Given a value passed to this widget for rendering (which may be None, an id, or a model
|
||||
instance), return a model instance or None
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
elif isinstance(value, self.model_class):
|
||||
return value
|
||||
else: # assume instance ID
|
||||
try:
|
||||
return self.model_class.objects.get(pk=value)
|
||||
except self.model_class.DoesNotExist:
|
||||
return None
|
||||
|
||||
def get_display_title(self, instance):
|
||||
"""
|
||||
Return the text to display as the title for this instance
|
||||
"""
|
||||
return str(instance)
|
||||
|
||||
def get_value_data_from_instance(self, instance):
|
||||
"""
|
||||
Given a model instance, return a value that we can pass to both the server-side template
|
||||
and the client-side rendering code (via telepath) that contains all the information needed
|
||||
for display. Typically this is a dict of id, title etc; it must be JSON-serialisable.
|
||||
"""
|
||||
return {
|
||||
"id": instance.pk,
|
||||
"edit_url": AdminURLFinder().get_edit_url(instance),
|
||||
self.display_title_key: self.get_display_title(instance),
|
||||
}
|
||||
|
||||
def get_value_data(self, value):
|
||||
"""
|
||||
Given a value passed to this widget for rendering (which may be None, an id, or a model
|
||||
instance), return a value that we can pass to both the server-side template and the
|
||||
client-side rendering code (via telepath) that contains all the information needed
|
||||
for display. Typically this is a dict of id, title etc; it must be JSON-serialisable.
|
||||
"""
|
||||
instance = self.get_instance(value)
|
||||
if instance:
|
||||
return self.get_value_data_from_instance(instance)
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
# no point trying to come up with sensible semantics for when 'id' is missing from attrs,
|
||||
# so let's make sure it fails early in the process
|
||||
try:
|
||||
id_ = attrs["id"]
|
||||
except (KeyError, TypeError):
|
||||
raise TypeError("BaseChooser cannot be rendered without an 'id' attribute")
|
||||
|
||||
value_data = self.get_value_data(value)
|
||||
widget_html = self.render_html(name, value_data, attrs)
|
||||
|
||||
js = self.render_js_init(id_, name, value_data)
|
||||
out = f"{widget_html}<script>{js}</script>"
|
||||
return mark_safe(out)
|
||||
|
||||
@property
|
||||
def base_js_init_options(self):
|
||||
"""The set of options to pass to the JS initialiser that are constant every time this widget
|
||||
instance is rendered (i.e. do not vary based on id / name / value)"""
|
||||
opts = {
|
||||
"modalUrl": self.get_chooser_modal_url(),
|
||||
}
|
||||
if self.linked_fields:
|
||||
opts["linkedFields"] = self.linked_fields
|
||||
return opts
|
||||
|
||||
def get_js_init_options(self, id_, name, value_data):
|
||||
return {**self.base_js_init_options}
|
||||
|
||||
def render_js_init(self, id_, name, value_data):
|
||||
opts = self.get_js_init_options(id_, name, value_data)
|
||||
return f"new {self.js_constructor}({json.dumps(id_)}, {json.dumps(opts)});"
|
||||
|
||||
@cached_property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/chooser-widget.js"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class BaseChooserAdapter(WidgetAdapter):
|
||||
js_constructor = "wagtail.admin.widgets.Chooser"
|
||||
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.render_html("__NAME__", None, attrs={"id": "__ID__"}),
|
||||
widget.id_for_label("__ID__"),
|
||||
widget.base_js_init_options,
|
||||
]
|
||||
|
||||
@cached_property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/chooser-widget-telepath.js"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
register(BaseChooserAdapter(), BaseChooser)
|
||||
|
||||
|
||||
class AdminPageChooser(BaseChooser):
|
||||
choose_one_text = _("Choose a page")
|
||||
choose_another_text = _("Choose another page")
|
||||
link_to_chosen_text = _("Edit this page")
|
||||
display_title_key = "display_title"
|
||||
chooser_modal_url_name = "wagtailadmin_choose_page"
|
||||
icon = "doc-empty-inverse"
|
||||
classname = "page-chooser"
|
||||
js_constructor = "PageChooser"
|
||||
|
||||
def __init__(
|
||||
self, target_models=None, can_choose_root=False, user_perms=None, **kwargs
|
||||
):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
if target_models:
|
||||
if not isinstance(target_models, (set, list, tuple)):
|
||||
# assume we've been passed a single instance; wrap it as a list
|
||||
target_models = [target_models]
|
||||
|
||||
# normalise the list of target models to a list of page classes
|
||||
cleaned_target_models = []
|
||||
for model in target_models:
|
||||
try:
|
||||
cleaned_target_models.append(resolve_model_string(model))
|
||||
except (ValueError, LookupError):
|
||||
raise ImproperlyConfigured(
|
||||
"Could not resolve %r into a model. "
|
||||
"Model names should be in the form app_label.model_name"
|
||||
% (model,)
|
||||
)
|
||||
else:
|
||||
cleaned_target_models = [Page]
|
||||
|
||||
if len(cleaned_target_models) == 1 and cleaned_target_models[0] is not Page:
|
||||
model_name = cleaned_target_models[0]._meta.verbose_name.title()
|
||||
self.choose_one_text += " (" + model_name + ")"
|
||||
|
||||
self.user_perms = user_perms
|
||||
self.target_models = cleaned_target_models
|
||||
if len(self.target_models) == 1:
|
||||
self.model = self.target_models[0]
|
||||
else:
|
||||
self.model = Page
|
||||
self.can_choose_root = bool(can_choose_root)
|
||||
|
||||
@property
|
||||
def model_names(self):
|
||||
return [
|
||||
"{app}.{model}".format(
|
||||
app=model._meta.app_label, model=model._meta.model_name
|
||||
)
|
||||
for model in self.target_models
|
||||
]
|
||||
|
||||
@property
|
||||
def base_js_init_options(self):
|
||||
# a JSON-serializable representation of the configuration options needed for the
|
||||
# client-side behaviour of this widget
|
||||
return {
|
||||
"modelNames": self.model_names,
|
||||
"canChooseRoot": self.can_choose_root,
|
||||
"userPerms": self.user_perms,
|
||||
**super().base_js_init_options,
|
||||
}
|
||||
|
||||
def get_instance(self, value):
|
||||
instance = super().get_instance(value)
|
||||
if instance:
|
||||
return instance.specific
|
||||
|
||||
def get_display_title(self, instance):
|
||||
return instance.get_admin_display_title()
|
||||
|
||||
def get_value_data_from_instance(self, instance):
|
||||
data = super().get_value_data_from_instance(instance)
|
||||
parent_page = instance.get_parent()
|
||||
data["parent_id"] = parent_page.pk if parent_page else None
|
||||
return data
|
||||
|
||||
def get_js_init_options(self, id_, name, value_data):
|
||||
opts = super().get_js_init_options(id_, name, value_data)
|
||||
value_data = value_data or {}
|
||||
parent_id = value_data.get("parent_id")
|
||||
if parent_id is not None:
|
||||
opts["parentId"] = parent_id
|
||||
return opts
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/page-chooser-modal.js"),
|
||||
versioned_static("wagtailadmin/js/page-chooser.js"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class PageChooserAdapter(BaseChooserAdapter):
|
||||
js_constructor = "wagtail.widgets.PageChooser"
|
||||
|
||||
@cached_property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/page-chooser-modal.js"),
|
||||
versioned_static("wagtailadmin/js/page-chooser-telepath.js"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class AdminPageMoveChooser(AdminPageChooser):
|
||||
def __init__(
|
||||
self, target_models=None, can_choose_root=False, user_perms=None, **kwargs
|
||||
):
|
||||
self.pages_to_move = kwargs.pop("pages_to_move", [])
|
||||
super().__init__(
|
||||
target_models=target_models,
|
||||
can_choose_root=can_choose_root,
|
||||
user_perms=user_perms,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@property
|
||||
def base_js_init_options(self):
|
||||
return {
|
||||
"targetPages": self.pages_to_move,
|
||||
"matchSubclass": False,
|
||||
**super().base_js_init_options,
|
||||
}
|
||||
|
||||
|
||||
register(PageChooserAdapter(), AdminPageChooser)
|
||||
165
env/lib/python3.10/site-packages/wagtail/admin/widgets/datetime.py
vendored
Normal file
165
env/lib/python3.10/site-packages/wagtail/admin/widgets/datetime.py
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
import json
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.forms import widgets
|
||||
from django.utils.formats import get_format
|
||||
|
||||
from wagtail.admin.datetimepicker import to_datetimepicker_format
|
||||
from wagtail.admin.staticfiles import versioned_static
|
||||
from wagtail.telepath import register
|
||||
from wagtail.widget_adapters import WidgetAdapter
|
||||
|
||||
DEFAULT_DATE_FORMAT = "%Y-%m-%d"
|
||||
DEFAULT_DATETIME_FORMAT = "%Y-%m-%d %H:%M"
|
||||
DEFAULT_TIME_FORMAT = "%H:%M"
|
||||
|
||||
|
||||
class AdminDateInput(widgets.DateInput):
|
||||
template_name = "wagtailadmin/widgets/date_input.html"
|
||||
|
||||
def __init__(self, attrs=None, format=None):
|
||||
default_attrs = {"autocomplete": "off"}
|
||||
fmt = format
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
if fmt is None:
|
||||
fmt = getattr(settings, "WAGTAIL_DATE_FORMAT", DEFAULT_DATE_FORMAT)
|
||||
self.js_format = to_datetimepicker_format(fmt)
|
||||
super().__init__(attrs=default_attrs, format=fmt)
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
"dayOfWeekStart": get_format("FIRST_DAY_OF_WEEK"),
|
||||
"format": self.js_format,
|
||||
}
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
|
||||
context["widget"]["config_json"] = json.dumps(self.get_config())
|
||||
|
||||
return context
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/date-time-chooser.js"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class AdminDateInputAdapter(WidgetAdapter):
|
||||
js_constructor = "wagtail.widgets.AdminDateInput"
|
||||
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.get_config(),
|
||||
]
|
||||
|
||||
|
||||
register(AdminDateInputAdapter(), AdminDateInput)
|
||||
|
||||
|
||||
class AdminTimeInput(widgets.TimeInput):
|
||||
template_name = "wagtailadmin/widgets/time_input.html"
|
||||
|
||||
def __init__(self, attrs=None, format=None):
|
||||
default_attrs = {"autocomplete": "off"}
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
fmt = format
|
||||
if fmt is None:
|
||||
fmt = getattr(settings, "WAGTAIL_TIME_FORMAT", DEFAULT_TIME_FORMAT)
|
||||
self.js_format = to_datetimepicker_format(fmt)
|
||||
super().__init__(attrs=default_attrs, format=fmt)
|
||||
|
||||
def get_config(self):
|
||||
return {"format": self.js_format, "formatTime": self.js_format}
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
context["widget"]["config_json"] = json.dumps(self.get_config())
|
||||
return context
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/date-time-chooser.js"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class AdminTimeInputAdapter(WidgetAdapter):
|
||||
js_constructor = "wagtail.widgets.AdminTimeInput"
|
||||
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.get_config(),
|
||||
]
|
||||
|
||||
|
||||
register(AdminTimeInputAdapter(), AdminTimeInput)
|
||||
|
||||
|
||||
class AdminDateTimeInput(widgets.DateTimeInput):
|
||||
template_name = "wagtailadmin/widgets/datetime_input.html"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
attrs=None,
|
||||
format=None,
|
||||
time_format=None,
|
||||
js_overlay_parent_selector="body",
|
||||
):
|
||||
default_attrs = {"autocomplete": "off"}
|
||||
fmt = format
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
if fmt is None:
|
||||
fmt = getattr(settings, "WAGTAIL_DATETIME_FORMAT", DEFAULT_DATETIME_FORMAT)
|
||||
time_fmt = time_format
|
||||
if time_fmt is None:
|
||||
time_fmt = getattr(settings, "WAGTAIL_TIME_FORMAT", DEFAULT_TIME_FORMAT)
|
||||
self.js_format = to_datetimepicker_format(fmt)
|
||||
self.js_time_format = to_datetimepicker_format(time_fmt)
|
||||
self.js_overlay_parent_selector = js_overlay_parent_selector
|
||||
super().__init__(attrs=default_attrs, format=fmt)
|
||||
|
||||
def get_config(self):
|
||||
return {
|
||||
"dayOfWeekStart": get_format("FIRST_DAY_OF_WEEK"),
|
||||
"format": self.js_format,
|
||||
"formatTime": self.js_time_format,
|
||||
# The parentID option actually takes a CSS selector instead of an ID
|
||||
"parentID": self.js_overlay_parent_selector,
|
||||
}
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
|
||||
context["widget"]["config_json"] = json.dumps(self.get_config())
|
||||
|
||||
return context
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/date-time-chooser.js"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class AdminDateTimeInputAdapter(WidgetAdapter):
|
||||
js_constructor = "wagtail.widgets.AdminDateTimeInput"
|
||||
|
||||
def js_args(self, widget):
|
||||
return [
|
||||
widget.get_config(),
|
||||
]
|
||||
|
||||
|
||||
register(AdminDateTimeInputAdapter(), AdminDateTimeInput)
|
||||
122
env/lib/python3.10/site-packages/wagtail/admin/widgets/filtered_select.py
vendored
Normal file
122
env/lib/python3.10/site-packages/wagtail/admin/widgets/filtered_select.py
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
from django import forms
|
||||
|
||||
from wagtail.admin.staticfiles import versioned_static
|
||||
|
||||
|
||||
class FilteredSelect(forms.Select):
|
||||
"""
|
||||
A select box where the options are shown and hidden dynamically in response to another
|
||||
form field whose HTML `id` is specified in `filter_field`.
|
||||
|
||||
The `choices` list accepts entries of the form `(value, label, filter_values)` in addition
|
||||
to the standard `(value, label)` tuples, where `filter_values` is a list of values;
|
||||
whenever `filter_field` is set to a non-empty value, only the items with that value in their
|
||||
`filter_values` list are shown.
|
||||
|
||||
filter_field and filter_values are inserted as 'data-' attributes on the rendered HTML, where
|
||||
they are picked up by the JavaScript behaviour code -
|
||||
see wagtailadmin/js/filtered-select.js for an example of how these attributes are configured.
|
||||
"""
|
||||
|
||||
def __init__(self, attrs=None, choices=(), filter_field=""):
|
||||
super().__init__(attrs, choices)
|
||||
self.filter_field = filter_field
|
||||
|
||||
def build_attrs(self, base_attrs, extra_attrs=None):
|
||||
my_attrs = {
|
||||
"data-widget": "filtered-select",
|
||||
"data-filter-field": self.filter_field,
|
||||
}
|
||||
if extra_attrs:
|
||||
my_attrs.update(extra_attrs)
|
||||
|
||||
return super().build_attrs(base_attrs, my_attrs)
|
||||
|
||||
def optgroups(self, name, value, attrs=None):
|
||||
# copy of Django's Select.optgroups, modified to accept filter_value as a
|
||||
# third item in the tuple and expose that as a data-filter-value attribute
|
||||
# on the final <option>
|
||||
groups = []
|
||||
has_selected = False
|
||||
|
||||
for index, choice in enumerate(self.choices):
|
||||
try:
|
||||
(option_value, option_label, filter_value) = choice
|
||||
except ValueError:
|
||||
# *ChoiceField will still output blank options as a 2-tuple,
|
||||
# so need to handle that too
|
||||
(option_value, option_label) = choice
|
||||
filter_value = None
|
||||
|
||||
if option_value is None:
|
||||
option_value = ""
|
||||
|
||||
subgroup = []
|
||||
if isinstance(option_label, (list, tuple)):
|
||||
# this is an optgroup - we will iterate over the list in the second item of
|
||||
# the tuple (which has been assigned to option_label)
|
||||
group_name = option_value
|
||||
subindex = 0
|
||||
choices = option_label
|
||||
else:
|
||||
# this is a top-level choice; put it in its own group with no name
|
||||
group_name = None
|
||||
subindex = None
|
||||
choices = [(option_value, option_label, filter_value)]
|
||||
groups.append((group_name, subgroup, index))
|
||||
|
||||
for choice in choices:
|
||||
try:
|
||||
(subvalue, sublabel, filter_value) = choice
|
||||
except ValueError:
|
||||
(subvalue, sublabel) = choice
|
||||
filter_value = None
|
||||
|
||||
selected = str(subvalue) in value and (
|
||||
not has_selected or self.allow_multiple_selected
|
||||
)
|
||||
has_selected |= selected
|
||||
|
||||
subgroup.append(
|
||||
self.create_option(
|
||||
name,
|
||||
subvalue,
|
||||
sublabel,
|
||||
selected,
|
||||
index,
|
||||
subindex=subindex,
|
||||
filter_value=filter_value,
|
||||
)
|
||||
)
|
||||
if subindex is not None:
|
||||
subindex += 1
|
||||
return groups
|
||||
|
||||
def create_option(
|
||||
self,
|
||||
name,
|
||||
value,
|
||||
label,
|
||||
selected,
|
||||
index,
|
||||
subindex=None,
|
||||
attrs=None,
|
||||
filter_value=None,
|
||||
):
|
||||
option = super().create_option(
|
||||
name, value, label, selected, index, subindex=subindex, attrs=attrs
|
||||
)
|
||||
if filter_value is not None:
|
||||
option["attrs"]["data-filter-value"] = ",".join(
|
||||
[str(val) for val in filter_value]
|
||||
)
|
||||
|
||||
return option
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/filtered-select.js"),
|
||||
]
|
||||
)
|
||||
24
env/lib/python3.10/site-packages/wagtail/admin/widgets/slug.py
vendored
Normal file
24
env/lib/python3.10/site-packages/wagtail/admin/widgets/slug.py
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
from django.conf import settings
|
||||
from django.forms import widgets
|
||||
|
||||
|
||||
class SlugInput(widgets.TextInput):
|
||||
"""
|
||||
Associates the input field with the Stimulus w-slug (SlugController).
|
||||
Slugifies content based on `WAGTAIL_ALLOW_UNICODE_SLUGS` and supports
|
||||
fields syncing their value to this field (see `TitleFieldPanel`) if
|
||||
also used.
|
||||
"""
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
default_attrs = {
|
||||
"data-controller": "w-slug",
|
||||
"data-action": "blur->w-slug#slugify w-sync:check->w-slug#compare w-sync:apply->w-slug#urlify:prevent",
|
||||
"data-w-slug-allow-unicode-value": getattr(
|
||||
settings, "WAGTAIL_ALLOW_UNICODE_SLUGS", True
|
||||
),
|
||||
"data-w-slug-compare-as-param": "urlify",
|
||||
}
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
super().__init__(default_attrs)
|
||||
5
env/lib/python3.10/site-packages/wagtail/admin/widgets/switch.py
vendored
Normal file
5
env/lib/python3.10/site-packages/wagtail/admin/widgets/switch.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.forms import widgets
|
||||
|
||||
|
||||
class SwitchInput(widgets.CheckboxInput):
|
||||
template_name = "wagtailadmin/widgets/switch.html"
|
||||
60
env/lib/python3.10/site-packages/wagtail/admin/widgets/tags.py
vendored
Normal file
60
env/lib/python3.10/site-packages/wagtail/admin/widgets/tags.py
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from taggit.forms import TagWidget
|
||||
from taggit.models import Tag
|
||||
|
||||
|
||||
class AdminTagWidget(TagWidget):
|
||||
template_name = "wagtailadmin/widgets/tag_widget.html"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.tag_model = kwargs.pop("tag_model", Tag)
|
||||
# free_tagging = None means defer to the tag model's setting
|
||||
self.free_tagging = kwargs.pop("free_tagging", None)
|
||||
|
||||
default_attrs = {"data-controller": "w-tag"}
|
||||
attrs = kwargs.get("attrs")
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
kwargs["attrs"] = default_attrs
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
|
||||
if self.tag_model == Tag:
|
||||
autocomplete_url = reverse("wagtailadmin_tag_autocomplete")
|
||||
else:
|
||||
autocomplete_url = reverse(
|
||||
"wagtailadmin_tag_model_autocomplete",
|
||||
args=(self.tag_model._meta.app_label, self.tag_model._meta.model_name),
|
||||
)
|
||||
|
||||
if self.free_tagging is None:
|
||||
free_tagging = getattr(self.tag_model, "free_tagging", True)
|
||||
else:
|
||||
free_tagging = self.free_tagging
|
||||
|
||||
tag_spaces_allowed = getattr(settings, "TAG_SPACES_ALLOWED", True)
|
||||
if tag_spaces_allowed:
|
||||
help_text = _(
|
||||
'Multi-word tags with spaces will automatically be enclosed in double quotes (").'
|
||||
)
|
||||
else:
|
||||
help_text = _("Tags can only consist of a single word, no spaces allowed.")
|
||||
|
||||
context["widget"]["help_text"] = help_text
|
||||
context["widget"]["attrs"]["data-w-tag-url-value"] = autocomplete_url
|
||||
context["widget"]["attrs"]["data-w-tag-options-value"] = json.dumps(
|
||||
{
|
||||
"allowSpaces": getattr(settings, "TAG_SPACES_ALLOWED", True),
|
||||
"tagLimit": getattr(settings, "TAG_LIMIT", None),
|
||||
"autocompleteOnly": not free_tagging,
|
||||
}
|
||||
)
|
||||
|
||||
return context
|
||||
30
env/lib/python3.10/site-packages/wagtail/admin/widgets/workflows.py
vendored
Normal file
30
env/lib/python3.10/site-packages/wagtail/admin/widgets/workflows.py
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import json
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from wagtail.admin.staticfiles import versioned_static
|
||||
from wagtail.admin.widgets import BaseChooser
|
||||
from wagtail.models import Task
|
||||
|
||||
|
||||
class AdminTaskChooser(BaseChooser):
|
||||
choose_one_text = _("Choose a task")
|
||||
choose_another_text = _("Choose another task")
|
||||
link_to_chosen_text = _("Edit this task")
|
||||
model = Task
|
||||
icon = "thumbtack"
|
||||
chooser_modal_url_name = "wagtailadmin_workflows:task_chooser"
|
||||
classname = "task-chooser"
|
||||
|
||||
def render_js_init(self, id_, name, value_data):
|
||||
return f"createTaskChooser({json.dumps(id_)});"
|
||||
|
||||
@property
|
||||
def media(self):
|
||||
return forms.Media(
|
||||
js=[
|
||||
versioned_static("wagtailadmin/js/task-chooser-modal.js"),
|
||||
versioned_static("wagtailadmin/js/task-chooser.js"),
|
||||
]
|
||||
)
|
||||
Reference in New Issue
Block a user