From 6ae735e913d30578dc1b210dae5c52ac158166fa Mon Sep 17 00:00:00 2001 From: albertlast Date: Sun, 9 Aug 2026 19:29:53 +0200 Subject: [PATCH] Keeps every recipient of a personal message, not just the last one pm_recipients holds one row per person a PM was sent to, and Received::$loaded is keyed accordingly, first by the PM and then by the member. Received::loadByPm() built its return value with only the PM as the key, so each new row overwrote the one before it and a PM came back with a single recipient - whichever the database handed over last. Two things go wrong from there. PM::canAccess() walks that list looking for the current member, so opening a PM you received alongside somebody else was "You are not allowed to access this section" unless you happened to be last. And PM::format() builds the recipient list from it, so the sent folder named one recipient of a PM that went to several, and the reply-to-all button was hidden because the count said one. Keys the list by recipient as well, which is the shape PM::format() and the popup already read it with, and picks out our own copy in applyActions() rather than labelling everybody's. Signed-off-by: Mathias Albert Signed-off-by: albertlast --- Sources/Actions/PersonalMessage.php | 16 +++++++++++----- Sources/PersonalMessage/PM.php | 4 ++-- Sources/PersonalMessage/Received.php | 13 ++++++++----- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Sources/Actions/PersonalMessage.php b/Sources/Actions/PersonalMessage.php index 5633e3c727..7874fa4a74 100644 --- a/Sources/Actions/PersonalMessage.php +++ b/Sources/Actions/PersonalMessage.php @@ -497,12 +497,18 @@ public function applyActions(): void } // Are we labeling anything? + // Only ever our own copy: the other recipients of a PM keep their own + // labels on it, and these are our labels, not theirs. if (!empty($to_label) && $this->folder === 'inbox') { - foreach (Received::loadByPm(array_keys($to_label)) as $received) { - if ($label_type[$received->id] === 'add') { - $received->addLabel($to_label[$received->id]); - } elseif ($label_type[$received->id] === 'rem') { - $received->removeLabel($to_label[$received->id]); + foreach (Received::loadByPm(array_keys($to_label)) as $pm => $recipients) { + if (!isset($recipients[User::$me->id])) { + continue; + } + + if ($label_type[$pm] === 'add') { + $recipients[User::$me->id]->addLabel($to_label[$pm]); + } elseif ($label_type[$pm] === 'rem') { + $recipients[User::$me->id]->removeLabel($to_label[$pm]); } } } diff --git a/Sources/PersonalMessage/PM.php b/Sources/PersonalMessage/PM.php index 68f36cddbf..562bb5346a 100644 --- a/Sources/PersonalMessage/PM.php +++ b/Sources/PersonalMessage/PM.php @@ -108,7 +108,7 @@ class PM implements \ArrayAccess /** * @var array * - * Data about received copies of this PM. + * Data about received copies of this PM, keyed by the recipient's ID. */ public array $received = []; @@ -229,7 +229,7 @@ public function __construct(int $id, array $props = []) { $this->id = $id; $this->set($props); - $this->received = Received::loadByPm($this->id); + $this->received = Received::loadByPm($this->id)[$this->id] ?? []; $this->folder = $this->member_from !== User::$me->id ? 'inbox' : 'sent'; self::$loaded[$id] = $this; } diff --git a/Sources/PersonalMessage/Received.php b/Sources/PersonalMessage/Received.php index 07b2e8eecf..1c7e67e9ff 100644 --- a/Sources/PersonalMessage/Received.php +++ b/Sources/PersonalMessage/Received.php @@ -315,7 +315,9 @@ public function __set(string $prop, mixed $value): void * for future reference. * * @param int|array $ids The IDs of one or more personal messages. - * @return array The newly loaded instances of this class. + * @return array The newly loaded instances of this class, keyed first by + * the PM's ID and then by the recipient's ID. A PM has one of these per + * person it was sent to. */ public static function loadByPm(int|array $ids): array { @@ -326,9 +328,7 @@ public static function loadByPm(int|array $ids): array // Have we already loaded these? foreach ($ids as $key => $id) { if (isset(self::$loaded[$id])) { - foreach (self::$loaded[$id] as $received) { - $loaded[$id] = $received; - } + $loaded[$id] = self::$loaded[$id]; unset($ids[$key]); } @@ -358,8 +358,11 @@ public static function loadByPm(int|array $ids): array 'ids' => $ids, ]; + // One row per recipient, so the member has to be part of the key here. + // Keying on the PM alone leaves only whichever recipient the database + // happened to hand over last. foreach (self::queryData($selects, $params, $joins, $where) as $row) { - $loaded[(int) $row['id_pm']] = new self($row); + $loaded[(int) $row['id_pm']][(int) $row['id_member']] = new self($row); } ksort($loaded);