]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
DB_DataObjects for people tags
authorShashi Gowda <connect2shashi@gmail.com>
Sun, 6 Mar 2011 17:58:03 +0000 (23:28 +0530)
committerShashi Gowda <connect2shashi@gmail.com>
Sun, 6 Mar 2011 17:58:03 +0000 (23:28 +0530)
classes/Notice.php
classes/Profile.php
classes/Profile_list.php [new file with mode: 0644]
classes/Profile_tag.php
classes/Profile_tag_inbox.php [new file with mode: 0644]
classes/Profile_tag_subscription.php [new file with mode: 0644]
classes/User.php
classes/statusnet.ini
classes/statusnet.links.ini

index 157fdf2dc466c698233118994b75a67718e34f22..21795ae21b5e27fe261711d91f7e34fde6e7c82f 100644 (file)
@@ -429,6 +429,12 @@ class Notice extends Memcached_DataObject
             $notice->saveGroups();
         }
 
+        if (isset($peopletags)) {
+            $notice->saveProfileTags($peopletags);
+        } else {
+            $notice->saveProfileTags();
+        }
+
         if (isset($urls)) {
             $notice->saveKnownUrls($urls);
         } else {
@@ -797,6 +803,7 @@ class Notice extends Memcached_DataObject
         }
 
         $users = $this->getSubscribedUsers();
+        $ptags = $this->getProfileTags();
 
         // FIXME: kind of ignoring 'transitional'...
         // we'll probably stop supporting inboxless mode
@@ -817,6 +824,18 @@ class Notice extends Memcached_DataObject
             }
         }
 
