From 07f71a66f5372c4d799e6656433726c0c7a19c48 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 11 Sep 2009 21:28:22 +0000 Subject: [PATCH 01/36] Extremely nascent RSSCloud plugin --- plugins/RSSCloud/RSSCloudPlugin.php | 169 ++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 plugins/RSSCloud/RSSCloudPlugin.php diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php new file mode 100644 index 0000000000..816046ffbb --- /dev/null +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -0,0 +1,169 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +define('RSSCLOUDPLUGIN_VERSION', '0.1'); + +class RSSCloudPlugin extends Plugin +{ + function __construct() + { + parent::__construct(); + } + + function onInitializePlugin(){ + $this->domain = common_config('rsscloud', 'domain'); + $this->port = common_config('rsscloud', 'port'); + $this->path = common_config('rsscloud', 'path'); + $this->funct = common_config('rsscloud', 'function'); + $this->protocol = common_config('rsscloud', 'protocol'); + + // set defaults + + if (empty($this->domain)) { + $this->domain = 'rpc.rsscloud.org'; + } + + if (empty($this->port)) { + $this->port = '5337'; + } + + if (empty($this->path)) { + $this->path = '/rsscloud/pleaseNotify'; + } + + if (empty($this->funct)) { + $this->funct = ''; + } + + if (empty($this->protocol)) { + $this->protocol = 'http-post'; + } + } + + function onStartApiRss($action){ + + $attrs = array('domain' => $this->domain, + 'port' => $this->port, + 'path' => $this->path, + 'registerProcedure' => $this->funct, + 'protocol' => $this->protocol); + + // Dipping into XMLWriter to avoid a full end element (). + + $action->xw->startElement('cloud'); + foreach ($attrs as $name => $value) { + $action->xw->writeAttribute($name, $value); + } + $action->xw->endElement('cloud'); + + } + + function onEndNoticeSave($notice){ + + $user = User::staticGet('id', $notice->profile_id); + $rss = common_local_url('api', array('apiaction' => 'statuses', + 'method' => 'user_timeline', + 'argument' => $user->nickname . '.rss')); + + $notifier = new CloudNotifier(); + $notifier->notify($rss); + } + + +} + + +class CloudNotifier { + + + function notify($feed) { + common_debug("CloudNotifier->notify: $feed"); + + $params = 'url=' . urlencode($feed); + + $result = $this->httpPost('http://rpc.rsscloud.org:5337/rsscloud/ping', + $params); + + if ($result) { + common_debug('success notifying cloud'); + } else { + common_debug('failure notifying cloud'); + } + + } + + function userAgent() + { + return 'rssCloudPlugin/' . RSSCLOUDPLUGIN_VERSION . + ' StatusNet/' . STATUSNET_VERSION; + } + + + private function httpPost($url, $params) { + + + common_debug('params: ' . var_export($params, true)); + + $options = array(CURLOPT_URL => $url, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $params, + CURLOPT_USERAGENT => $this->userAgent(), + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FAILONERROR => true, + CURLOPT_HEADER => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_TIMEOUT => 5); + + $ch = curl_init(); + curl_setopt_array($ch, $options); + + $response = curl_exec($ch); + + + + $info = curl_getinfo($ch); + + curl_close($ch); + + common_debug('curl response: ' . var_export($response, true)); + common_debug('curl info: ' . var_export($info, true)); + + if ($info['http_code'] == 200) { + return true; + } else { + return false; + } + } + +} \ No newline at end of file From 4e033138b31ffbc9a0f7d75d8bea7518de617b6c Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 14 Sep 2009 01:39:54 +0000 Subject: [PATCH 02/36] Test action to simulate an aggregator. Useful for checking that the cloud hub is sending notifications. --- plugins/RSSCloud/LoggingAggregator.php | 85 ++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 plugins/RSSCloud/LoggingAggregator.php diff --git a/plugins/RSSCloud/LoggingAggregator.php b/plugins/RSSCloud/LoggingAggregator.php new file mode 100644 index 0000000000..2573d9343e --- /dev/null +++ b/plugins/RSSCloud/LoggingAggregator.php @@ -0,0 +1,85 @@ + + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * 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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +class LoggingAggregatorAction extends Action +{ + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + function prepare($args) + { + parent::prepare($args); + return true; + } + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->showError('This resource requires an HTTP POST.'); + } + + $this->url = $this->arg('url'); + + if (empty($this->url)) { + $this->showError('Hey, you have to provide a url parameter.'); + } + + $this->ip = $_SERVER['REMOTE_ADDR']; + + common_log(LOG_INFO, 'RSSCloud Logging Aggregator - ' . $this->ip . ' claims the feed at ' . + $this->url . ' has been updated.'); + + header('Content-Type: text/xml'); + echo '' . "\n"; + + } + + function showError($msg) + { + header('HTTP/1.1 403 Forbidden'); + header('Content-Type: text/xml'); + echo "\n"; + echo "\n"; + exit(); + } + +} \ No newline at end of file From 391003c3c65572dd156244390d8eedd2dfd96f90 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 16 Sep 2009 19:20:25 +0000 Subject: [PATCH 03/36] Some foundational work. Not much to see here. Move along. --- plugins/RSSCloud/RSSCloudNotifier.php | 93 +++++++++++++ plugins/RSSCloud/RSSCloudPlugin.php | 141 +++++++++----------- plugins/RSSCloud/RSSCloudRequestNotify.php | 145 +++++++++++++++++++++ 3 files changed, 299 insertions(+), 80 deletions(-) create mode 100644 plugins/RSSCloud/RSSCloudNotifier.php create mode 100644 plugins/RSSCloud/RSSCloudRequestNotify.php diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php new file mode 100644 index 0000000000..65bfd032e9 --- /dev/null +++ b/plugins/RSSCloud/RSSCloudNotifier.php @@ -0,0 +1,93 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +class RSSCloudNotifier { + + function postUpdate($endpoint, $feed) { + common_debug("CloudNotifier->notify: $feed"); + + $params = 'url=' . urlencode($feed); + + $result = $this->httpPost($endpoint, $params); + + if ($result) { + common_debug('success notifying cloud'); + } else { + common_debug('failure notifying cloud'); + } + + } + + function userAgent() + { + return 'rssCloudPlugin/' . RSSCLOUDPLUGIN_VERSION . + ' StatusNet/' . STATUSNET_VERSION; + } + + private function httpPost($url, $params) { + + common_debug('params: ' . var_export($params, true)); + + $options = array(CURLOPT_URL => $url, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $params, + CURLOPT_USERAGENT => $this->userAgent(), + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FAILONERROR => true, + CURLOPT_HEADER => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_TIMEOUT => 5); + + $ch = curl_init(); + curl_setopt_array($ch, $options); + + $response = curl_exec($ch); + + $info = curl_getinfo($ch); + + curl_close($ch); + + common_debug('curl response: ' . var_export($response, true)); + common_debug('curl info: ' . var_export($info, true)); + + if ($info['http_code'] == 200) { + return true; + } else { + return false; + } + } + +} + + diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index 816046ffbb..50d9060efe 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -33,30 +33,33 @@ if (!defined('STATUSNET')) { define('RSSCLOUDPLUGIN_VERSION', '0.1'); -class RSSCloudPlugin extends Plugin +class RSSCloudPlugin extends Plugin { function __construct() { parent::__construct(); } - + function onInitializePlugin(){ + + common_debug("RSSCloudPlugin onInitializePlugin()"); + $this->domain = common_config('rsscloud', 'domain'); $this->port = common_config('rsscloud', 'port'); $this->path = common_config('rsscloud', 'path'); $this->funct = common_config('rsscloud', 'function'); $this->protocol = common_config('rsscloud', 'protocol'); - + // set defaults - + if (empty($this->domain)) { $this->domain = 'rpc.rsscloud.org'; } - + if (empty($this->port)) { $this->port = '5337'; } - + if (empty($this->path)) { $this->path = '/rsscloud/pleaseNotify'; } @@ -69,9 +72,47 @@ class RSSCloudPlugin extends Plugin $this->protocol = 'http-post'; } } - + + /** + * Add RSSCloud-related paths to the router table + * + * Hook for RouterInitialized event. + * + * @return boolean hook return + */ + + function onRouterInitialized(&$m) + { + $m->connect('rsscloud/request_notify', array('action' => 'RSSCloudRequestNotify')); + $m->connect('rsscloud/notify', array('action' => 'LoggingAggregator')); + + return true; + } + + function onAutoload($cls) + { + common_debug("onAutoload() $cls"); + + switch ($cls) + { + + case 'RSSCloudNotifier': + require_once(INSTALLDIR . '/plugins/RSSCloud/RSSCloudNotifier.php'); + return false; + case 'RSSCloudRequestNotifyAction': + case 'LoggingAggregatorAction': + common_debug(mb_substr($cls, 0, -6) . '.php'); + require_once(INSTALLDIR . '/plugins/RSSCloud/' . mb_substr($cls, 0, -6) . '.php'); + return false; + default: + return true; + } + } + function onStartApiRss($action){ - + + // XXX: No sure we want every feed to be cloud enabled + $attrs = array('domain' => $this->domain, 'port' => $this->port, 'path' => $this->path, @@ -79,91 +120,31 @@ class RSSCloudPlugin extends Plugin 'protocol' => $this->protocol); // Dipping into XMLWriter to avoid a full end element (). - + $action->xw->startElement('cloud'); foreach ($attrs as $name => $value) { $action->xw->writeAttribute($name, $value); } $action->xw->endElement('cloud'); - + } function onEndNoticeSave($notice){ + common_debug("RSSCloudPlugin oneEndNoticeSave()"); + $user = User::staticGet('id', $notice->profile_id); - $rss = common_local_url('api', array('apiaction' => 'statuses', + $feed = common_local_url('api', array('apiaction' => 'statuses', 'method' => 'user_timeline', 'argument' => $user->nickname . '.rss')); - - $notifier = new CloudNotifier(); - $notifier->notify($rss); + + // XXX: Dave's hub for testing + + $endpoint = 'http://rpc.rsscloud.org:5337/rsscloud/ping'; + + $notifier = new RSSCloudNotifier(); + $notifier->postUpdate($endpoint, $feed); } - } - -class CloudNotifier { - - - function notify($feed) { - common_debug("CloudNotifier->notify: $feed"); - - $params = 'url=' . urlencode($feed); - - $result = $this->httpPost('http://rpc.rsscloud.org:5337/rsscloud/ping', - $params); - - if ($result) { - common_debug('success notifying cloud'); - } else { - common_debug('failure notifying cloud'); - } - - } - - function userAgent() - { - return 'rssCloudPlugin/' . RSSCLOUDPLUGIN_VERSION . - ' StatusNet/' . STATUSNET_VERSION; - } - - - private function httpPost($url, $params) { - - - common_debug('params: ' . var_export($params, true)); - - $options = array(CURLOPT_URL => $url, - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => $params, - CURLOPT_USERAGENT => $this->userAgent(), - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FAILONERROR => true, - CURLOPT_HEADER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_CONNECTTIMEOUT => 5, - CURLOPT_TIMEOUT => 5); - - $ch = curl_init(); - curl_setopt_array($ch, $options); - - $response = curl_exec($ch); - - - - $info = curl_getinfo($ch); - - curl_close($ch); - - common_debug('curl response: ' . var_export($response, true)); - common_debug('curl info: ' . var_export($info, true)); - - if ($info['http_code'] == 200) { - return true; - } else { - return false; - } - } - -} \ No newline at end of file diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php new file mode 100644 index 0000000000..1d4087334f --- /dev/null +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -0,0 +1,145 @@ + + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * 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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +class RSSCloudRequestNotifyAction extends Action +{ + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if user doesn't exist + */ + function prepare($args) + { + parent::prepare($args); + return true; + } + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + showResult(false, 'Request must be POST.'); + } + + $ip = $_SERVER['REMOTE_ADDR']; + $missing = array(); + $port = $this->arg('port'); + + if (empty($this->port)) { + $missing[] = 'port'; + } + + $path = $this->arg('path'); + + if (empty($this->path)) { + $missing[] = 'path'; + } + + $protocol = $this->arg('protocol'); + + if (empty($this->protocol)) { + $missing[] = 'protocol'; + } + + if (empty($this->notifyProcedure)) { + $missing[] = 'notifyProcedure'; + } + + if (!empty($missing)) { + $msg = 'The following parameters were missing from the request body: ' . + implode(',', $missing) . '.'; + $this->showResult(false, $msg); + } + + $feeds = $this->getFeeds(); + + if (empty($feeds)) { + $this->showResult(false, + 'You must provide at least one feed url (url1, url2, url3 ... urlN).'); + } + + $endpoint = $ip . ':' . $port . $path; + + foreach ($feeds as $feed) { + + } + + + } + + + function getFeeds() + { + $feeds = array(); + + foreach ($this->args as $key => $feed ) { + if (preg_match('|url\d+|', $key)) { + + // XXX: validate feeds somehow and kick bad ones out + + $feeds[] = $feed; + } + } + + return $feeds; + } + + + function checkNotifyHandler() + { + + } + + function validateFeed() + { + } + + function showResult($success, $msg) + { + $this->startXML(); + $this->elementStart('notifyResult', array('success' => ($success) ? 'true' : 'false', + 'msg' => $msg)); + $this->endXML(); + + } + + +} + + + From 51ac7439e1875e7703c7aaeba91d19838b860032 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 21 Sep 2009 00:54:56 -0700 Subject: [PATCH 04/36] /rsscloud/request_notify should work now --- plugins/RSSCloud/RSSCloudNotifier.php | 14 +- plugins/RSSCloud/RSSCloudPlugin.php | 53 +++--- plugins/RSSCloud/RSSCloudRequestNotify.php | 187 ++++++++++++++++----- 3 files changed, 175 insertions(+), 79 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php index 65bfd032e9..c2130b7b83 100644 --- a/plugins/RSSCloud/RSSCloudNotifier.php +++ b/plugins/RSSCloud/RSSCloudNotifier.php @@ -2,7 +2,7 @@ /** * StatusNet, the distributed open-source microblogging tool * - * Class to ping an rssCloud hub when a feed has been updated + * Class to ping an rssCloud endpoint when a feed has been updated * * PHP version 5 * @@ -40,12 +40,15 @@ class RSSCloudNotifier { $result = $this->httpPost($endpoint, $params); + // XXX: Make all this use CurlClient (lib/curlclient.php) + if ($result) { - common_debug('success notifying cloud'); + common_debug('RSSCloud plugin - success notifying cloud endpoint!'); } else { - common_debug('failure notifying cloud'); + common_debug('RSSClous plugin - failure notifying cloud endpoint!'); } + return $result; } function userAgent() @@ -56,8 +59,6 @@ class RSSCloudNotifier { private function httpPost($url, $params) { - common_debug('params: ' . var_export($params, true)); - $options = array(CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $params, @@ -78,9 +79,6 @@ class RSSCloudNotifier { curl_close($ch); - common_debug('curl response: ' . var_export($response, true)); - common_debug('curl info: ' . var_export($info, true)); - if ($info['http_code'] == 200) { return true; } else { diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index 50d9060efe..a971c31567 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -40,10 +40,8 @@ class RSSCloudPlugin extends Plugin parent::__construct(); } - function onInitializePlugin(){ - - common_debug("RSSCloudPlugin onInitializePlugin()"); - + function onInitializePlugin() + { $this->domain = common_config('rsscloud', 'domain'); $this->port = common_config('rsscloud', 'port'); $this->path = common_config('rsscloud', 'path'); @@ -52,16 +50,18 @@ class RSSCloudPlugin extends Plugin // set defaults + $local_server = parse_url(common_path('rsscloud/request_notify')); + if (empty($this->domain)) { - $this->domain = 'rpc.rsscloud.org'; + $this->domain = $local_server['host']; } if (empty($this->port)) { - $this->port = '5337'; + $this->port = '80'; } if (empty($this->path)) { - $this->path = '/rsscloud/pleaseNotify'; + $this->path = '/rsscloud/request_notify'; } if (empty($this->funct)) { @@ -84,6 +84,8 @@ class RSSCloudPlugin extends Plugin function onRouterInitialized(&$m) { $m->connect('rsscloud/request_notify', array('action' => 'RSSCloudRequestNotify')); + + // XXX: This is just for end-to-end testing $m->connect('rsscloud/notify', array('action' => 'LoggingAggregator')); return true; @@ -91,8 +93,6 @@ class RSSCloudPlugin extends Plugin function onAutoload($cls) { - common_debug("onAutoload() $cls"); - switch ($cls) { @@ -111,22 +111,26 @@ class RSSCloudPlugin extends Plugin function onStartApiRss($action){ - // XXX: No sure we want every feed to be cloud enabled + // XXX: we want to only cloud enable the user_timeline so we need + // to be even more specific than this... FIXME - $attrs = array('domain' => $this->domain, - 'port' => $this->port, - 'path' => $this->path, - 'registerProcedure' => $this->funct, - 'protocol' => $this->protocol); + if (get_class($action) == 'TwitapistatusesAction') { - // Dipping into XMLWriter to avoid a full end element (). + $attrs = array('domain' => $this->domain, + 'port' => $this->port, + 'path' => $this->path, + 'registerProcedure' => $this->funct, + 'protocol' => $this->protocol); + + // Dipping into XMLWriter to avoid a full end element (). + + $action->xw->startElement('cloud'); + foreach ($attrs as $name => $value) { + $action->xw->writeAttribute($name, $value); + } + $action->xw->endElement(); - $action->xw->startElement('cloud'); - foreach ($attrs as $name => $value) { - $action->xw->writeAttribute($name, $value); } - $action->xw->endElement('cloud'); - } function onEndNoticeSave($notice){ @@ -139,11 +143,10 @@ class RSSCloudPlugin extends Plugin 'argument' => $user->nickname . '.rss')); // XXX: Dave's hub for testing + // $endpoint = 'http://rpc.rsscloud.org:5337/rsscloud/ping'; - $endpoint = 'http://rpc.rsscloud.org:5337/rsscloud/ping'; - - $notifier = new RSSCloudNotifier(); - $notifier->postUpdate($endpoint, $feed); + // $notifier = new RSSCloudNotifier(); + // $notifier->postUpdate($endpoint, $feed); } } diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index 1d4087334f..48bd3fb270 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -1,7 +1,8 @@ ip = $_SERVER['REMOTE_ADDR']; + $this->port = $this->arg('port'); + $this->path = $this->arg('path'); + $this->protocol = $this->arg('protocol'); + $this->procedure = $this->arg('notifyProcedure'); + $this->feeds = $this->getFeeds(); + + $this->subscriber_url = 'http://' . $this->ip . ':' . $this->port . $this->path; + return true; } function handle($args) { parent::handle($args); - + if ($_SERVER['REQUEST_METHOD'] != 'POST') { - showResult(false, 'Request must be POST.'); + $this->showResult(false, 'Request must be POST.'); + return; } - - $ip = $_SERVER['REMOTE_ADDR']; + $missing = array(); - $port = $this->arg('port'); - + if (empty($this->port)) { $missing[] = 'port'; } - + $path = $this->arg('path'); if (empty($this->path)) { $missing[] = 'path'; } - + $protocol = $this->arg('protocol'); if (empty($this->protocol)) { $missing[] = 'protocol'; } - - if (empty($this->notifyProcedure)) { + + if (!isset($this->procedure)) { $missing[] = 'notifyProcedure'; } - + if (!empty($missing)) { $msg = 'The following parameters were missing from the request body: ' . - implode(',', $missing) . '.'; + implode(', ', $missing) . '.'; $this->showResult(false, $msg); + return; } - - $feeds = $this->getFeeds(); - - if (empty($feeds)) { - $this->showResult(false, - 'You must provide at least one feed url (url1, url2, url3 ... urlN).'); + + if (empty($this->feeds)) { + $this->showResult(false, + 'You must provide at least one valid profile feed url (url1, url2, url3 ... urlN).'); + return; } - + $endpoint = $ip . ':' . $port . $path; - - foreach ($feeds as $feed) { - + + foreach ($this->feeds as $feed) { + $this->saveSubscription($feed); } - - + + // XXX: What to do about deleting stale subscriptions? 25 hours seems harsh. + // WordPress doesn't ever remove subscriptions. + + $msg = 'Thanks for the registration. It worked. When the feed(s) update(s) we\'ll notify you. ' . + ' Don\'t forget to re-register after 24 hours, your subscription will expire in 25.'; + + $this->showResult(true, $msg); } - - + function getFeeds() { $feeds = array(); - + foreach ($this->args as $key => $feed ) { if (preg_match('|url\d+|', $key)) { - - // XXX: validate feeds somehow and kick bad ones out - - $feeds[] = $feed; + + if ($this->testFeed($feed)) { + $feeds[] = $feed; + } else { + $msg = 'RSSCloud Plugin - ' . $this->ip . ' tried to subscribe ' . + 'to a non-existent feed: ' . $feed; + common_log(LOG_WARN, $msg); + } } } - + return $feeds; } - - - function checkNotifyHandler() + + function testNotificationHandler($feed) { - + $notifier = new RSSCloudNotifier(); + return $notifier->postUpdate($endpoint, $feed); } - - function validateFeed() + + // returns valid user or false + function testFeed($feed) { + $user = $this->userFromFeed($feed); + + if (!empty($user)) { + + common_debug("Valid feed: $feed"); + + // OK, so this is a valid profile feed url, now let's see if the + // other system reponds to our notifications before we + // add the sub... + + if ($this->testNotificationHandler($feed)) { + return true; + } + } + + return false; } - - function showResult($success, $msg) + + // this actually does the validating and figuring out the + // user, which it returns + function userFromFeed($feed) + { + // We only do profile feeds + + $path = common_path('api/statuses/user_timeline/'); + $valid = '%^' . $path . '(?.*)\.rss$%'; + + if (preg_match($valid, $feed, $matches)) { + $user = User::staticGet('nickname', $matches['nickname']); + if (!empty($user)) { + return $user; + } + } + + return false; + } + + function saveSubscription($feed) + { + // check to see if we already have a profile for this subscriber + + $other = Remote_profile::staticGet('uri', $this->subscriber_url); + + if ($other === false) { + $other->saveProfile(); + } + + $user = userFromFeed($feed); + + $result = subs_subscribe_to($user, $other); + + if ($result != true) { + $msg = "RSSPlugin - got '$result' trying to subscribe " . + "$this->subscriber_url to $user->nickname" . "'s profile feed."; + common_log(LOG_WARN, $msg); + } else { + $msg = 'RSSCloud plugin - subscribe: ' . $this->subscriber_url . + ' subscribed to ' . $feed; + + common_log(LOG_INFO, $msg); + } + } + + function saveProfile() + { + common_debug("Saving remote profile for $this->subscriber_url"); + + // XXX: We need to add a field to Remote_profile to indicate the kind + // of remote profile? i.e: OMB, RSSCloud, PuSH, Twitter + + $remote = new Remote_profile(); + $remote->uri = $this->subscriber_url; + $remote->postnoticeurl = $this->subscriber_url; + $remote->created = DB_DataObject_Cast::dateTime(); + + if (!$remote->insert()) { + throw new Exception(_('RSSCloud plugin - Error inserting remote profile!')); + } + } + + function showResult($success, $msg) { $this->startXML(); $this->elementStart('notifyResult', array('success' => ($success) ? 'true' : 'false', 'msg' => $msg)); $this->endXML(); - } - - + } From 46ac99cf4dc58b4e92a9b2397658ad8a093ee02f Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 3 Nov 2009 16:51:40 -0800 Subject: [PATCH 05/36] Only add rssCloud link to user timeline --- plugins/RSSCloud/RSSCloudPlugin.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index a971c31567..a86c153f14 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -111,10 +111,7 @@ class RSSCloudPlugin extends Plugin function onStartApiRss($action){ - // XXX: we want to only cloud enable the user_timeline so we need - // to be even more specific than this... FIXME - - if (get_class($action) == 'TwitapistatusesAction') { + if (get_class($action) == 'ApiTimelineUserAction') { $attrs = array('domain' => $this->domain, 'port' => $this->port, From aa9f81193e9f623dec136c6c5b7ddd6ebb948ab0 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 3 Nov 2009 17:53:17 -0800 Subject: [PATCH 06/36] Queue notices for rssCloud --- plugins/RSSCloud/RSSCloudPlugin.php | 59 ++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index a86c153f14..816739889d 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -130,21 +130,52 @@ class RSSCloudPlugin extends Plugin } } - function onEndNoticeSave($notice){ - - common_debug("RSSCloudPlugin oneEndNoticeSave()"); - - $user = User::staticGet('id', $notice->profile_id); - $feed = common_local_url('api', array('apiaction' => 'statuses', - 'method' => 'user_timeline', - 'argument' => $user->nickname . '.rss')); - - // XXX: Dave's hub for testing - // $endpoint = 'http://rpc.rsscloud.org:5337/rsscloud/ping'; - - // $notifier = new RSSCloudNotifier(); - // $notifier->postUpdate($endpoint, $feed); + /** + * Add an RSSCloud queue item for each notice + * + * @param Notice $notice the notice + * @param array &$transports the list of transports (queues) + * + * @return boolean hook return + */ + function onStartEnqueueNotice($notice, &$transports) + { + array_push($transports, 'rsscloud'); + return true; } + /** + * broadcast the message when not using queuehandler + * + * @param Notice &$notice the notice + * @param array $queue destination queue + * + * @return boolean hook return + */ + function onUnqueueHandleNotice(&$notice, $queue) + { + if (($queue == 'rsscloud') && ($this->_isLocal($notice))) { + + // broadcast the notice here + common_debug('broadcasting rssCloud bound notice ' . $notice->id); + + return false; + } + return true; + } + + /** + * Determine whether the notice was locally created + * + * @param Notice $notice + * + * @return boolean locality + */ + function _isLocal($notice) + { + return ($notice->is_local == Notice::LOCAL_PUBLIC || + $notice->is_local == Notice::LOCAL_NONPUBLIC); + } + } From 8980bebcb38eaaca934141b1828e243609577a51 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 4 Nov 2009 17:32:17 -0800 Subject: [PATCH 07/36] Add a table and DB_DataObject class for storing cloud subscriptions --- plugins/RSSCloud/RSSCloudPlugin.php | 33 +++- plugins/RSSCloud/RSSCloudRequestNotify.php | 173 +++++++++++---------- plugins/RSSCloud/RSSCloudSubscription.php | 82 ++++++++++ 3 files changed, 203 insertions(+), 85 deletions(-) create mode 100644 plugins/RSSCloud/RSSCloudSubscription.php diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index 816739889d..10f81b8ddf 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -95,14 +95,15 @@ class RSSCloudPlugin extends Plugin { switch ($cls) { - + case 'RSSCloudSubscription': + include_once(INSTALLDIR . '/plugins/RSSCloud/RSSCloudSubscription.php'); + return false; case 'RSSCloudNotifier': - require_once(INSTALLDIR . '/plugins/RSSCloud/RSSCloudNotifier.php'); + include_once(INSTALLDIR . '/plugins/RSSCloud/RSSCloudNotifier.php'); return false; case 'RSSCloudRequestNotifyAction': case 'LoggingAggregatorAction': - common_debug(mb_substr($cls, 0, -6) . '.php'); - require_once(INSTALLDIR . '/plugins/RSSCloud/' . mb_substr($cls, 0, -6) . '.php'); + include_once(INSTALLDIR . '/plugins/RSSCloud/' . mb_substr($cls, 0, -6) . '.php'); return false; default: return true; @@ -155,10 +156,10 @@ class RSSCloudPlugin extends Plugin function onUnqueueHandleNotice(&$notice, $queue) { if (($queue == 'rsscloud') && ($this->_isLocal($notice))) { - + // broadcast the notice here common_debug('broadcasting rssCloud bound notice ' . $notice->id); - + return false; } return true; @@ -176,6 +177,24 @@ class RSSCloudPlugin extends Plugin return ($notice->is_local == Notice::LOCAL_PUBLIC || $notice->is_local == Notice::LOCAL_NONPUBLIC); } - + + + function onCheckSchema() { + $schema = Schema::get(); + $schema->ensureTable('rsscloud_subscription', + array(new ColumnDef('subscribed', 'integer', + null, false, 'PRI'), + new ColumnDef('url', 'varchar', + '255', false, 'PRI'), + new ColumnDef('failures', 'integer', + null, false, 'MUL'), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('modified', 'timestamp') + ) + ); + return true; + } + } diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index 48bd3fb270..8b5deb1365 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -51,10 +51,10 @@ class RSSCloudRequestNotifyAction extends Action $this->path = $this->arg('path'); $this->protocol = $this->arg('protocol'); $this->procedure = $this->arg('notifyProcedure'); + $this->domain = $this->arg('domain'); + $this->feeds = $this->getFeeds(); - $this->subscriber_url = 'http://' . $this->ip . ':' . $this->port . $this->path; - return true; } @@ -102,11 +102,30 @@ class RSSCloudRequestNotifyAction extends Action return; } - $endpoint = $ip . ':' . $port . $path; + // We have to validate everything before saving anything. + // We only return one success or failure no matter how + // many feeds the subscriber is trying to subscribe to + + foreach ($this->feeds as $feed) { + + if (!$this->validateFeed($feed)) { + $msg = 'Feed subscription failed - Not a valid feed.'; + $this->showResult(false, $msg); + return; + } + + if (!$this->testNotificationHandler($feed)) { + $msg = 'Feed subscription failed - ' . + 'notification handler doesn\'t respond correctly.'; + $this->showResult(false, $msg); + return; + } + + } foreach ($this->feeds as $feed) { $this->saveSubscription($feed); - } + } // XXX: What to do about deleting stale subscriptions? 25 hours seems harsh. // WordPress doesn't ever remove subscriptions. @@ -114,58 +133,62 @@ class RSSCloudRequestNotifyAction extends Action $msg = 'Thanks for the registration. It worked. When the feed(s) update(s) we\'ll notify you. ' . ' Don\'t forget to re-register after 24 hours, your subscription will expire in 25.'; - $this->showResult(true, $msg); + $this->showResult(true, $msg); } + function validateFeed($feed) + { + $user = $this->userFromFeed($feed); + + if (empty($user)) { + return false; + } + + return true; + } + + function getFeeds() { $feeds = array(); - - foreach ($this->args as $key => $feed ) { - if (preg_match('|url\d+|', $key)) { - - if ($this->testFeed($feed)) { - $feeds[] = $feed; - } else { - $msg = 'RSSCloud Plugin - ' . $this->ip . ' tried to subscribe ' . - 'to a non-existent feed: ' . $feed; - common_log(LOG_WARN, $msg); - } - } + + while (list($key, $feed) = each ($this->args)) { + if (preg_match('/^url\d*$/', $key)) { + $feeds[] = $feed; + } } return $feeds; } function testNotificationHandler($feed) - { + { + common_debug("RSSCloudPlugin - testNotificationHandler()"); + $notifier = new RSSCloudNotifier(); - return $notifier->postUpdate($endpoint, $feed); + + if (isset($this->domain)) { + + //get + + $this->url = 'http://' . $this->domain . ':' . $this->port . '/' . $this->path; + + common_debug('domain set need to send challenge'); + + } else { + + //post + + $this->url = 'http://' . $this->ip . ':' . $this->port . '/' . $this->path; + + //return $notifier->postUpdate($endpoint, $feed); + + } + + return true; + } - // returns valid user or false - function testFeed($feed) - { - $user = $this->userFromFeed($feed); - - if (!empty($user)) { - - common_debug("Valid feed: $feed"); - - // OK, so this is a valid profile feed url, now let's see if the - // other system reponds to our notifications before we - // add the sub... - - if ($this->testNotificationHandler($feed)) { - return true; - } - } - - return false; - } - - // this actually does the validating and figuring out the - // user, which it returns function userFromFeed($feed) { // We only do profile feeds @@ -185,45 +208,39 @@ class RSSCloudRequestNotifyAction extends Action function saveSubscription($feed) { - // check to see if we already have a profile for this subscriber - - $other = Remote_profile::staticGet('uri', $this->subscriber_url); - - if ($other === false) { - $other->saveProfile(); - } - - $user = userFromFeed($feed); - - $result = subs_subscribe_to($user, $other); - - if ($result != true) { - $msg = "RSSPlugin - got '$result' trying to subscribe " . - "$this->subscriber_url to $user->nickname" . "'s profile feed."; - common_log(LOG_WARN, $msg); + $user = $this->userFromFeed($feed); + + common_debug('user = ' . $user->id); + + $sub = RSSCloudSubscription::getSubscription($user->id, $this->url); + + if ($sub) { + common_debug("already subscribed to that!"); } else { - $msg = 'RSSCloud plugin - subscribe: ' . $this->subscriber_url . - ' subscribed to ' . $feed; - - common_log(LOG_INFO, $msg); + common_debug('No feed for user ' . $user->id . ' notify: ' . $this->url); } - } - - function saveProfile() - { - common_debug("Saving remote profile for $this->subscriber_url"); - - // XXX: We need to add a field to Remote_profile to indicate the kind - // of remote profile? i.e: OMB, RSSCloud, PuSH, Twitter - - $remote = new Remote_profile(); - $remote->uri = $this->subscriber_url; - $remote->postnoticeurl = $this->subscriber_url; - $remote->created = DB_DataObject_Cast::dateTime(); - - if (!$remote->insert()) { - throw new Exception(_('RSSCloud plugin - Error inserting remote profile!')); + + common_debug('RSSPlugin - saveSubscription'); + // turn debugging high + DB_DataObject::debugLevel(5); + + $sub = new RSSCloudSubscription(); + + $sub->subscribed = $user->id; + $sub->url = $this->url; + $sub->created = common_sql_now(); + + // auto timestamp doesn't seem to work for me + + $sub->modified = common_sql_now(); + + if (!$sub->insert()) { + common_log_db_error($sub, 'INSERT', __FILE__); + return false; } + DB_DataObject::debugLevel(); + + return true; } function showResult($success, $msg) diff --git a/plugins/RSSCloud/RSSCloudSubscription.php b/plugins/RSSCloud/RSSCloudSubscription.php new file mode 100644 index 0000000000..0b102e2e6d --- /dev/null +++ b/plugins/RSSCloud/RSSCloudSubscription.php @@ -0,0 +1,82 @@ +. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Table Definition for rsscloud_subscription + */ + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +class RSSCloudSubscription extends Memcached_DataObject { + + var $__table='rsscloud_subscription'; // table name + var $subscribed; // int primary key user id + var $url; // string primary key + var $failures; // int + var $created; // datestamp() + var $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + + function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('DataObjects_Grp',$k,$v); } + + function table() + { + global $_DB_DATAOBJECT; + $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype']; + + $cols = array( + 'subscribed' => DB_DATAOBJECT_INT, + 'url' => DB_DATAOBJECT_STR, + 'failures' => DB_DATAOBJECT_INT, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, + 'modified' => ($dbtype == 'mysql') ? + DB_DATAOBJECT_MYSQLTIMESTAMP : + DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + ); + + + // common_debug(var_export($cols, true)); + return $cols; + } + + function keys() + { + return array('subscribed', 'url'); + } + + static function getSubscription($subscribed, $url) + { + $sub = new RSSCloudSubscription(); + $sub->whereAdd("subscribed = $subscribed"); + $sub->whereAdd("url = $url"); + $sub->limit(1); + + if ($sub->find()) { + $sub->fetch(); + return $sub; + } + + return false; + } + +} +?> \ No newline at end of file From 3209544b30776dc64b5d21c5725028d9d6016e2f Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sun, 8 Nov 2009 11:17:08 -0800 Subject: [PATCH 08/36] Fixed DB_DataObject to return the right keys info for a compound key & fix ini output --- plugins/RSSCloud/RSSCloudSubscription.php | 24 +++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudSubscription.php b/plugins/RSSCloud/RSSCloudSubscription.php index 0b102e2e6d..881e071658 100644 --- a/plugins/RSSCloud/RSSCloudSubscription.php +++ b/plugins/RSSCloud/RSSCloudSubscription.php @@ -40,27 +40,25 @@ class RSSCloudSubscription extends Memcached_DataObject { function table() { - global $_DB_DATAOBJECT; - $dbtype = $_DB_DATAOBJECT['CONNECTIONS'][$this->_database_dsn_md5]->dsn['phptype']; - $cols = array( - 'subscribed' => DB_DATAOBJECT_INT, - 'url' => DB_DATAOBJECT_STR, - 'failures' => DB_DATAOBJECT_INT, - 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, - 'modified' => ($dbtype == 'mysql') ? - DB_DATAOBJECT_MYSQLTIMESTAMP : - DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME - ); + $db = $this->getDatabaseConnection(); + $dbtype = $db->phptype; + $cols = array('subscribed' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'url' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'failures' => DB_DATAOBJECT_INT, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'modified' => ($dbtype == 'mysql' || $dbtype == 'mysqli') ? + DB_DATAOBJECT_MYSQLTIMESTAMP + DB_DATAOBJECT_NOTNULL : + DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + ); - // common_debug(var_export($cols, true)); return $cols; } function keys() { - return array('subscribed', 'url'); + return array('subscribed' => 'N', 'url' => 'N'); } static function getSubscription($subscribed, $url) From 7638e2713d91a829646358fbac5ab8d6ad08d6f6 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 1 Dec 2009 01:24:39 +0000 Subject: [PATCH 09/36] Set modified column correctly. --- plugins/RSSCloud/RSSCloudPlugin.php | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index 10f81b8ddf..8e57b4a3e0 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -50,7 +50,7 @@ class RSSCloudPlugin extends Plugin // set defaults - $local_server = parse_url(common_path('rsscloud/request_notify')); + $local_server = parse_url(common_path('/main/rsscloud/request_notify')); if (empty($this->domain)) { $this->domain = $local_server['host']; @@ -61,7 +61,7 @@ class RSSCloudPlugin extends Plugin } if (empty($this->path)) { - $this->path = '/rsscloud/request_notify'; + $this->path = '/main/rsscloud/request_notify'; } if (empty($this->funct)) { @@ -83,10 +83,10 @@ class RSSCloudPlugin extends Plugin function onRouterInitialized(&$m) { - $m->connect('rsscloud/request_notify', array('action' => 'RSSCloudRequestNotify')); + $m->connect('/main/rsscloud/request_notify', array('action' => 'RSSCloudRequestNotify')); // XXX: This is just for end-to-end testing - $m->connect('rsscloud/notify', array('action' => 'LoggingAggregator')); + $m->connect('/main/rsscloud/notify', array('action' => 'LoggingAggregator')); return true; } @@ -110,8 +110,8 @@ class RSSCloudPlugin extends Plugin } } - function onStartApiRss($action){ - + function onStartApiRss($action) + { if (get_class($action) == 'ApiTimelineUserAction') { $attrs = array('domain' => $this->domain, @@ -126,8 +126,8 @@ class RSSCloudPlugin extends Plugin foreach ($attrs as $name => $value) { $action->xw->writeAttribute($name, $value); } - $action->xw->endElement(); + $action->xw->endElement(); } } @@ -178,7 +178,6 @@ class RSSCloudPlugin extends Plugin $notice->is_local == Notice::LOCAL_NONPUBLIC); } - function onCheckSchema() { $schema = Schema::get(); $schema->ensureTable('rsscloud_subscription', @@ -187,10 +186,13 @@ class RSSCloudPlugin extends Plugin new ColumnDef('url', 'varchar', '255', false, 'PRI'), new ColumnDef('failures', 'integer', - null, false, 'MUL'), + null, false, null, 0), new ColumnDef('created', 'datetime', null, false), - new ColumnDef('modified', 'timestamp') + new ColumnDef('modified', 'timestamp', + null, false, null, + 'CURRENT_TIMESTAMP', + 'on update CURRENT_TIMESTAMP') ) ); return true; From 6b28fbe7b600b56ab06b373173d9f04fae9333ce Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sat, 5 Dec 2009 07:17:51 +0000 Subject: [PATCH 10/36] Make dummy aggregator handle RSSCloud challenge/response with domain parameter --- plugins/RSSCloud/LoggingAggregator.php | 55 +++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/plugins/RSSCloud/LoggingAggregator.php b/plugins/RSSCloud/LoggingAggregator.php index 2573d9343e..cbdefc36b8 100644 --- a/plugins/RSSCloud/LoggingAggregator.php +++ b/plugins/RSSCloud/LoggingAggregator.php @@ -36,6 +36,9 @@ if (!defined('STATUSNET')) { class LoggingAggregatorAction extends Action { + var $challenge = null; + var $url = null; + /** * Initialization. * @@ -46,6 +49,13 @@ class LoggingAggregatorAction extends Action function prepare($args) { parent::prepare($args); + + $this->url = $this->arg('url'); + $this->challenge = $this->arg('challenge'); + + common_debug("args = " . var_export($this->args, true)); + common_debug('url = ' . $this->url . ' challenge = ' . $this->challenge); + return true; } @@ -53,33 +63,50 @@ class LoggingAggregatorAction extends Action { parent::handle($args); - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - $this->showError('This resource requires an HTTP POST.'); - } - - $this->url = $this->arg('url'); - if (empty($this->url)) { $this->showError('Hey, you have to provide a url parameter.'); + return; } - $this->ip = $_SERVER['REMOTE_ADDR']; + if (!empty($this->challenge)) { + + // must be a GET + + if ($_SERVER['REQUEST_METHOD'] != 'GET') { + $this->showError('This resource requires an HTTP GET.'); + return; + } + + header('Content-Type: text/xml'); + echo "\n"; + + } else { + + // must be a POST + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->showError('This resource requires an HTTP POST.'); + return; + } + + header('Content-Type: text/xml'); + echo '' . "\n"; + + } + + $this->ip = $_SERVER['REMOTE_ADDR']; common_log(LOG_INFO, 'RSSCloud Logging Aggregator - ' . $this->ip . ' claims the feed at ' . $this->url . ' has been updated.'); - - header('Content-Type: text/xml'); - echo '' . "\n"; - - } + } function showError($msg) { - header('HTTP/1.1 403 Forbidden'); + header('HTTP/1.1 400 Bad Request'); header('Content-Type: text/xml'); echo "\n"; echo "\n"; - exit(); } } \ No newline at end of file From 4e07d9eeec0e2cc8f77dbd9d6e67c9ca03adc7ba Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sat, 5 Dec 2009 07:33:15 +0000 Subject: [PATCH 11/36] Better .ini info for RSSCloud subscription --- plugins/RSSCloud/RSSCloudSubscription.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudSubscription.php b/plugins/RSSCloud/RSSCloudSubscription.php index 881e071658..396c604e71 100644 --- a/plugins/RSSCloud/RSSCloudSubscription.php +++ b/plugins/RSSCloud/RSSCloudSubscription.php @@ -65,7 +65,7 @@ class RSSCloudSubscription extends Memcached_DataObject { { $sub = new RSSCloudSubscription(); $sub->whereAdd("subscribed = $subscribed"); - $sub->whereAdd("url = $url"); + $sub->whereAdd("url = '$url'"); $sub->limit(1); if ($sub->find()) { @@ -77,4 +77,3 @@ class RSSCloudSubscription extends Memcached_DataObject { } } -?> \ No newline at end of file From 61804bb7bbd0cea92ba2bbcce15e37a35195341a Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 7 Dec 2009 09:10:04 +0000 Subject: [PATCH 12/36] Plugin now checks notify handlers before registering subscriptions --- plugins/RSSCloud/LoggingAggregator.php | 5 +- plugins/RSSCloud/RSSCloudNotifier.php | 96 +++++++++++------- plugins/RSSCloud/RSSCloudPlugin.php | 2 + plugins/RSSCloud/RSSCloudRequestNotify.php | 112 ++++++++++----------- 4 files changed, 113 insertions(+), 102 deletions(-) diff --git a/plugins/RSSCloud/LoggingAggregator.php b/plugins/RSSCloud/LoggingAggregator.php index cbdefc36b8..02175f43a8 100644 --- a/plugins/RSSCloud/LoggingAggregator.php +++ b/plugins/RSSCloud/LoggingAggregator.php @@ -1,5 +1,4 @@ challenge . "' />\n"; + echo $this->challenge; } else { @@ -92,7 +90,6 @@ class LoggingAggregatorAction extends Action header('Content-Type: text/xml'); echo '' . "\n"; - } $this->ip = $_SERVER['REMOTE_ADDR']; diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php index c2130b7b83..eb3198b5a4 100644 --- a/plugins/RSSCloud/RSSCloudNotifier.php +++ b/plugins/RSSCloud/RSSCloudNotifier.php @@ -33,59 +33,77 @@ if (!defined('STATUSNET')) { class RSSCloudNotifier { - function postUpdate($endpoint, $feed) { - common_debug("CloudNotifier->notify: $feed"); + function challenge($endpoint, $feed) + { + $code = common_confirmation_code(128); + $params = array('url' => $feed, 'challenge' => $code); + $url = $endpoint . '?' . http_build_query($params); - $params = 'url=' . urlencode($feed); - - $result = $this->httpPost($endpoint, $params); - - // XXX: Make all this use CurlClient (lib/curlclient.php) - - if ($result) { - common_debug('RSSCloud plugin - success notifying cloud endpoint!'); - } else { - common_debug('RSSClous plugin - failure notifying cloud endpoint!'); + try { + $client = new HTTPClient(); + $response = $client->get($url); + } catch (HTTP_Request2_Exception $e) { + common_log(LOG_INFO, 'RSSCloud plugin - failure testing notify handler ' . + $endpoint . ' - ' . $e->getMessage()); + return false; } - return $result; + // Check response is betweet 200 and 299 and body contains challenge data + + $status = $response->getStatus(); + $body = $response->getBody(); + + if ($status >= 200 && $status < 300) { + + if (strpos($body, $code) !== false) { + common_log(LOG_INFO, 'RSSCloud plugin - ' . + "success testing notify handler: $endpoint"); + return true; + } else { + common_log(LOG_INFO, 'RSSCloud plugin - ' . + 'challenge/repsonse failed for notify handler ' . + $endpoint); + common_debug('body = ' . var_export($body, true)); + return false; + } + } else { + common_log(LOG_INFO, 'RSSCloud plugin - ' . + "failure testing notify handler: $endpoint " . + ' - got HTTP ' . $status); + common_debug('body = ' . var_export($body, true)); + return false; + } } - function userAgent() - { - return 'rssCloudPlugin/' . RSSCLOUDPLUGIN_VERSION . - ' StatusNet/' . STATUSNET_VERSION; - } + function postUpdate($endpoint, $feed) { - private function httpPost($url, $params) { + $headers = array(); + $postdata = array('url' => $feed); - $options = array(CURLOPT_URL => $url, - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => $params, - CURLOPT_USERAGENT => $this->userAgent(), - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FAILONERROR => true, - CURLOPT_HEADER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_CONNECTTIMEOUT => 5, - CURLOPT_TIMEOUT => 5); + try { + $client = new HTTPClient(); + $response = $client->post($endpoint, $headers, $postdata); + } catch (HTTP_Request2_Exception $e) { + common_log(LOG_INFO, 'RSSCloud plugin - failure notifying ' . + $endpoint . ' that feed ' . $feed . + ' has changed: ' . $e->getMessage()); + return false; + } - $ch = curl_init(); - curl_setopt_array($ch, $options); + $status = $response->getStatus(); - $response = curl_exec($ch); - - $info = curl_getinfo($ch); - - curl_close($ch); - - if ($info['http_code'] == 200) { + if ($status >= 200 && $status < 300) { + common_log(LOG_INFO, 'RSSCloud plugin - success notifying ' . + $endpoint . ' that feed ' . $feed . ' has changed.'); return true; } else { + common_log(LOG_INFO, 'RSSCloud plugin - failure notifying ' . + $endpoint . ' that feed ' . $feed . + ' has changed: got HTTP ' . $status); + common_debug('body = ' . var_export($response->getBody(), true)); return false; } } } - diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index 8e57b4a3e0..402fbec2d8 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -112,6 +112,8 @@ class RSSCloudPlugin extends Plugin function onStartApiRss($action) { + // XXX: Add RSS 1.0 user feeds + if (get_class($action) == 'ApiTimelineUserAction') { $attrs = array('domain' => $this->domain, diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index 8b5deb1365..135c316f7b 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -52,7 +52,7 @@ class RSSCloudRequestNotifyAction extends Action $this->protocol = $this->arg('protocol'); $this->procedure = $this->arg('notifyProcedure'); $this->domain = $this->arg('domain'); - + $this->feeds = $this->getFeeds(); return true; @@ -103,29 +103,29 @@ class RSSCloudRequestNotifyAction extends Action } // We have to validate everything before saving anything. - // We only return one success or failure no matter how + // We only return one success or failure no matter how // many feeds the subscriber is trying to subscribe to foreach ($this->feeds as $feed) { - + if (!$this->validateFeed($feed)) { $msg = 'Feed subscription failed - Not a valid feed.'; $this->showResult(false, $msg); return; } - + if (!$this->testNotificationHandler($feed)) { $msg = 'Feed subscription failed - ' . 'notification handler doesn\'t respond correctly.'; $this->showResult(false, $msg); - return; + return; } - + } foreach ($this->feeds as $feed) { $this->saveSubscription($feed); - } + } // XXX: What to do about deleting stale subscriptions? 25 hours seems harsh. // WordPress doesn't ever remove subscriptions. @@ -133,7 +133,7 @@ class RSSCloudRequestNotifyAction extends Action $msg = 'Thanks for the registration. It worked. When the feed(s) update(s) we\'ll notify you. ' . ' Don\'t forget to re-register after 24 hours, your subscription will expire in 25.'; - $this->showResult(true, $msg); + $this->showResult(true, $msg); } function validateFeed($feed) @@ -147,45 +147,45 @@ class RSSCloudRequestNotifyAction extends Action return true; } - function getFeeds() { $feeds = array(); - - while (list($key, $feed) = each ($this->args)) { + + while (list($key, $feed) = each ($this->args)) { if (preg_match('/^url\d*$/', $key)) { $feeds[] = $feed; - } + } } return $feeds; } function testNotificationHandler($feed) - { + { common_debug("RSSCloudPlugin - testNotificationHandler()"); - + $notifier = new RSSCloudNotifier(); - + if (isset($this->domain)) { - - //get - - $this->url = 'http://' . $this->domain . ':' . $this->port . '/' . $this->path; - - common_debug('domain set need to send challenge'); - + + // 'domain' param set, so we have to use GET and send a challenge + + $endpoint = 'http://' . $this->domain . ':' . $this->port . '/' . $this->path; + + common_log(LOG_INFO, 'Testing notification handler with challenge: ' . + $endpoint); + + return $notifier->challenge($endpoint, $feed); + } else { - - //post - - $this->url = 'http://' . $this->ip . ':' . $this->port . '/' . $this->path; - - //return $notifier->postUpdate($endpoint, $feed); - } + $endpoint = 'http://' . $this->ip . ':' . $this->port . '/' . $this->path; - return true; + common_log(LOG_INFO, 'Testing notification handler: ' . + $endpoint); + + return $notifier->postUpdate($endpoint, $feed); + } } @@ -193,6 +193,8 @@ class RSSCloudRequestNotifyAction extends Action { // We only do profile feeds + // XXX: Add cloud element to RSS 1.0 feeds + $path = common_path('api/statuses/user_timeline/'); $valid = '%^' . $path . '(?.*)\.rss$%'; @@ -209,37 +211,31 @@ class RSSCloudRequestNotifyAction extends Action function saveSubscription($feed) { $user = $this->userFromFeed($feed); - - common_debug('user = ' . $user->id); - + $sub = RSSCloudSubscription::getSubscription($user->id, $this->url); - + if ($sub) { - common_debug("already subscribed to that!"); + common_debug("Already subscribed to that!"); } else { - common_debug('No feed for user ' . $user->id . ' notify: ' . $this->url); + + $sub = new RSSCloudSubscription(); + + $sub->subscribed = $user->id; + $sub->url = $this->url; + $sub->created = common_sql_now(); + + // auto timestamp doesn't seem to work for me + + // $sub->modified = common_sql_now(); + + if (!$sub->insert()) { + common_log_db_error($sub, 'INSERT', __FILE__); + return false; + } + + DB_DataObject::debugLevel(); } - - common_debug('RSSPlugin - saveSubscription'); - // turn debugging high - DB_DataObject::debugLevel(5); - - $sub = new RSSCloudSubscription(); - - $sub->subscribed = $user->id; - $sub->url = $this->url; - $sub->created = common_sql_now(); - - // auto timestamp doesn't seem to work for me - - $sub->modified = common_sql_now(); - - if (!$sub->insert()) { - common_log_db_error($sub, 'INSERT', __FILE__); - return false; - } - DB_DataObject::debugLevel(); - + return true; } @@ -253,5 +249,3 @@ class RSSCloudRequestNotifyAction extends Action } - - From d091d061151749feddd3751f953f9bec48e382f2 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 8 Dec 2009 06:26:11 +0000 Subject: [PATCH 13/36] Notifier works, and bad subscriptions are deleted properly now. --- plugins/RSSCloud/RSSCloudNotifier.php | 78 +++++++++++++++++++++- plugins/RSSCloud/RSSCloudPlugin.php | 7 +- plugins/RSSCloud/RSSCloudRequestNotify.php | 58 ++++++++-------- 3 files changed, 113 insertions(+), 30 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php index eb3198b5a4..67bd000f00 100644 --- a/plugins/RSSCloud/RSSCloudNotifier.php +++ b/plugins/RSSCloud/RSSCloudNotifier.php @@ -33,6 +33,8 @@ if (!defined('STATUSNET')) { class RSSCloudNotifier { + const MAX_FAILURES = 3; + function challenge($endpoint, $feed) { $code = common_confirmation_code(128); @@ -55,6 +57,11 @@ class RSSCloudNotifier { if ($status >= 200 && $status < 300) { + // NOTE: the spec says that the body must contain the string + // challenge. It doesn't say that the body must contain the + // challenge string ONLY, although that seems to be the way + // the other implementations have interpreted it. + if (strpos($body, $code) !== false) { common_log(LOG_INFO, 'RSSCloud plugin - ' . "success testing notify handler: $endpoint"); @@ -100,10 +107,79 @@ class RSSCloudNotifier { common_log(LOG_INFO, 'RSSCloud plugin - failure notifying ' . $endpoint . ' that feed ' . $feed . ' has changed: got HTTP ' . $status); - common_debug('body = ' . var_export($response->getBody(), true)); return false; } } + function notify($profile) + { + $feed = common_path('api/statuses/user_timeline/') . + $profile->nickname . '.rss'; + + $cloudSub = new RSSCloudSubscription(); + $cloudSub->subscribed = $profile->id; + + if ($cloudSub->find()) { + while ($cloudSub->fetch()) { + $result = $this->postUpdate($cloudSub->url, $feed); + if ($result == false) { + $this->handleFailure($cloudSub); + } + } + } + } + + function handleFailure($cloudSub) + { + $failCnt = $cloudSub->failures + 1; + + if ($failCnt == self::MAX_FAILURES) { + + common_log(LOG_INFO, + 'Deleting RSSCloud subcription (max failure count reached), profile: ' . + $cloudSub->subscribed . + ' handler: ' . + $cloudSub->url); + + // XXX: WTF! ->delete() doesn't work. Clearly, there are some issues with + // the DB_DataObject, or my understanding of it. Have to drop into SQL. + + // $result = $cloudSub->delete(); + + $qry = 'DELETE from rsscloud_subscription' . + ' WHERE subscribed = ' . $cloudSub->subscribed . + ' AND url = \'' . $cloudSub->url . '\''; + + $result = $cloudSub->query($qry); + + if (!$result) { + common_log_db_error($cloudSub, 'DELETE', __FILE__); + common_log(LOG_ERR, 'Could not delete RSSCloud subscription.'); + } + + } else { + + common_debug('Updating failure count on RSSCloud subscription. ' . $failCnt); + + $failCnt = $cloudSub->failures + 1; + + // XXX: ->update() not working either, gar! + + $qry = 'UPDATE rsscloud_subscription' . + ' SET failures = ' . $failCnt . + ' WHERE subscribed = ' . $cloudSub->subscribed . + ' AND url = \'' . $cloudSub->url . '\''; + + common_debug($qry); + + $result = $cloudSub->query($qry); + + if (!$result) { + common_log_db_error($cloudsub, 'UPDATE', __FILE__); + common_log(LOG_ERR, 'Could not update failure count on RSSCloud subscription'); + } + } + } + } diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index 402fbec2d8..b9187d86c0 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -159,11 +159,16 @@ class RSSCloudPlugin extends Plugin { if (($queue == 'rsscloud') && ($this->_isLocal($notice))) { - // broadcast the notice here common_debug('broadcasting rssCloud bound notice ' . $notice->id); + $profile = $notice->getProfile(); + + $notifier = new RSSCloudNotifier(); + $notifier->notify($profile); + return false; } + return true; } diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index 135c316f7b..36959755a7 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -1,5 +1,4 @@ ip = $_SERVER['REMOTE_ADDR']; $this->port = $this->arg('port'); $this->path = $this->arg('path'); + + if ($this->path[0] != '/') { + $this->path = '/' . $this->path; + } + $this->protocol = $this->arg('protocol'); $this->procedure = $this->arg('notifyProcedure'); $this->domain = $this->arg('domain'); @@ -73,14 +77,10 @@ class RSSCloudRequestNotifyAction extends Action $missing[] = 'port'; } - $path = $this->arg('path'); - if (empty($this->path)) { $missing[] = 'path'; } - $protocol = $this->arg('protocol'); - if (empty($this->protocol)) { $missing[] = 'protocol'; } @@ -127,11 +127,12 @@ class RSSCloudRequestNotifyAction extends Action $this->saveSubscription($feed); } - // XXX: What to do about deleting stale subscriptions? 25 hours seems harsh. - // WordPress doesn't ever remove subscriptions. + // XXX: What to do about deleting stale subscriptions? + // 25 hours seems harsh. WordPress doesn't ever remove + // subscriptions. - $msg = 'Thanks for the registration. It worked. When the feed(s) update(s) we\'ll notify you. ' . - ' Don\'t forget to re-register after 24 hours, your subscription will expire in 25.'; + $msg = 'Thanks for the subscription. ' . + 'When the feed(s) update(s) we\'ll notify you.'; $this->showResult(true, $msg); } @@ -164,36 +165,40 @@ class RSSCloudRequestNotifyAction extends Action { common_debug("RSSCloudPlugin - testNotificationHandler()"); + $notifyUrl = $this->getNotifyUrl(); + $notifier = new RSSCloudNotifier(); if (isset($this->domain)) { // 'domain' param set, so we have to use GET and send a challenge - $endpoint = 'http://' . $this->domain . ':' . $this->port . '/' . $this->path; - common_log(LOG_INFO, 'Testing notification handler with challenge: ' . - $endpoint); - - return $notifier->challenge($endpoint, $feed); + $notifyUrl); + return $notifier->challenge($notifyUrl, $feed); } else { - - $endpoint = 'http://' . $this->ip . ':' . $this->port . '/' . $this->path; - common_log(LOG_INFO, 'Testing notification handler: ' . - $endpoint); + $notifyUrl); - return $notifier->postUpdate($endpoint, $feed); + return $notifier->postUpdate($notifyUrl, $feed); } - } + function getNotifyUrl() + { + if (isset($this->domain)) { + return 'http://' . $this->domain . ':' . $this->port . $this->path; + } else { + return 'http://' . $this->ip . ':' . $this->port . $this->path; + } + } + function userFromFeed($feed) { // We only do profile feeds - // XXX: Add cloud element to RSS 1.0 feeds + // XXX: Add cloud element to RSS 1.0 feeds? $path = common_path('api/statuses/user_timeline/'); $valid = '%^' . $path . '(?.*)\.rss$%'; @@ -212,7 +217,9 @@ class RSSCloudRequestNotifyAction extends Action { $user = $this->userFromFeed($feed); - $sub = RSSCloudSubscription::getSubscription($user->id, $this->url); + $notifyUrl = $this->getNotifyUrl(); + + $sub = RSSCloudSubscription::getSubscription($user->id, $notifyUrl); if ($sub) { common_debug("Already subscribed to that!"); @@ -221,19 +228,14 @@ class RSSCloudRequestNotifyAction extends Action $sub = new RSSCloudSubscription(); $sub->subscribed = $user->id; - $sub->url = $this->url; + $sub->url = $notifyUrl; $sub->created = common_sql_now(); - // auto timestamp doesn't seem to work for me - - // $sub->modified = common_sql_now(); - if (!$sub->insert()) { common_log_db_error($sub, 'INSERT', __FILE__); return false; } - DB_DataObject::debugLevel(); } return true; From ff26b8d88b9e3d185afa83fd2b08c0d67cd4f78d Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 8 Dec 2009 21:04:26 +0000 Subject: [PATCH 14/36] Add an RSSCloud queue handler daemon --- plugins/RSSCloud/RSSCloudNotifier.php | 2 + plugins/RSSCloud/RSSCloudPlugin.php | 7 ++ plugins/RSSCloud/RSSCloudQueueHandler.php | 78 +++++++++++++++++++++++ scripts/stopdaemons.sh | 2 +- 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100755 plugins/RSSCloud/RSSCloudQueueHandler.php diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php index 67bd000f00..909cf5c9f3 100644 --- a/plugins/RSSCloud/RSSCloudNotifier.php +++ b/plugins/RSSCloud/RSSCloudNotifier.php @@ -127,6 +127,8 @@ class RSSCloudNotifier { } } } + + return true; } function handleFailure($cloudSub) diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index b9187d86c0..fcd468f55d 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -205,5 +205,12 @@ class RSSCloudPlugin extends Plugin return true; } + function onGetValidDaemons($daemons) + { + array_push($daemons, INSTALLDIR . + '/plugins/RSSCloud/RSSCloudQueueHandler.php'); + return true; + } + } diff --git a/plugins/RSSCloud/RSSCloudQueueHandler.php b/plugins/RSSCloud/RSSCloudQueueHandler.php new file mode 100755 index 0000000000..693dd27c1f --- /dev/null +++ b/plugins/RSSCloud/RSSCloudQueueHandler.php @@ -0,0 +1,78 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..')); + +$shortoptions = 'i::'; +$longoptions = array('id::'); + +$helptext = <<log(LOG_INFO, "INITIALIZE"); + $this->notifier = new RSSCloudNotifier(); + return true; + } + + function handle_notice($notice) + { + $profile = $notice->getProfile(); + return $this->notifier->notify($profile); + } + + function finish() + { + } + +} + +if (have_option('i')) { + $id = get_option_value('i'); +} else if (have_option('--id')) { + $id = get_option_value('--id'); +} else if (count($args) > 0) { + $id = $args[0]; +} else { + $id = null; +} + +$handler = new RSSCloudQueueHandler($id); + +$handler->runOnce(); diff --git a/scripts/stopdaemons.sh b/scripts/stopdaemons.sh index 90e7331ca4..c790f1f349 100755 --- a/scripts/stopdaemons.sh +++ b/scripts/stopdaemons.sh @@ -25,7 +25,7 @@ DIR=`php $SDIR/getpiddir.php` for f in jabberhandler ombhandler publichandler smshandler pinghandler \ xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \ - twitterstatusfetcher synctwitterfriends pluginhandler; do + twitterstatusfetcher synctwitterfriends pluginhandler rsscloudhandler; do FILES="$DIR/$f.*.pid" for ff in "$FILES" ; do From 9960ec2143445417a5f44f307b1dfbbd97194b45 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 9 Dec 2009 02:14:48 +0000 Subject: [PATCH 15/36] Support an 'extra' clause when definining a column (e.g.: 'on update CURRENT_TIMESTAMP'). --- lib/schema.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/schema.php b/lib/schema.php index 6fe442d56b..a7f64ebed1 100644 --- a/lib/schema.php +++ b/lib/schema.php @@ -528,6 +528,10 @@ class Schema $sql .= " auto_increment "; } + if (!empty($cd->extra)) { + $sql .= "{$cd->extra} "; + } + return $sql; } } From c571c1323f3ff42baa31dd4f008d2417ac0f0e76 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 10 Dec 2009 19:19:15 -0800 Subject: [PATCH 16/36] Added intial README --- plugins/RSSCloud/README | 53 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 plugins/RSSCloud/README diff --git a/plugins/RSSCloud/README b/plugins/RSSCloud/README new file mode 100644 index 0000000000..8fd281aab6 --- /dev/null +++ b/plugins/RSSCloud/README @@ -0,0 +1,53 @@ +This plugin enables RSSCloud (http://rsscloud.org/) publishing and +subscription handling for RSS 2.0 profile feeds (i.e: +http://SITE/PATH/api/statuses/user_timeline/USERNAME.rss). When the +plugin is enabled, StatusNet acts as both the publisher and hub ('writer' and +'cloud' in RSSCloud parlance), but only for local StatusNet feeds. It's +not possible to use it as a general purpose hub -- for instance you can't +subscribe and get updates to a Wordpress feed from StatusNet. + +To use the plugin, add the following to your config.php: + + addPlugin('RSSCloud'); + +Enabling the plugin will add a element to your RSS 2.0 profile feeds +that looks like this: + + + +Aggregators may subscribe by sending a proper REST RSSCloud subscription +request (the optional 'domain' parameter with challenge is supported). +Subscribing aggregators will be notified ('pinged') when users they have +subscribed to post new notices. Currently, REST is the only protocol +supported for notifications. + +Deamon +------ + +There's also a daemon for offline processing of queued notices with +RSSCloud destinations, which will start automatically if/when you run +scripts/startdaemons.sh. + +Notes +----- + +- Again, only RSS 2.0 profile feeds may be subscribed to, and they have + be the ones with user names in them, like: + http://SITE/PATH/api/statuses/user_timeline/USERNAME.rss +- Subscriptions are deleted after three notification failures in a row + (not sure this is optimal). +- The plugin includes a dummy LoggingAggregator class that can be used + for end-to-end testing. You probably don't want to mess with it. + +TODO +---- + +- Figure out why the RSSCloudSubcription can't ->delete() or ->update() +- Support pinging via XML-RPC and SOAP +- Automatically delete subscriptions? Point of reference: Dave's hub + implementation auto-deletes them after 25 hours. WordPress never deletes them. +- Support additional feed URL addresses for the same feed (e.g.: by numeric ID, + ?user_id=xxx, etc.) +- Support additional feeds that make sense (e.g: replies)? +- Possibly use "rssCloud" (like Dave) instead of "RSSCloud" everywhere From 48af79dbb4cf1b4f915bf0d3d05e9fe3770664df Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 11 Dec 2009 17:50:10 -0800 Subject: [PATCH 17/36] Added a bunch of function commment blocks --- plugins/RSSCloud/LoggingAggregator.php | 30 ++++++++ plugins/RSSCloud/RSSCloudNotifier.php | 52 ++++++++++++- plugins/RSSCloud/RSSCloudPlugin.php | 63 +++++++++++++++- plugins/RSSCloud/RSSCloudRequestNotify.php | 85 +++++++++++++++++++++- 4 files changed, 222 insertions(+), 8 deletions(-) diff --git a/plugins/RSSCloud/LoggingAggregator.php b/plugins/RSSCloud/LoggingAggregator.php index 02175f43a8..c81a987f76 100644 --- a/plugins/RSSCloud/LoggingAggregator.php +++ b/plugins/RSSCloud/LoggingAggregator.php @@ -32,6 +32,19 @@ if (!defined('STATUSNET')) { exit(1); } +/** + * Dummy aggregator that acts as a proper notification handler. It + * doesn't do anything but respond correctly when notified via + * REST. Mostly, this is just and action I used to develop the plugin + * and easily test things end-to-end. I'm leaving it in here as it + * may be useful for developing the plugin further. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + **/ class LoggingAggregatorAction extends Action { @@ -45,6 +58,7 @@ class LoggingAggregatorAction extends Action * * @return boolean false if user doesn't exist */ + function prepare($args) { parent::prepare($args); @@ -58,6 +72,14 @@ class LoggingAggregatorAction extends Action return true; } + /** + * Handle the request + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + function handle($args) { parent::handle($args); @@ -98,6 +120,14 @@ class LoggingAggregatorAction extends Action $this->url . ' has been updated.'); } + /** + * Show an XML error when things go badly + * + * @param string $msg the error message + * + * @return void + */ + function showError($msg) { header('HTTP/1.1 400 Bad Request'); diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php index 909cf5c9f3..485c4dcdfa 100644 --- a/plugins/RSSCloud/RSSCloudNotifier.php +++ b/plugins/RSSCloud/RSSCloudNotifier.php @@ -31,10 +31,29 @@ if (!defined('STATUSNET')) { exit(1); } +/** + * Class for notifying cloud-enabled RSS aggregators that StatusNet + * feeds have been updated. + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + **/ class RSSCloudNotifier { const MAX_FAILURES = 3; + /** + * Send an HTTP GET to the notification handler with a + * challenge string to see if it repsonds correctly. + * + * @param String $endpoint URL of the notification handler + * @param String $feed the feed being subscribed to + * + * @return boolean success + */ function challenge($endpoint, $feed) { $code = common_confirmation_code(128); @@ -60,7 +79,7 @@ class RSSCloudNotifier { // NOTE: the spec says that the body must contain the string // challenge. It doesn't say that the body must contain the // challenge string ONLY, although that seems to be the way - // the other implementations have interpreted it. + // the other implementors have interpreted it. if (strpos($body, $code) !== false) { common_log(LOG_INFO, 'RSSCloud plugin - ' . @@ -82,6 +101,15 @@ class RSSCloudNotifier { } } + /** + * HTTP POST a notification that a feed has been updated + * ('ping the cloud'). + * + * @param String $endpoint URL of the notification handler + * @param String $feed the feed being subscribed to + * + * @return boolean success + */ function postUpdate($endpoint, $feed) { $headers = array(); @@ -111,6 +139,14 @@ class RSSCloudNotifier { } } + /** + * Notify all subscribers to a profile feed that it has changed. + * + * @param Profile $profile the profile whose feed has been + * updated + * + * @return boolean success + */ function notify($profile) { $feed = common_path('api/statuses/user_timeline/') . @@ -131,6 +167,18 @@ class RSSCloudNotifier { return true; } + /** + * Handle problems posting cloud notifications. Increment the failure + * count, or delete the subscription if the maximum number of failures + * is exceeded. + * + * XXX: Redo with proper DB_DataObject methods once I figure out what + * what the problem is with pluginized DB_DataObjects. -Z + * + * @param RSSCloudSubscription $cloudSub the subscription in question + * + * @return boolean success + */ function handleFailure($cloudSub) { $failCnt = $cloudSub->failures + 1; @@ -172,8 +220,6 @@ class RSSCloudNotifier { ' WHERE subscribed = ' . $cloudSub->subscribed . ' AND url = \'' . $cloudSub->url . '\''; - common_debug($qry); - $result = $cloudSub->query($qry); if (!$result) { diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index fcd468f55d..8c0bfa9040 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -31,15 +31,35 @@ if (!defined('STATUSNET')) { exit(1); } -define('RSSCLOUDPLUGIN_VERSION', '0.1'); +/** + * Plugin class for adding RSSCloud capabilities to StatusNet + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + **/ class RSSCloudPlugin extends Plugin { + /** + * Our friend, the constructor + * + * @return void + */ function __construct() { parent::__construct(); } + /** + * Setup the info for the subscription handler. Allow overriding + * to point at another cloud hub (not currently used). + * + * @return void + */ + function onInitializePlugin() { $this->domain = common_config('rsscloud', 'domain'); @@ -91,6 +111,16 @@ class RSSCloudPlugin extends Plugin return true; } + /** + * Automatically load the actions and libraries used by + * the RSSCloud plugin + * + * @param Class $cls the class + * + * @return boolean hook return + * + */ + function onAutoload($cls) { switch ($cls) @@ -110,10 +140,17 @@ class RSSCloudPlugin extends Plugin } } + /** + * Add a element to the RSS feed (after the rss + * element is started). + * + * @param Action $action + * + * @return void + */ + function onStartApiRss($action) { - // XXX: Add RSS 1.0 user feeds - if (get_class($action) == 'ApiTimelineUserAction') { $attrs = array('domain' => $this->domain, @@ -141,6 +178,7 @@ class RSSCloudPlugin extends Plugin * * @return boolean hook return */ + function onStartEnqueueNotice($notice, &$transports) { array_push($transports, 'rsscloud'); @@ -155,6 +193,7 @@ class RSSCloudPlugin extends Plugin * * @return boolean hook return */ + function onUnqueueHandleNotice(&$notice, $queue) { if (($queue == 'rsscloud') && ($this->_isLocal($notice))) { @@ -179,12 +218,20 @@ class RSSCloudPlugin extends Plugin * * @return boolean locality */ + function _isLocal($notice) { return ($notice->is_local == Notice::LOCAL_PUBLIC || $notice->is_local == Notice::LOCAL_NONPUBLIC); } + /** + * Create the rsscloud_subscription table if it's not + * already in the DB + * + * @return boolean hook return + */ + function onCheckSchema() { $schema = Schema::get(); $schema->ensureTable('rsscloud_subscription', @@ -205,6 +252,16 @@ class RSSCloudPlugin extends Plugin return true; } + /** + * Add RSSCloudQueueHandler to the list of valid daemons to + * start + * + * @param array $daemons the list of daemons to run + * + * @return boolean hook return + * + */ + function onGetValidDaemons($daemons) { array_push($daemons, INSTALLDIR . diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index 36959755a7..4703ecd104 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -32,6 +32,16 @@ if (!defined('STATUSNET')) { exit(1); } +/** + * Action class to handle RSSCloud notification (subscription) requests + * + * @category Plugin + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + **/ + class RSSCloudRequestNotifyAction extends Action { /** @@ -41,6 +51,7 @@ class RSSCloudRequestNotifyAction extends Action * * @return boolean false if user doesn't exist */ + function prepare($args) { parent::prepare($args); @@ -62,6 +73,18 @@ class RSSCloudRequestNotifyAction extends Action return true; } + /** + * Handle the request + * + * Checks for all the required parameters for a subscription, + * validates that the feed being subscribed to is real, and then + * saves the subsctiption. + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + function handle($args) { parent::handle($args); @@ -137,6 +160,15 @@ class RSSCloudRequestNotifyAction extends Action $this->showResult(true, $msg); } + /** + * Validate that the requested feed is one we serve + * up via RSSCloud. + * + * @param string $feed the feed in question + * + * @return void + */ + function validateFeed($feed) { $user = $this->userFromFeed($feed); @@ -148,6 +180,13 @@ class RSSCloudRequestNotifyAction extends Action return true; } + /** + * Pull all of the urls (url1, url2, url3...urlN) that + * the subscriber wants to subscribe to. + * + * @return array $feeds the list of feeds + */ + function getFeeds() { $feeds = array(); @@ -161,6 +200,15 @@ class RSSCloudRequestNotifyAction extends Action return $feeds; } + /** + * Test that a notification handler is there and is reponding + * correctly. This is called before adding a subscription. + * + * @param string $feed the feed to verify + * + * @return boolean success result + */ + function testNotificationHandler($feed) { common_debug("RSSCloudPlugin - testNotificationHandler()"); @@ -185,6 +233,13 @@ class RSSCloudRequestNotifyAction extends Action } } + /** + * Build the URL for the notification handler based on the + * parameters passed in with the subscription request. + * + * @return string notification handler url + */ + function getNotifyUrl() { if (isset($this->domain)) { @@ -194,12 +249,20 @@ class RSSCloudRequestNotifyAction extends Action } } + /** + * Uses the nickname part of the subscribed feed URL to figure out + * whethere there's really a user with such a feed. Used to + * validate feeds before adding a subscription. + * + * @param string $feed the feed in question + * + * @return boolean success + */ + function userFromFeed($feed) { // We only do profile feeds - // XXX: Add cloud element to RSS 1.0 feeds? - $path = common_path('api/statuses/user_timeline/'); $valid = '%^' . $path . '(?.*)\.rss$%'; @@ -213,6 +276,14 @@ class RSSCloudRequestNotifyAction extends Action return false; } + /** + * Save an RSSCloud subscription + * + * @param $feed a valid profile feed + * + * @return boolean success result + */ + function saveSubscription($feed) { $user = $this->userFromFeed($feed); @@ -241,6 +312,16 @@ class RSSCloudRequestNotifyAction extends Action return true; } + /** + * Show an XML message indicating the subscription + * was successful or failed. + * + * @param boolean $success whether it was good or bad + * @param string $msg the message to output + * + * @return boolean success result + */ + function showResult($success, $msg) { $this->startXML(); From aad54af448089868dd1c6bcd35a5b4a301837ae4 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sun, 13 Dec 2009 20:30:17 +0000 Subject: [PATCH 18/36] Reject subscription requests for handlers that don't support http-post --- plugins/RSSCloud/RSSCloudRequestNotify.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index 4703ecd104..9643bf4325 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -106,6 +106,10 @@ class RSSCloudRequestNotifyAction extends Action if (empty($this->protocol)) { $missing[] = 'protocol'; + } else if (strtolower($this->protocol) != 'http-post') { + $msg = 'Only http-post notifications are supported at this time.'; + $this->showResult(false, $msg); + return; } if (!isset($this->procedure)) { @@ -120,8 +124,8 @@ class RSSCloudRequestNotifyAction extends Action } if (empty($this->feeds)) { - $this->showResult(false, - 'You must provide at least one valid profile feed url (url1, url2, url3 ... urlN).'); + $msg = 'You must provide at least one valid profile feed url (url1, url2, url3 ... urlN).'; + $this->showResult(false, $msg); return; } From 655dbcedb327c79c655b928ba36140519e3b6daf Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 14 Dec 2009 05:32:37 +0000 Subject: [PATCH 19/36] Comment out the LoggingAggregator business --- plugins/RSSCloud/RSSCloudPlugin.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index 8c0bfa9040..b1af9b59cb 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -105,8 +105,9 @@ class RSSCloudPlugin extends Plugin { $m->connect('/main/rsscloud/request_notify', array('action' => 'RSSCloudRequestNotify')); - // XXX: This is just for end-to-end testing - $m->connect('/main/rsscloud/notify', array('action' => 'LoggingAggregator')); + // XXX: This is just for end-to-end testing. Uncomment if you need to pretend + // to be a cloud hub for some reason. + // $m->connect('/main/rsscloud/notify', array('action' => 'LoggingAggregator')); return true; } From 3e6b80d3e99eee4fc916a714f34e7b95ad0455a6 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 14 Dec 2009 21:24:49 +0000 Subject: [PATCH 20/36] Some phpcs cleanup --- plugins/RSSCloud/LoggingAggregator.php | 5 ++-- plugins/RSSCloud/RSSCloudNotifier.php | 35 +++++++++++++--------- plugins/RSSCloud/RSSCloudPlugin.php | 35 ++++++++++++---------- plugins/RSSCloud/RSSCloudRequestNotify.php | 24 ++++++++------- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/plugins/RSSCloud/LoggingAggregator.php b/plugins/RSSCloud/LoggingAggregator.php index c81a987f76..e37eed16a3 100644 --- a/plugins/RSSCloud/LoggingAggregator.php +++ b/plugins/RSSCloud/LoggingAggregator.php @@ -111,12 +111,13 @@ class LoggingAggregatorAction extends Action } header('Content-Type: text/xml'); - echo '' . "\n"; + Echo "\n"; } $this->ip = $_SERVER['REMOTE_ADDR']; - common_log(LOG_INFO, 'RSSCloud Logging Aggregator - ' . $this->ip . ' claims the feed at ' . + common_log(LOG_INFO, 'RSSCloud Logging Aggregator - ' . + $this->ip . ' claims the feed at ' . $this->url . ' has been updated.'); } diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php index 485c4dcdfa..d454691c80 100644 --- a/plugins/RSSCloud/RSSCloudNotifier.php +++ b/plugins/RSSCloud/RSSCloudNotifier.php @@ -41,16 +41,16 @@ if (!defined('STATUSNET')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ **/ -class RSSCloudNotifier { - +class RSSCloudNotifier +{ const MAX_FAILURES = 3; /** * Send an HTTP GET to the notification handler with a * challenge string to see if it repsonds correctly. * - * @param String $endpoint URL of the notification handler - * @param String $feed the feed being subscribed to + * @param string $endpoint URL of the notification handler + * @param string $feed the feed being subscribed to * * @return boolean success */ @@ -61,10 +61,11 @@ class RSSCloudNotifier { $url = $endpoint . '?' . http_build_query($params); try { - $client = new HTTPClient(); + $client = new HTTPClient(); $response = $client->get($url); } catch (HTTP_Request2_Exception $e) { - common_log(LOG_INFO, 'RSSCloud plugin - failure testing notify handler ' . + common_log(LOG_INFO, + 'RSSCloud plugin - failure testing notify handler ' . $endpoint . ' - ' . $e->getMessage()); return false; } @@ -105,18 +106,19 @@ class RSSCloudNotifier { * HTTP POST a notification that a feed has been updated * ('ping the cloud'). * - * @param String $endpoint URL of the notification handler - * @param String $feed the feed being subscribed to + * @param String $endpoint URL of the notification handler + * @param String $feed the feed being subscribed to * * @return boolean success */ - function postUpdate($endpoint, $feed) { + function postUpdate($endpoint, $feed) + { $headers = array(); $postdata = array('url' => $feed); try { - $client = new HTTPClient(); + $client = new HTTPClient(); $response = $client->post($endpoint, $headers, $postdata); } catch (HTTP_Request2_Exception $e) { common_log(LOG_INFO, 'RSSCloud plugin - failure notifying ' . @@ -153,6 +155,7 @@ class RSSCloudNotifier { $profile->nickname . '.rss'; $cloudSub = new RSSCloudSubscription(); + $cloudSub->subscribed = $profile->id; if ($cloudSub->find()) { @@ -186,7 +189,8 @@ class RSSCloudNotifier { if ($failCnt == self::MAX_FAILURES) { common_log(LOG_INFO, - 'Deleting RSSCloud subcription (max failure count reached), profile: ' . + 'Deleting RSSCloud subcription ' . + '(max failure count reached), profile: ' . $cloudSub->subscribed . ' handler: ' . $cloudSub->url); @@ -209,7 +213,8 @@ class RSSCloudNotifier { } else { - common_debug('Updating failure count on RSSCloud subscription. ' . $failCnt); + common_debug('Updating failure count on RSSCloud subscription. ' . + $failCnt); $failCnt = $cloudSub->failures + 1; @@ -224,9 +229,11 @@ class RSSCloudNotifier { if (!$result) { common_log_db_error($cloudsub, 'UPDATE', __FILE__); - common_log(LOG_ERR, 'Could not update failure count on RSSCloud subscription'); + common_log(LOG_ERR, + 'Could not update failure ' . + 'count on RSSCloud subscription'); } - } + } } } diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index b1af9b59cb..db2cdd74d7 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -98,16 +98,20 @@ class RSSCloudPlugin extends Plugin * * Hook for RouterInitialized event. * + * @param Mapper &$m URL parser and mapper + * * @return boolean hook return */ function onRouterInitialized(&$m) { - $m->connect('/main/rsscloud/request_notify', array('action' => 'RSSCloudRequestNotify')); + $m->connect('/main/rsscloud/request_notify', + array('action' => 'RSSCloudRequestNotify')); // XXX: This is just for end-to-end testing. Uncomment if you need to pretend // to be a cloud hub for some reason. - // $m->connect('/main/rsscloud/notify', array('action' => 'LoggingAggregator')); + //$m->connect('/main/rsscloud/notify', + // array('action' => 'LoggingAggregator')); return true; } @@ -126,17 +130,18 @@ class RSSCloudPlugin extends Plugin { switch ($cls) { - case 'RSSCloudSubscription': - include_once(INSTALLDIR . '/plugins/RSSCloud/RSSCloudSubscription.php'); + case 'RSSCloudSubscription': + include_once INSTALLDIR . '/plugins/RSSCloud/RSSCloudSubscription.php'; return false; - case 'RSSCloudNotifier': - include_once(INSTALLDIR . '/plugins/RSSCloud/RSSCloudNotifier.php'); + case 'RSSCloudNotifier': + include_once INSTALLDIR . '/plugins/RSSCloud/RSSCloudNotifier.php'; return false; - case 'RSSCloudRequestNotifyAction': - case 'LoggingAggregatorAction': - include_once(INSTALLDIR . '/plugins/RSSCloud/' . mb_substr($cls, 0, -6) . '.php'); + case 'RSSCloudRequestNotifyAction': + case 'LoggingAggregatorAction': + include_once INSTALLDIR . '/plugins/RSSCloud/' . + mb_substr($cls, 0, -6) . '.php'; return false; - default: + default: return true; } } @@ -145,7 +150,7 @@ class RSSCloudPlugin extends Plugin * Add a element to the RSS feed (after the rss * element is started). * - * @param Action $action + * @param Action $action the ApiAction * * @return void */ @@ -215,7 +220,7 @@ class RSSCloudPlugin extends Plugin /** * Determine whether the notice was locally created * - * @param Notice $notice + * @param Notice $notice the notice in question * * @return boolean locality */ @@ -233,7 +238,8 @@ class RSSCloudPlugin extends Plugin * @return boolean hook return */ - function onCheckSchema() { + function onCheckSchema() + { $schema = Schema::get(); $schema->ensureTable('rsscloud_subscription', array(new ColumnDef('subscribed', 'integer', @@ -248,8 +254,7 @@ class RSSCloudPlugin extends Plugin null, false, null, 'CURRENT_TIMESTAMP', 'on update CURRENT_TIMESTAMP') - ) - ); + )); return true; } diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index 9643bf4325..a648efff17 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -56,9 +56,9 @@ class RSSCloudRequestNotifyAction extends Action { parent::prepare($args); - $this->ip = $_SERVER['REMOTE_ADDR']; - $this->port = $this->arg('port'); - $this->path = $this->arg('path'); + $this->ip = $_SERVER['REMOTE_ADDR']; + $this->port = $this->arg('port'); + $this->path = $this->arg('path'); if ($this->path[0] != '/') { $this->path = '/' . $this->path; @@ -68,7 +68,7 @@ class RSSCloudRequestNotifyAction extends Action $this->procedure = $this->arg('notifyProcedure'); $this->domain = $this->arg('domain'); - $this->feeds = $this->getFeeds(); + $this->feeds = $this->getFeeds(); return true; } @@ -124,7 +124,8 @@ class RSSCloudRequestNotifyAction extends Action } if (empty($this->feeds)) { - $msg = 'You must provide at least one valid profile feed url (url1, url2, url3 ... urlN).'; + $msg = 'You must provide at least one valid profile feed url ' . + '(url1, url2, url3 ... urlN).'; $this->showResult(false, $msg); return; } @@ -195,7 +196,7 @@ class RSSCloudRequestNotifyAction extends Action { $feeds = array(); - while (list($key, $feed) = each ($this->args)) { + while (list($key, $feed) = each($this->args)) { if (preg_match('/^url\d*$/', $key)) { $feeds[] = $feed; } @@ -251,7 +252,7 @@ class RSSCloudRequestNotifyAction extends Action } else { return 'http://' . $this->ip . ':' . $this->port . $this->path; } - } + } /** * Uses the nickname part of the subscribed feed URL to figure out @@ -267,7 +268,7 @@ class RSSCloudRequestNotifyAction extends Action { // We only do profile feeds - $path = common_path('api/statuses/user_timeline/'); + $path = common_path('api/statuses/user_timeline/'); $valid = '%^' . $path . '(?.*)\.rss$%'; if (preg_match($valid, $feed, $matches)) { @@ -283,7 +284,7 @@ class RSSCloudRequestNotifyAction extends Action /** * Save an RSSCloud subscription * - * @param $feed a valid profile feed + * @param string $feed a valid profile feed * * @return boolean success result */ @@ -329,8 +330,9 @@ class RSSCloudRequestNotifyAction extends Action function showResult($success, $msg) { $this->startXML(); - $this->elementStart('notifyResult', array('success' => ($success) ? 'true' : 'false', - 'msg' => $msg)); + $this->elementStart('notifyResult', + array('success' => ($success) ? 'true' : 'false', + 'msg' => $msg)); $this->endXML(); } From fd33865258644d5f41341e8efa239e2e4d064897 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 29 Dec 2009 03:52:02 +0000 Subject: [PATCH 21/36] Fix subscription path in link element --- plugins/RSSCloud/RSSCloudPlugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index db2cdd74d7..4b9812a479 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -70,7 +70,7 @@ class RSSCloudPlugin extends Plugin // set defaults - $local_server = parse_url(common_path('/main/rsscloud/request_notify')); + $local_server = parse_url(common_path('main/rsscloud/request_notify')); if (empty($this->domain)) { $this->domain = $local_server['host']; @@ -81,7 +81,7 @@ class RSSCloudPlugin extends Plugin } if (empty($this->path)) { - $this->path = '/main/rsscloud/request_notify'; + $this->path = $local_server['path']; } if (empty($this->funct)) { From c95114ea02757cf114ae34f33c9a7cacee49a8da Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 6 Jan 2010 07:44:34 +0000 Subject: [PATCH 22/36] Some better log msgs --- plugins/RSSCloud/README | 5 +++-- plugins/RSSCloud/RSSCloudRequestNotify.php | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/RSSCloud/README b/plugins/RSSCloud/README index 8fd281aab6..1237e3e0e2 100644 --- a/plugins/RSSCloud/README +++ b/plugins/RSSCloud/README @@ -4,7 +4,8 @@ http://SITE/PATH/api/statuses/user_timeline/USERNAME.rss). When the plugin is enabled, StatusNet acts as both the publisher and hub ('writer' and 'cloud' in RSSCloud parlance), but only for local StatusNet feeds. It's not possible to use it as a general purpose hub -- for instance you can't -subscribe and get updates to a Wordpress feed from StatusNet. +subscribe and get updates to a Wordpress feed from StatusNet using this +plugin. To use the plugin, add the following to your config.php: @@ -33,7 +34,7 @@ Notes ----- - Again, only RSS 2.0 profile feeds may be subscribed to, and they have - be the ones with user names in them, like: + to be the ones with user names in them, like: http://SITE/PATH/api/statuses/user_timeline/USERNAME.rss - Subscriptions are deleted after three notification failures in a row (not sure this is optimal). diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index a648efff17..d76c08d379 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -137,6 +137,11 @@ class RSSCloudRequestNotifyAction extends Action foreach ($this->feeds as $feed) { if (!$this->validateFeed($feed)) { + + $nh = $this->getNotifyUrl(); + common_log(LOG_WARNING, + "RSSCloud plugin - $nh tried to subscribe to invalid feed: $feed"); + $msg = 'Feed subscription failed - Not a valid feed.'; $this->showResult(false, $msg); return; @@ -216,8 +221,6 @@ class RSSCloudRequestNotifyAction extends Action function testNotificationHandler($feed) { - common_debug("RSSCloudPlugin - testNotificationHandler()"); - $notifyUrl = $this->getNotifyUrl(); $notifier = new RSSCloudNotifier(); @@ -226,12 +229,13 @@ class RSSCloudRequestNotifyAction extends Action // 'domain' param set, so we have to use GET and send a challenge - common_log(LOG_INFO, 'Testing notification handler with challenge: ' . + common_log(LOG_INFO, + 'RSSCloud plugin - Testing notification handler with challenge: ' . $notifyUrl); return $notifier->challenge($notifyUrl, $feed); } else { - common_log(LOG_INFO, 'Testing notification handler: ' . + common_log(LOG_INFO, 'RSSCloud plugin - Testing notification handler: ' . $notifyUrl); return $notifier->postUpdate($notifyUrl, $feed); @@ -298,7 +302,8 @@ class RSSCloudRequestNotifyAction extends Action $sub = RSSCloudSubscription::getSubscription($user->id, $notifyUrl); if ($sub) { - common_debug("Already subscribed to that!"); + common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl refreshed subscription" . + " to user $user->nickname (id: $user->id)."); } else { $sub = new RSSCloudSubscription(); @@ -312,6 +317,8 @@ class RSSCloudRequestNotifyAction extends Action return false; } + common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl subscribed" . + " to user $user->nickname (id: $user->id)"); } return true; From d7e2a2949869dfc1b84d96259471868e75fc7899 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 6 Jan 2010 11:31:06 +0100 Subject: [PATCH 23/36] Removed unnecessary internal style --- plugins/Recaptcha/RecaptchaPlugin.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/Recaptcha/RecaptchaPlugin.php b/plugins/Recaptcha/RecaptchaPlugin.php index db118dbb81..3665214f85 100644 --- a/plugins/Recaptcha/RecaptchaPlugin.php +++ b/plugins/Recaptcha/RecaptchaPlugin.php @@ -62,9 +62,8 @@ class RecaptchaPlugin extends Plugin function onEndRegistrationFormData($action) { - $action->style('#recaptcha_area{float:left;}'); $action->elementStart('li'); - $action->raw(''); + $action->raw(''); if($this->checkssl() === true) { $action->raw(recaptcha_get_html($this->public_key), null, true); } else { From fe181002814359620e962454444917580ee51970 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 6 Jan 2010 18:02:46 +0000 Subject: [PATCH 24/36] Uses a fixed height for header to control the layout for notice form in Cloudy --- theme/cloudy/css/display.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/theme/cloudy/css/display.css b/theme/cloudy/css/display.css index 92d5449773..23e5c4ec65 100644 --- a/theme/cloudy/css/display.css +++ b/theme/cloudy/css/display.css @@ -242,6 +242,7 @@ margin-right:-47px; #header { width:100%; +height:10.5em; position:relative; float:left; padding-top:18px; @@ -1522,7 +1523,7 @@ min-width:0; #useradminpanel #content, #pathsadminpanel #content, #adminprofileflag #content { -padding-top:170px; +padding-top:12.5em; } #profilesettings #form_notice, From aa623f60df7f47a00c80a254aeb5b3f1365466bd Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 6 Jan 2010 18:08:43 +0000 Subject: [PATCH 25/36] Fixes layout for attachment page in Cloudy theme --- theme/cloudy/css/display.css | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/theme/cloudy/css/display.css b/theme/cloudy/css/display.css index 23e5c4ec65..6b44086027 100644 --- a/theme/cloudy/css/display.css +++ b/theme/cloudy/css/display.css @@ -1518,11 +1518,12 @@ min-width:0; #subscribers.user_in #content, #showgroup.user_in #content, #conversation.user_in #content, -#siteadminpanel #content, -#designadminpanel #content, -#useradminpanel #content, -#pathsadminpanel #content, -#adminprofileflag #content { +#attachment.user_in #content, +#siteadminpanel.user_in #content, +#designadminpanel.user_in #content, +#useradminpanel.user_in #content, +#pathsadminpanel.user_in #content, +#adminprofileflag.user_in #content { padding-top:12.5em; } From 9a7a2f2ff472f005d7cb9cd9e5ea5c59dc32dfeb Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 6 Jan 2010 18:12:13 +0000 Subject: [PATCH 26/36] Updated notice-option width in Cloudy theme --- theme/cloudy/css/display.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/cloudy/css/display.css b/theme/cloudy/css/display.css index 6b44086027..a27bd74b80 100644 --- a/theme/cloudy/css/display.css +++ b/theme/cloudy/css/display.css @@ -1001,7 +1001,7 @@ float:left; font-size:0.95em; margin-left:59px; min-width:60%; -max-width:66%; +max-width:62%; } #showstream .notice div.entry-content, #shownotice .notice div.entry-content { From b93244395f2c5643ae5e1be1e4e1d652c6d654c1 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 6 Jan 2010 11:10:33 -0800 Subject: [PATCH 27/36] Fix for broken profile flag admin UI: delete stray flag entries when users are deleted so broken entries don't litter the lookups. * added ProfileDeleteRelated event to match UserDeleteRelated, to allow plugins to add extra related tables on profile deletion * UserFlagPlugin: deleting flags when target profile is deleted * UserFlagPlugin: deleting flags when flagging user is deleted * UserFlagPlugin: fix for autoloader -- class names are case-insensitive. We may get lowercase class names coming in at times, such as when creating DB objects programatically from a table name. Note that any already-existing bogus entries need to be removed from the database: select * from user_flag_profile where (select id from profile where id=profile_id) is null; select * from user_flag_profile where (select id from user where id=user_id) is null; --- classes/Profile.php | 1 + plugins/UserFlag/UserFlagPlugin.php | 51 ++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/classes/Profile.php b/classes/Profile.php index 03196447b8..25d908dbf9 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -504,6 +504,7 @@ class Profile extends Memcached_DataObject 'Reply', 'Group_member', ); + Event::handle('ProfileDeleteRelated', array($this, &$related)); foreach ($related as $cls) { $inst = new $cls(); diff --git a/plugins/UserFlag/UserFlagPlugin.php b/plugins/UserFlag/UserFlagPlugin.php index 602a5bfa88..a33869c19e 100644 --- a/plugins/UserFlag/UserFlagPlugin.php +++ b/plugins/UserFlag/UserFlagPlugin.php @@ -102,20 +102,20 @@ class UserFlagPlugin extends Plugin function onAutoload($cls) { - switch ($cls) + switch (strtolower($cls)) { - case 'FlagprofileAction': - case 'AdminprofileflagAction': - case 'ClearflagAction': + case 'flagprofileaction': + case 'adminprofileflagaction': + case 'clearflagaction': include_once INSTALLDIR.'/plugins/UserFlag/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; return false; - case 'FlagProfileForm': - case 'ClearFlagForm': + case 'flagprofileform': + case 'clearflagform': include_once INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php'); return false; - case 'User_flag_profile': - include_once INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php'; + case 'user_flag_profile': + include_once INSTALLDIR.'/plugins/UserFlag/'.ucfirst(strtolower($cls)).'.php'; return false; default: return true; @@ -258,4 +258,39 @@ class UserFlagPlugin extends Plugin } return true; } + + /** + * Ensure that flag entries for a profile are deleted + * along with the profile when deleting users. + * This prevents breakage of the admin profile flag UI. + * + * @param Profile $profile + * @param array &$related list of related tables; entries + * with matching profile_id will be deleted. + * + * @return boolean hook result + */ + + function onProfileDeleteRelated($profile, &$related) + { + $related[] = 'user_flag_profile'; + return true; + } + + /** + * Ensure that flag entries created by a user are deleted + * when that user gets deleted. + * + * @param User $user + * @param array &$related list of related tables; entries + * with matching user_id will be deleted. + * + * @return boolean hook result + */ + + function onUserDeleteRelated($user, &$related) + { + $related[] = 'user_flag_profile'; + return true; + } } From 6f5b765c97c8616e4a79719bfe835cb03dc0a236 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 6 Jan 2010 13:08:56 -0800 Subject: [PATCH 28/36] suppress notice for undefined prompt variable when console.php is used from non-interactive terminal --- scripts/console.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/console.php b/scripts/console.php index 329caf4724..8b62a3a967 100755 --- a/scripts/console.php +++ b/scripts/console.php @@ -128,6 +128,8 @@ function console_help() if (CONSOLE_INTERACTIVE) { print "StatusNet interactive PHP console... type ctrl+D or enter 'exit' to exit.\n"; $prompt = common_config('site', 'name') . '> '; +} else { + $prompt = ''; } while (!feof(STDIN)) { $line = read_input_line($prompt); From 85554d0840642f4c1b47b50202dd648db565781c Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 6 Jan 2010 13:23:39 -0800 Subject: [PATCH 29/36] Rearrange Memcached_DataObject::staticGet() to avoid "only variables can be passed by reference" warnings when DB lookup fails and we return false. (We need to keep it returning a reference because the extlib parent class is stuck in PHP 4-land and uses references everywhere, including this function's return value. Yuck!) Also changed pkeyGet to drop the reference, since it doesn't have an upstream equivalent. --- classes/Memcached_DataObject.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index d11bd63682..04f75b775c 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -90,17 +90,16 @@ class Memcached_DataObject extends DB_DataObject unset($i); } $i = Memcached_DataObject::getcached($cls, $k, $v); - if ($i !== false) { // false == cache miss - return $i; - } else { + if ($i === false) { // false == cache miss $i = DB_DataObject::factory($cls); if (empty($i)) { - return false; + $i = false; + return $i; } $result = $i->get($k, $v); if ($result) { + // Hit! $i->encache(); - return $i; } else { // save the fact that no such row exists $c = self::memcache(); @@ -108,12 +107,16 @@ class Memcached_DataObject extends DB_DataObject $ck = self::cachekey($cls, $k, $v); $c->set($ck, null); } - return false; + $i = false; } } + return $i; } - function &pkeyGet($cls, $kv) + /** + * @fixme Should this return false on lookup fail to match staticGet? + */ + function pkeyGet($cls, $kv) { $i = Memcached_DataObject::multicache($cls, $kv); if ($i !== false) { // false == cache miss From 013e6dfdd481470cc02994b6db58a387a95016ca Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 6 Jan 2010 13:40:28 -0800 Subject: [PATCH 30/36] Don't output notices from deleted users. --- actions/twitapisearchatom.php | 9 ++++++++- lib/jsonsearchresultslist.php | 10 ++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/actions/twitapisearchatom.php b/actions/twitapisearchatom.php index 1cb8d7efe6..baed2a0c7c 100644 --- a/actions/twitapisearchatom.php +++ b/actions/twitapisearchatom.php @@ -208,7 +208,14 @@ class TwitapisearchatomAction extends ApiAction $this->showFeed(); foreach ($notices as $n) { - $this->showEntry($n); + + $profile = $n->getProfile(); + + // Don't show notices from deleted users + + if (!empty($profile)) { + $this->showEntry($n); + } } $this->endAtom(); diff --git a/lib/jsonsearchresultslist.php b/lib/jsonsearchresultslist.php index 569bfa8734..0d72ddf7ab 100644 --- a/lib/jsonsearchresultslist.php +++ b/lib/jsonsearchresultslist.php @@ -105,8 +105,14 @@ class JSONSearchResultsList break; } - $item = new ResultItem($this->notice); - array_push($this->results, $item); + $profile = $this->notice->getProfile(); + + // Don't show notices from deleted users + + if (!empty($profile)) { + $item = new ResultItem($this->notice); + array_push($this->results, $item); + } } $time_end = microtime(true); From 208bab32b7f9784701c538217d0c1c2779a22146 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Wed, 6 Jan 2010 16:48:24 -0500 Subject: [PATCH 31/36] Remove erroneous call to parent::onInitializePlugin() --- plugins/LdapAuthorization/LdapAuthorizationPlugin.php | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/LdapAuthorization/LdapAuthorizationPlugin.php b/plugins/LdapAuthorization/LdapAuthorizationPlugin.php index 7673e61efb..e5e22c0dde 100644 --- a/plugins/LdapAuthorization/LdapAuthorizationPlugin.php +++ b/plugins/LdapAuthorization/LdapAuthorizationPlugin.php @@ -52,7 +52,6 @@ class LdapAuthorizationPlugin extends AuthorizationPlugin public $attributes = array(); function onInitializePlugin(){ - parent::onInitializePlugin(); if(!isset($this->host)){ throw new Exception("must specify a host"); } From 541053e84b2754e0d2c8b735c624383b6a126122 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Wed, 6 Jan 2010 17:08:01 -0500 Subject: [PATCH 32/36] The structure return by parse_url is an associative array, not an object. --- lib/htmloutputter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php index 2091c6e2ca..31660ce954 100644 --- a/lib/htmloutputter.php +++ b/lib/htmloutputter.php @@ -352,7 +352,7 @@ class HTMLOutputter extends XMLOutputter { if(Event::handle('StartScriptElement', array($this,&$src,&$type))) { $url = parse_url($src); - if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment)) + if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) { $src = common_path($src) . '?version=' . STATUSNET_VERSION; } From 20144285ca610812abe09018ee208e12e38a966a Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Wed, 6 Jan 2010 17:13:09 -0500 Subject: [PATCH 33/36] The structure return by parse_url is an associative array, not an object. --- plugins/Minify/MinifyPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Minify/MinifyPlugin.php b/plugins/Minify/MinifyPlugin.php index 71fade19a5..718bfd1635 100644 --- a/plugins/Minify/MinifyPlugin.php +++ b/plugins/Minify/MinifyPlugin.php @@ -84,7 +84,7 @@ class MinifyPlugin extends Plugin function onStartScriptElement($action,&$src,&$type) { $url = parse_url($src); - if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment)) + if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) { $src = $this->minifyUrl($src); } From 4e2acd153b4e3208e24464478098fac458a13590 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 6 Jan 2010 14:28:40 -0800 Subject: [PATCH 34/36] ...and drop the unnecessary &reference from child class pkeyGet() overrides. --- classes/Avatar.php | 2 +- classes/Config.php | 2 +- classes/Fave.php | 2 +- classes/File_to_post.php | 2 +- classes/Group_block.php | 2 +- classes/Group_inbox.php | 2 +- classes/Group_member.php | 2 +- classes/Notice_inbox.php | 2 +- classes/Notice_tag.php | 2 +- classes/Profile_role.php | 2 +- classes/Queue_item.php | 2 +- classes/Subscription.php | 2 +- plugins/OpenID/User_openid_trustroot.php | 2 +- plugins/UserFlag/User_flag_profile.php | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/classes/Avatar.php b/classes/Avatar.php index 8d6424e8b2..91bde0f040 100644 --- a/classes/Avatar.php +++ b/classes/Avatar.php @@ -37,7 +37,7 @@ class Avatar extends Memcached_DataObject } } - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Avatar', $kv); } diff --git a/classes/Config.php b/classes/Config.php index 6d914ca1f6..43b99587fa 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -120,7 +120,7 @@ class Config extends Memcached_DataObject return $result; } - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Config', $kv); } diff --git a/classes/Fave.php b/classes/Fave.php index 11e876ff19..8113c8e166 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -32,7 +32,7 @@ class Fave extends Memcached_DataObject return $fave; } - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Fave', $kv); } diff --git a/classes/File_to_post.php b/classes/File_to_post.php index e3db91b205..72a42b0880 100644 --- a/classes/File_to_post.php +++ b/classes/File_to_post.php @@ -62,7 +62,7 @@ class File_to_post extends Memcached_DataObject } } - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('File_to_post', $kv); } diff --git a/classes/Group_block.php b/classes/Group_block.php index de2cf5f6eb..9f4d592956 100644 --- a/classes/Group_block.php +++ b/classes/Group_block.php @@ -40,7 +40,7 @@ class Group_block extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Group_block', $kv); } diff --git a/classes/Group_inbox.php b/classes/Group_inbox.php index 1af7439f7f..2a0787e387 100644 --- a/classes/Group_inbox.php +++ b/classes/Group_inbox.php @@ -20,7 +20,7 @@ class Group_inbox extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Group_inbox', $kv); } diff --git a/classes/Group_member.php b/classes/Group_member.php index 3c23a991f0..069b2c7a1c 100644 --- a/classes/Group_member.php +++ b/classes/Group_member.php @@ -21,7 +21,7 @@ class Group_member extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Group_member', $kv); } diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index d3ddad656a..e350e6e2f8 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -101,7 +101,7 @@ class Notice_inbox extends Memcached_DataObject return $ids; } - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Notice_inbox', $kv); } diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php index 02740280f5..79231f0b0c 100644 --- a/classes/Notice_tag.php +++ b/classes/Notice_tag.php @@ -96,7 +96,7 @@ class Notice_tag extends Memcached_DataObject } } - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Notice_tag', $kv); } diff --git a/classes/Profile_role.php b/classes/Profile_role.php index afa7fb74e4..74aca37305 100644 --- a/classes/Profile_role.php +++ b/classes/Profile_role.php @@ -43,7 +43,7 @@ class Profile_role extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Profile_role', $kv); } diff --git a/classes/Queue_item.php b/classes/Queue_item.php index 295c321b57..9c673540d7 100644 --- a/classes/Queue_item.php +++ b/classes/Queue_item.php @@ -55,7 +55,7 @@ class Queue_item extends Memcached_DataObject return null; } - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Queue_item', $kv); } diff --git a/classes/Subscription.php b/classes/Subscription.php index fedfd5f19e..faf1331cda 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -46,7 +46,7 @@ class Subscription extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('Subscription', $kv); } diff --git a/plugins/OpenID/User_openid_trustroot.php b/plugins/OpenID/User_openid_trustroot.php index 44288945be..0b411b8f7f 100644 --- a/plugins/OpenID/User_openid_trustroot.php +++ b/plugins/OpenID/User_openid_trustroot.php @@ -22,7 +22,7 @@ class User_openid_trustroot extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('User_openid_trustroot', $kv); } diff --git a/plugins/UserFlag/User_flag_profile.php b/plugins/UserFlag/User_flag_profile.php index 6582594524..063ed04eac 100644 --- a/plugins/UserFlag/User_flag_profile.php +++ b/plugins/UserFlag/User_flag_profile.php @@ -97,7 +97,7 @@ class User_flag_profile extends Memcached_DataObject * @return User_flag_profile found object or null */ - function &pkeyGet($kv) + function pkeyGet($kv) { return Memcached_DataObject::pkeyGet('User_flag_profile', $kv); } From 5d9a2eb17e3f6e3bc73b5aa80625a365761b6689 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 6 Jan 2010 14:42:46 -0800 Subject: [PATCH 35/36] Ticket 2107: remove "not implemented" items from sms/xmpp help; nobody likes being told what they can't do! Also broke up the localized help message into line-by-line pieces to ease translation maintenance. --- doc-src/sms | 11 +------- lib/command.php | 74 ++++++++++++++++++++++++------------------------- 2 files changed, 38 insertions(+), 47 deletions(-) diff --git a/doc-src/sms b/doc-src/sms index 1a3064318f..2c20921c35 100644 --- a/doc-src/sms +++ b/doc-src/sms @@ -56,13 +56,4 @@ You can use the following commands with %%site.name%%. * sub <nickname> - same as 'follow' * unsub <nickname> - same as 'leave' * last <nickname> - same as 'get' -* on <nickname> - not yet implemented. -* off <nickname> - not yet implemented. -* nudge <nickname> - not yet implemented. -* invite <phone number> - not yet implemented. -* track <word> - not yet implemented. -* untrack <word> - not yet implemented. -* track off - not yet implemented. -* untrack all - not yet implemented. -* tracks - not yet implemented. -* tracking - not yet implemented. +* nudge <nickname> - remind a user to update. diff --git a/lib/command.php b/lib/command.php index 67140c3485..ad2e0bb975 100644 --- a/lib/command.php +++ b/lib/command.php @@ -742,42 +742,42 @@ class HelpCommand extends Command function execute($channel) { $channel->output($this->user, - _("Commands:\n". - "on - turn on notifications\n". - "off - turn off notifications\n". - "help - show this help\n". - "follow - subscribe to user\n". - "groups - lists the groups you have joined\n". - "subscriptions - list the people you follow\n". - "subscribers - list the people that follow you\n". - "leave - unsubscribe from user\n". - "d - direct message to user\n". - "get - get last notice from user\n". - "whois - get profile info on user\n". - "fav - add user's last notice as a 'fave'\n". - "fav # - add notice with the given id as a 'fave'\n". - "repeat # - repeat a notice with a given id\n". - "repeat - repeat the last notice from user\n". - "reply # - reply to notice with a given id\n". - "reply - reply to the last notice from user\n". - "join - join group\n". - "login - Get a link to login to the web interface\n". - "drop - leave group\n". - "stats - get your stats\n". - "stop - same as 'off'\n". - "quit - same as 'off'\n". - "sub - same as 'follow'\n". - "unsub - same as 'leave'\n". - "last - same as 'get'\n". - "on - not yet implemented.\n". - "off - not yet implemented.\n". - "nudge - remind a user to update.\n". - "invite - not yet implemented.\n". - "track - not yet implemented.\n". - "untrack - not yet implemented.\n". - "track off - not yet implemented.\n". - "untrack all - not yet implemented.\n". - "tracks - not yet implemented.\n". - "tracking - not yet implemented.\n")); + _("Commands:")."\n". + _("on - turn on notifications")."\n". + _("off - turn off notifications")."\n". + _("help - show this help")."\n". + _("follow - subscribe to user")."\n". + _("groups - lists the groups you have joined")."\n". + _("subscriptions - list the people you follow")."\n". + _("subscribers - list the people that follow you")."\n". + _("leave - unsubscribe from user")."\n". + _("d - direct message to user")."\n". + _("get - get last notice from user")."\n". + _("whois - get profile info on user")."\n". + _("fav - add user's last notice as a 'fave'")."\n". + _("fav # - add notice with the given id as a 'fave'")."\n". + _("repeat # - repeat a notice with a given id")."\n". + _("repeat - repeat the last notice from user")."\n". + _("reply # - reply to notice with a given id")."\n". + _("reply - reply to the last notice from user")."\n". + _("join - join group")."\n". + #_("login - Get a link to login to the web interface")."\n". + _("drop - leave group")."\n". + _("stats - get your stats")."\n". + _("stop - same as 'off'")."\n". + _("quit - same as 'off'")."\n". + _("sub - same as 'follow'")."\n". + _("unsub - same as 'leave'")."\n". + _("last - same as 'get'")."\n". + #_("on - not yet implemented.")."\n". + #_("off - not yet implemented.")."\n". + _("nudge - remind a user to update.")."\n"); + #_("invite - not yet implemented.")."\n". + #_("track - not yet implemented.")."\n". + #_("untrack - not yet implemented.")."\n". + #_("track off - not yet implemented.")."\n". + #_("untrack all - not yet implemented.")."\n". + #_("tracks - not yet implemented.")."\n". + #_("tracking - not yet implemented.")."\n" } } From a1c3a2d3a12c1667492e4107007b31ec3a1f9c7b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 6 Jan 2010 16:21:29 -0800 Subject: [PATCH 36/36] Fix broken API method /api/statusnet/groups/leave/:id.:format --- actions/apigroupleave.php | 8 ++++---- actions/leavegroup.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actions/apigroupleave.php b/actions/apigroupleave.php index 514a3a557d..5627bfc146 100644 --- a/actions/apigroupleave.php +++ b/actions/apigroupleave.php @@ -108,7 +108,7 @@ class ApiGroupLeaveAction extends ApiAuthAction $member = new Group_member(); $member->group_id = $this->group->id; - $member->profile_id = $this->auth->id; + $member->profile_id = $this->auth_user->id; if (!$member->find(true)) { $this->serverError(_('You are not a member of this group.')); @@ -118,12 +118,12 @@ class ApiGroupLeaveAction extends ApiAuthAction $result = $member->delete(); if (!$result) { - common_log_db_error($member, 'INSERT', __FILE__); + common_log_db_error($member, 'DELETE', __FILE__); $this->serverError( sprintf( - _('Could not remove user %s to group %s.'), + _('Could not remove user %s from group %s.'), $this->user->nickname, - $this->$group->nickname + $this->group->nickname ) ); return; diff --git a/actions/leavegroup.php b/actions/leavegroup.php index 08fce15098..90c85e1a4e 100644 --- a/actions/leavegroup.php +++ b/actions/leavegroup.php @@ -123,8 +123,8 @@ class LeavegroupAction extends Action $result = $member->delete(); if (!$result) { - common_log_db_error($member, 'INSERT', __FILE__); - $this->serverError(sprintf(_('Could not remove user %s to group %s'), + common_log_db_error($member, 'DELETE', __FILE__); + $this->serverError(sprintf(_('Could not remove user %s from group %s'), $cur->nickname, $this->group->nickname)); }