X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=classes%2FUser.php;h=c8b334d489ab76c553f7a27e6601c1cb63c397d3;hb=94392ab00ceefec6105ac7d6e6846fb644bbd0f1;hp=7a56e0f12ee217881e57338ac46e3208d9a55c43;hpb=99312c8cc26e5494ed193154afca66693873c7e0;p=quix0rs-gnu-social.git diff --git a/classes/User.php b/classes/User.php index 7a56e0f12e..c8b334d489 100644 --- a/classes/User.php +++ b/classes/User.php @@ -17,17 +17,12 @@ * along with this program. If not, see . */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} +if (!defined('GNUSOCIAL')) { exit(1); } /** * Table Definition for user */ -require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -require_once 'Validate.php'; - class User extends Managed_DataObject { const SUBSCRIBE_POLICY_OPEN = 0; @@ -39,15 +34,14 @@ class User extends Managed_DataObject public $__table = 'user'; // table name public $id; // int(4) primary_key not_null public $nickname; // varchar(64) unique_key - public $password; // varchar(255) - public $email; // varchar(255) unique_key - public $incomingemail; // varchar(255) unique_key + public $password; // varchar(191) not 255 because utf8mb4 takes more space + public $email; // varchar(191) unique_key not 255 because utf8mb4 takes more space + public $incomingemail; // varchar(191) unique_key not 255 because utf8mb4 takes more space public $emailnotifysub; // tinyint(1) default_1 public $emailnotifyfav; // tinyint(1) default_1 public $emailnotifynudge; // tinyint(1) default_1 public $emailnotifymsg; // tinyint(1) default_1 public $emailnotifyattn; // tinyint(1) default_1 - public $emailmicroid; // tinyint(1) default_1 public $language; // varchar(50) public $timezone; // varchar(50) public $emailpost; // tinyint(1) default_1 @@ -55,12 +49,11 @@ class User extends Managed_DataObject public $carrier; // int(4) public $smsnotify; // tinyint(1) public $smsreplies; // tinyint(1) - public $smsemail; // varchar(255) - public $uri; // varchar(255) unique_key + public $smsemail; // varchar(191) not 255 because utf8mb4 takes more space + public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space public $autosubscribe; // tinyint(1) public $subscribe_policy; // tinyint(1) public $urlshorteningservice; // varchar(50) default_ur1.ca - public $inboxed; // tinyint(1) public $private_stream; // tinyint(1) default_0 public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP @@ -75,15 +68,14 @@ class User extends Managed_DataObject 'fields' => array( 'id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'), 'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname or username, duped in profile'), - 'password' => array('type' => 'varchar', 'length' => 255, 'description' => 'salted password, can be null for OpenID users'), - 'email' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for password recovery etc.'), - 'incomingemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for post-by-email'), + 'password' => array('type' => 'varchar', 'length' => 191, 'description' => 'salted password, can be null for OpenID users'), + 'email' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for password recovery etc.'), + 'incomingemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for post-by-email'), 'emailnotifysub' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of subscriptions'), - 'emailnotifyfav' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of favorites'), + 'emailnotifyfav' => array('type' => 'int', 'size' => 'tiny', 'default' => null, 'description' => 'Notify by email of favorites'), 'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'), 'emailnotifymsg' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of direct messages'), 'emailnotifyattn' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of @-replies'), - 'emailmicroid' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'whether to publish email microid'), 'language' => array('type' => 'varchar', 'length' => 50, 'description' => 'preferred language'), 'timezone' => array('type' => 'varchar', 'length' => 50, 'description' => 'timezone'), 'emailpost' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Post by email'), @@ -91,12 +83,11 @@ class User extends Managed_DataObject 'carrier' => array('type' => 'int', 'description' => 'foreign key to sms_carrier'), 'smsnotify' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to send notices to SMS'), 'smsreplies' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to send notices to SMS on replies'), - 'smsemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'built from sms and carrier'), - 'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'), + 'smsemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier'), + 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'), 'autosubscribe' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'automatically subscribe to users who subscribe to us'), 'subscribe_policy' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'), 'urlshorteningservice' => array('type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'), - 'inboxed' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'has an inbox been created for this user?'), 'private_stream' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to limit all notices to followers only'), 'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'), @@ -120,93 +111,58 @@ class User extends Managed_DataObject ); } - protected $_profile = -1; + protected $_profile = array(); /** * @return Profile + * + * @throws UserNoProfileException if user has no profile */ - function getProfile() + public function getProfile() { - if (is_int($this->_profile) && $this->_profile == -1) { // invalid but distinct from null - $this->_profile = Profile::getKV('id', $this->id); - if (empty($this->_profile)) { + if (!isset($this->_profile[$this->id])) { + $profile = Profile::getKV('id', $this->id); + if (!$profile instanceof Profile) { throw new UserNoProfileException($this); } + $this->_profile[$this->id] = $profile; } - - return $this->_profile; + return $this->_profile[$this->id]; } - function isSubscribed($other) + public function sameAs(Profile $other) { - $profile = $this->getProfile(); - return $profile->isSubscribed($other); + return $this->getProfile()->sameAs($other); } - function hasPendingSubscription($other) + public function getUri() { - $profile = $this->getProfile(); - return $profile->hasPendingSubscription($other); + return $this->uri; } - // 'update' won't write key columns, so we have to do it ourselves. + public function getNickname() + { + return $this->getProfile()->getNickname(); + } - function updateKeys(&$orig) + static function getByNickname($nickname) { - $this->_connect(); - $parts = array(); - foreach (array('nickname', 'email', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) { - if (strcmp($this->$k, $orig->$k) != 0) { - $parts[] = $k . ' = ' . $this->_quote($this->$k); - } - } - if (count($parts) == 0) { - // No changes - return true; + $user = User::getKV('nickname', $nickname); + if (!$user instanceof User) { + throw new NoSuchUserException(array('nickname' => $nickname)); } - $toupdate = implode(', ', $parts); - - $table = common_database_tablename($this->tableName()); - $qry = 'UPDATE ' . $table . ' SET ' . $toupdate . - ' WHERE id = ' . $this->id; - $orig->decache(); - $result = $this->query($qry); - if ($result) { - $this->encache(); - } - return $result; + + return $user; } - /** - * Check whether the given nickname is potentially usable, or if it's - * excluded by any blacklists on this system. - * - * WARNING: INPUT IS NOT VALIDATED OR NORMALIZED. NON-NORMALIZED INPUT - * OR INVALID INPUT MAY LEAD TO FALSE RESULTS. - * - * @param string $nickname - * @return boolean true if clear, false if blacklisted - */ - static function allowed_nickname($nickname) + function isSubscribed(Profile $other) { - // XXX: should already be validated for size, content, etc. - $blacklist = common_config('nickname', 'blacklist'); - - //all directory and file names should be blacklisted - $d = dir(INSTALLDIR); - while (false !== ($entry = $d->read())) { - $blacklist[]=$entry; - } - $d->close(); + return $this->getProfile()->isSubscribed($other); + } - //all top level names in the router should be blacklisted - $router = Router::get(); - foreach(array_keys($router->m->getPaths()) as $path){ - if(preg_match('/^\/(.*?)[\/\?]/',$path,$matches)){ - $blacklist[]=$matches[1]; - } - } - return !in_array($nickname, $blacklist); + function hasPendingSubscription(Profile $other) + { + return $this->getProfile()->hasPendingSubscription($other); } /** @@ -216,8 +172,7 @@ class User extends Managed_DataObject */ function getCurrentNotice() { - $profile = $this->getProfile(); - return $profile->getCurrentNotice(); + return $this->getProfile()->getCurrentNotice(); } function getCarrier() @@ -227,8 +182,7 @@ class User extends Managed_DataObject function hasBlocked($other) { - $profile = $this->getProfile(); - return $profile->hasBlocked($other); + return $this->getProfile()->hasBlocked($other); } /** @@ -250,7 +204,8 @@ class User extends Managed_DataObject * string 'password' (may be missing for eg OpenID registrations) * string 'code' invite code * ?string 'uri' permalink to notice; defaults to local notice URL - * @return mixed User object or false on failure + * @return User object + * @throws Exception on failure */ static function register(array $fields) { @@ -260,19 +215,14 @@ class User extends Managed_DataObject $profile = new Profile(); - if(!empty($email)) - { + if (!empty($email)) { $email = common_canonical_email($email); } - $nickname = common_canonical_nickname($nickname); - $profile->nickname = $nickname; - if(! User::allowed_nickname($nickname)){ - common_log(LOG_WARNING, sprintf("Attempted to register a nickname that is not allowed: %s", $profile->nickname), - __FILE__); - return false; - } - $profile->profileurl = common_profile_url($nickname); + // Normalize _and_ check whether it is in use. Throw NicknameException on failure. + $profile->nickname = Nickname::normalize($nickname, true); + + $profile->profileurl = common_profile_url($profile->nickname); if (!empty($fullname)) { $profile->fullname = $fullname; @@ -300,7 +250,7 @@ class User extends Managed_DataObject $user = new User(); - $user->nickname = $nickname; + $user->nickname = $profile->nickname; $invite = null; @@ -308,7 +258,7 @@ class User extends Managed_DataObject if (!empty($code)) { $invite = Invitation::getKV($code); - if ($invite && $invite->address && $invite->address_type == 'email' && $invite->address == $email) { + if ($invite instanceof Invitation && $invite->address && $invite->address_type == 'email' && $invite->address == $email) { $user->email = $invite->address; } } @@ -317,33 +267,27 @@ class User extends Managed_DataObject $user->email = $email; } - // This flag is ignored but still set to 1 - - $user->inboxed = 1; - // Set default-on options here, otherwise they'll be disabled // initially for sites using caching, since the initial encache // doesn't know about the defaults in the database. $user->emailnotifysub = 1; - $user->emailnotifyfav = 1; $user->emailnotifynudge = 1; $user->emailnotifymsg = 1; $user->emailnotifyattn = 1; - $user->emailmicroid = 1; $user->emailpost = 1; - $user->jabbermicroid = 1; $user->created = common_sql_now(); - if (Event::handle('StartUserRegister', array(&$user, &$profile))) { + if (Event::handle('StartUserRegister', array($profile))) { $profile->query('BEGIN'); $id = $profile->insert(); - - if (empty($id)) { + if ($id === false) { common_log_db_error($profile, 'INSERT', __FILE__); - return false; + $profile->query('ROLLBACK'); + // TRANS: Profile data could not be inserted for some reason. + throw new ServerException(_m('Could not insert profile data for new user.')); } $user->id = $id; @@ -355,28 +299,16 @@ class User extends Managed_DataObject } if (!empty($password)) { // may not have a password for OpenID users - $user->password = common_munge_password($password, $id); + $user->password = common_munge_password($password); } $result = $user->insert(); - if (!$result) { + if ($result === false) { common_log_db_error($user, 'INSERT', __FILE__); - return false; - } - - // Everyone gets an inbox - - $inbox = new Inbox(); - - $inbox->user_id = $user->id; - $inbox->notice_ids = ''; - - $result = $inbox->insert(); - - if (!$result) { - common_log_db_error($inbox, 'INSERT', __FILE__); - return false; + $profile->query('ROLLBACK'); + // TRANS: User data could not be inserted for some reason. + throw new ServerException(_m('Could not insert user data for new user.')); } // Everyone is subscribed to themself @@ -390,7 +322,9 @@ class User extends Managed_DataObject if (!$result) { common_log_db_error($subscription, 'INSERT', __FILE__); - return false; + $profile->query('ROLLBACK'); + // TRANS: Subscription data could not be inserted for some reason. + throw new ServerException(_m('Could not insert subscription data for new user.')); } // Mark that this invite was converted @@ -411,7 +345,9 @@ class User extends Managed_DataObject if (!$result) { common_log_db_error($confirm, 'INSERT', __FILE__); - return false; + $profile->query('ROLLBACK'); + // TRANS: Email confirmation data could not be inserted for some reason. + throw new ServerException(_m('Could not insert email confirmation data for new user.')); } } @@ -429,7 +365,7 @@ class User extends Managed_DataObject common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick), __FILE__); } else { - Subscription::start($profile, $defuser->getProfile()); + Subscription::ensureStart($profile, $defuser->getProfile()); } } @@ -459,7 +395,11 @@ class User extends Managed_DataObject } } - Event::handle('EndUserRegister', array(&$profile, &$user)); + Event::handle('EndUserRegister', array($profile)); + } + + if (!$user instanceof User) { + throw new ServerException('User could not be registered. Probably an event hook that failed.'); } return $user; @@ -475,22 +415,22 @@ class User extends Managed_DataObject if ($invites->find()) { while ($invites->fetch()) { - $other = User::getKV($invites->user_id); - subs_subscribe_to($other, $this); + try { + $other = Profile::getKV('id', $invites->user_id); + if (!($other instanceof Profile)) { // remove when getKV throws exceptions + continue; + } + Subscription::start($other, $this->getProfile()); + } catch (Exception $e) { + continue; + } } } } - function hasFave($notice) - { - $profile = $this->getProfile(); - return $profile->hasFave($notice); - } - - function mutuallySubscribed($other) + function mutuallySubscribed(Profile $other) { - $profile = $this->getProfile(); - return $profile->mutuallySubscribed($other); + return $this->getProfile()->mutuallySubscribed($other); } function mutuallySubscribedUsers() @@ -510,80 +450,16 @@ class User extends Managed_DataObject function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { - return Reply::stream($this->id, $offset, $limit, $since_id, $before_id); + return $this->getProfile()->getReplies($offset, $limit, $since_id, $before_id); } function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { - $profile = $this->getProfile(); - return $profile->getTaggedNotices($tag, $offset, $limit, $since_id, $before_id); + return $this->getProfile()->getTaggedNotices($tag, $offset, $limit, $since_id, $before_id); } function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { - $profile = $this->getProfile(); - return $profile->getNotices($offset, $limit, $since_id, $before_id); - } - - function favoriteNotices($own=false, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0) - { - return Fave::stream($this->id, $offset, $limit, $own, $since_id, $max_id); - } - - function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) - { - $stream = new InboxNoticeStream($this); - return $stream->getNotices($offset, $limit, $since_id, $before_id); - } - - // DEPRECATED, use noticeInbox() - - function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) - { - return $this->noticeInbox($offset, $limit, $since_id, $before_id); - } - - // DEPRECATED, use noticeInbox() - - function noticesWithFriendsThreaded($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) - { - return $this->noticeInbox($offset, $limit, $since_id, $before_id); - } - - // DEPRECATED, use noticeInbox() - - function noticeInboxThreaded($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) - { - return $this->noticeInbox($offset, $limit, $since_id, $before_id); - } - - // DEPRECATED, use noticeInbox() - - function friendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) - { - return $this->noticeInbox($offset, $limit, $since_id, $before_id); - } - - // DEPRECATED, use noticeInbox() - - function ownFriendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) - { - $this->noticeInbox($offset, $limit, $since_id, $before_id); - } - - function blowFavesCache() - { - $profile = $this->getProfile(); - $profile->blowFavesCache(); - } - - function getSelfTags() - { - return Profile_tag::getTagsArray($this->id, $this->id, $this->id); - } - - function setSelfTags($newtags, $privacy) - { - return Profile_tag::setTags($this->id, $this->id, $newtags, $privacy); + return $this->getProfile()->getNotices($offset, $limit, $since_id, $before_id); } function block(Profile $other) @@ -652,22 +528,19 @@ class User extends Managed_DataObject return true; } - function isMember($group) + function isMember(User_group $group) { - $profile = $this->getProfile(); - return $profile->isMember($group); + return $this->getProfile()->isMember($group); } - function isAdmin($group) + function isAdmin(User_group $group) { - $profile = $this->getProfile(); - return $profile->isAdmin($group); + return $this->getProfile()->isAdmin($group); } function getGroups($offset=0, $limit=null) { - $profile = $this->getProfile(); - return $profile->getGroups($offset, $limit); + return $this->getProfile()->getGroups($offset, $limit); } /** @@ -679,8 +552,7 @@ class User extends Managed_DataObject */ function joinGroup(User_group $group) { - $profile = $this->getProfile(); - return $profile->joinGroup($group); + return $this->getProfile()->joinGroup($group); } /** @@ -690,84 +562,51 @@ class User extends Managed_DataObject */ function leaveGroup(User_group $group) { - $profile = $this->getProfile(); - return $profile->leaveGroup($group); + return $this->getProfile()->leaveGroup($group); } - function getSubscriptions($offset=0, $limit=null) + function getSubscribed($offset=0, $limit=null) { - $profile = $this->getProfile(); - return $profile->getSubscriptions($offset, $limit); + return $this->getProfile()->getSubscribed($offset, $limit); } function getSubscribers($offset=0, $limit=null) { - $profile = $this->getProfile(); - return $profile->getSubscribers($offset, $limit); + return $this->getProfile()->getSubscribers($offset, $limit); } function getTaggedSubscribers($tag, $offset=0, $limit=null) { - $qry = - 'SELECT profile.* ' . - 'FROM profile JOIN subscription ' . - 'ON profile.id = subscription.subscriber ' . - 'JOIN profile_tag ON (profile_tag.tagged = subscription.subscriber ' . - 'AND profile_tag.tagger = subscription.subscribed) ' . - 'WHERE subscription.subscribed = %d ' . - "AND profile_tag.tag = '%s' " . - 'AND subscription.subscribed != subscription.subscriber ' . - 'ORDER BY subscription.created DESC '; - - if ($offset) { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } - - $profile = new Profile(); - - $cnt = $profile->query(sprintf($qry, $this->id, $profile->escape($tag))); - - return $profile; + return $this->getProfile()->getTaggedSubscribers($tag, $offset, $limit); } function getTaggedSubscriptions($tag, $offset=0, $limit=null) { - $qry = - 'SELECT profile.* ' . - 'FROM profile JOIN subscription ' . - 'ON profile.id = subscription.subscribed ' . - 'JOIN profile_tag on (profile_tag.tagged = subscription.subscribed ' . - 'AND profile_tag.tagger = subscription.subscriber) ' . - 'WHERE subscription.subscriber = %d ' . - "AND profile_tag.tag = '%s' " . - 'AND subscription.subscribed != subscription.subscriber ' . - 'ORDER BY subscription.created DESC '; - - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - - $profile = new Profile(); - - $profile->query(sprintf($qry, $this->id, $profile->escape($tag))); - - return $profile; + return $this->getProfile()->getTaggedSubscriptions($tag, $offset, $limit); } function hasRight($right) { - $profile = $this->getProfile(); - return $profile->hasRight($right); + return $this->getProfile()->hasRight($right); } - function delete() + function delete($useWhere=false) { + if (empty($this->id)) { + common_log(LOG_WARNING, "Ambiguous User->delete(); skipping related tables."); + return parent::delete($useWhere); + } + try { - $profile = $this->getProfile(); - $profile->delete(); + if (!$this->hasRole(Profile_role::DELETED)) { + $profile = $this->getProfile(); + $profile->delete(); + } } catch (UserNoProfileException $unp) { common_log(LOG_INFO, "User {$this->nickname} has no profile; continuing deletion."); } - $related = array('Fave', + $related = array( 'Confirm_address', 'Remember_me', 'Foreign_link', @@ -785,7 +624,7 @@ class User extends Managed_DataObject $this->_deleteTags(); $this->_deleteBlocks(); - parent::delete(); + return parent::delete($useWhere); } function _deleteTags() @@ -805,32 +644,42 @@ class User extends Managed_DataObject function hasRole($name) { - $profile = $this->getProfile(); - return $profile->hasRole($name); + return $this->getProfile()->hasRole($name); } function grantRole($name) { - $profile = $this->getProfile(); - return $profile->grantRole($name); + return $this->getProfile()->grantRole($name); } function revokeRole($name) { - $profile = $this->getProfile(); - return $profile->revokeRole($name); + return $this->getProfile()->revokeRole($name); } function isSandboxed() { - $profile = $this->getProfile(); - return $profile->isSandboxed(); + return $this->getProfile()->isSandboxed(); } function isSilenced() { - $profile = $this->getProfile(); - return $profile->isSilenced(); + return $this->getProfile()->isSilenced(); + } + + function receivesEmailNotifications() + { + // We could do this in one large if statement, but that's not as easy to read + // Don't send notifications if we don't know the user's email address or it is + // explicitly undesired by the user's own settings. + if (empty($this->email) || !$this->emailnotifyattn) { + return false; + } + // Don't send notifications to a user who is sandboxed or silenced + if ($this->isSandboxed() || $this->isSilenced()) { + return false; + } + return true; } function repeatedByMe($offset=0, $limit=20, $since_id=null, $max_id=null) @@ -847,60 +696,36 @@ class User extends Managed_DataObject return $stream->getNotices($offset, $limit, $since_id, $max_id); } - - function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null) + public function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null) { - // TRANS: Exception thrown when trying view "repeated to me". - throw new Exception(_('Not implemented since inbox change.')); + return $this->getProfile()->repeatedToMe($offset, $limit, $since_id, $max_id); } - function shareLocation() - { - $cfg = common_config('location', 'share'); - - if ($cfg == 'always') { - return true; - } else if ($cfg == 'never') { - return false; - } else { // user - $share = common_config('location', 'sharedefault'); - - // Check if user has a personal setting for this - $prefs = User_location_prefs::getKV('user_id', $this->id); - - if (!empty($prefs)) { - $share = $prefs->share_location; - $prefs->free(); - } - - return $share; - } - } - - static function siteOwner() + public static function siteOwner() { $owner = self::cacheGet('user:site_owner'); if ($owner === false) { // cache miss $pr = new Profile_role(); - $pr->role = Profile_role::OWNER; - $pr->orderBy('created'); - $pr->limit(1); - if ($pr->find(true)) { - $owner = User::getKV('id', $pr->profile_id); - } else { - $owner = null; + if (!$pr->find(true)) { + throw new NoResultException($pr); } + $owner = User::getKV('id', $pr->profile_id); + self::cacheSet('user:site_owner', $owner); } - return $owner; + if ($owner instanceof User) { + return $owner; + } + + throw new ServerException(_('No site owner configured.')); } /** @@ -913,35 +738,23 @@ class User extends Managed_DataObject * @throws ServerException if no valid single user account is present * @throws ServerException if called when not in single-user mode */ - static function singleUser() + public static function singleUser() { - if (common_config('singleuser', 'enabled')) { - - $user = null; - - $nickname = common_config('singleuser', 'nickname'); - - if (!empty($nickname)) { - $user = User::getKV('nickname', $nickname); - } - - // if there was no nickname or no user by that nickname, - // try the site owner. - - if (empty($user)) { - $user = User::siteOwner(); - } + if (!common_config('singleuser', 'enabled')) { + // TRANS: Server exception. + throw new ServerException(_('Single-user mode code called when not enabled.')); + } - if (!empty($user)) { + if ($nickname = common_config('singleuser', 'nickname')) { + $user = User::getKV('nickname', $nickname); + if ($user instanceof User) { return $user; - } else { - // TRANS: Server exception. - throw new ServerException(_('No single user defined for single-user mode.')); } - } else { - // TRANS: Server exception. - throw new ServerException(_('Single-user mode code called when not enabled.')); } + + // If there was no nickname or no user by that nickname, + // try the site owner. Throws exception if not configured. + return User::siteOwner(); } /** @@ -1043,57 +856,59 @@ class User extends Managed_DataObject static function recoverPassword($nore) { - $user = User::getKV('email', common_canonical_email($nore)); - - if (!$user) { - try { - $user = User::getKV('nickname', common_canonical_nickname($nore)); - } catch (NicknameException $e) { - // invalid + // $confirm_email will be used as a fallback if our user doesn't have a confirmed email + $confirm_email = null; + + if (common_is_email($nore)) { + $user = User::getKV('email', common_canonical_email($nore)); + + // See if it's an unconfirmed email address + if (!$user instanceof User) { + // Warning: it may actually be legit to have multiple folks + // who have claimed, but not yet confirmed, the same address. + // We'll only send to the first one that comes up. + $confirm_email = new Confirm_address(); + $confirm_email->address = common_canonical_email($nore); + $confirm_email->address_type = 'email'; + if ($confirm_email->find(true)) { + $user = User::getKV('id', $confirm_email->user_id); + } } - } - // See if it's an unconfirmed email address - - if (!$user) { - // Warning: it may actually be legit to have multiple folks - // who have claimed, but not yet confirmed, the same address. - // We'll only send to the first one that comes up. - $confirm_email = new Confirm_address(); - $confirm_email->address = common_canonical_email($nore); - $confirm_email->address_type = 'email'; - $confirm_email->find(); - if ($confirm_email->fetch()) { - $user = User::getKV($confirm_email->user_id); - } else { - $confirm_email = null; + // No luck finding anyone by that email address. + if (!$user instanceof User) { + if (common_config('site', 'fakeaddressrecovery')) { + // Return without actually doing anything! We fake address recovery + // to avoid revealing which email addresses are registered with the site. + return; + } + // TRANS: Information on password recovery form if no known e-mail address was specified. + throw new ClientException(_('No user with that email address exists here.')); } } else { - $confirm_email = null; - } - - if (!$user) { - // TRANS: Information on password recovery form if no known username or e-mail address was specified. - throw new ClientException(_('No user with that email address or username.')); - return; + // This might throw a NicknameException on bad nicknames + $user = User::getKV('nickname', common_canonical_nickname($nore)); + if (!$user instanceof User) { + // TRANS: Information on password recovery form if no known username was specified. + throw new ClientException(_('No user with that nickname exists here.')); + } } // Try to get an unconfirmed email address if they used a user name - - if (!$user->email && !$confirm_email) { + if (empty($user->email) && $confirm_email === null) { $confirm_email = new Confirm_address(); $confirm_email->user_id = $user->id; $confirm_email->address_type = 'email'; $confirm_email->find(); if (!$confirm_email->fetch()) { + // Nothing found, so let's reset it to null $confirm_email = null; } } - if (!$user->email && !$confirm_email) { + if (empty($user->email) && !$confirm_email instanceof Confirm_address) { // TRANS: Client error displayed on password recovery form if a user does not have a registered e-mail address. throw new ClientException(_('No registered email address for that user.')); - return; } // Success! We have a valid user and a confirmed or unconfirmed email address @@ -1102,13 +917,12 @@ class User extends Managed_DataObject $confirm->code = common_confirmation_code(128); $confirm->address_type = 'recover'; $confirm->user_id = $user->id; - $confirm->address = (!empty($user->email)) ? $user->email : $confirm_email->address; + $confirm->address = $user->email ?: $confirm_email->address; if (!$confirm->insert()) { common_log_db_error($confirm, 'INSERT', __FILE__); // TRANS: Server error displayed if e-mail address confirmation fails in the database on the password recovery form. throw new ServerException(_('Error saving address confirmation.')); - return; } // @todo FIXME: needs i18n. @@ -1146,18 +960,6 @@ class User extends Managed_DataObject return false; } - function conversationTree() - { - if (common_config('oldschool', 'enabled')) { - $osp = Old_school_prefs::getKV('user_id', $this->id); - if (!empty($osp)) { - return $osp->conversation_tree; - } - } - - return false; - } - function streamNicknames() { if (common_config('oldschool', 'enabled')) { @@ -1182,7 +984,7 @@ class User extends Managed_DataObject $act = new Activity(); - $act->actor = ActivityObject::fromProfile($profile); + $act->actor = $profile->asActivityObject(); $act->verb = ActivityVerb::JOIN; $act->objects[] = $service; @@ -1199,4 +1001,52 @@ class User extends Managed_DataObject $service->title); return $act; } + + public function isPrivateStream() + { + return $this->getProfile()->isPrivateStream(); + } + + public function hasPassword() + { + return !empty($this->password); + } + + public function setPassword($password) + { + $orig = clone($this); + $this->password = common_munge_password($password, $this->getProfile()); + + if ($this->validate() !== true) { + // TRANS: Form validation error on page where to change password. + throw new ServerException(_('Error saving user; invalid.')); + } + + if (!$this->update($orig)) { + common_log_db_error($this, 'UPDATE', __FILE__); + // TRANS: Server error displayed on page where to change password when password change + // TRANS: could not be made because of a server error. + throw new ServerException(_('Cannot save new password.')); + } + } + + public function delPref($namespace, $topic) + { + return $this->getProfile()->delPref($namespace, $topic); + } + + public function getPref($namespace, $topic, $default=null) + { + return $this->getProfile()->getPref($namespace, $topic, $default); + } + + public function getConfigPref($namespace, $topic) + { + return $this->getProfile()->getConfigPref($namespace, $topic); + } + + public function setPref($namespace, $topic, $data) + { + return $this->getProfile()->setPref($namespace, $topic, $data); + } }