Mike
Mike

Reputation: 332

How to filter page lists in the wagtail admin so editors only see pages they created?

I'm creating a wagtail site where users may signup, login to the wagtail admin and write article pages to publish. Is there a hook or possible way to intercept and filter data in the wagtail admin so that users see only the article pages they themselves created?

So far I've set up user registration with django-allauth and users are able to successfully login. Once registered the user_signed_up receiver fires and the user is assigned to the 'Author' role. In the admin, author's only have access to add articles. Articles is an app with an article index page and article page (basically a blog and blog index page). Once they login the only thing they see is the Pages tab on the left, clicking on that takes them to the 'Articles' list. This is where they are able to add an 'Article' page and view their articles created so far.

It's also where my problem is. From the Articles list page, the users are able to see all the articles created by all users on the site. They are only able to edit their own which is great, but eventually there will be hundreds of articles from others users. Is there a way to intercept the 'Articles' data before its displayed and filter it by the current user?

The result of intercepting this data would then keep each user that is logged in to the wagtail admin aware of only the article pages that they have created.

Much thanks, it's my first time using Wagtail and I'm enjoying it so far.

Upvotes: 4

Views: 2934

Answers (3)

manisar
manisar

Reputation: 123

Thanks to both @Pahikua and @Igor Margitich. I was able to combine their answers into a beautiful arrangement which was exactly what I wanted.

  1. Create an admin menu item for Article (or for any other model you want your authors to be able to work with) as suggested by @Pahikua in his Method 1. This will give the authors a shortcut to go to their content quickly (or even add content from here).

  2. I did not remove the Page Explorer admin menu item as I did really want my authors to be able to navigate through other authors' pages in order to go the right location for adding their page.
    Otherwise, adding pages to the right location only from the newly created Article menu item (in step 1) was very cumbersome.

  3. Add these "before" hooks as suggested by @Igor Margitich. I used the before_create_page hook as well, as I wanted my authors to be able to add only one type of page.
from wagtail.core import hooks
from django.core.exceptions import PermissionDenied
from django.views.defaults import permission_denied
from .models import Article

@hooks.register('before_edit_page')
def before_edit_page(request, page):
    # user_group = request.user.groups.filter(name='Author').exists() # I did not use user_group
    if not (request.user.is_superuser or page.owner == request.user):
        return permission_denied(request, PermissionDenied("You do not have permission to edit this page."))
    
@hooks.register('before_delete_page')
def before_delete_page(request, page):
    if not (request.user.is_superuser or page.owner == request.user):
        return permission_denied(request,PermissionDenied("You do not have permission to delete this page."))
    
@hooks.register('before_create_page')
def before_create_page(request, parent_page, page_class):
    if not (request.user.is_superuser or page_class == Article):
        return permission_denied(request, PermissionDenied("You do not have permission to add this page."))

Upvotes: 0

Igor Margitich
Igor Margitich

Reputation: 53

@Pahikua be aware that method 2 just hides the pages from the list and does not protect pages from editing and deleting. In your case it means that Author1 can open url like pages/id/edit/ where id is id of the page owned by Author2 and edit it.

To avoid this you have to add before_edit_page and before_delete_page hooks. Something like this:

@hooks.register('before_edit_page')
def before_edit_page(request, page):
    user_group = request.user.groups.filter(name='Author').exists()
    if user_group and page.owner != request.user:
        raise PermissionDenied

Upvotes: 0

Mike
Mike

Reputation: 332

I discovered two different solutions to this question.

  1. Implement ModelAdmin to create a custom page list view of your model. This lets you create an entirely new admin menu item and corresponding list view. This method is more involved but offers more control.
  2. Implement the construct_explorer_page_queryset admin hook to filer results by group. This method is quick and easy to implement and allows you to use Wagtails existing page explorer.

I'll go over how I handled each of the above methods. Both worked, it's a matter of deciding whether I prefer to use Wagtails built in page explorer or not.

Method 1: ModelAdmin

The use of ModelAdmin.get_queryset() and other helpful methods were found in the Wagtail Docs here.

As per this page in the Wagtail docs I added ModelAdmin to INSTALLED_APPS:

# base.py settings

INSTALLED_APPS = [
    ...
    'wagtail.contrib.modeladmin',
]

Then I created a file named wagtail_hooks.py in my 'article' app. In that file I added the code to create my article listing page in the admin:

# wagtail_hooks.py

from wagtail.contrib.modeladmin.options import (ModelAdmin, modeladmin_register)
from .models import Article


class ArticleAdmin(ModelAdmin):
    model = Article
    menu_label = 'Articles'
    menu_icon = 'doc-full-inverse'
    menu_order = 000
    add_to_settings_menu = False
    exclude_from_explorer = False
    list_display = ('title', 'owner')
    search_fields = ('title', 'owner')

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        #only show articles from the current user
        return qs.filter(owner=request.user)


modeladmin_register(ArticleAdmin)

They key here was to implement ModelAdmin.get_queryset() which let me filter the articles owner.

You'll also likely want to remove the Page explorer menu item as it's a bit redundant. I also recommend implementing Method 2 below as well. Even if you remove the Page explorer menu item the user could possibly get there by entering the URL directly in the browser. If they do, implementing Method 2 will ensure the do not see content authored by other users.

Method 2: construct_explorer_page_queryset @hooks

The hook information in Wagtail's documentation - construct_explorer_page_queryset Also referenced this Google Groups article - https://groups.google.com/forum/#!topic/wagtail/10tcq8PB8io

In the same wagtail_hooks.py file I created above I added this code:

@hooks.register('construct_explorer_page_queryset')
def show_authors_only_their_articles(parent_page, pages, request):
    user_group = request.user.groups.filter(name='Author').exists()
    if user_group:
        pages = pages.filter(owner=request.user)

    return pages

That's it for the second method.

Upvotes: 5

Related Questions