Add direct-save public content API
This commit is contained in:
1
apps/content/api/__init__.py
Normal file
1
apps/content/api/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
85
apps/content/api/schemas.py
Normal file
85
apps/content/api/schemas.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from typing import Literal, Optional, Union
|
||||
|
||||
from ninja import Schema
|
||||
|
||||
|
||||
class MediaOut(Schema):
|
||||
url: str
|
||||
alt: str
|
||||
|
||||
|
||||
class SeoOut(Schema):
|
||||
title: str
|
||||
description: str
|
||||
image: Optional[MediaOut] = None
|
||||
|
||||
|
||||
class SiteContentOut(Schema):
|
||||
official_name: str
|
||||
short_name: str
|
||||
faculty_subline: str
|
||||
description: str
|
||||
logo: Optional[MediaOut] = None
|
||||
navigation: dict[str, str]
|
||||
contacts: dict[str, str]
|
||||
address: str
|
||||
footer_copy: str
|
||||
seo: SeoOut
|
||||
|
||||
|
||||
class PageBaseOut(Schema):
|
||||
slug: str
|
||||
title: str
|
||||
intro: str
|
||||
seo: SeoOut
|
||||
|
||||
|
||||
class HomePageOut(PageBaseOut):
|
||||
slug: Literal["home"]
|
||||
hero_heading: str
|
||||
hero_body: str
|
||||
hero_image: Optional[MediaOut] = None
|
||||
hero_action_label: str
|
||||
featured_events_heading: str
|
||||
featured_events_intro: str
|
||||
featured_events_link_label: str
|
||||
featured_posts_heading: str
|
||||
featured_posts_intro: str
|
||||
featured_posts_link_label: str
|
||||
participation_heading: str
|
||||
participation_body: str
|
||||
participation_image: Optional[MediaOut] = None
|
||||
participation_action_label: str
|
||||
|
||||
|
||||
class AboutItemOut(Schema):
|
||||
kind: str
|
||||
title: str
|
||||
text: str
|
||||
position: int
|
||||
|
||||
|
||||
class AboutPageOut(PageBaseOut):
|
||||
slug: Literal["about"]
|
||||
identity_heading: str
|
||||
identity_body: str
|
||||
mission_heading: str
|
||||
mission_body: str
|
||||
activities_heading: str
|
||||
activities_body: str
|
||||
values_heading: str
|
||||
values_body: str
|
||||
committees_heading: str
|
||||
committees_body: str
|
||||
affiliations_heading: str
|
||||
affiliations_body: str
|
||||
faq_heading: str
|
||||
contact_heading: str
|
||||
items: list[AboutItemOut]
|
||||
|
||||
|
||||
class ListingPageOut(PageBaseOut):
|
||||
slug: Literal["events", "blog"]
|
||||
|
||||
|
||||
PageContentOut = Union[HomePageOut, AboutPageOut, ListingPageOut]
|
||||
103
apps/content/api/views.py
Normal file
103
apps/content/api/views.py
Normal file
@@ -0,0 +1,103 @@
|
||||
from django.http import HttpResponse
|
||||
from ninja import Router
|
||||
from ninja.errors import HttpError
|
||||
|
||||
from apps.content.current import AboutContent, HomeContent, ListingContent, SiteContent
|
||||
from apps.content.api.schemas import AboutPageOut, HomePageOut, ListingPageOut, PageContentOut, SiteContentOut
|
||||
|
||||
|
||||
content_router = Router(tags=["Public content"])
|
||||
|
||||
|
||||
def _media(request, field, alt):
|
||||
if not field or not field.name:
|
||||
return None
|
||||
return {"url": request.build_absolute_uri(field.url), "alt": alt or ""}
|
||||
|
||||
|
||||
def _seo(request, obj):
|
||||
return {
|
||||
"title": obj.seo_title,
|
||||
"description": obj.seo_description,
|
||||
"image": _media(request, obj.seo_image, obj.seo_image_alt),
|
||||
}
|
||||
|
||||
|
||||
@content_router.get("/site", response=SiteContentOut)
|
||||
def get_site_content(request, response: HttpResponse):
|
||||
site = SiteContent.objects.filter(pk=1).first()
|
||||
if site is None:
|
||||
raise HttpError(404, "Site content not found")
|
||||
response["Cache-Control"] = "no-store"
|
||||
return {
|
||||
"official_name": site.official_name,
|
||||
"short_name": site.short_name,
|
||||
"faculty_subline": site.faculty_subline,
|
||||
"description": site.description,
|
||||
"logo": _media(request, site.logo, site.logo_alt),
|
||||
"navigation": {
|
||||
"home": site.nav_home,
|
||||
"about": site.nav_about,
|
||||
"events": site.nav_events,
|
||||
"blog": site.nav_blog,
|
||||
},
|
||||
"contacts": {
|
||||
"email": site.email,
|
||||
"phone": site.phone,
|
||||
"telegram": site.telegram_url,
|
||||
"instagram": site.instagram_url,
|
||||
"website": site.website_url,
|
||||
},
|
||||
"address": site.address,
|
||||
"footer_copy": site.footer_copy,
|
||||
"seo": _seo(request, site),
|
||||
}
|
||||
|
||||
|
||||
@content_router.get("/pages/{slug}", response=PageContentOut)
|
||||
def get_page_content(request, response: HttpResponse, slug: str):
|
||||
response["Cache-Control"] = "no-store"
|
||||
if slug == "home":
|
||||
page = HomeContent.objects.filter(pk=1).first()
|
||||
if page is None:
|
||||
raise HttpError(404, "Page content not found")
|
||||
return {
|
||||
"slug": "home", "title": page.title, "intro": page.intro, "seo": _seo(request, page),
|
||||
"hero_heading": page.hero_heading, "hero_body": page.hero_body,
|
||||
"hero_image": _media(request, page.hero_image, page.hero_image_alt),
|
||||
"hero_action_label": page.hero_action_label,
|
||||
"featured_events_heading": page.featured_events_heading,
|
||||
"featured_events_intro": page.featured_events_intro,
|
||||
"featured_events_link_label": page.featured_events_link_label,
|
||||
"featured_posts_heading": page.featured_posts_heading,
|
||||
"featured_posts_intro": page.featured_posts_intro,
|
||||
"featured_posts_link_label": page.featured_posts_link_label,
|
||||
"participation_heading": page.participation_heading,
|
||||
"participation_body": page.participation_body,
|
||||
"participation_image": _media(request, page.participation_image, page.participation_image_alt),
|
||||
"participation_action_label": page.participation_action_label,
|
||||
}
|
||||
|
||||
if slug == "about":
|
||||
page = AboutContent.objects.filter(pk=1).first()
|
||||
if page is None:
|
||||
raise HttpError(404, "Page content not found")
|
||||
return {
|
||||
"slug": "about", "title": page.title, "intro": page.intro, "seo": _seo(request, page),
|
||||
"identity_heading": page.identity_heading, "identity_body": page.identity_body,
|
||||
"mission_heading": page.mission_heading, "mission_body": page.mission_body,
|
||||
"activities_heading": page.activities_heading, "activities_body": page.activities_body,
|
||||
"values_heading": page.values_heading, "values_body": page.values_body,
|
||||
"committees_heading": page.committees_heading, "committees_body": page.committees_body,
|
||||
"affiliations_heading": page.affiliations_heading, "affiliations_body": page.affiliations_body,
|
||||
"faq_heading": page.faq_heading, "contact_heading": page.contact_heading,
|
||||
"items": list(page.items.values("kind", "title", "text", "position")),
|
||||
}
|
||||
|
||||
if slug in ("events", "blog"):
|
||||
page = ListingContent.objects.filter(slug=slug).first()
|
||||
if page is None:
|
||||
raise HttpError(404, "Page content not found")
|
||||
return {"slug": page.slug, "title": page.title, "intro": page.intro, "seo": _seo(request, page)}
|
||||
|
||||
raise HttpError(404, "Page content not found")
|
||||
Reference in New Issue
Block a user