Metadata-Version: 2.1
Name: wagtail-headless-preview
Version: 0.6.0
Summary: Enhance Wagtail previews in headless setups.
Home-page: https://github.com/torchbox/wagtail-headless-preview
Author: Dan Braghis
Author-email: dan.braghis@torchbox.com
License: BSD
Project-URL: Changelog, https://github.com/torchbox/wagtail-headless-preview/blob/main/CHANGELOG.md
Description: # [Wagtail Headless Preview](https://pypi.org/project/wagtail-headless-preview/)
        
        [![Build status](https://img.shields.io/github/actions/workflow/status/torchbox/wagtail-headless-preview/ci.yml?style=for-the-badge)](https://github.com/torchbox/wagtail-headless-preview/actions)
        [![PyPI](https://img.shields.io/pypi/v/wagtail-headless-preview.svg?style=for-the-badge)](https://pypi.org/project/wagtail-headless-preview/)
        [![black](https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge)](https://github.com/psf/black)
        [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/torchbox/wagtail-headless-preview/main.svg)](https://results.pre-commit.ci/latest/github/torchbox/wagtail-headless-preview/main)
        
        
        ## 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:
        ```sh
        pip install wagtail-headless-preview
        ```
        
        After installing the module, add `wagtail_headless_preview` to installed apps in your settings file:
        
        ```python
        # settings.py
        
        INSTALLED_APPS = [
            # ...
            "wagtail_headless_preview",
        ]
        ```
        
        Run migrations:
        
        ```sh
        $ 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:
        
        ```python
        # 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:
        
        ```python
        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:
        
        ```python
        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:
        
        ```python
        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:
        
        ```python
        # 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`:
        
        ```python
        # 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:
        
        ```python
        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:
        
        ```python
        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](https://wagtail.io/blog/getting-started-with-wagtail-and-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:
        ```python
        # settings.py
        
        INSTALLED_APPS = [
            # ...
            "wagtail.api.v2",
        ]
        ```
        
        * create an `api.py` file in your project directory:
        
        ```python
        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:
        
        ```python
        # 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](https://docs.wagtail.io/en/stable/advanced_topics/api/v2/configuration.html)
        
        * Next, add a `client/index.html` file in your project root. This will query the API to display our preview:
        
        ```html
        <!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](https://pypi.org/project/django-cors-headers/): `pip install django-cors-headers`
        * Add CORS config to your settings file to allow the front-end to access the API
        
        ```python
        # 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](https://github.com/ottoyiu/django-cors-headers#setup).
        
        * 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](https://github.com/pre-commit/pre-commit). To set up locally:
        
        ```shell
        # 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:
        
        ```sh
        tox -p
        ```
        
        or, you can run them for a specific environment `tox -e py39-django3.2-wagtail4.1` or specific test
        `tox -e py310-django3.2-wagtail4.1 wagtail_headless_preview.tests.test_frontend.TestFrontendViews.test_redirect_on_preview`
        
        ## Credits
        
        - Matthew Westcott ([@gasman](https://github.com/gasman)), initial proof of concept
        - Karl Hobley ([@kaedroho](https://github.com/kaedroho)), PoC improvements
        
Keywords: wagtail,django,headless
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Framework :: Django
Classifier: Framework :: Django :: 3.2
Classifier: Framework :: Django :: 4.1
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Wagtail
Classifier: Framework :: Wagtail :: 4
Classifier: Framework :: Wagtail :: 5
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Provides-Extra: testing
