Commit | Line | Data |
---|---|---|
320f4a6d | 1 | <?php |
a9229942 | 2 | |
320f4a6d | 3 | namespace wcf\data\user\online; |
a9229942 | 4 | |
11e9145a | 5 | use wcf\data\option\OptionAction; |
320f4a6d MW |
6 | use wcf\data\session\SessionList; |
7 | use wcf\data\user\group\UserGroup; | |
8 | use wcf\data\user\User; | |
f990a9cc | 9 | use wcf\data\user\UserProfile; |
55bf83d4 | 10 | use wcf\system\event\EventHandler; |
320f4a6d MW |
11 | use wcf\system\WCF; |
12 | use wcf\util\StringUtil; | |
13 | ||
14 | /** | |
15 | * Represents a list of currently online users. | |
e82bf444 | 16 | * |
a9229942 TD |
17 | * @author Marcel Werk |
18 | * @copyright 2001-2019 WoltLab GmbH | |
19 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
20 | * @package WoltLabSuite\Core\Data\User\Online | |
21 | * | |
22 | * @method UserOnline current() | |
23 | * @method UserOnline[] getObjects() | |
24 | * @method UserOnline|null search($objectID) | |
25 | * @property UserOnline[] $objects | |
320f4a6d | 26 | */ |
a9229942 TD |
27 | class UsersOnlineList extends SessionList |
28 | { | |
29 | /** | |
30 | * @inheritDoc | |
31 | */ | |
32 | public $sqlOrderBy = 'user_table.username'; | |
33 | ||
34 | /** | |
35 | * users online stats | |
36 | * @var array | |
37 | */ | |
38 | public $stats = [ | |
39 | 'total' => 0, | |
40 | 'invisible' => 0, | |
41 | 'members' => 0, | |
42 | 'guests' => 0, | |
43 | ]; | |
44 | ||
45 | /** | |
46 | * users online markings | |
47 | * @var array | |
48 | */ | |
49 | public $usersOnlineMarkings; | |
50 | ||
51 | /** | |
52 | * @inheritDoc | |
53 | */ | |
54 | public function __construct() | |
55 | { | |
56 | parent::__construct(); | |
57 | ||
58 | $this->sqlSelects .= "user_avatar.*, user_option_value.*, user_group.userOnlineMarking, user_table.*"; | |
59 | ||
d3bd0a85 MS |
60 | $this->sqlConditionJoins .= " |
61 | LEFT JOIN wcf" . WCF_N . "_user user_table | |
62 | ON user_table.userID = session.userID"; | |
63 | $this->sqlJoins .= " | |
64 | LEFT JOIN wcf" . WCF_N . "_user user_table | |
65 | ON user_table.userID = session.userID | |
66 | LEFT JOIN wcf" . WCF_N . "_user_option_value user_option_value | |
67 | ON user_option_value.userID = user_table.userID | |
68 | LEFT JOIN wcf" . WCF_N . "_user_avatar user_avatar | |
69 | ON user_avatar.avatarID = user_table.avatarID | |
70 | LEFT JOIN wcf" . WCF_N . "_user_group user_group | |
71 | ON user_group.groupID = user_table.userOnlineGroupID"; | |
a9229942 TD |
72 | |
73 | $this->getConditionBuilder()->add('session.lastActivityTime > ?', [TIME_NOW - USER_ONLINE_TIMEOUT]); | |
74 | } | |
75 | ||
76 | /** | |
77 | * @inheritDoc | |
78 | */ | |
79 | public function readObjects() | |
80 | { | |
81 | parent::readObjects(); | |
82 | ||
83 | $objects = $this->objects; | |
84 | $this->indexToObject = $this->objects = []; | |
85 | ||
86 | foreach ($objects as $object) { | |
87 | $object = new UserOnline(new User(null, null, $object)); | |
88 | if (!$object->userID || self::isVisibleUser($object)) { | |
89 | $this->objects[$object->sessionID] = $object; | |
90 | $this->indexToObject[] = $object->sessionID; | |
91 | } | |
92 | } | |
93 | $this->objectIDs = $this->indexToObject; | |
94 | $this->rewind(); | |
95 | } | |
96 | ||
97 | /** | |
98 | * Fetches users online stats. | |
99 | */ | |
100 | public function readStats() | |
101 | { | |
102 | $conditionBuilder = clone $this->getConditionBuilder(); | |
103 | $conditionBuilder->add('session.spiderID IS NULL'); | |
104 | ||
105 | $sql = "SELECT user_option_value.userOption" . User::getUserOptionID('canViewOnlineStatus') . " AS canViewOnlineStatus, session.userID | |
106 | FROM wcf" . WCF_N . "_session session | |
107 | LEFT JOIN wcf" . WCF_N . "_user_option_value user_option_value | |
c240c98a | 108 | ON user_option_value.userID = session.userID |
a9229942 TD |
109 | " . $conditionBuilder; |
110 | $statement = WCF::getDB()->prepareStatement($sql); | |
111 | $statement->execute($conditionBuilder->getParameters()); | |
112 | ||
113 | $users = $userIDs = []; | |
114 | while ($row = $statement->fetchArray()) { | |
115 | $this->stats['total']++; | |
116 | ||
117 | $user = new UserOnline(new User(null, $row)); | |
118 | if ($user->userID) { | |
119 | $this->stats['members']++; | |
120 | $users[] = $user; | |
121 | $userIDs[] = $user->userID; | |
122 | } else { | |
123 | $this->stats['guests']++; | |
124 | } | |
125 | } | |
126 | ||
127 | foreach ($users as $user) { | |
128 | if ($user->canViewOnlineStatus && !self::isVisibleUser($user)) { | |
129 | $this->stats['invisible']++; | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | /** | |
135 | * Returns a list of the users online markings. | |
136 | * | |
137 | * @return array | |
138 | */ | |
139 | public function getUsersOnlineMarkings() | |
140 | { | |
141 | if ($this->usersOnlineMarkings === null) { | |
142 | $this->usersOnlineMarkings = $priorities = []; | |
143 | ||
144 | // get groups | |
145 | foreach (UserGroup::getGroupsByType() as $group) { | |
146 | if ($group->userOnlineMarking != '%s') { | |
147 | $priorities[] = $group->priority; | |
148 | $this->usersOnlineMarkings[] = \str_replace( | |
149 | '%s', | |
150 | StringUtil::encodeHTML(WCF::getLanguage()->get($group->groupName)), | |
151 | $group->userOnlineMarking | |
152 | ); | |
153 | } | |
154 | } | |
155 | ||
156 | // sort list | |
157 | \array_multisort($priorities, \SORT_DESC, $this->usersOnlineMarkings); | |
158 | } | |
159 | ||
160 | return $this->usersOnlineMarkings; | |
161 | } | |
162 | ||
163 | /** | |
164 | * Checks the users online record. | |
165 | */ | |
166 | public function checkRecord() | |
167 | { | |
168 | $usersOnlineTotal = (USERS_ONLINE_RECORD_NO_GUESTS ? $this->stats['members'] : $this->stats['total']); | |
169 | if ($usersOnlineTotal > USERS_ONLINE_RECORD) { | |
170 | // save new record | |
171 | $optionAction = new OptionAction([], 'import', [ | |
172 | 'data' => [ | |
173 | 'users_online_record' => $usersOnlineTotal, | |
174 | 'users_online_record_time' => TIME_NOW, | |
175 | ], | |
176 | ]); | |
177 | $optionAction->executeAction(); | |
178 | } | |
179 | } | |
180 | ||
181 | /** | |
182 | * Checks the 'canViewOnlineStatus' setting. | |
183 | * | |
184 | * @param int $userID | |
185 | * @param int $canViewOnlineStatus | |
186 | * @return bool | |
187 | * @deprecated 5.3 Use `isVisibleUser` instead | |
188 | */ | |
189 | public static function isVisible($userID, $canViewOnlineStatus) | |
190 | { | |
191 | if (WCF::getSession()->getPermission('admin.user.canViewInvisible') || $userID == WCF::getUser()->userID) { | |
192 | return true; | |
193 | } | |
194 | ||
195 | $data = [ | |
196 | 'result' => false, | |
197 | 'userID' => $userID, | |
198 | 'canViewOnlineStatus' => $canViewOnlineStatus, | |
199 | ]; | |
200 | ||
201 | switch ($canViewOnlineStatus) { | |
202 | case UserProfile::ACCESS_EVERYONE: | |
203 | $data['result'] = true; | |
204 | break; | |
205 | ||
206 | case UserProfile::ACCESS_REGISTERED: | |
207 | if (WCF::getUser()->userID) { | |
208 | $data['result'] = true; | |
209 | } | |
210 | break; | |
211 | ||
212 | case UserProfile::ACCESS_FOLLOWING: | |
213 | /** @noinspection PhpUndefinedMethodInspection */ | |
214 | if (WCF::getUserProfileHandler()->isFollower($userID)) { | |
215 | $data['result'] = true; | |
216 | } | |
217 | break; | |
218 | } | |
219 | ||
220 | EventHandler::getInstance()->fireAction(\get_called_class(), 'isVisible', $data); | |
221 | ||
222 | return $data['result']; | |
223 | } | |
224 | ||
225 | /** | |
226 | * Checks the 'canViewOnlineStatus' setting for the given user. | |
227 | * | |
228 | * @param UserOnline $userOnline | |
229 | * @return bool | |
230 | * @since 5.3 | |
231 | */ | |
232 | public static function isVisibleUser(UserOnline $userOnline) | |
233 | { | |
234 | if (WCF::getSession()->getPermission('admin.user.canViewInvisible') || $userOnline->userID == WCF::getUser()->userID) { | |
235 | return true; | |
236 | } | |
237 | ||
238 | $data = [ | |
239 | 'result' => false, | |
240 | 'userOnline' => $userOnline, | |
241 | ]; | |
242 | ||
243 | switch ($userOnline->canViewOnlineStatus) { | |
244 | case UserProfile::ACCESS_EVERYONE: | |
245 | $data['result'] = true; | |
246 | break; | |
247 | ||
248 | case UserProfile::ACCESS_REGISTERED: | |
249 | if (WCF::getUser()->userID) { | |
250 | $data['result'] = true; | |
251 | } | |
252 | break; | |
253 | ||
254 | case UserProfile::ACCESS_FOLLOWING: | |
255 | /** @noinspection PhpUndefinedMethodInspection */ | |
256 | if (WCF::getUserProfileHandler()->isFollower($userOnline->userID)) { | |
257 | $data['result'] = true; | |
258 | } | |
259 | break; | |
260 | } | |
261 | ||
262 | EventHandler::getInstance()->fireAction(\get_called_class(), 'isVisibleUser', $data); | |
263 | ||
264 | return $data['result']; | |
265 | } | |
320f4a6d | 266 | } |