Quelltext des aktuellen Standes
<?php $this->layout('layouts/admin', [
'page_title' => 'Statusseite – Einstellungen',
'current_admin_page' => 'statuspage-settings',
]); ?>
<?php $v = $values ?? []; $val = fn($k) => $this->e($v[$k] ?? ''); ?>
<div class="settings-page">
<form method="post" action="/admin/statuspage/settings">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<!-- Card 1: Darstellung -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🌐</span>
<div>
<h2><?= $this->te('Darstellung der öffentlichen Statusseite') ?></h2>
<p class="settings-card-desc"><?= $this->t('Untertitel, Akzentfarbe, Support-Link und Uptime-Anzeige der Seite unter <code>/status</code>.') ?></p>
</div>
</div>
<div class="settings-card-body">
<div class="form-group">
<label for="sp-subtitle"><?= $this->te('Untertitel') ?></label>
<input type="text" id="sp-subtitle" name="statuspage_subtitle" class="form-control"
value="<?= $val('statuspage_subtitle') ?>" placeholder="<?= $this->te('z. B. Aktueller Betriebsstatus unserer IT-Dienste') ?>">
<small class="form-hint"><?= $this->te('Wird unter der Überschrift „Systemstatus" angezeigt. Leer = kein Untertitel.') ?></small>
</div>
<div class="form-group">
<label for="sp-accent"><?= $this->te('Akzentfarbe') ?></label>
<input type="color" id="sp-accent" name="statuspage_accent" class="form-control" style="max-width:80px;padding:.2rem;"
value="<?= $v['statuspage_accent'] !== '' ? $val('statuspage_accent') : '#2563eb' ?>">
<small class="form-hint"><?= $this->te('Färbt Links, den Support-Button und die Websuche auf der öffentlichen Seite.') ?></small>
</div>
<div class="form-group">
<label for="sp-support-label"><?= $this->te('Support-Link – Beschriftung') ?></label>
<input type="text" id="sp-support-label" name="statuspage_support_label" class="form-control"
value="<?= $val('statuspage_support_label') ?>" placeholder="<?= $this->te('z. B. Störung melden') ?>">
</div>
<div class="form-group">
<label for="sp-support-url"><?= $this->te('Support-Link – Adresse') ?></label>
<input type="text" id="sp-support-url" name="statuspage_support_url" class="form-control"
value="<?= $val('statuspage_support_url') ?>" placeholder="<?= $this->te('https://… oder mailto:support@…') ?>">
<small class="form-hint"><?= $this->te('Beide Felder ausfüllen, damit der Button erscheint. Leer = kein Button.') ?></small>
</div>
<div class="form-group">
<label class="settings-toggle" style="display:inline-flex;align-items:center;gap:.5rem;">
<input type="checkbox" name="statuspage_show_uptime" value="1" <?= ($v['statuspage_show_uptime'] ?? '0') === '1' ? 'checked' : '' ?>>
<span><strong><?= $this->te('90-Tage-Uptime-Verlauf anzeigen') ?></strong></span>
</label>
<small class="form-hint"><?= $this->te('Zeigt je Dienst eine Balkenreihe der letzten 90 Tage. Die Tageswerte schreibt der Hintergrund-Task „Statusseite – Dienst-Snapshot" fort (Administration → Cronjobs).') ?></small>
</div>
<div class="form-group">
<label class="settings-toggle" style="display:inline-flex;align-items:center;gap:.5rem;">
<input type="checkbox" name="statuspage_maintenance_feed" value="1" <?= ($v['statuspage_maintenance_feed'] ?? '0') === '1' ? 'checked' : '' ?>>
<span><strong><?= $this->te('Wartungen aus dem Wartungskalender übernehmen') ?></strong></span>
</label>
<small class="form-hint"><?= $this->te('Termine vom Typ „Wartung" aus dem Wartungskalender erscheinen automatisch als geplante Wartung auf der öffentlichen Seite – maximal 10 Tage im Voraus. Setzt das aktive Modul „Wartungskalender" voraus.') ?></small>
</div>
<div class="form-group" style="margin-bottom:0;">
<label class="settings-toggle" style="display:inline-flex;align-items:center;gap:.5rem;">
<input type="checkbox" name="statuspage_configlog_feed" value="1" <?= ($v['statuspage_configlog_feed'] ?? '0') === '1' ? 'checked' : '' ?>>
<span><strong><?= $this->te('Geplante Änderungen aus dem Konfigurationslog übernehmen') ?></strong></span>
</label>
<small class="form-hint"><?= $this->te('Geplante Konfigurationslog-Einträge erscheinen als geplante Wartung – aber nur, wenn der jeweilige Eintrag ausdrücklich „Auf öffentlicher Statusseite anzeigen" gesetzt hat (max. 10 Tage im Voraus). Setzt das aktive Modul „Konfigurationslog" voraus.') ?></small>
</div>
</div>
</section>
<!-- Card 2: Websuche -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🔍</span>
<div>
<h2><?= $this->te('Websuche') ?></h2>
<p class="settings-card-desc"><?= $this->te('Optionales Suchfeld auf der Statusseite – als kompakter Block über dem Status oder im Startseiten-Modus (Suche im Mittelpunkt, z. B. als Browser-Startseite für Mitarbeitende). Besucher suchen mit dem hier festgelegten Anbieter (auf der Seite nicht änderbar).') ?></p>
</div>
</div>
<div class="settings-card-body">
<div class="form-group">
<label class="settings-toggle" style="display:inline-flex;align-items:center;gap:.5rem;">
<input type="checkbox" name="statuspage_search_enabled" value="1" <?= ($v['statuspage_search_enabled'] ?? '0') === '1' ? 'checked' : '' ?>>
<span><strong><?= $this->te('Websuche einblenden') ?></strong></span>
</label>
<small class="form-hint"><?= $this->te('Zeigt ein Suchfeld. Der eingegebene Begriff öffnet die Suche beim gewählten Anbieter in einem neuen Tab.') ?></small>
</div>
<div class="form-group">
<label><?= $this->te('Einbindung') ?></label>
<?php $curLayout = ($v['statuspage_search_layout'] ?? '') === 'start' ? 'start' : 'box'; ?>
<label class="settings-toggle" style="display:flex;align-items:flex-start;gap:.5rem;margin-bottom:.4rem;">
<input type="radio" name="statuspage_search_layout" value="box" <?= $curLayout === 'box' ? 'checked' : '' ?> style="margin-top:.2rem;">
<span><strong><?= $this->te('Kompakter Block über dem Status') ?></strong><br>
<small class="form-hint" style="margin-top:0;"><?= $this->te('Klassische Statusseite mit Suchfeld darüber. Die Suche öffnet in einem neuen Tab.') ?></small></span>
</label>
<label class="settings-toggle" style="display:flex;align-items:flex-start;gap:.5rem;">
<input type="radio" name="statuspage_search_layout" value="start" <?= $curLayout === 'start' ? 'checked' : '' ?> style="margin-top:.2rem;">
<span><strong><?= $this->te('Startseiten-Modus') ?></strong><br>
<small class="form-hint" style="margin-top:0;"><?= $this->te('Logo, Begrüßung und große Suche stehen im Mittelpunkt, dazu eine Status-Kurzinfo; die Status-Details folgen darunter. Ideal, wenn die Seite als Browser-Startseite für Mitarbeitende dient. Die Suche öffnet im selben Tab, das Suchfeld ist beim Laden fokussiert.') ?></small></span>
</label>
</div>
<div class="form-group" style="margin-bottom:0;">
<label for="sp-search-provider"><?= $this->te('Suchanbieter') ?></label>
<?php $curProv = ($v['statuspage_search_provider'] ?? '') ?: 'google'; ?>
<select id="sp-search-provider" name="statuspage_search_provider" class="form-control" style="max-width:220px;">
<?php foreach (\OpenNIT\Controllers\Admin\StatusAdminController::SEARCH_PROVIDERS as $pk => $pinfo): ?>
<option value="<?= $this->e($pk) ?>" <?= $pk === $curProv ? 'selected' : '' ?>><?= $this->e($pinfo[0]) ?></option>
<?php endforeach; ?>
</select>
<small class="form-hint"><?= $this->te('Google, Bing, DuckDuckGo, Startpage oder Ecosia. Dieser Anbieter wird auf der Statusseite fest verwendet – Besucher können ihn nicht ändern.') ?></small>
</div>
</div>
</section>
<!-- Card 3: Eigener HTML-Block -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">💻</span>
<div>
<h2><?= $this->te('Eigener HTML-Block') ?></h2>
<p class="settings-card-desc"><?= $this->te('Optionaler, frei befüllbarer Inhalt ganz oben auf der Statusseite (z. B. Hinweis, Kontaktbox, Bild).') ?></p>
</div>
</div>
<div class="settings-card-body">
<div class="form-group">
<label class="settings-toggle" style="display:inline-flex;align-items:center;gap:.5rem;">
<input type="checkbox" name="statuspage_html_enabled" value="1" <?= ($v['statuspage_html_enabled'] ?? '0') === '1' ? 'checked' : '' ?>>
<span><strong><?= $this->te('HTML-Block einblenden') ?></strong></span>
</label>
<small class="form-hint"><?= $this->te('Blendet den unten hinterlegten Inhalt oben auf der Seite ein.') ?></small>
</div>
<div class="form-group" style="margin-bottom:0;">
<label for="sp-html"><?= $this->te('HTML-Inhalt') ?></label>
<textarea id="sp-html" name="statuspage_html_content" class="form-control" rows="8" style="font-family:monospace;font-size:.85rem;" placeholder="<?= $this->te('<p>Willkommen auf unserer Statusseite …</p>') ?>"><?= $this->e($v['statuspage_html_content'] ?? '') ?></textarea>
<small class="form-hint"><?= $this->t('<strong>Wird nicht gefiltert</strong> – bitte nur vertrauenswürdiges HTML einfügen. Das HTML wird genau so dargestellt, wie es hier steht.') ?></small>
</div>
</div>
</section>
<!-- Card: Zugriff -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🔒</span>
<div>
<h2><?= $this->te('Zugriff auf die Statusseite') ?></h2>
<p class="settings-card-desc">
<?= $this->t('Standardmäßig ist <code>/status</code> für jeden erreichbar. Optional lässt sich der Zugriff an einen Token in der Adresse binden – dann öffnet sich die Seite nur noch über einen Link, der ihn enthält.') ?>
</p>
</div>
</div>
<div class="settings-card-body">
<div class="form-group" style="margin-bottom:0;">
<label class="settings-toggle" style="display:inline-flex;align-items:center;gap:.5rem;">
<input type="checkbox" name="statuspage_token_enabled" value="1" <?= ($v['statuspage_token_enabled'] ?? '0') === '1' ? 'checked' : '' ?>>
<span><strong><?= $this->te('Nur mit Token in der Adresse sichtbar') ?></strong></span>
</label>
<small class="form-hint">
<?= $this->te('Beim Einschalten wird ein Token erzeugt, falls noch keiner vorhanden ist. Ein Token in der Adresse steht in Browserverlauf, Lesezeichen und Server-Protokollen – er ersetzt keine Anmeldung, hält die Seite aber aus Suchmaschinen und vom zufälligen Aufruf fern.') ?>
</small>
</div>
</div>
</section>
<!-- Card: Benachrichtigungen für Besucher -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📩</span>
<div>
<h2><?= $this->te('Besucher benachrichtigen') ?></h2>
<p class="settings-card-desc">
<?= $this->te('Besucher können ihre E-Mail-Adresse auf der Statusseite eintragen und werden dann benachrichtigt, sobald eine Störung gemeldet oder behoben wird. Zwischenstände einer laufenden Störung gehen nicht hinaus.') ?>
</p>
</div>
</div>
<div class="settings-card-body">
<div class="form-group" style="margin-bottom:0;">
<label class="settings-toggle" style="display:inline-flex;align-items:center;gap:.5rem;">
<input type="checkbox" name="statuspage_subscribe_enabled" value="1" <?= ($v['statuspage_subscribe_enabled'] ?? '0') === '1' ? 'checked' : '' ?>>
<span><strong><?= $this->te('Eintragen auf der Statusseite anbieten') ?></strong></span>
</label>
<small class="form-hint">
<?= $this->t('Eine Adresse wird erst nach dem Klick im Bestätigungsschreiben eingetragen; unbestätigte Eintragungen werden nach sieben Tagen verworfen. Jede Nachricht enthält einen Abmeldelink. Der Versand läuft über die Zeitsteuerung (<a href="/admin/cronjobs">Cronjobs</a>, Aufgabe <em>Statusseite: Benachrichtigungen versenden</em>) und setzt eine eingerichtete E-Mail-Verbindung voraus.') ?>
</small>
</div>
</div>
</section>
<!-- Card: Vorschläge aus dem Monitoring -->
<?php if (!empty($modules['monitoring'])): ?>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📡</span>
<div>
<h2><?= $this->te('Störungen aus dem Monitoring vorschlagen') ?></h2>
<p class="settings-card-desc">
<?= $this->te('Offene Alarme des Monitorings erscheinen in der Statusseiten-Verwaltung als Vorschlag. Übernommen wird ein Vorschlag von Hand – damit nur das öffentlich wird, was auch öffentlich werden soll.') ?>
</p>
</div>
</div>
<div class="settings-card-body">
<div class="form-group">
<label class="settings-toggle" style="display:inline-flex;align-items:center;gap:.5rem;">
<input type="checkbox" name="statuspage_suggest_enabled" value="1" <?= ($v['statuspage_suggest_enabled'] ?? '0') === '1' ? 'checked' : '' ?>>
<span><strong><?= $this->te('Vorschläge in der Verwaltung anzeigen') ?></strong></span>
</label>
<small class="form-hint">
<?= $this->te('Ein verworfener Vorschlag kehrt nicht zurück. Meldet das Monitoring einen übernommenen Alarm später als behoben, weist die Verwaltung darauf hin – die Entwarnung selbst bleibt eine bewusste Handlung.') ?>
</small>
</div>
<div class="form-group" style="margin-bottom:0;max-width:320px;">
<label for="sp-suggest-sev"><?= $this->te('Ab welchem Schweregrad') ?></label>
<select id="sp-suggest-sev" name="statuspage_suggest_severity" class="form-control">
<?php foreach (OpenNIT\Status\StatusManager::MONITORING_SEVERITY as $key => $label): ?>
<option value="<?= $this->e($key) ?>" <?= ($v['statuspage_suggest_severity'] ?? 'high') === $key ? 'selected' : '' ?>>
<?= $this->te('{stufe} und höher', ['stufe' => $label]) ?>
</option>
<?php endforeach; ?>
</select>
<small class="form-hint"><?= $this->te('Niedrigere Stufen erzeugen viele Vorschläge, die kaum je eine öffentliche Störung sind.') ?></small>
</div>
</div>
</section>
<?php endif; ?>
<div class="form-actions" style="display:flex;gap:.75rem;flex-wrap:wrap;">
<button type="submit" class="btn btn-primary"><?= $this->te('Speichern') ?></button>
<a href="/status" target="_blank" rel="noopener" class="btn btn-default"><?= $this->te('Öffentliche Seite ansehen') ?></a>
</div>
</form>
<?php if (($v['statuspage_subscribe_enabled'] ?? '0') === '1'): ?>
<?php $sc = $subscriber_counts ?? ['bestaetigt' => 0, 'offen' => 0, 'wartend' => 0]; ?>
<!-- Eigenes Formular je Zeile: das Entfernen gehört nicht zum Speichern der Einstellungen -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📋</span>
<div>
<h2><?= $this->te('Eingetragene Adressen') ?></h2>
<p class="settings-card-desc">
<?= $this->t('{bestaetigt} bestätigt, {offen} unbestätigt, {wartend} Nachricht(en) warten auf den Versand.', ['bestaetigt' => (int)$sc['bestaetigt'], 'offen' => (int)$sc['offen'], 'wartend' => (int)$sc['wartend']]) ?>
</p>
</div>
</div>
<div class="settings-card-body">
<?php if (empty($subscribers)): ?>
<div class="empty-state">
<p><?= $this->te('Noch hat sich niemand eingetragen.') ?></p>
</div>
<?php else: ?>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th scope="col"><?= $this->te('Adresse') ?></th>
<th scope="col"><?= $this->te('Zustand') ?></th>
<th scope="col"><?= $this->te('Eingetragen') ?></th>
<th scope="col"><?= $this->te('Zuletzt benachrichtigt') ?></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<?php foreach ($subscribers as $abo): ?>
<tr>
<td><?= $this->e((string)$abo['email']) ?></td>
<td>
<?php if (!empty($abo['confirmed_at'])): ?>
<span class="status-pill status-pill--success"><?= $this->te('Bestätigt') ?></span>
<?php else: ?>
<span class="status-pill status-pill--warning"><?= $this->te('Unbestätigt') ?></span>
<?php endif; ?>
</td>
<td class="text-muted"><?= date('d.m.Y', (int)strtotime((string)$abo['created_at'])) ?></td>
<td class="text-muted">
<?= !empty($abo['last_sent_at'])
? date('d.m.Y H:i', (int)strtotime((string)$abo['last_sent_at']))
: '—' ?>
</td>
<td style="text-align:right;">
<form method="post" action="/admin/statuspage/subscribers/<?= (int)$abo['id'] ?>/delete"
onsubmit="return confirm('Diese Eintragung entfernen?');">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<button type="submit" class="btn btn-outline-danger btn-sm"><?= $this->te('Entfernen') ?></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
<?php if (($v['statuspage_token_enabled'] ?? '0') === '1' && ($status_token ?? '') !== ''): ?>
<!-- Eigenes Formular: das Neu-Erzeugen gehört nicht zum Speichern der Einstellungen -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🔑</span>
<div>
<h2><?= $this->te('Zugriffslinks') ?></h2>
<p class="settings-card-desc"><?= $this->te('Diese Adressen öffnen die Seite; ohne den Token bleibt sie verborgen.') ?></p>
</div>
</div>
<div class="settings-card-body">
<div class="form-group">
<label for="sp-link-public"><?= $this->te('Öffentliche Seite') ?></label>
<input type="text" id="sp-link-public" class="form-control" readonly onclick="this.select()"
value="/status?token=<?= $this->e($status_token) ?>">
</div>
<div class="form-group">
<label for="sp-link-embed"><?= $this->te('iframe-Variante') ?></label>
<input type="text" id="sp-link-embed" class="form-control" readonly onclick="this.select()"
value="/status/embed?token=<?= $this->e($status_token) ?>">
<small class="form-hint"><?= $this->te('Den fertigen Einbettungscode gibt es in der Statusseiten-Verwaltung unter „Einbetten & teilen" – er enthält den Token automatisch.') ?></small>
</div>
<form method="post" action="/admin/statuspage/token/regenerate"
onsubmit="return confirm('Neuen Token erzeugen? Bestehende Links und iframes funktionieren danach nicht mehr.');">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<div class="form-group" style="margin-bottom:0;">
<button type="submit" class="btn btn-default">
<i class="fas fa-sync-alt"></i> <?= $this->te('Neuen Token erzeugen') ?>
</button>
<small class="form-hint"><?= $this->te('Der bisherige Token verliert sofort seine Gültigkeit.') ?></small>
</div>
</form>
</div>
</section>
<?php endif; ?>
<!-- Eigenes Formular: die Aktion gehört nicht zum Speichern der Einstellungen -->
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📈</span>
<div>
<h2><?= $this->te('Verlauf aufbauen') ?></h2>
<p class="settings-card-desc">
<?= $this->t('Die Balken unter jedem Dienst zeigen die letzten 90 Tage. Solange nichts erfasst wurde, bleiben sie leer. Diese Aktion trägt für jeden angelegten Dienst die vergangenen 90 Tage bis heute als <strong>betriebsbereit</strong> nach – sinnvoll beim Start der Statusseite.') ?>
</p>
</div>
</div>
<div class="settings-card-body">
<form method="post" action="/admin/statuspage/history/backfill"
onsubmit="return confirm('Die vergangenen 90 Tage für alle Dienste als betriebsbereit eintragen?');">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<div class="form-group" style="margin-bottom:0;">
<button type="submit" class="btn btn-default">
<i class="fas fa-history"></i> <?= $this->te('Letzte 90 Tage als betriebsbereit eintragen') ?>
</button>
<small class="form-hint">
<?= $this->te('Bereits erfasste Tage bleiben unverändert – eine protokollierte Störung wird dadurch nicht nachträglich grün. Die Aktion lässt sich gefahrlos wiederholen und wird im Audit-Log festgehalten.') ?>
</small>
</div>
</form>
</div>
</section>
</div>