From 7f5bc9a55b7795ddd5847e3927192cde98c76889 Mon Sep 17 00:00:00 2001 From: albertlast Date: Sun, 9 Aug 2026 23:28:52 +0200 Subject: [PATCH] Saves the notification preference the member asked for Three things stopped a topic or board notification from being set. Db::insert() wants a list of rows, and changePref() handed it a single one, so turning notification on for a topic answered "Invalid data structure sent to the database" and logged a critical error. sa=off maps to mode -1, which is MODE_NO_ALERT, and setAlertPref() has no case for it. $alert_pref is a typed property with no default, so reading it threw - the unsubscribe link at the foot of every notification email was a fatal error, on topics and on boards. 2.1's -1 is what 3.0 calls MODE_NO_EMAIL, so point sa=off at that constant and the link means what it says again. Any other value of mode did the same thing, since mode comes from the query string. Give the switch a default, and give MODE_NO_ALERT the handling its name describes. Signed-off-by: Mathias Papenbrock Signed-off-by: albertlast --- Sources/Actions/Notify.php | 14 ++++++++++++++ Sources/Actions/NotifyBoard.php | 4 +++- Sources/Actions/NotifyTopic.php | 6 ++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Sources/Actions/Notify.php b/Sources/Actions/Notify.php index 47469a13858..841189290b1 100644 --- a/Sources/Actions/Notify.php +++ b/Sources/Actions/Notify.php @@ -446,6 +446,20 @@ protected function setAlertPref(): void $perfs = self::getNotifyPrefs((int) self::$member_info['id'], [$this->type . '_notify_' . $this->id], true); $this->alert_pref = ((int) $perfs[(int) self::$member_info['id']][$this->type . '_notify_' . $this->id]) & self::PREF_ALERT; break; + + // And its mirror image: turn off the alerts and keep the emails. + case self::MODE_NO_ALERT: + $perfs = self::getNotifyPrefs((int) self::$member_info['id'], [$this->type . '_notify_' . $this->id], true); + $this->alert_pref = ((int) $perfs[(int) self::$member_info['id']][$this->type . '_notify_' . $this->id]) & self::PREF_EMAIL; + break; + + // $this->mode comes straight from the query string, so it can be + // anything at all. Leaving it out here means the typed property + // below is never assigned, and reading it is a fatal error rather + // than a page. + default: + $this->alert_pref = self::PREF_NONE; + break; } } diff --git a/Sources/Actions/NotifyBoard.php b/Sources/Actions/NotifyBoard.php index 347129fa8b8..bff78e1f84c 100644 --- a/Sources/Actions/NotifyBoard.php +++ b/Sources/Actions/NotifyBoard.php @@ -61,7 +61,9 @@ protected function setId(): void protected function saToMode(): void { if (!isset($_GET['mode']) && isset($_GET['sa'])) { - $_GET['mode'] = $_GET['sa'] == 'on' ? 3 : -1; + // 'off' is the unsubscribe link in the notification emails, so it + // means stop emailing me - not stop telling me anything at all. + $_GET['mode'] = $_GET['sa'] == 'on' ? parent::MODE_BOTH : parent::MODE_NO_EMAIL; unset($_GET['sa']); } } diff --git a/Sources/Actions/NotifyTopic.php b/Sources/Actions/NotifyTopic.php index ff929809829..8c91df460dd 100644 --- a/Sources/Actions/NotifyTopic.php +++ b/Sources/Actions/NotifyTopic.php @@ -62,7 +62,9 @@ protected function setId(): void protected function saToMode(): void { if (!isset($_GET['mode']) && isset($_GET['sa'])) { - $_GET['mode'] = $_GET['sa'] == 'on' ? 3 : -1; + // 'off' is the unsubscribe link in the notification emails, so it + // means stop emailing me - not stop telling me anything at all. + $_GET['mode'] = $_GET['sa'] == 'on' ? parent::MODE_BOTH : parent::MODE_NO_EMAIL; unset($_GET['sa']); } } @@ -116,7 +118,7 @@ protected function changePref(): void [ 'id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'unwatched' => 'int', ], - $log, + [$log], ['id_member', 'id_topic'], );