Skip to content

Commit c986e19

Browse files
committed
Build BlogPosting JSON-LD as Python dict instead of in template
1 parent f11a111 commit c986e19

File tree

5 files changed

+75
-27
lines changed

5 files changed

+75
-27
lines changed

tbx/blog/models.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,53 @@ def first_author(self):
228228
return author.author
229229
return None
230230

231+
def get_blog_posting_jsonld(self, request=None):
232+
"""Build the BlogPosting JSON-LD structured data as a dict."""
233+
# Get site for URLs - prefer from request, fall back to page's site
234+
site = getattr(request, "site", None) if request else None
235+
if not site:
236+
site = self.get_site()
237+
root_url = site.root_url if site else ""
238+
239+
data = {
240+
"@context": "https://schema.org",
241+
"@type": "BlogPosting",
242+
"mainEntityOfPage": {
243+
"@type": "WebPage",
244+
"@id": self.get_full_url(request),
245+
},
246+
"headline": self.title,
247+
"description": self.search_description or self.listing_summary or "",
248+
"publisher": {
249+
"@type": "Organization",
250+
"name": "Torchbox",
251+
"logo": {
252+
"@type": "ImageObject",
253+
"url": f"{root_url}/apple-touch-icon.png",
254+
},
255+
},
256+
"datePublished": self.date.isoformat(),
257+
"dateModified": (
258+
self.last_published_at.date().isoformat()
259+
if self.last_published_at
260+
else self.date.isoformat()
261+
),
262+
}
263+
264+
if self.feed_image:
265+
data["image"] = self.feed_image.get_rendition("width-1200|format-webp").url
266+
267+
if self.first_author:
268+
author_data = {
269+
"@type": "Person",
270+
"name": self.first_author.name,
271+
}
272+
if self.first_author.person_page:
273+
author_data["url"] = self.first_author.person_page.get_full_url(request)
274+
data["author"] = author_data
275+
276+
return data
277+
231278
@property
232279
def read_time(self):
233280
if self.body_word_count:

tbx/blog/templatetags/__init__.py

Whitespace-only changes.

tbx/blog/templatetags/blog_tags.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import json
2+
3+
from django import template
4+
from django.utils.safestring import mark_safe
5+
6+
7+
register = template.Library()
8+
9+
10+
@register.simple_tag(takes_context=True)
11+
def blog_posting_jsonld(context):
12+
"""Return the BlogPosting JSON-LD structured data as JSON."""
13+
page = context.get("page")
14+
request = context.get("request")
15+
if page and hasattr(page, "get_blog_posting_jsonld"):
16+
return mark_safe(json.dumps(page.get_blog_posting_jsonld(request), indent=4)) # noqa: S308
17+
return ""

tbx/core/templatetags/util_tags.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import json
2+
13
from django import template
4+
from django.utils.safestring import mark_safe
25
from django.utils.text import camel_case_to_spaces, slugify
36

47
from wagtail.blocks import StreamValue
@@ -9,6 +12,12 @@
912
register = template.Library()
1013

1114

15+
@register.filter(name="json")
16+
def json_filter(value):
17+
"""Serialize a value to JSON."""
18+
return mark_safe(json.dumps(value, indent=4)) # noqa: S308
19+
20+
1221
# Social text
1322
@register.filter(name="social_text")
1423
def social_text(page, site):
Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,4 @@
1-
{% load wagtailcore_tags wagtailimages_tags %}
1+
{% load blog_tags %}
22
<script type="application/ld+json">
3-
{
4-
"@context": "https://schema.org",
5-
"@type": "BlogPosting",
6-
"mainEntityOfPage": {
7-
"@type": "WebPage",
8-
"@id": "{% fullpageurl page %}"
9-
},
10-
"headline": "{{ page.title|escapejs }}",
11-
"description": "{% if page.search_description %}{{ page.search_description|escapejs }}{% elif page.listing_summary %}{{ page.listing_summary|escapejs }}{% endif %}"{% if page.feed_image %},
12-
"image": "{% image page.feed_image format-webp width-1200 as blog_image %}{{ blog_image.url }}"{% endif %}{% if page.first_author %},
13-
"author": {
14-
"@type": "Person",
15-
"name": "{{ page.first_author.name|escapejs }}"{% if page.first_author.person_page %},
16-
"url": "{% fullpageurl page.first_author.person_page %}"{% endif %}
17-
}{% endif %},
18-
"publisher": {
19-
"@type": "Organization",
20-
"name": "Torchbox",
21-
"logo": {
22-
"@type": "ImageObject",
23-
"url": "{% wagtail_site as current_site %}{{ current_site.root_url }}/apple-touch-icon.png"
24-
}
25-
},
26-
"datePublished": "{{ page.date|date:'Y-m-d' }}",
27-
"dateModified": "{% if page.last_published_at %}{{ page.last_published_at|date:'Y-m-d' }}{% else %}{{ page.date|date:'Y-m-d' }}{% endif %}"
28-
}
3+
{% blog_posting_jsonld %}
294
</script>

0 commit comments

Comments
 (0)