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\system\cache\builder\UserOptionCacheBuilder
;
7 use wcf\system\language\LanguageFactory
;
8 use wcf\system\request\IRouteController
;
9 use wcf\system\user\storage\UserStorageHandler
;
11 use wcf\util\PasswordUtil
;
16 * @author Alexander Ebert
17 * @copyright 2001-2014 WoltLab GmbH
18 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
19 * @package com.woltlab.wcf
20 * @subpackage data.user
21 * @category Community Framework
23 final class User
extends DatabaseObject
implements IRouteController
{
25 * @see \wcf\data\DatabaseObject::$databaseTableName
27 protected static $databaseTableName = 'user';
30 * @see \wcf\data\DatabaseObject::$databaseTableIndexName
32 protected static $databaseTableIndexName = 'userID';
38 protected $groupIDs = null;
41 * true, if user has access to the ACP
44 protected $hasAdministrativePermissions = null;
47 * list of language ids
50 protected $languageIDs = null;
53 * date time zone object
56 protected $timezoneObj = null;
59 * list of user options
62 protected static $userOptions = null;
65 * @see \wcf\data\DatabaseObject::__construct()
67 public function __construct($id, $row = null, DatabaseObject
$object = null) {
69 $sql = "SELECT user_option_value.*, user_table.*
70 FROM wcf".WCF_N
."_user user_table
71 LEFT JOIN wcf".WCF_N
."_user_option_value user_option_value
72 ON (user_option_value.userID = user_table.userID)
73 WHERE user_table.userID = ?";
74 $statement = WCF
::getDB()->prepareStatement($sql);
75 $statement->execute(array($id));
76 $row = $statement->fetchArray();
78 // enforce data type 'array'
79 if ($row === false) $row = array();
81 else if ($object !== null) {
85 $this->handleData($row);
89 * Returns true if the given password is the correct password for this user.
91 * @param string $password
92 * @return boolean password correct
94 public function checkPassword($password) {
98 // check if password is a valid bcrypt hash
99 if (PasswordUtil
::isBlowfish($this->password
)) {
100 if (PasswordUtil
::isDifferentBlowfish($this->password
)) {
104 // password is correct
105 if (PasswordUtil
::secureCompare($this->password
, PasswordUtil
::getDoubleSaltedHash($password, $this->password
))) {
110 // different encryption type
111 if (PasswordUtil
::checkPassword($this->username
, $password, $this->password
)) {
117 // create new password hash, either different encryption or different blowfish cost factor
118 if ($rebuild && $isValid) {
119 $userEditor = new UserEditor($this);
120 $userEditor->update(array(
121 'password' => $password
129 * Returns true if the given password hash from a cookie is the correct password for this user.
131 * @param string $passwordHash
132 * @return boolean password correct
134 public function checkCookiePassword($passwordHash) {
135 if (PasswordUtil
::isBlowfish($this->password
) && PasswordUtil
::secureCompare($this->password
, PasswordUtil
::getSaltedHash($passwordHash, $this->password
))) {
143 * Returns an array with all the groups in which the actual user is a member.
145 * @param boolean $skipCache
146 * @return array $groupIDs
148 public function getGroupIDs($skipCache = false) {
149 if ($this->groupIDs
=== null ||
$skipCache) {
150 if (!$this->userID
) {
151 // user is a guest, use default guest group
152 $this->groupIDs
= UserGroup
::getGroupIDsByType(array(UserGroup
::GUESTS
, UserGroup
::EVERYONE
));
156 UserStorageHandler
::getInstance()->loadStorage(array($this->userID
));
159 $data = UserStorageHandler
::getInstance()->getStorage(array($this->userID
), 'groupIDs');
161 // cache does not exist or is outdated
162 if ($data[$this->userID
] === null ||
$skipCache) {
163 $this->groupIDs
= array();
164 $sql = "SELECT groupID
165 FROM wcf".WCF_N
."_user_to_group
167 $statement = WCF
::getDB()->prepareStatement($sql);
168 $statement->execute(array($this->userID
));
169 while ($row = $statement->fetchArray()) {
170 $this->groupIDs
[] = $row['groupID'];
173 // update storage data
175 UserStorageHandler
::getInstance()->update($this->userID
, 'groupIDs', serialize($this->groupIDs
));
179 $this->groupIDs
= unserialize($data[$this->userID
]);
183 sort($this->groupIDs
, SORT_NUMERIC
);
186 return $this->groupIDs
;
190 * Returns a list of language ids for this user.
192 * @return array<integer>
194 public function getLanguageIDs() {
195 if ($this->languageIDs
=== null) {
196 $this->languageIDs
= array();
200 UserStorageHandler
::getInstance()->loadStorage(array($this->userID
));
203 $data = UserStorageHandler
::getInstance()->getStorage(array($this->userID
), 'languageIDs');
205 // cache does not exist or is outdated
206 if ($data[$this->userID
] === null) {
207 $sql = "SELECT languageID
208 FROM wcf".WCF_N
."_user_to_language
210 $statement = WCF
::getDB()->prepareStatement($sql);
211 $statement->execute(array($this->userID
));
212 while ($row = $statement->fetchArray()) {
213 $this->languageIDs
[] = $row['languageID'];
216 // update storage data
217 UserStorageHandler
::getInstance()->update($this->userID
, 'languageIDs', serialize($this->languageIDs
));
220 $this->languageIDs
= unserialize($data[$this->userID
]);
223 else if (!WCF
::getSession()->spiderID
) {
224 $this->languageIDs
[] = WCF
::getLanguage()->languageID
;
228 return $this->languageIDs
;
232 * Returns the value of the user option with the given name.
234 * @param string $name user option name
235 * @return mixed user option value
237 public function getUserOption($name) {
238 $optionID = self
::getUserOptionID($name);
239 if ($optionID === null) {
243 if (!isset($this->data
['userOption'.$optionID])) return null;
244 return $this->data
['userOption'.$optionID];
248 * Gets all user options from cache.
250 protected static function getUserOptionCache() {
251 self
::$userOptions = UserOptionCacheBuilder
::getInstance()->getData(array(), 'options');
255 * Returns the id of a user option.
257 * @param string $name
260 public static function getUserOptionID($name) {
261 // get user option cache if necessary
262 if (self
::$userOptions === null) {
263 self
::getUserOptionCache();
266 if (!isset(self
::$userOptions[$name])) {
270 return self
::$userOptions[$name]->optionID
;
274 * @see \wcf\data\DatabaseObject::__get()
276 public function __get($name) {
277 $value = parent
::__get($name);
278 if ($value === null) $value = $this->getUserOption($name);
283 * Returns the user with the given username.
285 * @param string $username
286 * @return \wcf\data\user\User
288 public static function getUserByUsername($username) {
290 FROM wcf".WCF_N
."_user
292 $statement = WCF
::getDB()->prepareStatement($sql);
293 $statement->execute(array($username));
294 $row = $statement->fetchArray();
295 if (!$row) $row = array();
297 return new User(null, $row);
301 * Returns the user with the given email.
303 * @param string $email
304 * @return \wcf\data\user\User
306 public static function getUserByEmail($email) {
308 FROM wcf".WCF_N
."_user
310 $statement = WCF
::getDB()->prepareStatement($sql);
311 $statement->execute(array($email));
312 $row = $statement->fetchArray();
313 if (!$row) $row = array();
315 return new User(null, $row);
319 * Returns true if this user is marked.
323 public function isMarked() {
324 $markedUsers = WCF
::getSession()->getVar('markedUsers');
325 if ($markedUsers !== null) {
326 if (in_array($this->userID
, $markedUsers)) return 1;
333 * Returns the time zone of this user.
335 * @return DateTimeZone
337 public function getTimeZone() {
338 if ($this->timezoneObj
=== null) {
339 if ($this->timezone
) {
340 $this->timezoneObj
= new \
DateTimeZone($this->timezone
);
343 $this->timezoneObj
= new \
DateTimeZone(TIMEZONE
);
347 return $this->timezoneObj
;
351 * Returns a list of users.
353 * @param array $userIDs
354 * @return array<\wcf\data\user\User>
356 public static function getUsers(array $userIDs) {
357 $userList = new UserList();
358 $userList->getConditionBuilder()->add("user_table.userID IN (?)", array($userIDs));
359 $userList->readObjects();
361 return $userList->getObjects();
369 public function __toString() {
370 return $this->username
;
374 * @see \wcf\data\IStorableObject::getDatabaseTableAlias()
376 public static function getDatabaseTableAlias() {
381 * @see \wcf\system\request\IRouteController::getTitle()
383 public function getTitle() {
384 return $this->username
;
388 * Returns the language of this user.
390 * @return \wcf\data\language\Language
392 public function getLanguage() {
393 $language = LanguageFactory
::getInstance()->getLanguage($this->languageID
);
394 if ($language === null) {
395 $language = LanguageFactory
::getInstance()->getLanguage(LanguageFactory
::getInstance()->getDefaultLanguageID());
402 * Returns true if the active user can edit this user.
406 public function canEdit() {
407 return (WCF
::getSession()->getPermission('admin.user.canEditUser') && UserGroup
::isAccessibleGroup($this->getGroupIDs()));
411 * Returns true, if this user has access to the ACP.
415 public function hasAdministrativeAccess() {
416 if ($this->hasAdministrativePermissions
=== null) {
417 $this->hasAdministrativePermissions
= false;
420 foreach ($this->getGroupIDs() as $groupID) {
421 $group = UserGroup
::getGroupByID($groupID);
422 if ($group->isAdminGroup()) {
423 $this->hasAdministrativePermissions
= true;
430 return $this->hasAdministrativePermissions
;