OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

public.php

8 KB
<?php
use OpenNIT\Status\StatusManager;
$recent  = $recent_incidents ?? [];
$brand   = $brand ?? [];
$overall = $overall ?? ['key' => 'operational', 'label' => 'Alle Systeme betriebsbereit', 'all_ok' => true];
$e  = fn($v) => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
$dt = fn($v) => $v ? date('d.m.Y H:i', strtotime((string)$v)) : '';

// Optionale Zusatzblöcke oben auf der Seite. Nur auf der Vollseite (nicht im
// Embed) und nur wenn im Backend aktiviert. Zwei Einbindungen der Websuche:
//  - box:   kompakter Block über dem Status (klassische Statusseite)
//  - start: Startseiten-Modus – Logo, große Suche und Status-Kurzinfo im
//           Mittelpunkt (z. B. als Browser-Startseite für Mitarbeitende),
//           die Status-Details folgen darunter.
$searchProviders = [
    'google'     => ['Google',     'https://www.google.com/search?q='],
    'bing'       => ['Bing',       'https://www.bing.com/search?q='],
    'duckduckgo' => ['DuckDuckGo', 'https://duckduckgo.com/?q='],
    'startpage'  => ['Startpage',  'https://www.startpage.com/sp/search?query='],
    'ecosia'     => ['Ecosia',     'https://www.ecosia.org/search?q='],
];
$searchDefault = $brand['search_provider'] ?? 'google';
if (!isset($searchProviders[$searchDefault])) { $searchDefault = 'google'; }
$searchOn    = !empty($brand['search_enabled']);
$startMode   = $searchOn && ($brand['search_layout'] ?? 'box') === 'start';
$htmlContent = trim((string)($brand['html_content'] ?? ''));

$this->layout('layouts/status_public', [
    'page_title' => $startMode ? 'Startseite' : ($page_title ?? 'Systemstatus'),
    'embed'      => false,
    'brand'      => $brand,
    'start_mode' => $startMode,
]);

if ($searchOn):
    // Fester, im Backend gewählter Anbieter – auf der Seite nicht änderbar.
    $sp = $searchProviders[$searchDefault];
    // Gemeinsames Suchformular beider Einbindungen. Im Startseiten-Modus mit
    // Autofokus und Navigation im selben Tab (Startseiten-Verhalten), im
    // kompakten Block weiterhin in einem neuen Tab.
    $searchForm = function () use ($sp, $e, $startMode) { ?>
    <form class="sp-search-form" data-search-url="<?= $e($sp[1]) ?>" data-search-target="<?= $startMode ? 'self' : 'blank' ?>" role="search">
        <span class="sp-search-icon" aria-hidden="true">
            <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
        </span>
        <input type="search" class="sp-search-input" placeholder="<?= $this->te('Im Web suchen mit {anbieter}&hellip;', ['anbieter' => $e($sp[0])]) ?>" aria-label="<?= $this->te('Suchbegriff') ?>" autocomplete="off"<?= $startMode ? ' autofocus' : '' ?>>
        <button type="submit" class="sp-search-btn">
            <svg class="sp-search-btn-icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
            <span><?= $this->t('Mit {anbieter} suchen', ['anbieter' => $e($sp[0])]) ?></span>
        </button>
    </form>
    <?php };
?>

<?php if ($startMode): ?>
<!-- A) Startseiten-Modus: Logo + große Suche + Status-Kurzinfo im Mittelpunkt -->
<div class="sp-start-hero">
    <?php if (!empty($app_logo)): ?>
        <img src="<?= $e($app_logo) ?>" alt="<?= $e($app_name ?? 'OpenNIT') ?>" class="sp-start-logo">
    <?php else: ?>
        <div class="sp-start-appname"><?= $e($app_name ?? 'OpenNIT') ?></div>
    <?php endif; ?>
    <p class="sp-start-greeting" data-sp-greeting hidden></p>
    <?php $searchForm(); ?>
    <div class="sp-start-meta">
        <a class="sp-pill sp-pill--<?= $e($overall['key']) ?> sp-start-chip" href="#status" title="<?= $this->te('Zum Systemstatus springen') ?>"><?= $e($overall['label']) ?></a>
        <?php if (!empty($brand['support_url']) && !empty($brand['support_label'])): ?>
            <a class="sp-start-chip sp-start-chip--support" href="<?= $e($brand['support_url']) ?>" target="_blank" rel="noopener"><?= $e($brand['support_label']) ?></a>
        <?php endif; ?>
    </div>
