Django, Python, Linux, Dogs

Requiring staff permissions on your Wagtail site

May 9th, 2016

Learning Wagtail requires breaking some habits formed by working in Django. The biggest paradigm shift is that when creating Pages in Wagtail the view functionality is provided by the Model itself in a method named serve

Decorating each of your Page-derived models individually can be a pain and is more messy than DRY. Here's a simple way to override the default behavior of the serve method and require staff-level permissions for your Wagtail site. This was particularly useful to me while developing a Wagtail site that shared a Django project with dozens of existing live applications. 

Staff-Protected Wagtail Pages

At the top of your_wagtail_project/models.py, do the following. 

from django.http import HttpResponseRedirect
# from wagtail.wagtailcore.models import Page    # This is the standard Page import; when you're ready to serve your new Wagtail site to the public, comment out the custom class override below and import Page directly from Wagtail
from wagtail.wagtailcore.models import Page as PageBase


class Page(PageBase):
    '''
    PROTECTS CMS PAGES FROM PUBLIC. To remove, revert import of wagtail Page back to original name
    and comment out this class
    '''
    class Meta:
        abstract = True
    def serve(self, request, *args, **kwargs):
        # Need to test some other permission? Do it here.
        if request.user.is_staff:    
            return super(Page,self).serve(request, *args, **kwargs)
        return HttpResponseRedirect(reverse('admin:login')+"?next="+request.path)

''' Go about your business :) '''
class FAQPage(Page):
    pass

class InfoPage(Page):
    pass

When the pages are called, the serve method forces a redirect to the login if the the user isn't a staff member. Each page below this is now inheriting the abstract model we created, and calling our custom serve method.

But now I need the site to be public!

Simply comment out your custom Page class and revert to importing the standard Page class by it's original name. It's probably worth keeping around in case you ever want to switch back to protected mode. IF you do that, you'll probably want to make a custom property that overrides the database value for show_in_menus to always be safe to keep it hidden from the public.

from wagtail.wagtailcore.models import Page
# from wagtail.wagtailcore.models import Page as PageBase

# class Page(PageBase):
#     ...

I have certain page types I want to protect and others I'd like public!

Well, that's a piece of cake. Let's just use the method above to create some distinctions! 

from django.http import HttpResponseRedirect
from wagtail.wagtailadmin.edit_handlers import MultiFieldPanel
from wagtail.wagtailcore.models import Page    # This is the standard Page import


class StaffOnlyPage(Page):

    search_fields = []      # Let's hide this from all searches; if you really need to show in searches to staff members, override the search method based on permissions
    class Meta:
        abstract = True

    def serve(self, request, *args, **kwargs):
        if request.user.is_staff:    
            return super(Page,self).serve(request, *args, **kwargs)
        return HttpResponseRedirect(reverse('admin:login')+"?next="+request.path)


StaffOnlyPage.promote_panels = [
        FieldPanel('slug'),
        FieldPanel('seo_title'),
        # FieldPanel('show_in_menus'),      # Commented out so that StaffOnlyPages don't display the 'show_in_menus' field in the CMS/admin. This is False by default, so we're preventing these pages from being shown in our menu system.
        FieldPanel('search_description'),
]

class FAQPage(Page):
    pass

class StaffInfoPage(StaffOnlyPage):
    pass

Now we have a page where we can release news and info to internal staff members that is protected from the public; it won't show up in search results or menus. If existing pages were converted to this page type then be sure to set show_in_menus to False for each. Why not get adventurous and write a migration that uses RunPython to do so?

Have you done anything similar? Feel free to share!

Tags: django, wagtail

Contact

Want to work together? Send an email.

Follow

In addition to building web and in-house applications and systems for Thermaline, I'm on Twitter, LinkedIn and Instagram. Let's talk!

©2018 Ian Price / Red Mountain GIS