Move blog index rendering from Python to .sx composition defcomps
BlogPageService.index_data() assembles all data (cards, filters, actions) and 7 new .sx defcomps handle rendering: main content, aside, filter, actions, tag groups filter, authors filter, and sentinel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -171,6 +171,165 @@ class BlogPageService:
|
||||
"csrf": generate_csrf_token(),
|
||||
}
|
||||
|
||||
async def index_data(self, session, **kw):
|
||||
"""Blog index page data — posts or pages listing with filters."""
|
||||
from quart import g, request, url_for as qurl
|
||||
from bp.blog.services.posts_data import posts_data
|
||||
from bp.blog.services.pages_data import pages_data
|
||||
from bp.blog.filters.qs import decode
|
||||
from shared.utils import host_url
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
|
||||
q = decode()
|
||||
content_type = request.args.get("type", "posts")
|
||||
is_admin = bool((g.get("rights") or {}).get("admin"))
|
||||
user = getattr(g, "user", None)
|
||||
csrf = generate_csrf_token()
|
||||
|
||||
blog_url_base = host_url(qurl("blog.index")).rstrip("/index").rstrip("/")
|
||||
|
||||
if content_type == "pages":
|
||||
data = await pages_data(session, q.page, q.search)
|
||||
posts_list = data.get("pages", [])
|
||||
tag_groups_raw = []
|
||||
authors_raw = []
|
||||
draft_count = 0
|
||||
selected_tags = ()
|
||||
selected_authors = ()
|
||||
selected_groups = ()
|
||||
else:
|
||||
show_drafts = bool(q.drafts and user)
|
||||
drafts_user_id = None if (not show_drafts or is_admin) else user.id
|
||||
count_drafts_uid = None if (user and is_admin) else (user.id if user else False)
|
||||
data = await posts_data(
|
||||
session, q.page, q.search, q.sort, q.selected_tags,
|
||||
q.selected_authors, q.liked,
|
||||
drafts=show_drafts, drafts_user_id=drafts_user_id,
|
||||
count_drafts_for_user_id=count_drafts_uid,
|
||||
selected_groups=q.selected_groups,
|
||||
)
|
||||
posts_list = data.get("posts", [])
|
||||
tag_groups_raw = data.get("tag_groups", [])
|
||||
authors_raw = data.get("authors", [])
|
||||
draft_count = data.get("draft_count", 0)
|
||||
selected_tags = q.selected_tags
|
||||
selected_authors = q.selected_authors
|
||||
selected_groups = q.selected_groups
|
||||
|
||||
page_num = data.get("page", q.page)
|
||||
total_pages = data.get("total_pages", 1)
|
||||
card_widgets = data.get("card_widgets_html", {})
|
||||
|
||||
current_local_href = f"{blog_url_base}/index"
|
||||
if content_type == "pages":
|
||||
current_local_href += "?type=pages"
|
||||
hx_select = "#main-panel"
|
||||
|
||||
# Serialize posts for cards
|
||||
def _format_ts(dt):
|
||||
if not dt:
|
||||
return ""
|
||||
return dt.strftime("%-d %b %Y at %H:%M") if hasattr(dt, "strftime") else str(dt)
|
||||
|
||||
cards = []
|
||||
for p in posts_list:
|
||||
slug = p.get("slug", "")
|
||||
href = f"{blog_url_base}/{slug}/"
|
||||
status = p.get("status", "published")
|
||||
is_draft = status == "draft"
|
||||
ts = _format_ts(p.get("updated_at") if is_draft else p.get("published_at"))
|
||||
tags = []
|
||||
for t in (p.get("tags") or []):
|
||||
name = t.get("name") or getattr(t, "name", "")
|
||||
fi = t.get("feature_image") or getattr(t, "feature_image", None)
|
||||
tags.append({"name": name, "src": fi or "", "initial": name[:1] if name else ""})
|
||||
authors = []
|
||||
for a in (p.get("authors") or []):
|
||||
name = a.get("name") or getattr(a, "name", "")
|
||||
img = a.get("profile_image") or getattr(a, "profile_image", None)
|
||||
authors.append({"name": name, "image": img or ""})
|
||||
card = {
|
||||
"slug": slug, "href": href, "hx_select": hx_select,
|
||||
"title": p.get("title", ""), "feature_image": p.get("feature_image"),
|
||||
"excerpt": p.get("custom_excerpt") or p.get("excerpt", ""),
|
||||
"is_draft": is_draft,
|
||||
"publish_requested": p.get("publish_requested", False) if is_draft else False,
|
||||
"status_timestamp": ts,
|
||||
"tags": tags, "authors": authors,
|
||||
"has_like": bool(user),
|
||||
}
|
||||
if user:
|
||||
card["liked"] = p.get("is_liked", False)
|
||||
card["like_url"] = f"{blog_url_base}/{slug}/like/toggle/"
|
||||
card["csrf_token"] = csrf
|
||||
widget = card_widgets.get(str(p.get("id", "")), "")
|
||||
if widget:
|
||||
card["widget"] = widget
|
||||
# Page-specific fields
|
||||
features = p.get("features") or {}
|
||||
if content_type == "pages":
|
||||
card["has_calendar"] = features.get("calendar", False)
|
||||
card["has_market"] = features.get("market", False)
|
||||
card["pub_timestamp"] = ts
|
||||
cards.append(card)
|
||||
|
||||
# Serialize tag groups for filter
|
||||
tag_groups = []
|
||||
for grp in tag_groups_raw:
|
||||
g_slug = grp.get("slug", "") if isinstance(grp, dict) else getattr(grp, "slug", "")
|
||||
g_name = grp.get("name", "") if isinstance(grp, dict) else getattr(grp, "name", "")
|
||||
g_fi = grp.get("feature_image") if isinstance(grp, dict) else getattr(grp, "feature_image", None)
|
||||
g_colour = grp.get("colour") if isinstance(grp, dict) else getattr(grp, "colour", None)
|
||||
g_count = grp.get("post_count", 0) if isinstance(grp, dict) else getattr(grp, "post_count", 0)
|
||||
if g_count <= 0 and g_slug not in selected_groups:
|
||||
continue
|
||||
tag_groups.append({
|
||||
"slug": g_slug, "name": g_name, "feature_image": g_fi,
|
||||
"colour": g_colour, "post_count": g_count,
|
||||
"is_selected": g_slug in selected_groups,
|
||||
})
|
||||
|
||||
# Serialize authors for filter
|
||||
authors_list = []
|
||||
for a in authors_raw:
|
||||
a_slug = a.get("slug", "") if isinstance(a, dict) else getattr(a, "slug", "")
|
||||
a_name = a.get("name", "") if isinstance(a, dict) else getattr(a, "name", "")
|
||||
a_img = a.get("profile_image") if isinstance(a, dict) else getattr(a, "profile_image", None)
|
||||
a_count = a.get("published_post_count", 0) if isinstance(a, dict) else getattr(a, "published_post_count", 0)
|
||||
authors_list.append({
|
||||
"slug": a_slug, "name": a_name, "profile_image": a_img,
|
||||
"published_post_count": a_count,
|
||||
"is_selected": a_slug in selected_authors,
|
||||
})
|
||||
|
||||
# Filter summary names
|
||||
tg_summary_names = [grp["name"] for grp in tag_groups if grp["is_selected"]]
|
||||
au_summary_names = [a["name"] for a in authors_list if a["is_selected"]]
|
||||
|
||||
return {
|
||||
"content_type": content_type,
|
||||
"view": q.view,
|
||||
"cards": cards,
|
||||
"page": page_num,
|
||||
"total_pages": total_pages,
|
||||
"current_local_href": current_local_href,
|
||||
"hx_select": hx_select,
|
||||
"is_admin": is_admin,
|
||||
"has_user": bool(user),
|
||||
"draft_count": draft_count,
|
||||
"drafts": bool(q.drafts) if user else False,
|
||||
"new_post_href": f"{blog_url_base}/new/",
|
||||
"new_page_href": f"{blog_url_base}/new-page/",
|
||||
"tag_groups": tag_groups,
|
||||
"authors": authors_list,
|
||||
"is_any_group": len(selected_groups) == 0 and len(selected_tags) == 0,
|
||||
"is_any_author": len(selected_authors) == 0,
|
||||
"tg_summary": ", ".join(tg_summary_names) if tg_summary_names else "",
|
||||
"au_summary": ", ".join(au_summary_names) if au_summary_names else "",
|
||||
"blog_url_base": blog_url_base,
|
||||
"csrf": csrf,
|
||||
}
|
||||
|
||||
async def post_admin_data(self, session, *, slug=None, **kw):
|
||||
"""Post admin panel — just needs post loaded into context."""
|
||||
from quart import g
|
||||
|
||||
Reference in New Issue
Block a user