Merge commit 'origin/testing' into 0.9.x

This commit is contained in:
Brion Vibber 2010-01-21 16:33:11 -08:00
commit c9c7bb3234
10 changed files with 284 additions and 88 deletions

35
README
View File

@ -2,8 +2,8 @@
README README
------ ------
StatusNet 0.9.0 ("Stand") Release Candidate 2 StatusNet 0.9.0 ("Stand") Beta 3
22 Dec 2009 20 Jan 2010
This is the README file for StatusNet (formerly Laconica), the Open This is the README file for StatusNet (formerly Laconica), the Open
Source microblogging platform. It includes installation instructions, Source microblogging platform. It includes installation instructions,
@ -167,6 +167,37 @@ Notable changes this version:
- Add support for "repeats" (similar to Twitter's "retweets"). - Add support for "repeats" (similar to Twitter's "retweets").
- Support for repeats in Twitter API. - Support for repeats in Twitter API.
- Better notification of direct messages. - Better notification of direct messages.
- New plugin to add "powered by StatusNet" to logo.
- Returnto works for private sites.
- Localisation updates, including new Persian translation.
- CAS authentication plugin
- Get rid of DB_DataObject native cache (big memory leaker)
- setconfig.php script to set configuration variables
- Blacklist plugin, to blacklist URLs and nicknames
- Users can set flag whether they want to share location
both in notice form (for one notice) and profile settings
(any notice)
- notice inboxes moved from normalized notice_inbox table to
denormalized inbox table
- Automatic compression of Memcache
- Memory caching pluginized
- Memcache, XCache, APC and Diskcache plugins
- A script to update user locations
- cache empty query results
- A sample plugin to show best plugin practices
- CacheLog plugin to debug cache accesses
- Require users to login to view attachments on private sites
- Plugin to use Mollom spam detection service
- Plugin for RSSCloud
- Add an array of default plugins
- A version action to give credit to contributors and plugin
developers
- Daemon to read IMAP mailbox instead of using a mailbox script
- Pass session information between SSL and non-SSL server
when SSL set to 'sometimes'
- Major refactoring of queue handlers to manage very
large hosting site (like status.net)
- SubscriptionThrottle plugin to prevent subscription spamming
Prerequisites Prerequisites
============= =============

View File

@ -113,4 +113,19 @@ class ApiStatusesRetweetsAction extends ApiAuthAction
break; break;
} }
} }
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
} }

View File

@ -69,58 +69,21 @@ class ApiTimelineRetweetedByMeAction extends ApiAuthAction
{ {
parent::prepare($args); parent::prepare($args);
$cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1); $this->serverError('Unimplemented', 503);
$page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt)); return false;
$since_id = $this->int('since_id');
$max_id = $this->int('max_id');
return true;
} }
/** /**
* Handle the request * Return true if read only.
* *
* show a timeline of the user's repeated notices * @param array $args other arguments
* *
* @param array $args $_REQUEST data (unused) * @return boolean is read only action?
*
* @return void
*/ */
function handle($args) function isReadOnly($args)
{ {
parent::handle($args); return true;
$offset = ($this->page-1) * $this->cnt;
$limit = $this->cnt;
$strm = $this->auth_user->repeatedByMe($offset, $limit, $this->since_id, $this->max_id);
switch ($this->format) {
case 'xml':
$this->showXmlTimeline($strm);
break;
case 'json':
$this->showJsonTimeline($strm);
break;
case 'atom':
$profile = $this->auth_user->getProfile();
$title = sprintf(_("Repeated by %s"), $this->auth_user->nickname);
$taguribase = common_config('integration', 'taguri');
$id = "tag:$taguribase:RepeatedByMe:" . $this->auth_user->id;
$link = common_local_url('showstream',
array('nickname' => $this->auth_user->nickname));
$this->showAtomTimeline($strm, $title, $id, $link);
break;
default:
$this->clientError(_('API method not found.'), $code = 404);
break;
}
} }
} }

View File

@ -122,4 +122,19 @@ class ApiTimelineRetweetedToMeAction extends ApiAuthAction
break; break;
} }
} }
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
} }

View File

@ -123,4 +123,19 @@ class ApiTimelineRetweetsOfMeAction extends ApiAuthAction
break; break;
} }
} }
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
} }

View File

