Commit | Line | Data |
---|---|---|
9544b6b4 MW |
1 | <?php |
2 | namespace wcf\data\conversation; | |
5e279c42 | 3 | use wcf\data\conversation\label\ConversationLabel; |
9544b6b4 | 4 | use wcf\data\conversation\message\ConversationMessageAction; |
5d7f0df0 | 5 | use wcf\data\conversation\message\ConversationMessageList; |
e2196ce0 | 6 | use wcf\data\conversation\message\ViewableConversationMessageList; |
d0051596 | 7 | use wcf\data\package\PackageCache; |
b475685e | 8 | use wcf\data\AbstractDatabaseObjectAction; |
232cdc4b | 9 | use wcf\data\IClipboardAction; |
d8963ec2 | 10 | use wcf\data\IVisitableObjectAction; |
e8fe47c2 | 11 | use wcf\system\clipboard\ClipboardHandler; |
7f07124d | 12 | use wcf\system\conversation\ConversationHandler; |
5d7f0df0 | 13 | use wcf\system\database\util\PreparedStatementConditionBuilder; |
5e279c42 | 14 | use wcf\system\exception\PermissionDeniedException; |
e8fe47c2 | 15 | use wcf\system\exception\UserInputException; |
65a160b7 | 16 | use wcf\system\log\modification\ConversationModificationLogHandler; |
b2e0a2ad | 17 | use wcf\system\request\LinkHandler; |
8b467fcd MW |
18 | use wcf\system\user\notification\object\ConversationUserNotificationObject; |
19 | use wcf\system\user\notification\UserNotificationHandler; | |
9544b6b4 MW |
20 | use wcf\system\user\storage\UserStorageHandler; |
21 | use 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 | 33 | class 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 | } |