Merge branch '5.2' into 5.3
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / user / online / UserOnline.class.php
1 <?php
2 namespace wcf\data\user\online;
3 use wcf\data\page\PageCache;
4 use wcf\data\spider\Spider;
5 use wcf\data\user\UserProfile;
6 use wcf\system\cache\builder\SpiderCacheBuilder;
7 use wcf\system\event\EventHandler;
8 use wcf\system\page\handler\IOnlineLocationPageHandler;
9 use wcf\system\WCF;
10 use wcf\util\StringUtil;
11 use wcf\util\UserUtil;
12
13 /**
14 * Represents a user who is online.
15 *
16 * @author Marcel Werk
17 * @copyright 2001-2019 WoltLab GmbH
18 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
19 * @package WoltLabSuite\Core\Data\User\Online
20 *
21 * @property-read integer|null $pageID id of the last visited page
22 * @property-read integer|null $pageObjectID id of the object the last visited page belongs to
23 * @property-read integer|null $parentPageObjectID id of the parent of the object the last visited page belongs to
24 * @property-read string|null $userOnlineMarking HTML code used to print the formatted name of a user group member
25 */
26 class UserOnline extends UserProfile {
27 /**
28 * location of the user
29 * @var string
30 */
31 protected $location = '';
32
33 /**
34 * spider object
35 * @var Spider
36 */
37 protected $spider = null;
38
39 /**
40 * Returns the formatted username.
41 *
42 * @return string
43 */
44 public function getFormattedUsername() {
45 $username = StringUtil::encodeHTML($this->username);
46
47 if ($this->userOnlineMarking && $this->userOnlineMarking != '%s') {
48 $username = str_replace('%s', $username, $this->userOnlineMarking);
49 }
50
51 if ($this->canViewOnlineStatus == UserProfile::ACCESS_NOBODY) {
52 $username .= WCF::getLanguage()->get('wcf.user.usersOnline.invisible');
53 }
54
55 return $username;
56 }
57
58 /**
59 * Sets the location of the user. If no location is given, the method tries to
60 * automatically determine the location.
61 *
62 * @param string|null $location
63 * @return boolean `true` if the location has been successfully set, otherwise `false`
64 */
65 public function setLocation($location = null) {
66 if ($location === null) {
67 if ($this->pageID) {
68 $page = PageCache::getInstance()->getPage($this->pageID);
69 if ($page !== null) {
70 if ($page->getHandler() !== null && $page->getHandler() instanceof IOnlineLocationPageHandler) {
71 // refer to page handler
72 /** @noinspection PhpUndefinedMethodInspection */
73 $this->location = $page->getHandler()->getOnlineLocation($page, $this);
74 return true;
75 }
76 else if ($page->isVisible() && $page->isAccessible()) {
77 $title = $page->getTitle();
78 if (!empty($title)) {
79 if ($page->pageType != 'system') {
80 $this->location = '<a href="' . StringUtil::encodeHTML($page->getLink()) . '">' . StringUtil::encodeHTML($title) . '</a>';
81 }
82 else {
83 $this->location = StringUtil::encodeHTML($title);
84 }
85 }
86
87 return ($this->location != '');
88 }
89 }
90 }
91
92 $this->location = '';
93 return false;
94 }
95
96 $this->location = $location;
97 return true;
98 }
99
100 /**
101 * Returns the location of the user.
102 *
103 * @return string
104 */
105 public function getLocation() {
106 return $this->location;
107 }
108
109 /**
110 * Returns the ip address.
111 *
112 * @return string
113 */
114 public function getFormattedIPAddress() {
115 if ($address = UserUtil::convertIPv6To4($this->ipAddress)) {
116 return $address;
117 }
118
119 return $this->ipAddress;
120 }
121
122 /**
123 * Tries to retrieve browser name and version.
124 *
125 * @return string
126 */
127 public function getBrowser() {
128 $parameters = ['browser' => '', 'userAgent' => $this->userAgent];
129 EventHandler::getInstance()->fireAction($this, 'getBrowser', $parameters);
130 if (!empty($parameters['browser'])) {
131 return $parameters['browser'];
132 }
133
134 // lunascape
135 if (preg_match('~lunascape[ /]([\d\.]+)~i', $this->userAgent, $match)) {
136 return 'Lunascape '.$match[1];
137 }
138
139 // sleipnir
140 if (preg_match('~sleipnir/([\d\.]+)~i', $this->userAgent, $match)) {
141 return 'Sleipnir '.$match[1];
142 }
143
144 // uc browser
145 if (preg_match('~(?:ucbrowser|uc browser|ucweb)[ /]?([\d\.]+)~i', $this->userAgent, $match)) {
146 return 'UC Browser '.$match[1];
147 }
148
149 // baidu browser
150 if (preg_match('~(?:baidubrowser|flyflow)[ /]?([\d\.x]+)~i', $this->userAgent, $match)) {
151 return 'Baidubrowser '.$match[1];
152 }
153
154 // blackberry
155 if (preg_match('~blackberry.*version/([\d\.]+)~i', $this->userAgent, $match)) {
156 return 'Blackberry '.$match[1];
157 }
158
159 // opera mobile
160 if (preg_match('~opera/([\d\.]+).*(mobi|mini)~i', $this->userAgent, $match)) {
161 return 'Opera Mobile '.$match[1];
162 }
163
164 // opera
165 if (preg_match('~opera.*version/([\d\.]+)|opr/([\d\.]+)~i', $this->userAgent, $match)) {
166 return 'Opera '.(isset($match[2]) ? $match[2] : $match[1]);
167 }
168
169 // thunderbird
170 if (preg_match('~thunderbird/([\d\.]+)~i', $this->userAgent, $match)) {
171 return 'Thunderbird '.$match[1];
172 }
173
174 // icedragon
175 if (preg_match('~icedragon/([\d\.]+)~i', $this->userAgent, $match)) {
176 return 'IceDragon '.$match[1];
177 }
178
179 // palemoon
180 if (preg_match('~palemoon/([\d\.]+)~i', $this->userAgent, $match)) {
181 return 'PaleMoon '.$match[1];
182 }
183
184 // flock
185 if (preg_match('~flock/([\d\.]+)~i', $this->userAgent, $match)) {
186 return 'Flock '.$match[1];
187 }
188
189 // iceweasel
190 if (preg_match('~iceweasel/([\d\.]+)~i', $this->userAgent, $match)) {
191 return 'Iceweasel '.$match[1];
192 }
193
194 // firefox mobile
195 if (preg_match('~(?:mobile.*firefox|fxios)/([\d\.]+)|fennec/([\d\.]+)~i', $this->userAgent, $match)) {
196 return 'Firefox Mobile '.(isset($match[2]) ? $match[2] : $match[1]);
197 }
198
199 // tapatalk 4
200 if (preg_match('~tapatalk/([\d\.]+)?~i', $this->userAgent, $match)) {
201 return 'Tapatalk '.(isset($match[1]) ? $match[1] : 4);
202 }
203
204 // firefox
205 if (preg_match('~firefox/([\d\.]+)~i', $this->userAgent, $match)) {
206 return 'Firefox '.$match[1];
207 }
208
209 // maxthon
210 if (preg_match('~maxthon[ /]([\d\.]+)~i', $this->userAgent, $match)) {
211 return 'Maxthon '.$match[1];
212 }
213
214 // iemobile
215 if (preg_match('~iemobile[ /]([\d\.]+)|MSIE ([\d\.]+).*XBLWP7~i', $this->userAgent, $match)) {
216 return 'Internet Explorer Mobile '.(isset($match[2]) ? $match[2] : $match[1]);
217 }
218
219 // ie
220 if (preg_match('~msie ([\d\.]+)|Trident\/\d{1,2}.\d{1,2}; .*rv:([0-9]*)~i', $this->userAgent, $match)) {
221 return 'Internet Explorer '.(isset($match[2]) ? $match[2] : $match[1]);
222 }
223
224 // edge
225 if (preg_match('~edge?/([\d\.]+)~i', $this->userAgent, $match)) {
226 return 'Microsoft Edge '.$match[1];
227 }
228
229 // edge mobile
230 if (preg_match('~edga/([\d\.]+)~i', $this->userAgent, $match)) {
231 return 'Microsoft Edge Mobile '.$match[1];
232 }
233
234 // vivaldi
235 if (preg_match('~vivaldi/([\d\.]+)~i', $this->userAgent, $match)) {
236 return 'Vivaldi '.$match[1];
237 }
238
239 // iron
240 if (preg_match('~iron/([\d\.]+)~i', $this->userAgent, $match)) {
241 return 'Iron '.$match[1];
242 }
243
244 // coowon
245 if (preg_match('~coowon/([\d\.]+)~i', $this->userAgent, $match)) {
246 return 'Coowon '.$match[1];
247 }
248
249 // coolnovo
250 if (preg_match('~(?:coolnovo|chromeplus)/([\d\.]+)~i', $this->userAgent, $match)) {
251 return 'CoolNovo '.$match[1];
252 }
253
254 // yandex
255 if (preg_match('~yabrowser/([\d\.]+)~i', $this->userAgent, $match)) {
256 return 'Yandex '.$match[1];
257 }
258
259 // midori
260 if (preg_match('~midori/([\d\.]+)~i', $this->userAgent, $match)) {
261 return 'Midori '.$match[1];
262 }
263
264 // chrome mobile
265 if (preg_match('~(?:crios|crmo)/([\d\.]+)|chrome/([\d\.]+).*mobile~i', $this->userAgent, $match)) {
266 return 'Chrome Mobile '.(isset($match[2]) ? $match[2] : $match[1]);
267 }
268
269 // kindle
270 if (preg_match('~kindle/([\d\.]+)~i', $this->userAgent, $match)) {
271 return 'Kindle '.$match[1];
272 }
273
274 // silk
275 if (preg_match('~silk/([\d\.]+)~i', $this->userAgent, $match)) {
276 return 'Silk '.$match[1];
277 }
278
279 // android browser
280 if (preg_match('~Android ([\d\.]+).*AppleWebKit~i', $this->userAgent, $match)) {
281 return 'Android Browser '.$match[1];
282 }
283
284 // safari mobile
285 if (preg_match('~([\d\.]+) Mobile/\w+ safari~i', $this->userAgent, $match)) {
286 return 'Safari Mobile '.$match[1];
287 }
288
289 // chrome
290 if (preg_match('~(?:chromium|chrome)/([\d\.]+)~i', $this->userAgent, $match)) {
291 return 'Chrome '.$match[1];
292 }
293
294 // safari
295 if (preg_match('~([\d\.]+) safari~i', $this->userAgent, $match)) {
296 return 'Safari '.$match[1];
297 }
298
299 return $this->userAgent;
300 }
301
302 /**
303 * Returns the spider object
304 *
305 * @return Spider
306 */
307 public function getSpider() {
308 if (!$this->spiderID) return null;
309
310 if ($this->spider === null) {
311 $spiderList = SpiderCacheBuilder::getInstance()->getData();
312 $this->spider = $spiderList[$this->spiderID];
313 }
314
315 return $this->spider;
316 }
317 }