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

435 lines
12 KiB
Python

from django import forms
from django.http import HttpResponse
from django.utils.safestring import mark_safe
import wagtail.admin.rich_text.editors.draftail.features as draftail_features
from wagtail import hooks
from wagtail.admin.action_menu import ActionMenuItem
from wagtail.admin.filters import WagtailFilterSet
from wagtail.admin.menu import MenuItem
from wagtail.admin.panels import (
FieldPanel,
ObjectList,
PublishingPanel,
TabbedInterface,
)
from wagtail.admin.rich_text.converters.html_to_contentstate import BlockElementHandler
from wagtail.admin.search import SearchArea
from wagtail.admin.site_summary import SummaryItem
from wagtail.admin.ui.components import Component
from wagtail.admin.ui.tables import BooleanColumn, UpdatedAtColumn
from wagtail.admin.utils import set_query_params
from wagtail.admin.views.account import BaseSettingsPanel
from wagtail.admin.widgets import Button
from wagtail.permission_policies.base import ModelPermissionPolicy
from wagtail.snippets.bulk_actions.snippet_bulk_action import SnippetBulkAction
from wagtail.snippets.models import register_snippet
from wagtail.snippets.views.chooser import SnippetChooserViewSet
from wagtail.snippets.views.snippets import SnippetViewSet, SnippetViewSetGroup
from wagtail.test.testapp.models import (
DraftStateModel,
FullFeaturedSnippet,
ModeratedModel,
RevisableChildModel,
RevisableModel,
SnippetChooserModel,
VariousOnDeleteModel,
)
from wagtail.test.testapp.views import (
JSONModelViewSetGroup,
MiscellaneousViewSetGroup,
SearchTestModelViewSet,
ToyViewSetGroup,
animated_advert_chooser_viewset,
event_page_listing_viewset,
)
from .forms import FavouriteColourForm
# Register one hook using decorators...
@hooks.register("insert_global_admin_css")
def editor_css():
return """<link rel="stylesheet" href="/path/to/my/custom.css">"""
# And the other using old-style function calls
def editor_js():
return """<script src="/path/to/my/custom.js"></script>"""
hooks.register("insert_editor_js", editor_js)
def block_googlebot(page, request, serve_args, serve_kwargs):
if request.headers.get("user-agent") == "GoogleBot":
return HttpResponse("<h1>bad googlebot no cookie</h1>")
hooks.register("before_serve_page", block_googlebot)
class KittensMenuItem(MenuItem):
def is_shown(self, request):
return not request.GET.get("hide-kittens", False)
@hooks.register("register_admin_menu_item")
def register_kittens_menu_item():
return KittensMenuItem(
"Kittens!",
"http://www.tomroyal.com/teaandkittens/",
classname="kitten--test",
name="kittens",
icon_name="kitten",
attrs={"data-is-custom": "true"},
order=10000,
)
# Admin Other Searches hook
class MyCustomSearchArea(SearchArea):
def is_shown(self, request):
return not request.GET.get("hide-option", False)
def is_active(self, request, current=None):
return request.GET.get("active-option", False)
@hooks.register("register_admin_search_area")
def register_custom_search_area():
return MyCustomSearchArea(
"My Search",
"/customsearch/",
classname="search--custom-class",
icon_name="custom",
attrs={"is-custom": "true"},
order=10000,
)
@hooks.register("construct_explorer_page_queryset")
def polite_pages_only(parent_page, pages, request):
# if the URL parameter polite_pages_only is set,
# only return pages with a slug that starts with 'hello'
if request.GET.get("polite_pages_only"):
pages = pages.filter(slug__startswith="hello")
return pages
@hooks.register("construct_explorer_page_queryset")
def hide_hidden_pages(parent_page, pages, request):
# Pages with 'hidden' in their title are hidden. Magic!
return pages.exclude(title__icontains="hidden")
# register 'quotation' as a rich text feature supported by a Draftail feature
@hooks.register("register_rich_text_features")
def register_quotation_feature(features):
features.register_editor_plugin(
"draftail",
"quotation",
draftail_features.EntityFeature(
{},
js=["testapp/js/draftail-quotation.js"],
css={"all": ["testapp/css/draftail-quotation.css"]},
),
)
# register 'intro' as a rich text feature which converts an `intro-paragraph` contentstate block
# to a <p class="intro"> tag in db HTML and vice versa
@hooks.register("register_rich_text_features")
def register_intro_rule(features):
features.register_converter_rule(
"contentstate",
"intro",
{
"from_database_format": {
'p[class="intro"]': BlockElementHandler("intro-paragraph"),
},
"to_database_format": {
"block_map": {
"intro-paragraph": {"element": "p", "props": {"class": "intro"}}
},
},
},
)
class PanicMenuItem(ActionMenuItem):
label = "Panic!"
name = "action-panic"
class Media:
js = ["testapp/js/siren.js"]
@hooks.register("register_page_action_menu_item")
def register_panic_menu_item():
return PanicMenuItem()
@hooks.register("register_page_action_menu_item")
def register_none_menu_item():
return None
class RelaxMenuItem(ActionMenuItem):
label = "Relax."
name = "action-relax"
@hooks.register("construct_page_action_menu")
def register_relax_menu_item(menu_items, request, context):
# Run a validation check on all core menu items to ensure name attribute is present
names = [(item.__class__.__name__, item.name or "") for item in menu_items]
name_exists_on_all_items = [len(name[1]) > 1 for name in names]
if not all(name_exists_on_all_items):
raise AttributeError(
"all core sub-classes of ActionMenuItems must have a name attribute", names
)
menu_items.append(RelaxMenuItem())
@hooks.register("construct_snippet_listing_buttons")
def register_snippet_listing_button_item(buttons, snippet, user, context=None):
item = Button(
label="Dummy Button",
url="/dummy-button",
priority=10,
)
buttons.append(item)
@hooks.register("register_account_settings_panel")
class FavouriteColourPanel(BaseSettingsPanel):
name = "favourite_colour"
title = "Favourite colour"
order = 500
form_class = FavouriteColourForm
form_object = "user"
class ClippyPanel(Component):
order = 50
def render_html(self, parent_context):
return mark_safe(
"<p>It looks like you're making a website. Would you like some help?</p>"
)
class Media:
js = ["testapp/js/clippy.js"]
@hooks.register("construct_homepage_panels")
def add_clippy_panel(request, panels):
panels.append(ClippyPanel())
class BrokenLinksSummaryItem(SummaryItem):
order = 100
def render_html(self, parent_context):
return mark_safe("<li>0 broken links</li>")
class Media:
css = {"all": ["testapp/css/broken-links.css"]}
@hooks.register("construct_homepage_summary_items")
def add_broken_links_summary_item(request, items):
items.append(BrokenLinksSummaryItem(request))
@hooks.register("register_admin_viewset")
def register_viewsets():
return [
MiscellaneousViewSetGroup(),
JSONModelViewSetGroup(),
SearchTestModelViewSet(name="searchtest"),
]
@hooks.register("register_admin_viewset")
def register_toy_viewset():
return ToyViewSetGroup()
class FullFeaturedSnippetFilterSet(WagtailFilterSet):
class Meta:
model = FullFeaturedSnippet
fields = ["country_code", "some_date"]
class FullFeaturedPermissionPolicy(ModelPermissionPolicy):
def user_has_permission(self, user, action):
if not user.is_anonymous and "[FORBIDDEN]" in user.get_full_name():
return False
return super().user_has_permission(user, action)
class FullFeaturedSnippetChooserViewSet(SnippetChooserViewSet):
form_fields = ["text", "country_code", "some_number"]
class FullFeaturedSnippetViewSet(SnippetViewSet):
icon = "cog"
admin_url_namespace = "some_namespace"
base_url_path = "deep/within/the/admin"
chooser_admin_url_namespace = "my_chooser_namespace"
chooser_base_url_path = "choose/wisely"
chooser_viewset_class = FullFeaturedSnippetChooserViewSet
list_per_page = 5
chooser_per_page = 15
filterset_class = FullFeaturedSnippetFilterSet
list_display = [
"text",
"country_code",
"get_foo_country_code",
UpdatedAtColumn(),
"modulo_two",
BooleanColumn("tristate"),
]
list_export = [
"text",
"country_code",
"get_foo_country_code",
"some_date",
"some_number",
"first_published_at",
]
export_filename = "all-fullfeatured-snippets"
index_template_name = "tests/fullfeaturedsnippet_index.html"
ordering = ["text", "-_updated_at", "-pk"]
add_to_admin_menu = True
menu_label = "Full-Featured MenuItem" #
menu_name = "fullfeatured"
# Ensure that the menu item is placed last
menu_order = 999999
inspect_view_enabled = True
permission_policy = FullFeaturedPermissionPolicy(FullFeaturedSnippet)
class IndexView(SnippetViewSet.index_view_class):
def get_add_url(self):
if not (add_url := super().get_add_url()):
return None
return set_query_params(add_url, {"customised": "param"})
index_view_class = IndexView
# TODO: When specific search fields are supported in SQLite FTS (see #10217),
# specify search_fields or get_search_fields here
def get_history_template(self):
return "tests/snippet_history.html"
def get_queryset(self, request):
return self.model._default_manager.all().exclude(text__contains="[HIDDEN]")
class RevisableModelViewSet(SnippetViewSet):
model = RevisableModel
class RevisableChildModelViewSet(SnippetViewSet):
model = RevisableChildModel
edit_handler = TabbedInterface(
[
ObjectList([FieldPanel("text")], heading="Main"),
ObjectList(
[FieldPanel("secret_text", permission="superuser")],
heading="Other",
help_text="Other panels help text",
),
],
help_text="Top-level help text",
)
class RevisableViewSetGroup(SnippetViewSetGroup):
# Works with both classes and instances
items = (RevisableModelViewSet, RevisableChildModelViewSet())
menu_label = "Revisables"
menu_icon = "tasks"
class DraftStateModelViewSet(SnippetViewSet):
list_filter = ["text", "first_published_at"]
search_fields = ["text"]
search_backend_name = None
add_to_settings_menu = True
# Don't use "Draft" as the menu label,
# as it may cause incorrect assertion counts in tests
menu_label = "Publishables"
# Ensure that the menu item is placed first
menu_order = -999999
panels = [
FieldPanel("text"),
PublishingPanel(),
]
def get_form_class(self, for_update=False):
form_class = super().get_form_class(for_update)
if for_update:
form_class.base_fields["text"].widget = forms.TextInput()
return form_class
class ModeratedModelViewSet(SnippetViewSet):
model = ModeratedModel
list_filter = {
"text": ["exact", "contains"],
"first_published_at": ["exact", "lt", "gt"],
}
class VariousOnDeleteModelViewSet(SnippetViewSet):
model = VariousOnDeleteModel
inspect_view_enabled = True
class SnippetChooserModelViewSet(SnippetViewSet):
model = SnippetChooserModel
list_display = [
"__str__",
"full_featured__text",
"full_featured__latest_revision__created_at",
]
exclude_form_fields = []
register_snippet(FullFeaturedSnippet, viewset=FullFeaturedSnippetViewSet)
register_snippet(DraftStateModel, viewset=DraftStateModelViewSet)
# Works with both classes and instances
register_snippet(ModeratedModelViewSet())
register_snippet(RevisableViewSetGroup)
register_snippet(VariousOnDeleteModelViewSet)
register_snippet(SnippetChooserModelViewSet)
@hooks.register("register_bulk_action")
class DisableBulkAction(SnippetBulkAction):
template_name = "wagtailadmin/bulk_actions/confirmation/base.html"
models = [FullFeaturedSnippet]
display_name = "Disable"
aria_label = "Disable selected full-featured snippets"
action_type = "disable"
@hooks.register("register_admin_viewset")
def register_animated_advert_chooser_viewset():
return animated_advert_chooser_viewset
@hooks.register("register_admin_viewset")
def register_event_page_listing_viewset():
return event_page_listing_viewset