Phase 7: Replace render_template() with s-expression rendering in all POST/PUT/DELETE routes

Eliminates all render_template() calls from POST/PUT/DELETE handlers across
all 7 services. Moves sexp_components.py into sexp/ packages per service.

- Blog: like toggle, snippets, cache clear, features/sumup/entry panels,
  create/delete market, WYSIWYG editor panel (render_editor_panel)
- Federation: like/unlike/boost/unboost, follow/unfollow, actor card,
  interaction buttons
- Events: ticket widget, checkin, confirm/decline/provisional, tickets
  config, posts CRUD, description edit/save, calendar/slot/ticket_type
  CRUD, payments, buy tickets, day main panel, entry page
- Market: like toggle, cart add response
- Account: newsletter toggle
- Cart: checkout error pages (3 handlers)
- Orders: checkout error page (1 handler)

Remaining render_template() calls are exclusively in GET handlers and
internal services (email templates, fragment endpoints).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 01:15:29 +00:00
parent e65232761b
commit 838ec982eb
64 changed files with 2920 additions and 545 deletions

View File

@@ -11,7 +11,6 @@ from datetime import datetime, timezone, timedelta
from quart import (
Blueprint,
request,
render_template,
redirect,
url_for,
session as qsession,
@@ -101,7 +100,7 @@ def register(url_prefix="/auth"):
redirect_url = pop_login_redirect_target()
return redirect(redirect_url)
from shared.sexp.page import get_template_context
from sexp_components import render_login_page
from sexp.sexp_components import render_login_page
ctx = await get_template_context()
return await render_login_page(ctx)
@@ -112,14 +111,10 @@ def register(url_prefix="/auth"):
is_valid, email = validate_email(email_input)
if not is_valid:
return (
await render_template(
"auth/login.html",
error="Please enter a valid email address.",
email=email_input,
),
400,
)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_login_page
ctx = await get_template_context(error="Please enter a valid email address.", email=email_input)
return await render_login_page(ctx), 400
user = await find_or_create_user(g.s, email)
token, expires = await create_magic_link(g.s, user.id)
@@ -137,11 +132,10 @@ def register(url_prefix="/auth"):
"Please try again in a moment."
)
return await render_template(
"auth/check_email.html",
email=email,
email_error=email_error,
)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_check_email_page
ctx = await get_template_context(email=email, email_error=email_error)
return await render_check_email_page(ctx)
@auth_bp.get("/magic/<token>/")
async def magic(token: str):
@@ -154,20 +148,17 @@ def register(url_prefix="/auth"):
user, error = await validate_magic_link(s, token)
if error:
return (
await render_template("auth/login.html", error=error),
400,
)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_login_page
ctx = await get_template_context(error=error)
return await render_login_page(ctx), 400
user_id = user.id
except Exception:
return (
await render_template(
"auth/login.html",
error="Could not sign you in right now. Please try again.",
),
502,
)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_login_page
ctx = await get_template_context(error="Could not sign you in right now. Please try again.")
return await render_login_page(ctx), 502
assert user_id is not None

View File

