Files
kaffeekasse-saas/mitarbeiterverwalten.php
T
clemensandClaude Sonnet 5 aeb687f42e M5: Hinweise auf tenant-scoped Notices umziehen, Mitgliederzugang auf Rollen umstellen
Hinweise:
- Neue Tabelle notices (tenant-scoped, Soft-Delete via deleted_at) loest
  die global unscoped kl_hinweise als aktive Datenquelle ab; Migration
  uebernimmt einmalig aktuell gueltige kl_hinweise-Eintraege fuer den
  Default-Mandanten. kl_hinweise bleibt als Golden-Master-Referenz stehen.
- hinweise.php und der Banner in header.php sind tenant-scoped umgestellt.

Mitgliederverwaltung:
- mitarbeiterverwalten.php verwaltet jetzt participants (tenant-scoped)
  statt der global unscoped kl_Mitarbeiter-Tabelle als primaere Quelle.
  Das behebt nebenbei ein Mandanten-Datenleck: jeder SaaS-Mandant mit
  Owner/Admin-Rolle haette zuvor die komplette Default-Mandanten-
  Mitgliederliste sehen und bearbeiten koennen.
- Fuer den Default-Mandanten bleibt Dual-Write nach kl_Mitarbeiter
  bestehen, damit stricheintragen.php/einzahlung.php weiter funktionieren;
  andere Mandanten werden rein participant-nativ verwaltet.
- Die Legacy-Administrator-Checkbox ist raus. Stattdessen kann ein Admin
  je Mitglied unabhaengig von Name/E-Mail einen Login-Zugang mit Rolle
  (member/treasurer/admin) gewaehren oder entziehen
  (saas_grant_participant_access / saas_revoke_participant_access).
  Einladung laeuft ueber den bestehenden Passwort-Reset-Mechanismus,
  Entzug setzt die Mitgliedschaft auf revoked statt sie zu loeschen.
- Kompletter Flow live getestet: anlegen, Zugang gewaehren, Einladungsmail,
  Passwort setzen, Login, Rollenschutz, Zugang entziehen, Login-Sperre.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 00:18:55 +02:00

308 lines
11 KiB
PHP

