All Projects → torchbox → wagtail-headless-preview

torchbox / wagtail-headless-preview

Licence: BSD-3-Clause license
Previews for headless Wagtail setups

Programming Languages

python
139335 projects - #7 most used programming language
HTML
75241 projects
Makefile
30231 projects

Projects that are alternatives of or similar to wagtail-headless-preview

Django Salesman
Headless e-commerce framework for Django.
Stars: ✭ 157 (+58.59%)
Mutual labels:  wagtail, headless
WF-website
Website for Western Friend, part of the Religious Society of Friends
Stars: ✭ 34 (-65.66%)
Mutual labels:  wagtail, wagtail-cms
wagtailcolumnblocks
Streamfield columns for Wagtail
Stars: ✭ 38 (-61.62%)
Mutual labels:  wagtail, wagtail-cms
gatsby-source-wagtail
Plugin for sourcing Gatsby data from Wagtail CMS
Stars: ✭ 16 (-83.84%)
Mutual labels:  wagtail, wagtail-cms
wagtailgridder
Wagtail Gridder is a Bootstrap 4 enabled layout for the Wagtail CMS. Grid Items are created within categories, and displayed on a Grid Index Page. The JavaScript libraries Gridder and MixItUp are included.
Stars: ✭ 59 (-40.4%)
Mutual labels:  wagtail, wagtail-cms
wagtailmath
Beautiful equations in your StreamField content
Stars: ✭ 27 (-72.73%)
Mutual labels:  wagtail, wagtail-cms
wagtailvideos
Videos for Wagtail CMS, including transcoding
Stars: ✭ 43 (-56.57%)
Mutual labels:  wagtail, wagtail-cms
Wagtailtrans
A Wagtail add-on for supporting multilingual sites
Stars: ✭ 103 (+4.04%)
Mutual labels:  wagtail
wagtailcommonblocks
Common StreamField blocks for Wagtail
Stars: ✭ 40 (-59.6%)
Mutual labels:  wagtail
Wagtail Personalisation
Rule-based personalisation for Wagtail CMS
Stars: ✭ 96 (-3.03%)
Mutual labels:  wagtail
Wagtail Torchbox
Wagtail build of Torchbox.com
Stars: ✭ 84 (-15.15%)
Mutual labels:  wagtail
Awesome Wagtail
A curated list of awesome packages, articles, and other cool resources from the Wagtail community.
Stars: ✭ 1,527 (+1442.42%)
Mutual labels:  wagtail
chrome-headless-launcher
Run the latest Chrome browser on CLI without head
Stars: ✭ 39 (-60.61%)
Mutual labels:  headless
Reddit Wallpaper Changer
Reddit Wallpaper Changer
Stars: ✭ 96 (-3.03%)
Mutual labels:  wagtail
django-hyper-editor
Django Integration of Hyper Editor
Stars: ✭ 29 (-70.71%)
Mutual labels:  wagtail
Wagtail Geo Widget
Wagtail-Geo-Widget is the complete map solution for your Wagtail site.
Stars: ✭ 90 (-9.09%)
Mutual labels:  wagtail
puppeteer-lambda
Module for using Headless-Chrome by Puppeteer on AWS Lambda.
Stars: ✭ 117 (+18.18%)
Mutual labels:  headless
FlexDotnetCMS
A powerful, flexible, decoupled and easy to use and Fully Featured ASP .NET CMS, it can also be used as a Headless CMS
Stars: ✭ 45 (-54.55%)
Mutual labels:  headless
Consumerfinance.gov
Django project protecting American consumers
Stars: ✭ 164 (+65.66%)
Mutual labels:  wagtail
capybara-chrome
Chrome driver for Capybara using Chrome's remote debugging protocol
Stars: ✭ 27 (-72.73%)
Mutual labels:  headless

Wagtail Headless Preview

Build status PyPI black pre-commit.ci status

Overview

With Wagtail as the backend, and a separate app for the front-end (for example a single page React app), editors are no longer able to preview their changes. This is because the front-end is no longer within Wagtail's direct control. The preview data therefore needs to be exposed to the front-end app.

