From 64e74f3b7bb0b6b7c3b83973268ac367e8d67421 Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Sun, 27 Jan 2019 12:04:28 +0100 Subject: [PATCH] Add methods to mark comment-related notifications as read (And store the ids of the liked comment/comment respones as the base object id for like notifications.) --- .../data/comment/LikeableComment.class.php | 3 +- .../LikeableCommentResponse.class.php | 3 +- .../system/comment/CommentHandler.class.php | 449 ++++++++++++++++++ 3 files changed, 453 insertions(+), 2 deletions(-) diff --git a/wcfsetup/install/files/lib/data/comment/LikeableComment.class.php b/wcfsetup/install/files/lib/data/comment/LikeableComment.class.php index 06a3939843..ac0c497469 100644 --- a/wcfsetup/install/files/lib/data/comment/LikeableComment.class.php +++ b/wcfsetup/install/files/lib/data/comment/LikeableComment.class.php @@ -62,7 +62,8 @@ class LikeableComment extends AbstractLikeObject implements IReactionObject { [ 'objectID' => $this->getDecoratedObject()->objectID, 'objectOwnerID' => $this->userID - ] + ], + $this->commentID ); } } diff --git a/wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php b/wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php index 00655bef14..c0cc553498 100644 --- a/wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php +++ b/wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php @@ -65,7 +65,8 @@ class LikeableCommentResponse extends AbstractLikeObject implements IReactionObj 'commentID' => $comment->commentID, 'commentUserID' => $comment->userID, 'objectID' => $comment->objectID - ] + ], + $this->responseID ); } } diff --git a/wcfsetup/install/files/lib/system/comment/CommentHandler.class.php b/wcfsetup/install/files/lib/system/comment/CommentHandler.class.php index 171d6ef2eb..7fd43c0b69 100644 --- a/wcfsetup/install/files/lib/system/comment/CommentHandler.class.php +++ b/wcfsetup/install/files/lib/system/comment/CommentHandler.class.php @@ -3,9 +3,11 @@ namespace wcf\system\comment; use wcf\data\comment\response\CommentResponseList; use wcf\data\comment\CommentEditor; use wcf\data\comment\CommentList; +use wcf\data\comment\response\StructuredCommentResponseList; use wcf\data\comment\StructuredCommentList; use wcf\data\object\type\ObjectType; use wcf\data\object\type\ObjectTypeCache; +use wcf\data\user\notification\UserNotificationList; use wcf\system\comment\manager\ICommentManager; use wcf\system\exception\NamedUserException; use wcf\system\exception\SystemException; @@ -231,6 +233,453 @@ class CommentHandler extends SingletonFactory { } } + /** + * Marks all comment-related notifications for objects of the given object type and with + * the given ids as confirmed for the active user. + * + * @param string $objectType comment object type name + * @param integer[] $objectIDs ids of the objects whose comment-related notifications will be marked as confirmed + * @param integer $time only notifications older than the given timestamp will be marked as confirmed + * @throws \InvalidArgumentException if invalid comment object type name is given + * @since 5.2 + */ + public function markNotificationsAsConfirmed($objectType, array $objectIDs, $time = TIME_NOW) { + if ($this->getObjectTypeID($objectType) === null) { + throw new \InvalidArgumentException("Unknown comment object type '{$objectType}'."); + } + + if (empty($objectIDs)) { + return; + } + + // 1. comments + + // mark comment notifications as confirmed + $commentEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType.'.notification')) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.notification') as $event) { + $commentEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.notification' + ]; + } + } + + if (!empty($commentEvents)) { + $notificationList = new UserNotificationList(); + $notificationList->getConditionBuilder()->add('user_notification.eventID IN (?)', [array_keys($commentEvents)]); + $notificationList->getConditionBuilder()->add('user_notification.userID = ?', [WCF::getUser()->userID]); + $notificationList->sqlJoins .= " LEFT JOIN wcf" . WCF_N . "_comment comment + ON (comment.commentID = user_notification.objectID + AND comment.objectTypeID = " . intval($this->getObjectTypeID($objectType)) . ")"; + $notificationList->getConditionBuilder()->add('comment.objectID IN (?)', [$objectIDs]); + $notificationList->getConditionBuilder()->add('comment.time <= ?', [$time]); + $notificationList->readObjects(); + + $notificationObjectIDs = []; + foreach ($notificationList as $notification) { + if (!isset($notificationObjectIDs[$notification->eventID])) { + $notificationObjectIDs[$notification->eventID] = []; + } + + $notificationObjectIDs[$notification->eventID][] = $notification->objectID; + } + + if (!empty($notificationObjectIDs)) { + foreach ($notificationObjectIDs as $eventID => $commentIDs) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $commentEvents[$eventID]['eventName'], + $commentEvents[$eventID]['objectType'], + [WCF::getUser()->userID], + $commentIDs + ); + } + } + } + + // mark comment reaction notifications as confirmed + $reactionCommentEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType . '.like.notification') !== null) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.like.notification') as $event) { + $reactionCommentEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.like.notification' + ]; + } + } + + if (!empty($reactionCommentEvents)) { + // the value of the `objectID` property of the notifications is the like object + // id which is currently unknown, thus it needs to be read from database + $notificationList = new UserNotificationList(); + $notificationList->getConditionBuilder()->add('user_notification.eventID IN (?)', [array_keys($reactionCommentEvents)]); + $notificationList->getConditionBuilder()->add('user_notification.userID = ?', [WCF::getUser()->userID]); + $notificationList->sqlJoins .= " LEFT JOIN wcf" . WCF_N . "_comment comment + ON (comment.commentID = user_notification.baseObjectID + AND comment.objectTypeID = " . intval($this->getObjectTypeID($objectType)) . ")"; + $notificationList->getConditionBuilder()->add('comment.objectID IN (?)', [$objectIDs]); + $notificationList->getConditionBuilder()->add('comment.time <= ?', [$time]); + $notificationList->readObjects(); + + $notificationObjectIDs = []; + foreach ($notificationList as $notification) { + if (!isset($notificationObjectIDs[$notification->eventID])) { + $notificationObjectIDs[$notification->eventID] = []; + } + + $notificationObjectIDs[$notification->eventID][] = $notification->objectID; + } + + if (!empty($notificationObjectIDs)) { + foreach ($notificationObjectIDs as $eventID => $reactionIDs) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $reactionCommentEvents[$eventID]['eventName'], + $reactionCommentEvents[$eventID]['objectType'], + [WCF::getUser()->userID], + $reactionIDs + ); + } + } + } + + // 2. responses + + // mark response notifications as confirmed + $responseEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType.'.response.notification')) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.response.notification') as $event) { + $responseEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.response.notification' + ]; + } + } + + if (!empty($responseEvents)) { + $notificationList = new UserNotificationList(); + $notificationList->getConditionBuilder()->add('user_notification.eventID IN (?)', [array_keys($responseEvents)]); + $notificationList->getConditionBuilder()->add('user_notification.userID = ?', [WCF::getUser()->userID]); + $notificationList->sqlJoins .= " LEFT JOIN wcf" . WCF_N . "_comment_response comment_response + ON (comment_response.responseID = user_notification.objectID) + LEFT JOIN wcf" . WCF_N . "_comment comment + ON (comment.commentID = comment_response.commentID)"; + $notificationList->getConditionBuilder()->add('comment.objectTypeID IN (?)', [$this->getObjectTypeID($objectType)]); + $notificationList->getConditionBuilder()->add('comment.objectID IN (?)', [$objectIDs]); + $notificationList->getConditionBuilder()->add('comment_response.time <= ?', [$time]); + $notificationList->readObjects(); + + $notificationObjectIDs = []; + foreach ($notificationList as $notification) { + if (!isset($notificationObjectIDs[$notification->eventID])) { + $notificationObjectIDs[$notification->eventID] = []; + } + + $notificationObjectIDs[$notification->eventID][] = $notification->objectID; + } + + if (!empty($notificationObjectIDs)) { + foreach ($notificationObjectIDs as $eventID => $responseIDs) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $responseEvents[$eventID]['eventName'], + $responseEvents[$eventID]['objectType'], + [WCF::getUser()->userID], + $responseIDs + ); + } + } + } + + // mark comment response reaction notifications as confirmed + $reactionResponseEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType . '.response.like.notification') !== null) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.response.like.notification') as $event) { + $reactionResponseEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.response.like.notification' + ]; + } + } + + if (!empty($reactionResponseEvents)) { + // the value of the `objectID` property of the notifications is the like object + // id which is currently unknown, thus it needs to be read from database + $notificationList = new UserNotificationList(); + $notificationList->getConditionBuilder()->add('user_notification.eventID IN (?)', [array_keys($reactionResponseEvents)]); + $notificationList->getConditionBuilder()->add('user_notification.userID = ?', [WCF::getUser()->userID]); + $notificationList->sqlJoins .= " LEFT JOIN wcf" . WCF_N . "_comment_response comment_response + ON (comment_response.responseID = user_notification.baseObjectID) + LEFT JOIN wcf" . WCF_N . "_comment comment + ON (comment.commentID = comment_response.commentID)"; + $notificationList->getConditionBuilder()->add('comment.objectTypeID IN (?)', [$this->getObjectTypeID($objectType)]); + $notificationList->getConditionBuilder()->add('comment.objectID IN (?)', [$objectIDs]); + $notificationList->getConditionBuilder()->add('comment_response.time <= ?', [$time]); + $notificationList->readObjects(); + + $notificationObjectIDs = []; + foreach ($notificationList as $notification) { + if (!isset($notificationObjectIDs[$notification->eventID])) { + $notificationObjectIDs[$notification->eventID] = []; + } + + $notificationObjectIDs[$notification->eventID][] = $notification->objectID; + } + + if (!empty($notificationObjectIDs)) { + foreach ($notificationObjectIDs as $eventID => $reactionIDs) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $reactionResponseEvents[$eventID]['eventName'], + $reactionResponseEvents[$eventID]['objectType'], + [WCF::getUser()->userID], + $reactionIDs + ); + } + } + } + } + + /** + * Marks all comment-related notifications for objects of the given object type in the + * given comment list as confirmed for the active user. + * + * @param string $objectType comment object type name + * @param StructuredCommentList $commentList comments whose notifications will be marked as read + * @throws \InvalidArgumentException if invalid comment object type name is given + * @since 5.2 + */ + public function markNotificationsAsConfirmedForCommentList($objectType, StructuredCommentList $commentList) { + if ($this->getObjectTypeID($objectType) === null) { + throw new \InvalidArgumentException("Unknown comment object type '{$objectType}'."); + } + + if (count($commentList) === 0) { + return; + } + + // 1. comments + + // mark comment notifications as confirmed + $commentEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType.'.notification')) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.notification') as $event) { + $commentEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.notification' + ]; + } + } + + if (!empty($commentEvents)) { + foreach ($commentEvents as $eventID => $eventData) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $eventData['eventName'], + $eventData['objectType'], + [WCF::getUser()->userID], + $commentList->getObjectIDs() + ); + } + } + + // mark comment reaction notifications as confirmed + $reactionCommentEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType . '.like.notification') !== null) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.like.notification') as $event) { + $reactionCommentEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.like.notification' + ]; + } + } + + if (!empty($reactionCommentEvents)) { + // the value of the `objectID` property of the notifications is the like object + // id which is currently unknown, thus it needs to be read from database + $notificationList = new UserNotificationList(); + $notificationList->getConditionBuilder()->add('user_notification.eventID IN (?)', [array_keys($reactionCommentEvents)]); + $notificationList->getConditionBuilder()->add('user_notification.userID = ?', [WCF::getUser()->userID]); + $notificationList->getConditionBuilder()->add('user_notification.baseObjectID IN (?)', [$commentList->getObjectIDs()]); + $notificationList->readObjects(); + + $objectIDs = []; + foreach ($notificationList as $notification) { + if (!isset($objectIDs[$notification->eventID])) { + $objectIDs[$notification->eventID] = []; + } + + $objectIDs[$notification->eventID][] = $notification->objectID; + } + + if (!empty($objectIDs)) { + foreach ($objectIDs as $eventID => $reactionIDs) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $reactionCommentEvents[$eventID]['eventName'], + $reactionCommentEvents[$eventID]['objectType'], + [WCF::getUser()->userID], + $reactionIDs + ); + } + } + } + + // 2. responses + + $responseIDs = []; + foreach ($commentList as $comment) { + // as we do not know whether `Comment::getUnfilteredResponseIDs()` + // or `Comment::getResponseIDs()` has been used, collect response + // ids manually + foreach ($comment as $response) { + $responseIDs[] = $response->responseID; + } + } + + if (!empty($responseIDs)) { + // mark response notifications as confirmed + $responseEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType.'.response.notification')) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.response.notification') as $event) { + $responseEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.response.notification' + ]; + } + } + + if (!empty($responseEvents)) { + foreach ($responseEvents as $eventID => $eventData) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $eventData['eventName'], + $eventData['objectType'], + [WCF::getUser()->userID], + $responseIDs + ); + } + } + + // mark comment response reaction notifications as confirmed + $reactionResponseEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType . '.response.like.notification') !== null) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.response.like.notification') as $event) { + $reactionResponseEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.response.like.notification' + ]; + } + } + + if (!empty($reactionResponseEvents)) { + // the value of the `objectID` property of the notifications is the like object + // id which is currently unknown, thus it needs to be read from database + $notificationList = new UserNotificationList(); + $notificationList->getConditionBuilder()->add('user_notification.eventID IN (?)', [array_keys($reactionResponseEvents)]); + $notificationList->getConditionBuilder()->add('user_notification.userID = ?', [WCF::getUser()->userID]); + $notificationList->getConditionBuilder()->add('user_notification.baseObjectID IN (?)', [$responseIDs]); + $notificationList->readObjects(); + + $objectIDs = []; + foreach ($notificationList as $notification) { + if (!isset($objectIDs[$notification->eventID])) { + $objectIDs[$notification->eventID] = []; + } + + $objectIDs[$notification->eventID][] = $notification->objectID; + } + + if (!empty($objectIDs)) { + foreach ($objectIDs as $eventID => $reactionIDs) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $reactionResponseEvents[$eventID]['eventName'], + $reactionResponseEvents[$eventID]['objectType'], + [WCF::getUser()->userID], + $reactionIDs + ); + } + } + } + } + } + + /** + * Marks all comment response-related notifications for objects of the given object type in + * the given comment response list as confirmed for the active user. + * + * @param string $objectType comment object type name + * @param StructuredCommentResponseList $responseList comment responses whose notifications will be marked as read + * + * @throws \InvalidArgumentException if invalid comment object type name is given + * @since 5.2 + */ + public function markNotificationsAsConfirmedForResponseList($objectType, StructuredCommentResponseList $responseList) { + if ($this->getObjectTypeID($objectType) === null) { + throw new \InvalidArgumentException("Unknown comment object type '{$objectType}'."); + } + + if (count($responseList) === 0) { + return; + } + + $responseEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType.'.response.notification')) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.response.notification') as $event) { + $responseEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.response.notification' + ]; + } + } + + if (!empty($responseEvents)) { + foreach ($responseEvents as $eventID => $eventData) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $eventData['eventName'], + $eventData['objectType'], + [WCF::getUser()->userID], + $responseList->getObjectIDs() + ); + } + } + + // mark comment response reaction notifications as confirmed + $reactionResponseEvents = []; + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType . '.response.like.notification') !== null) { + foreach (UserNotificationHandler::getInstance()->getEvents($objectType . '.response.like.notification') as $event) { + $reactionResponseEvents[$event->eventID] = [ + 'eventName' => $event->eventName, + 'objectType' => $objectType . '.response.like.notification' + ]; + } + } + + if (!empty($reactionResponseEvents)) { + // the value of the `objectID` property of the notifications is the like object + // id which is currently unknown, thus it needs to be read from database + $notificationList = new UserNotificationList(); + $notificationList->getConditionBuilder()->add('user_notification.eventID IN (?)', [array_keys($reactionResponseEvents)]); + $notificationList->getConditionBuilder()->add('user_notification.userID = ?', [WCF::getUser()->userID]); + $notificationList->getConditionBuilder()->add('user_notification.baseObjectID IN (?)', [$responseList->getObjectIDs()]); + $notificationList->readObjects(); + + $objectIDs = []; + foreach ($notificationList as $notification) { + if (!isset($objectIDs[$notification->eventID])) { + $objectIDs[$notification->eventID] = []; + } + + $objectIDs[$notification->eventID][] = $notification->objectID; + } + + if (!empty($objectIDs)) { + foreach ($objectIDs as $eventID => $reactionIDs) { + UserNotificationHandler::getInstance()->markAsConfirmed( + $reactionResponseEvents[$eventID]['eventName'], + $reactionResponseEvents[$eventID]['objectType'], + [WCF::getUser()->userID], + $reactionIDs + ); + } + } + } + } + /** * Enforces the censorship. * -- 2.20.1