2 namespace wcf\data\user
;
3 use wcf\data\user\group\UserGroup
;
4 use wcf\data\user\UserList
;
5 use wcf\data\DatabaseObject
;
6 use wcf\data\IUserContent
;
7 use wcf\system\cache\builder\UserOptionCacheBuilder
;
8 use wcf\system\language\LanguageFactory
;
9 use wcf\system\request\IRouteController
;
10 use wcf\system\request\LinkHandler
;
11 use wcf\system\user\storage\UserStorageHandler
;
13 use wcf\util\PasswordUtil
;
18 * @author Alexander Ebert
19 * @copyright 2001-2014 WoltLab GmbH
20 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
21 * @package com.woltlab.wcf
22 * @subpackage data.user
23 * @category Community Framework
25 final class User
extends DatabaseObject
implements IRouteController
, IUserContent
{
27 * @see \wcf\data\DatabaseObject::$databaseTableName
29 protected static $databaseTableName = 'user';
32 * @see \wcf\data\DatabaseObject::$databaseTableIndexName
34 protected static $databaseTableIndexName = 'userID';
40 protected $groupIDs = null;
43 * true, if user has access to the ACP
46 protected $hasAdministrativePermissions = null;
49 * list of language ids
52 protected $languageIDs = null;
55 * date time zone object
58 protected $timezoneObj = null;
61 * list of user options
64 protected static $userOptions = null;
67 * @see \wcf\data\DatabaseObject::__construct()
69 public function __construct($id, $row = null, DatabaseObject
$object = null) {
71 $sql = "SELECT user_option_value.*, user_table.*
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();
80 // enforce data type 'array'
81 if ($row === false) $row = array();
83 else if ($object !== null) {
87 $this->handleData($row);
91 * Returns true if the given password is the correct password for this user.
93 * @param string $password
94 * @return boolean password correct
96 public function checkPassword($password) {
100 // check if password is a valid bcrypt hash
101 if (PasswordUtil
::isBlowfish($this->password
)) {
102 if (PasswordUtil
::isDifferentBlowfish($this->password
)) {
106 // password is correct
107 if (PasswordUtil
::secureCompare($this->password
, PasswordUtil
::getDoubleSaltedHash($password, $this->password
))) {
112 // different encryption type
113 if (PasswordUtil
::checkPassword($this->username
, $password, $this->password
)) {
119 // create new password hash, either different encryption or different blowfish cost factor
120 if ($rebuild && $isValid) {
121 $userEditor = new UserEditor($this);
122 $userEditor->update(array(
123 'password' => $password
131 * Returns true if the given password hash from a cookie is the correct password for this user.
133 * @param string $passwordHash
134 * @return boolean password correct
136 public function checkCookiePassword($passwordHash) {
137 if (PasswordUtil
::isBlowfish($this->password
) && PasswordUtil
::secureCompare($this->password
, PasswordUtil
::getSaltedHash($passwordHash, $this->password
))) {
145 * Returns an array with all the groups in which the actual user is a member.
147 * @param boolean $skipCache
148 * @return array $groupIDs
150 public function getGroupIDs($skipCache = false) {
151 if ($this->groupIDs
=== null ||
$skipCache) {
152 if (!$this->userID
) {
153 // user is a guest, use default guest group
154 $this->groupIDs
= UserGroup
::getGroupIDsByType(array(UserGroup
::GUESTS
, UserGroup
::EVERYONE
));
158 UserStorageHandler
::getInstance()->loadStorage(array($this->userID
));
161 $data = UserStorageHandler
::getInstance()->getStorage(array($this->userID
), 'groupIDs');
163 // cache does not exist or is outdated
164 if ($data[$this->userID
] === null ||
$skipCache) {
165 $this->groupIDs
= array();
166 $sql = "SELECT groupID
167 FROM wcf".WCF_N
."_user_to_group
169 $statement = WCF
::getDB()->prepareStatement($sql);
170 $statement->execute(array($this->userID
));
171 while ($row = $statement->fetchArray()) {
172 $this->groupIDs
[] = $row['groupID'];
175 // update storage data
177 UserStorageHandler
::getInstance()->update($this->userID
, 'groupIDs', serialize($this->groupIDs
));
181 $this->groupIDs
= unserialize($data[$this->userID
]);
185 sort($this->groupIDs
, SORT_NUMERIC
);
188 return $this->groupIDs
;
192 * Returns a list of language ids for this user.
194 * @return array<integer>
196 public function getLanguageIDs() {
197 if ($this->languageIDs
=== null) {
198 $this->languageIDs
= array();
202 UserStorageHandler
::getInstance()->loadStorage(array($this->userID
));
205 $data = UserStorageHandler
::getInstance()->getStorage(array($this->userID
), 'languageIDs');
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
212 $statement = WCF
::getDB()->prepareStatement($sql);
213 $statement->execute(array($this->userID
));
214 while ($row = $statement->fetchArray()) {
215 $this->languageIDs
[] = $row['languageID'];
218 // update storage data
219 UserStorageHandler
::getInstance()->update($this->userID
, 'languageIDs', serialize($this->languageIDs
));
222 $this->languageIDs
= unserialize($data[$this->userID
]);
225 else if (!WCF
::getSession()->spiderID
) {
226 $this->languageIDs
[] = WCF
::getLanguage()->languageID
;
230 return $this->languageIDs
;
234 * Returns the value of the user option with the given name.
236 * @param string $name user option name
237 * @return mixed user option value
239 public function getUserOption($name) {
240 $optionID = self
::getUserOptionID($name);
241 if ($optionID === null) {
245 if (!isset($this->data
['userOption'.$optionID])) return null;
246 return $this->data
['userOption'.$optionID];
250 * Gets all user options from cache.
252 protected static function getUserOptionCache() {
253 self
::$userOptions = UserOptionCacheBuilder
::getInstance()->getData(array(), 'options');
257 * Returns the id of a user option.
259 * @param string $name
262 public static function getUserOptionID($name) {
263 // get user option cache if necessary
264 if (self
::$userOptions === null) {
265 self
::getUserOptionCache();
268 if (!isset(self
::$userOptions[$name])) {
272 return self
::$userOptions[$name]->optionID
;
276 * @see \wcf\data\DatabaseObject::__get()
278 public function __get($name) {
279 $value = parent
::__get($name);
280 if ($value === null) $value = $this->getUserOption($name);
285 * Returns the user with the given username.
287 * @param string $username
288 * @return \wcf\data\user\User
290 public static function getUserByUsername($username) {
292 FROM wcf".WCF_N
."_user
294 $statement = WCF
::getDB()->prepareStatement($sql);
295 $statement->execute(array($username));
296 $row = $statement->fetchArray();
297 if (!$row) $row = array();
299 return new User(null, $row);
303 * Returns the user with the given email.
305 * @param string $email
306 * @return \wcf\data\user\User
308 public static function getUserByEmail($email) {
310 FROM wcf".WCF_N
."_user
312 $statement = WCF
::getDB()->prepareStatement($sql);
313 $statement->execute(array($email));
314 $row = $statement->fetchArray();
315 if (!$row) $row = array();
317 return new User(null, $row);
321 * Returns true if this user is marked.
325 public function isMarked() {
326 $markedUsers = WCF
::getSession()->getVar('markedUsers');
327 if ($markedUsers !== null) {
328 if (in_array($this->userID
, $markedUsers)) return 1;
335 * Returns the time zone of this user.
337 * @return DateTimeZone
339 public function getTimeZone() {
340 if ($this->timezoneObj
=== null) {
341 if ($this->timezone
) {
342 $this->timezoneObj
= new \
DateTimeZone($this->timezone
);
345 $this->timezoneObj
= new \
DateTimeZone(TIMEZONE
);
349 return $this->timezoneObj
;
353 * Returns a list of users.
355 * @param array $userIDs
356 * @return array<\wcf\data\user\User>
358 public static function getUsers(array $userIDs) {
359 $userList = new UserList();
360 $userList->getConditionBuilder()->add("user_table.userID IN (?)", array($userIDs));
361 $userList->readObjects();
363 return $userList->getObjects();
371 public function __toString() {
372 return $this->username
;
376 * @see \wcf\data\IStorableObject::getDatabaseTableAlias()
378 public static function getDatabaseTableAlias() {
383 * @see \wcf\system\request\IRouteController::getTitle()
385 public function getTitle() {
386 return $this->username
;
390 * Returns the language of this user.
392 * @return \wcf\data\language\Language
394 public function getLanguage() {
395 $language = LanguageFactory
::getInstance()->getLanguage($this->languageID
);
396 if ($language === null) {
397 $language = LanguageFactory
::getInstance()->getLanguage(LanguageFactory
::getInstance()->getDefaultLanguageID());
404 * Returns true if the active user can edit this user.
408 public function canEdit() {
409 return (WCF
::getSession()->getPermission('admin.user.canEditUser') && UserGroup
::isAccessibleGroup($this->getGroupIDs()));
413 * Returns true, if this user has access to the ACP.
417 public function hasAdministrativeAccess() {
418 if ($this->hasAdministrativePermissions
=== null) {
419 $this->hasAdministrativePermissions
= false;
422 foreach ($this->getGroupIDs() as $groupID) {
423 $group = UserGroup
::getGroupByID($groupID);
424 if ($group->isAdminGroup()) {
425 $this->hasAdministrativePermissions
= true;
432 return $this->hasAdministrativePermissions
;
436 * @see \wcf\data\IMessage::getUserID()
438 public function getUserID() {
439 return $this->userID
;
443 * @see \wcf\data\IMessage::getUsername()
445 public function getUsername() {
446 return $this->username
;
450 * @see \wcf\data\IMessage::getTime()
452 public function getTime() {
453 return $this->registrationDate
;
457 * @see \wcf\data\ILinkableObject::getLink()
459 public function getLink() {
460 return LinkHandler
::getInstance()->getLink('User', array(
461 'application' => 'wcf',
463 'forceFrontend' => true