5 use wcf\data\conversation\Conversation
;
6 use wcf\data\conversation\ConversationAction
;
7 use wcf\data\conversation\ConversationParticipantList
;
8 use wcf\data\conversation\label\ConversationLabel
;
9 use wcf\data\conversation\label\ConversationLabelList
;
10 use wcf\data\conversation\message\ConversationMessage
;
11 use wcf\data\conversation\message\ViewableConversationMessageList
;
12 use wcf\data\conversation\ViewableConversation
;
13 use wcf\data\modification\log\ConversationLogModificationLogList
;
14 use wcf\data\smiley\SmileyCache
;
15 use wcf\data\user\UserProfile
;
16 use wcf\system\attachment\AttachmentHandler
;
17 use wcf\system\bbcode\BBCodeHandler
;
18 use wcf\system\exception\IllegalLinkException
;
19 use wcf\system\exception\PermissionDeniedException
;
20 use wcf\system\message\quote\MessageQuoteManager
;
21 use wcf\system\page\PageLocationManager
;
22 use wcf\system\page\ParentPageLocation
;
23 use wcf\system\request\LinkHandler
;
24 use wcf\system\user\signature\SignatureCache
;
26 use wcf\util\HeaderUtil
;
27 use wcf\util\StringUtil
;
30 * Shows a conversation.
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
37 * @property ViewableConversationMessageList $objectList
39 class ConversationPage
extends MultipleLinkPage
44 public $itemsPerPage = CONVERSATION_MESSAGES_PER_PAGE
;
49 public $sortOrder = 'ASC';
54 public $objectListClassName = ViewableConversationMessageList
::class;
59 public $loginRequired = true;
64 public $neededModules = ['MODULE_CONVERSATION'];
69 public $neededPermissions = ['user.conversation.canUseConversation'];
75 public $conversationID = 0;
78 * viewable conversation object
79 * @var ViewableConversation
84 * conversation label list
85 * @var ConversationLabelList
93 public $messageID = 0;
96 * conversation message object
97 * @var ConversationMessage
102 * modification log list object
103 * @var ConversationLogModificationLogList
105 public $modificationLogList;
108 * list of participants
109 * @var ConversationParticipantList
111 public $participantList;
116 public function readParameters()
118 parent
::readParameters();
120 if (isset($_REQUEST['id'])) {
121 $this->conversationID
= \
intval($_REQUEST['id']);
123 if (isset($_REQUEST['messageID'])) {
124 $this->messageID
= \
intval($_REQUEST['messageID']);
126 if ($this->messageID
) {
127 $this->message
= new ConversationMessage($this->messageID
);
128 if (!$this->message
->messageID
) {
129 throw new IllegalLinkException();
131 $this->conversationID
= $this->message
->conversationID
;
134 $this->conversation
= Conversation
::getUserConversation($this->conversationID
, WCF
::getUser()->userID
);
135 if ($this->conversation
=== null) {
136 throw new IllegalLinkException();
138 if (!$this->conversation
->canRead()) {
139 throw new PermissionDeniedException();
143 $this->labelList
= ConversationLabel
::getLabelsByUser();
144 $this->conversation
= ViewableConversation
::getViewableConversation($this->conversation
, $this->labelList
);
147 /** @noinspection PhpUndefinedFieldInspection */
148 if (WCF
::getUser()->conversationMessagesPerPage
) {
149 /** @noinspection PhpUndefinedFieldInspection */
150 $this->itemsPerPage
= WCF
::getUser()->conversationMessagesPerPage
;
153 $this->canonicalURL
= LinkHandler
::getInstance()->getLink('Conversation', [
154 'object' => $this->conversation
,
155 ], ($this->pageNo ?
'pageNo=' . $this->pageNo
: ''));
161 protected function initObjectList()
163 parent
::initObjectList();
165 $this->objectList
->getConditionBuilder()
166 ->add('conversation_message.conversationID = ?', [$this->conversation
->conversationID
]);
167 $this->objectList
->setConversation($this->conversation
->getDecoratedObject());
169 // handle visibility filter
170 if ($this->conversation
->joinedAt
> 0) {
171 $this->objectList
->getConditionBuilder()
172 ->add('conversation_message.time >= ?', [$this->conversation
->joinedAt
]);
174 if ($this->conversation
->leftAt
> 0) {
175 $this->objectList
->getConditionBuilder()
176 ->add('conversation_message.time <= ?', [$this->conversation
->leftAt
]);
180 if ($this->action
== 'lastPost') {
181 $this->goToLastPost();
183 if ($this->action
== 'firstNew') {
184 $this->goToFirstNewPost();
186 if ($this->messageID
) {
194 public function readData()
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',
204 new ParentPageLocation(
205 WCF
::getLanguage()->get('wcf.conversation.folder.draft'),
206 LinkHandler
::getInstance()->getLink('ConversationList', ['filter' => 'draft'])
210 PageLocationManager
::getInstance()->addParentLocation('com.woltlab.wcf.conversation.ConversationList');
212 // update last visit time count
214 $this->conversation
->isNew()
216 $this->objectList
->getMaxPostTime() > $this->conversation
->lastVisitTime
217 ||
($this->conversation
->joinedAt
&& !\
count($this->objectList
))
220 $visitTime = $this->objectList
->getMaxPostTime();
221 if ($visitTime == $this->conversation
->lastPostTime
) {
222 $visitTime = TIME_NOW
;
224 $conversationAction = new ConversationAction(
225 [$this->conversation
->getDecoratedObject()],
227 ['visitTime' => $visitTime]
229 $conversationAction->executeAction();
233 $this->participantList
= new ConversationParticipantList(
234 $this->conversationID
,
235 WCF
::getUser()->userID
,
236 $this->conversation
->userID
== WCF
::getUser()->userID
238 $this->participantList
->readObjects();
240 // init quote objects
242 foreach ($this->objectList
as $message) {
243 $messageIDs[] = $message->messageID
;
245 MessageQuoteManager
::getInstance()->initObjects('com.woltlab.wcf.conversation.message', $messageIDs);
248 foreach ($this->objectList
as $message) {
249 if ($message->userID
) {
250 $userIDs[] = $message->userID
;
253 $userIDs = \array_unique
($userIDs);
255 // fetch special trophies
257 if (!empty($userIDs)) {
258 UserProfile
::prepareSpecialTrophies($userIDs);
262 if (MODULE_USER_SIGNATURE
) {
263 if (!empty($userIDs)) {
264 SignatureCache
::getInstance()->cacheUserSignature($userIDs);
268 // set attachment permissions
269 if ($this->objectList
->getAttachmentList() !== null) {
270 $this->objectList
->getAttachmentList()->setPermissions([
271 'canDownload' => true,
272 'canViewPreview' => true,
276 // get timeframe for modifications
277 $this->objectList
->rewind();
278 $startTime = ($this->conversation
->joinedAt ?
: $this->objectList
->current()->time
);
279 $endTime = ($this->conversation
->leftAt ?
: TIME_NOW
);
281 $count = \
count($this->objectList
);
283 $this->objectList
->seek($count - 1);
284 if ($this->objectList
->current()->time
< $this->conversation
->lastPostTime
) {
286 FROM wcf" . WCF_N
. "_conversation_message
287 WHERE conversationID = ?
290 $statement = WCF
::getDB()->prepareStatement($sql, 1);
291 $statement->execute([$this->conversationID
, $this->objectList
->current()->time
]);
292 $endTime = $statement->fetchSingleColumn() - 1;
295 $this->objectList
->rewind();
297 // get invisible participants
298 $invisibleParticipantIDs = [];
299 if (WCF
::getUser()->userID
!= $this->conversation
->userID
) {
300 foreach ($this->participantList
as $participant) {
301 /** @noinspection PhpUndefinedFieldInspection */
302 if ($participant->isInvisible
) {
303 $invisibleParticipantIDs[] = $participant->userID
;
308 // load modification log entries
309 $this->modificationLogList
= new ConversationLogModificationLogList($this->conversation
->conversationID
);
310 $this->modificationLogList
->getConditionBuilder()
311 ->add("modification_log.time BETWEEN ? AND ?", [$startTime, $endTime]);
313 if (!empty($invisibleParticipantIDs)) {
314 $this->modificationLogList
->getConditionBuilder()->add(
315 "(modification_log.action <> ? OR modification_log.userID NOT IN (?))",
316 ['leave', $invisibleParticipantIDs]
320 $this->modificationLogList
->readObjects();
326 public function assignVariables()
328 parent
::assignVariables();
330 MessageQuoteManager
::getInstance()->assignVariables();
332 $tmpHash = StringUtil
::getRandomID();
333 $attachmentHandler = new AttachmentHandler('com.woltlab.wcf.conversation.message', 0, $tmpHash, 0);
335 WCF
::getTPL()->assign([
336 'attachmentHandler' => $attachmentHandler,
337 'attachmentObjectID' => 0,
338 'attachmentObjectType' => 'com.woltlab.wcf.conversation.message',
339 'attachmentParentObjectID' => 0,
340 'tmpHash' => $tmpHash,
341 'attachmentList' => $this->objectList
->getAttachmentList(),
342 'labelList' => $this->labelList
,
343 'modificationLogList' => $this->modificationLogList
,
344 'sortOrder' => $this->sortOrder
,
345 'conversation' => $this->conversation
,
346 'conversationID' => $this->conversationID
,
347 'participants' => $this->participantList
->getObjects(),
348 'defaultSmilies' => SmileyCache
::getInstance()->getCategorySmilies(),
351 BBCodeHandler
::getInstance()->setDisallowedBBCodes(\
explode(
353 WCF
::getSession()->getPermission('user.message.disallowedBBCodes')
358 * Calculates the position of a specific post in this conversation.
360 protected function goToPost()
362 $conditionBuilder = clone $this->objectList
->getConditionBuilder();
363 $conditionBuilder->add('time ' . ($this->sortOrder
== 'ASC' ?
'<=' : '>=') . ' ?', [$this->message
->time
]);
365 $sql = "SELECT COUNT(*) AS messages
366 FROM wcf" . WCF_N
. "_conversation_message conversation_message
367 " . $conditionBuilder;
368 $statement = WCF
::getDB()->prepareStatement($sql);
369 $statement->execute($conditionBuilder->getParameters());
370 $row = $statement->fetchArray();
371 $this->pageNo
= \
intval(\
ceil($row['messages'] / $this->itemsPerPage
));
375 * Gets the id of the last post in this conversation and forwards the user to this post.
377 protected function goToLastPost()
379 $sql = "SELECT conversation_message.messageID
380 FROM wcf" . WCF_N
. "_conversation_message conversation_message
381 " . $this->objectList
->getConditionBuilder() . "
382 ORDER BY time " . ($this->sortOrder
== 'ASC' ?
'DESC' : 'ASC');
383 $statement = WCF
::getDB()->prepareStatement($sql, 1);
384 $statement->execute($this->objectList
->getConditionBuilder()->getParameters());
385 $row = $statement->fetchArray();
386 HeaderUtil
::redirect(
387 LinkHandler
::getInstance()->getLink(
390 'encodeTitle' => true,
391 'object' => $this->conversation
,
392 'messageID' => $row['messageID'],
394 ) . '#message' . $row['messageID']
401 * Forwards the user to the first new message in this conversation.
403 protected function goToFirstNewPost()
405 $conditionBuilder = clone $this->objectList
->getConditionBuilder();
406 $conditionBuilder->add('time > ?', [$this->conversation
->lastVisitTime
]);
408 $sql = "SELECT conversation_message.messageID
409 FROM wcf" . WCF_N
. "_conversation_message conversation_message
410 " . $conditionBuilder . "
412 $statement = WCF
::getDB()->prepareStatement($sql, 1);
413 $statement->execute($conditionBuilder->getParameters());
414 $row = $statement->fetchArray();
415 if ($row !== false) {
416 HeaderUtil
::redirect(
417 LinkHandler
::getInstance()->getLink(
420 'encodeTitle' => true,
421 'object' => $this->conversation
,
422 'messageID' => $row['messageID'],
424 ) . '#message' . $row['messageID']
429 $this->goToLastPost();