]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
OStatus/FeedSub: tweaked PuSH feed garbage collection so other plugins can declare...
authorBrion Vibber <brion@status.net>
Fri, 6 Aug 2010 17:56:18 +0000 (10:56 -0700)
committerBrion Vibber <brion@status.net>
Fri, 6 Aug 2010 18:49:52 +0000 (11:49 -0700)
SubMirror: redid add-mirror frontend to accept a feed URL, then pass that on to OStatus, instead of pulling from your subscriptions.
Profile: tweaked subscriberCount() so it doesn't subtract 1 for foreign profiles who aren't subscribed to themselves; instead excludes the self-subscription in the count query.
Memcached_DataObject: tweak to avoid extra error spew in the DB error raising

Work in progress: tweaking feedsub garbage collection so we can count other uses

classes/Memcached_DataObject.php
classes/Profile.php
plugins/OStatus/OStatusPlugin.php
plugins/OStatus/classes/FeedSub.php
plugins/OStatus/classes/Ostatus_profile.php
plugins/SubMirror/SubMirrorPlugin.php
plugins/SubMirror/actions/addmirror.php
plugins/SubMirror/actions/editmirror.php
plugins/SubMirror/actions/mirrorsettings.php
plugins/SubMirror/lib/addmirrorform.php

index 7768fe757147c7493c5c662a971e57e78ae13ce8..0f1ed04892a61335b7913177e385bcd32bfd56a3 100644 (file)
@@ -574,7 +574,7 @@ class Memcached_DataObject extends Safe_DataObject
     function raiseError($message, $type = null, $behaviour = null)
     {
         $id = get_class($this);
-        if ($this->id) {
+        if (!empty($this->id)) {
             $id .= ':' . $this->id;
         }
         if ($message instanceof PEAR_Error) {
index 0d0463b730564179e7f890921fa3da24844394db..d7617f0b74c0a27afbbd67ee20bb1ef7c03d1bf4 100644 (file)
@@ -464,11 +464,9 @@ class Profile extends Memcached_DataObject
 
         $sub = new Subscription();
         $sub->subscribed = $this->id;
-
+        $sub->whereAdd('subscriber != subscribed');
         $cnt = (int) $sub->count('distinct subscriber');
 
-        $cnt = ($cnt > 0) ? $cnt - 1 : $cnt;
-
         if (!empty($c)) {
             $c->set(common_cache_key('profile:subscriber_count:'.$this->id), $cnt);
         }
index 70971c5b34dab4e29afe5f37320f3a880e4d5d36..3b073a5d13dd8e19f38c960a586e43c96c10febc 100644 (file)
@@ -479,6 +479,24 @@ class OStatusPlugin extends Plugin
         }
     }
 
+    /**
+     * Tell the FeedSub infrastructure whether we have any active OStatus
+     * usage for the feed; if not it'll be able to garbage-collect the
+     * feed subscription.
+     * 
+     * @param FeedSub $feedsub
+     * @param integer $count in/out
+     * @return mixed hook return code
+     */
+    function onFeedSubSubscriberCount($feedsub, &$count)
+    {
+        $oprofile = Ostatus_profile::staticGet('feeduri', $feedsub->uri);
+        if ($oprofile) {
+            $count += $oprofile->subscriberCount();
+        }
+        return true;
+    }
+
     /**
      * When about to subscribe to a remote user, start a server-to-server
      * PuSH subscription if needed. If we can't establish that, abort.
index b10509dae6c31806c7f182781a5c4bb75932161d..9cd35e29c9fa51c46d76538bae8187b843eea418 100644 (file)
@@ -255,6 +255,9 @@ class FeedSub extends Memcached_DataObject
     /**
      * Send a PuSH unsubscription request to the hub for this feed.
      * The hub will later send us a confirmation POST to /main/push/callback.
+     * Warning: this will cancel the subscription even if someone else in
+     * the system is using it. Most callers will want garbageCollect() instead,
+     * which confirms there's no uses left.
      *
      * @return bool true on success, false on failure
      * @throws ServerException if feed state is not valid
@@ -275,6 +278,33 @@ class FeedSub extends Memcached_DataObject
         return $this->doSubscribe('unsubscribe');
     }
 
+    /**
+     * Check if there are any active local uses of this feed, and if not then
+     * make sure it's inactive, unsubscribing if necessary.
+     *
+     * @return boolean true if the subscription is now inactive, false if still active.
+     */
+    public function garbageCollect()
+    {
+        if ($this->sub_state == '' || $this->sub_state == 'inactive') {
+            // No active PuSH subscription, we can just leave it be.
+            return true;
+        } else {
+            // PuSH subscription is either active or in an indeterminate state.
+            // Check if we're out of subscribers, and if so send an unsubscribe.
+            $count = 0;
+            Event::handle('FeedSubSubscriberCount', array($this, &$count));
+
+            if ($count) {
+                common_log(LOG_INFO, __METHOD__ . ': ok, ' . $count . ' user(s) left for ' . $this->uri);
+                return false;
+            } else {
+                common_log(LOG_INFO, __METHOD__ . ': unsubscribing, no users left for ' . $this->uri);
+                return $this->unsubscribe();
+            }
+        }
+    }
+
     protected function doSubscribe($mode)
     {
         $orig = clone($this);
index 5d3f37cd07e00f4bdeb1349917639020a7ea0cb3..2d7c632e6391151bd46dda8923ce96ea6b4ff7d6 100644 (file)
@@ -215,22 +215,13 @@ class Ostatus_profile extends Memcached_DataObject
     }
 
     /**
-     * Send a PuSH unsubscription request to the hub for this feed.
-     * The hub will later send us a confirmation POST to /main/push/callback.
+     * Check if this remote profile has any active local subscriptions, and
+     * if not drop the PuSH subscription feed.
      *
      * @return bool true on success, false on failure
-     * @throws ServerException if feed state is not valid
      */
     public function unsubscribe() {
-        $feedsub = FeedSub::staticGet('uri', $this->feeduri);
-        if (!$feedsub || $feedsub->sub_state == '' || $feedsub->sub_state == 'inactive') {
-            // No active PuSH subscription, we can just leave it be.
-            return true;
-        } else {
-            // PuSH subscription is either active or in an indeterminate state.
-            // Send an unsubscribe.
-            return $feedsub->unsubscribe();
-        }
+        $this->garbageCollect();
     }
 
     /**
@@ -240,6 +231,21 @@ class Ostatus_profile extends Memcached_DataObject
      * @return boolean
      */
     public function garbageCollect()
+    {
+        $feedsub = FeedSub::staticGet('uri', $this->feeduri);
+        return $feedsub->garbageCollect();
+    }
+
+    /**
+     * Check if this remote profile has any active local subscriptions, so the
+     * PuSH subscription layer can decide if it can drop the feed.
+     *
+     * This gets called via the FeedSubSubscriberCount event when running
+     * FeedSub::garbageCollect().
+     *
+     * @return int
+     */
+    public function subscriberCount()
     {
         if ($this->isGroup()) {
             $members = $this->localGroup()->getMembers(0, 1);
@@ -247,13 +253,14 @@ class Ostatus_profile extends Memcached_DataObject
         } else {
             $count = $this->localProfile()->subscriberCount();
         }
-        if ($count == 0) {
-            common_log(LOG_INFO, "Unsubscribing from now-unused remote feed $this->feeduri");
-            $this->unsubscribe();
-            return true;
-        } else {
-            return false;
-        }
+        common_log(LOG_INFO, __METHOD__ . " SUB COUNT BEFORE: $count");
+
+        // Other plugins may be piggybacking on OStatus without having
+        // an active group or user-to-user subscription we know about.
+        Event::handle('Ostatus_profileSubscriberCount', array($this, &$count));
+        common_log(LOG_INFO, __METHOD__ . " SUB COUNT AFTER: $count");
+
+        return $count;
     }
 
     /**
index 8678cc3dd0be92bb57bc2eb51e98b5422e9aeb07..00c04ad0b6493074a2217125f05538abbcbaae20 100644 (file)
@@ -146,4 +146,27 @@ class SubMirrorPlugin extends Plugin
                          array('href' => common_local_url('mirrorsettings')),
                          _m('Set up mirroring options...'));
     }
+
+    /**
+     * Let the OStatus subscription garbage collection know if we're
+     * making use of a remote feed, so it doesn't get dropped out
+     * from under us.
+     *
+     * @param Ostatus_profile $oprofile
+     * @param int $count in/out
+     * @return mixed hook return value
+     */
+    function onOstatus_profileSubscriberCount($oprofile, &$count)
+    {
+        if ($oprofile->profile_id) {
+            $mirror = new SubMirror();
+            $mirror->subscribed = $oprofile->profile_id;
+            if ($mirror->find()) {
+                while ($mirror->fetch()) {
+                    $count++;
+                }
+            }
+        }
+        return true;
+    }
 }
