Move rendering into private templates

Add an explicit template renderer with HTML views and partials for the app, bootstrap, package, and catalog pages. Move shared reporting setup into config/reporting.php and relocate stylesheet assets under css/.
This commit is contained in:
www-data
2026-05-26 12:50:26 +02:00
parent 2f2e8869d6
commit 475765e31f
55 changed files with 2328 additions and 1175 deletions
+117 -65
View File
@@ -5,12 +5,19 @@
* Shared page header renderer for logged-in application pages.
*/
require_once __DIR__ . '/Template.php';
function app_header_h($s)
{
return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
return RacketSandboxTemplate::escape($s);
}
function render_app_header($options)
{
echo app_header_html($options);
}
function app_header_html($options)
{
$title = (string)($options['title'] ?? '');
$navItems = $options['nav_items'] ?? array();
@@ -26,69 +33,114 @@ function render_app_header($options)
$logoutLabel = (string)($options['logout_label'] ?? '');
$message = (string)($options['message'] ?? '');
$error = (string)($options['error'] ?? '');
?>
<header class="page-header">
<div class="page-titlebar">
<h1><?= app_header_h($title) ?></h1>
<nav class="header-nav" aria-label="<?= app_header_h($title) ?> navigation">
<?php foreach ($navItems as $item): ?>
<?php if (!empty($item['separator_before'])): ?>
<span class="nav-separator" aria-hidden="true">|</span>
<?php endif; ?>
<?php if (!empty($item['active'])): ?>
<strong><?= app_header_h($item['label'] ?? '') ?></strong>
<?php else: ?>
<a href="<?= app_header_h($item['url'] ?? '#') ?>"><?= app_header_h($item['label'] ?? '') ?></a>
<?php endif; ?>
<?php endforeach; ?>
<?php if ($logoutAction !== '' && $logoutLabel !== ''): ?>
<span class="nav-separator" aria-hidden="true">|</span>
<form class="header-action-form" method="post" action="<?= app_header_h($logoutAction) ?>">
<input type="hidden" name="action" value="logout">
<button type="submit"><?= app_header_h($logoutLabel) ?></button>
</form>
<?php endif; ?>
<?php if ($user !== null): ?>
<span class="nav-separator" aria-hidden="true">|</span>
<span class="nav-user">
<?php if ($userPrefix !== ''): ?>
<?= app_header_h($userPrefix) ?>
<?php endif; ?>
<?= app_header_h($user->displayName()) ?>
<?php if ($user->isAdmin()): ?>
<span class="small">(<?= app_header_h($adminLabel) ?>)</span>
<?php endif; ?>
</span>
<?php endif; ?>
<form class="header-language-form" method="get" action="<?= app_header_h($languageAction) ?>">
<?php foreach ($languageHidden as $name => $value): ?>
<input type="hidden" name="<?= app_header_h($name) ?>" value="<?= app_header_h($value) ?>">
<?php endforeach; ?>
<label>
<?= app_header_h($languageLabel) ?>
<select name="lang" onchange="this.form.submit()">
<?php foreach ($languages as $lang => $label): ?>
<option value="<?= app_header_h($lang) ?>" <?= $lang === $language ? 'selected' : '' ?>>
<?= app_header_h($label) ?>
</option>
<?php endforeach; ?>
</select>
</label>
</form>
</nav>
</div>
<?php if ($message !== ''): ?>
<div class="message"><?= app_header_h($message) ?></div>
<?php endif; ?>
<?php if ($error !== ''): ?>
<div class="error"><?= app_header_h($error) ?></div>
<?php endif; ?>
</header>
<?php
return RacketSandboxTemplate::renderFile('app-header.html', array(
'title' => $title,
'nav_html' => app_header_nav_html($navItems),
'logout_html' => app_header_logout_html($logoutAction, $logoutLabel),
'user_html' => app_header_user_html($user, $userPrefix, $adminLabel),
'language_html' => app_header_language_html($languageLabel, $language, $languages, $languageAction, $languageHidden),
'message_html' => app_header_status_html('message', $message),
'error_html' => app_header_status_html('error', $error),
));
}
function app_header_status_html($class, $message)
{
if ($message === '') {
return '';
}
return RacketSandboxTemplate::renderFile('partials/message.html', array(
'class' => $class,
'message' => $message,
));
}
function app_header_nav_html($navItems)
{
$html = '';
foreach ($navItems as $item) {
if (!empty($item['separator_before'])) {
$html .= RacketSandboxTemplate::renderFile('partials/header-separator.html', array()) . "\n";
}
if (!empty($item['active'])) {
$html .= RacketSandboxTemplate::renderFile('partials/header-nav-active.html', array(
'label' => $item['label'] ?? '',
)) . "\n";
continue;
}
$html .= RacketSandboxTemplate::renderFile('partials/header-nav-link.html', array(
'url' => $item['url'] ?? '#',
'label' => $item['label'] ?? '',
)) . "\n";
}
return $html;
}
function app_header_logout_html($logoutAction, $logoutLabel)
{
if ($logoutAction === '' || $logoutLabel === '') {
return '';
}
return RacketSandboxTemplate::renderFile('partials/header-logout.html', array(
'logout_action' => $logoutAction,
'logout_label' => $logoutLabel,
)) . "\n";
}
function app_header_user_html($user, $userPrefix, $adminLabel)
{
if ($user === null) {
return '';
}
$adminHtml = '';
if ($user->isAdmin()) {
$adminHtml = RacketSandboxTemplate::renderFile('partials/header-admin-badge.html', array(
'admin_label' => $adminLabel,
));
}
return RacketSandboxTemplate::renderFile('partials/header-user.html', array(
'user_prefix' => $userPrefix,
'display_name' => $user->displayName(),
'admin_html' => $adminHtml,
)) . "\n";
}
function app_header_language_html($languageLabel, $language, $languages, $languageAction, $languageHidden)
{
$hiddenInputsHtml = '';
foreach ($languageHidden as $name => $value) {
$hiddenInputsHtml .= RacketSandboxTemplate::renderFile('partials/header-hidden-input.html', array(
'name' => $name,
'value' => $value,
)) . "\n";
}
$languageOptionsHtml = '';
foreach ($languages as $lang => $label) {
$selected = $lang === $language ? ' selected' : '';
$languageOptionsHtml .= RacketSandboxTemplate::renderFile('partials/select-option.html', array(
'value' => $lang,
'selected' => $selected,
'label' => $label,
)) . "\n";
}
return RacketSandboxTemplate::renderFile('partials/header-language.html', array(
'language_action' => $languageAction,
'hidden_inputs_html' => $hiddenInputsHtml,
'language_label' => $languageLabel,
'language_options_html' => $languageOptionsHtml,
)) . "\n";
}