Quelltext des aktuellen Standes
<?php $this->layout('layouts/admin', [
'page_title' => 'Self-Service-Portal',
'current_admin_page' => 'portal',
'extra_css' => ['/assets/css/portal-admin.css'],
]);
$placementLabels = ['tile' => 'Kachel', 'menu' => 'Menü', 'both' => 'Kachel + Menü'];
$overlay = (int)($portal_overlay ?? 85);
// Effektives Upload-Limit des Servers (kleinster Wert aus PHP-Konfiguration).
// Das App-Wunschlimit je Feld wird darauf gedeckelt, damit die Hinweise nicht
// mehr versprechen, als der Server annimmt.
$uploadLimit = (int)($upload_limit_bytes ?? 2 * 1024 * 1024);
$fmtMb = static function (int $bytes): string {
$mb = $bytes / (1024 * 1024);
// Ganze MB ohne Nachkommastelle, krumme Werte mit einer (deutsches Komma).
$str = (abs($mb - round($mb)) < 0.05) ? (string)(int)round($mb) : number_format($mb, 1, ',', '.');
return $str . ' MB';
};
$logoLimit = min(2 * 1024 * 1024, $uploadLimit);
$bgLimit = min(8 * 1024 * 1024, $uploadLimit);
?>
<div class="settings-page pa-page">
<nav class="settings-tabs">
<a class="settings-tab active" href="/admin/portal">
<span class="settings-tab-icon"><i class="fas fa-sliders-h"></i></span> <?= $this->te('Einstellungen') ?>
</a>
<a class="settings-tab" href="/admin/portal/kennzahlen">
<span class="settings-tab-icon"><i class="fas fa-chart-line"></i></span> <?= $this->te('Kennzahlen') ?>
</a>
<a class="settings-tab" href="/admin/portal/confirmations">
<span class="settings-tab-icon"><i class="fas fa-clipboard-check"></i></span> <?= $this->te('Geräte-Bestätigungen') ?>
</a>
</nav>
<div class="pa-status">
<span class="status-pill <?= !empty($enabled) ? 'status-pill--success' : 'status-pill--neutral' ?>">
<?= $this->te(!empty($enabled) ? 'Portal aktiv' : 'Portal deaktiviert') ?>
</span>
<span class="pa-status-item"><i class="fas fa-users"></i> <strong><?= (int)$active_persons ?></strong> <?= $this->te('aktive Personen im Verzeichnis') ?></span>
<span class="pa-status-item"><i class="fas fa-link"></i> <code>/portal</code></span>
<a href="/portal" target="_blank" rel="noopener" class="btn btn-sm btn-default pa-status-action">
<i class="fas fa-external-link-alt"></i> <?= $this->te('Portal öffnen') ?>
</a>
</div>
<form method="post" action="/admin/portal" enctype="multipart/form-data" id="pa-form">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🏠</span>
<div>
<h2><?= $this->te('Grundeinstellungen') ?></h2>
<p class="settings-card-desc"><?= $this->t('Portal für Mitarbeiter:innen aus dem Verzeichnis – Tickets, Anträge und eigene Geräte unter <code>/portal</code>.') ?></p>
</div>
</div>
<div class="settings-card-body">
<p class="pa-lead"><?= $this->t('Mitarbeiter:innen aus dem <strong>Verzeichnis</strong> (LDAP/M365-Sync) melden sich über die normale Login-Seite an und landen im Portal – <strong>ohne</strong> als Benutzer im Tool zu erscheinen. IT-Mitarbeiter behalten ihren gewohnten Zugang und erhalten zusätzlich die Portal-Ansicht.') ?></p>
<div class="settings-toggles">
<label class="settings-toggle">
<input type="checkbox" name="enabled" value="1" id="pa-enabled" <?= !empty($enabled) ? 'checked' : '' ?>>
<span class="toggle-text">
<strong><?= $this->te('Self-Service-Portal aktivieren') ?></strong>
<small><?= $this->te('Solange deaktiviert, verhält sich das System exakt wie bisher – kein Portal-Zugang, keine Änderung am Login.') ?></small>
</span>
</label>
<label class="settings-toggle">
<input type="checkbox" name="portal_magic" value="1" <?= !empty($portal_magic) ? 'checked' : '' ?>>
<span class="toggle-text">
<strong><?= $this->te('Anmelde-Link (Magic Link) anbieten') ?></strong>
<small><?= $this->te('Manuell gepflegte Verzeichnis-Personen ohne LDAP-/M365-Konto fordern sich auf der Anmeldeseite einen Einmal-Link per E-Mail an. Deaktiviert verschwindet der Hinweis von der Anmeldeseite und bereits verschickte Links funktionieren nicht mehr.') ?></small>
</span>
</label>
</div>
<div class="form-group" style="margin-top:1rem;">
<label for="ticket-max-age"><?= $this->te('Alte Tickets ausblenden') ?></label>
<div class="pa-inline-field">
<input type="number" id="ticket-max-age" name="ticket_max_age" class="form-control"
min="0" max="120" value="<?= (int)($ticket_max_age ?? 0) ?>">
<span><?= $this->te('Monate') ?></span>
</div>
<small class="form-hint"><?= $this->t('Unter „Meine Tickets" werden <strong>abgeschlossene</strong> Tickets ausgeblendet, deren Erstelldatum älter ist. Offene Tickets bleiben immer sichtbar. <strong>0 = ohne Begrenzung</strong> (alle anzeigen).') ?></small>
</div>
</div>
</section>
<div id="pa-dependent" class="pa-dependent">
<p class="pa-note pa-note--off" id="pa-off-note" hidden>
<i class="fas fa-info-circle"></i>
<?= $this->te('Das Portal ist deaktiviert. Die folgenden Einstellungen lassen sich trotzdem vorbereiten – sie greifen, sobald du das Portal aktivierst.') ?>
</p>
<?php
// Zusatzhinweis je Bereich, wenn das zugrunde liegende Modul fehlt.
$moduleReq = [
'selfservice_mod_requests' => !empty($workflows_module) ? '' : 'Benötigt das Workflows-Modul – derzeit deaktiviert.',
'selfservice_mod_approvals' => !empty($workflows_module) ? '' : 'Benötigt das Workflows-Modul – derzeit deaktiviert.',
'selfservice_mod_devices' => !empty($assets_module) ? '' : 'Benötigt das Asset-Modul – derzeit deaktiviert.',
'selfservice_mod_status' => !empty($statuspage_module) ? '' : 'Benötigt das Statusseiten-Modul – derzeit deaktiviert.',
];
$moduleDesc = [
'selfservice_mod_tickets' => 'Tickets erstellen, verfolgen und beantworten.',
'selfservice_mod_requests' => 'Im Portal freigegebene Anträge starten und deren Stand verfolgen.',
'selfservice_mod_approvals' => 'Genehmiger:innen ohne Tool-Konto entscheiden Anträge direkt im Portal, statt nur per Mail-Token-Link.',
'selfservice_mod_devices' => 'Zugewiesene Geräte einsehen, Probleme melden und – falls aktiv – bestätigen.',
'selfservice_mod_knowledge' => 'Freigegebene Anleitungen lesen und durchsuchen.',
'selfservice_mod_status' => 'Aktuellen Zustand der IT-Dienste und geplante Wartungen anzeigen; beeinträchtigte Dienste erscheinen zusätzlich auf der Startseite.',
];
?>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🧩</span>
<div>
<h2><?= $this->te('Bereiche') ?></h2>
<p class="settings-card-desc"><?= $this->te('Lege fest, welche Funktionen im Portal sichtbar sind. Ein abgeschalteter Bereich verschwindet aus Navigation und Startseite und ist auch direkt nicht erreichbar.') ?></p>
</div>
</div>
<div class="settings-card-body">
<div class="settings-toggles">
<?php foreach (($module_labels ?? []) as $modKey => $modLabel):
$field = substr($modKey, strlen('selfservice_'));
$req = $moduleReq[$modKey] ?? '';
?>
<label class="settings-toggle">
<input type="checkbox" name="<?= $this->e($field) ?>" value="1" <?= !empty($module_state[$modKey]) ? 'checked' : '' ?>>
<span class="toggle-text">
<strong><?= $this->e($modLabel) ?></strong>
<small><?= $this->e($moduleDesc[$modKey] ?? '') ?>
<?php if ($req !== ''): ?><span class="pa-mod-req"><?= $this->e($req) ?></span><?php endif; ?>
</small>
</span>
</label>
<?php endforeach; ?>
</div>
</div>
</section>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🎨</span>
<div>
<h2><?= $this->te('Erscheinungsbild') ?></h2>
<p class="settings-card-desc"><?= $this->te('Titel, Logo, Anrede, Begrüßungstext und Hintergrundbild des Portals.') ?></p>
</div>
</div>
<div class="settings-card-body">
<div class="form-row-2">
<div class="form-group">
<label for="portal-title"><?= $this->te('Portal-Titel') ?></label>
<input type="text" id="portal-title" name="portal_title" class="form-control" autocomplete="off"
maxlength="100" value="<?= $this->e($portal_title) ?>" placeholder="<?= $this->te('z. B. IT-Serviceportal Musterfirma') ?>">
<small class="form-hint"><?= $this->te('Kopfleiste und Browser-Tab. Leer =') ?> „<?= $this->e(($app_name ?? 'OpenNIT') . ' · Self-Service') ?>“.</small>
</div>
<div class="form-group">
<label for="portal-address"><?= $this->te('Anrede') ?></label>
<select id="portal-address" name="portal_address" class="form-control">
<option value="du" <?= ($portal_address ?? 'du') !== 'sie' ? 'selected' : '' ?>><?= $this->te('Du (informell)') ?></option>
<option value="sie" <?= ($portal_address ?? 'du') === 'sie' ? 'selected' : '' ?>><?= $this->te('Sie (formell)') ?></option>
</select>
<small class="form-hint"><?= $this->te('Bestimmt, wie das Portal die Mitarbeiter:innen anspricht.') ?></small>
</div>
</div>
<div class="form-group">
<label for="portal-welcome"><?= $this->te('Begrüßungstext auf der Startseite') ?></label>
<textarea id="portal-welcome" name="portal_welcome" class="form-control" rows="3" maxlength="500"
placeholder="<?= $this->te('Willkommen im Self-Service-Portal. Hier können Sie Anliegen an die IT richten und deren Stand verfolgen.') ?>"><?= $this->e($portal_welcome ?? '') ?></textarea>
<small class="form-hint"><?= $this->te('Leer = Standardtext passend zur gewählten Anrede. Zeilenumbrüche bleiben erhalten.') ?></small>
</div>
<div class="form-group">
<label for="portal-footer"><?= $this->te('Fußzeilen-Text') ?></label>
<textarea id="portal-footer" name="portal_footer" class="form-control" rows="3" maxlength="1000"
placeholder="<?= $this->e(($app_name ?? 'OpenNIT') . ' · Self-Service-Portal') ?>"><?= $this->e($portal_footer ?? '') ?></textarea>
<small class="form-hint"><?= $this->te('Erscheint zentriert in der Fußzeile des Portals. Leer =') ?> „<?= $this->e(($app_name ?? 'OpenNIT') . ' · Self-Service-Portal') ?>“.
<?= $this->t('Einfaches HTML ist erlaubt – z. B. ein Link: <code>Bei Fragen erreichen Sie die IT unter <a href="tel:08003301000">0800 / 330 1000</a></code>. Zulässig sind nur <code>a</code>, <code>br</code>, <code>strong</code>, <code>b</code>, <code>em</code>, <code>i</code>, <code>span</code>, <code>small</code>; alles andere wird entfernt.') ?></small>
</div>
<div class="form-group">
<label for="portal-logo"><?= $this->te('Portal-Logo') ?></label>
<div class="pa-media">
<div class="pa-media-preview pa-media-preview--logo">
<?php if (!empty($portal_logo)): ?>
<img src="<?= $this->e($portal_logo) ?>" alt="<?= $this->te('Portal-Logo') ?>">
<?php else: ?>
<span class="pa-media-empty"><i class="fas fa-image"></i> <?= $this->te('App-Logo') ?></span>
<?php endif; ?>
</div>
<div class="pa-media-fields">
<input type="file" id="portal-logo" name="portal_logo" class="form-control form-control-sm"
accept="image/jpeg,image/png,image/gif,image/webp"
data-max-bytes="<?= $logoLimit ?>" data-max-label="<?= $this->e($fmtMb($logoLimit)) ?>">
<small class="form-hint"><?= $this->te('JPEG, PNG, GIF oder WebP, max.') ?> <?= $this->e($fmtMb($logoLimit)) ?>. <?= $this->te('Leer = App-Logo wird verwendet.') ?></small>
<?php if (!empty($portal_logo)): ?>
<label class="settings-toggle pa-media-remove">
<input type="checkbox" name="portal_logo_remove" value="1">
<span class="toggle-text"><strong><?= $this->te('Logo entfernen') ?></strong> <small><?= $this->te('Zurück zum App-Logo.') ?></small></span>
</label>
<?php endif; ?>
</div>
</div>
</div>
<div class="form-group">
<label for="portal-background"><?= $this->te('Hintergrundbild') ?></label>
<div class="pa-media">
<?php $bgPreviewUrl = !empty($portal_background) ? '/portal/branding-background?v=' . substr(md5((string)$portal_background), 0, 10) : ''; ?>
<div class="pa-media-preview pa-media-preview--bg" id="pa-bg-preview"
<?= $bgPreviewUrl !== '' ? 'style="--pa-bg:url(\'' . $this->e($bgPreviewUrl) . '\');--pa-overlay:' . number_format($overlay / 100, 2, '.', '') . '"' : '' ?>>
<?php if (empty($portal_background)): ?>
<span class="pa-media-empty"><i class="fas fa-image"></i> <?= $this->te('Kein Bild') ?></span>
<?php else: ?>
<span class="pa-media-caption"><?= $this->te('Vorschau') ?></span>
<?php endif; ?>
</div>
<div class="pa-media-fields">
<input type="file" id="portal-background" name="portal_background" class="form-control form-control-sm"
accept="image/jpeg,image/png,image/gif,image/webp"
data-max-bytes="<?= $bgLimit ?>" data-max-label="<?= $this->e($fmtMb($bgLimit)) ?>">
<small class="form-hint"><?= $this->te('JPEG, PNG, GIF oder WebP, max.') ?> <?= $this->e($fmtMb($bgLimit)) ?>. <?= $this->te('Das Bild füllt die Fläche aus und bleibt beim Scrollen stehen.') ?>
<?php if ($bgLimit < 8 * 1024 * 1024): ?><br><span class="pa-mod-req"><?= $this->t('Server-Limit: größere Dateien werden abgewiesen. Für mehr <code>upload_max_filesize</code>/<code>post_max_size</code> in der PHP-Konfiguration anheben.') ?></span><?php endif; ?>
</small>
<?php if (!empty($portal_background)): ?>
<label class="settings-toggle pa-media-remove">
<input type="checkbox" name="portal_background_remove" value="1">
<span class="toggle-text"><strong><?= $this->te('Hintergrundbild entfernen') ?></strong> <small><?= $this->te('Zurück zum einfarbigen Hintergrund.') ?></small></span>
</label>
<?php endif; ?>
</div>
</div>
</div>
<div class="form-group">
<label for="portal-overlay"><?= $this->te('Deckkraft des weißen Overlays') ?></label>
<div class="pa-slider">
<input type="range" id="portal-overlay" name="portal_overlay" min="0" max="100" step="5" value="<?= $overlay ?>">
<output class="pa-slider-value" id="portal-overlay-value"><?= $overlay ?> %</output>
</div>
<small class="form-hint"><?= $this->te('Weiße Schicht über dem Hintergrundbild. Höhere Werte machen das Bild dezenter und halten Texte gut lesbar; 0 % zeigt das Bild unverändert.') ?></small>
</div>
<div class="form-group">
<label for="portal-text-color"><?= $this->te('Textfarbe außerhalb der Karten') ?></label>
<?php $paTextColor = trim((string)($portal_text_color ?? '')); ?>
<div class="pa-inline-field">
<input type="color" id="portal-text-color" name="portal_text_color"
value="<?= $paTextColor !== '' ? $this->e($paTextColor) : '#0f172a' ?>"
<?= $paTextColor === '' ? 'disabled' : '' ?>>
<label class="settings-toggle" style="margin:0;">
<input type="checkbox" id="portal-text-color-on" name="portal_text_color_on" value="1" <?= $paTextColor !== '' ? 'checked' : '' ?>>
<span class="toggle-text"><strong><?= $this->te('Eigene Farbe verwenden') ?></strong></span>
</label>
</div>
<small class="form-hint"><?= $this->te('Betrifft Texte, die direkt auf dem Hintergrund liegen (Begrüßung, Seitentitel, Zurück-Link) – nicht die Karten. Nützlich bei dunklem Hintergrundbild, damit diese Texte lesbar bleiben. Ohne Haken gilt die Standardfarbe.') ?></small>
</div>
</div>
</section>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">👥</span>
<div>
<h2><?= $this->te('Portal-Zugriff') ?></h2>
<p class="settings-card-desc"><?= $this->te('Optional auf eine Gruppe beschränken. Ohne Gruppe haben alle aktiven Verzeichnis-Personen Zugriff.') ?></p>
</div>
</div>
<div class="settings-card-body">
<div class="form-row-2">
<div class="form-group">
<label for="access-group-entra"><?= $this->te('M365 / Entra ID – Gruppen-Objekt-ID') ?></label>
<input type="text" id="access-group-entra" name="access_group_entra" class="form-control" autocomplete="off"
value="<?= $this->e($access_group_entra) ?>" placeholder="<?= $this->te('GUID der Sicherheitsgruppe') ?>">
<small class="form-hint"><?= $this->t('Voraussetzung: In der Azure-App-Registrierung muss der <strong>Groups-Claim</strong> im ID-Token aktiviert sein (Token-Konfiguration → Gruppenanspruch „Sicherheitsgruppen“). Fehlt der Claim, schlägt die Gruppen-Prüfung bewusst fehl (fail-closed).') ?></small>
</div>
<div class="form-group">
<label for="access-group-ldap"><?= $this->te('LDAP / AD – Gruppen-DN') ?></label>
<input type="text" id="access-group-ldap" name="access_group_ldap" class="form-control" autocomplete="off"
value="<?= $this->e($access_group_ldap) ?>" placeholder="<?= $this->te('CN=Portal-Nutzer,OU=Gruppen,DC=firma,DC=de') ?>">
<small class="form-hint"><?= $this->t('Vergleich gegen das <code>memberOf</code>-Attribut des angemeldeten Kontos (direkte Mitgliedschaft).') ?></small>
</div>
</div>
</div>
</section>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🔐</span>
<div>
<h2><?= $this->te('Login-Weiche') ?></h2>
<p class="settings-card-desc"><?= $this->te('Steuert, ob ein SSO-Login (Azure/LDAP) einen IT-Benutzer anlegt oder in die Portal-Prüfung läuft.') ?></p>
</div>
</div>
<div class="settings-card-body">
<p class="pa-note">
<i class="fas fa-shield-alt"></i>
<?= $this->t('<strong>Bestehende, aktive Benutzer melden sich immer an</strong> – unabhängig von dieser Einstellung kann niemand ausgesperrt werden. Die Weiche betrifft nur Logins, zu denen noch <em>kein</em> Benutzer existiert.') ?>
</p>
<div class="form-group">
<label for="provisioning"><?= $this->te('Neue SSO-Logins') ?></label>
<select id="provisioning" name="provisioning" class="form-control">
<option value="all" <?= $provisioning === 'all' ? 'selected' : '' ?>><?= $this->te('Jeder Login wird IT-Benutzer (bisheriges Verhalten)') ?></option>
<option value="existing" <?= $provisioning === 'existing' ? 'selected' : '' ?>><?= $this->te('Nur bestehende Benutzer – neue Logins gehen ins Portal (empfohlen bei aktivem Portal)') ?></option>
<option value="group" <?= $provisioning === 'group' ? 'selected' : '' ?>><?= $this->te('Per Gruppe – Mitglieder der IT-Gruppe werden Benutzer, alle anderen gehen ins Portal') ?></option>
</select>
<small class="form-hint"><?= $this->t('Achtung bei „Jeder Login wird IT-Benutzer“ und aktivem Portal: Mitarbeiter:innen würden beim ersten SSO-Login weiterhin als Benutzer angelegt und landen <em>nicht</em> im Portal. Für den Portal-Betrieb daher „Nur bestehende Benutzer“ oder „Per Gruppe“ wählen.') ?></small>
</div>
<div class="form-row-2">
<div class="form-group">
<label for="user-group-entra"><?= $this->te('IT-Gruppe M365 / Entra ID – Gruppen-Objekt-ID') ?></label>
<input type="text" id="user-group-entra" name="user_group_entra" class="form-control" autocomplete="off"
value="<?= $this->e($user_group_entra) ?>" placeholder="<?= $this->te('GUID der IT-Mitarbeiter-Gruppe') ?>">
<small class="form-hint"><?= $this->te('Nur bei „Per Gruppe“ wirksam.') ?></small>
</div>
<div class="form-group">
<label for="user-group-ldap"><?= $this->te('IT-Gruppe LDAP / AD – Gruppen-DN') ?></label>
<input type="text" id="user-group-ldap" name="user_group_ldap" class="form-control" autocomplete="off"
value="<?= $this->e($user_group_ldap) ?>" placeholder="<?= $this->te('CN=IT-Team,OU=Gruppen,DC=firma,DC=de') ?>">
<small class="form-hint"><?= $this->te('Nur bei „Per Gruppe“ wirksam.') ?></small>
</div>
</div>
</div>
</section>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📋</span>
<div>
<h2><?= $this->te('Geräte-Selbstbestätigung') ?></h2>
<p class="settings-card-desc"><?= $this->te('Mitarbeiter:innen bestätigen im Portal periodisch, dass ihre Geräte noch vorhanden sind – die Inventur läuft damit nebenbei mit.') ?></p>
</div>
</div>
<div class="settings-card-body">
<?php if (empty($assets_module)): ?>
<p class="pa-note pa-note--warn">
<i class="fas fa-exclamation-triangle"></i>
<?= $this->te('Das Asset-Modul ist derzeit deaktiviert – ohne Assets gibt es nichts zu bestätigen.') ?>
</p>
<?php endif; ?>
<div class="form-group">
<label class="settings-toggle">
<input type="checkbox" name="confirm_enabled" value="1" id="pa-confirm-enabled" <?= !empty($confirm_enabled) ? 'checked' : '' ?>>
<span class="toggle-text">
<strong><?= $this->te('Geräte-Selbstbestätigung aktivieren') ?></strong>
<small><?= $this->te('Fällige Geräte erscheinen unter „Meine Geräte“ mit der Rückfrage „Gerät noch vorhanden?“. Deaktiviert ist im Portal nichts davon sichtbar.') ?></small>
</span>
</label>
</div>
<div id="pa-confirm-fields" class="pa-dependent">
<div class="form-group">
<label for="confirm-interval"><?= $this->te('Bestätigungs-Intervall') ?></label>
<div class="pa-inline-field">
<input type="number" id="confirm-interval" name="confirm_interval" class="form-control"
min="1" max="60" value="<?= (int)($confirm_interval ?? 12) ?>">
<span><?= $this->te('Monate') ?></span>
</div>
<small class="form-hint"><?= $this->te('Nach dieser Zeit wird ein Gerät erneut zur Bestätigung fällig. Standard: 12 Monate.') ?></small>
</div>
<div class="form-group">
<label class="settings-toggle">
<input type="checkbox" name="confirm_mail" value="1" <?= !empty($confirm_mail) ? 'checked' : '' ?>>
<span class="toggle-text">
<strong><?= $this->te('Erinnerungs-Mail versenden') ?></strong>
<small><?= $this->te('Ein Cron-Task prüft täglich, ob eine neue Erinnerungswelle fällig ist, und schreibt Personen mit offenen Geräten an.') ?>
<?php if (!empty($confirm_mail_last)): ?><?= $this->te('Zuletzt versendet am') ?> <?= $this->e(date('d.m.Y', strtotime((string)$confirm_mail_last))) ?>.<?php endif; ?></small>
</span>
</label>
</div>
<div class="pa-stats">
<span class="pa-stat"><strong><?= (int)($confirm_stats['confirmed'] ?? 0) ?></strong> <?= $this->te('bestätigt') ?></span>
<span class="pa-stat"><strong><?= (int)($confirm_stats['open'] ?? 0) ?></strong> <?= $this->te('offen') ?></span>
<span class="pa-stat"><strong><?= (int)($confirm_stats['missing'] ?? 0) ?></strong> <?= $this->te('nicht mehr vorhanden') ?></span>
<a href="/admin/portal/confirmations" class="pa-stat-link"><?= $this->te('Rückläufer-Report öffnen') ?> <i class="fas fa-arrow-right"></i></a>
</div>
</div>
</div>
</section>
</div>
<div class="pa-actions">
<button type="submit" class="btn btn-primary"><i class="fas fa-save"></i> <?= $this->te('Einstellungen speichern') ?></button>
<span class="pa-actions-hint"><?= $this->te('Gilt für alle Abschnitte auf dieser Seite – eigene Links werden separat gespeichert.') ?></span>
</div>
</form>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🔗</span>
<div>
<h2><?= $this->te('Eigene Links') ?></h2>
<p class="settings-card-desc"><?= $this->te('Verweise auf andere Programme (z. B. Intranet) – wahlweise als Kachel auf der Startseite oder als Eintrag in der Kopfleiste.') ?></p>
</div>
</div>
<div class="settings-card-body">
<?php if (!empty($links)): ?>
<div class="table-wrapper pa-links-table">
<table class="data-table">
<thead>
<tr>
<th scope="col"><?= $this->te('Titel') ?></th>
<th scope="col"><?= $this->te('Adresse') ?></th>
<th scope="col"><?= $this->te('Anzeige') ?></th>
<th scope="col"><?= $this->te('Standort') ?></th>
<th scope="col" class="pa-col-num"><?= $this->te('Reihenfolge') ?></th>
<th scope="col"><?= $this->te('Status') ?></th>
<th scope="col" class="pa-col-actions"></th>
</tr>
</thead>
<tbody>
<?php if (!empty($global_content_hint)): ?>
<div class="alert alert-info" role="status">
<strong><?= $this->te('Hinweis zu standortbezogenen Inhalten:') ?></strong>
<?= $this->te('Es sind') ?> <?= (int)$global_content_hint['domains'] ?> <?= $this->te('E-Mail-Domain(s) Standorten zugeordnet, aber') ?> <?= (int)$global_content_hint['links'] ?> <?= $this->te('Link(s) und') ?>
<?= (int)$global_content_hint['announcements'] ?> <?= $this->te('Ankündigung(en) stehen auf „Alle Standorte“ – sie erscheinen damit bei allen Portal-Nutzern.') ?>
</div>
<?php endif; ?>
<?php
// Standort-Namen für die Anzeige (leer = alle Standorte)
$locNames = [];
foreach (($locations ?? []) as $lc) { $locNames[(int)$lc['id']] = (string)$lc['name']; }
$locLabel = function ($id) use ($locNames) {
$id = (int)$id;
return $id > 0 ? ($locNames[$id] ?? ('#' . $id)) : null;
};
?>
<?php foreach ($links as $l): ?>
<tr>
<td>
<span class="pa-link-title">
<span class="pa-link-icon"><i class="fas <?= $this->e($l['icon']) ?>"></i></span>
<span>
<strong><?= $this->e($l['title']) ?></strong>
<?php if (!empty($l['description'])): ?><br><span class="text-muted"><?= $this->e($l['description']) ?></span><?php endif; ?>
</span>
</span>
</td>
<td><span class="text-muted pa-link-url"><?= $this->e($l['url']) ?></span>
<?php if ((int)$l['new_tab'] === 1): ?><i class="fas fa-external-link-alt pa-link-tab" title="<?= $this->te('Öffnet in neuem Tab') ?>"></i><?php endif; ?>
</td>
<td><?= $this->e($placementLabels[$l['placement']] ?? $l['placement']) ?></td>
<td>
<?php $ln = $locLabel($l['location_id'] ?? 0); ?>
<?php if ($ln !== null): ?>
<span class="badge badge-secondary"><?= $this->e($ln) ?></span>
<?php else: ?>
<span class="text-muted"><?= $this->te('Alle') ?></span>
<?php endif; ?>
</td>
<td class="pa-col-num"><?= (int)$l['sort_order'] ?></td>
<td>
<?php if ((int)$l['is_active'] === 1): ?>
<span class="status-pill status-pill--success"><?= $this->te('Aktiv') ?></span>
<?php else: ?>
<span class="status-pill status-pill--neutral"><?= $this->te('Inaktiv') ?></span>
<?php endif; ?>
</td>
<td class="pa-col-actions">
<button type="button" class="btn btn-xs btn-default"
onclick='portalLinkEdit(<?= htmlspecialchars(json_encode($l, JSON_UNESCAPED_UNICODE), ENT_QUOTES) ?>)'
title="<?= $this->te('Bearbeiten') ?>"><i class="fas fa-pencil-alt"></i></button>
<form method="post" action="/admin/portal/links/<?= (int)$l['id'] ?>/delete"
onsubmit="return confirm('Link wirklich entfernen?');">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<button type="submit" class="btn btn-xs btn-outline-danger" title="<?= $this->te('Entfernen') ?>"><i class="fas fa-times"></i></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="empty-state">
<i class="fas fa-link"></i>
<p><?= $this->te('Noch keine eigenen Links angelegt.') ?></p>
</div>
<?php endif; ?>
<form method="post" action="/admin/portal/links" id="portal-link-form" class="pa-link-form">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<input type="hidden" name="id" id="pl-id" value="">
<h3 class="pa-subhead" id="pl-heading"><?= $this->te('Neuen Link anlegen') ?></h3>
<div class="form-row-2">
<div class="form-group">
<label for="pl-title"><?= $this->te('Titel') ?> <span class="text-danger">*</span></label>
<input type="text" id="pl-title" name="title" class="form-control" maxlength="100" required placeholder="<?= $this->te('z. B. Intranet') ?>">
</div>
<div class="form-group">
<label for="pl-url"><?= $this->te('Adresse') ?> <span class="text-danger">*</span></label>
<input type="text" id="pl-url" name="url" class="form-control" maxlength="500" required placeholder="https://intranet.firma.de">
<small class="form-hint"><?= $this->t('Vollständige Adresse mit <code>https://</code> oder ein interner Pfad wie <code>/portal/hilfe</code>.') ?></small>
</div>
</div>
<div class="form-group">
<label for="pl-description"><?= $this->te('Kurzbeschreibung (für die Kachel)') ?></label>
<input type="text" id="pl-description" name="description" class="form-control" maxlength="255" placeholder="<?= $this->te('Nachrichten, Formulare und Termine') ?>">
</div>
<div class="form-row-2">
<div class="form-group">
<label for="pl-placement"><?= $this->te('Anzeige') ?></label>
<select id="pl-placement" name="placement" class="form-control">
<option value="tile"><?= $this->te('Kachel auf der Startseite') ?></option>
<option value="menu"><?= $this->te('Eintrag in der Kopfleiste') ?></option>
<option value="both"><?= $this->te('Beides') ?></option>
</select>
</div>
<div class="form-group">
<label for="pl-sort"><?= $this->te('Reihenfolge') ?></label>
<input type="number" id="pl-sort" name="sort_order" class="form-control" value="0">
<small class="form-hint"><?= $this->te('Kleinere Werte stehen weiter vorn.') ?></small>
</div>
</div>
<div class="form-row-2">
<div class="form-group">
<label for="pl-location"><?= $this->te('Standort') ?></label>
<select id="pl-location" name="location_id" class="form-control">
<?php if ($can_manage_global ?? true): ?>
<option value=""><?= $this->te('Alle Standorte') ?></option>
<?php endif; ?>
<?php foreach (($locations ?? []) as $loc): ?>
<option value="<?= (int)$loc['id'] ?>"><?= str_repeat(' ', (int)($loc['_depth'] ?? 0)) . $this->e($loc['name']) ?></option>
<?php endforeach; ?>
</select>
<small class="form-hint"><?= $this->te('Leer = für alle sichtbar. Ein Oberstandort gilt auch für dessen Unterstandorte.') ?></small>
</div>
<div class="form-group"></div>
</div>
<div class="form-group">
<label for="pl-icon-search"><?= $this->te('Symbol') ?></label>
<input type="hidden" id="pl-icon" name="icon" value="fa-link">
<div class="pli-picker">
<div class="pli-current">
<span class="pli-preview"><i class="fas fa-link" id="pl-icon-preview"></i></span>
<code id="pl-icon-name">fa-link</code>
</div>
<input type="search" id="pl-icon-search" class="form-control form-control-sm"
placeholder="<?= $this->te('Symbol suchen (z. B. globe, news, clock)…') ?>" autocomplete="off"
aria-label="<?= $this->te('Symbole durchsuchen') ?>">
<div class="pli-grid" id="pl-icon-grid">
<?php foreach (($icon_choices ?? []) as $ic): ?>
<button type="button" class="pli-item<?= $ic === 'fa-link' ? ' active' : '' ?>"
data-icon="<?= $this->e($ic) ?>" title="<?= $this->e($ic) ?>">
<i class="fas <?= $this->e($ic) ?>"></i>
</button>
<?php endforeach; ?>
</div>
</div>
<small class="form-hint"><?= $this->te('Erscheint auf der Kachel und im Menü des Portals.') ?></small>
</div>
<div class="settings-toggles pa-link-toggles">
<label class="settings-toggle">
<input type="checkbox" name="new_tab" id="pl-newtab" value="1" checked>
<span class="toggle-text"><strong><?= $this->te('In neuem Tab öffnen') ?></strong></span>
</label>
<label class="settings-toggle">
<input type="checkbox" name="is_active" id="pl-active" value="1" checked>
<span class="toggle-text"><strong><?= $this->te('Aktiv') ?></strong> <small><?= $this->te('Nur aktive Links erscheinen im Portal.') ?></small></span>
</label>
</div>
<div class="pa-form-actions">
<button type="submit" class="btn btn-primary"><i class="fas fa-save"></i> <?= $this->te('Link speichern') ?></button>
<button type="button" class="btn btn-default" id="pl-reset" hidden><?= $this->te('Abbrechen') ?></button>
</div>
</form>
</div>
</section>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📣</span>
<div>
<h2><?= $this->te('Ankündigungen') ?></h2>
<p class="settings-card-desc"><?= $this->te('Allgemeine Hinweise für Mitarbeiter:innen (z. B. „Neue Software verfügbar"). Erscheinen im aktiven Zeitraum als Karte auf der Portal-Startseite – getrennt von Störungen und Wartungen.') ?></p>
</div>
</div>
<div class="settings-card-body">
<?php
$annLevels = ['info' => 'Info', 'success' => 'Erfolg', 'warning' => 'Warnung'];
$fmtLocal = static fn(?string $v): string => $v ? date('d.m.Y H:i', strtotime($v)) : '';
?>
<?php if (!empty($announcements)): ?>
<div class="table-wrapper pa-links-table">
<table class="data-table">
<thead>
<tr>
<th scope="col"><?= $this->te('Titel') ?></th>
<th scope="col"><?= $this->te('Stufe') ?></th>
<th scope="col"><?= $this->te('Standort') ?></th>
<th scope="col"><?= $this->te('Zeitraum') ?></th>
<th scope="col"><?= $this->te('Status') ?></th>
<th scope="col" class="pa-col-actions"></th>
</tr>
</thead>
<tbody>
<?php foreach ($announcements as $a): ?>
<tr>
<td>
<strong><?= $this->e($a['title']) ?></strong>
<?php if (!empty($a['body'])): ?><br><span class="text-muted"><?= $this->e(mb_strimwidth((string)$a['body'], 0, 90, '…')) ?></span><?php endif; ?>
</td>
<td><?= $this->e($annLevels[$a['level']] ?? $a['level']) ?></td>
<td>
<?php $an = $locLabel($a['location_id'] ?? 0); ?>
<?php if ($an !== null): ?>
<span class="badge badge-secondary"><?= $this->e($an) ?></span>
<?php else: ?>
<span class="text-muted"><?= $this->te('Alle') ?></span>
<?php endif; ?>
</td>
<td class="text-muted">
<?php if (empty($a['starts_at']) && empty($a['ends_at'])): ?>—<?php else: ?>
<?= $this->e($fmtLocal($a['starts_at'] ?? null) ?: 'sofort') ?> – <?= $this->e($fmtLocal($a['ends_at'] ?? null) ?: 'unbefristet') ?>
<?php endif; ?>
</td>
<td>
<?php if ((int)$a['is_active'] === 1): ?>
<span class="status-pill status-pill--success"><?= $this->te('Aktiv') ?></span>
<?php else: ?>
<span class="status-pill status-pill--neutral"><?= $this->te('Inaktiv') ?></span>
<?php endif; ?>
</td>
<td class="pa-col-actions">
<button type="button" class="btn btn-xs btn-default"
onclick='portalAnnEdit(<?= htmlspecialchars(json_encode($a, JSON_UNESCAPED_UNICODE), ENT_QUOTES) ?>)'
title="<?= $this->te('Bearbeiten') ?>"><i class="fas fa-pencil-alt"></i></button>
<form method="post" action="/admin/portal/announcements/<?= (int)$a['id'] ?>/delete"
onsubmit="return confirm('Ankündigung wirklich entfernen?');">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<button type="submit" class="btn btn-xs btn-outline-danger" title="<?= $this->te('Entfernen') ?>"><i class="fas fa-times"></i></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="empty-state">
<i class="fas fa-bullhorn"></i>
<p><?= $this->te('Noch keine Ankündigungen angelegt.') ?></p>
</div>
<?php endif; ?>
<form method="post" action="/admin/portal/announcements" id="portal-ann-form" class="pa-link-form">
<input type="hidden" name="_csrf" value="<?= $this->e($this->csrf()) ?>">
<input type="hidden" name="id" id="pa-ann-id" value="">
<h3 class="pa-subhead" id="pa-ann-heading"><?= $this->te('Neue Ankündigung') ?></h3>
<div class="form-row-2">
<div class="form-group">
<label for="pa-ann-title"><?= $this->te('Titel') ?> <span class="text-danger">*</span></label>
<input type="text" id="pa-ann-title" name="title" class="form-control" maxlength="200" required placeholder="<?= $this->te('z. B. Wartungsfreies Wochenende') ?>">
</div>
<div class="form-group">
<label for="pa-ann-level"><?= $this->te('Stufe') ?></label>
<select id="pa-ann-level" name="level" class="form-control">
<option value="info"><?= $this->te('Info (blau)') ?></option>
<option value="success"><?= $this->te('Erfolg (grün)') ?></option>
<option value="warning"><?= $this->te('Warnung (gelb)') ?></option>
</select>
</div>
</div>
<div class="form-group">
<label for="pa-ann-body"><?= $this->te('Text') ?></label>
<textarea id="pa-ann-body" name="body" class="form-control" rows="3" maxlength="5000" placeholder="<?= $this->te('Kurzer Hinweis für Mitarbeiter:innen…') ?>"></textarea>
</div>
<div class="form-row-2">
<div class="form-group">
<label for="pa-ann-start"><?= $this->te('Sichtbar ab') ?></label>
<input type="datetime-local" id="pa-ann-start" name="starts_at" class="form-control">
<small class="form-hint"><?= $this->te('Leer = sofort.') ?></small>
</div>
<div class="form-group">
<label for="pa-ann-end"><?= $this->te('Sichtbar bis') ?></label>
<input type="datetime-local" id="pa-ann-end" name="ends_at" class="form-control">
<small class="form-hint"><?= $this->te('Leer = unbefristet.') ?></small>
</div>
</div>
<div class="form-row-2">
<div class="form-group">
<label for="pa-ann-location"><?= $this->te('Standort') ?></label>
<select id="pa-ann-location" name="location_id" class="form-control">
<?php if ($can_manage_global ?? true): ?>
<option value=""><?= $this->te('Alle Standorte') ?></option>
<?php endif; ?>
<?php foreach (($locations ?? []) as $loc): ?>
<option value="<?= (int)$loc['id'] ?>"><?= str_repeat(' ', (int)($loc['_depth'] ?? 0)) . $this->e($loc['name']) ?></option>
<?php endforeach; ?>
</select>
<small class="form-hint"><?= $this->te('Leer = für alle sichtbar. Ein Oberstandort gilt auch für dessen Unterstandorte.') ?></small>
</div>
<div class="form-group"></div>
</div>
<div class="settings-toggles pa-link-toggles">
<label class="settings-toggle">
<input type="checkbox" name="is_active" id="pa-ann-active" value="1" checked>
<span class="toggle-text"><strong><?= $this->te('Aktiv') ?></strong> <small><?= $this->te('Nur aktive Ankündigungen erscheinen im Portal.') ?></small></span>
</label>
</div>
<div class="pa-form-actions">
<button type="submit" class="btn btn-primary"><i class="fas fa-save"></i> <?= $this->te('Ankündigung speichern') ?></button>
<button type="button" class="btn btn-default" id="pa-ann-reset" hidden><?= $this->te('Abbrechen') ?></button>
</div>
</form>
</div>
</section>
</div>
<script>
// Ankündigung bearbeiten: bestehende Werte ins Formular laden; Abbrechen setzt zurück.
// datetime-local erwartet 'YYYY-MM-DDTHH:MM' – aus dem DB-DATETIME ableiten.
function portalAnnToLocal(v) {
if (!v) { return ''; }
return String(v).replace(' ', 'T').slice(0, 16);
}
function portalAnnEdit(a) {
document.getElementById('pa-ann-id').value = a.id;
document.getElementById('pa-ann-title').value = a.title || '';
document.getElementById('pa-ann-body').value = a.body || '';
document.getElementById('pa-ann-level').value = a.level || 'info';
document.getElementById('pa-ann-start').value = portalAnnToLocal(a.starts_at);
document.getElementById('pa-ann-end').value = portalAnnToLocal(a.ends_at);
document.getElementById('pa-ann-active').checked = String(a.is_active) === '1';
document.getElementById('pa-ann-heading').textContent = 'Ankündigung bearbeiten';
document.getElementById('pa-ann-location').value = a.location_id ? String(a.location_id) : '';
document.getElementById('pa-ann-reset').hidden = false;
document.getElementById('portal-ann-form').scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.getElementById('pa-ann-reset').addEventListener('click', function () {
document.getElementById('portal-ann-form').reset();
document.getElementById('pa-ann-id').value = '';
document.getElementById('pa-ann-heading').textContent = 'Neue Ankündigung';
this.hidden = true;
});
</script>
<script>
// Abhaengige Abschnitte optisch daempfen, solange das Portal bzw. die
// Selbstbestaetigung aus ist - bedienbar bleiben sie, damit man alles
// vorbereiten kann.
function paSyncDependent(sourceId, targetId, noteId) {
var source = document.getElementById(sourceId);
var target = document.getElementById(targetId);
if (!source || !target) { return; }
var apply = function () {
target.classList.toggle('is-off', !source.checked);
if (noteId) {
var note = document.getElementById(noteId);
if (note) { note.hidden = source.checked; }
}
};
source.addEventListener('change', apply);
apply();
}
paSyncDependent('pa-enabled', 'pa-dependent', 'pa-off-note');
paSyncDependent('pa-confirm-enabled', 'pa-confirm-fields', null);
// Overlay-Regler: Prozentwert und Vorschau des Hintergrundbilds mitfuehren.
(function () {
var slider = document.getElementById('portal-overlay');
if (!slider) { return; }
var value = document.getElementById('portal-overlay-value');
var preview = document.getElementById('pa-bg-preview');
slider.addEventListener('input', function () {
value.textContent = slider.value + ' %';
if (preview) { preview.style.setProperty('--pa-overlay', (slider.value / 100).toFixed(2)); }
});
})();
// Eigene Textfarbe: Farbwaehler nur aktiv, wenn der Haken gesetzt ist.
(function () {
var toggle = document.getElementById('portal-text-color-on');
var picker = document.getElementById('portal-text-color');
if (!toggle || !picker) { return; }
var apply = function () { picker.disabled = !toggle.checked; };
toggle.addEventListener('change', apply);
apply();
})();
// Datei-Uploads vor dem Absenden gegen das Server-Limit pruefen: eine zu große
// Datei wuerde sonst serverseitig abgewiesen (oder – bei Ueberschreiten von
// post_max_size – den kompletten POST samt CSRF-Token verwerfen). Deshalb hier
// frueh und mit klarer Meldung stoppen.
(function () {
var form = document.getElementById('pa-form');
if (!form) { return; }
var fileInputs = form.querySelectorAll('input[type="file"][data-max-bytes]');
function tooBig(input) {
var max = parseInt(input.getAttribute('data-max-bytes'), 10);
if (!max || !input.files || !input.files.length) { return null; }
var f = input.files[0];
return f.size > max ? { name: f.name, label: input.getAttribute('data-max-label') || '' } : null;
}
fileInputs.forEach(function (input) {
input.addEventListener('change', function () {
var over = tooBig(input);
input.setCustomValidity(over ? 'Datei zu groß (max. ' + over.label + ').' : '');
if (over) {
alert('„' + over.name + '" ist zu groß.\nErlaubt sind maximal ' + over.label + '. Bitte ein kleineres Bild wählen.');
input.value = '';
input.setCustomValidity('');
}
});
});
form.addEventListener('submit', function (e) {
for (var i = 0; i < fileInputs.length; i++) {
var over = tooBig(fileInputs[i]);
if (over) {
e.preventDefault();
alert('„' + over.name + '" ist zu groß.\nErlaubt sind maximal ' + over.label + '. Bitte ein kleineres Bild wählen.');
fileInputs[i].focus();
return false;
}
}
});
})();
// Symbolauswahl: Klick setzt das versteckte Feld, das Suchfeld filtert die Kacheln.
function portalLinkSetIcon(icon) {
document.getElementById('pl-icon').value = icon;
document.getElementById('pl-icon-name').textContent = icon;
document.getElementById('pl-icon-preview').className = 'fas ' + icon;
document.querySelectorAll('#pl-icon-grid .pli-item').forEach(function (btn) {
btn.classList.toggle('active', btn.dataset.icon === icon);
});
}
document.getElementById('pl-icon-grid').addEventListener('click', function (e) {
var btn = e.target.closest('.pli-item');
if (btn) { portalLinkSetIcon(btn.dataset.icon); }
});
document.getElementById('pl-icon-search').addEventListener('input', function () {
var needle = this.value.trim().toLowerCase();
var visible = 0;
document.querySelectorAll('#pl-icon-grid .pli-item').forEach(function (btn) {
var match = needle === '' || btn.dataset.icon.indexOf(needle) !== -1;
btn.style.display = match ? '' : 'none';
if (match) { visible++; }
});
var empty = document.getElementById('pl-icon-empty');
if (!empty) {
empty = document.createElement('p');
empty.id = 'pl-icon-empty';
empty.className = 'pli-empty';
empty.textContent = 'Kein Symbol gefunden.';
document.getElementById('pl-icon-grid').appendChild(empty);
}
empty.style.display = visible === 0 ? '' : 'none';
});
// Bearbeiten: bestehenden Link ins Formular laden; Abbrechen setzt zurueck.
function portalLinkEdit(link) {
document.getElementById('pl-id').value = link.id;
document.getElementById('pl-title').value = link.title;
document.getElementById('pl-url').value = link.url;
document.getElementById('pl-description').value = link.description || '';
portalLinkSetIcon(link.icon || 'fa-link');
document.getElementById('pl-placement').value = link.placement;
document.getElementById('pl-sort').value = link.sort_order;
document.getElementById('pl-newtab').checked = String(link.new_tab) === '1';
document.getElementById('pl-active').checked = String(link.is_active) === '1';
document.getElementById('pl-location').value = link.location_id ? String(link.location_id) : '';
document.getElementById('pl-heading').textContent = 'Link bearbeiten';
document.getElementById('pl-reset').hidden = false;
document.getElementById('portal-link-form').scrollIntoView({ behavior: 'smooth', block: 'center' });
}
document.getElementById('pl-reset').addEventListener('click', function () {
document.getElementById('portal-link-form').reset();
document.getElementById('pl-id').value = '';
portalLinkSetIcon('fa-link');
document.getElementById('pl-heading').textContent = 'Neuen Link anlegen';
this.hidden = true;
});
</script>