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