diff --git a/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts b/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts new file mode 100644 index 00000000000..9684b16ad5f --- /dev/null +++ b/ts/WoltLabSuite/Core/Api/Notices/DismissNotice.ts @@ -0,0 +1,21 @@ +/** + * Dismiss a notice. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.3 + */ + +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..cef314914a6 100644 --- a/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts +++ b/ts/WoltLabSuite/Core/Controller/Notice/Dismiss.ts @@ -4,34 +4,28 @@ * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License - * @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))).unwrap(); - 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..ff0628366bb --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Api/Notices/DismissNotice.js @@ -0,0 +1,22 @@ +/** + * Dismiss a notice. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.3 + */ +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..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,35 +4,24 @@ * @author Alexander Ebert * @copyright 2001-2019 WoltLab GmbH * @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))).unwrap(); + 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..f60937309d1 --- /dev/null +++ b/wcfsetup/install/files/lib/command/notice/DismissNotice.class.php @@ -0,0 +1,59 @@ + + * @since 6.3 + */ +final class DismissNotice +{ + public function __construct( + private readonly Notice $notice, + ) {} + + public function __invoke(): void + { + 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..637dd86c5cc 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,32 +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)); + if ($editor->isDismissible) { + (new DismissNotice($editor->getDecoratedObject()))(); } return [ @@ -112,6 +93,7 @@ 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..d7ad6ec1da9 --- /dev/null +++ b/wcfsetup/install/files/lib/event/notice/NoticeDismissed.class.php @@ -0,0 +1,21 @@ + + * @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..87b295f9a4d --- /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(); + } + } +}