Commit | Line | Data |
---|---|---|
9544b6b4 | 1 | <?php |
fea86294 | 2 | |
9544b6b4 | 3 | namespace wcf\page; |
fea86294 TD |
4 | |
5 | use wcf\data\conversation\Conversation; | |
6 | use wcf\data\conversation\ConversationAction; | |
7 | use wcf\data\conversation\ConversationParticipantList; | |
2e0bc870 | 8 | use wcf\data\conversation\label\ConversationLabel; |
9d25a4e3 | 9 | use wcf\data\conversation\label\ConversationLabelList; |
9544b6b4 | 10 | use wcf\data\conversation\message\ConversationMessage; |
65f1cc0b | 11 | use wcf\data\conversation\message\ViewableConversationMessageList; |
2e0bc870 | 12 | use wcf\data\conversation\ViewableConversation; |
83e23d22 | 13 | use wcf\data\modification\log\ConversationLogModificationLogList; |
b2de9161 | 14 | use wcf\data\smiley\SmileyCache; |
195a4249 | 15 | use wcf\data\user\UserProfile; |
41c23899 | 16 | use wcf\system\attachment\AttachmentHandler; |
a21c8732 | 17 | use wcf\system\bbcode\BBCodeHandler; |
9544b6b4 MW |
18 | use wcf\system\exception\IllegalLinkException; |
19 | use wcf\system\exception\PermissionDeniedException; | |
2d181735 | 20 | use wcf\system\message\quote\MessageQuoteManager; |
e298db3c | 21 | use wcf\system\page\PageLocationManager; |
2163ec8f | 22 | use wcf\system\page\ParentPageLocation; |
9544b6b4 | 23 | use wcf\system\request\LinkHandler; |
a3273089 | 24 | use wcf\system\user\signature\SignatureCache; |
9544b6b4 MW |
25 | use wcf\system\WCF; |
26 | use wcf\util\HeaderUtil; | |
41c23899 | 27 | use wcf\util\StringUtil; |
9544b6b4 MW |
28 | |
29 | /** | |
30 | * Shows a conversation. | |
fea86294 TD |
31 | * |
32 | * @author Marcel Werk | |
33 | * @copyright 2001-2019 WoltLab GmbH | |
34 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
35 | * @package WoltLabSuite\Core\Page | |
36 | * | |
37 | * @property ViewableConversationMessageList $objectList | |
9544b6b4 | 38 | */ |
fea86294 TD |
39 | class ConversationPage extends MultipleLinkPage |
40 | { | |
41 | /** | |
42 | * @inheritDoc | |
43 | */ | |
44 | public $itemsPerPage = CONVERSATION_MESSAGES_PER_PAGE; | |
45 | ||
46 | /** | |
47 | * @inheritDoc | |
48 | */ | |
49 | public $sortOrder = 'ASC'; | |
50 | ||
51 | /** | |
52 | * @inheritDoc | |
53 | */ | |
54 | public $objectListClassName = ViewableConversationMessageList::class; | |
55 | ||
56 | /** | |
57 | * @inheritDoc | |
58 | */ | |
59 | public $loginRequired = true; | |
60 | ||
61 | /** | |
62 | * @inheritDoc | |
63 | */ | |
64 | public $neededModules = ['MODULE_CONVERSATION']; | |
65 | ||
66 | /** | |
67 | * @inheritDoc | |
68 | */ | |
69 | public $neededPermissions = ['user.conversation.canUseConversation']; | |
70 | ||
71 | /** | |
72 | * conversation id | |
73 | * @var integer | |
74 | */ | |
75 | public $conversationID = 0; | |
76 | ||
77 | /** | |
78 | * viewable conversation object | |
79 | * @var ViewableConversation | |
80 | */ | |
81 | public $conversation; | |
82 | ||
83 | /** | |
84 | * conversation label list | |
85 | * @var ConversationLabelList | |
86 | */ | |
87 | public $labelList; | |
88 | ||
89 | /** | |
90 | * message id | |
91 | * @var integer | |
92 | */ | |
93 | public $messageID = 0; | |
94 | ||
95 | /** | |
96 | * conversation message object | |
97 | * @var ConversationMessage | |
98 | */ | |
99 | public $message; | |
100 | ||
101 | /** | |
102 | * modification log list object | |
103 | * @var ConversationLogModificationLogList | |
104 | */ | |
105 | public $modificationLogList; | |
106 | ||
107 | /** | |
108 | * list of participants | |
109 | * @var ConversationParticipantList | |
110 | */ | |
111 | public $participantList; | |
112 | ||
113 | /** | |
114 | * @inheritDoc | |
115 | */ | |
116 | public function readParameters() | |
117 | { | |
118 | parent::readParameters(); | |
119 | ||
120 | if (isset($_REQUEST['id'])) { | |
121 | $this->conversationID = \intval($_REQUEST['id']); | |
122 | } | |
123 | if (isset($_REQUEST['messageID'])) { | |
124 | $this->messageID = \intval($_REQUEST['messageID']); | |
125 | } | |
126 | if ($this->messageID) { | |
127 | $this->message = new ConversationMessage($this->messageID); | |
128 | if (!$this->message->messageID) { | |
129 | throw new IllegalLinkException(); | |
130 | } | |
131 | $this->conversationID = $this->message->conversationID; | |
132 | } | |
133 | ||
134 | $this->conversation = Conversation::getUserConversation($this->conversationID, WCF::getUser()->userID); | |
135 | if ($this->conversation === null) { | |
136 | throw new IllegalLinkException(); | |
137 | } | |
138 | if (!$this->conversation->canRead()) { | |
139 | throw new PermissionDeniedException(); | |
140 | } | |
141 | ||
142 | // load labels | |
143 | $this->labelList = ConversationLabel::getLabelsByUser(); | |
144 | $this->conversation = ViewableConversation::getViewableConversation($this->conversation, $this->labelList); | |
145 | ||
146 | // messages per page | |
147 | /** @noinspection PhpUndefinedFieldInspection */ | |
148 | if (WCF::getUser()->conversationMessagesPerPage) { | |
149 | /** @noinspection PhpUndefinedFieldInspection */ | |
150 | $this->itemsPerPage = WCF::getUser()->conversationMessagesPerPage; | |
151 | } | |
152 | ||
153 | $this->canonicalURL = LinkHandler::getInstance()->getLink('Conversation', [ | |
154 | 'object' => $this->conversation, | |
155 | ], ($this->pageNo ? 'pageNo=' . $this->pageNo : '')); | |
156 | } | |
157 | ||
158 | /** | |
159 | * @inheritDoc | |
160 | */ | |
161 | protected function initObjectList() | |
162 | { | |
163 | parent::initObjectList(); | |
164 | ||
165 | $this->objectList->getConditionBuilder() | |
166 | ->add('conversation_message.conversationID = ?', [$this->conversation->conversationID]); | |
167 | $this->objectList->setConversation($this->conversation->getDecoratedObject()); | |
168 | ||
169 | // handle visibility filter | |
170 | if ($this->conversation->joinedAt > 0) { | |
171 | $this->objectList->getConditionBuilder() | |
172 | ->add('conversation_message.time >= ?', [$this->conversation->joinedAt]); | |
173 | } | |
174 | if ($this->conversation->leftAt > 0) { | |
175 | $this->objectList->getConditionBuilder() | |
176 | ->add('conversation_message.time <= ?', [$this->conversation->leftAt]); | |
177 | } | |
178 | ||
179 | // handle jump to | |
180 | if ($this->action == 'lastPost') { | |
181 | $this->goToLastPost(); | |
182 | } | |
183 | if ($this->action == 'firstNew') { | |
184 | $this->goToFirstNewPost(); | |
185 | } | |
186 | if ($this->messageID) { | |
187 | $this->goToPost(); | |
188 | } | |
189 | } | |
190 | ||
191 | /** | |
192 | * @inheritDoc | |
193 | */ | |
194 | public function readData() | |
195 | { | |
196 | parent::readData(); | |
197 | ||
198 | // add breadcrumbs | |
199 | if ($this->conversation->isDraft) { | |
200 | // `-1` = pseudo object id to have to pages with identifier `com.woltlab.wcf.conversation.ConversationList` | |
201 | PageLocationManager::getInstance()->addParentLocation( | |
202 | 'com.woltlab.wcf.conversation.ConversationList', | |
203 | -1, | |
204 | new ParentPageLocation( | |
205 | WCF::getLanguage()->get('wcf.conversation.folder.draft'), | |
206 | LinkHandler::getInstance()->getLink('ConversationList', ['filter' => 'draft']) | |
207 | ) | |
208 | ); | |
209 | } | |
210 | PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.conversation.ConversationList'); | |
211 | ||
212 | // update last visit time count | |
213 | if ( | |
214 | $this->conversation->isNew() | |
215 | && ( | |
216 | $this->objectList->getMaxPostTime() > $this->conversation->lastVisitTime | |
217 | || ($this->conversation->joinedAt && !\count($this->objectList)) | |
218 | ) | |
219 | ) { | |
220 | $visitTime = $this->objectList->getMaxPostTime(); | |
221 | if ($visitTime == $this->conversation->lastPostTime) { | |
222 | $visitTime = TIME_NOW; | |
223 | } | |
224 | $conversationAction = new ConversationAction( | |
225 | [$this->conversation->getDecoratedObject()], | |
226 | 'markAsRead', | |
227 | ['visitTime' => $visitTime] | |
228 | ); | |
229 | $conversationAction->executeAction(); | |
230 | } | |
231 | ||
232 | // get participants | |
233 | $this->participantList = new ConversationParticipantList( | |
234 | $this->conversationID, | |
235 | WCF::getUser()->userID, | |
236 | $this->conversation->userID == WCF::getUser()->userID | |
237 | ); | |
238 | $this->participantList->readObjects(); | |
239 | ||
240 | // init quote objects | |
241 | $messageIDs = []; | |
242 | foreach ($this->objectList as $message) { | |
243 | $messageIDs[] = $message->messageID; | |
244 | } | |
245 | MessageQuoteManager::getInstance()->initObjects('com.woltlab.wcf.conversation.message', $messageIDs); | |
246 | ||
247 | $userIDs = []; | |
248 | foreach ($this->objectList as $message) { | |
249 | if ($message->userID) { | |
250 | $userIDs[] = $message->userID; | |
251 | } | |
252 | } | |
253 | ||
254 | // fetch special trophies | |
255 | if (MODULE_TROPHY) { | |
256 | if (!empty($userIDs)) { | |
257 | UserProfile::prepareSpecialTrophies(\array_unique($userIDs)); | |
258 | } | |
259 | } | |
260 | ||
261 | if (MODULE_USER_SIGNATURE) { | |
262 | if (!empty($userIDs)) { | |
263 | SignatureCache::getInstance()->cacheUserSignature($userIDs); | |
264 | } | |
265 | } | |
266 | ||
267 | // set attachment permissions | |
268 | if ($this->objectList->getAttachmentList() !== null) { | |
269 | $this->objectList->getAttachmentList()->setPermissions([ | |
270 | 'canDownload' => true, | |
271 | 'canViewPreview' => true, | |
272 | ]); | |
273 | } | |
274 | ||
275 | // get timeframe for modifications | |
276 | $this->objectList->rewind(); | |
277 | $startTime = ($this->conversation->joinedAt ?: $this->objectList->current()->time); | |
278 | $endTime = ($this->conversation->leftAt ?: TIME_NOW); | |
279 | ||
280 | $count = \count($this->objectList); | |
281 | if ($count > 1) { | |
282 | $this->objectList->seek($count - 1); | |
283 | if ($this->objectList->current()->time < $this->conversation->lastPostTime) { | |
284 | $sql = "SELECT time | |
285 | FROM wcf" . WCF_N . "_conversation_message | |
a7e5e13c MW |
286 | WHERE conversationID = ? |
287 | AND time > ? | |
288 | ORDER BY time"; | |
fea86294 TD |
289 | $statement = WCF::getDB()->prepareStatement($sql, 1); |
290 | $statement->execute([$this->conversationID, $this->objectList->current()->time]); | |
291 | $endTime = $statement->fetchSingleColumn() - 1; | |
292 | } | |
293 | } | |
294 | $this->objectList->rewind(); | |
295 | ||
296 | // get invisible participants | |
297 | $invisibleParticipantIDs = []; | |
298 | if (WCF::getUser()->userID != $this->conversation->userID) { | |
299 | foreach ($this->participantList as $participant) { | |
300 | if ($participant->isInvisible) { | |
301 | $invisibleParticipantIDs[] = $participant->userID; | |
302 | } | |
303 | } | |
304 | } | |
305 | ||
306 | // load modification log entries | |
307 | $this->modificationLogList = new ConversationLogModificationLogList($this->conversation->conversationID); | |
308 | $this->modificationLogList->getConditionBuilder() | |
309 | ->add("modification_log.time BETWEEN ? AND ?", [$startTime, $endTime]); | |
310 | ||
311 | if (!empty($invisibleParticipantIDs)) { | |
312 | $this->modificationLogList->getConditionBuilder()->add( | |
313 | "(modification_log.action <> ? OR modification_log.userID NOT IN (?))", | |
314 | ['leave', $invisibleParticipantIDs] | |
315 | ); | |
316 | } | |
317 | ||
318 | $this->modificationLogList->readObjects(); | |
319 | } | |
320 | ||
321 | /** | |
322 | * @inheritDoc | |
323 | */ | |
324 | public function assignVariables() | |
325 | { | |
326 | parent::assignVariables(); | |
327 | ||
328 | MessageQuoteManager::getInstance()->assignVariables(); | |
329 | ||
330 | $tmpHash = StringUtil::getRandomID(); | |
331 | $attachmentHandler = new AttachmentHandler('com.woltlab.wcf.conversation.message', 0, $tmpHash, 0); | |
332 | ||
333 | WCF::getTPL()->assign([ | |
334 | 'attachmentHandler' => $attachmentHandler, | |
335 | 'attachmentObjectID' => 0, | |
336 | 'attachmentObjectType' => 'com.woltlab.wcf.conversation.message', | |
337 | 'attachmentParentObjectID' => 0, | |
338 | 'tmpHash' => $tmpHash, | |
339 | 'attachmentList' => $this->objectList->getAttachmentList(), | |
340 | 'labelList' => $this->labelList, | |
341 | 'modificationLogList' => $this->modificationLogList, | |
342 | 'sortOrder' => $this->sortOrder, | |
343 | 'conversation' => $this->conversation, | |
344 | 'conversationID' => $this->conversationID, | |
345 | 'participants' => $this->participantList->getObjects(), | |
346 | 'defaultSmilies' => SmileyCache::getInstance()->getCategorySmilies(), | |
347 | ]); | |
348 | ||
349 | BBCodeHandler::getInstance()->setDisallowedBBCodes(\explode( | |
350 | ',', | |
351 | WCF::getSession()->getPermission('user.message.disallowedBBCodes') | |
352 | )); | |
353 | } | |
354 | ||
355 | /** | |
356 | * Calculates the position of a specific post in this conversation. | |
357 | */ | |
358 | protected function goToPost() | |
359 | { | |
360 | $conditionBuilder = clone $this->objectList->getConditionBuilder(); | |
361 | $conditionBuilder->add('time ' . ($this->sortOrder == 'ASC' ? '<=' : '>=') . ' ?', [$this->message->time]); | |
362 | ||
363 | $sql = "SELECT COUNT(*) AS messages | |
364 | FROM wcf" . WCF_N . "_conversation_message conversation_message | |
365 | " . $conditionBuilder; | |
366 | $statement = WCF::getDB()->prepareStatement($sql); | |
367 | $statement->execute($conditionBuilder->getParameters()); | |
368 | $row = $statement->fetchArray(); | |
369 | $this->pageNo = \intval(\ceil($row['messages'] / $this->itemsPerPage)); | |
370 | } | |
371 | ||
372 | /** | |
373 | * Gets the id of the last post in this conversation and forwards the user to this post. | |
374 | */ | |
375 | protected function goToLastPost() | |
376 | { | |
377 | $sql = "SELECT conversation_message.messageID | |
378 | FROM wcf" . WCF_N . "_conversation_message conversation_message | |
379 | " . $this->objectList->getConditionBuilder() . " | |
380 | ORDER BY time " . ($this->sortOrder == 'ASC' ? 'DESC' : 'ASC'); | |
381 | $statement = WCF::getDB()->prepareStatement($sql, 1); | |
382 | $statement->execute($this->objectList->getConditionBuilder()->getParameters()); | |
383 | $row = $statement->fetchArray(); | |
384 | HeaderUtil::redirect( | |
385 | LinkHandler::getInstance()->getLink( | |
386 | 'Conversation', | |
387 | [ | |
388 | 'encodeTitle' => true, | |
389 | 'object' => $this->conversation, | |
390 | 'messageID' => $row['messageID'], | |
391 | ] | |
392 | ) . '#message' . $row['messageID'] | |
393 | ); | |
394 | ||
395 | exit; | |
396 | } | |
397 | ||
398 | /** | |
399 | * Forwards the user to the first new message in this conversation. | |
400 | */ | |
401 | protected function goToFirstNewPost() | |
402 | { | |
403 | $conditionBuilder = clone $this->objectList->getConditionBuilder(); | |
404 | $conditionBuilder->add('time > ?', [$this->conversation->lastVisitTime]); | |
405 | ||
406 | $sql = "SELECT conversation_message.messageID | |
407 | FROM wcf" . WCF_N . "_conversation_message conversation_message | |
408 | " . $conditionBuilder . " | |
a480521b | 409 | ORDER BY time ASC"; |
fea86294 TD |
410 | $statement = WCF::getDB()->prepareStatement($sql, 1); |
411 | $statement->execute($conditionBuilder->getParameters()); | |
412 | $row = $statement->fetchArray(); | |
413 | if ($row !== false) { | |
414 | HeaderUtil::redirect( | |
415 | LinkHandler::getInstance()->getLink( | |
416 | 'Conversation', | |
417 | [ | |
418 | 'encodeTitle' => true, | |
419 | 'object' => $this->conversation, | |
420 | 'messageID' => $row['messageID'], | |
421 | ] | |
422 | ) . '#message' . $row['messageID'] | |
423 | ); | |
424 | ||
425 | exit; | |
426 | } else { | |
427 | $this->goToLastPost(); | |
428 | } | |
429 | } | |
78fd78a7 | 430 | } |