fix: make layout templates generic — remove app-specific nav and config
Lint / JS (eslint) (push) Successful in 10s

php/layout.php: nav is now data-driven via $navLinks array (supports
  top-level links, dropdowns, adminOnly flag); removed tinker_tickets
  hardcoded nav items; moved APP_CONFIG note to comment
python/base.html: nav driven by nav_links list from context; removed
  gandalf-specific routes (links_page, inspector, suppressions_page);
  removed APP_CONFIG.ticketWebUrl from shared script block; added
  nav_links format documentation in header comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-18 13:53:29 -04:00
parent f61705afb8
commit 75c57092f8
2 changed files with 73 additions and 44 deletions
+37 -23
View File
@@ -17,7 +17,8 @@
Required Flask setup (app.py):
- Pass `nonce` into every render_template() call via a context processor
- Pass `user` dict from _get_user() helper
- Pass `config` dict with APP_NAME, etc.
- Pass `config` dict with APP_NAME, APP_SUBTITLE, etc.
- Pass `nav_links` list of dicts defining navigation
Context processor example:
@app.context_processor
@@ -25,6 +26,16 @@
import base64, os
nonce = base64.b64encode(os.urandom(16)).decode()
return dict(nonce=nonce, user=_get_user(), config=_config())
nav_links format (pass from route or context processor):
nav_links = [
{'href': url_for('index'), 'key': 'dashboard', 'label': 'Dashboard'},
{'href': url_for('settings'), 'key': 'settings', 'label': 'Settings'},
# Admin-only dropdown:
{'label': 'Admin', 'key': 'admin', 'admin_only': True, 'children': [
{'href': url_for('admin_users'), 'label': 'Users'},
]},
]
#}
<!DOCTYPE html>
<html lang="en">
@@ -64,24 +75,28 @@
</div>
<nav class="lt-nav" aria-label="Main navigation">
{# Each page sets {% block active_nav %}pagename{% endblock %} #}
{% set active = self.active_nav() | default('') %}
<a href="{{ url_for('index') }}"
class="lt-nav-link {% if active == 'dashboard' %}active{% endif %}">
Dashboard
</a>
<a href="{{ url_for('links_page') }}"
class="lt-nav-link {% if active == 'links' %}active{% endif %}">
Link Debug
</a>
<a href="{{ url_for('inspector') }}"
class="lt-nav-link {% if active == 'inspector' %}active{% endif %}">
Inspector
</a>
<a href="{{ url_for('suppressions_page') }}"
class="lt-nav-link {% if active == 'suppressions' %}active{% endif %}">
Suppressions
</a>
{% for link in nav_links | default([]) %}
{% if not link.get('admin_only') or 'admin' in user.groups %}
{% if link.get('children') %}
<div class="lt-nav-dropdown">
<a href="#" class="lt-nav-link {% if active.startswith(link.key) %}active{% endif %}">
{{ link.label }} ▾
</a>
<ul class="lt-nav-dropdown-menu">
{% for child in link.children %}
<li><a href="{{ child.href }}">{{ child.label }}</a></li>
{% endfor %}
</ul>
</div>
{% else %}
<a href="{{ link.href }}"
class="lt-nav-link {% if active == link.key %}active{% endif %}">
{{ link.label }}
</a>
{% endif %}
{% endif %}
{% endfor %}
</nav>
</div>
@@ -107,17 +122,16 @@
All <script> tags MUST carry the nonce attribute for CSP.
========================================================= -->
<!-- Runtime config (no CSRF needed for Gandalf — SameSite=Strict) -->
<!-- Runtime config injected by the server -->
<script nonce="{{ nonce }}">
window.APP_CONFIG = {
ticketWebUrl: {{ config.get('ticket_api', {}).get('web_url', 'https://t.lotusguild.org/ticket/') | tojson }},
};
window.CURRENT_USER = {
username: {{ user.username | tojson }},
name: {{ (user.name or user.username) | tojson }},
groups: {{ user.groups | tojson }},
isAdmin: {{ ('admin' in user.groups) | lower }},
};
// App-specific config: set window.APP_CONFIG in your app's own template block,
// not here. This file is shared across all apps.
</script>
<!-- Unified design system JS -->
@@ -136,6 +150,6 @@
{% block active_nav %}dashboard{% endblock %}
Values: dashboard | links | inspector | suppressions
Value must match a 'key' in your nav_links list.
--------------------------------------------------------------- #}
{% block active_nav %}{% endblock %}