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