@@ -8,7 +8,7 @@ from __future__ import annotations
import re
from quart import (
Blueprint, request, render_template, redirect, url_for, g, abort,
Blueprint, request, redirect, url_for, g, abort,
)
from shared.services.registry import services
@@ -40,7 +40,7 @@ def register(url_prefix="/identity"):
return redirect(url_for("activitypub.actor_profile", username=actor.preferred_username))
from shared.sexp.page import get_template_context
from sexp_components import render_choose_username_page
from sexp.sexp_components import render_choose_username_page
ctx = await get_template_context()
ctx["actor"] = actor
return await render_choose_username_page(ctx)
@@ -71,11 +71,11 @@ def register(url_prefix="/identity"):
error = "This username is already taken."
if error:
return await render_template(
"federation/choose_username.html",
error=error,
username=username,
), 400
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_choose_username_page
ctx = await get_template_context(error=error, username=username)
ctx["actor"] = None
return await render_choose_username_page(ctx), 400
# Create ActorProfile with RSA keys
display_name = g.user.name or username

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
import logging
from datetime import datetime
from quart import Blueprint, request, g, redirect, url_for, abort, render_template, Response
from quart import Blueprint, request, g, redirect, url_for, abort, Response
from shared.services.registry import services
@@ -40,7 +40,7 @@ def register(url_prefix="/social"):
actor = _require_actor()
items = await services.federation.get_home_timeline(g.s, actor.id)
from shared.sexp.page import get_template_context
from sexp_components import render_timeline_page
from sexp.sexp_components import render_timeline_page
ctx = await get_template_context()
return await render_timeline_page(ctx, items, "home", actor)
@@ -57,7 +57,7 @@ def register(url_prefix="/social"):
items = await services.federation.get_home_timeline(
g.s, actor.id, before=before,
)
from sexp_components import render_timeline_items
from sexp.sexp_components import render_timeline_items
return await render_timeline_items(items, "home", actor)
@bp.get("/public")
@@ -65,7 +65,7 @@ def register(url_prefix="/social"):
items = await services.federation.get_public_timeline(g.s)
actor = getattr(g, "_social_actor", None)
from shared.sexp.page import get_template_context
from sexp_components import render_timeline_page
from sexp.sexp_components import render_timeline_page
ctx = await get_template_context()
return await render_timeline_page(ctx, items, "public", actor)
@@ -80,7 +80,7 @@ def register(url_prefix="/social"):
pass
items = await services.federation.get_public_timeline(g.s, before=before)
actor = getattr(g, "_social_actor", None)
from sexp_components import render_timeline_items
from sexp.sexp_components import render_timeline_items
return await render_timeline_items(items, "public", actor)
# -- Compose --------------------------------------------------------------
@@ -90,7 +90,7 @@ def register(url_prefix="/social"):
actor = _require_actor()
reply_to = request.args.get("reply_to")
from shared.sexp.page import get_template_context
from sexp_components import render_compose_page
from sexp.sexp_components import render_compose_page
ctx = await get_template_context()
return await render_compose_page(ctx, actor, reply_to)
@@ -136,7 +136,7 @@ def register(url_prefix="/social"):
)
followed_urls = {a.actor_url for a in following}
from shared.sexp.page import get_template_context
from sexp_components import render_search_page
from sexp.sexp_components import render_search_page
ctx = await get_template_context()
return await render_search_page(ctx, query, actors, total, 1, followed_urls, actor)
@@ -157,7 +157,7 @@ def register(url_prefix="/social"):
g.s, actor.preferred_username, page=1, per_page=1000,
)
followed_urls = {a.actor_url for a in following}
from sexp_components import render_search_results
from sexp.sexp_components import render_search_results
return await render_search_results(actors, query, page, followed_urls, actor)
@bp.post("/follow")
@@ -200,15 +200,8 @@ def register(url_prefix="/social"):
list_type = "followers"
else:
list_type = "following"
return await render_template(
"federation/_actor_list_items.html",
actors=[remote_dto],
total=0,
page=1,
list_type=list_type,
followed_urls=followed_urls,
actor=actor,
)
from sexp.sexp_components import render_actor_card
return render_actor_card(remote_dto, actor, followed_urls, list_type=list_type)
# -- Interactions ---------------------------------------------------------
@@ -296,10 +289,10 @@ def register(url_prefix="/social"):
).limit(1)
)).scalar())
return await render_template(
"federation/_interaction_buttons.html",
item_object_id=object_id,
item_author_inbox=author_inbox,
from sexp.sexp_components import render_interaction_buttons
return render_interaction_buttons(
object_id=object_id,
author_inbox=author_inbox,
like_count=like_count,
boost_count=boost_count,
liked_by_me=liked_by_me,
@@ -316,7 +309,7 @@ def register(url_prefix="/social"):
g.s, actor.preferred_username,
)
from shared.sexp.page import get_template_context
from sexp_components import render_following_page
from sexp.sexp_components import render_following_page
ctx = await get_template_context()
return await render_following_page(ctx, actors, total, actor)
@@ -327,7 +320,7 @@ def register(url_prefix="/social"):
actors, total = await services.federation.get_following(
g.s, actor.preferred_username, page=page,
)
from sexp_components import render_following_items
from sexp.sexp_components import render_following_items
return await render_following_items(actors, page, actor)
@bp.get("/followers")
@@ -342,7 +335,7 @@ def register(url_prefix="/social"):
)
followed_urls = {a.actor_url for a in following}
from shared.sexp.page import get_template_context
from sexp_components import render_followers_page
from sexp.sexp_components import render_followers_page
ctx = await get_template_context()
return await render_followers_page(ctx, actors, total, followed_urls, actor)
@@ -357,7 +350,7 @@ def register(url_prefix="/social"):
g.s, actor.preferred_username, page=1, per_page=1000,
)
followed_urls = {a.actor_url for a in following}
from sexp_components import render_followers_items
from sexp.sexp_components import render_followers_items
return await render_followers_items(actors, page, followed_urls, actor)
@bp.get("/actor/<int:id>")
@@ -390,7 +383,7 @@ def register(url_prefix="/social"):
).scalar_one_or_none()
is_following = existing is not None
from shared.sexp.page import get_template_context
from sexp_components import render_actor_timeline_page
from sexp.sexp_components import render_actor_timeline_page
ctx = await get_template_context()
return await render_actor_timeline_page(ctx, remote_dto, items, is_following, actor)
@@ -407,7 +400,7 @@ def register(url_prefix="/social"):
items = await services.federation.get_actor_timeline(
g.s, id, before=before,
)
from sexp_components import render_actor_timeline_items
from sexp.sexp_components import render_actor_timeline_items
return await render_actor_timeline_items(items, id, actor)
# -- Notifications --------------------------------------------------------
@@ -418,7 +411,7 @@ def register(url_prefix="/social"):
items = await services.federation.get_notifications(g.s, actor.id)
await services.federation.mark_notifications_read(g.s, actor.id)
from shared.sexp.page import get_template_context
from sexp_components import render_notifications_page
from sexp.sexp_components import render_notifications_page
ctx = await get_template_context()
return await render_notifications_page(ctx, items, actor)