contact messages: contact settings in /_api/settings, ?lang overrides

GET reports contact_enabled/label/email/subject; with a translation
?lang= it also reports the per-language label/subject overrides (*_tr).
PUT accepts the contact fields; with a translation ?lang= only the
label/subject apply, written as overrides into the chunk-keyed trans
store (i18n.set_string_translation) — empty or the original's text
drops the override.
This commit is contained in:
LeoVasanko committed 2026-09-28 06:27:45 +00:00
1 parent f68071cb51
commit f5fbe8ddc5
1 file changed
+63 -6
+63 -6
View File
@@ -24,7 +24,7 @@ from html5tagger import E
from pydantic import BaseModel
from pagerite import i18n, messages, views
from pagerite.chunks import store_chunks
from pagerite.chunks import chunk_key, store_chunks
from pagerite.data import (
Node,
append_order,
@@ -276,12 +276,18 @@ async def update_structure(op: StructureOp, request: Request) -> None:
@router.get("/_api/settings")
async def get_settings() -> dict:
async def get_settings(lang: str | None = None) -> dict:
"""Site-wide settings (brand, theme, custom CSS and favicon URL), plus
the themes, banner designs and user fonts available on disk for the
selectors, the translator service keys and the wanted translation
languages (for the /_translate socket)."""
return {
languages (for the /_translate socket), and the contact-messaging
configuration.
With a ``?lang=`` translation language the contact label/subject also
come out as ``*_tr`` (their per-language override, "" = none — the
site settings' messaging section edits those, falling back to the
original it edits without the query)."""
out = {
"brand": data.brand,
"brand_html": data.brand_html,
"theme": data.theme,
@@ -296,11 +302,23 @@ async def get_settings() -> dict:
# Unanswered contact threads: the corner ✉️ icon's badge for
# admins (this probe runs on every page load; no polling).
"contact_unread": messages.unread_count(),
"contact_enabled": data.contact_enabled,
"contact_label": data.contact_label,
"contact_email": data.contact_email,
"contact_subject": data.contact_subject,
# The site default primary language: the front page's resolved
# setting (every page may override it, inherited down the tree).
"primary_lang": i18n.primary_lang(data.menu, ""),
"translate_langs": sorted(data.translate_langs),
}
tag = i18n.base_tag(lang or "")
if tag and tag != out["primary_lang"]:
for field in ("contact_label", "contact_subject"):
original = getattr(data, field) or ("Contact us" if field == "contact_label" else "")
out[f"{field}_tr"] = (
data.trans.get(chunk_key(original), {}).get(tag, "") if original else ""
)
return out
class SettingsIn(BaseModel):
@@ -313,17 +331,56 @@ class SettingsIn(BaseModel):
transition: str = "cube"
translate_langs: list[str] | None = None # None keeps the current set
translate_keys: dict[str, str] | None = None # None keeps the current keys
contact_enabled: bool | None = None # None keeps the current setting
contact_label: str | None = None
contact_email: str | None = None
contact_subject: str | None = None
@router.put("/_api/settings", status_code=204)
async def put_settings(settings: SettingsIn, request: Request) -> None:
"""Update site-wide settings; invalidates cached pages and ETags."""
async def put_settings(
settings: SettingsIn, request: Request, lang: str | None = None
) -> None:
"""Update site-wide settings; invalidates cached pages and ETags.
With a ``?lang=`` translation language only the contact label/subject
apply — as per-language overrides in the chunk-keyed trans store, the
same storage machine translations use (i18n.set_string_translation);
empty or the original's text drops the override."""
tag = i18n.base_tag(lang or "")
if tag and tag != i18n.primary_lang(data.menu, ""):
with kanta.transaction(
f"settings:{tag}", user=request.headers.get("remote-user")
):
changed = False
for field in ("contact_label", "contact_subject"):
value = getattr(settings, field)
if value is None:
continue
original = getattr(data, field) or (
"Contact us" if field == "contact_label" else ""
)
if original:
changed |= i18n.set_string_translation(
data, tag, original, value.strip()
)
if changed:
_invalidate_pages()
return
with kanta.transaction("settings", user=request.headers.get("remote-user")):
data.brand = settings.brand
data.brand_html = settings.brand_html
data.theme = settings.theme
data.custom_css = settings.custom_css
data.transition = settings.transition
if settings.contact_enabled is not None:
data.contact_enabled = settings.contact_enabled
if settings.contact_label is not None:
data.contact_label = settings.contact_label.strip()
if settings.contact_email is not None:
data.contact_email = settings.contact_email.strip()
if settings.contact_subject is not None:
data.contact_subject = settings.contact_subject.strip()
if settings.translate_langs is not None:
# Any language may be a target — including the site default
# (an article in another language can be translated INTO it);