Name | wagtail-headless-preview JSON |
Version |
0.8.0
JSON |
| download |
home_page | |
Summary | Enhance Wagtail previews in headless setups. |
upload_time | 2024-02-23 15:53:44 |
maintainer | |
docs_url | None |
author | |
requires_python | >=3.8 |
license | |
keywords |
wagtail
django
headless
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# [Wagtail Headless Preview](https://pypi.org/project/wagtail-headless-preview/)
[![Build status](https://img.shields.io/github/actions/workflow/status/torchbox/wagtail-headless-preview/test.yml)](https://github.com/torchbox/wagtail-headless-preview/actions)
[![PyPI](https://img.shields.io/pypi/v/wagtail-headless-preview.svg)](https://pypi.org/project/wagtail-headless-preview/)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![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.models import Page
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.models import Page
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):
# Delegate to detail_view, specifically so there's no
# difference between serialization formats.
self.action = "detail_view"
return self.detail_view(request, 0)
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 py311-django4.2-wagtail5.1` or specific test
`tox -e py311-django4.2-wagtail5.0 -- 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
Raw data
{
"_id": null,
"home_page": "",
"name": "wagtail-headless-preview",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "",
"keywords": "Wagtail,Django,headless",
"author": "",
"author_email": "Dan Braghis <dan.braghis@torchbox.com>",
"download_url": "https://files.pythonhosted.org/packages/b4/28/87fda34fd89999bee03519a614ddca608def6d07b69d93365833c434b9c5/wagtail_headless_preview-0.8.0.tar.gz",
"platform": null,
"description": "# [Wagtail Headless Preview](https://pypi.org/project/wagtail-headless-preview/)\n\n[![Build status](https://img.shields.io/github/actions/workflow/status/torchbox/wagtail-headless-preview/test.yml)](https://github.com/torchbox/wagtail-headless-preview/actions)\n[![PyPI](https://img.shields.io/pypi/v/wagtail-headless-preview.svg)](https://pypi.org/project/wagtail-headless-preview/)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![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)\n\n\n## Overview\n\nWith Wagtail as the backend, and a separate app for the front-end (for example a single page React app), editors are no\nlonger able to preview their changes. This is because the front-end is no longer within Wagtail's direct control.\nThe preview data therefore needs to be exposed to the front-end app.\n\nThis package enables previews for Wagtail pages when used in a headless setup by routing the preview to the specified\nfront-end URL.\n\n## Setup\n\nInstall using pip:\n```sh\npip install wagtail-headless-preview\n```\n\nAfter installing the module, add `wagtail_headless_preview` to installed apps in your settings file:\n\n```python\n# settings.py\n\nINSTALLED_APPS = [\n # ...\n \"wagtail_headless_preview\",\n]\n```\n\nRun migrations:\n\n```sh\n$ python manage.py migrate\n```\n\nThen configure the preview client URL using the `CLIENT_URLS` option in the `WAGTAIL_HEADLESS_PREVIEW` setting.\n\n## Configuration\n\n`wagtail_headless_preview` uses a single settings dictionary:\n\n```python\n# settings.py\n\nWAGTAIL_HEADLESS_PREVIEW = {\n \"CLIENT_URLS\": {}, # defaults to an empty dict. You must at the very least define the default client URL.\n \"SERVE_BASE_URL\": None, # can be used for HeadlessServeMixin\n \"REDIRECT_ON_PREVIEW\": False, # set to True to redirect to the preview instead of using the Wagtail default mechanism\n \"ENFORCE_TRAILING_SLASH\": True, # set to False in order to disable the trailing slash enforcement\n}\n```\n\n### Single site setup\n\nFor single sites, add the front-end URL as the default entry:\n\n```python\nWAGTAIL_HEADLESS_PREVIEW = {\n \"CLIENT_URLS\": {\n \"default\": \"http://localhost:8020\",\n }\n}\n```\n\nIf you have configured your Wagtail `Site` entry to use the front-end URL, then you can update your configuration to:\n\n```python\nWAGTAIL_HEADLESS_PREVIEW = {\n \"CLIENT_URLS\": {\n \"default\": \"{SITE_ROOT_URL}\",\n }\n}\n```\n\nThe `{SITE_ROOT_URL}` placeholder is replaced with the `root_url` property of the `Site` the preview page belongs to.\n\n\n### Multi-site setup\n\nFor a multi-site setup, add each site as a separate entry in the `CLIENT_URLS` option in the `WAGTAIL_HEADLESS_PREVIEW` setting:\n\n```python\nWAGTAIL_HEADLESS_PREVIEW = {\n \"CLIENT_URLS\": {\n \"default\": \"https://wagtail.org\", # adjust to match your front-end URL. e.g. locally it may be something like http://localhost:8020\n \"cms.wagtail.org\": \"https://wagtail.org\",\n \"cms.torchbox.com\": \"http://torchbox.com\",\n },\n # ...\n}\n```\n\n### Serve URL\n\nTo make the editing experience seamles and to avoid server errors due to missing templates,\nyou can use the `HeadlessMixin` which combines the `HeadlessServeMixin` and `HeadlessPreviewMixin` mixins.\n\n`HeadlessServeMixin` overrides the Wagtail `Page.serve` method to redirect to the client URL. By default,\nit uses the hosts defined in `CLIENT_URLS`. However, you can provide a single URL to rule them all:\n\n```python\n# settings.py\n\nWAGTAIL_HEADLESS_PREVIEW = {\n # ...\n \"SERVE_BASE_URL\": \"https://my.headless.site\",\n}\n```\n\n### Enforce trailing slash\n\nBy default, `wagtail_headless_preview` enforces a trailing slash on the client URL. You can disable this behaviour by\nsetting `ENFORCE_TRAILING_SLASH` to `False`:\n\n```python\n# settings.py\nWAGTAIL_HEADLESS_PREVIEW = {\n # ...\n \"ENFORCE_TRAILING_SLASH\": False\n}\n```\n\n## Usage\n\nTo enable preview as well as wire in the \"View live\" button in the Wagtail UI, add the `HeadlessMixin`\nto your `Page` class:\n\n```python\nfrom wagtail.models import Page\nfrom wagtail_headless_preview.models import HeadlessMixin\n\n\nclass MyWonderfulPage(HeadlessMixin, Page):\n pass\n```\n\nIf you require more granular control, or if you've modified you `Page` model's `serve` method, you can\nadd `HeadlessPreviewMixin` to your `Page` class to only handle previews:\n\n```python\nfrom wagtail.models import Page\nfrom wagtail_headless_preview.models import HeadlessPreviewMixin\n\n\nclass MyWonderfulPage(HeadlessPreviewMixin, Page):\n pass\n```\n\n## How will my front-end app display preview content?\n\nThis depends on your project, as it will be dictated by the requirements of your front-end app.\n\nThe following example uses a Wagtail API endpoint to access previews -\nyour app may opt to access page previews using [GraphQL](https://wagtail.io/blog/getting-started-with-wagtail-and-graphql/) instead.\n\n### Example\n\nThis example sets up an API endpoint which will return the preview for a page, and then displays that data\non a simplified demo front-end app.\n\n* Add `wagtail.api.v2` to the installed apps:\n```python\n# settings.py\n\nINSTALLED_APPS = [\n # ...\n \"wagtail.api.v2\",\n]\n```\n\n* create an `api.py` file in your project directory:\n\n```python\nfrom django.contrib.contenttypes.models import ContentType\n\nfrom wagtail.api.v2.router import WagtailAPIRouter\nfrom wagtail.api.v2.views import PagesAPIViewSet\n\nfrom wagtail_headless_preview.models import PagePreview\nfrom rest_framework.response import Response\n\n\n# Create the router. \"wagtailapi\" is the URL namespace\napi_router = WagtailAPIRouter(\"wagtailapi\")\n\n\nclass PagePreviewAPIViewSet(PagesAPIViewSet):\n known_query_parameters = PagesAPIViewSet.known_query_parameters.union(\n [\"content_type\", \"token\"]\n )\n\n def listing_view(self, request):\n # Delegate to detail_view, specifically so there's no\n # difference between serialization formats.\n self.action = \"detail_view\"\n return self.detail_view(request, 0)\n\n def detail_view(self, request, pk):\n page = self.get_object()\n serializer = self.get_serializer(page)\n return Response(serializer.data)\n\n def get_object(self):\n app_label, model = self.request.GET[\"content_type\"].split(\".\")\n content_type = ContentType.objects.get(app_label=app_label, model=model)\n\n page_preview = PagePreview.objects.get(\n content_type=content_type, token=self.request.GET[\"token\"]\n )\n page = page_preview.as_page()\n if not page.pk:\n # fake primary key to stop API URL routing from complaining\n page.pk = 0\n\n return page\n\n\napi_router.register_endpoint(\"page_preview\", PagePreviewAPIViewSet)\n```\n\n* Register the API URLs so Django can route requests into the API:\n\n```python\n# urls.py\n\nfrom .api import api_router\n\nurlpatterns = [\n # ...\n path(\"api/v2/\", api_router.urls),\n # ...\n # Ensure that the api_router line appears above the default Wagtail page serving route\n path(\"\", include(wagtail_urls)),\n]\n```\n\nFor 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)\n\n* Next, add a `client/index.html` file in your project root. This will query the API to display our preview:\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n <script>\n function go() {\n var querystring = window.location.search.replace(/^\\?/, '');\n var params = {};\n querystring.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {\n params[decodeURIComponent(key)] = decodeURIComponent(value);\n });\n\n var apiUrl = 'http://localhost:8000/api/v2/page_preview/1/?content_type=' + encodeURIComponent(params['content_type']) + '&token=' + encodeURIComponent(params['token']) + '&format=json';\n fetch(apiUrl).then(function(response) {\n response.text().then(function(text) {\n document.body.innerText = text;\n });\n });\n }\n </script>\n</head>\n<body onload=\"go()\"></body>\n</html>\n```\n\n\n* Install [django-cors-headers](https://pypi.org/project/django-cors-headers/): `pip install django-cors-headers`\n* Add CORS config to your settings file to allow the front-end to access the API\n\n```python\n# settings.py\nCORS_ORIGIN_ALLOW_ALL = True\nCORS_URLS_REGEX = r\"^/api/v2/\"\n```\n\nand follow the rest of the [setup instructions for django-cors-headers](https://github.com/ottoyiu/django-cors-headers#setup).\n\n* Start up your site as normal: `python manage.py runserver 0:8000`\n* Serve the front-end `client/index.html` at `http://localhost:8020/`\n - this can be done by running `python3 -m http.server 8020` from inside the client directory\n* From the wagtail admin interface, edit (or create) and preview a page that uses `HeadlessPreviewMixin`\n\nThe preview page should now show you the API response for the preview! \ud83c\udf89\n\nThis is where a real front-end would take over and display the preview as it would be seen on the live site.\n\n## Contributing\n\nAll contributions are welcome!\n\nNote that this project uses [pre-commit](https://github.com/pre-commit/pre-commit). To set up locally:\n\n```shell\n# if you don't have it yet\n$ pip install pre-commit\n# go to the project directory\n$ cd wagtail-headless-preview\n# initialize pre-commit\n$ pre-commit install\n\n# Optional, run all checks once for this, then the checks will run only on the changed files\n$ pre-commit run --all-files\n```\n\n### How to run tests\n\nNow you can run tests as shown below:\n\n```sh\ntox -p\n```\n\nor, you can run them for a specific environment `tox -e py311-django4.2-wagtail5.1` or specific test\n`tox -e py311-django4.2-wagtail5.0 -- wagtail_headless_preview.tests.test_frontend.TestFrontendViews.test_redirect_on_preview`\n\n## Credits\n\n- Matthew Westcott ([@gasman](https://github.com/gasman)), initial proof of concept\n- Karl Hobley ([@kaedroho](https://github.com/kaedroho)), PoC improvements\n\n",
"bugtrack_url": null,
"license": "",
"summary": "Enhance Wagtail previews in headless setups.",
"version": "0.8.0",
"project_urls": {
"Changelog": "https://github.com/torchbox/wagtail-headless-preview/blob/main/CHANGELOG.md",
"Issues": "https://github.com/torchbox/wagtail-headless-preview/issues",
"Repository": "https://github.com/torchbox/wagtail-headless-preview"
},
"split_keywords": [
"wagtail",
"django",
"headless"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "6257bb6c10a11f7a0ec0d8e10ec99b4c7b5a6036e8846e61d145966b6c72113d",
"md5": "13ec72e351fb89cb27a5813b931d63c3",
"sha256": "91b305c36573490856e212f9e5645ada81f72ad37f1bb00e6feddc5537252358"
},
"downloads": -1,
"filename": "wagtail_headless_preview-0.8.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "13ec72e351fb89cb27a5813b931d63c3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 12301,
"upload_time": "2024-02-23T15:53:43",
"upload_time_iso_8601": "2024-02-23T15:53:43.904476Z",
"url": "https://files.pythonhosted.org/packages/62/57/bb6c10a11f7a0ec0d8e10ec99b4c7b5a6036e8846e61d145966b6c72113d/wagtail_headless_preview-0.8.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "b42887fda34fd89999bee03519a614ddca608def6d07b69d93365833c434b9c5",
"md5": "ac63d07498f4dcf17073a544f2e7a394",
"sha256": "581d8419cd1ef1f7de88235445e9695e5591d46259283d56bfe814e8620fa1d5"
},
"downloads": -1,
"filename": "wagtail_headless_preview-0.8.0.tar.gz",
"has_sig": false,
"md5_digest": "ac63d07498f4dcf17073a544f2e7a394",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 13957,
"upload_time": "2024-02-23T15:53:44",
"upload_time_iso_8601": "2024-02-23T15:53:44.986116Z",
"url": "https://files.pythonhosted.org/packages/b4/28/87fda34fd89999bee03519a614ddca608def6d07b69d93365833c434b9c5/wagtail_headless_preview-0.8.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-02-23 15:53:44",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "torchbox",
"github_project": "wagtail-headless-preview",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "wagtail-headless-preview"
}