X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=classes%2FUser.php;h=495a982360e04c716a2a52708d4f8ff87c197283;hb=affb2f9359ee498c2c20240b592a0f62a770e8c8;hp=84a980db4dd275a897ac8b8b0b68bc9128cdbc96;hpb=4476a4286f012bc72b274133203c6a9c1a6c00b3;p=quix0rs-gnu-social.git diff --git a/classes/User.php b/classes/User.php index 84a980db4d..495a982360 100644 --- a/classes/User.php +++ b/classes/User.php @@ -19,19 +19,13 @@ if (!defined('LACONICA')) { exit(1); } -/* We keep the first three 20-notice pages, plus one for pagination check, - * in the memcached cache. */ - -define('WITHFRIENDS_CACHE_WINDOW', 61); - /** * Table Definition for user */ -require_once 'DB/DataObject.php'; +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; require_once 'Validate.php'; -require_once(INSTALLDIR.'/lib/noticewrapper.php'); -class User extends DB_DataObject +class User extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -39,301 +33,615 @@ class User extends DB_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 $password; // varchar(255) public $email; // varchar(255) unique_key public $incomingemail; // varchar(255) unique_key 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 $language; // varchar(50) + public $timezone; // varchar(50) public $emailpost; // tinyint(1) default_1 public $jabber; // varchar(255) unique_key - public $jabbernotify; // tinyint(1) - public $jabberreplies; // tinyint(1) + public $jabbernotify; // tinyint(1) + public $jabberreplies; // tinyint(1) public $jabbermicroid; // tinyint(1) default_1 - public $updatefrompresence; // tinyint(1) + public $updatefrompresence; // tinyint(1) public $sms; // varchar(64) unique_key - public $carrier; // int(4) - public $smsnotify; // tinyint(1) - public $smsreplies; // tinyint(1) - public $smsemail; // varchar(255) + public $carrier; // int(4) + public $smsnotify; // tinyint(1) + public $smsreplies; // tinyint(1) + public $smsemail; // varchar(255) public $uri; // varchar(255) unique_key - public $autosubscribe; // tinyint(1) + public $autosubscribe; // tinyint(1) + public $urlshorteningservice; // varchar(50) default_ur1.ca + public $inboxed; // tinyint(1) public $created; // datetime() not_null public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('User',$k,$v); } + function staticGet($k,$v=NULL) + { + return Memcached_DataObject::staticGet('User',$k,$v); + } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function getProfile() { - $profile = DB_DataObject::factory('profile'); - $profile->id = $this->id; - if ($profile->find()) { - $profile->fetch(); - return $profile; - } - return NULL; - } - - function isSubscribed($other) { - assert(!is_null($other)); - $sub = DB_DataObject::factory('subscription'); - $sub->subscriber = $this->id; - $sub->subscribed = $other->id; - return $sub->find(); - } - - # 'update' won't write key columns, so we have to do it ourselves. - - function updateKeys(&$orig) { - $parts = array(); - foreach (array('nickname', 'email', 'jabber', '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; - } - $toupdate = implode(', ', $parts); - $qry = 'UPDATE ' . $this->tableName() . ' SET ' . $toupdate . - ' WHERE id = ' . $this->id; - return $this->query($qry); - } - - function allowed_nickname($nickname) { - # XXX: should already be validated for size, content, etc. - static $blacklist = array('rss', 'xrds', 'doc', 'main', - 'settings', 'notice', 'user', - 'search', 'avatar', 'tag', 'tags'); - $merged = array_merge($blacklist, common_config('nickname', 'blacklist')); - return !in_array($nickname, $merged); - } - - function getCurrentNotice($dt=NULL) { - $profile = $this->getProfile(); - if (!$profile) { - return NULL; - } - return $profile->getCurrentNotice($dt); - } - - function getCarrier() { - return Sms_carrier::staticGet($this->carrier); - } - - 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; - } - - function noticesWithFriends($offset=0, $limit=20) { - - $notice = new Notice(); - - $notice->query('SELECT notice.* ' . - 'FROM notice JOIN subscription on notice.profile_id = subscription.subscribed ' . - 'WHERE subscription.subscriber = ' . $this->id . ' ' . - 'ORDER BY created DESC, notice.id DESC ' . - 'LIMIT ' . $offset . ', ' . $limit); - - return $notice; - } - - function favoriteNotices($offset=0, $limit=20) { - - $notice = new Notice(); - - $notice->query('SELECT notice.* ' . - 'FROM notice JOIN fave on notice.id = fave.notice_id ' . - 'WHERE fave.user_id = ' . $this->id . ' ' . - 'ORDER BY notice.created DESC, notice.id DESC ' . - 'LIMIT ' . $offset . ', ' . $limit); - - return $notice; - } - - function noticesWithFriendsWindow() { - - $cache = new Memcache(); - $res = $cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port')); - - if (!$res) { - return NULL; - } - - $notices = $cache->get(common_cache_key('user:notices_with_friends:' . $this->id)); - - if ($notices) { - return $notices; - } - - $notice = new Notice(); - - $notice->query('SELECT notice.* ' . - 'FROM notice JOIN subscription on notice.profile_id = subscription.subscribed ' . - 'WHERE subscription.subscriber = ' . $this->id . ' ' . - 'ORDER BY created DESC, notice.id DESC ' . - 'LIMIT 0, ' . WITHFRIENDS_CACHE_WINDOW); - - $notices = array(); - - while ($notice->fetch()) { - $notices[] = clone($notice); - } - - $cache->set(common_cache_key('user:notices_with_friends:' . $this->id), $notices); - return $notices; - } - - static function register($fields) { - - # MAGICALLY put fields into current scope - - extract($fields); - - $profile = new Profile(); - - $profile->query('BEGIN'); - - $profile->nickname = $nickname; - $profile->profileurl = common_profile_url($nickname); - - if ($fullname) { - $profile->fullname = $fullname; - } - if ($homepage) { - $profile->homepage = $homepage; - } - if ($bio) { - $profile->bio = $bio; - } - if ($location) { - $profile->location = $location; - } - - $profile->created = common_sql_now(); - - $id = $profile->insert(); - - if (!$id) { - common_log_db_error($profile, 'INSERT', __FILE__); - return FALSE; - } - - $user = new User(); - - $user->id = $id; - $user->nickname = $nickname; - - if ($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 ($code) { - $invite = Invitation::staticGet($code); - if ($invite && $invite->address && $invite->address_type == 'email' && $invite->address == $email) { - $user->email = $invite->address; - } - } - - $user->created = common_sql_now(); - $user->uri = common_user_uri($user); - - $result = $user->insert(); - - if (!$result) { - common_log_db_error($user, 'INSERT', __FILE__); - return FALSE; - } - - # Everyone is subscribed to themself - - $subscription = new Subscription(); - $subscription->subscriber = $user->id; - $subscription->subscribed = $user->id; - $subscription->created = $user->created; - - $result = $subscription->insert(); - - if (!$result) { - common_log_db_error($subscription, 'INSERT', __FILE__); - return FALSE; - } - - if ($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($confirm, 'INSERT', __FILE__); - return FALSE; - } - } - - if ($code && $user->email) { - $user->emailChanged(); - } - - $profile->query('COMMIT'); - - if ($email && !$user->email) { - mail_confirm_address($confirm->code, - $profile->nickname, - $email); - } - - return $user; - } - - # Things we do when the email changes - - function emailChanged() { - - $invites = new Invitation(); - $invites->address = $user->email; - $invites->address_type = 'email'; - - if ($invites->find()) { - while ($invites->fetch()) { - $other = User::staticGet($invites->user_id); - subs_subscribe_to($other, $this); - } - } - } - - function hasFave($notice) { - $fave = new Fave(); - $fave->user_id = $this->id; - $fave->notice_id = $notice->id; - if ($fave->find()) { - $result = true; - } else { - $result = false; - } - $fave->free(); - unset($fave); - return $result; - } + function getProfile() + { + return Profile::staticGet('id', $this->id); + } + + 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; + } + + # 'update' won't write key columns, so we have to do it ourselves. + + function updateKeys(&$orig) + { + $parts = array(); + foreach (array('nickname', 'email', 'jabber', '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; + } + $toupdate = implode(', ', $parts); + + $table = $this->tableName(); + if(common_config('db','quote_identifiers')) { + $table = '"' . $table . '"'; + } + $qry = 'UPDATE ' . $table . ' SET ' . $toupdate . + ' WHERE id = ' . $this->id; + $orig->decache(); + $result = $this->query($qry); + if ($result) { + $this->encache(); + } + return $result; + } + + function allowed_nickname($nickname) + { + # XXX: should already be validated for size, content, etc. + static $blacklist = array('rss', 'xrds', 'doc', 'main', + 'settings', 'notice', 'user', + 'search', 'avatar', 'tag', 'tags', + 'api', 'message', 'group', 'groups'); + $merged = array_merge($blacklist, common_config('nickname', 'blacklist')); + return !in_array($nickname, $merged); + } + + function getCurrentNotice($dt=null) + { + $profile = $this->getProfile(); + if (!$profile) { + return null; + } + return $profile->getCurrentNotice($dt); + } + + function getCarrier() + { + return Sms_carrier::staticGet('id', $this->carrier); + } + + 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; + } + + function hasBlocked($other) + { + + $block = Profile_block::get($this->id, $other->id); + + if (is_null($block)) { + $result = false; + } else { + $result = true; + $block->free(); + } + + return $result; + } + + static function register($fields) { + + # MAGICALLY put fields into current scope + + extract($fields); + + $profile = new Profile(); + + $profile->query('BEGIN'); + + $profile->nickname = $nickname; + $profile->profileurl = common_profile_url($nickname); + + if ($fullname) { + $profile->fullname = $fullname; + } + if ($homepage) { + $profile->homepage = $homepage; + } + if ($bio) { + $profile->bio = $bio; + } + if ($location) { + $profile->location = $location; + } + + $profile->created = common_sql_now(); + + $id = $profile->insert(); + + if (!$id) { + common_log_db_error($profile, 'INSERT', __FILE__); + return false; + } + + $user = new User(); + + $user->id = $id; + $user->nickname = $nickname; + + if ($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 ($code) { + $invite = Invitation::staticGet($code); + if ($invite && $invite->address && $invite->address_type == 'email' && $invite->address == $email) { + $user->email = $invite->address; + } + } + + $inboxes = common_config('inboxes', 'enabled'); + + if ($inboxes === true || $inboxes == 'transitional') { + $user->inboxed = 1; + } + + $user->created = common_sql_now(); + $user->uri = common_user_uri($user); + + $result = $user->insert(); + + if (!$result) { + common_log_db_error($user, 'INSERT', __FILE__); + return false; + } + + # Everyone is subscribed to themself + + $subscription = new Subscription(); + $subscription->subscriber = $user->id; + $subscription->subscribed = $user->id; + $subscription->created = $user->created; + + $result = $subscription->insert(); + + if (!$result) { + common_log_db_error($subscription, 'INSERT', __FILE__); + return false; + } + + if ($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($confirm, 'INSERT', __FILE__); + return false; + } + } + + if ($code && $user->email) { + $user->emailChanged(); + } + + $profile->query('COMMIT'); + + if ($email && !$user->email) { + mail_confirm_address($user, $confirm->code, $profile->nickname, $email); + } + + return $user; + } + + # Things we do when the email changes + + function emailChanged() + { + + $invites = new Invitation(); + $invites->address = $this->email; + $invites->address_type = 'email'; + + if ($invites->find()) { + while ($invites->fetch()) { + $other = User::staticGet($invites->user_id); + subs_subscribe_to($other, $this); + } + } + } + + function hasFave($notice) + { + $cache = common_memcache(); + + # XXX: Kind of a hack. + if ($cache) { + # This is the stream of favorite notices, in rev chron + # order. This forces it into cache. + $faves = $this->favoriteNotices(0, NOTICE_CACHE_WINDOW); + $cnt = 0; + while ($faves->fetch()) { + if ($faves->id < $notice->id) { + # If we passed it, it's not a fave + return false; + } else if ($faves->id == $notice->id) { + # If it matches a cached notice, then it's a fave + return true; + } + $cnt++; + } + # If we're not past the end of the cache window, + # then the cache has all available faves, so this one + # is not a fave. + if ($cnt < NOTICE_CACHE_WINDOW) { + return false; + } + # Otherwise, cache doesn't have all faves; + # fall through to the default + } + $fave = Fave::pkeyGet(array('user_id' => $this->id, + 'notice_id' => $notice->id)); + return ((is_null($fave)) ? false : true); + } + function mutuallySubscribed($other) + { + return $this->isSubscribed($other) && + $other->isSubscribed($this); + } + + function mutuallySubscribedUsers() + { + + # 3-way join; probably should get cached + $UT = common_config('db','type')=='pgsql'?'"user"':'user'; + $qry = "SELECT $UT.* " . + "FROM subscription sub1 JOIN $UT ON sub1.subscribed = $UT.id " . + "JOIN subscription sub2 ON $UT.id = sub2.subscriber " . + 'WHERE sub1.subscriber = %d and sub2.subscribed = %d ' . + "ORDER BY $UT.nickname"; + $user = new User(); + $user->query(sprintf($qry, $this->id, $this->id)); + + return $user; + } + + function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN reply ON notice.id = reply.notice_id ' . + 'WHERE reply.profile_id = %d '; + return Notice::getStream(sprintf($qry, $this->id), + 'user:replies:'.$this->id, + $offset, $limit, $since_id, $before_id, null, $since); + } + + function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { + $profile = $this->getProfile(); + if (!$profile) { + return null; + } else { + return $profile->getNotices($offset, $limit, $since_id, $before_id); + } + } + + function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE) + { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . + 'WHERE fave.user_id = %d '; + return Notice::getStream(sprintf($qry, $this->id), + 'user:faves:'.$this->id, + $offset, $limit); + } + + function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { + $enabled = common_config('inboxes', 'enabled'); + + # Complicated code, depending on whether we support inboxes yet + # XXX: make this go away when inboxes become mandatory + + if ($enabled === false || + ($enabled == 'transitional' && $this->inboxed == 0)) { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' . + 'WHERE subscription.subscriber = %d '; + $order = null; + } else if ($enabled === true || + ($enabled == 'transitional' && $this->inboxed == 1)) { + + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . + 'WHERE notice_inbox.user_id = %d '; + # NOTE: we override ORDER + $order = null; + } + return Notice::getStream(sprintf($qry, $this->id), + 'user:notices_with_friends:' . $this->id, + $offset, $limit, $since_id, $before_id, + $order, $since); + } + + function blowFavesCache() + { + $cache = common_memcache(); + if ($cache) { + # Faves don't happen chronologically, so we need to blow + # ;last cache, too + $cache->delete(common_cache_key('user:faves:'.$this->id)); + $cache->delete(common_cache_key('user:faves:'.$this->id).';last'); + } + } + + function getSelfTags() + { + return Profile_tag::getTags($this->id, $this->id); + } + + function setSelfTags($newtags) + { + return Profile_tag::setTags($this->id, $this->id, $newtags); + } + + function block($other) + { + + # Add a new block record + + $block = new Profile_block(); + + # Begin a transaction + + $block->query('BEGIN'); + + $block->blocker = $this->id; + $block->blocked = $other->id; + + $result = $block->insert(); + + if (!$result) { + common_log_db_error($block, 'INSERT', __FILE__); + 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; + } + } + + $block->query('COMMIT'); + + return true; + } + + function unblock($other) + { + + # Get the block record + + $block = Profile_block::get($this->id, $other->id); + + if (!$block) { + return false; + } + + $result = $block->delete(); + + if (!$result) { + common_log_db_error($block, 'DELETE', __FILE__); + return false; + } + + return true; + } + + function isMember($group) + { + $profile = $this->getProfile(); + return $profile->isMember($group); + } + + function isAdmin($group) + { + $profile = $this->getProfile(); + return $profile->isAdmin($group); + } + + 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; + } + + function getSubscriptions($offset=0, $limit=null) + { + $qry = + 'SELECT profile.* ' . + 'FROM profile JOIN subscription ' . + 'ON profile.id = subscription.subscribed ' . + 'WHERE subscription.subscriber = %d ' . + 'AND subscription.subscribed != subscription.subscriber ' . + 'ORDER BY subscription.created DESC '; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $profile = new Profile(); + + $profile->query(sprintf($qry, $this->id)); + + return $profile; + } + + function getSubscribers($offset=0, $limit=null) + { + $qry = + 'SELECT profile.* ' . + 'FROM profile JOIN subscription ' . + 'ON profile.id = subscription.subscriber ' . + 'WHERE subscription.subscribed = %d ' . + 'AND subscription.subscribed != subscription.subscriber ' . + 'ORDER BY subscription.created DESC '; + + if ($offset) { + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + } + + $profile = new Profile(); + + $cnt = $profile->query(sprintf($qry, $this->id)); + + return $profile; + } + + 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) { + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + } + + $profile = new Profile(); + + $cnt = $profile->query(sprintf($qry, $this->id, $tag)); + + return $profile; + } + + 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 '; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $profile = new Profile(); + + $profile->query(sprintf($qry, $this->id, $tag)); + + return $profile; + } + + function hasOpenID() + { + $oid = new User_openid(); + + $oid->user_id = $this->id; + + $cnt = $oid->find(); + + return ($cnt > 0); + } }