<?php
include "functions.php";
require_once __DIR__ . "/app/ledger.php";
require_once __DIR__ . "/app/saas-mail.php";
app_require_csrf();
include "header.php";
include "headerline.php";
include "nav.php";
?>
<!-- Banner -->
<section id="banner">
<div class="content">
<?php
$pdo = app_db_pdo();
$saasUser = saas_current_user($pdo);
$tenantId = 0;
$hasAccess = false;
if ($saasUser !== null && saas_user_has_role(['owner', 'admin'], $saasUser)) {
$tenantId = (int)$saasUser['tenant_id'];
$hasAccess = true;
}
if (!$hasAccess && $saasUser === null && checkKaffeelisteAdmin($conn, $mailadress)) {
$tenant = ledger_fetch_default_tenant($pdo);
if ($tenant !== null) {
$tenantId = (int)$tenant['id'];
$hasAccess = true;
}
}
if($hasAccess){
$meldung = null;
$fehler = null;
$einladungslink = null;
$bearbeitenId = null;
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$aktion = $_POST["aktion"] ?? '';
if ($aktion === 'bearbeiten') {
$bearbeitenId = (int)$_POST["mitgliedID"];
} elseif ($aktion === 'anlegen') {
$name = trim((string)($_POST["name"] ?? ''));
$email = trim((string)($_POST["email"] ?? ''));
$paypalname = trim((string)($_POST["paypalname"] ?? ''));
$aktiv = isset($_POST["aktiv"]);
if ($name === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$fehler = 'Bitte einen Namen und eine gültige E-Mail-Adresse angeben.';
} else {
try {
ledger_create_participant($pdo, $tenantId, $name, $email, $paypalname, $aktiv);
$meldung = 'Mitglied wurde angelegt.';
} catch (Throwable $e) {
$fehler = 'Das Mitglied konnte nicht angelegt werden (E-Mail eventuell schon vergeben).';
}
}
} elseif ($aktion === 'bearbeitenspeichern') {
$participantId = (int)$_POST["mitgliedID"];
$name = trim((string)($_POST["name"] ?? ''));
$email = trim((string)($_POST["email"] ?? ''));
$paypalname = trim((string)($_POST["paypalname"] ?? ''));
$aktiv = isset($_POST["aktiv"]);
if ($name === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$fehler = 'Bitte einen Namen und eine gültige E-Mail-Adresse angeben.';
} elseif (ledger_update_participant($pdo, $tenantId, $participantId, $name, $email, $paypalname, $aktiv)) {
$meldung = 'Mitglied wurde gespeichert.';
} else {
$fehler = 'Das Mitglied konnte nicht gespeichert werden.';
}
} elseif ($aktion === 'aktivieren' || $aktion === 'deaktivieren') {
$participantId = (int)$_POST["mitgliedID"];
if (ledger_set_participant_active($pdo, $tenantId, $participantId, $aktion === 'aktivieren')) {
$meldung = $aktion === 'aktivieren' ? 'Mitglied wurde aktiviert.' : 'Mitglied wurde deaktiviert.';
} else {
$fehler = 'Der Status konnte nicht geändert werden.';
}
} elseif ($aktion === 'zugang_gewaehren') {
$participantId = (int)$_POST["mitgliedID"];
$rolle = (string)($_POST["rolle"] ?? '');
$ergebnis = saas_grant_participant_access($pdo, $tenantId, $participantId, $rolle);
if ($ergebnis['ok']) {
$einladendeTenantSettings = saas_fetch_tenant_settings($pdo, $tenantId);
$mailErgebnis = saas_send_invite_mail(
$ergebnis['user_email'],
$einladendeTenantSettings['name'] ?? 'Kaffeeliste',
$rolle,
$ergebnis['token']
);
$meldung = 'Zugang wurde gewährt, Einladung wurde verschickt.';
if (saas_should_show_auth_links()) {
$einladungslink = saas_app_url('passwort-zuruecksetzen.php?token=' . urlencode($ergebnis['token']));
}
} else {
$fehler = implode(' ', $ergebnis['errors']);
}
} elseif ($aktion === 'zugang_entziehen') {
$participantId = (int)$_POST["mitgliedID"];
$ergebnis = saas_revoke_participant_access($pdo, $tenantId, $participantId);
if ($ergebnis['ok']) {
$meldung = 'Zugang wurde entzogen.';
} else {
$fehler = implode(' ', $ergebnis['errors']);
}
}
}
$mitglieder = ledger_fetch_participants_for_admin($pdo, $tenantId);
$rollenLabels = ['owner' => 'Inhaber', 'admin' => 'Administrator', 'treasurer' => 'Kassenwart', 'member' => 'Mitglied', 'viewer' => 'Betrachter'];
$bearbeitenMitglied = null;
if ($bearbeitenId !== null) {
foreach ($mitglieder as $m) {
if ($m['participant_id'] === $bearbeitenId) {
$bearbeitenMitglied = $m;
break;
}
}
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kaffeeliste - Mitglieder verwalten</title>
</head>
<body>
<h2>Mitglieder verwalten</h2>
<?php if ($meldung !== null): ?>
<div class="hint-box success"><p><?php echo saas_html($meldung); ?></p></div>
<?php endif; ?>
<?php if ($fehler !== null): ?>
<div class="hint-box error"><p><?php echo saas_html($fehler); ?></p></div>
<?php endif; ?>
<?php if ($einladungslink !== null): ?>
<div class="hint-box"><p>Dev-Link zur Einladung: <a href="<?php echo saas_html($einladungslink); ?>"><?php echo saas_html($einladungslink); ?></a></p></div>
<?php endif; ?>
<?php if ($bearbeitenMitglied !== null): ?>
<h3>Bearbeiten von <?php echo saas_html($bearbeitenMitglied['display_name']); ?></h3>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="aktion" value="bearbeitenspeichern">
<input type="hidden" name="mitgliedID" value="<?php echo $bearbeitenMitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<label for="name">Name:</label>
<input type="text" name="name" id="name" value="<?php echo saas_html($bearbeitenMitglied['display_name']); ?>" required>
<label for="email">E-Mail:</label>
<input type="email" name="email" id="email" value="<?php echo saas_html($bearbeitenMitglied['email'] ?? ''); ?>" required>
<label for="paypalname">PayPal-Name:</label>
<input type="text" name="paypalname" id="paypalname" value="<?php echo saas_html($bearbeitenMitglied['paypal_name'] ?? ''); ?>">
<br>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="aktiv" id="aktiv" <?php echo $bearbeitenMitglied['active'] ? 'checked' : ''; ?>>
<label class="form-check-label" for="aktiv">Aktiv:</label>
</div>
<button type="submit">Speichern</button>
</form>
<?php endif; ?>
<!-- Formular für das Anlegen von Mitgliedern -->
<h3>Neues Mitglied anlegen</h3>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="aktion" id="aktion" value="anlegen">
<?php echo app_csrf_field(); ?>
<label for="new-name">Name:</label>
<input type="text" name="name" id="new-name" required>
<label for="new-email">E-Mail:</label>
<input type="email" name="email" id="new-email" required>
<label for="new-paypalname">PayPal-Name:</label>
<input type="text" name="paypalname" id="new-paypalname">
<br>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="aktiv" id="new-aktiv" checked>
<label class="form-check-label" for="new-aktiv">Aktiv</label>
</div>
<button type="submit">Mitglied anlegen</button>
</form>
<p>Name und E-Mail dienen der Kaffeeliste und Benachrichtigungen. Ein Login-Zugang
ist davon unabhängig und wird separat je Mitglied gewährt oder entzogen.</p>
<!-- Tabelle zur Anzeige und Bearbeitung von Mitgliedern -->
<table class="table table-striped">
<tr>
<th>Name</th>
<th>E-Mail</th>
<th>PayPal-Name</th>
<th>Aktiv</th>
<th>Zugang</th>
<th>Aktionen</th>
</tr>
<?php foreach ($mitglieder as $mitglied): ?>
<tr>
<td><?php echo saas_html($mitglied['display_name']); ?></td>
<td><?php echo saas_html($mitglied['email'] ?? ''); ?></td>
<td><?php echo saas_html($mitglied['paypal_name'] ?? ''); ?></td>
<td><?php echo $mitglied['active'] ? '1' : '0'; ?></td>
<td>
<?php if ($mitglied['membership_status'] === 'active'): ?>
<?php if ($mitglied['role'] === 'owner'): ?>
Inhaber
<?php else: ?>
<?php echo saas_html($rollenLabels[$mitglied['role']] ?? $mitglied['role']); ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" style="display:inline">
<input type="hidden" name="aktion" value="zugang_gewaehren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<select name="rolle">
<?php foreach (saas_grantable_roles() as $rolle): ?>
<option value="<?php echo saas_html($rolle); ?>" <?php echo $rolle === $mitglied['role'] ? 'selected' : ''; ?>><?php echo saas_html($rollenLabels[$rolle] ?? $rolle); ?></option>
<?php endforeach; ?>
</select>
<button type="submit">Rolle ändern</button>
</form>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" style="display:inline">
<input type="hidden" name="aktion" value="zugang_entziehen">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<button type="submit">Zugang entziehen</button>
</form>
<?php endif; ?>
<?php else: ?>
<?php echo $mitglied['membership_status'] === 'revoked' ? 'Zugang entzogen' : 'Kein Zugang'; ?>
<?php if ($mitglied['email'] !== null && $mitglied['email'] !== ''): ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" style="display:inline">
<input type="hidden" name="aktion" value="zugang_gewaehren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<select name="rolle">
<?php foreach (saas_grantable_roles() as $rolle): ?>
<option value="<?php echo saas_html($rolle); ?>"><?php echo saas_html($rollenLabels[$rolle] ?? $rolle); ?></option>
<?php endforeach; ?>
</select>
<button type="submit">Zugang gewähren</button>
</form>
<?php endif; ?>
<?php endif; ?>
</td>
<td>
<ul class="actions">
<li>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="aktion" value="bearbeiten">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<button type="submit">Bearbeiten</button>
</form>
</li>
<li>
<?php if ($mitglied['active']): ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="aktion" value="deaktivieren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<button type="submit">Deaktivieren</button>
</form>
<?php else: ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="aktion" value="aktivieren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<button type="submit">Aktivieren</button>
</form>
<?php endif; ?>
</li>
</ul>
</td>
</tr>
<?php endforeach; ?>
</table>
</body>
</html>
<?php
}else{
echo "<h2>Sie haben keine Zugang zu dieser Webseite</h2>";
}
?>
</div>
</section>
<?php include "footer.php"; ?>