如何自定义 Wagtail 页面复制体验?

Col*_*lin 5 customization wagtail

我有一些自定义逻辑(复杂的唯一约束验证),我想检查用户何时尝试在 Wagtail 中复制(或移动)某种类型的页面。我还想让用户有机会更改与验证检查相关的字段。

我知道 Wagtail 公开了一种通过钩子(http://docs.wagtail.io/en/stable/reference/hooks.html#before-copy-page)自定义复制(和移动)体验的方法,但我能想到的最好的办法是创建一个全新的界面并在 HttpResponse 中返回它。有没有办法只为特定页面类型自定义现有的复制(和移动)界面?

@hooks.register('before-copy-page')
def before-copy-page(request, page):
    return HttpResponse("New copy interface", content_type="text/plain")
Run Code Online (Sandbox Code Playgroud)

LB *_*ton 2

这三种方法可让您更深入地自定义 Wagtail 页面副本视图和验证。您可能不需要执行所有这三个操作,但下面的示例代码假设所有更改都已在某种程度上完成。

可能有更好的方法来完成您想要的事情,但希望这为您提供了一些方法来自定义整个副本视图/表单交互的部分。

这些方法应该适用于移动页面交互,但有更多的表单和视图。

概述

1.覆盖页面复制模板

  • Wagtail 提供了一种轻松覆盖任何管理模板的方法。
  • 添加模板templates/wagtailadmin/pages/copy.html将覆盖复制页面表单模板
  • {% extends "wagtailadmin/pages/copy.html" %}我们还可以通过在顶部添加来轻松扩展复制页面的原始模板,这使我们不必复制/粘贴页面的大部分内容,而只需自定义我们需要的块。
  • {{ block.super }}如果您只想在模板内的块的开头或结尾添加某些内容,请记住这里可能会派上用场。
  • 在下面的示例代码中,我复制了整个内容块(需要为将来的版本进行维护)并添加了一个自定义字段。

2. 覆盖页面复制的 URL 和视图

  • 在您的 urls.py 中,应将其配置为包含Wagtail urls
  • 在 url上方添加一个新的 URL 路径admin/,这将首先被访问。
  • 例如url(r'^admin/pages/(\d+)/copy/$', base_views.customCopy, name='copy'),,这会将管理副本页面定向到我们的customCopy视图。
  • 该视图可以是函数或类视图,并且可以完全自定义整个视图(和模板)或只是其中的一部分。
  • 这里使用的 Wagtail 视图是一个函数视图,因此不能轻易复制,因此您的自定义在这里受到一些限制。
  • 您可以在admin/views/pages.py中查看此视图​​的源代码。

3. 猴子修补鹡鸰CopyForm

  • 这可能并不理想,但您始终可以修补CopyForm自定义其方法(__init__clean根据需要的任何其他方法)。
  • 您可以查看源代码CopyForm以查看需要修改的内容,如果您想向表单添加字段,则需要这样做(以及模板更改)。

代码

(1) 模板/wagtailadmin/pages/copy.html

{% extends "wagtailadmin/pages/copy.html" %}
{% load i18n %}
{% block content %}
    {% comment %} source - wagtail/admin/templates/wagtailadmin/pages/copy.html {% endcomment %}
    {% trans "Copy" as copy_str %}
    {% include "wagtailadmin/shared/header.html" with title=copy_str subtitle=page.get_admin_display_title icon="doc-empty-inverse" %}

    <div class="nice-padding">
        <form action="{% url 'wagtailadmin_pages:copy' page.id %}" method="POST" novalidate>
            {% csrf_token %}
            <input type="hidden" name="next" value="{{ next }}" />

            <ul class="fields">
                {% include "wagtailadmin/shared/field_as_li.html" with field=form.new_title %}
                {% include "wagtailadmin/shared/field_as_li.html" with field=form.new_slug %}
                {% include "wagtailadmin/shared/field_as_li.html" with field=form.new_parent_page %}

                {% if form.copy_subpages %}
                    {% include "wagtailadmin/shared/field_as_li.html" with field=form.copy_subpages %}
                {% endif %}

                {% if form.publish_copies %}
                    {% include "wagtailadmin/shared/field_as_li.html" with field=form.publish_copies %}
                {% endif %}
                {% comment %} BEGIN CUSTOM CONTENT {% endcomment %}
                {% include "wagtailadmin/shared/field_as_li.html" with field=form.other %}
                {% comment %} END CUSTOM CONTENT {% endcomment %}
            </ul>

            <input type="submit" value="{% trans 'Copy this page' %}" class="button">
        </form>
    </div>
{% endblock %}
Run Code Online (Sandbox Code Playgroud)

(2) 网址.py

from django.conf.urls import include, url
from django.contrib import admin

from wagtail.admin import urls as wagtailadmin_urls
from wagtail.admin.views import pages
from wagtail.documents import urls as wagtaildocs_urls
from wagtail.core import urls as wagtail_urls

from myapp.base import views as base_views  # added

urlpatterns = [
    url(r'^django-admin/', admin.site.urls),
    url(r'^admin/pages/(\d+)/copy/$', base_views.customCopy, name='copy'),  # added
    url(r'^admin/', include(wagtailadmin_urls)),
    url(r'^documents/', include(wagtaildocs_urls)),
    url(r'', include(wagtail_urls)),
]
Run Code Online (Sandbox Code Playgroud)

(2 & 3)views.py


from django import forms
from django.core.exceptions import PermissionDenied

from wagtail.admin.forms.pages import CopyForm
from wagtail.admin.views import pages
from wagtail.core.models import Page


# BEGIN monkey patch of CopyForm
# See: wagtail/admin/forms/pages.py

original_form_init = CopyForm.__init__
original_form_clean = CopyForm.clean


def custom_form_init(self, *args, **kwargs):
    # note - the template will need to be overridden to show additional fields

    original_form_init(self, *args, **kwargs)
    self.fields['other'] = forms.CharField(initial="will fail", label="Other", required=False)


def custom_form_clean(self):
    cleaned_data = original_form_clean(self)

    other = cleaned_data.get('other')
    if other == 'will fail':
        self._errors['other'] = self.error_class(["This field failed due to custom form validation"])
        del cleaned_data['other']

    return cleaned_data


CopyForm.__init__ = custom_form_init
CopyForm.clean = custom_form_clean

# END monkey patch of CopyForm


def customCopy(request, page_id):
    """
    here we can inject any custom code for the response as a whole
    the template is a view function so we cannot easily customise it
    we can respond to POST or GET with any customisations though
    See: wagtail/admin/views/pages.py
    """

    page = Page.objects.get(id=page_id)

    # Parent page defaults to parent of source page
    parent_page = page.get_parent()

    # Check if the user has permission to publish subpages on the parent
    can_publish = parent_page.permissions_for_user(request.user).can_publish_subpage()

    # Create the form
    form = CopyForm(request.POST or None, user=request.user, page=page, can_publish=can_publish)

    if request.method == 'POST':
        if form.is_valid():
            # if the form has been validated (using the form clean above)
            # we get another chance here to fail the request, or redirect to another page
            # we can also easily access the specific page's model for any Page model methods
            try:
                if not page.specific.can_copy_check():
                    raise PermissionDenied
            except AttributeError:
                # continue through to the normal behaviour
                pass

    response = pages.copy(request, page_id)

    return response

Run Code Online (Sandbox Code Playgroud)