Commit | Line | Data |
---|---|---|
11ade432 AE |
1 | <?php |
2 | namespace wcf\data\user; | |
ec1b1daf | 3 | use wcf\data\user\group\UserGroup; |
2bc9f31d | 4 | use wcf\data\user\UserList; |
931f6597 | 5 | use wcf\data\DatabaseObject; |
647741fd | 6 | use wcf\data\IUserContent; |
b401cd0d | 7 | use wcf\system\cache\builder\UserOptionCacheBuilder; |
0d30adfb | 8 | use wcf\system\language\LanguageFactory; |
0602bb11 | 9 | use wcf\system\request\IRouteController; |
647741fd | 10 | use wcf\system\request\LinkHandler; |
c96ee721 | 11 | use wcf\system\user\storage\UserStorageHandler; |
2bc9f31d | 12 | use wcf\system\WCF; |
4e273b1f | 13 | use wcf\util\PasswordUtil; |
11ade432 AE |
14 | |
15 | /** | |
16 | * Represents a user. | |
9f959ced | 17 | * |
11ade432 | 18 | * @author Alexander Ebert |
ca4ba303 | 19 | * @copyright 2001-2014 WoltLab GmbH |
11ade432 AE |
20 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
21 | * @package com.woltlab.wcf | |
22 | * @subpackage data.user | |
9f959ced | 23 | * @category Community Framework |
11ade432 | 24 | */ |
647741fd | 25 | final class User extends DatabaseObject implements IRouteController, IUserContent { |
11ade432 | 26 | /** |
0ad90fc3 | 27 | * @see \wcf\data\DatabaseObject::$databaseTableName |
11ade432 AE |
28 | */ |
29 | protected static $databaseTableName = 'user'; | |
30 | ||
31 | /** | |
0ad90fc3 | 32 | * @see \wcf\data\DatabaseObject::$databaseTableIndexName |
11ade432 AE |
33 | */ |
34 | protected static $databaseTableIndexName = 'userID'; | |
35 | ||
36 | /** | |
37 | * list of group ids | |
11ade432 AE |
38 | * @var array<integer> |
39 | */ | |
40 | protected $groupIDs = null; | |
41 | ||
45ecfc0d AE |
42 | /** |
43 | * true, if user has access to the ACP | |
44 | * @var boolean | |
45 | */ | |
46 | protected $hasAdministrativePermissions = null; | |
47 | ||
11ade432 AE |
48 | /** |
49 | * list of language ids | |
11ade432 AE |
50 | * @var array<integer> |
51 | */ | |
52 | protected $languageIDs = null; | |
53 | ||
54 | /** | |
55 | * date time zone object | |
0c166126 | 56 | * @var DateTimeZone |
11ade432 AE |
57 | */ |
58 | protected $timezoneObj = null; | |
59 | ||
60 | /** | |
9f959ced | 61 | * list of user options |
11ade432 AE |
62 | * @var array<string> |
63 | */ | |
64 | protected static $userOptions = null; | |
65 | ||
21ab9270 | 66 | /** |
0ad90fc3 | 67 | * @see \wcf\data\DatabaseObject::__construct() |
21ab9270 | 68 | */ |
590d691b | 69 | public function __construct($id, $row = null, DatabaseObject $object = null) { |
21ab9270 | 70 | if ($id !== null) { |
35306344 | 71 | $sql = "SELECT user_option_value.*, user_table.* |
21ab9270 AE |
72 | FROM wcf".WCF_N."_user user_table |
73 | LEFT JOIN wcf".WCF_N."_user_option_value user_option_value | |
74 | ON (user_option_value.userID = user_table.userID) | |
75 | WHERE user_table.userID = ?"; | |
76 | $statement = WCF::getDB()->prepareStatement($sql); | |
77 | $statement->execute(array($id)); | |
78 | $row = $statement->fetchArray(); | |
79 | ||
80 | // enforce data type 'array' | |
81 | if ($row === false) $row = array(); | |
82 | } | |
83 | else if ($object !== null) { | |
84 | $row = $object->data; | |
85 | } | |
86 | ||
87 | $this->handleData($row); | |
88 | } | |
89 | ||
11ade432 | 90 | /** |
28410a97 | 91 | * Returns true if the given password is the correct password for this user. |
9f959ced | 92 | * |
39bea7dd MS |
93 | * @param string $password |
94 | * @return boolean password correct | |
11ade432 AE |
95 | */ |
96 | public function checkPassword($password) { | |
4e273b1f AE |
97 | $isValid = false; |
98 | $rebuild = false; | |
99 | ||
100 | // check if password is a valid bcrypt hash | |
101 | if (PasswordUtil::isBlowfish($this->password)) { | |
102 | if (PasswordUtil::isDifferentBlowfish($this->password)) { | |
103 | $rebuild = true; | |
104 | } | |
105 | ||
106 | // password is correct | |
75c41a54 | 107 | if (PasswordUtil::secureCompare($this->password, PasswordUtil::getDoubleSaltedHash($password, $this->password))) { |
4e273b1f AE |
108 | $isValid = true; |
109 | } | |
110 | } | |
111 | else { | |
112 | // different encryption type | |
113 | if (PasswordUtil::checkPassword($this->username, $password, $this->password)) { | |
114 | $isValid = true; | |
115 | $rebuild = true; | |
116 | } | |
117 | } | |
118 | ||
119 | // create new password hash, either different encryption or different blowfish cost factor | |
5833e440 | 120 | if ($rebuild && $isValid) { |
4e273b1f AE |
121 | $userEditor = new UserEditor($this); |
122 | $userEditor->update(array( | |
2984f8d8 | 123 | 'password' => $password |
4e273b1f AE |
124 | )); |
125 | } | |
126 | ||
127 | return $isValid; | |
11ade432 AE |
128 | } |
129 | ||
130 | /** | |
28410a97 | 131 | * Returns true if the given password hash from a cookie is the correct password for this user. |
9f959ced | 132 | * |
39bea7dd MS |
133 | * @param string $passwordHash |
134 | * @return boolean password correct | |
11ade432 AE |
135 | */ |
136 | public function checkCookiePassword($passwordHash) { | |
75c41a54 | 137 | if (PasswordUtil::isBlowfish($this->password) && PasswordUtil::secureCompare($this->password, PasswordUtil::getSaltedHash($passwordHash, $this->password))) { |
4e273b1f AE |
138 | return true; |
139 | } | |
140 | ||
141 | return false; | |
11ade432 AE |
142 | } |
143 | ||
144 | /** | |
a90028e5 | 145 | * Returns an array with all the groups in which the actual user is a member. |
d3db5e74 TD |
146 | * |
147 | * @param boolean $skipCache | |
39bea7dd | 148 | * @return array $groupIDs |
11ade432 | 149 | */ |
d3db5e74 TD |
150 | public function getGroupIDs($skipCache = false) { |
151 | if ($this->groupIDs === null || $skipCache) { | |
11ade432 AE |
152 | if (!$this->userID) { |
153 | // user is a guest, use default guest group | |
c68604d8 | 154 | $this->groupIDs = UserGroup::getGroupIDsByType(array(UserGroup::GUESTS, UserGroup::EVERYONE)); |
11ade432 AE |
155 | } |
156 | else { | |
157 | // load storage data | |
c2ff7e2b | 158 | UserStorageHandler::getInstance()->loadStorage(array($this->userID)); |
11ade432 AE |
159 | |
160 | // get group ids | |
c2ff7e2b | 161 | $data = UserStorageHandler::getInstance()->getStorage(array($this->userID), 'groupIDs'); |
11ade432 AE |
162 | |
163 | // cache does not exist or is outdated | |
d3db5e74 | 164 | if ($data[$this->userID] === null || $skipCache) { |
11ade432 AE |
165 | $this->groupIDs = array(); |
166 | $sql = "SELECT groupID | |
167 | FROM wcf".WCF_N."_user_to_group | |
168 | WHERE userID = ?"; | |
169 | $statement = WCF::getDB()->prepareStatement($sql); | |
170 | $statement->execute(array($this->userID)); | |
171 | while ($row = $statement->fetchArray()) { | |
172 | $this->groupIDs[] = $row['groupID']; | |
173 | } | |
174 | ||
175 | // update storage data | |
d3db5e74 | 176 | if (!$skipCache) { |
fedc1f42 | 177 | UserStorageHandler::getInstance()->update($this->userID, 'groupIDs', serialize($this->groupIDs)); |
d3db5e74 | 178 | } |
11ade432 AE |
179 | } |
180 | else { | |
181 | $this->groupIDs = unserialize($data[$this->userID]); | |
182 | } | |
183 | } | |
fb6d9e90 MW |
184 | |
185 | sort($this->groupIDs, SORT_NUMERIC); | |
11ade432 AE |
186 | } |
187 | ||
188 | return $this->groupIDs; | |
189 | } | |
190 | ||
191 | /** | |
192 | * Returns a list of language ids for this user. | |
193 | * | |
194 | * @return array<integer> | |
195 | */ | |
196 | public function getLanguageIDs() { | |
197 | if ($this->languageIDs === null) { | |
cd760846 MS |
198 | $this->languageIDs = array(); |
199 | ||
200 | if ($this->userID) { | |
f37d4742 | 201 | // load storage data |
c2ff7e2b | 202 | UserStorageHandler::getInstance()->loadStorage(array($this->userID)); |
f37d4742 MW |
203 | |
204 | // get language ids | |
c2ff7e2b | 205 | $data = UserStorageHandler::getInstance()->getStorage(array($this->userID), 'languageIDs'); |
f37d4742 MW |
206 | |
207 | // cache does not exist or is outdated | |
208 | if ($data[$this->userID] === null) { | |
209 | $sql = "SELECT languageID | |
210 | FROM wcf".WCF_N."_user_to_language | |
211 | WHERE userID = ?"; | |
212 | $statement = WCF::getDB()->prepareStatement($sql); | |
213 | $statement->execute(array($this->userID)); | |
214 | while ($row = $statement->fetchArray()) { | |
215 | $this->languageIDs[] = $row['languageID']; | |
216 | } | |
217 | ||
218 | // update storage data | |
fedc1f42 | 219 | UserStorageHandler::getInstance()->update($this->userID, 'languageIDs', serialize($this->languageIDs)); |
f37d4742 MW |
220 | } |
221 | else { | |
222 | $this->languageIDs = unserialize($data[$this->userID]); | |
223 | } | |
11ade432 | 224 | } |
a26762e7 | 225 | else if (!WCF::getSession()->spiderID) { |
f672f1df | 226 | $this->languageIDs[] = WCF::getLanguage()->languageID; |
b37663f5 | 227 | } |
11ade432 AE |
228 | } |
229 | ||
230 | return $this->languageIDs; | |
231 | } | |
232 | ||
233 | /** | |
234 | * Returns the value of the user option with the given name. | |
9f959ced | 235 | * |
11ade432 AE |
236 | * @param string $name user option name |
237 | * @return mixed user option value | |
238 | */ | |
239 | public function getUserOption($name) { | |
240 | $optionID = self::getUserOptionID($name); | |
241 | if ($optionID === null) { | |
242 | return null; | |
243 | } | |
244 | ||
245 | if (!isset($this->data['userOption'.$optionID])) return null; | |
246 | return $this->data['userOption'.$optionID]; | |
247 | } | |
248 | ||
249 | /** | |
250 | * Gets all user options from cache. | |
251 | */ | |
252 | protected static function getUserOptionCache() { | |
b401cd0d | 253 | self::$userOptions = UserOptionCacheBuilder::getInstance()->getData(array(), 'options'); |
11ade432 AE |
254 | } |
255 | ||
256 | /** | |
257 | * Returns the id of a user option. | |
9f959ced | 258 | * |
11ade432 AE |
259 | * @param string $name |
260 | * @return integer id | |
261 | */ | |
262 | public static function getUserOptionID($name) { | |
263 | // get user option cache if necessary | |
264 | if (self::$userOptions === null) { | |
265 | self::getUserOptionCache(); | |
266 | } | |
267 | ||
268 | if (!isset(self::$userOptions[$name])) { | |
269 | return null; | |
270 | } | |
271 | ||
cd26e5f4 | 272 | return self::$userOptions[$name]->optionID; |
11ade432 AE |
273 | } |
274 | ||
275 | /** | |
0ad90fc3 | 276 | * @see \wcf\data\DatabaseObject::__get() |
11ade432 AE |
277 | */ |
278 | public function __get($name) { | |
279 | $value = parent::__get($name); | |
280 | if ($value === null) $value = $this->getUserOption($name); | |
281 | return $value; | |
282 | } | |
283 | ||
284 | /** | |
9f959ced MS |
285 | * Returns the user with the given username. |
286 | * | |
11ade432 | 287 | * @param string $username |
0ad90fc3 | 288 | * @return \wcf\data\user\User |
11ade432 AE |
289 | */ |
290 | public static function getUserByUsername($username) { | |
291 | $sql = "SELECT * | |
292 | FROM wcf".WCF_N."_user | |
293 | WHERE username = ?"; | |
294 | $statement = WCF::getDB()->prepareStatement($sql); | |
295 | $statement->execute(array($username)); | |
296 | $row = $statement->fetchArray(); | |
297 | if (!$row) $row = array(); | |
6331ef65 R |
298 | |
299 | return new User(null, $row); | |
300 | } | |
301 | ||
302 | /** | |
9f959ced MS |
303 | * Returns the user with the given email. |
304 | * | |
6331ef65 | 305 | * @param string $email |
0ad90fc3 | 306 | * @return \wcf\data\user\User |
6331ef65 R |
307 | */ |
308 | public static function getUserByEmail($email) { | |
309 | $sql = "SELECT * | |
310 | FROM wcf".WCF_N."_user | |
311 | WHERE email = ?"; | |
312 | $statement = WCF::getDB()->prepareStatement($sql); | |
313 | $statement->execute(array($email)); | |
314 | $row = $statement->fetchArray(); | |
315 | if (!$row) $row = array(); | |
11ade432 AE |
316 | |
317 | return new User(null, $row); | |
318 | } | |
319 | ||
320 | /** | |
28410a97 | 321 | * Returns true if this user is marked. |
9f959ced | 322 | * |
39bea7dd | 323 | * @return boolean |
11ade432 AE |
324 | */ |
325 | public function isMarked() { | |
326 | $markedUsers = WCF::getSession()->getVar('markedUsers'); | |
327 | if ($markedUsers !== null) { | |
328 | if (in_array($this->userID, $markedUsers)) return 1; | |
329 | } | |
330 | ||
331 | return 0; | |
332 | } | |
333 | ||
334 | /** | |
335 | * Returns the time zone of this user. | |
9f959ced | 336 | * |
a17de04e | 337 | * @return DateTimeZone |
11ade432 AE |
338 | */ |
339 | public function getTimeZone() { | |
340 | if ($this->timezoneObj === null) { | |
341 | if ($this->timezone) { | |
342 | $this->timezoneObj = new \DateTimeZone($this->timezone); | |
343 | } | |
344 | else { | |
345 | $this->timezoneObj = new \DateTimeZone(TIMEZONE); | |
346 | } | |
347 | } | |
348 | ||
349 | return $this->timezoneObj; | |
350 | } | |
351 | ||
352 | /** | |
353 | * Returns a list of users. | |
354 | * | |
355 | * @param array $userIDs | |
0ad90fc3 | 356 | * @return array<\wcf\data\user\User> |
d3db5e74 | 357 | */ |
11ade432 AE |
358 | public static function getUsers(array $userIDs) { |
359 | $userList = new UserList(); | |
360 | $userList->getConditionBuilder()->add("user_table.userID IN (?)", array($userIDs)); | |
361 | $userList->readObjects(); | |
362 | ||
363 | return $userList->getObjects(); | |
364 | } | |
5ea6afcf AE |
365 | |
366 | /** | |
367 | * Returns username. | |
368 | * | |
369 | * @return string | |
d3db5e74 | 370 | */ |
5ea6afcf AE |
371 | public function __toString() { |
372 | return $this->username; | |
373 | } | |
487011f2 AE |
374 | |
375 | /** | |
0ad90fc3 | 376 | * @see \wcf\data\IStorableObject::getDatabaseTableAlias() |
487011f2 AE |
377 | */ |
378 | public static function getDatabaseTableAlias() { | |
379 | return 'user_table'; | |
380 | } | |
0602bb11 | 381 | |
0602bb11 | 382 | /** |
0ad90fc3 | 383 | * @see \wcf\system\request\IRouteController::getTitle() |
0602bb11 MW |
384 | */ |
385 | public function getTitle() { | |
386 | return $this->username; | |
387 | } | |
0d30adfb MW |
388 | |
389 | /** | |
390 | * Returns the language of this user. | |
a17de04e | 391 | * |
0ad90fc3 | 392 | * @return \wcf\data\language\Language |
0d30adfb MW |
393 | */ |
394 | public function getLanguage() { | |
395 | $language = LanguageFactory::getInstance()->getLanguage($this->languageID); | |
396 | if ($language === null) { | |
397 | $language = LanguageFactory::getInstance()->getLanguage(LanguageFactory::getInstance()->getDefaultLanguageID()); | |
398 | } | |
399 | ||
400 | return $language; | |
401 | } | |
d1e2e6d8 MW |
402 | |
403 | /** | |
28410a97 | 404 | * Returns true if the active user can edit this user. |
d1e2e6d8 MW |
405 | * |
406 | * @return boolean | |
407 | */ | |
408 | public function canEdit() { | |
409 | return (WCF::getSession()->getPermission('admin.user.canEditUser') && UserGroup::isAccessibleGroup($this->getGroupIDs())); | |
410 | } | |
45ecfc0d AE |
411 | |
412 | /** | |
413 | * Returns true, if this user has access to the ACP. | |
414 | * | |
415 | * @return boolean | |
416 | */ | |
417 | public function hasAdministrativeAccess() { | |
418 | if ($this->hasAdministrativePermissions === null) { | |
419 | $this->hasAdministrativePermissions = false; | |
420 | ||
421 | if ($this->userID) { | |
422 | foreach ($this->getGroupIDs() as $groupID) { | |
423 | $group = UserGroup::getGroupByID($groupID); | |
424 | if ($group->isAdminGroup()) { | |
425 | $this->hasAdministrativePermissions = true; | |
426 | break; | |
427 | } | |
428 | } | |
429 | } | |
430 | } | |
431 | ||
432 | return $this->hasAdministrativePermissions; | |
433 | } | |
647741fd MW |
434 | |
435 | /** | |
436 | * @see \wcf\data\IMessage::getUserID() | |
437 | */ | |
438 | public function getUserID() { | |
439 | return $this->userID; | |
440 | } | |
441 | ||
442 | /** | |
443 | * @see \wcf\data\IMessage::getUsername() | |
444 | */ | |
445 | public function getUsername() { | |
446 | return $this->username; | |
447 | } | |
448 | ||
449 | /** | |
450 | * @see \wcf\data\IMessage::getTime() | |
451 | */ | |
452 | public function getTime() { | |
453 | return $this->registrationDate; | |
454 | } | |
455 | ||
456 | /** | |
457 | * @see \wcf\data\ILinkableObject::getLink() | |
458 | */ | |
459 | public function getLink() { | |
460 | return LinkHandler::getInstance()->getLink('User', array( | |
461 | 'application' => 'wcf', | |
462 | 'object' => $this, | |
463 | 'forceFrontend' => true | |
464 | )); | |
465 | } | |
11ade432 | 466 | } |