diff --git a/api/ticket_status_api.php b/api/ticket_status_api.php index 144ce68..c7007dd 100644 --- a/api/ticket_status_api.php +++ b/api/ticket_status_api.php @@ -171,7 +171,8 @@ if ($currentStatus !== $newStatus) { $currentStatus, $newStatus, (string)$ticket['title'], - $keyName + $keyName, + $ticket['visibility'] ?? 'public' ); NotificationHelper::notifyWatchers( $conn, diff --git a/api/update_ticket.php b/api/update_ticket.php index de210be..0b51442 100644 --- a/api/update_ticket.php +++ b/api/update_ticket.php @@ -267,7 +267,8 @@ try { $currentTicket['status'], $updateData['status'], $updateData['title'], - $changedBy + $changedBy, + $currentTicket['visibility'] ?? 'public' ); NotificationHelper::notifyWatchers( $this->conn, diff --git a/helpers/NotificationHelper.php b/helpers/NotificationHelper.php index af4cf91..548c105 100644 --- a/helpers/NotificationHelper.php +++ b/helpers/NotificationHelper.php @@ -40,20 +40,40 @@ class NotificationHelper return array_values(array_filter(array_map('trim', explode(',', $raw)))); } + /** + * Redact a ticket title for the shared Matrix notify list when the + * ticket isn't public, matching how sendCommentNotification() and + * notifyWatchers() already redact comment/activity previews for the + * same list. + */ + private static function redactedTitle(string $title, string $visibility): string + { + return $visibility === 'public' ? $title : '(restricted ticket — title hidden)'; + } + // ─── Public event methods ───────────────────────────────────────────────── /** * New ticket created (manual or automated/API). + * + * $ticketData['visibility'] ('public', 'internal', or 'confidential') is + * used to redact the title sent to the shared MATRIX_NOTIFY_USERS list + * for non-public tickets, same as sendCommentNotification()'s preview + * redaction. Defaults to 'public' for callers (e.g. the hwmonDaemon + * Bearer-API paths) that never set a non-default visibility. */ public static function sendTicketNotification($ticketId, array $ticketData, string $trigger = 'manual'): void { - preg_match('/^\[([^\]]+)\]/', $ticketData['title'] ?? '', $m); + $visibility = $ticketData['visibility'] ?? 'public'; + $title = $ticketData['title'] ?? 'Untitled'; + + preg_match('/^\[([^\]]+)\]/', $title, $m); $source = $m[1] ?? ($trigger === 'automated' ? 'Automated' : 'Manual'); self::fire([ 'event' => 'ticket_created', 'ticket_id' => $ticketId, - 'title' => $ticketData['title'] ?? 'Untitled', + 'title' => self::redactedTitle($title, $visibility), 'priority' => (int)($ticketData['priority'] ?? 4), 'category' => $ticketData['category'] ?? 'General', 'type' => $ticketData['type'] ?? 'Issue', @@ -73,13 +93,16 @@ class NotificationHelper * @param string $newStatus * @param string $ticketTitle * @param string|null $changedByDisplay Display name of the user who changed status + * @param string $visibility Ticket visibility; non-public titles are + * redacted before being sent to the shared + * notify list, same as sendTicketNotification(). */ - public static function sendStatusChangeNotification($ticketId, string $oldStatus, string $newStatus, string $ticketTitle, ?string $changedByDisplay = null): void + public static function sendStatusChangeNotification($ticketId, string $oldStatus, string $newStatus, string $ticketTitle, ?string $changedByDisplay = null, string $visibility = 'public'): void { self::fire([ 'event' => 'status_changed', 'ticket_id' => $ticketId, - 'title' => $ticketTitle, + 'title' => self::redactedTitle($ticketTitle, $visibility), 'old_status' => $oldStatus, 'new_status' => $newStatus, 'changed_by' => $changedByDisplay,