index df6939491fdbf595ba8a5b9c51e96933f087e55d..5acdf1dfe498c97d43eef019e00a21c3e095049f 100644 (file)
@@ -46,9 +46,8 @@ if (!defined('STATUSNET')) {
  * @link      http://status.net/
  */
 
-class AddMirrorAction extends Action
+class AddMirrorAction extends BaseMirrorAction
 {
-    var $user;
     var $feedurl;
 
     /**
@@ -62,94 +61,17 @@ class AddMirrorAction extends Action
     function prepare($args)
     {
         parent::prepare($args);
-        $ok = $this->sharedBoilerplate();
-        if ($ok) {
-            // and now do something useful!
-            $this->profile = $this->validateProfile($this->trimmed('profile'));
-            return true;
-        } else {
-            return $ok;
-        }
-    }
-
-    function validateProfile($id)
-    {
-        $id = intval($id);
-        $profile = Profile::staticGet('id', $id);
-        if ($profile && $profile->id != $this->user->id) {
-            return $profile;
-        }
-        // TRANS: Error message returned to user when setting up feed mirroring, but we were unable to resolve the given URL to a working feed.
-        $this->clientError(_m("Invalid profile for mirroring."));
-    }
-
-    /**
-     * @fixme none of this belongs in end classes
-     * this stuff belongs in shared code!
-     */
-    function sharedBoilerplate()
-    {
-        // Only allow POST requests
-
-        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
-            $this->clientError(_('This action only accepts POST requests.'));
-            return false;
-        }
-
-        // CSRF protection
-
-        $token = $this->trimmed('token');
-
-        if (!$token || $token != common_session_token()) {
-            $this->clientError(_('There was a problem with your session token.'.
-                                 ' Try again, please.'));
-            return false;
-        }
-
-        // Only for logged-in users
-
-        $this->user = common_current_user();
-
-        if (empty($this->user)) {
-            $this->clientError(_('Not logged in.'));
-            return false;
-        }
+        $this->feedurl = $this->validateFeedUrl($this->trimmed('feedurl'));
+        $this->profile = $this->profileForFeed($this->feedurl);
         return true;
     }
 
-    /**
-     * Handle request
-     *
-     * Does the subscription and returns results.
-     *
-     * @param Array $args unused.
-     *
-     * @return void
-     */
-
-    function handle($args)
+    function saveMirror()
     {
-        // Throws exception on error
-        $this->saveMirror();
-
-        if ($this->boolean('ajax')) {
-            $this->startHTML('text/xml;charset=utf-8');
-            $this->elementStart('head');
-            $this->element('title', null, _('Subscribed'));
-            $this->elementEnd('head');
-            $this->elementStart('body');
-            $unsubscribe = new EditMirrorForm($this, $this->profile);
-            $unsubscribe->show();
-            $this->elementEnd('body');
-            $this->elementEnd('html');
+        if ($this->oprofile->subscribe()) {
+            SubMirror::saveMirror($this->user, $this->profile);
         } else {
-            $url = common_local_url('mirrorsettings');
-            common_redirect($url, 303);
+            $this->serverError(_m("Could not subscribe to feed."));
         }
     }
-
-    function saveMirror()
-    {
-        SubMirror::saveMirror($this->user, $this->profile);
-    }
 }
index 7ddd32ef359a327cbabb031d3c575a4a1bf5aa9a..c7fdab0d67d4552898a51bddc786d20f878a5dee 100644 (file)
@@ -46,7 +46,7 @@ if (!defined('STATUSNET')) {
  * @link      http://status.net/
  */
 
-class EditMirrorAction extends AddMirrorAction
+class EditMirrorAction extends BaseMirrorAction
 {
 
     /**
@@ -60,6 +60,9 @@ class EditMirrorAction extends AddMirrorAction
     function prepare($args)
     {
         parent::prepare($args);
+
+        $this->profile = $this->validateProfile($this->trimmed('profile'));
+
         $this->mirror = SubMirror::pkeyGet(array('subscriber' => $this->user->id,
                                                  'subscribed' => $this->profile->id));
 
@@ -95,6 +98,10 @@ class EditMirrorAction extends AddMirrorAction
 
         if ($this->delete) {
             $mirror->delete();
+            $oprofile = Ostatus_profile::staticGet('profile_id', $this->profile->id);
+            if ($oprofile) {
+                $oprofile->garbageCollect();
+            }
         } else if ($this->style != $mirror->style) {
             $orig = clone($mirror);
             $mirror->style = $this->style;
index edb024183dc22444b9e8c3638662647e9ffcd107..5463a8dc0c7cbb4094cdef729d25d724956aeec4 100644 (file)
@@ -50,7 +50,7 @@ class MirrorSettingsAction extends AccountSettingsAction
 
     function getInstructions()
     {
-        return _m('You can mirror updates from your RSS and Atom feeds ' .
+        return _m('You can mirror updates from many RSS and Atom feeds ' .
                   'into your StatusNet timeline!');
     }
 
@@ -65,6 +65,8 @@ class MirrorSettingsAction extends AccountSettingsAction
     function showContent()
     {
         $user = common_current_user();
+
+        $this->showAddFeedForm();
         
         $mirror = new SubMirror();
         $mirror->subscriber = $user->id;
@@ -73,7 +75,6 @@ class MirrorSettingsAction extends AccountSettingsAction
                 $this->showFeedForm($mirror);
             }
         }
-        $this->showAddFeedForm();
     }
 
     function showFeedForm($mirror)
index 9ee59661a5718878b8009d855052b54a3de40c0f..0a798c9eaf9f10d8bc9ec81d67aca99b81614652 100644 (file)
@@ -57,46 +57,15 @@ class AddMirrorForm extends Form
         $this->out->elementStart('ul');
 
         $this->li();
-        $this->out->element('label', array('for' => $this->id() . '-profile'),
-                            _m("Mirror one of your existing subscriptions:"));
-        $this->out->elementStart('select', array('name' => 'profile'));
-
-        $user = common_current_user();
-        $profile = $user->getSubscriptions();
-        while ($profile->fetch()) {
-            $mirror = SubMirror::pkeyGet(array('subscriber' => $user->id,
-                                               'subscribed' => $profile->id));
-            if (!$mirror) {
-                $this->out->element('option',
-                                    array('value' => $profile->id),
-                                    $profile->getBestName());
-            }
-        }
-        $this->out->elementEnd('select');
-        $this->out->submit($this->id() . '-save', _m('Save'));
+        $this->doInput('addmirror-feedurl',
+                       'feedurl',
+                       _m('Web page or feed URL:'),
+                       $this->out->trimmed('feedurl'));
         $this->unli();
 
-
         $this->li();
-
-        $this->out->elementStart('fieldset', array('style' => 'width: 360px; margin-left: auto; margin-right: auto'));
-        $this->out->element('p', false,
-                            _m("Not already subscribed to the feed you want? " .
-                               "Add a new remote subscription and paste in the URL!"));
-
-        $this->out->elementStart('div', 'entity_actions');
-        $this->out->elementStart('p', array('id' => 'entity_remote_subscribe',
-                                         'class' => 'entity_subscribe'));
-        $this->out->element('a', array('href' => common_local_url('ostatussub'),
-                                    'class' => 'entity_remote_subscribe')
-                            , _m('Remote'));
-        $this->out->elementEnd('p');
-        $this->out->elementEnd('div');
-
-        $this->out->element('div', array('style' => 'clear: both'));
-        $this->out->elementEnd('fieldset');
+        $this->out->submit('addmirror-save', _m('Add feed'));
         $this->unli();
-        
         $this->out->elementEnd('ul');
         $this->out->elementEnd('fieldset');
     }
@@ -106,7 +75,8 @@ class AddMirrorForm extends Form
         $this->out->element('label', array('for' => $id), $label);
         $attrs = array('name' => $name,
                        'type' => 'text',
-                       'id' => $id);
+                       'id' => $id,
+                       'style' => 'width: 80%');
         if ($value) {
             $attrs['value'] = $value;
         }