Redirect to GET request after using the participant filter
[GitHub/WoltLab/com.woltlab.wcf.conversation.git] / files / lib / page / ConversationListPage.class.php
CommitLineData
9544b6b4
MW
1<?php
2namespace wcf\page;
5e279c42 3use wcf\data\conversation\label\ConversationLabel;
65f1cc0b 4use wcf\data\conversation\label\ConversationLabelList;
9544b6b4 5use wcf\data\conversation\UserConversationList;
18ec67a4 6use wcf\system\clipboard\ClipboardHandler;
4d951026 7use wcf\system\database\util\PreparedStatementConditionBuilder;
5e279c42 8use wcf\system\exception\IllegalLinkException;
e298db3c 9use wcf\system\page\PageLocationManager;
85876c0f 10use wcf\system\request\LinkHandler;
9544b6b4 11use wcf\system\WCF;
3d6dd2ed 12use wcf\util\ArrayUtil;
85876c0f 13use wcf\util\HeaderUtil;
9544b6b4
MW
14
15/**
16 * Shows a list of conversations.
61f754e0 17 *
9544b6b4 18 * @author Marcel Werk
4ddaa70e 19 * @copyright 2001-2019 WoltLab GmbH
9544b6b4 20 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
c032049e 21 * @package WoltLabSuite\Core\Page
7e95339f
MS
22 *
23 * @property UserConversationList $objectList
9544b6b4
MW
24 */
25class ConversationListPage extends SortablePage {
26 /**
3d6dd2ed 27 * @inheritDoc
9544b6b4
MW
28 */
29 public $defaultSortField = CONVERSATION_LIST_DEFAULT_SORT_FIELD;
30
31 /**
3d6dd2ed 32 * @inheritDoc
9544b6b4
MW
33 */
34 public $defaultSortOrder = CONVERSATION_LIST_DEFAULT_SORT_ORDER;
35
36 /**
3d6dd2ed 37 * @inheritDoc
9544b6b4 38 */
3d6dd2ed 39 public $validSortFields = ['subject', 'time', 'username', 'lastPostTime', 'replies', 'participants'];
9544b6b4
MW
40
41 /**
3d6dd2ed 42 * @inheritDoc
9544b6b4 43 */
3675ca40 44 public $itemsPerPage = CONVERSATIONS_PER_PAGE;
9544b6b4 45
84e18f05 46 /**
3d6dd2ed 47 * @inheritDoc
84e18f05
MS
48 */
49 public $loginRequired = true;
50
9544b6b4 51 /**
3d6dd2ed 52 * @inheritDoc
9544b6b4 53 */
3d6dd2ed 54 public $neededModules = ['MODULE_CONVERSATION'];
9544b6b4
MW
55
56 /**
3d6dd2ed 57 * @inheritDoc
9544b6b4 58 */
3d6dd2ed 59 public $neededPermissions = ['user.conversation.canUseConversation'];
9544b6b4
MW
60
61 /**
62 * list filter
61f754e0 63 * @var string
9544b6b4
MW
64 */
65 public $filter = '';
66
5e279c42
AE
67 /**
68 * label id
69 * @var integer
70 */
71 public $labelID = 0;
72
73 /**
74 * label list object
3d6dd2ed 75 * @var ConversationLabelList
5e279c42 76 */
09ab08af 77 public $labelList;
5e279c42 78
f5406cc8
MS
79 /**
80 * number of conversations (no filter)
81 * @var integer
82 */
83 public $conversationCount = 0;
84
85 /**
86 * number of drafts
87 * @var integer
88 */
89 public $draftCount = 0;
90
91 /**
92 * number of hidden conversations
93 * @var integer
94 */
95 public $hiddenCount = 0;
96
97 /**
98 * number of sent conversations
99 * @var integer
100 */
101 public $outboxCount = 0;
102
9544b6b4 103 /**
3d6dd2ed
MS
104 * participant that
105 * @var string[]
106 */
107 public $participants = [];
108
109 /**
110 * @inheritDoc
9544b6b4
MW
111 */
112 public function readParameters() {
113 parent::readParameters();
114
115 if (isset($_REQUEST['filter'])) $this->filter = $_REQUEST['filter'];
116 if (!in_array($this->filter, UserConversationList::$availableFilters)) $this->filter = '';
117
118 // user settings
b41c9fd6
MS
119 /** @noinspection PhpUndefinedFieldInspection */
120 if (WCF::getUser()->conversationsPerPage) {
121 /** @noinspection PhpUndefinedFieldInspection */
122 $this->itemsPerPage = WCF::getUser()->conversationsPerPage;
123 }
5e279c42
AE
124
125 // labels
126 $this->labelList = ConversationLabel::getLabelsByUser();
85876c0f 127 if (!empty($_REQUEST['labelID'])) {
5e279c42
AE
128 $this->labelID = intval($_REQUEST['labelID']);
129
130 $validLabel = false;
131 foreach ($this->labelList as $label) {
132 if ($label->labelID == $this->labelID) {
133 $validLabel = true;
134 break;
135 }
136 }
137
138 if (!$validLabel) {
139 throw new IllegalLinkException();
140 }
141 }
3d6dd2ed 142
4d951026 143 if (isset($_REQUEST['participants'])) $this->participants = array_slice(ArrayUtil::trim(explode(',', $_REQUEST['participants'])), 0, 20);
85876c0f
MW
144
145 if (!empty($_POST)) {
146 $participantsParameter = '';
147 foreach ($this->participants as $participant) {
148 if (!empty($participantsParameter)) $participantsParameter .= ',';
149 $participantsParameter .= \rawurlencode($participant);
150 }
151 if (!empty($participantsParameter)) {
152 $participantsParameter = '&participants=' . $participantsParameter;
153 }
154
155 HeaderUtil::redirect(LinkHandler::getInstance()->getLink('ConversationList', [], 'sortField='.$this->sortField.'&sortOrder='.$this->sortOrder.'&filter='.$this->filter.'&labelID='.$this->labelID.'&pageNo='.$this->pageNo.$participantsParameter));
156 exit;
157 }
9544b6b4
MW
158 }
159
be03f342 160 /** @noinspection PhpMissingParentCallCommonInspection */
9544b6b4 161 /**
3d6dd2ed 162 * @inheritDoc
9544b6b4
MW
163 */
164 protected function initObjectList() {
72f75266 165 $this->objectList = new UserConversationList(WCF::getUser()->userID, $this->filter, $this->labelID);
02b42a61 166 $this->objectList->setLabelList($this->labelList);
3d6dd2ed
MS
167
168 if (!empty($this->participants)) {
4d951026
AE
169 // The column `conversation_to_user.username` has no index, causing full table scans when
170 // trying to filter by it, therefore we'll read the user ids in advance.
171 $conditions = new PreparedStatementConditionBuilder();
172 $conditions->add('username IN (?)', [$this->participants]);
173 $sql = "SELECT userID
174 FROM wcf".WCF_N."_user
175 ".$conditions;
176 $statement = WCF::getDB()->prepareStatement($sql);
177 $statement->execute($conditions->getParameters());
178 $userIDs = [];
179 while ($userID = $statement->fetchColumn()) {
180 $userIDs[] = $userID;
181 }
182
183 if (!empty($userIDs)) {
184 // The condition is split into two branches in order to account for invisible participants.
185 // Invisible participants are only visible to the conversation starter and remain invisible
186 // until the write their first message.
187 //
188 // We need to protect these users from being exposed as participants by including them for
189 // any conversation that the current user has started. For all other conversations, users
190 // flagged with `isInvisible = 0` must be excluded.
191 //
192 // See https://github.com/WoltLab/com.woltlab.wcf.conversation/issues/131
193 $this->objectList->getConditionBuilder()->add('
90d407f3 194 (
4d951026
AE
195 (
196 conversation.userID = ?
197 AND
198 conversation.conversationID IN (
199 SELECT conversationID
200 FROM wcf'.WCF_N.'_conversation_to_user
201 WHERE participantID IN (?)
202 GROUP BY conversationID
203 HAVING COUNT(conversationID) = ?
204 )
90d407f3 205 )
4d951026
AE
206 OR
207 (
208 conversation.userID <> ?
209 AND
210 conversation.conversationID IN (
211 SELECT conversationID
212 FROM wcf'.WCF_N.'_conversation_to_user
213 WHERE participantID IN (?)
214 AND isInvisible = ?
215 GROUP BY conversationID
216 HAVING COUNT(conversationID) = ?
217 )
90d407f3 218 )
4d951026
AE
219 )', [
220 // Parameters for the first condition.
221 WCF::getUser()->userID,
222 $userIDs,
223 count($userIDs),
224
225 // Parameters for the second condition.
226 WCF::getUser()->userID,
227 $userIDs,
228 0,
229 count($userIDs),
230 ]);
231 }
3d6dd2ed 232 }
9544b6b4
MW
233 }
234
235 /**
3d6dd2ed 236 * @inheritDoc
9544b6b4
MW
237 */
238 public function readData() {
f788f251 239 // if sort field is `username`, `conversation.` has to prepended because `username`
d8838bd9 240 // alone is ambiguous
f788f251
MS
241 if ($this->sortField === 'username') {
242 $this->sortField = 'conversation.username';
243 }
244
9544b6b4
MW
245 parent::readData();
246
f788f251
MS
247 // change back to old value
248 if ($this->sortField === 'conversation.username') {
249 $this->sortField = 'username';
250 }
251
9544b6b4 252 if ($this->filter != '') {
2163ec8f
MS
253 // `-1` = pseudo object id to have to pages with identifier `com.woltlab.wcf.conversation.ConversationList`
254 PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.conversation.ConversationList', -1);
9544b6b4 255 }
f5406cc8
MS
256
257 // read stats
3d6dd2ed 258 if (!$this->labelID && empty($this->participants)) {
f5406cc8
MS
259 switch ($this->filter) {
260 case '':
261 $this->conversationCount = $this->items;
262 break;
263
264 case 'draft':
265 $this->draftCount = $this->items;
266 break;
267
268 case 'hidden':
269 $this->hiddenCount = $this->items;
270 break;
271
272 case 'outbox':
273 $this->outboxCount = $this->items;
274 break;
275 }
276 }
277
3d6dd2ed 278 if ($this->filter != '' || $this->labelID || !empty($this->participants)) {
f5406cc8
MS
279 $conversationList = new UserConversationList(WCF::getUser()->userID, '');
280 $this->conversationCount = $conversationList->countObjects();
281 }
3d6dd2ed 282 if ($this->filter != 'draft' || $this->labelID || !empty($this->participants)) {
f5406cc8
MS
283 $conversationList = new UserConversationList(WCF::getUser()->userID, 'draft');
284 $this->draftCount = $conversationList->countObjects();
285 }
3d6dd2ed 286 if ($this->filter != 'hidden' || $this->labelID || !empty($this->participants)) {
f5406cc8
MS
287 $conversationList = new UserConversationList(WCF::getUser()->userID, 'hidden');
288 $this->hiddenCount = $conversationList->countObjects();
289 }
3d6dd2ed 290 if ($this->filter != 'outbox' || $this->labelID || !empty($this->participants)) {
f5406cc8
MS
291 $conversationList = new UserConversationList(WCF::getUser()->userID, 'outbox');
292 $this->outboxCount = $conversationList->countObjects();
293 }
9544b6b4
MW
294 }
295
296 /**
3d6dd2ed 297 * @inheritDoc
9544b6b4
MW
298 */
299 public function assignVariables() {
300 parent::assignVariables();
301
3d6dd2ed 302 WCF::getTPL()->assign([
18ec67a4 303 'filter' => $this->filter,
5e279c42
AE
304 'hasMarkedItems' => ClipboardHandler::getInstance()->hasMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.conversation.conversation')),
305 'labelID' => $this->labelID,
f5406cc8
MS
306 'labelList' => $this->labelList,
307 'conversationCount' => $this->conversationCount,
308 'draftCount' => $this->draftCount,
309 'hiddenCount' => $this->hiddenCount,
3d6dd2ed 310 'outboxCount' => $this->outboxCount,
37f3e150
MW
311 'participants' => $this->participants,
312 'validSortFields' => $this->validSortFields,
3d6dd2ed 313 ]);
9544b6b4 314 }
9544b6b4 315}