2 namespace wcf\data\comment
;
3 use wcf\data\comment\response\CommentResponse
;
4 use wcf\data\comment\response\CommentResponseAction
;
5 use wcf\data\comment\response\CommentResponseEditor
;
6 use wcf\data\comment\response\CommentResponseList
;
7 use wcf\data\comment\response\StructuredCommentResponse
;
8 use wcf\data\
object\type\ObjectTypeCache
;
9 use wcf\data\user\UserProfile
;
10 use wcf\data\AbstractDatabaseObjectAction
;
11 use wcf\system\comment\CommentHandler
;
12 use wcf\system\exception\PermissionDeniedException
;
13 use wcf\system\exception\UserInputException
;
14 use wcf\system\like\LikeHandler
;
15 use wcf\system\recaptcha\RecaptchaHandler
;
16 use wcf\system\user\activity\event\UserActivityEventHandler
;
17 use wcf\system\user\notification\
object\CommentResponseUserNotificationObject
;
18 use wcf\system\user\notification\
object\CommentUserNotificationObject
;
19 use wcf\system\user\notification\UserNotificationHandler
;
21 use wcf\util\MessageUtil
;
22 use wcf\util\UserUtil
;
25 * Executes comment-related actions.
27 * @author Alexander Ebert
28 * @copyright 2001-2014 WoltLab GmbH
29 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
30 * @package com.woltlab.wcf
31 * @subpackage data.comment
32 * @category Community Framework
34 class CommentAction
extends AbstractDatabaseObjectAction
{
36 * @see \wcf\data\AbstractDatabaseObjectAction::$allowGuestAccess
38 protected $allowGuestAccess = array('addComment', 'addResponse', 'loadComments', 'getGuestDialog');
41 * @see \wcf\data\AbstractDatabaseObjectAction::$className
43 protected $className = 'wcf\data\comment\CommentEditor';
47 * @var \wcf\data\comment\Comment
49 protected $comment = null;
53 * @var \wcf\system\comment\manager\ICommentManager
55 protected $commentProcessor = null;
59 * @var \wcf\data\comment\response\CommentResponse
61 protected $response = null;
64 * comment object created by addComment()
65 * @var \wcf\data\comment\Comment
67 public $createdComment = null;
70 * response object created by addResponse()
71 * @var \wcf\data\comment\response\CommentResponse
73 public $createdResponse = null;
76 * errors occuring durch the validation of addComment or addResponse
79 public $validationErrors = array();
82 * @see \wcf\data\AbstractDatabaseObjectAction::delete()
84 public function delete() {
85 if (empty($this->objects
)) {
90 $processors = array();
91 $groupCommentIDs = $commentIDs = array();
92 foreach ($this->objects
as $comment) {
93 if (!isset($processors[$comment->objectTypeID
])) {
94 $objectType = ObjectTypeCache
::getInstance()->getObjectType($comment->objectTypeID
);
95 $processors[$comment->objectTypeID
] = $objectType->getProcessor();
97 $groupCommentIDs[$comment->objectTypeID
] = array();
100 $processors[$comment->objectTypeID
]->updateCounter($comment->objectID
, -1 * ($comment->responses +
1));
101 $groupCommentIDs[$comment->objectTypeID
][] = $comment->commentID
;
102 $commentIDs[] = $comment->commentID
;
105 if (!empty($groupCommentIDs)) {
106 $likeObjectIDs = array();
107 foreach ($groupCommentIDs as $objectTypeID => $objectIDs) {
108 // remove activity events
109 $objectType = ObjectTypeCache
::getInstance()->getObjectType($objectTypeID);
110 if (UserActivityEventHandler
::getInstance()->getObjectTypeID($objectType->objectType
.'.recentActivityEvent')) {
111 UserActivityEventHandler
::getInstance()->removeEvents($objectType->objectType
.'.recentActivityEvent', $objectIDs);
114 $likeObjectIDs = array_merge($likeObjectIDs, $objectIDs);
116 // delete notifications
117 $objectType = ObjectTypeCache
::getInstance()->getObjectType($comment->objectTypeID
);
118 if (UserNotificationHandler
::getInstance()->getObjectTypeID($objectType->objectType
.'.notification')) {
119 UserNotificationHandler
::getInstance()->deleteNotifications('comment', $objectType->objectType
.'.notification', array(), $objectIDs);
124 LikeHandler
::getInstance()->removeLikes('com.woltlab.wcf.comment', $likeObjectIDs);
128 if (!empty($commentIDs)) {
129 $commentResponseList = new CommentResponseList();
130 $commentResponseList->getConditionBuilder()->add('comment_response.commentID IN (?)', array($commentIDs));
131 $commentResponseList->readObjectIDs();
132 if (count($commentResponseList->getObjectIDs())) {
133 $action = new CommentResponseAction($commentResponseList->getObjectIDs(), 'delete', array(
134 'ignoreCounters' => true
136 $action->executeAction();
140 return parent
::delete();
144 * Validates parameters to load comments.
146 public function validateLoadComments() {
147 $this->readInteger('lastCommentTime', false, 'data');
148 $this->readInteger('objectID', false, 'data');
150 $objectType = $this->validateObjectType();
151 $this->commentProcessor
= $objectType->getProcessor();
152 if (!$this->commentProcessor
->isAccessible($this->parameters
['data']['objectID'])) {
153 throw new PermissionDeniedException();
158 * Returns parsed comments.
162 public function loadComments() {
163 $commentList = CommentHandler
::getInstance()->getCommentList($this->commentProcessor
, $this->parameters
['data']['objectTypeID'], $this->parameters
['data']['objectID'], false);
164 $commentList->getConditionBuilder()->add("comment.time < ?", array($this->parameters
['data']['lastCommentTime']));
165 $commentList->readObjects();
167 WCF
::getTPL()->assign(array(
168 'commentList' => $commentList,
169 'likeData' => (MODULE_LIKE ?
$commentList->getLikeData() : array())
173 'lastCommentTime' => $commentList->getMinCommentTime(),
174 'template' => WCF
::getTPL()->fetch('commentList')
179 * Validates parameters to add a comment.
181 public function validateAddComment() {
182 CommentHandler
::enforceFloodControl();
184 $this->readInteger('objectID', false, 'data');
186 $this->validateUsername();
187 $this->validateRecaptcha();
189 $this->validateMessage();
190 $objectType = $this->validateObjectType();
192 // validate object id and permissions
193 $this->commentProcessor
= $objectType->getProcessor();
194 if (!$this->commentProcessor
->canAdd($this->parameters
['data']['objectID'])) {
195 throw new PermissionDeniedException();
204 public function addComment() {
205 if (!empty($this->validationErrors
)) {
207 'errors' => $this->validationErrors
212 $this->createdComment
= CommentEditor
::create(array(
213 'objectTypeID' => $this->parameters
['data']['objectTypeID'],
214 'objectID' => $this->parameters
['data']['objectID'],
216 'userID' => WCF
::getUser()->userID ?
: null,
217 'username' => WCF
::getUser()->userID ? WCF
::getUser()->username
: $this->parameters
['data']['username'],
218 'message' => $this->parameters
['data']['message'],
220 'responseIDs' => serialize(array())
224 $this->commentProcessor
->updateCounter($this->parameters
['data']['objectID'], 1);
226 // fire activity event
227 $objectType = ObjectTypeCache
::getInstance()->getObjectType($this->parameters
['data']['objectTypeID']);
228 if ($this->createdComment
->userID
&& UserActivityEventHandler
::getInstance()->getObjectTypeID($objectType->objectType
.'.recentActivityEvent')) {
229 UserActivityEventHandler
::getInstance()->fireEvent($objectType->objectType
.'.recentActivityEvent', $this->createdComment
->commentID
);
232 // fire notification event
233 if (UserNotificationHandler
::getInstance()->getObjectTypeID($objectType->objectType
.'.notification')) {
234 $notificationObjectType = UserNotificationHandler
::getInstance()->getObjectTypeProcessor($objectType->objectType
.'.notification');
235 $userID = $notificationObjectType->getOwnerID($this->createdComment
->commentID
);
236 if ($userID != WCF
::getUser()->userID
) {
237 $notificationObject = new CommentUserNotificationObject($this->createdComment
);
239 UserNotificationHandler
::getInstance()->fireEvent('comment', $objectType->objectType
.'.notification', $notificationObject, array($userID));
243 if (!$this->createdComment
->userID
) {
244 // save user name is session
245 WCF
::getSession()->register('username', $this->createdComment
->username
);
247 // save last comment time for flood control
248 WCF
::getSession()->register('lastCommentTime', $this->createdComment
->time
);
250 // unmark recaptcha as done for furture requests
251 WCF
::getSession()->unregister('recaptchaDone');
255 'template' => $this->renderComment($this->createdComment
)
260 * Validates parameters to add a response.
262 public function validateAddResponse() {
263 CommentHandler
::enforceFloodControl();
265 $this->readInteger('objectID', false, 'data');
266 $this->validateMessage();
268 $this->validateUsername();
269 $this->validateRecaptcha();
271 // validate comment id
272 $this->validateCommentID();
274 $objectType = $this->validateObjectType();
276 // validate object id and permissions
277 $this->commentProcessor
= $objectType->getProcessor();
278 if (!$this->commentProcessor
->canAdd($this->parameters
['data']['objectID'])) {
279 throw new PermissionDeniedException();
288 public function addResponse() {
289 if (!empty($this->validationErrors
)) {
291 'errors' => $this->validationErrors
296 $this->createdResponse
= CommentResponseEditor
::create(array(
297 'commentID' => $this->comment
->commentID
,
299 'userID' => WCF
::getUser()->userID ?
: null,
300 'username' => WCF
::getUser()->userID ? WCF
::getUser()->username
: $this->parameters
['data']['username'],
301 'message' => $this->parameters
['data']['message']
304 // update response data
305 $responseIDs = $this->comment
->getResponseIDs();
306 if (count($responseIDs) < 3) {
307 $responseIDs[] = $this->createdResponse
->responseID
;
309 $responses = $this->comment
->responses +
1;
312 $commentEditor = new CommentEditor($this->comment
);
313 $commentEditor->update(array(
314 'responseIDs' => serialize($responseIDs),
315 'responses' => $responses
319 $this->commentProcessor
->updateCounter($this->parameters
['data']['objectID'], 1);
321 // fire activity event
322 $objectType = ObjectTypeCache
::getInstance()->getObjectType($this->comment
->objectTypeID
);
323 if ($this->createdResponse
->userID
&& UserActivityEventHandler
::getInstance()->getObjectTypeID($objectType->objectType
.'.response.recentActivityEvent')) {
324 UserActivityEventHandler
::getInstance()->fireEvent($objectType->objectType
.'.response.recentActivityEvent', $this->createdResponse
->responseID
);
327 // fire notification event
328 if (UserNotificationHandler
::getInstance()->getObjectTypeID($objectType->objectType
.'.response.notification')) {
329 $notificationObject = new CommentResponseUserNotificationObject($this->createdResponse
);
330 if ($this->comment
->userID
!= WCF
::getUser()->userID
) {
331 UserNotificationHandler
::getInstance()->fireEvent('commentResponse', $objectType->objectType
.'.response.notification', $notificationObject, array($this->comment
->userID
));
334 // notify the container owner
335 if (UserNotificationHandler
::getInstance()->getObjectTypeID($objectType->objectType
.'.notification')) {
336 $notificationObjectType = UserNotificationHandler
::getInstance()->getObjectTypeProcessor($objectType->objectType
.'.notification');
337 $userID = $notificationObjectType->getOwnerID($this->comment
->commentID
);
339 if ($userID != $this->comment
->userID
&& $userID != WCF
::getUser()->userID
) {
340 UserNotificationHandler
::getInstance()->fireEvent('commentResponseOwner', $objectType->objectType
.'.response.notification', $notificationObject, array($userID));
345 if (!$this->createdResponse
->userID
) {
346 // save user name is session
347 WCF
::getSession()->register('username', $this->createdResponse
->username
);
349 // save last comment time for flood control
350 WCF
::getSession()->register('lastCommentTime', $this->createdResponse
->time
);
352 // unmark recaptcha as done for furture requests
353 WCF
::getSession()->unregister('recaptchaDone');
357 'commentID' => $this->comment
->commentID
,
358 'template' => $this->renderResponse($this->createdResponse
),
359 'responses' => $responses
364 * Validates parameters to edit a comment or a response.
366 public function validatePrepareEdit() {
367 // validate comment id or response id
369 $this->validateCommentID();
371 catch (UserInputException
$e) {
373 $this->validateResponseID();
375 catch (UserInputException
$e) {
376 throw new UserInputException('objectIDs');
380 // validate object type id
381 $objectType = $this->validateObjectType();
383 // validate object id and permissions
384 $this->commentProcessor
= $objectType->getProcessor();
385 if ($this->comment
!== null) {
386 if (!$this->commentProcessor
->canEditComment($this->comment
)) {
387 throw new PermissionDeniedException();
391 if (!$this->commentProcessor
->canEditResponse($this->response
)) {
392 throw new PermissionDeniedException();
398 * Prepares editing of a comment or a response.
402 public function prepareEdit() {
404 if ($this->comment
!== null) {
405 $message = $this->comment
->message
;
408 $message = $this->response
->message
;
411 $returnValues = array(
412 'action' => 'prepare',
413 'message' => $message
415 if ($this->comment
!== null) {
416 $returnValues['commentID'] = $this->comment
->commentID
;
419 $returnValues['responseID'] = $this->response
->responseID
;
422 return $returnValues;
426 * @see \wcf\data\comment\CommentAction::validatePrepareEdit()
428 public function validateEdit() {
429 $this->validatePrepareEdit();
431 $this->validateMessage();
435 * Edits a comment or response.
439 public function edit() {
440 $returnValues = array(
444 if ($this->response
=== null) {
445 $editor = new CommentEditor($this->comment
);
446 $editor->update(array(
447 'message' => $this->parameters
['data']['message']
449 $comment = new Comment($this->comment
->commentID
);
450 $returnValues['commentID'] = $this->comment
->commentID
;
451 $returnValues['message'] = $comment->getFormattedMessage();
454 $editor = new CommentResponseEditor($this->response
);
455 $editor->update(array(
456 'message' => $this->parameters
['data']['message']
458 $response = new CommentResponse($this->response
->responseID
);
459 $returnValues['responseID'] = $this->response
->responseID
;
460 $returnValues['message'] = $response->getFormattedMessage();
463 return $returnValues;
467 * Validates parameters to remove a comment or response.
469 public function validateRemove() {
470 // validate comment id or response id
472 $this->validateCommentID();
474 catch (UserInputException
$e) {
476 $this->validateResponseID();
478 catch (UserInputException
$e) {
479 throw new UserInputException('objectIDs');
483 // validate object type id
484 $objectType = $this->validateObjectType();
486 // validate object id and permissions
487 $this->commentProcessor
= $objectType->getProcessor();
488 if ($this->comment
!== null) {
489 if (!$this->commentProcessor
->canDeleteComment($this->comment
)) {
490 throw new PermissionDeniedException();
494 if (!$this->commentProcessor
->canDeleteResponse($this->response
)) {
495 throw new PermissionDeniedException();
501 * Removes a comment or response.
505 public function remove() {
506 if ($this->comment
!== null) {
507 $objectAction = new CommentAction(array($this->comment
), 'delete');
508 $objectAction->executeAction();
511 'commentID' => $this->comment
->commentID
515 $objectAction = new CommentResponseAction(array($this->response
), 'delete');
516 $objectAction->executeAction();
519 'responseID' => $this->response
->responseID
525 * Validates the 'getGuestDialog' action.
527 public function validateGetGuestDialog() {
528 if (WCF
::getUser()->userID
) {
529 throw new PermissionDeniedException();
532 CommentHandler
::enforceFloodControl();
534 $this->readInteger('objectID', false, 'data');
535 $objectType = $this->validateObjectType();
537 // validate object id and permissions
538 $this->commentProcessor
= $objectType->getProcessor();
539 if (!$this->commentProcessor
->canAdd($this->parameters
['data']['objectID'])) {
540 throw new PermissionDeniedException();
543 // validate message already at this point to make sure that the
544 // message is valid when submitting the dialog to avoid having to
545 // go back to the message to fix it
546 $this->validateMessage();
550 * Returns the dialog for guests when they try to write a comment letting
551 * them enter a username and solving a captcha.
555 public function getGuestDialog() {
556 RecaptchaHandler
::getInstance()->assignVariables();
559 'template' => WCF
::getTPL()->fetch('commentAddGuestDialog', 'wcf', array(
560 'ajaxRecaptcha' => true,
561 'username' => WCF
::getSession()->getVar('username')
569 * @param \wcf\data\comment\Comment $comment
572 protected function renderComment(Comment
$comment) {
573 $comment = new StructuredComment($comment);
574 $comment->setIsDeletable($this->commentProcessor
->canDeleteComment($comment->getDecoratedObject()));
575 $comment->setIsEditable($this->commentProcessor
->canEditComment($comment->getDecoratedObject()));
578 if ($comment->userID
) {
579 $userProfile = UserProfile
::getUserProfile($comment->userID
);
580 $comment->setUserProfile($userProfile);
583 WCF
::getTPL()->assign(array(
584 'commentList' => array($comment)
586 return WCF
::getTPL()->fetch('commentList');
590 * Renders a response.
592 * @param \wcf\data\comment\response\CommentResponse $response
595 protected function renderResponse(CommentResponse
$response) {
596 $response = new StructuredCommentResponse($response);
597 $response->setIsDeletable($this->commentProcessor
->canDeleteResponse($response->getDecoratedObject()));
598 $response->setIsEditable($this->commentProcessor
->canEditResponse($response->getDecoratedObject()));
601 if ($response->userID
) {
602 $userProfile = UserProfile
::getUserProfile($response->userID
);
603 $response->setUserProfile($userProfile);
607 WCF
::getTPL()->assign(array(
608 'responseList' => array($response)
610 return WCF
::getTPL()->fetch('commentResponseList');
614 * Validates message parameter.
616 protected function validateMessage() {
617 $this->readString('message', false, 'data');
618 $this->parameters
['data']['message'] = MessageUtil
::stripCrap($this->parameters
['data']['message']);
620 if (empty($this->parameters
['data']['message'])) {
621 throw new UserInputException('message');
626 * Validates object type id parameter.
628 * @return \wcf\data\object\type\ObjectType
630 protected function validateObjectType() {
631 $this->readInteger('objectTypeID', false, 'data');
633 $objectType = ObjectTypeCache
::getInstance()->getObjectType($this->parameters
['data']['objectTypeID']);
634 if ($objectType === null) {
635 throw new UserInputException('objectTypeID');
642 * Validates comment id parameter.
644 protected function validateCommentID() {
645 $this->readInteger('commentID', false, 'data');
647 $this->comment
= new Comment($this->parameters
['data']['commentID']);
648 if ($this->comment
=== null ||
!$this->comment
->commentID
) {
649 throw new UserInputException('commentID');
654 * Validates response id parameter.
656 protected function validateResponseID() {
657 if (isset($this->parameters
['data']['responseID'])) {
658 $this->response
= new CommentResponse($this->parameters
['data']['responseID']);
660 if ($this->response
=== null ||
!$this->response
->responseID
) {
661 throw new UserInputException('responseID');
666 * Validates the username parameter.
668 protected function validateUsername() {
669 if (WCF
::getUser()->userID
) return;
672 $this->readString('username', false, 'data');
674 if (!UserUtil
::isValidUsername($this->parameters
['data']['username'])) {
675 throw new UserInputException('username', 'notValid');
677 if (!UserUtil
::isAvailableUsername($this->parameters
['data']['username'])) {
678 throw new UserInputException('username', 'notUnique');
681 catch (UserInputException
$e) {
682 if ($e->getType() == 'empty') {
683 $this->validationErrors
['username'] = WCF
::getLanguage()->get('wcf.global.form.error.empty');
686 $this->validationErrors
['username'] = WCF
::getLanguage()->get('wcf.user.username.error.'.$e->getType());
692 * Validates the recaptcha challenge.
694 protected function validateRecaptcha() {
695 if (WCF
::getUser()->userID ||
!MODULE_SYSTEM_RECAPTCHA || WCF
::getSession()->getVar('recaptchaDone')) return;
697 $this->readString('recaptchaChallenge');
698 $this->readString('recaptchaResponse');
701 RecaptchaHandler
::getInstance()->validate($this->parameters
['recaptchaChallenge'], $this->parameters
['recaptchaResponse']);
703 catch (UserInputException
$e) {
704 $this->validationErrors
['recaptcha'] = WCF
::getLanguage()->get('wcf.recaptcha.error.recaptchaString.false');
709 * Returns the comment object.
711 * @return \wcf\data\comment\Comment
713 public function getComment() {
714 return $this->comment
;
718 * Returns the comment response object.
720 * @return \wcf\data\comment\response\CommentResponse
722 public function getResponse() {
723 return $this->response
;
727 * Returns the comment manager.
729 * @return \wcf\system\comment\manager\ICommentManager
731 public function getCommentManager() {
732 return $this->commentProcessor
;