diff --git a/Sources/PersonalMessage/Received.php b/Sources/PersonalMessage/Received.php index 07b2e8eecf..995aff7de6 100644 --- a/Sources/PersonalMessage/Received.php +++ b/Sources/PersonalMessage/Received.php @@ -236,16 +236,21 @@ public function save(): void $this->in_inbox = true; } + // A row here is one member's copy of one PM, and (id_pm, id_member) is + // the primary key. Naming only the PM makes this statement try to hand + // every recipient's copy to the same member, so anything that saves a + // PM with more than one recipient - marking it read, labelling it, + // deleting it - is a duplicate key error rather than a save. Db::$db->query( 'UPDATE {db_prefix}pm_recipients SET - id_member = {int:member}, bcc = {int:bcc}, is_read = {int:is_read}, is_new = {int:is_new}, deleted = {int:deleted}, in_inbox = {int:in_inbox} - WHERE id_pm = {int:id}', + WHERE id_pm = {int:id} + AND id_member = {int:member}', [ 'id' => (int) $this->id, 'member' => (int) $this->member, @@ -259,12 +264,22 @@ public function save(): void $labels = array_diff($this->labels, [-1]); + // Labels belong to a member too, so only clear the ones this member + // owns. Every other recipient has their own labels on the same PM, and + // with the update above fixed this line is finally reached for a PM + // that has some. Db::$db->query( 'DELETE FROM {db_prefix}pm_labeled_messages - WHERE id_pm = {int:current_pm}' . (empty($labels) ? '' : ' + WHERE id_pm = {int:current_pm} + AND id_label IN ( + SELECT id_label + FROM {db_prefix}pm_labels + WHERE id_member = {int:member} + )' . (empty($labels) ? '' : ' AND id_label NOT IN ({array_int:labels})'), [ 'current_pm' => $this->id, + 'member' => (int) $this->member, 'labels' => $labels, ], );