263 lines
15 KiB
PHP
263 lines
15 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../includes/bootstrap.php';
|
||
$staff = require_admin_or_permission('manage_email_addresses');
|
||
$pdo = get_db();
|
||
|
||
$errors = [];
|
||
$departments = departments_for_select($pdo, array_map('intval', $pdo->query('SELECT department_id FROM imap_accounts WHERE department_id IS NOT NULL')->fetchAll(PDO::FETCH_COLUMN)));
|
||
$priorities = $pdo->query('SELECT * FROM priorities ORDER BY sort_order')->fetchAll();
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
verify_csrf();
|
||
$action = $_POST['action'] ?? '';
|
||
|
||
if ($action === 'create' || $action === 'update') {
|
||
$label = trim($_POST['label'] ?? '') ?: 'Postfach';
|
||
$host = trim($_POST['host'] ?? '');
|
||
$port = (int) ($_POST['port'] ?? 993) ?: 993;
|
||
$enc = in_array($_POST['encryption'] ?? 'ssl', ['ssl','tls','none'], true) ? $_POST['encryption'] : 'ssl';
|
||
$username = trim($_POST['username'] ?? '');
|
||
$folder = trim($_POST['folder'] ?? '') ?: 'INBOX';
|
||
$afterFetch = in_array($_POST['after_fetch'] ?? 'seen', ['seen','archive','delete'], true) ? $_POST['after_fetch'] : 'seen';
|
||
$archiveFolder = trim($_POST['archive_folder'] ?? '') ?: 'Processed';
|
||
$spamFolder = trim($_POST['spam_folder'] ?? '') ?: 'Junk';
|
||
$deptId = $_POST['department_id'] !== '' ? (int) $_POST['department_id'] : null;
|
||
$prioId = $_POST['priority_id'] !== '' ? (int) $_POST['priority_id'] : null;
|
||
$isActive = !empty($_POST['is_active']) ? 1 : 0;
|
||
$plainPass = trim($_POST['password'] ?? '');
|
||
|
||
if ($host === '') $errors[] = 'Bitte einen Server (Host) angeben.';
|
||
if ($username === '') $errors[] = 'Bitte einen Benutzernamen angeben.';
|
||
if ($action === 'create' && $plainPass === '') $errors[] = 'Bitte ein Passwort angeben.';
|
||
|
||
if (!$errors) {
|
||
if ($action === 'create') {
|
||
$pdo->prepare(
|
||
'INSERT INTO imap_accounts (label, host, port, encryption, username, password_enc, folder, after_fetch, archive_folder, spam_folder, department_id, priority_id, is_active, created_at)
|
||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)'
|
||
)->execute([
|
||
$label, $host, $port, $enc, $username, encrypt_secret($plainPass), $folder,
|
||
$afterFetch, $archiveFolder, $spamFolder, $deptId, $prioId, $isActive, db_now(),
|
||
]);
|
||
audit_log($staff['id'], "IMAP-Postfach angelegt: {$label} ({$host})");
|
||
flash('success', 'Postfach angelegt.');
|
||
} else {
|
||
$id = (int) $_POST['id'];
|
||
if ($plainPass !== '') {
|
||
$pdo->prepare(
|
||
'UPDATE imap_accounts SET label=?, host=?, port=?, encryption=?, username=?, password_enc=?, folder=?, after_fetch=?, archive_folder=?, spam_folder=?, department_id=?, priority_id=?, is_active=? WHERE id=?'
|
||
)->execute([$label, $host, $port, $enc, $username, encrypt_secret($plainPass), $folder, $afterFetch, $archiveFolder, $spamFolder, $deptId, $prioId, $isActive, $id]);
|
||
} else {
|
||
// Passwort leer = unverändert lassen
|
||
$pdo->prepare(
|
||
'UPDATE imap_accounts SET label=?, host=?, port=?, encryption=?, username=?, folder=?, after_fetch=?, archive_folder=?, spam_folder=?, department_id=?, priority_id=?, is_active=? WHERE id=?'
|
||
)->execute([$label, $host, $port, $enc, $username, $folder, $afterFetch, $archiveFolder, $spamFolder, $deptId, $prioId, $isActive, $id]);
|
||
}
|
||
audit_log($staff['id'], "IMAP-Postfach aktualisiert: {$label} ({$host})");
|
||
flash('success', 'Postfach gespeichert.');
|
||
}
|
||
redirect('/staff/imap_accounts.php');
|
||
}
|
||
}
|
||
|
||
if ($action === 'delete') {
|
||
$id = (int) $_POST['id'];
|
||
$lblStmt = $pdo->prepare('SELECT label FROM imap_accounts WHERE id = ?');
|
||
$lblStmt->execute([$id]);
|
||
$lbl = $lblStmt->fetchColumn();
|
||
$pdo->prepare('DELETE FROM imap_accounts WHERE id = ?')->execute([$id]);
|
||
audit_log($staff['id'], "IMAP-Postfach gelöscht: {$lbl}");
|
||
flash('success', 'Postfach gelöscht.');
|
||
redirect('/staff/imap_accounts.php');
|
||
}
|
||
|
||
if ($action === 'test') {
|
||
$id = (int) $_POST['id'];
|
||
$stmt = $pdo->prepare('SELECT * FROM imap_accounts WHERE id = ?');
|
||
$stmt->execute([$id]);
|
||
$acc = $stmt->fetch();
|
||
if (!$acc) {
|
||
flash('error', 'Postfach nicht gefunden.');
|
||
} elseif (!function_exists('imap_open')) {
|
||
flash('error', 'PHP-IMAP-Erweiterung (php-imap) ist auf diesem Server nicht installiert.');
|
||
} else {
|
||
$sslFlag = match ($acc['encryption']) {
|
||
'ssl' => '/imap/ssl', 'tls' => '/imap/tls', default => '/imap/notls',
|
||
};
|
||
$mailbox = '{' . $acc['host'] . ':' . $acc['port'] . $sslFlag . '}' . $acc['folder'];
|
||
$mbox = @imap_open($mailbox, $acc['username'], decrypt_secret($acc['password_enc']), 0, 1);
|
||
if ($mbox) {
|
||
$unseen = imap_search($mbox, 'UNSEEN') ?: [];
|
||
imap_close($mbox);
|
||
flash('success', "Verbindung zu „{$acc['label']}" . "\" erfolgreich. " . count($unseen) . ' ungelesene Mail(s) im Postfach.');
|
||
} else {
|
||
flash('error', "Verbindung zu „{$acc['label']}\" fehlgeschlagen: " . imap_last_error());
|
||
}
|
||
}
|
||
redirect('/staff/imap_accounts.php');
|
||
}
|
||
}
|
||
|
||
$accounts = $pdo->query(
|
||
'SELECT ia.*, d.name AS department_name, p.name AS priority_name
|
||
FROM imap_accounts ia
|
||
LEFT JOIN departments d ON d.id = ia.department_id
|
||
LEFT JOIN priorities p ON p.id = ia.priority_id
|
||
WHERE ia.email_address_id IS NULL
|
||
ORDER BY ia.id'
|
||
)->fetchAll();
|
||
|
||
$editId = $_GET['edit'] ?? null;
|
||
$editAcc = null;
|
||
if ($editId) {
|
||
$stmt = $pdo->prepare('SELECT * FROM imap_accounts WHERE id = ?');
|
||
$stmt->execute([$editId]);
|
||
$editAcc = $stmt->fetch();
|
||
}
|
||
|
||
$pageTitle = 'Postfächer (E-Mail-Abruf)';
|
||
$activePage = 'imap_accounts';
|
||
require __DIR__ . '/_header.php';
|
||
?>
|
||
|
||
<div class="flash info" style="margin-bottom:16px">
|
||
Postfächer werden jetzt direkt bei der jeweiligen <a href="/staff/email_addresses.php"><strong>E-Mail-Adresse</strong></a> im Reiter „Entferntes Postfach“ eingerichtet.
|
||
Diese Seite zeigt nur noch ältere, nicht verknüpfte Postfächer.
|
||
</div>
|
||
<h1>Postfächer für den E-Mail-Abruf</h1>
|
||
<p class="muted">
|
||
Jedes Postfach wird beim Abruf (Cronjob <code>fetch_emails.php</code>) einzeln per IMAP abgefragt.
|
||
Nutzt ihr getrennte Mail-Konten pro Abteilung (statt eines Sammelpostfachs mit Weiterleitungen),
|
||
legt hier einfach für jede Abteilung ein eigenes Postfach an und wählt die passende Standardabteilung –
|
||
die <a href="/staff/email_addresses.php">Abteilungs-Routing-Tabelle</a> per Empfängeradresse ist dann optional
|
||
und greift nur noch zusätzlich, falls ein einzelnes Postfach doch Mails für mehrere Abteilungen empfängt.
|
||
</p>
|
||
|
||
<?php foreach ($errors as $err): ?>
|
||
<div class="flash error"><?= e($err) ?></div>
|
||
<?php endforeach; ?>
|
||
|
||
<div style="display:flex; gap:24px; align-items:flex-start">
|
||
<div style="flex:2">
|
||
<div class="card">
|
||
<table>
|
||
<thead><tr><th>Postfach</th><th>Server</th><th>Standardabteilung</th><th>Aktiv</th><th>Letzter Abruf</th><th></th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($accounts as $a): ?>
|
||
<tr>
|
||
<td><strong><?= e($a['label']) ?></strong><br><span class="muted" style="font-size:.78rem"><?= e($a['username']) ?></span></td>
|
||
<td class="muted" style="font-size:.82rem"><?= e($a['host']) ?>:<?= e($a['port']) ?></td>
|
||
<td><?= e($a['department_name'] ?? 'Automatisch (Routing/Standard)') ?></td>
|
||
<td><?= $a['is_active'] ? '✓' : '–' ?></td>
|
||
<td class="muted" style="font-size:.82rem"><?= e(cron_last_run_label($a['last_run_at'] ?? null)) ?></td>
|
||
<td style="white-space:nowrap">
|
||
<form method="post" style="display:inline">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="test">
|
||
<input type="hidden" name="id" value="<?= $a['id'] ?>">
|
||
<button type="submit" style="background:none;border:none;color:var(--primary);cursor:pointer;padding:0;font-size:.85rem">Testen</button>
|
||
</form> ·
|
||
<a href="/staff/imap_accounts.php?edit=<?= $a['id'] ?>">Bearbeiten</a> ·
|
||
<form method="post" style="display:inline" onsubmit="return confirm('Postfach wirklich löschen?');">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="delete">
|
||
<input type="hidden" name="id" value="<?= $a['id'] ?>">
|
||
<button type="submit" style="background:none;border:none;color:#dc2626;cursor:pointer;padding:0;font-size:.85rem">Löschen</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php if (!$accounts): ?>
|
||
<tr><td colspan="6" class="muted">Noch kein Postfach angelegt.</td></tr>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div style="flex:1; min-width:300px">
|
||
<div class="card">
|
||
<h3><?= $editAcc ? 'Postfach bearbeiten' : 'Neues Postfach' ?></h3>
|
||
<form method="post">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="action" value="<?= $editAcc ? 'update' : 'create' ?>">
|
||
<?php if ($editAcc): ?><input type="hidden" name="id" value="<?= $editAcc['id'] ?>"><?php endif; ?>
|
||
<label for="label">Bezeichnung</label>
|
||
<input type="text" id="label" name="label" value="<?= e($editAcc['label'] ?? '') ?>" placeholder="z.B. Technik-Postfach" required>
|
||
<div style="display:grid;grid-template-columns:1fr auto;gap:8px;align-items:flex-end;margin-top:10px">
|
||
<div>
|
||
<label for="host">Server</label>
|
||
<input type="text" id="host" name="host" value="<?= e($editAcc['host'] ?? '') ?>" placeholder="imap.beispiel.de" required>
|
||
</div>
|
||
<div style="width:80px">
|
||
<label for="port">Port</label>
|
||
<input type="number" id="port" name="port" value="<?= e($editAcc['port'] ?? '993') ?>">
|
||
</div>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">
|
||
<div>
|
||
<label for="username">Benutzername / E-Mail</label>
|
||
<input type="text" id="username" name="username" value="<?= e($editAcc['username'] ?? '') ?>" autocomplete="off" required>
|
||
</div>
|
||
<div>
|
||
<label for="password">Passwort</label>
|
||
<input type="password" id="password" name="password" placeholder="<?= $editAcc ? '(unverändert)' : '' ?>" autocomplete="new-password">
|
||
</div>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:10px">
|
||
<div>
|
||
<label for="folder">Postfach / Ordner</label>
|
||
<input type="text" id="folder" name="folder" value="<?= e($editAcc['folder'] ?? 'INBOX') ?>">
|
||
</div>
|
||
<div>
|
||
<label for="encryption">Verschlüsselung</label>
|
||
<select id="encryption" name="encryption">
|
||
<option value="ssl" <?= ($editAcc['encryption'] ?? 'ssl')==='ssl' ?'selected':'' ?>>SSL/TLS (993)</option>
|
||
<option value="tls" <?= ($editAcc['encryption'] ?? '')==='tls' ?'selected':'' ?>>STARTTLS (143)</option>
|
||
<option value="none" <?= ($editAcc['encryption'] ?? '')==='none'?'selected':'' ?>>Keine</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<label for="department_id" style="margin-top:10px">Standardabteilung</label>
|
||
<select id="department_id" name="department_id">
|
||
<option value="">Automatisch (Routing-Tabelle / globale Standardabteilung)</option>
|
||
<?php foreach ($departments as $d): ?>
|
||
<option value="<?= $d['id'] ?>" <?= ($editAcc['department_id'] ?? '') == $d['id'] ? 'selected' : '' ?>><?= e($d['name']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label for="priority_id">Standardpriorität (optional)</label>
|
||
<select id="priority_id" name="priority_id">
|
||
<option value="">Automatisch</option>
|
||
<?php foreach ($priorities as $p): ?>
|
||
<option value="<?= $p['id'] ?>" <?= ($editAcc['priority_id'] ?? '') == $p['id'] ? 'selected' : '' ?>><?= e($p['name']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label for="after_fetch" style="margin-top:10px">Aktion nach Abruf</label>
|
||
<select id="after_fetch" name="after_fetch" onchange="document.getElementById('archive-row').style.display=this.value==='archive'?'block':'none'">
|
||
<option value="seen" <?= ($editAcc['after_fetch'] ?? 'seen')==='seen' ?'selected':'' ?>>Als gelesen markieren</option>
|
||
<option value="archive" <?= ($editAcc['after_fetch'] ?? '')==='archive'?'selected':'' ?>>In Ordner verschieben</option>
|
||
<option value="delete" <?= ($editAcc['after_fetch'] ?? '')==='delete' ?'selected':'' ?>>Löschen</option>
|
||
</select>
|
||
<div id="archive-row" style="display:<?= ($editAcc['after_fetch'] ?? '')==='archive'?'block':'none' ?>;margin-top:10px">
|
||
<label for="archive_folder">Zielordner</label>
|
||
<input type="text" id="archive_folder" name="archive_folder" value="<?= e($editAcc['archive_folder'] ?? 'Processed') ?>">
|
||
</div>
|
||
<div style="margin-top:10px">
|
||
<label for="spam_folder">Spam-/Junk-Ordner</label>
|
||
<input type="text" id="spam_folder" name="spam_folder" value="<?= e($editAcc['spam_folder'] ?? 'Junk') ?>" placeholder="z.B. Junk, Spam oder INBOX.Junk">
|
||
<div class="muted" style="font-size:.75rem;margin-top:3px">Wird verwendet, wenn ein eingegangenes E-Mail-Ticket als Spam markiert wird.</div>
|
||
</div>
|
||
<label style="display:flex; align-items:center; gap:8px; margin-top:14px">
|
||
<input type="checkbox" name="is_active" style="width:auto" <?= ($editAcc['is_active'] ?? 1) ? 'checked' : '' ?>>
|
||
Aktiv (wird beim Abruf berücksichtigt)
|
||
</label>
|
||
<div class="btn-row">
|
||
<button type="submit" class="btn"><?= $editAcc ? 'Speichern' : 'Erstellen' ?></button>
|
||
<?php if ($editAcc): ?><a href="/staff/imap_accounts.php" class="btn secondary">Abbrechen</a><?php endif; ?>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php require __DIR__ . '/_footer.php'; ?>
|