def check(self, skip_permission_checks=False): from wagtail.models import UserPagePermissionsProxy # Essential data model checks if self.page._state.adding: raise CopyPageIntegrityError( "Page.copy() called on an unsaved page") if (self.to and self.recursive and (self.to.id == self.page.id or self.to.is_descendant_of(self.page))): raise CopyPageIntegrityError( "You cannot copy a tree branch recursively into itself") # Permission checks if self.user and not skip_permission_checks: to = self.to if to is None: to = self.page.get_parent() if not self.page.permissions_for_user(self.user).can_copy_to( to, self.recursive): raise CopyPagePermissionError( "You do not have permission to copy this page") if self.keep_live: destination_perms = UserPagePermissionsProxy( self.user).for_page(self.to) if not destination_perms.can_publish_subpage(): raise CopyPagePermissionError( "You do not have permission to publish a page at the destination" )
def test_page_locked_for_unlocked_page(self): user = get_user_model().objects.get(email="*****@*****.**") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertFalse(perms.page_locked())
def is_active(self): # Hide the panel if the user can't edit or publish pages user_perms = UserPagePermissionsProxy(self.request.user) if not user_perms.can_edit_pages() and not user_perms.can_publish_pages(): return False # Hide the panel if there are no notification preferences return self.get_form().fields
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) user_perms = UserPagePermissionsProxy(self.instance.user) if not user_perms.can_publish_pages(): del self.fields["submitted_notifications"] if not user_perms.can_edit_pages(): del self.fields["approved_notifications"] del self.fields["rejected_notifications"] del self.fields["updated_comments_notifications"]
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 get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context.update({ "page": self.page, "page_for_status": self.page_for_status, "content_type": self.page_content_type, "edit_handler": self.edit_handler, "errors_debug": self.errors_debug, "action_menu": PageActionMenu(self.request, view="edit", page=self.page), "preview_modes": self.page.preview_modes, "form": self.form, "next": self.next_url, "has_unsaved_changes": self.has_unsaved_changes, "page_locked": self.page_perms.page_locked(), "workflow_state": self.workflow_state if self.workflow_state and self.workflow_state.is_active else None, "current_task_state": self.page.current_workflow_task_state, "publishing_will_cancel_workflow": self.workflow_tasks and getattr(settings, "WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH", True), "locale": None, "translations": [], }) if getattr(settings, "WAGTAIL_I18N_ENABLED", False): user_perms = UserPagePermissionsProxy(self.request.user) context.update({ "locale": self.page.locale, "translations": [{ "locale": translation.locale, "url": reverse("wagtailadmin_pages:edit", args=[translation.id]), } for translation in self.page.get_translations().only( "id", "locale", "depth").select_related("locale") if user_perms.for_page(translation).can_edit()], }) return context
def test_lock_page_for_non_editing_user(self): user = get_user_model().objects.get( email="*****@*****.**") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertFalse(perms.can_lock()) self.assertFalse(perms.can_unlock())
def get_context_data(self, parent_context): request = parent_context["request"] context = super().get_context_data(parent_context) user_perms = UserPagePermissionsProxy(request.user) context["page_revisions_for_moderation"] = ( user_perms.revisions_for_moderation().select_related( "user").order_by("-created_at")) context["request"] = request context["csrf_token"] = parent_context["csrf_token"] return context
def test_page_locked_in_workflow(self): workflow, task = self.create_workflow_and_task() editor = get_user_model().objects.get(email="*****@*****.**") moderator = get_user_model().objects.get( email="*****@*****.**") superuser = get_user_model().objects.get(email="*****@*****.**") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") christmas_page.save_revision() workflow.start(christmas_page, editor) moderator_perms = UserPagePermissionsProxy(moderator).for_page( christmas_page) # the moderator is in the group assigned to moderate the task, so the page should # not be locked for them self.assertFalse(moderator_perms.page_locked()) superuser_perms = UserPagePermissionsProxy(superuser).for_page( christmas_page) # superusers can moderate any GroupApprovalTask, so the page should not be locked # for them self.assertFalse(superuser_perms.page_locked()) editor_perms = UserPagePermissionsProxy(editor).for_page( christmas_page) # the editor is not in the group assigned to moderate the task, so the page should # be locked for them self.assertTrue(editor_perms.page_locked())
def get_context_data(self, parent_context): context = super().get_context_data(parent_context) user_perms = UserPagePermissionsProxy(self.request.user) page = self.object if page.id: context.update({ "history_url": reverse("wagtailadmin_pages:history", args=(page.id, )), "lock_url": reverse("wagtailadmin_pages:lock", args=(page.id, )), "unlock_url": reverse("wagtailadmin_pages:unlock", args=(page.id, )), "user_can_lock": user_perms.for_page(page).can_lock(), "user_can_unlock": user_perms.for_page(page).can_unlock(), "locale": None, "translations": [], }) else: context.update({ "locale": None, "translations": [], }) if getattr(settings, "WAGTAIL_I18N_ENABLED", False): url_name = "wagtailadmin_pages:edit" if self.in_explorer: url_name = "wagtailadmin_explore" context.update({ "locale": page.locale, "translations": [{ "locale": translation.locale, "url": reverse(url_name, args=[translation.id]), } for translation in page.get_translations().only( "id", "locale", "depth").select_related("locale") if user_perms.for_page(translation).can_edit()], # The sum of translated pages plus 1 to account for the current page "translations_total": page.get_translations().count() + 1, }) context.update({ "model_name": self.model.get_verbose_name(), "model_description": self.model.get_page_description(), "status_templates": self.get_status_templates(context), }) return context
def test_lock_page_for_moderator_without_unlock_permission(self): user = get_user_model().objects.get(email="*****@*****.**") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") GroupPagePermission.objects.filter(group__name="Event moderators", permission_type="unlock").delete() perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertTrue(perms.can_lock()) self.assertFalse(perms.can_unlock())
def execute(self, skip_permission_checks=False): super().execute(skip_permission_checks) if self.include_descendants: from wagtail.models import UserPagePermissionsProxy user_perms = UserPagePermissionsProxy(self.user) for live_descendant_page in (self.object.get_descendants().live( ).defer_streamfields().specific().iterator()): action = UnpublishPageAction(live_descendant_page) if user_perms.for_page(live_descendant_page).can_unpublish(): action.execute(skip_permission_checks=True)
def get_forms_for_user(user): """ Return a queryset of form pages that this user is allowed to access the submissions for """ editable_forms = UserPagePermissionsProxy(user).editable_pages() editable_forms = editable_forms.filter(content_type__in=get_form_types()) # Apply hooks for fn in hooks.get_hooks("filter_form_submissions_for_user"): editable_forms = fn(user, editable_forms) return editable_forms
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 filter_queryset(self, request, queryset, view): if request.GET.get("for_explorer"): if not hasattr(queryset, "_filtered_by_child_of"): raise BadRequestError( "filtering by for_explorer 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) user_perms = UserPagePermissionsProxy(request.user) queryset = user_perms.explorable_pages() & queryset return queryset
def test_explorable_pages_with_permission_gap_in_hierarchy(self): corporate_editor = get_user_model().objects.get( email="*****@*****.**") user_perms = UserPagePermissionsProxy(corporate_editor) about_us_page = Page.objects.get(url_path="/home/about-us/") businessy_events = Page.objects.get( url_path="/home/events/businessy-events/") events_page = Page.objects.get(url_path="/home/events/") explorable_pages = user_perms.explorable_pages() self.assertTrue(explorable_pages.filter(id=about_us_page.id).exists()) self.assertTrue( explorable_pages.filter(id=businessy_events.id).exists()) self.assertTrue(explorable_pages.filter(id=events_page.id).exists())
def get_valid_parent_pages(self, user): """ Identifies possible parent pages for the current user by first looking at allowed_parent_page_models() on self.model to limit options to the correct type of page, then checking permissions on those individual pages to make sure we have permission to add a subpage to it. """ # Get queryset of pages where this page type can be added allowed_parent_page_content_types = list( ContentType.objects.get_for_models( *self.model.allowed_parent_page_models()).values()) allowed_parent_pages = Page.objects.filter( content_type__in=allowed_parent_page_content_types) # Get queryset of pages where the user has permission to add subpages if user.is_superuser: pages_where_user_can_add = Page.objects.all() else: pages_where_user_can_add = Page.objects.none() user_perms = UserPagePermissionsProxy(user) for perm in user_perms.permissions.filter(permission_type="add"): # user has add permission on any subpage of perm.page # (including perm.page itself) pages_where_user_can_add |= Page.objects.descendant_of( perm.page, inclusive=True) # Combine them return allowed_parent_pages & pages_where_user_can_add
def usage(request, pk): workflow = get_object_or_404(Workflow, id=pk) perms = UserPagePermissionsProxy(request.user) pages = workflow.all_pages() & perms.editable_pages() paginator = Paginator(pages, per_page=10) pages = paginator.get_page(request.GET.get("p")) return render( request, "wagtailadmin/workflows/usage.html", { "workflow": workflow, "used_by": pages, }, )
def _get_user_page_permissions(context): # Create a UserPagePermissionsProxy object to represent the user's global permissions, and # cache it in the context for the duration of the page request, if one does not exist already if "user_page_permissions" not in context: context["user_page_permissions"] = UserPagePermissionsProxy( context["request"].user) return context["user_page_permissions"]
def test_lock_page_for_editor_with_lock_permission(self): user = get_user_model().objects.get(email="*****@*****.**") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") GroupPagePermission.objects.create( group=Group.objects.get(name="Event editors"), page=christmas_page, permission_type="lock", ) perms = UserPagePermissionsProxy(user).for_page(christmas_page) self.assertTrue(perms.can_lock()) # Still shouldn't have unlock permission self.assertFalse(perms.can_unlock())
def revisions_unschedule(request, page_id, revision_id): page = get_object_or_404(Page, id=page_id).specific user_perms = UserPagePermissionsProxy(request.user) if not user_perms.for_page(page).can_unschedule(): raise PermissionDenied revision = get_object_or_404(page.revisions, id=revision_id) next_url = get_valid_next_url_from_request(request) subtitle = _('revision {0} of "{1}"').format( revision.id, page.get_admin_display_title()) if request.method == "POST": revision.approved_go_live_at = None revision.save(user=request.user, update_fields=["approved_go_live_at"]) messages.success( request, _('Version {0} of "{1}" unscheduled.').format( revision.id, 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_pages:history", page.id) return TemplateResponse( request, "wagtailadmin/pages/revisions/confirm_unschedule.html", { "page": page, "revision": revision, "next": next_url, "subtitle": subtitle }, )
def test_lock_page_for_moderator_whole_locked_page_without_unlock_permission( self): user = get_user_model().objects.get(email="*****@*****.**") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") # Lock the page christmas_page.locked = True christmas_page.locked_by = user christmas_page.locked_at = timezone.now() christmas_page.save() GroupPagePermission.objects.filter(group__name="Event moderators", permission_type="unlock").delete() perms = UserPagePermissionsProxy(user).for_page(christmas_page) # Unlike in the previous test, the user can unlock this page as it was them who locked self.assertTrue(perms.can_lock()) self.assertTrue(perms.can_unlock())
def execute(self, skip_permission_checks=False): self.check(skip_permission_checks=skip_permission_checks) self._unpublish_page( self.page, set_expired=self.set_expired, commit=self.commit, user=self.user, log_action=self.log_action, ) if self.include_descendants: from wagtail.models import UserPagePermissionsProxy user_perms = UserPagePermissionsProxy(self.user) for live_descendant_page in (self.page.get_descendants().live(). defer_streamfields().specific()): action = UnpublishPageAction(live_descendant_page) if user_perms.for_page(live_descendant_page).can_unpublish(): action.execute(skip_permission_checks=True)
def test_with_user_no_permission(self): homepage = Page.objects.get(url_path="/home/") # event editor does not have permissions on homepage event_editor = get_user_model().objects.get( email="*****@*****.**") permission_proxy = UserPagePermissionsProxy(event_editor) result = can_choose_page(homepage, permission_proxy, self.desired_classes, user_perm="copy_to") self.assertFalse(result)
def get_queryset(self): latest_publishing_log = PageLogEntry.objects.filter( page=OuterRef("pk"), action__exact="wagtail.publish") self.queryset = (UserPagePermissionsProxy( self.request.user).publishable_pages().exclude( last_published_at__isnull=True).prefetch_workflow_states( ).select_related("content_type").annotate_approved_schedule(). order_by("last_published_at").annotate( last_published_by=Subquery( latest_publishing_log.values("user")[:1]))) return super().get_queryset()
def workflow_history(request, page_id): page = get_object_or_404(Page, id=page_id) user_perms = UserPagePermissionsProxy(request.user) if not user_perms.for_page(page).can_edit(): raise PermissionDenied workflow_states = WorkflowState.objects.filter( page=page).order_by("-created_at") paginator = Paginator(workflow_states, per_page=20) workflow_states = paginator.get_page(request.GET.get("p")) return TemplateResponse( request, "wagtailadmin/pages/workflow_history/index.html", { "page": page, "workflow_states": workflow_states, }, )
def test_editable_pages_for_non_editing_user(self): user = get_user_model().objects.get( email="*****@*****.**") homepage = Page.objects.get(url_path="/home/") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") unpublished_event_page = EventPage.objects.get( url_path="/home/events/tentative-unpublished-event/") someone_elses_event_page = EventPage.objects.get( url_path="/home/events/someone-elses-event/") user_perms = UserPagePermissionsProxy(user) editable_pages = user_perms.editable_pages() can_edit_pages = user_perms.can_edit_pages() publishable_pages = user_perms.publishable_pages() can_publish_pages = user_perms.can_publish_pages() self.assertFalse(editable_pages.filter(id=homepage.id).exists()) self.assertFalse(editable_pages.filter(id=christmas_page.id).exists()) self.assertFalse( editable_pages.filter(id=unpublished_event_page.id).exists()) self.assertFalse( editable_pages.filter(id=someone_elses_event_page.id).exists()) self.assertFalse(can_edit_pages) self.assertFalse(publishable_pages.filter(id=homepage.id).exists()) self.assertFalse( publishable_pages.filter(id=christmas_page.id).exists()) self.assertFalse( publishable_pages.filter(id=unpublished_event_page.id).exists()) self.assertFalse( publishable_pages.filter(id=someone_elses_event_page.id).exists()) self.assertFalse(can_publish_pages)
def get_context_data(self, parent_context): context = super().get_context_data(parent_context) user_perms = UserPagePermissionsProxy(self.request.user) if self.page.id: context.update({ "lock_url": reverse("wagtailadmin_pages:lock", args=(self.page.id, )), "unlock_url": reverse("wagtailadmin_pages:unlock", args=(self.page.id, )), "user_can_lock": user_perms.for_page(self.page).can_lock(), "user_can_unlock": user_perms.for_page(self.page).can_unlock(), "locale": None, "translations": [], }) else: context.update({ "locale": None, "translations": [], }) if getattr(settings, "WAGTAIL_I18N_ENABLED", False): context.update({ "locale": self.page.locale, "translations": [{ "locale": translation.locale, "url": reverse("wagtailadmin_pages:edit", args=[translation.id]), } for translation in self.page.get_translations().only( "id", "locale", "depth").select_related("locale") if user_perms.for_page(translation).can_edit()], }) return context
def test_explorable_pages(self): event_editor = get_user_model().objects.get( email="*****@*****.**") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") unpublished_event_page = EventPage.objects.get( url_path="/home/events/tentative-unpublished-event/") someone_elses_event_page = EventPage.objects.get( url_path="/home/events/someone-elses-event/") about_us_page = Page.objects.get(url_path="/home/about-us/") user_perms = UserPagePermissionsProxy(event_editor) explorable_pages = user_perms.explorable_pages() # Verify all pages below /home/events/ are explorable self.assertTrue(explorable_pages.filter(id=christmas_page.id).exists()) self.assertTrue( explorable_pages.filter(id=unpublished_event_page.id).exists()) self.assertTrue( explorable_pages.filter(id=someone_elses_event_page.id).exists()) # Verify page outside /events/ tree are not explorable self.assertFalse(explorable_pages.filter(id=about_us_page.id).exists())
def test_lock_page_for_superuser(self): user = get_user_model().objects.get(email="*****@*****.**") christmas_page = EventPage.objects.get( url_path="/home/events/christmas/") locked_page = Page.objects.get(url_path="/home/my-locked-page/") perms = UserPagePermissionsProxy(user).for_page(christmas_page) locked_perms = UserPagePermissionsProxy(user).for_page(locked_page) self.assertTrue(perms.can_lock()) self.assertFalse( locked_perms.can_unpublish()) # locked pages can't be unpublished self.assertTrue(perms.can_unlock())