717c1ebf326c8bc9168cdac854a965d36ec33407
[GitHub/WoltLab/com.woltlab.wcf.conversation.git] / files / lib / data / conversation / UserConversationList.class.php
1 <?php
2 namespace wcf\data\conversation;
3 use wcf\data\conversation\label\ConversationLabel;
4 use wcf\data\conversation\label\ConversationLabelList;
5 use wcf\system\cache\runtime\UserProfileRuntimeCache;
6 use wcf\system\database\util\PreparedStatementConditionBuilder;
7 use wcf\system\WCF;
8
9 /**
10 * Represents a list of conversations.
11 *
12 * @author Marcel Werk
13 * @copyright 2001-2019 WoltLab GmbH
14 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
15 * @package WoltLabSuite\Core\Data\Conversation
16 *
17 * @method ViewableConversation current()
18 * @method ViewableConversation[] getObjects()
19 * @method ViewableConversation|null search($objectID)
20 * @property ViewableConversation[] $objects
21 */
22 class UserConversationList extends ConversationList {
23 /**
24 * list of available filters
25 * @var string[]
26 */
27 public static $availableFilters = ['hidden', 'draft', 'outbox'];
28
29 /**
30 * active filter
31 * @var string
32 */
33 public $filter = '';
34
35 /**
36 * label list object
37 * @var ConversationLabelList
38 */
39 public $labelList;
40
41 /**
42 * @inheritDoc
43 */
44 public $decoratorClassName = ViewableConversation::class;
45
46 /**
47 * Creates a new UserConversationList
48 *
49 * @param integer $userID
50 * @param string $filter
51 * @param integer $labelID
52 */
53 public function __construct($userID, $filter = '', $labelID = 0) {
54 parent::__construct();
55
56 $this->filter = $filter;
57
58 // apply filter
59 if ($this->filter === 'draft') {
60 $this->getConditionBuilder()->add('conversation.userID = ?', [$userID]);
61 $this->getConditionBuilder()->add('conversation.isDraft = 1');
62 }
63 else {
64 $this->getConditionBuilder()->add('conversation_to_user.participantID = ?', [$userID]);
65 $this->getConditionBuilder()->add('conversation_to_user.hideConversation = ?', [$this->filter == 'hidden' ? 1 : 0]);
66 $this->sqlConditionJoins = "LEFT JOIN wcf".WCF_N."_conversation conversation ON (conversation.conversationID = conversation_to_user.conversationID)";
67 if ($this->filter == 'outbox') $this->getConditionBuilder()->add('conversation.userID = ?', [$userID]);
68 }
69
70 // filter by label id
71 if ($labelID) {
72 $this->getConditionBuilder()->add("conversation.conversationID IN (
73 SELECT conversationID
74 FROM wcf".WCF_N."_conversation_label_to_object
75 WHERE labelID = ?
76 )", [$labelID]);
77 }
78
79 // own posts
80 $this->sqlSelects = "DISTINCT conversation_message.userID AS ownPosts";
81 $this->sqlJoins = "LEFT JOIN wcf".WCF_N."_conversation_message conversation_message ON (conversation_message.conversationID = conversation.conversationID AND conversation_message.userID = ".$userID.")";
82
83 // user info
84 if (!empty($this->sqlSelects)) $this->sqlSelects .= ',';
85 $this->sqlSelects .= "conversation_to_user.*";
86 $this->sqlJoins .= "LEFT JOIN wcf".WCF_N."_conversation_to_user conversation_to_user ON (conversation_to_user.participantID = ".$userID." AND conversation_to_user.conversationID = conversation.conversationID)";
87
88 if ($this->filter !== 'draft') {
89 $this->sqlSelects .= ", conversation.*, CASE WHEN conversation_to_user.leftAt <> 0 THEN conversation_to_user.leftAt ELSE conversation.lastPostTime END AS lastPostTime";
90 // this avoids appending `conversation.*` to the SELECT list
91 $this->useQualifiedShorthand = false;
92 }
93 }
94
95 /**
96 * Sets the label list of the user the conversations belong to.
97 *
98 * @param ConversationLabelList $labelList
99 */
100 public function setLabelList(ConversationLabelList $labelList) {
101 $this->labelList = $labelList;
102 }
103
104 /**
105 * @inheritDoc
106 */
107 public function countObjects() {
108 if ($this->filter == 'draft') return parent::countObjects();
109
110 $sql = "SELECT COUNT(*) AS count
111 FROM wcf".WCF_N."_conversation_to_user conversation_to_user
112 ".$this->sqlConditionJoins."
113 ".$this->getConditionBuilder()->__toString();
114 $statement = WCF::getDB()->prepareStatement($sql);
115 $statement->execute($this->getConditionBuilder()->getParameters());
116 $row = $statement->fetchArray();
117 return $row['count'];
118 }
119
120 /**
121 * @inheritDoc
122 */
123 public function readObjectIDs() {
124 if ($this->filter === 'draft') {
125 parent::readObjectIDs();
126
127 return;
128 }
129
130 $sql = "SELECT conversation_to_user.conversationID AS objectID
131 FROM wcf".WCF_N."_conversation_to_user conversation_to_user
132 ".$this->sqlConditionJoins."
133 ".$this->getConditionBuilder()->__toString()."
134 ".(!empty($this->sqlOrderBy) ? "ORDER BY ".$this->sqlOrderBy : '');
135 $statement = WCF::getDB()->prepareStatement($sql, $this->sqlLimit, $this->sqlOffset);
136 $statement->execute($this->getConditionBuilder()->getParameters());
137 $this->objectIDs = $statement->fetchAll(\PDO::FETCH_COLUMN);
138 }
139
140 /**
141 * @inheritDoc
142 */
143 public function readObjects() {
144 if ($this->objectIDs === null) {
145 $this->readObjectIDs();
146 }
147
148 parent::readObjects();
149
150 if (!empty($this->objects)) {
151 $messageIDs = [];
152 foreach ($this->objects as $conversation) {
153 if ($conversation->lastMessageID) {
154 $messageIDs[] = $conversation->lastMessageID;
155 }
156 }
157 if (!empty($messageIDs)) {
158 $conditions = new PreparedStatementConditionBuilder();
159 $conditions->add("messageID IN (?)", [$messageIDs]);
160 $sql = "SELECT messageID, userID, username, time
161 FROM wcf".WCF_N."_conversation_message
162 ".$conditions;
163 $statement = WCF::getDB()->prepareStatement($sql);
164 $statement->execute($conditions->getParameters());
165 $messageData = [];
166 while ($row = $statement->fetchArray()) {
167 $messageData[$row['messageID']] = $row;
168 }
169
170 foreach ($this->objects as $conversation) {
171 if ($conversation->lastMessageID) {
172 $data = (isset($messageData[$conversation->lastMessageID])) ? $messageData[$conversation->lastMessageID] : null;
173 if ($data !== null) {
174 $conversation->setLastMessage($data['userID'], $data['username'], $data['time']);
175 }
176 else {
177 $conversation->setLastMessage(null, '', 0);
178 }
179 }
180 }
181 }
182
183 $labels = $this->loadLabelAssignments();
184
185 $userIDs = [];
186 foreach ($this->objects as $conversationID => $conversation) {
187 if (isset($labels[$conversationID])) {
188 foreach ($labels[$conversationID] as $label) {
189 $conversation->assignLabel($label);
190 }
191 }
192
193 if ($conversation->userID) {
194 $userIDs[] = $conversation->userID;
195 }
196 if ($conversation->lastPosterID) {
197 $userIDs[] = $conversation->lastPosterID;
198 }
199 }
200
201 if (!empty($userIDs)) {
202 UserProfileRuntimeCache::getInstance()->cacheObjectIDs($userIDs);
203 }
204 }
205 }
206
207 /**
208 * Returns a list of conversation labels.
209 *
210 * @return ConversationLabel[]
211 */
212 protected function getLabels() {
213 if ($this->labelList === null) {
214 $this->labelList = ConversationLabel::getLabelsByUser();
215 }
216
217 return $this->labelList->getObjects();
218 }
219
220 /**
221 * Returns label assignments per conversation.
222 *
223 * @return ConversationLabel[][]
224 */
225 protected function loadLabelAssignments() {
226 $labels = $this->getLabels();
227 if (empty($labels)) {
228 return [];
229 }
230
231 $conditions = new PreparedStatementConditionBuilder();
232 $conditions->add("conversationID IN (?)", [array_keys($this->objects)]);
233 $conditions->add("labelID IN (?)", [array_keys($labels)]);
234
235 $sql = "SELECT labelID, conversationID
236 FROM wcf".WCF_N."_conversation_label_to_object
237 ".$conditions;
238 $statement = WCF::getDB()->prepareStatement($sql);
239 $statement->execute($conditions->getParameters());
240 $data = [];
241 while ($row = $statement->fetchArray()) {
242 if (!isset($data[$row['conversationID']])) {
243 $data[$row['conversationID']] = [];
244 }
245
246 $data[$row['conversationID']][$row['labelID']] = $labels[$row['labelID']];
247 }
248
249 return $data;
250 }
251 }