diff --git a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.3_step1.php b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.3_step1.php index 2aee5c18e77..c089e60b350 100644 --- a/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.3_step1.php +++ b/wcfsetup/install/files/acp/database/update_com.woltlab.wcf_6.3_step1.php @@ -113,6 +113,12 @@ ->columns([ IntDatabaseTableColumn::create('templateGroupID'), ]), + PartialDatabaseTable::create('wcf1_user_activity_event') + ->columns([ + IntDatabaseTableColumn::create('userID'), + VarcharDatabaseTableColumn::create('username') + ->length(255), + ]), PartialDatabaseTable::create('wcf1_acp_session_log') ->columns([ BinaryDatabaseTableColumn::create('sessionID') diff --git a/wcfsetup/install/files/lib/command/article/CreateArticle.class.php b/wcfsetup/install/files/lib/command/article/CreateArticle.class.php index 906b6d3d016..f8e9a107227 100644 --- a/wcfsetup/install/files/lib/command/article/CreateArticle.class.php +++ b/wcfsetup/install/files/lib/command/article/CreateArticle.class.php @@ -51,7 +51,8 @@ public function __invoke(): Article $article->articleID, null, $article->userID, - $article->time + $article->time, + username: $article->username ); new MarkArticleAsRead($article)(); diff --git a/wcfsetup/install/files/lib/command/article/PublishArticle.class.php b/wcfsetup/install/files/lib/command/article/PublishArticle.class.php index 7cbbef226ee..9c149ff06d2 100644 --- a/wcfsetup/install/files/lib/command/article/PublishArticle.class.php +++ b/wcfsetup/install/files/lib/command/article/PublishArticle.class.php @@ -31,7 +31,7 @@ public function __invoke(): void ->update(); $this->updateUserWatch($this->article); - $this->addUserActivity($this->article->articleID, $this->article->userID); + $this->addUserActivity($this->article); if ($this->article->userID !== null) { ArticleBuilder::incrementArticleCounter($this->article->userID, 1); @@ -54,14 +54,15 @@ private function updateUserWatch(Article $article): void ); } - private function addUserActivity(int $articleID, int $userID): void + private function addUserActivity(Article $article): void { UserActivityEventHandler::getInstance()->fireEvent( 'com.woltlab.wcf.article.recentActivityEvent', - $articleID, + $article->articleID, null, - $userID, - \TIME_NOW + $article->userID, + \TIME_NOW, + username: $article->username ); } } diff --git a/wcfsetup/install/files/lib/command/article/UpdateArticle.class.php b/wcfsetup/install/files/lib/command/article/UpdateArticle.class.php index 630a55c3307..67ecac18cf7 100644 --- a/wcfsetup/install/files/lib/command/article/UpdateArticle.class.php +++ b/wcfsetup/install/files/lib/command/article/UpdateArticle.class.php @@ -34,7 +34,6 @@ public function __invoke(): Article { $oldArticle = $this->builder->getObject(); $oldStatus = $oldArticle->publicationStatus; - $oldUserID = $oldArticle->userID; // Capture the current content before it is overwritten so that the // previous state can be stored as a version. @@ -76,9 +75,8 @@ public function __invoke(): Article $this->handlePublicationStatusChange($article, $oldStatus, $newStatus); } - $newUserID = $this->builder->properties['userID'] ?? $oldUserID; - if ($newUserID !== $oldUserID) { - $this->updateActivityEventAuthor($article->articleID, (int)$newUserID); + if ($article->userID !== $oldArticle->userID || $article->username !== $oldArticle->username) { + $this->updateActivityEventAuthor($article); } EventHandler::getInstance()->fire(new ArticleUpdated($article, $this->builder)); @@ -108,7 +106,8 @@ private function handlePublicationStatusChange(Article $article, int $oldStatus, $article->articleID, null, $article->userID, - $article->time + $article->time, + username: $article->username ); } else { UserNotificationHandler::getInstance()->removeNotifications( @@ -122,17 +121,19 @@ private function handlePublicationStatusChange(Article $article, int $oldStatus, } } - private function updateActivityEventAuthor(int $articleID, int $userID): void + private function updateActivityEventAuthor(Article $article): void { $sql = "UPDATE wcf1_user_activity_event - SET userID = ? + SET userID = ?, + username = ? WHERE objectTypeID = ? AND objectID = ?"; $statement = WCF::getDB()->prepare($sql); $statement->execute([ - $userID, + $article->userID, + $article->userID === null ? $article->username : null, UserActivityEventHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.article.recentActivityEvent'), - $articleID, + $article->articleID, ]); } diff --git a/wcfsetup/install/files/lib/command/comment/PublishComment.class.php b/wcfsetup/install/files/lib/command/comment/PublishComment.class.php index 931792d0c5d..a9b9ba17253 100644 --- a/wcfsetup/install/files/lib/command/comment/PublishComment.class.php +++ b/wcfsetup/install/files/lib/command/comment/PublishComment.class.php @@ -54,8 +54,7 @@ public function __invoke(): void private function fireActivityEvent(): void { if ( - $this->comment->userID !== null - && UserActivityEventHandler::getInstance()->getObjectTypeID( + UserActivityEventHandler::getInstance()->getObjectTypeID( $this->objectType->objectType . '.recentActivityEvent' ) !== null ) { @@ -64,7 +63,8 @@ private function fireActivityEvent(): void $this->comment->commentID, null, $this->comment->userID, - $this->comment->time + $this->comment->time, + username: $this->comment->username ); } } diff --git a/wcfsetup/install/files/lib/command/comment/response/PublishResponse.class.php b/wcfsetup/install/files/lib/command/comment/response/PublishResponse.class.php index f48066d8326..d637b688591 100644 --- a/wcfsetup/install/files/lib/command/comment/response/PublishResponse.class.php +++ b/wcfsetup/install/files/lib/command/comment/response/PublishResponse.class.php @@ -65,8 +65,7 @@ public function __invoke(): void private function fireActivityEvent(): void { if ( - $this->response->userID !== null - && UserActivityEventHandler::getInstance()->getObjectTypeID( + UserActivityEventHandler::getInstance()->getObjectTypeID( $this->objectType->objectType . '.response.recentActivityEvent' ) !== null ) { @@ -75,7 +74,8 @@ private function fireActivityEvent(): void $this->response->responseID, null, $this->response->userID, - $this->response->time + $this->response->time, + username: $this->response->username ); } } diff --git a/wcfsetup/install/files/lib/command/user/Follow.class.php b/wcfsetup/install/files/lib/command/user/Follow.class.php index c52c4433a19..7998b152365 100644 --- a/wcfsetup/install/files/lib/command/user/Follow.class.php +++ b/wcfsetup/install/files/lib/command/user/Follow.class.php @@ -60,7 +60,8 @@ private function fireActivityEvent(): void { UserActivityEventHandler::getInstance()->fireEvent( 'com.woltlab.wcf.user.recentActivityEvent.follow', - $this->target->userID + $this->target->userID, + userID: $this->user->userID, ); } diff --git a/wcfsetup/install/files/lib/command/user/Unfollow.class.php b/wcfsetup/install/files/lib/command/user/Unfollow.class.php index 4f059502d97..7c3bde1f1c4 100644 --- a/wcfsetup/install/files/lib/command/user/Unfollow.class.php +++ b/wcfsetup/install/files/lib/command/user/Unfollow.class.php @@ -45,7 +45,8 @@ private function removeActivityEvent(): void { UserActivityEventHandler::getInstance()->removeEvent( 'com.woltlab.wcf.user.recentActivityEvent.follow', - $this->target->userID + $this->target->userID, + userID: $this->user->userID, ); } diff --git a/wcfsetup/install/files/lib/data/article/ArticleAction.class.php b/wcfsetup/install/files/lib/data/article/ArticleAction.class.php index 65d5612e08e..0afa8b2a8d9 100644 --- a/wcfsetup/install/files/lib/data/article/ArticleAction.class.php +++ b/wcfsetup/install/files/lib/data/article/ArticleAction.class.php @@ -177,7 +177,8 @@ public function create() $article->articleID, null, $article->userID, - $article->time + $article->time, + username: $article->username ); } @@ -336,7 +337,8 @@ public function update() $articleEditor->articleID, null, $this->parameters['data']['userID'] ?? $articleEditor->userID, - $this->parameters['data']['time'] ?? $articleEditor->time + $this->parameters['data']['time'] ?? $articleEditor->time, + username: $this->parameters['data']['username'] ?? $articleEditor->username ); } else { $resetArticleIDs[] = $articleEditor->articleID; @@ -363,9 +365,10 @@ public function update() } // update author in recent activities - if (isset($this->parameters['data']['userID'])) { + if (\array_key_exists('userID', $this->parameters['data'] ?? [])) { $sql = "UPDATE wcf1_user_activity_event - SET userID = ? + SET userID = ?, + username = ? WHERE objectTypeID = ? AND objectID = ?"; $statement = WCF::getDB()->prepare($sql); @@ -374,6 +377,9 @@ public function update() if ($articleEditor->userID !== $this->parameters['data']['userID']) { $statement->execute([ $this->parameters['data']['userID'], + $this->parameters['data']['userID'] === null + ? ($this->parameters['data']['username'] ?? $articleEditor->username) + : null, UserActivityEventHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.article.recentActivityEvent'), $articleEditor->articleID, ]); diff --git a/wcfsetup/install/files/lib/data/comment/CommentAction.class.php b/wcfsetup/install/files/lib/data/comment/CommentAction.class.php index 005155b4e90..f50f50fa933 100644 --- a/wcfsetup/install/files/lib/data/comment/CommentAction.class.php +++ b/wcfsetup/install/files/lib/data/comment/CommentAction.class.php @@ -412,13 +412,14 @@ public function triggerPublication() $this->commentProcessor->updateCounter($comment->objectID, 1); // fire activity event - if ($comment->userID !== null && UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.recentActivityEvent') !== null) { + if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.recentActivityEvent') !== null) { UserActivityEventHandler::getInstance()->fireEvent( $objectType->objectType . '.recentActivityEvent', $comment->commentID, null, $comment->userID, - $comment->time + $comment->time, + username: $comment->username ); } @@ -627,13 +628,14 @@ public function triggerPublicationResponse() $this->commentProcessor->updateCounter($comment->objectID, 1); // fire activity event - if ($response->userID !== null && UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.response.recentActivityEvent') !== null) { + if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.response.recentActivityEvent') !== null) { UserActivityEventHandler::getInstance()->fireEvent( $objectType->objectType . '.response.recentActivityEvent', $response->responseID, null, $response->userID, - $response->time + $response->time, + username: $response->username ); } diff --git a/wcfsetup/install/files/lib/data/user/activity/event/UserActivityEvent.class.php b/wcfsetup/install/files/lib/data/user/activity/event/UserActivityEvent.class.php index e33a526daad..ee9a0d8c8b0 100644 --- a/wcfsetup/install/files/lib/data/user/activity/event/UserActivityEvent.class.php +++ b/wcfsetup/install/files/lib/data/user/activity/event/UserActivityEvent.class.php @@ -15,7 +15,8 @@ * @property-read int $objectTypeID id of the `com.woltlab.wcf.user.recentActivityEvent` object type * @property-read int $objectID id of the object the user activity event belongs to * @property-read ?int $languageID id of the language of the related object or null if the object has no specific language - * @property-read int $userID id of the user who has triggered the user activity event + * @property-read ?int $userID id of the user who has triggered the user activity event or `null` if the event was triggered by a guest + * @property-read ?string $username name of the guest who has triggered the user activity event or `null` if the event was triggered by a registered user * @property-read int $time timestamp at which the user activity event has been triggered * @property-read mixed[] $additionalData array with additional data of the user activity event */ diff --git a/wcfsetup/install/files/lib/data/user/activity/event/ViewableUserActivityEvent.class.php b/wcfsetup/install/files/lib/data/user/activity/event/ViewableUserActivityEvent.class.php index 442a639153b..b71c9992d47 100644 --- a/wcfsetup/install/files/lib/data/user/activity/event/ViewableUserActivityEvent.class.php +++ b/wcfsetup/install/files/lib/data/user/activity/event/ViewableUserActivityEvent.class.php @@ -131,7 +131,11 @@ public function setUserProfile(UserProfile $userProfile) public function getUserProfile() { if ($this->userProfile === null) { - $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->userID); + if ($this->userID !== null) { + $this->userProfile = UserProfileRuntimeCache::getInstance()->getObject($this->userID); + } else { + $this->userProfile = UserProfile::getGuestUserProfile($this->username ?? ''); + } } return $this->userProfile; diff --git a/wcfsetup/install/files/lib/data/user/activity/event/ViewableUserActivityEventList.class.php b/wcfsetup/install/files/lib/data/user/activity/event/ViewableUserActivityEventList.class.php index f947a0fa779..4677733380d 100644 --- a/wcfsetup/install/files/lib/data/user/activity/event/ViewableUserActivityEventList.class.php +++ b/wcfsetup/install/files/lib/data/user/activity/event/ViewableUserActivityEventList.class.php @@ -62,7 +62,9 @@ public function readObjects() $userIDs = []; $eventGroups = []; foreach ($this->objects as $event) { - $userIDs[] = $event->userID; + if ($event->userID !== null) { + $userIDs[] = $event->userID; + } if (!isset($eventGroups[$event->objectTypeID])) { $objectType = UserActivityEventHandler::getInstance()->getObjectType($event->objectTypeID); diff --git a/wcfsetup/install/files/lib/page/RecentActivityListPage.class.php b/wcfsetup/install/files/lib/page/RecentActivityListPage.class.php index 145527bcfb4..160cdb92175 100644 --- a/wcfsetup/install/files/lib/page/RecentActivityListPage.class.php +++ b/wcfsetup/install/files/lib/page/RecentActivityListPage.class.php @@ -42,7 +42,7 @@ public function readData() if (UserProfileHandler::getInstance()->getIgnoredUsers(UserIgnore::TYPE_HIDE_MESSAGES) !== []) { $this->eventList->getConditionBuilder()->add( - "user_activity_event.userID NOT IN (?)", + "(user_activity_event.userID IS NULL OR user_activity_event.userID NOT IN (?))", [UserProfileHandler::getInstance()->getIgnoredUsers(UserIgnore::TYPE_HIDE_MESSAGES)] ); } diff --git a/wcfsetup/install/files/lib/system/box/RecentActivityListBoxController.class.php b/wcfsetup/install/files/lib/system/box/RecentActivityListBoxController.class.php index e5f816b5d10..e363d99fd4f 100644 --- a/wcfsetup/install/files/lib/system/box/RecentActivityListBoxController.class.php +++ b/wcfsetup/install/files/lib/system/box/RecentActivityListBoxController.class.php @@ -142,7 +142,7 @@ protected function readObjects() ); } elseif (UserProfileHandler::getInstance()->getIgnoredUsers(UserIgnore::TYPE_HIDE_MESSAGES) !== []) { $this->objectList->getConditionBuilder()->add( - "user_activity_event.userID NOT IN (?)", + "(user_activity_event.userID IS NULL OR user_activity_event.userID NOT IN (?))", [UserProfileHandler::getInstance()->getIgnoredUsers(UserIgnore::TYPE_HIDE_MESSAGES)] ); } @@ -193,7 +193,7 @@ public function getFilteredList() ); } elseif (UserProfileHandler::getInstance()->getIgnoredUsers(UserIgnore::TYPE_HIDE_MESSAGES) !== []) { $this->objectList->getConditionBuilder()->add( - "user_activity_event.userID NOT IN (?)", + "(user_activity_event.userID IS NULL OR user_activity_event.userID NOT IN (?))", [UserProfileHandler::getInstance()->getIgnoredUsers(UserIgnore::TYPE_HIDE_MESSAGES)] ); } diff --git a/wcfsetup/install/files/lib/system/user/activity/event/ArticleCommentResponseUserActivityEvent.class.php b/wcfsetup/install/files/lib/system/user/activity/event/ArticleCommentResponseUserActivityEvent.class.php index 523cec56dd5..25697da2b9b 100644 --- a/wcfsetup/install/files/lib/system/user/activity/event/ArticleCommentResponseUserActivityEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/activity/event/ArticleCommentResponseUserActivityEvent.class.php @@ -54,7 +54,7 @@ public function prepare(array $events) $comment = $this->comments[$response->commentID]; if ( isset($articleContentToArticle[$comment->objectID]) - && isset($this->commentAuthors[$comment->userID]) + && $this->getCommentAuthor($comment) !== null ) { $article = $articles[$articleContentToArticle[$comment->objectID]]; @@ -67,7 +67,7 @@ public function prepare(array $events) $event->setTitle(WCF::getLanguage()->getDynamicVariable( 'wcf.article.recentActivity.articleCommentResponse', [ - 'commentAuthor' => $this->commentAuthors[$comment->userID], + 'commentAuthor' => $this->getCommentAuthor($comment), 'commentID' => $comment->commentID, 'responseID' => $response->responseID, 'article' => $article, diff --git a/wcfsetup/install/files/lib/system/user/activity/event/PageCommentResponseUserActivityEvent.class.php b/wcfsetup/install/files/lib/system/user/activity/event/PageCommentResponseUserActivityEvent.class.php index 34ae0c395f3..66c133d74e3 100644 --- a/wcfsetup/install/files/lib/system/user/activity/event/PageCommentResponseUserActivityEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/activity/event/PageCommentResponseUserActivityEvent.class.php @@ -31,7 +31,7 @@ public function prepare(array $events) $comment = $this->comments[$response->commentID]; if ( PageCache::getInstance()->getPage($comment->objectID) !== null - && isset($this->commentAuthors[$comment->userID]) + && $this->getCommentAuthor($comment) !== null ) { $page = PageCache::getInstance()->getPage($comment->objectID); @@ -42,7 +42,7 @@ public function prepare(array $events) $event->setIsAccessible(); $event->setTitle(WCF::getLanguage()->getDynamicVariable('wcf.page.recentActivity.pageCommentResponse', [ - 'commentAuthor' => $this->commentAuthors[$comment->userID], + 'commentAuthor' => $this->getCommentAuthor($comment), 'commentID' => $comment->commentID, 'responseID' => $response->responseID, 'page' => $page, diff --git a/wcfsetup/install/files/lib/system/user/activity/event/ProfileCommentResponseUserActivityEvent.class.php b/wcfsetup/install/files/lib/system/user/activity/event/ProfileCommentResponseUserActivityEvent.class.php index d6837604ee2..4595321e541 100644 --- a/wcfsetup/install/files/lib/system/user/activity/event/ProfileCommentResponseUserActivityEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/activity/event/ProfileCommentResponseUserActivityEvent.class.php @@ -41,14 +41,14 @@ public function prepare(array $events) if (isset($this->responses[$event->objectID])) { $response = $this->responses[$event->objectID]; $comment = $this->comments[$response->commentID]; - if (isset($users[$comment->objectID]) && isset($this->commentAuthors[$comment->userID])) { + if (isset($users[$comment->objectID]) && $this->getCommentAuthor($comment) !== null) { if (!$users[$comment->objectID]->isProtected()) { $event->setIsAccessible(); $event->setTitle(WCF::getLanguage()->getDynamicVariable( 'wcf.user.profile.recentActivity.profileCommentResponse', [ - 'commentAuthor' => $this->commentAuthors[$comment->userID], + 'commentAuthor' => $this->getCommentAuthor($comment), 'commentID' => $comment->commentID, 'responseID' => $response->responseID, 'user' => $users[$comment->objectID], diff --git a/wcfsetup/install/files/lib/system/user/activity/event/TCommentResponseUserActivityEvent.class.php b/wcfsetup/install/files/lib/system/user/activity/event/TCommentResponseUserActivityEvent.class.php index 6eb25b3e436..7923b1f3bbd 100644 --- a/wcfsetup/install/files/lib/system/user/activity/event/TCommentResponseUserActivityEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/activity/event/TCommentResponseUserActivityEvent.class.php @@ -71,11 +71,28 @@ protected function readResponseData(array $events) $userIDs = []; foreach ($this->comments as $comment) { - $userIDs[] = $comment->userID; + if ($comment->userID !== null) { + $userIDs[] = $comment->userID; + } $this->commentObjectIDs[] = $comment->objectID; } if ($userIDs !== []) { $this->commentAuthors = \array_filter(UserProfileRuntimeCache::getInstance()->getObjects($userIDs)); } } + + /** + * Returns the user profile of the comment's author or a guest profile if + * the comment was written by a guest. + * + * @since 6.3 + */ + protected function getCommentAuthor(Comment $comment): ?UserProfile + { + if ($comment->userID === null) { + return UserProfile::getGuestUserProfile($comment->username); + } + + return $this->commentAuthors[$comment->userID] ?? null; + } } diff --git a/wcfsetup/install/files/lib/system/user/activity/event/UserActivityEventHandler.class.php b/wcfsetup/install/files/lib/system/user/activity/event/UserActivityEventHandler.class.php index fcee4eb28ae..8c51d1508ea 100644 --- a/wcfsetup/install/files/lib/system/user/activity/event/UserActivityEventHandler.class.php +++ b/wcfsetup/install/files/lib/system/user/activity/event/UserActivityEventHandler.class.php @@ -4,32 +4,30 @@ use wcf\data\object\type\ObjectType; use wcf\data\object\type\ObjectTypeCache; +use wcf\data\user\activity\event\UserActivityEvent; use wcf\data\user\activity\event\UserActivityEventAction; use wcf\data\user\activity\event\ViewableUserActivityEventList; use wcf\system\database\util\PreparedStatementConditionBuilder; -use wcf\system\exception\SystemException; use wcf\system\SingletonFactory; use wcf\system\WCF; /** * User activity event handler. * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License + * @author Alexander Ebert + * @copyright 2001-2026 WoltLab GmbH + * @license GNU Lesser General Public License */ -class UserActivityEventHandler extends SingletonFactory +final class UserActivityEventHandler extends SingletonFactory { /** - * cached object types * @var array{names?: array, objects?: array} */ - protected $objectTypes = []; + private array $objectTypes = []; #[\Override] - protected function init() + protected function init(): void { - // load object types $cache = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.user.recentActivityEvent'); foreach ($cache as $objectType) { $this->objectTypes['names'][$objectType->objectType] = $objectType->objectTypeID; @@ -39,20 +37,16 @@ protected function init() /** * Returns an object type by id. - * - * @return ObjectType|null */ - public function getObjectType(int $objectTypeID) + public function getObjectType(int $objectTypeID): ?ObjectType { return $this->objectTypes['objects'][$objectTypeID] ?? null; } /** * Returns an object type id by object type name. - * - * @return int|null */ - public function getObjectTypeID(string $objectType) + public function getObjectTypeID(string $objectType): ?int { return $this->objectTypes['names'][$objectType] ?? null; } @@ -61,8 +55,6 @@ public function getObjectTypeID(string $objectType) * Fires a new activity event. * * @param mixed[] $additionalData - * @return \wcf\data\user\activity\event\UserActivityEvent - * @throws SystemException */ public function fireEvent( string $objectType, @@ -70,15 +62,16 @@ public function fireEvent( ?int $languageID = null, ?int $userID = null, int $time = \TIME_NOW, - array $additionalData = [] - ) { + array $additionalData = [], + ?string $username = null + ): UserActivityEvent { $objectTypeID = $this->getObjectTypeID($objectType); if ($objectTypeID === null) { - throw new SystemException("Unknown recent activity event '" . $objectType . "'"); + throw new \BadMethodCallException("Unknown recent activity event '" . $objectType . "'"); } - if ($userID === null) { - $userID = WCF::getUser()->userID; + if ($userID === null && $username === null) { + throw new \BadMethodCallException("Recent activity events of guests require a username"); } $eventAction = new UserActivityEventAction([], 'create', [ @@ -87,6 +80,7 @@ public function fireEvent( 'objectID' => $objectID, 'languageID' => $languageID, 'userID' => $userID, + 'username' => $userID === null ? $username : null, 'time' => $time, 'additionalData' => \serialize($additionalData), ], @@ -102,14 +96,12 @@ public function fireEvent( * This method is intended for bulk processing. * * @param mixed[] $eventData - * @return void - * @throws SystemException */ - public function fireEvents(string $objectType, array $eventData) + public function fireEvents(string $objectType, array $eventData): void { $objectTypeID = $this->getObjectTypeID($objectType); if ($objectTypeID === null) { - throw new SystemException("Unknown recent activity event '" . $objectType . "'"); + throw new \BadMethodCallException("Unknown recent activity event '" . $objectType . "'"); } $itemsPerLoop = 1000; @@ -121,19 +113,27 @@ public function fireEvents(string $objectType, array $eventData) $parameters = []; foreach ($batchEventData as $data) { + $userID = $data['userID'] ?? null; + $username = $data['username'] ?? null; + + if ($userID === null && $username === null) { + throw new \BadMethodCallException("Recent activity events of guests require a username"); + } + $parameters = \array_merge($parameters, [ $objectTypeID, $data['objectID'], $data['languageID'] ?? null, - $data['userID'] ?? WCF::getUser()->userID, + $userID, + $userID !== null ? null : $username, $data['time'] ?? \TIME_NOW, \serialize($data['additionalData'] ?? []), ]); } $sql = "INSERT INTO wcf1_user_activity_event - (objectTypeID, objectID, languageID, userID, time, additionalData) - VALUES (?, ?, ?, ?, ?, ?)" . \str_repeat(', (?, ?, ?, ?, ?, ?)', \count($batchEventData) - 1); + (objectTypeID, objectID, languageID, userID, username, time, additionalData) + VALUES (?, ?, ?, ?, ?, ?, ?)" . \str_repeat(', (?, ?, ?, ?, ?, ?, ?)', \count($batchEventData) - 1); $statement = WCF::getDB()->prepare($sql); $statement->execute($parameters); } @@ -142,19 +142,12 @@ public function fireEvents(string $objectType, array $eventData) /** * Removes an activity event. - * - * @return void - * @throws SystemException */ - public function removeEvent(string $objectType, int $objectID, ?int $userID = null) + public function removeEvent(string $objectType, int $objectID, ?int $userID = null): void { $objectTypeID = $this->getObjectTypeID($objectType); if ($objectTypeID === null) { - throw new SystemException("Unknown recent activity event '" . $objectType . "'"); - } - - if ($userID === null) { - $userID = WCF::getUser()->userID; + throw new \BadMethodCallException("Unknown recent activity event '" . $objectType . "'"); } $sql = "DELETE FROM wcf1_user_activity_event @@ -172,11 +165,9 @@ public function removeEvent(string $objectType, int $objectID, ?int $userID = nu /** * Removes activity events. * - * @param int[] $objectIDs - * @return void - * @throws SystemException + * @param list $objectIDs */ - public function removeEvents(string $objectType, array $objectIDs) + public function removeEvents(string $objectType, array $objectIDs): void { if ($objectIDs === []) { return; @@ -184,7 +175,7 @@ public function removeEvents(string $objectType, array $objectIDs) $objectTypeID = $this->getObjectTypeID($objectType); if ($objectTypeID === null) { - throw new SystemException("Unknown recent activity event '" . $objectType . "'"); + throw new \BadMethodCallException("Unknown recent activity event '" . $objectType . "'"); } $conditions = new PreparedStatementConditionBuilder(); @@ -199,22 +190,20 @@ public function removeEvents(string $objectType, array $objectIDs) /** * Validates an event list and removes orphaned events. - * - * @return void */ - public static function validateEvents(ViewableUserActivityEventList $eventList) + public static function validateEvents(ViewableUserActivityEventList $eventList): void { - $eventIDs = $eventList->validateEvents(); + $orphanedEventIDs = $eventList->validateEvents(); + if ($orphanedEventIDs === []) { + return; + } - // remove orphaned event ids - if ($eventIDs !== []) { - $sql = "DELETE FROM wcf1_user_activity_event - WHERE eventID = ?"; - $statement = WCF::getDB()->prepare($sql); + $sql = "DELETE FROM wcf1_user_activity_event + WHERE eventID = ?"; + $statement = WCF::getDB()->prepare($sql); - foreach ($eventIDs as $eventID) { - $statement->execute([$eventID]); - } + foreach ($orphanedEventIDs as $eventID) { + $statement->execute([$eventID]); } } } diff --git a/wcfsetup/setup/db/install_com.woltlab.wcf.php b/wcfsetup/setup/db/install_com.woltlab.wcf.php index 8aa9af79a5d..03b8be44f41 100644 --- a/wcfsetup/setup/db/install_com.woltlab.wcf.php +++ b/wcfsetup/setup/db/install_com.woltlab.wcf.php @@ -4114,7 +4114,9 @@ NotNullInt10DatabaseTableColumn::create('objectTypeID'), NotNullInt10DatabaseTableColumn::create('objectID'), IntDatabaseTableColumn::create('languageID'), - NotNullInt10DatabaseTableColumn::create('userID'), + IntDatabaseTableColumn::create('userID'), + VarcharDatabaseTableColumn::create('username') + ->length(255), NotNullInt10DatabaseTableColumn::create('time'), TextDatabaseTableColumn::create('additionalData'), ])