from django.http import HttpResponseForbidden from django.shortcuts import get_object_or_404, redirect from django.utils.translation import gettext_lazy from wagtail import hooks from wagtail.admin import messages from wagtail.admin.forms.collections import CollectionForm from wagtail.admin.ui.tables import TitleColumn from wagtail.admin.views.generic import CreateView, DeleteView, EditView, IndexView from wagtail.models import Collection from wagtail.permissions import collection_permission_policy class Index(IndexView): permission_policy = collection_permission_policy model = Collection context_object_name = "collections" results_template_name = "wagtailadmin/collections/index_results.html" add_url_name = "wagtailadmin_collections:add" index_url_name = "wagtailadmin_collections:index" page_title = gettext_lazy("Collections") add_item_label = gettext_lazy("Add a collection") header_icon = "folder-open-1" columns = [ TitleColumn( "name", label=gettext_lazy("Name"), url_name="wagtailadmin_collections:edit", id_accessor="0", accessor="1", ) ] def get_queryset(self): return self.permission_policy.instances_user_has_any_permission_for( self.request.user, ["add", "change", "delete"] ).exclude(depth=1) def get_table(self, object_list): return super().get_table(object_list.get_indented_choices()) class Create(CreateView): permission_policy = collection_permission_policy model = Collection form_class = CollectionForm page_title = gettext_lazy("Add collection") success_message = gettext_lazy("Collection '%(object)s' created.") add_url_name = "wagtailadmin_collections:add" edit_url_name = "wagtailadmin_collections:edit" index_url_name = "wagtailadmin_collections:index" header_icon = "folder-open-1" def get_form(self, form_class=None): form = super().get_form(form_class) # Now filter collections offered in parent field by current user's add permissions collections = self.permission_policy.instances_user_has_permission_for( self.request.user, "add" ) form.fields["parent"].queryset = collections return form def save_instance(self): instance = self.form.save(commit=False) parent = self.form.cleaned_data["parent"] parent.add_child(instance=instance) return instance class Edit(EditView): permission_policy = collection_permission_policy model = Collection form_class = CollectionForm template_name = "wagtailadmin/collections/edit.html" success_message = gettext_lazy("Collection '%(object)s' updated.") error_message = gettext_lazy("The collection could not be saved due to errors.") edit_url_name = "wagtailadmin_collections:edit" index_url_name = "wagtailadmin_collections:index" delete_url_name = "wagtailadmin_collections:delete" context_object_name = "collection" header_icon = "folder-open-1" def _user_may_move_collection(self, user, instance): """ Is this instance used for assigning GroupCollectionPermissions to the user? If so, this user is not allowed do move the collection to a new part of the tree """ if user.is_active and user.is_superuser: return True else: permissions = ( self.permission_policy._get_user_permission_objects_for_actions( user, {"add", "change", "delete"} ) ) return not { permission for permission in permissions if permission.collection_id == instance.pk } def get_queryset(self): return self.permission_policy.instances_user_has_permission_for( self.request.user, "change" ).exclude(depth=1) def get_form(self, form_class=None): form = super().get_form(form_class) user = self.request.user # if user does not have add permission anywhere, they can't move a collection if not self.permission_policy.user_has_permission(user, "add"): form.fields.pop("parent") # If this instance is a collection used to assign permissions for this user, # do not let the user move this collection. elif not self._user_may_move_collection(user, form.instance): form.fields.pop("parent") else: # Filter collections offered in parent field by current user's add permissions collections = self.permission_policy.instances_user_has_permission_for( user, "add" ) form.fields["parent"].queryset = collections # Disable unavailable options in CollectionChoiceField select widget form.fields["parent"].disabled_queryset = form.instance.get_descendants( inclusive=True ) form.initial["parent"] = form.instance.get_parent().pk return form def save_instance(self): instance = self.form.save() if "parent" in self.form.changed_data: instance.move(self.form.cleaned_data["parent"], "sorted-child") return instance class Delete(DeleteView): permission_policy = collection_permission_policy model = Collection success_message = gettext_lazy("Collection '%(object)s' deleted.") index_url_name = "wagtailadmin_collections:index" edit_url_name = "wagtailadmin_collections:edit" delete_url_name = "wagtailadmin_collections:delete" page_title = gettext_lazy("Delete collection") confirmation_message = gettext_lazy( "Are you sure you want to delete this collection?" ) header_icon = "folder-open-1" def get_queryset(self): return self.permission_policy.instances_user_has_permission_for( self.request.user, "delete" ).exclude(depth=1) def get_collection_contents(self): collection_contents = [ hook(self.object) for hook in hooks.get_hooks("describe_collection_contents") ] # filter out any hook responses that report that the collection is empty # (by returning None, or a dict with 'count': 0) def is_nonempty(item_type): return item_type and item_type["count"] > 0 return list(filter(is_nonempty, collection_contents)) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) collection_contents = self.get_collection_contents() if collection_contents: # collection is non-empty; render the 'not allowed to delete' response self.template_name = "wagtailadmin/collections/delete_not_empty.html" context["collection_contents"] = collection_contents return context def post(self, request, pk): self.object = get_object_or_404(self.get_queryset(), id=pk) collection_contents = self.get_collection_contents() if collection_contents: # collection is non-empty; refuse to delete it return HttpResponseForbidden() messages.success(request, self.get_success_message()) self.object.delete() return redirect(self.index_url_name)