Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft: improved article body output #26

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions aktuelt/handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Extends https://github.com/wagtail/wagtail/blob/main/wagtail/images/rich_text/__init__.py#L8
from django.utils.html import escape
from wagtail.images.formats import get_image_format
from wagtail.images.rich_text import ImageEmbedHandler


class CustomImageEmbedHandler(ImageEmbedHandler):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A handler is how Wagtail converts internal xml/html-like format into frontend html. The current example works but should ideally be expanded to include srcset of all relevant sizes based on output of https://github.com/gathering/tgno-backend/blob/main/aktuelt/serializers.py#L11 (which is used for other news article images)

identifier = "image"

@classmethod
def expand_db_attributes_many(cls, attrs_list: list[dict]) -> list[str]:
"""
Given a dict of attributes from the <embed> tag, return the real HTML
representation for use on the front-end.
"""
images = cls.get_many(attrs_list)

tags = []
for attrs, image in zip(attrs_list, images):
if image:
image_format = get_image_format(attrs["format"])
tag = image_format.image_to_html(image, attrs.get("alt", ""))
else:
tag = '<img alt="">'

tags.append(tag)

return tags
15 changes: 15 additions & 0 deletions aktuelt/image_formats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Ref: https://docs.wagtail.org/en/stable/advanced_topics/images/changing_rich_text_representation.html
from django.utils.html import format_html
from wagtail.images.formats import Format, register_image_format


class InlineNewsImageFormat(Format):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This formatter is what Wagtail uses to display different image "formats" that can be selected when inserting an image in editor UI. This example does nothing useful, but outlines how we could use this to define a new format. This could for example be

  • A fullscreen image
  • An image with or without caption
  • An image that includes custom field data
  • ...etc

def image_to_html(self, image, alt_text, extra_attributes=None):
default_html = super().image_to_html(image, alt_text, extra_attributes)

return format_html("<div>{}<figcaption>{}</figcaption></div>", default_html, alt_text)


register_image_format(
InlineNewsImageFormat("inline_news_image", "Inline news image", "image-classes object-contain", "max-3200x3200")
)
3 changes: 2 additions & 1 deletion aktuelt/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from aktuelt.constants import ContributionTypes
from aktuelt.serializers import (
ContributorsSerializer,
NewsBodySerializer,
NewsImageSerializer,
NewsPageGallerySerializer,
NewsPageTagsSerializer,
Expand Down Expand Up @@ -99,7 +100,7 @@ def schedule(self):

api_fields = [
APIField("intro"),
APIField("body"),
APIField("body", serializer=NewsBodySerializer()),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also the option of going with https://docs.wagtail.org/en/stable/topics/streamfield.html which seems to allow a few more advanced options. Mainly I think it could be useful if we wanted to transition to JSON array output rather than html and leave more of the body rendering logic to frontend (and remove the need for extending/hacking into wagtail frontend related code).

# TODO: Replace with prettier (main model based?) serializer pattern?
APIField("contributors", serializer=ContributorsSerializer(source="news_page_contributors")),
APIField("tags", serializer=NewsPageTagsSerializer()),
Expand Down
6 changes: 6 additions & 0 deletions aktuelt/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from rest_framework.fields import Field
from wagtail.images.api.fields import ImageRenditionField
from wagtail.rich_text import expand_db_html


class NewsBodySerializer(Field):
def to_representation(self, value):
return expand_db_html(value)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what Wagtail uses in own frontend when rendering this type of data, mainly via a richtext template filter



class NewsImageSerializer(Field):
Expand Down
10 changes: 10 additions & 0 deletions aktuelt/wagtail_hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# https://docs.wagtail.org/en/stable/extending/rich_text_internals.html#rewrite-handlers
from wagtail import hooks

from aktuelt.handlers import CustomImageEmbedHandler


# Higher order to make sure it runs after image handler hook in images app
@hooks.register("register_rich_text_features", order=10)
def register_embed_handler(features):
features.register_embed_type(CustomImageEmbedHandler)