]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - classes/User.php
move core schema to class files
[quix0rs-gnu-social.git] / classes / User.php
index 4baa6d1b33f70c59dc09503b58cc1491e689f708..93340f021def182f96c3a49a7459588ef62bed1e 100644 (file)
@@ -28,8 +28,11 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
 require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 require_once 'Validate.php';
 
-class User extends Memcached_DataObject
+class User extends Managed_DataObject
 {
+    const SUBSCRIBE_POLICY_OPEN = 0;
+    const SUBSCRIBE_POLICY_MODERATE = 1;
+
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
 
@@ -55,10 +58,10 @@ class User extends Memcached_DataObject
     public $smsemail;                        // varchar(255)
     public $uri;                             // varchar(255)  unique_key
     public $autosubscribe;                   // tinyint(1)
+    public $subscribe_policy;                // tinyint(1)
     public $urlshorteningservice;            // varchar(50)   default_ur1.ca
     public $inboxed;                         // tinyint(1)
-    public $design_id;                       // int(4)
-    public $viewdesigns;                     // tinyint(1)   default_1
+    public $private_stream;                  // tinyint(1)   default_0
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
@@ -68,13 +71,73 @@ class User extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
+    public static function schemaDef()
+    {
+        return array(
+            'description' => 'local users',
+            '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'),
+                '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'),
+                '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'),
+                'sms' => array('type' => 'varchar', 'length' => 64, 'description' => 'sms phone number'),
+                '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'),
+                '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'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'user_nickname_key' => array('nickname'),
+                'user_email_key' => array('email'),
+                'user_incomingemail_key' => array('incomingemail'),
+                'user_sms_key' => array('sms'),
+                'user_uri_key' => array('uri'),
+            ),
+            'foreign keys' => array(
+                'user_id_fkey' => array('profile', array('id' => 'id')),
+                'user_carrier_fkey' => array('sms_carrier', array('carrier' => 'id')),
+            ),
+            'indexes' => array(
+                'user_smsemail_idx' => array('smsemail'),
+            ),
+        );
+    }
+
+    protected $_profile = -1;
+
+    /**
+     * @return Profile
+     */
     function getProfile()
     {
-        $profile = Profile::staticGet('id', $this->id);
-        if (empty($profile)) {
-            throw new UserNoProfileException($this);
+        if (is_int($this->_profile) && $this->_profile == -1) { // invalid but distinct from null
+            $this->_profile = Profile::staticGet('id', $this->id);
+            if (empty($this->_profile)) {
+                throw new UserNoProfileException($this);
+            }
         }
-        return $profile;
+
+        return $this->_profile;
     }
 
     function isSubscribed($other)
@@ -83,6 +146,12 @@ class User extends Memcached_DataObject
         return $profile->isSubscribed($other);
     }
 
+    function hasPendingSubscription($other)
+    {
+        $profile = $this->getProfile();
+        return $profile->hasPendingSubscription($other);
+    }
+
     // 'update' won't write key columns, so we have to do it ourselves.
 
     function updateKeys(&$orig)
@@ -244,6 +313,8 @@ class User extends Memcached_DataObject
 
         $user->nickname = $nickname;
 
+        $invite = null;
+
         // Users who respond to invite email have proven their ownership of that address
 
         if (!empty($code)) {
@@ -272,7 +343,6 @@ class User extends Memcached_DataObject
         $user->emailmicroid = 1;
         $user->emailpost = 1;
         $user->jabbermicroid = 1;
-        $user->viewdesigns = 1;
 
         $user->created = common_sql_now();
 
@@ -334,6 +404,12 @@ class User extends Memcached_DataObject
                 return false;
             }
 
+            // Mark that this invite was converted
+
+            if (!empty($invite)) {
+                $invite->convert($user);
+            }
+
             if (!empty($email) && !$user->email) {
 
                 $confirm = new Confirm_address();
@@ -445,8 +521,7 @@ class User extends Memcached_DataObject
 
     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);
-        return Notice::getStreamByIds($ids);
+        return Reply::stream($this->id, $offset, $limit, $since_id, $before_id);
     }
 
     function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) {
@@ -462,28 +537,48 @@ class User extends Memcached_DataObject
 
     function favoriteNotices($own=false, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
     {
-        $ids = Fave::stream($this->id, $offset, $limit, $own, $since_id, $max_id);
-        return Notice::getStreamByIds($ids);
+        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 Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, false);
+        return $this->noticeInbox($offset, $limit, $since_id, $before_id);
     }
 
-    function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0)
+    // 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 Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, true);
+        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 Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, false);
+        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)
     {
-        return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, true);
+        $this->noticeInbox($offset, $limit, $since_id, $before_id);
     }
 
     function blowFavesCache()
@@ -494,12 +589,12 @@ class User extends Memcached_DataObject
 
     function getSelfTags()
     {
-        return Profile_tag::getTags($this->id, $this->id);
+        return Profile_tag::getTagsArray($this->id, $this->id, $this->id);
     }
 
