Merge branch '0.9.x' of git@gitorious.org:statusnet/mainline into 0.9.x
This commit is contained in:
commit
a55939f3b1
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -37,7 +37,7 @@ class Avatar extends Memcached_DataObject
|
|||
}
|
||||
}
|
||||
|
||||
function &pkeyGet($kv)
|
||||
function pkeyGet($kv)
|
||||
{
|
||||
return Memcached_DataObject::pkeyGet('Avatar', $kv);
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ class Config extends Memcached_DataObject
|
|||
return $result;
|
||||
}
|
||||
|
||||
function &pkeyGet($kv)
|
||||
function pkeyGet($kv)
|
||||
{
|
||||
return Memcached_DataObject::pkeyGet('Config', $kv);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class Fave extends Memcached_DataObject
|
|||
return $fave;
|
||||
}
|
||||
|
||||
function &pkeyGet($kv)
|
||||
function pkeyGet($kv)
|
||||
{
|
||||
return Memcached_DataObject::pkeyGet('Fave', $kv);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ class Notice_tag extends Memcached_DataObject
|
|||
}
|
||||
}
|
||||
|
||||
function &pkeyGet($kv)
|
||||
function pkeyGet($kv)
|
||||
{
|
||||
return Memcached_DataObject::pkeyGet('Notice_tag', $kv);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
11
doc-src/sms
11
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.
|
||||
|
|
|
@ -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 <nickname> - 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 <nickname> - unsubscribe from user\n".
|
||||
"d <nickname> <text> - direct message to user\n".
|
||||
"get <nickname> - get last notice from user\n".
|
||||
"whois <nickname> - get profile info on user\n".
|
||||
"fav <nickname> - add user's last notice as a 'fave'\n".
|
||||
"fav #<notice_id> - add notice with the given id as a 'fave'\n".
|
||||
"repeat #<notice_id> - repeat a notice with a given id\n".
|
||||
"repeat <nickname> - repeat the last notice from user\n".
|
||||
"reply #<notice_id> - reply to notice with a given id\n".
|
||||
"reply <nickname> - reply to the last notice from user\n".
|
||||
"join <group> - join group\n".
|
||||
"login - Get a link to login to the web interface\n".
|
||||
"drop <group> - leave group\n".
|
||||
"stats - get your stats\n".
|
||||
"stop - same as 'off'\n".
|
||||
"quit - same as 'off'\n".
|
||||
"sub <nickname> - same as 'follow'\n".
|
||||
"unsub <nickname> - same as 'leave'\n".
|
||||
"last <nickname> - same as 'get'\n".
|
||||
"on <nickname> - not yet implemented.\n".
|
||||
"off <nickname> - not yet implemented.\n".
|
||||
"nudge <nickname> - remind a user to update.\n".
|
||||
"invite <phone number> - not yet implemented.\n".
|
||||
"track <word> - not yet implemented.\n".
|
||||
"untrack <word> - 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 <nickname> - 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 <nickname> - unsubscribe from user")."\n".
|
||||
_("d <nickname> <text> - direct message to user")."\n".
|
||||
_("get <nickname> - get last notice from user")."\n".
|
||||
_("whois <nickname> - get profile info on user")."\n".
|
||||
_("fav <nickname> - add user's last notice as a 'fave'")."\n".
|
||||
_("fav #<notice_id> - add notice with the given id as a 'fave'")."\n".
|
||||
_("repeat #<notice_id> - repeat a notice with a given id")."\n".
|
||||
_("repeat <nickname> - repeat the last notice from user")."\n".
|
||||
_("reply #<notice_id> - reply to notice with a given id")."\n".
|
||||
_("reply <nickname> - reply to the last notice from user")."\n".
|
||||
_("join <group> - join group")."\n".
|
||||
#_("login - Get a link to login to the web interface")."\n".
|
||||
_("drop <group> - leave group")."\n".
|
||||
_("stats - get your stats")."\n".
|
||||
_("stop - same as 'off'")."\n".
|
||||
_("quit - same as 'off'")."\n".
|
||||
_("sub <nickname> - same as 'follow'")."\n".
|
||||
_("unsub <nickname> - same as 'leave'")."\n".
|
||||
_("last <nickname> - same as 'get'")."\n".
|
||||
#_("on <nickname> - not yet implemented.")."\n".
|
||||
#_("off <nickname> - not yet implemented.")."\n".
|
||||
_("nudge <nickname> - remind a user to update.")."\n");
|
||||
#_("invite <phone number> - not yet implemented.")."\n".
|
||||
#_("track <word> - not yet implemented.")."\n".
|
||||
#_("untrack <word> - 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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -528,6 +528,10 @@ class Schema
|
|||
$sql .= " auto_increment ";
|
||||
}
|
||||
|
||||
if (!empty($cd->extra)) {
|
||||
$sql .= "{$cd->extra} ";
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
140
plugins/RSSCloud/LoggingAggregator.php
Normal file
140
plugins/RSSCloud/LoggingAggregator.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
/**
|
||||
* This test class pretends to be an RSS aggregator. It logs notifications
|
||||
* from the cloud.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Plugin
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 <zach@status.net>
|
||||
* @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
|
||||
{
|
||||
|
||||
var $challenge = null;
|
||||
var $url = null;
|
||||
|
||||
/**
|
||||
* Initialization.
|
||||
*
|
||||
* @param array $args Web and URL arguments
|
||||
*
|
||||
* @return boolean false if user doesn't exist
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the request
|
||||
*
|
||||
* @param array $args $_REQUEST data (unused)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function handle($args)
|
||||
{
|
||||
parent::handle($args);
|
||||
|
||||
if (empty($this->url)) {
|
||||
$this->showError('Hey, you have to provide a url parameter.');
|
||||
return;
|
||||
}
|
||||
|
||||
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 $this->challenge;
|
||||
|
||||
} 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 "<notifyResult success='true' msg='Thanks for the update.' />\n";
|
||||
}
|
||||
|
||||
$this->ip = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
common_log(LOG_INFO, 'RSSCloud Logging Aggregator - ' .
|
||||
$this->ip . ' claims the feed at ' .
|
||||
$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');
|
||||
header('Content-Type: text/xml');
|
||||
echo "<?xml version='1.0'?>\n";
|
||||
echo "<notifyResult success='false' msg='$msg' />\n";
|
||||
}
|
||||
|
||||
}
|
54
plugins/RSSCloud/README
Normal file
54
plugins/RSSCloud/README
Normal file
|
@ -0,0 +1,54 @@
|
|||
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 using this
|
||||
plugin.
|
||||
|
||||
To use the plugin, add the following to your config.php:
|
||||
|
||||
addPlugin('RSSCloud');
|
||||
|
||||
Enabling the plugin will add a <cloud> element to your RSS 2.0 profile feeds
|
||||
that looks like this:
|
||||
|
||||
<cloud domain="SITE" port="80" path="/main/rsscloud/request_notify"
|
||||
registerProcedure="" protocol="http-post"/>
|
||||
|
||||
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
|
||||
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).
|
||||
- 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
|
240
plugins/RSSCloud/RSSCloudNotifier.php
Normal file
240
plugins/RSSCloud/RSSCloudNotifier.php
Normal file
|
@ -0,0 +1,240 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet, the distributed open-source microblogging tool
|
||||
*
|
||||
* Class to ping an rssCloud endpoint when a feed has been updated
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENCE: This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @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 for notifying cloud-enabled RSS aggregators that StatusNet
|
||||
* feeds have been updated.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @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);
|
||||
$params = array('url' => $feed, 'challenge' => $code);
|
||||
$url = $endpoint . '?' . http_build_query($params);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Check response is betweet 200 and 299 and body contains challenge data
|
||||
|
||||
$status = $response->getStatus();
|
||||
$body = $response->getBody();
|
||||
|
||||
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 implementors have interpreted it.
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
$postdata = array('url' => $feed);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$status = $response->getStatus();
|
||||
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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/') .
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 . '\'';
|
||||
|
||||
$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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
279
plugins/RSSCloud/RSSCloudPlugin.php
Normal file
279
plugins/RSSCloud/RSSCloudPlugin.php
Normal file
|
@ -0,0 +1,279 @@
|
|||
<?php
|
||||
/**
|
||||
* StatusNet, the distributed open-source microblogging tool
|
||||
*
|
||||
* Plugin to support RSSCloud
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* LICENCE: This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin class for adding RSSCloud capabilities to StatusNet
|
||||
*
|
||||
* @category Plugin
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @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');
|
||||
$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
|
||||
|
||||
$local_server = parse_url(common_path('main/rsscloud/request_notify'));
|
||||
|
||||
if (empty($this->domain)) {
|
||||
$this->domain = $local_server['host'];
|
||||
}
|
||||
|
||||
if (empty($this->port)) {
|
||||
$this->port = '80';
|
||||
}
|
||||
|
||||
if (empty($this->path)) {
|
||||
$this->path = $local_server['path'];
|
||||
}
|
||||
|
||||
if (empty($this->funct)) {
|
||||
$this->funct = '';
|
||||
}
|
||||
|
||||
if (empty($this->protocol)) {
|
||||
$this->protocol = 'http-post';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add RSSCloud-related paths to the router table
|
||||
*
|
||||
* 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'));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
case 'RSSCloudSubscription':
|
||||
include_once INSTALLDIR . '/plugins/RSSCloud/RSSCloudSubscription.php';
|
||||
return false;
|
||||
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';
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a <cloud> element to the RSS feed (after the rss <channel>
|
||||
* element is started).
|
||||
*
|
||||
* @param Action $action the ApiAction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function onStartApiRss($action)
|
||||
{
|
||||
if (get_class($action) == 'ApiTimelineUserAction') {
|
||||
|
||||
$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 (</cloud>).
|
||||
|
||||
$action->xw->startElement('cloud');
|
||||
foreach ($attrs as $name => $value) {
|
||||
$action->xw->writeAttribute($name, $value);
|
||||
}
|
||||
|
||||
$action->xw->endElement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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))) {
|
||||
|
||||
common_debug('broadcasting rssCloud bound notice ' . $notice->id);
|
||||
|
||||
$profile = $notice->getProfile();
|
||||
|
||||
$notifier = new RSSCloudNotifier();
|
||||
$notifier->notify($profile);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the notice was locally created
|
||||
*
|
||||
* @param Notice $notice the notice in question
|
||||
*
|
||||
* @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',
|
||||
array(new ColumnDef('subscribed', 'integer',
|
||||
null, false, 'PRI'),
|
||||
new ColumnDef('url', 'varchar',
|
||||
'255', false, 'PRI'),
|
||||
new ColumnDef('failures', 'integer',
|
||||
null, false, null, 0),
|
||||
new ColumnDef('created', 'datetime',
|
||||
null, false),
|
||||
new ColumnDef('modified', 'timestamp',
|
||||
null, false, null,
|
||||
'CURRENT_TIMESTAMP',
|
||||
'on update CURRENT_TIMESTAMP')
|
||||
));
|
||||
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 .
|
||||
'/plugins/RSSCloud/RSSCloudQueueHandler.php');
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
78
plugins/RSSCloud/RSSCloudQueueHandler.php
Executable file
78
plugins/RSSCloud/RSSCloudQueueHandler.php
Executable file
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
|
||||
|
||||
$shortoptions = 'i::';
|
||||
$longoptions = array('id::');
|
||||
|
||||
$helptext = <<<END_OF_ENJIT_HELP
|
||||
Daemon script for pushing new notices to RSSCloud subscribers.
|
||||
|
||||
-i --id Identity (default none)
|
||||
|
||||
END_OF_ENJIT_HELP;
|
||||
|
||||
require_once INSTALLDIR . '/scripts/commandline.inc';
|
||||
require_once INSTALLDIR . '/lib/queuehandler.php';
|
||||
require_once INSTALLDIR . '/plugins/RSSCloud/RSSCloudNotifier.php';
|
||||
require_once INSTALLDIR . '/plugins/RSSCloud/RSSCloudSubscription.php';
|
||||
|
||||
class RSSCloudQueueHandler extends QueueHandler
|
||||
{
|
||||
var $notifier = null;
|
||||
|
||||
function transport()
|
||||
{
|
||||
return 'rsscloud';
|
||||
}
|
||||
|
||||
function start()
|
||||
{
|
||||
$this->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();
|
347
plugins/RSSCloud/RSSCloudRequestNotify.php
Normal file
347
plugins/RSSCloud/RSSCloudRequestNotify.php
Normal file
|
@ -0,0 +1,347 @@
|
|||
<?php
|
||||
/**
|
||||
* Action to let RSSCloud aggregators request update notification when
|
||||
* user profile feeds change.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Plugin
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action class to handle RSSCloud notification (subscription) requests
|
||||
*
|
||||
* @category Plugin
|
||||
* @package StatusNet
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @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
|
||||
{
|
||||
/**
|
||||
* Initialization.
|
||||
*
|
||||
* @param array $args Web and URL arguments
|
||||
*
|
||||
* @return boolean false if user doesn't exist
|
||||
*/
|
||||
|
||||
function prepare($args)
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$this->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');
|
||||
|
||||
$this->feeds = $this->getFeeds();
|
||||
|
||||
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);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
|
||||
$this->showResult(false, 'Request must be POST.');
|
||||
return;
|
||||
}
|
||||
|
||||
$missing = array();
|
||||
|
||||
if (empty($this->port)) {
|
||||
$missing[] = 'port';
|
||||
}
|
||||
|
||||
if (empty($this->path)) {
|
||||
$missing[] = 'path';
|
||||
}
|
||||
|
||||
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)) {
|
||||
$missing[] = 'notifyProcedure';
|
||||
}
|
||||
|
||||
if (!empty($missing)) {
|
||||
$msg = 'The following parameters were missing from the request body: ' .
|
||||
implode(', ', $missing) . '.';
|
||||
$this->showResult(false, $msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->feeds)) {
|
||||
$msg = 'You must provide at least one valid profile feed url ' .
|
||||
'(url1, url2, url3 ... urlN).';
|
||||
$this->showResult(false, $msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
$msg = 'Thanks for the subscription. ' .
|
||||
'When the feed(s) update(s) we\'ll notify you.';
|
||||
|
||||
$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);
|
||||
|
||||
if (empty($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
while (list($key, $feed) = each($this->args)) {
|
||||
if (preg_match('/^url\d*$/', $key)) {
|
||||
$feeds[] = $feed;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$notifyUrl = $this->getNotifyUrl();
|
||||
|
||||
$notifier = new RSSCloudNotifier();
|
||||
|
||||
if (isset($this->domain)) {
|
||||
|
||||
// 'domain' param set, so we have to use GET and send a challenge
|
||||
|
||||
common_log(LOG_INFO,
|
||||
'RSSCloud plugin - Testing notification handler with challenge: ' .
|
||||
$notifyUrl);
|
||||
return $notifier->challenge($notifyUrl, $feed);
|
||||
|
||||
} else {
|
||||
common_log(LOG_INFO, 'RSSCloud plugin - Testing notification handler: ' .
|
||||
$notifyUrl);
|
||||
|
||||
return $notifier->postUpdate($notifyUrl, $feed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)) {
|
||||
return 'http://' . $this->domain . ':' . $this->port . $this->path;
|
||||
} else {
|
||||
return 'http://' . $this->ip . ':' . $this->port . $this->path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
$path = common_path('api/statuses/user_timeline/');
|
||||
$valid = '%^' . $path . '(?<nickname>.*)\.rss$%';
|
||||
|
||||
if (preg_match($valid, $feed, $matches)) {
|
||||
$user = User::staticGet('nickname', $matches['nickname']);
|
||||
if (!empty($user)) {
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an RSSCloud subscription
|
||||
*
|
||||
* @param string $feed a valid profile feed
|
||||
*
|
||||
* @return boolean success result
|
||||
*/
|
||||
|
||||
function saveSubscription($feed)
|
||||
{
|
||||
$user = $this->userFromFeed($feed);
|
||||
|
||||
$notifyUrl = $this->getNotifyUrl();
|
||||
|
||||
$sub = RSSCloudSubscription::getSubscription($user->id, $notifyUrl);
|
||||
|
||||
if ($sub) {
|
||||
common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl refreshed subscription" .
|
||||
" to user $user->nickname (id: $user->id).");
|
||||
} else {
|
||||
|
||||
$sub = new RSSCloudSubscription();
|
||||
|
||||
$sub->subscribed = $user->id;
|
||||
$sub->url = $notifyUrl;
|
||||
$sub->created = common_sql_now();
|
||||
|
||||
if (!$sub->insert()) {
|
||||
common_log_db_error($sub, 'INSERT', __FILE__);
|
||||
return false;
|
||||
}
|
||||
|
||||
common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl subscribed" .
|
||||
" to user $user->nickname (id: $user->id)");
|
||||
}
|
||||
|
||||
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();
|
||||
$this->elementStart('notifyResult',
|
||||
array('success' => ($success) ? 'true' : 'false',
|
||||
'msg' => $msg));
|
||||
$this->endXML();
|
||||
}
|
||||
|
||||
}
|
||||
|
79
plugins/RSSCloud/RSSCloudSubscription.php
Normal file
79
plugins/RSSCloud/RSSCloudSubscription.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/*
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2008, 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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()
|
||||
{
|
||||
|
||||
$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
|
||||
);
|
||||
|
||||
return $cols;
|
||||
}
|
||||
|
||||
function keys()
|
||||
{
|
||||
return array('subscribed' => 'N', 'url' => 'N');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -62,9 +62,8 @@ class RecaptchaPlugin extends Plugin
|
|||
|
||||
function onEndRegistrationFormData($action)
|
||||
{
|
||||
$action->style('#recaptcha_area{float:left;}');
|
||||
$action->elementStart('li');
|
||||
$action->raw('<label for="recaptcha_area">Captcha</label>');
|
||||
$action->raw('<label for="recaptcha">Captcha</label>');
|
||||
if($this->checkssl() === true) {
|
||||
$action->raw(recaptcha_get_html($this->public_key), null, true);
|
||||
} else {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -242,6 +242,7 @@ margin-right:-47px;
|
|||
|
||||
#header {
|
||||
width:100%;
|
||||
height:10.5em;
|
||||
position:relative;
|
||||
float:left;
|
||||
padding-top:18px;
|
||||
|
@ -1000,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 {
|
||||
|
@ -1517,12 +1518,13 @@ 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 {
|
||||
padding-top:170px;
|
||||
#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;
|
||||
}
|
||||
|
||||
#profilesettings #form_notice,
|
||||
|
|
Loading…
Reference in New Issue
Block a user