adssd
This commit is contained in:
@@ -52,6 +52,11 @@ if ($csrfToken === '') {
|
||||
$service = new AdminAppsService($projectRoot);
|
||||
$messages = [];
|
||||
$errors = [];
|
||||
$installForm = [
|
||||
'target_directory_name' => '',
|
||||
'required_groups' => [],
|
||||
'available_without_login' => false,
|
||||
];
|
||||
$section = trim((string) ($_GET['section'] ?? $_POST['active_section'] ?? 'overview'));
|
||||
$allowedSections = ['overview', 'installation', 'widgets', 'integrations'];
|
||||
if (!in_array($section, $allowedSections, true)) {
|
||||
@@ -59,6 +64,11 @@ if (!in_array($section, $allowedSections, true)) {
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$installForm = [
|
||||
'target_directory_name' => trim((string) ($_POST['target_directory_name'] ?? '')),
|
||||
'required_groups' => array_values(array_map('strval', (array) ($_POST['required_groups'] ?? []))),
|
||||
'available_without_login' => !empty($_POST['available_without_login']),
|
||||
];
|
||||
$postedToken = (string) ($_POST['csrf_token'] ?? '');
|
||||
if ($postedToken === '' || !hash_equals($csrfToken, $postedToken)) {
|
||||
$errors[] = 'Die Formularpruefung ist fehlgeschlagen.';
|
||||
@@ -86,9 +96,28 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
);
|
||||
$messages[] = 'LDAP-Gruppen fuer die App wurden gespeichert.';
|
||||
$section = 'overview';
|
||||
} elseif ($action === 'install-app') {
|
||||
$result = $service->installUploadedApp(
|
||||
is_array($_FILES['module_zip'] ?? null) ? $_FILES['module_zip'] : [],
|
||||
$installForm['target_directory_name'],
|
||||
$installForm['required_groups'],
|
||||
$installForm['available_without_login']
|
||||
);
|
||||
$messages[] = 'App installiert: ' . (string) ($result['manifest']['title'] ?? $result['module_directory']);
|
||||
$messages[] = 'Zielverzeichnis: custom/apps/' . (string) $result['module_directory'];
|
||||
$messages[] = 'App-ID fuer Berechtigungen: ' . (string) $result['app_id'];
|
||||
$installForm = [
|
||||
'target_directory_name' => '',
|
||||
'required_groups' => [],
|
||||
'available_without_login' => false,
|
||||
];
|
||||
$section = 'installation';
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
$errors[] = $exception->getMessage();
|
||||
if ($action === 'install-app') {
|
||||
$section = 'installation';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,15 +143,15 @@ $sectionMeta = [
|
||||
],
|
||||
'installation' => [
|
||||
'label' => 'Installation',
|
||||
'title' => 'Installationswege fuer Apps',
|
||||
'description' => 'Installierbare Fach-Apps liegen unter custom/apps/. Upload- und Importwege werden hier als naechster Ausbauschritt vorbereitet.',
|
||||
'nav' => 'ZIP-, Verzeichnis- und Server-Workflow beschreiben.',
|
||||
'title' => 'ZIP-Upload und Installation',
|
||||
'description' => 'Installierbare Fach-Apps liegen unter custom/apps/. ZIP-Dateien koennen hier geprueft, entpackt und mit LDAP-Gruppen verknuepft werden.',
|
||||
'nav' => 'ZIP hochladen und nach /custom/apps/ installieren.',
|
||||
],
|
||||
'widgets' => [
|
||||
'label' => 'Widgets',
|
||||
'title' => 'Widgets und Quellen',
|
||||
'description' => 'Widgets sind kleine Desktop-Bereiche ohne klassische Fensterfunktionen. Sie koennen eigenstaendig oder Teil einer App sein.',
|
||||
'nav' => 'Widget-Quellen und Bedeutung pruefen.',
|
||||
'title' => 'Widget-Quellen',
|
||||
'description' => 'Hier sieht man nur, welche Widgets aktuell im System registriert sind, aus welcher App sie kommen und welche Start-App oder Status-API dahinterliegt.',
|
||||
'nav' => 'Registrierte Widgets und Herkunft pruefen.',
|
||||
],
|
||||
'integrations' => [
|
||||
'label' => 'Integrationen',
|
||||
@@ -326,7 +355,7 @@ ob_start();
|
||||
<div>
|
||||
<p class="window-app-kicker aa-kicker">Installation</p>
|
||||
<h3 class="aa-section-title">Aktueller Installationsweg</h3>
|
||||
<p class="aa-copy">Neue Apps koennen aktuell als Modul-Verzeichnis unter <code>custom/apps/</code> abgelegt werden. Ein eigener Upload-Bereich fuer ZIP-Dateien oder Verzeichnisse ist noch offen und wird als naechster Ausbauschritt vorbereitet.</p>
|
||||
<p class="aa-copy">Neue Apps koennen als Modul-Verzeichnis unter <code>custom/apps/</code> liegen. Fuer ZIP-Dateien steht der eigentliche Installer im Bereich <strong>Installation</strong> zur Verfuegung.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -358,25 +387,72 @@ ob_start();
|
||||
<section class="window-app-card aa-card">
|
||||
<div class="aa-section-head">
|
||||
<div>
|
||||
<p class="window-app-kicker aa-kicker">Installationswege</p>
|
||||
<h3 class="aa-section-title">Aktueller und geplanter Ablauf</h3>
|
||||
<p class="aa-copy">Installierbare Apps liegen im Projekt unter <code>custom/apps/</code>. Der Desktop erkennt neue Module automatisch ueber deren Desktop-Metadaten.</p>
|
||||
<p class="window-app-kicker aa-kicker">ZIP-Installer</p>
|
||||
<h3 class="aa-section-title">App als ZIP hochladen</h3>
|
||||
<p class="aa-copy">Das ZIP muss genau ein Modulverzeichnis enthalten. Geprueft werden oberstes Verzeichnis, Pflichtdateien wie <code>module.json</code> und <code>desktop.php</code>, Name-Konflikte sowie einfache Hochrisiko-Muster im Code.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aa-grid">
|
||||
<article class="window-app-card aa-card aa-inline-card">
|
||||
<strong>1. Manuell ueber den Server</strong>
|
||||
<p>Ein Modul-Verzeichnis wird nach <code>custom/apps/<app>/</code> kopiert. Das ist der aktuell gueltige produktive Weg.</p>
|
||||
</article>
|
||||
<article class="window-app-card aa-card aa-inline-card">
|
||||
<strong>2. Verzeichnis-Upload</strong>
|
||||
<p>Ein Browser-Upload fuer ein vorbereitetes Modul-Verzeichnis ist fachlich vorgesehen, aber noch nicht implementiert.</p>
|
||||
</article>
|
||||
<article class="window-app-card aa-card aa-inline-card">
|
||||
<strong>3. ZIP-Upload</strong>
|
||||
<p>Ein ZIP-Import mit Entpacken und Validierung ist vorgesehen, benoetigt aber noch einen dedizierten Installations-Workflow.</p>
|
||||
</article>
|
||||
<form class="aa-form" method="post" action="/admin/apps/" enctype="multipart/form-data">
|
||||
<input type="hidden" name="csrf_token" value="<?= $h($csrfToken) ?>">
|
||||
<input type="hidden" name="action" value="install-app">
|
||||
<input type="hidden" name="active_section" value="installation">
|
||||
|
||||
<div class="aa-form-grid">
|
||||
<label class="aa-field aa-field--full">
|
||||
<span>ZIP-Datei</span>
|
||||
<input type="file" name="module_zip" accept=".zip,application/zip">
|
||||
</label>
|
||||
<label class="aa-field">
|
||||
<span>Zielverzeichnis in <code>custom/apps/</code></span>
|
||||
<input type="text" name="target_directory_name" value="<?= $h((string) $installForm['target_directory_name']) ?>" placeholder="Optional, sonst Name aus ZIP">
|
||||
</label>
|
||||
<label class="aa-field aa-field--full aa-field--check">
|
||||
<span class="aa-check-row">
|
||||
<input type="checkbox" name="available_without_login" value="1" <?= !empty($installForm['available_without_login']) ? 'checked' : '' ?>>
|
||||
<span>App nach der Installation direkt fuer den Logoff-Desktop freigeben</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="aa-install-groups">
|
||||
<strong>LDAP-Gruppen nach der Installation</strong>
|
||||
<p class="aa-copy">Leere Auswahl bedeutet: alle sichtbaren Benutzer. Die Werte werden sofort in <?= $h((string) ($meta['app_access_config_path'] ?? '')) ?> hinterlegt.</p>
|
||||
<div class="aa-access-groups">
|
||||
<?php if ($availableGroups === []): ?>
|
||||
<p class="aa-copy">Keine LDAP-Gruppen aus der Registration-Konfiguration gefunden.</p>
|
||||
<?php else: ?>
|
||||
<?php foreach ($availableGroups as $group): ?>
|
||||
<label class="window-app-item aa-group-item">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="required_groups[]"
|
||||
value="<?= $h((string) ($group['id'] ?? '')) ?>"
|
||||
<?= in_array((string) ($group['id'] ?? ''), array_values(array_map('strval', (array) $installForm['required_groups'])), true) ? 'checked' : '' ?>
|
||||
>
|
||||
<span class="window-app-item-main aa-group-main">
|
||||
<strong><?= $h((string) ($group['label'] ?? '')) ?></strong>
|
||||
<span class="aa-meta"><?= $h((string) ($group['id'] ?? '')) ?></span>
|
||||
</span>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aa-actions">
|
||||
<button class="window-app-button aa-primary" type="submit">ZIP pruefen und installieren</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="window-app-card aa-card">
|
||||
<div class="aa-section-head">
|
||||
<div>
|
||||
<p class="window-app-kicker aa-kicker">Checks</p>
|
||||
<h3 class="aa-section-title">Was geprueft wird</h3>
|
||||
<p class="aa-copy">Der Installer erwartet ein einzelnes Modulverzeichnis im ZIP, prueft auf <code>module.json</code> und <code>desktop.php</code>, blockiert Pfad-Traversal, Symlinks und typische Hochrisiko-Funktionen wie <code>exec()</code> oder <code>shell_exec()</code>. Das ist ein Plausibilitaetscheck, keine vollstaendige Sicherheitsgarantie.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
@@ -386,8 +462,8 @@ ob_start();
|
||||
<div class="aa-section-head">
|
||||
<div>
|
||||
<p class="window-app-kicker aa-kicker">Widget Registry</p>
|
||||
<h3 class="aa-section-title">Alle Widgets</h3>
|
||||
<p class="aa-copy">Globale Widgets und modulbasierte Widgets werden hier gemeinsam sichtbar. Fachlich sind Widgets kleine Desktop-Elemente; technisch werden sie aktuell noch uebergangsweise im rechten Infobereich gerendert.</p>
|
||||
<h3 class="aa-section-title">Registrierte Widgets</h3>
|
||||
<p class="aa-copy">Dieser Bereich ist rein technisch gemeint: Er zeigt, welche Widgets derzeit registriert sind, aus welcher Quelle sie kommen und welche App oder API damit verbunden ist. Er ist keine separate Benutzerverwaltung.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user