This package enables previews for Wagtail pages when used in a headless setup by routing the preview to the specified front-end URL.

Setup

Install using pip:

pip install wagtail-headless-preview

After installing the module, add wagtail_headless_preview to installed apps in your settings file:

# settings.py

INSTALLED_APPS = [
    # ...
    "wagtail_headless_preview",
]

Run migrations:

$ python manage.py migrate

Then configure the preview client URL using the CLIENT_URLS option in the WAGTAIL_HEADLESS_PREVIEW setting.

Configuration

wagtail_headless_preview uses a single settings dictionary:

# settings.py

WAGTAIL_HEADLESS_PREVIEW = {
    "CLIENT_URLS": {},  # defaults to an empty dict. You must at the very least define the default client URL.
    "SERVE_BASE_URL": None,  # can be used for HeadlessServeMixin
    "REDIRECT_ON_PREVIEW": False,  # set to True to redirect to the preview instead of using the Wagtail default mechanism
    "ENFORCE_TRAILING_SLASH": True,  # set to False in order to disable the trailing slash enforcement
}

Single site setup

For single sites, add the front-end URL as the default entry:

WAGTAIL_HEADLESS_PREVIEW = {
    "CLIENT_URLS": {
        "default": "http://localhost:8020",
    }
}

If you have configured your Wagtail Site entry to use the front-end URL, then you can update your configuration to:

WAGTAIL_HEADLESS_PREVIEW = {
    "CLIENT_URLS": {
        "default": "{SITE_ROOT_URL}",
    }
}

The {SITE_ROOT_URL} placeholder is replaced with the root_url property of the Site the preview page belongs to.

Multi-site setup

For a multi-site setup, add each site as a separate entry in the CLIENT_URLS option in the WAGTAIL_HEADLESS_PREVIEW setting:

WAGTAIL_HEADLESS_PREVIEW = {
    "CLIENT_URLS": {
        "default": "https://wagtail.org",  # adjust to match your front-end URL. e.g. locally it may be something like http://localhost:8020
        "cms.wagtail.org": "https://wagtail.org",
        "cms.torchbox.com": "http://torchbox.com",
    },
    # ...
}

Serve URL

To make the editing experience seamles and to avoid server errors due to missing templates, you can use the HeadlessMixin which combines the HeadlessServeMixin and HeadlessPreviewMixin mixins.

HeadlessServeMixin overrides the Wagtail Page.serve method to redirect to the client URL. By default, it uses the hosts defined in CLIENT_URLS. However, you can provide a single URL to rule them all:

# settings.py

WAGTAIL_HEADLESS_PREVIEW = {
    # ...
    "SERVE_BASE_URL": "https://my.headless.site",
}

Enforce trailing slash

By default, wagtail_headless_preview enforces a trailing slash on the client URL. You can disable this behaviour by setting ENFORCE_TRAILING_SLASH to False:

# settings.py
WAGTAIL_HEADLESS_PREVIEW = {
    # ...
    "ENFORCE_TRAILING_SLASH": False
}

Usage

To enable preview as well as wire in the "View live" button in the Wagtail UI, add the HeadlessMixin to your Page class:

from wagtail_headless_preview.models import HeadlessMixin


class MyWonderfulPage(HeadlessMixin, Page):
    pass

If you require more granular control, or if you've modified you Page model's serve method, you can add HeadlessPreviewMixin to your Page class to only handle previews:

from wagtail_headless_preview.models import HeadlessPreviewMixin


class MyWonderfulPage(HeadlessPreviewMixin, Page):
    pass

How will my front-end app display preview content?

This depends on your project, as it will be dictated by the requirements of your front-end app.

The following example uses a Wagtail API endpoint to access previews - your app may opt to access page previews using GraphQL instead.

Example

This example sets up an API endpoint which will return the preview for a page, and then displays that data on a simplified demo front-end app.

  • Add wagtail.api.v2 to the installed apps:
# settings.py

