Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / user / User.class.php
CommitLineData
11ade432
AE
1<?php
2namespace wcf\data\user;
ec1b1daf 3use wcf\data\user\group\UserGroup;
2bc9f31d 4use wcf\data\user\UserList;
931f6597 5use wcf\data\DatabaseObject;
647741fd 6use wcf\data\IUserContent;
b401cd0d 7use wcf\system\cache\builder\UserOptionCacheBuilder;
0d30adfb 8use wcf\system\language\LanguageFactory;
0602bb11 9use wcf\system\request\IRouteController;
647741fd 10use wcf\system\request\LinkHandler;
c96ee721 11use wcf\system\user\storage\UserStorageHandler;
2bc9f31d 12use wcf\system\WCF;
4e273b1f 13use 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 25final 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}