Merge branch '3.1' into 5.2
[GitHub/WoltLab/com.woltlab.wcf.conversation.git] / files / lib / system / conversation / ConversationHandler.class.php
1 <?php
2 namespace wcf\system\conversation;
3 use wcf\system\database\util\PreparedStatementConditionBuilder;
4 use wcf\system\exception\NamedUserException;
5 use wcf\system\exception\PermissionDeniedException;
6 use wcf\system\user\storage\UserStorageHandler;
7 use wcf\system\SingletonFactory;
8 use wcf\system\WCF;
9
10 /**
11 * Handles the number of conversations and unread conversations of the active user.
12 *
13 * @author Marcel Werk
14 * @copyright 2001-2019 WoltLab GmbH
15 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
16 * @package WoltLabSuite\Core\System\Conversation
17 */
18 class ConversationHandler extends SingletonFactory {
19 /**
20 * number of unread conversations
21 * @var integer[]
22 */
23 protected $unreadConversationCount = [];
24
25 /**
26 * number of conversations
27 * @var integer[]
28 */
29 protected $conversationCount = [];
30
31 /**
32 * Returns the number of unread conversations for given user.
33 *
34 * @param integer $userID
35 * @param boolean $skipCache
36 * @return integer
37 */
38 public function getUnreadConversationCount($userID = null, $skipCache = false) {
39 if ($userID === null) $userID = WCF::getUser()->userID;
40
41 if (!isset($this->unreadConversationCount[$userID]) || $skipCache) {
42 $this->unreadConversationCount[$userID] = 0;
43
44 // load storage data
45 UserStorageHandler::getInstance()->loadStorage([$userID]);
46
47 // get ids
48 $data = UserStorageHandler::getInstance()->getStorage([$userID], 'unreadConversationCount');
49
50 // cache does not exist or is outdated
51 if ($data[$userID] === null || $skipCache) {
52 $conditionBuilder = new PreparedStatementConditionBuilder();
53 $conditionBuilder->add('conversation.conversationID = conversation_to_user.conversationID');
54 $conditionBuilder->add('conversation_to_user.participantID = ?', [$userID]);
55 $conditionBuilder->add('conversation_to_user.hideConversation = 0');
56 $conditionBuilder->add('conversation_to_user.lastVisitTime < conversation.lastPostTime');
57 $conditionBuilder->add('conversation_to_user.leftAt = 0');
58
59 $sql = "SELECT COUNT(*) AS count
60 FROM wcf".WCF_N."_conversation_to_user conversation_to_user,
61 wcf".WCF_N."_conversation conversation
62 ".$conditionBuilder;
63 $statement = WCF::getDB()->prepareStatement($sql);
64 $statement->execute($conditionBuilder->getParameters());
65 $row = $statement->fetchArray();
66 $this->unreadConversationCount[$userID] = $row['count'];
67
68 // update storage data
69 UserStorageHandler::getInstance()->update($userID, 'unreadConversationCount', serialize($this->unreadConversationCount[$userID]));
70 }
71 else {
72 $this->unreadConversationCount[$userID] = unserialize($data[$userID]);
73 }
74 }
75
76 return $this->unreadConversationCount[$userID];
77 }
78
79 /**
80 * Returns the number of conversations for given user.
81 *
82 * @param integer $userID
83 * @return integer
84 */
85 public function getConversationCount($userID = null) {
86 if ($userID === null) $userID = WCF::getUser()->userID;
87
88 if (!isset($this->conversationCount[$userID])) {
89 $this->conversationCount[$userID] = 0;
90
91 // load storage data
92 UserStorageHandler::getInstance()->loadStorage([$userID]);
93
94 // get ids
95 $data = UserStorageHandler::getInstance()->getStorage([$userID], 'conversationCount');
96
97 // cache does not exist or is outdated
98 if ($data[$userID] === null) {
99 $conditionBuilder1 = new PreparedStatementConditionBuilder();
100 $conditionBuilder1->add('conversation_to_user.participantID = ?', [$userID]);
101 $conditionBuilder1->add('conversation_to_user.hideConversation IN (0,1)');
102 $conditionBuilder2 = new PreparedStatementConditionBuilder();
103 $conditionBuilder2->add('conversation.userID = ?', [$userID]);
104 $conditionBuilder2->add('conversation.isDraft = 1');
105
106 $sql = "SELECT (SELECT COUNT(*)
107 FROM wcf".WCF_N."_conversation_to_user conversation_to_user
108 ".$conditionBuilder1->__toString().")
109 +
110 (SELECT COUNT(*)
111 FROM wcf".WCF_N."_conversation conversation
112 ".$conditionBuilder2->__toString().") AS count";
113 $statement = WCF::getDB()->prepareStatement($sql);
114 $statement->execute(array_merge($conditionBuilder1->getParameters(), $conditionBuilder2->getParameters()));
115 $row = $statement->fetchArray();
116 $this->conversationCount[$userID] = $row['count'];
117
118 // update storage data
119 UserStorageHandler::getInstance()->update($userID, 'conversationCount', serialize($this->conversationCount[$userID]));
120 }
121 else {
122 $this->conversationCount[$userID] = unserialize($data[$userID]);
123 }
124 }
125
126 return $this->conversationCount[$userID];
127 }
128
129 /**
130 * Enforces the flood control.
131 */
132 public function enforceFloodControl() {
133 $limit = WCF::getSession()->getPermission('user.conversation.maxStartedConversationsPer24Hours');
134 if ($limit == -1) {
135 return;
136 }
137 else if ($limit == 0) {
138 // `0` is not a valid value, but the interface logic does not permit and exclusion
139 // while also allowing the special value `-1`. Therefore, `0` behaves like the
140 // 'canStartConversation' permission added in WoltLab Suite 5.2.
141 throw new PermissionDeniedException();
142 }
143
144 $sql = "SELECT COUNT(*) AS count, MIN(time) AS oldestDate
145 FROM wcf" . WCF_N . "_conversation
146 WHERE userID = ?
147 AND time > ?";
148 $statement = WCF::getDB()->prepareStatement($sql);
149 $statement->execute([
150 WCF::getUser()->userID,
151 TIME_NOW - 86400,
152 ]);
153 $row = $statement->fetchSingleRow();
154
155 if ($row['count'] >= $limit) {
156 throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.conversation.error.floodControl', [
157 'limit' => $limit,
158 'notBefore' => $row['oldestDate'] + 86400,
159 ]));
160 }
161 }
162 }