X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=classes%2FUser.php;h=8033229c4b74553ce0279f65fa896851f96b86d3;hb=08fc6053ec55e911b842fd05dafc5e0c99c4e992;hp=7267ec2d793a3788b8ab8c1ea68ca64cf9032af3;hpb=691beefd0f3755bab195279b1c0d7cc583942b72;p=quix0rs-gnu-social.git diff --git a/classes/User.php b/classes/User.php index 7267ec2d79..8033229c4b 100644 --- a/classes/User.php +++ b/classes/User.php @@ -75,22 +75,23 @@ class User extends Memcached_DataObject function getProfile() { - return Profile::staticGet('id', $this->id); + $profile = Profile::staticGet('id', $this->id); + if (empty($profile)) { + throw new UserNoProfileException($this); + } + return $profile; } function isSubscribed($other) { - assert(!is_null($other)); - // XXX: cache results of this query - $sub = Subscription::pkeyGet(array('subscriber' => $this->id, - 'subscribed' => $other->id)); - return (is_null($sub)) ? false : true; + return Subscription::exists($this->getProfile(), $other); } - // 'update' will not write key columns, so we have to do it ourselves. + // 'update' won't write key columns, so we have to do it ourselves. function updateKeys(&$orig) { + $this->_connect(); $parts = array(); foreach (array('nickname', 'email', 'jabber', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) { if (strcmp($this->$k, $orig->$k) != 0) { @@ -114,7 +115,7 @@ class User extends Memcached_DataObject return $result; } - function allowed_nickname($nickname) + static function allowed_nickname($nickname) { // XXX: should already be validated for size, content, etc. $blacklist = common_config('nickname', 'blacklist'); @@ -136,13 +137,15 @@ class User extends Memcached_DataObject return !in_array($nickname, $blacklist); } - function getCurrentNotice($dt=null) + /** + * Get the most recent notice posted by this user, if any. + * + * @return mixed Notice or null + */ + function getCurrentNotice() { $profile = $this->getProfile(); - if (!$profile) { - return null; - } - return $profile->getCurrentNotice($dt); + return $profile->getCurrentNotice(); } function getCarrier() @@ -150,36 +153,41 @@ class User extends Memcached_DataObject return Sms_carrier::staticGet('id', $this->carrier); } + /** + * @deprecated use Subscription::start($sub, $other); + */ function subscribeTo($other) { - $sub = new Subscription(); - $sub->subscriber = $this->id; - $sub->subscribed = $other->id; - - $sub->created = common_sql_now(); // current time - - if (!$sub->insert()) { - return false; - } - - return true; + return Subscription::start($this->getProfile(), $other); } function hasBlocked($other) { - - $block = Profile_block::get($this->id, $other->id); - - if (is_null($block)) { - $result = false; - } else { - $result = true; - $block->free(); - } - - return $result; + $profile = $this->getProfile(); + return $profile->hasBlocked($other); } + /** + * Register a new user account and profile and set up default subscriptions. + * If a new-user welcome message is configured, this will be sent. + * + * @param array $fields associative array of optional properties + * string 'bio' + * string 'email' + * bool 'email_confirmed' pass true to mark email as pre-confirmed + * string 'fullname' + * string 'homepage' + * string 'location' informal string description of geolocation + * float 'lat' decimal latitude for geolocation + * float 'lon' decimal longitude for geolocation + * int 'location_id' geoname identifier + * int 'location_ns' geoname namespace to interpret location_id + * string 'nickname' REQUIRED + * 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 + */ static function register($fields) { // MAGICALLY put fields into current scope @@ -188,9 +196,18 @@ class User extends Memcached_DataObject $profile = new Profile(); - $profile->query('BEGIN'); + 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); if (!empty($fullname)) { @@ -217,22 +234,10 @@ class User extends Memcached_DataObject $profile->created = common_sql_now(); - $id = $profile->insert(); - - if (empty($id)) { - common_log_db_error($profile, 'INSERT', __FILE__); - return false; - } - $user = new User(); - $user->id = $id; $user->nickname = $nickname; - if (!empty($password)) { // may not have a password for OpenID users - $user->password = common_munge_password($password, $id); - } - // Users who respond to invite email have proven their ownership of that address if (!empty($code)) { @@ -242,107 +247,135 @@ class User extends Memcached_DataObject } } + if(isset($email_confirmed) && $email_confirmed) { + $user->email = $email; + } + // This flag is ignored but still set to 1 $user->inboxed = 1; $user->created = common_sql_now(); - $user->uri = common_user_uri($user); - $result = $user->insert(); + if (Event::handle('StartUserRegister', array(&$user, &$profile))) { - if (!$result) { - common_log_db_error($user, 'INSERT', __FILE__); - return false; - } + $profile->query('BEGIN'); - // Everyone is subscribed to themself + $id = $profile->insert(); - $subscription = new Subscription(); - $subscription->subscriber = $user->id; - $subscription->subscribed = $user->id; - $subscription->created = $user->created; + if (empty($id)) { + common_log_db_error($profile, 'INSERT', __FILE__); + return false; + } - $result = $subscription->insert(); + $user->id = $id; + $user->uri = common_user_uri($user); + if (!empty($password)) { // may not have a password for OpenID users + $user->password = common_munge_password($password, $id); + } - if (!$result) { - common_log_db_error($subscription, 'INSERT', __FILE__); - return false; - } + $result = $user->insert(); - if (!empty($email) && !$user->email) { + if (!$result) { + common_log_db_error($user, 'INSERT', __FILE__); + return false; + } + + // Everyone gets an inbox + + $inbox = new Inbox(); - $confirm = new Confirm_address(); - $confirm->code = common_confirmation_code(128); - $confirm->user_id = $user->id; - $confirm->address = $email; - $confirm->address_type = 'email'; + $inbox->user_id = $user->id; + $inbox->notice_ids = ''; + + $result = $inbox->insert(); - $result = $confirm->insert(); if (!$result) { - common_log_db_error($confirm, 'INSERT', __FILE__); + common_log_db_error($inbox, 'INSERT', __FILE__); return false; } - } - if (!empty($code) && $user->email) { - $user->emailChanged(); - } + // Everyone is subscribed to themself - // Default system subscription + $subscription = new Subscription(); + $subscription->subscriber = $user->id; + $subscription->subscribed = $user->id; + $subscription->created = $user->created; - $defnick = common_config('newuser', 'default'); + $result = $subscription->insert(); - if (!empty($defnick)) { - $defuser = User::staticGet('nickname', $defnick); - if (empty($defuser)) { - common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick), - __FILE__); - } else { - $defsub = new Subscription(); - $defsub->subscriber = $user->id; - $defsub->subscribed = $defuser->id; - $defsub->created = $user->created; + if (!$result) { + common_log_db_error($subscription, 'INSERT', __FILE__); + return false; + } - $result = $defsub->insert(); + if (!empty($email) && !$user->email) { + + $confirm = new Confirm_address(); + $confirm->code = common_confirmation_code(128); + $confirm->user_id = $user->id; + $confirm->address = $email; + $confirm->address_type = 'email'; + + $result = $confirm->insert(); if (!$result) { - common_log_db_error($defsub, 'INSERT', __FILE__); + common_log_db_error($confirm, 'INSERT', __FILE__); return false; } } - } - $profile->query('COMMIT'); + if (!empty($code) && $user->email) { + $user->emailChanged(); + } - if ($email && !$user->email) { - mail_confirm_address($user, $confirm->code, $profile->nickname, $email); - } + // Default system subscription - // Welcome message + $defnick = common_config('newuser', 'default'); + + if (!empty($defnick)) { + $defuser = User::staticGet('nickname', $defnick); + if (empty($defuser)) { + common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick), + __FILE__); + } else { + Subscription::start($user, $defuser); + } + } - $welcome = common_config('newuser', 'welcome'); + $profile->query('COMMIT'); - if (!empty($welcome)) { - $welcomeuser = User::staticGet('nickname', $welcome); - if (empty($welcomeuser)) { - common_log(LOG_WARNING, sprintf("Welcome user %s does not exist.", $defnick), - __FILE__); - } else { - $notice = Notice::saveNew($welcomeuser->id, - sprintf(_('Welcome to %1$s, @%2$s!'), - common_config('site', 'name'), - $user->nickname), - 'system'); - common_broadcast_notice($notice); + if (!empty($email) && !$user->email) { + mail_confirm_address($user, $confirm->code, $profile->nickname, $email); } + + // Welcome message + + $welcome = common_config('newuser', 'welcome'); + + if (!empty($welcome)) { + $welcomeuser = User::staticGet('nickname', $welcome); + if (empty($welcomeuser)) { + common_log(LOG_WARNING, sprintf("Welcome user %s does not exist.", $defnick), + __FILE__); + } else { + $notice = Notice::saveNew($welcomeuser->id, + // TRANS: Notice given on user registration. + // TRANS: %1$s is the sitename, $2$s is the registering user's nickname. + sprintf(_('Welcome to %1$s, @%2$s!'), + common_config('site', 'name'), + $user->nickname), + 'system'); + } + } + + Event::handle('EndUserRegister', array(&$profile, &$user)); } return $user; } // Things we do when the email changes - function emailChanged() { @@ -384,7 +417,7 @@ class User extends Memcached_DataObject return false; } - // Otherwise, cache does not have all faves; + // Otherwise, cache doesn't have all faves; // fall through to the default } @@ -414,56 +447,54 @@ class User extends Memcached_DataObject return $user; } - function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { - $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id, $since); + $ids = Reply::stream($this->id, $offset, $limit, $since_id, $before_id); return Notice::getStreamByIds($ids); } - function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) { + function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { $profile = $this->getProfile(); - if (!$profile) { - return null; - } else { - return $profile->getTaggedNotices($tag, $offset, $limit, $since_id, $before_id, $since); - } + return $profile->getTaggedNotices($tag, $offset, $limit, $since_id, $before_id); } - function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { $profile = $this->getProfile(); - if (!$profile) { - return null; - } else { - return $profile->getNotices($offset, $limit, $since_id, $before_id, $since); - } + return $profile->getNotices($offset, $limit, $since_id, $before_id); } - function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE, $own=false) + function favoriteNotices($own=false, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0) { - $ids = Fave::stream($this->id, $offset, $limit, $own); + $ids = Fave::stream($this->id, $offset, $limit, $own, $since_id, $max_id); return Notice::getStreamByIds($ids); } - function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { - $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false); + return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, false); + } - return Notice::getStreamByIds($ids); + function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) + { + return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, true); } - function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + function friendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { - $ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true); + return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, false); + } - return Notice::getStreamByIds($ids); + function ownFriendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) + { + return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, true); } function blowFavesCache() { $cache = common_memcache(); if ($cache) { - // Faves do not happen chronologically, so we need to blow + // Faves don't happen chronologically, so we need to blow // ;last cache, too $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id)); $cache->delete(common_cache_key('fave:ids_by_user:'.$this->id.';last')); @@ -488,6 +519,19 @@ class User extends Memcached_DataObject { // Add a new block record + // no blocking (and thus unsubbing from) yourself + + if ($this->id == $other->id) { + common_log(LOG_WARNING, + sprintf( + "Profile ID %d (%s) tried to block themself.", + $this->id, + $this->nickname + ) + ); + return false; + } + $block = new Profile_block(); // Begin a transaction @@ -504,17 +548,9 @@ class User extends Memcached_DataObject return false; } - // Cancel their subscription, if it exists - - $sub = Subscription::pkeyGet(array('subscriber' => $other->id, - 'subscribed' => $this->id)); - - if ($sub) { - $result = $sub->delete(); - if (!$result) { - common_log_db_error($sub, 'DELETE', __FILE__); - return false; - } + $self = $this->getProfile(); + if (Subscription::exists($other, $self)) { + Subscription::cancel($other, $self); } $block->query('COMMIT'); @@ -556,39 +592,19 @@ class User extends Memcached_DataObject function getGroups($offset=0, $limit=null) { - $qry = - 'SELECT user_group.* ' . - 'FROM user_group JOIN group_member '. - 'ON user_group.id = group_member.group_id ' . - 'WHERE group_member.profile_id = %d ' . - 'ORDER BY group_member.created DESC '; - - if ($offset) { - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - } - - $groups = new User_group(); - - $cnt = $groups->query(sprintf($qry, $this->id)); - - return $groups; + $profile = $this->getProfile(); + return $profile->getGroups($offset, $limit); } function getSubscriptions($offset=0, $limit=null) { $profile = $this->getProfile(); - assert(!empty($profile)); return $profile->getSubscriptions($offset, $limit); } function getSubscribers($offset=0, $limit=null) { $profile = $this->getProfile(); - assert(!empty($profile)); return $profile->getSubscribers($offset, $limit); } @@ -643,87 +659,19 @@ class User extends Memcached_DataObject return Design::staticGet('id', $this->design_id); } - function hasRole($name) - { - $role = User_role::pkeyGet(array('user_id' => $this->id, - 'role' => $name)); - return (!empty($role)); - } - - function grantRole($name) - { - $role = new User_role(); - - $role->user_id = $this->id; - $role->role = $name; - $role->created = common_sql_now(); - - $result = $role->insert(); - - if (!$result) { - common_log_db_error($role, 'INSERT', __FILE__); - return false; - } - - return true; - } - - function revokeRole($name) - { - $role = User_role::pkeyGet(array('user_id' => $this->id, - 'role' => $name)); - - if (empty($role)) { - throw new Exception('Cannot revoke role "'.$name.'" for user #'.$this->id.'; does not exist.'); - } - - $result = $role->delete(); - - if (!$result) { - common_log_db_error($role, 'DELETE', __FILE__); - throw new Exception('Cannot revoke role "'.$name.'" for user #'.$this->id.'; database error.'); - } - - return true; - } - - /** - * Does this user have the right to do X? - * - * With our role-based authorization, this is merely a lookup for whether the user - * has a particular role. The implementation currently uses a switch statement - * to determine if the user has the pre-defined role to exercise the right. Future - * implementations may allow per-site roles, and different mappings of roles to rights. - * - * @param $right string Name of the right, usually a constant in class Right - * @return boolean whether the user has the right in question - */ - function hasRight($right) { - $result = false; - if (Event::handle('UserRightsCheck', array($this, $right, &$result))) { - switch ($right) - { - case Right::DELETEOTHERSNOTICE: - $result = $this->hasRole(User_role::MODERATOR); - break; - case Right::CONFIGURESITE: - $result = $this->hasRole(User_role::ADMINISTRATOR); - break; - default: - $result = false; - break; - } - } - return $result; + $profile = $this->getProfile(); + return $profile->hasRight($right); } function delete() { - $profile = $this->getProfile(); - if ($profile) { + try { + $profile = $this->getProfile(); $profile->delete(); + } catch (UserNoProfileException $unp) { + common_log(LOG_INFO, "User {$this->nickname} has no profile; continuing deletion."); } $related = array('Fave', @@ -731,8 +679,8 @@ class User extends Memcached_DataObject 'Remember_me', 'Foreign_link', 'Invitation', - 'Notice_inbox', ); + Event::handle('UserDeleteRelated', array($this, &$related)); foreach ($related as $cls) { @@ -761,4 +709,186 @@ class User extends Memcached_DataObject $block->delete(); // XXX delete group block? Reset blocker? } + + function hasRole($name) + { + $profile = $this->getProfile(); + return $profile->hasRole($name); + } + + function grantRole($name) + { + $profile = $this->getProfile(); + return $profile->grantRole($name); + } + + function revokeRole($name) + { + $profile = $this->getProfile(); + return $profile->revokeRole($name); + } + + function isSandboxed() + { + $profile = $this->getProfile(); + return $profile->isSandboxed(); + } + + function isSilenced() + { + $profile = $this->getProfile(); + return $profile->isSilenced(); + } + + function repeatedByMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + $ids = Notice::stream(array($this, '_repeatedByMeDirect'), + array(), + 'user:repeated_by_me:'.$this->id, + $offset, $limit, $since_id, $max_id, null); + + return Notice::getStreamByIds($ids); + } + + function _repeatedByMeDirect($offset, $limit, $since_id, $max_id) + { + $notice = new Notice(); + + $notice->selectAdd(); // clears it + $notice->selectAdd('id'); + + $notice->profile_id = $this->id; + $notice->whereAdd('repeat_of IS NOT NULL'); + + $notice->orderBy('id DESC'); + + if (!is_null($offset)) { + $notice->limit($offset, $limit); + } + + if ($since_id != 0) { + $notice->whereAdd('id > ' . $since_id); + } + + if ($max_id != 0) { + $notice->whereAdd('id <= ' . $max_id); + } + + $ids = array(); + + if ($notice->find()) { + while ($notice->fetch()) { + $ids[] = $notice->id; + } + } + + $notice->free(); + $notice = NULL; + + return $ids; + } + + function repeatsOfMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + $ids = Notice::stream(array($this, '_repeatsOfMeDirect'), + array(), + 'user:repeats_of_me:'.$this->id, + $offset, $limit, $since_id, $max_id); + + return Notice::getStreamByIds($ids); + } + + function _repeatsOfMeDirect($offset, $limit, $since_id, $max_id) + { + $qry = + 'SELECT DISTINCT original.id AS id ' . + 'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' . + 'WHERE original.profile_id = ' . $this->id . ' '; + + if ($since_id != 0) { + $qry .= 'AND original.id > ' . $since_id . ' '; + } + + if ($max_id != 0) { + $qry .= 'AND original.id <= ' . $max_id . ' '; + } + + // NOTE: we sort by fave time, not by notice time! + + $qry .= 'ORDER BY original.id DESC '; + + if (!is_null($offset)) { + $qry .= "LIMIT $limit OFFSET $offset"; + } + + $ids = array(); + + $notice = new Notice(); + + $notice->query($qry); + + while ($notice->fetch()) { + $ids[] = $notice->id; + } + + $notice->free(); + $notice = NULL; + + return $ids; + } + + function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null) + { + throw new Exception("Not implemented since inbox change."); + } + + function shareLocation() + { + $cfg = common_config('location', 'share'); + + if ($cfg == 'always') { + return true; + } else if ($cfg == 'never') { + return false; + } else { // user + $share = true; + + $prefs = User_location_prefs::staticGet('user_id', $this->id); + + if (empty($prefs)) { + $share = common_config('location', 'sharedefault'); + } else { + $share = $prefs->share_location; + $prefs->free(); + } + + return $share; + } + } + + 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::staticGet('id', $pr->profile_id); + } else { + $owner = null; + } + + self::cacheSet('user:site_owner', $owner); + } + + return $owner; + } }