-    function setSelfTags($newtags)
+    function setSelfTags($newtags, $privacy)
     {
-        return Profile_tag::setTags($this->id, $this->id, $newtags);
+        return Profile_tag::setTags($this->id, $this->id, $newtags, $privacy);
     }
 
     function block($other)
@@ -586,6 +681,30 @@ class User extends Memcached_DataObject
         return $profile->getGroups($offset, $limit);
     }
 
+    /**
+     * Request to join the given group.
+     * May throw exceptions on failure.
+     *
+     * @param User_group $group
+     * @return Group_member
+     */
+    function joinGroup(User_group $group)
+    {
+        $profile = $this->getProfile();
+        return $profile->joinGroup($group);
+    }
+
+    /**
+     * Leave a group that this user is a member of.
+     *
+     * @param User_group $group
+     */
+    function leaveGroup(User_group $group)
+    {
+        $profile = $this->getProfile();
+        return $profile->leaveGroup($group);
+    }
+
     function getSubscriptions($offset=0, $limit=null)
     {
         $profile = $this->getProfile();
@@ -644,11 +763,6 @@ class User extends Memcached_DataObject
         return $profile;
     }
 
-    function getDesign()
-    {
-        return Design::staticGet('id', $this->design_id);
-    }
-
     function hasRight($right)
     {
         $profile = $this->getProfile();
@@ -732,104 +846,23 @@ class User extends Memcached_DataObject
 
     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);
+        $stream = new RepeatedByMeNoticeStream($this);
+        return $stream->getNotices($offset, $limit, $since_id, $max_id);
     }
 
-    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);
+        $stream = new RepeatsOfMeNoticeStream($this);
 
-        return Notice::getStreamByIds($ids);
+        return $stream->getNotices($offset, $limit, $since_id, $max_id);
     }
 
-    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.");
+        // TRANS: Exception thrown when trying view "repeated to me".
+        throw new Exception(_('Not implemented since inbox change.'));
     }
 
     function shareLocation()
@@ -975,7 +1008,7 @@ class User extends Memcached_DataObject
     }
 
     /*
-     * Get a list of OAuth client application that have access to this
+     * Get a list of OAuth client applications that have access to this
      * user's account.
      */
     function getConnectedApps($offset = 0, $limit = null)
@@ -1003,4 +1036,113 @@ class User extends Memcached_DataObject
         return $apps;
     }
 
+    /**
+     * Magic function called at serialize() time.
+     *
+     * We use this to drop a couple process-specific references
+     * from DB_DataObject which can cause trouble in future
+     * processes.
+     *
+     * @return array of variable names to include in serialization.
+     */
+
+    function __sleep()
+    {
+        $vars = parent::__sleep();
+        $skip = array('_profile');
+        return array_diff($vars, $skip);
+    }
+
+    static function recoverPassword($nore)
+    {
+        $user = User::staticGet('email', common_canonical_email($nore));
+
+        if (!$user) {
+            try {
+                $user = User::staticGet('nickname', common_canonical_nickname($nore));
+            } catch (NicknameException $e) {
+                // invalid
+            }
+        }
+
+        // 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::staticGet($confirm_email->user_id);
+            } else {
+                $confirm_email = null;
+            }
+        } else {
+            $confirm_email = null;
+        }
+
+        if (!$user) {
+            // TRANS: Information on password recovery form if no known username or e-mail address was specified.
+            throw new ClientError(_('No user with that email address or username.'));
+            return;
+        }
+
+        // Try to get an unconfirmed email address if they used a user name
+
+        if (!$user->email && !$confirm_email) {
+            $confirm_email = new Confirm_address();
+            $confirm_email->user_id = $user->id;
+            $confirm_email->address_type = 'email';
+            $confirm_email->find();
+            if (!$confirm_email->fetch()) {
+                $confirm_email = null;
+            }
+        }
+
+        if (!$user->email && !$confirm_email) {
+            // 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
+
+        $confirm = new Confirm_address();
+        $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;
+
+        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.
+        $body = "Hey, $user->nickname.";
+        $body .= "\n\n";
+        $body .= 'Someone just asked for a new password ' .
+                 'for this account on ' . common_config('site', 'name') . '.';
+        $body .= "\n\n";
+        $body .= 'If it was you, and you want to confirm, use the URL below:';
+        $body .= "\n\n";
+        $body .= "\t".common_local_url('recoverpassword',
+                                   array('code' => $confirm->code));
+        $body .= "\n\n";
+        $body .= 'If not, just ignore this message.';
+        $body .= "\n\n";
+        $body .= 'Thanks for your time, ';
+        $body .= "\n";
+        $body .= common_config('site', 'name');
+        $body .= "\n";
+
+        $headers = _mail_prepare_headers('recoverpassword', $user->nickname, $user->nickname);
+        // TRANS: Subject for password recovery e-mail.
+        mail_to_user($user, _('Password recovery requested'), $body, $headers, $confirm->address);
+    }
 }