Upload via GUI (396 Dateien)
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* includes/mail_reload.php
|
||||
*
|
||||
* Lädt die Anhänge einer schon abgerufenen E-Mail nachträglich ins Ticket (für Mails, die abgerufen wurden, bevor DeskHub Anhänge
|
||||
* übernommen hat, oder deren Anhang damals nicht übernommen werden konnte). Die Original-Mail wird im Postfach gesucht: über die
|
||||
* gespeicherte UID im aktuellen Ordner, sonst über die Message-ID.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/imap_spam.php';
|
||||
require_once __DIR__ . '/mail_text.php';
|
||||
|
||||
/**
|
||||
* Nachricht des Tickets, zu der eine Mail gehört: die Kundennachricht, die zur Zeit des Abrufs angelegt wurde (die nächstliegende).
|
||||
*
|
||||
* @param array<int,array{id:int|string,created_at:string}> $threads Kundennachrichten des Tickets
|
||||
*/
|
||||
function mail_reload_pick_thread(array $threads, string $sourceCreatedAt): ?int
|
||||
{
|
||||
$at = strtotime($sourceCreatedAt) ?: 0;
|
||||
$best = null;
|
||||
$bestDiff = PHP_INT_MAX;
|
||||
foreach ($threads as $t) {
|
||||
$diff = abs((strtotime((string) $t['created_at']) ?: 0) - $at);
|
||||
if ($diff < $bestDiff) {
|
||||
$bestDiff = $diff;
|
||||
$best = (int) $t['id'];
|
||||
}
|
||||
}
|
||||
return $best;
|
||||
}
|
||||
|
||||
/**
|
||||
* Findet die Mail im geöffneten Ordner: über die UID (und passende Message-ID, falls beide bekannt), sonst nur über die Message-ID.
|
||||
*/
|
||||
function mail_reload_find_message(mixed $mbox, array $source): ?int
|
||||
{
|
||||
$wanted = imap_normalize_message_id((string) ($source['message_id'] ?? ''));
|
||||
$uid = (int) ($source['message_uid'] ?? 0);
|
||||
if ($uid > 0) {
|
||||
$num = (int) @imap_msgno($mbox, $uid);
|
||||
if ($num > 0) {
|
||||
if ($wanted === '') return $num;
|
||||
$h = @imap_headerinfo($mbox, $num);
|
||||
if ($h && imap_normalize_message_id($h->message_id ?? '') === $wanted) return $num;
|
||||
}
|
||||
}
|
||||
if ($wanted !== '') {
|
||||
$hits = @imap_search($mbox, 'ALL') ?: [];
|
||||
foreach ($hits as $num) {
|
||||
$h = @imap_headerinfo($mbox, (int) $num);
|
||||
if ($h && imap_normalize_message_id($h->message_id ?? '') === $wanted) return (int) $num;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt die Anhänge aller Original-Mails eines Tickets nach. Bereits vorhandene Anhänge werden nicht doppelt gespeichert.
|
||||
*
|
||||
* @return array{error:?string,added:int,inline:int,html:int,skipped:int,existing:int,mails:int,failed:string[]}
|
||||
*/
|
||||
function reload_ticket_mail_attachments(PDO $pdo, array $ticket, ?int $staffId = null): array
|
||||
{
|
||||
$res = ['error' => null, 'added' => 0, 'inline' => 0, 'html' => 0, 'skipped' => 0, 'existing' => 0, 'mails' => 0, 'failed' => []];
|
||||
if (!function_exists('imap_open')) {
|
||||
$res['error'] = 'PHP-IMAP ist nicht installiert.';
|
||||
return $res;
|
||||
}
|
||||
$ticketId = (int) $ticket['id'];
|
||||
$srcStmt = $pdo->prepare(
|
||||
'SELECT tes.*, ia.label, ia.host, ia.port, ia.encryption, ia.username, ia.password_enc
|
||||
FROM ticket_email_sources tes JOIN imap_accounts ia ON ia.id = tes.imap_account_id
|
||||
WHERE tes.ticket_id = ? ORDER BY tes.id'
|
||||
);
|
||||
$srcStmt->execute([$ticketId]);
|
||||
$sources = $srcStmt->fetchAll();
|
||||
if (!$sources) {
|
||||
$res['error'] = 'Zu diesem Ticket ist keine Original-E-Mail gespeichert (nur per E-Mail abgerufene Tickets).';
|
||||
return $res;
|
||||
}
|
||||
$thStmt = $pdo->prepare("SELECT id, created_at FROM ticket_threads WHERE ticket_id = ? AND author_type = 'customer' AND is_internal_note = 0 ORDER BY id");
|
||||
$thStmt->execute([$ticketId]);
|
||||
$threads = $thStmt->fetchAll();
|
||||
if (!$threads) {
|
||||
$res['error'] = 'Das Ticket hat keine Kundennachricht, an die Anhänge gehängt werden könnten.';
|
||||
return $res;
|
||||
}
|
||||
|
||||
foreach ($sources as $source) {
|
||||
$folder = trim((string) ($source['current_folder'] ?? '')) ?: 'INBOX';
|
||||
$label = (string) ($source['label'] ?: $source['username']);
|
||||
$mbox = imap_connect_account($source, $folder);
|
||||
if (!$mbox) {
|
||||
$res['failed'][] = "[$label] Postfach nicht erreichbar: " . (imap_last_error() ?: 'unbekannter Fehler');
|
||||
continue;
|
||||
}
|
||||
$num = mail_reload_find_message($mbox, $source);
|
||||
if ($num === null) {
|
||||
imap_close($mbox);
|
||||
$res['failed'][] = "[$label] Original-E-Mail wurde im Ordner „{$folder}“ nicht gefunden (verschoben oder gelöscht?).";
|
||||
continue;
|
||||
}
|
||||
$threadId = mail_reload_pick_thread($threads, (string) $source['created_at']);
|
||||
$structure = @imap_fetchstructure($mbox, $num) ?: null;
|
||||
$log = [];
|
||||
$fetchPart = fn(string $no) => (string) imap_fetchbody($mbox, $num, $no);
|
||||
// Formatierte Fassung der Nachricht (HTML) nachtragen, wenn es noch keine gibt
|
||||
$htmlBody = null;
|
||||
if (ticket_mail_html_ready($pdo) && $structure) {
|
||||
$htmlBody = mail_html_for_display($structure, $fetchPart);
|
||||
$has = $pdo->prepare('SELECT message_html FROM ticket_threads WHERE id = ?');
|
||||
$has->execute([(int) $threadId]);
|
||||
if ($htmlBody !== null && trim((string) $has->fetchColumn()) === '') {
|
||||
$pdo->prepare('UPDATE ticket_threads SET message_html = ? WHERE id = ?')->execute([$htmlBody, (int) $threadId]);
|
||||
$res['html']++;
|
||||
}
|
||||
}
|
||||
$c = mail_store_attachments($pdo, $structure, $fetchPart, (int) $threadId, $ticketId, $label, $log, true, $htmlBody !== null);
|
||||
imap_close($mbox);
|
||||
$res['mails']++;
|
||||
foreach (['added', 'inline', 'skipped', 'existing'] as $k) $res[$k] += $c[$k];
|
||||
}
|
||||
if ($res['added'] + $res['inline'] + $res['html'] > 0) log_activity($pdo, $ticketId, $staffId, 'Anhänge und Bilder aus der E-Mail nachgeladen: ' . $res['added'] . ' Anhang/Anhänge, ' . $res['inline'] . ' Bild(er)' . ($res['html'] ? ', Formatierung' : ''));
|
||||
return $res;
|
||||
}
|
||||
Reference in New Issue
Block a user