From: Matthias Schmidt Date: Wed, 12 May 2021 07:49:01 +0000 (+0200) Subject: Merge branch '5.3' X-Git-Tag: 5.4.0_Alpha_3~49 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=6b4c1e13247824c2e6c775afda032a6cbc09f6c0;p=GitHub%2FWoltLab%2FWCF.git Merge branch '5.3' --- 6b4c1e13247824c2e6c775afda032a6cbc09f6c0 diff --cc wcfsetup/install/files/lib/data/comment/CommentAction.class.php index 905aa9834a,e53f4fee11..ab14c6d2e7 --- a/wcfsetup/install/files/lib/data/comment/CommentAction.class.php +++ b/wcfsetup/install/files/lib/data/comment/CommentAction.class.php @@@ -19,13 -16,11 +19,14 @@@ use wcf\system\exception\NamedUserExcep use wcf\system\exception\PermissionDeniedException; use wcf\system\exception\SystemException; use wcf\system\exception\UserInputException; +use wcf\system\flood\FloodControl; use wcf\system\html\input\HtmlInputProcessor; use wcf\system\moderation\queue\ModerationQueueActivationManager; -use wcf\system\reaction\ReactionHandler; + use wcf\system\moderation\queue\ModerationQueueManager; +use wcf\system\reaction\ReactionHandler; use wcf\system\user\activity\event\UserActivityEventHandler; +use wcf\system\user\notification\object\CommentResponseUserNotificationObject; +use wcf\system\user\notification\object\CommentUserNotificationObject; use wcf\system\user\notification\object\type\ICommentUserNotificationObjectType; use wcf\system\user\notification\object\type\IMultiRecipientCommentResponseOwnerUserNotificationObjectType; use wcf\system\user\notification\object\type\IMultiRecipientCommentUserNotificationObjectType; @@@ -37,1416 -34,1283 +38,1421 @@@ use wcf\util\UserUtil /** * Executes comment-related actions. - * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @package WoltLabSuite\Core\Data\Comment - * - * @method Comment create() - * @method CommentEditor[] getObjects() - * @method CommentEditor getSingleObject() + * + * @author Alexander Ebert + * @copyright 2001-2020 WoltLab GmbH + * @license GNU Lesser General Public License + * @package WoltLabSuite\Core\Data\Comment + * + * @method Comment create() + * @method CommentEditor[] getObjects() + * @method CommentEditor getSingleObject() */ -class CommentAction extends AbstractDatabaseObjectAction implements IMessageInlineEditorAction { - /** - * @inheritDoc - */ - protected $allowGuestAccess = ['addComment', 'addResponse', 'loadComment', 'loadComments', 'loadResponse', 'getGuestDialog']; - - /** - * captcha object type used for comments - * @var ObjectType - */ - public $captchaObjectType; - - /** - * @inheritDoc - */ - protected $className = CommentEditor::class; - - /** - * comment object - * @var Comment - */ - protected $comment; - - /** - * comment processor - * @var ICommentManager - */ - protected $commentProcessor; - - /** - * @var HtmlInputProcessor - */ - protected $htmlInputProcessor; - - /** - * response object - * @var CommentResponse - */ - protected $response; - - /** - * comment object created by addComment() - * @var Comment - */ - public $createdComment; - - /** - * response object created by addResponse() - * @var CommentResponse - */ - public $createdResponse; - - /** - * errors occurring through the validation of addComment or addResponse - * @var array - */ - public $validationErrors = []; - - /** - * @inheritDoc - */ - public function delete() { - if (empty($this->objects)) { - $this->readObjects(); - } - - // update counters - /** @var ICommentManager[] $processors */ - $processors = []; - $groupCommentIDs = $commentIDs = []; - foreach ($this->getObjects() as $comment) { - if (!isset($processors[$comment->objectTypeID])) { - $objectType = ObjectTypeCache::getInstance()->getObjectType($comment->objectTypeID); - $processors[$comment->objectTypeID] = $objectType->getProcessor(); - - $groupCommentIDs[$comment->objectTypeID] = []; - } - - if (!$comment->isDisabled) { - $processors[$comment->objectTypeID]->updateCounter($comment->objectID, -1 * ($comment->responses + 1)); - } - - $groupCommentIDs[$comment->objectTypeID][] = $comment->commentID; - $commentIDs[] = $comment->commentID; - } - - if (!empty($groupCommentIDs)) { - $likeObjectIDs = []; - $notificationObjectTypes = []; - foreach ($groupCommentIDs as $objectTypeID => $objectIDs) { - // remove activity events - $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); - if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType.'.recentActivityEvent')) { - UserActivityEventHandler::getInstance()->removeEvents($objectType->objectType.'.recentActivityEvent', $objectIDs); - } - - $likeObjectIDs = array_merge($likeObjectIDs, $objectIDs); - - // delete notifications - $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); - if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.notification')) { - UserNotificationHandler::getInstance()->removeNotifications($objectType->objectType.'.notification', $objectIDs); - } - - if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.like.notification')) { - $notificationObjectTypes[] = $objectType->objectType.'.like.notification'; - } - } - - // remove likes - ReactionHandler::getInstance()->removeReactions('com.woltlab.wcf.comment', $likeObjectIDs, $notificationObjectTypes); - } - - if (!empty($commentIDs)) { - // delete responses - $commentResponseList = new CommentResponseList(); - $commentResponseList->getConditionBuilder()->add('comment_response.commentID IN (?)', [$commentIDs]); - $commentResponseList->readObjectIDs(); - if (count($commentResponseList->getObjectIDs())) { - $action = new CommentResponseAction($commentResponseList->getObjectIDs(), 'delete', [ - 'ignoreCounters' => true - ]); - $action->executeAction(); - } - - ModerationQueueManager::getInstance()->removeQueues( - 'com.woltlab.wcf.comment.comment', - $commentIDs - ); - } - - return parent::delete(); - } - - /** - * Validates parameters to load comments. - * - * @throws PermissionDeniedException - */ - public function validateLoadComments() { - $this->readInteger('lastCommentTime', false, 'data'); - $this->readInteger('objectID', false, 'data'); - - $objectType = $this->validateObjectType(); - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->isAccessible($this->parameters['data']['objectID'])) { - throw new PermissionDeniedException(); - } - } - - /** - * Returns parsed comments. - * - * @return array - */ - public function loadComments() { - $commentList = CommentHandler::getInstance()->getCommentList($this->commentProcessor, $this->parameters['data']['objectTypeID'], $this->parameters['data']['objectID'], false); - $commentList->getConditionBuilder()->add("comment.time < ?", [$this->parameters['data']['lastCommentTime']]); - $commentList->readObjects(); - - // mark notifications for loaded comments as read - CommentHandler::getInstance()->markNotificationsAsConfirmedForComments( - CommentHandler::getInstance()->getObjectType($this->parameters['data']['objectTypeID'])->objectType, - $commentList->getObjects() - ); - - WCF::getTPL()->assign([ - 'commentList' => $commentList, - 'likeData' => MODULE_LIKE ? $commentList->getLikeData() : [] - ]); - - return [ - 'lastCommentTime' => $commentList->getMinCommentTime(), - 'template' => WCF::getTPL()->fetch('commentList') - ]; - } - - /** - * Validates the `loadComment` action. - * - * @throws PermissionDeniedException - * @since 3.1 - */ - public function validateLoadComment() { - $this->readInteger('objectID', false, 'data'); - $this->readInteger('responseID', true, 'data'); - - try { - $this->comment = $this->getSingleObject()->getDecoratedObject(); - } - catch (UserInputException $e) { - /* unknown comment id, error handling takes place in `loadComment()` */ - } - - $objectType = $this->validateObjectType(); - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->isAccessible($this->parameters['data']['objectID'])) { - throw new PermissionDeniedException(); - } - - if (!empty($this->parameters['data']['responseID'])) { - $this->response = new CommentResponse($this->parameters['data']['responseID']); - if (!$this->response->responseID) { - $this->response = null; - } - } - } - - /** - * Returns a rendered comment. - * - * @return string[] - * @since 3.1 - */ - public function loadComment() { - if ($this->comment === null) { - return ['template' => '']; - } - else if ($this->comment->objectTypeID != $this->parameters['data']['objectTypeID'] || $this->comment->objectID != $this->parameters['data']['objectID']) { - return ['template' => '']; - } - - // mark notifications for loaded comment/response as read - $objectType = CommentHandler::getInstance()->getObjectType($this->parameters['data']['objectTypeID'])->objectType; - if ($this->response === null) { - CommentHandler::getInstance()->markNotificationsAsConfirmedForComments( - $objectType, - [new StructuredComment($this->comment)] - ); - } - else { - CommentHandler::getInstance()->markNotificationsAsConfirmedForResponses( - $objectType, - [$this->response] - ); - } - - $returnValues = $this->renderComment($this->comment, $this->response); - return (is_array($returnValues)) ? $returnValues : ['template' => $returnValues]; - } - - /** - * Validates the `loadResponse` action. - * - * @since 3.1 - */ - public function validateLoadResponse() { - $this->validateLoadComment(); - } - - /** - * Returns a rendered comment. - * - * @return string[] - * @since 3.1 - */ - public function loadResponse() { - if ($this->comment === null || $this->response === null) { - return ['template' => '']; - } - else if ($this->comment->objectTypeID != $this->parameters['data']['objectTypeID'] || $this->comment->objectID != $this->parameters['data']['objectID']) { - return ['template' => '']; - } - - return [ - 'template' => $this->renderResponse($this->response) - ]; - } - - /** - * Validates parameters to add a comment. - * - * @throws PermissionDeniedException - */ - public function validateAddComment() { - CommentHandler::enforceFloodControl(); - - $this->readInteger('objectID', false, 'data'); - $this->readBoolean('requireGuestDialog', true); - - if (!$this->parameters['requireGuestDialog']) { - $this->validateUsername(); - $this->validateCaptcha(); - } - - $this->validateMessage(); - $objectType = $this->validateObjectType(); - - // validate object id and permissions - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) { - throw new PermissionDeniedException(); - } - } - - /** - * Adds a comment. - * - * @return string[] - */ - public function addComment() { - if ($this->parameters['requireGuestDialog'] || !empty($this->validationErrors)) { - if (!empty($this->validationErrors)) { - if (!empty($this->parameters['data']['username'])) { - WCF::getSession()->register('username', $this->parameters['data']['username']); - } - WCF::getTPL()->assign('errorType', $this->validationErrors); - } - - $guestDialog = $this->getGuestDialog(); - return [ - 'useCaptcha' => $guestDialog['useCaptcha'], - 'guestDialog' => $guestDialog['template'] - ]; - } - - /** @var HtmlInputProcessor $htmlInputProcessor */ - $htmlInputProcessor = $this->parameters['htmlInputProcessor']; - - // create comment - $this->createdComment = CommentEditor::create([ - 'objectTypeID' => $this->parameters['data']['objectTypeID'], - 'objectID' => $this->parameters['data']['objectID'], - 'time' => TIME_NOW, - 'userID' => WCF::getUser()->userID ?: null, - 'username' => WCF::getUser()->userID ? WCF::getUser()->username : $this->parameters['data']['username'], - 'message' => $htmlInputProcessor->getHtml(), - 'responses' => 0, - 'responseIDs' => serialize([]), - 'enableHtml' => 1, - 'isDisabled' => $this->commentProcessor->canAddWithoutApproval($this->parameters['data']['objectID']) ? 0 : 1 - ]); - - if (!$this->createdComment->isDisabled) { - $action = new CommentAction([$this->createdComment], 'triggerPublication', [ - 'commentProcessor' => $this->commentProcessor - ]); - $action->executeAction(); - } - else { - // mark comment for moderated content - ModerationQueueActivationManager::getInstance()->addModeratedContent('com.woltlab.wcf.comment.comment', $this->createdComment->commentID); - } - - if (!$this->createdComment->userID) { - // save user name is session - WCF::getSession()->register('username', $this->createdComment->username); - - // save last comment time for flood control - WCF::getSession()->register('lastCommentTime', $this->createdComment->time); - - // reset captcha for future requests - if ($this->captchaObjectType) { - $this->captchaObjectType->getProcessor()->reset(); - } - } - - return [ - 'template' => $this->renderComment($this->createdComment) - ]; - } - - public function triggerPublication() { - if (!empty($this->parameters['commentProcessor'])) { - $objectType = null; - if (!empty($this->objects)) { - /** @var Comment $comment */ - $comment = reset($this->objects); - $objectType = $this->validateObjectType($comment->objectTypeID); - } - - $this->commentProcessor = $this->parameters['commentProcessor']; - } - else { - $objectType = $this->validateObjectType($this->parameters['objectTypeID']); - $this->commentProcessor = $objectType->getProcessor(); - } - - /** @var CommentEditor $comment */ - foreach ($this->objects as $comment) { - // update counter - $comment->update(['isDisabled' => 0]); - $this->commentProcessor->updateCounter($comment->objectID, 1); - - // fire activity event - if ($comment->userID && UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.recentActivityEvent')) { - UserActivityEventHandler::getInstance()->fireEvent($objectType->objectType . '.recentActivityEvent', $comment->commentID, null, $comment->userID, $comment->time); - } - - // fire notification event - if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.notification')) { - $notificationObject = new CommentUserNotificationObject($comment->getDecoratedObject()); - $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType . '.notification'); - - if ($notificationObjectType instanceof IMultiRecipientCommentUserNotificationObjectType) { - $recipientIDs = $notificationObjectType->getRecipientIDs($comment->getDecoratedObject()); - - // make sure that the active user gets no notification - $recipientIDs = array_diff($recipientIDs, [WCF::getUser()->userID]); - - if (!empty($recipientIDs)) { - UserNotificationHandler::getInstance()->fireEvent('comment', $objectType->objectType . '.notification', $notificationObject, $recipientIDs); - } - } - else { - /** @var ICommentUserNotificationObjectType $notificationObjectType */ - - $userID = $notificationObjectType->getOwnerID($comment->commentID); - if ($userID != WCF::getUser()->userID) { - UserNotificationHandler::getInstance()->fireEvent('comment', $objectType->objectType . '.notification', $notificationObject, [$userID], ['objectUserID' => $userID]); - } - } - } - } - } - - /** - * Validates parameters to add a response. - * - * @throws PermissionDeniedException - */ - public function validateAddResponse() { - CommentHandler::enforceFloodControl(); - - $this->readInteger('objectID', false, 'data'); - $this->readBoolean('requireGuestDialog', true); - - if (!$this->parameters['requireGuestDialog']) { - $this->validateUsername(); - $this->validateCaptcha(); - } - - $this->validateMessage(); - - // validate comment id - $this->validateCommentID(); - - // disallow responses on disabled comments - if ($this->comment->isDisabled) { - throw new PermissionDeniedException(); - } - - $objectType = $this->validateObjectType(); - - // validate object id and permissions - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) { - throw new PermissionDeniedException(); - } - } - - /** - * Adds a response. - * - * @return array - */ - public function addResponse() { - if ($this->parameters['requireGuestDialog'] || !empty($this->validationErrors)) { - if (!empty($this->parameters['data']['username'])) { - WCF::getSession()->register('username', $this->parameters['data']['username']); - } - WCF::getTPL()->assign('errorType', $this->validationErrors); - - $guestDialog = $this->getGuestDialog(); - return [ - 'useCaptcha' => $guestDialog['useCaptcha'], - 'guestDialog' => $guestDialog['template'] - ]; - } - - /** @var HtmlInputProcessor $htmlInputProcessor */ - $htmlInputProcessor = $this->parameters['htmlInputProcessor']; - - // create response - $this->createdResponse = CommentResponseEditor::create([ - 'commentID' => $this->comment->commentID, - 'time' => TIME_NOW, - 'userID' => WCF::getUser()->userID ?: null, - 'username' => WCF::getUser()->userID ? WCF::getUser()->username : $this->parameters['data']['username'], - 'message' => $htmlInputProcessor->getHtml(), - 'enableHtml' => 1, - 'isDisabled' => $this->commentProcessor->canAddWithoutApproval($this->parameters['data']['objectID']) ? 0 : 1 - ]); - $this->createdResponse->setComment($this->comment); - - // update response data - $unfilteredResponseIDs = $this->comment->getUnfilteredResponseIDs(); - if (count($unfilteredResponseIDs) < 5) { - $unfilteredResponseIDs[] = $this->createdResponse->responseID; - } - $unfilteredResponses = $this->comment->unfilteredResponses + 1; - - // update comment - $commentEditor = new CommentEditor($this->comment); - $commentEditor->update([ - 'unfilteredResponseIDs' => serialize($unfilteredResponseIDs), - 'unfilteredResponses' => $unfilteredResponses - ]); - - if (!$this->createdResponse->isDisabled) { - $action = new CommentAction([], 'triggerPublicationResponse', [ - 'commentProcessor' => $this->commentProcessor, - 'responses' => [$this->createdResponse] - ]); - $action->executeAction(); - } - else { - // mark response for moderated content - ModerationQueueActivationManager::getInstance()->addModeratedContent('com.woltlab.wcf.comment.response', $this->createdResponse->responseID); - } - - if (!$this->createdResponse->userID) { - // save user name is session - WCF::getSession()->register('username', $this->createdResponse->username); - - // save last comment time for flood control - WCF::getSession()->register('lastCommentTime', $this->createdResponse->time); - - // reset captcha for future requests - if ($this->captchaObjectType) { - $this->captchaObjectType->getProcessor()->reset(); - } - } - - $responses = $this->comment->responses; - if ($this->commentProcessor->canModerate($this->parameters['data']['objectTypeID'], $this->parameters['data']['objectID'])) { - $responses = $this->comment->unfilteredResponses; - } - - return [ - 'commentID' => $this->comment->commentID, - 'template' => $this->renderResponse($this->createdResponse), - 'responses' => $responses + 1 - ]; - } - - /** - * Publishes a response. - */ - public function triggerPublicationResponse() { - if (!empty($this->parameters['commentProcessor'])) { - $objectType = null; - if (!empty($this->parameters['responses'])) { - /** @var CommentResponse $response */ - $response = reset($this->parameters['responses']); - $objectType = $this->validateObjectType($response->getComment()->objectTypeID); - } - - $this->commentProcessor = $this->parameters['commentProcessor']; - } - else { - $objectType = $this->validateObjectType($this->parameters['objectTypeID']); - $this->commentProcessor = $objectType->getProcessor(); - } - - /** @var CommentResponse $response */ - foreach ($this->parameters['responses'] as $response) { - (new CommentResponseEditor($response))->update(['isDisabled' => 0]); - - $comment = $response->getComment(); - - // update response count - $commentEditor = new CommentEditor($comment); - $commentEditor->updateCounters(['responses' => 1]); - - // do not prepend the response id as the approved response can appear anywhere - $commentEditor->updateResponseIDs(); - - // update counter - $this->commentProcessor->updateCounter($comment->objectID, 1); - - // fire activity event - if ($response->userID && UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.recentActivityEvent')) { - UserActivityEventHandler::getInstance()->fireEvent($objectType->objectType.'.response.recentActivityEvent', $response->responseID, null, $response->userID, $response->time); - } - - // fire notification event - if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.notification') && UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.notification')) { - $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType.'.notification'); - $notificationObject = new CommentResponseUserNotificationObject($response); - - if ($notificationObjectType instanceof IMultiRecipientCommentUserNotificationObjectType) { - $recipientIDs = $notificationObjectType->getRecipientIDs($comment); - - // make sure that the active user gets no notification - $recipientIDs = array_diff($recipientIDs, [WCF::getUser()->userID]); - - if ($notificationObjectType instanceof IMultiRecipientCommentResponseOwnerUserNotificationObjectType) { - $userID = $notificationObjectType->getCommentOwnerID($comment); - if ($userID && $userID != WCF::getUser()->userID) { - UserNotificationHandler::getInstance()->fireEvent( - 'commentResponseOwner', - $objectType->objectType . '.response.notification', - $notificationObject, - [$userID], - [ - 'commentID' => $comment->commentID, - 'objectID' => $comment->objectID, - 'objectUserID' => $userID, - 'userID' => $comment->userID - ] - ); - - $recipientIDs = array_diff($recipientIDs, [$userID]); - } - } - - if (!empty($recipientIDs)) { - UserNotificationHandler::getInstance()->fireEvent( - 'commentResponse', - $objectType->objectType . '.response.notification', - $notificationObject, - $recipientIDs, - [ - 'commentID' => $comment->commentID, - 'objectID' => $comment->objectID, - 'userID' => $comment->userID - ] - ); - } - } - else { - /** @var ICommentUserNotificationObjectType $notificationObjectType */ - $userID = $notificationObjectType->getOwnerID($comment->commentID); - - if ($comment->userID != WCF::getUser()->userID) { - UserNotificationHandler::getInstance()->fireEvent( - 'commentResponse', - $objectType->objectType . '.response.notification', - $notificationObject, - [$comment->userID], - [ - 'commentID' => $comment->commentID, - 'objectID' => $comment->objectID, - 'objectUserID' => $userID, - 'userID' => $comment->userID - ] - ); - } - - // notify the container owner - if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.notification')) { - if ($userID != $comment->userID && $userID != WCF::getUser()->userID) { - UserNotificationHandler::getInstance()->fireEvent( - 'commentResponseOwner', - $objectType->objectType . '.response.notification', - $notificationObject, - [$userID], - [ - 'commentID' => $comment->commentID, - 'objectID' => $comment->objectID, - 'objectUserID' => $userID, - 'userID' => $comment->userID - ] - ); - } - } - } - } - } - } - - /** - * Validates the `enable` action. - * - * @throws PermissionDeniedException - */ - public function validateEnable() { - $this->comment = $this->getSingleObject()->getDecoratedObject(); - - $objectType = $this->validateObjectType($this->comment->objectTypeID); - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->canModerate($this->comment->objectTypeID, $this->comment->objectID)) { - throw new PermissionDeniedException(); - } - } - - /** - * Enables a comment. - * - * @return integer[] - */ - public function enable() { - if ($this->comment === null) $this->comment = reset($this->objects); - - if ($this->comment->isDisabled) { - $action = new CommentAction([$this->comment], 'triggerPublication', [ - 'commentProcessor' => $this->commentProcessor, - 'objectTypeID' => $this->comment->objectTypeID - ]); - $action->executeAction(); - } - - ModerationQueueActivationManager::getInstance()->removeModeratedContent('com.woltlab.wcf.comment.comment', [$this->comment->commentID]); - - return ['commentID' => $this->comment->commentID]; - } - - /** - * Validates the `enableResponse` action. - * - * @throws PermissionDeniedException - * @throws UserInputException - */ - public function validateEnableResponse() { - $this->readInteger('responseID', false, 'data'); - $this->response = new CommentResponse($this->parameters['data']['responseID']); - if (!$this->response->responseID) { - throw new UserInputException('responseID'); - } - - $this->comment = $this->response->getComment(); - - $objectType = $this->validateObjectType($this->comment->objectTypeID); - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->canModerate($this->comment->objectTypeID, $this->comment->objectID)) { - throw new PermissionDeniedException(); - } - } - - /** - * Enables a response. - * - * @return integer[] - */ - public function enableResponse() { - if ($this->comment === null) $this->comment = reset($this->objects); - if ($this->response === null) $this->response = reset($this->parameters['responses']); - - if ($this->response->isDisabled) { - $action = new CommentAction([], 'triggerPublicationResponse', [ - 'commentProcessor' => $this->commentProcessor, - 'objectTypeID' => $this->comment->objectTypeID, - 'responses' => [$this->response] - ]); - $action->executeAction(); - } - - ModerationQueueActivationManager::getInstance()->removeModeratedContent('com.woltlab.wcf.comment.response', [$this->response->responseID]); - - return ['responseID' => $this->response->responseID]; - } - - /** - * Validates parameters to edit a comment or a response. - * - * @throws PermissionDeniedException - * @throws UserInputException - */ - public function validatePrepareEdit() { - // validate response id - try { - $this->validateResponseID(); - } - catch (UserInputException $e) { - throw new UserInputException('objectIDs'); - } - - // validate object type id - $objectType = $this->validateObjectType(); - - // validate object id and permissions - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->canEditResponse($this->response)) { - throw new PermissionDeniedException(); - } - } - - /** - * Prepares editing of a comment or a response. - * - * @return array - */ - public function prepareEdit() { - return [ - 'action' => 'prepare', - 'message' => $this->response->message, - 'responseID' => $this->response->responseID - ]; - } - - /** - * @inheritDoc - */ - public function validateEdit() { - $this->validatePrepareEdit(); - - $this->validateMessage(); - } - - /** - * Edits a comment or response. - * - * @return array - */ - public function edit() { - $editor = new CommentResponseEditor($this->response); - $editor->update([ - 'message' => $this->parameters['data']['message'] - ]); - $response = new CommentResponse($this->response->responseID); - - return [ - 'action' => 'saved', - 'message' => $response->getFormattedMessage(), - 'responseID' => $this->response->responseID - ]; - } - - /** - * @inheritDoc - */ - public function validateBeginEdit() { - $this->comment = $this->getSingleObject(); - - // validate object type id - $objectType = $this->validateObjectType(); - - // validate object id and permissions - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->canEditComment($this->comment->getDecoratedObject())) { - throw new PermissionDeniedException(); - } - - $this->setDisallowedBBCodes(); - } - - /** - * @inheritDoc - */ - public function beginEdit() { - WCF::getTPL()->assign([ - 'comment' => $this->comment, - 'wysiwygSelector' => 'commentEditor'.$this->comment->commentID - ]); - - return [ - 'actionName' => 'beginEdit', - 'template' => WCF::getTPL()->fetch('commentEditor', 'wcf') - ]; - } - - /** - * @inheritDoc - */ - public function validateSave() { - $this->validateBeginEdit(); - - $this->validateMessage(); - } - - /** - * @inheritDoc - */ - public function save() { - /** @var HtmlInputProcessor $htmlInputProcessor */ - $htmlInputProcessor = $this->parameters['htmlInputProcessor']; - - $action = new CommentAction([$this->comment], 'update', [ - 'data' => [ - 'message' => $htmlInputProcessor->getHtml() - ] - ]); - $action->executeAction(); - - return [ - 'actionName' => 'save', - 'message' => (new Comment($this->comment->commentID))->getFormattedMessage() - ]; - } - - /** - * Validates parameters to remove a comment or response. - * - * @throws PermissionDeniedException - * @throws UserInputException - */ - public function validateRemove() { - // validate comment id or response id - try { - $this->validateCommentID(); - } - catch (UserInputException $e) { - try { - $this->validateResponseID(); - } - catch (UserInputException $e) { - throw new UserInputException('objectIDs'); - } - } - - // validate object type id - $objectType = $this->validateObjectType(); - - // validate object id and permissions - $this->commentProcessor = $objectType->getProcessor(); - if ($this->comment !== null) { - if (!$this->commentProcessor->canDeleteComment($this->comment)) { - throw new PermissionDeniedException(); - } - } - else { - if (!$this->commentProcessor->canDeleteResponse($this->response)) { - throw new PermissionDeniedException(); - } - } - } - - /** - * Removes a comment or response. - * - * @return integer[] - */ - public function remove() { - if ($this->comment !== null) { - $objectAction = new CommentAction([$this->comment], 'delete'); - $objectAction->executeAction(); - - return ['commentID' => $this->comment->commentID]; - } - else { - $objectAction = new CommentResponseAction([$this->response], 'delete'); - $objectAction->executeAction(); - - return ['responseID' => $this->response->responseID]; - } - } - - /** - * Validates the 'getGuestDialog' action. - * - * @throws PermissionDeniedException - */ - public function validateGetGuestDialog() { - if (WCF::getUser()->userID) { - throw new PermissionDeniedException(); - } - - CommentHandler::enforceFloodControl(); - - $this->readInteger('objectID', false, 'data'); - $objectType = $this->validateObjectType(); - - // validate object id and permissions - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) { - throw new PermissionDeniedException(); - } - - // validate message already at this point to make sure that the - // message is valid when submitting the dialog to avoid having to - // go back to the message to fix it - $this->validateMessage(); - } - - /** - * Returns the dialog for guests when they try to write a comment letting - * them enter a username and solving a captcha. - * - * @return array - * @throws SystemException - */ - public function getGuestDialog() { - $captchaObjectType = null; - - if (CAPTCHA_TYPE) { - $captchaObjectType = CaptchaHandler::getInstance()->getObjectTypeByName(CAPTCHA_TYPE); - if ($captchaObjectType === null) { - throw new SystemException("Unknown captcha object type with name '".CAPTCHA_TYPE."'"); - } - - if (!$captchaObjectType->getProcessor()->isAvailable()) { - $captchaObjectType = null; - } - } - - return [ - 'useCaptcha' => $captchaObjectType !== null, - 'template' => WCF::getTPL()->fetch('commentAddGuestDialog', 'wcf', [ - 'ajaxCaptcha' => true, - 'captchaID' => 'commentAdd', - 'captchaObjectType' => $captchaObjectType, - 'supportsAsyncCaptcha' => true, - 'username' => WCF::getSession()->getVar('username') - ]) - ]; - } - - /** - * Renders a comment. - * - * @param Comment $comment - * @param CommentResponse $response - * @return string|string[] - */ - protected function renderComment(Comment $comment, CommentResponse $response = null) { - $comment = new StructuredComment($comment); - $comment->setIsDeletable($this->commentProcessor->canDeleteComment($comment->getDecoratedObject())); - $comment->setIsEditable($this->commentProcessor->canEditComment($comment->getDecoratedObject())); - - if ($response !== null) { - // check if response is not visible - /** @var CommentResponse $visibleResponse */ - foreach ($comment as $visibleResponse) { - if ($visibleResponse->responseID == $response->responseID) { - $response = null; - break; - } - } - } - - // load last response time - if ($comment->getDecoratedObject()->responses) { - $sql = "SELECT time - FROM wcf".WCF_N."_comment_response - WHERE commentID = ? - ORDER BY time"; - $statement = WCF::getDB()->prepareStatement($sql, 1); - $statement->execute([$comment->commentID]); - $lastResponseTime = $statement->fetchSingleColumn(); - if ($lastResponseTime && $lastResponseTime > 1) { - WCF::getTPL()->assign('commentLastResponseTime', ($lastResponseTime - 1)); - } - } - - WCF::getTPL()->assign([ - 'commentCanModerate' => $this->commentProcessor->canModerate($comment->getDecoratedObject()->objectTypeID, $comment->getDecoratedObject()->objectID), - 'commentList' => [$comment], - 'commentManager' => $this->commentProcessor - ]); - - // load like data - if (MODULE_LIKE) { - $likeData = []; - $commentObjectType = ReactionHandler::getInstance()->getObjectType('com.woltlab.wcf.comment'); - ReactionHandler::getInstance()->loadLikeObjects($commentObjectType, [$comment->commentID]); - $likeData['comment'] = ReactionHandler::getInstance()->getLikeObjects($commentObjectType); - - $responseIDs = []; - foreach ($comment as $visibleResponse) { - $responseIDs[] = $visibleResponse->responseID; - } - - if ($response !== null) { - $responseIDs[] = $response->responseID; - } - - if (!empty($responseIDs)) { - $responseObjectType = ReactionHandler::getInstance()->getObjectType('com.woltlab.wcf.comment.response'); - ReactionHandler::getInstance()->loadLikeObjects($responseObjectType, $responseIDs); - $likeData['response'] = ReactionHandler::getInstance()->getLikeObjects($responseObjectType); - } - - WCF::getTPL()->assign('likeData', $likeData); - } - - $template = WCF::getTPL()->fetch('commentList'); - if ($response === null) { - return $template; - } - - return [ - 'template' => $template, - 'response' => $this->renderResponse($response) - ]; - } - - /** - * Renders a response. - * - * @param CommentResponse $response - * @return string - */ - protected function renderResponse(CommentResponse $response) { - $response = new StructuredCommentResponse($response); - $response->setIsDeletable($this->commentProcessor->canDeleteResponse($response->getDecoratedObject())); - $response->setIsEditable($this->commentProcessor->canEditResponse($response->getDecoratedObject())); - - // render response - WCF::getTPL()->assign([ - 'responseList' => [$response], - 'commentCanModerate' => $this->commentProcessor->canModerate($response->getComment()->objectTypeID, $response->getComment()->objectID), - 'commentManager' => $this->commentProcessor - ]); - return WCF::getTPL()->fetch('commentResponseList'); - } - - /** - * Validates message parameters. - * - * @throws UserInputException - */ - protected function validateMessage() { - $this->readString('message', false, 'data'); - $this->parameters['data']['message'] = MessageUtil::stripCrap($this->parameters['data']['message']); - - if (empty($this->parameters['data']['message'])) { - throw new UserInputException('message'); - } - - CommentHandler::enforceCensorship($this->parameters['data']['message']); - - $this->setDisallowedBBCodes(); - $htmlInputProcessor = $this->getHtmlInputProcessor($this->parameters['data']['message'], ($this->comment !== null ? $this->comment->commentID : 0)); - - // search for disallowed bbcodes - $disallowedBBCodes = $htmlInputProcessor->validate(); - if (!empty($disallowedBBCodes)) { - throw new UserInputException('text', WCF::getLanguage()->getDynamicVariable('wcf.message.error.disallowedBBCodes', ['disallowedBBCodes' => $disallowedBBCodes])); - } - - if ($htmlInputProcessor->appearsToBeEmpty()) { - throw new UserInputException('message'); - } - - $this->parameters['htmlInputProcessor'] = $htmlInputProcessor; - } - - /** - * Validates object type id parameter. - * - * @param integer $objectTypeID - * @return ObjectType - * @throws UserInputException - */ - protected function validateObjectType($objectTypeID = null) { - if ($objectTypeID === null) { - $this->readInteger('objectTypeID', false, 'data'); - $objectTypeID = $this->parameters['data']['objectTypeID']; - } - - $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); - if ($objectType === null) { - throw new UserInputException('objectTypeID'); - } - - return $objectType; - } - - /** - * Validates comment id parameter. - * - * @throws UserInputException - */ - protected function validateCommentID() { - $this->readInteger('commentID', false, 'data'); - - $this->comment = new Comment($this->parameters['data']['commentID']); - if ($this->comment === null || !$this->comment->commentID) { - throw new UserInputException('commentID'); - } - } - - /** - * Validates response id parameter. - * - * @throws UserInputException - */ - protected function validateResponseID() { - if (isset($this->parameters['data']['responseID'])) { - $this->response = new CommentResponse($this->parameters['data']['responseID']); - } - if ($this->response === null || !$this->response->responseID) { - throw new UserInputException('responseID'); - } - } - - /** - * Validates the username parameter. - */ - protected function validateUsername() { - if (WCF::getUser()->userID) return; - - try { - $this->readString('username', false, 'data'); - - if (!UserRegistrationUtil::isValidUsername($this->parameters['data']['username'])) { - throw new UserInputException('username', 'invalid'); - } - if (!UserUtil::isAvailableUsername($this->parameters['data']['username'])) { - throw new UserInputException('username', 'notUnique'); - } - } - catch (UserInputException $e) { - $this->validationErrors['username'] = $e->getType(); - } - } - - /** - * Validates the captcha challenge. - * - * @throws SystemException - */ - protected function validateCaptcha() { - if (WCF::getUser()->userID) return; - - if (CAPTCHA_TYPE) { - $this->captchaObjectType = CaptchaHandler::getInstance()->getObjectTypeByName(CAPTCHA_TYPE); - if ($this->captchaObjectType === null) { - throw new SystemException("Unknown captcha object type with name '".CAPTCHA_TYPE."'"); - } - - if (!$this->captchaObjectType->getProcessor()->isAvailable()) { - $this->captchaObjectType = null; - } - } - - if ($this->captchaObjectType === null) return; - - try { - $this->captchaObjectType->getProcessor()->readFormParameters(); - $this->captchaObjectType->getProcessor()->validate(); - } - catch (UserInputException $e) { - $this->validationErrors = array_merge($this->validationErrors, [$e->getField() => $e->getType()]); - } - } - - /** - * Sets the list of disallowed bbcodes for comments. - */ - protected function setDisallowedBBCodes() { - BBCodeHandler::getInstance()->setDisallowedBBCodes(explode(',', WCF::getSession()->getPermission('user.comment.disallowedBBCodes'))); - } - - /** - * Returns the current html input processor or a new one if `$message` is not null. - * - * @param string|null $message source message - * @param integer $objectID object id - * @return HtmlInputProcessor - */ - public function getHtmlInputProcessor($message = null, $objectID = 0) { - if ($message === null) { - return $this->htmlInputProcessor; - } - - $this->htmlInputProcessor = new HtmlInputProcessor(); - $this->htmlInputProcessor->process($message, 'com.woltlab.wcf.comment', $objectID); - - return $this->htmlInputProcessor; - } - - /** - * Returns the comment object. - * - * @return Comment - */ - public function getComment() { - return $this->comment; - } - - /** - * Returns the comment response object. - * - * @return CommentResponse - */ - public function getResponse() { - return $this->response; - } - - /** - * Returns the comment manager. - * - * @return ICommentManager - */ - public function getCommentManager() { - return $this->commentProcessor; - } +class CommentAction extends AbstractDatabaseObjectAction implements IMessageInlineEditorAction +{ + /** + * @inheritDoc + */ + protected $allowGuestAccess = [ + 'addComment', + 'addResponse', + 'loadComment', + 'loadComments', + 'loadResponse', + 'getGuestDialog', + ]; + + /** + * captcha object type used for comments + * @var ObjectType + */ + public $captchaObjectType; + + /** + * @inheritDoc + */ + protected $className = CommentEditor::class; + + /** + * comment object + * @var Comment + */ + protected $comment; + + /** + * comment processor + * @var ICommentManager + */ + protected $commentProcessor; + + /** + * @var HtmlInputProcessor + */ + protected $htmlInputProcessor; + + /** + * response object + * @var CommentResponse + */ + protected $response; + + /** + * comment object created by addComment() + * @var Comment + */ + public $createdComment; + + /** + * response object created by addResponse() + * @var CommentResponse + */ + public $createdResponse; + + /** + * errors occurring through the validation of addComment or addResponse + * @var array + */ + public $validationErrors = []; + + /** + * @inheritDoc + */ + public function delete() + { + if (empty($this->objects)) { + $this->readObjects(); + } + + // update counters + /** @var ICommentManager[] $processors */ + $processors = []; + $groupCommentIDs = $commentIDs = []; + foreach ($this->getObjects() as $comment) { + if (!isset($processors[$comment->objectTypeID])) { + $objectType = ObjectTypeCache::getInstance()->getObjectType($comment->objectTypeID); + $processors[$comment->objectTypeID] = $objectType->getProcessor(); + + $groupCommentIDs[$comment->objectTypeID] = []; + } + + if (!$comment->isDisabled) { + $processors[$comment->objectTypeID]->updateCounter($comment->objectID, -1 * ($comment->responses + 1)); + } + + $groupCommentIDs[$comment->objectTypeID][] = $comment->commentID; + $commentIDs[] = $comment->commentID; + } + + if (!empty($groupCommentIDs)) { + $likeObjectIDs = []; + $notificationObjectTypes = []; + foreach ($groupCommentIDs as $objectTypeID => $objectIDs) { + // remove activity events + $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); + if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.recentActivityEvent')) { + UserActivityEventHandler::getInstance()->removeEvents( + $objectType->objectType . '.recentActivityEvent', + $objectIDs + ); + } + + $likeObjectIDs = \array_merge($likeObjectIDs, $objectIDs); + + // delete notifications + $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.notification')) { + UserNotificationHandler::getInstance()->removeNotifications( + $objectType->objectType . '.notification', + $objectIDs + ); + } + + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.like.notification')) { + $notificationObjectTypes[] = $objectType->objectType . '.like.notification'; + } + } + + // remove likes + ReactionHandler::getInstance()->removeReactions( + 'com.woltlab.wcf.comment', + $likeObjectIDs, + $notificationObjectTypes + ); + } + - // delete responses + if (!empty($commentIDs)) { ++ // delete responses + $commentResponseList = new CommentResponseList(); + $commentResponseList->getConditionBuilder()->add('comment_response.commentID IN (?)', [$commentIDs]); + $commentResponseList->readObjectIDs(); + if (\count($commentResponseList->getObjectIDs())) { + $action = new CommentResponseAction($commentResponseList->getObjectIDs(), 'delete', [ + 'ignoreCounters' => true, + ]); + $action->executeAction(); + } ++ ++ ModerationQueueManager::getInstance()->removeQueues( ++ 'com.woltlab.wcf.comment.comment', ++ $commentIDs ++ ); + } + + return parent::delete(); + } + + /** + * Validates parameters to load comments. + * + * @throws PermissionDeniedException + */ + public function validateLoadComments() + { + $this->readInteger('lastCommentTime', false, 'data'); + $this->readInteger('objectID', false, 'data'); + + $objectType = $this->validateObjectType(); + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->isAccessible($this->parameters['data']['objectID'])) { + throw new PermissionDeniedException(); + } + } + + /** + * Returns parsed comments. + * + * @return array + */ + public function loadComments() + { + $commentList = CommentHandler::getInstance()->getCommentList( + $this->commentProcessor, + $this->parameters['data']['objectTypeID'], + $this->parameters['data']['objectID'], + false + ); + $commentList->getConditionBuilder()->add("comment.time < ?", [$this->parameters['data']['lastCommentTime']]); + $commentList->readObjects(); + + // mark notifications for loaded comments as read + CommentHandler::getInstance()->markNotificationsAsConfirmedForComments( + CommentHandler::getInstance()->getObjectType($this->parameters['data']['objectTypeID'])->objectType, + $commentList->getObjects() + ); + + WCF::getTPL()->assign([ + 'commentList' => $commentList, + 'likeData' => MODULE_LIKE ? $commentList->getLikeData() : [], + ]); + + return [ + 'lastCommentTime' => $commentList->getMinCommentTime(), + 'template' => WCF::getTPL()->fetch('commentList'), + ]; + } + + /** + * Validates the `loadComment` action. + * + * @throws PermissionDeniedException + * @since 3.1 + */ + public function validateLoadComment() + { + $this->readInteger('objectID', false, 'data'); + $this->readInteger('responseID', true, 'data'); + + try { + $this->comment = $this->getSingleObject()->getDecoratedObject(); + } catch (UserInputException $e) { + /* unknown comment id, error handling takes place in `loadComment()` */ + } + + $objectType = $this->validateObjectType(); + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->isAccessible($this->parameters['data']['objectID'])) { + throw new PermissionDeniedException(); + } + + if (!empty($this->parameters['data']['responseID'])) { + $this->response = new CommentResponse($this->parameters['data']['responseID']); + if (!$this->response->responseID) { + $this->response = null; + } + } + } + + /** + * Returns a rendered comment. + * + * @return string[] + * @since 3.1 + */ + public function loadComment() + { + if ($this->comment === null) { + return ['template' => '']; + } elseif ($this->comment->objectTypeID != $this->parameters['data']['objectTypeID'] || $this->comment->objectID != $this->parameters['data']['objectID']) { + return ['template' => '']; + } + + // mark notifications for loaded comment/response as read + $objectType = CommentHandler::getInstance()->getObjectType($this->parameters['data']['objectTypeID'])->objectType; + if ($this->response === null) { + CommentHandler::getInstance()->markNotificationsAsConfirmedForComments( + $objectType, + [new StructuredComment($this->comment)] + ); + } else { + CommentHandler::getInstance()->markNotificationsAsConfirmedForResponses( + $objectType, + [$this->response] + ); + } + + $returnValues = $this->renderComment($this->comment, $this->response); + + return (\is_array($returnValues)) ? $returnValues : ['template' => $returnValues]; + } + + /** + * Validates the `loadResponse` action. + * + * @since 3.1 + */ + public function validateLoadResponse() + { + $this->validateLoadComment(); + } + + /** + * Returns a rendered comment. + * + * @return string[] + * @since 3.1 + */ + public function loadResponse() + { + if ($this->comment === null || $this->response === null) { + return ['template' => '']; + } elseif ($this->comment->objectTypeID != $this->parameters['data']['objectTypeID'] || $this->comment->objectID != $this->parameters['data']['objectID']) { + return ['template' => '']; + } + + return [ + 'template' => $this->renderResponse($this->response), + ]; + } + + /** + * Validates parameters to add a comment. + * + * @throws PermissionDeniedException + */ + public function validateAddComment() + { + try { + CommentHandler::enforceFloodControl(); + } catch (NamedUserException $e) { + throw new UserInputException('message', $e->getMessage()); + } + + $this->readInteger('objectID', false, 'data'); + $this->readBoolean('requireGuestDialog', true); + + if (!$this->parameters['requireGuestDialog']) { + $this->validateUsername(); + $this->validateCaptcha(); + } + + $this->validateMessage(); + $objectType = $this->validateObjectType(); + + // validate object id and permissions + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) { + throw new PermissionDeniedException(); + } + } + + /** + * Adds a comment. + * + * @return string[] + */ + public function addComment() + { + if ($this->parameters['requireGuestDialog'] || !empty($this->validationErrors)) { + if (!empty($this->validationErrors)) { + if (!empty($this->parameters['data']['username'])) { + WCF::getSession()->register('username', $this->parameters['data']['username']); + } + WCF::getTPL()->assign('errorType', $this->validationErrors); + } + + $guestDialog = $this->getGuestDialog(); + + return [ + 'useCaptcha' => $guestDialog['useCaptcha'], + 'guestDialog' => $guestDialog['template'], + ]; + } + + /** @var HtmlInputProcessor $htmlInputProcessor */ + $htmlInputProcessor = $this->parameters['htmlInputProcessor']; + + // create comment + $this->createdComment = CommentEditor::create([ + 'objectTypeID' => $this->parameters['data']['objectTypeID'], + 'objectID' => $this->parameters['data']['objectID'], + 'time' => TIME_NOW, + 'userID' => WCF::getUser()->userID ?: null, + 'username' => WCF::getUser()->userID ? WCF::getUser()->username : $this->parameters['data']['username'], + 'message' => $htmlInputProcessor->getHtml(), + 'responses' => 0, + 'responseIDs' => \serialize([]), + 'enableHtml' => 1, + 'isDisabled' => $this->commentProcessor->canAddWithoutApproval($this->parameters['data']['objectID']) ? 0 : 1, + ]); + + if (!$this->createdComment->isDisabled) { + $action = new self([$this->createdComment], 'triggerPublication', [ + 'commentProcessor' => $this->commentProcessor, + ]); + $action->executeAction(); + } else { + // mark comment for moderated content + ModerationQueueActivationManager::getInstance()->addModeratedContent( + 'com.woltlab.wcf.comment.comment', + $this->createdComment->commentID + ); + } + + FloodControl::getInstance()->registerContent('com.woltlab.wcf.comment'); + + if (!$this->createdComment->userID) { + // save user name is session + WCF::getSession()->register('username', $this->createdComment->username); + + // save last comment time for flood control + WCF::getSession()->register('lastCommentTime', $this->createdComment->time); + + // reset captcha for future requests + if ($this->captchaObjectType) { + $this->captchaObjectType->getProcessor()->reset(); + } + } + + return [ + 'template' => $this->renderComment($this->createdComment), + ]; + } + + public function triggerPublication() + { + if (!empty($this->parameters['commentProcessor'])) { + $objectType = null; + if (!empty($this->objects)) { + /** @var Comment $comment */ + $comment = \reset($this->objects); + $objectType = $this->validateObjectType($comment->objectTypeID); + } + + $this->commentProcessor = $this->parameters['commentProcessor']; + } else { + $objectType = $this->validateObjectType($this->parameters['objectTypeID']); + $this->commentProcessor = $objectType->getProcessor(); + } + + /** @var CommentEditor $comment */ + foreach ($this->objects as $comment) { + // update counter + $comment->update(['isDisabled' => 0]); + $this->commentProcessor->updateCounter($comment->objectID, 1); + + // fire activity event + if ($comment->userID && UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.recentActivityEvent')) { + UserActivityEventHandler::getInstance()->fireEvent( + $objectType->objectType . '.recentActivityEvent', + $comment->commentID, + null, + $comment->userID, + $comment->time + ); + } + + // fire notification event + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.notification')) { + $notificationObject = new CommentUserNotificationObject($comment->getDecoratedObject()); + $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType . '.notification'); + + if ($notificationObjectType instanceof IMultiRecipientCommentUserNotificationObjectType) { + $recipientIDs = $notificationObjectType->getRecipientIDs($comment->getDecoratedObject()); + + // make sure that the active user gets no notification + $recipientIDs = \array_diff($recipientIDs, [WCF::getUser()->userID]); + + if (!empty($recipientIDs)) { + UserNotificationHandler::getInstance()->fireEvent( + 'comment', + $objectType->objectType . '.notification', + $notificationObject, + $recipientIDs + ); + } + } else { + /** @var ICommentUserNotificationObjectType $notificationObjectType */ + + $userID = $notificationObjectType->getOwnerID($comment->commentID); + if ($userID != WCF::getUser()->userID) { + UserNotificationHandler::getInstance()->fireEvent( + 'comment', + $objectType->objectType . '.notification', + $notificationObject, + [$userID], + ['objectUserID' => $userID] + ); + } + } + } + } + } + + /** + * Validates parameters to add a response. + * + * @throws PermissionDeniedException + */ + public function validateAddResponse() + { + try { + CommentHandler::enforceFloodControl(); + } catch (NamedUserException $e) { + throw new UserInputException('message', $e->getMessage()); + } + + $this->readInteger('objectID', false, 'data'); + $this->readBoolean('requireGuestDialog', true); + + if (!$this->parameters['requireGuestDialog']) { + $this->validateUsername(); + $this->validateCaptcha(); + } + + $this->validateMessage(); + + // validate comment id + $this->validateCommentID(); + + // disallow responses on disabled comments + if ($this->comment->isDisabled) { + throw new PermissionDeniedException(); + } + + $objectType = $this->validateObjectType(); + + // validate object id and permissions + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) { + throw new PermissionDeniedException(); + } + } + + /** + * Adds a response. + * + * @return array + */ + public function addResponse() + { + if ($this->parameters['requireGuestDialog'] || !empty($this->validationErrors)) { + if (!empty($this->parameters['data']['username'])) { + WCF::getSession()->register('username', $this->parameters['data']['username']); + } + WCF::getTPL()->assign('errorType', $this->validationErrors); + + $guestDialog = $this->getGuestDialog(); + + return [ + 'useCaptcha' => $guestDialog['useCaptcha'], + 'guestDialog' => $guestDialog['template'], + ]; + } + + /** @var HtmlInputProcessor $htmlInputProcessor */ + $htmlInputProcessor = $this->parameters['htmlInputProcessor']; + + // create response + $this->createdResponse = CommentResponseEditor::create([ + 'commentID' => $this->comment->commentID, + 'time' => TIME_NOW, + 'userID' => WCF::getUser()->userID ?: null, + 'username' => WCF::getUser()->userID ? WCF::getUser()->username : $this->parameters['data']['username'], + 'message' => $htmlInputProcessor->getHtml(), + 'enableHtml' => 1, + 'isDisabled' => $this->commentProcessor->canAddWithoutApproval($this->parameters['data']['objectID']) ? 0 : 1, + ]); + $this->createdResponse->setComment($this->comment); + + // update response data + $unfilteredResponseIDs = $this->comment->getUnfilteredResponseIDs(); + if (\count($unfilteredResponseIDs) < 5) { + $unfilteredResponseIDs[] = $this->createdResponse->responseID; + } + $unfilteredResponses = $this->comment->unfilteredResponses + 1; + + // update comment + $commentEditor = new CommentEditor($this->comment); + $commentEditor->update([ + 'unfilteredResponseIDs' => \serialize($unfilteredResponseIDs), + 'unfilteredResponses' => $unfilteredResponses, + ]); + + if (!$this->createdResponse->isDisabled) { + $action = new self([], 'triggerPublicationResponse', [ + 'commentProcessor' => $this->commentProcessor, + 'responses' => [$this->createdResponse], + ]); + $action->executeAction(); + } else { + // mark response for moderated content + ModerationQueueActivationManager::getInstance()->addModeratedContent( + 'com.woltlab.wcf.comment.response', + $this->createdResponse->responseID + ); + } + + FloodControl::getInstance()->registerContent('com.woltlab.wcf.comment'); + + if (!$this->createdResponse->userID) { + // save user name is session + WCF::getSession()->register('username', $this->createdResponse->username); + + // save last comment time for flood control + WCF::getSession()->register('lastCommentTime', $this->createdResponse->time); + + // reset captcha for future requests + if ($this->captchaObjectType) { + $this->captchaObjectType->getProcessor()->reset(); + } + } + + $responses = $this->comment->responses; + if ( + $this->commentProcessor->canModerate( + $this->parameters['data']['objectTypeID'], + $this->parameters['data']['objectID'] + ) + ) { + $responses = $this->comment->unfilteredResponses; + } + + return [ + 'commentID' => $this->comment->commentID, + 'template' => $this->renderResponse($this->createdResponse), + 'responses' => $responses + 1, + ]; + } + + /** + * Publishes a response. + */ + public function triggerPublicationResponse() + { + if (!empty($this->parameters['commentProcessor'])) { + $objectType = null; + if (!empty($this->parameters['responses'])) { + /** @var CommentResponse $response */ + $response = \reset($this->parameters['responses']); + $objectType = $this->validateObjectType($response->getComment()->objectTypeID); + } + + $this->commentProcessor = $this->parameters['commentProcessor']; + } else { + $objectType = $this->validateObjectType($this->parameters['objectTypeID']); + $this->commentProcessor = $objectType->getProcessor(); + } + + /** @var CommentResponse $response */ + foreach ($this->parameters['responses'] as $response) { + (new CommentResponseEditor($response))->update(['isDisabled' => 0]); + + $comment = $response->getComment(); + + // update response count + $commentEditor = new CommentEditor($comment); + $commentEditor->updateCounters(['responses' => 1]); + + // do not prepend the response id as the approved response can appear anywhere + $commentEditor->updateResponseIDs(); + + // update counter + $this->commentProcessor->updateCounter($comment->objectID, 1); + + // fire activity event + if ($response->userID && UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.response.recentActivityEvent')) { + UserActivityEventHandler::getInstance()->fireEvent( + $objectType->objectType . '.response.recentActivityEvent', + $response->responseID, + null, + $response->userID, + $response->time + ); + } + + // fire notification event + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.response.notification') && UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.notification')) { + $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType . '.notification'); + $notificationObject = new CommentResponseUserNotificationObject($response); + + if ($notificationObjectType instanceof IMultiRecipientCommentUserNotificationObjectType) { + $recipientIDs = $notificationObjectType->getRecipientIDs($comment); + + // make sure that the active user gets no notification + $recipientIDs = \array_diff($recipientIDs, [WCF::getUser()->userID]); + + if ($notificationObjectType instanceof IMultiRecipientCommentResponseOwnerUserNotificationObjectType) { + $userID = $notificationObjectType->getCommentOwnerID($comment); + if ($userID && $userID != WCF::getUser()->userID) { + UserNotificationHandler::getInstance()->fireEvent( + 'commentResponseOwner', + $objectType->objectType . '.response.notification', + $notificationObject, + [$userID], + [ + 'commentID' => $comment->commentID, + 'objectID' => $comment->objectID, + 'objectUserID' => $userID, + 'userID' => $comment->userID, + ] + ); + + $recipientIDs = \array_diff($recipientIDs, [$userID]); + } + } + + if (!empty($recipientIDs)) { + UserNotificationHandler::getInstance()->fireEvent( + 'commentResponse', + $objectType->objectType . '.response.notification', + $notificationObject, + $recipientIDs, + [ + 'commentID' => $comment->commentID, + 'objectID' => $comment->objectID, + 'userID' => $comment->userID, + ] + ); + } + } else { + /** @var ICommentUserNotificationObjectType $notificationObjectType */ + $userID = $notificationObjectType->getOwnerID($comment->commentID); + + if ($comment->userID != WCF::getUser()->userID) { + UserNotificationHandler::getInstance()->fireEvent( + 'commentResponse', + $objectType->objectType . '.response.notification', + $notificationObject, + [$comment->userID], + [ + 'commentID' => $comment->commentID, + 'objectID' => $comment->objectID, + 'objectUserID' => $userID, + 'userID' => $comment->userID, + ] + ); + } + + // notify the container owner + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.notification')) { + if ($userID != $comment->userID && $userID != WCF::getUser()->userID) { + UserNotificationHandler::getInstance()->fireEvent( + 'commentResponseOwner', + $objectType->objectType . '.response.notification', + $notificationObject, + [$userID], + [ + 'commentID' => $comment->commentID, + 'objectID' => $comment->objectID, + 'objectUserID' => $userID, + 'userID' => $comment->userID, + ] + ); + } + } + } + } + } + } + + /** + * Validates the `enable` action. + * + * @throws PermissionDeniedException + */ + public function validateEnable() + { + $this->comment = $this->getSingleObject()->getDecoratedObject(); + + $objectType = $this->validateObjectType($this->comment->objectTypeID); + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->canModerate($this->comment->objectTypeID, $this->comment->objectID)) { + throw new PermissionDeniedException(); + } + } + + /** + * Enables a comment. + * + * @return int[] + */ + public function enable() + { + if ($this->comment === null) { + $this->comment = \reset($this->objects); + } + + if ($this->comment->isDisabled) { + $action = new self([$this->comment], 'triggerPublication', [ + 'commentProcessor' => $this->commentProcessor, + 'objectTypeID' => $this->comment->objectTypeID, + ]); + $action->executeAction(); + } + + ModerationQueueActivationManager::getInstance()->removeModeratedContent( + 'com.woltlab.wcf.comment.comment', + [$this->comment->commentID] + ); + + return ['commentID' => $this->comment->commentID]; + } + + /** + * Validates the `enableResponse` action. + * + * @throws PermissionDeniedException + * @throws UserInputException + */ + public function validateEnableResponse() + { + $this->readInteger('responseID', false, 'data'); + $this->response = new CommentResponse($this->parameters['data']['responseID']); + if (!$this->response->responseID) { + throw new UserInputException('responseID'); + } + + $this->comment = $this->response->getComment(); + + $objectType = $this->validateObjectType($this->comment->objectTypeID); + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->canModerate($this->comment->objectTypeID, $this->comment->objectID)) { + throw new PermissionDeniedException(); + } + } + + /** + * Enables a response. + * + * @return int[] + */ + public function enableResponse() + { + if ($this->comment === null) { + $this->comment = \reset($this->objects); + } + if ($this->response === null) { + $this->response = \reset($this->parameters['responses']); + } + + if ($this->response->isDisabled) { + $action = new self([], 'triggerPublicationResponse', [ + 'commentProcessor' => $this->commentProcessor, + 'objectTypeID' => $this->comment->objectTypeID, + 'responses' => [$this->response], + ]); + $action->executeAction(); + } + + ModerationQueueActivationManager::getInstance()->removeModeratedContent( + 'com.woltlab.wcf.comment.response', + [$this->response->responseID] + ); + + return ['responseID' => $this->response->responseID]; + } + + /** + * Validates parameters to edit a comment or a response. + * + * @throws PermissionDeniedException + * @throws UserInputException + */ + public function validatePrepareEdit() + { + // validate response id + try { + $this->validateResponseID(); + } catch (UserInputException $e) { + throw new UserInputException('objectIDs'); + } + + // validate object type id + $objectType = $this->validateObjectType(); + + // validate object id and permissions + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->canEditResponse($this->response)) { + throw new PermissionDeniedException(); + } + } + + /** + * Prepares editing of a comment or a response. + * + * @return array + */ + public function prepareEdit() + { + return [ + 'action' => 'prepare', + 'message' => $this->response->message, + 'responseID' => $this->response->responseID, + ]; + } + + /** + * @inheritDoc + */ + public function validateEdit() + { + $this->validatePrepareEdit(); + + $this->validateMessage(); + } + + /** + * Edits a comment or response. + * + * @return array + */ + public function edit() + { + $editor = new CommentResponseEditor($this->response); + $editor->update([ + 'message' => $this->parameters['data']['message'], + ]); + $response = new CommentResponse($this->response->responseID); + + return [ + 'action' => 'saved', + 'message' => $response->getFormattedMessage(), + 'responseID' => $this->response->responseID, + ]; + } + + /** + * @inheritDoc + */ + public function validateBeginEdit() + { + $this->comment = $this->getSingleObject(); + + // validate object type id + $objectType = $this->validateObjectType(); + + // validate object id and permissions + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->canEditComment($this->comment->getDecoratedObject())) { + throw new PermissionDeniedException(); + } + + $this->setDisallowedBBCodes(); + } + + /** + * @inheritDoc + */ + public function beginEdit() + { + WCF::getTPL()->assign([ + 'comment' => $this->comment, + 'wysiwygSelector' => 'commentEditor' . $this->comment->commentID, + ]); + + return [ + 'actionName' => 'beginEdit', + 'template' => WCF::getTPL()->fetch('commentEditor', 'wcf'), + ]; + } + + /** + * @inheritDoc + */ + public function validateSave() + { + $this->validateBeginEdit(); + + $this->validateMessage(); + } + + /** + * @inheritDoc + */ + public function save() + { + /** @var HtmlInputProcessor $htmlInputProcessor */ + $htmlInputProcessor = $this->parameters['htmlInputProcessor']; + + $action = new self([$this->comment], 'update', [ + 'data' => [ + 'message' => $htmlInputProcessor->getHtml(), + ], + ]); + $action->executeAction(); + + return [ + 'actionName' => 'save', + 'message' => (new Comment($this->comment->commentID))->getFormattedMessage(), + ]; + } + + /** + * Validates parameters to remove a comment or response. + * + * @throws PermissionDeniedException + * @throws UserInputException + */ + public function validateRemove() + { + // validate comment id or response id + try { + $this->validateCommentID(); + } catch (UserInputException $e) { + try { + $this->validateResponseID(); + } catch (UserInputException $e) { + throw new UserInputException('objectIDs'); + } + } + + // validate object type id + $objectType = $this->validateObjectType(); + + // validate object id and permissions + $this->commentProcessor = $objectType->getProcessor(); + if ($this->comment !== null) { + if (!$this->commentProcessor->canDeleteComment($this->comment)) { + throw new PermissionDeniedException(); + } + } else { + if (!$this->commentProcessor->canDeleteResponse($this->response)) { + throw new PermissionDeniedException(); + } + } + } + + /** + * Removes a comment or response. + * + * @return int[] + */ + public function remove() + { + if ($this->comment !== null) { + $objectAction = new self([$this->comment], 'delete'); + $objectAction->executeAction(); + + return ['commentID' => $this->comment->commentID]; + } else { + $objectAction = new CommentResponseAction([$this->response], 'delete'); + $objectAction->executeAction(); + + return ['responseID' => $this->response->responseID]; + } + } + + /** + * Validates the 'getGuestDialog' action. + * + * @throws PermissionDeniedException + */ + public function validateGetGuestDialog() + { + if (WCF::getUser()->userID) { + throw new PermissionDeniedException(); + } + + try { + CommentHandler::enforceFloodControl(); + } catch (NamedUserException $e) { + throw new UserInputException('message', $e->getMessage()); + } + + $this->readInteger('objectID', false, 'data'); + $objectType = $this->validateObjectType(); + + // validate object id and permissions + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) { + throw new PermissionDeniedException(); + } + + // validate message already at this point to make sure that the + // message is valid when submitting the dialog to avoid having to + // go back to the message to fix it + $this->validateMessage(); + } + + /** + * Returns the dialog for guests when they try to write a comment letting + * them enter a username and solving a captcha. + * + * @return array + * @throws SystemException + */ + public function getGuestDialog() + { + $captchaObjectType = null; + + if (CAPTCHA_TYPE) { + $captchaObjectType = CaptchaHandler::getInstance()->getObjectTypeByName(CAPTCHA_TYPE); + if ($captchaObjectType === null) { + throw new SystemException("Unknown captcha object type with name '" . CAPTCHA_TYPE . "'"); + } + + if (!$captchaObjectType->getProcessor()->isAvailable()) { + $captchaObjectType = null; + } + } + + return [ + 'useCaptcha' => $captchaObjectType !== null, + 'template' => WCF::getTPL()->fetch('commentAddGuestDialog', 'wcf', [ + 'ajaxCaptcha' => true, + 'captchaID' => 'commentAdd', + 'captchaObjectType' => $captchaObjectType, + 'supportsAsyncCaptcha' => true, + 'username' => WCF::getSession()->getVar('username'), + ]), + ]; + } + + /** + * Renders a comment. + * + * @param Comment $comment + * @param CommentResponse $response + * @return string|string[] + */ + protected function renderComment(Comment $comment, ?CommentResponse $response = null) + { + $comment = new StructuredComment($comment); + $comment->setIsDeletable($this->commentProcessor->canDeleteComment($comment->getDecoratedObject())); + $comment->setIsEditable($this->commentProcessor->canEditComment($comment->getDecoratedObject())); + + if ($response !== null) { + // check if response is not visible + /** @var CommentResponse $visibleResponse */ + foreach ($comment as $visibleResponse) { + if ($visibleResponse->responseID == $response->responseID) { + $response = null; + break; + } + } + } + + // load last response time + if ($comment->getDecoratedObject()->responses) { + $sql = "SELECT time + FROM wcf" . WCF_N . "_comment_response + WHERE commentID = ? + ORDER BY time"; + $statement = WCF::getDB()->prepareStatement($sql, 1); + $statement->execute([$comment->commentID]); + $lastResponseTime = $statement->fetchSingleColumn(); + if ($lastResponseTime && $lastResponseTime > 1) { + WCF::getTPL()->assign('commentLastResponseTime', ($lastResponseTime - 1)); + } + } + + WCF::getTPL()->assign([ + 'commentCanModerate' => $this->commentProcessor->canModerate( + $comment->getDecoratedObject()->objectTypeID, + $comment->getDecoratedObject()->objectID + ), + 'commentList' => [$comment], + 'commentManager' => $this->commentProcessor, + ]); + + // load like data + if (MODULE_LIKE) { + $likeData = []; + $commentObjectType = ReactionHandler::getInstance()->getObjectType('com.woltlab.wcf.comment'); + ReactionHandler::getInstance()->loadLikeObjects($commentObjectType, [$comment->commentID]); + $likeData['comment'] = ReactionHandler::getInstance()->getLikeObjects($commentObjectType); + + $responseIDs = []; + foreach ($comment as $visibleResponse) { + $responseIDs[] = $visibleResponse->responseID; + } + + if ($response !== null) { + $responseIDs[] = $response->responseID; + } + + if (!empty($responseIDs)) { + $responseObjectType = ReactionHandler::getInstance()->getObjectType('com.woltlab.wcf.comment.response'); + ReactionHandler::getInstance()->loadLikeObjects($responseObjectType, $responseIDs); + $likeData['response'] = ReactionHandler::getInstance()->getLikeObjects($responseObjectType); + } + + WCF::getTPL()->assign('likeData', $likeData); + } + + $template = WCF::getTPL()->fetch('commentList'); + if ($response === null) { + return $template; + } + + return [ + 'template' => $template, + 'response' => $this->renderResponse($response), + ]; + } + + /** + * Renders a response. + * + * @param CommentResponse $response + * @return string + */ + protected function renderResponse(CommentResponse $response) + { + $response = new StructuredCommentResponse($response); + $response->setIsDeletable($this->commentProcessor->canDeleteResponse($response->getDecoratedObject())); + $response->setIsEditable($this->commentProcessor->canEditResponse($response->getDecoratedObject())); + + // render response + WCF::getTPL()->assign([ + 'responseList' => [$response], + 'commentCanModerate' => $this->commentProcessor->canModerate( + $response->getComment()->objectTypeID, + $response->getComment()->objectID + ), + 'commentManager' => $this->commentProcessor, + ]); + + return WCF::getTPL()->fetch('commentResponseList'); + } + + /** + * Validates message parameters. + * + * @throws UserInputException + */ + protected function validateMessage() + { + $this->readString('message', false, 'data'); + $this->parameters['data']['message'] = MessageUtil::stripCrap($this->parameters['data']['message']); + + if (empty($this->parameters['data']['message'])) { + throw new UserInputException('message'); + } + + CommentHandler::enforceCensorship($this->parameters['data']['message']); + + $this->setDisallowedBBCodes(); + $htmlInputProcessor = $this->getHtmlInputProcessor( + $this->parameters['data']['message'], + ($this->comment !== null ? $this->comment->commentID : 0) + ); + + // search for disallowed bbcodes + $disallowedBBCodes = $htmlInputProcessor->validate(); + if (!empty($disallowedBBCodes)) { + throw new UserInputException( + 'text', + WCF::getLanguage()->getDynamicVariable( + 'wcf.message.error.disallowedBBCodes', + ['disallowedBBCodes' => $disallowedBBCodes] + ) + ); + } + + if ($htmlInputProcessor->appearsToBeEmpty()) { + throw new UserInputException('message'); + } + + $this->parameters['htmlInputProcessor'] = $htmlInputProcessor; + } + + /** + * Validates object type id parameter. + * + * @param int $objectTypeID + * @return ObjectType + * @throws UserInputException + */ + protected function validateObjectType($objectTypeID = null) + { + if ($objectTypeID === null) { + $this->readInteger('objectTypeID', false, 'data'); + $objectTypeID = $this->parameters['data']['objectTypeID']; + } + + $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); + if ($objectType === null) { + throw new UserInputException('objectTypeID'); + } + + return $objectType; + } + + /** + * Validates comment id parameter. + * + * @throws UserInputException + */ + protected function validateCommentID() + { + $this->readInteger('commentID', false, 'data'); + + $this->comment = new Comment($this->parameters['data']['commentID']); + if ($this->comment === null || !$this->comment->commentID) { + throw new UserInputException('commentID'); + } + } + + /** + * Validates response id parameter. + * + * @throws UserInputException + */ + protected function validateResponseID() + { + if (isset($this->parameters['data']['responseID'])) { + $this->response = new CommentResponse($this->parameters['data']['responseID']); + } + if ($this->response === null || !$this->response->responseID) { + throw new UserInputException('responseID'); + } + } + + /** + * Validates the username parameter. + */ + protected function validateUsername() + { + if (WCF::getUser()->userID) { + return; + } + + try { + $this->readString('username', false, 'data'); + + if (!UserRegistrationUtil::isValidUsername($this->parameters['data']['username'])) { + throw new UserInputException('username', 'invalid'); + } + if (!UserUtil::isAvailableUsername($this->parameters['data']['username'])) { + throw new UserInputException('username', 'notUnique'); + } + } catch (UserInputException $e) { + $this->validationErrors['username'] = $e->getType(); + } + } + + /** + * Validates the captcha challenge. + * + * @throws SystemException + */ + protected function validateCaptcha() + { + if (WCF::getUser()->userID) { + return; + } + + if (CAPTCHA_TYPE) { + $this->captchaObjectType = CaptchaHandler::getInstance()->getObjectTypeByName(CAPTCHA_TYPE); + if ($this->captchaObjectType === null) { + throw new SystemException("Unknown captcha object type with name '" . CAPTCHA_TYPE . "'"); + } + + if (!$this->captchaObjectType->getProcessor()->isAvailable()) { + $this->captchaObjectType = null; + } + } + + if ($this->captchaObjectType === null) { + return; + } + + try { + $this->captchaObjectType->getProcessor()->readFormParameters(); + $this->captchaObjectType->getProcessor()->validate(); + } catch (UserInputException $e) { + $this->validationErrors = \array_merge($this->validationErrors, [$e->getField() => $e->getType()]); + } + } + + /** + * Sets the list of disallowed bbcodes for comments. + */ + protected function setDisallowedBBCodes() + { + BBCodeHandler::getInstance()->setDisallowedBBCodes(\explode( + ',', + WCF::getSession()->getPermission('user.comment.disallowedBBCodes') + )); + } + + /** + * Returns the current html input processor or a new one if `$message` is not null. + * + * @param string|null $message source message + * @param int $objectID object id + * @return HtmlInputProcessor + */ + public function getHtmlInputProcessor($message = null, $objectID = 0) + { + if ($message === null) { + return $this->htmlInputProcessor; + } + + $this->htmlInputProcessor = new HtmlInputProcessor(); + $this->htmlInputProcessor->process($message, 'com.woltlab.wcf.comment', $objectID); + + return $this->htmlInputProcessor; + } + + /** + * Returns the comment object. + * + * @return Comment + */ + public function getComment() + { + return $this->comment; + } + + /** + * Returns the comment response object. + * + * @return CommentResponse + */ + public function getResponse() + { + return $this->response; + } + + /** + * Returns the comment manager. + * + * @return ICommentManager + */ + public function getCommentManager() + { + return $this->commentProcessor; + } } diff --cc wcfsetup/install/files/lib/data/comment/response/CommentResponseAction.class.php index 6fcd93de75,68efcd0a1c..83d113e72a --- a/wcfsetup/install/files/lib/data/comment/response/CommentResponseAction.class.php +++ b/wcfsetup/install/files/lib/data/comment/response/CommentResponseAction.class.php @@@ -14,7 -12,8 +14,8 @@@ use wcf\system\comment\manager\IComment use wcf\system\exception\PermissionDeniedException; use wcf\system\exception\UserInputException; use wcf\system\html\input\HtmlInputProcessor; -use wcf\system\reaction\ReactionHandler; + use wcf\system\moderation\queue\ModerationQueueManager; +use wcf\system\reaction\ReactionHandler; use wcf\system\user\activity\event\UserActivityEventHandler; use wcf\system\user\notification\UserNotificationHandler; use wcf\system\WCF; @@@ -22,393 -21,355 +23,398 @@@ use wcf\util\MessageUtil /** * Executes comment response-related actions. - * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @package WoltLabSuite\Core\Data\Comment\Response - * - * @method CommentResponse create() - * @method CommentResponseEditor[] getObjects() - * @method CommentResponseEditor getSingleObject() + * + * @author Alexander Ebert + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @package WoltLabSuite\Core\Data\Comment\Response + * + * @method CommentResponse create() + * @method CommentResponseEditor[] getObjects() + * @method CommentResponseEditor getSingleObject() */ -class CommentResponseAction extends AbstractDatabaseObjectAction { - /** - * @inheritDoc - */ - protected $allowGuestAccess = ['loadResponses']; - - /** - * @inheritDoc - */ - protected $className = CommentResponseEditor::class; - - /** - * comment object - * @var Comment - */ - public $comment; - - /** - * comment manager object - * @var ICommentManager - */ - public $commentManager; - - /** - * comment processor - * @var ICommentManager - */ - protected $commentProcessor; - - /** - * @var HtmlInputProcessor - */ - protected $htmlInputProcessor; - - /** - * response object - * @var CommentResponse - */ - protected $response; - - /** - * @inheritDoc - */ - public function delete() { - if (empty($this->objects)) { - $this->readObjects(); - } - - if (empty($this->objects)) { - return 0; - } - - $ignoreCounters = !empty($this->parameters['ignoreCounters']); - - // read object type ids for comments - $commentIDs = []; - foreach ($this->getObjects() as $response) { - $commentIDs[] = $response->commentID; - } - - $commentList = new CommentList(); - $commentList->setObjectIDs($commentIDs); - $commentList->readObjects(); - $comments = $commentList->getObjects(); - - // update counters - /** @var ICommentManager[] $processors */ - $processors = $responseIDs = $updateComments = []; - foreach ($this->getObjects() as $response) { - $objectTypeID = $comments[$response->commentID]->objectTypeID; - - if (!isset($processors[$objectTypeID])) { - $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); - $processors[$objectTypeID] = $objectType->getProcessor(); - $responseIDs[$objectTypeID] = []; - } - $responseIDs[$objectTypeID][] = $response->responseID; - - if (!$ignoreCounters && !$response->isDisabled) { - $processors[$objectTypeID]->updateCounter($comments[$response->commentID]->objectID, -1); - - if (!isset($updateComments[$response->commentID])) { - $updateComments[$response->commentID] = 0; - } - - $updateComments[$response->commentID]++; - } - } - - // remove responses - $count = parent::delete(); - - // update comment responses and cached response ids - if (!$ignoreCounters) { - foreach ($comments as $comment) { - $commentEditor = new CommentEditor($comment); - $commentEditor->updateResponseIDs(); - if (isset($updateComments[$comment->commentID])) { - $commentEditor->updateCounters([ - 'responses' => -1 * $updateComments[$comment->commentID] - ]); - } - } - } - - $deletedResponseIDs = []; - $notificationObjectTypes = []; - foreach ($responseIDs as $objectTypeID => $objectIDs) { - // remove activity events - $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); - if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.recentActivityEvent')) { - UserActivityEventHandler::getInstance()->removeEvents($objectType->objectType.'.response.recentActivityEvent', $objectIDs); - } - - // delete notifications - if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.notification')) { - UserNotificationHandler::getInstance()->removeNotifications($objectType->objectType.'.response.notification', $objectIDs); - } - - $deletedResponseIDs = array_merge($deletedResponseIDs, $objectIDs); - - if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.like.notification')) { - $notificationObjectTypes[] = $objectType->objectType.'.response.like.notification'; - } - } - - if (!empty($deletedResponseIDs)) { - // remove likes - ReactionHandler::getInstance()->removeReactions('com.woltlab.wcf.comment.response', $deletedResponseIDs, $notificationObjectTypes); - - ModerationQueueManager::getInstance()->removeQueues( - 'com.woltlab.wcf.comment.response', - $deletedResponseIDs - ); - } - - return $count; - } - - /** - * Validates parameters to load responses for a given comment id. - */ - public function validateLoadResponses() { - $this->readInteger('commentID', false, 'data'); - $this->readInteger('lastResponseTime', false, 'data'); - $this->readBoolean('loadAllResponses', true, 'data'); - - $this->comment = new Comment($this->parameters['data']['commentID']); - if (!$this->comment->commentID) { - throw new UserInputException('commentID'); - } - - $this->commentManager = ObjectTypeCache::getInstance()->getObjectType($this->comment->objectTypeID)->getProcessor(); - if (!$this->commentManager->isAccessible($this->comment->objectID)) { - throw new PermissionDeniedException(); - } - } - - /** - * Returns parsed responses for given comment id. - * - * @return array - */ - public function loadResponses() { - $commentCanModerate = $this->commentManager->canModerate($this->comment->objectTypeID, $this->comment->objectID); - - // get response list - $responseList = new StructuredCommentResponseList($this->commentManager, $this->comment); - $responseList->getConditionBuilder()->add("comment_response.time > ?", [$this->parameters['data']['lastResponseTime']]); - if (!$commentCanModerate) $responseList->getConditionBuilder()->add("comment_response.isDisabled = ?", [0]); - if (!$this->parameters['data']['loadAllResponses']) $responseList->sqlLimit = 50; - $responseList->readObjects(); - - $lastResponseTime = 0; - foreach ($responseList as $response) { - if (!$lastResponseTime) { - $lastResponseTime = $response->time; - } - - $lastResponseTime = max($lastResponseTime, $response->time); - } - - // mark notifications for loaded responses as read - CommentHandler::getInstance()->markNotificationsAsConfirmedForResponses( - CommentHandler::getInstance()->getObjectType($this->comment->objectTypeID)->objectType, - $responseList->getObjects() - ); - - WCF::getTPL()->assign([ - 'commentCanModerate' => $commentCanModerate, - 'likeData' => MODULE_LIKE ? $responseList->getLikeData() : [], - 'responseList' => $responseList, - 'commentManager' => $this->commentManager - ]); - - return [ - 'commentID' => $this->comment->commentID, - 'lastResponseTime' => $lastResponseTime, - 'template' => WCF::getTPL()->fetch('commentResponseList') - ]; - } - - - /** - * @inheritDoc - */ - public function validateBeginEdit() { - $this->response = $this->getSingleObject(); - - // validate object type id - $objectType = $this->validateObjectType(); - - // validate object id and permissions - $this->commentProcessor = $objectType->getProcessor(); - if (!$this->commentProcessor->canEditResponse($this->response->getDecoratedObject())) { - throw new PermissionDeniedException(); - } - - $this->setDisallowedBBCodes(); - } - - /** - * @inheritDoc - */ - public function beginEdit() { - WCF::getTPL()->assign([ - 'response' => $this->response, - 'wysiwygSelector' => 'commentResponseEditor'.$this->response->responseID - ]); - - return [ - 'actionName' => 'beginEdit', - 'template' => WCF::getTPL()->fetch('commentResponseEditor', 'wcf') - ]; - } - - /** - * @inheritDoc - */ - public function validateSave() { - $this->validateBeginEdit(); - - $this->validateMessage(); - } - - /** - * @inheritDoc - */ - public function save() { - /** @var HtmlInputProcessor $htmlInputProcessor */ - $htmlInputProcessor = $this->parameters['htmlInputProcessor']; - - $action = new CommentResponseAction([$this->response], 'update', [ - 'data' => [ - 'message' => $htmlInputProcessor->getHtml() - ] - ]); - $action->executeAction(); - - return [ - 'actionName' => 'save', - 'message' => (new CommentResponse($this->response->responseID))->getFormattedMessage() - ]; - } - - /** - * Validates message parameter. - * - * @throws UserInputException - */ - protected function validateMessage() { - $this->readString('message', false, 'data'); - $this->parameters['data']['message'] = MessageUtil::stripCrap($this->parameters['data']['message']); - - if (empty($this->parameters['data']['message'])) { - throw new UserInputException('message'); - } - - CommentHandler::enforceCensorship($this->parameters['data']['message']); - - $this->setDisallowedBBCodes(); - $htmlInputProcessor = $this->getHtmlInputProcessor($this->parameters['data']['message'], ($this->comment !== null ? $this->comment->commentID : 0)); - - // search for disallowed bbcodes - $disallowedBBCodes = $htmlInputProcessor->validate(); - if (!empty($disallowedBBCodes)) { - throw new UserInputException('text', WCF::getLanguage()->getDynamicVariable('wcf.message.error.disallowedBBCodes', ['disallowedBBCodes' => $disallowedBBCodes])); - } - - if ($htmlInputProcessor->appearsToBeEmpty()) { - throw new UserInputException('message'); - } - - $this->parameters['htmlInputProcessor'] = $htmlInputProcessor; - } - - /** - * Validates object type id parameter. - * - * @param integer $objectTypeID - * @return ObjectType - * @throws UserInputException - */ - protected function validateObjectType($objectTypeID = null) { - if ($objectTypeID === null) { - $this->readInteger('objectTypeID', false, 'data'); - $objectTypeID = $this->parameters['data']['objectTypeID']; - } - - $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); - if ($objectType === null) { - throw new UserInputException('objectTypeID'); - } - - return $objectType; - } - - /** - * Sets the list of disallowed bbcodes for comments. - */ - protected function setDisallowedBBCodes() { - BBCodeHandler::getInstance()->setDisallowedBBCodes(explode(',', WCF::getSession()->getPermission('user.comment.disallowedBBCodes'))); - } - - /** - * Returns the current html input processor or a new one if `$message` is not null. - * - * @param string|null $message source message - * @param integer $objectID object id - * @return HtmlInputProcessor - */ - public function getHtmlInputProcessor($message = null, $objectID = 0) { - if ($message === null) { - return $this->htmlInputProcessor; - } - - $this->htmlInputProcessor = new HtmlInputProcessor(); - $this->htmlInputProcessor->process($message, 'com.woltlab.wcf.comment', $objectID); - - return $this->htmlInputProcessor; - } +class CommentResponseAction extends AbstractDatabaseObjectAction +{ + /** + * @inheritDoc + */ + protected $allowGuestAccess = ['loadResponses']; + + /** + * @inheritDoc + */ + protected $className = CommentResponseEditor::class; + + /** + * comment object + * @var Comment + */ + public $comment; + + /** + * comment manager object + * @var ICommentManager + */ + public $commentManager; + + /** + * comment processor + * @var ICommentManager + */ + protected $commentProcessor; + + /** + * @var HtmlInputProcessor + */ + protected $htmlInputProcessor; + + /** + * response object + * @var CommentResponse + */ + protected $response; + + /** + * @inheritDoc + */ + public function delete() + { + if (empty($this->objects)) { + $this->readObjects(); + } + + if (empty($this->objects)) { + return 0; + } + + $ignoreCounters = !empty($this->parameters['ignoreCounters']); + + // read object type ids for comments + $commentIDs = []; + foreach ($this->getObjects() as $response) { + $commentIDs[] = $response->commentID; + } + + $commentList = new CommentList(); + $commentList->setObjectIDs($commentIDs); + $commentList->readObjects(); + $comments = $commentList->getObjects(); + + // update counters + /** @var ICommentManager[] $processors */ + $processors = $responseIDs = $updateComments = []; + foreach ($this->getObjects() as $response) { + $objectTypeID = $comments[$response->commentID]->objectTypeID; + + if (!isset($processors[$objectTypeID])) { + $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); + $processors[$objectTypeID] = $objectType->getProcessor(); + $responseIDs[$objectTypeID] = []; + } + $responseIDs[$objectTypeID][] = $response->responseID; + + if (!$ignoreCounters && !$response->isDisabled) { + $processors[$objectTypeID]->updateCounter($comments[$response->commentID]->objectID, -1); + + if (!isset($updateComments[$response->commentID])) { + $updateComments[$response->commentID] = 0; + } + + $updateComments[$response->commentID]++; + } + } + + // remove responses + $count = parent::delete(); + + // update comment responses and cached response ids + if (!$ignoreCounters) { + foreach ($comments as $comment) { + $commentEditor = new CommentEditor($comment); + $commentEditor->updateResponseIDs(); + if (isset($updateComments[$comment->commentID])) { + $commentEditor->updateCounters([ + 'responses' => -1 * $updateComments[$comment->commentID], + ]); + } + } + } + - $likeObjectIDs = []; ++ $deletedResponseIDs = []; + $notificationObjectTypes = []; + foreach ($responseIDs as $objectTypeID => $objectIDs) { + // remove activity events + $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); + if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType . '.response.recentActivityEvent')) { + UserActivityEventHandler::getInstance()->removeEvents( + $objectType->objectType . '.response.recentActivityEvent', + $objectIDs + ); + } + + // delete notifications + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.response.notification')) { + UserNotificationHandler::getInstance()->removeNotifications( + $objectType->objectType . '.response.notification', + $objectIDs + ); + } + - $likeObjectIDs = \array_merge($likeObjectIDs, $objectIDs); ++ $deletedResponseIDs = \array_merge($deletedResponseIDs, $objectIDs); + + if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType . '.response.like.notification')) { + $notificationObjectTypes[] = $objectType->objectType . '.response.like.notification'; + } + } + - // remove likes - if (!empty($likeObjectIDs)) { ++ if (!empty($deletedResponseIDs)) { ++ // remove likes + ReactionHandler::getInstance()->removeReactions( + 'com.woltlab.wcf.comment.response', - $likeObjectIDs, ++ $deletedResponseIDs, + $notificationObjectTypes + ); ++ ++ ModerationQueueManager::getInstance()->removeQueues( ++ 'com.woltlab.wcf.comment.response', ++ $deletedResponseIDs ++ ); + } + + return $count; + } + + /** + * Validates parameters to load responses for a given comment id. + */ + public function validateLoadResponses() + { + $this->readInteger('commentID', false, 'data'); + $this->readInteger('lastResponseTime', false, 'data'); + $this->readBoolean('loadAllResponses', true, 'data'); + + $this->comment = new Comment($this->parameters['data']['commentID']); + if (!$this->comment->commentID) { + throw new UserInputException('commentID'); + } + + $this->commentManager = ObjectTypeCache::getInstance()->getObjectType($this->comment->objectTypeID)->getProcessor(); + if (!$this->commentManager->isAccessible($this->comment->objectID)) { + throw new PermissionDeniedException(); + } + } + + /** + * Returns parsed responses for given comment id. + * + * @return array + */ + public function loadResponses() + { + $commentCanModerate = $this->commentManager->canModerate( + $this->comment->objectTypeID, + $this->comment->objectID + ); + + // get response list + $responseList = new StructuredCommentResponseList($this->commentManager, $this->comment); + $responseList->getConditionBuilder()->add( + "comment_response.time > ?", + [$this->parameters['data']['lastResponseTime']] + ); + if (!$commentCanModerate) { + $responseList->getConditionBuilder()->add("comment_response.isDisabled = ?", [0]); + } + if (!$this->parameters['data']['loadAllResponses']) { + $responseList->sqlLimit = 50; + } + $responseList->readObjects(); + + $lastResponseTime = 0; + foreach ($responseList as $response) { + if (!$lastResponseTime) { + $lastResponseTime = $response->time; + } + + $lastResponseTime = \max($lastResponseTime, $response->time); + } + + // mark notifications for loaded responses as read + CommentHandler::getInstance()->markNotificationsAsConfirmedForResponses( + CommentHandler::getInstance()->getObjectType($this->comment->objectTypeID)->objectType, + $responseList->getObjects() + ); + + WCF::getTPL()->assign([ + 'commentCanModerate' => $commentCanModerate, + 'likeData' => MODULE_LIKE ? $responseList->getLikeData() : [], + 'responseList' => $responseList, + 'commentManager' => $this->commentManager, + ]); + + return [ + 'commentID' => $this->comment->commentID, + 'lastResponseTime' => $lastResponseTime, + 'template' => WCF::getTPL()->fetch('commentResponseList'), + ]; + } + + /** + * @inheritDoc + */ + public function validateBeginEdit() + { + $this->response = $this->getSingleObject(); + + // validate object type id + $objectType = $this->validateObjectType(); + + // validate object id and permissions + $this->commentProcessor = $objectType->getProcessor(); + if (!$this->commentProcessor->canEditResponse($this->response->getDecoratedObject())) { + throw new PermissionDeniedException(); + } + + $this->setDisallowedBBCodes(); + } + + /** + * @inheritDoc + */ + public function beginEdit() + { + WCF::getTPL()->assign([ + 'response' => $this->response, + 'wysiwygSelector' => 'commentResponseEditor' . $this->response->responseID, + ]); + + return [ + 'actionName' => 'beginEdit', + 'template' => WCF::getTPL()->fetch('commentResponseEditor', 'wcf'), + ]; + } + + /** + * @inheritDoc + */ + public function validateSave() + { + $this->validateBeginEdit(); + + $this->validateMessage(); + } + + /** + * @inheritDoc + */ + public function save() + { + /** @var HtmlInputProcessor $htmlInputProcessor */ + $htmlInputProcessor = $this->parameters['htmlInputProcessor']; + + $action = new self([$this->response], 'update', [ + 'data' => [ + 'message' => $htmlInputProcessor->getHtml(), + ], + ]); + $action->executeAction(); + + return [ + 'actionName' => 'save', + 'message' => (new CommentResponse($this->response->responseID))->getFormattedMessage(), + ]; + } + + /** + * Validates message parameter. + * + * @throws UserInputException + */ + protected function validateMessage() + { + $this->readString('message', false, 'data'); + $this->parameters['data']['message'] = MessageUtil::stripCrap($this->parameters['data']['message']); + + if (empty($this->parameters['data']['message'])) { + throw new UserInputException('message'); + } + + CommentHandler::enforceCensorship($this->parameters['data']['message']); + + $this->setDisallowedBBCodes(); + $htmlInputProcessor = $this->getHtmlInputProcessor( + $this->parameters['data']['message'], + ($this->comment !== null ? $this->comment->commentID : 0) + ); + + // search for disallowed bbcodes + $disallowedBBCodes = $htmlInputProcessor->validate(); + if (!empty($disallowedBBCodes)) { + throw new UserInputException( + 'text', + WCF::getLanguage()->getDynamicVariable( + 'wcf.message.error.disallowedBBCodes', + ['disallowedBBCodes' => $disallowedBBCodes] + ) + ); + } + + if ($htmlInputProcessor->appearsToBeEmpty()) { + throw new UserInputException('message'); + } + + $this->parameters['htmlInputProcessor'] = $htmlInputProcessor; + } + + /** + * Validates object type id parameter. + * + * @param int $objectTypeID + * @return ObjectType + * @throws UserInputException + */ + protected function validateObjectType($objectTypeID = null) + { + if ($objectTypeID === null) { + $this->readInteger('objectTypeID', false, 'data'); + $objectTypeID = $this->parameters['data']['objectTypeID']; + } + + $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID); + if ($objectType === null) { + throw new UserInputException('objectTypeID'); + } + + return $objectType; + } + + /** + * Sets the list of disallowed bbcodes for comments. + */ + protected function setDisallowedBBCodes() + { + BBCodeHandler::getInstance()->setDisallowedBBCodes(\explode( + ',', + WCF::getSession()->getPermission('user.comment.disallowedBBCodes') + )); + } + + /** + * Returns the current html input processor or a new one if `$message` is not null. + * + * @param string|null $message source message + * @param int $objectID object id + * @return HtmlInputProcessor + */ + public function getHtmlInputProcessor($message = null, $objectID = 0) + { + if ($message === null) { + return $this->htmlInputProcessor; + } + + $this->htmlInputProcessor = new HtmlInputProcessor(); + $this->htmlInputProcessor->process($message, 'com.woltlab.wcf.comment', $objectID); + + return $this->htmlInputProcessor; + } } diff --cc wcfsetup/install/files/lib/system/moderation/queue/activation/CommentResponseModerationQueueActivationHandler.class.php index 93262c7b28,ef1a670c4a..373ba3eee3 --- a/wcfsetup/install/files/lib/system/moderation/queue/activation/CommentResponseModerationQueueActivationHandler.class.php +++ b/wcfsetup/install/files/lib/system/moderation/queue/activation/CommentResponseModerationQueueActivationHandler.class.php @@@ -9,35 -7,36 +9,40 @@@ use wcf\system\moderation\queue\Abstrac /** * An implementation of IModerationQueueReportHandler for comment responses. - * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @package WoltLabSuite\Core\System\Moderation\Queue + * + * @author Alexander Ebert + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @package WoltLabSuite\Core\System\Moderation\Queue */ -class CommentResponseModerationQueueActivationHandler extends AbstractCommentResponseModerationQueueHandler implements IModerationQueueActivationHandler { - /** - * @inheritDoc - */ - protected $definitionName = 'com.woltlab.wcf.moderation.activation'; - - /** - * @inheritDoc - */ - public function enableContent(ModerationQueue $queue) { - if ($this->isValid($queue->objectID) && $this->getResponse($queue->objectID)->isDisabled) { - $response = $this->getResponse($queue->objectID); - - $commentAction = new CommentAction([$this->getComment($response->commentID)], 'enableResponse', [ - 'responses' => [$response] - ]); - $commentAction->executeAction(); - } - } - - /** - * @inheritDoc - */ - public function getDisabledContent(ViewableModerationQueue $queue) { - return $this->getRelatedContent($queue); - } +class CommentResponseModerationQueueActivationHandler extends AbstractCommentResponseModerationQueueHandler implements + IModerationQueueActivationHandler +{ ++ /** ++ * @inheritDoc ++ */ ++ protected $definitionName = 'com.woltlab.wcf.moderation.activation'; ++ + /** + * @inheritDoc + */ + public function enableContent(ModerationQueue $queue) + { + if ($this->isValid($queue->objectID) && $this->getResponse($queue->objectID)->isDisabled) { + $response = $this->getResponse($queue->objectID); + + $commentAction = new CommentAction([$this->getComment($response->commentID)], 'enableResponse', [ + 'responses' => [$response], + ]); + $commentAction->executeAction(); + } + } + + /** + * @inheritDoc + */ + public function getDisabledContent(ViewableModerationQueue $queue) + { + return $this->getRelatedContent($queue); + } } diff --cc wcfsetup/install/files/lib/system/moderation/queue/report/CommentResponseModerationQueueReportHandler.class.php index c304ad8df8,b43bec10aa..b1c839d819 --- a/wcfsetup/install/files/lib/system/moderation/queue/report/CommentResponseModerationQueueReportHandler.class.php +++ b/wcfsetup/install/files/lib/system/moderation/queue/report/CommentResponseModerationQueueReportHandler.class.php @@@ -7,46 -5,46 +7,51 @@@ use wcf\system\moderation\queue\Abstrac /** * An implementation of IModerationQueueReportHandler for comment responses. - * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @package WoltLabSuite\Core\System\Moderation\Queue + * + * @author Alexander Ebert + * @copyright 2001-2019 WoltLab GmbH + * @license GNU Lesser General Public License + * @package WoltLabSuite\Core\System\Moderation\Queue */ -class CommentResponseModerationQueueReportHandler extends AbstractCommentResponseModerationQueueHandler implements IModerationQueueReportHandler { - /** - * @inheritDoc - */ - protected $definitionName = 'com.woltlab.wcf.moderation.report'; - - /** - * @inheritDoc - */ - public function canReport($objectID) { - if (!$this->isValid($objectID)) { - return false; - } - - $response = $this->getResponse($objectID); - $comment = $this->getComment($response->commentID); - if (!$this->getCommentManager($comment)->isAccessible($comment->objectID)) { - return false; - } - - return true; - } - - /** - * @inheritDoc - */ - public function getReportedContent(ViewableModerationQueue $queue) { - return $this->getRelatedContent($queue); - } - - /** - * @inheritDoc - */ - public function getReportedObject($objectID) { - return $this->getResponse($objectID); - } +class CommentResponseModerationQueueReportHandler extends AbstractCommentResponseModerationQueueHandler implements + IModerationQueueReportHandler +{ ++ /** ++ * @inheritDoc ++ */ ++ protected $definitionName = 'com.woltlab.wcf.moderation.report'; ++ + /** + * @inheritDoc + */ + public function canReport($objectID) + { + if (!$this->isValid($objectID)) { + return false; + } + + $response = $this->getResponse($objectID); + $comment = $this->getComment($response->commentID); + if (!$this->getCommentManager($comment)->isAccessible($comment->objectID)) { + return false; + } + + return true; + } + + /** + * @inheritDoc + */ + public function getReportedContent(ViewableModerationQueue $queue) + { + return $this->getRelatedContent($queue); + } + + /** + * @inheritDoc + */ + public function getReportedObject($objectID) + { + return $this->getResponse($objectID); + } }