diff --git a/pagerite/api.py b/pagerite/api.py index c02fb1b..8f4bc61 100644 --- a/pagerite/api.py +++ b/pagerite/api.py @@ -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);