Commit | Line | Data |
---|---|---|
9544b6b4 | 1 | <?php |
fea86294 | 2 | |
9544b6b4 | 3 | namespace wcf\data\conversation; |
fea86294 TD |
4 | |
5 | use wcf\data\AbstractDatabaseObjectAction; | |
5e279c42 | 6 | use wcf\data\conversation\label\ConversationLabel; |
9544b6b4 | 7 | use wcf\data\conversation\message\ConversationMessageAction; |
5d7f0df0 | 8 | use wcf\data\conversation\message\ConversationMessageList; |
a0c1a541 | 9 | use wcf\data\conversation\message\SimplifiedViewableConversationMessageList; |
232cdc4b | 10 | use wcf\data\IClipboardAction; |
ce8c322f | 11 | use wcf\data\IPopoverAction; |
d8963ec2 | 12 | use wcf\data\IVisitableObjectAction; |
265e4e9f | 13 | use wcf\data\user\group\UserGroup; |
e8fe47c2 | 14 | use wcf\system\clipboard\ClipboardHandler; |
7f07124d | 15 | use wcf\system\conversation\ConversationHandler; |
5d7f0df0 | 16 | use wcf\system\database\util\PreparedStatementConditionBuilder; |
c5a889cc | 17 | use wcf\system\event\EventHandler; |
5e279c42 | 18 | use wcf\system\exception\PermissionDeniedException; |
e8fe47c2 | 19 | use wcf\system\exception\UserInputException; |
65a160b7 | 20 | use wcf\system\log\modification\ConversationModificationLogHandler; |
b2e0a2ad | 21 | use wcf\system\request\LinkHandler; |
87de5988 | 22 | use wcf\system\search\SearchIndexManager; |
8b467fcd MW |
23 | use wcf\system\user\notification\object\ConversationUserNotificationObject; |
24 | use wcf\system\user\notification\UserNotificationHandler; | |
9544b6b4 MW |
25 | use wcf\system\user\storage\UserStorageHandler; |
26 | use wcf\system\WCF; | |
27 | ||
28 | /** | |
29 | * Executes conversation-related actions. | |
fea86294 TD |
30 | * |
31 | * @author Marcel Werk | |
32 | * @copyright 2001-2019 WoltLab GmbH | |
33 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
34 | * @package WoltLabSuite\Core\Data\Conversation | |
35 | * | |
36 | * @method ConversationEditor[] getObjects() | |
37 | * @method ConversationEditor getSingleObject() | |
9544b6b4 | 38 | */ |
fea86294 TD |
39 | class ConversationAction extends AbstractDatabaseObjectAction implements |
40 | IClipboardAction, | |
41 | IPopoverAction, | |
42 | IVisitableObjectAction | |
43 | { | |
44 | /** | |
45 | * @inheritDoc | |
46 | */ | |
47 | protected $className = ConversationEditor::class; | |
48 | ||
49 | /** | |
50 | * conversation object | |
51 | * @var ConversationEditor | |
52 | */ | |
53 | public $conversation; | |
54 | ||
55 | /** | |
56 | * list of conversation data modifications | |
57 | * @var mixed[][] | |
58 | */ | |
59 | protected $conversationData = []; | |
60 | ||
fea86294 TD |
61 | /** |
62 | * @inheritDoc | |
63 | * @return Conversation | |
64 | */ | |
65 | public function create() | |
66 | { | |
67 | // create conversation | |
68 | $data = $this->parameters['data']; | |
69 | $data['lastPosterID'] = $data['userID']; | |
70 | $data['lastPoster'] = $data['username']; | |
71 | $data['lastPostTime'] = $data['time']; | |
72 | // count participants | |
73 | if (!empty($this->parameters['participants'])) { | |
74 | $data['participants'] = \count($this->parameters['participants']); | |
75 | } | |
76 | // count attachments | |
77 | if (isset($this->parameters['attachmentHandler']) && $this->parameters['attachmentHandler'] !== null) { | |
78 | $data['attachments'] = \count($this->parameters['attachmentHandler']); | |
79 | } | |
80 | $conversation = \call_user_func([$this->className, 'create'], $data); | |
81 | $conversationEditor = new ConversationEditor($conversation); | |
82 | ||
83 | if (!$conversation->isDraft) { | |
84 | // save participants | |
85 | $conversationEditor->updateParticipants( | |
86 | (!empty($this->parameters['participants']) ? $this->parameters['participants'] : []), | |
87 | (!empty($this->parameters['invisibleParticipants']) ? $this->parameters['invisibleParticipants'] : []), | |
88 | 'all' | |
89 | ); | |
90 | ||
91 | // add author | |
92 | if ($data['userID'] !== null) { | |
93 | $conversationEditor->updateParticipants([$data['userID']], [], 'all'); | |
94 | } | |
95 | ||
96 | // update conversation count | |
97 | UserStorageHandler::getInstance()->reset($conversation->getParticipantIDs(), 'conversationCount'); | |
98 | ||
99 | // mark conversation as read for the author | |
8fbd8b01 MS |
100 | $sql = "UPDATE wcf" . WCF_N . "_conversation_to_user |
101 | SET lastVisitTime = ? | |
102 | WHERE participantID = ? | |
103 | AND conversationID = ?"; | |
fea86294 TD |
104 | $statement = WCF::getDB()->prepareStatement($sql); |
105 | $statement->execute([$data['time'], $data['userID'], $conversation->conversationID]); | |
106 | } else { | |
107 | // update conversation count | |
108 | UserStorageHandler::getInstance()->reset([$data['userID']], 'conversationCount'); | |
109 | } | |
110 | ||
111 | // update participant summary | |
112 | $conversationEditor->updateParticipantSummary(); | |
113 | ||
114 | // create message | |
115 | $messageData = $this->parameters['messageData']; | |
116 | $messageData['conversationID'] = $conversation->conversationID; | |
117 | $messageData['time'] = $this->parameters['data']['time']; | |
118 | $messageData['userID'] = $this->parameters['data']['userID']; | |
119 | $messageData['username'] = $this->parameters['data']['username']; | |
120 | ||
121 | $messageAction = new ConversationMessageAction([], 'create', [ | |
122 | 'data' => $messageData, | |
123 | 'conversation' => $conversation, | |
124 | 'isFirstPost' => true, | |
125 | 'attachmentHandler' => $this->parameters['attachmentHandler'] ?? null, | |
126 | 'htmlInputProcessor' => $this->parameters['htmlInputProcessor'] ?? null, | |
127 | ]); | |
128 | $resultValues = $messageAction->executeAction(); | |
129 | ||
130 | // update first message id | |
131 | $conversationEditor->update([ | |
132 | 'firstMessageID' => $resultValues['returnValues']->messageID, | |
133 | ]); | |
134 | ||
135 | $conversation->setFirstMessage($resultValues['returnValues']); | |
136 | if (!$conversation->isDraft) { | |
137 | // fire notification event | |
138 | $notificationRecipients = \array_merge( | |
139 | (!empty($this->parameters['participants']) ? $this->parameters['participants'] : [])(!empty($this->parameters['invisibleParticipants']) ? $this->parameters['invisibleParticipants'] : []) | |
140 | ); | |
141 | UserNotificationHandler::getInstance()->fireEvent( | |
142 | 'conversation', | |
143 | 'com.woltlab.wcf.conversation.notification', | |
144 | new ConversationUserNotificationObject($conversation), | |
145 | $notificationRecipients | |
146 | ); | |
147 | } | |
148 | ||
149 | return $conversation; | |
150 | } | |
151 | ||
152 | /** | |
153 | * @inheritDoc | |
154 | */ | |
155 | public function delete() | |
156 | { | |
157 | // deletes messages | |
158 | $messageList = new ConversationMessageList(); | |
159 | $messageList->getConditionBuilder()->add('conversation_message.conversationID IN (?)', [$this->objectIDs]); | |
160 | $messageList->readObjectIDs(); | |
161 | $action = new ConversationMessageAction($messageList->getObjectIDs(), 'delete'); | |
162 | $action->executeAction(); | |
163 | ||
164 | // get the list of participants in order to reset the 'unread conversation'-counter | |
165 | $participantIDs = []; | |
166 | if (!empty($this->objectIDs)) { | |
167 | $conditions = new PreparedStatementConditionBuilder(); | |
168 | $conditions->add("conversationID IN (?)", [$this->objectIDs]); | |
169 | $sql = "SELECT DISTINCT participantID | |
8fbd8b01 MS |
170 | FROM wcf" . WCF_N . "_conversation_to_user |
171 | " . $conditions; | |
fea86294 TD |
172 | $statement = WCF::getDB()->prepareStatement($sql); |
173 | $statement->execute($conditions->getParameters()); | |
174 | ||
175 | while ($participantID = $statement->fetchColumn()) { | |
176 | $participantIDs[] = $participantID; | |
177 | } | |
178 | } | |
179 | ||
180 | // delete conversations | |
181 | parent::delete(); | |
182 | ||
183 | if (!empty($this->objectIDs)) { | |
184 | // delete notifications | |
185 | UserNotificationHandler::getInstance() | |
186 | ->removeNotifications('com.woltlab.wcf.conversation.notification', $this->objectIDs); | |
187 | ||
188 | // remove modification logs | |
189 | ConversationModificationLogHandler::getInstance()->deleteLogs($this->objectIDs); | |
190 | ||
191 | // reset the number of unread conversations | |
192 | if (!empty($participantIDs)) { | |
193 | UserStorageHandler::getInstance()->reset($participantIDs, 'unreadConversationCount'); | |
194 | } | |
195 | } | |
196 | } | |
197 | ||
198 | /** | |
199 | * @inheritDoc | |
200 | */ | |
201 | public function update() | |
202 | { | |
203 | if (!isset($this->parameters['participants'])) { | |
204 | $this->parameters['participants'] = []; | |
205 | } | |
206 | if (!isset($this->parameters['invisibleParticipants'])) { | |
207 | $this->parameters['invisibleParticipants'] = []; | |
208 | } | |
209 | ||
210 | // count participants | |
211 | if (!empty($this->parameters['participants'])) { | |
212 | $this->parameters['data']['participants'] = \count($this->parameters['participants']); | |
213 | } | |
214 | ||
215 | parent::update(); | |
216 | ||
217 | foreach ($this->getObjects() as $conversation) { | |
218 | // participants | |
219 | if (!empty($this->parameters['participants']) || !empty($this->parameters['invisibleParticipants'])) { | |
220 | // get current participants | |
221 | $participantIDs = $conversation->getParticipantIDs(); | |
222 | ||
223 | $conversation->updateParticipants( | |
224 | (!empty($this->parameters['participants']) ? $this->parameters['participants'] : []), | |
225 | (!empty($this->parameters['invisibleParticipants']) ? $this->parameters['invisibleParticipants'] : []), | |
226 | (!empty($this->parameters['visibility']) ? $this->parameters['visibility'] : 'all') | |
227 | ); | |
228 | $conversation->updateParticipantSummary(); | |
229 | ||
230 | // check if new participants have been added | |
231 | $newParticipantIDs = \array_diff(\array_merge( | |
232 | $this->parameters['participants'], | |
233 | $this->parameters['invisibleParticipants'] | |
234 | ), $participantIDs); | |
235 | if (!empty($newParticipantIDs)) { | |
236 | // update conversation count | |
237 | UserStorageHandler::getInstance()->reset($newParticipantIDs, 'unreadConversationCount'); | |
238 | UserStorageHandler::getInstance()->reset($newParticipantIDs, 'conversationCount'); | |
239 | ||
240 | // fire notification event | |
241 | UserNotificationHandler::getInstance()->fireEvent( | |
242 | 'conversation', | |
243 | 'com.woltlab.wcf.conversation.notification', | |
244 | new ConversationUserNotificationObject($conversation->getDecoratedObject()), | |
245 | $newParticipantIDs | |
246 | ); | |
247 | } | |
248 | } | |
249 | ||
250 | // draft status | |
251 | if (isset($this->parameters['data']['isDraft'])) { | |
252 | if ($conversation->isDraft && !$this->parameters['data']['isDraft']) { | |
253 | // add author | |
254 | $conversation->updateParticipants([$conversation->userID], [], 'all'); | |
255 | ||
256 | // update conversation count | |
257 | UserStorageHandler::getInstance() | |
258 | ->reset($conversation->getParticipantIDs(), 'unreadConversationCount'); | |
259 | UserStorageHandler::getInstance() | |
260 | ->reset($conversation->getParticipantIDs(), 'conversationCount'); | |
261 | } | |
262 | } | |
263 | } | |
264 | } | |
265 | ||
266 | /** | |
267 | * @inheritDoc | |
268 | */ | |
269 | public function markAsRead() | |
270 | { | |
271 | if (empty($this->parameters['visitTime'])) { | |
272 | $this->parameters['visitTime'] = TIME_NOW; | |
273 | } | |
274 | ||
275 | // in case this is a call via PHP and the userID parameter is missing, set it to the userID of the current user | |
276 | if (!isset($this->parameters['userID'])) { | |
277 | $this->parameters['userID'] = WCF::getUser()->userID; | |
278 | } | |
279 | ||
280 | if (empty($this->objects)) { | |
281 | $this->readObjects(); | |
282 | } | |
283 | ||
284 | $conversationIDs = []; | |
8fbd8b01 MS |
285 | $sql = "UPDATE wcf" . WCF_N . "_conversation_to_user |
286 | SET lastVisitTime = ? | |
287 | WHERE participantID = ? | |
288 | AND conversationID = ?"; | |
fea86294 TD |
289 | $statement = WCF::getDB()->prepareStatement($sql); |
290 | WCF::getDB()->beginTransaction(); | |
291 | foreach ($this->getObjects() as $conversation) { | |
292 | $statement->execute([ | |
293 | $this->parameters['visitTime'], | |
294 | $this->parameters['userID'], | |
295 | $conversation->conversationID, | |
296 | ]); | |
297 | $conversationIDs[] = $conversation->conversationID; | |
298 | } | |
299 | WCF::getDB()->commitTransaction(); | |
300 | ||
301 | // reset storage | |
302 | UserStorageHandler::getInstance()->reset([$this->parameters['userID']], 'unreadConversationCount'); | |
303 | ||
304 | // mark notifications as confirmed | |
305 | if (!empty($conversationIDs)) { | |
306 | // conversation start notification | |
307 | $conditionBuilder = new PreparedStatementConditionBuilder(); | |
308 | $conditionBuilder->add('notification.eventID = ?', [ | |
309 | UserNotificationHandler::getInstance() | |
310 | ->getEvent('com.woltlab.wcf.conversation.notification', 'conversation') | |
311 | ->eventID, | |
312 | ]); | |
313 | $conditionBuilder->add('notification.objectID = conversation.conversationID'); | |
314 | $conditionBuilder->add('notification.userID = ?', [$this->parameters['userID']]); | |
315 | $conditionBuilder->add('conversation.conversationID IN (?)', [$conversationIDs]); | |
316 | $conditionBuilder->add('conversation.time <= ?', [$this->parameters['visitTime']]); | |
317 | ||
8fbd8b01 MS |
318 | $sql = "SELECT conversation.conversationID |
319 | FROM wcf" . WCF_N . "_conversation conversation, | |
320 | wcf" . WCF_N . "_user_notification notification | |
321 | " . $conditionBuilder; | |
fea86294 TD |
322 | $statement = WCF::getDB()->prepareStatement($sql); |
323 | $statement->execute($conditionBuilder->getParameters()); | |
324 | $notificationObjectIDs = $statement->fetchAll(\PDO::FETCH_COLUMN); | |
325 | ||
326 | if (!empty($notificationObjectIDs)) { | |
327 | UserNotificationHandler::getInstance()->markAsConfirmed( | |
328 | 'conversation', | |
329 | 'com.woltlab.wcf.conversation.notification', | |
330 | [$this->parameters['userID']], | |
331 | $notificationObjectIDs | |
332 | ); | |
333 | } | |
334 | ||
335 | // conversation reply notification | |
336 | $conditionBuilder = new PreparedStatementConditionBuilder(); | |
337 | $conditionBuilder->add('notification.eventID = ?', [ | |
338 | UserNotificationHandler::getInstance() | |
339 | ->getEvent('com.woltlab.wcf.conversation.message.notification', 'conversationMessage') | |
340 | ->eventID, | |
341 | ]); | |
342 | $conditionBuilder->add('notification.objectID = conversation_message.messageID'); | |
343 | $conditionBuilder->add('notification.userID = ?', [$this->parameters['userID']]); | |
344 | $conditionBuilder->add('conversation_message.conversationID IN (?)', [$conversationIDs]); | |
345 | $conditionBuilder->add('conversation_message.time <= ?', [$this->parameters['visitTime']]); | |
346 | ||
8fbd8b01 MS |
347 | $sql = "SELECT conversation_message.messageID |
348 | FROM wcf" . WCF_N . "_conversation_message conversation_message, | |
349 | wcf" . WCF_N . "_user_notification notification | |
350 | " . $conditionBuilder; | |
fea86294 TD |
351 | $statement = WCF::getDB()->prepareStatement($sql); |
352 | $statement->execute($conditionBuilder->getParameters()); | |
353 | $notificationObjectIDs = $statement->fetchAll(\PDO::FETCH_COLUMN); | |
354 | ||
355 | if (!empty($notificationObjectIDs)) { | |
356 | UserNotificationHandler::getInstance()->markAsConfirmed( | |
357 | 'conversationMessage', | |
358 | 'com.woltlab.wcf.conversation.message.notification', | |
359 | [$this->parameters['userID']], | |
360 | $notificationObjectIDs | |
361 | ); | |
362 | } | |
363 | } | |
364 | ||
365 | if (!empty($conversationIDs)) { | |
366 | $this->unmarkItems($conversationIDs); | |
367 | } | |
368 | ||
369 | $returnValues = [ | |
370 | 'totalCount' => ConversationHandler::getInstance() | |
371 | ->getUnreadConversationCount($this->parameters['userID'], true), | |
372 | ]; | |
373 | ||
374 | if (\count($conversationIDs) == 1) { | |
375 | $returnValues['markAsRead'] = \reset($conversationIDs); | |
376 | } | |
377 | ||
378 | return $returnValues; | |
379 | } | |
380 | ||
381 | /** | |
382 | * @inheritDoc | |
383 | */ | |
384 | public function validateMarkAsRead() | |
385 | { | |
386 | // visitTime might not be in the future | |
387 | if (isset($this->parameters['visitTime'])) { | |
388 | $this->parameters['visitTime'] = \intval($this->parameters['visitTime']); | |
389 | if ($this->parameters['visitTime'] > TIME_NOW) { | |
390 | $this->parameters['visitTime'] = TIME_NOW; | |
391 | } | |
392 | } | |
393 | ||
394 | // userID should always be equal to the userID of the current user when called via AJAX | |
395 | $this->parameters['userID'] = WCF::getUser()->userID; | |
396 | ||
397 | if (empty($this->objects)) { | |
398 | $this->readObjects(); | |
399 | } | |
400 | ||
401 | // check participation | |
402 | $conversationIDs = []; | |
403 | foreach ($this->getObjects() as $conversation) { | |
404 | $conversationIDs[] = $conversation->conversationID; | |
405 | } | |
406 | ||
407 | if (empty($conversationIDs)) { | |
408 | throw new UserInputException('objectIDs'); | |
409 | } | |
410 | ||
411 | if (!Conversation::isParticipant($conversationIDs)) { | |
412 | throw new PermissionDeniedException(); | |
413 | } | |
414 | } | |
415 | ||
416 | /** | |
417 | * Marks all conversations as read. | |
418 | */ | |
419 | public function markAllAsRead() | |
420 | { | |
8fbd8b01 MS |
421 | $sql = "UPDATE wcf" . WCF_N . "_conversation_to_user |
422 | SET lastVisitTime = ? | |
423 | WHERE participantID = ?"; | |
fea86294 TD |
424 | $statement = WCF::getDB()->prepareStatement($sql); |
425 | $statement->execute([ | |
426 | TIME_NOW, | |
427 | WCF::getUser()->userID, | |
428 | ]); | |
429 | ||
430 | // reset storage | |
431 | UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadConversationCount'); | |
432 | ||
433 | // confirm obsolete notifications | |
434 | UserNotificationHandler::getInstance()->markAsConfirmed( | |
435 | 'conversation', | |
436 | 'com.woltlab.wcf.conversation.notification', | |
437 | [WCF::getUser()->userID] | |
438 | ); | |
439 | UserNotificationHandler::getInstance()->markAsConfirmed( | |
440 | 'conversationMessage', | |
441 | 'com.woltlab.wcf.conversation.message.notification', | |
442 | [WCF::getUser()->userID] | |
443 | ); | |
444 | ||
445 | return [ | |
446 | 'markAllAsRead' => true, | |
447 | ]; | |
448 | } | |
449 | ||
450 | /** | |
451 | * Validates the markAllAsRead action. | |
452 | */ | |
453 | public function validateMarkAllAsRead() | |
454 | { | |
455 | // does nothing | |
456 | } | |
457 | ||
458 | /** | |
459 | * Validates user access for label management. | |
460 | * | |
461 | * @throws PermissionDeniedException | |
462 | */ | |
463 | public function validateGetLabelManagement() | |
464 | { | |
465 | if (!WCF::getSession()->getPermission('user.conversation.canUseConversation')) { | |
466 | throw new PermissionDeniedException(); | |
467 | } | |
468 | } | |
469 | ||
470 | /** | |
471 | * Returns the conversation label management. | |
472 | * | |
473 | * @return array | |
474 | */ | |
475 | public function getLabelManagement() | |
476 | { | |
477 | WCF::getTPL()->assign([ | |
478 | 'cssClassNames' => ConversationLabel::getLabelCssClassNames(), | |
479 | 'labelList' => ConversationLabel::getLabelsByUser(), | |
480 | ]); | |
481 | ||
482 | return [ | |
483 | 'actionName' => 'getLabelManagement', | |
484 | 'template' => WCF::getTPL()->fetch('conversationLabelManagement'), | |
485 | 'maxLabels' => WCF::getSession()->getPermission('user.conversation.maxLabels'), | |
486 | 'labelCount' => \count(ConversationLabel::getLabelsByUser()), | |
487 | ]; | |
488 | } | |
489 | ||
490 | /** | |
491 | * @inheritDoc | |
492 | */ | |
493 | public function validateGetPopover() | |
494 | { | |
495 | $this->conversation = $this->getSingleObject(); | |
496 | if (!Conversation::isParticipant([$this->conversation->conversationID])) { | |
497 | throw new PermissionDeniedException(); | |
498 | } | |
499 | } | |
500 | ||
501 | /** | |
502 | * @inheritDoc | |
503 | */ | |
504 | public function getPopover() | |
505 | { | |
506 | $messageList = new SimplifiedViewableConversationMessageList(); | |
507 | $messageList->getConditionBuilder() | |
508 | ->add("conversation_message.messageID = ?", [$this->conversation->firstMessageID]); | |
509 | $messageList->readObjects(); | |
510 | ||
511 | return [ | |
512 | 'template' => WCF::getTPL()->fetch('conversationMessagePreview', 'wcf', [ | |
513 | 'message' => $messageList->getSingleObject(), | |
514 | ]), | |
515 | ]; | |
516 | } | |
517 | ||
518 | /** | |
519 | * Validates the get message preview action. | |
520 | * | |
521 | * @throws PermissionDeniedException | |
522 | * @deprecated 5.3 Use `validateGetPopover()` instead. | |
523 | */ | |
524 | public function validateGetMessagePreview() | |
525 | { | |
526 | $this->validateGetPopover(); | |
527 | } | |
528 | ||
529 | /** | |
530 | * Returns a preview of a message in a specific conversation. | |
531 | * | |
532 | * @return string[] | |
533 | * @deprecated 5.3 Use `getPopover()` instead. | |
534 | */ | |
535 | public function getMessagePreview() | |
536 | { | |
537 | return $this->getPopover(); | |
538 | } | |
539 | ||
540 | /** | |
541 | * Validates parameters to close conversations. | |
542 | * | |
543 | * @throws PermissionDeniedException | |
544 | * @throws UserInputException | |
545 | */ | |
546 | public function validateClose() | |
547 | { | |
548 | // read objects | |
549 | if (empty($this->objects)) { | |
550 | $this->readObjects(); | |
551 | ||
552 | if (empty($this->objects)) { | |
553 | throw new UserInputException('objectIDs'); | |
554 | } | |
555 | } | |
556 | ||
557 | // validate ownership | |
558 | foreach ($this->getObjects() as $conversation) { | |
559 | if ($conversation->isClosed || ($conversation->userID != WCF::getUser()->userID)) { | |
560 | throw new PermissionDeniedException(); | |
561 | } | |
562 | } | |
563 | } | |
564 | ||
565 | /** | |
566 | * Closes conversations. | |
567 | * | |
568 | * @return mixed[][] | |
569 | */ | |
570 | public function close() | |
571 | { | |
572 | foreach ($this->getObjects() as $conversation) { | |
573 | $conversation->update(['isClosed' => 1]); | |
574 | $this->addConversationData($conversation->getDecoratedObject(), 'isClosed', 1); | |
575 | ||
576 | ConversationModificationLogHandler::getInstance()->close($conversation->getDecoratedObject()); | |
577 | } | |
578 | ||
579 | $this->unmarkItems(); | |
580 | ||
581 | return $this->getConversationData(); | |
582 | } | |
583 | ||
584 | /** | |
585 | * Validates parameters to open conversations. | |
586 | * | |
587 | * @throws PermissionDeniedException | |
588 | * @throws UserInputException | |
589 | */ | |
590 | public function validateOpen() | |
591 | { | |
592 | // read objects | |
593 | if (empty($this->objects)) { | |
594 | $this->readObjects(); | |
595 | ||
596 | if (empty($this->objects)) { | |
597 | throw new UserInputException('objectIDs'); | |
598 | } | |
599 | } | |
600 | ||
601 | // validate ownership | |
602 | foreach ($this->getObjects() as $conversation) { | |
603 | if (!$conversation->isClosed || ($conversation->userID != WCF::getUser()->userID)) { | |
604 | throw new PermissionDeniedException(); | |
605 | } | |
606 | } | |
607 | } | |
608 | ||
609 | /** | |
610 | * Opens conversations. | |
611 | * | |
612 | * @return mixed[][] | |
613 | */ | |
614 | public function open() | |
615 | { | |
616 | foreach ($this->getObjects() as $conversation) { | |
617 | $conversation->update(['isClosed' => 0]); | |
618 | $this->addConversationData($conversation->getDecoratedObject(), 'isClosed', 0); | |
619 | ||
620 | ConversationModificationLogHandler::getInstance()->open($conversation->getDecoratedObject()); | |
621 | } | |
622 | ||
623 | $this->unmarkItems(); | |
624 | ||
625 | return $this->getConversationData(); | |
626 | } | |
627 | ||
628 | /** | |
629 | * Validates conversations for leave form. | |
630 | * | |
631 | * @throws PermissionDeniedException | |
632 | * @throws UserInputException | |
633 | */ | |
634 | public function validateGetLeaveForm() | |
635 | { | |
636 | if (empty($this->objectIDs)) { | |
637 | throw new UserInputException('objectIDs'); | |
638 | } | |
639 | ||
640 | // validate participation | |
641 | if (!Conversation::isParticipant($this->objectIDs)) { | |
642 | throw new PermissionDeniedException(); | |
643 | } | |
644 | } | |
645 | ||
646 | /** | |
647 | * Returns dialog form to leave conversations. | |
648 | * | |
649 | * @return array | |
650 | */ | |
651 | public function getLeaveForm() | |
652 | { | |
653 | // get hidden state from first conversation (all others have the same state) | |
8fbd8b01 MS |
654 | $sql = "SELECT hideConversation |
655 | FROM wcf" . WCF_N . "_conversation_to_user | |
656 | WHERE conversationID = ? | |
657 | AND participantID = ?"; | |
fea86294 TD |
658 | $statement = WCF::getDB()->prepareStatement($sql); |
659 | $statement->execute([ | |
660 | \current($this->objectIDs), | |
661 | WCF::getUser()->userID, | |
662 | ]); | |
663 | $row = $statement->fetchArray(); | |
664 | ||
665 | WCF::getTPL()->assign('hideConversation', ($row !== false ? $row['hideConversation'] : 0)); | |
666 | ||
667 | return [ | |
668 | 'actionName' => 'getLeaveForm', | |
669 | 'template' => WCF::getTPL()->fetch('conversationLeave'), | |
670 | ]; | |
671 | } | |
672 | ||
673 | /** | |
674 | * Validates parameters to hide conversations. | |
675 | * | |
676 | * @throws PermissionDeniedException | |
677 | * @throws UserInputException | |
678 | */ | |
679 | public function validateHideConversation() | |
680 | { | |
681 | $this->parameters['hideConversation'] = isset($this->parameters['hideConversation']) ? \intval($this->parameters['hideConversation']) : null; | |
682 | if ( | |
683 | $this->parameters['hideConversation'] === null | |
684 | || !\in_array( | |
685 | $this->parameters['hideConversation'], | |
686 | [Conversation::STATE_DEFAULT, Conversation::STATE_HIDDEN, Conversation::STATE_LEFT] | |
687 | ) | |
688 | ) { | |
689 | throw new UserInputException('hideConversation'); | |
690 | } | |
691 | ||
692 | if (empty($this->objectIDs)) { | |
693 | throw new UserInputException('objectIDs'); | |
694 | } | |
695 | ||
696 | // validate participation | |
697 | if (!Conversation::isParticipant($this->objectIDs)) { | |
698 | throw new PermissionDeniedException(); | |
699 | } | |
700 | } | |
701 | ||
702 | /** | |
703 | * Hides or restores conversations. | |
704 | * | |
705 | * @return string[] | |
706 | */ | |
707 | public function hideConversation() | |
708 | { | |
8fbd8b01 MS |
709 | $sql = "UPDATE wcf" . WCF_N . "_conversation_to_user |
710 | SET hideConversation = ? | |
711 | WHERE conversationID = ? | |
712 | AND participantID = ?"; | |
fea86294 TD |
713 | $statement = WCF::getDB()->prepareStatement($sql); |
714 | ||
715 | WCF::getDB()->beginTransaction(); | |
716 | foreach ($this->objectIDs as $conversationID) { | |
717 | $statement->execute([ | |
718 | $this->parameters['hideConversation'], | |
719 | $conversationID, | |
720 | WCF::getUser()->userID, | |
721 | ]); | |
722 | } | |
723 | WCF::getDB()->commitTransaction(); | |
724 | ||
725 | // reset user's conversation counters if user leaves conversation | |
726 | // permanently | |
727 | if ($this->parameters['hideConversation'] == Conversation::STATE_LEFT) { | |
728 | UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'conversationCount'); | |
729 | UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadConversationCount'); | |
730 | } | |
731 | ||
732 | // add modification log entry | |
733 | if ($this->parameters['hideConversation'] == Conversation::STATE_LEFT) { | |
734 | if (empty($this->objects)) { | |
735 | $this->readObjects(); | |
736 | } | |
737 | ||
738 | foreach ($this->getObjects() as $conversation) { | |
739 | ConversationModificationLogHandler::getInstance()->leave($conversation->getDecoratedObject()); | |
740 | } | |
741 | } | |
742 | ||
743 | // unmark items | |
744 | $this->unmarkItems(); | |
745 | ||
746 | if ($this->parameters['hideConversation'] == Conversation::STATE_LEFT) { | |
747 | // update participants count and participant summary | |
748 | ConversationEditor::updateParticipantCounts($this->objectIDs); | |
749 | ConversationEditor::updateParticipantSummaries($this->objectIDs); | |
750 | ||
751 | // delete conversation if all users have left it | |
752 | $conditionBuilder = new PreparedStatementConditionBuilder(); | |
753 | $conditionBuilder->add('conversation.conversationID IN (?)', [$this->objectIDs]); | |
754 | $conditionBuilder->add('conversation_to_user.conversationID IS NULL'); | |
8fbd8b01 MS |
755 | $sql = "SELECT DISTINCT conversation.conversationID |
756 | FROM wcf" . WCF_N . "_conversation conversation | |
757 | LEFT JOIN wcf" . WCF_N . "_conversation_to_user conversation_to_user | |
758 | ON ( | |
759 | conversation_to_user.conversationID = conversation.conversationID | |
760 | AND conversation_to_user.hideConversation <> " . Conversation::STATE_LEFT . " | |
761 | AND conversation_to_user.participantID IS NOT NULL | |
762 | ) | |
763 | " . $conditionBuilder; | |
fea86294 TD |
764 | $statement = WCF::getDB()->prepareStatement($sql); |
765 | $statement->execute($conditionBuilder->getParameters()); | |
766 | $conversationIDs = $statement->fetchAll(\PDO::FETCH_COLUMN); | |
767 | ||
768 | if (!empty($conversationIDs)) { | |
769 | $action = new self($conversationIDs, 'delete'); | |
770 | $action->executeAction(); | |
771 | } | |
772 | } | |
773 | ||
774 | return [ | |
775 | 'actionName' => 'hideConversation', | |
776 | 'redirectURL' => LinkHandler::getInstance()->getLink('ConversationList'), | |
777 | ]; | |
778 | } | |
779 | ||
780 | /** | |
781 | * Validates parameters to return the mixed conversation list. | |
782 | */ | |
783 | public function validateGetMixedConversationList() | |
784 | { | |
785 | // does nothing | |
786 | } | |
787 | ||
788 | /** | |
789 | * Returns a mixed conversation list with up to 10 unread conversations. | |
790 | * | |
791 | * @return mixed[][] | |
792 | */ | |
793 | public function getMixedConversationList() | |
794 | { | |
8fbd8b01 MS |
795 | $sqlSelect = ' , ( |
796 | SELECT participantID | |
797 | FROM wcf' . WCF_N . '_conversation_to_user | |
798 | WHERE conversationID = conversation.conversationID | |
799 | AND participantID <> conversation.userID | |
800 | AND isInvisible = 0 | |
801 | ORDER BY username, participantID | |
802 | LIMIT 1 | |
803 | ) AS otherParticipantID | |
804 | , ( | |
805 | SELECT username | |
806 | FROM wcf' . WCF_N . '_conversation_to_user | |
807 | WHERE conversationID = conversation.conversationID | |
808 | AND participantID <> conversation.userID | |
809 | AND isInvisible = 0 | |
810 | ORDER BY username, participantID | |
811 | LIMIT 1 | |
812 | ) AS otherParticipant'; | |
fea86294 TD |
813 | |
814 | $unreadConversationList = new UserConversationList(WCF::getUser()->userID); | |
815 | $unreadConversationList->sqlSelects .= $sqlSelect; | |
816 | $unreadConversationList->getConditionBuilder()->add('conversation_to_user.lastVisitTime < lastPostTime'); | |
817 | $unreadConversationList->sqlLimit = 10; | |
818 | $unreadConversationList->sqlOrderBy = 'lastPostTime DESC'; | |
819 | $unreadConversationList->readObjects(); | |
820 | ||
821 | $conversations = []; | |
822 | $count = 0; | |
823 | foreach ($unreadConversationList as $conversation) { | |
824 | $conversations[] = $conversation; | |
825 | $count++; | |
826 | } | |
827 | ||
828 | if ($count < 10) { | |
829 | $conversationList = new UserConversationList(WCF::getUser()->userID); | |
830 | $conversationList->sqlSelects .= $sqlSelect; | |
831 | $conversationList->getConditionBuilder()->add('conversation_to_user.lastVisitTime >= lastPostTime'); | |
832 | $conversationList->sqlLimit = (10 - $count); | |
833 | $conversationList->sqlOrderBy = 'lastPostTime DESC'; | |
834 | $conversationList->readObjects(); | |
835 | ||
836 | foreach ($conversationList as $conversation) { | |
837 | $conversations[] = $conversation; | |
838 | } | |
839 | } | |
840 | ||
841 | WCF::getTPL()->assign([ | |
842 | 'conversations' => $conversations, | |
843 | ]); | |
844 | ||
845 | $totalCount = ConversationHandler::getInstance()->getUnreadConversationCount(); | |
846 | if ($count < 10 && $count < $totalCount) { | |
847 | UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'unreadConversationCount'); | |
848 | } | |
849 | ||
850 | return [ | |
851 | 'template' => WCF::getTPL()->fetch('conversationListUserPanel'), | |
852 | 'totalCount' => $totalCount, | |
853 | ]; | |
854 | } | |
855 | ||
856 | /** | |
857 | * Validates the 'unmarkAll' action. | |
858 | */ | |
859 | public function validateUnmarkAll() | |
860 | { | |
861 | // does nothing | |
862 | } | |
863 | ||
864 | /** | |
865 | * Unmarks all conversations. | |
866 | */ | |
867 | public function unmarkAll() | |
868 | { | |
869 | ClipboardHandler::getInstance()->removeItems( | |
870 | ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.conversation.conversation') | |
871 | ); | |
872 | } | |
873 | ||
874 | /** | |
875 | * Validates parameters to display the 'add participants' form. | |
876 | * | |
877 | * @throws PermissionDeniedException | |
878 | */ | |
879 | public function validateGetAddParticipantsForm() | |
880 | { | |
881 | $this->conversation = $this->getSingleObject(); | |
882 | if ( | |
883 | !Conversation::isParticipant([$this->conversation->conversationID]) | |
884 | || !$this->conversation->canAddParticipants() | |
885 | ) { | |
886 | throw new PermissionDeniedException(); | |
887 | } | |
888 | } | |
889 | ||
890 | /** | |
891 | * Shows the 'add participants' form. | |
892 | * | |
893 | * @return array | |
894 | */ | |
895 | public function getAddParticipantsForm() | |
896 | { | |
897 | $restrictUserGroupIDs = []; | |
898 | foreach (UserGroup::getAllGroups() as $group) { | |
899 | if ($group->canBeAddedAsConversationParticipant) { | |
900 | $restrictUserGroupIDs[] = $group->groupID; | |
901 | } | |
902 | } | |
903 | ||
904 | return [ | |
905 | 'excludedSearchValues' => $this->conversation->getParticipantNames(false, true), | |
906 | 'maxItems' => WCF::getSession()->getPermission('user.conversation.maxParticipants') - $this->conversation->participants, | |
907 | 'canAddGroupParticipants' => WCF::getSession()->getPermission('user.conversation.canAddGroupParticipants'), | |
908 | 'template' => WCF::getTPL()->fetch( | |
909 | 'conversationAddParticipants', | |
910 | 'wcf', | |
911 | ['conversation' => $this->conversation] | |
912 | ), | |
913 | 'restrictUserGroupIDs' => $restrictUserGroupIDs, | |
914 | ]; | |
915 | } | |
916 | ||
917 | /** | |
918 | * Validates parameters to add new participants. | |
919 | */ | |
920 | public function validateAddParticipants() | |
921 | { | |
922 | $this->validateGetAddParticipantsForm(); | |
923 | ||
924 | // validate participants | |
925 | $this->readStringArray('participants', true); | |
926 | $this->readIntegerArray('participantsGroupIDs', true); | |
927 | ||
928 | if (!$this->conversation->getDecoratedObject()->isDraft) { | |
929 | $this->readString('visibility'); | |
930 | if (!\in_array($this->parameters['visibility'], ['all', 'new'])) { | |
931 | throw new UserInputException('visibility'); | |
932 | } | |
933 | ||
934 | if ($this->parameters['visibility'] === 'all' && !$this->conversation->canAddParticipantsUnrestricted()) { | |
935 | throw new UserInputException('visibility'); | |
936 | } | |
937 | } | |
938 | } | |
939 | ||
940 | /** | |
941 | * Adds new participants. | |
942 | * | |
943 | * @return array | |
944 | */ | |
945 | public function addParticipants() | |
946 | { | |
947 | try { | |
948 | $existingParticipants = $this->conversation->getParticipantIDs(true); | |
949 | $participantIDs = Conversation::validateParticipants( | |
950 | $this->parameters['participants'], | |
951 | 'participants', | |
952 | $existingParticipants | |
953 | ); | |
954 | if ( | |
955 | !empty($this->parameters['participantsGroupIDs']) | |
956 | && WCF::getSession()->getPermission('user.conversation.canAddGroupParticipants') | |
957 | ) { | |
958 | $validGroupParticipants = Conversation::validateGroupParticipants( | |
959 | $this->parameters['participantsGroupIDs'], | |
960 | 'participants', | |
961 | $existingParticipants | |
962 | ); | |
963 | $validGroupParticipants = \array_diff($validGroupParticipants, $participantIDs); | |
964 | if (empty($validGroupParticipants)) { | |
965 | throw new UserInputException('participants', 'emptyGroup'); | |
966 | } | |
967 | $participantIDs = \array_merge($participantIDs, $validGroupParticipants); | |
968 | } | |
969 | ||
970 | $parameters = [ | |
971 | 'participantIDs' => $participantIDs, | |
972 | ]; | |
973 | EventHandler::getInstance()->fireAction($this, 'addParticipants_validateParticipants', $parameters); | |
974 | $participantIDs = $parameters['participantIDs']; | |
975 | } catch (UserInputException $e) { | |
976 | $errorMessage = ''; | |
977 | $errors = \is_array($e->getType()) ? $e->getType() : [['type' => $e->getType()]]; | |
978 | foreach ($errors as $type) { | |
979 | if (!empty($errorMessage)) { | |
980 | $errorMessage .= ' '; | |
981 | } | |
982 | $errorMessage .= WCF::getLanguage()->getDynamicVariable( | |
983 | 'wcf.conversation.participants.error.' . $type['type'], | |
984 | ['errorData' => $type] | |
985 | ); | |
986 | } | |
987 | ||
988 | return [ | |
989 | 'actionName' => 'addParticipants', | |
990 | 'errorMessage' => $errorMessage, | |
991 | ]; | |
992 | } | |
993 | ||
994 | // validate limit | |
995 | $newCount = $this->conversation->participants + \count($participantIDs); | |
996 | if ($newCount > WCF::getSession()->getPermission('user.conversation.maxParticipants')) { | |
997 | return [ | |
998 | 'actionName' => 'addParticipants', | |
999 | 'errorMessage' => WCF::getLanguage()->getDynamicVariable('wcf.conversation.participants.error.tooManyParticipants'), | |
1000 | ]; | |
1001 | } | |
1002 | ||
1003 | $count = 0; | |
1004 | $successMessage = ''; | |
1005 | if (!empty($participantIDs)) { | |
1006 | // check for already added participants | |
1007 | if ($this->conversation->isDraft) { | |
1008 | $draftData = \unserialize($this->conversation->draftData); | |
1009 | $draftData['participants'] = \array_merge($draftData['participants'], $participantIDs); | |
1010 | $data = ['data' => ['draftData' => \serialize($draftData)]]; | |
1011 | } else { | |
1012 | $data = [ | |
1013 | 'participants' => $participantIDs, | |
1014 | 'visibility' => (isset($this->parameters['visibility'])) ? $this->parameters['visibility'] : 'all', | |
1015 | ]; | |
1016 | } | |
1017 | ||
1018 | $conversationAction = new self([$this->conversation], 'update', $data); | |
1019 | $conversationAction->executeAction(); | |
1020 | ||
1021 | $count = \count($participantIDs); | |
1022 | $successMessage = WCF::getLanguage()->getDynamicVariable( | |
1023 | 'wcf.conversation.edit.addParticipants.success', | |
1024 | ['count' => $count] | |
1025 | ); | |
1026 | ||
1027 | ConversationModificationLogHandler::getInstance() | |
1028 | ->addParticipants($this->conversation->getDecoratedObject(), $participantIDs); | |
1029 | ||
1030 | if (!$this->conversation->isDraft) { | |
1031 | // update participant summary | |
1032 | $this->conversation->updateParticipantSummary(); | |
1033 | } | |
1034 | } | |
1035 | ||
1036 | return [ | |
1037 | 'count' => $count, | |
1038 | 'successMessage' => $successMessage, | |
1039 | ]; | |
1040 | } | |
1041 | ||
1042 | /** | |
1043 | * Validates parameters to remove a participant from a conversation. | |
1044 | * | |
1045 | * @throws PermissionDeniedException | |
1046 | * @throws UserInputException | |
1047 | */ | |
1048 | public function validateRemoveParticipant() | |
1049 | { | |
1050 | $this->readInteger('userID'); | |
1051 | ||
1052 | // validate conversation | |
1053 | $this->conversation = $this->getSingleObject(); | |
1054 | if (!$this->conversation->conversationID) { | |
1055 | throw new UserInputException('objectIDs'); | |
1056 | } | |
1057 | ||
1058 | // check ownership | |
1059 | if ($this->conversation->userID != WCF::getUser()->userID) { | |
1060 | throw new PermissionDeniedException(); | |
1061 | } | |
1062 | ||
1063 | // validate participants | |
1064 | if ( | |
1065 | $this->parameters['userID'] == WCF::getUser()->userID | |
1066 | || !Conversation::isParticipant([$this->conversation->conversationID]) | |
1067 | || !Conversation::isParticipant([$this->conversation->conversationID], $this->parameters['userID']) | |
1068 | ) { | |
1069 | throw new PermissionDeniedException(); | |
1070 | } | |
1071 | } | |
1072 | ||
1073 | /** | |
1074 | * Removes a participant from a conversation. | |
1075 | */ | |
1076 | public function removeParticipant() | |
1077 | { | |
1078 | $this->conversation->removeParticipant($this->parameters['userID']); | |
1079 | $this->conversation->updateParticipantSummary(); | |
1080 | ||
1081 | ConversationModificationLogHandler::getInstance() | |
1082 | ->removeParticipant($this->conversation->getDecoratedObject(), $this->parameters['userID']); | |
1083 | ||
1084 | // reset storage | |
1085 | UserStorageHandler::getInstance()->reset([$this->parameters['userID']], 'unreadConversationCount'); | |
1086 | ||
1087 | return [ | |
1088 | 'userID' => $this->parameters['userID'], | |
1089 | ]; | |
1090 | } | |
1091 | ||
1092 | /** | |
1093 | * Rebuilds the conversation data of the relevant conversations. | |
1094 | */ | |
1095 | public function rebuild() | |
1096 | { | |
1097 | if (empty($this->objects)) { | |
1098 | $this->readObjects(); | |
1099 | } | |
1100 | ||
1101 | // collect number of messages for each conversation | |
1102 | $conditionBuilder = new PreparedStatementConditionBuilder(); | |
1103 | $conditionBuilder->add('conversation_message.conversationID IN (?)', [$this->objectIDs]); | |
8fbd8b01 MS |
1104 | $sql = "SELECT conversationID, COUNT(messageID) AS messages, SUM(attachments) AS attachments |
1105 | FROM wcf" . WCF_N . "_conversation_message conversation_message | |
1106 | " . $conditionBuilder . " | |
1107 | GROUP BY conversationID"; | |
fea86294 TD |
1108 | $statement = WCF::getDB()->prepareStatement($sql); |
1109 | $statement->execute($conditionBuilder->getParameters()); | |
1110 | ||
1111 | $objectIDs = []; | |
1112 | while ($row = $statement->fetchArray()) { | |
1113 | if (!$row['messages']) { | |
1114 | continue; | |
1115 | } | |
1116 | $objectIDs[] = $row['conversationID']; | |
1117 | ||
1118 | $conversationEditor = new ConversationEditor(new Conversation(null, [ | |
1119 | 'conversationID' => $row['conversationID'], | |
1120 | ])); | |
1121 | $conversationEditor->update([ | |
1122 | 'attachments' => $row['attachments'], | |
1123 | 'replies' => $row['messages'] - 1, | |
1124 | ]); | |
1125 | $conversationEditor->updateFirstMessage(); | |
1126 | $conversationEditor->updateLastMessage(); | |
1127 | } | |
1128 | ||
1129 | // delete conversations without messages | |
1130 | $deleteConversationIDs = \array_diff($this->objectIDs, $objectIDs); | |
1131 | if (!empty($deleteConversationIDs)) { | |
1132 | $conversationAction = new self($deleteConversationIDs, 'delete'); | |
1133 | $conversationAction->executeAction(); | |
1134 | } | |
1135 | } | |
1136 | ||
1137 | /** | |
1138 | * Validates the parameters to edit a conversation's subject. | |
1139 | * | |
1140 | * @throws PermissionDeniedException | |
1141 | */ | |
1142 | public function validateEditSubject() | |
1143 | { | |
1144 | $this->readString('subject'); | |
1145 | ||
1146 | $this->conversation = $this->getSingleObject(); | |
1147 | if ($this->conversation->userID != WCF::getUser()->userID) { | |
1148 | throw new PermissionDeniedException(); | |
1149 | } | |
1150 | } | |
1151 | ||
1152 | /** | |
1153 | * Edits a conversation's subject. | |
1154 | * | |
1155 | * @return string[] | |
1156 | */ | |
1157 | public function editSubject() | |
1158 | { | |
1159 | $subject = \mb_substr($this->parameters['subject'], 0, 255); | |
1160 | ||
1161 | $this->conversation->update([ | |
1162 | 'subject' => $subject, | |
1163 | ]); | |
1164 | ||
1165 | $message = $this->conversation->getFirstMessage(); | |
1166 | ||
1167 | SearchIndexManager::getInstance()->set( | |
1168 | 'com.woltlab.wcf.conversation.message', | |
1169 | $message->messageID, | |
1170 | $message->message, | |
1171 | $subject, | |
1172 | $message->time, | |
1173 | $message->userID, | |
1174 | $message->username | |
1175 | ); | |
1176 | ||
1177 | return [ | |
1178 | 'subject' => $subject, | |
1179 | ]; | |
1180 | } | |
1181 | ||
1182 | /** | |
1183 | * Adds conversation modification data. | |
1184 | * | |
1185 | * @param Conversation $conversation | |
1186 | * @param string $key | |
1187 | * @param mixed $value | |
1188 | */ | |
1189 | protected function addConversationData(Conversation $conversation, $key, $value) | |
1190 | { | |
1191 | if (!isset($this->conversationData[$conversation->conversationID])) { | |
1192 | $this->conversationData[$conversation->conversationID] = []; | |
1193 | } | |
1194 | ||
1195 | $this->conversationData[$conversation->conversationID][$key] = $value; | |
1196 | } | |
1197 | ||
1198 | /** | |
1199 | * Returns conversation data. | |
1200 | * | |
1201 | * @return mixed[][] | |
1202 | */ | |
1203 | protected function getConversationData() | |
1204 | { | |
1205 | return [ | |
1206 | 'conversationData' => $this->conversationData, | |
1207 | ]; | |
1208 | } | |
1209 | ||
1210 | /** | |
1211 | * Unmarks conversations. | |
1212 | * | |
1213 | * @param integer[] $conversationIDs | |
1214 | */ | |
1215 | protected function unmarkItems(array $conversationIDs = []) | |
1216 | { | |
1217 | if (empty($conversationIDs)) { | |
1218 | $conversationIDs = $this->objectIDs; | |
1219 | } | |
1220 | ||
1221 | ClipboardHandler::getInstance()->unmark( | |
1222 | $conversationIDs, | |
1223 | ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.conversation.conversation') | |
1224 | ); | |
1225 | } | |
9544b6b4 | 1226 | } |