X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=classes%2FProfile.php;h=57522d28dc98f4cab849385c0fa35173550f4a22;hb=471a4805871c44ad8770342ca8ca05536068dc85;hp=064ba551c4bf6de46047989e3b7a3cad8dba1a70;hpb=905d621b6f4fed60deb772df691c15e4a57a1902;p=quix0rs-gnu-social.git diff --git a/classes/Profile.php b/classes/Profile.php index 064ba551c4..57522d28dc 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -125,6 +125,14 @@ class Profile extends Memcached_DataObject return $avatar; } + /** + * Delete attached avatars for this user from the database and filesystem. + * This should be used instead of a batch delete() to ensure that files + * get removed correctly. + * + * @param boolean $original true to delete only the original-size file + * @return + */ function delete_avatars($original=true) { $avatar = new Avatar(); @@ -161,7 +169,7 @@ class Profile extends Memcached_DataObject { if ($this->fullname) { // TRANS: Full name of a profile or group followed by nickname in parens - return sprintf(_('%1$s (%2$s)'), $this->fullname, $this->nickname); + return sprintf(_m('FANCYNAME','%1$s (%2$s)'), $this->fullname, $this->nickname); } else { return $this->nickname; } @@ -178,6 +186,10 @@ class Profile extends Memcached_DataObject $notice = $this->getNotices(0, 1); if ($notice->fetch()) { + if ($notice instanceof ArrayWrapper) { + // hack for things trying to work with single notices + return $notice->_items[0]; + } return $notice; } else { return null; @@ -207,26 +219,29 @@ class Profile extends Memcached_DataObject function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id) { // XXX It would be nice to do this without a join + // (necessary to do it efficiently on accounts with long history) $notice = new Notice(); $query = "select id from notice join notice_tag on id=notice_id where tag='". $notice->escape($tag) . - "' and profile_id=" . $notice->escape($this->id); + "' and profile_id=" . intval($this->id); - if ($since_id != 0) { - $query .= " and id > $since_id"; + $since = Notice::whereSinceId($since_id, 'id', 'notice.created'); + if ($since) { + $query .= " and ($since)"; } - if ($max_id != 0) { - $query .= " and id <= $max_id"; + $max = Notice::whereMaxId($max_id, 'id', 'notice.created'); + if ($max) { + $query .= " and ($max)"; } - $query .= ' order by id DESC'; + $query .= ' order by notice.created DESC, id DESC'; if (!is_null($offset)) { - $query .= " LIMIT $limit OFFSET $offset"; + $query .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset); } $notice->query($query); @@ -244,58 +259,22 @@ class Profile extends Memcached_DataObject { $notice = new Notice(); - // Temporary hack until notice_profile_id_idx is updated - // to (profile_id, id) instead of (profile_id, created, id). - // It's been falling back to PRIMARY instead, which is really - // very inefficient for a profile that hasn't posted in a few - // months. Even though forcing the index will cause a filesort, - // it's usually going to be better. - if (common_config('db', 'type') == 'mysql') { - $index = ''; - $query = - "select id from notice force index (notice_profile_id_idx) ". - "where profile_id=" . $notice->escape($this->id); - - if ($since_id != 0) { - $query .= " and id > $since_id"; - } - - if ($max_id != 0) { - $query .= " and id <= $max_id"; - } - - $query .= ' order by id DESC'; - - if (!is_null($offset)) { - $query .= " LIMIT $limit OFFSET $offset"; - } - - $notice->query($query); - } else { - $index = ''; - - $notice->profile_id = $this->id; - - $notice->selectAdd(); - $notice->selectAdd('id'); + $notice->profile_id = $this->id; - if ($since_id != 0) { - $notice->whereAdd('id > ' . $since_id); - } + $notice->selectAdd(); + $notice->selectAdd('id'); - if ($max_id != 0) { - $notice->whereAdd('id <= ' . $max_id); - } + Notice::addWhereSinceId($notice, $since_id); + Notice::addWhereMaxId($notice, $max_id); - $notice->orderBy('id DESC'); + $notice->orderBy('created DESC, id DESC'); - if (!is_null($offset)) { - $notice->limit($offset, $limit); - } - - $notice->find(); + if (!is_null($offset)) { + $notice->limit($offset, $limit); } + $notice->find(); + $ids = array(); while ($notice->fetch()) { @@ -334,6 +313,13 @@ class Profile extends Memcached_DataObject } } + function isPendingMember($group) + { + $request = Group_join_queue::pkeyGet(array('profile_id' => $this->id, + 'group_id' => $group->id)); + return !empty($request); + } + function getGroups($offset=0, $limit=null) { $qry = @@ -360,6 +346,57 @@ class Profile extends Memcached_DataObject return $groups; } + /** + * Request to join the given group. + * May throw exceptions on failure. + * + * @param User_group $group + * @return mixed: Group_member on success, Group_join_queue if pending approval, null on some cancels? + */ + function joinGroup(User_group $group) + { + $ok = null; + if ($group->join_policy == User_group::JOIN_POLICY_MODERATE) { + $ok = Group_join_queue::saveNew($this, $group); + } else { + if (Event::handle('StartJoinGroup', array($group, $this))) { + $ok = Group_member::join($group->id, $this->id); + Event::handle('EndJoinGroup', array($group, $this)); + } + } + return $ok; + } + + /** + * Cancel a pending group join... + * + * @param User_group $group + */ + function cancelJoinGroup(User_group $group) + { + $request = Group_join_queue::pkeyGet(array('profile_id' => $this->id, + 'group_id' => $group->id)); + if ($request) { + if (Event::handle('StartCancelJoinGroup', array($group, $this))) { + $request->delete(); + Event::handle('EndCancelJoinGroup', array($group, $this)); + } + } + } + + /** + * Leave a group that this profile is a member of. + * + * @param User_group $group + */ + function leaveGroup(User_group $group) + { + if (Event::handle('StartLeaveGroup', array($group, $this))) { + Group_member::leave($group->id, $this->id); + Event::handle('EndLeaveGroup', array($group, $this)); + } + } + function avatarUrl($size=AVATAR_PROFILE_SIZE) { $avatar = $this->getAvatar($size); @@ -372,87 +409,46 @@ class Profile extends Memcached_DataObject 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 ($offset>0 && !is_null($limit)){ - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - } + $subs = Subscription::bySubscriber($this->id, + $offset, + $limit); - $profile = new Profile(); + $profiles = array(); - $profile->query(sprintf($qry, $this->id)); + while ($subs->fetch()) { + $profile = Profile::staticGet($subs->subscribed); + if ($profile) { + $profiles[] = $profile; + } + } - return $profile; + return new ArrayWrapper($profiles); } 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>0 && !is_null($limit)){ - if ($offset) { - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } - } - } + $subs = Subscription::bySubscribed($this->id, + $offset, + $limit); - $profile = new Profile(); + $profiles = array(); - $cnt = $profile->query(sprintf($qry, $this->id)); - - return $profile; - } - - function getConnectedApps($offset = 0, $limit = null) - { - $qry = - 'SELECT u.* ' . - 'FROM oauth_application_user u, oauth_application a ' . - 'WHERE u.profile_id = %d ' . - 'AND a.id = u.application_id ' . - 'AND u.access_type > 0 ' . - 'ORDER BY u.created DESC '; - - if ($offset > 0) { - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; + while ($subs->fetch()) { + $profile = Profile::staticGet($subs->subscriber); + if ($profile) { + $profiles[] = $profile; } } - $apps = new Oauth_application_user(); - - $cnt = $apps->query(sprintf($qry, $this->id)); - - return $apps; + return new ArrayWrapper($profiles); } function subscriptionCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cnt = $c->get(common_cache_key('profile:subscription_count:'.$this->id)); + $cnt = $c->get(Cache::key('profile:subscription_count:'.$this->id)); if (is_integer($cnt)) { return (int) $cnt; } @@ -466,7 +462,7 @@ class Profile extends Memcached_DataObject $cnt = ($cnt > 0) ? $cnt - 1 : $cnt; if (!empty($c)) { - $c->set(common_cache_key('profile:subscription_count:'.$this->id), $cnt); + $c->set(Cache::key('profile:subscription_count:'.$this->id), $cnt); } return $cnt; @@ -474,9 +470,9 @@ class Profile extends Memcached_DataObject function subscriberCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cnt = $c->get(common_cache_key('profile:subscriber_count:'.$this->id)); + $cnt = $c->get(Cache::key('profile:subscriber_count:'.$this->id)); if (is_integer($cnt)) { return (int) $cnt; } @@ -488,15 +484,38 @@ class Profile extends Memcached_DataObject $cnt = (int) $sub->count('distinct subscriber'); if (!empty($c)) { - $c->set(common_cache_key('profile:subscriber_count:'.$this->id), $cnt); + $c->set(Cache::key('profile:subscriber_count:'.$this->id), $cnt); } return $cnt; } + /** + * Is this profile subscribed to another profile? + * + * @param Profile $other + * @return boolean + */ + function isSubscribed($other) + { + return Subscription::exists($this, $other); + } + + /** + * Are these two profiles subscribed to each other? + * + * @param Profile $other + * @return boolean + */ + function mutuallySubscribed($other) + { + return $this->isSubscribed($other) && + $other->isSubscribed($this); + } + function hasFave($notice) { - $cache = common_memcache(); + $cache = Cache::instance(); // XXX: Kind of a hack. @@ -531,9 +550,9 @@ class Profile extends Memcached_DataObject function faveCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cnt = $c->get(common_cache_key('profile:fave_count:'.$this->id)); + $cnt = $c->get(Cache::key('profile:fave_count:'.$this->id)); if (is_integer($cnt)) { return (int) $cnt; } @@ -544,7 +563,7 @@ class Profile extends Memcached_DataObject $cnt = (int) $faves->count('distinct notice_id'); if (!empty($c)) { - $c->set(common_cache_key('profile:fave_count:'.$this->id), $cnt); + $c->set(Cache::key('profile:fave_count:'.$this->id), $cnt); } return $cnt; @@ -552,10 +571,10 @@ class Profile extends Memcached_DataObject function noticeCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $cnt = $c->get(common_cache_key('profile:notice_count:'.$this->id)); + $cnt = $c->get(Cache::key('profile:notice_count:'.$this->id)); if (is_integer($cnt)) { return (int) $cnt; } @@ -566,7 +585,7 @@ class Profile extends Memcached_DataObject $cnt = (int) $notices->count('distinct id'); if (!empty($c)) { - $c->set(common_cache_key('profile:notice_count:'.$this->id), $cnt); + $c->set(Cache::key('profile:notice_count:'.$this->id), $cnt); } return $cnt; @@ -574,47 +593,47 @@ class Profile extends Memcached_DataObject function blowFavesCache() { - $cache = common_memcache(); + $cache = Cache::instance(); if ($cache) { // 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')); - $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id)); - $cache->delete(common_cache_key('fave:ids_by_user_own:'.$this->id.';last')); + $cache->delete(Cache::key('fave:ids_by_user:'.$this->id)); + $cache->delete(Cache::key('fave:ids_by_user:'.$this->id.';last')); + $cache->delete(Cache::key('fave:ids_by_user_own:'.$this->id)); + $cache->delete(Cache::key('fave:ids_by_user_own:'.$this->id.';last')); } $this->blowFaveCount(); } function blowSubscriberCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $c->delete(common_cache_key('profile:subscriber_count:'.$this->id)); + $c->delete(Cache::key('profile:subscriber_count:'.$this->id)); } } function blowSubscriptionCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $c->delete(common_cache_key('profile:subscription_count:'.$this->id)); + $c->delete(Cache::key('profile:subscription_count:'.$this->id)); } } function blowFaveCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $c->delete(common_cache_key('profile:fave_count:'.$this->id)); + $c->delete(Cache::key('profile:fave_count:'.$this->id)); } } function blowNoticeCount() { - $c = common_memcache(); + $c = Cache::instance(); if (!empty($c)) { - $c->delete(common_cache_key('profile:notice_count:'.$this->id)); + $c->delete(Cache::key('profile:notice_count:'.$this->id)); } } @@ -641,9 +660,11 @@ class Profile extends Memcached_DataObject $this->_deleteMessages(); $this->_deleteTags(); $this->_deleteBlocks(); + $this->delete_avatars(); - $related = array('Avatar', - 'Reply', + // Warning: delete() will run on the batch objects, + // not on individual objects. + $related = array('Reply', 'Group_member', ); Event::handle('ProfileDeleteRelated', array($this, &$related)); @@ -793,6 +814,10 @@ class Profile extends Memcached_DataObject throw new Exception("Can't save role '$name' for profile '{$this->id}'"); } + if ($name == 'owner') { + User::blow('user:site_owner'); + } + Event::handle('EndGrantRole', array($this, $name)); } @@ -821,6 +846,10 @@ class Profile extends Memcached_DataObject throw new Exception(sprintf(_('Cannot revoke role "%1$s" for user #%2$d; database error.'),$name, $this->id)); } + if ($name == 'owner') { + User::blow('user:site_owner'); + } + Event::handle('EndRevokeRole', array($this, $name)); return true; @@ -897,6 +926,7 @@ class Profile extends Memcached_DataObject case Right::NEWNOTICE: case Right::NEWMESSAGE: case Right::SUBSCRIBE: + case Right::CREATEGROUP: $result = !$this->isSilenced(); break; case Right::PUBLICNOTICE: @@ -905,6 +935,24 @@ class Profile extends Memcached_DataObject case Right::EMAILONFAVE: $result = !$this->isSandboxed(); break; + case Right::WEBLOGIN: + $result = !$this->isSilenced(); + break; + case Right::API: + $result = !$this->isSilenced(); + break; + case Right::BACKUPACCOUNT: + $result = common_config('profile', 'backup'); + break; + case Right::RESTOREACCOUNT: + $result = common_config('profile', 'restore'); + break; + case Right::DELETEACCOUNT: + $result = common_config('profile', 'delete'); + break; + case Right::MOVEACCOUNT: + $result = common_config('profile', 'move'); + break; default: $result = false; break; @@ -952,6 +1000,31 @@ class Profile extends Memcached_DataObject return $xs->getString(); } + /** + * Extra profile info for atom entries + * + * Clients use some extra profile info in the atom stream. + * This gives it to them. + * + * @param User $cur Current user + * + * @return array representation of element or null + */ + + function profileInfo($cur) + { + $profileInfoAttr = array('local_id' => $this->id); + + if ($cur != null) { + // Whether the current user is a subscribed to this profile + $profileInfoAttr['following'] = $cur->isSubscribed($this) ? 'true' : 'false'; + // Whether the current user is has blocked this profile + $profileInfoAttr['blocking'] = $cur->hasBlocked($this) ? 'true' : 'false'; + } + + return array('statusnet:profile_info', $profileInfoAttr, null); + } + /** * Returns an XML string fragment with profile information as an * Activity Streams element.