P2: nearby units CRUD, unit meeting times, site settings - the first admin writes

Nearby rows bump verified_at on every save and deactivate rather than delete.
Unit edits are meeting fields only, gated by unit:write_own scoped to the unit
in the URL. Settings are a typed key registry falling back to code defaults;
find-a-unit now reads its source name and URL from it. link_url and contact
reject attribute-breakout characters and the nearby renderer escapes quotes.
Covered by tests/smoke_admin.py, 53 checks in-process.
This commit is contained in:
2026-09-04 14:10:29 -04:00
parent 8c8dab12e3
commit 32e1c67a6f
5 changed files with 609 additions and 14 deletions
+10 -9
View File
@@ -17,6 +17,7 @@ and from this page. The rest of the site is our pitch; this one is a door out
for a family we are not the right fit for.
"""
import identity
import store
# unit_type -> the badge a parent actually recognises.
@@ -39,12 +40,10 @@ SERVES_LABEL = {
BEASCOUT = "https://beascout.scouting.org"
# The district's own short link to its unit list. This is the page the district
# hands out, so it is the right thing to credit and the right thing to link.
# One URL for the whole page rather than a per-row column: when the admin panel
# exists this belongs in a settings row, not in seventeen copies.
SOURCE_NAME = "Continental District unit list"
SOURCE_URL = "https://tinyurl.com/ContinentalScouts"
# The district's short link to its unit list is one URL for the whole page
# rather than a per-row column, and it now lives in settings (defaults and
# validation in identity.SETTINGS_KEYS) so the admin panel can change it when
# the district moves the document.
MONTHS = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"]
@@ -103,8 +102,10 @@ def checked_on():
def _esc(s):
"""Quotes included: several of these values are interpolated into
href="..." attributes, where a bare quote is a breakout."""
return (str(s).replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
if s else "")
.replace('"', "&quot;") if s else "")
def unit_name(u):
@@ -176,8 +177,8 @@ def body_html(hero_html):
'has every unit in the area.</div>')
checked = checked_on()
src = (f'<a class="nu-src" href="{SOURCE_URL}" target="_blank" '
f'rel="noopener">{SOURCE_NAME}</a>')
src = (f'<a class="nu-src" href="{_esc(identity.get_setting("nearby_source_url"))}" target="_blank" '
f'rel="noopener">{_esc(identity.get_setting("nearby_source_name"))}</a>')
stamp = (f'Pulled from the {src} on {pretty_date(checked)}.' if checked
else f'Pulled from the {src}.')