118 lines
3.6 KiB
Python
118 lines
3.6 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
import json
|
|
import re
|
|
|
|
from molodetz import config
|
|
from molodetz.assets import static_url
|
|
from molodetz.seo_meta_text import DEFAULT_DESCRIPTION
|
|
|
|
DEFAULT_OG_IMAGE_PATH = "/static/media/flyer-elite-twice.jpg"
|
|
|
|
|
|
def base_url(request):
|
|
if config.SITE_URL:
|
|
return config.SITE_URL
|
|
return str(request.base_url).rstrip("/")
|
|
|
|
|
|
def _clean(text):
|
|
return re.sub(r"[*_`#\[\]]", "", text or "").strip()
|
|
|
|
|
|
def website_schema(request):
|
|
base = base_url(request)
|
|
return {
|
|
"@context": "https://schema.org",
|
|
"@type": "WebSite",
|
|
"name": config.APP_TITLE,
|
|
"url": base + "/",
|
|
"inLanguage": "en",
|
|
"potentialAction": {
|
|
"@type": "SearchAction",
|
|
"target": base + "/docs/search?q={search_term_string}",
|
|
"query-input": "required name=search_term_string",
|
|
},
|
|
}
|
|
|
|
|
|
def organization_schema(request):
|
|
base = base_url(request)
|
|
return {"@context": "https://schema.org", "@type": "Organization", "name": config.APP_TITLE, "url": base + "/"}
|
|
|
|
|
|
def breadcrumb_schema(request, breadcrumbs):
|
|
base = base_url(request)
|
|
return {
|
|
"@context": "https://schema.org",
|
|
"@type": "BreadcrumbList",
|
|
"itemListElement": [
|
|
{"@type": "ListItem", "position": index + 1, "name": _clean(name), "item": base + path}
|
|
for index, (name, path) in enumerate(breadcrumbs)
|
|
],
|
|
}
|
|
|
|
|
|
def article_schema(request, post, author):
|
|
base = base_url(request)
|
|
return {
|
|
"@context": "https://schema.org",
|
|
"@type": "BlogPosting",
|
|
"headline": _clean(post["title"]),
|
|
"datePublished": post.get("published_at") or post.get("created_at"),
|
|
"dateModified": post.get("updated_at"),
|
|
"inLanguage": "en",
|
|
"mainEntityOfPage": f"{base}/posts/{post['slug']}",
|
|
"author": {"@type": "Person", "name": author["username"] if author else "retoor", "url": f"{base}/people/{author['username'] if author else 'retoor'}"},
|
|
"publisher": {"@type": "Organization", "name": config.APP_TITLE},
|
|
}
|
|
|
|
|
|
def profile_schema(request, person):
|
|
base = base_url(request)
|
|
return {
|
|
"@context": "https://schema.org",
|
|
"@type": "ProfilePage",
|
|
"mainEntity": {"@type": "Person", "name": person["username"], "url": f"{base}/people/{person['username']}"},
|
|
}
|
|
|
|
|
|
def base_seo_context(
|
|
request,
|
|
title,
|
|
description=None,
|
|
robots="index,follow",
|
|
og_type="website",
|
|
og_image=None,
|
|
breadcrumbs=None,
|
|
schemas=None,
|
|
prev_url=None,
|
|
next_url=None,
|
|
keywords=None,
|
|
):
|
|
base = base_url(request)
|
|
canonical = base + request.url.path
|
|
page = request.query_params.get("page")
|
|
if page:
|
|
canonical += f"?page={page}"
|
|
image = og_image or static_url(DEFAULT_OG_IMAGE_PATH)
|
|
if image.startswith("/"):
|
|
image = base + image
|
|
crumbs = [(_clean(name), path) for name, path in (breadcrumbs or [])]
|
|
all_schemas = list(schemas or [])
|
|
if crumbs:
|
|
all_schemas.append(breadcrumb_schema(request, crumbs))
|
|
full_title = f"{title} | {config.APP_TITLE}" if title and title != config.APP_TITLE else config.APP_TITLE
|
|
return {
|
|
"title": full_title,
|
|
"description": description or DEFAULT_DESCRIPTION,
|
|
"robots": robots,
|
|
"canonical": canonical,
|
|
"og_type": og_type,
|
|
"og_image": image,
|
|
"breadcrumbs": crumbs,
|
|
"json_ld": [json.dumps(schema, ensure_ascii=False).replace("</", "<\\/") for schema in all_schemas],
|
|
"prev_url": prev_url,
|
|
"next_url": next_url,
|
|
"keywords": keywords or "molodetz, writing, craftsmanship, software, community",
|
|
}
|