def move_confirm(request, page_to_move_id, destination_id): page_to_move = get_object_or_404(Page, id=page_to_move_id).specific # Needs .specific_deferred because the .get_admin_display_title method is called in template destination = get_object_or_404(Page, id=destination_id).specific_deferred if not Page._slug_is_available( page_to_move.slug, destination, page=page_to_move): messages.error( request, _("The slug '{0}' is already in use at the selected parent page. Make sure the slug is unique and try again" ).format(page_to_move.slug), ) return redirect( "wagtailadmin_pages:move_choose_destination", page_to_move.id, destination.id, ) for fn in hooks.get_hooks("before_move_page"): result = fn(request, page_to_move, destination) if hasattr(result, "status_code"): return result if request.method == "POST": # any invalid moves *should* be caught by the permission check in the action class, # so don't bother to catch InvalidMoveToDescendant action = MovePageAction(page_to_move, destination, pos="last-child", user=request.user) action.execute() messages.success( request, _("Page '{0}' moved.").format( page_to_move.get_admin_display_title()), buttons=[ messages.button( reverse("wagtailadmin_pages:edit", args=(page_to_move.id, )), _("Edit"), ) ], ) for fn in hooks.get_hooks("after_move_page"): result = fn(request, page_to_move) if hasattr(result, "status_code"): return result return redirect("wagtailadmin_explore", destination.id) return TemplateResponse( request, "wagtailadmin/pages/confirm_move.html", { "page_to_move": page_to_move, "destination": destination, }, )
def delete(request, user_id): user = get_object_or_404(User, pk=user_id) if not user_can_delete_user(request.user, user): raise PermissionDenied for fn in hooks.get_hooks("before_delete_user"): result = fn(request, user) if hasattr(result, "status_code"): return result if request.method == "POST": with transaction.atomic(): log(user, "wagtail.delete") user.delete() messages.success(request, _("User '{0}' deleted.").format(user)) for fn in hooks.get_hooks("after_delete_user"): result = fn(request, user) if hasattr(result, "status_code"): return result return redirect("wagtailusers_users:index") return TemplateResponse( request, "wagtailusers/users/confirm_delete.html", { "user": user, }, )
def edit(request, user_id): user = get_object_or_404(User, pk=user_id) can_delete = user_can_delete_user(request.user, user) editing_self = request.user == user for fn in hooks.get_hooks("before_edit_user"): result = fn(request, user) if hasattr(result, "status_code"): return result if request.method == "POST": form = get_user_edit_form()(request.POST, request.FILES, instance=user, editing_self=editing_self) if form.is_valid(): with transaction.atomic(): user = form.save() log(user, "wagtail.edit") if user == request.user and "password1" in form.changed_data: # User is changing their own password; need to update their session hash update_session_auth_hash(request, user) messages.success( request, _("User '{0}' updated.").format(user), buttons=[ messages.button( reverse("wagtailusers_users:edit", args=(user.pk, )), _("Edit")) ], ) for fn in hooks.get_hooks("after_edit_user"): result = fn(request, user) if hasattr(result, "status_code"): return result return redirect("wagtailusers_users:index") else: messages.error(request, _("The user could not be saved due to errors.")) else: form = get_user_edit_form()(instance=user, editing_self=editing_self) return TemplateResponse( request, "wagtailusers/users/edit.html", { "user": user, "form": form, "can_delete": can_delete, }, )
def unpublish(request, page_id): page = get_object_or_404(Page, id=page_id).specific user_perms = UserPagePermissionsProxy(request.user) if not user_perms.for_page(page).can_unpublish(): raise PermissionDenied next_url = get_valid_next_url_from_request(request) if request.method == "POST": include_descendants = request.POST.get("include_descendants", False) for fn in hooks.get_hooks("before_unpublish_page"): result = fn(request, page) if hasattr(result, "status_code"): return result action = UnpublishPageAction(page, user=request.user, include_descendants=include_descendants) action.execute(skip_permission_checks=True) for fn in hooks.get_hooks("after_unpublish_page"): result = fn(request, page) if hasattr(result, "status_code"): return result messages.success( request, _("Page '{0}' unpublished.").format( page.get_admin_display_title()), buttons=[ messages.button( reverse("wagtailadmin_pages:edit", args=(page.id, )), _("Edit")) ], ) if next_url: return redirect(next_url) return redirect("wagtailadmin_explore", page.get_parent().id) return TemplateResponse( request, "wagtailadmin/pages/confirm_unpublish.html", { "page": page, "next": next_url, "live_descendant_count": page.get_descendants().live().count(), }, )
def page_listing_buttons(context, page, page_perms, is_parent=False): next_url = context.request.path button_hooks = hooks.get_hooks("register_page_listing_buttons") buttons = [] for hook in button_hooks: buttons.extend(hook(page, page_perms, is_parent, next_url)) buttons.sort() for hook in hooks.get_hooks("construct_page_listing_buttons"): hook(buttons, page, page_perms, is_parent, context) return {"page": page, "buttons": buttons}
def snippet_listing_buttons(context, snippet): next_url = context["request"].path button_hooks = hooks.get_hooks("register_snippet_listing_buttons") buttons = [] for hook in button_hooks: buttons.extend(hook(snippet, context["request"].user, next_url)) buttons.sort() for hook in hooks.get_hooks("construct_snippet_listing_buttons"): hook(buttons, snippet, context["request"].user, context) return {"snippet": snippet, "buttons": buttons}
def _get_base_page_action_menu_items(): """ Retrieve the global list of menu items for the page action menu, which may then be customised on a per-request basis """ global BASE_PAGE_ACTION_MENU_ITEMS if BASE_PAGE_ACTION_MENU_ITEMS is None: BASE_PAGE_ACTION_MENU_ITEMS = [ SaveDraftMenuItem(order=0), DeleteMenuItem(order=10), LockMenuItem(order=15), UnlockMenuItem(order=15), UnpublishMenuItem(order=20), PublishMenuItem(order=30), CancelWorkflowMenuItem(order=40), RestartWorkflowMenuItem(order=50), SubmitForModerationMenuItem(order=60), PageLockedMenuItem(order=10000), ] for hook in hooks.get_hooks("register_page_action_menu_item"): action_menu_item = hook() if action_menu_item: BASE_PAGE_ACTION_MENU_ITEMS.append(action_menu_item) return BASE_PAGE_ACTION_MENU_ITEMS
def for_moderation(request, revision_id): items = [ EditPageItem( Revision.page_revisions.get(id=revision_id).content_object), AddPageItem( Revision.page_revisions.get(id=revision_id).content_object), ApproveModerationEditPageItem( Revision.page_revisions.get(id=revision_id)), RejectModerationEditPageItem( Revision.page_revisions.get(id=revision_id)), ] for fn in hooks.get_hooks("construct_wagtail_userbar"): fn(request, items) # Render the items rendered_items = [item.render(request) for item in items] # Remove any unrendered items rendered_items = [item for item in rendered_items if item] # Render the edit bird return TemplateResponse( request, "wagtailadmin/userbar/base.html", { "items": rendered_items, }, )
def page_header_buttons(context, page, page_perms): next_url = context.request.path button_hooks = hooks.get_hooks("register_page_header_buttons") buttons = [] for hook in button_hooks: buttons.extend(hook(page, page_perms, next_url)) buttons.sort() return { "page": page, "buttons": buttons, "title": _("Actions"), "icon_name": "ellipsis-v", "classes": [ "w-flex", "w-justify-center", "w-items-center", "w-h-full", "w-ml-1", ], "button_classes": [ "w-p-0", "w-w-12", "w-h-12", "w-text-primary", "w-bg-transparent", "hover:w-scale-110", "w-transition", "w-outline-offset-inside", "w-relative", "w-z-30", ], "hide_title": True, }
def test_after_hook(self): def after_hook(): pass with self.register_hook("test_hook_name", after_hook, order=1): hook_fns = hooks.get_hooks("test_hook_name") self.assertEqual(hook_fns, [test_hook, after_hook])
def get(self, request): self.image_model = get_image_model() images = permission_policy.instances_user_has_any_permission_for( request.user, ["choose"]).order_by("-created_at") # allow hooks to modify the queryset for hook in hooks.get_hooks("construct_image_chooser_queryset"): images = hook(images, request) collection_id = request.GET.get("collection_id") if collection_id: images = images.filter(collection=collection_id) self.is_searching = False self.q = None if "q" in request.GET: self.search_form = SearchForm(request.GET) if self.search_form.is_valid(): self.q = self.search_form.cleaned_data["q"] self.is_searching = True images = images.search(self.q) else: self.search_form = SearchForm() if not self.is_searching: tag_name = request.GET.get("tag") if tag_name: images = images.filter(tags__name=tag_name) # Pagination paginator = Paginator(images, per_page=CHOOSER_PAGE_SIZE) self.images = paginator.get_page(request.GET.get("p")) return self.render_to_response()
def test_before_hook(self): def before_hook(): pass with self.register_hook("test_hook_name", before_hook, order=-1): hook_fns = hooks.get_hooks("test_hook_name") self.assertEqual(hook_fns, [before_hook, test_hook])
def __init__(self, request, **kwargs): self.request = request self.context = kwargs self.context["request"] = request if "instance" in self.context: self.context["model"] = self.context["instance"].__class__ self.context["draftstate_enabled"] = issubclass( self.context["model"], DraftStateMixin) self.menu_items = [ menu_item for menu_item in get_base_snippet_action_menu_items( self.context["model"]) if menu_item.is_shown(self.context) ] self.menu_items.sort(key=lambda item: item.order) for hook in hooks.get_hooks("construct_snippet_action_menu"): hook(self.menu_items, self.request, self.context) try: self.default_item = self.menu_items.pop(0) except IndexError: self.default_item = None
def home(request): panels = [ SiteSummaryPanel(request), UpgradeNotificationPanel(), WorkflowPagesToModeratePanel(), PagesForModerationPanel(), UserPagesInWorkflowModerationPanel(), RecentEditsPanel(), LockedPagesPanel(), ] for fn in hooks.get_hooks("construct_homepage_panels"): fn(request, panels) media = Media() for panel in panels: media += panel.media site_details = get_site_for_user(request.user) return TemplateResponse( request, "wagtailadmin/home.html", { "root_page": site_details["root_page"], "root_site": site_details["root_site"], "site_name": site_details["site_name"], "panels": sorted(panels, key=lambda p: p.order), "user": request.user, "media": media, }, )
def __init__(self, request): self.request = request summary_items = [] for fn in hooks.get_hooks("construct_homepage_summary_items"): fn(request, summary_items) self.summary_items = [s for s in summary_items if s.is_shown()] self.summary_items.sort(key=lambda p: p.order)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.registered_permissions = Permission.objects.none() for fn in hooks.get_hooks("register_permissions"): self.registered_permissions = self.registered_permissions | fn() self.fields[ "permissions"].queryset = self.registered_permissions.select_related( "content_type")
def test_page_tree_sync_off(self): with hooks.register_temporarily( "construct_translated_pages_to_cascade_actions", self.unpublish_hook): for fn in hooks.get_hooks( "construct_translated_pages_to_cascade_actions"): response = fn([self.en_homepage], "unpublish") self.assertIsNone(response)
def get_object_list(self): documents = self.permission_policy.instances_user_has_any_permission_for( self.request.user, ["choose"]) # allow hooks to modify the queryset for hook in hooks.get_hooks("construct_document_chooser_queryset"): documents = hook(documents, self.request) return documents
def get_permission_panel_classes(): global _permission_panel_classes if _permission_panel_classes is None: _permission_panel_classes = [GroupPagePermissionFormSet] for fn in hooks.get_hooks("register_group_permission_panel"): _permission_panel_classes.append(fn()) return _permission_panel_classes
def _scan_for_serializers(self): serializers = dict(self.BASE_SERIALIZERS_BY_MODEL_CLASS) for fn in hooks.get_hooks('register_custom_serializers'): serializers.update(fn()) self.serializers_by_model_class = serializers self._scanned_for_serializers = True
def populate(self): for fn in hooks.get_hooks("register_admin_viewset"): viewset = fn() if isinstance(viewset, (list, tuple)): for vs in viewset: self.register(vs) else: self.register(viewset)
def _scan_for_adapters(self): adapters = dict(self.BASE_ADAPTERS_BY_FIELD_CLASS) for fn in hooks.get_hooks('register_field_adapters'): adapters.update(fn()) self.adapters_by_field_class = adapters self._scanned_for_adapters = True
def test_missing_hook_action(self): with hooks.register_temporarily( "construct_translated_pages_to_cascade_actions", self.missing_hook_action): for fn in hooks.get_hooks( "construct_translated_pages_to_cascade_actions"): response = fn([self.en_homepage], "") if response is not None: self.assertIsInstance(response, dict)
def test_registered_permission(self): permission = Permission.objects.get_by_natural_key( app_label="tests", model="testsetting", codename="change_testsetting" ) for fn in hooks.get_hooks("register_permissions"): if permission in fn(): break else: self.fail("Change permission for tests.TestSetting not registered")
def run_hook(self, hook_name, *args, **kwargs): """ Run the named hook, passing args and kwargs to each function registered under that hook name. If any return an HttpResponse, stop processing and return that response """ for fn in hooks.get_hooks(hook_name): result = fn(*args, **kwargs) if hasattr(result, "status_code"): return result
def dropdown_buttons(self): button_hooks = hooks.get_hooks(self.hook_name) buttons = [] for hook in button_hooks: buttons.extend(hook(self.page, self.page_perms, self.next_url)) buttons.sort() return buttons
def filter_object_list(self, objects): if self.construct_queryset_hook_name: # allow hooks to modify the queryset for hook in hooks.get_hooks(self.construct_queryset_hook_name): objects = hook(objects, self.request) if self.filter_form.is_valid(): objects = self.filter_form.filter(objects) return objects
def __init__(self, request, **kwargs): self.request = request self.context = kwargs self.context["request"] = request page = self.context.get("page") user_page_permissions = UserPagePermissionsProxy(self.request.user) self.context["user_page_permissions"] = user_page_permissions if page: self.context[ "user_page_permissions_tester"] = user_page_permissions.for_page( page) self.menu_items = [] if page: task = page.current_workflow_task current_workflow_state = page.current_workflow_state is_final_task = (current_workflow_state and current_workflow_state.is_at_final_task) if task: actions = task.get_actions(page, request.user) workflow_menu_items = [] for name, label, launch_modal in actions: icon_name = "edit" if name == "approve": if is_final_task and not getattr( settings, "WAGTAIL_WORKFLOW_REQUIRE_REAPPROVAL_ON_EDIT", False, ): label = _("%(label)s and Publish") % { "label": label } icon_name = "success" item = WorkflowMenuItem(name, label, launch_modal, icon_name=icon_name) if item.is_shown(self.context): workflow_menu_items.append(item) self.menu_items.extend(workflow_menu_items) for menu_item in _get_base_page_action_menu_items(): if menu_item.is_shown(self.context): self.menu_items.append(menu_item) self.menu_items.sort(key=lambda item: item.order) for hook in hooks.get_hooks("construct_page_action_menu"): hook(self.menu_items, self.request, self.context) try: self.default_item = self.menu_items.pop(0) except IndexError: self.default_item = None
def hook_output(hook_name): """ Example: {% hook_output 'insert_editor_css' %} Whenever we have a hook whose functions take no parameters and return a string, this tag can be used to output the concatenation of all of those return values onto the page. Note that the output is not escaped - it is the hook function's responsibility to escape unsafe content. """ snippets = [fn() for fn in hooks.get_hooks(hook_name)] return mark_safe("".join(snippets))
def filter_queryset(self, request, queryset, view): if not hasattr(queryset, '_filtered_by_child_of'): raise BadRequestError( "filtering without child_of is not supported") parent_page = queryset._filtered_by_child_of for hook in hooks.get_hooks('construct_explorer_page_queryset'): queryset = hook(parent_page, queryset, request) return queryset