OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

index.php

291 KB
<?php $this->layout('layouts/admin', [
    'page_title'         => 'Systemhandbuch',
    'current_admin_page' => 'manual',
    'page_actions'       => '<a href="/admin/manual/pdf" class="btn btn-primary"><i class="fas fa-file-pdf"></i> Als PDF herunterladen</a>',
]); ?>

<style>
.manual-layout { display: flex; align-items: flex-start; gap: 28px; }
.manual-content { flex: 1 1 auto; min-width: 0; }
.manual-toc { flex: 0 0 300px; background: var(--color-gray-50); border: 1px solid var(--color-gray-300); border-radius: 6px; padding: 16px 20px; position: sticky; top: 16px; max-height: calc(100vh - 32px); overflow-y: auto; }
@media (max-width: 992px) {
    .manual-layout { flex-direction: column; gap: 16px; }
    .manual-toc { flex-basis: auto; width: 100%; position: static; max-height: none; margin-bottom: 0; }
}
/* Sanftes Scrollen + Sprungmarken nicht hinter der fixierten Navbar verstecken */
html { scroll-behavior: smooth; }
.manual-section,
.manual-content [id] { scroll-margin-top: calc(var(--navbar-height, 60px) + 14px); }
/* „Nach oben"-Schaltfläche für die lange Seite */
.manual-top {
    position: fixed;
    right: 20px;
    bottom: 20px;
    width: 42px;
    height: 42px;
    border: none;
    border-radius: 50%;
    background: var(--color-primary, var(--color-primary-deep));
    color: var(--color-on-accent);
    font-size: 1rem;
    cursor: pointer;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.22);
    opacity: 0;
    visibility: hidden;
    transform: translateY(10px);
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
    z-index: 1040;
}
.manual-top.is-visible { opacity: 1; visibility: visible; transform: translateY(0); }
.manual-top:hover { filter: brightness(1.08); }
@media (max-width: 640px) { .manual-top { right: 14px; bottom: 14px; } }
.manual-toc-title {
    margin: 0 0 10px;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--color-primary-deep);
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 8px;
}
.manual-toc-title::-webkit-details-marker { display: none; }
.manual-toc-title::after {
    content: '\25BE'; /* ▾ – robustes Unicode-Dreieck, unabhängig von Icon-Fonts */
    font-size: 0.8rem;
    color: var(--gray-500);
    margin-left: auto;
    transition: transform 0.15s ease;
}
.manual-toc:not([open]) .manual-toc-title::after { transform: rotate(-90deg); }
.manual-toc ol { margin: 0; padding-left: 20px; }
.manual-toc ol ol { padding-left: 16px; margin: 2px 0 4px; }
.manual-toc li { margin-bottom: 4px; }
.manual-toc a { color: var(--color-primary-deep); text-decoration: none; }
.manual-toc a:hover { text-decoration: underline; }
.manual-toc-search { width: 100%; box-sizing: border-box; margin: 10px 0 8px; padding: 6px 10px; font-size: 0.85rem; border: 1px solid var(--color-gray-400); border-radius: 4px; }
.manual-toc-search:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.15); }
.manual-toc-noresult { font-size: 0.82rem; color: var(--gray-500); padding: 2px 2px 6px; }
.manual-toc .toc-cat > a { font-weight: 600; }
.manual-section { margin-bottom: 48px; }
.manual-section h2 { border-bottom: 2px solid var(--color-primary-deep); padding-bottom: 8px; color: var(--color-primary-deep); }
.manual-section h3 { color: var(--color-primary-deep); margin-top: 28px; }
.manual-section h3.cat-heading { background: var(--color-gray-100); border-left: 4px solid var(--color-primary-deep); padding: 8px 14px; border-radius: 0 4px 4px 0; margin-top: 36px; }
.manual-section h4 { margin-top: 16px; margin-bottom: 6px; }
.module-card { border: 1px solid var(--color-gray-300); border-radius: 6px; padding: 16px 20px; margin-bottom: 16px; background: var(--color-bg); }
.module-card h4 { margin: 0 0 6px 0; font-size: 1rem; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.module-card p { margin: 0 0 8px 0; color: var(--color-gray-700); font-size: 0.92rem; }
.module-card ul { margin: 4px 0 0 0; padding-left: 18px; font-size: 0.9rem; color: var(--color-gray-700); }
.module-card ul li { margin-bottom: 2px; }
.badge { display: inline-block; font-size: 0.72rem; padding: 2px 8px; border-radius: 3px; font-weight: 600; }
.badge-enabled { background: var(--alert-success-bg); color: var(--alert-success-text); border: 1px solid var(--alert-success-border); }
.badge-disabled { background: var(--alert-danger-bg); color: var(--alert-danger-text); border: 1px solid var(--alert-danger-border); }
.dep-list { background: var(--slate-100); border-left: 3px solid var(--color-primary); padding: 8px 14px; font-size: 0.88rem; margin: 10px 0 0; border-radius: 0 4px 4px 0; }
.dep-list strong { color: var(--color-primary-deep); }
.admin-links { background: var(--color-gray-50); border-left: 3px solid var(--color-gray-500); padding: 8px 14px; font-size: 0.88rem; margin: 8px 0 0; border-radius: 0 4px 4px 0; }
.admin-links strong { color: var(--color-primary-deep); }
.settings-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; margin: 12px 0; }
.settings-table th { background: var(--color-gray-200); color: var(--color-primary-deep); font-weight: 600; text-align: left; padding: 8px 10px; border: 1px solid var(--color-gray-400); }
.settings-table td { padding: 7px 10px; border: 1px solid var(--color-gray-300); vertical-align: top; }
.settings-table tr:nth-child(even) td { background: var(--color-gray-50); }
.info-box { background: var(--color-primary-pale); border-left: 4px solid var(--color-primary); padding: 10px 14px; margin: 12px 0; border-radius: 0 4px 4px 0; font-size: 0.9rem; }
.warn-box { background: var(--alert-warning-bg); border-left: 4px solid var(--color-warning); padding: 10px 14px; margin: 12px 0; border-radius: 0 4px 4px 0; font-size: 0.9rem; }
.module-card kbd { display: inline-block; background: var(--gray-100); border: 1px solid var(--gray-300); border-bottom-width: 2px; border-radius: 3px; padding: 1px 6px; font-family: 'SFMono-Regular', Menlo, Consolas, monospace; font-size: 0.82rem; color: var(--gray-800); line-height: 1.4; }
.module-card code, .manual-section code { background: var(--gray-100); padding: 1px 5px; border-radius: 3px; font-size: 0.85rem; }
.module-card h5 { font-size: 0.95rem; color: var(--color-primary-deep); margin: 0.75rem 0 0.25rem; }
.setup-box { background: var(--blue-50); border: 1px solid var(--color-primary-soft); border-left: 4px solid var(--color-primary); border-radius: 0 4px 4px 0; padding: 10px 14px 12px; margin: 12px 0 0; }
.setup-box .setup-title { font-weight: 600; color: var(--color-primary-deep); font-size: 0.9rem; margin: 0 0 6px; display: flex; align-items: center; gap: 6px; }
.setup-box ol { margin: 4px 0 0; padding-left: 20px; font-size: 0.9rem; color: var(--color-gray-800); }
.setup-box ol li { margin-bottom: 5px; }
.setup-box ul { margin: 4px 0 0; padding-left: 18px; font-size: 0.88rem; color: var(--color-gray-700); }
.setup-box code { background: var(--color-gray-200); padding: 1px 5px; border-radius: 3px; font-size: 0.85rem; }
.setup-box .req { background: var(--alert-warning-bg); border: 1px solid var(--alert-warning-border); border-radius: 3px; padding: 6px 10px; margin: 8px 0 0; font-size: 0.85rem; color: var(--alert-warning-text); }
/* Bedienungs-/Nutzungs-Box (grün) – Gegenstück zur blauen Einrichtungs-Box */
.usage-box { background: var(--green-50); border: 1px solid var(--green-100); border-left: 4px solid var(--green-600); border-radius: 0 4px 4px 0; padding: 10px 14px 12px; margin: 12px 0 0; }
.usage-box .usage-title { font-weight: 600; color: var(--green-700); font-size: 0.9rem; margin: 0 0 6px; display: flex; align-items: center; gap: 6px; }
.usage-box ol { margin: 4px 0 0; padding-left: 20px; font-size: 0.9rem; color: var(--color-gray-800); }
.usage-box ol li { margin-bottom: 5px; }
.usage-box ul { margin: 4px 0 0; padding-left: 18px; font-size: 0.88rem; color: var(--color-gray-700); }
.usage-box code { background: var(--tint-green-bg); padding: 1px 5px; border-radius: 3px; font-size: 0.85rem; }
.usage-box .req { background: var(--alert-warning-bg); border: 1px solid var(--alert-warning-border); border-radius: 3px; padding: 6px 10px; margin: 8px 0 0; font-size: 0.85rem; color: var(--alert-warning-text); }
.lead { font-size: 0.98rem; color: var(--color-gray-800); }
.manual-code { background: var(--slate-900); color: var(--slate-200); border-radius: 5px; padding: 10px 13px; margin: 8px 0; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.82rem; line-height: 1.5; overflow-x: auto; white-space: pre; }
/* Responsiv: lange Bezeichner (Pfade, Klassen) umbrechen statt horizontal zu scrollen */
.module-card code, .manual-section code, .dep-list, .admin-links, .setup-box { overflow-wrap: anywhere; word-break: break-word; }
.module-card h4 code { word-break: break-all; }
/* Tabellen auf schmalen Geräten horizontal scrollbar machen */
@media (max-width: 640px) {
    .settings-table, .settings-table tbody, .settings-table thead, .settings-table tr { display: block; }
    .settings-table thead { display: none; }
    .settings-table td { display: block; border: none; border-bottom: 1px solid var(--color-gray-200); padding: 4px 0; }
    .settings-table td:first-child { font-weight: 600; color: var(--color-primary-deep); padding-top: 8px; }
    .module-card, .manual-section { padding-left: 12px; padding-right: 12px; }
}
</style>

<?php
/*
 * Kleiner Helfer für die Modul-Karten-Kopfzeile (Icon, Titel, Aktiv-Badge,
 * Modul-Key). Hält die 26 Karten konsistent und token-sparsam.
 */
$moduleHeader = function (array $modules, string $key, string $icon, string $title) {
    $active = !empty($modules[$key]);
    return '<h4><i class="fas ' . $icon . '"></i> ' . $title
        . ' <span class="badge ' . ($active ? 'badge-enabled' : 'badge-disabled') . '">'
        . ($active ? 'Aktiv' : 'Inaktiv') . '</span>'
        . ' <code style="font-size:0.75rem;">' . htmlspecialchars($key) . '</code></h4>';
};
?>

<!-- Inhaltsverzeichnis -->
<div class="manual-layout">
<details class="manual-toc" open>
    <summary class="manual-toc-title"><i class="fas fa-list"></i> Inhaltsverzeichnis</summary>
    <input type="search" id="manualTocSearch" class="manual-toc-search" placeholder="Handbuch durchsuchen …" aria-label="Handbuch durchsuchen" autocomplete="off">
    <div class="manual-toc-noresult" id="manualTocNoResult" hidden>Keine passenden Einträge</div>
    <ol>
        <li><a href="#uebersicht">Systemübersicht</a></li>
        <li><a href="#erste-schritte">Erste Schritte / Inbetriebnahme</a></li>
        <li><a href="#kern">Kernsystem (immer aktiv)</a></li>
        <li><a href="#module">Module</a>
            <ol>
                <li class="toc-cat"><a href="#cat-inventar">Inventar &amp; Beschaffung</a>
                    <ol>
                        <li><a href="#mod-orders">Bestellboard</a></li>
                        <li><a href="#mod-budget">Budget</a></li>
                        <li><a href="#mod-assets">Inventar</a></li>
                        <li><a href="#mod-stock">Lager</a></li>
                        <li><a href="#mod-licenses">Lizenzverwaltung</a></li>
                        <li><a href="#mod-software">Software-Inventar</a></li>
                    </ol>
                </li>
                <li class="toc-cat"><a href="#cat-service">Service &amp; Support</a>
                    <ol>
                        <li><a href="#mod-todos">Aufgabenliste</a></li>
                        <li><a href="#mod-teamboard">Notizbrett</a></li>
                        <li><a href="#mod-loans">Geräteausleihe</a></li>
                        <li><a href="#mod-services">IT-Servicekatalog</a></li>
                        <li><a href="#mod-kanban">Kanban-Board</a></li>
                        <li><a href="#mod-tickets">Ticketsystem</a></li>
                        <li><a href="#mod-maintenance">Wartungskalender</a></li>
                    </ol>
                </li>
                <li class="toc-cat"><a href="#cat-netzwerk">Netzwerk &amp; Infrastruktur</a>
                    <ol>
                        <li><a href="#mod-backup">Backup-Übersicht</a></li>
                        <li><a href="#mod-ipam">IP-Adressmanagement</a></li>
                        <li><a href="#mod-dns">DNS-Zonen</a></li>
                        <li><a href="#mod-monitoring">Monitoring</a></li>
                        <li><a href="#mod-netdiagram">Netzwerkpläne</a></li>
                        <li><a href="#mod-ssl">SSL-Zertifikate</a></li>
                        <li><a href="#mod-unifi">UniFi-Integration</a></li>
                        <li><a href="#mod-vms">Virtualisierung</a></li>
                    </ol>
                </li>
                <li class="toc-cat"><a href="#cat-organisation">Organisation &amp; Kommunikation</a>
                    <ol>
                        <li><a href="#mod-contacts">Kontakte &amp; Unternehmen</a></li>
                        <li><a href="#mod-phonebook">Telefonbuch</a></li>
                        <li><a href="#mod-contracts">Vertragsmanagement</a></li>
                        <li><a href="#mod-workflows">Anträge &amp; Genehmigungen</a></li>
                        <li><a href="#mod-statuspage">Statusseite</a></li>
                    </ol>
                </li>
                <li class="toc-cat"><a href="#cat-sicherheit-mod">Sicherheit &amp; Nachvollziehbarkeit</a>
                    <ol>
                        <li><a href="#mod-isms">Informationssicherheit (ISMS)</a></li>
                        <li><a href="#mod-cve">CVE-Sicherheitsmeldungen</a></li>
                        <li><a href="#mod-xdr">XDR-Sicherheitswarnungen</a></li>
                        <li><a href="#mod-configlog">Konfigurationslog</a></li>
                        <li><a href="#mod-vault">Passwort-Tresor</a></li>
                    </ol>
                </li>
                <li class="toc-cat"><a href="#cat-doku">Dokumentation &amp; KI</a>
                    <ol>
                        <li><a href="#mod-files">Datei-Explorer</a></li>
                        <li><a href="#mod-ai_chat">KI-Chat</a></li>
                        <li><a href="#mod-api_auto">API-Auto</a></li>
                    </ol>
                </li>
            </ol>
        </li>
        <li><a href="#abhaengigkeiten">Modulabhängigkeiten</a></li>
        <li><a href="#szenarien">Anwendungsszenarien (modulübergreifend)</a></li>
        <li><a href="#admin">Administrationsbereiche</a></li>
        <li><a href="#einstellungen">Systemeinstellungen</a></li>
        <li><a href="#email-oauth">E-Mail-Versand &amp; OAuth (Microsoft 365 / Google)</a></li>
        <li><a href="#sicherheit">Sicherheit &amp; Compliance</a></li>
        <li><a href="#cronjobs">Scheduler &amp; Hintergrundaufgaben</a></li>
        <li><a href="#auth">Authentifizierung, Benutzer &amp; Rollen</a></li>
        <li><a href="#troubleshooting">Troubleshooting &amp; häufige Fragen</a></li>
        <li><a href="#glossar">Glossar &amp; Pflege</a></li>
    </ol>
</details>
<!-- /Inhaltsverzeichnis -->

<div class="manual-content">

<!-- ============================================================= -->
<!-- 1. Systemübersicht -->
<!-- ============================================================= -->
<div class="manual-section" id="uebersicht">
    <h2>1. Systemübersicht</h2>
    <p class="lead">
        <strong>OpenNIT</strong> ist eine selbst gehostete PHP-Webanwendung für revisionssichere IT-Dokumentation
        und Asset-Management. Das System ist modular aufgebaut – jedes Modul kann separat aktiviert oder deaktiviert
        werden, ohne den Rest des Systems zu beeinträchtigen.
    </p>
    <p>
        Kernmerkmal ist ein <strong>unveränderlicher Audit-Log mit kryptografischer Hash-Chain</strong>, der alle
        Änderungen nachvollziehbar dokumentiert und für Compliance-Anforderungen (ISO&nbsp;27001, BSI-Grundschutz,
        SOC&nbsp;2) geeignet ist. Sämtliche Zugangsdaten werden verschlüsselt gespeichert, und der Zugriff wird über
        ein rollenbasiertes Berechtigungssystem (RBAC) gesteuert.
    </p>

    <h3>Aufbau dieses Handbuchs</h3>
    <ul>
        <li><strong>Kapitel 2</strong> – Erste Schritte / Inbetriebnahme: der geführte Einstieg für neue Installationen.</li>
        <li><strong>Kapitel 3</strong> – fest eingebaute Kernfunktionen, die immer verfügbar sind.</li>
        <li><strong>Kapitel 4–5</strong> – alle optionalen Module (in sechs Gruppen) und ihre Abhängigkeiten.</li>
        <li><strong>Kapitel 6</strong> – Anwendungsszenarien, die das Zusammenspiel mehrerer Module zeigen.</li>
        <li><strong>Kapitel 7–11</strong> – Administration, Systemeinstellungen, E-Mail/OAuth, Sicherheit und Scheduler.</li>
        <li><strong>Kapitel 12</strong> – Authentifizierung, Benutzer &amp; Rollen (mit Berechtigungs-Referenz).</li>
        <li><strong>Kapitel 13–14</strong> – Troubleshooting/FAQ und Glossar.</li>
    </ul>
    <p>Jedes Modul beschreibt zuerst seine Funktionen und enthält darüber hinaus – farblich abgesetzt – zwei Anleitungsblöcke:</p>
    <div class="setup-box" style="margin-bottom:8px;">
        <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt (blau)</div>
        <p style="margin:2px 0 0;font-size:0.9rem;color:var(--color-gray-800);">Wie der Administrator das Modul aktiviert und konfiguriert (Module, Berechtigungen, Schnittstellen, Vorlagen).</p>
    </div>
    <div class="usage-box">
        <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt (grün)</div>
        <p style="margin:2px 0 0;font-size:0.9rem;color:var(--color-gray-800);">Wie man das Modul im Alltag benutzt – von der ersten Eingabe bis zur Auswertung.</p>
    </div>

    <h3>Technischer Steckbrief</h3>
    <table class="settings-table">
        <thead>
            <tr><th scope="col">Eigenschaft</th><th scope="col">Wert</th></tr>
        </thead>
        <tbody>
            <tr><td>Technologie</td><td>PHP (OOP, PSR-konforme Struktur), MariaDB/MySQL, HTML/CSS/JS</td></tr>
            <tr><td>Authentifizierung</td><td>Lokal, LDAP, Azure AD (OAuth2/PKCE), Keycloak, WebAuthn/FIDO2, TOTP</td></tr>
            <tr><td>Verschlüsselung</td><td>AES-256-GCM (Vault, sensible Felder), Argon2id (Passwörter)</td></tr>
            <tr><td>E-Mail</td><td>SMTP/IMAP mit Passwort oder OAuth2 (Microsoft 365 Graph/XOAUTH2, Google Workspace)</td></tr>
            <tr><td>PDF-Export</td><td>Dompdf (Seiten, ZIP-Exporte, Übergabeprotokolle, Handbuch)</td></tr>
            <tr><td>Editor</td><td>TinyMCE (Rich-Text für Dokumentationsseiten)</td></tr>
            <tr><td>Diagramme</td><td>draw.io (Netzwerkpläne, selbst gehostet)</td></tr>
            <tr><td>Audit-Log</td><td>Hash-Chain (SHA-256), 10 Jahre Aufbewahrung</td></tr>
            <tr><td>Schnittstellen</td><td>REST-API (WordPress-Plugin, Self-Service), Chrome-Extension (Vault-Autofill), PowerShell-Agent (Asset-Sync)</td></tr>
        </tbody>
    </table>
</div>