+        foreach ($ptags as $ptag) {
+            $users = $ptag->getUserSubscribers();
+            foreach ($users as $id) {
+                if (!array_key_exists($id, $ni)) {
+                    $user = User::staticGet('id', $id);
+                    if (!$user->hasBlocked($profile)) {
+                        $ni[$id] = NOTICE_INBOX_SOURCE_PROFILE_TAG;
+                    }
+                }
+            }
+        }
+
         foreach ($recipients as $recipient) {
             if (!array_key_exists($recipient, $ni)) {
                 $ni[$recipient] = NOTICE_INBOX_SOURCE_REPLY;
@@ -913,6 +932,39 @@ class Notice extends Memcached_DataObject
         return $ids;
     }
 
+    function getProfileTags()
+    {
+        // Don't save ptags for repeats, for now.
+
+        if (!empty($this->repeat_of)) {
+            return array();
+        }
+
+        // XXX: cache me
+
+        $ptags = array();
+
+        $ptagi = new Profile_tag_inbox();
+
+        $ptagi->selectAdd();
+        $ptagi->selectAdd('profile_tag_id');
+
+        $ptagi->notice_id = $this->id;
+
+        if ($ptagi->find()) {
+            while ($ptagi->fetch()) {
+                $profile_list = Profile_list::staticGet('id', $ptagi->profile_tag_id);
+                if ($profile_list) {
+                    $ptags[] = $profile_list;
+                }
+            }
+        }
+
+        $ptagi->free();
+
+        return $ptags;
+    }
+
     /**
      * Record this notice to the given group inboxes for delivery.
      * Overrides the regular parsing of !group markup.
@@ -1034,6 +1086,69 @@ class Notice extends Memcached_DataObject
         return true;
     }
 
+    /**
+     * record targets into profile_tag_inbox.
+     * @return array of Profile_list objects
+     */
+    function saveProfileTags($known=array())
+    {
+        // Don't save ptags for repeats, for now
+
+        if (!empty($this->repeat_of)) {
+            return array();
+        }
+
+        if (is_array($known)) {
+            $ptags = $known;
+        } else {
+            $ptags = array();
+        }
+
+        $ptag = new Profile_tag();
+        $ptag->tagged = $this->profile_id;
+
+        if($ptag->find()) {
+            while($ptag->fetch()) {
+                $plist = Profile_list::getByTaggerAndTag($ptag->tagger, $ptag->tag);
+                $ptags[] = clone($plist);
+            }
+        }
+
+        foreach ($ptags as $target) {
+            $this->addToProfileTagInbox($target);
+        }
+
+        return $ptags;
+    }
+
+    function addToProfileTagInbox($plist)
+    {
+        $ptagi = Profile_tag_inbox::pkeyGet(array('profile_tag_id' => $plist->id,
+                                         'notice_id' => $this->id));
+
+        if (empty($ptagi)) {
+
+            $ptagi = new Profile_tag_inbox();
+
+            $ptagi->query('BEGIN');
+            $ptagi->profile_tag_id  = $plist->id;
+            $ptagi->notice_id = $this->id;
+            $ptagi->created   = $this->created;
+
+            $result = $ptagi->insert();
+            if (!$result) {
+                common_log_db_error($ptagi, 'INSERT', __FILE__);
+                throw new ServerException(_('Problem saving profile_tag inbox.'));
+            }
+
+            $ptagi->query('COMMIT');
+
+            self::blow('profile_tag:notice_ids:%d', $ptagi->profile_tag_id);
+        }
+
+        return true;
+    }
+
     /**
      * Save reply records indicating that this notice needs to be
      * delivered to the local users with the given URIs.
index 88edf5cbb3c1d0912e4cd9b5d86f6b949f768be9..963b41e0d492c7b6753f55f9b189645ca9f4ed69 100644 (file)
@@ -339,6 +339,183 @@ class Profile extends Memcached_DataObject
         return $groups;
     }
 
+    function isTagged($peopletag)
+    {
+        $tag = Profile_tag::pkeyGet(array('tagger' => $peopletag->tagger,
+                                          'tagged' => $this->id,
+                                          'tag'    => $peopletag->tag));
+        return !empty($tag);
+    }
+
+    function canTag($tagged)
+    {
+        if (empty($tagged)) {
+            return false;
+        }
+
+        if ($tagged->id == $this->id) {
+            return true;
+        }
+
+        $all = common_config('peopletag', 'allow_tagging', 'all');
+        $local = common_config('peopletag', 'allow_tagging', 'local');
+        $remote = common_config('peopletag', 'allow_tagging', 'remote');
+        $subs = common_config('peopletag', 'allow_tagging', 'subs');
+
+        if ($all) {
+            return true;
+        }
+
+        $tagged_user = $tagged->getUser();
+        if (!empty($tagged_user)) {
+            if ($local) {
+                return true;
+            }
+        } else if ($subs) {
+            return (Subscription::exists($this, $tagged) ||
+                    Subscription::exists($tagged, $this));
+        } else if ($remote) {
+            return true;
+        }
+        return false;
+    }
+
+    function getOwnedTags($auth_user, $offset=0, $limit=null, $since_id=0, $max_id=0)
+    {
+        $tags = new Profile_list();
+        $tags->tagger = $this->id;
+
+        if (($auth_user instanceof User || $auth_user instanceof Profile) &&
+                $auth_user->id === $this->id) {
+            // no condition, get both private and public tags
+        } else {
+            $tags->private = false;
+        }
+
+        $tags->selectAdd('id as "cursor"');
+
+        if ($since_id>0) {
+           $tags->whereAdd('id > '.$since_id);
+        }
+
+        if ($max_id>0) {
+            $tags->whereAdd('id <= '.$max_id);
+        }
+
+        if($offset>=0 && !is_null($limit)) {
+            $tags->limit($offset, $limit);
+        }
+
+        $tags->orderBy('id DESC');
+        $tags->find();
+
+        return $tags;
+    }
+
+    function getOtherTags($auth_user=null, $offset=0, $limit=null, $since_id=0, $max_id=0)
+    {
+        $lists = new Profile_list();
+
+        $tags = new Profile_tag();
+        $tags->tagged = $this->id;
+
+        $lists->joinAdd($tags);
+        #@fixme: postgres (round(date_part('epoch', my_date)))
+        $lists->selectAdd('unix_timestamp(profile_tag.modified) as "cursor"');
+
+        if ($auth_user instanceof User || $auth_user instanceof Profile) {
+            $lists->whereAdd('( ( profile_list.private = false ) ' .
+                             'OR ( profile_list.tagger = ' . $auth_user->id . ' AND ' .
+                             'profile_list.private = true ) )');
+        } else {
+            $lists->private = false;
+        }
+
+        if ($since_id>0) {
+           $lists->whereAdd('cursor > '.$since_id);
+        }
+
+        if ($max_id>0) {
+            $lists->whereAdd('cursor <= '.$max_id);
+        }
+
+        if($offset>=0 && !is_null($limit)) {
+            $lists->limit($offset, $limit);
+        }
+
+        $lists->orderBy('profile_tag.modified DESC');
+        $lists->find();
+
+        return $lists;
+    }
+
+    function getPrivateTags($offset=0, $limit=null, $since_id=0, $max_id=0)
+    {
+        $tags = new Profile_list();
+        $tags->private = true;
+        $tags->tagger = $this->id;
+
+        if ($since_id>0) {
+           $tags->whereAdd('id > '.$since_id);
+        }
+
+        if ($max_id>0) {
+            $tags->whereAdd('id <= '.$max_id);
+        }
+
+        if($offset>=0 && !is_null($limit)) {
+            $tags->limit($offset, $limit);
+        }
+
+        $tags->orderBy('id DESC');
+        $tags->find();
+
+        return $tags;
+    }
+
+    function hasLocalTags()
+    {
+        $tags = new Profile_tag();
+
+        $tags->joinAdd(array('tagger', 'user:id'));
+        $tags->whereAdd('tagged  = '.$this->id);
+        $tags->whereAdd('tagger != '.$this->id);
+
+        $tags->limit(0, 1);
+        $tags->fetch();
+
+        return ($tags->N == 0) ? false : true;
+    }
+
+    function getTagSubscriptions($offset=0, $limit=null, $since_id=0, $max_id=0)
+    {
+        $lists = new Profile_list();
+        $subs = new Profile_tag_subscription();
+
+        $lists->joinAdd($subs);
+        #@fixme: postgres (round(date_part('epoch', my_date)))
+        $lists->selectAdd('unix_timestamp(profile_tag_subscription.created) as "cursor"');
+
+        $lists->whereAdd('profile_tag_subscription.profile_id = '.$this->id);
+
+        if ($since_id>0) {
+           $lists->whereAdd('cursor > '.$since_id);
+        }
+
+        if ($max_id>0) {
+            $lists->whereAdd('cursor <= '.$max_id);
+        }
+
+        if($offset>=0 && !is_null($limit)) {
+            $lists->limit($offset, $limit);
+        }
+
+        $lists->orderBy('"cursor" DESC');
+        $lists->find();
+
+        return $lists;
+    }
+
     function avatarUrl($size=AVATAR_PROFILE_SIZE)
     {
         $avatar = $this->getAvatar($size);
@@ -385,6 +562,32 @@ class Profile extends Memcached_DataObject
         return new ArrayWrapper($profiles);
     }
 
+
+    function getTaggedSubscribers($tag)
+    {
+        $qry =
+          'SELECT profile.* ' .
+          'FROM profile JOIN (subscription, profile_tag, profile_list) ' .
+          'ON profile.id = subscription.subscriber ' .
+          'AND profile.id = profile_tag.tagged ' .
+          'AND profile_tag.tagger = profile_list.tagger AND profile_tag.tag = profile_list.tag ' .
+          'WHERE subscription.subscribed = %d ' .
+          'AND subscription.subscribed != subscription.subscriber ' .
+          'AND profile_tag.tagger = %d AND profile_tag.tag = "%s" ' .
+          'AND profile_list.private = false ' .
+          'ORDER BY subscription.created DESC';
+
+        $profile = new Profile();
+        $tagged = array();
+
+        $cnt = $profile->query(sprintf($qry, $this->id, $this->id, $tag));
+
+        while ($profile->fetch()) {
+            $tagged[] = clone($profile);
+        }
+        return $tagged;
+    }
+
     function subscriptionCount()
     {
         $c = Cache::instance();
diff --git a/classes/Profile_list.php b/classes/Profile_list.php
new file mode 100644 (file)
index 0000000..df7bef5
--- /dev/null
@@ -0,0 +1,929 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.     If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Notices
+ * @package  StatusNet
+ * @author   Shashi Gowda <connect2shashi@gmail.com>
+ * @license  GNU Affero General Public License http://www.gnu.org/licenses/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Table Definition for profile_list
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Profile_list extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'profile_list';                      // table name
+    public $id;                              // int(4)  primary_key not_null
+    public $tagger;                          // int(4)
+    public $tag;                             // varchar(64)
+    public $description;                     // text
+    public $private;                         // tinyint(1)
+    public $created;                         // datetime   not_null default_0000-00-00%2000%3A00%3A00
+    public $modified;                        // timestamp   not_null default_CURRENT_TIMESTAMP
+    public $uri;                             // varchar(255)  unique_key
+    public $mainpage;                        // varchar(255)
+    public $tagged_count;                    // smallint
+    public $subscriber_count;                // smallint
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Profile_list',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    /**
+     * return a profile_list record, given its tag and tagger.
+     *
+     * @param array $kv ideally array('tag' => $tag, 'tagger' => $tagger)
+     *
+     * @return Profile_list a Profile_list object with the given tag and tagger.
+     */
+
+    function pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('Profile_list', $kv);
+    }
+
+    /**
+     * get the tagger of this profile_list object
+     *
+     * @return Profile the tagger
+     */
+
+    function getTagger()
+    {
+        return Profile::staticGet('id', $this->tagger);
+    }
+
+    /**
+     * return a string to identify this
+     * profile_list in the user interface etc.
+     *
+     * @return String
+     */
+
+    function getBestName()
+    {
+        return $this->tag;
+    }
+
+    /**
+     * return a uri string for this profile_list
+     *
+     * @return String uri
+     */
+
+    function getUri()
+    {
+        $uri = null;
+        if (Event::handle('StartProfiletagGetUri', array($this, &$uri))) {
+            if (!empty($this->uri)) {
+                $uri = $this->uri;
+            } else {
+                $uri = common_local_url('profiletagbyid',
+                                        array('id' => $this->id, 'tagger_id' => $this->tagger));
+            }
+        }
+        Event::handle('EndProfiletagGetUri', array($this, &$uri));
+        return $uri;
+    }
+
+    /**
+     * return a url to the homepage of this item
+     *
+     * @return String home url
+     */
+
+    function homeUrl()
+    {
+        $url = null;
+        if (Event::handle('StartUserPeopletagHomeUrl', array($this, &$url))) {
+            // normally stored in mainpage, but older ones may be null
+            if (!empty($this->mainpage)) {
+                $url = $this->mainpage;
+            } else {
+                $url = common_local_url('showprofiletag',
+                                        array('tagger' => $this->getTagger()->nickname,
+                                              'tag'    => $this->tag));
+            }
+        }
+        Event::handle('EndUserPeopletagHomeUrl', array($this, &$url));
+        return $url;
+    }
+
+    /**
+     * return an immutable url for this object
+     *
+     * @return String permalink
+     */
+
+    function permalink()
+    {
+        $url = null;
+        if (Event::handle('StartProfiletagPermalink', array($this, &$url))) {
+            $url = common_local_url('profiletagbyid',
+                                    array('id' => $this->id));
+        }
+        Event::handle('EndProfiletagPermalink', array($this, &$url));
+        return $url;
+    }
+
+    /**
+     * Query notices by users associated with this tag,
+     * but first check the cache before hitting the DB.
+     *
+     * @param integer $offset   offset
+     * @param integer $limit    maximum no of results
+     * @param integer $since_id=null    since this id
+     * @param integer $max_id=null  maximum id in result
+     *
+     * @return Notice the query
+     */
+
+    function getNotices($offset, $limit, $since_id=null, $max_id=null)
+    {
+        $ids = Notice::stream(array($this, '_streamDirect'),
+                              array(),
+                              'profile_tag:notice_ids:' . $this->id,
+                              $offset, $limit, $since_id, $max_id);
+
+        return Notice::getStreamByIds($ids);
+    }
+
+    /**
+     * Query notices by users associated with this tag from the database.
+     *
+     * @param integer $offset   offset
+     * @param integer $limit    maximum no of results
+     * @param integer $since_id=null    since this id
+     * @param integer $max_id=null  maximum id in result
+     *
+     * @return array array of notice ids.
+     */
+
+    function _streamDirect($offset, $limit, $since_id, $max_id)
+    {
+        $inbox = new Profile_tag_inbox();
+
+        $inbox->profile_tag_id = $this->id;
+
+        $inbox->selectAdd();
+        $inbox->selectAdd('notice_id');
+
+        if ($since_id != 0) {
+            $inbox->whereAdd('notice_id > ' . $since_id);
+        }
+
+        if ($max_id != 0) {
+            $inbox->whereAdd('notice_id <= ' . $max_id);
+        }
+
+        $inbox->orderBy('notice_id DESC');
+
+        if (!is_null($offset)) {
+            $inbox->limit($offset, $limit);
+        }
+
+        $ids = array();
+
+        if ($inbox->find()) {
+            while ($inbox->fetch()) {
+                $ids[] = $inbox->notice_id;
+            }
+        }
+
+        return $ids;
+    }
+
+    /**
+     * Get subscribers (local and remote) to this people tag
+     * Order by reverse chronology
+     *
+     * @param integer $offset   offset
+     * @param integer $limit    maximum no of results
+     * @param integer $since_id=null    since unix timestamp
+     * @param integer $upto=null  maximum unix timestamp when subscription was made
+     *
+     * @return Profile results
+     */
+
+    function getSubscribers($offset=0, $limit=null, $since=0, $upto=0)
+    {
+        $subs = new Profile();
+        $sub = new Profile_tag_subscription();
+        $sub->profile_tag_id = $this->id;
+
+        $subs->joinAdd($sub);
+        $subs->selectAdd('unix_timestamp(profile_tag_subscription.' .
+                         'created) as "cursor"');
+
+        if ($since != 0) {
+            $subs->whereAdd('cursor > ' . $since);
+        }
+
+        if ($upto != 0) {
+            $subs->whereAdd('cursor <= ' . $upto);
+        }
+
+        if ($limit != null) {
+            $subs->limit($offset, $limit);
+        }
+
+        $subs->orderBy('profile_tag_subscription.created DESC');
+        $subs->find();
+
+        return $subs;
+    }
+
+    /**
+     * Get all and only local subscribers to this people tag
+     * used for distributing notices to user inboxes.
+     *
+     * @return array ids of users
+     */
+
+    function getUserSubscribers()
+    {
+        // XXX: cache this
+
+        $user = new User();
+        if(common_config('db','quote_identifiers'))
+            $user_table = '"user"';
+        else $user_table = 'user';
+
+        $qry =
+          'SELECT id ' .
+          'FROM '. $user_table .' JOIN profile_tag_subscription '.
+          'ON '. $user_table .'.id = profile_tag_subscription.profile_id ' .
+          'WHERE profile_tag_subscription.profile_tag_id = %d ';
+
+        $user->query(sprintf($qry, $this->id));
+
+        $ids = array();
+
+        while ($user->fetch()) {
+            $ids[] = $user->id;
+        }
+
+        $user->free();
+
+        return $ids;
+    }
+
+    /**
+     * Check to see if a given profile has
+     * subscribed to this people tag's timeline
+     *
+     * @param mixed $id User or Profile object or integer id
+     *
+     * @return boolean subscription status
+     */
+
+    function hasSubscriber($id)
+    {
+        if (!is_numeric($id)) {
+            $id = $id->id;
+        }
+
+        $sub = Profile_tag_subscription::pkeyGet(array('profile_tag_id' => $this->id,
+                                                       'profile_id'     => $id));
+        return !empty($sub);
+    }
+
+    /**
+     * Get profiles tagged with this people tag,
+     * include modified timestamp as a "cursor" field
+     * order by descending order of modified time
+     *
+     * @param integer $offset   offset
+     * @param integer $limit    maximum no of results
+     * @param integer $since_id=null    since unix timestamp
+     * @param integer $upto=null  maximum unix timestamp when subscription was made
+     *
+     * @return Profile results
+     */
+
+    function getTagged($offset=0, $limit=null, $since=0, $upto=0)
+    {
+        $tagged = new Profile();
+        $tagged->joinAdd(array('id', 'profile_tag:tagged'));
+
+        #@fixme: postgres
+        $tagged->selectAdd('unix_timestamp(profile_tag.modified) as "cursor"');
+        $tagged->whereAdd('profile_tag.tagger = '.$this->tagger);
+        $tagged->whereAdd("profile_tag.tag = '{$this->tag}'");
+
+        if ($since != 0) {
+            $tagged->whereAdd('cursor > ' . $since);
+        }
+
+        if ($upto != 0) {
+            $tagged->whereAdd('cursor <= ' . $upto);
+        }
+
+        if ($limit != null) {
+            $tagged->limit($offset, $limit);
+        }
+
+        $tagged->orderBy('profile_tag.modified DESC');
+        $tagged->find();
+
+        return $tagged;
+    }
+
+    /**
+     * Gracefully delete one or many people tags
+     * along with their members and subscriptions data
+     *
+     * @return boolean success
+     */
+
+    function delete()
+    {
+        // force delete one item at a time.
+        if (empty($this->id)) {
+            $this->find();
+            while ($this->fetch()) {
+                $this->delete();
+            }
+        }
+
+        Profile_tag::cleanup($this);
+        Profile_tag_subscription::cleanup($this);
+
+        return parent::delete();
+    }
+
+    /**
+     * Update a people tag gracefully
+     * also change "tag" fields in profile_tag table
+     *
+     * @param Profile_list $orig    Object's original form
+     *
+     * @return boolean success
+     */
+
+    function update($orig=null)
+    {
+        $result = true;
+
+        if (!is_object($orig) && !$orig instanceof Profile_list) {
+            parent::update($orig);
+        }
+
+        // if original tag was different
+        // check to see if the new tag already exists
+        // if not, rename the tag correctly
+        if($orig->tag != $this->tag || $orig->tagger != $this->tagger) {
+            $existing = Profile_list::getByTaggerAndTag($this->tagger, $this->tag);
+            if(!empty($existing)) {
+                throw new ServerException(_('The tag you are trying to rename ' .
+                                            'to already exists.'));
+            }
+            // move the tag
+            // XXX: allow OStatus plugin to send out profile tag
+            $result = Profile_tag::moveTag($orig, $this);
+        }
+        parent::update($orig);
+        return $result;
+    }
+
+    /**
+     * return an xml string representing this people tag
+     * as the author of an atom feed
+     *
+     * @return string atom author element
+     */
+
+    function asAtomAuthor()
+    {
+        $xs = new XMLStringer(true);
+
+        $tagger = $this->getTagger();
+        $xs->elementStart('author');
+        $xs->element('name', null, '@' . $tagger->nickname . '/' . $this->tag);
+        $xs->element('uri', null, $this->permalink());
+        $xs->elementEnd('author');
+
+        return $xs->getString();
+    }
+
+    /**
+     * return an xml string to represent this people tag
+     * as the subject of an activitystreams feed.
+     *
+     * @return string activitystreams subject
+     */
+
+    function asActivitySubject()
+    {
+        return $this->asActivityNoun('subject');
+    }
+
+    /**
+     * return an xml string to represent this people tag
+     * as a noun in an activitystreams feed.
+     *
+     * @param string $element the xml tag
+     *
+     * @return string activitystreams noun
+     */
+
+    function asActivityNoun($element)
+    {
+        $noun = ActivityObject::fromPeopletag($this);
+        return $noun->asString('activity:' . $element);
+    }
+
+    /**
+     * get the cached number of profiles tagged with this
+     * people tag, re-count if the argument is true.
+     *
+     * @param boolean $recount  whether to ignore cache
+     *
+     * @return integer count
+     */
+
+    function taggedCount($recount=false)
+    {
+        if (!$recount) {
+            return $this->tagged_count;
+        }
+
+        $tags = new Profile_tag();
+        $tags->tag = $this->tag;
+        $tags->tagger = $this->tagger;
+        $orig = clone($this);
+        $this->tagged_count = (int) $tags->count('distinct tagged');
+        $this->update($orig);
+
+        return $this->tagged_count;
+    }
+
+    /**
+     * get the cached number of profiles subscribed to this
+     * people tag, re-count if the argument is true.
+     *
+     * @param boolean $recount  whether to ignore cache
+     *
+     * @return integer count
+     */
+
+    function subscriberCount($recount=false)
+    {
+        if ($recount) {
+            return $this->subscriber_count;
+        }
+
+        $sub = new Profile_tag_subscription();
+        $sub->profile_tag_id = $this->id;
+        $orig = clone($this);
+        $this->subscriber_count = (int) $sub->count('distinct profile_id');
+        $this->update($orig);
+
+        return $this->subscriber_count;
+    }
+
+    /**
+     * get the Profile_list object by the
+     * given tagger and with given tag
+     *
+     * @param integer $tagger   the id of the creator profile
+     * @param integer $tag      the tag
+     *
+     * @return integer count
+     */
+
+    static function getByTaggerAndTag($tagger, $tag)
+    {
+        $ptag = Profile_list::pkeyGet(array('tagger' => $tagger, 'tag' => $tag));
+        return $ptag;
+    }
+
+    /**
+     * create a profile_list record for a tag, tagger pair
+     * if it doesn't exist, return it.
+     *
+     * @param integer $tagger   the tagger
+     * @param string  $tag      the tag
+     * @param string  $description description
+     * @param boolean $private  protected or not
+     *
+     * @return Profile_list the people tag object
+     */
+
+    static function ensureTag($tagger, $tag, $description=null, $private=false)
+    {
+        $ptag = Profile_list::getByTaggerAndTag($tagger, $tag);
+
+        if(empty($ptag->id)) {
+            $args = array(
+                'tag' => $tag,
+                'tagger' => $tagger,
+                'description' => $description,
+                'private' => $private
+            );
+
+            $new_tag = Profile_list::saveNew($args);
+
+            return $new_tag;
+        }
+        return $ptag;
+    }
+
+    /**
+     * get the maximum number of characters
+     * that can be used in the description of
+     * a people tag.
+     *
+     * determined by $config['peopletag']['desclimit']
+     * if not set, falls back to $config['site']['textlimit']
+     *
+     * @return integer maximum number of characters
+     */
+
+    static function maxDescription()
+    {
+        $desclimit = common_config('peopletag', 'desclimit');
+        // null => use global limit (distinct from 0!)
+        if (is_null($desclimit)) {
+            $desclimit = common_config('site', 'textlimit');
+        }
+        return $desclimit;
+    }
+
+    /**
+     * check if the length of given text exceeds
+     * character limit.
+     *
+     * @param string $desc  the description
+     *
+     * @return boolean is the descripition too long?
+     */
+
+    static function descriptionTooLong($desc)
+    {
+        $desclimit = self::maxDescription();
+        return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit));
+    }
+
+    /**
+     * save a new people tag, this should be always used
+     * since it makes uri, homeurl, created and modified
+     * timestamps and performs checks.
+     *
+     * @param array $fields an array with fields and their values
+     *
+     * @return mixed Profile_list on success, false on fail
+     */
+    static function saveNew($fields) {
+
+        extract($fields);
+
+        $ptag = new Profile_list();
+
+        $ptag->query('BEGIN');
+
+        if (empty($tagger)) {
+            throw new Exception(_('No tagger specified.'));
+        }
+
+        if (empty($tag)) {
+            throw new Exception(_('No tag specified.'));
+        }
+
+        if (empty($mainpage)) {
+            $mainpage = null;
+        }
+
+        if (empty($uri)) {
+            // fill in later...
+            $uri = null;
+        }
+
+        if (empty($mainpage)) {
+            $mainpage = null;
+        }
+
+        if (empty($description)) {
+            $description = null;
+        }
+
+        if (empty($private)) {
+            $private = false;
+        }
+
+        $ptag->tagger      = $tagger;
+        $ptag->tag         = $tag;
+        $ptag->description = $description;
+        $ptag->private     = $private;
+        $ptag->uri         = $uri;
+        $ptag->mainpage    = $mainpage;
+        $ptag->created     = common_sql_now();
+        $ptag->modified    = common_sql_now();
+
+        $result = $ptag->insert();
+
+        if (!$result) {
+            common_log_db_error($ptag, 'INSERT', __FILE__);
+            throw new ServerException(_('Could not create profile tag.'));
+        }
+
+        if (!isset($uri) || empty($uri)) {
+            $orig = clone($ptag);
+            $ptag->uri = common_local_url('profiletagbyid', array('id' => $ptag->id, 'tagger_id' => $ptag->tagger));
+            $result = $ptag->update($orig);
+            if (!$result) {
+                common_log_db_error($ptag, 'UPDATE', __FILE__);
+                throw new ServerException(_('Could not set profile tag URI.'));
+            }
+        }
+
+        if (!isset($mainpage) || empty($mainpage)) {
+            $orig = clone($ptag);
+            $user = User::staticGet('id', $ptag->tagger);
+            if(!empty($user)) {
+                $ptag->mainpage = common_local_url('showprofiletag', array('tag' => $ptag->tag, 'tagger' => $user->nickname));
+            } else {
+                $ptag->mainpage = $uri; // assume this is a remote peopletag and the uri works
+            }
+
+            $result = $ptag->update($orig);
+            if (!$result) {
+                common_log_db_error($ptag, 'UPDATE', __FILE__);
+                throw new ServerException(_('Could not set profile tag mainpage.'));
+            }
+        }
+        return $ptag;
+    }
+
+    /**
+     * get all items at given cursor position for api
+     *
+     * @param callback $fn  a function that takes the following arguments in order:
+     *                      $offset, $limit, $since_id, $max_id
+     *                      and returns a Profile_list object after making the DB query
+     * @param array $args   arguments required for $fn
+     * @param integer $cursor   the cursor
+     * @param integer $count    max. number of results
+     *
+     * Algorithm:
+     * - if cursor is 0, return empty list
+     * - if cursor is -1, get first 21 items, next_cursor = 20th prev_cursor = 0
+     * - if cursor is +ve get 22 consecutive items before starting at cursor
+     *   - return items[1..20] if items[0] == cursor else return items[0..21]
+     *   - prev_cursor = items[1]
+     *   - next_cursor = id of the last item being returned
+     *
+     * - if cursor is -ve get 22 consecutive items after cursor starting at cursor
+     *   - return items[1..20]
+     *
+     * @returns array (array (mixed items), int next_cursor, int previous_cursor)
+     */
+
+     // XXX: This should be in Memcached_DataObject... eventually.
+
+    static function getAtCursor($fn, $args, $cursor, $count=20)
+    {
+        $items = array();
+
+        $since_id = 0;
+        $max_id = 0;
+        $next_cursor = 0;
+        $prev_cursor = 0;
+
+        if($cursor > 0) {
+            // if cursor is +ve fetch $count+2 items before cursor starting at cursor
+            $max_id = $cursor;
+            $fn_args = array_merge($args, array(0, $count+2, 0, $max_id));
+            $list = call_user_func_array($fn, $fn_args);
+            while($list->fetch()) {
+                $items[] = clone($list);
+            }
+
+            if ((isset($items[0]->cursor) && $items[0]->cursor == $cursor) ||
+                $items[0]->id == $cursor) {
+                array_shift($items);
+                $prev_cursor = isset($items[0]->cursor) ?
+                    -$items[0]->cursor : -$items[0]->id;
+            } else {
+                if (count($items) > $count+1) {
+                    array_shift($items);
+                }
+                // this means the cursor item has been deleted, check to see if there are more
+                $fn_args = array_merge($args, array(0, 1, $cursor));
+                $more = call_user_func($fn, $fn_args);
+                if (!$more->fetch() || empty($more)) {
+                    // no more items.
+                    $prev_cursor = 0;
+                } else {
+                    $prev_cursor = isset($items[0]->cursor) ?
+                        -$items[0]->cursor : -$items[0]->id;
+                }
+            }
+
+            if (count($items)==$count+1) {
+                // this means there is a next page.
+                $next = array_pop($items);
+                $next_cursor = isset($next->cursor) ?
+                    $items[$count-1]->cursor : $items[$count-1]->id;
+            }
+
+        } else if($cursor < -1) {
+            // if cursor is -ve fetch $count+2 items created after -$cursor-1
+            $cursor = abs($cursor);
+            $since_id = $cursor-1;
+
+            $fn_args = array_merge($args, array(0, $count+2, $since_id));
+            $list = call_user_func_array($fn, $fn_args);
+            while($list->fetch()) {
+                $items[] = clone($list);
+            }
+
+            $end = count($items)-1;
+            if ((isset($items[$end]->cursor) && $items[$end]->cursor == $cursor) ||
+                $items[$end]->id == $cursor) {
+                array_pop($items);
+                $next_cursor = isset($items[$end-1]->cursor) ?
+                    $items[$end-1]->cursor : $items[$end-1]->id;
+            } else {
+                $next_cursor = isset($items[$end]->cursor) ?
+                    $items[$end]->cursor : $items[$end]->id;
+                if ($end > $count) array_pop($items); // excess item.
+
+                // check if there are more items for next page
+                $fn_args = array_merge($args, array(0, 1, 0, $cursor));
+                $more = call_user_func_array($fn, $fn_args);
+                if (!$more->fetch() || empty($more)) {
+                    $next_cursor = 0;
+                }
+            }
+
+            if (count($items) == $count+1) {
+                // this means there is a previous page.
+                $prev = array_shift($items);
+                $prev_cursor = isset($prev->cursor) ?
+                    -$items[0]->cursor : -$items[0]->id;
+            }
+        } else if($cursor == -1) {
+            $fn_args = array_merge($args, array(0, $count+1));
+            $list = call_user_func_array($fn, $fn_args);
+
+            while($list->fetch()) {
+                $items[] = clone($list);
+            }
+
+            if (count($items)==$count+1) {
+                $next = array_pop($items);
+                if(isset($next->cursor)) {
+                    $next_cursor = $items[$count-1]->cursor;
+                } else {
+                    $next_cursor = $items[$count-1]->id;
+                }
+            }
+
+        }
+        return array($items, $next_cursor, $prev_cursor);
+    }
+
+    /**
+     * save a collection of people tags into the cache
+     *
+     * @param string $ckey  cache key
+     * @param Profile_list &$tag the results to store
+     * @param integer $offset   offset for slicing results
+     * @param integer $limit    maximum number of results
+     *
+     * @return boolean success
+     */
+
+    static function setCache($ckey, &$tag, $offset=0, $limit=null) {
+        $cache = Cache::instance();
+        if (empty($cache)) {
+            return false;
+        }
+        $str = '';
+        $tags = array();
+        while ($tag->fetch()) {
+            $str .= $tag->tagger . ':' . $tag->tag . ';';
+            $tags[] = clone($tag);
+        }
+        $str = substr($str, 0, -1);
+        if ($offset>=0 && !is_null($limit)) {
+            $tags = array_slice($tags, $offset, $limit);
+        }
+
+        $tag = new ArrayWrapper($tags);
+
+        return self::cacheSet($ckey, $str);
+    }
+
+    /**
+     * get people tags from the cache
+     *
+     * @param string $ckey  cache key
+     * @param integer $offset   offset for slicing
+     * @param integer $limit    limit
+     *
+     * @return Profile_list results
+     */
+
+    static function getCached($ckey, $offset=0, $limit=null) {
+
+        $keys_str = self::cacheGet($ckey);
+        if ($keys_str === false) {
+            return false;
+        }
+
+        $pairs = explode(';', $keys_str);
+        $keys = array();
+        foreach ($pairs as $pair) {
+            $keys[] = explode(':', $pair);
+        }
+
+        if ($offset>=0 && !is_null($limit)) {
+            $keys = array_slice($keys, $offset, $limit);
+        }
+        return self::getByKeys($keys);
+    }
+
+    /**
+     * get Profile_list objects from the database
+     * given their (tag, tagger) key pairs.
+     *
+     * @param array $keys   array of array(tagger, tag)
+     *
+     * @return Profile_list results
+     */
+
+    static function getByKeys($keys) {
+        $cache = Cache::instance();
+
+        if (!empty($cache)) {
+            $tags = array();
+
+            foreach ($keys as $key) {
+                $t = Profile_list::getByTaggerAndTag($key[0], $key[1]);
+                if (!empty($t)) {
+                    $tags[] = $t;
+                }
+            }
+            return new ArrayWrapper($tags);
+        } else {
+            $tag = new Profile_list();
+            if (empty($keys)) {
+                //if no IDs requested, just return the tag object
+                return $tag;
+            }
+
+            $pairs = array();
+            foreach ($keys as $key) {
+                $pairs[] = '(' . $key[0] . ', "' . $key[1] . '")';
+            }
+
+            $tag->whereAdd('(tagger, tag) in (' . implode(', ', $pairs) . ')');
+
+            $tag->find();
+
+            $temp = array();
+
+            while ($tag->fetch()) {
+                $temp[$tag->tagger.'-'.$tag->tag] = clone($tag);
+            }
+
+            $wrapped = array();
+
+            foreach ($keys as $key) {
+                $id = $key[0].'-'.$key[1];
+                if (array_key_exists($id, $temp)) {
+                    $wrapped[] = $temp[$id];
+                }
+            }
+
+            return new ArrayWrapper($wrapped);
+        }
+    }
+}
index ab6bab09649d5fbc7b0995a3e6a0dd143adb72e0..183f79145a136463472eb3dec305131ff22809b0 100644 (file)
@@ -22,31 +22,91 @@ class Profile_tag extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-    static function getTags($tagger, $tagged) {
-        $tags = array();
+    function pkeyGet($kv) {
+        return Memcached_DataObject::pkeyGet('Profile_tag', $kv);
+    }
+
+    function links()
+    {
+        return array('tagger,tag' => 'profile_list:tagger,tag');
+    }
+
+    function getMeta()
+    {
+        return Profile_list::pkeyGet(array('tagger' => $this->tagger, 'tag' => $this->tag));
+    }
+
+    static function getTags($tagger, $tagged, $auth_user=null) {
+
+        $profile_list = new Profile_list();
+        $include_priv = 1;
+
+        if (!($auth_user instanceof User ||
+            $auth_user instanceof Profile) ||
+            ($auth_user->id !== $tagger)) {
 
-        # XXX: store this in memcached
+            $profile_list->private = false;
+            $include_priv = 0;
+        }
+
+        $key = sprintf('profile_tag:tagger_tagged_privacy:%d-%d-%d', $tagger, $tagged, $include_priv);
+        $tags = Profile_list::getCached($key);
+        if ($tags !== false) {
+            return $tags;
+        }
 
         $profile_tag = new Profile_tag();
-        $profile_tag->tagger = $tagger;
+        $profile_list->tagger = $tagger;
         $profile_tag->tagged = $tagged;
 
-        $profile_tag->find();
+        $profile_list->selectAdd();
+
+        // only fetch id, tag, mainpage and
+        // private hoping this will be faster
+        $profile_list->selectAdd('profile_list.id, ' .
+                                 'profile_list.tag, ' .
+                                 'profile_list.mainpage, ' .
+                                 'profile_list.private');
+        $profile_list->joinAdd($profile_tag);
+        $profile_list->find();
+
+        Profile_list::setCache($key, $profile_list);
 
-        while ($profile_tag->fetch()) {
-            $tags[] = $profile_tag->tag;
+        return $profile_list;
+    }
+
+    static function getTagsArray($tagger, $tagged, $auth_user_id=null)
+    {
+        $ptag = new Profile_tag();
+        $ptag->tagger = $tagger;
+        $ptag->tagged = $tagged;
+
+        if ($tagger != $auth_user_id) {
+            $list = new Profile_list();
+            $list->private = false;
+            $ptag->joinAdd($list);
+            $ptag->selectAdd();
+            $ptag->selectAdd('profile_tag.tag');
         }
 
-        $profile_tag->free();
+        $tags = array();
+        $ptag->find();
+        while ($ptag->fetch()) {
+            $tags[] = $ptag->tag;
+        }
+        $ptag->free();
 
         return $tags;
     }
 
-    static function setTags($tagger, $tagged, $newtags) {
+    static function setTags($tagger, $tagged, $newtags, $privacy=array()) {
+
         $newtags = array_unique($newtags);
-        $oldtags = Profile_tag::getTags($tagger, $tagged);
+        $oldtags = self::getTagsArray($tagger, $tagged, $tagger);
+
+        $ptag = new Profile_tag();
 
-        # Delete stuff that's old that not in new
+        # Delete stuff that's in old and not in new
 
         $to_delete = array_diff($oldtags, $newtags);
 
@@ -54,48 +114,161 @@ class Profile_tag extends Memcached_DataObject
 
         $to_insert = array_diff($newtags, $oldtags);
 
-        $profile_tag = new Profile_tag();
+        foreach ($to_delete as $deltag) {
+            self::unTag($tagger, $tagged, $deltag);
+        }
 
-        $profile_tag->tagger = $tagger;
-        $profile_tag->tagged = $tagged;
+        foreach ($to_insert as $instag) {
+            $private = isset($privacy[$instag]) ? $privacy[$instag] : false;
+            self::setTag($tagger, $tagged, $instag, null, $private);
+        }
+        return true;
+    }
+
+    # set a single tag
+    static function setTag($tagger, $tagged, $tag, $desc=null, $private=false) {
+
+        $ptag = Profile_tag::pkeyGet(array('tagger' => $tagger,
+                                           'tagged' => $tagged,
+                                           'tag' => $tag));
+
+        # if tag already exists, return it
+        if(!empty($ptag)) {
+            return $ptag;
+        }
+
+        $tagger_profile = Profile::staticGet('id', $tagger);
+        $tagged_profile = Profile::staticGet('id', $tagged);
+
+        if (Event::handle('StartTagProfile', array($tagger_profile, $tagged_profile, $tag))) {
+
+            if (!$tagger_profile->canTag($tagged_profile)) {
+                throw new ClientException(_('You cannot tag this user.'));
+                return false;
+            }
+
+            $tags = new Profile_list();
+            $tags->tagger = $tagger;
+            $count = (int) $tags->count('distinct tag');
+
+            if ($count >= common_config('peopletag', 'maxtags')) {
+                throw new ClientException(sprintf(_('You already have created %d or more tags ' .
+                                                    'which is the maximum allowed number of tags. ' .
+                                                    'Try using or deleting some existing tags.'),
+                                                    common_config('peopletag', 'maxtags')));
+                return false;
+            }
+
+            $plist = new Profile_list();
+            $plist->query('BEGIN');
+
+            $profile_list = Profile_list::ensureTag($tagger, $tag, $desc, $private);
+
+            if ($profile_list->taggedCount() >= common_config('peopletag', 'maxpeople')) {
+                throw new ClientException(sprintf(_('You already have %d or more people tagged %s ' .
+                                                    'which is the maximum allowed number.' .
+                                                    'Try untagging others with the same tag first.'),
+                                                    common_config('peopletag', 'maxpeople'), $tag));
+                return false;
+            }
+
+            $newtag = new Profile_tag();
+
+            $newtag->tagger = $tagger;
+            $newtag->tagged = $tagged;
+            $newtag->tag = $tag;
+
+            $result = $newtag->insert();
 
-        $profile_tag->query('BEGIN');
 
-        foreach ($to_delete as $deltag) {
-            $profile_tag->tag = $deltag;
-            $result = $profile_tag->delete();
             if (!$result) {
-                common_log_db_error($profile_tag, 'DELETE', __FILE__);
+                common_log_db_error($newtag, 'INSERT', __FILE__);
+                return false;
+            }
+
+            try {
+                $plist->query('COMMIT');
+                Event::handle('EndTagProfile', array($newtag));
+            } catch (Exception $e) {
+                $newtag->delete();
+                $profile_list->delete();
+                throw $e;
                 return false;
             }
+
+            $profile_list->taggedCount(true);
+            self::blowCaches($tagger, $tagged);
         }
 
-        foreach ($to_insert as $instag) {
-            $profile_tag->tag = $instag;
-            $result = $profile_tag->insert();
+        return $newtag;
+    }
+
+    static function unTag($tagger, $tagged, $tag) {
+        $ptag = Profile_tag::pkeyGet(array('tagger' => $tagger,
+                                           'tagged' => $tagged,
+                                           'tag'    => $tag));
+        if (!$ptag) {
+            return true;
+        }
+
+        if (Event::handle('StartUntagProfile', array($ptag))) {
+            $orig = clone($ptag);
+            $result = $ptag->delete();
             if (!$result) {
-                common_log_db_error($profile_tag, 'INSERT', __FILE__);
+                common_log_db_error($this, 'DELETE', __FILE__);
                 return false;
             }
+            Event::handle('EndUntagProfile', array($orig));
+            if ($result) {
+                $profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger));
+                $profile_list->taggedCount(true);
+                self::blowCaches($tagger, $tagged);
+                return true;
+            }
+            return false;
         }
+    }
+
+    // @fixme: move this to Profile_list?
+    static function cleanup($profile_list) {
+        $ptag = new Profile_tag();
+        $ptag->tagger = $profile_list->tagger;
+        $ptag->tag = $profile_list->tag;
+        $ptag->find();
+
+        while($ptag->fetch()) {
+            if (Event::handle('StartUntagProfile', array($ptag))) {
+                $orig = clone($ptag);
+                $result = $ptag->delete();
+                if (!$result) {
+                    common_log_db_error($this, 'DELETE', __FILE__);
+                }
+                Event::handle('EndUntagProfile', array($orig));
+            }
+        }
+    }
 
-        $profile_tag->query('COMMIT');
+    // move a tag!
+    static function moveTag($orig, $new) {
+        $tags = new Profile_tag();
+        $qry = 'UPDATE profile_tag SET ' .
+               'tag = "%s", tagger = "%s" ' .
+               'WHERE tag = "%s" ' .
+               'AND tagger = "%s"';
+        $result = $tags->query(sprintf($qry, $new->tag, $new->tagger,
+                                             $orig->tag, $orig->tagger));
 
+        if (!$result) {
+            common_log_db_error($tags, 'UPDATE', __FILE__);
+            return false;
+        }
         return true;
     }
 
-    # Return profiles with a given tag
-    static function getTagged($tagger, $tag) {
-        $profile = new Profile();
-        $profile->query('SELECT profile.* ' .
-                        'FROM profile JOIN profile_tag ' .
-                        'ON profile.id = profile_tag.tagged ' .
-                        'WHERE profile_tag.tagger = ' . $tagger . ' ' .
-                        'AND profile_tag.tag = "' . $tag . '" ');
-        $tagged = array();
-        while ($profile->fetch()) {
-            $tagged[] = clone($profile);
+    static function blowCaches($tagger, $tagged) {
+        foreach (array(0, 1) as $perm) {
+            self::blow(sprintf('profile_tag:tagger_tagged_privacy:%d-%d-%d', $tagger, $tagged, $perm));
         }
-        return $tagged;
+        return true;
     }
 }
diff --git a/classes/Profile_tag_inbox.php b/classes/Profile_tag_inbox.php
new file mode 100644 (file)
index 0000000..dd517b3
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Table Definition for profile_tag_inbox
+ */
+
+class Profile_tag_inbox extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'profile_tag_inbox';                     // table name
+    public $profile_tag_id;                        // int(4)  primary_key not_null
+    public $notice_id;                       // int(4)  primary_key not_null
+    public $created;                         // datetime()   not_null
+
+    /* Static get */
+
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile_tag_inbox',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    function pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('Profile_tag_inbox', $kv);
+    }
+}
diff --git a/classes/Profile_tag_subscription.php b/classes/Profile_tag_subscription.php
new file mode 100644 (file)
index 0000000..870640c
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+/**
+ * Table Definition for profile_tag_subscription
+ */
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class Profile_tag_subscription extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'profile_tag_subscription';                     // table name
+    public $profile_tag_id;                         // int(4)  not_null
+    public $profile_id;                             // int(4)  not_null
+    public $created;                                // datetime   not_null default_0000-00-00%2000%3A00%3A00
+    public $modified;                               // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    /* Static get */
+    function staticGet($k,$v=null)
+    { return Memcached_DataObject::staticGet('Profile_tag_subscription',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    function pkeyGet($kv)
+    {
+        return Memcached_DataObject::pkeyGet('Profile_tag_subscription', $kv);
+    }
+
+    static function add($peopletag, $profile)
+    {
+        if ($peopletag->private) {
+            return false;
+        }
+
+        if (Event::handle('StartSubscribePeopletag', array($peopletag, $profile))) {
+            $args = array('profile_tag_id' => $peopletag->id,
+                          'profile_id' => $profile->id);
+            $existing = Profile_tag_subscription::pkeyGet($args);
+            if(!empty($existing)) {
+                return $existing;
+            }
+
+            $sub = new Profile_tag_subscription();
+            $sub->profile_tag_id = $peopletag->id;
+            $sub->profile_id = $profile->id;
+            $sub->created = common_sql_now();
+
+            $result = $sub->insert();
+
+            if (!$result) {
+                common_log_db_error($sub, 'INSERT', __FILE__);
+                throw new Exception(_("Adding people tag subscription failed."));
+            }
+
+            $ptag = Profile_list::staticGet('id', $peopletag->id);
+            $ptag->subscriberCount(true);
+
+            Event::handle('EndSubscribePeopletag', array($peopletag, $profile));
+            return $ptag;
+        }
+    }
+
+    static function remove($peopletag, $profile)
+    {
+        $sub = Profile_tag_subscription::pkeyGet(array('profile_tag_id' => $peopletag->id,
+                                              'profile_id' => $profile->id));
+
+        if (empty($sub)) {
+            // silence is golden?
+            return true;
+        }
+
+        if (Event::handle('StartUnsubscribePeopletag', array($peopletag, $profile))) {
+            $result = $sub->delete();
+
+            if (!$result) {
+                common_log_db_error($sub, 'DELETE', __FILE__);
+                throw new Exception(_("Removing people tag subscription failed."));
+            }
+
+            $peopletag->subscriberCount(true);
+
+            Event::handle('EndUnsubscribePeopletag', array($peopletag, $profile));
+            return true;
+        }
+    }
+
+    // called if a tag gets deleted / made private
+    static function cleanup($profile_list) {
+        $subs = new self();
+        $subs->profile_tag_id = $profile_list->id;
+        $subs->find();
+
+        while($subs->fetch()) {
+            $profile = Profile::staticGet('id', $subs->profile_id);
+            Event::handle('StartUnsubscribePeopletag', array($profile_list, $profile));
+            // Delete anyway
+            $subs->delete();
+            Event::handle('StartUnsubscribePeopletag', array($profile_list, $profile));
+        }
+    }
+}
index 970e167a3bdd6372e745f2bf38b16c9dd0d51da1..be362e67fa9042d392aa805e73e1e7c1e17f07cc 100644 (file)
@@ -504,12 +504,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)
index bf8d173805b9b50446769dded95c242a46d451db..6b68dfe713e9eee1d8157cbbac99273b9f77214f 100644 (file)
@@ -460,6 +460,43 @@ tagger = K
 tagged = K
 tag = K
 
+[profile_list]
+id = 129
+tagger = 129
+tag = 130
+description = 34
+private = 17
+created = 142
+modified = 384
+uri = 130
+mainpage = 130
+tagged_count = 129
+subscriber_count = 129
+
+[profile_list__keys]
+id = U
+tagger = K
+tag = K
+
+[profile_tag_inbox]
+profile_tag_id = 129
+notice_id = 129
+created = 142
+
+[profile_tag_inbox__keys]
+profile_tag_id = K
+notice_id = K
+
+[profile_tag_subscription]
+profile_tag_id = 129
+profile_id = 129
+created = 142
+modified = 384 
+
+[profile_tag_subscription__keys]
+profile_tag_id = K
+profile_id = K
+
 [queue_item]
 id = 129
 frame = 194
index b9dd5af0c9b6ab284653422d3ef5c5d3c3f29cb3..28bf03fd452ed5b157ef84fcb57e51b4c534f2e3 100644 (file)
@@ -55,3 +55,23 @@ file_id = file:id
 file_id = file:id
 post_id = notice:id
 
+[profile_list]
+tagger = profile:id
+
+[profile_tag]
+tagger = profile:id
+tagged = profile:id
+; in class definition:
+;tag,tagger = profile_list:tag,tagger
+
+[profile_list]
+tagger = profile:id
+
+[profile_tag_inbox]
+profile_tag_id = profile_list:id
+notice_id = notice:id
+
+[profile_tag_subscription]
+profile_tag_id = profile_list:id
+profile_id = profile:id
+