diff --git a/Plugins/plugins/lang-en-1.1.10.zip b/Plugins/plugins/lang-en-1.1.10.zip index 95334748..e84a80ff 100644 Binary files a/Plugins/plugins/lang-en-1.1.10.zip and b/Plugins/plugins/lang-en-1.1.10.zip differ diff --git a/Plugins/plugins/lang-en-1.1.11.zip b/Plugins/plugins/lang-en-1.1.11.zip new file mode 100644 index 00000000..236c6db0 Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.11.zip differ diff --git a/Plugins/plugins/lang-en-1.1.12.zip b/Plugins/plugins/lang-en-1.1.12.zip new file mode 100644 index 00000000..892aa260 Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.12.zip differ diff --git a/Plugins/plugins/lang-en-1.1.13.zip b/Plugins/plugins/lang-en-1.1.13.zip new file mode 100644 index 00000000..61d7c3bc Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.13.zip differ diff --git a/Plugins/plugins/lang-en-1.1.14.zip b/Plugins/plugins/lang-en-1.1.14.zip new file mode 100644 index 00000000..186e0139 Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.14.zip differ diff --git a/Plugins/plugins/lang-en-1.1.15.zip b/Plugins/plugins/lang-en-1.1.15.zip new file mode 100644 index 00000000..d606ccfb Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.15.zip differ diff --git a/Plugins/plugins/lang-en-1.1.16.zip b/Plugins/plugins/lang-en-1.1.16.zip new file mode 100644 index 00000000..b303c9e4 Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.16.zip differ diff --git a/Plugins/plugins/lang-en-1.1.17.zip b/Plugins/plugins/lang-en-1.1.17.zip new file mode 100644 index 00000000..294e8b46 Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.17.zip differ diff --git a/Plugins/plugins/lang-en-1.1.18.zip b/Plugins/plugins/lang-en-1.1.18.zip new file mode 100644 index 00000000..fc247d41 Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.18.zip differ diff --git a/Plugins/plugins/lang-en-1.1.19.zip b/Plugins/plugins/lang-en-1.1.19.zip new file mode 100644 index 00000000..3e3f3e79 Binary files /dev/null and b/Plugins/plugins/lang-en-1.1.19.zip differ diff --git a/Plugins/plugins/tagesbericht-1.1.0.zip b/Plugins/plugins/tagesbericht-1.1.0.zip new file mode 100644 index 00000000..d0b7bd4c Binary files /dev/null and b/Plugins/plugins/tagesbericht-1.1.0.zip differ diff --git a/Plugins/plugins/tagesbericht-1.1.1.zip b/Plugins/plugins/tagesbericht-1.1.1.zip new file mode 100644 index 00000000..88406da1 Binary files /dev/null and b/Plugins/plugins/tagesbericht-1.1.1.zip differ diff --git a/Plugins/plugins/tagesbericht/lang/en.json b/Plugins/plugins/tagesbericht/lang/en.json new file mode 100644 index 00000000..9e9bd1fe --- /dev/null +++ b/Plugins/plugins/tagesbericht/lang/en.json @@ -0,0 +1,34 @@ +{ + "_info": "English translations for this plugin. Key = German source text, value = English. {} stands for a changing part, {#} for a number.", + "Ab Uhrzeit (Serverzeit)": "From time (server time)", + "Kein Bericht gesendet: Entweder gibt es nichts zu berichten oder der Mailversand ist nicht eingerichtet.": "No report sent: either there is nothing to report or mail sending is not set up.", + "Nur an Werktagen (Mo–Fr)": "Weekdays only (Mon–Fri)", + "Ohne Auswahl erhalten alle aktiven Mitarbeiter den Bericht. Jeder sieht nur Tickets seiner Abteilungen.": "Without a selection all active staff receive the report. Everyone sees only tickets of their own departments.", + "Schickt dir den Bericht sofort zum Ausprobieren (an": "Sends you the report right away to try it out (to", + "Schickt dir den Bericht sofort zum Ausprobieren (an {}).": "Sends you the report right away to try it out (to {}).", + "Sendet Mitarbeitern täglich eine Zusammenfassung: offene, unzugewiesene und überfällige Tickets, Zugänge und Erledigungen der letzten 24 Stunden sowie die ältesten offenen Tickets. Jeder sieht nur seine Abteilungen.": "Sends staff a daily summary: open, unassigned and overdue tickets, new arrivals and completions of the last 24 hours, and the oldest open tickets. Everyone sees only their own departments.", + "Tagesbericht": "Daily report", + "Tagesbericht per E-Mail": "Daily report by email", + "Testbericht an mich senden": "Send test report to me", + "Täglichen Bericht per E-Mail senden": "Send daily report by email", + "Zuletzt gesendet: noch nie": "Last sent: never", + "Täglich": "Daily", + "Einmal pro Woche": "Once a week", + "Wie eingestellt (Standard)": "As configured (default)", + "Nur wenn Tickets überfällig sind": "Only when tickets are overdue", + "Nur einmal pro Woche (montags)": "Only once a week (Mondays)", + "Gar keinen Tagesbericht mehr": "No daily report at all any more", + "Bericht per E-Mail senden": "Send report by email", + "Rhythmus": "Frequency", + "Wochentag (bei „Einmal pro Woche“)": "Weekday (for “Once a week”)", + "Nur senden, wenn es etwas zu tun gibt (unzugewiesene oder überfällige Tickets)": "Only send if there is something to do (unassigned or overdue tickets)", + "Ohne Haken kommt der Bericht auch, wenn nur normale offene Tickets da sind.": "Without the tick the report is also sent when there are only ordinary open tickets.", + "Ohne Auswahl erhalten alle aktiven Mitarbeiter den Bericht. Jeder sieht nur Tickets seiner Abteilungen. Jeder Mitarbeiter kann unter „Mein Tagesbericht“ selbst wählen, wie oft er ihn bekommt (auch gar nicht).": "Without a selection all active staff receive the report. Everyone sees only tickets of their own departments. Each staff member can choose under “My daily report” how often they get it (including never).", + "{} Mitarbeiter haben den Bericht persönlich abgestellt.": "{} staff members have switched the report off for themselves.", + "Mein Tagesbericht": "My daily report", + "Gespeichert.": "Saved.", + "Bitte eine Auswahl treffen.": "Please make a selection.", + "Der Tagesbericht ist zurzeit für niemanden eingeschaltet. Deine Wahl gilt, sobald ein Administrator ihn aktiviert.": "The daily report is currently switched off for everyone. Your choice applies as soon as an administrator activates it.", + "Hier legst du fest, wie oft du den Bericht bekommst.": "Here you decide how often you get the report.", + "Sendet Mitarbeitern regelmäßig eine Zusammenfassung: offene, unzugewiesene und überfällige Tickets, Zugänge und Erledigungen der letzten 24 Stunden sowie die ältesten offenen Tickets. Rhythmus einstellbar (täglich, Werktage, wöchentlich), auf Wunsch nur bei Handlungsbedarf; jeder Mitarbeiter kann seine Mails selbst reduzieren oder abstellen. Jeder sieht nur seine Abteilungen.": "Sends staff a regular summary: open, unassigned and overdue tickets, new arrivals and completions of the last 24 hours, and the oldest open tickets. Frequency can be set (daily, weekdays, weekly), optionally only when action is needed; every staff member can reduce or switch off their own emails. Everyone sees only their own departments." +} diff --git a/Plugins/plugins/tagesbericht/lib.php b/Plugins/plugins/tagesbericht/lib.php index 71a5f105..4213f967 100644 --- a/Plugins/plugins/tagesbericht/lib.php +++ b/Plugins/plugins/tagesbericht/lib.php @@ -1,106 +1,197 @@ - plugin_setting(TAGESBERICHT, 'enabled', '0') === '1', - 'hour' => max(0, min(23, (int) plugin_setting(TAGESBERICHT, 'hour', '7'))), - 'workdays' => plugin_setting(TAGESBERICHT, 'workdays', '1') === '1', - // Leer = alle aktiven Mitarbeiter, sonst Liste von Mitarbeiter-IDs - 'recipients' => array_values(array_filter(array_map('intval', explode(',', (string) plugin_setting(TAGESBERICHT, 'recipients', ''))))), - 'last_sent' => plugin_setting(TAGESBERICHT, 'last_sent', ''), - ]; -} - -/** Empfänger laut Einstellung (nur aktive Mitarbeiter mit E-Mail-Adresse). */ -function tagesbericht_recipients(PDO $pdo, array $ids): array -{ - $rows = $pdo->query("SELECT * FROM staff WHERE is_active = 1 AND email <> ''")->fetchAll(); - return array_values(array_filter($rows, fn($s) => !$ids || in_array((int) $s['id'], $ids, true))); -} - -/** Zahlen und Listen für einen Mitarbeiter; berücksichtigt dessen Abteilungen. */ -function tagesbericht_data(PDO $pdo, array $staff, ?string $now = null): array -{ - $now = $now ?? db_now(); - $since = date('Y-m-d H:i:s', strtotime($now) - 86400); - [$dept, $deptParams] = dept_filter_sql(staff_department_ids($staff), 't.department_id'); - $open = "t.status NOT IN ('resolved','closed') AND t.is_spam = 0"; - - $count = function (string $where, array $params = []) use ($pdo, $dept, $deptParams): int { - $stmt = $pdo->prepare("SELECT COUNT(*) FROM tickets t WHERE {$where}{$dept}"); - $stmt->execute(array_merge($params, $deptParams)); - return (int) $stmt->fetchColumn(); - }; - $oldest = $pdo->prepare("SELECT t.ticket_number, t.subject, t.created_at, t.resolution_due_at, d.name AS department - FROM tickets t LEFT JOIN departments d ON d.id = t.department_id - WHERE {$open}{$dept} ORDER BY t.created_at ASC LIMIT 5"); - $oldest->execute($deptParams); - - return [ - 'open' => $count($open), - 'unassigned' => $count($open . ' AND t.assigned_staff_id IS NULL'), - 'mine' => $count($open . ' AND t.assigned_staff_id = ?', [(int) $staff['id']]), - 'overdue' => $count($open . ' AND t.resolution_due_at IS NOT NULL AND t.resolution_due_at < ?', [$now]), - 'created' => $count('t.is_spam = 0 AND t.created_at >= ?', [$since]), - 'resolved' => $count('t.is_spam = 0 AND t.resolved_at IS NOT NULL AND t.resolved_at >= ?', [$since]), - 'oldest' => $oldest->fetchAll(), - ]; -} - -function tagesbericht_html(array $staff, array $d): string -{ - $card = function (string $label, int $value, string $color) { - return '
Älteste offene Tickets
Älteste offene Tickets
Zu viele Mails? Hier einstellen, wie oft du diesen Bericht bekommst (auch „gar nicht“).
'; + return email_wrap($inner); +} + +/** Ob der Bericht an diesem Tag laut Rhythmus des Administrators fällig ist. */ +function tagesbericht_day_ok(array $cfg, int $time): bool +{ + $dow = (int) date('N', $time); + return match ($cfg['rhythm']) { + 'weekly' => $dow === $cfg['weekday'], + 'workdays' => $dow <= 5, + default => true, + }; +} + +/** + * Ob dieser Mitarbeiter heute einen Bericht bekommt (ohne Blick auf die Zahlen): persönliche Wahl „gar nicht“ und „nur montags“. + */ +function tagesbericht_pref_allows_today(string $pref, array $cfg, int $time): bool +{ + if ($pref === 'off') return false; + if ($pref === 'weekly' && $cfg['rhythm'] !== 'weekly' && (int) date('N', $time) !== 1) return false; + return true; +} + +/** + * Ob dieser Bericht gesendet wird, wenn der Tag und die persönliche Wahl es erlauben: nicht bei „nichts zu berichten“, bei + * „nur bei Handlungsbedarf“ nur mit unzugewiesenen oder überfälligen Tickets, bei persönlich „nur Überfälliges“ nur mit überfälligen. + * Ein Testbericht ($force) prüft nur, ob es überhaupt etwas zu berichten gibt. + */ +function tagesbericht_should_send(array $cfg, string $pref, array $d, bool $force = false): bool +{ + if ($d['open'] === 0 && $d['created'] === 0 && $d['resolved'] === 0) return false; + if ($force) return true; + if ($cfg['only_action'] && $d['unassigned'] === 0 && $d['overdue'] === 0) return false; + if ($pref === 'urgent' && $d['overdue'] === 0) return false; + return true; +} + +/** + * Sendet den Bericht an die Empfänger; liefert die Anzahl erfolgreich versendeter Mails. + * $only = feste Empfänger (Testbericht): Dann gelten weder persönliche Wahl noch „nur bei Handlungsbedarf“. + */ +function tagesbericht_send(PDO $pdo, ?array $only = null, ?int $time = null): int +{ + $cfg = tagesbericht_config(); + $time = $time ?? time(); + $force = $only !== null; + $sent = 0; + foreach ($only ?? tagesbericht_recipients($pdo, $cfg['recipients']) as $staff) { + $pref = $force ? 'default' : tagesbericht_pref((int) $staff['id']); + if (!tagesbericht_pref_allows_today($pref, $cfg, $time)) continue; + $d = tagesbericht_data($pdo, $staff); + if (!tagesbericht_should_send($cfg, $pref, $d, $force)) continue; + $subject = 'Tagesbericht: ' . $d['open'] . ' offen, ' . $d['overdue'] . ' überfällig'; + if (send_mail($staff['email'], $subject, tagesbericht_html($staff, $d))) $sent++; + } + return $sent; +} + +/** Wird alle paar Minuten aufgerufen: sendet einmal pro Tag ab der eingestellten Uhrzeit (soweit der Rhythmus es erlaubt). */ +function tagesbericht_tick(PDO $pdo, ?int $time = null): int +{ + $cfg = tagesbericht_config(); + $time = $time ?? time(); + if (!$cfg['enabled']) return 0; + if ((int) date('G', $time) < $cfg['hour']) return 0; + if (!tagesbericht_day_ok($cfg, $time)) return 0; + $today = date('Y-m-d', $time); + if ($cfg['last_sent'] === $today) return 0; + plugin_set_setting(TAGESBERICHT, 'last_sent', $today); // zuerst merken, damit parallele Aufrufe nicht doppelt senden + return tagesbericht_send($pdo, null, $time); +} diff --git a/Plugins/plugins/tagesbericht/pages/einstellungen.php b/Plugins/plugins/tagesbericht/pages/einstellungen.php index b1572217..f9da79c7 100644 --- a/Plugins/plugins/tagesbericht/pages/einstellungen.php +++ b/Plugins/plugins/tagesbericht/pages/einstellungen.php @@ -9,7 +9,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($action === 'save') { plugin_set_setting(TAGESBERICHT, 'enabled', !empty($_POST['enabled']) ? '1' : '0'); plugin_set_setting(TAGESBERICHT, 'hour', (string) max(0, min(23, (int) ($_POST['hour'] ?? 7)))); - plugin_set_setting(TAGESBERICHT, 'workdays', !empty($_POST['workdays']) ? '1' : '0'); + $rhythm = isset(tagesbericht_rhythms()[$_POST['rhythm'] ?? '']) ? (string) $_POST['rhythm'] : 'workdays'; + plugin_set_setting(TAGESBERICHT, 'rhythm', $rhythm); + plugin_set_setting(TAGESBERICHT, 'workdays', $rhythm === 'workdays' ? '1' : '0'); + plugin_set_setting(TAGESBERICHT, 'weekday', (string) max(1, min(7, (int) ($_POST['weekday'] ?? 1)))); + plugin_set_setting(TAGESBERICHT, 'only_action', !empty($_POST['only_action']) ? '1' : '0'); $ids = array_map('intval', (array) ($_POST['recipients'] ?? [])); plugin_set_setting(TAGESBERICHT, 'recipients', implode(',', array_filter($ids))); $message = 'Einstellungen gespeichert.'; @@ -30,19 +34,30 @@ $allStaff = $pdo->query("SELECT id, name, email FROM staff WHERE is_active = 1 O diff --git a/Plugins/plugins/tagesbericht/pages/persoenlich.php b/Plugins/plugins/tagesbericht/pages/persoenlich.php new file mode 100644 index 00000000..7af27422 --- /dev/null +++ b/Plugins/plugins/tagesbericht/pages/persoenlich.php @@ -0,0 +1,52 @@ + 0) $message = 'Testbericht an ' . $staff['email'] . ' gesendet.'; + else $error = 'Kein Bericht gesendet: Entweder gibt es nichts zu berichten oder der Mailversand ist nicht eingerichtet.'; + } +} + +$cfg = tagesbericht_config(); +$current = tagesbericht_pref($myId); +$rhythms = tagesbericht_rhythms(); +$days = [1 => 'Montag', 2 => 'Dienstag', 3 => 'Mittwoch', 4 => 'Donnerstag', 5 => 'Freitag', 6 => 'Samstag', 7 => 'Sonntag']; +?> +