diff --git a/wagtail/admin/panels/__init__.py b/wagtail/admin/panels/__init__.py index 3c9a920d31..d280d147fe 100644 --- a/wagtail/admin/panels/__init__.py +++ b/wagtail/admin/panels/__init__.py @@ -12,7 +12,6 @@ from django.forms import Media from django.forms.formsets import DELETION_FIELD_NAME, ORDERING_FIELD_NAME from django.forms.models import fields_for_model from django.utils.functional import cached_property -from django.utils.safestring import mark_safe from django.utils.text import format_lazy from django.utils.translation import gettext_lazy from modelcluster.models import get_serializable_data_for_fields @@ -26,56 +25,20 @@ from wagtail.admin.forms.comments import CommentForm from wagtail.admin.forms.models import ( # NOQA DIRECT_FORM_FIELD_OVERRIDES, FORM_FIELD_OVERRIDES, - WagtailAdminDraftStateFormMixin, - WagtailAdminModelForm, formfield_for_dbfield, ) from wagtail.admin.forms.pages import WagtailAdminPageForm from wagtail.admin.staticfiles import versioned_static from wagtail.admin.templatetags.wagtailadmin_tags import avatar_url, user_display_name -from wagtail.admin.ui.components import Component from wagtail.admin.widgets import AdminPageChooser from wagtail.admin.widgets.datetime import AdminDateTimeInput from wagtail.blocks import BlockField -from wagtail.coreutils import safe_snake_case -from wagtail.models import COMMENTS_RELATION_NAME, DraftStateMixin, Page +from wagtail.models import COMMENTS_RELATION_NAME, Page from wagtail.utils.decorators import cached_classmethod from wagtail.utils.deprecation import RemovedInWagtail50Warning - -def get_form_for_model( - model, - form_class=WagtailAdminModelForm, - **kwargs, -): - """ - Construct a ModelForm subclass using the given model and base form class. Any additional - keyword arguments are used to populate the form's Meta class. - """ - - # This is really just Django's modelform_factory, tweaked to accept arbitrary kwargs. - - meta_class_attrs = kwargs - meta_class_attrs["model"] = model - - # The kwargs passed here are expected to come from EditHandler.get_form_options, which collects - # them by descending the tree of child edit handlers. If there are no edit handlers that - # specify form fields, this can legitimately result in both 'fields' and 'exclude' being - # absent, which ModelForm doesn't normally allow. In this case, explicitly set fields to []. - if "fields" not in meta_class_attrs and "exclude" not in meta_class_attrs: - meta_class_attrs["fields"] = [] - - # Give this new form class a reasonable name. - class_name = model.__name__ + "Form" - bases = (form_class.Meta,) if hasattr(form_class, "Meta") else () - Meta = type("Meta", bases, meta_class_attrs) - form_class_attrs = {"Meta": Meta} - - metaclass = type(form_class) - bases = [form_class] - if issubclass(model, DraftStateMixin): - bases.insert(0, WagtailAdminDraftStateFormMixin) - return metaclass(class_name, tuple(bases), form_class_attrs) +from .base import * # NOQA +from .base import Panel def extract_panel_definitions_from_model_class(model, exclude=None): @@ -104,316 +67,6 @@ def extract_panel_definitions_from_model_class(model, exclude=None): return panels -class Panel: - """ - Defines part (or all) of the edit form interface for pages and other models within the Wagtail - admin. Each model has an associated panel definition, consisting of a nested structure of Panel - objects - this provides methods for obtaining a ModelForm subclass, with the field list and - other parameters collated from all panels in the structure. It then handles rendering that form - as HTML. - """ - - def __init__( - self, - heading="", - classname="", - help_text="", - base_form_class=None, - icon="", - ): - self.heading = heading - self.classname = classname - self.help_text = help_text - self.base_form_class = base_form_class - self.icon = icon - self.model = None - - def clone(self): - """ - Create a clone of this panel definition. By default, constructs a new instance, passing the - keyword arguments returned by ``clone_kwargs``. - """ - return self.__class__(**self.clone_kwargs()) - - def clone_kwargs(self): - """ - Return a dictionary of keyword arguments that can be used to create a clone of this panel definition. - """ - return { - "icon": self.icon, - "heading": self.heading, - "classname": self.classname, - "help_text": self.help_text, - "base_form_class": self.base_form_class, - } - - def get_form_options(self): - """ - Return a dictionary of attributes such as 'fields', 'formsets' and 'widgets' - which should be incorporated into the form class definition to generate a form - that this panel can use. - This will only be called after binding to a model (i.e. self.model is available). - """ - options = {} - - if not getattr(self.widget_overrides, "is_original_method", False): - warn( - "The `widget_overrides` method (on %r) is deprecated; " - "these should be returned from `get_form_options` as a " - "`widgets` item instead." % type(self), - category=RemovedInWagtail50Warning, - ) - options["widgets"] = self.widget_overrides() - - if not getattr(self.required_fields, "is_original_method", False): - warn( - "The `required_fields` method (on %r) is deprecated; " - "these should be returned from `get_form_options` as a " - "`fields` item instead." % type(self), - category=RemovedInWagtail50Warning, - ) - options["fields"] = self.required_fields() - - if not getattr(self.required_formsets, "is_original_method", False): - warn( - "The `required_formsets` method (on %r) is deprecated; " - "these should be returned from `get_form_options` as a " - "`formsets` item instead." % type(self), - category=RemovedInWagtail50Warning, - ) - options["formsets"] = self.required_formsets() - - return options - - # RemovedInWagtail50Warning - edit handlers should override get_form_options instead - def widget_overrides(self): - return {} - - widget_overrides.is_original_method = True - - # RemovedInWagtail50Warning - edit handlers should override get_form_options instead - def required_fields(self): - return [] - - required_fields.is_original_method = True - - # RemovedInWagtail50Warning - edit handlers should override get_form_options instead - def required_formsets(self): - return {} - - required_formsets.is_original_method = True - - def get_form_class(self): - """ - Construct a form class that has all the fields and formsets named in - the children of this edit handler. - """ - form_options = self.get_form_options() - # If a custom form class was passed to the EditHandler, use it. - # Otherwise, use the base_form_class from the model. - # If that is not defined, use WagtailAdminModelForm. - model_form_class = getattr(self.model, "base_form_class", WagtailAdminModelForm) - base_form_class = self.base_form_class or model_form_class - - return get_form_for_model( - self.model, - form_class=base_form_class, - **form_options, - ) - - def bind_to_model(self, model): - """ - Create a clone of this panel definition with a ``model`` attribute pointing to the linked model class. - """ - new = self.clone() - new.model = model - new.on_model_bound() - return new - - def bind_to(self, model=None, instance=None, request=None, form=None): - warn( - "The %s.bind_to() method has been replaced by bind_to_model(model) and get_bound_panel(instance=instance, request=request, form=form)" - % type(self).__name__, - category=RemovedInWagtail50Warning, - stacklevel=2, - ) - return self.get_bound_panel(instance=instance, request=request, form=form) - - def get_bound_panel(self, instance=None, request=None, form=None, prefix="panel"): - """ - Return a ``BoundPanel`` instance that can be rendered onto the template as a component. By default, this creates an instance - of the panel class's inner ``BoundPanel`` class, which must inherit from ``Panel.BoundPanel``. - """ - if self.model is None: - raise ImproperlyConfigured( - "%s.bind_to_model(model) must be called before get_bound_panel" - % type(self).__name__ - ) - - if not issubclass(self.BoundPanel, EditHandler.BoundPanel): - raise ImproperlyConfigured( - "%s.BoundPanel must be a subclass of EditHandler.BoundPanel" - % type(self).__name__ - ) - - return self.BoundPanel( - panel=self, instance=instance, request=request, form=form, prefix=prefix - ) - - def on_model_bound(self): - """ - Called after the panel has been associated with a model class and the ``self.model`` attribute is available; - panels can override this method to perform additional initialisation related to the model. - """ - pass - - def __repr__(self): - return "<%s with model=%s>" % ( - self.__class__.__name__, - self.model, - ) - - def classes(self): - """ - Additional CSS classnames to add to whatever kind of object this is at output. - Subclasses of Panel should override this, invoking super().classes() to - append more classes specific to the situation. - """ - if self.classname: - return [self.classname] - return [] - - def id_for_label(self): - """ - The ID to be used as the 'for' attribute of any