Apply PSR-12 code style (#145)
[GitHub/WoltLab/com.woltlab.wcf.conversation.git] / files / lib / page / ConversationPage.class.php
CommitLineData
9544b6b4 1<?php
fea86294 2
9544b6b4 3namespace wcf\page;
fea86294
TD
4
5use wcf\data\conversation\Conversation;
6use wcf\data\conversation\ConversationAction;
7use wcf\data\conversation\ConversationParticipantList;
2e0bc870 8use wcf\data\conversation\label\ConversationLabel;
9d25a4e3 9use wcf\data\conversation\label\ConversationLabelList;
9544b6b4 10use wcf\data\conversation\message\ConversationMessage;
65f1cc0b 11use wcf\data\conversation\message\ViewableConversationMessageList;
2e0bc870 12use wcf\data\conversation\ViewableConversation;
83e23d22 13use wcf\data\modification\log\ConversationLogModificationLogList;
b2de9161 14use wcf\data\smiley\SmileyCache;
195a4249 15use wcf\data\user\UserProfile;
41c23899 16use wcf\system\attachment\AttachmentHandler;
a21c8732 17use wcf\system\bbcode\BBCodeHandler;
9544b6b4
MW
18use wcf\system\exception\IllegalLinkException;
19use wcf\system\exception\PermissionDeniedException;
2d181735 20use wcf\system\message\quote\MessageQuoteManager;
e298db3c 21use wcf\system\page\PageLocationManager;
2163ec8f 22use wcf\system\page\ParentPageLocation;
9544b6b4 23use wcf\system\request\LinkHandler;
a3273089 24use wcf\system\user\signature\SignatureCache;
9544b6b4
MW
25use wcf\system\WCF;
26use wcf\util\HeaderUtil;
41c23899 27use 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
39class 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}