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 = new Group_member();
|
||||||
|
|
||||||
$member->group_id = $this->group->id;
|
$member->group_id = $this->group->id;
|
||||||
$member->profile_id = $this->auth->id;
|
$member->profile_id = $this->auth_user->id;
|
||||||
|
|
||||||
if (!$member->find(true)) {
|
if (!$member->find(true)) {
|
||||||
$this->serverError(_('You are not a member of this group.'));
|
$this->serverError(_('You are not a member of this group.'));
|
||||||
|
@ -118,12 +118,12 @@ class ApiGroupLeaveAction extends ApiAuthAction
|
||||||
$result = $member->delete();
|
$result = $member->delete();
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
common_log_db_error($member, 'INSERT', __FILE__);
|
common_log_db_error($member, 'DELETE', __FILE__);
|
||||||
$this->serverError(
|
$this->serverError(
|
||||||
sprintf(
|
sprintf(
|
||||||
_('Could not remove user %s to group %s.'),
|
_('Could not remove user %s from group %s.'),
|
||||||
$this->user->nickname,
|
$this->user->nickname,
|
||||||
$this->$group->nickname
|
$this->group->nickname
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -123,8 +123,8 @@ class LeavegroupAction extends Action
|
||||||
$result = $member->delete();
|
$result = $member->delete();
|
||||||
|
|
||||||
if (!$result) {
|
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'),
|
$this->serverError(sprintf(_('Could not remove user %s from group %s'),
|
||||||
$cur->nickname, $this->group->nickname));
|
$cur->nickname, $this->group->nickname));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,8 +208,15 @@ class TwitapisearchatomAction extends ApiAction
|
||||||
$this->showFeed();
|
$this->showFeed();
|
||||||
|
|
||||||
foreach ($notices as $n) {
|
foreach ($notices as $n) {
|
||||||
|
|
||||||
|
$profile = $n->getProfile();
|
||||||
|
|
||||||
|
// Don't show notices from deleted users
|
||||||
|
|
||||||
|
if (!empty($profile)) {
|
||||||
$this->showEntry($n);
|
$this->showEntry($n);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->endAtom();
|
$this->endAtom();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ class Avatar extends Memcached_DataObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Avatar', $kv);
|
return Memcached_DataObject::pkeyGet('Avatar', $kv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ class Config extends Memcached_DataObject
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Config', $kv);
|
return Memcached_DataObject::pkeyGet('Config', $kv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Fave extends Memcached_DataObject
|
||||||
return $fave;
|
return $fave;
|
||||||
}
|
}
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Fave', $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);
|
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 */
|
/* the code above is auto generated do not remove the tag below */
|
||||||
###END_AUTOCODE
|
###END_AUTOCODE
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Group_block', $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 */
|
/* the code above is auto generated do not remove the tag below */
|
||||||
###END_AUTOCODE
|
###END_AUTOCODE
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Group_inbox', $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 */
|
/* the code above is auto generated do not remove the tag below */
|
||||||
###END_AUTOCODE
|
###END_AUTOCODE
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Group_member', $kv);
|
return Memcached_DataObject::pkeyGet('Group_member', $kv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,17 +90,16 @@ class Memcached_DataObject extends DB_DataObject
|
||||||
unset($i);
|
unset($i);
|
||||||
}
|
}
|
||||||
$i = Memcached_DataObject::getcached($cls, $k, $v);
|
$i = Memcached_DataObject::getcached($cls, $k, $v);
|
||||||
if ($i !== false) { // false == cache miss
|
if ($i === false) { // false == cache miss
|
||||||
return $i;
|
|
||||||
} else {
|
|
||||||
$i = DB_DataObject::factory($cls);
|
$i = DB_DataObject::factory($cls);
|
||||||
if (empty($i)) {
|
if (empty($i)) {
|
||||||
return false;
|
$i = false;
|
||||||
|
return $i;
|
||||||
}
|
}
|
||||||
$result = $i->get($k, $v);
|
$result = $i->get($k, $v);
|
||||||
if ($result) {
|
if ($result) {
|
||||||
|
// Hit!
|
||||||
$i->encache();
|
$i->encache();
|
||||||
return $i;
|
|
||||||
} else {
|
} else {
|
||||||
// save the fact that no such row exists
|
// save the fact that no such row exists
|
||||||
$c = self::memcache();
|
$c = self::memcache();
|
||||||
|
@ -108,12 +107,16 @@ class Memcached_DataObject extends DB_DataObject
|
||||||
$ck = self::cachekey($cls, $k, $v);
|
$ck = self::cachekey($cls, $k, $v);
|
||||||
$c->set($ck, null);
|
$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);
|
$i = Memcached_DataObject::multicache($cls, $kv);
|
||||||
if ($i !== false) { // false == cache miss
|
if ($i !== false) { // false == cache miss
|
||||||
|
|
|
@ -101,7 +101,7 @@ class Notice_inbox extends Memcached_DataObject
|
||||||
return $ids;
|
return $ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Notice_inbox', $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);
|
return Memcached_DataObject::pkeyGet('Notice_tag', $kv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -504,6 +504,7 @@ class Profile extends Memcached_DataObject
|
||||||
'Reply',
|
'Reply',
|
||||||
'Group_member',
|
'Group_member',
|
||||||
);
|
);
|
||||||
|
Event::handle('ProfileDeleteRelated', array($this, &$related));
|
||||||
|
|
||||||
foreach ($related as $cls) {
|
foreach ($related as $cls) {
|
||||||
$inst = new $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 */
|
/* the code above is auto generated do not remove the tag below */
|
||||||
###END_AUTOCODE
|
###END_AUTOCODE
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Profile_role', $kv);
|
return Memcached_DataObject::pkeyGet('Profile_role', $kv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Queue_item extends Memcached_DataObject
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Queue_item', $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 */
|
/* the code above is auto generated do not remove the tag below */
|
||||||
###END_AUTOCODE
|
###END_AUTOCODE
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('Subscription', $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'
|
* sub <nickname> - same as 'follow'
|
||||||
* unsub <nickname> - same as 'leave'
|
* unsub <nickname> - same as 'leave'
|
||||||
* last <nickname> - same as 'get'
|
* last <nickname> - same as 'get'
|
||||||
* on <nickname> - not yet implemented.
|
* nudge <nickname> - remind a user to update.
|
||||||
* 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.
|
|
||||||
|
|
|
@ -742,42 +742,42 @@ class HelpCommand extends Command
|
||||||
function execute($channel)
|
function execute($channel)
|
||||||
{
|
{
|
||||||
$channel->output($this->user,
|
$channel->output($this->user,
|
||||||
_("Commands:\n".
|
_("Commands:")."\n".
|
||||||
"on - turn on notifications\n".
|
_("on - turn on notifications")."\n".
|
||||||
"off - turn off notifications\n".
|
_("off - turn off notifications")."\n".
|
||||||
"help - show this help\n".
|
_("help - show this help")."\n".
|
||||||
"follow <nickname> - subscribe to user\n".
|
_("follow <nickname> - subscribe to user")."\n".
|
||||||
"groups - lists the groups you have joined\n".
|
_("groups - lists the groups you have joined")."\n".
|
||||||
"subscriptions - list the people you follow\n".
|
_("subscriptions - list the people you follow")."\n".
|
||||||
"subscribers - list the people that follow you\n".
|
_("subscribers - list the people that follow you")."\n".
|
||||||
"leave <nickname> - unsubscribe from user\n".
|
_("leave <nickname> - unsubscribe from user")."\n".
|
||||||
"d <nickname> <text> - direct message to user\n".
|
_("d <nickname> <text> - direct message to user")."\n".
|
||||||
"get <nickname> - get last notice from user\n".
|
_("get <nickname> - get last notice from user")."\n".
|
||||||
"whois <nickname> - get profile info on user\n".
|
_("whois <nickname> - get profile info on user")."\n".
|
||||||
"fav <nickname> - add user's last notice as a 'fave'\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".
|
_("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 #<notice_id> - repeat a notice with a given id")."\n".
|
||||||
"repeat <nickname> - repeat the last notice from user\n".
|
_("repeat <nickname> - repeat the last notice from user")."\n".
|
||||||
"reply #<notice_id> - reply to notice with a given id\n".
|
_("reply #<notice_id> - reply to notice with a given id")."\n".
|
||||||
"reply <nickname> - reply to the last notice from user\n".
|
_("reply <nickname> - reply to the last notice from user")."\n".
|
||||||
"join <group> - join group\n".
|
_("join <group> - join group")."\n".
|
||||||
"login - Get a link to login to the web interface\n".
|
#_("login - Get a link to login to the web interface")."\n".
|
||||||
"drop <group> - leave group\n".
|
_("drop <group> - leave group")."\n".
|
||||||
"stats - get your stats\n".
|
_("stats - get your stats")."\n".
|
||||||
"stop - same as 'off'\n".
|
_("stop - same as 'off'")."\n".
|
||||||
"quit - same as 'off'\n".
|
_("quit - same as 'off'")."\n".
|
||||||
"sub <nickname> - same as 'follow'\n".
|
_("sub <nickname> - same as 'follow'")."\n".
|
||||||
"unsub <nickname> - same as 'leave'\n".
|
_("unsub <nickname> - same as 'leave'")."\n".
|
||||||
"last <nickname> - same as 'get'\n".
|
_("last <nickname> - same as 'get'")."\n".
|
||||||
"on <nickname> - not yet implemented.\n".
|
#_("on <nickname> - not yet implemented.")."\n".
|
||||||
"off <nickname> - not yet implemented.\n".
|
#_("off <nickname> - not yet implemented.")."\n".
|
||||||
"nudge <nickname> - remind a user to update.\n".
|
_("nudge <nickname> - remind a user to update.")."\n");
|
||||||
"invite <phone number> - not yet implemented.\n".
|
#_("invite <phone number> - not yet implemented.")."\n".
|
||||||
"track <word> - not yet implemented.\n".
|
#_("track <word> - not yet implemented.")."\n".
|
||||||
"untrack <word> - not yet implemented.\n".
|
#_("untrack <word> - not yet implemented.")."\n".
|
||||||
"track off - not yet implemented.\n".
|
#_("track off - not yet implemented.")."\n".
|
||||||
"untrack all - not yet implemented.\n".
|
#_("untrack all - not yet implemented.")."\n".
|
||||||
"tracks - not yet implemented.\n".
|
#_("tracks - not yet implemented.")."\n".
|
||||||
"tracking - 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))) {
|
if(Event::handle('StartScriptElement', array($this,&$src,&$type))) {
|
||||||
$url = parse_url($src);
|
$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;
|
$src = common_path($src) . '?version=' . STATUSNET_VERSION;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,9 +105,15 @@ class JSONSearchResultsList
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$profile = $this->notice->getProfile();
|
||||||
|
|
||||||
|
// Don't show notices from deleted users
|
||||||
|
|
||||||
|
if (!empty($profile)) {
|
||||||
$item = new ResultItem($this->notice);
|
$item = new ResultItem($this->notice);
|
||||||
array_push($this->results, $item);
|
array_push($this->results, $item);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$time_end = microtime(true);
|
$time_end = microtime(true);
|
||||||
$this->completed_in = $time_end - $time_start;
|
$this->completed_in = $time_end - $time_start;
|
||||||
|
|
|
@ -528,6 +528,10 @@ class Schema
|
||||||
$sql .= " auto_increment ";
|
$sql .= " auto_increment ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($cd->extra)) {
|
||||||
|
$sql .= "{$cd->extra} ";
|
||||||
|
}
|
||||||
|
|
||||||
return $sql;
|
return $sql;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,6 @@ class LdapAuthorizationPlugin extends AuthorizationPlugin
|
||||||
public $attributes = array();
|
public $attributes = array();
|
||||||
|
|
||||||
function onInitializePlugin(){
|
function onInitializePlugin(){
|
||||||
parent::onInitializePlugin();
|
|
||||||
if(!isset($this->host)){
|
if(!isset($this->host)){
|
||||||
throw new Exception("must specify a host");
|
throw new Exception("must specify a host");
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ class MinifyPlugin extends Plugin
|
||||||
|
|
||||||
function onStartScriptElement($action,&$src,&$type) {
|
function onStartScriptElement($action,&$src,&$type) {
|
||||||
$url = parse_url($src);
|
$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);
|
$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 */
|
/* the code above is auto generated do not remove the tag below */
|
||||||
###END_AUTOCODE
|
###END_AUTOCODE
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('User_openid_trustroot', $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)
|
function onEndRegistrationFormData($action)
|
||||||
{
|
{
|
||||||
$action->style('#recaptcha_area{float:left;}');
|
|
||||||
$action->elementStart('li');
|
$action->elementStart('li');
|
||||||
$action->raw('<label for="recaptcha_area">Captcha</label>');
|
$action->raw('<label for="recaptcha">Captcha</label>');
|
||||||
if($this->checkssl() === true) {
|
if($this->checkssl() === true) {
|
||||||
$action->raw(recaptcha_get_html($this->public_key), null, true);
|
$action->raw(recaptcha_get_html($this->public_key), null, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -102,20 +102,20 @@ class UserFlagPlugin extends Plugin
|
||||||
|
|
||||||
function onAutoload($cls)
|
function onAutoload($cls)
|
||||||
{
|
{
|
||||||
switch ($cls)
|
switch (strtolower($cls))
|
||||||
{
|
{
|
||||||
case 'FlagprofileAction':
|
case 'flagprofileaction':
|
||||||
case 'AdminprofileflagAction':
|
case 'adminprofileflagaction':
|
||||||
case 'ClearflagAction':
|
case 'clearflagaction':
|
||||||
include_once INSTALLDIR.'/plugins/UserFlag/' .
|
include_once INSTALLDIR.'/plugins/UserFlag/' .
|
||||||
strtolower(mb_substr($cls, 0, -6)) . '.php';
|
strtolower(mb_substr($cls, 0, -6)) . '.php';
|
||||||
return false;
|
return false;
|
||||||
case 'FlagProfileForm':
|
case 'flagprofileform':
|
||||||
case 'ClearFlagForm':
|
case 'clearflagform':
|
||||||
include_once INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php');
|
include_once INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php');
|
||||||
return false;
|
return false;
|
||||||
case 'User_flag_profile':
|
case 'user_flag_profile':
|
||||||
include_once INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php';
|
include_once INSTALLDIR.'/plugins/UserFlag/'.ucfirst(strtolower($cls)).'.php';
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
|
@ -258,4 +258,39 @@ class UserFlagPlugin extends Plugin
|
||||||
}
|
}
|
||||||
return true;
|
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
|
* @return User_flag_profile found object or null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function &pkeyGet($kv)
|
function pkeyGet($kv)
|
||||||
{
|
{
|
||||||
return Memcached_DataObject::pkeyGet('User_flag_profile', $kv);
|
return Memcached_DataObject::pkeyGet('User_flag_profile', $kv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,8 @@ function console_help()
|
||||||
if (CONSOLE_INTERACTIVE) {
|
if (CONSOLE_INTERACTIVE) {
|
||||||
print "StatusNet interactive PHP console... type ctrl+D or enter 'exit' to exit.\n";
|
print "StatusNet interactive PHP console... type ctrl+D or enter 'exit' to exit.\n";
|
||||||
$prompt = common_config('site', 'name') . '> ';
|
$prompt = common_config('site', 'name') . '> ';
|
||||||
|
} else {
|
||||||
|
$prompt = '';
|
||||||
}
|
}
|
||||||
while (!feof(STDIN)) {
|
while (!feof(STDIN)) {
|
||||||
$line = read_input_line($prompt);
|
$line = read_input_line($prompt);
|
||||||
|
|
|
@ -25,7 +25,7 @@ DIR=`php $SDIR/getpiddir.php`
|
||||||
|
|
||||||
for f in jabberhandler ombhandler publichandler smshandler pinghandler \
|
for f in jabberhandler ombhandler publichandler smshandler pinghandler \
|
||||||
xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \
|
xmppconfirmhandler xmppdaemon twitterhandler facebookhandler \
|
||||||
twitterstatusfetcher synctwitterfriends pluginhandler; do
|
twitterstatusfetcher synctwitterfriends pluginhandler rsscloudhandler; do
|
||||||
|
|
||||||
FILES="$DIR/$f.*.pid"
|
FILES="$DIR/$f.*.pid"
|
||||||
for ff in "$FILES" ; do
|
for ff in "$FILES" ; do
|
||||||
|
|
|
@ -242,6 +242,7 @@ margin-right:-47px;
|
||||||
|
|
||||||
#header {
|
#header {
|
||||||
width:100%;
|
width:100%;
|
||||||
|
height:10.5em;
|
||||||
position:relative;
|
position:relative;
|
||||||
float:left;
|
float:left;
|
||||||
padding-top:18px;
|
padding-top:18px;
|
||||||
|
@ -1000,7 +1001,7 @@ float:left;
|
||||||
font-size:0.95em;
|
font-size:0.95em;
|
||||||
margin-left:59px;
|
margin-left:59px;
|
||||||
min-width:60%;
|
min-width:60%;
|
||||||
max-width:66%;
|
max-width:62%;
|
||||||
}
|
}
|
||||||
#showstream .notice div.entry-content,
|
#showstream .notice div.entry-content,
|
||||||
#shownotice .notice div.entry-content {
|
#shownotice .notice div.entry-content {
|
||||||
|
@ -1517,12 +1518,13 @@ min-width:0;
|
||||||
#subscribers.user_in #content,
|
#subscribers.user_in #content,
|
||||||
#showgroup.user_in #content,
|
#showgroup.user_in #content,
|
||||||
#conversation.user_in #content,
|
#conversation.user_in #content,
|
||||||
#siteadminpanel #content,
|
#attachment.user_in #content,
|
||||||
#designadminpanel #content,
|
#siteadminpanel.user_in #content,
|
||||||
#useradminpanel #content,
|
#designadminpanel.user_in #content,
|
||||||
#pathsadminpanel #content,
|
#useradminpanel.user_in #content,
|
||||||
#adminprofileflag #content {
|
#pathsadminpanel.user_in #content,
|
||||||
padding-top:170px;
|
#adminprofileflag.user_in #content {
|
||||||
|
padding-top:12.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profilesettings #form_notice,
|
#profilesettings #form_notice,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user