INSTALLED_APPS = [
    # ...
    "wagtail.api.v2",
]
  • create an api.py file in your project directory:
from django.contrib.contenttypes.models import ContentType

from wagtail.api.v2.router import WagtailAPIRouter
from wagtail.api.v2.views import PagesAPIViewSet

from wagtail_headless_preview.models import PagePreview
from rest_framework.response import Response


# Create the router. "wagtailapi" is the URL namespace
api_router = WagtailAPIRouter("wagtailapi")


class PagePreviewAPIViewSet(PagesAPIViewSet):
    known_query_parameters = PagesAPIViewSet.known_query_parameters.union(
        ["content_type", "token"]
    )

    def listing_view(self, request):
        page = self.get_object()
        serializer = self.get_serializer(page)
        return Response(serializer.data)

    def detail_view(self, request, pk):
        page = self.get_object()
        serializer = self.get_serializer(page)
        return Response(serializer.data)

    def get_object(self):
        app_label, model = self.request.GET["content_type"].split(".")
        content_type = ContentType.objects.get(app_label=app_label, model=model)

        page_preview = PagePreview.objects.get(
            content_type=content_type, token=self.request.GET["token"]
        )
        page = page_preview.as_page()
        if not page.pk:
            # fake primary key to stop API URL routing from complaining
            page.pk = 0

        return page


api_router.register_endpoint("page_preview", PagePreviewAPIViewSet)
  • Register the API URLs so Django can route requests into the API:
# urls.py

from .api import api_router

urlpatterns = [
    # ...
    path("api/v2/", api_router.urls),
    # ...
    # Ensure that the api_router line appears above the default Wagtail page serving route
    path("", include(wagtail_urls)),
]

For further information about configuring the wagtail API, refer to the Wagtail API v2 Configuration Guide

  • Next, add a client/index.html file in your project root. This will query the API to display our preview:
<!DOCTYPE html>
<html>
<head>
    <script>
        function go() {
            var querystring = window.location.search.replace(/^\?/, '');
            var params = {};
            querystring.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
                params[decodeURIComponent(key)] = decodeURIComponent(value);
            });

            var apiUrl = 'http://localhost:8000/api/v2/page_preview/1/?content_type=' + encodeURIComponent(params['content_type']) + '&token=' + encodeURIComponent(params['token']) + '&format=json';
            fetch(apiUrl).then(function(response) {
                response.text().then(function(text) {
                    document.body.innerText = text;
                });
            });
        }
    </script>
</head>
<body onload="go()"></body>
</html>
  • Install django-cors-headers: pip install django-cors-headers
  • Add CORS config to your settings file to allow the front-end to access the API
# settings.py
CORS_ORIGIN_ALLOW_ALL = True
CORS_URLS_REGEX = r"^/api/v2/"

and follow the rest of the setup instructions for django-cors-headers.

  • Start up your site as normal: python manage.py runserver 0:8000
  • Serve the front-end client/index.html at http://localhost:8020/
    • this can be done by running python3 -m http.server 8020 from inside the client directory
  • From the wagtail admin interface, edit (or create) and preview a page that uses HeadlessPreviewMixin

The preview page should now show you the API response for the preview! 🎉

This is where a real front-end would take over and display the preview as it would be seen on the live site.

Contributing

All contributions are welcome!

Note that this project uses pre-commit. To set up locally:

# if you don't have it yet
$ pip install pre-commit
# go to the project directory
$ cd wagtail-headless-preview
# initialize pre-commit
$ pre-commit install

# Optional, run all checks once for this, then the checks will run only on the changed files
$ pre-commit run --all-files

How to run tests

Now you can run tests as shown below:

tox -p

or, you can run them for a specific environment tox -e py39-django3.2-wagtail2.15 or specific test tox -e py310-django3.2-wagtail2.15 wagtail_headless_preview.tests.test_frontend.TestFrontendViews.test_redirect_on_preview

Credits

  • Matthew Westcott (@gasman), initial proof of concept
  • Karl Hobley (@kaedroho), PoC improvements
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].