diff --git a/README b/README index 386b3264a2..6022887899 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ README ------ -StatusNet 0.9.0 ("Stand") Release Candidate 2 -22 Dec 2009 +StatusNet 0.9.0 ("Stand") Beta 3 +20 Jan 2010 This is the README file for StatusNet (formerly Laconica), the Open Source microblogging platform. It includes installation instructions, @@ -167,6 +167,37 @@ Notable changes this version: - Add support for "repeats" (similar to Twitter's "retweets"). - Support for repeats in Twitter API. - 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 ============= diff --git a/actions/apistatusesretweets.php b/actions/apistatusesretweets.php index f7a3dd60a0..a79d43168e 100644 --- a/actions/apistatusesretweets.php +++ b/actions/apistatusesretweets.php @@ -113,4 +113,19 @@ class ApiStatusesRetweetsAction extends ApiAuthAction break; } } + + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } } diff --git a/actions/apitimelineretweetedbyme.php b/actions/apitimelineretweetedbyme.php index 88652c3fdc..564e98619a 100644 --- a/actions/apitimelineretweetedbyme.php +++ b/actions/apitimelineretweetedbyme.php @@ -69,58 +69,21 @@ class ApiTimelineRetweetedByMeAction extends ApiAuthAction { 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)); - - $since_id = $this->int('since_id'); - - $max_id = $this->int('max_id'); - - return true; + return false; } /** - * 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 void + * @return boolean is read only action? */ - function handle($args) + function isReadOnly($args) { - parent::handle($args); - - $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; - } + return true; } } diff --git a/actions/apitimelineretweetedtome.php b/actions/apitimelineretweetedtome.php index 113ab96d2c..e47bc30b85 100644 --- a/actions/apitimelineretweetedtome.php +++ b/actions/apitimelineretweetedtome.php @@ -122,4 +122,19 @@ class ApiTimelineRetweetedToMeAction extends ApiAuthAction break; } } + + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } } diff --git a/actions/apitimelineretweetsofme.php b/actions/apitimelineretweetsofme.php index 6ca2c779cb..e4b09e9bda 100644 --- a/actions/apitimelineretweetsofme.php +++ b/actions/apitimelineretweetsofme.php @@ -123,4 +123,19 @@ class ApiTimelineRetweetsOfMeAction extends ApiAuthAction break; } } + + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + return true; + } } diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 4ecab9db62..6ddef48160 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -19,8 +19,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; - class Memcached_DataObject extends DB_DataObject { /** @@ -353,7 +351,7 @@ class Memcached_DataObject extends DB_DataObject unset($_DB_DATAOBJECT['CONNECTIONS'][$index]); } } - + $result = parent::_connect(); if ($result && !$exists) { diff --git a/plugins/PubSubHubBub/PubSubHubBubPlugin.php b/plugins/PubSubHubBub/PubSubHubBubPlugin.php index 367b354034..8286cd5489 100644 --- a/plugins/PubSubHubBub/PubSubHubBubPlugin.php +++ b/plugins/PubSubHubBub/PubSubHubBubPlugin.php @@ -31,66 +31,152 @@ if (!defined('STATUSNET')) { 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 + * @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 { - private $hub; + /** + * URL of the hub to advertise and publish to. + */ + + public $hub = DEFAULT_HUB; + + /** + * Default constructor. + */ function __construct() { parent::__construct(); } - function onInitializePlugin(){ - $this->hub = common_config('PubSubHubBub', 'hub'); - if(empty($this->hub)){ - $this->hub = DEFAULT_HUB; - } + /** + * Hooks the StartApiAtom event + * + * 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); $feeds = array(); //public timeline feeds - $feeds[]=common_local_url('ApiTimelinePublic',array('format' => 'rss')); - $feeds[]=common_local_url('ApiTimelinePublic',array('format' => 'atom')); + $feeds[] = common_local_url('ApiTimelinePublic', array('format' => 'rss')); + $feeds[] = common_local_url('ApiTimelinePublic', array('format' => 'atom')); //author's own feeds - $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')); + $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')); //tag feeds $tag = new Notice_tag(); + $tag->notice_id = $notice->id; if ($tag->find()) { while ($tag->fetch()) { - $feeds[]=common_local_url('ApiTimelineTag',array('tag'=>$tag->tag, 'format'=>'rss')); - $feeds[]=common_local_url('ApiTimelineTag',array('tag'=>$tag->tag, 'format'=>'atom')); + $feeds[] = common_local_url('ApiTimelineTag', + array('tag' => $tag->tag, + 'format' => 'rss')); + $feeds[] = common_local_url('ApiTimelineTag', + array('tag' => $tag->tag, + 'format' => 'atom')); } } //group feeds $group_inbox = new Group_inbox(); + $group_inbox->notice_id = $notice->id; if ($group_inbox->find()) { while ($group_inbox->fetch()) { - $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')); + $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')); } } @@ -103,32 +189,63 @@ class PubSubHubBubPlugin extends Plugin if (empty($user)) { continue; } - $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('ApiTimelineFriends', + 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 - if($notice->reply_to){ - $user = User::staticGet('id',$notice->reply_to); - $feeds[]=common_local_url('ApiTimelineMentions',array('id' => $user->nickname,'format'=>'rss')); - $feeds[]=common_local_url('ApiTimelineMentions',array('id' => $user->nickname,'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()); + foreach ($replies as $recipient) { + $user = User::staticGet('id', $recipient); + if (!empty($user)) { + $feeds[] = common_local_url('ApiTimelineMentions', + array('id' => $user->nickname, + 'format' => 'rss')); + $feeds[] = common_local_url('ApiTimelineMentions', + array('id' => $user->nickname, + '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) { $versions[] = array('name' => 'PubSubHubBub', 'version' => STATUSNET_VERSION, 'author' => 'Craig Andrews', - 'homepage' => 'http://status.net/wiki/Plugin:PubSubHubBub', + 'homepage' => + 'http://status.net/wiki/Plugin:PubSubHubBub', 'rawdescription' => - _m('The PubSubHubBub plugin pushes RSS/Atom updates to a PubSubHubBub hub.')); + _m('The PubSubHubBub plugin pushes RSS/Atom updates '. + 'to a PubSubHubBub hub.')); return true; } diff --git a/scripts/xmppdaemon.php b/scripts/xmppdaemon.php index cef9c4bd07..0c118c53d6 100755 --- a/scripts/xmppdaemon.php +++ b/scripts/xmppdaemon.php @@ -148,6 +148,7 @@ class XMPPDaemon extends Daemon function handle_message(&$pl) { + $this->log(LOG_DEBUG, "Received message: " . str_replace("\n", " ", var_export($pl, true))); $from = jabber_normalize_jid($pl['from']); if ($pl['type'] != 'chat') {