Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
This package adds support for AMP web stories to Wagtail. Stories created elsewhere can be linked or embedded from their original URL locations, or imported as Wagtail pages, including optionally importing images and videos into the Wagtail media library.
wagtail-webstories
is compatible with Wagtail 4.1 and above.
Install with pip:
pip install wagtail-webstories
Add to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
'wagtail_webstories',
# ...
]
Run migrations:
./manage.py migrate
To embed a web story into a regular (non-AMP) StreamField-based page, include the wagtail_webstories.blocks.ExternalStoryEmbedBlock
block type in your StreamField definition:
from wagtail_webstories.blocks import ExternalStoryEmbedBlock
class BlogPage(Page):
body = StreamField([
('heading', blocks.CharBlock()),
('paragraph', blocks.RichTextBlock()),
('story_embed', ExternalStoryEmbedBlock()),
])
This block allows the page author to provide the URL to an AMP web story, which will render on the front-end template (when using {% include_block %}
) as an <amp-story-player>
element; your template should include the necessary scripts for rendering this, along with a CSS rule to specify appropriate dimensions:
<script async src="https://cdn.ampproject.org/amp-story-player-v0.js"></script>
<link href="https://cdn.ampproject.org/amp-story-player-v0.css" rel="stylesheet" type="text/css">
<style>
amp-story-player { width: 360px; height: 600px; }
</style>
To include a link to a story rather than embedding it, wagtail_webstories.blocks.ExternalStoryBlock
can be used in place of ExternalStoryEmbedBlock
. The default template wagtail_webstories/blocks/external_story_poster_link.html
outputs a 'card' rendering of the story using the story's poster image, to be used with the following CSS:
.webstory-poster {
display: block; width: 300px; height: 400px; border-radius: 15px; background-size: cover; position: relative;
}
.webstory-poster .webstory-info {
position: absolute; bottom: 0; width: 100%; background-color: #ccc; color: black; border-bottom-left-radius: 15px; border-bottom-right-radius: 15px;
}
.webstory-poster .title {
font-size: 1.5em; padding: 10px;
}
.webstory-poster .publisher {
padding: 0 10px 20px 10px;
}
.webstory-poster .publisher img {
vertical-align: middle;
}
External stories are handled through the model wagtail_webstories.models.ExternalStory
. To obtain an ExternalStory instance for a given URL, use: ExternalStory.get_for_url(story_url)
. The story's metadata is cached within the ExternalStory model to avoid having to re-fetch the story on every request - the available metadata fields are url
, title
, publisher
, publisher_logo_src
, poster_portrait_src
, poster_square_src
and poster_landscape_src
.
The StreamField block templates wagtail_webstories/blocks/external_story_embed_block.html
and wagtail_webstories/blocks/external_story_poster_link.html
can be used to render an ExternalStory object, by passing it as the variable story
:
# models.py
class BlogPostWithStoryPage(Page):
story_url = model.URLField(max_length=2048)
def get_context(self, request):
context = super().get_context(request)
context['story_obj'] = ExternalStory.get_for_url(self.story_url)
return context
{# blog_post_with_story_page.html #}
{% include "wagtail_webstories/blocks/external_story_embed_block.html" with story=story_obj %}
To allow stories to be imported as Wagtail pages, define a model that extends wagtail_webstories.models.BaseWebStoryPage
:
from wagtail_webstories.models import BaseWebStoryPage
class StoryPage(BaseWebStoryPage):
pass
Alternatively, if your project has an existing base page class that all page types must inherit from (which would prevent the use of BaseWebStoryPage in addition to that base class), extend wagtail_webstories.models.WebStoryPageMixin
and define content_panels
and promote_panels
to incorporate its panel definitions:
from wagtail_webstories.models import WebStoryPageMixin
class StoryPage(WebStoryPageMixin, BasePage):
content_panels = BasePage.content_panels + WebStoryPageMixin.web_story_content_panels
promote_panels = BasePage.promote_panels + WebStoryPageMixin.web_story_promote_panels
Now create a corresponding template that extends wagtail_webstories/base_web_story_page.html
:
{% extends "wagtail_webstories/base_web_story_page.html" %}
Define a setting WAGTAIL_WEBSTORIES_IMPORT_MODEL
pointing to the page model to use:
WAGTAIL_WEBSTORIES_IMPORT_MODEL = 'myapp.StoryPage'
This will now add a "Web stories" item to the Wagtail admin menu, allowing you to import stories by URL.
By default, all HTML elements and attributes not permitted by the AMP web story specification will be stripped out on importing and saving. To disable this, set WAGTAIL_WEBSTORIES_CLEAN_HTML
to False:
WAGTAIL_WEBSTORIES_CLEAN_HTML = False
By default, image references within imported stories are left at their original URLs. BaseWebStoryPage provides a method import_images()
to fetch all referenced images and import them into the Wagtail image library, de-duplicating if they already exist. It is recommended that you call this from a post_save
signal handler:
# myapp/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import StoryPage
@receiver(post_save, sender=StoryPage)
def import_story_images(sender, instance, **kwargs):
changed = instance.import_images()
if changed:
instance.save()
# myapp/apps.py
from django.apps import AppConfig
class MyappConfig(AppConfig):
name = 'myapp'
def ready(self):
import myapp.signals # noqa
# myapp/__init__.py
default_app_config = 'myapp.apps.MyappConfig'
Since importing images can be a time-consuming process, you may wish to offload the call to import_images
to a background task using Celery or similar, to avoid this blocking a web server thread.
To customise the creation of new images (e.g. to assign imported images to a particular collection, or to populate additional metadata fields on a custom image model), override the story page model's _create_image
method:
class StoryPage(BaseWebStoryPage):
def _create_image(self, file, title=None):
image = super()._create_image(file, title=title)
image.copyright = "All rights reserved"
return image
If you have wagtailmedia installed, you can similarly import videos into the local media library by calling import_videos()
. The signal handler above then becomes:
# myapp/signals.py
@receiver(post_save, sender=StoryPage)
def import_story_images(sender, instance, **kwargs):
images_changed = instance.import_images()
videos_changed = instance.import_videos()
if images_changed or videos_changed:
instance.save()
To embed or link an imported web story into a regular (non-AMP) StreamField-based page, include the wagtail_webstories.blocks.StoryEmbedBlock
or wagtail_webstories.blocks.StoryChooserBlock
block type in your StreamField definition. These work similarly to ExternalStoryEmbedBlock and ExternalStoryBlock, but provide the page author with a page chooser interface rather than a URL field.
from wagtail_webstories.blocks import StoryEmbedBlock
class BlogPage(Page):
body = StreamField([
('heading', blocks.CharBlock()),
('paragraph', blocks.RichTextBlock()),
('local_story_embed', StoryEmbedBlock(target_model=StoryPage)),
])
The target_model
argument is optional - by default, any page type inheriting from BaseWebStoryPage can be chosen. As with ExternalStoryEmbedBlock and ExternalStoryBlock, your page template must contain the appropriate JavaScript or CSS include for rendering the block.
The templates wagtail_webstories/blocks/story_poster_link.html
and wagtail_webstories/blocks/story_embed_block.html
expect a single variable page
containing the story page instance, so these can also be used outside of StreamField:
# models.py
class StoryIndexPage(Page):
def get_context(self, request):
context = super().get_context(request)
context['stories'] = StoryPage.objects.child_of(self).live().order_by('-first_published_at')
return context
{# story_index_page.html #}
{% for story in stories %}
{% include "wagtail_webstories/blocks/story_poster_link.html" with page=story %}
{% endfor %}
FAQs
AMP web story support for Wagtail
We found that wagtail-webstories demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.