@ -315,6 +315,39 @@ class Memcached_DataObject extends DB_DataObject
return new ArrayWrapper($cached); return new ArrayWrapper($cached);
} }
/**
* sends query to database - this is the private one that must work
* - internal functions use this rather than $this->query()
*
* Overridden to do logging.
*
* @param string $string
* @access private
* @return mixed none or PEAR_Error
*/
function _query($string)
{
$start = microtime(true);
$result = parent::_query($string);
$delta = microtime(true) - $start;
$limit = common_config('db', 'log_slow_queries');
if (($limit > 0 && $delta >= $limit) || common_config('db', 'log_queries')) {
$clean = $this->sanitizeQuery($string);
common_log(LOG_DEBUG, sprintf("DB query (%0.3fs): %s", $delta, $clean));
}
return $result;
}
// Sanitize a query for logging
// @fixme don't trim spaces in string literals
function sanitizeQuery($string)
{
$string = preg_replace('/\s+/', ' ', $string);
$string = trim($string);
return $string;
}
// We overload so that 'SET NAMES "utf8"' is called for // We overload so that 'SET NAMES "utf8"' is called for
// each connection // each connection

View File

@ -205,8 +205,10 @@ var SN = { // StatusNet
cookieValue = JSON.parse(cookieValue); cookieValue = JSON.parse(cookieValue);
NLat = $('#'+SN.C.S.NoticeLat).val(cookieValue.NLat).val(); NLat = $('#'+SN.C.S.NoticeLat).val(cookieValue.NLat).val();
NLon = $('#'+SN.C.S.NoticeLon).val(cookieValue.NLon).val(); NLon = $('#'+SN.C.S.NoticeLon).val(cookieValue.NLon).val();
NLNS = $('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS).val(); if ($('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS)) {
NLID = $('#'+SN.C.S.NoticeLocationId).val(cookieValue.NLID).val(); NLNS = $('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS).val();
NLID = $('#'+SN.C.S.NoticeLocationId).val(cookieValue.NLID).val();
}
} }
if (cookieValue == 'disabled') { if (cookieValue == 'disabled') {
NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', false).attr('checked'); NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', false).attr('checked');
@ -301,8 +303,10 @@ var SN = { // StatusNet
$('#'+SN.C.S.NoticeLat).val(NLat); $('#'+SN.C.S.NoticeLat).val(NLat);
$('#'+SN.C.S.NoticeLon).val(NLon); $('#'+SN.C.S.NoticeLon).val(NLon);
$('#'+SN.C.S.NoticeLocationNs).val(NLNS); if ($('#'+SN.C.S.NoticeLocationNs)) {
$('#'+SN.C.S.NoticeLocationId).val(NLID); $('#'+SN.C.S.NoticeLocationNs).val(NLNS);
$('#'+SN.C.S.NoticeLocationId).val(NLID);
}
$('#'+SN.C.S.NoticeDataGeo).attr('checked', NDG); $('#'+SN.C.S.NoticeDataGeo).attr('checked', NDG);
} }
}); });

View File

