Show the kiosk label prefix, and let a plugin declare the settings it owns

This commit is contained in:
2026-08-06 18:17:56 -04:00
parent 307de925f1
commit c674000ccc
15 changed files with 1070 additions and 475 deletions

View File

@@ -52,6 +52,8 @@ SECRET_MASK = '********'
# frontend/src/utils/siteSettings.js + mapConfig.js + setupState.js read before
# login. Whole categories that are purely presentation are allowed wholesale;
# the rest are named keys so a new integration key does not leak by default.
# A plugin adds its own public keys by declaring public=True in
# get_settings_defaults - core does not carry a list of every plugin's keys.
PUBLIC_SETTING_CATEGORIES = {'branding', 'map'}
PUBLIC_SETTING_KEYS = {
'site_base_url', 'facility_name', 'printer_hostname_template',
@@ -60,9 +62,25 @@ PUBLIC_SETTING_KEYS = {
}
def _plugin_declared_settings() -> dict:
"""Declared settings ({key: entry}) of every loaded plugin, or {}."""
pm = current_app.extensions.get('plugin_manager')
if not pm:
return {}
try:
return pm.get_declared_plugin_settings()
except Exception:
current_app.logger.exception('Could not read plugin setting declarations')
return {}
def _is_public_setting(setting) -> bool:
return (setting.category in PUBLIC_SETTING_CATEGORIES
or setting.key in PUBLIC_SETTING_KEYS)
# A plugin declares its own public keys (get_settings_defaults, public=True)
# so a kiosk or print page that renders before login can read them without
# core carrying a list of every plugin's keys.
if setting.category in PUBLIC_SETTING_CATEGORIES or setting.key in PUBLIC_SETTING_KEYS:
return True
return bool(_plugin_declared_settings().get(setting.key, {}).get('public'))
# Optional asset identifiers and the asset types they can be toggled on.
# Drives per-type seed keys and the Settings matrix UI. The asset type names
@@ -92,6 +110,17 @@ SEARCH_DOMAINS = {
'subnet': 'Subnets',
}
def _declared_default(key: str) -> dict:
"""Return the declared default for a key (core defaults, then plugins).
Empty dict when nobody declares it - a genuinely ad-hoc key.
"""
for entry in build_default_settings():
if entry['key'] == key:
return entry
return _plugin_declared_settings().get(key, {})
def _is_secret(key: str) -> bool:
return 'password' in key or 'token' in key or 'secret' in key
@@ -256,9 +285,19 @@ def update_setting(key: str):
setting = Setting.query.filter_by(key=key).first()
# Upsert: create the row on first write (e.g. plugin config keys the setup
# wizard saves). New keys default to a plugin-scoped string setting.
# wizard saves). Take the category, type and description from whoever
# declares the key - core defaults or a plugin's get_settings_defaults.
# Filing a declared key under a placeholder category hid it from the
# settings page that had just written it, which read as "did not save".
if not setting:
setting = Setting(key=key, value='', valuetype='string', category='plugin')
declared = _declared_default(key)
setting = Setting(
key=key,
value='',
valuetype=declared.get('valuetype', 'string'),
category=declared.get('category', 'plugin'),
description=declared.get('description'),
)
db.session.add(setting)
# Track old value for audit