From e21c44a0c0a199855a9cd36fb48be11b20adc72b Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Fri, 15 Aug 2025 11:18:38 +0200 Subject: [PATCH 1/4] Implement a command to update the user rank --- .../acp/install_com.woltlab.wcf_step2.php | 4 +- .../files/lib/acp/form/UserEditForm.class.php | 5 +- .../lib/command/user/UpdateUserRank.class.php | 64 +++++++++++++++++++ .../files/lib/data/user/UserAction.class.php | 14 ++-- .../lib/data/user/UserProfileAction.class.php | 53 ++------------- .../lib/event/user/UserRankUpdated.class.php | 22 +++++++ .../point/UserActivityPointHandler.class.php | 13 +++- 7 files changed, 114 insertions(+), 61 deletions(-) create mode 100644 wcfsetup/install/files/lib/command/user/UpdateUserRank.class.php create mode 100644 wcfsetup/install/files/lib/event/user/UserRankUpdated.class.php diff --git a/wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php b/wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php index 7f0455495b0..a07fed4d999 100644 --- a/wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php +++ b/wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php @@ -1,5 +1,6 @@ executeAction(); +(new UpdateUserRank(WCF::getUser()))(); $action = new UserProfileAction([$editor], 'updateUserOnlineMarking'); $action->executeAction(); diff --git a/wcfsetup/install/files/lib/acp/form/UserEditForm.class.php b/wcfsetup/install/files/lib/acp/form/UserEditForm.class.php index 1aedd29006a..9db2b30186a 100755 --- a/wcfsetup/install/files/lib/acp/form/UserEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/UserEditForm.class.php @@ -2,6 +2,7 @@ namespace wcf\acp\form; +use wcf\command\user\UpdateUserRank; use wcf\data\file\File; use wcf\data\style\Style; use wcf\data\user\cover\photo\IUserCoverPhoto; @@ -19,7 +20,6 @@ use wcf\system\html\upcast\HtmlUpcastProcessor; use wcf\system\message\embedded\object\MessageEmbeddedObjectManager; use wcf\system\moderation\queue\ModerationQueueManager; -use wcf\system\option\user\UserOptionHandler; use wcf\system\style\StyleHandler; use wcf\system\user\command\SetColorScheme; use wcf\system\user\multifactor\Setup; @@ -500,8 +500,7 @@ public function save() // update user rank if (MODULE_USER_RANK) { - $action = new UserProfileAction([$this->user], 'updateUserRank'); - $action->executeAction(); + (new UpdateUserRank($this->user->getDecoratedObject()))(); } if (MODULE_USERS_ONLINE) { $action = new UserProfileAction([$this->user], 'updateUserOnlineMarking'); diff --git a/wcfsetup/install/files/lib/command/user/UpdateUserRank.class.php b/wcfsetup/install/files/lib/command/user/UpdateUserRank.class.php new file mode 100644 index 00000000000..4a471432780 --- /dev/null +++ b/wcfsetup/install/files/lib/command/user/UpdateUserRank.class.php @@ -0,0 +1,64 @@ + + * @since 6.3 + */ +final class UpdateUserRank +{ + public function __construct( + private readonly User $user + ) {} + + public function __invoke(): void + { + $newRankID = $this->getNewRankId(); + + $this->updateUserRank($this->user, $newRankID); + + $event = new UserRankUpdated($this->user, $newRankID); + EventHandler::getInstance()->fire($event); + } + + private function getNewRankId(): ?int + { + $conditionBuilder = new PreparedStatementConditionBuilder(); + $conditionBuilder->add('user_rank.groupID IN (?)', [$this->user->getGroupIDs()]); + $conditionBuilder->add('user_rank.requiredPoints <= ?', [$this->user->activityPoints]); + + if ($this->user->gender) { + $conditionBuilder->add('user_rank.requiredGender IN (?)', [[0, $this->user->gender]]); + } else { + $conditionBuilder->add('user_rank.requiredGender = ?', [0]); + } + + $sql = "SELECT user_rank.rankID + FROM wcf1_user_rank user_rank + LEFT JOIN wcf1_user_group user_group + ON user_group.groupID = user_rank.groupID + " . $conditionBuilder . " + ORDER BY user_group.priority DESC, user_rank.requiredPoints DESC, user_rank.requiredGender DESC"; + $statement = WCF::getDB()->prepare($sql, 1); + $statement->execute($conditionBuilder->getParameters()); + + return $statement->fetchSingleColumn() ?: null; + } + + private function updateUserRank(User $user, ?int $rankID): void + { + (new UserEditor($user))->update(['rankID' => $rankID]); + } +} diff --git a/wcfsetup/install/files/lib/data/user/UserAction.class.php b/wcfsetup/install/files/lib/data/user/UserAction.class.php index f74ac389fac..f433908bc0e 100644 --- a/wcfsetup/install/files/lib/data/user/UserAction.class.php +++ b/wcfsetup/install/files/lib/data/user/UserAction.class.php @@ -3,6 +3,7 @@ namespace wcf\data\user; use ParagonIE\ConstantTime\Hex; +use wcf\command\user\UpdateUserRank; use wcf\data\AbstractDatabaseObjectAction; use wcf\data\file\FileAction; use wcf\data\IClipboardAction; @@ -323,8 +324,7 @@ public function create() // update user rank if (MODULE_USER_RANK) { - $action = new UserProfileAction([$userEditor], 'updateUserRank'); - $action->executeAction(); + (new UpdateUserRank($userEditor->getDecoratedObject()))(); } // update user online marking $action = new UserProfileAction([$userEditor], 'updateUserOnlineMarking'); @@ -476,8 +476,9 @@ public function removeFromGroups() $this->readObjects(); if (MODULE_USER_RANK) { - $action = new UserProfileAction($this->objects, 'updateUserRank'); - $action->executeAction(); + foreach ($this->objects as $userEditor) { + (new UpdateUserRank($userEditor->getDecoratedObject()))(); + } } if (MODULE_USERS_ONLINE) { $action = new UserProfileAction($this->objects, 'updateUserOnlineMarking'); @@ -521,8 +522,9 @@ public function addToGroups() $this->readObjects(); if (MODULE_USER_RANK) { - $action = new UserProfileAction($this->objects, 'updateUserRank'); - $action->executeAction(); + foreach ($this->objects as $userEditor) { + (new UpdateUserRank($userEditor->getDecoratedObject()))(); + } } if (MODULE_USERS_ONLINE) { $action = new UserProfileAction($this->objects, 'updateUserOnlineMarking'); diff --git a/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php b/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php index 60f19bde719..c59676817e4 100644 --- a/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php +++ b/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php @@ -2,6 +2,7 @@ namespace wcf\data\user; +use wcf\command\user\UpdateUserRank; use wcf\data\object\type\ObjectTypeCache; use wcf\data\user\group\UserGroup; use wcf\system\bbcode\BBCodeHandler; @@ -292,8 +293,7 @@ public function save() // update user rank if (MODULE_USER_RANK) { - $action = new self([new UserEditor($user)], 'updateUserRank'); - $action->executeAction(); + (new UpdateUserRank($user))(); } // reload option handler @@ -326,6 +326,8 @@ public function save() * Updates user ranks. * * @return void + * + * @deprecated 6.3 use the `UpdateUserRank` command instead. */ public function updateUserRank() { @@ -333,51 +335,8 @@ public function updateUserRank() $this->readObjects(); } - $userToRank = []; - foreach ($this->getObjects() as $user) { - $conditionBuilder = new PreparedStatementConditionBuilder(); - $conditionBuilder->add('user_rank.groupID IN (?)', [$user->getGroupIDs()]); - $conditionBuilder->add('user_rank.requiredPoints <= ?', [$user->activityPoints]); - if ($user->gender) { - $conditionBuilder->add('user_rank.requiredGender IN (?)', [[0, $user->gender]]); - } else { - $conditionBuilder->add('user_rank.requiredGender = ?', [0]); - } - - $sql = "SELECT user_rank.rankID - FROM wcf1_user_rank user_rank - LEFT JOIN wcf1_user_group user_group - ON user_group.groupID = user_rank.groupID - " . $conditionBuilder . " - ORDER BY user_group.priority DESC, user_rank.requiredPoints DESC, user_rank.requiredGender DESC"; - $statement = WCF::getDB()->prepare($sql, 1); - $statement->execute($conditionBuilder->getParameters()); - $row = $statement->fetchArray(); - if ($row === false) { - if ($user->rankID) { - $userToRank[$user->userID] = null; - } - } else { - if ($row['rankID'] != $user->rankID) { - $userToRank[$user->userID] = $row['rankID']; - } - } - } - - if (!empty($userToRank)) { - $sql = "UPDATE wcf1_user - SET rankID = ? - WHERE userID = ?"; - $statement = WCF::getDB()->prepare($sql); - - WCF::getDB()->beginTransaction(); - foreach ($userToRank as $userID => $rankID) { - $statement->execute([ - $rankID, - $userID, - ]); - } - WCF::getDB()->commitTransaction(); + foreach ($this->getObjects() as $editor) { + (new UpdateUserRank($editor->getDecoratedObject()))(); } } diff --git a/wcfsetup/install/files/lib/event/user/UserRankUpdated.class.php b/wcfsetup/install/files/lib/event/user/UserRankUpdated.class.php new file mode 100644 index 00000000000..e68168f534a --- /dev/null +++ b/wcfsetup/install/files/lib/event/user/UserRankUpdated.class.php @@ -0,0 +1,22 @@ + + * @since 6.3 + */ +final class UserRankUpdated implements IPsr14Event +{ + public function __construct( + public readonly User $user, + public readonly ?int $newRankID, + ) {} +} diff --git a/wcfsetup/install/files/lib/system/user/activity/point/UserActivityPointHandler.class.php b/wcfsetup/install/files/lib/system/user/activity/point/UserActivityPointHandler.class.php index b720ba3d688..c18610f4969 100644 --- a/wcfsetup/install/files/lib/system/user/activity/point/UserActivityPointHandler.class.php +++ b/wcfsetup/install/files/lib/system/user/activity/point/UserActivityPointHandler.class.php @@ -2,9 +2,10 @@ namespace wcf\system\user\activity\point; +use wcf\command\user\UpdateUserRank; use wcf\data\object\type\ObjectType; use wcf\data\object\type\ObjectTypeCache; -use wcf\data\user\UserProfileAction; +use wcf\data\user\UserList; use wcf\system\database\util\PreparedStatementConditionBuilder; use wcf\system\exception\InvalidObjectTypeException; use wcf\system\exception\SystemException; @@ -289,7 +290,13 @@ public function getObjectTypeByName($objectType) */ protected function updateUserRanks(array $userIDs) { - $action = new UserProfileAction($userIDs, 'updateUserRank'); - $action->executeAction(); + // Do not use the `UserRuntimeCache` to get up-to-date values. + $userList = new UserList(); + $userList->setObjectIDs($userIDs); + $userList->readObjects(); + + foreach ($userList->getObjects() as $user) { + (new UpdateUserRank($user))(); + } } } From 6cf7a99e4dc420968e1dcc732d129d401acf14f1 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Fri, 15 Aug 2025 11:57:41 +0200 Subject: [PATCH 2/4] Implement a command to update the user's online marking group --- .../acp/install_com.woltlab.wcf_step2.php | 5 +- .../files/lib/acp/form/UserEditForm.class.php | 5 +- .../command/user/UpdateUserGroups.class.php | 90 ++++++++++++++ .../user/UpdateUserOnlineMarking.class.php | 62 ++++++++++ .../files/lib/data/user/UserAction.class.php | 29 +++-- .../lib/data/user/UserProfileAction.class.php | 111 +----------------- .../user/UserOnlineMarkingUpdated.class.php | 22 ++++ .../worker/UserRebuildDataWorker.class.php | 8 +- 8 files changed, 200 insertions(+), 132 deletions(-) create mode 100644 wcfsetup/install/files/lib/command/user/UpdateUserGroups.class.php create mode 100644 wcfsetup/install/files/lib/command/user/UpdateUserOnlineMarking.class.php create mode 100644 wcfsetup/install/files/lib/event/user/UserOnlineMarkingUpdated.class.php diff --git a/wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php b/wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php index a07fed4d999..baaae8b3700 100644 --- a/wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php +++ b/wcfsetup/install/files/acp/install_com.woltlab.wcf_step2.php @@ -1,12 +1,12 @@ executeAction(); +(new UpdateUserOnlineMarking(WCF::getUser()))(); // install default reactions foreach ([ diff --git a/wcfsetup/install/files/lib/acp/form/UserEditForm.class.php b/wcfsetup/install/files/lib/acp/form/UserEditForm.class.php index 9db2b30186a..6584e7317cc 100755 --- a/wcfsetup/install/files/lib/acp/form/UserEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/UserEditForm.class.php @@ -2,6 +2,7 @@ namespace wcf\acp\form; +use wcf\command\user\UpdateUserOnlineMarking; use wcf\command\user\UpdateUserRank; use wcf\data\file\File; use wcf\data\style\Style; @@ -10,7 +11,6 @@ use wcf\data\user\User; use wcf\data\user\UserAction; use wcf\data\user\UserEditor; -use wcf\data\user\UserProfileAction; use wcf\form\AbstractForm; use wcf\system\cache\runtime\FileRuntimeCache; use wcf\system\cache\runtime\UserProfileRuntimeCache; @@ -503,8 +503,7 @@ public function save() (new UpdateUserRank($this->user->getDecoratedObject()))(); } if (MODULE_USERS_ONLINE) { - $action = new UserProfileAction([$this->user], 'updateUserOnlineMarking'); - $action->executeAction(); + (new UpdateUserOnlineMarking($this->user->getDecoratedObject()))(); } // remove assignments diff --git a/wcfsetup/install/files/lib/command/user/UpdateUserGroups.class.php b/wcfsetup/install/files/lib/command/user/UpdateUserGroups.class.php new file mode 100644 index 00000000000..370a951d70f --- /dev/null +++ b/wcfsetup/install/files/lib/command/user/UpdateUserGroups.class.php @@ -0,0 +1,90 @@ + + * @since 6.3 + */ +final class UpdateUserGroups +{ + public function __construct( + private readonly User $user + ) {} + + /** + * @return list + */ + public function __invoke(): array + { + $groupIDs = $this->user->getGroupIDs(); + + $fixGroupIDs = []; + $removeGroupIDs = []; + + if (!\in_array(UserGroup::EVERYONE, $groupIDs)) { + $fixGroupIDs[] = UserGroup::EVERYONE; + $groupIDs[] = UserGroup::EVERYONE; + } + + if ($this->user->pendingActivation()) { + if (!\in_array(UserGroup::GUESTS, $groupIDs)) { + $fixGroupIDs[] = UserGroup::GUESTS; + $groupIDs[] = UserGroup::GUESTS; + } + + if (\in_array(UserGroup::USERS, $groupIDs)) { + $removeGroupIDs[] = UserGroup::USERS; + } + } else { + if (!\in_array(UserGroup::USERS, $groupIDs)) { + $fixGroupIDs[] = UserGroup::USERS; + $groupIDs[] = UserGroup::USERS; + } + + if (\in_array(UserGroup::GUESTS, $groupIDs)) { + $removeGroupIDs[] = UserGroup::GUESTS; + } + } + + $this->addUserGroups($fixGroupIDs); + $this->removeUserGroups($removeGroupIDs); + + UserStorageHandler::getInstance()->update($this->user->userID, 'groupIDs', \serialize($groupIDs)); + + return $groupIDs; + } + + /** + * @param list $groupIDs + */ + private function addUserGroups(array $groupIDs): void + { + if ($groupIDs === []) { + return; + } + + (new UserEditor($this->user))->addToGroups($groupIDs, false, false); + } + + /** + * @param list $groupIDs + */ + private function removeUserGroups(array $groupIDs): void + { + if ($groupIDs === []) { + return; + } + + (new UserEditor($this->user))->removeFromGroups($groupIDs); + } +} diff --git a/wcfsetup/install/files/lib/command/user/UpdateUserOnlineMarking.class.php b/wcfsetup/install/files/lib/command/user/UpdateUserOnlineMarking.class.php new file mode 100644 index 00000000000..f17fa81afde --- /dev/null +++ b/wcfsetup/install/files/lib/command/user/UpdateUserOnlineMarking.class.php @@ -0,0 +1,62 @@ + + * @since 6.3 + */ +final class UpdateUserOnlineMarking +{ + public function __construct( + private readonly User $user + ) {} + + public function __invoke(): void + { + $groupIDs = (new UpdateUserGroups($this->user))(); + + $newOnlineGroupID = $this->newOnlineGroupID($groupIDs); + + $this->updateOnlineGroupID($newOnlineGroupID); + + $event = new UserOnlineMarkingUpdated($this->user, $newOnlineGroupID); + EventHandler::getInstance()->fire($event); + } + + /** + * @param list $groupIDs + */ + private function newOnlineGroupID(array $groupIDs): ?int + { + $conditionBuilder = new PreparedStatementConditionBuilder(); + $conditionBuilder->add('groupID IN (?)', [$groupIDs]); + + $sql = "SELECT groupID + FROM wcf1_user_group + " . $conditionBuilder . " + ORDER BY priority DESC"; + $statement = WCF::getDB()->prepare($sql, 1); + $statement->execute($conditionBuilder->getParameters()); + + return $statement->fetchSingleColumn() ?: null; + } + + private function updateOnlineGroupID(?int $newOnlineGroupID): void + { + $sql = "UPDATE wcf1_user SET userOnlineGroupID = ? WHERE userID = ?"; + $statement = WCF::getDB()->prepare($sql); + + $statement->execute([$newOnlineGroupID, $this->user->userID]); + } +} diff --git a/wcfsetup/install/files/lib/data/user/UserAction.class.php b/wcfsetup/install/files/lib/data/user/UserAction.class.php index f433908bc0e..31cb8b1c1a0 100644 --- a/wcfsetup/install/files/lib/data/user/UserAction.class.php +++ b/wcfsetup/install/files/lib/data/user/UserAction.class.php @@ -3,6 +3,7 @@ namespace wcf\data\user; use ParagonIE\ConstantTime\Hex; +use wcf\command\user\UpdateUserOnlineMarking; use wcf\command\user\UpdateUserRank; use wcf\data\AbstractDatabaseObjectAction; use wcf\data\file\FileAction; @@ -326,9 +327,9 @@ public function create() if (MODULE_USER_RANK) { (new UpdateUserRank($userEditor->getDecoratedObject()))(); } - // update user online marking - $action = new UserProfileAction([$userEditor], 'updateUserOnlineMarking'); - $action->executeAction(); + if (MODULE_USERS_ONLINE) { + (new UpdateUserOnlineMarking($userEditor->getDecoratedObject()))(); + } } return $user; @@ -475,14 +476,13 @@ public function removeFromGroups() UserEditor::resetCache(); $this->readObjects(); - if (MODULE_USER_RANK) { - foreach ($this->objects as $userEditor) { + foreach ($this->objects as $userEditor) { + if (MODULE_USER_RANK) { (new UpdateUserRank($userEditor->getDecoratedObject()))(); } - } - if (MODULE_USERS_ONLINE) { - $action = new UserProfileAction($this->objects, 'updateUserOnlineMarking'); - $action->executeAction(); + if (MODULE_USERS_ONLINE) { + (new UpdateUserOnlineMarking($userEditor->getDecoratedObject()))(); + } } } @@ -521,14 +521,13 @@ public function addToGroups() UserEditor::resetCache(); $this->readObjects(); - if (MODULE_USER_RANK) { - foreach ($this->objects as $userEditor) { + foreach ($this->objects as $userEditor) { + if (MODULE_USER_RANK) { (new UpdateUserRank($userEditor->getDecoratedObject()))(); } - } - if (MODULE_USERS_ONLINE) { - $action = new UserProfileAction($this->objects, 'updateUserOnlineMarking'); - $action->executeAction(); + if (MODULE_USERS_ONLINE) { + (new UpdateUserOnlineMarking($userEditor->getDecoratedObject()))(); + } } } diff --git a/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php b/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php index c59676817e4..98fa65a84aa 100644 --- a/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php +++ b/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php @@ -2,9 +2,9 @@ namespace wcf\data\user; +use wcf\command\user\UpdateUserOnlineMarking; use wcf\command\user\UpdateUserRank; use wcf\data\object\type\ObjectTypeCache; -use wcf\data\user\group\UserGroup; use wcf\system\bbcode\BBCodeHandler; use wcf\system\cache\runtime\UserProfileRuntimeCache; use wcf\system\database\util\PreparedStatementConditionBuilder; @@ -344,6 +344,8 @@ public function updateUserRank() * Updates user online markings. * * @return void + * + * @deprecated 6.3 use the `UpdateUserOnlineMarking` command instead. */ public function updateUserOnlineMarking() { @@ -351,111 +353,8 @@ public function updateUserOnlineMarking() $this->readObjects(); } - $fixUserGroupIDs = $userToGroup = $removeFromGroupIDs = []; - $newGroupIDs = []; - foreach ($this->getObjects() as $user) { - $groupIDs = $user->getGroupIDs(); - if (!\in_array(UserGroup::EVERYONE, $groupIDs)) { - $fixUserGroupIDs[$user->userID] = [UserGroup::EVERYONE]; - $groupIDs[] = UserGroup::EVERYONE; - } - if ($user->pendingActivation()) { - if (!\in_array(UserGroup::GUESTS, $groupIDs)) { - if (!isset($fixUserGroupIDs[$user->userID])) { - $fixUserGroupIDs[$user->userID] = []; - } - $fixUserGroupIDs[$user->userID][] = UserGroup::GUESTS; - $groupIDs[] = UserGroup::GUESTS; - } - - if (\in_array(UserGroup::USERS, $groupIDs)) { - if (!isset($removeFromGroupIDs[$user->userID])) { - $removeFromGroupIDs[$user->userID] = []; - } - - $removeFromGroupIDs[$user->userID][] = UserGroup::USERS; - } - } else { - if (!\in_array(UserGroup::USERS, $groupIDs)) { - if (!isset($fixUserGroupIDs[$user->userID])) { - $fixUserGroupIDs[$user->userID] = []; - } - $fixUserGroupIDs[$user->userID][] = UserGroup::USERS; - $groupIDs[] = UserGroup::USERS; - } - - if (\in_array(UserGroup::GUESTS, $groupIDs)) { - if (!isset($removeFromGroupIDs[$user->userID])) { - $removeFromGroupIDs[$user->userID] = []; - } - - $removeFromGroupIDs[$user->userID][] = UserGroup::GUESTS; - } - } - $newGroupIDs[$user->userID] = $groupIDs; - - $conditionBuilder = new PreparedStatementConditionBuilder(); - $conditionBuilder->add('groupID IN (?)', [$groupIDs]); - - $sql = "SELECT groupID - FROM wcf1_user_group - " . $conditionBuilder . " - ORDER BY priority DESC"; - $statement = WCF::getDB()->prepare($sql, 1); - $statement->execute($conditionBuilder->getParameters()); - $row = $statement->fetchArray(); - if ($row['groupID'] != $user->userOnlineGroupID) { - $userToGroup[$user->userID] = $row['groupID']; - } - } - - // add users to missing default user groups - if (!empty($fixUserGroupIDs)) { - $sql = "INSERT INTO wcf1_user_to_group - (userID, groupID) - VALUES (?, ?)"; - $statement = WCF::getDB()->prepare($sql); - - WCF::getDB()->beginTransaction(); - foreach ($fixUserGroupIDs as $userID => $groupIDs) { - foreach ($groupIDs as $groupID) { - $statement->execute([$userID, $groupID]); - } - - UserStorageHandler::getInstance()->update($userID, 'groupIDs', \serialize($newGroupIDs[$userID])); - } - WCF::getDB()->commitTransaction(); - } - - if ($removeFromGroupIDs !== []) { - $sql = "DELETE FROM wcf1_user_to_group - WHERE userID = ? - AND groupID = ?"; - $statement = WCF::getDB()->prepare($sql); - - WCF::getDB()->beginTransaction(); - foreach ($removeFromGroupIDs as $userID => $groupIDs) { - foreach ($groupIDs as $groupID) { - $statement->execute([$userID, $groupID]); - } - } - WCF::getDB()->commitTransaction(); - } - - if (!empty($userToGroup)) { - $sql = "UPDATE wcf1_user - SET userOnlineGroupID = ? - WHERE userID = ?"; - $statement = WCF::getDB()->prepare($sql); - - WCF::getDB()->beginTransaction(); - foreach ($userToGroup as $userID => $groupID) { - $statement->execute([ - $groupID, - $userID, - ]); - } - WCF::getDB()->commitTransaction(); + foreach ($this->getObjects() as $editor) { + (new UpdateUserOnlineMarking($editor->getDecoratedObject()))(); } } diff --git a/wcfsetup/install/files/lib/event/user/UserOnlineMarkingUpdated.class.php b/wcfsetup/install/files/lib/event/user/UserOnlineMarkingUpdated.class.php new file mode 100644 index 00000000000..b49c38e0ca4 --- /dev/null +++ b/wcfsetup/install/files/lib/event/user/UserOnlineMarkingUpdated.class.php @@ -0,0 +1,22 @@ + + * @since 6.3 + */ +final class UserOnlineMarkingUpdated implements IPsr14Event +{ + public function __construct( + public readonly User $user, + public readonly ?int $newOnlineGroupID + ) {} +} diff --git a/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php b/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php index 912134028ea..df581fe1667 100644 --- a/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php +++ b/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php @@ -2,6 +2,7 @@ namespace wcf\system\worker; +use wcf\command\user\UpdateUserOnlineMarking; use wcf\data\file\FileEditor; use wcf\data\reaction\type\ReactionTypeCache; use wcf\data\user\avatar\UserAvatarEditor; @@ -11,7 +12,6 @@ use wcf\data\user\UserEditor; use wcf\data\user\UserList; use wcf\data\user\UserProfile; -use wcf\data\user\UserProfileAction; use wcf\system\bbcode\BBCodeHandler; use wcf\system\database\util\PreparedStatementConditionBuilder; use wcf\system\exception\SystemException; @@ -70,10 +70,8 @@ public function execute() $userIDs[] = $user->userID; } - // update user ranks - if (!empty($users)) { - $action = new UserProfileAction($users, 'updateUserOnlineMarking'); - $action->executeAction(); + foreach ($users as $user) { + (new UpdateUserOnlineMarking($user->getDecoratedObject()))(); } $this->updateUserOnlineStatus($users); From c12a7db164a0330aa2ef5e168b7018597715a063 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Fri, 15 Aug 2025 12:07:57 +0200 Subject: [PATCH 3/4] Implement a command to update the special trophies for a user --- .../user/UpdateUserSpecialTrophies.class.php | 65 +++++++++++++++++++ .../lib/data/user/UserProfileAction.class.php | 31 ++------- .../user/trophy/UserTrophyAction.class.php | 18 ++--- .../user/UserSpecialTrophiesUpdated.class.php | 23 +++++++ 4 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 wcfsetup/install/files/lib/command/user/UpdateUserSpecialTrophies.class.php create mode 100644 wcfsetup/install/files/lib/event/user/UserSpecialTrophiesUpdated.class.php diff --git a/wcfsetup/install/files/lib/command/user/UpdateUserSpecialTrophies.class.php b/wcfsetup/install/files/lib/command/user/UpdateUserSpecialTrophies.class.php new file mode 100644 index 00000000000..ff84649c7d4 --- /dev/null +++ b/wcfsetup/install/files/lib/command/user/UpdateUserSpecialTrophies.class.php @@ -0,0 +1,65 @@ + + * @since 6.3 + */ +final class UpdateUserSpecialTrophies +{ + public function __construct( + private readonly User $user, + /** @var int[] */ + private readonly array $trophyIDs + ) { + } + + public function __invoke(): void + { + $this->deleteExistingSpecialTrophies(); + $this->insertSpecialTrophies(); + + UserStorageHandler::getInstance()->reset([$this->user->userID], 'specialTrophies'); + + $event = new UserSpecialTrophiesUpdated($this->user, $this->trophyIDs); + EventHandler::getInstance()->fire($event); + } + + private function deleteExistingSpecialTrophies(): void + { + $sql = "DELETE FROM wcf1_user_special_trophy + WHERE userID = ?"; + $statement = WCF::getDB()->prepare($sql); + $statement->execute([$this->user->userID]); + } + + private function insertSpecialTrophies(): void + { + if ($this->trophyIDs === []) { + return; + } + + $sql = "INSERT INTO wcf1_user_special_trophy + (userID, trophyID) + VALUES (?, ?)"; + $statement = WCF::getDB()->prepare($sql); + + foreach ($this->trophyIDs as $trophyID) { + $statement->execute([ + $this->user->userID, + $trophyID, + ]); + } + } +} diff --git a/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php b/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php index 98fa65a84aa..081dbfdf79f 100644 --- a/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php +++ b/wcfsetup/install/files/lib/data/user/UserProfileAction.class.php @@ -4,6 +4,7 @@ use wcf\command\user\UpdateUserOnlineMarking; use wcf\command\user\UpdateUserRank; +use wcf\command\user\UpdateUserSpecialTrophies; use wcf\data\object\type\ObjectTypeCache; use wcf\system\bbcode\BBCodeHandler; use wcf\system\cache\runtime\UserProfileRuntimeCache; @@ -16,7 +17,6 @@ use wcf\system\option\user\UserOptionHandler; use wcf\system\upload\UploadFile; use wcf\system\user\group\assignment\UserGroupAssignmentHandler; -use wcf\system\user\storage\UserStorageHandler; use wcf\system\WCF; use wcf\util\ArrayUtil; use wcf\util\MessageUtil; @@ -362,6 +362,8 @@ public function updateUserOnlineMarking() * Updates the special trophies. * * @return void + * + * @deprecated 6.3 use the `UpdateUserSpecialTrophies` command instead. */ public function updateSpecialTrophies() { @@ -369,33 +371,8 @@ public function updateSpecialTrophies() $this->readObjects(); } - $sql = "DELETE FROM wcf1_user_special_trophy - WHERE userID = ?"; - $deleteStatement = WCF::getDB()->prepare($sql); - - $sql = "INSERT INTO wcf1_user_special_trophy - (userID, trophyID) - VALUES (?, ?)"; - $insertStatement = WCF::getDB()->prepare($sql); - foreach ($this->getObjects() as $user) { - WCF::getDB()->beginTransaction(); - - // delete all user special trophies for the user - $deleteStatement->execute([$user->userID]); - - if (!empty($this->parameters['trophyIDs'])) { - foreach ($this->parameters['trophyIDs'] as $trophyID) { - $insertStatement->execute([ - $user->userID, - $trophyID, - ]); - } - } - - WCF::getDB()->commitTransaction(); - - UserStorageHandler::getInstance()->reset([$user->userID], 'specialTrophies'); + (new UpdateUserSpecialTrophies($user->getDecoratedObject(), $this->parameters['trophyIDs'] ?? []))(); } } diff --git a/wcfsetup/install/files/lib/data/user/trophy/UserTrophyAction.class.php b/wcfsetup/install/files/lib/data/user/trophy/UserTrophyAction.class.php index 8556ba7d8a8..94aa6b4e154 100644 --- a/wcfsetup/install/files/lib/data/user/trophy/UserTrophyAction.class.php +++ b/wcfsetup/install/files/lib/data/user/trophy/UserTrophyAction.class.php @@ -2,10 +2,10 @@ namespace wcf\data\user\trophy; +use wcf\command\user\UpdateUserSpecialTrophies; use wcf\data\AbstractDatabaseObjectAction; use wcf\data\user\UserAction; use wcf\data\user\UserProfile; -use wcf\data\user\UserProfileAction; use wcf\system\cache\runtime\UserProfileRuntimeCache; use wcf\system\database\util\PreparedStatementConditionBuilder; use wcf\system\exception\IllegalLinkException; @@ -71,16 +71,12 @@ public function create() } if (!$hasTrophy) { - $userProfileAction = new UserProfileAction( - [$userTrophy->getUserProfile()->getDecoratedObject()], - 'updateSpecialTrophies', - [ - 'trophyIDs' => \array_unique(\array_merge(\array_map(static function ($trophy) { - return $trophy->trophyID; - }, $userTrophy->getUserProfile()->getSpecialTrophies()), [$userTrophy->trophyID])), - ] - ); - $userProfileAction->executeAction(); + (new UpdateUserSpecialTrophies( + $userTrophy->getUserProfile()->getDecoratedObject(), + \array_unique(\array_merge(\array_map(static function ($trophy) { + return $trophy->trophyID; + }, $userTrophy->getUserProfile()->getSpecialTrophies()), [$userTrophy->trophyID])) + ))(); } } } diff --git a/wcfsetup/install/files/lib/event/user/UserSpecialTrophiesUpdated.class.php b/wcfsetup/install/files/lib/event/user/UserSpecialTrophiesUpdated.class.php new file mode 100644 index 00000000000..084715310c9 --- /dev/null +++ b/wcfsetup/install/files/lib/event/user/UserSpecialTrophiesUpdated.class.php @@ -0,0 +1,23 @@ + + * @since 6.3 + */ +final class UserSpecialTrophiesUpdated implements IPsr14Event +{ + public function __construct( + public readonly User $user, + /** @var int[] */ + public readonly array $trophyIDs + ) {} +} From 60123ece4728acc5cf9ec9ad3721d33731f08c58 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Fri, 15 Aug 2025 12:15:51 +0200 Subject: [PATCH 4/4] Rename `UpdateUserGroups` to `UpdateMandatoryUserGroups` --- ...rGroups.class.php => UpdateMandatoryUserGroups.class.php} | 5 +++-- .../files/lib/command/user/UpdateUserOnlineMarking.class.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) rename wcfsetup/install/files/lib/command/user/{UpdateUserGroups.class.php => UpdateMandatoryUserGroups.class.php} (92%) diff --git a/wcfsetup/install/files/lib/command/user/UpdateUserGroups.class.php b/wcfsetup/install/files/lib/command/user/UpdateMandatoryUserGroups.class.php similarity index 92% rename from wcfsetup/install/files/lib/command/user/UpdateUserGroups.class.php rename to wcfsetup/install/files/lib/command/user/UpdateMandatoryUserGroups.class.php index 370a951d70f..b0995ef5668 100644 --- a/wcfsetup/install/files/lib/command/user/UpdateUserGroups.class.php +++ b/wcfsetup/install/files/lib/command/user/UpdateMandatoryUserGroups.class.php @@ -8,14 +8,15 @@ use wcf\system\user\storage\UserStorageHandler; /** - * Updates the groups of users. Removes unnecessary groups and adds missing groups. + * Updates the mandatory user groups of a user. + * Adds missing groups and removes groups in which the user is not allowed to be. * * @author Olaf Braun * @copyright 2001-2025 WoltLab GmbH * @license GNU Lesser General Public License * @since 6.3 */ -final class UpdateUserGroups +final class UpdateMandatoryUserGroups { public function __construct( private readonly User $user diff --git a/wcfsetup/install/files/lib/command/user/UpdateUserOnlineMarking.class.php b/wcfsetup/install/files/lib/command/user/UpdateUserOnlineMarking.class.php index f17fa81afde..51204269abe 100644 --- a/wcfsetup/install/files/lib/command/user/UpdateUserOnlineMarking.class.php +++ b/wcfsetup/install/files/lib/command/user/UpdateUserOnlineMarking.class.php @@ -24,7 +24,7 @@ public function __construct( public function __invoke(): void { - $groupIDs = (new UpdateUserGroups($this->user))(); + $groupIDs = (new UpdateMandatoryUserGroups($this->user))(); $newOnlineGroupID = $this->newOnlineGroupID($groupIDs);