@ -67,7 +67,9 @@ $default =
'db_driver' => 'DB', # XXX: JanRain libs only work with DB 'db_driver' => 'DB', # XXX: JanRain libs only work with DB
'quote_identifiers' => false, 'quote_identifiers' => false,
'type' => 'mysql', 'type' => 'mysql',
'schemacheck' => 'runtime'), // 'runtime' or 'script' 'schemacheck' => 'runtime', // 'runtime' or 'script'
'log_queries' => false, // true to log all DB queries
'log_slow_queries' => 0), // if set, log queries taking over N seconds
'syslog' => 'syslog' =>
array('appname' => 'statusnet', # for syslog array('appname' => 'statusnet', # for syslog
'priority' => 'debug', # XXX: currently ignored 'priority' => 'debug', # XXX: currently ignored

View File

@ -31,66 +31,152 @@ if (!defined('STATUSNET')) {
exit(1); exit(1);
} }
define('DEFAULT_HUB','http://pubsubhubbub.appspot.com'); define('DEFAULT_HUB', 'http://pubsubhubbub.appspot.com');
require_once(INSTALLDIR.'/plugins/PubSubHubBub/publisher.php'); require_once INSTALLDIR.'/plugins/PubSubHubBub/publisher.php';
/**
* Plugin to provide publisher side of PubSubHubBub (PuSH)
* relationship.
*
* PuSH is a real-time or near-real-time protocol for Atom
* and RSS feeds. More information here:
*
* http://code.google.com/p/pubsubhubbub/
*
* To enable, add the following line to your config.php:
*
* addPlugin('PubSubHubBub');
*
* This will use the Google default hub. If you'd like to use
* another, try:
*
* addPlugin('PubSubHubBub',
* array('hub' => 'http://yourhub.example.net/'));
*
* @category Plugin
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @copyright 2009 Craig Andrews http://candrews.integralblue.com
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
* @link http://status.net/
*/
class PubSubHubBubPlugin extends Plugin class PubSubHubBubPlugin extends Plugin
{ {
private $hub; /**
* URL of the hub to advertise and publish to.
*/
public $hub = DEFAULT_HUB;
/**
* Default constructor.
*/
function __construct() function __construct()
{ {
parent::__construct(); parent::__construct();
} }
function onInitializePlugin(){ /**
$this->hub = common_config('PubSubHubBub', 'hub'); * Hooks the StartApiAtom event
if(empty($this->hub)){ *
$this->hub = DEFAULT_HUB; * Adds the necessary bits to advertise PubSubHubBub
} * for the Atom feed.
*
* @param Action $action The API action being shown.
*
* @return boolean hook value
*/
function onStartApiAtom($action)
{
$action->element('link', array('rel' => 'hub', 'href' => $this->hub), null);
return true;
} }
function onStartApiAtom($action){ /**
$action->element('link',array('rel'=>'hub','href'=>$this->hub),null); * Hooks the StartApiRss event
*
* Adds the necessary bits to advertise PubSubHubBub
* for the RSS 2.0 feeds.
*
* @param Action $action The API action being shown.
*
* @return boolean hook value
*/
function onStartApiRss($action)
{
$action->element('atom:link', array('rel' => 'hub',
'href' => $this->hub),
null);
return true;
} }
function onStartApiRss($action){ /**
$action->element('atom:link',array('rel'=>'hub','href'=>$this->hub),null); * Hook for a queued notice.
} *
* When a notice has been queued, will ping the
* PuSH hub for each Atom and RSS feed in which
* the notice appears.
*
* @param Notice $notice The notice that's been queued
*
* @return boolean hook value
*/
function onHandleQueuedNotice($notice){ function onHandleQueuedNotice($notice)
{
$publisher = new Publisher($this->hub); $publisher = new Publisher($this->hub);
$feeds = array(); $feeds = array();
//public timeline feeds //public timeline feeds
$feeds[]=common_local_url('ApiTimelinePublic',array('format' => 'rss')); $feeds[] = common_local_url('ApiTimelinePublic', array('format' => 'rss'));
$feeds[]=common_local_url('ApiTimelinePublic',array('format' => 'atom')); $feeds[] = common_local_url('ApiTimelinePublic', array('format' => 'atom'));
//author's own feeds //author's own feeds
$user = User::staticGet('id',$notice->profile_id); $user = User::staticGet('id', $notice->profile_id);
$feeds[]=common_local_url('ApiTimelineUser',array('id' => $user->nickname, 'format'=>'rss'));
$feeds[]=common_local_url('ApiTimelineUser',array('id' => $user->nickname, 'format'=>'atom')); $feeds[] = common_local_url('ApiTimelineUser',
array('id' => $user->nickname,
'format' => 'rss'));
$feeds[] = common_local_url('ApiTimelineUser',
array('id' => $user->nickname,
'format' => 'atom'));
//tag feeds //tag feeds
$tag = new Notice_tag(); $tag = new Notice_tag();
$tag->notice_id = $notice->id; $tag->notice_id = $notice->id;
if ($tag->find()) { if ($tag->find()) {
while ($tag->fetch()) { while ($tag->fetch()) {
$feeds[]=common_local_url('ApiTimelineTag',array('tag'=>$tag->tag, 'format'=>'rss')); $feeds[] = common_local_url('ApiTimelineTag',
$feeds[]=common_local_url('ApiTimelineTag',array('tag'=>$tag->tag, 'format'=>'atom')); array('tag' => $tag->tag,
'format' => 'rss'));
$feeds[] = common_local_url('ApiTimelineTag',
array('tag' => $tag->tag,
'format' => 'atom'));
} }
} }
//group feeds //group feeds
$group_inbox = new Group_inbox(); $group_inbox = new Group_inbox();
$group_inbox->notice_id = $notice->id; $group_inbox->notice_id = $notice->id;
if ($group_inbox->find()) { if ($group_inbox->find()) {
while ($group_inbox->fetch()) { while ($group_inbox->fetch()) {
$group = User_group::staticGet('id',$group_inbox->group_id); $group = User_group::staticGet('id', $group_inbox->group_id);
$feeds[]=common_local_url('ApiTimelineGroup',array('id' => $group->nickname,'format'=>'rss'));
$feeds[]=common_local_url('ApiTimelineGroup',array('id' => $group->nickname,'format'=>'atom')); $feeds[] = common_local_url('ApiTimelineGroup',
array('id' => $group->nickname,
'format' => 'rss'));
$feeds[] = common_local_url('ApiTimelineGroup',
array('id' => $group->nickname,
'format' => 'atom'));
} }
} }
@ -103,32 +189,63 @@ class PubSubHubBubPlugin extends Plugin
if (empty($user)) { if (empty($user)) {
continue; continue;
} }
$feeds[]=common_local_url('ApiTimelineUser',array('id' => $user->nickname, 'format'=>'rss')); $feeds[] = common_local_url('ApiTimelineFriends',
$feeds[]=common_local_url('ApiTimelineUser',array('id' => $user->nickname, 'format'=>'atom')); array('id' => $user->nickname,
'format' => 'rss'));
$feeds[] = common_local_url('ApiTimelineFriends',
array('id' => $user->nickname,
'format' => 'atom'));
} }
$replies = $notice->getReplies();
//feed of user replied to //feed of user replied to
if($notice->reply_to){ foreach ($replies as $recipient) {
$user = User::staticGet('id',$notice->reply_to); $user = User::staticGet('id', $recipient);
$feeds[]=common_local_url('ApiTimelineMentions',array('id' => $user->nickname,'format'=>'rss')); if (!empty($user)) {
$feeds[]=common_local_url('ApiTimelineMentions',array('id' => $user->nickname,'format'=>'atom')); $feeds[] = common_local_url('ApiTimelineMentions',
} array('id' => $user->nickname,
'format' => 'rss'));
foreach(array_unique($feeds) as $feed){ $feeds[] = common_local_url('ApiTimelineMentions',
if(! $publisher->publish_update($feed)){ array('id' => $user->nickname,
common_log_line(LOG_WARNING,$feed.' was not published to hub at '.$this->hub.':'.$publisher->last_response()); 'format' => 'atom'));
} }
} }
foreach (array_unique($feeds) as $feed) {
if (!$publisher->publish_update($feed)) {
common_log_line(LOG_WARNING,
$feed.' was not published to hub at '.
$this->hub.':'.$publisher->last_response());
}
}
return true;
} }
/**
* Provide version information
*
* Adds this plugin's version data to the global
* version array, for e.g. displaying on the version page.
*
* @param array &$versions array of array of versions
*
* @return boolean hook value
*/
function onPluginVersion(&$versions) function onPluginVersion(&$versions)
{ {
$versions[] = array('name' => 'PubSubHubBub', $versions[] = array('name' => 'PubSubHubBub',
'version' => STATUSNET_VERSION, 'version' => STATUSNET_VERSION,
'author' => 'Craig Andrews', 'author' => 'Craig Andrews',
'homepage' => 'http://status.net/wiki/Plugin:PubSubHubBub', 'homepage' =>
'http://status.net/wiki/Plugin:PubSubHubBub',
'rawdescription' => 'rawdescription' =>
_m('The PubSubHubBub plugin pushes RSS/Atom updates to a <a href="http://pubsubhubbub.googlecode.com/">PubSubHubBub</a> hub.')); _m('The PubSubHubBub plugin pushes RSS/Atom updates '.
'to a <a href = "'.
'http://pubsubhubbub.googlecode.com/'.
'">PubSubHubBub</a> hub.'));
return true; return true;
} }

View File

@ -148,6 +148,7 @@ class XMPPDaemon extends Daemon
function handle_message(&$pl) function handle_message(&$pl)
{ {
$this->log(LOG_DEBUG, "Received message: " . str_replace("\n", " ", var_export($pl, true)));
$from = jabber_normalize_jid($pl['from']); $from = jabber_normalize_jid($pl['from']);
if ($pl['type'] != 'chat') { if ($pl['type'] != 'chat') {