Merge branch '2.1' of github.com:WoltLab/com.woltlab.wcf.conversation into 2.1
[GitHub/WoltLab/com.woltlab.wcf.conversation.git] / files / lib / data / conversation / ConversationAction.class.php
CommitLineData
9544b6b4
MW
1<?php
2namespace wcf\data\conversation;
5e279c42 3use wcf\data\conversation\label\ConversationLabel;
9544b6b4 4use wcf\data\conversation\message\ConversationMessageAction;
5d7f0df0 5use wcf\data\conversation\message\ConversationMessageList;
e2196ce0 6use wcf\data\conversation\message\ViewableConversationMessageList;
d0051596 7use wcf\data\package\PackageCache;
b475685e 8use wcf\data\AbstractDatabaseObjectAction;
232cdc4b 9use wcf\data\IClipboardAction;
d8963ec2 10use wcf\data\IVisitableObjectAction;
e8fe47c2 11use wcf\system\clipboard\ClipboardHandler;
7f07124d 12use wcf\system\conversation\ConversationHandler;
5d7f0df0 13use wcf\system\database\util\PreparedStatementConditionBuilder;
5e279c42 14use wcf\system\exception\PermissionDeniedException;
e8fe47c2 15use wcf\system\exception\UserInputException;
65a160b7 16use wcf\system\log\modification\ConversationModificationLogHandler;
b2e0a2ad 17use wcf\system\request\LinkHandler;
8b467fcd
MW
18use wcf\system\user\notification\object\ConversationUserNotificationObject;
19use wcf\system\user\notification\UserNotificationHandler;
9544b6b4
MW
20use wcf\system\user\storage\UserStorageHandler;
21use wcf\system\WCF;
22
23/**
24 * Executes conversation-related actions.
25 *
26 * @author Marcel Werk
c7b2df3b 27 * @copyright 2001-2014 WoltLab GmbH
9544b6b4
MW
28 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
29 * @package com.woltlab.wcf.conversation
30 * @subpackage data.conversation
61f754e0 31 * @category Community Framework
9544b6b4 32 */
d8963ec2 33class ConversationAction extends AbstractDatabaseObjectAction implements IClipboardAction, IVisitableObjectAction {
9544b6b4 34 /**
2270f7e9 35 * @see \wcf\data\AbstractDatabaseObjectAction::$className
9544b6b4
MW
36 */
37 protected $className = 'wcf\data\conversation\ConversationEditor';
38
65b37bf6
AE
39 /**
40 * conversation object
2270f7e9 41 * @var \wcf\data\conversation\ConversationEditor
65b37bf6
AE
42 */
43 protected $conversation = null;
44
ecc8c621
AE
45 /**
46 * list of conversation data modifications
47 * @var array<array>
48 */
49 protected $conversationData = array();
50
9544b6b4 51 /**
2270f7e9 52 * @see \wcf\data\AbstractDatabaseObjectAction::create()
9544b6b4
MW
53 */
54 public function create() {
55 // create conversation
56 $data = $this->parameters['data'];
57 $data['lastPosterID'] = $data['userID'];
58 $data['lastPoster'] = $data['username'];
59 $data['lastPostTime'] = $data['time'];
60 // count participants
61 if (!empty($this->parameters['participants'])) {
62 $data['participants'] = count($this->parameters['participants']);
63 }
64 // count attachments
65 if (isset($this->parameters['attachmentHandler']) && $this->parameters['attachmentHandler'] !== null) {
66 $data['attachments'] = count($this->parameters['attachmentHandler']);
67 }
68 $conversation = call_user_func(array($this->className, 'create'), $data);
f34884b9 69 $conversationEditor = new ConversationEditor($conversation);
9544b6b4 70
f34884b9 71 if (!$conversation->isDraft) {
11cde7d7 72 // save participants
f34884b9
MW
73 $conversationEditor->updateParticipants((!empty($this->parameters['participants']) ? $this->parameters['participants'] : array()), (!empty($this->parameters['invisibleParticipants']) ? $this->parameters['invisibleParticipants'] : array()));
74
75 // add author
76 $conversationEditor->updateParticipants(array($data['userID']));
3bbcb27d
MW
77
78 // update conversation count
462d532a 79 UserStorageHandler::getInstance()->reset($conversation->getParticipantIDs(), 'conversationCount');
db864366 80
1920cd3d
MW
81 // mark conversation as read for the author
82 $sql = "UPDATE wcf".WCF_N."_conversation_to_user
83 SET lastVisitTime = ?
84 WHERE participantID = ?
85 AND conversationID = ?";
86 $statement = WCF::getDB()->prepareStatement($sql);
87 $statement->execute(array($data['time'], $data['userID'], $conversation->conversationID));
3bbcb27d
MW
88 }
89 else {
90 // update conversation count
462d532a 91 UserStorageHandler::getInstance()->reset(array($data['userID']), 'conversationCount');
9544b6b4 92 }
9544b6b4
MW
93
94 // update participant summary
9544b6b4
MW
95 $conversationEditor->updateParticipantSummary();
96
97 // create message
1920cd3d
MW
98 $messageData = $this->parameters['messageData'];
99 $messageData['conversationID'] = $conversation->conversationID;
100 $messageData['time'] = $this->parameters['data']['time'];
101 $messageData['userID'] = $this->parameters['data']['userID'];
102 $messageData['username'] = $this->parameters['data']['username'];
9544b6b4
MW
103
104 $messageAction = new ConversationMessageAction(array(), 'create', array(
1920cd3d 105 'data' => $messageData,
9544b6b4
MW
106 'conversation' => $conversation,
107 'isFirstPost' => true,
108 'attachmentHandler' => (isset($this->parameters['attachmentHandler']) ? $this->parameters['attachmentHandler'] : null)
109 ));
110 $resultValues = $messageAction->executeAction();
111
ba1ab5e5 112 // update first message id
f34884b9
MW
113 $conversationEditor->update(array(
114 'firstMessageID' => $resultValues['returnValues']->messageID
115 ));
116
11cde7d7
MW
117 $conversation->setFirstMessage($resultValues['returnValues']);
118 if (!$conversation->isDraft) {
119 // fire notification event
120 $notificationRecipients = array_merge((!empty($this->parameters['participants']) ? $this->parameters['participants'] : array()), (!empty($this->parameters['invisibleParticipants']) ? $this->parameters['invisibleParticipants'] : array()));
121 UserNotificationHandler::getInstance()->fireEvent('conversation', 'com.woltlab.wcf.conversation.notification', new ConversationUserNotificationObject($conversation), $notificationRecipients);
122 }
123
9544b6b4
MW
124 return $conversation;
125 }
126
5d7f0df0 127 /**
2270f7e9 128 * @see \wcf\data\IDeleteAction::delete()
5d7f0df0
MW
129 */
130 public function delete() {
131 // deletes messages
132 $messageList = new ConversationMessageList();
f664c19f 133 $messageList->getConditionBuilder()->add('conversation_message.conversationID IN (?)', array($this->objectIDs));
5d7f0df0
MW
134 $messageList->readObjectIDs();
135 $action = new ConversationMessageAction($messageList->getObjectIDs(), 'delete');
136 $action->executeAction();
137
138 // delete conversations
139 parent::delete();
d0cc88e1 140
5d7f0df0
MW
141 if (!empty($this->objectIDs)) {
142 // delete notifications
143 UserNotificationHandler::getInstance()->deleteNotifications('conversation', 'com.woltlab.wcf.conversation.notification', array(), $this->objectIDs);
3b37e024
AE
144
145 // remove modification logs
d0cc88e1 146 ConversationModificationLogHandler::getInstance()->remove($this->objectIDs);
5d7f0df0
MW
147 }
148 }
149
f34884b9 150 /**
2270f7e9 151 * @see \wcf\data\AbstractDatabaseObjectAction::update()
f34884b9
MW
152 */
153 public function update() {
f73a10ed
AE
154 if (!isset($this->parameters['participants'])) $this->parameters['participants'] = array();
155 if (!isset($this->parameters['invisibleParticipants'])) $this->parameters['invisibleParticipants'] = array();
156
f34884b9
MW
157 // count participants
158 if (!empty($this->parameters['participants'])) {
159 $this->parameters['data']['participants'] = count($this->parameters['participants']);
160 }
161
162 parent::update();
163
164 foreach ($this->objects as $conversation) {
165 // partipants
166 if (!empty($this->parameters['participants']) || !empty($this->parameters['invisibleParticipants'])) {
f73a10ed
AE
167 // get current participants
168 $participantIDs = $conversation->getParticipantIDs();
169
f34884b9
MW
170 $conversation->updateParticipants((!empty($this->parameters['participants']) ? $this->parameters['participants'] : array()), (!empty($this->parameters['invisibleParticipants']) ? $this->parameters['invisibleParticipants'] : array()));
171 $conversation->updateParticipantSummary();
f73a10ed
AE
172
173 // check if new participants have been added
1773c844
MW
174 $newParticipantIDs = array_diff(array_merge($this->parameters['participants'], $this->parameters['invisibleParticipants']), $participantIDs);
175 if (!empty($newParticipantIDs)) {
f73a10ed 176 // update conversation count
1846a5f4 177 UserStorageHandler::getInstance()->reset($newParticipantIDs, 'unreadConversationCount');
1773c844 178 UserStorageHandler::getInstance()->reset($newParticipantIDs, 'conversationCount');
f73a10ed
AE
179
180 // fire notification event
1773c844 181 UserNotificationHandler::getInstance()->fireEvent('conversation', 'com.woltlab.wcf.conversation.notification', new ConversationUserNotificationObject($conversation->getDecoratedObject()), $newParticipantIDs);
f73a10ed 182 }
f34884b9
MW
183 }
184
185 // draft status
186 if (isset($this->parameters['data']['isDraft'])) {
187 if ($conversation->isDraft && !$this->parameters['data']['isDraft']) {
188 // add author
189 $conversation->updateParticipants(array($conversation->userID));
3bbcb27d
MW
190
191 // update conversation count
1846a5f4 192 UserStorageHandler::getInstance()->reset($conversation->getParticipantIDs(), 'unreadConversationCount');
462d532a 193 UserStorageHandler::getInstance()->reset($conversation->getParticipantIDs(), 'conversationCount');
f34884b9
MW
194 }
195 }
196 }
197 }
198
9544b6b4 199 /**
2270f7e9 200 * @see \wcf\data\IVisitableObjectAction::markAsRead()
9544b6b4
MW
201 */
202 public function markAsRead() {
203 if (empty($this->parameters['visitTime'])) {
204 $this->parameters['visitTime'] = TIME_NOW;
205 }
206
ac18582b 207 if (empty($this->objects)) {
9544b6b4
MW
208 $this->readObjects();
209 }
210
d0051596 211 $conversationIDs = array();
9544b6b4
MW
212 $sql = "UPDATE wcf".WCF_N."_conversation_to_user
213 SET lastVisitTime = ?
214 WHERE participantID = ?
215 AND conversationID = ?";
216 $statement = WCF::getDB()->prepareStatement($sql);
c2b184f9 217 WCF::getDB()->beginTransaction();
9544b6b4 218 foreach ($this->objects as $conversation) {
c2b184f9
AE
219 $statement->execute(array(
220 $this->parameters['visitTime'],
221 WCF::getUser()->userID,
222 $conversation->conversationID
223 ));
d0051596 224 $conversationIDs[] = $conversation->conversationID;
9544b6b4 225 }
c2b184f9 226 WCF::getDB()->commitTransaction();
9544b6b4
MW
227
228 // reset storage
462d532a 229 UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'unreadConversationCount');
d0051596
MW
230
231 // delete obsolete notifications
232 if (!empty($conversationIDs)) {
233 // conversation start notification
234 $conditionBuilder = new PreparedStatementConditionBuilder();
235 $conditionBuilder->add('notification.packageID = ?', array(PackageCache::getInstance()->getPackageID('com.woltlab.wcf.conversation')));
236 $conditionBuilder->add('notification.eventID = ?', array(UserNotificationHandler::getInstance()->getEvent('com.woltlab.wcf.conversation.notification', 'conversation')->eventID));
237 $conditionBuilder->add('notification.objectID = conversation.conversationID');
238 $conditionBuilder->add('notification_to_user.notificationID = notification.notificationID');
239 $conditionBuilder->add('notification_to_user.userID = ?', array(WCF::getUser()->userID));
240 $conditionBuilder->add('conversation.conversationID IN (?)', array($conversationIDs));
241 $conditionBuilder->add('conversation.time <= ?', array($this->parameters['visitTime']));
242
243 $sql = "SELECT conversation.conversationID
244 FROM wcf".WCF_N."_conversation conversation,
245 wcf".WCF_N."_user_notification notification,
246 wcf".WCF_N."_user_notification_to_user notification_to_user
247 ".$conditionBuilder;
248 $statement = WCF::getDB()->prepareStatement($sql);
249 $statement->execute($conditionBuilder->getParameters());
250 $notificationObjectIDs = array();
251 while ($row = $statement->fetchArray()) {
252 $notificationObjectIDs[] = $row['conversationID'];
253 }
254
255 if (!empty($notificationObjectIDs)) {
256 UserNotificationHandler::getInstance()->deleteNotifications('conversation', 'com.woltlab.wcf.conversation.notification', array(WCF::getUser()->userID), $notificationObjectIDs);
257 }
258
259 // conversation reply notification
260 $conditionBuilder = new PreparedStatementConditionBuilder();
261 $conditionBuilder->add('notification.packageID = ?', array(PackageCache::getInstance()->getPackageID('com.woltlab.wcf.conversation')));
262 $conditionBuilder->add('notification.eventID = ?', array(UserNotificationHandler::getInstance()->getEvent('com.woltlab.wcf.conversation.message.notification', 'conversationMessage')->eventID));
263 $conditionBuilder->add('notification.objectID = conversation_message.messageID');
264 $conditionBuilder->add('notification_to_user.notificationID = notification.notificationID');
265 $conditionBuilder->add('notification_to_user.userID = ?', array(WCF::getUser()->userID));
266 $conditionBuilder->add('conversation_message.conversationID IN (?)', array($conversationIDs));
267 $conditionBuilder->add('conversation_message.time <= ?', array($this->parameters['visitTime']));
a480521b 268
d0051596
MW
269 $sql = "SELECT conversation_message.messageID
270 FROM wcf".WCF_N."_conversation_message conversation_message,
271 wcf".WCF_N."_user_notification notification,
272 wcf".WCF_N."_user_notification_to_user notification_to_user
273 ".$conditionBuilder;
274 $statement = WCF::getDB()->prepareStatement($sql);
275 $statement->execute($conditionBuilder->getParameters());
276 $notificationObjectIDs = array();
277 while ($row = $statement->fetchArray()) {
278 $notificationObjectIDs[] = $row['messageID'];
279 }
a480521b 280
d0051596
MW
281 if (!empty($notificationObjectIDs)) {
282 UserNotificationHandler::getInstance()->deleteNotifications('conversationMessage', 'com.woltlab.wcf.conversation.message.notification', array(WCF::getUser()->userID), $notificationObjectIDs);
283 }
284 }
9544b6b4
MW
285 }
286
287 /**
2270f7e9 288 * @see \wcf\data\IVisitableObjectAction::validateMarkAsRead()
9544b6b4
MW
289 */
290 public function validateMarkAsRead() {
c2b184f9
AE
291 // visitTime might not be in the future
292 if (isset($this->parameters['visitTime'])) {
293 $this->parameters['visitTime'] = intval($this->parameters['visitTime']);
294 if ($this->parameters['visitTime'] > TIME_NOW) {
295 $this->parameters['visitTime'] = TIME_NOW;
296 }
297 }
298
299 if (empty($this->objects)) {
300 $this->readObjects();
301 }
302
303 // check participation
304 $conversationIDs = array();
305 foreach ($this->objects as $conversation) {
306 $conversationIDs[] = $conversation->conversationID;
307 }
308
309 if (empty($conversationIDs)) {
310 throw new UserInputException('objectIDs');
311 }
312
313 if (!Conversation::isParticipant($conversationIDs)) {
314 throw new PermissionDeniedException();
315 }
9544b6b4 316 }
5e279c42 317
38cc68ad
MW
318 /**
319 * Marks all conversations as read.
320 */
321 public function markAllAsRead() {
322 $sql = "UPDATE wcf".WCF_N."_conversation_to_user
323 SET lastVisitTime = ?
324 WHERE participantID = ?";
325 $statement = WCF::getDB()->prepareStatement($sql);
326 $statement->execute(array(
327 TIME_NOW,
328 WCF::getUser()->userID
329 ));
330
331 // reset storage
332 UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'unreadConversationCount');
333
334 // delete obsolete notifications
335 UserNotificationHandler::getInstance()->deleteNotifications('conversation', 'com.woltlab.wcf.conversation.notification', array(WCF::getUser()->userID));
336 UserNotificationHandler::getInstance()->deleteNotifications('conversationMessage', 'com.woltlab.wcf.conversation.message.notification', array(WCF::getUser()->userID));
337 }
338
339 /**
340 * Validates the markAllAsRead action.
341 */
342 public function validateMarkAllAsRead() {}
343
5e279c42
AE
344 /**
345 * Validates user access for label management.
346 */
347 public function validateGetLabelmanagement() {
348 if (!WCF::getSession()->getPermission('user.conversation.canUseConversation')) {
349 throw new PermissionDeniedException();
350 }
351 }
352
353 /**
354 * Returns the conversation label management.
355 *
356 * @return array
357 */
358 public function getLabelManagement() {
359 WCF::getTPL()->assign(array(
360 'cssClassNames' => ConversationLabel::getLabelCssClassNames(),
361 'labelList' => ConversationLabel::getLabelsByUser()
362 ));
363
364 return array(
365 'actionName' => 'getLabelManagement',
366 'template' => WCF::getTPL()->fetch('conversationLabelManagement')
367 );
368 }
e2196ce0
MW
369
370 /**
371 * Validates the get message preview action.
372 */
373 public function validateGetMessagePreview() {
953b3e7b
AE
374 $this->conversation = $this->getSingleObject();
375 if (!Conversation::isParticipant(array($this->conversation->conversationID))) {
376 throw new PermissionDeniedException();
e2196ce0 377 }
e2196ce0
MW
378 }
379
380 /**
a480521b 381 * Returns a preview of a message in a specific conversation.
e2196ce0 382 *
db864366 383 * @return array
e2196ce0
MW
384 */
385 public function getMessagePreview() {
386 $messageList = new ViewableConversationMessageList();
e2196ce0 387
953b3e7b 388 $messageList->getConditionBuilder()->add("conversation_message.messageID = ?", array($this->conversation->firstMessageID));
e2196ce0
MW
389 $messageList->readObjects();
390 $messages = $messageList->getObjects();
391
392 WCF::getTPL()->assign(array(
393 'message' => reset($messages)
394 ));
395 return array(
396 'template' => WCF::getTPL()->fetch('conversationMessagePreview')
397 );
398 }
ecc8c621
AE
399
400 /**
401 * Validates parameters to close conversations.
402 */
403 public function validateClose() {
404 // read objects
405 if (empty($this->objects)) {
406 $this->readObjects();
eb6d1db7
AE
407
408 if (empty($this->objects)) {
409 throw new UserInputException('objectIDs');
410 }
ecc8c621
AE
411 }
412
413 // validate ownership
414 foreach ($this->objects as $conversation) {
415 if ($conversation->isClosed || ($conversation->userID != WCF::getUser()->userID)) {
416 throw new PermissionDeniedException();
417 }
418 }
419 }
420
421 /**
422 * Closes conversations.
423 *
424 * @return array<array>
425 */
426 public function close() {
427 foreach ($this->objects as $conversation) {
ecc8c621 428 $conversation->update(array('isClosed' => 1));
2e0bc870 429 $this->addConversationData($conversation->getDecoratedObject(), 'isClosed', 1);
953b3e7b
AE
430
431 ConversationModificationLogHandler::getInstance()->close($conversation->getDecoratedObject());
ecc8c621
AE
432 }
433
e8fe47c2
AE
434 $this->unmarkItems();
435
ecc8c621
AE
436 return $this->getConversationData();
437 }
438
439 /**
440 * Validates parameters to open conversations.
441 */
442 public function validateOpen() {
443 // read objects
444 if (empty($this->objects)) {
445 $this->readObjects();
eb6d1db7
AE
446
447 if (empty($this->objects)) {
448 throw new UserInputException('objectIDs');
449 }
ecc8c621 450 }
db864366 451
ecc8c621
AE
452 // validate ownership
453 foreach ($this->objects as $conversation) {
454 if (!$conversation->isClosed || ($conversation->userID != WCF::getUser()->userID)) {
455 throw new PermissionDeniedException();
456 }
457 }
458 }
459
460 /**
461 * Opens conversations.
ba1ab5e5 462 *
ecc8c621
AE
463 * @return array<array>
464 */
465 public function open() {
466 foreach ($this->objects as $conversation) {
ecc8c621 467 $conversation->update(array('isClosed' => 0));
2e0bc870 468 $this->addConversationData($conversation->getDecoratedObject(), 'isClosed', 0);
953b3e7b 469
518ad17c 470 ConversationModificationLogHandler::getInstance()->open($conversation->getDecoratedObject());
ecc8c621 471 }
e8fe47c2
AE
472
473 $this->unmarkItems();
db864366 474
ecc8c621
AE
475 return $this->getConversationData();
476 }
477
50cd21a1
AE
478 /**
479 * Validates conversations for leave form.
480 */
481 public function validateGetLeaveForm() {
2f5b4859
AE
482 if (empty($this->objectIDs)) {
483 throw new UserInputException('objectIDs');
50cd21a1
AE
484 }
485
486 // validate participation
2f5b4859 487 if (!Conversation::isParticipant($this->objectIDs)) {
50cd21a1
AE
488 throw new PermissionDeniedException();
489 }
490 }
491
492 /**
493 * Returns dialog form to leave conversations.
494 *
495 * @return array
496 */
497 public function getLeaveForm() {
498 // get hidden state from first conversation (all others have the same state)
499 $sql = "SELECT hideConversation
500 FROM wcf".WCF_N."_conversation_to_user
501 WHERE conversationID = ?
502 AND participantID = ?";
503 $statement = WCF::getDB()->prepareStatement($sql);
504 $statement->execute(array(
2f5b4859 505 current($this->objectIDs),
50cd21a1
AE
506 WCF::getUser()->userID
507 ));
508 $row = $statement->fetchArray();
509
510 WCF::getTPL()->assign('hideConversation', $row['hideConversation']);
511
512 return array(
513 'actionName' => 'getLeaveForm',
514 'template' => WCF::getTPL()->fetch('conversationLeave')
515 );
516 }
517
518 /**
519 * Validates parameters to hide conversations.
520 */
521 public function validateHideConversation() {
522 $this->parameters['hideConversation'] = (isset($this->parameters['hideConversation'])) ? intval($this->parameters['hideConversation']) : null;
523 if ($this->parameters['hideConversation'] === null || !in_array($this->parameters['hideConversation'], array(Conversation::STATE_DEFAULT, Conversation::STATE_HIDDEN, Conversation::STATE_LEFT))) {
524 throw new UserInputException('hideConversation');
525 }
526
2f5b4859
AE
527 if (empty($this->objectIDs)) {
528 throw new UserInputException('objectIDs');
50cd21a1
AE
529 }
530
531 // validate participation
2f5b4859 532 if (!Conversation::isParticipant($this->objectIDs)) {
50cd21a1
AE
533 throw new PermissionDeniedException();
534 }
535 }
536
537 /**
538 * Hides or restores conversations.
539 *
540 * @return array
541 */
542 public function hideConversation() {
543 $sql = "UPDATE wcf".WCF_N."_conversation_to_user
544 SET hideConversation = ?
545 WHERE conversationID = ?
546 AND participantID = ?";
547 $statement = WCF::getDB()->prepareStatement($sql);
548
549 WCF::getDB()->beginTransaction();
2f5b4859 550 foreach ($this->objectIDs as $conversationID) {
50cd21a1
AE
551 $statement->execute(array(
552 $this->parameters['hideConversation'],
553 $conversationID,
554 WCF::getUser()->userID
555 ));
556 }
557 WCF::getDB()->commitTransaction();
558
c26a69bc
MS
559 // reset user's conversation counters if user leaves conversation
560 // permanently
a056f309 561 if ($this->parameters['hideConversation'] == Conversation::STATE_LEFT) {
8bbdb36d 562 UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'conversationCount');
24011031 563 UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'unreadConversationCount');
8bbdb36d
MS
564 }
565
e829b862
MW
566 // add modification log entry
567 if ($this->parameters['hideConversation'] == Conversation::STATE_LEFT) {
568 if (empty($this->objects)) $this->readObjects();
569
570 foreach ($this->objects as $conversation) {
571 ConversationModificationLogHandler::getInstance()->leave($conversation->getDecoratedObject());
572 }
573 }
574
02d63678 575 // unmark items
e8fe47c2
AE
576 $this->unmarkItems();
577
5d7f0df0 578 if ($this->parameters['hideConversation'] == Conversation::STATE_LEFT) {
1920cd3d
MW
579 // update participant summary
580 ConversationEditor::updateParticipantSummaries($this->objectIDs);
581
582 // delete conversation if all users have left it
5d7f0df0
MW
583 $conditionBuilder = new PreparedStatementConditionBuilder();
584 $conditionBuilder->add('conversation.conversationID IN (?)', array($this->objectIDs));
585 $conditionBuilder->add('conversation_to_user.conversationID IS NULL');
586 $conversationIDs = array();
587 $sql = "SELECT DISTINCT conversation.conversationID
588 FROM wcf".WCF_N."_conversation conversation
589 LEFT JOIN wcf".WCF_N."_conversation_to_user conversation_to_user
590 ON (conversation_to_user.conversationID = conversation.conversationID AND conversation_to_user.hideConversation <> ".Conversation::STATE_LEFT.")
591 ".$conditionBuilder;
592 $statement = WCF::getDB()->prepareStatement($sql);
593 $statement->execute($conditionBuilder->getParameters());
594 while ($row = $statement->fetchArray()) {
595 $conversationIDs[] = $row['conversationID'];
596 }
597 if (!empty($conversationIDs)) {
598 $action = new ConversationAction($conversationIDs, 'delete');
599 $action->executeAction();
600 }
601 }
602
50cd21a1 603 return array(
b2e0a2ad
AE
604 'actionName' => 'hideConversation',
605 'redirectURL' => LinkHandler::getInstance()->getLink('ConversationList')
50cd21a1
AE
606 );
607 }
608
742b8736 609 /**
a480521b 610 * Validates the 'getUnreadConversations' action.
742b8736 611 */
a480521b
MS
612 public function validateGetUnreadConversations() {
613 // does nothing
614 }
742b8736
AE
615
616 /**
617 * Returns the last 5 unread conversations.
618 *
619 * @return array
620 */
621 public function getUnreadConversations() {
20dc48a1
MW
622 $conversationList = new UserConversationList(WCF::getUser()->userID);
623 $conversationList->getConditionBuilder()->add('conversation_to_user.lastVisitTime < conversation.lastPostTime');
624 $conversationList->sqlLimit = 5;
ab20ba25 625 $conversationList->sqlOrderBy = 'conversation.lastPostTime DESC';
20dc48a1 626 $conversationList->readObjects();
742b8736
AE
627
628 WCF::getTPL()->assign(array(
20dc48a1 629 'conversations' => $conversationList->getObjects()
742b8736
AE
630 ));
631
acd16520
AE
632 $totalCount = ConversationHandler::getInstance()->getUnreadConversationCount();
633 if (count($conversationList) < $totalCount) {
634 UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'unreadConversationCount');
635 }
636
742b8736 637 return array(
7f07124d 638 'template' => WCF::getTPL()->fetch('conversationListUnread'),
acd16520 639 'totalCount' => $totalCount
742b8736
AE
640 );
641 }
642
0035c8ea 643 /**
a480521b 644 * Validates the 'unmarkAll' action.
0035c8ea 645 */
a480521b
MS
646 public function validateUnmarkAll() {
647 // does nothing
648 }
0035c8ea
AE
649
650 /**
651 * Unmarks all conversations.
652 */
653 public function unmarkAll() {
654 ClipboardHandler::getInstance()->removeItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.conversation.conversation'));
655 }
656
65b37bf6
AE
657 /**
658 * Validates parameters to display the 'add participants' form.
659 */
660 public function validateGetAddParticipantsForm() {
661 $this->conversation = $this->getSingleObject();
662 if (!Conversation::isParticipant(array($this->conversation->conversationID)) || !$this->conversation->canAddParticipants()) {
663 throw new PermissionDeniedException();
664 }
665 }
666
667 /**
668 * Shows the 'add participants' form.
669 *
670 * @return array
671 */
672 public function getAddParticipantsForm() {
673 return array(
674 'actionName' => 'getAddParticipantsForm',
675 'excludeSearchValues' => $this->conversation->getParticipantNames(),
676 'template' => WCF::getTPL()->fetch('conversationAddParticipants')
677 );
678 }
679
680 /**
681 * Validates parameters to add new participants.
682 */
683 public function validateAddParticipants() {
684 $this->validateGetAddParticipantsForm();
685
686 // validate participants
687 $this->readString('participants');
688 }
689
690 /**
691 * Adds new participants.
692 *
693 * @return array
694 */
695 public function addParticipants() {
094ca1e9 696 try {
b5b88a93 697 $participantIDs = Conversation::validateParticipants($this->parameters['participants'], 'participants', $this->conversation->getParticipantIDs(true));
094ca1e9
AE
698 }
699 catch (UserInputException $e) {
700 $errorMessage = '';
701 foreach ($e->getType() as $type) {
0bca0d8c 702 if (!empty($errorMessage)) $errorMessage .= ' ';
b5b88a93 703 $errorMessage .= WCF::getLanguage()->getDynamicVariable('wcf.conversation.participants.error.'.$type['type'], array('errorData' => array('username' => $type['username'])));
094ca1e9
AE
704 }
705
706 return array(
707 'actionName' => 'addParticipants',
708 'errorMessage' => $errorMessage
709 );
710 }
711
ee3847e4
AE
712 // validate limit
713 $newCount = $this->conversation->participants + count($participantIDs);
714 if ($newCount > WCF::getSession()->getPermission('user.conversation.maxParticipants')) {
715 return array(
716 'actionName' => 'addParticipants',
3846d50e 717 'errorMessage' => WCF::getLanguage()->getDynamicVariable('wcf.conversation.participants.error.tooManyParticipants')
ee3847e4
AE
718 );
719 }
720
65b37bf6
AE
721 $count = 0;
722 $successMessage = '';
65b37bf6
AE
723 if (!empty($participantIDs)) {
724 // check for already added participants
b5b88a93
MW
725 $data = array();
726 if ($this->conversation->isDraft) {
727 $draftData = unserialize($this->conversation->draftData);
728 $draftData['participants'] = array_merge($draftData['participants'], $participantIDs);
729 $data = array('data' => array('draftData' => serialize($draftData)));
730 }
731 else {
732 $data = array('participants' => $participantIDs);
733 }
734
735 $conversationAction = new ConversationAction(array($this->conversation), 'update', $data);
736 $conversationAction->executeAction();
737
738 $count = count($participantIDs);
739 $successMessage = WCF::getLanguage()->getDynamicVariable('wcf.conversation.edit.addParticipants.success', array('count' => $count));
740
741 ConversationModificationLogHandler::getInstance()->addParticipants($this->conversation->getDecoratedObject(), $participantIDs);
742
743 if (!$this->conversation->isDraft) {
744 // update participant summary
745 $this->conversation->updateParticipantSummary();
65b37bf6
AE
746 }
747 }
748
749 return array(
750 'actionName' => 'addParticipants',
751 'count' => $count,
752 'successMessage' => $successMessage
753 );
754 }
755
a208d1f4
AE
756 /**
757 * Validates parameters to remove a participant from a conversation.
758 */
759 public function validateRemoveParticipant() {
760 $this->readInteger('userID');
761
762 // validate conversation
763 $this->conversation = $this->getSingleObject();
764 if (!$this->conversation->conversationID) {
765 throw new UserInputException('objectIDs');
766 }
767
768 // check ownership
769 if ($this->conversation->userID != WCF::getUser()->userID) {
770 throw new PermissionDeniedException();
771 }
772
773 // validate participants
774 if ($this->parameters['userID'] == WCF::getUser()->userID || !Conversation::isParticipant(array($this->conversation->conversationID)) || !Conversation::isParticipant(array($this->conversation->conversationID), $this->parameters['userID'])) {
775 throw new PermissionDeniedException();
776 }
777
778 }
779
780 /**
781 * Removes a participant from a conversation.
782 */
783 public function removeParticipant() {
784 $this->conversation->removeParticipant($this->parameters['userID']);
785 $this->conversation->updateParticipantSummary();
786
787 ConversationModificationLogHandler::getInstance()->removeParticipant($this->conversation->getDecoratedObject(), $this->parameters['userID']);
788
92e66692
AE
789 // reset storage
790 UserStorageHandler::getInstance()->reset(array($this->parameters['userID']), 'unreadConversationCount');
791
a208d1f4
AE
792 return array(
793 'userID' => $this->parameters['userID']
794 );
795 }
796
3b34d39f
MS
797 /**
798 * Rebuilds the conversation data of the relevant conversations.
799 */
800 public function rebuild() {
801 if (empty($this->objects)) {
802 $this->readObjects();
803 }
804
805 // collect number of messages for each conversation
806 $conditionBuilder = new PreparedStatementConditionBuilder();
807 $conditionBuilder->add('conversation_message.conversationID IN (?)', array($this->objectIDs));
487b839f 808 $sql = "SELECT conversationID, COUNT(messageID) AS messages, SUM(attachments) AS attachments
3b34d39f
MS
809 FROM wcf".WCF_N."_conversation_message conversation_message
810 ".$conditionBuilder."
811 GROUP BY conversationID";
812 $statement = WCF::getDB()->prepareStatement($sql);
813 $statement->execute($conditionBuilder->getParameters());
814
815 $objectIDs = array();
816 while ($row = $statement->fetchArray()) {
817 if (!$row['messages']) {
818 continue;
819 }
820 $objectIDs[] = $row['conversationID'];
821
822 $conversationEditor = new ConversationEditor(new Conversation(null, array(
823 'conversationID' => $row['conversationID']
824 )));
825 $conversationEditor->update(array(
487b839f 826 'attachments' => $row['attachments'],
3b34d39f
MS
827 'replies' => $row['messages'] - 1
828 ));
829 $conversationEditor->updateFirstMessage();
830 $conversationEditor->updateLastMessage();
831 }
832
833 // delete conversations without messages
834 $deleteConversationIDs = array_diff($this->objectIDs, $objectIDs);
835 if (!empty($deleteConversationIDs)) {
836 $conversationAction = new ConversationAction($deleteConversationIDs, 'delete');
837 $conversationAction->executeAction();
838 }
839 }
840
ecc8c621
AE
841 /**
842 * Adds conversation modification data.
843 *
2270f7e9 844 * @param \wcf\data\conversation\Conversation $conversation
ecc8c621
AE
845 * @param string $key
846 * @param mixed $value
847 */
848 protected function addConversationData(Conversation $conversation, $key, $value) {
849 if (!isset($this->conversationData[$conversation->conversationID])) {
850 $this->conversationData[$conversation->conversationID] = array();
851 }
852
853 $this->conversationData[$conversation->conversationID][$key] = $value;
854 }
855
856 /**
db864366
MS
857 * Returns conversation data.
858 *
ecc8c621
AE
859 * @return array<array>
860 */
861 protected function getConversationData() {
862 return array(
863 'conversationData' => $this->conversationData
864 );
865 }
e8fe47c2
AE
866
867 /**
868 * Unmarks conversations.
869 *
870 * @param array<integer> $conversationIDs
871 */
872 protected function unmarkItems(array $conversationIDs = array()) {
873 if (empty($conversationIDs)) {
874 $conversationIDs = $this->objectIDs;
875 }
876
877 ClipboardHandler::getInstance()->unmark($conversationIDs, ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.conversation.conversation'));
878 }
9544b6b4 879}