Setzt die Rechte aus dem vorigen Commit in den Oberflaechen um und
ergaenzt die fehlenden Verwaltungsseiten.
Neu:
- /admin/users - Konten anlegen, Rolle und Sperre setzen, Passwort
zuruecksetzen, Gruppenzuordnung je Konto
- /admin/groups - Gruppen anlegen und umbenennen, Mitglieder samt
Rolle pflegen, zugeordnete Domains einsehen
- /admin/profile - eigenes Konto: Gruppenzugehoerigkeit ansehen und
das Passwort aendern. Das fehlte bisher vollstaendig, auch fuer
den einen Administrator.
Domains sind jetzt Administratorsache, weil dort die Gruppe zugewiesen
wird. Kurzlinks, Statistik und QR-Codes richten sich nach der Gruppe:
ein fremder Kurzlink antwortet mit 404 (seine Existenz geht den
Aufrufer nichts an), ein sichtbarer ohne Schreibrecht mit 403.
Geschlossene Luecke: /admin/uploads/{file} pruefte bisher nur, ob
ueberhaupt jemand angemeldet ist. Da alle Logos in einem gemeinsamen
Verzeichnis liegen, war der Dateiname die einzige Huerde. Ausgeliefert
wird jetzt nur noch, was laut Datenbank zu einem Kurzlink aus einer
Gruppe des Aufrufers gehoert.
Die Navigation blendet aus, wofuer die Rechte fehlen - das ist
Bedienkomfort, kein Zugriffsschutz: die Routen pruefen unabhaengig
davon noch einmal.
Der Setup-Assistent legt das erste Konto als Administrator an und
traegt es gleich als Editor der Gruppe "Standard" ein. Ohne das
koennte es zwar verwalten, aber selbst keine Kurzlinks anlegen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
161 lines
7.5 KiB
PHP
161 lines
7.5 KiB
PHP
<?php
|
||
|
||
/**
|
||
* Gruppe anlegen und bearbeiten - samt Mitgliedern und zugeordneten Domains.
|
||
*
|
||
* @var array<string,mixed>|null $group
|
||
* @var list<array<string,mixed>> $members
|
||
* @var list<array<string,mixed>> $domains Domains dieser Gruppe
|
||
* @var list<array<string,mixed>> $availableUsers noch nicht Mitglied
|
||
* @var array{name:string} $values
|
||
* @var array<string,string> $errors
|
||
* @var App\Security\Csrf $csrf
|
||
*/
|
||
|
||
use App\Security\Role;
|
||
use App\Support\ShortDomain;
|
||
|
||
$isEdit = $group !== null;
|
||
$action = $isEdit
|
||
? $url('/admin/groups/' . (int) $group['id'] . '/edit')
|
||
: $url('/admin/groups/new');
|
||
?>
|
||
<div class="page-head">
|
||
<div>
|
||
<h1><?= $isEdit ? 'Gruppe bearbeiten' : 'Neue Gruppe' ?></h1>
|
||
<?php if (!$isEdit): ?>
|
||
<p class="muted">Mitglieder und Domains werden im nächsten Schritt zugeordnet.</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
<a href="<?= $e($url('/admin/groups')) ?>" class="btn btn--ghost">Zurück zur Liste</a>
|
||
</div>
|
||
|
||
<div class="card card--form">
|
||
<form method="post" action="<?= $e($action) ?>" class="form">
|
||
<?= $csrf->field() ?>
|
||
|
||
<div class="field <?= isset($errors['name']) ? 'field--error' : '' ?>">
|
||
<label for="name">Name <span class="req">*</span></label>
|
||
<input type="text" id="name" name="name" value="<?= $e($values['name']) ?>" maxlength="60"
|
||
required autofocus placeholder="z. B. Marketing">
|
||
<?php if (isset($errors['name'])): ?>
|
||
<p class="field__error"><?= $e($errors['name']) ?></p>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="form__actions">
|
||
<button type="submit" class="btn btn--primary"><?= $isEdit ? 'Namen speichern' : 'Gruppe anlegen' ?></button>
|
||
<a href="<?= $e($url('/admin/groups')) ?>" class="btn btn--ghost">Abbrechen</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<?php if ($isEdit): $groupId = (int) $group['id']; ?>
|
||
|
||
<div class="card">
|
||
<h2>Mitglieder</h2>
|
||
|
||
<?php if ($members === []): ?>
|
||
<p class="muted">Noch niemand in dieser Gruppe.</p>
|
||
<?php else: ?>
|
||
<div class="table-wrap">
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">Benutzer</th>
|
||
<th scope="col">Rolle in der Gruppe</th>
|
||
<th scope="col"><span class="sr-only">Aktionen</span></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($members as $member):
|
||
$memberId = (int) $member['id'];
|
||
$memberRole = Role::groupOrDefault($member['group_role']);
|
||
?>
|
||
<tr class="<?= (int) $member['is_active'] === 1 ? '' : 'is-inactive' ?>">
|
||
<td>
|
||
<div class="cell-stack">
|
||
<span class="mono"><?= $e($member['username']) ?></span>
|
||
<?php if (Role::globalOrDefault($member['global_role']) === Role::ADMIN): ?>
|
||
<span class="muted small">Administrator – sieht ohnehin alles</span>
|
||
<?php endif; ?>
|
||
<?php if ((int) $member['is_active'] !== 1): ?>
|
||
<span class="badge badge--muted">gesperrt</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
</td>
|
||
<td>
|
||
<form method="post" action="<?= $e($url('/admin/groups/' . $groupId . '/members')) ?>" class="inline">
|
||
<?= $csrf->field() ?>
|
||
<input type="hidden" name="user_id" value="<?= $memberId ?>">
|
||
<select name="role" aria-label="Rolle von <?= $e($member['username']) ?>">
|
||
<?php foreach (Role::GROUP_LABELS as $value => $label): ?>
|
||
<option value="<?= $e($value) ?>" <?= $memberRole === $value ? 'selected' : '' ?>>
|
||
<?= $e($label) ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<button type="submit" class="btn btn--sm">Übernehmen</button>
|
||
</form>
|
||
</td>
|
||
<td class="actions">
|
||
<form method="post" action="<?= $e($url('/admin/groups/' . $groupId . '/members/remove')) ?>" class="inline"
|
||
data-confirm="<?= $e($member['username']) ?> aus der Gruppe entfernen?">
|
||
<?= $csrf->field() ?>
|
||
<input type="hidden" name="user_id" value="<?= $memberId ?>">
|
||
<button type="submit" class="btn btn--sm btn--danger">Entfernen</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($availableUsers === []): ?>
|
||
<p class="muted small">Alle vorhandenen Konten sind bereits Mitglied.
|
||
<a href="<?= $e($url('/admin/users/new')) ?>">Neues Konto anlegen.</a></p>
|
||
<?php else: ?>
|
||
<form method="post" action="<?= $e($url('/admin/groups/' . $groupId . '/members')) ?>" class="toolbar">
|
||
<?= $csrf->field() ?>
|
||
<select name="user_id" aria-label="Benutzer">
|
||
<?php foreach ($availableUsers as $candidate): ?>
|
||
<option value="<?= (int) $candidate['id'] ?>"><?= $e($candidate['username']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<select name="role" aria-label="Rolle">
|
||
<?php foreach (Role::GROUP_LABELS as $value => $label): ?>
|
||
<option value="<?= $e($value) ?>"><?= $e($label) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<button type="submit" class="btn btn--primary">Hinzufügen</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>Domains dieser Gruppe</h2>
|
||
|
||
<?php if ($domains === []): ?>
|
||
<p class="muted">Dieser Gruppe ist noch keine Domain zugeordnet – ihre Mitglieder sehen
|
||
deshalb noch keine Kurzlinks. Die Zuordnung erfolgt beim
|
||
<a href="<?= $e($url('/admin/domains')) ?>">Bearbeiten einer Domain</a>.</p>
|
||
<?php else: ?>
|
||
<ul class="plain">
|
||
<?php foreach ($domains as $domain): ?>
|
||
<li>
|
||
<a class="mono" href="<?= $e($url('/admin/domains/' . (int) $domain['id'] . '/edit')) ?>">
|
||
<?= $e(ShortDomain::displayName((string) $domain['base_url'])) ?>
|
||
</a>
|
||
<?php if (($domain['label'] ?? '') !== ''): ?>
|
||
<span class="muted small"><?= $e($domain['label']) ?></span>
|
||
<?php endif; ?>
|
||
</li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php endif; ?>
|