From b03a2e63dc0a259eaf5bcd6baee133bced87fc87 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Tue, 12 Aug 2025 12:22:06 +0200 Subject: [PATCH 1/3] Implement a command to dismiss a notice. Implement and use an API endpoint to dismiss a notice. --- .../Core/Api/Notices/DismissNotice.ts | 22 +++++++ .../Core/Controller/Notice/Dismiss.ts | 25 +++----- .../Core/Api/Notices/DismissNotice.js | 23 +++++++ .../Core/Controller/Notice/Dismiss.js | 20 ++---- .../files/lib/bootstrap/com.woltlab.wcf.php | 1 + .../command/notice/DismissNotice.class.php | 63 +++++++++++++++++++ .../lib/data/notice/NoticeAction.class.php | 32 +++------- .../event/notice/NoticeDismissed.class.php | 19 ++++++ .../core/notices/DismissNotice.class.php | 42 +++++++++++++ 9 files changed, 192 insertions(+), 55 deletions(-) create mode 100644 ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js create mode 100644 wcfsetup/install/files/lib/command/notice/DismissNotice.class.php create mode 100644 wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php create mode 100644 wcfsetup/install/files/lib/system/endpoint/controller/core/notices/DismissNotice.class.php diff --git a/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts b/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts new file mode 100644 index 00000000000..27327a50f66 --- /dev/null +++ b/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts @@ -0,0 +1,22 @@ +/** + * Dismiss a notice. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.3 + * @woltlabExcludeBundle tiny + */ + +import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; +import { ApiResult, apiResultFromError, apiResultFromValue } from "../Result"; + +export async function dismissNotice(noticeId: number): Promise> { + try { + await prepareRequest(`${window.WSC_RPC_API_URL}core/notices/${noticeId}/dismiss`).post().fetchAsJson(); + } catch (e) { + return apiResultFromError(e); + } + + return apiResultFromValue([]); +} diff --git a/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts b/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts index 03e897dbf25..b4cfe2ed475 100644 --- a/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts +++ b/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts @@ -7,31 +7,26 @@ * @woltlabExcludeBundle tiny */ -import * as Ajax from "../../Ajax"; +import { dismissNotice } from "WoltLabSuite/Core/Api/Notices/DismissNotice"; +import { promiseMutex } from "WoltLabSuite/Core/Helper/PromiseMutex"; /** * Initializes dismiss buttons. */ export function setup(): void { - document.querySelectorAll(".jsDismissNoticeButton").forEach((button) => { - button.addEventListener("click", (ev) => click(ev)); + document.querySelectorAll(".jsDismissNoticeButton").forEach((button) => { + button.addEventListener( + "click", + promiseMutex(() => click(button)), + ); }); } /** * Sends a request to dismiss a notice and removes it afterwards. */ -function click(event: Event): void { - const button = event.currentTarget as HTMLElement; +async function click(button: HTMLElement): Promise { + await dismissNotice(parseInt(button.dataset.objectId!, 10)); - Ajax.apiOnce({ - data: { - actionName: "dismiss", - className: "wcf\\data\\notice\\NoticeAction", - objectIDs: [button.dataset.objectId!], - }, - success: () => { - button.parentElement!.remove(); - }, - }); + button.parentElement!.remove(); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js new file mode 100644 index 00000000000..a97fabf7f17 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js @@ -0,0 +1,23 @@ +/** + * Dismiss a notice. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.3 + * @woltlabExcludeBundle tiny + */ +define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "../Result"], function (require, exports, Backend_1, Result_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.dismissNotice = dismissNotice; + async function dismissNotice(noticeId) { + try { + await (0, Backend_1.prepareRequest)(`${window.WSC_RPC_API_URL}core/notices/${noticeId}/dismiss`).post().fetchAsJson(); + } + catch (e) { + return (0, Result_1.apiResultFromError)(e); + } + return (0, Result_1.apiResultFromValue)([]); + } +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js index 448e690a7cd..8a279fb2ac6 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js @@ -6,33 +6,23 @@ * @license GNU Lesser General Public License * @woltlabExcludeBundle tiny */ -define(["require", "exports", "tslib", "../../Ajax"], function (require, exports, tslib_1, Ajax) { +define(["require", "exports", "WoltLabSuite/Core/Api/Notices/DismissNotice", "WoltLabSuite/Core/Helper/PromiseMutex"], function (require, exports, DismissNotice_1, PromiseMutex_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setup = setup; - Ajax = tslib_1.__importStar(Ajax); /** * Initializes dismiss buttons. */ function setup() { document.querySelectorAll(".jsDismissNoticeButton").forEach((button) => { - button.addEventListener("click", (ev) => click(ev)); + button.addEventListener("click", (0, PromiseMutex_1.promiseMutex)(() => click(button))); }); } /** * Sends a request to dismiss a notice and removes it afterwards. */ - function click(event) { - const button = event.currentTarget; - Ajax.apiOnce({ - data: { - actionName: "dismiss", - className: "wcf\\data\\notice\\NoticeAction", - objectIDs: [button.dataset.objectId], - }, - success: () => { - button.parentElement.remove(); - }, - }); + async function click(button) { + await (0, DismissNotice_1.dismissNotice)(parseInt(button.dataset.objectId, 10)); + button.parentElement.remove(); } }); diff --git a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php index 0c326a7c681..ee4d1fb7193 100644 --- a/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php +++ b/wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php @@ -241,6 +241,7 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) { $event->register(new \wcf\system\endpoint\controller\core\notices\DeleteNotice()); $event->register(new \wcf\system\endpoint\controller\core\notices\GetShowOrder()); $event->register(new \wcf\system\endpoint\controller\core\notices\ChangeShowOrder()); + $event->register(new \wcf\system\endpoint\controller\core\notices\DismissNotice()); $event->register(new \wcf\system\endpoint\controller\core\reactions\types\EnableType()); $event->register(new \wcf\system\endpoint\controller\core\reactions\types\DisableType()); $event->register(new \wcf\system\endpoint\controller\core\reactions\types\DeleteType()); diff --git a/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php b/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php new file mode 100644 index 00000000000..b3b4d8ac60a --- /dev/null +++ b/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php @@ -0,0 +1,63 @@ + + * @since 6.3 + */ +final class DismissNotice +{ + public function __construct( + private readonly Notice $notice, + ) {} + + public function __invoke(): void + { + if (!$this->notice->isDismissible) { + return; + } + + if (WCF::getUser()->userID) { + $this->dismissForUser($this->notice, WCF::getUser()->userID); + } else { + $this->dismissForGuest($this->notice); + } + + $event = new NoticeDismissed($this->notice); + EventHandler::getInstance()->fire($event); + } + + private function dismissForUser(Notice $notice, int $userID): void + { + $sql = "INSERT IGNORE INTO wcf1_notice_dismissed + (noticeID, userID) + VALUES (?, ?)"; + $statement = WCF::getDB()->prepare($sql); + $statement->execute([ + $notice->noticeID, + $userID, + ]); + + UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'dismissedNotices'); + } + + private function dismissForGuest(Notice $notice): void + { + $sessionVar = WCF::getSession()->getVar('dismissedNotices') ?? ''; + $dismissedNotices = @\unserialize($sessionVar) ?: []; + $dismissedNotices[] = $notice->noticeID; + + WCF::getSession()->register('dismissedNotices', \serialize($dismissedNotices)); + } +} diff --git a/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php b/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php index 11a831d9684..73fc489bba0 100644 --- a/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php +++ b/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php @@ -4,11 +4,10 @@ use wcf\command\notice\DisableNotice; use wcf\command\notice\EnableNotice; +use wcf\command\notice\DismissNotice; use wcf\data\AbstractDatabaseObjectAction; use wcf\data\IToggleAction; use wcf\system\condition\ConditionHandler; -use wcf\system\user\storage\UserStorageHandler; -use wcf\system\WCF; /** * Executes notice-related actions. @@ -75,33 +74,14 @@ public function delete() * Dismisses a certain notice. * * @return int[] + * + * @deprecated 6.3 Use the `DismissNotice` command instead. */ public function dismiss() { - if (WCF::getUser()->userID) { - $sql = "INSERT IGNORE INTO wcf1_notice_dismissed - (noticeID, userID) - VALUES (?, ?)"; - $statement = WCF::getDB()->prepare($sql); - $statement->execute([ - \reset($this->objectIDs), - WCF::getUser()->userID, - ]); - - UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'dismissedNotices'); - } else { - $dismissedNotices = WCF::getSession()->getVar('dismissedNotices'); - if ($dismissedNotices !== null) { - $dismissedNotices = @\unserialize($dismissedNotices); - $dismissedNotices[] = \reset($this->objectIDs); - } else { - $dismissedNotices = [ - \reset($this->objectIDs), - ]; - } + $editor = $this->getSingleObject(); - WCF::getSession()->register('dismissedNotices', \serialize($dismissedNotices)); - } + (new DismissNotice($editor->getDecoratedObject()))(); return [ 'noticeID' => \reset($this->objectIDs), @@ -112,6 +92,8 @@ public function dismiss() * Validates the 'dismiss' action. * * @return void + * + * @deprecated 6.3 */ public function validateDismiss() { diff --git a/wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php b/wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php new file mode 100644 index 00000000000..4d0797b603c --- /dev/null +++ b/wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php @@ -0,0 +1,19 @@ + + * @since 6.3 + */ +final class NoticeDismissed implements IPsr14Event +{ + public function __construct(public readonly Notice $notice) {} +} diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/notices/DismissNotice.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/notices/DismissNotice.class.php new file mode 100644 index 00000000000..46e3533dfe6 --- /dev/null +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/notices/DismissNotice.class.php @@ -0,0 +1,42 @@ + + * @since 6.3 + */ +#[PostRequest('/core/notices/{id:\d+}/dismiss')] +final class DismissNotice implements IController +{ + public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface + { + $notice = Helper::fetchObjectFromRequestParameter($variables['id'], Notice::class); + + $this->assertNoticeCanBeDismissed($notice); + + (new \wcf\command\notice\DismissNotice($notice))(); + + return new JsonResponse([]); + } + + private function assertNoticeCanBeDismissed(Notice $notice): void + { + if (!$notice->isDismissible) { + throw new PermissionDeniedException(); + } + } +} From 87bde1b0d00d65ef5f184742801dd10887022c03 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Wed, 13 Aug 2025 11:56:29 +0200 Subject: [PATCH 2/3] Check if the notice can be dismissed not in the command --- .../install/files/lib/command/notice/DismissNotice.class.php | 4 ---- wcfsetup/install/files/lib/data/notice/NoticeAction.class.php | 4 +++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php b/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php index b3b4d8ac60a..7f91a90d68c 100644 --- a/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php +++ b/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php @@ -24,10 +24,6 @@ public function __construct( public function __invoke(): void { - if (!$this->notice->isDismissible) { - return; - } - if (WCF::getUser()->userID) { $this->dismissForUser($this->notice, WCF::getUser()->userID); } else { diff --git a/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php b/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php index 73fc489bba0..6e97f8096e9 100644 --- a/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php +++ b/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php @@ -81,7 +81,9 @@ public function dismiss() { $editor = $this->getSingleObject(); - (new DismissNotice($editor->getDecoratedObject()))(); + if ($editor->isDismissible) { + (new DismissNotice($editor->getDecoratedObject()))(); + } return [ 'noticeID' => \reset($this->objectIDs), From fc4a3876fc4c77e59ba40d8118eece1f4f243254 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 13 Aug 2025 12:59:57 +0200 Subject: [PATCH 3/3] Minor fixes --- ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts | 1 - ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts | 3 +-- .../files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js | 1 - .../files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js | 3 +-- .../files/lib/command/notice/DismissNotice.class.php | 2 +- .../install/files/lib/data/notice/NoticeAction.class.php | 2 -- .../files/lib/event/notice/NoticeDismissed.class.php | 6 ++++-- .../controller/core/notices/DismissNotice.class.php | 2 +- 8 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts b/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts index 27327a50f66..9684b16ad5f 100644 --- a/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts +++ b/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts @@ -5,7 +5,6 @@ * @copyright 2001-2025 WoltLab GmbH * @license GNU Lesser General Public License * @since 6.3 - * @woltlabExcludeBundle tiny */ import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; diff --git a/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts b/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts index b4cfe2ed475..cef314914a6 100644 --- a/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts +++ b/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts @@ -4,7 +4,6 @@ * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License - * @woltlabExcludeBundle tiny */ import { dismissNotice } from "WoltLabSuite/Core/Api/Notices/DismissNotice"; @@ -26,7 +25,7 @@ export function setup(): void { * Sends a request to dismiss a notice and removes it afterwards. */ async function click(button: HTMLElement): Promise { - await dismissNotice(parseInt(button.dataset.objectId!, 10)); + (await dismissNotice(parseInt(button.dataset.objectId!, 10))).unwrap(); button.parentElement!.remove(); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js index a97fabf7f17..ff0628366bb 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js @@ -5,7 +5,6 @@ * @copyright 2001-2025 WoltLab GmbH * @license GNU Lesser General Public License * @since 6.3 - * @woltlabExcludeBundle tiny */ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "../Result"], function (require, exports, Backend_1, Result_1) { "use strict"; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js index 8a279fb2ac6..5bdf1d1f313 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/Notice/Dismiss.js @@ -4,7 +4,6 @@ * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License - * @woltlabExcludeBundle tiny */ define(["require", "exports", "WoltLabSuite/Core/Api/Notices/DismissNotice", "WoltLabSuite/Core/Helper/PromiseMutex"], function (require, exports, DismissNotice_1, PromiseMutex_1) { "use strict"; @@ -22,7 +21,7 @@ define(["require", "exports", "WoltLabSuite/Core/Api/Notices/DismissNotice", "Wo * Sends a request to dismiss a notice and removes it afterwards. */ async function click(button) { - await (0, DismissNotice_1.dismissNotice)(parseInt(button.dataset.objectId, 10)); + (await (0, DismissNotice_1.dismissNotice)(parseInt(button.dataset.objectId, 10))).unwrap(); button.parentElement.remove(); } }); diff --git a/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php b/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php index 7f91a90d68c..f60937309d1 100644 --- a/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php +++ b/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php @@ -9,7 +9,7 @@ use wcf\system\WCF; /** - * Dismisses a notice for the current user. + * Dismisses a notice. * * @author Olaf Braun * @copyright 2001-2025 WoltLab GmbH diff --git a/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php b/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php index 6e97f8096e9..637dd86c5cc 100644 --- a/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php +++ b/wcfsetup/install/files/lib/data/notice/NoticeAction.class.php @@ -74,7 +74,6 @@ public function delete() * Dismisses a certain notice. * * @return int[] - * * @deprecated 6.3 Use the `DismissNotice` command instead. */ public function dismiss() @@ -94,7 +93,6 @@ public function dismiss() * Validates the 'dismiss' action. * * @return void - * * @deprecated 6.3 */ public function validateDismiss() diff --git a/wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php b/wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php index 4d0797b603c..d7ad6ec1da9 100644 --- a/wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php +++ b/wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php @@ -6,7 +6,7 @@ use wcf\event\IPsr14Event; /** - * Indicates that a notice has been dismissed by the current user. + * Indicates that a notice has been dismissed. * * @author Olaf Braun * @copyright 2001-2025 WoltLab GmbH @@ -15,5 +15,7 @@ */ final class NoticeDismissed implements IPsr14Event { - public function __construct(public readonly Notice $notice) {} + public function __construct( + public readonly Notice $notice + ) {} } diff --git a/wcfsetup/install/files/lib/system/endpoint/controller/core/notices/DismissNotice.class.php b/wcfsetup/install/files/lib/system/endpoint/controller/core/notices/DismissNotice.class.php index 46e3533dfe6..87b295f9a4d 100644 --- a/wcfsetup/install/files/lib/system/endpoint/controller/core/notices/DismissNotice.class.php +++ b/wcfsetup/install/files/lib/system/endpoint/controller/core/notices/DismissNotice.class.php @@ -12,7 +12,7 @@ use wcf\system\exception\PermissionDeniedException; /** - * API endpoint to dismiss a notice for the current user. + * Dismisses a notice. * * @author Olaf Braun * @copyright 2001-2025 WoltLab GmbH