<!-- ============================================================= -->
<!-- 2. Kernsystem -->
<!-- ============================================================= -->
<div class="manual-section" id="erste-schritte">
    <h2>2. Erste Schritte / Inbetriebnahme</h2>
    <p class="lead">Dieses Kapitel führt Sie nach abgeschlossener Installation in der empfohlenen Reihenfolge durch die Grundeinrichtung – vom ersten Login bis zum laufenden Betrieb. Die Detail-Anleitungen je Funktion finden Sie in den späteren Kapiteln; hier geht es um den roten Faden.</p>

    <div class="warn-box"><i class="fas fa-exclamation-triangle"></i> <strong>Wichtigster Schritt zuerst:</strong> Ohne den <strong>Scheduler</strong> (Crontab, Schritt&nbsp;6) laufen <em>keine</em> Hintergrundaufgaben – also keine E-Mail-Erinnerungen, kein Ticket-Abruf, keine SSL-/Vertrags-/CVE-Prüfungen. Richten Sie ihn früh ein.</p></div>

    <div class="setup-box">
        <div class="setup-title"><i class="fas fa-flag-checkered"></i> Grundeinrichtung Schritt für Schritt</div>
        <ol>
            <li><strong>Anmelden:</strong> Mit dem bei der Installation angelegten Administrator-Konto unter <code>/login</code> einloggen. (Steht später SSO bereit, kann der lokale Login optional deaktiviert werden – siehe Kapitel&nbsp;12.)</li>
            <li><strong>Grunddaten setzen</strong> (<code>/admin/settings</code>): App-Name, Logo/Favicon, Zeitzone, Standard-Review-Intervall und – falls gewünscht – den Icon-Stil (Font&nbsp;Awesome oder Emoji).</li>
            <li><strong>Module aktivieren</strong> (<code>/admin/settings</code> → Bereich „Module"): Nur die Module einschalten, die Sie tatsächlich nutzen. Jedes Modul erscheint erst danach in Navigation und Berechtigungen. (Welche Module es gibt und was sie tun: Kapitel&nbsp;4.)</li>
            <li><strong>Stammdaten anlegen:</strong> <em>Standorte</em> (<code>/admin/locations</code>, hierarchisch) und bei Bedarf <em>Tags</em> (<code>/admin/tags</code>). Standorte dienen später auch als Geltungsbereich für Berechtigungen.</li>
            <li><strong>Benutzer &amp; Rollen einrichten</strong> (<code>/admin/roles</code>, dann <code>/admin/users</code>): Erst Rollen mit den passenden Berechtigungen anlegen, dann Benutzer anlegen und ihnen Rollen zuweisen – optional eingeschränkt auf ein Themengebiet oder einen Standort. Details und Berechtigungs-Referenz: Kapitel&nbsp;12.</li>
            <li><strong>Scheduler einrichten</strong> (Pflicht): Eine einzige Crontab-Zeile aktiviert alle Hintergrundaufgaben:
                <pre class="manual-code">* * * * * /usr/bin/php /pfad/zu/bin/scheduler.php &gt;&gt; /pfad/zu/storage/logs/scheduler.log 2&gt;&amp;1</pre>
                Anschließend unter <code>/admin/cronjobs</code> prüfen, ob Tasks laufen, und Intervalle/Aktiv-Status anpassen (Kapitel&nbsp;11).</li>
            <li><strong>E-Mail-Versand konfigurieren</strong> (<code>/admin/settings/email</code>): SMTP (Basic) oder OAuth (Microsoft&nbsp;365/Google) hinterlegen und eine Test-Mail senden. Viele Funktionen (Erinnerungen, Tickets, Genehmigungen) brauchen funktionierenden Mailversand – Details in Kapitel&nbsp;9.</li>
            <li><strong>Anmeldung &amp; Sicherheit</strong> (optional): SSO einrichten (Microsoft&nbsp;365 / Keycloak / AD-LDAP) und unter <code>/admin/settings/security</code> die optionalen Schutzfunktionen (Passwort-Richtlinie, Konto-Sperre, MFA-Erzwingung) abwägen. Für Web-Push zusätzlich VAPID unter <code>/admin/settings/pwa</code> erzeugen.</li>
            <li><strong>Loslegen:</strong> Die aktiven Module mit ersten Daten füllen (z. B. Standorte/Kategorien im Inventar, Postfach im Ticketsystem) – die jeweiligen „Einrichtung" und „Bedienung" stehen pro Modul in Kapitel&nbsp;4.</li>
        </ol>
        <div class="req">Vor dem Produktivgang zusätzlich die <strong>Produktions-Checkliste</strong> in Kapitel&nbsp;10 abarbeiten (Debug aus, Schlüssel gesetzt, <code>install.php</code> gelöscht, Backups).</div>
    </div>

    <h3>Mindest-Setup auf einen Blick</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Schritt</th><th scope="col">Wo</th><th scope="col">Pflicht?</th></tr></thead>
        <tbody>
            <tr><td>Module aktivieren</td><td><code>/admin/settings</code></td><td>ja</td></tr>
            <tr><td>Standorte anlegen</td><td><code>/admin/locations</code></td><td>für Tickets/Inventar</td></tr>
            <tr><td>Rollen &amp; Benutzer</td><td><code>/admin/roles</code>, <code>/admin/users</code></td><td>ja</td></tr>
            <tr><td>Scheduler-Crontab</td><td>Server-Crontab + <code>/admin/cronjobs</code></td><td>ja</td></tr>
            <tr><td>E-Mail-Versand</td><td><code>/admin/settings/email</code></td><td>dringend empfohlen</td></tr>
            <tr><td>SSO / MFA</td><td><code>/admin/settings/*</code></td><td>optional</td></tr>
        </tbody>
    </table>
</div>

<div class="manual-section" id="kern">
    <h2>3. Kernsystem (immer aktiv)</h2>
    <p>Folgende Funktionen sind fest im System integriert und können nicht deaktiviert werden:</p>

    <div class="module-card">
        <h4><i class="fas fa-file-alt"></i> Dokumentation: Seiten &amp; Themengebiete</h4>
        <p>Das Kernstück von OpenNIT. Inhalte werden in einer Hierarchie aus <strong>Themengebieten</strong> (Topics) und <strong>Seiten</strong> (Pages) organisiert. Themengebiete können verschachtelt werden. Jede Seite hat eine Versionshistorie.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Rich-Text-Editor (TinyMCE) für Seiteninhalte</li>
            <li>Vollständige Versionshistorie mit Diff-Anzeige und Wiederherstellung</li>
            <li>Verantwortlichkeiten: technisch und fachlich verantwortliche Person pro Seite</li>
            <li>Review-Fälligkeiten: konfigurierbare Prüfintervalle (30 / 90 / 180 / 365 / 730 Tage)</li>
            <li>Ansprechpartner: Verlinkung zu Kontakt, Unternehmen, Benutzer oder Verzeichnisperson</li>
            <li>Dateianhänge pro Seite und Tags zur thematischen Verschlagwortung</li>
            <li>PDF-Export einzelner Seiten, ZIP-Export ganzer Themengebiete (wahlweise verschlüsselt)</li>
            <li>Archivierung und Wiederherstellung von Seiten</li>
            <li>Seitenvorlagen für standardisierte Dokumentation</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Betriebs- und Notfalldokumentation mit fester Review-Pflicht</li>
            <li>Wissensdatenbank, deren Inhalte der KI-Chat als Kontext nutzt</li>
            <li>Strukturierte Übergabe-Dokumentation an Auditoren via verschlüsseltem ZIP-Export</li>
        </ul>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-search"></i> Volltext- &amp; modulübergreifende Suche</h4>
        <p>Systemweite Suche über alle Dokumentationsseiten (Titel und Inhalte, mit hervorgehobenen Fundstellen). Zusätzlich gibt es eine <strong>modulübergreifende Suche</strong>: ein gruppiertes Live-Dropdown in der Kopfzeile sowie eine Vollseite unter <code>/search</code>. Sie durchsucht – je nach aktiviertem Modul und Berechtigung – u.&nbsp;a. Inventar, Tickets, Verträge, Lizenzen, Leihgeräte, Lager, SSL und Bestellungen. Es werden nur Ergebnisse angezeigt, die der Benutzer sehen darf.</p>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-bell"></i> Benachrichtigungen</h4>
        <p>In-App- und E-Mail-Benachrichtigungen für relevante Ereignisse (Seitenänderungen, Ticketupdates, Ausleihvorgänge, ablaufende Verträge/Zertifikate etc.). Administratoren können Vorlagen anpassen.</p>
        <ul>
            <li>Progressive Web App (PWA) mit Push-Benachrichtigungen (optional)</li>
            <li>Konfigurierbare E-Mail-Vorlagen pro Ereignistyp (<code>/admin/notifications</code>)</li>
            <li>E-Mail-Queue für zuverlässige Zustellung</li>
        </ul>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-shield-alt"></i> Audit-Log</h4>
        <p>Alle schreibenden Operationen werden unveränderlich protokolliert. Der Log ist durch eine kryptografische Hash-Chain gegen nachträgliche Manipulation gesichert.</p>
        <ul>
            <li>Anzeige und Filterung unter <code>/admin/audit</code>, Export als CSV</li>
            <li>Aggregierte Berichte und Zusammenfassungen (<code>/admin/audit/reports</code>)</li>
            <li>Aufbewahrungsdauer: 10 Jahre; sensible Felder werden maskiert gespeichert</li>
        </ul>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-user"></i> Benutzerprofil</h4>
        <p>Jeder Benutzer kann sein Profil pflegen: Anzeigename, E-Mail, Passwort, TOTP-MFA, WebAuthn-Sicherheitsschlüssel und Unterschrift für Übergabeprotokolle.</p>
    </div>
</div>

<!-- ============================================================= -->
<!-- 3. Module -->
<!-- ============================================================= -->
<div class="manual-section" id="module">
    <h2>4. Module</h2>
    <p>Module werden unter <strong>Einstellungen → Allgemein</strong> bzw. in der Konfiguration aktiviert/deaktiviert.
       Der Aktiv-Status wird je Modul angezeigt. Die Module sind hier in sechs thematische Gruppen sortiert.</p>

    <!-- ---------- 3.1 Inventar & Beschaffung ---------- -->
    <h3 class="cat-heading" id="cat-inventar">4.1 Inventar &amp; Beschaffung</h3>

    <!-- orders -->
    <div class="module-card" id="mod-orders">
        <?= $moduleHeader($modules, 'orders', 'fa-shopping-cart', 'Bestellboard') ?>
        <p>Board-Ansicht für Bestellungen und Beschaffungsvorgänge. Bestellungen werden als Karten mit Statusverlauf verwaltet und lassen sich optional mit dem Budgetmodul verknüpfen.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Karten mit Artikelbezeichnung, Menge, Preis und Lieferant; Feld „Bestellt am" (Standard: heute, nachträglich bearbeitbar)</li>
            <li>Statusverlauf: <strong>Offen → Eingetroffen → (Abholbereit) → Archiviert</strong>. Der Zwischenschritt „Abholbereit" ist optional global aktivierbar (Einstellungen → Bestellungen) und benachrichtigt einen hinterlegten Empfänger</li>
            <li><strong>KPI-Kacheln</strong> am Seitenkopf (Offen / Eingetroffen / Abholbereit / Archiviert), klickbar als Tab-Filter</li>
            <li><strong>Server-seitige Suche</strong> über Titel, Referenz/Info, Empfänger und Lieferant – durchsucht auch das vollständige Archiv; zusätzlich Sortierung (Neueste/Älteste/Priorität/Titel) und Prioritäts-Filter</li>
            <li><strong>Teil-Lieferung</strong> (optional aktivierbar): Pro Komponente die erhaltene Menge erfassen; der Fortschritt wird stückgenau angezeigt (z. B. „3 von 5 erhalten")</li>
            <li><strong>Weiterleiten per E-Mail:</strong> Eine Bestellung lässt sich mit Empfänger, Betreff, Begleittext und Auswahl der angehängten Dateien direkt weiterleiten (Empfänger im Verzeichnis suchbar); die Weiterleitung erscheint im Verlauf der Bestellung</li>
            <li><strong>CSV-Export</strong> aller Bestellungen für Beschaffungsübersichten</li>
            <li><strong>Assets aus Bestellung anlegen:</strong> ab Status „Eingetroffen" (sowie Abholbereit/Archiviert) lassen sich die bestellten Komponenten direkt als Inventar-Geräte übernehmen</li>
            <li>Verknüpfung mit Tickets: Per Schnellaktion aus einem Ticket erstellte Bestellungen verlinken zurück auf das Ursprungs-Ticket (Link in den Bestell-Metadaten); das Ticket verweist im Verlauf auf die Bestellung</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Beschaffungs-Pipeline für Hardware/Software sichtbar machen</li>
            <li>Lieferzeiten und offene Bestellungen im Team nachverfolgen</li>
            <li>Beschaffung direkt aus einem Support-Ticket anstoßen (Schnellaktion im Ticket)</li>
            <li>Eingetroffene Hardware ohne Doppelerfassung direkt ins Inventar überführen</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Bestellung anlegen:</strong> auf <code>/orders</code> „Neue Bestellung" – Titel und Info/Referenz eingeben, je Zeile eine Komponente mit Name und Menge erfassen, Priorität, Lieferant (verknüpft oder als Text) und Empfänger (Person aus dem Verzeichnis oder frei) wählen; das Bestelldatum steht auf heute und ist anpassbar.</li>
                <li><strong>Fortschritt pflegen:</strong> eingetroffene Komponenten abhaken; bei aktivierter Teil-Lieferung stattdessen die erhaltene Menge eintragen. Sind alle Komponenten erledigt, wechselt die Bestellung auf „Eingetroffen".</li>
                <li><strong>Abschließen:</strong> optional zuerst „Abholbereit" setzen (benachrichtigt den Empfänger), dann „Abschließen" mit Lieferart und optionaler Notiz – die Bestellung wandert ins Archiv. Versehentlich? „Wieder öffnen".</li>
                <li><strong>Dokumentieren &amp; weiterleiten:</strong> über die Karte Kommentare schreiben und Dateien anhängen; mit „Weiterleiten" geht die Bestellung mit ausgewählten Anhängen an eine E-Mail-Adresse.</li>
                <li><strong>Finden &amp; auswerten:</strong> über die KPI-Tabs filtern, per Suchfeld (inkl. Archiv) suchen, sortieren; „CSV-Export" für Auswertungen; ab „Eingetroffen" lassen sich die Komponenten direkt als Inventar-Geräte übernehmen.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional: Budget (Kostenzuordnung), Tickets (Schnellaktion &amp; Rückverknüpfung), Assets (Geräte aus Bestellung anlegen)</div>
    </div>

    <!-- budget -->
    <div class="module-card" id="mod-budget">
        <?= $moduleHeader($modules, 'budget', 'fa-euro-sign', 'Budget') ?>
        <p>Budgetverwaltung für IT-Ausgaben mit Soll-Ist-Vergleich und revisionssicherem Belegnachweis. Jedes Budget lässt sich entweder <strong>strukturiert</strong> (in einzelne Positionen unterteilt) oder als <strong>Pauschalbudget</strong> (ein Gesamtbetrag, von dem Belege direkt abgezogen werden) führen.</p>

        <h5>Übersicht &amp; Kennzahlen</h5>
        <ul>
            <li>KPI-Kacheln am Seitenkopf: <strong>aktive Budgets</strong>, <strong>geplantes Gesamtbudget</strong> (€), <strong>tatsächliche Kosten</strong> (€, grün solange ≤ Plan, rot bei Überschreitung) und <strong>überschrittene Budgets</strong> (Anzahl)</li>
            <li>Filter &amp; Suche: Freitext (Titel, Abteilung, Beschreibung), Status (Entwurf / Aktiv / Abgeschlossen / Archiviert), Typ (Abteilung / Projekt), Geschäftsjahr und Kategorie</li>
        </ul>

        <h5>Budget anlegen &amp; führen</h5>
        <ul>
            <li><strong>Zwei Modi:</strong> <em>strukturiert</em> (geplante Positionen mit Einzelbeträgen) oder <em>pauschal</em> (Gesamtbetrag, Belege ziehen direkt ab)</li>
            <li>Stammdaten: Titel, Beschreibung, Typ (Abteilung/Projekt), Abteilungsname, verantwortliche Person, Zeitraum (von/bis), Geschäftsjahr, Status und <strong>Warnschwelle</strong> (Auslastung in %, Standard 80 %)</li>
            <li><strong>Positionen</strong> mit Titel, Kategorie, Beschreibung, <em>Planwert</em> und <em>verplantem Betrag</em> (z. B. bereits ausgelöste Bestellung); optionale Verknüpfung zu einem Vertrag, einer Lizenz oder einer Bestellung (Nachvollziehbarkeit)</li>
            <li><strong>Zusammenfassung</strong> je Budget: geplant, verplant, Ist, verbleibend, Varianz und Auslastung (%) mit Fortschrittsbalken; Positionen über Budget werden hervorgehoben</li>
        </ul>

        <h5>Belege</h5>
        <ul>
            <li>Beleg-Upload pro Position (strukturiert) bzw. direkt zum Budget (pauschal); erlaubt sind PDF und Bilder (JPG/PNG/GIF/WEBP)</li>
            <li>Metadaten je Beleg: Bezeichnung, Lieferant, Rechnungsnummer, Rechnungsdatum, Notiz sowie <strong>Brutto- und Netto-Betrag</strong>; über den MwSt-Satz wird der jeweils fehlende Wert automatisch berechnet</li>
            <li><strong>Beleg-Vorschau</strong> direkt im Fenster (PDF und Bilder), ohne Download; <strong>Metadaten nachträglich bearbeitbar</strong> (die Datei selbst bleibt unverändert)</li>
            <li>Das Ist eines Budgets ergibt sich aus der Summe der Belegbeträge</li>
        </ul>

        <h5>Kategorien, Duplizieren &amp; Export</h5>
        <ul>
            <li><strong>Kategorien</strong> mit Name, Farbe und Sortierung (Kategorie-Aufschlüsselung geplant vs. Ist)</li>
            <li><strong>Budget duplizieren / Jahreswechsel:</strong> ein Budget inkl. Positionen kopieren (ohne Belege/Ist-Werte) – optional mit neuem Titel und automatisch erhöhtem Geschäftsjahr; die Kopie startet als „Entwurf"</li>
            <li><strong>CSV-Export</strong> eines einzelnen Budgets (Positionen mit Plan/Verplant/Ist/Differenz/Auslastung) und der Gesamtübersicht; <strong>PDF-Druckbericht</strong> je Budget; eigene Auswertungsseite mit Kategorie-Aufschlüsselung</li>
            <li><strong>Schwellenwert-Warnung:</strong> Ein Hintergrund-Task benachrichtigt die verantwortliche Person, sobald die Warnschwelle bzw. 100 % erreicht ist (je Stufe nur einmal)</li>
        </ul>

        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>IT-Jahresbudget pro Kostenstelle planen und überwachen</li>
            <li>Belegnachweis für Bestellungen aus dem Bestellboard</li>
            <li>Pauschalbudget für ein Projekt, von dem laufend Rechnungen abgezogen werden</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Budget anlegen:</strong> auf <code>/budget</code> „Neues Budget" – Modus wählen (<em>strukturiert</em> oder <em>pauschal</em>), Titel, Typ (Abteilung/Projekt), verantwortliche Person, Geschäftsjahr/Zeitraum und Warnschwelle setzen. Status zunächst „Entwurf", später auf „Aktiv" stellen.</li>
                <li><strong>Positionen planen</strong> (strukturiert): im Budget Positionen mit Titel, Kategorie und Planwert hinzufügen; bereits ausgelöste Beträge als „verplant" eintragen. Beim Pauschalbudget entfällt das – Belege ziehen direkt vom Gesamtbetrag ab.</li>
                <li><strong>Belege erfassen:</strong> über „Neuer Beleg" eine Rechnung (PDF/Bild) zur Position bzw. zum Budget hochladen, mit Bezeichnung, Lieferant, Rechnungsnummer/-datum und Brutto- oder Netto-Betrag (der MwSt-Satz ergänzt den jeweils fehlenden Wert). Belege lassen sich im Fenster ansehen und nachbearbeiten.</li>
                <li><strong>Überwachen:</strong> Zusammenfassung und Fortschrittsbalken zeigen geplant/verplant/Ist/verbleibend und die Auslastung; überschrittene Positionen sind hervorgehoben. Bei aktivem Scheduler meldet sich das System ab Erreichen der Warnschwelle.</li>
                <li><strong>Auswerten &amp; fortschreiben:</strong> CSV/PDF exportieren, die Auswertungsseite mit Kategorie-Aufschlüsselung öffnen; zum Jahreswechsel das Budget über „Duplizieren" inkl. Positionen ins neue Jahr kopieren.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Berechtigungen:</strong> <code>view_budgets</code> (Lesen/Export), <code>manage_budgets</code> (Anlegen/Bearbeiten, Belege, Kategorien)</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional: Orders, Contracts, Licenses (Verknüpfung von Positionen); Scheduler für die Schwellenwert-Warnung</div>
    </div>

    <!-- assets -->
    <div class="module-card" id="mod-assets">
        <?= $moduleHeader($modules, 'assets', 'fa-laptop', 'Inventar') ?>
        <p>Zentrale Verwaltung aller IT-Assets (Hardware, Peripherie, Lizenzen etc.). Jedes Asset hat Stammdaten, Standort, Kategorie, Hersteller, Seriennummer, Kaufdatum, Garantie und benutzerdefinierte Felder.</p>

        <h5>Stammdaten &amp; Struktur</h5>
        <ul>
            <li>Kategorien, Typen und Hersteller frei konfigurierbar</li>
            <li>Benutzerdefinierte Felder pro Kategorie</li>
            <li>Standortzuweisung (Standorte aus Stammdaten, inkl. untergeordneter Standorte)</li>
            <li>Automatische Inventarnummern pro Kategorie (Format mit Jahr/Standortpräfix konfigurierbar)</li>
            <li>Massenanlage (Bulk) – die gesamte Charge wird atomar angelegt (alles oder nichts)</li>
            <li>Verlinkung mit Tickets</li>
        </ul>

        <h5>Suche, Filter &amp; Ansichten</h5>
        <ul>
            <li>Volltextsuche über Inventarnummer, Seriennummer, Name, Modell, Hersteller, zugewiesene Person</li>
            <li>Filter nach Hersteller, Modell, Standort, Status, Zuweisung sowie Garantie (läuft bald ab / abgelaufen)</li>
            <li><strong>Gespeicherte Ansichten</strong> pro Benutzer: aktuelle Filterkombination benennen und mit einem Klick wieder aufrufen</li>
            <li>Kachel- oder Tabellenansicht (Auswahl wird pro Benutzer gemerkt)</li>
            <li><strong>CSV-Export</strong> der aktuell gefilterten Liste (UTF-8, Excel-kompatibel) – im Audit-Log protokolliert</li>
        </ul>

        <h5>Wert &amp; Nutzungsdauer (optional, einzeln zuschaltbar)</h5>
        <ul>
            <li>Drei Angaben, die unter <code>/admin/assets/valuation</code> <strong>getrennt</strong> ein- und ausgeschaltet werden; keine setzt eine andere voraus. Ab Werk sind alle drei aus – solange sie es sind, erscheinen weder Felder noch Spalten.</li>
            <li><strong>Nutzungsdauer:</strong> Feld „Nutzungsdauer (Monate)" am Gerät, ab dem Kaufdatum gerechnet. Daraus entstehen das <em>Nutzungsende</em> auf der Geräteseite und in der Liste, ein Filter (läuft aus in 6 Monaten / abgelaufen / ohne Angabe) und eine KPI-Kachel „Nutzungsdauer abgelaufen" für die Ersatzplanung. <strong>Kommt ohne Anschaffungswert aus</strong> – wer nur den Ersatzzeitpunkt braucht, schaltet nur das ein.</li>
            <li><strong>Abschreibung:</strong> lineare Abschreibung aus Anschaffungswert und Nutzungsdauer – Betrag je Jahr, je Monat und die bis heute abgeflossene Summe.</li>
            <li><strong>Restwert:</strong> der heutige Buchwert am Gerät, als Spalte in der Tabellenansicht, auf den Kacheln und als Summe über den gefilterten Bestand sowie als KPI-Kachel „Restwert gesamt". Abschreibung und Restwert lassen sich einzeln nutzen – der Restwert braucht die Abschreibungstabelle nicht und umgekehrt.</li>
            <li><strong>Rechenweg</strong> wählbar: <em>monatsgenau</em> (der Anschaffungsmonat zählt voll mit, wie bei der zeitanteiligen AfA) oder <em>volle Jahre</em> (bucht erst nach jedem vollendeten Jahr ab Kaufdatum). Gilt für Abschreibung und Restwert gleichermaßen.</li>
            <li><strong>Vorgabe je Kategorie:</strong> eine übliche Nutzungsdauer hinterlegen (z. B. Clients 36, Server 60, Netzwerk 96 Monate) – sie belegt das Feld beim Anlegen vor und füllt nur eine Lücke. Eine geänderte Vorgabe wirkt nur auf neu angelegte Geräte; vorhandene Buchwerte bleiben unverändert.</li>
            <li>Fehlt eine Grundlage (Kaufdatum, Nutzungsdauer, bei Beträgen der Anschaffungswert), bleibt die Angabe leer – es wird nichts geschätzt. Der Restwert fällt nie unter null.</li>
            <li>Die zugeschalteten Angaben erscheinen zusätzlich im <strong>CSV-Export</strong>.</li>
        </ul>

        <h5>Datei-Anhänge</h5>
        <ul>
            <li>Beliebige Dateien pro Gerät (Rechnungen, Lieferscheine, Fotos, Konfig-Doku …) im Tab „Anhänge"</li>
            <li>Erlaubt: PDF, Bilder, Office-Dokumente, Text, ZIP – max. 20&nbsp;MB pro Datei</li>
            <li>Sichere Ablage außerhalb des öffentlichen Bereichs; Download nur für berechtigte Benutzer</li>
        </ul>

        <h5>Etiketten &amp; QR-Code</h5>
        <ul>
            <li>Etikettendruck via DYMO-Drucker oder Windows-Druckdialog</li>
            <li>Etikett enthält einen <strong>QR-Code</strong>, der direkt auf die Geräteseite verlinkt (Voraussetzung: konfigurierte <code>app.url</code>)</li>
            <li>Scanner in der Listenansicht: QR-Code öffnet das Gerät, Barcode (Seriennummer) startet die Suche</li>
        </ul>

        <h5>Garantie (Dell / HP / HPE)</h5>
        <ul>
            <li>Automatische Garantieabfrage über die Hersteller-APIs</li>
            <li><strong>Dell:</strong> Abruf läuft nicht mehr live beim Öffnen, sondern <strong>gestaffelt über den Scheduler</strong> – die Daten sind beim Aufruf sofort da. Wegen des Dell-API-Tageslimits wird die Anzahl pro 24&nbsp;h begrenzt (einstellbar). Der Abruf <strong>stoppt</strong>, sobald alle Geräte erfasst sind.</li>
            <li>Pro Gerät jederzeit manuell aktualisierbar; die aktive Garantie wird nach Datum erkannt</li>
            <li>Fortschritt („X/Y abgefragt") und Tageslimit unter <code>/admin/settings/dell</code></li>
        </ul>

        <h5>Automatischer Hardware-Sync (PowerShell-Agent)</h5>
        <ul>
            <li>Windows-Clients melden Hardware-Daten an <code>/api/assets/sync</code> (Einrichtung unter <code>/admin/inventory/sync</code>)</li>
            <li>Zuordnung über Seriennummer bzw. Hostname; <strong>mehrdeutige Treffer</strong> werden nicht geraten, sondern unter „Unbekannte Geräte" zur manuellen Klärung abgelegt</li>
            <li>Absicherung: Bearer-Token, Zeitstempel-Prüfung, Replay-Schutz und ein pro API-Key konfigurierbares Rate-Limit</li>
            <li>Hardware-Abweichungen (Modell ≠ hinterlegter Typ) werden markiert</li>
        </ul>

        <h5>Weitere Importe</h5>
        <ul>
            <li>Relution MDM-Sync (mobile Geräte) über den Scheduler</li>
            <li>GLPI-Import (einmalig oder wiederholt)</li>
        </ul>

        <h5>Übergabe &amp; Datensicherheit</h5>
        <ul>
            <li>Übergabeprotokoll (Ausgabe/Rücknahme) mit digitaler Unterschrift auf dem Tablet und PDF</li>
            <li>Schutz vor versehentlichem Überschreiben: zeitgleiche Bearbeitung wird erkannt und der zweite Speichervorgang abgelehnt</li>
            <li>Warnung bei doppelter Seriennummer (ohne harte Sperre, um Altdaten/Importe nicht zu blockieren)</li>
        </ul>

        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/assets/categories</code>, <code>/admin/assets/manufacturers</code>, <code>/admin/inventory/sync</code>, <code>/admin/inventory/unmatched-devices</code>, <code>/admin/inventory/import/glpi</code>, <code>/admin/assets/valuation</code>, <code>/admin/assets/handover-settings</code>, <code>/admin/settings/dell</code>, <code>/admin/settings/hp</code>, <code>/admin/settings/label-print</code> &nbsp;·&nbsp; Berechtigung: <code>admin_assets</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Kategorien anlegen</strong> (<code>/admin/assets/categories</code>): Name, Icon, Farbe und Nummernformat (Platzhalter z.&nbsp;B. <code>INV-%Jahr%-%fortlaufendenummer%</code>) festlegen; optional „Zähler jährlich zurücksetzen". Pro Kategorie über „🔧 Felder" benutzerdefinierte Felder (Freitext/Dropdown) definieren.</li>
                <li><strong>Hersteller pflegen</strong> (<code>/admin/assets/manufacturers</code>): Name und – für automatische Garantieabfragen – die Garantie-API (Dell, HP Inc. oder HPE) hinterlegen.</li>
                <li><strong>Modelle/Typen</strong> (<code>/admin/assets/types</code>): Geräte­typen je Hersteller mit technischen Daten und Bild anlegen.</li>
                <li><strong>Automatische Inventarisierung</strong> (optional):
                    <ul>
                        <li><em>PowerShell-Agent</em> (<code>/admin/inventory/sync</code>): API-Key kopieren, Rate-Limit setzen, fertiges Skript <code>asset-sync-agent.ps1</code> herunterladen und per GPO verteilen.</li>
                        <li><em>Microsoft Intune</em> (<code>/admin/inventory/intune</code>): nutzt die Zugangsdaten aus den M365-Einstellungen (keine separate App nötig); Berechtigung <code>DeviceManagementManagedDevices.Read.All</code> per Consent-Knopf erteilen, „Verbindung testen", Betriebssysteme und Kategorien je Geräteklasse wählen, „Auto-Anlegen" vs. Liste unbekannter Geräte entscheiden, optional Richtlinien/Beta-Hardware; „Vorschau" dann „Sync".</li>
                        <li><em>Relution MDM</em> (<code>/admin/inventory/relution</code>): Server-URL + Access-Token, Plattformen und Kategorien wählen.</li>
                        <li><em>GLPI-Import</em> (<code>/admin/inventory/import/glpi</code>): URL/Token, Gerätetypen den Kategorien zuordnen, „Vorschau" → „Importieren".</li>
                    </ul>
                </li>
                <li><strong>Garantieabfrage</strong> (optional): Dell unter <code>/admin/settings/dell</code> (Client-ID/Secret, Geräte pro Tag) – der Cron <code>dell_warranty_refresh</code> ruft gestaffelt ab; HP/HPE unter <code>/admin/settings/hp</code>.</li>
                <li><strong>Wert &amp; Nutzungsdauer</strong> (optional, <code>/admin/assets/valuation</code>): entscheiden, welche der drei Angaben – Nutzungsdauer, Abschreibung, Restwert – geführt werden; jede einzeln zuschaltbar. Danach den Rechenweg (monatsgenau oder volle Jahre) wählen und je Kategorie eine übliche Nutzungsdauer hinterlegen.</li>
                <li><strong>Geräteübergabe</strong> (optional): PDF- und E-Mail-Vorlagen unter <code>/admin/assets/handover-settings</code>; das login-freie Unterschriften-Tablet wird per IP-Freigabe unter <code>/admin/settings/signature</code> abgesichert.</li>
                <li><strong>Etikettendruck</strong> (optional, <code>/admin/settings/label-print</code>): DYMO-Etikettengröße, Druckmethode und Etikett-Logo wählen.</li>
                <li><strong>Unbekannte Geräte</strong> (<code>/admin/inventory/unmatched-devices</code>): vom Sync gemeldete, nicht zugeordnete Geräte einem Asset zuordnen oder neu anlegen.</li>
            </ol>
            <div class="req">Berechtigung: <code>admin_assets</code> für alle Inventar-Einstellungen. Sync-Agenten benötigen einen API-Token; Garantie-/MDM-APIs Zugangsdaten des jeweiligen Anbieters.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Gerät anlegen:</strong> auf <code>/assets</code> eine Kategorie öffnen und „Neu" – die Inventarnummer wird nach dem Kategorieformat automatisch vergeben; Stammdaten (Name, Seriennummer, Hersteller, Typ, Standort, Zuweisung, Kaufdatum, Garantie) erfassen. Für viele gleiche Geräte „Massenanlage" (Charge bis 50 Stück, optional je Seriennummer).</li>
                <li><strong>Suchen &amp; filtern:</strong> Volltextsuche (Inventar-/Seriennummer, Name, Person …) und Filter (Hersteller, Standort, Status, Garantie) kombinieren; häufige Kombinationen als <em>gespeicherte Ansicht</em> benennen, zwischen Kachel- und Tabellenansicht wechseln und die gefilterte Liste als CSV exportieren.</li>
                <li><strong>Gerät pflegen:</strong> auf der Detailseite Dateien im Tab „Anhänge" ablegen, Tickets verknüpfen, benutzerdefinierte Felder ausfüllen und die Hersteller-Garantie bei Bedarf manuell aktualisieren.</li>
                <li><strong>Etikett &amp; Scan:</strong> über „Etikett drucken" ein Label mit QR-Code erzeugen; in der Liste per Scanner einen QR-Code öffnen oder eine Seriennummer suchen.</li>
                <li><strong>Übergeben &amp; zurücknehmen:</strong> „Unterschrift anfordern" → die Person quittiert auf dem Tablet, OpenNIT erzeugt das Übergabe-PDF und versendet es; bei der Rücknahme wird der Status wieder auf „Lager" gesetzt.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> Standorte (Stammdaten), optional: Tickets, Loans, Stock</div>
    </div>

    <!-- stock -->
    <div class="module-card" id="mod-stock">
        <?= $moduleHeader($modules, 'stock', 'fa-boxes', 'Lager') ?>
        <p>Lagerverwaltung für IT-Verbrauchsmaterial und Ersatzteile mit fortlaufender Bestandsführung. Der Bestand ändert sich ausschließlich über Ein- und Ausgangsbuchungen, sodass jede Bewegung nachvollziehbar bleibt.</p>
        <h5>Übersicht &amp; Artikel</h5>
        <ul>
            <li><strong>KPI-Kacheln:</strong> Artikel gesamt, Stück gesamt, „geringer Bestand" (Menge ≤ Mindestmenge) und „nicht vorrätig" (Menge 0)</li>
            <li><strong>Bestandsliste</strong> mit farbcodiertem Mengen-Badge (grün OK / gelb gering / rot leer), Kategorie, Modell/Artikel-Nr. und Lagerort; Suche über Name/Modell/Beschreibung und Filter nach Kategorie (mit Artikelzählern)</li>
            <li><strong>Artikel anlegen</strong> (Modal): Kategorie (Pflicht), Name, Modell/Artikel-Nr., Beschreibung, <em>Anfangsbestand</em>, <em>Mindestmenge</em> (löst die „gering"-Warnung aus) und Lagerort; Bearbeiten und Löschen (löscht auch die Buchungen) laufen ebenfalls als Modal-Dialoge</li>
        </ul>
        <h5>Buchungen &amp; Historie</h5>
        <ul>
            <li><strong>Einbuchen</strong> (Menge + optionaler Grund, z. B. Lieferung) und <strong>Ausbuchen</strong> (Menge + Empfänger/Grund); Ausbuchen über den vorhandenen Bestand hinaus wird abgelehnt</li>
            <li><strong>Buchungshistorie</strong> je Artikel (<code>/stock/{id}/movements</code>): Datum/Uhrzeit, Typ (Eingang/Ausgang, farbig), Menge (mit Vorzeichen), Grund/Notiz und Benutzer; jede Buchung wird im Audit-Log protokolliert</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Bestand an Tonern, Kabeln, Ersatznetzteilen führen</li>
            <li>Automatische Warnung, wenn ein Verbrauchsartikel zur Neige geht</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Kategorien anlegen</strong> (einmalig, <code>/admin/stock/categories</code>): z. B. „Toner", „Kabel", „Ersatzteile" mit Icon und Sortierung.</li>
                <li><strong>Artikel anlegen:</strong> auf <code>/stock</code> „Artikel anlegen" – Kategorie, Name, Modell/Artikel-Nr., Anfangsbestand, Mindestmenge und Lagerort eintragen (alles als Modal, ohne Seitenwechsel).</li>
                <li><strong>Buchen:</strong> bei einer Lieferung „Einbuchen" (Menge + Grund), bei Entnahme „Ausbuchen" (Menge + Empfänger/Grund). Mehr ausbuchen als vorhanden ist nicht möglich.</li>
                <li><strong>Überwachen:</strong> die KPI-Kacheln und farbigen Mengen-Badges zeigen knappe und leere Artikel; ein Klick auf einen Artikel öffnet die vollständige Buchungshistorie.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine</div>
    </div>

    <!-- licenses -->
    <div class="module-card" id="mod-licenses">
        <?= $moduleHeader($modules, 'licenses', 'fa-certificate', 'Lizenzverwaltung') ?>
        <p>Verwaltung von Software-Lizenzen mit Sitz-, Kosten- und Ablaufüberwachung sowie optionalem automatischem Sync aus Microsoft 365 / Entra ID. Lizenzen können Verträgen zugeordnet und gruppiert werden.</p>

        <h5>Übersicht &amp; Kennzahlen</h5>
        <ul>
            <li>KPI-Kacheln: Anzahl Lizenzen, <strong>belegte Sitze</strong> (genutzt/gesamt, „∞" bei unbegrenzten), <strong>Kosten pro Monat</strong> und <strong>pro Jahr</strong> (Abrechnungsintervalle werden automatisch normalisiert) sowie <strong>bald ablaufend</strong> (≤ 90 Tage)</li>
            <li>Filter &amp; Suche über Name/Hersteller/Produkt, Lizenztyp, Status, „bald ablaufend" und Archiv; sortierbare Tabelle mit farbcodiertem Status und Ablaufdatum</li>
        </ul>

        <h5>Lizenz-Stammdaten</h5>
        <ul>
            <li><strong>Lizenztypen:</strong> Abonnement, Dauerlizenz, Volumenlizenz, OEM, Open Source, Testlizenz; <strong>Status:</strong> Aktiv, Test, Abgelaufen, Gekündigt, Archiviert</li>
            <li><strong>Sitze</strong> (gesamt/genutzt, leer = unbegrenzt) sowie <strong>Kosten</strong> mit Betrag und Intervall (monatlich/quartalsweise/jährlich/einmalig); Gültigkeit von/bis</li>
            <li><strong>Lizenzschlüssel</strong> wird verschlüsselt gespeichert (nur für angemeldete Benutzer sichtbar, mit „Anzeigen"-Schalter); zusätzlich beliebig viele <strong>Lizenzdateien</strong> pro Eintrag (außerhalb des Webroots abgelegt)</li>
            <li><strong>Gruppierung:</strong> Lizenzen lassen sich einer übergeordneten Haupt-Lizenz zuordnen (eine Ebene); aus Microsoft Entra synchronisierte Lizenzen erscheinen zusätzlich in einer eigenen, einklappbaren „Microsoft Entra"-Gruppe</li>
            <li><strong>Vertragsverknüpfung</strong> per Suchfeld (mehrere Verträge je Lizenz möglich, zeigt auch die zugehörige Firma)</li>
        </ul>

        <h5>Automatik &amp; Export</h5>
        <ul>
            <li><strong>Automatische Archivierung:</strong> Beim Öffnen der Liste werden abgelaufene Lizenzen automatisch auf „Archiviert" gesetzt</li>
            <li><strong>Ablauf im Wartungskalender</strong> und im Fristen-Cockpit; Benachrichtigung vor Ablauf</li>
            <li><strong>Microsoft-365-Sync (Entra):</strong> liest die abonnierten SKUs aus dem Tenant und legt je SKU einen Eintrag an bzw. aktualisiert Sitze und Status – Notizen, Kosten und Vertragsbezüge bleiben dabei unangetastet</li>
            <li><strong>PDF-Export</strong> der (gefilterten) Lizenzliste, nach Typ gruppiert</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Microsoft-365-Lizenzbestand automatisch aktuell halten</li>
            <li>Lizenz-Ablauf rechtzeitig erkennen und Vertrag zuordnen</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/licenses/sync</code> (M365-Sync-Einstellungen)</div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>licenses</code>.</li>
                <li><strong>Lizenzen manuell pflegen</strong> oder den automatischen Microsoft-365-Sync nutzen.</li>
                <li><strong>Entra-Sync</strong> (optional): App-Registrierung mit Berechtigung <code>Organization.Read.All</code> + Admin-Consent unter <code>/admin/settings/m365</code> hinterlegen (dieselben Zugangsdaten wie für SSO/Mail).</li>
                <li><strong>Sync einschalten</strong> (<code>/admin/licenses/sync</code>): „Lizenzen automatisch aus Microsoft Entra synchronisieren" aktivieren und speichern; mit „Lizenzen jetzt synchronisieren" sofort starten. Notizen, Kosten und Vertragsbezüge bleiben erhalten.</li>
            </ol>
            <div class="req">Berechtigung: <code>manage_users</code> für den Verzeichnis-Sync. Der automatische Lauf erfolgt über den Verzeichnis-/Lizenz-Cron.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Lizenz anlegen:</strong> auf <code>/licenses</code> „Lizenz anlegen" – Name, Hersteller/Produkt, Lizenztyp und Status wählen, Gültigkeit und Sitze (gesamt/genutzt, leer = unbegrenzt) sowie Kosten mit Abrechnungsintervall erfassen.</li>
                <li><strong>Schlüssel &amp; Dateien:</strong> den Lizenzschlüssel hinterlegen (wird verschlüsselt gespeichert, mit „Anzeigen"-Schalter) und Lizenzdateien hochladen; optional Verträge per Suchfeld verknüpfen.</li>
                <li><strong>Gruppieren:</strong> zusammengehörige Lizenzen einer übergeordneten Haupt-Lizenz zuordnen, um sie gebündelt zu führen.</li>
                <li><strong>Überwachen:</strong> die KPI-Kacheln zeigen belegte Sitze, Monats-/Jahreskosten und bald ablaufende Lizenzen; abgelaufene werden beim Öffnen der Liste automatisch archiviert. Über Filter und Suche gezielt einschränken und bei Bedarf als PDF exportieren.</li>
                <li><strong>Microsoft 365</strong> (optional): den Entra-Sync einschalten (siehe Einrichtung) – die SKUs werden automatisch eingelesen; Notizen, Kosten und Vertragsbezüge bleiben erhalten.</li>
                <li><strong>Abgleich mit dem Software-Inventar</strong> (optional, siehe unten): auf der Lizenzseite unter <em>Software-Inventar</em> das zugehörige Programm zuordnen, dann unter <em>Lizenzen → Abgleich</em> gekaufte Plätze und tatsächliche Installation nebeneinander lesen.</li>
            </ol>
        </div>
        <h5>Wer hält welchen Platz (Microsoft)</h5>
        <p>An einer Microsoft-Lizenz steht standardmäßig nur die <em>Zahl</em> der belegten Plätze (<code>consumedUnits</code> aus dem Entra-Abgleich). Optional übernimmt der Abgleich auch die Namen.</p>
        <ul>
            <li>Einzuschalten unter <em>Administration → Lizenz-Sync → Platzhalter</em>; standardmäßig aus, weil es eine Angabe über Personen ist.</li>
            <li>Die Angabe stammt aus <code>assignedLicenses</code> am Benutzerobjekt und kostet keinen Abruf je Person. Zusätzlich nötig: <code>User.Read.All</code> als Application-Permission.</li>
            <li>Sichtbar an der Lizenz unter <em>Zugeordnet an</em> mit der Herkunft <em>Entra</em>. Solche Zuordnungen lassen sich hier nicht aufheben – der nächste Lauf trüge sie wieder ein; geändert werden sie in Entra.</li>
            <li>Der Abgleich ersetzt bei jedem Lauf <strong>nur seine eigenen</strong> Zeilen. Von Hand gepflegte Zuordnungen bleiben bestehen, auch an denselben Lizenzen.</li>
            <li>Personen, die der Verzeichnisabgleich nicht kennt, werden übersprungen – angelegt werden sie dort, nicht hier.</li>
        </ul>

        <h5>Abgleich mit dem Software-Inventar</h5>
        <p>Erfordert das Modul <em>Software-Inventar</em>. Stellt unter <code>/licenses/abgleich</code> die gekauften Plätze der tatsächlichen Installation gegenüber.</p>
        <ul>
            <li><strong>Die Zuordnung Lizenz ↔ Programm wird gepflegt, nicht geraten.</strong> Eine Lizenz heißt „Microsoft 365 Apps for Enterprise", installiert ist „Microsoft 365 Apps for enterprise – de-de". Auf der Lizenzseite gibt es dafür Vorschläge aus dem Bestand; übernommen wird mit einem Klick. Mehrere Programme je Lizenz sind der Normalfall.</li>
            <li>Bewertung je Lizenz: <em>frei</em>, <em>genau ausgelastet</em> oder <em>mehr installiert als lizenziert</em>. Eine Lizenz ohne Platzbegrenzung gilt nie als überzogen.</li>
            <li>Darunter die Programme, die installiert sind, aber zu keiner Lizenz gehören. Das ist <strong>keine</strong> Aussage über Lizenzpflicht – vieles davon ist frei –, sondern die Liste dessen, worüber noch niemand entschieden hat. Systembestandteile sind ausgenommen.</li>
        </ul>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional: Contracts, Microsoft 365 / Entra ID (für Sync), Software-Inventar (für den Abgleich)</div>
    </div>

    <!-- software -->
    <div class="module-card" id="mod-software">
        <?= $moduleHeader($modules, 'software', 'fa-box-open', 'Software-Inventar') ?>
        <p>Welche Programme auf welchem Gerät installiert sind. Erfasst wird über die beiden Wege, die ohnehin Gerätedaten holen: den <strong>Inventar-Agenten</strong> (Windows, PowerShell) und den <strong>Intune-Abgleich</strong>. Das Modul selbst erhebt nichts – es zeigt, was diese Quellen melden.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Liste aller erfassten Programme unter <code>/software</code> mit Hersteller, Zahl der Geräte, Zahl der Versionen im Umlauf und letzter Sichtung</li>
            <li>Je Programm die Geräte, auf denen es liegt – mit Version, Standort, Installationsdatum und Quelle</li>
            <li>Reiter <em>Software</em> auf jeder Geräteseite</li>
            <li>Filter nach Hersteller und Freitext; Systembestandteile ein-/ausblendbar</li>
            <li>Zusammenfassung gleicher Programme: 32-/64-Bit-Fassung, abweichende Schreibweise und Schutzzeichen ergeben einen Eintrag; die Version steht an der Installation, nicht am Programm</li>
            <li>Automatische Einstufung von Updates, Laufzeitumgebungen, Treibern und Sprachpaketen als <em>Systembestandteil</em> (erfasst, aber ausgeblendet) – je Programm von Hand korrigierbar</li>
        </ul>
        <h5>Was erfasst wird – und was nicht</h5>
        <ul>
            <li><strong>Beide Quellen sind einzeln einzuschalten</strong> und im Auslieferungszustand aus. Ohne eingeschaltete Quelle wird nichts gelesen und nichts gespeichert.</li>
            <li><strong>Agent:</strong> liest die maschinenweit installierten Programme aus der Registry (HKLM, beide Zweige). Was einzelne Anmeldenamen für sich installiert haben (HKCU), bleibt bewusst außen vor – das wäre eine Aussage über Personen und gehört nicht ins Geräteinventar. Einträge, die Windows selbst als Systembestandteil oder Update führt, werden schon auf dem Gerät weggelassen.</li>
            <li><strong>Intune:</strong> übernimmt die von Intune erkannten Programme (<code>detectedApps</code>). Kostet einen zusätzlichen Abruf je Gerät. Name und Version kommen immer, der Hersteller nur mit eingeschalteter Beta-Schnittstelle.</li>
            <li>Jede Quelle meldet den vollständigen Stand eines Geräts; ihr Bestand wird deshalb <strong>ersetzt</strong>, nicht ergänzt. Ein deinstalliertes Programm verschwindet damit von selbst – und die jeweils andere Quelle bleibt unberührt.</li>
        </ul>
        <div class="admin-links"><strong>Erfassung einstellen:</strong> <code>/admin/software/settings</code> &nbsp;·&nbsp; Berechtigungen: <code>view_software</code> (ansehen), <code>manage_software</code> (einstellen)</div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <em>Software-Inventar</em>.</li>
                <li><strong>Quelle wählen</strong> unter <code>/admin/software/settings</code> – Agent, Intune oder beides.</li>
                <li><strong>Beim Agenten:</strong> das PowerShell-Skript unter <code>/admin/inventory/sync</code> <strong>neu herunterladen</strong> und auf den Geräten ersetzen. Ein vorher heruntergeladenes Skript sammelt keine Programme.</li>
                <li><strong>Beim Intune-Weg:</strong> nichts weiter zu tun – der nächste Abgleich nimmt die Programme mit.</li>
            </ol>
            <div class="req">Voraussetzung: ein eingerichteter Inventar-Agent bzw. eine eingerichtete Intune-Verbindung. Ohne beides bleibt die Liste leer.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Überblick:</strong> <code>/software</code> zeigt die Programme nach Verbreitung sortiert – das meistinstallierte oben.</li>
                <li><strong>Einzelnes Programm:</strong> anklicken, um die Geräte und die Versionsverteilung zu sehen. Mehrere Versionen im Umlauf sind der übliche Hinweis auf ungepflegte Geräte.</li>
                <li><strong>Einstufung korrigieren:</strong> auf der Programmseite zwischen <em>Systembestandteil</em> und <em>eigenständiges Programm</em> wechseln, wenn die automatische Einstufung nicht passt.</li>
                <li><strong>Je Gerät:</strong> auf der Geräteseite den Reiter <em>Software</em> öffnen.</li>
                <li><strong>Abschalten:</strong> Quelle ausschalten; der bisherige Bestand bleibt stehen und lässt sich unter <em>Erfasste Daten</em> leeren.</li>
            </ol>
        </div>
        <h5>Wofür das gut ist</h5>
        <ul>
            <li><strong>Lizenz-Abgleich:</strong> Bei aktivem Lizenz-Modul lässt sich jede Lizenz mit dem zugehörigen Programm verbinden; unter <code>/licenses/abgleich</code> stehen dann gekaufte Plätze und tatsächliche Installation nebeneinander.</li>
            <li><strong>Betroffene Geräte bei Schwachstellen:</strong> Bei aktivem CVE-Modul lässt sich einem Programm seine <strong>CPE-Kennung</strong> der NVD hinterlegen (<code>hersteller:produkt</code>, z. B. <code>adobe:acrobat_reader</code>). Danach steht auf der Programmseite, welche Meldungen es betreffen – und damit, welche Geräte. Vorschläge kommen aus den bereits abgerufenen Meldungen; die vollständige Kennung (<code>cpe:2.3:a:…</code>) lässt sich ebenso einsetzen. Zugeordnet wird je Programm, nicht je Meldung: Einmal hinterlegt, trifft jede neue Meldung automatisch die richtigen Geräte. Ohne Kennung bleibt das CVE-Modul bei seinen Stichwörtern.</li>
        </ul>
        <div class="req">Die Kennungen an den Meldungen entstehen beim Abruf; vor dem ersten Abruf nach dem Update stehen noch keine Vorschläge bereit.</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> Assets (Geräte); mindestens eine Quelle: Inventar-Agent oder Intune-Abgleich; optional: Lizenzen (Abgleich), CVE (betroffene Geräte)</div>
    </div>

    <!-- ---------- 3.2 Service & Support ---------- -->
    <h3 class="cat-heading" id="cat-service">4.2 Service &amp; Support</h3>

    <!-- todos -->
    <div class="module-card" id="mod-todos">
        <?= $moduleHeader($modules, 'todos', 'fa-check-square', 'Aufgabenliste') ?>
        <p>Persönliche Aufgabenliste für jeden Benutzer. Aufgaben sind ausschließlich für den jeweiligen Benutzer sichtbar.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li><strong>KPI-Kacheln:</strong> Offen, „Heute fällig" und „Überfällig" am Seitenkopf</li>
            <li><strong>Aufgabe anlegen</strong> mit Titel, Fälligkeitsdatum und optionaler Uhrzeit, Priorität (Keine / Niedrig / Mittel / Hoch), frei wählbarer Kategorie (mit Autovervollständigung) sowie Wiederholung (täglich / wöchentlich / monatlich / jährlich)</li>
            <li><strong>Erinnerung:</strong> pro Aufgabe „Erinnern (Tage vorher)" (0–60, Standard 1); eine fällige Aufgabe löst rechtzeitig eine E-Mail-Erinnerung aus</li>
            <li><strong>Wiederkehrende Aufgaben:</strong> beim Abhaken wird automatisch die nächste Instanz mit neuem Fälligkeitsdatum erzeugt</li>
            <li><strong>Unteraufgaben</strong> je Aufgabe mit eigener Erledigt-Markierung und Fortschrittsanzeige („erledigt/gesamt")</li>
            <li><strong>Inline-Editor:</strong> Notiz, Priorität, Kategorie, Datum/Uhrzeit, Wiederholung und Erinnerungs-Vorlauf bearbeiten; Titel per Doppelklick umbenennen</li>
            <li><strong>Erledigen / Wiederöffnen</strong> per Checkbox; erledigte Aufgaben in einem separaten, einklappbaren Bereich; „Aufräumen" entfernt alle erledigten auf einmal</li>
            <li><strong>Suche</strong> (Titel/Notiz/Kategorie), <strong>Filter</strong> nach Kategorie und Priorität, <strong>Sortierung</strong> (Smart, Fälligkeit, Priorität, Erstellt, A–Z oder manuell per Drag &amp; Drop)</li>
            <li>Privat: nur für den erstellenden Benutzer sichtbar</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Aufgabe anlegen:</strong> auf <code>/todos</code> Titel eingeben und im Dialog Fälligkeitsdatum/-uhrzeit, Priorität, Kategorie, Wiederholung und „Erinnern (Tage vorher)" festlegen.</li>
                <li><strong>Strukturieren:</strong> je Aufgabe Unteraufgaben anlegen (mit eigener Erledigt-Markierung und Fortschritt); per Drag &amp; Drop in die gewünschte Reihenfolge ziehen.</li>
                <li><strong>Abarbeiten:</strong> Aufgaben abhaken – wiederkehrende erzeugen automatisch die nächste Instanz; erledigte sammeln sich im einklappbaren Bereich, „Aufräumen" entfernt sie gesammelt.</li>
                <li><strong>Wiederfinden:</strong> über Suche, Kategorie-/Prioritätsfilter und Sortierung (Smart, Fälligkeit, Priorität …) den Überblick behalten; die KPI-Kacheln zeigen Offen/Heute/Überfällig.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Berechtigung:</strong> <code>view_todos</code> &nbsp;·&nbsp; Cron-Task <code>todo_due_reminder</code> (E-Mail-Erinnerung, <code>/admin/cronjobs</code>)</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine (E-Mail-Versand für die Erinnerungen)</div>
    </div>

    <!-- teamboard -->
    <div class="module-card" id="mod-teamboard">
        <?= $moduleHeader($modules, 'teamboard', 'fa-sticky-note', 'Notizbrett') ?>
        <p>Gemeinsames Brett für kurze Zurufe im Team – gedacht für den Satz „ich habe gestern X umgestellt, falls Y auffällt". Anders als die Aufgabenliste sehen alle Beteiligten dieselben Einträge.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li><strong>Verlauf:</strong> neueste Notiz oben, jeweils mit Namen und Zeitpunkt</li>
            <li><strong>Farbe je Person:</strong> jede Person erscheint durchgehend in derselben Farbe, abgeleitet aus ihrem Konto – nichts einzustellen</li>
            <li><strong>Schreiben</strong> direkt am Seitenkopf, bis zu 4.000 Zeichen Klartext mit Zeilenumbrüchen</li>
            <li><strong>Eigene Einträge</strong> bearbeiten und löschen; eine Änderung wird als „bearbeitet" gekennzeichnet</li>
            <li><strong>Fremde Einträge</strong> nur mit <code>manage_teamboard</code></li>
            <li><strong>Suche</strong> im Text und <strong>Filter</strong> nach Person</li>
            <li><strong>Dashboard-Widget</strong> mit den letzten sechs Notizen</li>
        </ul>
        <div class="usage-box">
            <h5>Typischer Ablauf</h5>
            <ol>
                <li><strong>Notiz schreiben:</strong> auf <code>/teamboard</code> in das Feld am Seitenkopf tippen und auf „Eintragen".</li>
                <li><strong>Mitlesen:</strong> das Dashboard-Widget zeigt die letzten Einträge, ohne dass man die Seite aufrufen muss.</li>
                <li><strong>Nachschlagen:</strong> über die Suche oder den Personenfilter finden, wer wann was gemeldet hat.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Berechtigung:</strong> <code>view_teamboard</code> (lesen und eigene Einträge) &nbsp;·&nbsp; <code>manage_teamboard</code> (fremde Einträge)</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine</div>
    </div>

    <!-- loans -->
    <div class="module-card" id="mod-loans">
        <?= $moduleHeader($modules, 'loans', 'fa-hand-holding', 'Geräteausleihe') ?>
        <p>Verwaltung von Leihgeräten. Benutzer können Geräte direkt reservieren oder per Self-Service anfragen; das IT-Team bestätigt oder lehnt Anfragen ab. Ein digitales Übergabeprotokoll wird mit Unterschrift erstellt.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Gerätekatalog mit Typ, Seriennummer und Zustand</li>
            <li>Kalender, Geräteliste und globale Ausleih-Übersicht (mit Filtern)</li>
            <li>Ausleihvorgang: (Anfrage →) Reservierung → Übergabe → Rückgabe</li>
            <li><strong>Self-Service-Anfragen</strong> über Portal/API werden als <em>offene Anfrage</em> angelegt und müssen vom IT-Team <strong>bestätigt oder abgelehnt</strong> werden (mit E-Mail an den Anfragenden inkl. Ablehnungsgrund)</li>
            <li>Einstellbar unter <code>/admin/api</code>: ob Anfragen bestätigt werden müssen oder direkt fest reserviert werden</li>
            <li>Digitale Unterschrift auf Tablet oder Touch-Display</li>
            <li>Leih- und Rückgabeschein als PDF (per Vorlage editierbar); Rückgabeschein dokumentiert defektes/fehlendes Zubehör</li>
            <li>Überfälligkeits-Alerts, „Heute abzuholen/zurück"-Übersicht und Gruppen-Ausleihe</li>
            <li>E-Mail-Benachrichtigungen bei Ausgabe/Reservierung, Rückgabe, Bestätigung und Ablehnung</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Leih-Laptops/Beamer mit unterschriebenem Protokoll ausgeben</li>
            <li>Self-Service-Anfragen von Mitarbeitenden über das Portal abwickeln</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/api</code> (Anfrage-Workflow &amp; API-Scope <code>loans</code>)</div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>loans</code>.</li>
                <li><strong>Vorlagen &amp; Bedingungen</strong> (<code>/admin/loans</code>): Leih-/Rückgabeschein-PDFs und die E-Mail-Vorlagen (Bestätigung, Rückgabe, Überfällig, Anfrage genehmigt/abgelehnt) anpassen; die auf dem Unterschriften-Tablet angezeigten Bedingungen unter „Bedingungen" pflegen.</li>
                <li><strong>Unterschriften-Tablet</strong> (optional): IP-Freigabe für die login-freie Seite <code>/signature</code> unter <code>/admin/settings/signature</code> setzen.</li>
                <li><strong>Self-Service</strong> (optional, <code>/admin/api</code>): festlegen, ob Anfragen vom IT-Team bestätigt werden müssen (bleibt „angefragt") oder direkt reserviert werden, sowie maximale Vorlaufzeit und Ausleihdauer; Self-Service läuft über das WordPress-Plugin / die REST-API.</li>
                <li><strong>Erinnerungen:</strong> Überfälligkeits- und Status-Tasks laufen über den <a href="#cronjobs">Scheduler</a> (<code>loan_status_reconcile</code> u.&nbsp;a.).</li>
            </ol>
            <div class="req">Berechtigung: <code>manage_loan_devices</code> für die Einstellungen. Für E-Mails ist eine funktionierende Mail-Konfiguration nötig.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Leihgeräte erfassen:</strong> unter <code>/loans/devices</code> Geräte anlegen (Titel, Typ, Seriennummer, Foto, Zubehörliste, optional „über Portal anfragbar" und Asset-Verknüpfung).</li>
                <li><strong>Ausgeben:</strong> über „Ausgeben" ein oder mehrere Geräte wählen, Ausleiher und Zeitraum erfassen, Zubehör mitgeben – die Live-Konfliktprüfung verhindert Doppelbuchungen, eine Bestätigungs-E-Mail geht an den Ausleiher.</li>
                <li><strong>Quittieren:</strong> die Übergabe auf dem Unterschriften-Tablet bestätigen lassen; OpenNIT erzeugt den Leihschein als PDF.</li>
                <li><strong>Zurücknehmen:</strong> die Ausleihe öffnen und „Rücknahme" – die Zubehör-Checkliste durchgehen (bei fehlendem/defektem Zubehör ist eine Zustandsnotiz Pflicht); der Rückgabeschein wird erzeugt und versendet.</li>
                <li><strong>Überblick &amp; Self-Service:</strong> der Belegungskalender und die Ausleih-Liste (mit Filtern) zeigen den Stand; Vorgänge lassen sich verlängern oder stornieren, eingehende Portal-Anfragen bestätigen oder ablehnen.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional: Assets (Gerätekatalog), E-Mail-Versand; Self-Service über WordPress-Plugin / REST-API</div>
    </div>

    <!-- services -->
    <div class="module-card" id="mod-services">
        <?= $moduleHeader($modules, 'services', 'fa-concierge-bell', 'IT-Servicekatalog') ?>
        <p>Start-Board mit verlinkten IT-Diensten und Web-Oberflächen (Controller, NAS, Firewall, Webmail …) als Kacheln. Jeder Dienst ist eine anklickbare Kachel, die die hinterlegte Adresse in einem neuen Tab öffnet.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li><strong>Dienst-Kacheln</strong> gruppiert nach Kategorie (jede Kategorie mit Farbe); je Kachel ein Icon, der Name, der Host aus der URL und ein optionaler Beschreibungs-Tooltip</li>
            <li><strong>Dienst anlegen/bearbeiten</strong> (Modal): Name, URL (Pflicht), Beschreibung, Kategorie, Erreichbarkeit (<em>intern</em> 🏢 / <em>extern</em> 🌐 / keine) und ein Icon – per Upload (PNG/JPG/SVG/ICO/WebP/GIF) oder aus vorhandenen Icons; doppelte URLs werden abgelehnt</li>
            <li><strong>Automatischer Favicon-Abruf:</strong> Ohne hochgeladenes Icon wird das Favicon der Zielseite einmalig geladen und lokal gespeichert (manuell gesetzte Icons bleiben unangetastet); hochgeladene SVGs werden sicherheitshalber bereinigt</li>
            <li><strong>Live-Suche</strong> filtert die Kacheln nach Name/URL; leere Kategorien werden ausgeblendet</li>
            <li><strong>Angepinnte Services &amp; Header-Favoriten:</strong> Services lassen sich pro Benutzer anpinnen (Stern-Symbol) und erscheinen oben im Bereich „Meine Pins"; die Pin-Reihenfolge ist per Drag &amp; Drop sortierbar. Optional werden die ersten Pins (1–15) zusätzlich als Schnellzugriff-Icons im Seitenkopf angezeigt (Desktop) – per Schalter „Im Header anzeigen" je Benutzer ein-/ausschaltbar</li>
            <li><strong>Kategorien</strong> (eigene Seite <code>/services/categories</code>): anlegen/bearbeiten/löschen mit Name und Farbe; die globale Reihenfolge der Dienste ist per Drag &amp; Drop einstellbar (mit <code>manage_services</code>)</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Häufig genutzte Web-Oberflächen (Controller, NAS, Firewall) mit einem Klick aus dem Header öffnen</li>
            <li>Ein gemeinsames IT-Start-/Linkboard für das Team bereitstellen</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Kategorien anlegen</strong> (<code>/services/categories</code>): z. B. „Infrastruktur", „Tools" – jeweils mit Farbe.</li>
                <li><strong>Dienst anlegen:</strong> auf <code>/services</code> „Neuer Dienst" – Name, URL und Kategorie eingeben, Erreichbarkeit (intern/extern) wählen und ein Icon hochladen oder das automatisch geladene Favicon nutzen.</li>
                <li><strong>Nutzen:</strong> eine Kachel anklicken öffnet die Adresse im neuen Tab; über die Live-Suche schnell den passenden Dienst finden.</li>
                <li><strong>Personalisieren:</strong> häufige Dienste mit dem Stern anpinnen (Bereich „Meine Pins", per Drag &amp; Drop sortierbar) und optional „Im Header anzeigen" aktivieren, um sie als Schnellzugriff in der Kopfzeile zu haben.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/services/categories</code> &nbsp;·&nbsp; Berechtigungen: <code>view_services</code>, <code>manage_services</code> &nbsp;·&nbsp; Cron-Task <code>service_favicon_fetch</code></div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine</div>
    </div>

    <!-- kanban -->
    <div class="module-card" id="mod-kanban">
        <?= $moduleHeader($modules, 'kanban', 'fa-columns', 'Kanban-Board') ?>
        <p>Gemeinsames, spaltenbasiertes Team-Board im Kanban-Stil. Aufgaben werden als Karten geführt und lassen sich per Drag &amp; Drop zwischen den Spalten verschieben.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li><strong>Frei konfigurierbare Spalten</strong> mit Name und Farbe sowie optionalem <strong>WIP-Limit</strong> (Begrenzung paralleler Aufgaben); Spalten lassen sich umsortieren – beim Löschen wandern die Karten automatisch in die erste verbleibende Spalte</li>
            <li><strong>Aufgabenkarten</strong> mit Titel, Beschreibung, Spalte, Priorität (Niedrig/Normal/Hoch/Dringend), Label, Fälligkeitsdatum (überfällig hervorgehoben) und <strong>mehreren Bearbeitern</strong> (Avatare mit Initialen)</li>
            <li><strong>Drag &amp; Drop</strong> zwischen Spalten und innerhalb einer Spalte; <strong>Schnell-Hinzufügen</strong> per „+" je Spalte; Erledigt-Markierung je Karte</li>
            <li><strong>Filterleiste:</strong> Volltextsuche, Filter nach Priorität und Label sowie Schalter „Nur offen"</li>
            <li><strong>Labels</strong> (eigene Seite <code>/kanban/categories</code>): anlegen/bearbeiten/löschen mit Name und Farbe; je Label wird die Aufgabenanzahl angezeigt</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>IT-Projekte oder wiederkehrende Aufgaben im Team visualisieren</li>
            <li>Onboarding-/Offboarding-Checklisten als Board führen</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Spalten einrichten:</strong> auf <code>/kanban</code> die gewünschten Phasen als Spalten anlegen (Name, Farbe, optional WIP-Limit) und per Drag &amp; Drop sortieren.</li>
                <li><strong>Labels anlegen</strong> (<code>/kanban/categories</code>): Themen/Kategorien mit Farbe für die spätere Filterung.</li>
                <li><strong>Aufgaben erstellen:</strong> über das „+" einer Spalte oder „Neue Aufgabe" Titel, Beschreibung, Priorität, Label, Fälligkeitsdatum und Bearbeiter festlegen.</li>
                <li><strong>Arbeiten:</strong> Karten per Drag &amp; Drop zwischen den Spalten verschieben und abhaken; über die Filterleiste (Suche, Priorität, Label, „Nur offen") das Board fokussieren.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Berechtigungen:</strong> <code>view_kanban</code> (Karten ansehen/abhaken), <code>manage_kanban</code> (Aufgaben, Spalten, Labels verwalten)</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine (aktive Benutzer als Bearbeiter wählbar)</div>
    </div>

    <!-- tickets -->
    <div class="module-card" id="mod-tickets">
        <?= $moduleHeader($modules, 'tickets', 'fa-ticket-alt', 'Ticketsystem') ?>
        <p>Internes Helpdesk-Ticketsystem. Tickets können per E-Mail eingehend erstellt oder manuell angelegt werden. Kategorien, Prioritäten, Zustände und automatische Benachrichtigungen sind konfigurierbar.</p>

        <h5>Grundfunktionen</h5>
        <ul>
            <li>E-Mail-Eingang (IMAP oder OAuth – Microsoft 365 / Google) zur automatischen Ticket-Erstellung</li>
            <li>Kategorien und Unterkategorien (mit optionalem Standard-Bearbeiter)</li>
            <li>Ticket-Zustände: Neu, In Bearbeitung, Warteschlange, Gelöst, Geschlossen</li>
            <li>Prioritäten (Kritisch / Normal / Niedrig) und Fälligkeitsdaten</li>
            <li>Verlinkung zu Assets und Kontakten/Unternehmen, Standortzuweisung</li>
            <li>Interne Kommentare (nur für Bearbeiter sichtbar)</li>
            <li>E-Mail-Queue für zuverlässige Benachrichtigungen, Statistiken und Auswertungen</li>
            <li>Livesuche in der Ticketliste: findet Betreff, Melder-Name/-E-Mail sowie direkt die Ticketnummer (z.&nbsp;B. <code>123</code> oder <code>#123</code>)</li>
        </ul>

        <h5>Ticket-Detailseite</h5>
        <ul>
            <li><strong>Verlauf direkt am Ticket sichtbar.</strong> Zuweisungen und Statuswechsel erscheinen als kompakte System-Zeilen zwischen den Chat-Bubbles, chronologisch eingebettet. Ein Uhr-Icon im Header öffnet ein Modal mit der vollständigen Audit-Historie inklusive Metadaten-Updates und Asset-Verknüpfungen.</li>
            <li><strong>SLA-Pill im Header.</strong> Vor der ersten Antwort Countdown bis zur SLA-Frist (rot bei Überfälligkeit), danach die gemessene Reaktionszeit (grün im SLA, orange darüber). Schwellenwerte pro Priorität in <code>config/runtime.php</code>:
                <pre style="background:var(--gray-100);padding:0.5rem;border-radius:0.25rem;font-size:0.78rem;margin:0.3rem 0;">'tickets' =&gt; ['sla_targets' =&gt; [1 =&gt; 60, 2 =&gt; 240, 3 =&gt; 1440]]</pre>
                Werte in Minuten. Defaults: P1=60min, P2=4h, P3=24h. Ein Wert ≤ 0 deaktiviert die SLA-Anzeige für diese Priorität.</li>
            <li><strong>Antwortvorlagen (Canned Responses).</strong> Global gepflegt unter <em>Administration → Antwortvorlagen</em>. Platzhalter werden serverseitig ersetzt: <code>{{ticket.id}}</code>, <code>{{ticket.subject}}</code>, <code>{{customer.name}}</code>, <code>{{assignee.name}}</code>. Smart-Insert an die Cursor-Position.</li>
            <li><strong>Relative Zeitangaben.</strong> „vor 5 Minuten" / „vor 2 Stunden"; exakter Zeitstempel im Tooltip.</li>
            <li><strong>Status-Pill als Dropdown.</strong> <em>„Erledigt — wartet auf Rückmeldung"</em> markiert gelöst (Melder kann reklamieren); <em>„Endgültig schließen"</em> archiviert nach Bestätigungsabfrage.</li>
            <li><strong>Wiedervorlage (Snooze).</strong> Unter dem Fälligkeitsdatum lässt sich ein „Wiedervorlage bis"-Datum setzen. Bis dahin verschwindet das Ticket aus „Mein Bereich" (auch aus dem Dashboard-Zähler) und sammelt sich im Tab <em>Wiedervorlage</em>; am hinterlegten Tag taucht es automatisch wieder in „Mein Bereich" auf. Zum Aufheben das Feld leeren und speichern.</li>
        </ul>

        <h5>Schnellaktionen &amp; Sidebar</h5>
        <ul>
            <li><strong>Melder &amp; Ersteller</strong> (oberste Sidebar-Karte): zeigt getrennt den <em>Melder</em> (die Person, deren Anliegen es ist – an sie gehen alle Ticket-Mails) und den <em>Ersteller</em> (wer das Ticket erfasst hat; bei E-Mail-Eingang identisch mit dem Melder, bei manueller Erfassung der Mitarbeiter). Der Ersteller wird bei der Erstellung unveränderlich gespeichert. Der Melder lässt sich über das Stift-Symbol ändern (Verzeichnis-Suche oder manuelle Eingabe) – ab dann gehen alle weiteren Antworten an den neuen Melder; der Wechsel wird im Verlauf und hinter dem Uhr-Symbol dokumentiert.</li>
            <li><strong>Schnellaktionen</strong> (Karte am Ende der Sidebar, je nach aktivierten Modulen/Berechtigungen):
                <em>Bestellung aus Ticket erstellen</em> (Bestellung wird mit dem Ticket rückverknüpft, das Ticket auf „Warteschlange" gesetzt und im Verlauf verlinkt),
                <em>Änderung aus Ticket erstellen</em> (ConfigLog-Eintrag; ein einzelnes verknüpftes Asset wird vorbelegt),
                <em>UniFi-Voucher erstellen</em> (Voucher wird direkt in die Antwort übernommen; erfordert aktivierte Voucher im UniFi-Modul) und
                <em>Leihgerät reservieren</em> (reserviert bzw. gibt ein Gerät aus dem Leihgeräte-Modul direkt aus; die Ausleiher-Daten werden aus dem Ticket-Anforderer vorbefüllt und mit dessen Verzeichnis-Person verknüpft, eine interne Notiz verlinkt auf den Verleihvorgang. Auch aktuell verliehene Geräte stehen für eine spätere Reservierung zur Auswahl; erfordert aktives Leihgeräte-Modul).</li>
            <li><strong>Asset-Schnellansicht:</strong> Verknüpfte Assets öffnen per Klick eine Schnellansicht als Modal (Kerndaten ohne Seitenwechsel), mit Link zur vollständigen Asset-Seite.</li>
        </ul>

        <h5>Tastenkürzel (auf der Ticket-Detailseite)</h5>
        <ul>
            <li><kbd>R</kbd> – Antwort schreiben · <kbd>N</kbd> – Interne Notiz · <kbd>V</kbd> – Antwortvorlage einfügen</li>
            <li><kbd>S</kbd> – Als erledigt markieren · <kbd>C</kbd> – Endgültig schließen · <kbd>E</kbd> – Wieder öffnen</li>
            <li><kbd>Strg</kbd>+<kbd>Enter</kbd> – Antwort absenden · <kbd>?</kbd> – Vollständige Liste anzeigen</li>
        </ul>

        <h5>Verknüpfte Tickets &amp; Zusammenführen</h5>
        <ul>
            <li><strong>Verknüpft mit</strong> (symmetrisch), <strong>Duplikat von</strong> (mit inverser Beschriftung „Wird als Duplikat geführt"), <strong>Blockiert</strong> (Ziel zeigt „Wird blockiert von").</li>
            <li>Header-Button <em>„⇿ Zusammenführen"</em>: alle Antworten/Anhänge wandern ins Ziel, die ursprüngliche Anfrage wird dort interne Notiz, die Quelle wird als Duplikat geschlossen (ohne Close-Mail/CSAT).</li>
        </ul>

        <h5>Sammelstörung</h5>
        <p>Für Vorfälle, die mehrfach gemeldet werden (Ausfall eines Druckers, des WLANs, eines Fachverfahrens): Ein Ticket führt den Vorfall, die übrigen Meldungen hängen daran. Steuerung über die Karte <em>Sammelstörung</em> in der Ticket-Seitenleiste.</p>
        <ul>
            <li><strong>Anhängen / lösen:</strong> In der Meldung das übergeordnete Ticket suchen und anhängen; in der Sammelstörung lässt sich jede Meldung einzeln wieder lösen. Die Hierarchie ist <strong>eine Ebene tief</strong> – eine Sammelstörung sammelt Meldungen, eine Meldung sammelt keine weiteren.</li>
            <li><strong>Gemeinsam antworten:</strong> Haken <em>„Allen"</em> in der Antwortleiste der Sammelstörung. Die Antwort wird zusätzlich in jeder angehängten Meldung ausgegeben – jeder Melder bekommt sie also in seinem eigenen Ticket, mit eigenem Verlauf. Gilt ebenso für interne Notizen.</li>
            <li><strong>Gemeinsam schließen:</strong> <em>„Mitschließen"</em> setzt alle noch offenen Meldungen auf <em>Gelöst</em> oder <em>Geschlossen</em>. Bereits abgeschlossene Meldungen bleiben unberührt (keine zweite Benachrichtigung).</li>
            <li>Wird die Sammelstörung gelöscht, bleiben die Meldungen bestehen und stehen wieder für sich.</li>
            <li>Erfordert <code>manage_tickets</code>; Sammelstörung und Meldung müssen im eigenen Standort-Bereich liegen. Anhängen, Lösen und Mitschließen stehen im Audit-Log.</li>
        </ul>

        <h5>Wiederkehrende Tickets</h5>
        <p>Regelmäßige Arbeiten (monatlicher Rückspiel-Test, Quartalsprüfung, Jahresinventur) werden unter <em>Administration → Tickets → Wiederkehrende Tickets</em> als Vorlage hinterlegt und entstehen automatisch als Ticket, sobald ihr Termin erreicht ist.</p>
        <ul>
            <li>Vorlage trägt Betreff, Beschreibung, Kategorie, Standort, Bearbeiter und Priorität; optional eine Fälligkeit in Tagen nach Entstehung des Tickets.</li>
            <li>Takt von täglich bis jährlich, jeweils auch als „alle N". Der Monatstakt bleibt im Monat: eine Vorlage zum 31. trifft im Februar den Monatsletzten.</li>
            <li>Stand eine Vorlage länger still, entsteht <strong>ein</strong> Ticket – der Rückstand wird übersprungen, nicht für jeden versäumten Takt eines erzeugt.</li>
            <li>Vorlagen lassen sich pausieren; entstandene Tickets tragen in der Liste die Herkunft <em>Serie</em>.</li>
            <li>Erfordert <code>admin_tickets</code> und eine eingerichtete Zeitsteuerung (siehe <em>Administration → Cronjobs</em>).</li>
        </ul>

        <h5>Optionale Features</h5>
        <ul>
            <li><strong>Automatische Zuweisung:</strong> Unter <em>Administration → Tickets → Automatisierung</em>. Zwei einzeln oder kombiniert schaltbare Verfahren: <em>Kategorie-Zuweisung</em> (Standard-Bearbeiter je Kategorie, gepflegt auf der Kategorien-Seite) und <em>Round-Robin</em> (gleichmäßige Verteilung auf aktive Bearbeiter mit Berechtigung <code>manage_tickets</code>, die im Benutzerprofil als „für Ticket-Zuweisung verfügbar" markiert sind; vergeben wird an den am längsten nicht zugewiesenen). Greift bei manuell, per E-Mail und per WordPress-API erstellten Tickets ohne Bearbeiter.</li>
            <li><strong>SLA-Pause bei „Warteschlange":</strong> Die SLA-Uhr stoppt bei Kunden-Rückfragen und läuft beim Verlassen weiter; Pausenzeit wird abgezogen.</li>
            <li><strong>Geschäftszeiten-Kalender:</strong> Optional rechnet die SLA-Uhr nur innerhalb definierter Arbeitszeiten (Wochenplan + Feiertage, unter <em>Features &amp; SLA</em>). Außerhalb der Geschäftszeiten zählt die Frist nicht — Tickets vom Wochenende werden nicht als überfällig gewertet.</li>
            <li><strong>SLA-Eskalation:</strong> Ein Cron-Task (<code>sla_escalation</code>, Verwaltung unter <em>/admin/cronjobs</em>) prüft offene Tickets ohne Erstreaktion gegen die SLA-Frist. Überfällige Tickets werden im Header sichtbar als „Eskaliert" markiert, ihre Priorität wird angehoben, und die Bearbeiter (bzw. Ticket-Admins bei unzugewiesenen Tickets) werden benachrichtigt.</li>
            <li><strong>Benachrichtigungen an Bearbeiter:</strong> In-App- und Web-Push-Hinweise (sobald VAPID konfiguriert ist) bei Zuweisung, neuer Kundenantwort und SLA-Eskalation. Pro Ereignis abschaltbar unter <em>Features &amp; SLA</em>. Davon getrennt steuerbar: die E-Mail-Benachrichtigungen an den Melder (Eingangsbestätigung, Zuweisung, Antwort, Gelöst, Geschlossen).</li>
            <li><strong>Eigene Felder (Custom Fields):</strong> Unter <em>Administration → Tickets → Eigene Felder</em>. Felder gelten global oder pro Kategorie, sechs Typen (Text, mehrzeilig, Zahl, Datum, Auswahlliste, Ja/Nein), optional Pflichtfeld. Bearbeitbar in der Sidebar der Ticket-Detailseite.</li>
            <li><strong>Aufwandserfassung (Time Tracking):</strong> Stoppuhr (sekundengenau, globale Pill in der Navigation, auto-Pause/Stop) <em>und</em> manuelle Minuten-Eingabe im Reply. Summe als Header-Pill, Einzelwerte als Badges.</li>
            <li><strong>CSAT-Bewertung:</strong> Beim Schließen automatische E-Mail mit Link zur öffentlichen 1–5-Sterne-Seite; Auswertung (Durchschnitt, Rücklauf, Verteilung) unter <em>Administration → CSAT-Bewertungen</em>. In den CSAT-Einstellungen (<em>Features &amp; SLA</em>) lässt sich die Bewertungsseite per <em>„Bewertungsseite als Vorschau öffnen"</em> mit den gespeicherten Texten und dem Logo ansehen, ohne eine echte Bewertung auszulösen.</li>
        </ul>

        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>tickets</code>.</li>
                <li><strong>Postfach anbinden</strong> (<code>/admin/tickets/settings</code>): Verbindungsverfahren wählen –
                    <em>SMTP/IMAP mit Passwort</em>, <em>Microsoft 365 (Graph API, empfohlen)</em> oder <em>XOAUTH2 (Microsoft 365 / Google)</em>.
                    <ul>
                        <li><em>Passwort:</em> IMAP-Host, Port, Verschlüsselung und Zugangsdaten eintragen → <strong>„Verbindung testen"</strong> (füllt die Ordnerliste). SMTP entweder „Haupt-SMTP des Systems verwenden" oder eigene Daten + <strong>„SMTP-Verbindung testen"</strong>.</li>
                        <li><em>OAuth:</em> Anbieter, Tenant-/Client-ID, Client-Secret und Postfach (UPN) eintragen, Flow <em>„App-only / Client Credentials"</em> (empfohlen) wählen, dann <strong>„Mit Microsoft/Google verbinden"</strong> für den Consent; anschließend <strong>„Abruf testen"</strong> und <strong>„Versand testen"</strong>. Optional Secret-Ablaufdatum hinterlegen (Warnung 30 Tage vorher).</li>
                    </ul>
                    Zum Schluss unter <em>„Aktion nach Abruf"</em> festlegen, ob abgerufene Mails gelöscht oder in einen Ordner verschoben werden.</li>
                <li><strong>Kategorien anlegen</strong> (<code>/admin/tickets/categories</code>): Name, optional übergeordnete Kategorie und Standard-Bearbeiter.</li>
                <li><strong>Automatische Zuweisung</strong> (optional, <code>/admin/tickets/automation</code>): aktivieren, dann <em>Kategorie-Zuweisung</em> und/oder <em>Round-Robin</em>. Round-Robin verteilt auf aktive Bearbeiter mit <code>manage_tickets</code>, die im Profil als „für Ticket-Zuweisung verfügbar" markiert sind.</li>
                <li><strong>SLA &amp; Features</strong> (<code>/admin/tickets/features</code>): Reaktionszeit je Priorität (Minuten, 0 = aus), optional SLA-Pause bei „Warteschlange", Geschäftszeiten (Wochenplan + Feiertage als <code>JJJJ-MM-TT</code>), Aufwandserfassung, CSAT (Titel/Intro/Dank) sowie die Benachrichtigungs-Schalter für Melder und Bearbeiter.</li>
                <li><strong>Eigene Felder</strong> (optional, <code>/admin/tickets/custom-fields</code>): Bezeichnung, Typ (Text/Mehrzeilig/Zahl/Datum/Auswahlliste/Ja-Nein), Geltungsbereich global oder pro Kategorie, optional als Pflichtfeld.</li>
                <li><strong>Vorlagen:</strong> automatische Ereignis-Mails unter <code>/admin/tickets/templates</code>, Antwortbausteine unter <code>/admin/tickets/canned-responses</code>.</li>
                <li><strong>Hintergrund aktivieren:</strong> Abruf, Mail-Queue und SLA-Eskalation laufen über den <a href="#cronjobs">Scheduler</a>. Für Web-Push an Bearbeiter VAPID unter <code>/admin/settings/pwa</code> einrichten.</li>
            </ol>
            <div class="req">Voraussetzung: ein erreichbares Postfach (IMAP/Graph) und – für den Versand – eine funktionierende E-Mail-Konfiguration (siehe Abschnitt 9).</div>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/tickets/settings</code> (Postfach), <code>/admin/tickets/features</code> (SLA &amp; Features), <code>/admin/tickets/categories</code>, <code>/admin/tickets/automation</code>, <code>/admin/tickets/custom-fields</code>, <code>/admin/tickets/templates</code>, <code>/admin/tickets/canned-responses</code>, <code>/admin/tickets/stats</code>, <code>/admin/tickets/mail-queue</code> &nbsp;·&nbsp; Berechtigung: <code>admin_tickets</code></div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Ticket entsteht:</strong> entweder automatisch aus einer eingehenden E-Mail oder manuell über „Neues Ticket" (Betreff, Beschreibung, Kategorie, Standort).</li>
                <li><strong>Übernehmen &amp; bearbeiten:</strong> das Ticket öffnen, sich (oder einen Kollegen) zuweisen, über die Status-Pille den Zustand setzen und im Antwortfeld an den Melder schreiben – mit <kbd>V</kbd> eine Antwortvorlage einfügen, mit <kbd>N</kbd> eine nur intern sichtbare Notiz.</li>
                <li><strong>Kontext ergänzen:</strong> Assets und Kontakte verknüpfen, eigene Felder ausfüllen und – falls aktiviert – den Aufwand per Stoppuhr oder Minuten-Eingabe erfassen.</li>
                <li><strong>Schnellaktionen:</strong> aus dem Ticket heraus eine Bestellung, einen Konfigurationslog-Eintrag, einen UniFi-Voucher oder eine Geräte-Reservierung erzeugen (je nach aktiven Modulen).</li>
                <li><strong>Abschließen:</strong> auf „Erledigt — wartet auf Rückmeldung" stellen und später „Endgültig schließen" (löst – falls aktiviert – die CSAT-Bewertungs-Mail aus). Soll ein Ticket vorübergehend ruhen, ein „Wiedervorlage bis"-Datum setzen.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> E-Mail-Konfiguration (SMTP/IMAP/OAuth), Standorte, optional: Assets, Contacts, Loans (Schnellaktion „Leihgerät reservieren"), AI-Chat (KI-Antwortvorschläge)</div>
    </div>

    <!-- maintenance -->
    <div class="module-card" id="mod-maintenance">
        <?= $moduleHeader($modules, 'maintenance', 'fa-calendar-check', 'Wartungskalender') ?>
        <p>Monatskalender für geplante Wartungsfenster und technische Arbeiten – ergänzt um automatisch eingeblendete Ablauf- und Fälligkeitstermine aus anderen Modulen, sodass alle wartungsrelevanten Termine an einer Stelle zusammenlaufen.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li><strong>Monatskalender</strong> mit Vor-/Zurück-Navigation und „Heute"-Sprung; Termine als farbige Pills (mit Farb-Legende je Quelle/Typ)</li>
            <li><strong>Seitenleiste „Nächste 30 Tage"</strong> mit Restlaufzeit-Badge (Heute/Morgen/in N Tagen, rot ab ≤ 7 Tagen)</li>
            <li><strong>Manuellen Termin anlegen:</strong> Titel, Datum, optionales Enddatum, Kategorie, Beschreibung und ein Schalter <em>„Auf öffentlicher Statusseite anzeigen"</em> (bei aktivem Statusseite-Modul)</li>
            <li><strong>Kategorien:</strong> mitgeliefert sind Wartung, Überprüfung, Erneuerung und Sonstiges. Eigene Kategorien – etwa <em>Urlaub</em> oder <em>Schulung</em> – legen Sie unter <em>Administration → Terminkategorien</em> (<code>/admin/maintenance/types</code>) an, mit eigener Farbe und Reihenfolge. Eine Kategorie lässt sich abschalten, statt sie zu löschen: Sie steht dann bei neuen Terminen nicht mehr zur Wahl, vorhandene behalten sie. Mitgelieferte Kategorien sind umbenennbar und abschaltbar, aber nicht löschbar.</li>
            <li><strong>Serientermine:</strong> Termine als Serie anlegen – mit Intervall (täglich, wöchentlich, monatlich, vierteljährlich, jährlich, jeweils auch „jedes N-te") und optionalem Serienende. Die Folgetermine erscheinen automatisch im Kalender; beim Löschen einer Serie werden alle zugehörigen Termine mit entfernt</li>
            <li><strong>Automatisch aggregierte Termine</strong> (nur lesend, nicht löschbar) erscheinen direkt im Kalender: SSL-Zertifikatsabläufe, Asset-Garantieabläufe, Vertragsenden samt Kündigungsfristen, Lizenzabläufe und geplante Changes aus dem Konfigurationslog (jeweils nur bei aktivem Modul)</li>
            <li>Auf dem Smartphone: Terminliste des Monats statt Kalender-Raster (Einträge antippbar, Detailansicht als Bottom-Sheet)</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Geplante Downtimes ankündigen und dokumentieren</li>
            <li>Wiederkehrende Wartung (z. B. USV-Test) als Serientermin führen</li>
            <li>Alle ablaufenden Zertifikate, Garantien und Verträge in einer Kalenderansicht im Blick behalten</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Termin anlegen:</strong> auf <code>/maintenance</code> einen Tag wählen bzw. „Neuer Termin" – Titel, Datum, optionales Enddatum und Kategorie erfassen; bei Bedarf „Auf öffentlicher Statusseite anzeigen" setzen. Fehlt die passende Kategorie, legen Sie sie unter <em>Administration → Terminkategorien</em> an.</li>
                <li><strong>Serie:</strong> für wiederkehrende Wartung das Intervall (z. B. monatlich, „jedes N-te") und optional ein Serienende wählen – die Folgetermine erscheinen automatisch.</li>
                <li><strong>Überblick:</strong> der Monatskalender und die Seitenleiste „Nächste 30 Tage" bündeln eigene Termine und die automatisch eingeblendeten Abläufe aus SSL, Garantien, Verträgen, Lizenzen und geplanten Changes.</li>
                <li><strong>Detail:</strong> einen Termin antippen, um Details zu sehen, zum Quellobjekt zu springen oder ihn zu löschen (nur manuell angelegte Termine sind löschbar).</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Berechtigungen:</strong> <code>view_maintenance</code>, <code>manage_maintenance</code></div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine zwingend; blendet Termine aus SSL, Assets, Contracts, Licenses und ConfigLog ein (sofern aktiv); Wartungstermine können auf der Statusseite erscheinen</div>
    </div>

    <!-- deadlines -->
    <div class="module-card" id="mod-deadlines">
        <?= $moduleHeader($modules, 'deadlines', 'fa-hourglass-half', 'Fristen-Cockpit') ?>
        <p>Zentrale, modulübergreifende Übersicht aller anstehenden Termine und Fristen an einer Stelle – erreichbar im Frontend unter „Fristen-Cockpit" (Hauptnavigation, Bereich Werkzeuge). Bündelt Fristen aus den verschiedenen Modulen und verlinkt direkt zum jeweiligen Eintrag.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Sammelt Fristen aus: SSL-Zertifikaten, Verträgen &amp; Kündigungsfristen, Lizenzen, Asset-Garantien, Leihgeräte-Rückgaben, Wartungsterminen, ISMS-Reviews und geplanten Changes</li>
            <li>Filter nach Zeitraum (7 / 30 / 90 Tage, 1 Jahr) und Bereich; Schalter „Überfällige ausblenden". Die gewählten Filter werden pro Benutzer gemerkt, „Zurücksetzen" stellt die Standardansicht wieder her</li>
            <li>Kennzahlen-Kacheln (überfällig / ≤ 7 / ≤ 30 Tage / später) über der Filterleiste</li>
            <li><strong>Berechtigungsgenau:</strong> Jeder Nutzer sieht ausschließlich Fristen aus Modulen, auf die er Lese-Zugriff hat (ohne Vertrags-Recht z. B. keine Vertragsfristen)</li>
            <li><strong>Fristen-Digest (optional):</strong> Einmal pro Woche eine gebündelte E-Mail mit allen anstehenden Fristen an frei wählbare Empfänger – statt vieler einzelner Modul-Erinnerungen. Versandtag, Zeitraum und Empfänger im Backend konfigurierbar; standardmäßig deaktiviert</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Cockpit öffnen:</strong> in der Hauptnavigation unter „Werkzeuge → Fristen-Cockpit" (<code>/deadlines</code>) – die KPI-Kacheln zeigen überfällige sowie in ≤ 7 / ≤ 30 Tagen fällige Termine.</li>
                <li><strong>Eingrenzen:</strong> Zeitraum (7 / 30 / 90 Tage, 1 Jahr) und Bereich wählen oder „Überfällige ausblenden"; die Auswahl wird pro Benutzer gemerkt, „Zurücksetzen" stellt die Standardansicht her.</li>
                <li><strong>Handeln:</strong> jeder Tabelleneintrag verlinkt direkt auf das Quellobjekt (Vertrag, Zertifikat, Lizenz …), um die Frist dort zu bearbeiten.</li>
                <li><strong>Digest</strong> (optional): unter „Digest-Mail" bzw. <code>/admin/deadlines/settings</code> eine wöchentliche Sammel-E-Mail mit Empfängern, Zeitraum und Wochentag aktivieren.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Cockpit:</strong> <code>/deadlines</code> &nbsp;·&nbsp; <strong>Digest-Einstellungen:</strong> <code>/admin/deadlines/settings</code> &nbsp;·&nbsp; Berechtigung: <code>view_deadlines</code> (Administratoren automatisch)</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine zwingend; zeigt Fristen aus den jeweils aktiven Modulen (SSL, Contracts, Licenses, Assets, Loans, Maintenance, ISMS, ConfigLog). Digest-Versand: E-Mail-Konfiguration + Scheduler</div>
    </div>

    <!-- ---------- 3.3 Netzwerk & Infrastruktur ---------- -->
    <h3 class="cat-heading" id="cat-netzwerk">4.3 Netzwerk &amp; Infrastruktur</h3>

    <!-- backup -->
    <div class="module-card" id="mod-backup">
        <?= $moduleHeader($modules, 'backup', 'fa-save', 'Backup-Übersicht (Veeam & Proxmox PBS)') ?>
        <p>Schreibgeschützte Übersicht über Backup-Jobs. Als Quellen werden <strong>Veeam Backup &amp; Replication</strong> und <strong>Proxmox Backup Server (PBS)</strong> unterstützt; mehrere Backup-Server können parallel angebunden werden. OpenNIT liest die Status über die jeweilige REST API aus – steuert die Backup-Systeme selbst aber nicht.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Mehrere Backup-Server verwalten (Veeam und/oder Proxmox PBS) unter <code>/admin/backup/servers</code></li>
            <li>Übersicht aller Backup-Jobs und ihres letzten Status</li>
            <li>Anzeige von Laufzeit, Datenmenge und Fehlern</li>
            <li>Automatischer Abgleich per Cron (Task <code>backup_sync</code>)</li>
            <li>Standardmäßig deaktiviert (erfordert Veeam- bzw. PBS-Installation)</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/backup/servers</code>, <code>/admin/backup/settings</code> &nbsp;·&nbsp; Berechtigung: <code>manage_backup</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>backup</code>.</li>
                <li><strong>Backup-Server hinzufügen</strong> (<code>/admin/backup/servers</code> → „Neu"): Typ wählen –
                    <em>Veeam</em> (REST-API, Port 9419: Benutzername <code>DOMAIN\\user</code>, Passwort, VBR-Version) oder
                    <em>Proxmox Backup Server</em> (Port 8007: Authentifizierung per <em>API-Token</em> (empfohlen, <code>user@realm!name</code> + Secret) oder Benutzer/Passwort, dazu Node-Name).</li>
                <li><strong>Verbindung prüfen:</strong> Server-URL eintragen, „SSL-Zertifikat überprüfen" nach Bedarf, dann <strong>„Verbindung testen"</strong> und speichern.</li>
                <li><strong>Automatischer Abgleich:</strong> Der Cron-Task <code>backup_sync</code> holt Jobs/Repositories und warnt bei neuen Fehlern; manuell über „Sync" im Modul (<code>/backup</code>).</li>
            </ol>
            <div class="req">Berechtigung: <code>manage_backup</code>. Es können mehrere Server (auch gemischt Veeam + PBS) parallel angebunden werden.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Übersicht öffnen:</strong> unter <code>/backup</code> erscheinen die Backup-Jobs aller angebundenen Server mit ihrem letzten Status, Laufzeit und Datenmenge sowie eine Repository-/Speicherübersicht.</li>
                <li><strong>Status prüfen:</strong> einen Job aufklappen, um Details und Fehler zu sehen; OpenNIT liest nur aus und steuert die Backup-Systeme nicht.</li>
                <li><strong>Aktualisieren:</strong> der Abgleich läuft automatisch per Scheduler (<code>backup_sync</code>); für einen sofortigen Stand „Sync" klicken.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> Veeam Backup &amp; Replication oder Proxmox Backup Server (extern), API-Zugangsdaten erforderlich</div>
    </div>

    <!-- ipam -->
    <div class="module-card" id="mod-ipam">
        <?= $moduleHeader($modules, 'ipam', 'fa-network-wired', 'IP-Adressmanagement (IPAM)') ?>
        <p>Verwaltung von IP-Netzwerken, Subnetzen und einzelnen IP-Adressen. VLANs und Standorte können zugeordnet werden.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Netzwerkverwaltung mit Subnetz-Berechnung</li>
            <li>IP-Zuweisung an Geräte oder Dienste mit Beschreibung</li>
            <li>VLAN-Verwaltung und Standortzuweisung für Netzwerksegmente</li>
            <li>Optionaler VLAN-Sync zum UniFi-Controller</li>
            <li>Ping-/Verfügbarkeitsstatus</li>
            <li>Netzwerk-Scan direkt aus der Subnetz-Ansicht („Scan starten") oder per Remote-Scan-Agent</li>
            <li>Gateway-Vorschlag aus Scan-Ergebnissen („Scan-Vorschlag" mit „Übernehmen"-Knopf)</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Freie/belegte IPs pro Subnetz im Blick behalten</li>
            <li>VLAN-Struktur dokumentieren und mit UniFi abgleichen</li>
            <li>Netze inventarisieren: erreichbare Hosts samt Details automatisch erfassen</li>
        </ul>
        <h5>Netzwerk-Scan: zwei Wege</h5>
        <ul>
            <li><strong>Scan-Knopf in der Subnetz-Ansicht:</strong> Der Webserver pingt alle Adressen des Subnetzes
                (DHCP-Bereiche werden übersprungen, max. /22) und reichert erreichbare Hosts an: Hostname, Antwortzeit,
                OS-Vermutung (TTL), offene Ports mit Dienstnamen, HTTP-Server &amp; Webseiten-Titel, SSH-Banner,
                MAC-Adresse (bei direkt angeschlossenen Netzen) und Gerätetyp-Vermutung. Hosts, die Ping blockieren,
                werden per TCP-Probe erkannt. Beim <strong>Import</strong> werden neue IPs angelegt und bestehende
                aktualisiert (zuletzt gesehen, leere Felder ergänzt, Offline-Hosts reaktiviert) – manuell gepflegte
                Werte bleiben unangetastet. Voraussetzung: Der Webserver kann das Subnetz erreichen.</li>
            <li><strong>Remote-Scan-Agent</strong> (<code>/admin/ipam-scan-agent</code>): für Netze, die der Webserver
                selbst nicht erreicht (z.&nbsp;B. hinter Firewalls/DMZ). Als <em>Linux-Script (Bash)</em> oder
                <em>Windows-Script (PowerShell)</em> herunterladbar – inklusive Anleitung für Cron bzw.
                Windows-Aufgabenplanung (fertiger <code>schtasks</code>-Befehl auf der Seite). Erfasst zusätzlich
                SNMP-Beschreibung/-Name (Linux) bzw. NetBIOS-Name/-Arbeitsgruppe (Windows).</li>
        </ul>
        <p>Beide Scan-Wege schlagen automatisch ein <strong>Gateway</strong> pro Subnetz vor: Der Vorschlag erscheint in
            der Subnetz-Detailansicht als „Scan-Vorschlag" mit Schaltfläche <em>„Übernehmen"</em>. Ein manuell
            gepflegtes Gateway wird dabei nie überschrieben. Der Agent erkennt das Gateway gestuft (SNMP-Routing-Check,
            eigenes Standard-Gateway bei direkt angeschlossenen Netzen, Traceroute, Heuristik – einstellbar über
            <code>OPENNIT_GW_DETECT</code>).</p>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Netze anlegen:</strong> Subnetze/VLANs im Modul (<code>/ipam</code>) erfassen; IPs Geräten/Diensten zuweisen.</li>
                <li><strong>Scan aktivieren</strong> (optional, pro Subnetz): im Subnetz-Formular „Dieses Subnetz scannen" setzen.</li>
                <li><strong>Scan-Agent einrichten</strong> (<code>/admin/ipam-scan-agent</code>): unter <code>/admin/api</code> einen API-Token mit Scope <em>IPAM-Scan-Agent</em> (<code>ipam_scan</code>) erzeugen, das passende Script herunterladen – <code>ipam-scan-agent.sh</code> (Linux) oder <code>ipam-scan-agent.ps1</code> (Windows) –, den Token eintragen (oder als Env <code>OPENNIT_TOKEN</code>) und regelmäßig ausführen: per Cron (z.&nbsp;B. <code>*/30 * * * *</code>) bzw. Windows-Aufgabenplanung (Anleitung mit <code>schtasks</code>-Befehl auf der Seite).</li>
                <li><strong>VLAN-Sync</strong> (optional): VLANs zum UniFi-Controller abgleichen.</li>
            </ol>
            <div class="req">Das Linux-Script benötigt bash/curl/ping (optional: ip/arp, dig, nmblookup, avahi, snmpget für mehr Details); das Windows-Script nutzt nur Bordmittel. Optionale Env-Variablen steuern das Verhalten, u.&nbsp;a. <code>OPENNIT_PARALLEL</code> (Parallelität), <code>OPENNIT_TCP_PING</code> (TCP-Fallback), <code>OPENNIT_PORTSCAN</code> (Port-Scan), <code>OPENNIT_BANNER</code> (HTTP/SSH-Banner), <code>OPENNIT_SNMP</code> + <code>OPENNIT_SNMP_COMMUNITY</code>, <code>OPENNIT_GW_DETECT</code> (Gateway-Erkennung), <code>OPENNIT_MARK_OFFLINE</code> (nicht gesehene Hosts offline markieren) und <code>OPENNIT_SHUFFLE</code>/<code>OPENNIT_JITTER_MS</code> (unauffälligere Scan-Reihenfolge). Für große Netze lässt sich die ausführliche Erkennung gezielt abschalten.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Netze anlegen:</strong> auf <code>/ipam</code> Subnetze mit CIDR erfassen und optional VLAN und Standort zuordnen.</li>
                <li><strong>IPs vergeben:</strong> in der Subnetz-Ansicht einzelne Adressen Geräten oder Diensten zuweisen (mit Beschreibung); belegte und freie Adressen sind direkt ersichtlich.</li>
                <li><strong>Netz inventarisieren:</strong> „Scan starten" pingt das Subnetz und reichert erreichbare Hosts an (Hostname, Ports, OS-Vermutung …); per „Importieren" werden neue IPs angelegt und bestehende aktualisiert – manuell gepflegte Werte bleiben erhalten. Den vorgeschlagenen Gateway per „Übernehmen" setzen.</li>
                <li><strong>Auswerten:</strong> die visuelle IP-Karte und der Änderungsverlauf zeigen den Stand; die Liste lässt sich als CSV exportieren.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional: UniFi (VLAN-Sync)</div>
    </div>

    <!-- dns -->
    <div class="module-card" id="mod-dns">
        <?= $moduleHeader($modules, 'dns', 'fa-sitemap', 'DNS-Zonen') ?>
        <p>Importiert die Zonen und Records eines DNS-Servers und stellt sie revisionssicher dar – damit nachvollziehbar ist, wie die DNS-Zonen aufgebaut sind. OpenNIT selbst braucht keinen Zugriff auf den DNS-Server; ein Agent läuft intern und schickt die Daten an die API. Angebunden sind <strong>Windows-DNS</strong> (PowerShell), <strong>BIND</strong> und <strong>PowerDNS</strong> (je ein Python-3-Agent); alle liefern an denselben Endpunkt.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Zonen-Übersicht (Typ, AD-integriert, Reverse, DNS-Server, Herkunft, Record-Anzahl, letzter Sync)</li>
            <li>Record-Ansicht je Zone mit Typ-Filter (A/AAAA/CNAME/MX/TXT/SRV/PTR/NS/SOA …)</li>
            <li>Globale Suche über Zonen und Records</li>
            <li>Snapshot-Import: je Zone wird der Record-Satz vollständig ersetzt; verschwundene Zonen werden optional entfernt</li>
            <li><strong>Änderungsverlauf je Zone:</strong> jeder Import vergleicht den gemeldeten Satz mit dem bisherigen und hält fest, was hinzugekommen, geändert oder verschwunden ist – auch eine reine TTL-Änderung. Der erste Import einer Zone legt nur den Ausgangsstand fest. Die Aufbewahrung ist einstellbar</li>
            <li><strong>Abgleich mit der IP-Verwaltung</strong> (<code>/dns/ipam-check</code>, bei aktivem IPAM-Modul): Namen, deren Adresse dort nicht geführt wird, und vergebene Adressen ohne passenden A-/AAAA-Record. Eine Liste zum Durchsehen – geändert wird nichts</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/dns-agent</code> (Agenten-Downloads und Aufbewahrung des Verlaufs) &nbsp;·&nbsp; Frontend: <code>/dns</code> &nbsp;·&nbsp; Berechtigungen: <code>view_dns</code>, <code>manage_dns</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>DNS-Zonen</code>.</li>
                <li><strong>API-Token erzeugen:</strong> unter <code>/admin/api</code> einen Token anlegen und den Scope <em>DNS-Import</em> (<code>dns_import</code>) auswählen.</li>
                <li><strong>Agent herunterladen:</strong> auf <code>/admin/dns-agent</code> den PowerShell-Agent (<code>opennit-dns-agent.ps1</code>) laden und auf dem DNS-Server (oder einem Rechner mit RSAT „DNS Server Tools") ablegen.</li>
                <li><strong>Token eintragen:</strong> im Script bei <code>$ApiToken</code> oder per Env <code>OPENNIT_TOKEN</code>; die Server-URL ist vorbefüllt. Test:
                    <pre class="manual-code">powershell -NoProfile -ExecutionPolicy Bypass -File C:\OpenNIT\dns-agent.ps1</pre></li>
                <li><strong>Automatisieren:</strong> per Windows-Aufgabenplanung regelmäßig ausführen (z.&nbsp;B. stündlich). Die Zonen erscheinen anschließend unter <code>/dns</code>.</li>
            </ol>
            <div class="req">Benötigt das PowerShell-Modul <code>DnsServer</code> (auf dem DNS-Server/DC vorhanden, sonst RSAT „DNS Server Tools" nachinstallieren). Die API verlangt HTTPS. Auth per Bearer-Token (Scope <code>dns_import</code>), ohne HMAC – wie beim IPAM-Scan-Agent. Optionale Env-Variablen: <code>OPENNIT_DNS_SERVER</code> (Ziel-Server), <code>OPENNIT_PRUNE=0</code> (entfernte Zonen behalten), <code>OPENNIT_INSECURE=1</code> (Self-Signed-Zertifikat).</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Zonen ansehen:</strong> nach dem ersten Agent-Lauf zeigt <code>/dns</code> alle importierten Zonen (Typ, AD-integriert, Reverse, DNS-Server, Record-Anzahl, letzter Sync).</li>
                <li><strong>Records prüfen:</strong> eine Zone öffnen und die Einträge nach Typ filtern (A/AAAA/CNAME/MX/TXT/SRV/PTR/NS/SOA …).</li>
                <li><strong>Suchen:</strong> über die globale Suche zonenübergreifend nach Records fahnden. Die Ansicht ist schreibgeschützt – Änderungen erfolgen weiterhin am DNS-Server, der Agent spiegelt sie beim nächsten Lauf.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> Windows-DNS-Server + PowerShell-Modul <code>DnsServer</code> (extern); interner Host zum Ausführen des Agents</div>
    </div>

    <!-- monitoring -->
    <div class="module-card" id="mod-monitoring">
        <?= $moduleHeader($modules, 'monitoring', 'fa-heartbeat', 'Monitoring (Multi-Source)') ?>
        <p>Zentrales Alarm-Board für mehrere Monitoring-Systeme. Probleme/Alarme landen per Webhook (oder Zabbix-API-Polling) in OpenNIT, werden vereinheitlicht dargestellt und können mit Dokumentationsseiten/Assets verknüpft werden. „resolved/up"-Meldungen schließen das zugehörige offene Problem automatisch.</p>
        <h5>Unterstützte Quellen</h5>
        <ul>
            <li><strong>Zabbix</strong> – Webhook <em>oder</em> API-Polling</li>
            <li><strong>Prometheus Alertmanager</strong> – Webhook (mehrere Alarme pro Aufruf)</li>
            <li><strong>Grafana Alerting</strong> – Webhook-Contact-Point</li>
            <li><strong>Uptime Kuma</strong> – Webhook-Notification</li>
            <li><strong>Generisch</strong> (Checkmk, PRTG, eigene Skripte) – flexibles Feld-Mapping</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/monitoring/settings</code> &nbsp;·&nbsp; Berechtigung: <code>manage_monitoring</code></div>

        <h5>So gelangen die Infos nach OpenNIT</h5>
        <p>Jedes System sendet per HTTP&nbsp;POST ein JSON an eine <strong>quellen-spezifische Webhook-URL</strong>. OpenNIT normalisiert den Payload (Host, Schweregrad, Status, Meldung) und legt ein Ereignis an bzw. löst es wieder auf. Den <strong>Token</strong> immer als <code>?token=…</code> anhängen (oder Header <code>X-Monitoring-Token</code>). Die Basis-URL und der Token stehen unter <code>/admin/monitoring/settings</code> (Tab „Webhook"); dort sind auch alle Quellen-URLs aufgelistet.</p>
        <pre class="manual-code">Schema:  https://&lt;OpenNIT&gt;/monitoring/webhook/&lt;quelle&gt;?token=&lt;TOKEN&gt;

  Zabbix .............. /monitoring/webhook/zabbix
  Alertmanager ........ /monitoring/webhook/prometheus
  Grafana ............. /monitoring/webhook/grafana
  Uptime Kuma ......... /monitoring/webhook/uptimekuma
  Checkmk ............. /monitoring/webhook/checkmk
  Icinga 2 ............ /monitoring/webhook/icinga
  Generisch (PRTG/eigene) ....... /monitoring/webhook/generic</pre>

        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Dienste einbinden – Schritt für Schritt</div>
            <ol>
                <li><strong>Zabbix (Webhook):</strong> <em>Administration → Media types → Webhook</em> anlegen; als URL die <code>/zabbix</code>-URL inkl. Token eintragen. Fertige Schritt-für-Schritt-Anleitung samt Skript auf <code>/admin/monitoring/settings</code>. <br><strong>Alternativ API-Polling:</strong> Tab „Zabbix API", URL (ohne <code>/api_jsonrpc.php</code>) + API-Token, Sync-Modus „API-Polling", „Jetzt synchronisieren".</li>
                <li><strong>Prometheus Alertmanager:</strong> in der <code>alertmanager.yml</code> einen Webhook-Receiver auf die <code>/prometheus</code>-URL setzen:
                    <pre class="manual-code">receivers:
  - name: opennit
    webhook_configs:
      - url: 'https://&lt;OpenNIT&gt;/monitoring/webhook/prometheus?token=&lt;TOKEN&gt;'
        send_resolved: true
route:
  receiver: opennit</pre>
                    <code>send_resolved: true</code> sorgt dafür, dass behobene Alarme automatisch geschlossen werden. Schweregrad kommt aus dem Label <code>severity</code>, der Host aus <code>instance</code>.</li>
                <li><strong>Grafana Alerting:</strong> <em>Alerting → Contact points → Add</em>, Typ <em>Webhook</em>, URL = <code>/grafana</code>-URL inkl. Token; diesen Contact-Point in der Notification-Policy verwenden. (Gleiches Alerts-Format wie Alertmanager.)</li>
                <li><strong>Uptime Kuma:</strong> <em>Settings → Notifications → Setup Notification</em>, Typ <em>Webhook</em>, „Post URL" = <code>/uptimekuma</code>-URL inkl. Token, „Content Type: application/json". Dem Monitor zuweisen. DOWN → Problem, UP → automatisch behoben.</li>
                <li><strong>Checkmk / PRTG / eigene Skripte (generisch):</strong> an die <code>/generic</code>-URL ein JSON posten. Erkannte Felder (mehrere Schreibweisen erlaubt):
                    <pre class="manual-code">{
  "host":       "srv01",            // host | hostname | host_name
  "trigger_name":"Dienst down",     // trigger_name | name | subject
  "severity":   "high",             // info|warning|average|high|disaster (+ critical/error/…)
  "status":     "PROBLEM",          // PROBLEM | RESOLVED/OK
  "message":    "Details…",         // message | alert_message | body
  "event_id":   "abc-123"           // optional: zum automatischen Auflösen
}</pre>
                    Bei PRTG/Checkmk die jeweilige „HTTP-Action"/Notification mit dieser URL und diesem Body-Template einrichten. Wird <code>event_id</code> mitgegeben, schließt ein späteres <code>"status":"RESOLVED"</code> mit gleicher <code>event_id</code> das Problem automatisch.</li>
                <li><strong>Anzeige &amp; Verknüpfung:</strong> Alle Ereignisse erscheinen unter <code>/monitoring</code>; Hosts lassen sich mit Dokumentationsseiten/Assets verbinden, um Kontext zu zeigen.</li>
            </ol>
            <div class="req">Berechtigung: <code>manage_monitoring</code>. Token ist Pflicht (sonst HTTP&nbsp;401). Hohe/disaster-Schweregrade lösen zusätzlich eine In-App-Benachrichtigung aus.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Alarme empfangen:</strong> nach dem Einbinden einer Quelle (siehe oben) erscheinen eingehende Probleme automatisch unter <code>/monitoring</code>; bei hohem/disaster-Schweregrad zusätzlich als In-App-Benachrichtigung.</li>
                <li><strong>Bearbeiten:</strong> ein Ereignis quittieren, als erledigt markieren oder löschen; eine „resolved/up"-Meldung der Quelle schließt das zugehörige offene Problem automatisch.</li>
                <li><strong>Kontext herstellen:</strong> einen Host mit einer Dokumentationsseite oder einem Asset verknüpfen, damit beim nächsten Alarm sofort der Zusammenhang sichtbar ist.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> mindestens ein Monitoring-System (Zabbix, Prometheus/Alertmanager, Grafana, Uptime Kuma, Checkmk, PRTG …) – extern</div>
    </div>

    <!-- netdiagram -->
    <div class="module-card" id="mod-netdiagram">
        <?= $moduleHeader($modules, 'netdiagram', 'fa-project-diagram', 'Netzwerkpläne') ?>
        <p>Erstellung und Verwaltung von Netzwerk-, Topologie- und Infrastrukturplänen mit einem selbst gehosteten draw.io-Editor. Die Diagramme werden in der Datenbank gespeichert.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li><strong>Plan-Übersicht</strong> als Kachel-Grid mit automatisch erzeugter Vorschau (SVG-Thumbnail), Name, Beschreibung und Änderungsdatum</li>
            <li><strong>Neuen Plan anlegen</strong> (Name/Beschreibung) und direkt im eingebetteten <strong>draw.io-Editor</strong> bearbeiten; beim Speichern wird automatisch ein Vorschaubild generiert</li>
            <li><strong>Diagrammdatei importieren</strong> (<code>.drawio</code> oder <code>.xml</code>, mit XML-Sicherheitsprüfung)</li>
            <li>Pläne <strong>umbenennen</strong> und <strong>löschen</strong>; alle Änderungen werden im Audit-Log protokolliert</li>
            <li><strong>Eigene Gerätetypen</strong> für die Pläne pflegbar (Schlüssel, Label, Emoji oder Bild-Icon, Farben, Reihenfolge)</li>
            <li>draw.io wird über den Admin-Bereich installiert und aktualisiert</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Plan anlegen:</strong> auf <code>/netdiagram</code> „Neuer Plan" mit Name/Beschreibung – danach öffnet sich direkt der draw.io-Editor.</li>
                <li><strong>Zeichnen &amp; speichern:</strong> die Topologie im Editor erstellen und speichern; OpenNIT erzeugt automatisch ein Vorschaubild für die Kachel-Übersicht.</li>
                <li><strong>Importieren:</strong> ein vorhandenes Diagramm als <code>.drawio</code> oder <code>.xml</code> hochladen.</li>
                <li><strong>Verwalten:</strong> Pläne aus der Übersicht heraus umbenennen oder löschen.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/netdiagram/drawio</code> (draw.io-Installation), <code>/admin/netdiagram/devices</code> (Gerätetypen) &nbsp;·&nbsp; Berechtigungen: <code>view_netdiagram</code>, <code>edit_netdiagram</code>, <code>admin_netdiagram</code></div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> selbst gehostete draw.io-Installation</div>
    </div>

    <!-- ssl -->
    <div class="module-card" id="mod-ssl">
        <?= $moduleHeader($modules, 'ssl', 'fa-lock', 'SSL-Zertifikate') ?>
        <p>Überwachung von TLS-Endpunkten auf Ablauf und Sicherheit. Hinterlegte Hosts werden regelmäßig geprüft, das Zertifikat ausgelesen und bewertet, sodass rechtzeitig vor dem Ablauf gewarnt wird.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li><strong>KPI-Kacheln:</strong> Überwacht (gesamt), OK (&gt; 30 Tage), ≤ 30 Tage, ≤ 14 Tage und „Fehler / Unsicher"</li>
            <li><strong>Direkte TLS- und STARTTLS-Prüfung</strong> – neben HTTPS auch SMTP, IMAP und POP3 (Transport je Eintrag wählbar); Port frei einstellbar (Standard 443)</li>
            <li><strong>Sicherheitsbewertung als Schulnote (A/B/C/F):</strong> bewertet Vertrauenskette, Hostname-Übereinstimmung, TLS-Version, Schlüsselstärke und Restlaufzeit; F z. B. bei abgelaufen/Trust-Fehler/Hostname-Mismatch, C bei TLS 1.0/1.1 oder schwachen Schlüsseln</li>
            <li><strong>Statusanzeige</strong> (OK / Bald / Kritisch / Unsicher / Fehler) sowie Sicherheits-Pills (Trust ✓/✗, Hostname ✓/✗, TLS-Version), Aussteller, Verantwortlicher und letzte Prüfung</li>
            <li><strong>Zertifikat hinzufügen/bearbeiten</strong> mit Host, Port, Transport, Bezeichnung, Verantwortlichem, eigenen Warn-Tagen und Tags; <strong>Live-Suche</strong> und Tag-Filter in der Liste</li>
            <li><strong>Detailseite</strong> je Eintrag mit vollem Zertifikatsstatus und Prüf-Verlauf (Historie)</li>
            <li>Prüfung einzeln, per „Alle prüfen" oder automatisch über den Scheduler; Ablauf-Erinnerungen vor den gesetzten Warn-Tagen</li>
        </ul>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>ssl</code>.</li>
                <li><strong>Zertifikat hinzufügen</strong> (<code>/ssl</code> → „Hinzufügen"): Host (<code>domain.de</code> oder <code>https://domain.de</code>), Port (Standard 443), optional Bezeichnung, Verantwortlicher, eigene Warn-Tage und Tags. Beim Speichern wird sofort geprüft.</li>
                <li><strong>Prüfen:</strong> einzeln über „Prüfen" am Eintrag oder „Alle prüfen".</li>
                <li><strong>Automatik:</strong> Der Cron-Task für SSL prüft regelmäßig und verschickt Ablauf-Erinnerungen (Verwaltung unter <code>/admin/cronjobs</code>).</li>
            </ol>
            <div class="req">Berechtigungen: <code>view_ssl</code>, <code>manage_ssl</code>. Der Server muss die zu prüfenden Hosts erreichen.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Endpunkt aufnehmen:</strong> auf <code>/ssl</code> „Hinzufügen" – Host, Port und Transport (TLS/SMTP/IMAP/POP3) wählen, optional Bezeichnung, Verantwortlichen, eigene Warn-Tage und Tags; beim Speichern wird sofort geprüft.</li>
                <li><strong>Bewerten:</strong> in der Liste Schulnote (A/B/C/F), Status und die Sicherheits-Pills (Trust, Hostname, TLS-Version) lesen; eine Zeile öffnen, um Details und den Prüf-Verlauf zu sehen.</li>
                <li><strong>Aktuell halten:</strong> einzeln „Prüfen" oder „Alle prüfen"; der Scheduler prüft regelmäßig und verschickt vor Ablauf automatisch Erinnerungen an den Verantwortlichen.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> Internetzugang für Domain-Checks; E-Mail-Versand für Erinnerungen</div>
    </div>

    <!-- unifi -->
    <div class="module-card" id="mod-unifi">
        <?= $moduleHeader($modules, 'unifi', 'fa-wifi', 'UniFi-Integration') ?>
        <p>Integration mit einem Ubiquiti UniFi Network Controller. Zeigt Geräte, Netzwerke und angemeldete Clients an und verwaltet Gäste-Voucher.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Verbindung zum UniFi Network Controller (konfigurierbare Timeouts)</li>
            <li>Client-Übersicht mit Status-Labels; Topologie mit Filtern (Typ/Netzwerke/Offline)</li>
            <li>Automatischer Sync per Cron (Task <code>unifi_sync</code>) in einen lokalen Cache</li>
            <li>Gäste-Voucher: anlegen, auffrischen, löschen, Diagnose &amp; Druckansicht (Berechtigung <code>manage_unifi_vouchers</code>)</li>
            <li>Inventar-Abgleich: aus UniFi erkannte Geräte als Assets anlegen</li>
            <li>VLAN-/IPAM-Synchronisation (Berechtigung <code>manage_unifi</code>)</li>
            <li>Standardmäßig deaktiviert (erfordert UniFi-Controller)</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/settings/unifi</code> &nbsp;·&nbsp; Berechtigungen: <code>view_unifi</code>, <code>manage_unifi</code>, <code>manage_unifi_vouchers</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Controller anbinden</strong> (<code>/admin/settings/unifi</code>): Controller-URL inkl. Port eintragen, Controller-Typ wählen (<em>UniFi OS</em>, Port 11443, oder <em>Network Application Legacy</em>, Port 8443), Benutzername und Passwort hinterlegen.</li>
                <li><strong>Testen:</strong> „SSL-Zertifikat überprüfen" nach Bedarf, dann <strong>„Verbindung testen"</strong> (listet die Sites auf). Bei gesetzten Zugangsdaten aktiviert sich das Modul automatisch.</li>
                <li><strong>Erweitert</strong> (optional): Daten-/Login-Timeouts, Login-Wiederholungen und max. gecachte Clients pro Site anpassen.</li>
                <li><strong>Automatischer Sync:</strong> Der Cron-Task <code>unifi_sync</code> lädt Geräte, Netzwerke und Clients in den lokalen Cache; manuell „Aktualisieren" im Modul (<code>/unifi</code>).</li>
                <li><strong>Voucher &amp; Inventar:</strong> Gäste-Voucher werden direkt im Modul verwaltet (Berechtigung <code>manage_unifi_vouchers</code>); erkannte Geräte lassen sich über „Inventar-Abgleich" als Assets anlegen.</li>
            </ol>
            <div class="req">Berechtigungen: <code>view_unifi</code>, <code>manage_unifi</code>, <code>manage_unifi_vouchers</code>.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Geräte &amp; Clients ansehen:</strong> auf <code>/unifi</code> erscheinen Geräte und angemeldete Clients je Site mit Online-/Offline-Status; „Jetzt aktualisieren" holt den Live-Stand, ansonsten läuft der Sync per Scheduler.</li>
                <li><strong>Topologie:</strong> unter „Topologie" den Netzwerk-Graph mit Uplinks und VLAN-Segmenten betrachten (Filter nach Site).</li>
                <li><strong>Gäste-Voucher:</strong> unter „Voucher" Codes erzeugen (Anzahl, Gültigkeit, Datenlimit), bei Bedarf als DYMO-Etikett drucken sowie auffrischen/löschen.</li>
                <li><strong>Abgleichen</strong> (optional): per „IPAM-Sync"/„VLAN-Sync" Geräte und VLANs ins IPAM übernehmen (mit Vorschau) und über „Inventar-Abgleich" erkannte Geräte den Assets zuordnen.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> UniFi Network Controller (extern); optional: IPAM (VLAN-Sync), Inventar (Asset-Anlage)</div>
    </div>

    <!-- vms -->
    <div class="module-card" id="mod-vms">
        <?= $moduleHeader($modules, 'vms', 'fa-server', 'Virtualisierung') ?>
        <p>Inventarisierung und Verwaltung virtueller Maschinen, physischer Hosts und Cluster mit Auslastungs-Kennzahlen. VMs lassen sich manuell pflegen oder über Hypervisor-APIs einlesen.</p>

        <h5>Unterstützte Hypervisoren</h5>
        <p>Pro Host wird der Hypervisor-Typ gewählt; je Typ gibt es einen eigenen Konnektor:</p>
        <ul>
            <li><strong>Proxmox VE</strong> (REST-API, Port 8006; optional Token-Auth)</li>
            <li><strong>VMware vSphere / ESXi</strong> (vCenter-REST 6.5–6.7 bzw. Automation-API ab 7.0; ESXi 6.5+ eingeschränkt – ESXi 6.0 ohne REST-API wird nicht unterstützt)</li>
            <li><strong>Microsoft Hyper-V</strong> (WinRM, HTTP 5985 / HTTPS 5986, PowerShell)</li>
            <li><strong>KVM / QEMU</strong> (libvirt/<code>virsh</code>, lokal oder per SSH-Schlüssel)</li>
            <li><strong>Xen / XCP-ng</strong> (XAPI XML-RPC)</li>
        </ul>

        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Sub-Navigation <strong>VMs / Hosts / Cluster</strong>; KPI-Kacheln: VMs gesamt, laufend, <strong>RAM zugewiesen %</strong> und <strong>vCPU zugewiesen %</strong> (Auslastung gegen die Host-Ressourcen, farblich gestaffelt)</li>
            <li><strong>VMs</strong> anlegen/bearbeiten/löschen mit Name, IP, Host, CPU-Kernen, RAM, Speicher, Zweck, OS, Status (laufend/gestoppt/pausiert/Fehler) und Notizen; Live-Suche und Filter nach Status/Host</li>
            <li><strong>Hosts</strong> mit Hypervisor-Typ, Cluster-Zuordnung, Ressourcen, Standort und Status; Host-Detailseite zeigt zugeordnete VMs und den <strong>Sync-Verlauf</strong></li>
            <li><strong>Cluster</strong> zur Gruppierung von Hosts</li>
            <li><strong>Verbindungstest</strong> und <strong>manueller Sync je Host</strong> (über die VM-Seite oder den Admin-Bereich) – neue VMs werden angelegt, bestehende aktualisiert</li>
            <li><strong>Im Hypervisor entfernte VMs</strong> werden als „nicht mehr gefunden" gekennzeichnet, statt unverändert stehen zu bleiben. Gelöscht wird nichts von selbst; an der Dauer ist erkennbar, ob eine VM nur kurz abgeschaltet oder wirklich weg ist. Unter <code>/vms/missing</code> lassen sie sich gesammelt aufräumen – der Eintrag erscheint nur, wenn es etwas aufzuräumen gibt</li>
            <li><strong>Snapshots je VM</strong> mit Aufnahmezeitpunkt, Alter und Größe; ab einer einstellbaren Zahl von Tagen (Vorgabe 30) gelten sie als alt und werden gekennzeichnet. Geliefert von Proxmox VE und Hyper-V</li>
            <li><strong>Einzelne Speicherorte je Host</strong> statt nur der Gesamtkapazität – mit Belegung in Prozent, der vollste zuerst. Geliefert von Proxmox VE, VMware und Hyper-V</li>
            <li><strong>Gastinformationen</strong> aus den Gast-Tools: der Rechnername, den das Gastsystem selbst trägt, und die Fassung der Tools – hilfreich für die Zuordnung zum Inventar und um veraltete Tools zu erkennen</li>
            <li>VM- und Host-IPs werden automatisch ins IPAM eingetragen (CIDR-Zuordnung), sofern das IPAM-Modul aktiv ist</li>
            <li>Integrierte Einrichtungsanleitung für die Hypervisor-APIs (<code>/admin/vms/setup-guide</code>)</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/vms/hypervisor</code> (dort auch die Schwelle, ab der ein Snapshot als alt gilt) &nbsp;·&nbsp; Berechtigungen: <code>view_vms</code>, <code>manage_vms</code>, <code>manage_vm_hosts</code>, <code>manage_vm_clusters</code>, <code>sync_vms</code>, <code>admin_vms</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>vms</code>.</li>
                <li><strong>Host anlegen</strong> (<code>/vms/hosts/create</code>): Hypervisor-Typ wählen (Proxmox VE, VMware vSphere/ESXi, Hyper-V, KVM/QEMU oder Xen/XCP-ng) und die Stammdaten erfassen.</li>
                <li><strong>Verbindung konfigurieren</strong> (am Host → „Sync-Einstellungen", <code>/admin/vms/hosts/{id}/sync-settings</code>): API-URL (Proxmox <code>https://host:8006</code>, VMware vCenter-REST, Hyper-V WinRM <code>http://host:5985/wsman</code>), Benutzer/Passwort bzw. API-Token, Node-ID, „SSL prüfen". Passwort und Token werden verschlüsselt gespeichert.</li>
                <li><strong>Testen &amp; Synchronisieren:</strong> erst speichern, dann <strong>„Verbindung testen"</strong> und <strong>„Jetzt synchronisieren"</strong>. Der Sync wird <em>manuell</em> ausgelöst (es gibt keinen automatischen VM-Sync-Cron) – jederzeit pro Host wiederholbar; jeder Lauf wird im Sync-Verlauf protokolliert.</li>
            </ol>
            <div class="req">Berechtigung: <code>admin_vms</code> für die API-Einstellungen. Übersicht der Hosts unter <code>/admin/vms/hypervisor</code>.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Navigieren:</strong> auf <code>/vms</code> zwischen VMs, Hosts und Cluster wechseln; die KPI-Kacheln zeigen Anzahl, laufende VMs und die RAM-/vCPU-Auslastung.</li>
                <li><strong>Erfassen:</strong> VMs entweder manuell anlegen (Name, Host, CPU/RAM/Speicher, OS, Status) oder über den Host-Sync automatisch einlesen.</li>
                <li><strong>Synchronisieren:</strong> am Host „Verbindung testen" und „Jetzt synchronisieren" – neue VMs werden angelegt, bestehende aktualisiert; jeder Lauf landet im Sync-Verlauf.</li>
                <li><strong>Auswerten:</strong> Auslastung und Sync-Historie je Host prüfen; VM-/Host-IPs erscheinen bei aktivem IPAM automatisch dort.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> mindestens ein Hypervisor (Proxmox, VMware, Hyper-V, KVM/QEMU oder Xen, extern); optional: IPAM (automatischer IP-Eintrag)</div>
    </div>

    <!-- ---------- 3.4 Organisation & Kommunikation ---------- -->
    <h3 class="cat-heading" id="cat-organisation">4.4 Organisation &amp; Kommunikation</h3>

    <!-- contacts -->
    <div class="module-card" id="mod-contacts">
        <?= $moduleHeader($modules, 'contacts', 'fa-address-book', 'Kontakte &amp; Unternehmen') ?>
        <p>Adressbuch für externe Firmen und ihre Ansprechpartner. Wird von anderen Modulen (Tickets, Verträge, Seiten, Inventar) als Ansprechpartner- bzw. Lieferanten-Referenz verwendet.</p>

        <h5>Firmen &amp; Personen</h5>
        <ul>
            <li><strong>Firmen</strong> mit Name, Kundennummer, Adresse, Telefon, E-Mail, Website, Notizen und Logo/Bild; jeder Firma sind beliebig viele <strong>Ansprechpartner</strong> zugeordnet (Vorname, Nachname, Position, Telefon, Mobil, E-Mail, Notizen, Bild)</li>
            <li><strong>Karten- oder Listenansicht</strong> der Firmen (Auswahl wird pro Benutzer gemerkt), Sortierung nach Name, Anzahl Kontakte oder „neueste zuerst", mit Seitenblättern</li>
            <li><strong>Globale Suche</strong> über Firmen und Personen; zusätzlich eine ratenbegrenzte AJAX-Schnellsuche für Navigation/Widgets</li>
            <li><strong>Dynamische Kontaktkanäle</strong> (Typ + Label + Wert, z. B. Telefon/E-Mail/Website) – auf der Detailseite als klickbare Chips (<code>tel:</code>/<code>mailto:</code>/Link); E-Mail und Website werden geprüft, unsichere <code>javascript:</code>/<code>data:</code>-Links werden blockiert</li>
            <li><strong>Bild-Upload</strong> (JPEG/PNG/GIF/WebP, max. 5 MB): Bilder werden neu kodiert, dabei werden EXIF-/GPS-Daten entfernt</li>
        </ul>

        <h5>Papierkorb, Import &amp; Export</h5>
        <ul>
            <li><strong>Papierkorb (Soft-Delete):</strong> gelöschte Firmen und Kontakte landen im Papierkorb und lassen sich <em>wiederherstellen</em> oder <em>endgültig löschen</em> (inkl. Bilder)</li>
            <li><strong>Import</strong> aus CSV oder vCard (Format wird automatisch erkannt); fehlt die Firma, landet der Kontakt unter „Unzugeordnet", ungültige Einträge werden übersprungen statt den Import abzubrechen</li>
            <li><strong>Export</strong> als vCard (einzelner Kontakt oder ganze Firma) und als CSV (alle Kontakte einer Firma)</li>
        </ul>

        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Lieferanten-/Dienstleisterkontakte zentral pflegen</li>
            <li>Vertragspartner direkt aus dem Vertragsmodul referenzieren</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Firma anlegen:</strong> auf <code>/contacts</code> „Neue Firma" – Name (Pflicht) und Stammdaten erfassen, optional ein Logo hochladen.</li>
                <li><strong>Ansprechpartner ergänzen:</strong> die Firma öffnen und „Kontakt hinzufügen" (Vor-/Nachname, Position, Telefon/Mobil/E-Mail); zusätzliche Kontaktkanäle als Chips hinterlegen.</li>
                <li><strong>Importieren/Exportieren:</strong> über „Import" eine CSV- oder vCard-Datei einlesen; einzelne Kontakte oder ganze Firmen als vCard bzw. CSV exportieren.</li>
                <li><strong>Aufräumen:</strong> gelöschte Einträge landen im Papierkorb und lassen sich dort wiederherstellen oder endgültig entfernen.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Berechtigungen:</strong> <code>view_contacts</code> (Lesen/Export), <code>manage_contacts</code> (Anlegen/Bearbeiten, Import, Papierkorb)</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> wird genutzt von Tickets, Contracts (Firmen-Detail zeigt deren Verträge), Inventar (Lieferant) und dem Seiten-Editor (Ansprechpartner)</div>
    </div>

    <!-- phonebook -->
    <div class="module-card" id="mod-phonebook">
        <?= $moduleHeader($modules, 'phonebook', 'fa-phone-alt', 'Telefonbuch') ?>
        <p>Internes Telefonbuch für Mitarbeiter und Abteilungen. Daten können aus dem Verzeichnis (AD-Sync) übernommen werden.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Durchwahlnummern, Mobilnummern, E-Mail-Adressen</li>
            <li>Abteilungszuweisung und Standort</li>
            <li>Synchronisierbar mit dem internen Verzeichnis (Active Directory)</li>
            <li>Manuell angelegte Einträge lassen sich direkt in der Übersicht bearbeiten und löschen (Berechtigung „Telefonbuch verwalten"); aus dem Verzeichnis synchronisierte Einträge bleiben dabei unverändert</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/directory</code> (AD-Sync &amp; Personenpflege)</div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>phonebook</code>.</li>
                <li><strong>Verzeichnis-Quelle wählen</strong> (<code>/admin/directory/connections</code>): <em>Microsoft Entra (Azure AD)</em> – nutzt die M365-App aus <code>/admin/settings/m365</code> (Berechtigung <code>User.Read.All</code> + Consent) – oder <em>Active Directory (LDAP)</em> mit Host, Port/LDAPS, Bind-DN/Passwort, Base-DN und Filter; optional das Attribut-Mapping anpassen.</li>
                <li><strong>Synchronisieren:</strong> auf der Personen-Übersicht (<code>/admin/directory</code>) „Jetzt synchronisieren"; regelmäßig über den Cron-Task Verzeichnis-Sync.</li>
                <li><strong>Telefonbuch nutzen:</strong> die synchronisierten Personen mit Durchwahlen erscheinen im Telefonbuch; manuelle Einträge sind ebenfalls möglich.</li>
            </ol>
            <div class="req">Hinweis: Die <em>AD-Login</em>-Anmeldung (<code>/admin/settings/ldap-auth</code>) ist unabhängig vom Verzeichnis-Sync und wird separat konfiguriert.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Nachschlagen:</strong> auf <code>/phonebook</code> die synchronisierten Personen mit Durchwahl, Mobil und E-Mail durchsehen.</li>
                <li><strong>Finden:</strong> über die Suche oder den Abteilungsfilter schnell die richtige Person eingrenzen.</li>
                <li><strong>Manuelle Einträge:</strong> Personen ohne Verzeichnis (z. B. Funktions­nummern) direkt in der Übersicht anlegen, bearbeiten oder löschen; aus dem Verzeichnis synchronisierte Einträge bleiben schreibgeschützt.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional: Verzeichnis &amp; Personen (AD-Sync)</div>
    </div>

    <!-- contracts -->
    <div class="module-card" id="mod-contracts">
        <?= $moduleHeader($modules, 'contracts', 'fa-file-contract', 'Vertragsmanagement') ?>
        <p>Verwaltung aller IT-Verträge (Wartungsverträge, SLAs, Lizenzen, Dienstleistungsverträge). Automatische Fälligkeits- und Ablaufbenachrichtigungen.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Vertragspartner aus dem Kontaktmodul verknüpfbar</li>
            <li>Dateianhänge (PDF, Office-Dokumente)</li>
            <li>Laufzeit, Verlängerungsoptionen, Kündigungsfristen</li>
            <li>Statusübersicht: Aktiv / Ausgelaufen / Zukünftig</li>
            <li>Benachrichtigung bei bald ablaufenden Verträgen (Erinnerung vor Beginn der Kündigungsfrist)</li>
            <li><strong>Automatische Verlängerung:</strong> Läuft die Kündigungsfrist ohne Kündigung ab, rollen Enddatum und Kündigungs-/Erinnerungstermine automatisch um die Verlängerungsdauer nach vorn und der Status geht zurück auf „Aktiv" (im Hintergrund per Erinnerungslauf sowie beim Öffnen der Vertragsliste). Das Feld „Verlängerung um (Monate)" legt die Dauer fest (Standard 12 Monate); bereits gekündigte Verträge bleiben unberührt</li>
            <li>Kategorisierung und Kostenerfassung (Betrag je Abrechnungsintervall: monatlich, quartalsweise, jährlich oder einmalig)</li>
            <li><strong>Einheitliche Kostenanzeige:</strong> In der Vertragsliste werden die Kosten unabhängig vom Intervall stets als Betrag <em>pro Monat</em> dargestellt (vergleichbar über alle Verträge); die tatsächliche Zahlweise und der Originalbetrag (z. B. „1.200,00 € · jährlich") stehen als Zusatzzeile darunter. Kennzahlen-Kacheln zeigen die monatlichen und jährlichen Gesamtkosten</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Kündigungsfristen automatisch überwachen</li>
            <li>Verträge mit Lizenzen und Vertragspartnern verknüpfen</li>
            <li>Monatliche IT-Vertragskosten auf einen Blick vergleichen</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Vertrag anlegen:</strong> auf <code>/contracts</code> „Neuer Vertrag" – Titel, Vertragspartner (aus den Kontakten), Kategorie, Laufzeit, Kündigungsfrist, Verlängerungsdauer und Kosten je Abrechnungsintervall erfassen; Dokumente als Anhang hochladen.</li>
                <li><strong>Überwachen:</strong> die KPI-Kacheln zeigen die monatlichen und jährlichen Gesamtkosten; in der Liste werden alle Kosten einheitlich als Monatswert dargestellt, der Status (Aktiv/Ausgelaufen/Zukünftig) ergibt sich automatisch.</li>
                <li><strong>Fristen:</strong> vor Beginn der Kündigungsfrist wird erinnert; ohne Kündigung verlängert sich der Vertrag automatisch um die hinterlegte Dauer.</li>
                <li><strong>Exportieren:</strong> Kostenreport als PDF oder CSV ziehen; per iCal-Export bzw. token-geschütztem Kalender-Abo die Fristen in den eigenen Kalender holen.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/contracts/categories</code> (Kategorien), <code>/admin/contracts/settings</code> (Preisanzeige brutto/netto, Vorlaufzeiten) &nbsp;·&nbsp; Cron-Task <code>contract_reminders</code></div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional: Contacts (Vertragspartner), Licenses</div>
    </div>

    <!-- statuspage -->
    <div class="module-card" id="mod-statuspage">
        <?= $moduleHeader($modules, 'statuspage', 'fa-traffic-light', 'Statusseite') ?>
        <p>Öffentliche, eigenständig gestaltete Statusseite unter <code>/status</code>, auf der der Betriebsstatus der IT-Dienste, aktuelle Störungen und geplante Wartungen kommuniziert werden. Die Seite ist <strong>ohne Anmeldung</strong> erreichbar und lässt sich per iframe (<code>/status/embed</code>) in andere Webseiten einbetten. Gepflegt wird alles im geschützten Backend unter <code>/status/manage</code>.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Dienste mit vier Status-Stufen: Betriebsbereit, Beeinträchtigt, Teilausfall, Ausfall</li>
            <li>Optionale Gruppierung der Dienste (z. B. „Fachverfahren", „Infrastruktur")</li>
            <li>Störungsmeldungen mit Verlaufs-Updates (Untersuchung → identifiziert → Beobachtung → behoben)</li>
            <li>Geplante Wartungen mit Zeitraum und betroffenen Diensten</li>
            <li>Automatik: betroffene Dienste werden beim Beheben wieder auf „Betriebsbereit" gesetzt; beim Melden optional passend zur Auswirkung herabgestuft</li>
            <li>Optionaler 90-Tage-Uptime-Verlauf je Dienst (täglicher Snapshot-Task)</li>
            <li>Optionale Kopplung an Wartungskalender und Konfigurationslog: anstehende Wartungen bzw. freigegebene geplante Änderungen erscheinen automatisch (max. 10 Tage im Voraus) als geplante Wartung – ohne Doppelungen</li>
            <li>Konfigurationslog-Verzahnung: Ein geplanter Eintrag kann einen Dienst auf einen Status während der Maßnahme schalten und nach „Erledigt" automatisch wieder freigeben (nur bei aktivem Konfigurationslog-Modul)</li>
            <li>Optionale Benachrichtigung von Besuchern per E-Mail, wenn eine Störung beginnt oder behoben ist (siehe unten)</li>
            <li>Optionale Vorschläge aus dem Monitoring: offene Alarme lassen sich als Störungsmeldung übernehmen (siehe unten)</li>
        </ul>

        <h5>Besucher benachrichtigen</h5>
        <p>Optional, einzuschalten unter <em>Administration → Statusseite → Besucher benachrichtigen</em>. Besucher tragen auf <code>/status</code> ihre E-Mail-Adresse ein und erhalten je Vorfall genau zwei Nachrichten: beim Melden einer Störung und beim Beheben.</p>
        <ul>
            <li><strong>Bestätigung Pflicht:</strong> Eine Adresse wird erst durch den Klick im Bestätigungsschreiben eingetragen – eine fremd eingetragene Adresse bekommt also nichts als dieses eine Schreiben. Unbestätigte Eintragungen werden nach sieben Tagen automatisch verworfen.</li>
            <li><strong>Kein Rauschen:</strong> Zwischenstände einer laufenden Störung gehen nicht hinaus; eine nachträglich als „behoben" dokumentierte Störung löst gar nichts aus.</li>
            <li><strong>Abmelden:</strong> Jede Nachricht enthält einen Abmeldelink. In der Administration stehen alle Eintragungen mit ihrem Zustand und lassen sich dort entfernen – gedacht für Adressen, die nicht mehr existieren.</li>
            <li><strong>Versand über die Zeitsteuerung</strong> (Aufgabe <code>status_notify</code>), damit das Melden einer Störung nicht auf den Mailserver wartet. Eine unzustellbare Adresse wird dreimal versucht und bleibt dann liegen, ohne die übrigen aufzuhalten.</li>
            <li>Das Eintragungsformular kommt ohne Sitzungs-Token aus, weil die öffentliche Seite zwischengespeichert und allen Besuchern gleich ausgeliefert wird; geschützt ist es durch eine Drosselung je Adresse und dadurch, dass es allein nichts bewirkt.</li>
        </ul>
        <div class="req">Voraussetzung: laufende Zeitsteuerung und eingerichteter E-Mail-Versand.</div>

        <h5>Störungen aus dem Monitoring übernehmen</h5>
        <p>Optional, einzuschalten unter <em>Administration → Statusseite → Störungen aus dem Monitoring vorschlagen</em> (nur bei aktivem Monitoring-Modul sichtbar). Offene Alarme ab einem einstellbaren Schweregrad – standardmäßig <em>Hoch</em> – erscheinen im Tab „Störungen" der Verwaltung als Vorschlag.</p>
        <ul>
            <li><strong>Übernehmen</strong> öffnet den gewohnten Melden-Dialog mit vorbelegtem Titel (Alarmtext und Host), Text und Auswirkung (Katastrophe → kritisch, Hoch → größer, sonst gering). Betroffene Dienste, Wortlaut und Veröffentlichung bleiben eine Entscheidung – der Vorschlag veröffentlicht nichts von selbst.</li>
            <li><strong>Verwerfen</strong> nimmt einen Alarm dauerhaft aus der Liste; der Alarm selbst bleibt im Monitoring unberührt.</li>
            <li>Ein übernommener Alarm verschwindet aus den Vorschlägen und lässt sich nicht ein zweites Mal übernehmen.</li>
            <li>Führt das Monitoring einen übernommenen Alarm später als behoben, steht das als Hinweis an der offenen Störung. Die Entwarnung selbst bleibt ein bewusster Schritt, damit keine Störung ohne Nachricht an die Eingetragenen still schließt.</li>
        </ul>
        <h5>Typische Anwendungsfälle</h5>
        <ul>
            <li>Anwender transparent über Ausfälle informieren (statt vieler Einzel-Rückfragen)</li>
            <li>Wartungsfenster vorab ankündigen und auf der Intranet-Startseite einbinden</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/status/manage</code> &nbsp;·&nbsp; Einstellungen (Untertitel, Akzentfarbe, Support-Link, Uptime): <code>/admin/statuspage/settings</code> &nbsp;·&nbsp; Berechtigung: <code>manage_statuspage</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>statuspage</code>. Standardmäßig deaktiviert, da öffentlich erreichbar.</li>
                <li><strong>Dienste anlegen</strong> unter <code>/status/manage</code> (optional Gruppen vergeben).</li>
                <li><strong>Darstellung</strong> unter <code>/admin/statuspage/settings</code> anpassen (Untertitel, Akzentfarbe, Support-Link, Uptime-Verlauf ein/aus).</li>
                <li><strong>Veröffentlichen/Einbetten:</strong> Button „Einbetten &amp; teilen" im Header liefert öffentliche URL und iframe-Code.</li>
                <li><strong>Optional: Benachrichtigungen</strong> unter <code>/admin/statuspage/settings</code> einschalten, damit Besucher sich eintragen können.</li>
                <li><strong>Optional: Monitoring-Vorschläge</strong> ebenda einschalten, damit offene Alarme als Störungsmeldung übernommen werden können.</li>
            </ol>
            <div class="req">Für den 90-Tage-Uptime-Verlauf muss der Scheduler laufen (Task <code>status_service_snapshot</code>).</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Dienste pflegen:</strong> unter <code>/status/manage</code> die öffentlich sichtbaren Dienste anlegen, optional gruppieren und per Drag &amp; Drop sortieren.</li>
                <li><strong>Störung melden:</strong> eine Störung mit Auswirkung erfassen – betroffene Dienste werden passend herabgestuft; den Vorfall über Verlaufs-Updates (Untersuchung → identifiziert → Beobachtung → behoben) aktuell halten. Beim Beheben werden die Dienste automatisch wieder „Betriebsbereit".</li>
                <li><strong>Wartung ankündigen:</strong> eine geplante Wartung mit Zeitraum und betroffenen Diensten eintragen (anstehende Wartungen/Changes erscheinen auch automatisch).</li>
                <li><strong>Veröffentlichen:</strong> die öffentliche Seite unter <code>/status</code> teilen oder über „Einbetten &amp; teilen" als iframe in die Intranet-Startseite einbinden.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine (Logo/Name aus den App-Einstellungen); optional: Monitoring (Störungsvorschläge), Maintenance und ConfigLog (Wartungen), Scheduler (Uptime-Verlauf, Besucher-Benachrichtigungen)</div>
    </div>

    <!-- ---------- 3.5 Sicherheit & Nachvollziehbarkeit ---------- -->
    <h3 class="cat-heading" id="cat-sicherheit-mod">4.5 Sicherheit &amp; Nachvollziehbarkeit</h3>

    <!-- isms -->
    <div class="module-card" id="mod-isms">
        <?= $moduleHeader($modules, 'isms', 'fa-user-shield', 'Informationssicherheit (ISMS)') ?>
        <p>Informationssicherheits-Managementsystem nach ISO/IEC 27001:2022 – Risikoregister, Annex-A-Controlkatalog und Statement of Applicability, um eine ISO-27001-Zertifizierung vorzubereiten und gegenüber Auditoren zu belegen.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Risikoregister mit 5×5-Bewertung (Wahrscheinlichkeit × Schadenshöhe), getrennt nach inhärentem Risiko und Restrisiko, mit Wiedervorlage-Terminen</li>
            <li>Vier Behandlungsoptionen (Vermindern, Akzeptieren, Übertragen, Vermeiden) mit Maßnahmenplan; Maßnahmen als Todo/Ticket ausleitbar</li>
            <li>Risikomatrix als Heatmap, Kennzahlen und Risiko-Trend (täglicher Snapshot)</li>
            <li>Annex-A-Controlkatalog (alle 93 Controls der ISO 27001:2022) inkl. Statement of Applicability (SoA)</li>
            <li>Verknüpfung von Risiken mit Assets, CVE-Meldungen und Verträgen; Controls mit Dokumentationsseiten</li>
            <li>Risikoakzeptanz-Nachweis (Freigeber, Zeitpunkt, Begründung) und Management-Review</li>
            <li>Export: Risikoregister &amp; SoA als CSV, ISMS-Bericht als PDF; CSV-Import von Risiken</li>
            <li>Automatisierungen: Risiko-Vorschläge aus CVEs, automatische Wiedervorlage, Register-Gesundheitscheck, SoA-Status-Ableitung, Monatsbericht</li>
        </ul>
        <div class="admin-links"><strong>Berechtigungen:</strong> <code>view_isms</code> (Lesen/Export), <code>manage_isms</code> (Bearbeiten) &nbsp;·&nbsp; Cron-Tasks (<code>/admin/cronjobs</code>): u. a. <code>isms_metrics_snapshot</code>, <code>isms_cve_risk_suggestion</code>, <code>isms_register_health</code>, <code>isms_monthly_report</code> &nbsp;·&nbsp; integrierte Anleitung: <code>/isms/guide</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>isms</code> (standardmäßig deaktiviert).</li>
                <li><strong>Rollen vergeben:</strong> fertige Rollen „ISMS-Verantwortlicher" (Verwalten) und „ISMS-Auditor" (Lesen/Export) zuweisen oder eigene Berechtigungen setzen.</li>
                <li><strong>Risiken erfassen</strong> unter <code>/isms</code>, bewerten/behandeln und mit Assets, CVEs und Controls verknüpfen.</li>
                <li><strong>SoA pflegen</strong> unter <code>/isms/controls</code>; bei Bedarf Risiken per CSV importieren.</li>
                <li><strong>Scheduler aktivieren</strong>, damit Wiedervorlagen, Trend-Snapshot, Gesundheitscheck und Berichte laufen.</li>
            </ol>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Risiko erfassen:</strong> auf <code>/isms</code> ein Risiko anlegen und mit der 5×5-Matrix bewerten (inhärent und – nach Maßnahmen – Restrisiko).</li>
                <li><strong>Behandeln:</strong> eine der vier Optionen (Vermindern/Akzeptieren/Übertragen/Vermeiden) wählen, Maßnahmen festlegen und bei Bedarf als To-do oder Ticket ausleiten; Risiken mit Assets, CVEs und Verträgen verknüpfen.</li>
                <li><strong>SoA pflegen:</strong> unter <code>/isms/controls</code> den Annex-A-Controlkatalog durchgehen, Anwendbarkeit und Umsetzungsstand setzen und Controls mit Dokumentationsseiten verknüpfen.</li>
                <li><strong>Nachweisen:</strong> Risikoakzeptanz und Management-Review dokumentieren; Risikoregister und SoA als CSV, den ISMS-Bericht als PDF exportieren. Die Automatisierungen (Wiedervorlagen, Trend, Berichte) laufen über den Scheduler.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional Assets, CVE, Verträge, Dokumentation (Verknüpfungen); Scheduler für die Automatisierungen</div>
    </div>

    <!-- cve -->
    <div class="module-card" id="mod-cve">
        <?= $moduleHeader($modules, 'cve', 'fa-bug', 'CVE-Sicherheitsmeldungen') ?>
        <p>Übersicht relevanter CVE-Sicherheitsmeldungen (Common Vulnerabilities and Exposures). Administratoren definieren zu überwachende Produkte, das System prüft regelmäßig auf neue Einträge.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Konfigurierbare Produktliste zur Überwachung</li>
            <li>Automatischer Abruf neuer CVEs per Cron (NVD)</li>
            <li>Schweregrad-Anzeige (CVSS-Score)</li>
            <li>Benachrichtigungen bei kritischen CVEs</li>
            <li>Status-Tracking: Bekannt / In Bearbeitung / Behoben / Nicht betroffen</li>
            <li><strong>BSI CERT-Bund</strong> (optional): deutschsprachige Sicherheitswarnungen (WID), mit deiner Watchlist verknüpft – watchlist-relevante Warnungen werden hervorgehoben, jede betroffene CVE erhält ein 🇩🇪-Badge zur deutschen Einordnung</li>
        </ul>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> BSI CERT-Bund aktivieren</div>
            <ol>
                <li>Unter <code>/admin/cve/settings</code> den Schalter „BSI CERT-Bund Warnungen aktivieren" setzen.</li>
                <li>Quell-URLs sind optional: Bevorzugt wird die <strong>strukturierte JSON-Quelle</strong> abgerufen (liefert die referenzierten CVE-IDs und den Schweregrad mit); ist sie nicht erreichbar, greift automatisch der <strong>RSS-Feed</strong> als Fallback. Beide Felder leer lassen für die öffentlichen Standard-Endpunkte des CERT-Bund (WID).</li>
                <li>Der Abruf läuft automatisch mit dem CVE-Refresh (Cron-Task <code>cve-refresh</code>) bzw. beim Klick auf „Aktualisieren". Auf <code>/cve</code> erscheinen oben die watchlist-relevanten Warnungen, betroffene CVE-Zeilen tragen ein 🇩🇪-BSI-Badge; die referenzierten CVE-IDs verlinken auf die CVE-Suche.</li>
            </ol>
            <div class="req">Benötigt ausgehenden Zugriff auf <code>wid.cert-bund.de</code>. Berechtigung: <code>manage_cve</code>.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Produkte festlegen:</strong> auf <code>/cve</code> die zu überwachenden Produkte/Versionen in die Watchlist aufnehmen.</li>
                <li><strong>Abrufen:</strong> neue Meldungen kommen automatisch über den Cron (<code>cve-refresh</code>) bzw. per „Aktualisieren"; kritische CVEs lösen eine Benachrichtigung aus.</li>
                <li><strong>Bewerten &amp; abarbeiten:</strong> Schweregrad (CVSS) und – falls aktiviert – die deutsche BSI-Einordnung lesen und je CVE den Status setzen (Bekannt / In Bearbeitung / Behoben / Nicht betroffen).</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/cve/settings</code> (NVD-Key, BSI CERT-Bund) &nbsp;·&nbsp; Cron-Task <code>cve-refresh</code> (<code>/admin/cronjobs</code>)</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> Internetzugang für NVD- und (optional) BSI-CERT-Bund-Abfragen</div>
    </div>

    <!-- xdr -->
    <div class="module-card" id="mod-xdr">
        <?= $moduleHeader($modules, 'xdr', 'fa-shield-virus', 'XDR-Sicherheitswarnungen') ?>
        <p>Anbieter-neutrales, zentrales Board für Sicherheitswarnungen aus XDR-/EDR-Plattformen – unabhängig davon, ob eine Warnung bereits einem Asset zugeordnet ist. Angebunden sind <strong>Trend Vision One</strong> (Workbench Alerts; <em>Observed Attack Techniques</em> optional) und <strong>Microsoft Defender XDR</strong> (Warnungen aus Defender for Endpoint, Identity, Office&nbsp;365 und Cloud Apps). Beide Anbieter lassen sich gleichzeitig betreiben; ihre Warnungen landen im selben Board.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Zentrales Board mit Filtern (Anbieter, Schweregrad, Status, Zuordnung, Suche)</li>
            <li>Eigene Ansicht „Nicht zugeordnet" hebt blinde Flecken hervor</li>
            <li>Automatische Asset-Zuordnung über Endpoint-GUID, Hostname oder MAC-Adresse</li>
            <li>Getrennter Anbieter- und Triage-Status (lokale Bearbeitung bleibt erhalten)</li>
            <li>Regeln für die automatische Behandlung: Warnungen nach Muster verwerfen, einer Person zuweisen oder in ein Ticket überführen</li>
            <li>Benachrichtigung bei kritischen/hohen sowie nicht zuordenbaren Warnungen</li>
            <li>Optional: Ticket aus einer Warnung erstellen (erfordert Tickets-Modul)</li>
            <li>Regelmäßiger Abgleich per Cron, automatisches Aufräumen erledigter Warnungen</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/xdr/settings</code>, <code>/admin/xdr/rules</code> &nbsp;·&nbsp; Berechtigungen: <code>view_xdr</code> (lesen), <code>manage_xdr</code> (verwalten) &nbsp;·&nbsp; Cron-Tasks <code>xdr_sync</code>, <code>xdr_prune</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt (Trend Vision One)</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>xdr</code>.</li>
                <li><strong>Region wählen</strong> (<code>/admin/xdr/settings</code>): die Region deines Trend-Vision-One-Mandanten – sie bestimmt den API-Endpunkt.</li>
                <li><strong>API-Key erstellen:</strong> in der Trend-Vision-One-Konsole unter <em>Administration → API Keys → „Add API Key"</em>, einer Rolle mit <em>Lesezugriff auf Workbench</em> (optional auch OAT) zuweisen. Der passende Konsolen-Link wird auf der Einstellungsseite je nach Region direkt angeboten. Doku: <code>automation.trendmicro.com/xdr</code>.</li>
                <li><strong>Token einfügen &amp; speichern</strong>, dann <em>„Verbindung testen"</em> und <em>„Jetzt synchronisieren"</em>.</li>
                <li><strong>OAT</strong> nur bei Bedarf aktivieren (sehr granular) – mit Mindest-Schweregrad gegen Flutung.</li>
            </ol>
            <div class="req">Berechtigung: <code>manage_xdr</code>. Region des API-Keys und gewählte Region müssen übereinstimmen. Der API-Key wird verschlüsselt gespeichert.</div>
        </div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt (Microsoft Defender XDR)</div>
            <ol>
                <li><strong>App registrieren:</strong> im Entra-Portal unter <em>App-Registrierungen → Neue Registrierung</em> eine Anwendung anlegen (ein Umleitungs-URI wird nicht benötigt).</li>
                <li><strong>Kennungen übernehmen:</strong> <em>Anwendungs-ID</em> und <em>Verzeichnis-ID</em> von der Übersichtsseite nach <code>/admin/xdr/settings</code> kopieren.</li>
                <li><strong>Clientschlüssel:</strong> unter <em>Zertifikate &amp; Geheimnisse</em> einen geheimen Clientschlüssel erzeugen und dessen <em>Wert</em> (nicht die ID) eintragen. Der Schlüssel läuft ab – nach Ablauf meldet der Verbindungstest einen Authentifizierungsfehler.</li>
                <li><strong>Berechtigung:</strong> unter <em>API-Berechtigungen → Microsoft Graph → Anwendungsberechtigungen</em> <code>SecurityAlert.Read.All</code> hinzufügen und die Administrator-Einwilligung erteilen. Mehr Rechte sind nicht nötig.</li>
                <li><strong>Mindest-Schweregrad</strong> setzen – Defender meldet auch rein informative Ereignisse.</li>
                <li><strong>Speichern</strong>, dann <em>„Verbindung testen"</em> und <em>„Jetzt synchronisieren"</em>.</li>
            </ol>
            <div class="req">Berechtigung: <code>manage_xdr</code>. Der Clientschlüssel wird verschlüsselt gespeichert und nie protokolliert.</div>
        </div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-filter"></i> Regeln für die automatische Behandlung</div>
            <ol>
                <li><strong>Regel anlegen</strong> unter <code>/admin/xdr/rules</code>: Name, Reihenfolge und die Bedingungen (Anbieter, Art, Mindest-Schweregrad, Zuordnungslage, Textmuster auf Titel oder Gerätename). Leere Felder bedeuten „alles"; alle ausgefüllten Bedingungen müssen zutreffen.</li>
                <li><strong>Aktion wählen:</strong> <em>Verwerfen</em> (Warnung gilt sofort als erledigt – keine Benachrichtigung, kein automatisches Ticket), <em>Person zuweisen</em> (nur diese Person wird benachrichtigt) oder <em>Ticket erstellen</em> (unabhängig vom Schweregrad).</li>
                <li><strong>Reihenfolge beachten:</strong> geprüft wird von der kleinsten Zahl aufwärts; die erste zutreffende Regel gewinnt.</li>
            </ol>
            <div class="req">Regeln greifen nur bei <strong>neu eingehenden</strong> Warnungen – bereits vorhandene bleiben unverändert. Welche Regel eine Warnung behandelt hat, steht in deren Detailansicht.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Board sichten:</strong> auf <code>/xdr</code> die eingegangenen Warnungen über die Filter (Anbieter, Schweregrad, Status, Zuordnung, Suche) eingrenzen.</li>
                <li><strong>Blinde Flecken prüfen:</strong> über die Ansicht „Nicht zugeordnet" Warnungen finden, die keinem Asset zugeordnet werden konnten.</li>
                <li><strong>Bearbeiten:</strong> den lokalen Triage-Status setzen (unabhängig vom Anbieter-Status) und bei Bedarf direkt ein Ticket aus der Warnung erstellen.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> XDR-/EDR-Plattform (extern: Trend Vision One und/oder Microsoft Defender XDR); optional Tickets-Modul für die Ticket-Erstellung</div>
    </div>

    <!-- configlog -->
    <div class="module-card" id="mod-configlog">
        <?= $moduleHeader($modules, 'configlog', 'fa-history', 'Konfigurationslog') ?>
        <p>Protokoll aller Konfigurationsänderungen an IT-Systemen (z. B. Router, Switches, Server) mit Dashboard und Erinnerungen. Dient der Nachvollziehbarkeit von Änderungen außerhalb des Audit-Logs.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Dashboard mit KPI-Karten; Einträge mit Datum, System, Beschreibung und Benutzer</li>
            <li>Geplante Änderungen mit Fälligkeit; Erinnerung per Cron (Task <code>configlog_due_reminder</code>)</li>
            <li>Status „erledigt"/„fehlgeschlagen", Duplizieren von Einträgen</li>
            <li>Verknüpfung mit Assets, Tickets und VMs; CSV-Export</li>
            <li>Filterung nach System und Zeitraum</li>
            <li>Statusseiten-Anbindung (bei aktivem Statusseite-Modul): geplanter Eintrag optional öffentlich als Wartung anzeigen; betroffenen Dienst hinterlegen, der während der Maßnahme (ab geplanter Uhrzeit für 8&nbsp;Stunden) auf den gewählten Status schaltet und nach „Erledigt" wieder freigegeben wird</li>
        </ul>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Änderung erfassen:</strong> auf <code>/configlog</code> einen Eintrag mit Datum, betroffenem System und Beschreibung anlegen; optional Assets, Tickets oder VMs verknüpfen.</li>
                <li><strong>Geplante Änderung:</strong> einen Eintrag mit Fälligkeit vormerken – der Cron erinnert daran; nach Durchführung den Status auf „erledigt" oder „fehlgeschlagen" setzen. Ähnliche Einträge lassen sich duplizieren.</li>
                <li><strong>Auswerten:</strong> über das Dashboard mit KPI-Karten und die Filter (System, Zeitraum) den Überblick behalten und bei Bedarf als CSV exportieren.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Berechtigungen:</strong> <code>view_config_log</code>, <code>manage_config_log</code></div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional Assets, Tickets, VMs (Verknüpfung), Statusseite (öffentliche Anzeige geplanter Änderungen)</div>
    </div>

    <!-- vault -->
    <div class="module-card" id="mod-vault">
        <?= $moduleHeader($modules, 'vault', 'fa-key', 'Passwort-Tresor') ?>
        <p>Verschlüsselter Passwort-Tresor (AES-256-GCM) für persönliche und Team-Zugangsdaten. Zugriff auch über eine Chrome-Browsererweiterung möglich.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Persönliche Tresore: nur vom Eigentümer einsehbar</li>
            <li>Team-Tresore: geteilter Zugriff für definierte Benutzergruppen mit Rollen (Admin / Mitglied / Betrachter)</li>
            <li>Felder: Name, Benutzername, Passwort, URL, Notizen; <strong>Ordner</strong> zur Strukturierung der Einträge</li>
            <li>Integrierter Passwort-Generator</li>
            <li><strong>Import/Export</strong> per CSV</li>
            <li>Chrome-Extension-API für Browser-Autofill</li>
            <li>Automatische Leerlauf-Sperre: Nach 15 Minuten ohne Tresor-Aktivität sperrt der Tresor – clientseitig per Timer und zusätzlich serverseitig erzwungen (greift auch bei Hintergrund-Tabs oder Standby); jeder Zugriff verlängert das Fenster</li>
        </ul>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/vault/teams</code> &nbsp;·&nbsp; Berechtigung: <code>admin_vault_teams</code></div>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>vault</code>. Persönlicher Tresor benötigt die Berechtigung <code>view_vault</code>.</li>
                <li><strong>Persönlicher Tresor:</strong> Einträge anlegen; optional eine <em>PIN</em> unter <code>/vault/pin</code> setzen (Schnell-Entsperren, Sperre nach Fehlversuchen).</li>
                <li><strong>Team-Tresore</strong> (optional, <code>/admin/vault/teams</code>): Team anlegen (Team-Schlüssel wird erzeugt), dann unter „Mitglieder" Benutzer mit Rolle hinzufügen – <em>Mitglied</em> (Lesen &amp; Schreiben), <em>Betrachter</em> (nur Lesen) oder <em>Admin</em>. Berechtigung <code>admin_vault_teams</code>; mit <code>manage_vault_teams</code> können Benutzer eigene Team-Tresore unter <code>/vault/teams/create</code> erstellen.</li>
                <li><strong>Browser-Autofill</strong> (optional): Chrome-Extension per API-Token (<code>/admin/api</code>) anbinden.</li>
            </ol>
            <div class="req">Voraussetzung: gesetzter <code>APP_KEY</code> (32-stelliger Schlüssel) in der Konfiguration – ohne ihn können verschlüsselte Einträge nicht ge-/entschlüsselt werden.</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Eintrag anlegen:</strong> auf <code>/vault</code> einen Eintrag mit Name, Benutzername, Passwort (oder über den integrierten Generator), URL und Notizen erstellen und in einen Ordner einsortieren.</li>
                <li><strong>Im Team teilen:</strong> in einem Team-Tresor abgelegte Einträge stehen den Mitgliedern entsprechend ihrer Rolle (Admin/Mitglied/Betrachter) zur Verfügung.</li>
                <li><strong>Entsperren &amp; sperren:</strong> den Tresor per Passwort/PIN entsperren; nach 15 Minuten ohne Aktivität sperrt er automatisch wieder.</li>
                <li><strong>Browser-Autofill</strong> (optional): die Chrome-Erweiterung per API-Token anbinden, um Zugangsdaten direkt in Webseiten einzusetzen; per CSV importieren/exportieren.</li>
            </ol>
        </div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> Verschlüsselungsschlüssel (<code>APP_KEY</code>) in <code>config/app.php</code></div>
    </div>

    <!-- ---------- 3.6 Dokumentation & KI ---------- -->
    <h3 class="cat-heading" id="cat-doku">4.6 Dokumentation &amp; KI</h3>

    <!-- files -->
    <div class="module-card" id="mod-files">
        <?= $moduleHeader($modules, 'files', 'fa-folder-open', 'Datei-Explorer') ?>
        <p>Vollwertiger Dateimanager mit Ordnerstruktur, Berechtigungen, Versionierung und Papierkorb &ndash; mit Listen- und Kachelansicht (pro Benutzer gespeichert).</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li><strong>Upload:</strong> normaler sowie chunked/resumable Upload großer Dateien; Drag &amp; Drop-Verschieben von Dateien und Ordnern</li>
            <li><strong>Vorschau:</strong> Inline-Modal für Bilder und PDFs, Thumbnails in der Kachelansicht</li>
            <li><strong>Versionierung:</strong> Überschreiben erzeugt eine neue Version; Verlauf einsehen, alte Versionen herunterladen und wiederherstellen</li>
            <li><strong>Papierkorb (Soft-Delete):</strong> gelöschte Dateien wiederherstellen oder endgültig entfernen; automatische Bereinigung per Cron (Aufbewahrungsfrist konfigurierbar)</li>
            <li><strong>Ordner-Berechtigungen (ACL):</strong> Stufen <code>Ansehen</code> &lt; <code>Hochladen</code> &lt; <code>Verwalten</code> für einzelne Benutzer und Rollen, mit Vererbung vom übergeordneten Ordner</li>
            <li><strong>Geteilte Links:</strong> öffentliche, tokenisierte Links für Dateien (Download/Vorschau) und Ordner (ZIP) &ndash; optional mit Passwort, Ablaufdatum und Download-Zähler</li>
            <li><strong>ZIP-Download:</strong> einzelne Ordner oder eine Mehrfachauswahl als ZIP herunterladen; Bulk-Aktionen (Löschen, ZIP)</li>
            <li><strong>Zugriffs-Report:</strong> protokollierter Zugriff je Datei (wer hat wann heruntergeladen/angesehen)</li>
            <li><strong>Optionaler Virenscan:</strong> ClamAV (lokal, via <code>clamd</code>-Socket) prüft Uploads; Verhalten bei Fehlern (zulassen/blockieren) einstellbar</li>
        </ul>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>files</code>.</li>
                <li><strong>Ordner &amp; Berechtigungen:</strong> Im Explorer (<code>/files</code>) Ordner anlegen und je Ordner über das ACL-Menü Benutzern/Rollen die Stufe <em>Ansehen</em>, <em>Hochladen</em> oder <em>Verwalten</em> zuweisen. Unterordner erben die Rechte des nächsten übergeordneten Ordners (überschreibbar). Erfordert die Berechtigung <code>manage_file_folders</code>.</li>
                <li><strong>Papierkorb-Aufbewahrung</strong> (<code>/admin/files</code>): Aufbewahrung in Tagen setzen (0 = keine automatische Bereinigung). Der Cron-Task <code>file_trash_purge</code> löscht abgelaufene Einträge endgültig.</li>
                <li><strong>Virenscan</strong> (optional, <code>/admin/files</code>): „Virenscan aktivieren", clamd-Socket eintragen (z.&nbsp;B. <code>tcp://127.0.0.1:3310</code>), Timeout und Verhalten bei Nichterreichbarkeit (Durchlassen/Blockieren) wählen, dann <strong>„Verbindung testen"</strong>.</li>
                <li><strong>Teilen</strong> (optional): Im Explorer pro Datei/Ordner einen öffentlichen Link erzeugen – optional mit Passwort und Ablaufdatum; der Zugriff wird gezählt und protokolliert.</li>
            </ol>
            <div class="req">Berechtigungen: <code>view_files</code>, <code>upload_files</code>, <code>delete_files</code>, <code>manage_file_folders</code>. Virenscan benötigt einen erreichbaren ClamAV-Dienst (<code>clamd</code>).</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Ablegen:</strong> auf <code>/files</code> Ordner anlegen und Dateien hochladen (auch große Dateien per fortsetzbarem Upload); Dateien und Ordner per Drag &amp; Drop verschieben.</li>
                <li><strong>Versionieren:</strong> beim erneuten Hochladen entsteht eine neue Version – der Verlauf lässt sich einsehen, ältere Stände herunterladen oder wiederherstellen.</li>
                <li><strong>Rechte vergeben:</strong> je Ordner über das ACL-Menü Benutzern/Rollen die Stufe Ansehen/Hochladen/Verwalten zuweisen (Unterordner erben die Rechte).</li>
                <li><strong>Teilen &amp; herunterladen:</strong> für eine Datei oder einen Ordner einen öffentlichen Link erzeugen (optional mit Passwort/Ablauf); Ordner oder eine Mehrfachauswahl als ZIP laden. Gelöschtes landet im Papierkorb.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/files</code> (Virenscan &amp; Papierkorb-Aufbewahrung)</div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> keine; optional ClamAV (<code>clamd</code>) für den Virenscan</div>
    </div>

    <!-- ai_chat -->
    <div class="module-card" id="mod-ai_chat">
        <?= $moduleHeader($modules, 'ai_chat', 'fa-robot', 'KI-Chat') ?>
        <p>Integrierter KI-Assistent, der auf Basis der vorhandenen Dokumentation, Tickets und Stammdaten Antworten generiert und auf Wunsch Aktionen ausführt. Als Vollbild-Seite unter <code>/assistant</code> oder als seitliches Chat-Panel. Anbieter: Anthropic (Claude), OpenAI, DeepSeek und OpenAI-kompatible Endpunkte. Konfiguration über <code>/admin/settings/ai-chat</code>.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Vollbild-Assistent (<code>/assistant</code>) mit mehreren Chat-Sitzungen, Streaming, Tool-Fortschritt, Stopp-Button und Markdown-Tabellen</li>
            <li>Semantische Doku-Suche (RAG) über Embeddings &ndash; optional, Index per Cron (Task <code>ai_embedding_index</code>)</li>
            <li><strong>Aktions-Registry:</strong> die KI kann nach Bestätigung Datensätze anlegen/ändern (z. B. Ticket, Bestellung, To-do, Leihe) &ndash; jede Aktion erzwingt die passende Berechtigung; Dry-Run-Vorschau vor dem Schreiben</li>
            <li><strong>Datenschutz-Modus:</strong> optionale Anonymisierung (Personen, E-Mails, IPs, Hosts …) mit De-Anonymisierung der Antwort</li>
            <li><strong>Dokument-ACL:</strong> die KI sieht nur Seiten, die der Benutzer lesen darf</li>
            <li>Kosten-Budget je Benutzer/Monat sowie Nutzungs- und Feedback-Auswertung im Admin-Bereich</li>
            <li>KI-gestützte Antwortvorschläge direkt im Ticket; konfigurierbare Schlüsselwörter</li>
        </ul>
        <h5>Website-Bot (WordPress)</h5>
        <p>Optionaler öffentlicher Chat-Bot für die eigene Website: das WordPress-Plugin spricht die abgesicherte Bot-API (<code>POST /api/ex/assistant</code>) an &ndash; nur mit API-Token (Scope <code>assistant</code>), HMAC-Signatur, Nonce-Replay-Schutz, Rate-Limit und Admin-Schalter <code>bot_enabled</code>. Antworten sind in der Doku verankert (grounded) und in der Länge begrenzt; Quellenangaben werden im Website-Bot nicht mehr angezeigt.</p>
        <p>Der Bot führt Mehr-Runden-Dialoge. Die E-Mail des angemeldeten WordPress-Nutzers wird ihm automatisch als Kontext übergeben, und im Plugin lässt sich ein zusätzlicher Hintergrundkontext hinterlegen. Besitzt der Token zusätzlich den Scope <code>tickets</code>, kann der Bot im Gespräch ein Support-Ticket erstellen: Er fragt nach Kategorie und Standort und legt das Ticket nach Bestätigung über <code>POST /api/ex/tickets</code> an. Die schwebende Chat-Bubble lässt sich im Plugin optional auf angemeldete Nutzer beschränken.</p>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Admin → Einstellungen → Module</em>, Haken bei <code>ai_chat</code>.</li>
                <li><strong>Anbieter &amp; API-Key</strong> (<code>/admin/settings/ai-chat</code>, Tab „Integration"): KI-Anbieter wählen (OpenAI, Anthropic/Claude, DeepSeek oder <em>OpenAI-kompatibel</em> für selbst-gehostete Modelle wie Ollama/LM Studio/vLLM) und den API-Key eintragen; bei „OpenAI-kompatibel" zusätzlich die Server-URL.</li>
                <li><strong>Funktionen freischalten:</strong> KI-Chat, Textverbesserung im Editor, KI-Assistent im Seiten-Editor und/oder KI-Antwortvorschläge in Tickets per Schalter aktivieren.</li>
                <li><strong>Modell &amp; Limits:</strong> Modell aus der Liste wählen, Token-Obergrenzen setzen, dann <strong>„Verbindung testen"</strong>.</li>
                <li><strong>Datenschutz &amp; Budget:</strong> Anonymisierung (auto/an/aus), Budget pro Nutzer und Gesamt pro Monat (€; 0 = unbegrenzt), optional experimentelle Aktionen erlauben.</li>
                <li><strong>Semantische Suche</strong> (optional): aktivieren, Embedding-Modell angeben, dann <strong>„Seiten jetzt indizieren"</strong>; danach hält der Cron-Task <code>ai_embedding_index</code> den Index aktuell.</li>
                <li><strong>Website-Bot</strong> (optional): im Card „Website-Bot (WordPress)" aktivieren und Token-Limit setzen; im WordPress-Plugin einen API-Token mit Scope <code>assistant</code> hinterlegen (für Ticket-Erstellung zusätzlich <code>tickets</code>). Die Anzeige-Einstellungen des Bots (Bubble, Titel, Begrüßung, zusätzlicher Kontext) werden unter <code>/admin/api</code> gepflegt und vom Plugin automatisch übernommen.</li>
            </ol>
            <div class="req">Voraussetzung: gültiger API-Schlüssel eines KI-Anbieters (oder ein erreichbarer selbst-gehosteter Endpunkt). Web-Push-Vorschläge für Tickets benötigen VAPID (<code>/admin/settings/pwa</code>).</div>
        </div>
        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Fragen stellen:</strong> den Assistenten als Vollbild-Seite (<code>/assistant</code>) oder seitliches Panel öffnen und in natürlicher Sprache fragen – die Antworten stützen sich auf Dokumentation, Tickets und Stammdaten (mehrere Chat-Sitzungen möglich).</li>
                <li><strong>Aktionen ausführen:</strong> auf Wunsch legt die KI Datensätze an oder ändert sie (z. B. Ticket, Bestellung, To-do) – stets erst nach einer Vorschau und Bestätigung, und nur im Rahmen der eigenen Berechtigungen.</li>
                <li><strong>Im Ticket nutzen:</strong> auf der Ticket-Detailseite einen KI-Antwortvorschlag erzeugen und vor dem Senden anpassen.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/settings/ai-chat</code> (inkl. Nutzung, Feedback, Re-Index, Website-Bot) &nbsp;·&nbsp; Berechtigung: <code>use_ai_chat</code></div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> API-Schlüssel eines KI-Anbieters; nutzt Tickets, Seiten &amp; Stammdaten als Kontext; Website-Bot zusätzlich: WordPress-Plugin <code>opennit-connector</code></div>
    </div>

    <!-- api_auto -->
    <div class="module-card" id="mod-api_auto">
        <?= $moduleHeader($modules, 'api_auto', 'fa-plug', 'API-Auto') ?>
        <div class="warn-box">
            <i class="fas fa-plug"></i>
            <strong>Standardmäßig deaktiviert.</strong> Das Modul bindet fremde Systeme an und wird deshalb bewusst eingeschaltet.
            Eine Verbindung läuft erst dann nach Zeitplan, wenn sie freigegeben ist <em>und</em> eine Vorschau erfolgreich war –
            ein Entwurf wird nie automatisch ausgeführt.
        </div>
        <p>Ermöglicht das Anbinden externer REST-APIs ohne eigenen Code: Sie beschreiben in natürlicher Sprache, welche Daten Sie übernehmen möchten, laden (oder ermitteln automatisch) die API-Spezifikation, und die KI (Anthropic/Claude) leitet daraus eine deklarative Zuordnung (Mapping) ab. Eine feste Engine ruft die Daten anschließend ab und befüllt vorhandene OpenNIT-Module über eine Whitelist erlaubter Felder &ndash; die KI erzeugt <em>keinen</em> ausführbaren Code.</p>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Verbindungsassistent: Spezifikation hochladen oder per Basis-URL automatisch ermitteln (Spec-Discovery)</li>
            <li>Import-Ziele: u.&nbsp;a. Assets, Unternehmen, Kontakte, Verzeichnis, IPAM, Lizenzen, Monitoring, SSL, Lager, Tickets</li>
            <li>Optionale Web-Suche (Anthropic) zur besseren Herleitung der Verbindung &ndash; per Admin-Schalter freizugeben</li>
            <li>Inkrementeller Sync (Cursor/Zeitstempel), Pagination, Datensatz-Limit und Wert-Transformationen</li>
            <li>Umgang mit Löschungen upstream (nur selbst importierte Datensätze werden deaktiviert)</li>
            <li>Test- und Duplizieren-Funktion; Vorschau vor dem Schreiben</li>
            <li>Kostenerfassung (KI-Nutzung im Budget) und E-Mail-Benachrichtigung bei Fehlern</li>
        </ul>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
            <ol>
                <li><strong>Voraussetzung:</strong> KI-Chat einrichten (Anthropic-Anbieter) – API-Auto nutzt dieselbe KI-Integration. Modul <code>api_auto</code> unter <em>Admin → Einstellungen → Module</em> aktivieren.</li>
                <li><strong>Web-Suche</strong> (optional): unter <code>/admin/settings/ai-chat</code> den Schalter „API-Auto Web-Suche" setzen (nur mit Anbieter Anthropic) – die KI darf dann API-Dokumentationen selbst recherchieren.</li>
                <li><strong>Verbindung anlegen</strong> (<code>/admin/api-auto/new</code>): Richtung (Import/Export) wählen, eine API-Beschreibung/URL eingeben oder eine OpenAPI-/Swagger-Spezifikation hochladen und kurz beschreiben, welche Daten übernommen werden sollen.</li>
                <li><strong>KI analysieren lassen:</strong> Button <strong>„KI analysieren lassen"</strong> – die KI leitet Basis-URL, Authentifizierung (inkl. Login-/Token-Exchange mit Passwort-Hashing), Endpunkt, Feld-Mapping und Zielmodul ab. Zugangsdaten (Token/Benutzer/Login) werden in einer eigenen Karte abgefragt und lassen sich später jederzeit ändern, ohne die KI-Analyse zu wiederholen. Dann <strong>„Übernehmen &amp; bereitstellen"</strong>.</li>
                <li><strong>Prüfen (Pflicht):</strong> <strong>„Verbindung testen"</strong>, dann <strong>„Vorschau (Dry-Run)"</strong> (schreibt nichts). Erst ein erfolgreicher Dry-Run auf der gespeicherten Konfiguration gibt <strong>„Jetzt ausführen"</strong> und die regelmäßige Ausführung frei.</li>
                <li><strong>Feineinstellungen</strong> (Bearbeiten): Sync-Modus (voll/inkrementell), max. Datensätze pro Lauf, im Quellsystem gelöschte deaktivieren, Fehler-Benachrichtigung per E-Mail.</li>
                <li><strong>Zeitplan</strong> (optional): „Wiederkehrend" mit Intervall aktivieren – dann führt der Cron-Task <code>api_auto_dispatch</code> die Verbindung automatisch aus.</li>
            </ol>
            <div class="req">Verbindungen vor dem produktiven Einsatz immer mit „Test" und „Vorschau" prüfen – die Vorschau ist zugleich die Voraussetzung für den Zeitplan. Die KI erzeugt nur ein deklaratives Mapping – keinen ausführbaren Code; beim Lauf selbst ist sie nicht beteiligt.</div>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> Backend-Oberfläche unter <code>/admin/api-auto</code>; Status &amp; Diagnose unter <code>/admin/settings/api-auto</code>; Web-Suche-Schalter unter <code>/admin/settings/ai-chat</code></div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> konfigurierte Anthropic-API (KI-Integration); Erreichbarkeit der jeweiligen externen API. Berechtigungen: <code>view_api_auto</code>, <code>run_api_auto</code>, <code>create_api_auto</code></div>
    </div>

    <!-- workflows -->
    <div class="module-card" id="mod-workflows">
        <?= $moduleHeader($modules, 'workflows', 'fa-clipboard-check', 'Anträge &amp; Genehmigungen') ?>
        <p>Modularer Baukasten für Genehmigungsprozesse (z.&nbsp;B. Mobiles Arbeiten, Software-Einführung, Onboarding, Offboarding, Employee Change). Eine Engine bildet beliebig viele Prozesse ab – der Unterschied steckt nur in der Konfiguration einer <strong>Vorlage</strong>. Standardmäßig deaktiviert.</p>
        <h5>Begriffe</h5>
        <ul>
            <li><strong>Vorlage</strong> (Definition): Formularfelder + Stationen + Genehmiger-Regeln, unter <code>/admin/workflows</code> per Baukasten gepflegt.</li>
            <li><strong>Antrag</strong> (Instanz): ein ausgefüllter Vorgang, der beim Start eine eingefrorene Kopie der Vorlage erhält (revisionssicher) und durch die Stationen läuft.</li>
            <li><strong>Drei Akteure</strong>: Antragsteller, Antragsgegenstand (betroffene Person – auch ohne Login) und Genehmiger/Bearbeiter (intern oder extern).</li>
        </ul>
        <h5>Hauptfunktionen</h5>
        <ul>
            <li>Formular-Builder: frei definierbare Felder (Text, Auswahl, Datum, Datei, Person, <strong>Betrag&nbsp;(€)</strong>, <strong>Lookup</strong> aus Stammdaten, <strong>Liste/Repeater</strong> …), Gruppierung in Abschnitte, halbe/volle Breite, bedingte Sichtbarkeit, Validierung (Regex/Min/Max/Länge/Datei-Typen) und <strong>Auto-Vorbelegung</strong> aus dem Verzeichnis (Abteilung/Position/E-Mail/Vorgesetzte/r).</li>
            <li>Stationen in sequenziellen Stufen, innerhalb einer Stufe <strong>parallel</strong>; Regeln „einer reicht / N&nbsp;von&nbsp;M / alle". <strong>Wertbasierte Verzweigung</strong> (Bedingungs-Stationen mit mehreren Regeln, UND/ODER und numerischen Vergleichen).</li>
            <li>Genehmiger-Findung über Fallback-Kette: fester Nutzer, Capability, Abteilung, <strong>Vorgesetzter</strong> (aus dem Verzeichnis – hat die/der Vorgesetzte kein Konto, wird sie/er automatisch per Magic-Link eingeladen), oder <strong>externe E-Mail</strong> (Magic-Link ohne Login). Optional <strong>Funktionstrennung (4-Augen)</strong> pro Station.</li>
            <li>Externe Genehmigung per Einmal-Link unter <code>/approval/{token}</code> (Ablehnung mit Pflichtbegründung, Zustimmung mit Notiz für Folgestationen).</li>
            <li>Aufgaben-Stationen erzeugen Tickets/Kanban-Aufgaben und werden automatisch geschlossen, sobald diese erledigt sind.</li>
            <li>Rückfragen, <strong>Kommentar-Thread mit Anhang</strong>, <strong>Ad-hoc-Weiterleitung</strong> durch Genehmiger, Korrigieren &amp; erneutes Einreichen, Entwürfe, Stellvertretung; Fristen mit wiederholten Erinnerungen und <strong>Auto-Eskalation an Vorgesetzte</strong> (nie Auto-Genehmigung).</li>
            <li>Inbox „Meine Genehmigungen" (mit Nav-Badge, Dashboard-Widget, <strong>Sammelaktionen</strong> und Zuweisungsgrund), Verwaltungs-Übersicht mit Kennzahlen und CSV-Export unter <code>/admin/workflows/uebersicht</code>, PDF-Nachweis je Antrag, Vorlagen duplizierbar.</li>
            <li><strong>Persönliche Webhooks</strong> je Nutzer (unter „Anträge → Webhooks", optional HMAC-signiert) für eigene Automatisierung (n8n/Make/Slack).</li>
            <li>Datenschutz: Sichtbarkeit nur für Beteiligte+Admin (pro Vorlage), automatische Anonymisierung nach Aufbewahrungsfrist, manuelle Sofort-Anonymisierung sowie <strong>DSGVO-Betroffenenauskunft</strong> (Daten-Export je Person).</li>
        </ul>
        <h5>Einrichtung Schritt für Schritt</h5>
        <div class="setup-box">
            <div class="setup-title"><i class="fas fa-list-ol"></i> So richten Sie einen Prozess ein</div>
            <ol>
                <li><strong>Modul aktivieren:</strong> <em>Einstellungen &rarr; Module</em> &rarr; „Anträge &amp; Genehmigungen" einschalten (standardmäßig aus).</li>
                <li><strong>Berechtigungen vergeben</strong> (siehe Tabelle): <code>view_workflows</code> hat jede Rolle automatisch; <code>manage_workflows</code> einer Verwalter-Rolle unter <em>Administration &rarr; Rollen &amp; Berechtigungen</em> zuweisen (globale Admins haben es bereits).</li>
                <li><strong>Vorlage anlegen:</strong> <code>/admin/workflows</code> &rarr; „Neue Vorlage". Danach <em>Stammdaten</em> (Wer darf starten, Antragsgegenstand, Sichtbarkeit, Aufbewahrungsfrist) speichern.</li>
                <li><strong>Formularfelder</strong> ergänzen: Typ wählen, optional Pflichtfeld, Abschnitt/Breite, bedingte Sichtbarkeit und Validierung.</li>
                <li><strong>Stationen anlegen</strong> (Stufen): je Station Typ (Genehmigung/Aufgabe/Info/Bedingung), Genehmiger-Kette, Entscheidungsregel (einer/N&nbsp;von&nbsp;M/alle), Frist/Eskalation und – bei Aufgaben – das Fulfillment (Ticket/Kanban).</li>
                <li><strong>Vorlage aktivieren</strong> (Schalter in der Vorlage). Erst dann erscheint sie im Katalog unter <code>/antraege/neu</code>.</li>
            </ol>
            <div class="req"><strong>Voraussetzungen je nach Nutzung:</strong> „Vorgesetzter"-Findung benötigt den Verzeichnis-Sync (der Vorgesetzte wird aus Entra/M365 per <code>$expand=manager</code> bzw. aus LDAP über den Manager-DN importiert); externe Mail-Genehmigung und Erinnerungen benötigen einen konfigurierten E-Mail-Versand. Eskalation/Anonymisierung laufen über den Scheduler.</div>
        </div>

        <h5>Berechtigungen (Capabilities) setzen</h5>
        <table class="settings-table">
            <thead><tr><th scope="col">Capability</th><th scope="col">Zweck</th><th scope="col">Zuweisung</th></tr></thead>
            <tbody>
                <tr><td><code>view_workflows</code></td><td>Modul nutzen: Inbox, Katalog, eigene Anträge stellen und über zugewiesene Stationen entscheiden.</td><td>Wird allen bestehenden Rollen automatisch erteilt; kann pro Rolle entzogen werden.</td></tr>
                <tr><td><code>manage_workflows</code></td><td>Vorlagen verwalten/duplizieren, Verwaltungs-Übersicht inkl. CSV-Export, festhängende Stationen neu zuweisen/überspringen/sofort erinnern, Anträge anonymisieren.</td><td>Globale Admins automatisch; sonst einer Rolle unter <em>Rollen &amp; Berechtigungen</em> zuweisen.</td></tr>
            </tbody>
        </table>
        <div class="info-box"><strong>Genehmiger über Capabilities steuern:</strong> Für das Resolver-Verfahren „Capability" können Sie eine <em>eigene</em> Berechtigung (z.&nbsp;B. <code>approve_it_requests</code>) verwenden bzw. anlegen und einer Rolle zuweisen. Tragen Sie den Capability-Namen in der Station als Genehmiger-Wert ein – alle Träger dieser Rolle werden zu Genehmigern. Das eigentliche „Genehmigen" erfordert <strong>keine</strong> eigene Capability; es ergibt sich aus dem aufgelösten Genehmiger-Satz der Station.</div>

        <h5>Externe Genehmigung &amp; Schnittstelle</h5>
        <ul>
            <li><strong>Ohne Login (Mail-Link):</strong> Station mit Genehmiger „externe E-Mail" – die Person erhält einen persönlichen Einmal-Link auf <code>/approval/{token}</code> (Gültigkeit unter <code>/admin/workflows/settings</code>).</li>
            <li><strong>Portal-/REST-API:</strong> Über die API (<code>/admin/api</code>, Scope <code>workflows</code>) lassen sich offene Genehmigungen abrufen (<code>GET /api/ex/my-approvals</code>), eigene Anträge auflisten (<code>GET /api/ex/my-requests</code>) und Entscheidungen treffen (<code>POST /api/ex/approvals/{id}/decide</code>).</li>
        </ul>

        <div class="usage-box">
            <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung Schritt für Schritt</div>
            <ol>
                <li><strong>Antrag stellen:</strong> unter <code>/antraege/neu</code> die passende Vorlage wählen, das Formular ausfüllen (Pflichtfelder/Validierung) und einreichen; Entwürfe lassen sich zwischenspeichern.</li>
                <li><strong>Verfolgen:</strong> der eigene Antrag durchläuft die Stationen – Rückfragen beantworten oder den Antrag korrigieren und erneut einreichen.</li>
                <li><strong>Genehmigen:</strong> zugewiesene Vorgänge erscheinen in der Inbox „Meine Genehmigungen" (mit Nav-Badge); dort zustimmen oder mit Pflichtbegründung ablehnen, weiterleiten oder zurückfragen. Externe Genehmiger entscheiden über einen Einmal-Link per E-Mail – ganz ohne Login.</li>
                <li><strong>Steuern</strong> (Verwalter): unter <code>/admin/workflows/uebersicht</code> den Stand aller Anträge mit Kennzahlen verfolgen, festhängende Stationen neu zuweisen, überspringen oder erinnern und einen PDF-Nachweis je Antrag erzeugen.</li>
            </ol>
        </div>
        <div class="admin-links"><strong>Verwaltung:</strong> <code>/admin/workflows</code> (Vorlagen, Felder, Stationen), <code>/admin/workflows/uebersicht</code>, <code>/admin/workflows/settings</code> &nbsp;·&nbsp; Berechtigungen: <code>view_workflows</code>, <code>manage_workflows</code></div>
        <div class="dep-list"><strong>Abhängigkeiten:</strong> optional Tickets/Kanban (Fulfillment), Verzeichnis-Sync (für „Vorgesetzter"-Findung), E-Mail-Versand (externe Genehmigung/Erinnerungen). Scheduler-Tasks: <code>workflow_escalation</code>, <code>workflow_anonymization</code>.</div>
    </div>
</div>

<!-- ============================================================= -->
<!-- 4. Modulabhängigkeiten -->
<!-- ============================================================= -->
<div class="manual-section" id="abhaengigkeiten">
    <h2>5. Modulabhängigkeiten</h2>
    <p>Folgende Tabelle zeigt die wesentlichen Abhängigkeiten zwischen Modulen und externen Diensten:</p>

    <table class="settings-table">
        <thead>
            <tr>
                <th scope="col">Modul</th>
                <th scope="col">Benötigt / genutzt von</th>
                <th scope="col">Art der Abhängigkeit</th>
            </tr>
        </thead>
        <tbody>
            <tr><td><strong>Contacts</strong></td><td>Tickets, Contracts, Seiten (Ansprechpartner)</td><td>Andere Module verlinken auf Kontakte als Ansprechpartner oder Vertragspartner</td></tr>
            <tr><td><strong>Assets</strong></td><td>Loans, Stock, Tickets</td><td>Loan-Geräte und Lagerartikel referenzieren Asset-Typen; Tickets können Assets verknüpfen</td></tr>
            <tr><td><strong>Contracts</strong></td><td>Licenses</td><td>Lizenzen können einem Vertrag zugeordnet werden</td></tr>
            <tr><td><strong>Locations (Stammdaten)</strong></td><td>Assets, Tickets, IPAM</td><td>Standorte werden in Assets, Tickets und Netzwerksegmenten genutzt</td></tr>
            <tr><td><strong>IPAM</strong></td><td>UniFi</td><td>VLANs aus IPAM können zum UniFi-Controller synchronisiert werden</td></tr>
            <tr><td><strong>VMs</strong></td><td>Monitoring (Zabbix), Backup (Veeam)</td><td>VMs werden in Zabbix überwacht und via Veeam gesichert – beide zeigen Status in OpenNIT</td></tr>
            <tr><td><strong>Licenses</strong></td><td>Microsoft 365 / Entra ID</td><td>Automatischer Lizenzsync über die Microsoft Graph API</td></tr>
            <tr><td><strong>AI Chat</strong></td><td>Tickets, Seiten</td><td>KI greift auf Ticket- und Dokumentationsinhalte zu, um kontextbezogene Antworten zu geben</td></tr>
            <tr><td><strong>API-Auto</strong></td><td>KI-Integration (Anthropic), Zielmodule (Assets, Kontakte, IPAM …)</td><td>Die KI leitet das Mapping ab; importierte Daten landen in den vorhandenen Zielmodulen</td></tr>
            <tr><td><strong>Directory / Personen</strong></td><td>Phonebook, Seiten (Ansprechpartner), Asset-Übergabe</td><td>Verzeichnispersonen aus AD-Sync können als Ansprechpartner referenziert werden</td></tr>
            <tr><td><strong>Budget</strong></td><td>Orders</td><td>Bestellungen können optional Budgetpositionen zugeordnet werden</td></tr>
            <tr><td><strong>E-Mail-Versand</strong></td><td>Tickets, Loans, Contracts, Notifications, SSL, CVE</td><td>Alle Benachrichtigungs-E-Mails über den konfigurierten Versand (SMTP-Passwort oder OAuth)</td></tr>
        </tbody>
    </table>

    <div class="info-box">
        <i class="fas fa-info-circle"></i>
        <strong>Hinweis:</strong> Externe Dienste (Zabbix, Veeam, UniFi, Entra ID, Google Workspace) müssen separat betrieben und deren Zugangsdaten in OpenNIT hinterlegt werden. Die jeweiligen Module zeigen nur Daten an – sie steuern externe Systeme nicht.
    </div>
</div>

<!-- ============================================================= -->
<!-- 5. Administration -->
<!-- ============================================================= -->
<div class="manual-section" id="szenarien">
    <h2>6. Anwendungsszenarien (modulübergreifend)</h2>
    <p class="lead">Der eigentliche Nutzen entsteht im Zusammenspiel mehrerer Module. Die folgenden durchgängigen Abläufe zeigen, wie die Bausteine ineinandergreifen. Jeder Schritt nutzt nur tatsächlich vorhandene Funktionen; die Einzelheiten stehen beim jeweiligen Modul (Kapitel&nbsp;4).</p>

    <div class="module-card">
        <h4><i class="fas fa-shopping-cart"></i> Szenario 1 – Von der Bedarfsmeldung zum inventarisierten Gerät</h4>
        <ol>
            <li>Ein Anwender meldet per <strong>Ticket</strong> Bedarf (z. B. „neuer Laptop").</li>
            <li>Über die Schnellaktion <em>„Bestellung aus Ticket erstellen"</em> entsteht eine <strong>Bestellung</strong>; Ticket und Bestellung sind gegenseitig verlinkt, das Ticket wechselt auf „Warteschlange".</li>
            <li>Im <strong>Bestellboard</strong> wird der Fortschritt gepflegt; bei Lieferung wechselt die Bestellung auf „Eingetroffen".</li>
            <li>Ab „Eingetroffen" werden die Komponenten direkt als <strong>Inventar</strong>-Geräte übernommen (keine Doppelerfassung).</li>
            <li>Bei der Ausgabe quittiert der Empfänger das <strong>Übergabeprotokoll</strong> auf dem Tablet – PDF und E-Mail werden erzeugt.</li>
        </ol>
        <div class="dep-list"><strong>Beteiligte Module:</strong> Tickets · Bestellboard · Inventar (· Budget für die Kostenzuordnung)</div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-hourglass-half"></i> Szenario 2 – Verträge &amp; Lizenzen fristgerecht steuern</h4>
        <ol>
            <li><strong>Verträge</strong> mit Laufzeit/Kündigungsfrist und <strong>Lizenzen</strong> mit Ablaufdatum werden gepflegt.</li>
            <li>Beide Ablauftermine erscheinen automatisch im <strong>Wartungskalender</strong> und – berechtigungsgenau – im <strong>Fristen-Cockpit</strong>.</li>
            <li>Der optionale <strong>Fristen-Digest</strong> verschickt einmal pro Woche eine gebündelte Übersicht per E-Mail.</li>
            <li>Vor Beginn der Kündigungsfrist wird erinnert; ohne Kündigung verlängert sich der Vertrag automatisch um die hinterlegte Dauer.</li>
        </ol>
        <div class="dep-list"><strong>Beteiligte Module:</strong> Verträge · Lizenzen · Wartungskalender · Fristen-Cockpit (· Scheduler für Erinnerungen/Digest)</div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-shield-virus"></i> Szenario 3 – Sicherheitswarnung von der Meldung bis zum Nachweis</h4>
        <ol>
            <li>Ein Problem trifft per <strong>Monitoring</strong>-Webhook, als <strong>XDR</strong>-Warnung oder als neue <strong>CVE</strong> ein.</li>
            <li>Optional erzeugt der Task <code>alert_to_ticket</code> automatisch ein <strong>Ticket</strong> zur Bearbeitung.</li>
            <li>Wird etwas geändert, dokumentiert das <strong>Konfigurationslog</strong> die Maßnahme (geplant/erledigt).</li>
            <li>Im <strong>ISMS</strong> lässt sich aus einer CVE ein Risiko ableiten und behandeln; Maßnahmen werden als To-do/Ticket ausgeleitet.</li>
            <li>Jeder Schritt liegt unveränderlich im <strong>Audit-Log</strong> – nachweisbar für ISO&nbsp;27001 / Wirtschaftsprüfung.</li>
        </ol>
        <div class="dep-list"><strong>Beteiligte Module:</strong> Monitoring/XDR/CVE · Tickets · Konfigurationslog · ISMS · Audit-Log</div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-user-plus"></i> Szenario 4 – Onboarding/Offboarding als Antrag</h4>
        <ol>
            <li>Die Personalstelle startet einen <strong>Antrag</strong> (Workflow „Onboarding") und füllt das Formular aus.</li>
            <li>Nach den <strong>Genehmigungen</strong> erzeugen Aufgaben-Stationen automatisch <strong>Tickets</strong> oder <strong>Kanban</strong>-Aufgaben für die IT.</li>
            <li>Geräte werden über <strong>Leihgeräte</strong> bzw. das <strong>Inventar</strong> ausgegeben, Zugänge im <strong>Vault</strong> geteilt.</li>
            <li>Beim Offboarding läuft derselbe Mechanismus rückwärts (Geräte zurück, Lizenz freigeben); abgeschlossene Anträge werden nach Frist DSGVO-konform anonymisiert.</li>
        </ol>
        <div class="dep-list"><strong>Beteiligte Module:</strong> Anträge &amp; Genehmigungen · Tickets/Kanban · Leihgeräte/Inventar · Vault · Lizenzen</div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-traffic-light"></i> Szenario 5 – Geplante Wartung transparent kommunizieren</h4>
        <ol>
            <li>Eine geplante Änderung im <strong>Konfigurationslog</strong> oder ein Termin im <strong>Wartungskalender</strong> wird angelegt und als öffentlich markiert.</li>
            <li>Der Eintrag erscheint automatisch als geplante Wartung auf der öffentlichen <strong>Statusseite</strong> (max. 10&nbsp;Tage im Voraus, ohne Doppelungen).</li>
            <li>Während der Maßnahme kann der betroffene Dienst auf einen passenden Status geschaltet werden; nach „Erledigt" wird er automatisch wieder freigegeben.</li>
        </ol>
        <div class="dep-list"><strong>Beteiligte Module:</strong> Konfigurationslog/Wartungskalender · Statusseite</div>
    </div>
</div>

<div class="manual-section" id="admin">
    <h2>7. Administrationsbereiche</h2>
    <p>Der Administrationsbereich (<code>/admin</code>) ist nur für Benutzer mit entsprechenden Berechtigungen zugänglich. Die Seitenleiste ist in einklappbare Gruppen (Übersicht, Benutzer &amp; Sicherheit, Inhalte &amp; Kommunikation, Ticketsystem, Datei-Explorer, Inventar &amp; Assets, Geschäftsprozesse, Infrastruktur, System) unterteilt; ein Suchfeld oben filtert die Menüpunkte live.</p>

    <h3>Allgemein</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>Dashboard</td><td><code>/admin</code></td><td>Übersicht: aktive Benutzer, Seiten, fällige Reviews, letzte Aktivitäten, Logins, Systemstatus</td></tr>
            <tr><td>Statistiken</td><td><code>/admin/statistics</code></td><td>Detaillierte Systemstatistiken: Inhalte, Benutzeraktivität, Modulnutzung</td></tr>
            <tr><td>System-Health</td><td><code>/admin/system-health</code></td><td>Prüfung von DB-Verbindung, Dateisystem, Schreibrechten, Verschlüsselung und E-Mail/OAuth (Live-Token-Check)</td></tr>
            <tr><td>Systemhandbuch</td><td><code>/admin/manual</code></td><td>Dieses Handbuch (Web-Ansicht und PDF-Export)</td></tr>
        </tbody>
    </table>

    <h3>Benutzer &amp; Rollen</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>Benutzerverwaltung</td><td><code>/admin/users</code></td><td>Benutzer anlegen, bearbeiten, deaktivieren/aktivieren, MFA zurücksetzen</td></tr>
            <tr><td>Rollenverwaltung</td><td><code>/admin/roles</code></td><td>Rollen erstellen und Berechtigungen zuweisen</td></tr>
            <tr><td>Berechtigungsmatrix</td><td><code>/admin/permissions</code></td><td>Übersicht aller Rollen und ihrer Berechtigungen (Capability-Matrix)</td></tr>
        </tbody>
    </table>

    <h3>Stammdaten</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>Standorte</td><td><code>/admin/locations</code></td><td>Physische Standorte und Räume verwalten (genutzt von Assets und Tickets)</td></tr>
            <tr><td>Verzeichnis &amp; Personen</td><td><code>/admin/directory</code></td><td>AD/LDAP-Sync-Einstellungen, manuelle Pflege von Personen im Verzeichnis</td></tr>
            <tr><td>Vertrags-Einstellungen</td><td><code>/admin/contracts/settings</code></td><td>Konfigurierbare Fristen/Erinnerungen und Optionen für das Vertragsmodul</td></tr>
        </tbody>
    </table>

    <h3>Kommunikation &amp; Inhalte</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>E-Mail-Vorlagen</td><td><code>/admin/email</code></td><td>Globale E-Mail-Vorlagen für Systembenachrichtigungen anpassen</td></tr>
            <tr><td>Ticket-E-Mails</td><td><code>/admin/tickets/templates</code></td><td>Automatische Benachrichtigungsvorlagen für Ticket-Ereignisse</td></tr>
            <tr><td>Antwortvorlagen</td><td><code>/admin/tickets/canned-responses</code></td><td>Globale Canned Responses mit Platzhaltern für Ticket-Antworten</td></tr>
            <tr><td>Benachrichtigungen</td><td><code>/admin/notifications</code></td><td>In-App- und E-Mail-Benachrichtigungsvorlagen verwalten</td></tr>
            <tr><td>Seitenvorlagen</td><td><code>/admin/page-templates</code></td><td>Vorlagen für Dokumentationsseiten erstellen und verwalten</td></tr>
            <tr><td>Tags</td><td><code>/admin/tags</code></td><td>Systemweite Tags für Inhalte verwalten</td></tr>
            <tr><td>Archivierte Seiten</td><td><code>/admin/archived-pages</code></td><td>Gelöschte (archivierte) Seiten anzeigen und wiederherstellen</td></tr>
        </tbody>
    </table>

    <h3>Ticketsystem</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>Ticket-Einstellungen (Postfach)</td><td><code>/admin/tickets/settings</code></td><td>E-Mail-Eingang/-Versand (IMAP/SMTP, Microsoft 365 Graph/XOAUTH2 oder Google), Abruf-Status, Aktion nach Abruf, Secret-Ablauf</td></tr>
            <tr><td>Features &amp; SLA</td><td><code>/admin/tickets/features</code></td><td>SLA-Reaktionszeit pro Priorität, SLA-Pause, Geschäftszeiten + Feiertage, Aufwandserfassung, CSAT, Benachrichtigungs-Schalter (Melder &amp; Bearbeiter)</td></tr>
            <tr><td>Automatisierung</td><td><code>/admin/tickets/automation</code></td><td>Automatische Zuweisung: Kategorie-Standardbearbeiter und/oder Round-Robin</td></tr>
            <tr><td>Eigene Felder</td><td><code>/admin/tickets/custom-fields</code></td><td>Custom Fields (global oder pro Kategorie), 6 Feldtypen, optional Pflichtfeld</td></tr>
            <tr><td>Ticket-Kategorien</td><td><code>/admin/tickets/categories</code></td><td>Kategorien/Unterkategorien mit optionalem Standard-Bearbeiter</td></tr>
            <tr><td>Ticket-Statistiken</td><td><code>/admin/tickets/stats</code></td><td>Auswertungen zu Volumen, Reaktionszeiten und Kategorien</td></tr>
            <tr><td>CSAT-Bewertungen</td><td><code>/admin/tickets/csat</code></td><td>Zufriedenheits-Auswertung (Durchschnitt, Rücklauf, Verteilung)</td></tr>
            <tr><td>Leistungsnachweise</td><td><code>/admin/tickets/service-reports</code></td><td>Nachweise aus der Zeiterfassung: Verhalten beim Ticket-Abschluss (nachfragen oder fest vorgeben), Monatslauf je Standort samt Stichtag und Umfang, Gültigkeit des Unterschriften-Links, Nummernkreis, Gestaltung der Dokumente und Übersicht der erstellten Nachweise. Die Empfängeradresse wird je Standort gepflegt (<code>/admin/locations</code>) und von Unterstandorten ohne eigene Adresse nach oben geerbt</td></tr>
            <tr><td>E-Mail-Queue</td><td><code>/admin/tickets/mail-queue</code></td><td>Status der ausgehenden Ticket-E-Mails und Fehlerbehebung</td></tr>
        </tbody>
    </table>

    <h3>Inventar</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>Asset-Kategorien</td><td><code>/admin/assets/categories</code></td><td>Kategorien, Typen, benutzerdefinierte Felder für Assets verwalten</td></tr>
            <tr><td>Hersteller</td><td><code>/admin/assets/manufacturers</code></td><td>Hersteller und Marken für Assets pflegen</td></tr>
            <tr><td>Asset Sync</td><td><code>/admin/inventory/sync</code></td><td>PowerShell-Agent-Einrichtung für automatischen Asset-Sync; konfigurierbares Rate-Limit pro API-Key</td></tr>
            <tr><td>Unbekannte Geräte</td><td><code>/admin/inventory/unmatched-devices</code></td><td>Vom Sync erkannte, aber noch nicht zugeordnete Geräte manuell zuweisen</td></tr>
            <tr><td>GLPI Import</td><td><code>/admin/inventory/import/glpi</code></td><td>Einmaliger oder wiederholter Import aus GLPI</td></tr>
            <tr><td>Übergabe-Einstellungen</td><td><code>/admin/assets/handover-settings</code></td><td>PDF-Vorlage und E-Mail für Asset-Übergabeprotokolle konfigurieren</td></tr>
            <tr><td>Etikettendruck</td><td><code>/admin/settings/label-print</code></td><td>DYMO-Druckeranbindung für Asset-Etiketten einrichten</td></tr>
            <tr><td>Relution MDM</td><td><code>/admin/inventory/relution</code></td><td>Anbindung an Relution MDM für mobile Geräteverwaltung</td></tr>
            <tr><td>Microsoft Intune</td><td><code>/admin/inventory/intune</code></td><td>Geräteimport aus Microsoft Intune (Graph API): Hardware-Anreicherung, Richtlinien je Gerät, Auto-Anlegen oder Liste unbekannter Geräte</td></tr>
            <tr><td>Dell API</td><td><code>/admin/settings/dell</code></td><td>Dell-TechDirect-API; gestaffelter Garantie-Abruf mit Tageslimit und Fortschritt</td></tr>
            <tr><td>HP API</td><td><code>/admin/settings/hp</code></td><td>HP-API-Zugangsdaten für automatische Garantieabfragen</td></tr>
        </tbody>
    </table>

    <h3>Externe Integrationen</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>Monitoring</td><td><code>/admin/monitoring/settings</code></td><td>Zabbix-Server-URL und API-Token</td></tr>
            <tr><td>XDR-Warnungen</td><td><code>/admin/xdr/settings</code></td><td>Trend Vision One: Region und API-Key (verschlüsselt), Verbindungstest und Sofort-Abgleich</td></tr>
            <tr><td>Backup-Server</td><td><code>/admin/backup/servers</code>, <code>/admin/backup/settings</code></td><td>Mehrere Backup-Server verwalten: Veeam REST API und/oder Proxmox Backup Server (PBS); Verbindung testen</td></tr>
            <tr><td>UniFi</td><td><code>/admin/settings/unifi</code></td><td>UniFi-Controller URL, Zugangsdaten und Timeouts</td></tr>
            <tr><td>Hypervisor-APIs</td><td><code>/admin/vms/hypervisor</code></td><td>VMware vCenter / Hyper-V / Proxmox VE API-Verbindungen konfigurieren</td></tr>
            <tr><td>IPAM-Scan-Agent</td><td><code>/admin/ipam-scan-agent</code></td><td>Agent für Netzwerk-/Verfügbarkeitsscans herunterladen und einrichten</td></tr>
            <tr><td>Lizenzsync (M365)</td><td><code>/admin/licenses/sync</code></td><td>Microsoft 365 / Entra ID Sync für die Lizenzverwaltung</td></tr>
            <tr><td>KI-Integration</td><td><code>/admin/settings/ai-chat</code></td><td>API-Schlüssel und Einstellungen für den KI-Assistenten; Monatsbudgets, Umrechnungskurs USD → Währung der Installation für die Kostenschätzung; Schalter für die API-Auto-Web-Suche</td></tr>
            <tr><td>API-Auto</td><td><code>/admin/settings/api-auto</code></td><td>Übersicht der KI-gestützten API-Integrationen</td></tr>
            <tr><td>API-Token</td><td><code>/admin/api</code></td><td>Token-Verwaltung für die WordPress-Plugin-API und Chrome-Extension; interaktive Swagger-UI-API-Dokumentation</td></tr>
        </tbody>
    </table>

    <h3>Editor &amp; Diagramme</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>draw.io Installation</td><td><code>/admin/netdiagram/drawio</code></td><td>draw.io-Binaries herunterladen und aktualisieren (Netzwerkdiagramme)</td></tr>
            <tr><td>TinyMCE Editor</td><td><code>/admin/tinymce</code></td><td>TinyMCE-Editor installieren und aktualisieren (Dokumentationsseiten)</td></tr>
        </tbody>
    </table>

    <h3>System</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Seite</th><th scope="col">URL</th><th scope="col">Funktion</th></tr></thead>
        <tbody>
            <tr><td>Cronjobs</td><td><code>/admin/cronjobs</code></td><td>Geplante Aufgaben verwalten: Status, Protokolle, manuelle Ausführung, Intervalle</td></tr>
            <tr><td>Log-Viewer</td><td><code>/admin/logs</code></td><td>Protokolldateien aus <code>storage/logs</code> im Backend ansehen (Datei wählen, Zeilenanzahl, Textfilter) und herunterladen</td></tr>
            <tr><td>Bild-Optimierung</td><td><code>/admin/image-optimization</code></td><td>Hochgeladene Bilder serverseitig komprimieren (Stapellauf mit Fortschritt; benötigt GD)</td></tr>
            <tr><td>Signatur-Tablet (IP-Freigabe)</td><td><code>/admin/settings/signature</code></td><td>Erlaubte IP-Adressen/Netze für die login-freie Unterschriften-Seite (<code>/signature</code>) bei der Geräteübergabe</td></tr>
            <tr><td>System-Update</td><td><code>/admin/update</code></td><td>System-Updates einspielen, Changelog ansehen, PHP-Abhängigkeiten (Composer) aktualisieren und Datenbankmigrationen ausführen</td></tr>
            <tr><td>Audit-Log</td><td><code>/admin/audit</code></td><td>Vollständiges Audit-Log mit Filter; Export als CSV; Berichte unter <code>/admin/audit/reports</code></td></tr>
            <tr><td>Audit- &amp; Compliance-Bericht</td><td><code>/admin/compliance</code></td><td>PDF-Nachweisdokument für ISO&nbsp;27001 / BSI / DSGVO / GoBD mit Live-Kennzahlen und Control-Mapping (Profile: Vollständig, ISO, Wirtschaftsprüfung)</td></tr>
        </tbody>
    </table>

    <h3>Update-Seite (<code>/admin/update</code>)</h3>
    <p>Die Update-Seite bündelt alles rund um Aktualisierungen:</p>
    <ul>
        <li><strong>System-Update:</strong> installierte Version prüfen und Updates über den Update-Proxy installieren; Datenbank-Migrationen werden angezeigt und lassen sich ausführen.</li>
        <li><strong>Änderungen (Changelog):</strong> Die Karte zeigt die aktuelle Version und die letzten Aktualisierungen (ältere zum Aufklappen). Über <em>„Gesamtes Changelog anzeigen"</em> öffnet sich der vollständige Änderungsverlauf.</li>
        <li><strong>PHP-Abhängigkeiten (Composer):</strong> aktualisiert den Ordner <code>vendor/</code>.
            <em>Variante A</em>: per SSH (<code>composer update --no-dev --optimize-autoloader</code> im Projektverzeichnis).
            <em>Variante B</em>: direkt im Backend in zwei Schritten – <em>„composer.phar ins Verzeichnis laden"</em> (lädt die offizielle Datei in die Projektwurzel; funktioniert auch mit <code>open_basedir</code>) und danach <em>„composer update ausführen"</em>. Die vollständige Composer-Ausgabe wird angezeigt, der Vorgang im Audit-Log vermerkt. Es wird automatisch eine zur Website passende PHP-Version verwendet.</li>
    </ul>
    <div class="info-box">
        <i class="fas fa-info-circle"></i>
        Sonderfälle per Umgebungsvariable: <code>OPENNIT_COMPOSER_BIN</code> (Pfad zu einem vorhandenen Composer-Binary außerhalb der Standardpfade) und <code>OPENNIT_PHP_BIN</code> (PHP-CLI-Pfad, falls die automatische Erkennung keine passende Version findet). Alternativ lässt sich der Composer-Pfad direkt auf der Seite eintragen (z.&nbsp;B. das Ergebnis von <code>which composer</code>). Vor <code>composer update</code> wird ein Backup empfohlen.
    </div>
</div>

<!-- ============================================================= -->
<!-- 6. Einstellungen -->
<!-- ============================================================= -->
<div class="manual-section" id="einstellungen">
    <h2>8. Systemeinstellungen</h2>

    <h3>Allgemeine Einstellungen (<code>/admin/settings</code>)</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Einstellung</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>Anwendungsname</td><td>Anzeigename in der Oberfläche und in E-Mails (Standard: „OpenNIT")</td></tr>
            <tr><td>Logo / Favicon</td><td>Eigenes Logo (JPG, PNG, GIF, WebP, SVG) und Browser-Tab-Symbol (ICO, PNG)</td></tr>
            <tr><td>Login-Hintergrundbild</td><td>Hintergrundbild auf der Anmeldeseite</td></tr>
            <tr><td>Footer-Text</td><td>Angezeigter Text in der Fußzeile</td></tr>
            <tr><td>Anwendungs-URL</td><td>Öffentliche Basis-URL (wichtig für QR-Codes, E-Mail-Links und OAuth-Redirect-URIs)</td></tr>
            <tr><td>Zeitzone</td><td>Systemzeitzone (Standard: Europe/Berlin)</td></tr>
            <tr><td>Sprache der Oberfläche</td><td>Vorgabe für alle Benutzer (Deutsch oder Englisch). Jede Person kann davon abweichen: <em>Benutzermenü → Sprache</em>. Ohne eigene Wahl folgt sie dieser Vorgabe – auch wenn sie später geändert wird. Übersetzt sind derzeit der Seitenrahmen (Haupt- und Benutzermenü, Kopf- und Fußzeile, Benachrichtigungen, Administrationsmenü und die Dialoge darin), die Anmeldung samt zweitem Faktor, die Fehlerseiten und das Dashboard; die Modulseiten folgen. Noch nicht übersetzte Stellen erscheinen auf Deutsch</td></tr>
            <tr><td>Währung &amp; Zahlenformat</td><td>Gilt für die gesamte Installation: Währungscode, angezeigtes Zeichen und dessen Stellung (vor oder hinter dem Betrag) sowie die Trennzeichen für Dezimal- und Tausenderstellen. Die Angabe wirkt überall, wo Beträge erscheinen oder eingegeben werden – Verträge, Budget, Lizenzen, Inventar, Leihgeräte, Kostenübersicht, „Ausstattung &amp; Preise", PDF-Ausgaben, CSV-Exporte und Benachrichtigungen. <strong>Umgerechnet wird nichts:</strong> Ein gespeicherter Betrag bleibt die Zahl, die er ist; die Einstellung sagt nur, wie sie zu lesen und zu schreiben ist. In CSV-Dateien steht die Währung in der Spaltenüberschrift, der Wert als reine Zahl ohne Tausendertrenner</td></tr>
            <tr><td>Review-Intervall</td><td>Standard-Prüfintervall für Dokumentationsseiten in Monaten</td></tr>
            <tr><td>Lokaler Login</td><td>Lokal gespeicherte Anmeldung erlauben oder deaktivieren (z. B. bei reinem SSO)</td></tr>
            <tr><td>Flash-Meldungen / Icon-Stil</td><td>Einblend-Meldungen an/aus; Umschaltung zwischen (monochromen) Font-Awesome-Icons und bunten Emojis – wirkt im gesamten Frontend (Menü, Dashboard, alle Modul-Seiten und nachgeladene Inhalte). Symbole ohne passendes Emoji bleiben als Font-Awesome-Icon erhalten; im Standard (Font Awesome) ändert sich nichts</td></tr>
            <tr><td>Module</td><td>Einzelne Module aktivieren/deaktivieren</td></tr>
        </tbody>
    </table>

    <h4>Bedienung ohne Maus</h4>
    <p>OpenNIT lässt sich vollständig mit der Tastatur bedienen; dafür ist nichts einzurichten.</p>
    <ul>
        <li><strong>Sprungmarke:</strong> Der erste Tabulator auf jeder Seite bietet „Zum Inhalt springen" an und überspringt das Menü.</li>
        <li><strong>Sichtbarer Fokus:</strong> Das Element, auf dem der Fokus steht, ist umrandet. Beim Bedienen mit der Maus erscheint die Umrandung nicht.</li>
        <li><strong>Dialoge:</strong> Beim Öffnen springt der Fokus in den Dialog und bleibt darin; <kbd>Esc</kbd> schließt ihn, und der Fokus kehrt dorthin zurück, wo er herkam. Vorlese-Software kündigt den Dialog mit seiner Überschrift an.</li>
        <li><strong>Weniger Bewegung:</strong> Wer Animationen im Betriebssystem abbestellt hat, bekommt keine. Im Windows-Kontrastmodus zeichnet das System den Fokusring.</li>
    </ul>
    <p>Die Modulseiten sind daraufhin durchgegangen worden: Symbol-Schaltflächen tragen eine Bezeichnung,
    Bilder einen Alternativtext, Tabellenüberschriften ihre Richtung, und Formularbeschriftungen sind mit
    ihrem Feld verbunden. Bei einem Teil der Eingabefelder steht diese Verbindung noch aus; sie sind
    weiterhin bedienbar, aber Vorlese-Software liest die Beschriftung dort nicht automatisch mit vor.</p>

    <h4>Sicherheit &amp; Sessions</h4>
    <p>Zeit- und Limit-Werte, die früher nur in <code>.env</code>/<code>config/security.php</code> lagen, sind jetzt direkt unter <code>/admin/settings</code> (Card „Sicherheit &amp; Sessions") pflegbar. Die Werte werden in <code>config/runtime.php</code> gespeichert und überschreiben die <code>.env</code>-Vorgaben; sie greifen sofort für neue Sessions/Anfragen.</p>
    <table class="settings-table">
        <thead><tr><th scope="col">Einstellung</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>Session-Lebensdauer (Min.)</td><td>Inaktivitäts-Timeout bis zur Abmeldung (Standard 480 = 8&nbsp;h)</td></tr>
            <tr><td>Session-ID erneuern alle (Min.)</td><td>Intervall der Session-ID-Regenerierung gegen Session-Fixation (Standard 30)</td></tr>
            <tr><td>CSRF-Token-Lebensdauer (Min.)</td><td>Gültigkeit der Formular-Tokens; ≥ Session-Lebensdauer empfohlen (Standard 480)</td></tr>
            <tr><td>Max. Upload-Größe (MB)</td><td>Anwendungs-Limit; PHP <code>upload_max_filesize</code>/<code>post_max_size</code> müssen mind. so hoch sein (Standard 50)</td></tr>
            <tr><td>Audit-Aufbewahrung (Jahre)</td><td>Dokumentierte Aufbewahrungs-Policy (Einträge sind unveränderlich; Standard 10)</td></tr>
            <tr><td>Rate-Limiting</td><td>Aktivieren/Deaktivieren sowie Anfragen je Zeitfenster (Sekunden); Login/MFA haben eigene, strengere Limits</td></tr>
        </tbody>
    </table>
    <div class="info-box">
        <i class="fas fa-info-circle"></i>
        Harte Sicherheits-Schalter (HTTPS-only-Cookie <code>SESSION_SECURE</code>, HSTS, CSP, Verschlüsselungsschlüssel) bleiben bewusst in <code>.env</code>/<code>config/security.php</code> — eine Fehlbedienung im Backend könnte sonst den Zugang sperren.
    </div>

    <h3>E-Mail-Einstellungen (<code>/admin/settings/email</code>)</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Einstellung</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>Verbindungsverfahren</td><td>SMTP mit Passwort, <strong>Microsoft 365 – Graph API</strong> (empfohlen), <strong>Microsoft 365 – SMTP/XOAUTH2</strong> oder <strong>Google Workspace (XOAUTH2)</strong></td></tr>
            <tr><td>SMTP-Host / Port / Verschlüsselung</td><td>Ausgehender Mailserver (587 STARTTLS, 465 SSL/TLS) – nur bei „SMTP mit Passwort"</td></tr>
            <tr><td>SMTP-Benutzer / Passwort</td><td>Zugangsdaten der SMTP-Authentifizierung – nur bei „SMTP mit Passwort"</td></tr>
            <tr><td>OAuth: Anbieter</td><td>Microsoft 365 / Entra ID oder Google Workspace (Gmail)</td></tr>
            <tr><td>OAuth: Tenant-/Client-ID, Secret, Postfach</td><td>App-Registrierungsdaten; Secret-Ablaufdatum optional hinterlegbar (Warnung 30 Tage vorher)</td></tr>
            <tr><td>OAuth: „Verbinden"</td><td>Holt den Refresh-Token per Consent-Flow (delegiert); Redirect-URI wird angezeigt</td></tr>
            <tr><td>Absenderadresse / Absendername</td><td>From-Adresse und Anzeigename für alle Systemmails (bei OAuth leer = Postfach-Adresse)</td></tr>
        </tbody>
    </table>
    <p class="text-muted" style="font-size:0.85rem;">
        Details siehe Abschnitt <a href="#email-oauth">„9. E-Mail-Versand &amp; OAuth"</a>.
        Diese Einstellungen gelten für alle System-Mails; das Ticket-Postfach wird separat unter
        <code>/admin/tickets/settings</code> konfiguriert.
    </p>

    <h3>Microsoft 365 SSO-Login (<code>/admin/settings/m365</code>)</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Einstellung</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>Tenant-ID</td><td>Azure AD / Entra ID Tenant-ID (für den OAuth2/PKCE-<strong>Login</strong>)</td></tr>
            <tr><td>Client-ID / Client-Secret</td><td>App-Registrierung in Entra ID</td></tr>
            <tr><td>Redirect-URI</td><td>Rückruf-URL nach erfolgreicher Anmeldung (muss in Entra ID registriert sein)</td></tr>
        </tbody>
    </table>
    <p class="text-muted" style="font-size:0.85rem;">
        Hinweis: Dies betrifft ausschließlich die <strong>Benutzer-Anmeldung</strong> (Single Sign-on) –
        unabhängig vom E-Mail-Versand. Für den E-Mail-Versand wird eine eigene App-Registrierung empfohlen.
    </p>
    <div class="setup-box">
        <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt</div>
        <ol>
            <li><strong>App-Registrierung in Entra ID</strong> anlegen (Azure-Portal → App-Registrierungen).</li>
            <li>Als <strong>Redirect-URI</strong> (Typ „Web") die in OpenNIT angezeigte Rückruf-URL eintragen.</li>
            <li>Ein <strong>Client-Secret</strong> erzeugen und zusammen mit Tenant- und Client-ID in <code>/admin/settings/m365</code> hinterlegen.</li>
            <li>Delegierte Berechtigung <code>openid</code>/<code>profile</code>/<code>email</code> (User.Read) freigeben, speichern – danach erscheint „Mit Microsoft anmelden" auf der Login-Seite.</li>
        </ol>
        <div class="req">Tipp: Für SSO-Login, E-Mail-Versand und Verzeichnis-/Lizenz-Sync getrennte App-Registrierungen verwenden (unterschiedliche Berechtigungen).</div>
    </div>

    <h3>Keycloak SSO (<code>/admin/settings/keycloak</code>)</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Einstellung</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>Keycloak URL / Realm</td><td>Basis-URL des Keycloak-Servers und Realm-Name</td></tr>
            <tr><td>Client-ID / Secret</td><td>Zugangsdaten der Keycloak-Client-Konfiguration</td></tr>
        </tbody>
    </table>

    <h3>LDAP-Authentifizierung (<code>/admin/settings/ldap-auth</code>)</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Einstellung</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>LDAP-Server / Port</td><td>Hostname/IP und Port (Standard: 389 / 636 für LDAPS)</td></tr>
            <tr><td>Bind-DN / Bind-Passwort</td><td>Service-Account für LDAP-Abfragen</td></tr>
            <tr><td>Such-Basis (Base DN)</td><td>LDAP-Pfad, unter dem Benutzer gesucht werden</td></tr>
            <tr><td>Benutzerfilter</td><td>LDAP-Filter zur Einschränkung der berechtigten Benutzer</td></tr>
        </tbody>
    </table>

    <h3>Progressive Web App (<code>/admin/settings/pwa</code>)</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Einstellung</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>Push-Benachrichtigungen</td><td>Browser-Push aktivieren (erfordert VAPID-Schlüsselpaar)</td></tr>
            <tr><td>VAPID-Schlüssel</td><td>Werden automatisch generiert; öffentlicher Schlüssel für die Browser-Registrierung</td></tr>
        </tbody>
    </table>
    <div class="setup-box">
        <div class="setup-title"><i class="fas fa-sliders-h"></i> Web-Push einrichten (für Benachrichtigungen)</div>
        <ol>
            <li>Unter <code>/admin/settings/pwa</code> Push-Benachrichtigungen aktivieren – das <strong>VAPID-Schlüsselpaar</strong> wird automatisch erzeugt (alternativ einmalig <code>bin/generate-vapid-keys.php</code>).</li>
            <li>Speichern; Benutzer aktivieren Push danach pro Gerät über das Glocken-Menü und bestätigen die Browser-Abfrage.</li>
            <li>Stellt die Voraussetzung für Web-Push in Tickets (Bearbeiter-Hinweise) und anderen Modulen dar.</li>
        </ol>
        <div class="req">Web-Push funktioniert nur über HTTPS mit gültigem Zertifikat.</div>
    </div>
</div>

<!-- ============================================================= -->
<!-- 7. E-Mail & OAuth -->
<!-- ============================================================= -->
<div class="manual-section" id="email-oauth">
    <h2>9. E-Mail-Versand &amp; OAuth (Microsoft 365 / Google)</h2>
    <p class="lead">
        OpenNIT versendet und empfängt E-Mails wahlweise über klassisches SMTP/IMAP mit Passwort
        oder über <strong>OAuth2</strong> – unterstützt werden <strong>Microsoft 365 / Exchange Online</strong>
        und <strong>Google Workspace / Gmail</strong>. Da sowohl Microsoft als auch Google die
        Basic-Authentifizierung (Passwort) für IMAP/SMTP abgekündigt haben, ist OAuth für diese
        Postfächer erforderlich.
    </p>

    <div class="module-card">
        <h4><i class="fas fa-sliders-h"></i> Wo wird das eingestellt?</h4>
        <ul>
            <li><strong>Globaler Versand</strong> (Benachrichtigungen, Bestellungen, Leihscheine, Asset-Übergaben, Vertrags-/Review-Erinnerungen): <code>Einstellungen → E-Mail</code>.</li>
            <li><strong>Ticket-Postfach</strong> (Abruf eingehender Mails + Antworten): <code>Administration → Ticketsystem → Einstellungen</code>.</li>
        </ul>
        <p>In beiden Bereichen wählt man oben das <strong>Verbindungsverfahren</strong>:</p>
        <table class="settings-table">
            <thead><tr><th scope="col">Verfahren</th><th scope="col">Beschreibung</th></tr></thead>
            <tbody>
                <tr><td>IMAP/SMTP mit Passwort</td><td>Klassisch, wie bisher.</td></tr>
                <tr><td>Microsoft 365 – Graph API</td><td><strong>Empfohlen für M365.</strong> Versand &amp; Abruf über die Graph-REST-API. Robust, rein über HTTPS.</td></tr>
                <tr><td>Microsoft 365 / Google – IMAP/SMTP (XOAUTH2)</td><td>Klassische Protokolle mit OAuth-Token. Für M365 nur, falls Graph organisatorisch nicht erlaubt ist. <strong>Dieses Verfahren wird auch für Google verwendet.</strong></td></tr>
            </tbody>
        </table>
        <p>Im OAuth-Block lässt sich der <strong>Anbieter</strong> umschalten: <em>Microsoft 365 / Entra ID</em> oder <em>Google Workspace (Gmail)</em>. Bei Google entfällt das Tenant-Feld; es wird automatisch das XOAUTH2-Verfahren über <code>imap.gmail.com</code> / <code>smtp.gmail.com</code> genutzt.</p>
        <div class="info-box">
            <strong>Microsoft 365</strong> nutzt standardmäßig den <strong>App-only-Flow (Client Credentials)</strong> – keinen interaktiven Login, ideal für den automatischen Abruf/Versand per Scheduler.
            <strong>Google</strong> kennt für Mail keinen App-only-Flow und verwendet daher immer den <strong>delegierten Refresh-Token-Flow</strong> (einmaliger Consent per „Verbinden"-Button).
        </div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-cloud"></i> Einrichtung in Microsoft Entra ID (Azure AD)</h4>
        <ol>
            <li><strong>App registrieren:</strong> Entra Admin Center → <em>App-Registrierungen → Neue Registrierung</em>. Anschließend <em>Anwendungs-(Client-)ID</em> und <em>Verzeichnis-(Tenant-)ID</em> notieren.</li>
            <li><strong>Client-Secret</strong> unter <em>Zertifikate &amp; Geheimnisse</em> erzeugen und den Wert sofort kopieren.</li>
            <li><strong>API-Berechtigungen (Application)</strong> hinzufügen und <em>Administratorzustimmung erteilen</em>:
                <ul>
                    <li>Graph: <code>Mail.ReadWrite</code> (Abruf), <code>Mail.Send</code> (Versand)</li>
                    <li>XOAUTH2: Office 365 Exchange Online <code>IMAP.AccessAsApp</code>, <code>SMTP.SendAsApp</code></li>
                </ul>
            </li>
            <li><strong>Zugriff einschränken</strong> (empfohlen): per <code>New-ApplicationAccessPolicy</code> (Graph) bzw. <code>Add-MailboxPermission</code> + <code>New-ServicePrincipal</code> (XOAUTH2) auf das eine Postfach begrenzen.</li>
        </ol>
        <div class="info-box">Ausführliche Schritt-für-Schritt-Anleitung inkl. PowerShell-Befehle: <code>docs/m365-oauth-email.md</code>.</div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-cloud"></i> Graph-Berechtigungen (eine gemeinsame App)</h4>
        <p>Anmeldung, Lizenz-Abruf, Personen-Import und Geräte-Import (Intune) nutzen <strong>dieselbe</strong>
            App-Registrierung aus den M365-Einstellungen – die Zugangsdaten werden dort zentral gepflegt. Die
            nötigen Graph-Berechtigungen werden im Entra-Portal an dieser App hinterlegt und per
            <em>Administratorzustimmung</em> freigegeben.</p>
        <table class="settings-table">
            <thead><tr><th scope="col">Modul</th><th scope="col">Graph-Berechtigung</th><th scope="col">Typ</th></tr></thead>
            <tbody>
                <tr><td>Microsoft-Anmeldung (SSO)</td><td><code>User.Read</code>, <code>openid</code>, <code>profile</code>, <code>email</code>, <code>offline_access</code></td><td>Delegiert</td></tr>
                <tr><td>Lizenz-Abruf</td><td><code>Organization.Read.All</code></td><td><strong>Anwendung</strong></td></tr>
                <tr><td>Personen-Import</td><td><code>User.Read.All</code></td><td><strong>Anwendung</strong></td></tr>
                <tr><td>Geräte-Import (Intune)</td><td><code>DeviceManagementManagedDevices.Read.All</code></td><td><strong>Anwendung</strong></td></tr>
            </tbody>
        </table>
        <div class="info-box">
            <strong>Wichtig:</strong> Lizenz-Abruf, Personen- und Geräte-Import laufen als App (Client-Credentials).
            Sie benötigen die Berechtigung als <strong>Anwendungsberechtigung (Application)</strong> – eine
            <em>delegierte</em> Berechtigung gleichen Namens genügt nicht und führt zu „Forbidden". Nach dem Hinzufügen
            jeweils die <em>Administratorzustimmung erteilen</em>.
        </div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-globe"></i> Einrichtung für Google Workspace (Gmail)</h4>
        <ol>
            <li><strong>Gmail-API aktivieren:</strong> Google Cloud Console → <em>APIs &amp; Dienste → Bibliothek → Gmail API → Aktivieren</em>.</li>
            <li><strong>OAuth-Zustimmungsbildschirm</strong> konfigurieren (Nutzertyp „Intern" empfohlen), Scope <code>https://mail.google.com/</code> hinzufügen.</li>
            <li><strong>OAuth-Client-ID (Webanwendung)</strong> erstellen. Als <em>autorisierte Redirect-URI</em> exakt die in OpenNIT angezeigte URL eintragen: <code>https://&lt;domain&gt;/admin/settings/mail-oauth/callback</code>.</li>
            <li>In OpenNIT Verfahren <em>IMAP/SMTP (XOAUTH2)</em> wählen, <strong>Anbieter „Google Workspace"</strong>, dann Client-ID, Secret und Postfach eintragen, speichern und <strong>„Mit Google verbinden"</strong> klicken.</li>
        </ol>
        <div class="info-box">Google bietet für Mail <strong>keinen</strong> App-only-Flow – der Refresh-Token wird per Consent geholt. IMAP muss für das Postfach aktiviert sein (Gmail → Einstellungen → POP/IMAP). Ausführlich: <code>docs/google-oauth-email.md</code>.</div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-link"></i> Delegiert verbinden (Refresh-Token per Klick)</h4>
        <p>Für den delegierten Flow (Microsoft <em>oder</em> Google) muss kein Refresh-Token manuell kopiert werden:</p>
        <ol>
            <li>Client-ID/Secret (und bei Microsoft Tenant-ID) eintragen und <strong>speichern</strong>.</li>
            <li>Auf <strong>„Mit Microsoft/Google verbinden"</strong> klicken – es öffnet sich der Consent-Dialog des Anbieters.</li>
            <li>Nach der Zustimmung kehrt der Browser zurück; OpenNIT speichert den Refresh-Token <strong>verschlüsselt</strong> und setzt den OAuth-Flow auf „Delegiert".</li>
        </ol>
        <div class="warn-box">Die <strong>Redirect-URI</strong> (<code>/admin/settings/mail-oauth/callback</code>) muss in der App-Registrierung bzw. Cloud Console zeichengenau hinterlegt sein. OpenNIT zeigt die vollständige URL direkt unter dem „Verbinden"-Button an.</div>
    </div>

    <div class="module-card">
        <h4><i class="fas fa-heartbeat"></i> Überwachung &amp; Diagnose</h4>
        <ul>
            <li><strong>Secret-Ablauf-Warnung:</strong> Das Ablaufdatum des Client-Secrets lässt sich hinterlegen. Ein täglicher Task warnt Administratoren 30 Tage vor Ablauf (und bei abgelaufenem Secret) per In-App-Benachrichtigung.</li>
            <li><strong>Live-Health-Check:</strong> Unter <code>Administration → System-Health</code> prüft die Gruppe „E-Mail / OAuth" Vollständigkeit, Secret-Ablauf und holt ein <em>echtes Token</em> vom Anbieter.</li>
            <li><strong>Abruf-Status:</strong> In den Ticket-Einstellungen wird das Ergebnis des letzten automatischen Abruf-Laufs angezeigt.</li>
        </ul>
        <div class="warn-box">Client-Secret und Refresh-Token werden mit dem <code>APP_KEY</code> verschlüsselt in <code>config/runtime.php</code> gespeichert (AES-256-GCM).</div>
    </div>
</div>

<!-- ============================================================= -->
<!-- 8. Sicherheit -->
<!-- ============================================================= -->
<div class="manual-section" id="sicherheit">
    <h2>10. Sicherheit &amp; Compliance</h2>

    <h3>Authentifizierung &amp; Session</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Merkmal</th><th scope="col">Konfiguration</th></tr></thead>
        <tbody>
            <tr><td>Session-Lebensdauer</td><td>8 Stunden (konfigurierbar)</td></tr>
            <tr><td>Session-Regenerierung</td><td>Alle 30 Minuten automatisch</td></tr>
            <tr><td>Cookie-Flags</td><td>HttpOnly, SameSite=Lax</td></tr>
            <tr><td>Passwort-Hashing</td><td>Argon2id (Memory: 64 MB, Iterationen: 4, Threads: 3)</td></tr>
            <tr><td>CSRF-Schutz</td><td>Aktiviert, pro Formular, 8 Stunden Lebensdauer</td></tr>
            <tr><td>Multi-Faktor</td><td>TOTP (Authenticator-Apps) und WebAuthn/FIDO2 (Hardware-Schlüssel)</td></tr>
        </tbody>
    </table>

    <h3>Anmeldesicherheit (optional, im Backend aktivierbar)</h3>
    <p>Unter <code>Administration → Einstellungen → Sicherheit</code> lassen sich – standardmäßig <strong>deaktiviert</strong> – zusätzliche Schutzfunktionen für <strong>lokale</strong> Konten einschalten (SSO-/LDAP-Konten sind nicht betroffen; deren MFA übernimmt der Identity-Provider):</p>
    <table class="settings-table">
        <thead><tr><th scope="col">Funktion</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>Passwort-Richtlinie</td><td>Erzwingt eine Mindestlänge sowie optional Groß-/Kleinbuchstabe, Ziffer und Sonderzeichen beim Anlegen/Setzen lokaler Passwörter.</td></tr>
            <tr><td>Account-Lockout</td><td>Sperrt ein lokales Konto nach mehreren fehlgeschlagenen Anmeldungen vorübergehend (Schwelle, Zeitfenster und Sperrdauer konfigurierbar); ein erfolgreicher Login setzt den Zähler zurück. Ergänzt das Rate-Limiting.</td></tr>
            <tr><td>MFA-Erzwingung</td><td>Verpflichtet betroffene Nutzer (wahlweise nur Administratoren oder alle lokalen Konten) zu TOTP/WebAuthn. Ohne eingerichtete Methode werden sie nach dem Login zur Einrichtung geführt, bis eine Methode hinterlegt ist.</td></tr>
        </tbody>
    </table>

    <h3>Rate Limiting</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Endpunkt</th><th scope="col">Limit</th></tr></thead>
        <tbody>
            <tr><td>Globale Anfragen</td><td>1.000 pro 60 Sekunden</td></tr>
            <tr><td>Login</td><td>15 Versuche pro 60 Sekunden</td></tr>
            <tr><td>Passwort-Reset</td><td>10 Anfragen pro 300 Sekunden</td></tr>
            <tr><td>Uploads &amp; Exporte</td><td>50 pro 60 Sekunden</td></tr>
        </tbody>
    </table>

    <h3>HTTP-Sicherheits-Header</h3>
    <ul>
        <li><strong>X-Content-Type-Options:</strong> nosniff</li>
        <li><strong>X-Frame-Options:</strong> SAMEORIGIN</li>
        <li><strong>Referrer-Policy:</strong> strict-origin-when-cross-origin</li>
        <li><strong>Permissions-Policy:</strong> Geolocation/Mikrofon/Kamera deaktiviert</li>
        <li><strong>Strict-Transport-Security:</strong> 1 Jahr (HSTS, nur über HTTPS)</li>
        <li><strong>Content-Security-Policy:</strong> konfiguriert (maßgeblich für den XSS-Schutz)</li>
    </ul>
    <div class="info-box">Der veraltete Header <code>X-XSS-Protection</code> wird bewusst nicht mehr gesetzt – moderne Browser ignorieren ihn; maßgeblich ist die Content-Security-Policy.</div>

    <h3>Datei-Uploads</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Einstellung</th><th scope="col">Wert</th></tr></thead>
        <tbody>
            <tr><td>Maximale Dateigröße</td><td>50 MB</td></tr>
            <tr><td>Erlaubte MIME-Typen</td><td>PDF, Office-Dokumente, Bilder, Archive</td></tr>
            <tr><td>Blockierte Dateierweiterungen</td><td>Ausführbare Dateien (.exe, .php, .sh u. a.)</td></tr>
            <tr><td>Virenscan (optional)</td><td>ClamAV (<code>clamd</code>, lokal) für Uploads im Datei-Explorer &ndash; Aktivierung unter <code>/admin/files</code></td></tr>
        </tbody>
    </table>

    <h3>Audit-Log</h3>
    <div class="info-box">
        Alle schreibenden Operationen werden im Audit-Log erfasst. Der Log verwendet eine kryptografische Hash-Chain (SHA-256/HMAC), sodass nachträgliche Manipulationen erkennbar sind. Sensible Felder (Passwörter, Geheimnisse) werden automatisch maskiert. Aufbewahrungsdauer: 10 Jahre (konfigurierbar).
    </div>
    <p>Zusätzlich lassen sich unter <code>Administration → Einstellungen → Sicherheit</code> – standardmäßig <strong>deaktiviert</strong> – folgende Funktionen rund um das Audit-Log einschalten:</p>
    <table class="settings-table">
        <thead><tr><th scope="col">Funktion</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>Aufbewahrungs-Purge</td><td>Ein täglicher Task löscht Audit-Einträge, die älter als die Aufbewahrungsfrist sind, und begrenzt so das Wachstum. Die Unveränderlichkeit bleibt gewahrt; die Hash-Ketten-Verifikation der behaltenen Einträge funktioniert über einen gespeicherten Anker weiter.</td></tr>
            <tr><td>SIEM-/Log-Weiterleitung</td><td>Neue Audit-Ereignisse werden – ab einer wählbaren Mindest-Risikostufe – an ein zentrales Logsystem/SIEM gesendet (HTTPS-Webhook und/oder UDP-Syslog), z. B. zur externen Alarmierung bei fehlgeschlagenen Anmeldungen oder Rechteänderungen.</td></tr>
            <tr><td>Periodische Integritätsprüfung</td><td>Ein täglicher Task prüft eine Stichprobe der Hash-Kette; bei einem Bruch werden Administratoren benachrichtigt und ein kritisches Audit-Ereignis erzeugt (das bei aktiver SIEM-Weiterleitung mit exportiert wird).</td></tr>
        </tbody>
    </table>
    <div class="info-box">Manuelle Integritätsprüfung jederzeit unter <code>/admin/audit/verify</code>.</div>

    <h3>Audit- &amp; Compliance-Bericht (für ISO-Audit &amp; Wirtschaftsprüfung)</h3>
    <p>Unter <code>/admin/compliance</code> erzeugt ein Generator ein <strong>PDF-Nachweisdokument</strong> mit Live-Kennzahlen (Audit-Ereignisse, Hash-Ketten-Status, MFA-Quote, Aufbewahrung, Sicherheitskonfiguration sowie der Status der optionalen Schutzfunktionen – Passwort-Richtlinie, Account-Lockout, MFA-Erzwingung, Integritätsprüfung, SIEM-Weiterleitung, Aufbewahrungs-Purge) und einer Control-Matrix, die OpenNIT-Funktionen auf <strong>ISO&nbsp;27001:2022</strong>, <strong>BSI-Grundschutz</strong>, <strong>DSGVO</strong> und die <strong>GoBD</strong> (Wirtschaftsprüfung) abbildet. Drei Profile: Vollständig, ISO/BSI, Wirtschaftsprüfung&nbsp;&amp;&nbsp;GoBD.</p>
    <div class="info-box">
        <i class="fas fa-info-circle"></i>
        ISO&nbsp;27001 zertifiziert ein Managementsystem (ISMS), kein Tool – OpenNIT liefert die technischen Nachweise. Policies, Risikoanalyse, AV-Verträge und die Sicherung von OpenNIT selbst (DB&nbsp;+&nbsp;Storage inkl. getestetem Restore) bleiben Aufgabe der Organisation. Vollständiges Control-Mapping: <code>docs/COMPLIANCE.md</code>.
    </div>

    <h3>Produktions-Checkliste</h3>
    <div class="warn-box">
        <ul style="margin:0;padding-left:18px;">
            <li><code>APP_DEBUG=false</code> in der Konfiguration (Pflicht in Produktion)</li>
            <li><code>install.php</code> nach der Installation löschen</li>
            <li>Storage-Verzeichnis außerhalb des Webroots platzieren</li>
            <li>HTTPS mit gültigem Zertifikat verwenden</li>
            <li>Regelmäßige Datenbank-Backups einrichten</li>
            <li>Cron-Job für den Scheduler konfigurieren</li>
        </ul>
    </div>
</div>

<!-- ============================================================= -->
<!-- 9. Scheduler & Hintergrundaufgaben -->
<!-- ============================================================= -->
<div class="manual-section" id="cronjobs">
    <h2>11. Scheduler &amp; Hintergrundaufgaben</h2>
    <p>OpenNIT verwendet einen zentralen Scheduler (<code>bin/scheduler.php</code>), der alle Hintergrundaufgaben verwaltet. Ein einziger Cron-Eintrag genügt:</p>
    <div class="info-box">
        <code>* * * * * /usr/bin/php /pfad/zu/bin/scheduler.php &gt;&gt; /pfad/zu/storage/logs/scheduler.log 2&gt;&amp;1</code>
    </div>
    <p>Alle Tasks sind über die Admin-Oberfläche unter <code>/admin/cronjobs</code> konfigurierbar (Intervall, Aktivierung, Hintergrundausführung) und lassen sich dort auch manuell ausführen. <strong>Modul-Tasks laufen nur, wenn das zugehörige Modul aktiv ist.</strong> Standardmäßig sind alle Tasks aktiv – <em>außer</em> den beiden ausdrücklich als „aus" gekennzeichneten.</p>

    <table class="settings-table">
        <thead><tr><th scope="col">Task</th><th scope="col">Zweck</th><th scope="col">Standard</th></tr></thead>
        <tbody>
            <tr><td colspan="3"><strong>Kern &amp; modulübergreifend</strong></td></tr>
            <tr><td><code>review_notifications</code></td><td>Benachrichtigt, wenn das Review-Datum einer Seite erreicht ist</td><td>aktiv</td></tr>
            <tr><td><code>review_status_refresh</code></td><td>Berechnet den Review-Status aller Seiten neu</td><td>aktiv</td></tr>
            <tr><td><code>contract_reminders</code></td><td>Warnt vor ablaufenden Verträgen/Kündigungsfristen</td><td>aktiv</td></tr>
            <tr><td><code>ssl_check</code></td><td>Ruft TLS-Zertifikate ab und aktualisiert Ablauf/Status/Bewertung</td><td>aktiv</td></tr>
            <tr><td><code>ssl_reminder</code></td><td>Benachrichtigt vor SSL-Ablauf</td><td>aktiv</td></tr>
            <tr><td><code>todo_due_reminder</code></td><td>Erinnert vor Fälligkeit eigener To-dos</td><td>aktiv</td></tr>
            <tr><td><code>configlog_due_reminder</code></td><td>Erinnert an fällige geplante Konfigurationsänderungen</td><td>aktiv</td></tr>
            <tr><td><code>budget_threshold_alert</code></td><td>Warnt bei Erreichen der Budget-Auslastungsschwelle</td><td>aktiv</td></tr>
            <tr><td><code>loan_overdue_reminder</code></td><td>E-Mail an Ausleiher mit überschrittenem Rückgabedatum</td><td>aktiv</td></tr>
            <tr><td><code>loan_status_reconcile</code></td><td>Rechnet den Leihgeräte-Status neu (Drift-Sicherheitsnetz)</td><td>aktiv</td></tr>
            <tr><td><code>service_favicon_fetch</code></td><td>Lädt Favicons für Dienste ohne eigenes Icon nach</td><td>aktiv</td></tr>
            <tr><td><code>status_service_snapshot</code></td><td>Schreibt täglich den Dienst-Status fort (90-Tage-Uptime)</td><td>aktiv</td></tr>
            <tr><td><code>deadline_digest</code></td><td>Wöchentliche gebündelte Fristen-E-Mail</td><td><strong>aus</strong></td></tr>
            <tr><td><code>file_trash_purge</code></td><td>Löscht abgelaufene Papierkorb-Einträge des Datei-Explorers endgültig</td><td>aktiv</td></tr>
            <tr><td><code>image_optimize</code></td><td>Komprimiert hochgeladene Bilder schrittweise im Hintergrund</td><td>aktiv</td></tr>
            <tr><td><code>email_queue</code></td><td>Versendet ausstehende E-Mails aus der Warteschlange</td><td>aktiv</td></tr>
            <tr><td><code>mail_secret_expiry</code></td><td>Warnt 30 Tage vor Ablauf des M365-OAuth-Mail-Secrets</td><td>aktiv</td></tr>
            <tr><td><code>update_check</code></td><td>Prüft auf neue OpenNIT-Version und benachrichtigt Admins</td><td>aktiv</td></tr>
            <tr><td><code>dell_warranty_refresh</code></td><td>Holt gestaffelt (Tageslimit) Dell-Garantiedaten, stoppt bei Vollständigkeit</td><td>aktiv</td></tr>

            <tr><td colspan="3"><strong>Tickets</strong></td></tr>
            <tr><td><code>ticket_fetch</code></td><td>Ruft E-Mails aus dem Postfach ab und legt Tickets an</td><td>aktiv</td></tr>
            <tr><td><code>ticket_mail_queue</code></td><td>Versendet Ticket-E-Mails via SMTP</td><td>aktiv</td></tr>
            <tr><td><code>ticket_auto_close</code></td><td>Schließt gelöste Tickets nach einer Wartezeit</td><td>aktiv</td></tr>
            <tr><td><code>ticket_resume_snooze</code></td><td>Reaktiviert Tickets, deren Wiedervorlage-Datum erreicht ist</td><td>aktiv</td></tr>
            <tr><td><code>sla_escalation</code></td><td>Eskaliert SLA-überfällige Tickets ohne Erstreaktion</td><td>aktiv</td></tr>
            <tr><td><code>ticket_sentiment</code></td><td>KI-Stimmungsanalyse der ersten Ticket-Nachricht</td><td>aktiv</td></tr>
            <tr><td><code>alert_to_ticket</code></td><td>Erstellt Tickets aus Monitoring-/XDR-/CVE-/SSL-Ereignissen (je Quelle schaltbar)</td><td>aktiv</td></tr>

            <tr><td colspan="3"><strong>Verzeichnis, MDM &amp; Lizenzen</strong></td></tr>
            <tr><td><code>directory_sync</code></td><td>Importiert Personen aus Entra/LDAP</td><td>aktiv</td></tr>
            <tr><td><code>relution_sync</code></td><td>Synchronisiert Geräte aus Relution MDM</td><td>aktiv</td></tr>
            <tr><td><code>intune_sync</code></td><td>Synchronisiert Geräte aus Microsoft Intune (nur wenn konfiguriert)</td><td>aktiv</td></tr>
            <tr><td><code>entra_license_sync</code></td><td>Lädt Microsoft-365-Lizenzen aus dem Tenant</td><td>aktiv</td></tr>

            <tr><td colspan="3"><strong>Sicherheit: CVE, XDR, UniFi, Backup</strong></td></tr>
            <tr><td><code>cve_refresh</code></td><td>Holt neue CVEs von der NVD-API (und BSI CERT-Bund, falls aktiv)</td><td>aktiv</td></tr>
            <tr><td><code>cve_prune</code></td><td>Entfernt verwaiste/veraltete CVE-Einträge</td><td>aktiv</td></tr>
            <tr><td><code>xdr_sync</code></td><td>Holt XDR-Warnungen (z. B. Trend Vision One) und ordnet Assets zu</td><td>aktiv</td></tr>
            <tr><td><code>xdr_prune</code></td><td>Entfernt erledigte XDR-Warnungen</td><td>aktiv</td></tr>
            <tr><td><code>unifi_sync</code></td><td>Synchronisiert UniFi-Geräte, -Netzwerke und -Clients in den Cache</td><td>aktiv</td></tr>
            <tr><td><code>backup_sync</code></td><td>Gleicht Veeam-/Proxmox-PBS-Jobs ab und warnt bei Fehlern</td><td>aktiv</td></tr>

            <tr><td colspan="3"><strong>Audit &amp; Compliance</strong> (greifen nur, wenn in den Sicherheitseinstellungen aktiviert)</td></tr>
            <tr><td><code>audit_retention_purge</code></td><td>Löscht Audit-Einträge nach Aufbewahrungsfrist (Hash-Kette bleibt über Anker prüfbar)</td><td>aktiv*</td></tr>
            <tr><td><code>audit_siem_forward</code></td><td>Leitet neue Audit-Ereignisse an ein SIEM weiter (Webhook/Syslog)</td><td>aktiv*</td></tr>
            <tr><td><code>audit_integrity_check</code></td><td>Prüft periodisch die Hash-Kette und alarmiert bei Bruch</td><td>aktiv*</td></tr>

            <tr><td colspan="3"><strong>ISMS</strong></td></tr>
            <tr><td><code>isms_review_reminder</code></td><td>Erinnert an fällige Risiko-Wiedervorlagen</td><td>aktiv</td></tr>
            <tr><td><code>isms_cve_risk_suggestion</code></td><td>Erzeugt Risiko-Vorschläge aus relevanten CVEs</td><td>aktiv</td></tr>
            <tr><td><code>isms_register_health</code></td><td>Prüft die Qualität/Vollständigkeit des Risikoregisters</td><td>aktiv</td></tr>
            <tr><td><code>isms_soa_derive</code></td><td>Leitet den SoA-Status aus den Verknüpfungen ab</td><td>aktiv</td></tr>
            <tr><td><code>isms_monthly_report</code></td><td>Erstellt den ISMS-Monatsbericht</td><td>aktiv</td></tr>
            <tr><td><code>isms_metrics_snapshot</code></td><td>Schreibt täglich die Kennzahlen für den Risiko-Trend fort</td><td>aktiv</td></tr>

            <tr><td colspan="3"><strong>Workflows &amp; API-Auto</strong></td></tr>
            <tr><td><code>workflow_escalation</code></td><td>Erinnert/eskaliert überfällige Genehmigungen, schließt erledigte Aufgaben-Stationen</td><td>aktiv</td></tr>
            <tr><td><code>workflow_anonymization</code></td><td>Anonymisiert abgeschlossene Anträge nach Aufbewahrungsfrist (DSGVO)</td><td>aktiv</td></tr>
            <tr><td><code>workflow_webhooks</code></td><td>Stellt persönliche Workflow-Webhooks zu (n8n/Make/Slack …)</td><td>aktiv</td></tr>
            <tr><td><code>api_auto_dispatch</code></td><td>Führt fällige API-Auto-Verbindungen aus</td><td>aktiv</td></tr>

            <tr><td colspan="3"><strong>KI</strong></td></tr>
            <tr><td><code>ai_embedding_index</code></td><td>Indiziert die Dokumentation für die semantische KI-Suche</td><td><strong>aus</strong></td></tr>
        </tbody>
    </table>
    <p style="font-size:0.85rem;color:var(--gray-500);">* Die Audit-Weiterverarbeitung ist als Task aktiv, tut aber nichts, solange die jeweilige Funktion (Purge / SIEM / Integritätsprüfung) in <code>/admin/settings/security</code> nicht eingeschaltet ist.</p>

    <p>Für manuelle Ausführung oder Fehleranalyse steht die Protokollanzeige unter <code>/admin/cronjobs</code> zur Verfügung. Der Fortschritt des Dell-Garantie-Abrufs (z.&nbsp;B. „50/200 abgefragt") wird zusätzlich unter <code>/admin/settings/dell</code> angezeigt.</p>
</div>

<!-- ============================================================= -->
<!-- 10. Authentifizierung -->
<!-- ============================================================= -->
<div class="manual-section" id="auth">
    <h2>12. Authentifizierung, Benutzer &amp; Rollen</h2>

    <h3>Anmeldemethoden</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Methode</th><th scope="col">Beschreibung</th><th scope="col">Konfiguration</th></tr></thead>
        <tbody>
            <tr><td>Lokale Anmeldung</td><td>Benutzername/Passwort in der OpenNIT-Datenbank</td><td>Kann in den Einstellungen deaktiviert werden</td></tr>
            <tr><td>Azure AD / Microsoft 365</td><td>OAuth2/PKCE-Login über Microsoft-Konto</td><td><code>/admin/settings/m365</code></td></tr>
            <tr><td>Keycloak SSO</td><td>OAuth2-Login über Keycloak-Server</td><td><code>/admin/settings/keycloak</code></td></tr>
            <tr><td>LDAP / Active Directory</td><td>Direkte LDAP-Bind-Authentifizierung</td><td><code>/admin/settings/ldap-auth</code></td></tr>
        </tbody>
    </table>
    <div class="setup-box">
        <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt – Single Sign-on (SSO)</div>
        <ol>
            <li><strong>Anbieter wählen</strong> und die App im IdP registrieren: Microsoft 365 (<code>/admin/settings/m365</code>), Keycloak (<code>/admin/settings/keycloak</code>) oder AD per LDAP-Bind (<code>/admin/settings/ldap-auth</code>).</li>
            <li><strong>Zugangsdaten hinterlegen:</strong> bei M365/Keycloak Tenant-/Client-ID und Client-Secret eintragen und die <em>Redirect-URI</em> exakt im IdP setzen (sie wird auf der Einstellungsseite angezeigt); bei LDAP Host/Port/SSL, Base-DN, Bind-DN/Passwort und User-Filter – dann <strong>„Verbindung testen"</strong>.</li>
            <li><strong>Aktivieren &amp; absichern:</strong> SSO einschalten und – sobald ein Admin sich per SSO anmelden kann – den lokalen Login optional unter <code>/admin/settings</code> deaktivieren. Anmeldende ohne Konto werden je nach Konfiguration angelegt bzw. einem Verzeichnis-Eintrag zugeordnet.</li>
        </ol>
        <div class="req">Bei SSO/LDAP verwaltet der Identity-Provider Passwort und MFA – die optionalen lokalen Schutzfunktionen (Passwort-Richtlinie, Lockout, MFA-Erzwingung) wirken nur auf lokale Konten.</div>
    </div>

    <h3>Multi-Faktor-Authentifizierung</h3>
    <table class="settings-table">
        <thead><tr><th scope="col">Methode</th><th scope="col">Beschreibung</th></tr></thead>
        <tbody>
            <tr><td>TOTP</td><td>Zeitbasierte Einmalpasswörter (Google Authenticator, Authy etc.) – pro Benutzer im Profil aktivierbar</td></tr>
            <tr><td>WebAuthn / FIDO2</td><td>Hardware-Sicherheitsschlüssel/Passkeys (YubiKey u. a.) – pro Benutzer im Profil registrierbar</td></tr>
            <tr><td>Backup-Codes</td><td>Einmal-Codes als Notfall-Login, falls das zweite Gerät fehlt (jeder Code wird beim Einlösen verbraucht)</td></tr>
        </tbody>
    </table>
    <div class="usage-box">
        <div class="usage-title"><i class="fas fa-hand-pointer"></i> Bedienung – Zwei-Faktor selbst aktivieren</div>
        <ol>
            <li>Unter <code>/account/mfa</code> die Konto-Sicherheit öffnen.</li>
            <li><strong>TOTP:</strong> den QR-Code mit einer Authenticator-App scannen und zur Bestätigung einen Code eingeben – fertig.</li>
            <li><strong>Sicherheitsschlüssel/Passkey:</strong> „Schlüssel registrieren" und der Browser-Aufforderung folgen; mehrere Schlüssel sind möglich (mit Gerätename).</li>
            <li>Verliert jemand sein zweites Gerät, kann ein Administrator unter <code>/admin/users/{id}</code> die MFA zurücksetzen (nur für lokale Konten).</li>
        </ol>
        <div class="req">Optional lässt sich MFA systemweit erzwingen (nur Admins oder alle lokalen Konten) unter <code>/admin/settings/security</code>; betroffene Nutzer werden nach dem Login zur Einrichtung geführt.</div>
    </div>

    <h3>Rollen &amp; Berechtigungen (RBAC)</h3>
    <p>
        OpenNIT verwendet ein rollenbasiertes Berechtigungssystem. Administratoren erstellen Rollen und weisen ihnen
        Capabilities (Berechtigungen) zu. Benutzer erhalten eine oder mehrere Rollen.
    </p>

    <h3><i class="fas fa-bullseye"></i> Geltungsbereich: global, Themengebiet, Standort</h3>
    <p>
        Jede Rollenzuweisung (Benutzerverwaltung &rarr; Benutzer &rarr; „Rolle zuweisen") hat einen
        <strong>Geltungsbereich</strong>. Die Rolle bestimmt das <em>Was</em> (z.&nbsp;B. lesen/bearbeiten),
        der Geltungsbereich das <em>Wo</em>:
    </p>
    <ul>
        <li><strong>Global</strong> – die Rolle gilt uneingeschränkt (bisheriges Standardverhalten).</li>
        <li><strong>Themengebiet</strong> – Dokumentations-Rechte (<code>read_page</code>, <code>edit_page</code> &hellip;)
            gelten nur für das gewählte Themengebiet <em>inklusive aller Unter-Themengebiete</em>.</li>
        <li><strong>Standort</strong> – Ticket- und Inventar-Rechte (<code>view_tickets</code>/<code>manage_tickets</code>,
            <code>view_assets</code>/<code>manage_assets</code>) gelten nur für den gewählten Standort
            <em>inklusive aller Unterstandorte</em>.</li>
    </ul>
    <p>
        Es lassen sich <strong>mehrere</strong> Zuweisungen je Benutzer kombinieren, z.&nbsp;B. „Ticket-Bearbeiter für
        Standort A" und zusätzlich „Ticket-Leser für Standort Z", oder „Doku-Leser für Themengebiet A" und
        „Doku-Bearbeiter für Themengebiet C". Für „A&nbsp;lesen / C&nbsp;bearbeiten" verwendet man zwei kleine Rollen
        (z.&nbsp;B. „Leser" und „Bearbeiter") mit jeweils eigenem Geltungsbereich.
    </p>
    <div class="info-box">
        Wer für einen Standort eingeschränkt ist, sieht in Tickets und Inventar (Liste, Suche, Detail, Zähler,
        Dashboard-Kacheln, CSV-Export) ausschließlich Einträge dieses Standorts und seiner Unterstandorte.
        Tickets/Assets <strong>ohne</strong> Standort sind für standort-beschränkte Benutzer nicht sichtbar.
        Eine <em>globale</em> Zuweisung derselben Rechte hebt die Einschränkung auf (wirkt uneingeschränkt);
        globale Administratoren sehen ohnehin alles.
    </div>
    <h3>Berechtigungs-Referenz (Capabilities)</h3>
    <p>Berechtigungen folgen einem einheitlichen Namensmuster. Die konkreten Capabilities je Modul stehen in der jeweiligen Modul-Karte (Zeile „Berechtigungen"). Als Faustregel:</p>
    <table class="settings-table">
        <thead><tr><th scope="col">Muster</th><th scope="col">Bedeutung</th><th scope="col">Beispiele</th></tr></thead>
        <tbody>
            <tr><td><code>view_*</code></td><td>Lesen/Ansehen (oft inkl. Export)</td><td><code>view_tickets</code>, <code>view_assets</code>, <code>view_contracts</code></td></tr>
            <tr><td><code>manage_*</code> / <code>edit_*</code></td><td>Anlegen, Bearbeiten, Löschen im Modul</td><td><code>manage_orders</code>, <code>manage_stock</code>, <code>edit_netdiagram</code></td></tr>
            <tr><td><code>admin_*</code></td><td>Modul-Einstellungen/Administration</td><td><code>admin_tickets</code>, <code>admin_assets</code>, <code>admin_vms</code></td></tr>
        </tbody>
    </table>
    <p>Wichtige modulübergreifende (Kern-)Berechtigungen:</p>
    <table class="settings-table">
        <thead><tr><th scope="col">Capability</th><th scope="col">Gewährt</th></tr></thead>
        <tbody>
            <tr><td><code>system_admin</code></td><td>Globaler Vollzugriff (Administrator-Rolle) – sieht und darf alles, unabhängig vom Geltungsbereich</td></tr>
            <tr><td><code>manage_users</code></td><td>Benutzerverwaltung und Personenverzeichnis</td></tr>
            <tr><td><code>admin_system</code></td><td>Systemzustand, Cronjobs, Protokolle, Bild-Optimierung. <strong>Nicht</strong> das System-Update &ndash; das setzt <code>system_admin</code> voraus.</td></tr>
            <tr><td><code>admin_settings</code></td><td>Allgemeine Einstellungen, Sicherheit, Anmeldeverfahren (Keycloak, LDAP, Microsoft 365), Hersteller-Zugänge, KI-Integration</td></tr>
            <tr><td><code>admin_api</code></td><td>REST-API, Zugangsschlüssel und WordPress-Connector</td></tr>
            <tr><td><code>admin_email</code></td><td>Postausgang, E-Mail-Vorlagen und Benachrichtigungskanäle</td></tr>
            <tr><td><code>admin_docs</code></td><td>Seitenvorlagen, Tags, archivierte Seiten, Editor</td></tr>
            <tr><td><code>view_manual</code></td><td>Dieses Handbuch lesen</td></tr>
            <tr><td><code>manage_roles</code></td><td>Rollen anlegen/bearbeiten (nur mit selbst gehaltenen Rechten – Schutz vor Rechte-Eskalation)</td></tr>
            <tr><td><code>manage_all_permissions</code></td><td>Berechtigungen direkt vergeben/entziehen, Berechtigungsübersicht</td></tr>
            <tr><td><code>view_audit_log</code></td><td>Audit-Log, Berechtigungsmatrix und Statistik-Dashboard einsehen</td></tr>
            <tr><td><code>manage_topic_permissions</code></td><td>Zugriffsrechte auf Themengebiete der Dokumentation verwalten</td></tr>
        </tbody>
    </table>
    <div class="info-box">
        <strong>Rechte-Eskalation ist ausgeschlossen:</strong> Ein Benutzer kann nur Berechtigungen vergeben, die er selbst besitzt, und keine Rolle bearbeiten/zuweisen, die mehr darf als er. Globale Administratoren sind davon ausgenommen.
    </div>

    <h3>Benutzerverwaltung</h3>
    <ul>
        <li>Benutzer anlegen mit E-Mail, Anzeigename, Rolle und Authentifizierungsmethode</li>
        <li>Benutzer deaktivieren (kein Login möglich, Daten bleiben erhalten)</li>
        <li>MFA-Reset für gesperrte Benutzer durch Administratoren</li>
        <li>Unterschrift für Übergabeprotokolle im Benutzerprofil hinterlegbar</li>
        <li>Letzte Login-Zeiten einsehbar im Admin-Dashboard</li>
    </ul>
    <div class="setup-box">
        <div class="setup-title"><i class="fas fa-sliders-h"></i> Einrichtung Schritt für Schritt – Benutzer anlegen &amp; Rolle zuweisen</div>
        <ol>
            <li><strong>Rolle vorbereiten</strong> (<code>/admin/roles</code> → „Neue Rolle"): Name vergeben und die passenden Capabilities setzen (gruppiert nach Kategorie). Tipp: lieber mehrere kleine Rollen (z. B. „Ticket-Bearbeiter", „Doku-Leser") als eine große.</li>
            <li><strong>Benutzer anlegen</strong> (<code>/admin/users</code> → „Neuer Benutzer"): E-Mail, Anzeigename und – bei lokalem Konto – ein Passwort gemäß Passwort-Richtlinie. (SSO-Konten entstehen meist automatisch beim ersten Login.)</li>
            <li><strong>Rolle zuweisen</strong> (Benutzer-Detail → „Rolle zuweisen"): Rolle wählen und den <em>Geltungsbereich</em> festlegen – <strong>global</strong>, ein <strong>Themengebiet</strong> oder ein <strong>Standort</strong> (inkl. Unterebenen). Mehrere Zuweisungen pro Benutzer sind möglich.</li>
            <li><strong>Prüfen:</strong> über die Berechtigungsmatrix (<code>/admin/permissions/matrix</code>) kontrollieren, wer welche Rolle in welchem Bereich hat. Gesperrte Konten lassen sich deaktivieren statt löschen (Daten bleiben erhalten).</li>
        </ol>
    </div>
</div>

<!-- ============================================================= -->
<!-- 13. Troubleshooting & häufige Fragen -->
<!-- ============================================================= -->
<div class="manual-section" id="troubleshooting">
    <h2>13. Troubleshooting &amp; häufige Fragen</h2>
    <p class="lead">Die häufigsten Stolpersteine und ihre Lösung. Viele Probleme lassen sich über <code>/admin/system-health</code>, <code>/admin/cronjobs</code> und <code>/admin/logs</code> eingrenzen.</p>

    <div class="warn-box"><i class="fas fa-key"></i> <strong>Niemals den <code>APP_KEY</code> nachträglich ändern!</strong> Sonst sind Vault-Einträge, verschlüsselte Zugangsdaten/Lizenzschlüssel und die Audit-HMAC-Kette nicht mehr entschlüssel- bzw. prüfbar. <code>APP_KEY</code>/<code>.env</code> sichern.</div>

    <table class="settings-table">
        <thead><tr><th scope="col">Symptom</th><th scope="col">Wahrscheinliche Ursache</th><th scope="col">Lösung</th></tr></thead>
        <tbody>
            <tr><td>„Sitzung abgelaufen" beim Absenden eines Formulars</td><td>Fehlendes/abgelaufenes CSRF-Token (Feldname muss <code>_csrf</code> sein) oder Session abgelaufen</td><td>Seite neu laden und erneut anmelden; bei eigenen Formularen das versteckte <code>_csrf</code>-Feld sicherstellen. Session-/CSRF-Lebensdauer unter <code>/admin/settings/security</code>.</td></tr>
            <tr><td>Keine Erinnerungen, Tickets werden nicht abgerufen, nichts „passiert"</td><td>Der Scheduler läuft nicht</td><td>Crontab-Zeile für <code>bin/scheduler.php</code> einrichten (Kapitel&nbsp;2/11) und unter <code>/admin/cronjobs</code> prüfen, ob Tasks ausgeführt werden. Cron als Web-/App-User, nicht als root.</td></tr>
            <tr><td>E-Mails kommen nicht an</td><td>E-Mail nicht/fehlerhaft konfiguriert, Queue hängt oder OAuth-Secret abgelaufen</td><td>Unter <code>/admin/settings/email</code> Test-Mail senden; Warteschlange unter <code>/admin/email</code> prüfen/verarbeiten; bei OAuth das Mail-Secret erneuern (Warnung via <code>mail_secret_expiry</code>).</td></tr>
            <tr><td>Eingehende Mails werden nicht zu Tickets</td><td>Postfach-Anbindung oder <code>ticket_fetch</code> inaktiv</td><td>Unter <code>/admin/tickets/settings</code> „Verbindung testen"/„Abruf testen"; <code>ticket_fetch</code> in <code>/admin/cronjobs</code> aktiv? „Aktion nach Abruf" korrekt?</td></tr>
            <tr><td><code>/login</code> oder Unterseiten liefern „Not Found"</td><td>URL-Rewriting nicht aktiv (Apache <code>mod_rewrite</code>/<code>.htaccess</code> bzw. nginx-Regel fehlt)</td><td>Rewrite-Regeln aktivieren, damit alle Pfade auf <code>public/index.php</code> geleitet werden; hinter Reverse-Proxy den korrekten Pfad/Host durchreichen.</td></tr>
            <tr><td>Ein Menüpunkt/Knopf fehlt einem Benutzer</td><td>Modul inaktiv, fehlende Capability oder Standort-/Themen-Scope schränkt ein</td><td>Modul unter <code>/admin/settings</code> aktivieren; Rolle/Capability prüfen (<code>/admin/permissions/matrix</code>); Geltungsbereich der Rollenzuweisung kontrollieren.</td></tr>
            <tr><td>Brute-Force-/Rate-Limit greift hinter Proxy nicht</td><td><code>TRUSTED_PROXIES</code> nicht gesetzt – echte Client-IP wird nicht erkannt</td><td>In <code>.env</code> die Proxy-/LB-IPs in <code>TRUSTED_PROXIES</code> eintragen.</td></tr>
            <tr><td>Bestimmte Seiten zeigen „Interner Fehler" nach einem Update</td><td>Eine Datenbank-Migration ist fehlgeschlagen (fehlende Spalte/Tabelle)</td><td>Unter <code>/admin/update</code> ausstehende Migrationen ausführen bzw. wiederholen; Server-Error-Log (<code>/admin/logs</code>) prüfen.</td></tr>
            <tr><td>Benutzer durch MFA ausgesperrt</td><td>Zweites Gerät verloren, keine Backup-Codes</td><td>Administrator setzt die MFA unter <code>/admin/users/{id}</code> zurück (nur lokale Konten; SSO/LDAP-MFA liegt beim IdP).</td></tr>
            <tr><td>Vault/Lizenzschlüssel lassen sich nicht entschlüsseln</td><td><code>APP_KEY</code> wurde geändert oder fehlt</td><td>Den ursprünglichen <code>APP_KEY</code> wiederherstellen – ein geänderter Schlüssel macht bestehende Geheimnisse unlesbar.</td></tr>
            <tr><td>SSL-/Domain-Prüfung schlägt fehl</td><td>Der Server erreicht den Zielhost/Port nicht</td><td>Netzwerk/Firewall prüfen; bei internen Hosts den korrekten Port/Transport (TLS/STARTTLS) wählen.</td></tr>
            <tr><td>Unterschriften-Tablet: jeder im Netz könnte unterschreiben</td><td>Die login-freien <code>/signature</code>-Endpunkte sind ohne IP-Allowlist offen</td><td>Unter <code>/admin/settings/signature</code> die erlaubten Geräte-IPs eintragen.</td></tr>
        </tbody>
    </table>
    <div class="info-box">Weiterführend: die ausführliche Betriebs-Checkliste steht in <code>docs/SECURITY-HARDENING.md</code>; Systemzustand und Reparatur-Aktionen (Storage/Verschlüsselung) unter <code>/admin/system-health</code>.</div>
</div>

<!-- ============================================================= -->
<!-- 14. Glossar & Pflege -->
<!-- ============================================================= -->
<div class="manual-section" id="glossar">
    <h2>14. Glossar &amp; Pflege</h2>
    <p>Kurzerklärungen der wichtigsten Begriffe dieses Handbuchs.</p>
    <table class="settings-table">
        <thead><tr><th scope="col">Begriff</th><th scope="col">Bedeutung</th></tr></thead>
        <tbody>
            <tr><td>Modul</td><td>Optionaler Funktionsbereich, der unter <code>/admin/settings</code> ein-/ausgeschaltet wird (z. B. Tickets, Inventar). Inaktive Module erscheinen nicht in Navigation/Berechtigungen.</td></tr>
            <tr><td>Capability (Berechtigung)</td><td>Einzelnes Recht (z. B. <code>view_tickets</code>). Capabilities werden zu Rollen gebündelt, nicht direkt an Benutzer vergeben.</td></tr>
            <tr><td>Rolle</td><td>Benannte Sammlung von Capabilities (das „Was"). Ein Benutzer kann mehrere Rollen haben.</td></tr>
            <tr><td>Geltungsbereich (Scope)</td><td>Das „Wo" einer Rollenzuweisung: <em>global</em>, ein <em>Themengebiet</em> (Doku) oder ein <em>Standort</em> (Tickets/Inventar), jeweils inkl. Unterebenen.</td></tr>
            <tr><td>Themengebiet</td><td>Oberkategorie der Dokumentation; dient auch als Scope für Lese-/Schreibrechte auf Seiten.</td></tr>
            <tr><td>Standort</td><td>Hierarchischer Ort (Standort › Abteilung › Team); Scope für Ticket-/Inventar-Sichtbarkeit.</td></tr>
            <tr><td>SLA</td><td>Service Level Agreement – hier die Reaktionszeit-Vorgabe je Ticket-Priorität (mit Geschäftszeiten/Pause/Eskalation).</td></tr>
            <tr><td>ISMS / SoA</td><td>Informationssicherheits-Managementsystem nach ISO 27001; SoA = Statement of Applicability (Anwendbarkeitserklärung der Controls).</td></tr>
            <tr><td>Audit-Log / Hash-Chain</td><td>Unveränderliches Protokoll aller Änderungen; per kryptografischer Verkettung (HMAC-SHA256) und DB-Triggern gegen nachträgliche Änderung gesichert.</td></tr>
            <tr><td>MFA / TOTP / WebAuthn / Passkey</td><td>Zwei-Faktor-Authentifizierung: TOTP = App-Code, WebAuthn/FIDO2 = Hardware-Schlüssel/Passkey.</td></tr>
            <tr><td>SSO / IdP / OAuth / OIDC</td><td>Single Sign-on über einen Identity-Provider (Microsoft 365, Keycloak) bzw. AD-LDAP; OAuth2/OIDC sind die Protokolle.</td></tr>
            <tr><td>VAPID / Web-Push</td><td>Schlüsselpaar bzw. Verfahren für Browser-Push-Benachrichtigungen der PWA.</td></tr>
            <tr><td>Scheduler / Cron-Task</td><td>Hintergrundaufgaben, die <code>bin/scheduler.php</code> per Crontab fällig ausführt (Kapitel&nbsp;11).</td></tr>
            <tr><td>IPAM / CVE / XDR</td><td>IP-Adressverwaltung · bekannte Schwachstelle (Common Vulnerabilities and Exposures) · Extended Detection &amp; Response (Sicherheitsplattform).</td></tr>
            <tr><td>Vault</td><td>Verschlüsselter Passwort-Tresor (AES-256-GCM) für persönliche und Team-Zugangsdaten.</td></tr>
            <tr><td>Magic-Link</td><td>Persönlicher Einmal-Link (ohne Login), z. B. für externe Genehmiger in Workflows.</td></tr>
        </tbody>
    </table>

    <h3>Hinweis zur Pflege dieses Handbuchs</h3>
    <div class="info-box">
        Dieses Handbuch wird gepflegt und sollte bei funktionalen Änderungen mitgezogen werden (HTML unter <code>templates/admin/manual/index.php</code>, PDF unter <code>templates/admin/manual/pdf.php</code>). Grundsatz: <strong>nur dokumentieren, was es wirklich gibt</strong> – im Zweifel die Funktion im Code bzw. in der Oberfläche gegenprüfen, bevor sie beschrieben wird. Der Versionsverlauf der Anwendung steht im Backend unter „Changelog".
    </div>
</div>

</div><!-- /manual-content -->
</div><!-- /manual-layout -->

<button type="button" class="manual-top" id="manualTop" aria-label="Nach oben" onclick="window.scrollTo({top:0,behavior:'smooth'})">
    <i class="fas fa-arrow-up"></i>
</button>
<script>
(function () {
    var btn = document.getElementById('manualTop');
    if (!btn) return;
    var onScroll = function () {
        btn.classList.toggle('is-visible', window.scrollY > 400);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
})();

// Inhaltsverzeichnis filtern (Handbuch durchsuchen)
(function () {
    var input = document.getElementById('manualTocSearch');
    var toc = document.querySelector('.manual-toc');
    if (!input || !toc) return;
    var allItems = Array.prototype.slice.call(toc.querySelectorAll('li'));
    var noResult = document.getElementById('manualTocNoResult');

    function isLeaf(li) { return li.querySelector(':scope > ol') === null; }
    function ownLink(li) { return li.querySelector(':scope > a'); }

    input.addEventListener('input', function () {
        var q = input.value.trim().toLowerCase();
        if (!q) {
            allItems.forEach(function (li) { li.style.display = ''; });
            if (noResult) noResult.hidden = true;
            return;
        }
        var anyVisible = false;
        // 1) Blatt-Einträge nach Text filtern
        allItems.forEach(function (li) {
            if (!isLeaf(li)) return;
            var link = ownLink(li);
            var match = link && link.textContent.toLowerCase().indexOf(q) !== -1;
            li.style.display = match ? '' : 'none';
            if (match) anyVisible = true;
        });
        // 2) Gruppen-Einträge: sichtbar, wenn ein Blatt-Nachfahre sichtbar ist
        //    (oder die Gruppe selbst passt -> ganze Untergruppe zeigen)
        allItems.forEach(function (li) {
            if (isLeaf(li)) return;
            var link = ownLink(li);
            var ownMatch = link && link.textContent.toLowerCase().indexOf(q) !== -1;
            if (ownMatch) {
                li.style.display = '';
                Array.prototype.forEach.call(li.querySelectorAll('li'), function (c) { c.style.display = ''; });
                anyVisible = true;
                return;
            }
            var hasVisibleLeaf = Array.prototype.some.call(li.querySelectorAll('li'), function (c) {
                return isLeaf(c) && c.style.display !== 'none';
            });
            li.style.display = hasVisibleLeaf ? '' : 'none';
        });
        if (noResult) noResult.hidden = anyVisible;
    });

    // Esc leert die Suche
    input.addEventListener('keydown', function (e) {
        if (e.key === 'Escape') { input.value = ''; input.dispatchEvent(new Event('input')); }
    });
})();
</script>