</div>
<?php else: ?>
<!-- A) Kompakte Websuche über dem Status (fester Anbieter) -->
<div class="sp-section sp-search sp-search--hero">
    <h2 class="sp-search-title"><?= $this->te('Schnellsuche im Web') ?></h2>
    <?php $searchForm(); ?>
</div>
<?php endif; ?>
<script>
(function () {
    document.querySelectorAll('.sp-search-form').forEach(function (f) {
        f.addEventListener('submit', function (ev) {
            ev.preventDefault();
            var q = f.querySelector('.sp-search-input').value.trim();
            if (!q) { return; }
            var url = f.getAttribute('data-search-url') + encodeURIComponent(q);
            if (f.getAttribute('data-search-target') === 'self') {
                window.location.href = url;
            } else {
                window.open(url, '_blank', 'noopener');
            }
        });
    });
    // Begrüßung im Startseiten-Modus clientseitig (Seite wird serverseitig gecacht)
    var g = document.querySelector('[data-sp-greeting]');
    if (g) {
        var h = new Date().getHours();
        var text = h >= 5 && h < 11 ? 'Guten Morgen' : (h >= 11 && h < 18 ? 'Guten Tag' : 'Guten Abend');
        g.textContent = text + ' · ' + new Date().toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
        g.hidden = false;
    }
})();
</script>
<?php endif; ?>

<?php if (!empty($brand['html_enabled']) && $htmlContent !== ''): ?>
<!-- B) Frei befüllbarer HTML-Block (bewusst roh, vom Betreiber gepflegt) -->
<div class="sp-section sp-custom-html"><?= $brand['html_content'] ?></div>
<?php endif; ?>

<?php if ($startMode): ?>
<!-- Im Startseiten-Modus entfällt die Kopfzeile – Überschrift + Sprungziel hier -->
<h2 class="sp-section-title" id="status"><?= $this->t('Systemstatus') ?><?= !empty($brand['subtitle']) ? ' &ndash; ' . $e($brand['subtitle']) : '' ?></h2>
<?php endif; ?>

<?php include __DIR__ . '/_content.php'; ?>

<!-- Benachrichtigungen -->
<?php if (!empty($brand['subscribe'])): ?>
<div class="sp-section">
    <h2 class="sp-section-title"><?= $this->te('Benachrichtigt werden') ?></h2>
    <div class="sp-card sp-subscribe">
        <p class="sp-subscribe-lead">
            <?= $this->te('Wir schreiben Ihnen, sobald eine Störung beginnt oder behoben ist &ndash; sonst nie.') ?>
        </p>
        <!-- Ohne Sitzungs-Token: Die Seite wird zwischengespeichert und allen
             Besuchern gleich ausgeliefert. Eingetragen wird eine Adresse erst
             durch die Bestätigung aus ihrem Postfach. -->
        <form class="sp-subscribe-form" method="POST" action="/status/subscribe">
            <input type="email" name="email" class="sp-subscribe-input" required maxlength="255"
                   placeholder="<?= $this->te('ihre@adresse.de') ?>" aria-label="<?= $this->te('E-Mail-Adresse') ?>" autocomplete="email">
            <button type="submit" class="sp-search-btn"><?= $this->te('Eintragen') ?></button>
        </form>
        <p class="sp-subscribe-note">
            <?= $this->te('Sie erhalten zuerst ein Schreiben mit einem Bestätigungslink. Jede Nachricht enth&auml;lt einen Link zum Abmelden.') ?>
        </p>
    </div>
</div>
<?php endif; ?>

<!-- Verlauf (zuletzt behoben) -->
<?php if (!empty($recent)): ?>
<div class="sp-section">
    <h2 class="sp-section-title"><?= $this->te('Vergangene Störungen') ?></h2>
    <div class="sp-card" style="padding: .25rem 1.15rem;">
        <?php foreach ($recent as $inc): ?>
        <div class="sp-history-item">
            <span class="sp-history-title"><?= $e($inc['title']) ?></span>
            <span class="sp-pill sp-pill--operational" style="margin-left:.4rem;"><?= $this->te('Behoben') ?></span>
            <div class="sp-incident-meta"><?= $dt($inc['started_at']) ?> &ndash; <?= $this->te('{zeitpunkt} Uhr', ['zeitpunkt' => $dt($inc['resolved_at'])]) ?></div>
        </div>
        <?php endforeach; ?>
    </div>
</div>
<?php endif; ?>