diff options
Diffstat (limited to 'plugins')
23 files changed, 731 insertions, 186 deletions
diff --git a/plugins/Autocomplete/Autocomplete.js b/plugins/Autocomplete/Autocomplete.js index dfadea004..3eff685a8 100644 --- a/plugins/Autocomplete/Autocomplete.js +++ b/plugins/Autocomplete/Autocomplete.js @@ -1,38 +1,37 @@ $(document).ready(function(){ - $.getJSON($('address .url')[0].href+'/api/statuses/friends.json?user_id=' + current_user['id'] + '&lite=true&callback=?', - function(friends){ - $('#notice_data-text').autocomplete(friends, { + $('#notice_data-text').autocomplete($('address .url')[0].href+'/plugins/Autocomplete/autocomplete.json', { multiple: true, multipleSeparator: " ", minChars: 1, formatItem: function(row, i, max){ - return '@' + row.screen_name + ' (' + row.name + ')'; + row = eval("(" + row + ")"); + switch(row.type) + { + case 'user': + return row.nickname + ' (' + row.fullname + ')'; + case 'group': + return row.nickname + ' (' + row.fullname + ')'; + } }, formatMatch: function(row, i, max){ - return '@' + row.screen_name; + row = eval("(" + row + ")"); + switch(row.type) + { + case 'user': + return row.nickname; + case 'group': + return row.nickname; + } }, formatResult: function(row){ - return '@' + row.screen_name; + row = eval("(" + row + ")"); + switch(row.type) + { + case 'user': + return '@' + row.nickname; + case 'group': + return '!' + row.nickname; + } } }); - } - ); - $.getJSON($('address .url')[0].href+'/api/statusnet/groups/list.json?user_id=' + current_user['id'] + '&callback=?', - function(groups){ - $('#notice_data-text').autocomplete(groups, { - multiple: true, - multipleSeparator: " ", - minChars: 1, - formatItem: function(row, i, max){ - return '!' + row.nickname + ' (' + row.fullname + ')'; - }, - formatMatch: function(row, i, max){ - return '!' + row.nickname; - }, - formatResult: function(row){ - return '!' + row.nickname; - } - }); - } - ); }); diff --git a/plugins/Autocomplete/AutocompletePlugin.php b/plugins/Autocomplete/AutocompletePlugin.php index b75397270..baaec73c1 100644 --- a/plugins/Autocomplete/AutocompletePlugin.php +++ b/plugins/Autocomplete/AutocompletePlugin.php @@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +require_once(INSTALLDIR.'/plugins/Autocomplete/autocomplete.php'); + class AutocompletePlugin extends Plugin { function __construct() @@ -40,13 +42,6 @@ class AutocompletePlugin extends Plugin function onEndShowScripts($action){ if (common_logged_in()) { - $current_user = common_current_user(); - $js_string = <<<EOT -<script type="text/javascript"> -var current_user = { id: '$current_user->id' }; -</script> -EOT; - $action->raw($js_string); $action->script('plugins/Autocomplete/jquery-autocomplete/jquery.autocomplete.pack.js'); $action->script('plugins/Autocomplete/Autocomplete.js'); } @@ -59,5 +54,12 @@ EOT; } } + function onRouterInitialized($m) + { + if (common_logged_in()) { + $m->connect('plugins/Autocomplete/autocomplete.json', array('action'=>'autocomplete')); + } + } + } ?> diff --git a/plugins/Autocomplete/autocomplete.php b/plugins/Autocomplete/autocomplete.php new file mode 100644 index 000000000..aa57b3915 --- /dev/null +++ b/plugins/Autocomplete/autocomplete.php @@ -0,0 +1,136 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * List users for autocompletion + * + * 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 Craig Andrews <candrews@integralblue.com> + * @copyright 2008-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') && !defined('LACONICA')) { + exit(1); +} + +/** + * List users for autocompletion + * + * This is the form for adding a new g + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class AutocompleteAction extends Action +{ + private $result; + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ + function lastModified() + { + $max=0; + foreach($this->users as $user){ + $max = max($max,strtotime($user->modified),strtotime($user->profile->modified)); + } + foreach($this->groups as $group){ + $max = max($max,strtotime($group->modified)); + } + return $max; + } + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ + function etag() + { + return '"' . implode(':', array($this->arg('action'), + crc32($this->arg('q')), //the actual string can have funny characters in we don't want showing up in the etag + $this->arg('limit'), + $this->lastModified())) . '"'; + } + + function prepare($args) + { + parent::prepare($args); + $this->groups=array(); + $this->users=array(); + $q = $this->arg('q'); + $limit = $this->arg('limit'); + if($limit > 200) $limit=200; //prevent DOS attacks + if(substr($q,0,1)=='@'){ + //user search + $q=substr($q,1); + $user = new User(); + $user->limit($limit); + $user->whereAdd('nickname like \'' . trim($user->escape($q), '\'') . '%\''); + $user->find(); + while($user->fetch()) { + $profile = Profile::staticGet($user->id); + $user->profile=$profile; + $this->users[]=$user; + } + } + if(substr($q,0,1)=='!'){ + //group search + $q=substr($q,1); + $group = new User_group(); + $group->limit($limit); + $group->whereAdd('nickname like \'' . trim($group->escape($q), '\'') . '%\''); + $group->find(); + while($group->fetch()) { + $this->groups[]=$group; + } + } + return true; + } + + function handle($args) + { + parent::handle($args); + $results = array(); + foreach($this->users as $user){ + $results[]=array('nickname' => $user->nickname, 'fullname'=> $user->profile->fullname, 'type'=>'user'); + } + foreach($this->groups as $group){ + $results[]=array('nickname' => $group->nickname, 'fullname'=> $group->fullname, 'type'=>'group'); + } + foreach($results as $result) { + print json_encode($result) . "\n"; + } + } +} diff --git a/plugins/Autocomplete/readme.txt b/plugins/Autocomplete/readme.txt index 3272aa1ee..1db4c6565 100644 --- a/plugins/Autocomplete/readme.txt +++ b/plugins/Autocomplete/readme.txt @@ -1,5 +1,7 @@ Autocomplete allows users to autocomplete screen names in @ replies. When an "@" is typed into the notice text area, an autocomplete box is displayed populated with the user's friends' screen names. +Note: This plugin doesn't work if the site is in Private mode, i.e. when $config['site']['private'] is set to true. + Installation ============ Add "addPlugin('Autocomplete');" to the bottom of your config.php diff --git a/plugins/FBConnect/FBCLoginGroupNav.php b/plugins/FBConnect/FBCLoginGroupNav.php index 6e12c2040..81b2520a4 100644 --- a/plugins/FBConnect/FBCLoginGroupNav.php +++ b/plugins/FBConnect/FBCLoginGroupNav.php @@ -78,16 +78,20 @@ class FBCLoginGroupNav extends Widget // action => array('prompt', 'title') $menu = array(); - $menu['login'] = array(_('Login'), - _('Login with a username and password')); - - if (!(common_config('site','closed') || common_config('site','inviteonly'))) { - $menu['register'] = array(_('Register'), - _('Sign up for a new account')); + if (!common_config('site','openidonly')) { + $menu['login'] = array(_('Login'), + _('Login with a username and password')); + + if (!(common_config('site','closed') || common_config('site','inviteonly'))) { + $menu['register'] = array(_('Register'), + _('Sign up for a new account')); + } } - $menu['openidlogin'] = array(_('OpenID'), - _('Login or register with OpenID')); + if (common_config('openid', 'enabled')) { + $menu['openidlogin'] = array(_('OpenID'), + _('Login or register with OpenID')); + } $menu['FBConnectLogin'] = array(_('Facebook'), _('Login or register using Facebook')); diff --git a/plugins/FBConnect/FBCSettingsNav.php b/plugins/FBConnect/FBCSettingsNav.php index 29724d6bd..ed02371e2 100644 --- a/plugins/FBConnect/FBCSettingsNav.php +++ b/plugins/FBConnect/FBCSettingsNav.php @@ -77,32 +77,34 @@ class FBCSettingsNav extends Widget $this->action->elementStart('dd'); # action => array('prompt', 'title') - $menu = - array('imsettings' => - array(_('IM'), - _('Updates by instant messenger (IM)')), - 'smssettings' => - array(_('SMS'), - _('Updates by SMS')), - 'twittersettings' => - array(_('Twitter'), - _('Twitter integration options')), - 'FBConnectSettings' => - array(_('Facebook'), - _('Facebook Connect settings'))); + $menu = array(); + if (common_config('xmpp', 'enabled')) { + $menu['imsettings'] = + array(_('IM'), + _('Updates by instant messenger (IM)')); + } + if (common_config('sms', 'enabled')) { + $menu['smssettings'] = + array(_('SMS'), + _('Updates by SMS')); + } + if (common_config('twitter', 'enabled')) { + $menu['twittersettings'] = + array(_('Twitter'), + _('Twitter integration options')); + } + $menu['FBConnectSettings'] = + array(_('Facebook'), + _('Facebook Connect settings')); $action_name = $this->action->trimmed('action'); $this->action->elementStart('ul', array('class' => 'nav')); foreach ($menu as $menuaction => $menudesc) { - if ($menuaction == 'imsettings' && - !common_config('xmpp', 'enabled')) { - continue; - } $this->action->menuItem(common_local_url($menuaction), - $menudesc[0], - $menudesc[1], - $action_name === $menuaction); + $menudesc[0], + $menudesc[1], + $action_name === $menuaction); } $this->action->elementEnd('ul'); diff --git a/plugins/FBConnect/FBConnectPlugin.php b/plugins/FBConnect/FBConnectPlugin.php index 593b49b4e..ff74aade4 100644 --- a/plugins/FBConnect/FBConnectPlugin.php +++ b/plugins/FBConnect/FBConnectPlugin.php @@ -232,6 +232,14 @@ class FBConnectPlugin extends Plugin { $user = common_current_user(); + $connect = 'FBConnectSettings'; + if (common_config('xmpp', 'enabled')) { + $connect = 'imsettings'; + } else if (common_config('sms', 'enabled')) { + $connect = 'smssettings'; + } else if (common_config('twitter', 'enabled')) { + $connect = 'twittersettings'; + } if (!empty($user)) { @@ -266,13 +274,8 @@ class FBConnectPlugin extends Plugin _('Home'), _('Personal profile and friends timeline'), false, 'nav_home'); $action->menuItem(common_local_url('profilesettings'), _('Account'), _('Change your email, avatar, password, profile'), false, 'nav_account'); - if (common_config('xmpp', 'enabled')) { - $action->menuItem(common_local_url('imsettings'), - _('Connect'), _('Connect to IM, SMS, Twitter'), false, 'nav_connect'); - } else { - $action->menuItem(common_local_url('smssettings'), - _('Connect'), _('Connect to SMS, Twitter'), false, 'nav_connect'); - } + $action->menuItem(common_local_url($connect), + _('Connect'), _('Connect to services'), false, 'nav_connect'); if (common_config('invite', 'enabled')) { $action->menuItem(common_local_url('invite'), _('Invite'), @@ -300,18 +303,30 @@ class FBConnectPlugin extends Plugin } } else { - if (!common_config('site', 'closed')) { - $action->menuItem(common_local_url('register'), - _('Register'), _('Create an account'), false, 'nav_register'); + if (!common_config('site', 'openidonly')) { + if (!common_config('site', 'closed')) { + $action->menuItem(common_local_url('register'), + _('Register'), _('Create an account'), false, 'nav_register'); + } + $action->menuItem(common_local_url('login'), + _('Login'), _('Login to the site'), false, 'nav_login'); + } else { + $this->menuItem(common_local_url('openidlogin'), + _('OpenID'), _('Login with OpenID'), false, 'nav_openid'); } - $action->menuItem(common_local_url('login'), - _('Login'), _('Login to the site'), false, 'nav_login'); } $action->menuItem(common_local_url('doc', array('title' => 'help')), _('Help'), _('Help me!'), false, 'nav_help'); - $action->menuItem(common_local_url('peoplesearch'), - _('Search'), _('Search for people or text'), false, 'nav_search'); + if ($user || !common_config('site', 'private')) { + $action->menuItem(common_local_url('peoplesearch'), + _('Search'), _('Search for people or text'), false, 'nav_search'); + } + + // We are replacing the primary nav entirely; give other + // plugins a chance to handle it here. + + Event::handle('EndPrimaryNav', array($action)); return false; } diff --git a/plugins/InfiniteScroll/InfiniteScrollPlugin.php b/plugins/InfiniteScroll/InfiniteScrollPlugin.php index c955298cb..5928c007f 100644 --- a/plugins/InfiniteScroll/InfiniteScrollPlugin.php +++ b/plugins/InfiniteScroll/InfiniteScrollPlugin.php @@ -40,7 +40,7 @@ class InfiniteScrollPlugin extends Plugin function onEndShowScripts($action) { - $action->script('plugins/InfiniteScroll/jquery.infinitescroll.min.js'); + $action->script('plugins/InfiniteScroll/jquery.infinitescroll.js'); $action->script('plugins/InfiniteScroll/infinitescroll.js'); } } diff --git a/plugins/InfiniteScroll/infinitescroll.js b/plugins/InfiniteScroll/infinitescroll.js index 6513072d0..ae4d53d09 100644 --- a/plugins/InfiniteScroll/infinitescroll.js +++ b/plugins/InfiniteScroll/infinitescroll.js @@ -1,6 +1,7 @@ jQuery(document).ready(function($){ $('notices_primary').infinitescroll({ debug: true, + infiniteScroll : false, nextSelector : "li.nav_next a", loadingImg : $('address .url')[0].href+'plugins/InfiniteScroll/ajax-loader.gif', text : "<em>Loading the next set of posts...</em>", @@ -12,4 +13,3 @@ jQuery(document).ready(function($){ NoticeAttachments(); }); }); - diff --git a/plugins/InfiniteScroll/jquery.infinitescroll.js b/plugins/InfiniteScroll/jquery.infinitescroll.js index 670686b0e..ec31bb086 100644 --- a/plugins/InfiniteScroll/jquery.infinitescroll.js +++ b/plugins/InfiniteScroll/jquery.infinitescroll.js @@ -92,14 +92,14 @@ if (props.isDuringAjax || props.isInvalidPage || props.isDone) return; - if ( !isNearBottom(opts,props) ) return; + if ( opts.infiniteScroll && !isNearBottom(opts,props) ) return; // we dont want to fire the ajax multiple times props.isDuringAjax = true; // show the loading message and hide the previous/next links props.loadingMsg.appendTo( opts.contentSelector ).show(); - $( opts.navSelector ).hide(); + if(opts.infiniteScroll) $( opts.navSelector ).hide(); // increment the URL bit. e.g. /page/3/ props.currPage++; @@ -205,10 +205,19 @@ } }); - // bind scroll handler to element (if its a local scroll) or window - $(opts.localMode ? this : window) - .bind('scroll.infscr', function(){ infscrSetup(path,opts,props,callback); } ) - .trigger('scroll.infscr'); // trigger the event, in case it's a short page + if(opts.infiniteScroll){ + // bind scroll handler to element (if its a local scroll) or window + $(opts.localMode ? this : window) + .bind('scroll.infscr', function(){ infscrSetup(path,opts,props,callback); } ) + .trigger('scroll.infscr'); // trigger the event, in case it's a short page + }else{ + $(opts.nextSelector).click( + function(){ + infscrSetup(path,opts,props,callback); + return false; + } + ); + } return this; @@ -222,6 +231,7 @@ $.infinitescroll = { defaults : { debug : false, + infiniteScroll : true, preload : false, nextSelector : "div.navigation a:first", loadingImg : "http://www.infinite-scroll.com/loading.gif", diff --git a/plugins/Meteor/meteorupdater.js b/plugins/Meteor/meteorupdater.js index 2e688336f..9ce68775b 100644 --- a/plugins/Meteor/meteorupdater.js +++ b/plugins/Meteor/meteorupdater.js @@ -1,5 +1,6 @@ -// update the local timeline from a Meteor server -// +// Update the local timeline from a Meteor server +// XXX: If @a is subscribed to @b, @a should get @b's notices in @a's Personal timeline. +// Do Replies timeline. var MeteorUpdater = function() { diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php index 91bddf381..a933a1155 100644 --- a/plugins/OpenID/OpenIDPlugin.php +++ b/plugins/OpenID/OpenIDPlugin.php @@ -222,4 +222,19 @@ class OpenIDPlugin extends Plugin return true; } + + function onCheckSchema() { + $schema = Schema::get(); + $schema->ensureTable('user_openid', + array(new ColumnDef('canonical', 'varchar', + '255', false, 'PRI'), + new ColumnDef('display', 'varchar', + '255', false), + new ColumnDef('user_id', 'integer', + null, false, 'MUL'), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('modified', 'timestamp'))); + return true; + } } diff --git a/plugins/Orbited/OrbitedPlugin.php b/plugins/Orbited/OrbitedPlugin.php new file mode 100644 index 000000000..ba87b266a --- /dev/null +++ b/plugins/Orbited/OrbitedPlugin.php @@ -0,0 +1,154 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Plugin to do "real time" updates using Orbited + STOMP + * + * 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 Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/plugins/Realtime/RealtimePlugin.php'; + +/** + * Plugin to do realtime updates using Orbited + STOMP + * + * This plugin pushes data to a STOMP server which is then served to the + * browser by the Orbited server. + * + * @category Plugin + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class OrbitedPlugin extends RealtimePlugin +{ + public $webserver = null; + public $webport = null; + public $channelbase = null; + public $stompserver = null; + public $stompport = null; + public $username = null; + public $password = null; + public $webuser = null; + public $webpass = null; + + protected $con = null; + + function onStartShowHeadElements($action) + { + // See http://orbited.org/wiki/Deployment#Cross-SubdomainDeployment + $action->element('script', null, ' document.domain = document.domain; '); + } + + function _getScripts() + { + $scripts = parent::_getScripts(); + + $port = (is_null($this->webport)) ? 8000 : $this->webport; + + $server = (is_null($this->webserver)) ? common_config('site', 'server') : $this->webserver; + + $root = 'http://'.$server.(($port == 80) ? '':':'.$port); + + $scripts[] = $root.'/static/Orbited.js'; + $scripts[] = common_path('plugins/Orbited/orbitedextra.js'); + $scripts[] = $root.'/static/protocols/stomp/stomp.js'; + $scripts[] = common_path('plugins/Orbited/orbitedupdater.js'); + + return $scripts; + } + + function _updateInitialize($timeline, $user_id) + { + $script = parent::_updateInitialize($timeline, $user_id); + + $server = $this->_getStompServer(); + $port = $this->_getStompPort(); + + return $script." OrbitedUpdater.init(\"$server\", $port, ". + "\"{$timeline}\", \"{$this->webuser}\", \"{$this->webpass}\");"; + } + + function _connect() + { + require_once(INSTALLDIR.'/extlib/Stomp.php'); + + $url = $this->_getStompUrl(); + + $this->con = new Stomp($url); + + if ($this->con->connect($this->username, $this->password)) { + $this->log(LOG_INFO, "Connected."); + } else { + $this->log(LOG_ERR, 'Failed to connect to queue server'); + throw new ServerException('Failed to connect to queue server'); + } + } + + function _publish($channel, $message) + { + $result = $this->con->send($channel, + json_encode($message)); + + return $result; + // TODO: parse and deal with result + } + + function _disconnect() + { + $this->con->disconnect(); + } + + function _pathToChannel($path) + { + if (!empty($this->channelbase)) { + array_unshift($path, $this->channelbase); + } + return '/' . implode('/', $path); + } + + function _getStompServer() + { + return (!is_null($this->stompserver)) ? $this->stompserver : + (!is_null($this->webserver)) ? $this->webserver : + common_config('site', 'server'); + } + + function _getStompPort() + { + return (!is_null($this->stompport)) ? $this->stompport : 61613; + } + + function _getStompUrl() + { + $server = $this->_getStompServer(); + $port = $this->_getStompPort(); + return "tcp://$server:$port/"; + } +} diff --git a/plugins/Orbited/orbitedextra.js b/plugins/Orbited/orbitedextra.js new file mode 100644 index 000000000..47e5c0c80 --- /dev/null +++ b/plugins/Orbited/orbitedextra.js @@ -0,0 +1,2 @@ +TCPSocket = Orbited.TCPSocket; + diff --git a/plugins/Orbited/orbitedupdater.js b/plugins/Orbited/orbitedupdater.js new file mode 100644 index 000000000..8c5ab3b73 --- /dev/null +++ b/plugins/Orbited/orbitedupdater.js @@ -0,0 +1,24 @@ +// Update the local timeline from a Orbited server + +var OrbitedUpdater = function() +{ + return { + + init: function(server, port, timeline, username, password) + { + // set up stomp client. + stomp = new STOMPClient(); + + stomp.onmessageframe = function(frame) { + RealtimeUpdate.receive(JSON.parse(frame.body)); + }; + + stomp.onconnectedframe = function() { + stomp.subscribe(timeline); + } + + stomp.connect(server, port, username, password); + } + } +}(); + diff --git a/plugins/PiwikAnalyticsPlugin.php b/plugins/PiwikAnalyticsPlugin.php index 85a24c132..54faa0bdb 100644 --- a/plugins/PiwikAnalyticsPlugin.php +++ b/plugins/PiwikAnalyticsPlugin.php @@ -38,30 +38,24 @@ if (!defined('STATUSNET')) { * This plugin will spoot out the correct JavaScript spell to invoke * Piwik Analytics on a page. * - * To use this plugin please add the following three lines to your config.php + * To use this plugin add the following to your config.php * - * require_once('plugins/PiwikAnalyticsPlugin.php'); - * $pa = new PiwikAnalyticsPlugin("example.com/piwik/","id"); + * addPlugin('PiwikAnalytics', array('piwikroot' => 'example.com/piwik/', + * 'piwikId' => 'id')); * - * exchange example.com/piwik/ with the url to your piwik installation and - * make sure you don't forget the final / - * exchange id with the ID your statusnet installation has in your Piwik analytics + * Replace 'example.com/piwik/' with the URL to your Piwik installation and + * make sure you don't forget the final /. + * Replace 'id' with the ID your statusnet installation has in your Piwik + * analytics setup - for example '8'. * - * @category Plugin - * @package StatusNet - * @author Tobias Diekershoff <tobias.diekershoff@gmx.net> - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - * - * @see Event */ class PiwikAnalyticsPlugin extends Plugin { /** the base of your Piwik installation */ - var $piwikroot = null; + public $piwikroot = null; /** the Piwik Id of your statusnet installation */ - var $piwikId = null; + public $piwikId = null; /** * constructor @@ -73,7 +67,7 @@ class PiwikAnalyticsPlugin extends Plugin function __construct($root=null, $id=null) { $this->piwikroot = $root; - $this->piwikid = $id; + $this->piwikId = $id; parent::__construct(); } @@ -96,7 +90,7 @@ document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/ja </script> <script type="text/javascript"> try { - var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 4); + var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", {$this->piwikId}); piwikTracker.trackPageView(); piwikTracker.enableLinkTracking(); } catch( err ) {} @@ -108,4 +102,4 @@ ENDOFPIWIK; $action->raw($piwikCode); return true; } -}
\ No newline at end of file +} diff --git a/plugins/PubSubHubBub/PubSubHubBubPlugin.php b/plugins/PubSubHubBub/PubSubHubBubPlugin.php index 013a234d7..e1e82e352 100644 --- a/plugins/PubSubHubBub/PubSubHubBubPlugin.php +++ b/plugins/PubSubHubBub/PubSubHubBubPlugin.php @@ -31,7 +31,7 @@ if (!defined('STATUSNET')) { exit(1); } -define('DEFAULT_HUB','http://2pubsubhubbub.appspot.com'); +define('DEFAULT_HUB','http://pubsubhubbub.appspot.com'); require_once(INSTALLDIR.'/plugins/PubSubHubBub/publisher.php'); @@ -59,7 +59,7 @@ class PubSubHubBubPlugin extends Plugin $action->element('atom:link',array('rel'=>'hub','href'=>$this->hub),null); } - function onEndNoticeSave($notice){ + function onHandleQueuedNotice($notice){ $publisher = new Publisher($this->hub); $feeds = array(); diff --git a/plugins/Realtime/RealtimePlugin.php b/plugins/Realtime/RealtimePlugin.php index 82eca3d08..0c7c1240c 100644 --- a/plugins/Realtime/RealtimePlugin.php +++ b/plugins/Realtime/RealtimePlugin.php @@ -50,6 +50,11 @@ class RealtimePlugin extends Plugin protected $favorurl = null; protected $deleteurl = null; + /** + * When it's time to initialize the plugin, calculate and + * pass the URLs we need. + */ + function onInitializePlugin() { $this->replyurl = common_local_url('newnotice'); @@ -57,29 +62,26 @@ class RealtimePlugin extends Plugin // FIXME: need to find a better way to pass this pattern in $this->deleteurl = common_local_url('deletenotice', array('notice' => '0000000000')); + return true; } function onEndShowScripts($action) { - $path = null; + $timeline = $this->_getTimeline($action); - switch ($action->trimmed('action')) { - case 'public': - $path = array('public'); - break; - case 'tag': - $tag = $action->trimmed('tag'); - if (!empty($tag)) { - $path = array('tag', $tag); - } else { - return true; - } - break; - default: + // If there's not a timeline on this page, + // just return true + + if (empty($timeline)) { return true; } - $timeline = $this->_pathToChannel($path); + $base = $action->selfUrl(); + if (mb_strstr($base, '?')) { + $url = $base . '&realtime=1'; + } else { + $url = $base . '?realtime=1'; + } $scripts = $this->_getScripts(); @@ -95,10 +97,22 @@ class RealtimePlugin extends Plugin $user_id = 0; } + if ($action->boolean('realtime')) { + $realtimeUI = ' RealtimeUpdate.initPopupWindow();'; + } + else { + $iconurl = common_path('plugins/Realtime/icon_external.gif'); + $realtimeUI = ' RealtimeUpdate.addPopup("'.$url.'", "'.$timeline.'", "'. $iconurl .'");'; + } + $action->elementStart('script', array('type' => 'text/javascript')); - $action->raw("$(document).ready(function() { "); - $action->raw($this->_updateInitialize($timeline, $user_id)); - $action->raw(" });"); + + $script = ' $(document).ready(function() { '. + $realtimeUI. + $this->_updateInitialize($timeline, $user_id). + '}); '; + $action->raw($script); + $action->elementEnd('script'); return true; @@ -108,13 +122,23 @@ class RealtimePlugin extends Plugin { $paths = array(); - // XXX: Add other timelines; this is just for the public one + // Add to the author's timeline + + $user = User::staticGet('id', $notice->profile_id); + + if (!empty($user)) { + $paths[] = array('showstream', $user->nickname); + } + + // Add to the public timeline if ($notice->is_local || ($notice->is_local == 0 && !common_config('public', 'localonly'))) { $paths[] = array('public'); } + // Add to the tags timeline + $tags = $this->getNoticeTags($notice); if (!empty($tags)) { @@ -123,6 +147,46 @@ class RealtimePlugin extends Plugin } } + // Add to inbox timelines + // XXX: do a join + + $inbox = new Notice_inbox(); + $inbox->notice_id = $notice->id; + + if ($inbox->find()) { + while ($inbox->fetch()) { + $user = User::staticGet('id', $inbox->user_id); + $paths[] = array('all', $user->nickname); + } + } + + // Add to the replies timeline + + $reply = new Reply(); + $reply->notice_id = $notice->id; + + if ($reply->find()) { + while ($reply->fetch()) { + $user = User::staticGet('id', $reply->profile_id); + if (!empty($user)) { + $paths[] = array('replies', $user->nickname); + } + } + } + + // Add to the group timeline + // XXX: join + + $gi = new Group_inbox(); + $gi->notice_id = $notice->id; + + if ($gi->find()) { + while ($gi->fetch()) { + $ug = User_group::staticGet('id', $gi->group_id); + $paths[] = array('showgroup', $ug->nickname); + } + } + if (count($paths) > 0) { $json = $this->noticeAsJson($notice); @@ -140,18 +204,49 @@ class RealtimePlugin extends Plugin return true; } + function onStartShowBody($action) + { + $realtime = $action->boolean('realtime'); + if (!$realtime) { + return true; + } + + $action->elementStart('body', + (common_current_user()) ? array('id' => $action->trimmed('action'), + 'class' => 'user_in') + : array('id' => $action->trimmed('action'))); + + // XXX hack to deal with JS that tries to get the + // root url from page output + + $action->elementStart('address'); + $action->element('a', array('class' => 'url', + 'href' => common_local_url('public')), + ''); + $action->elementEnd('address'); + + if (common_logged_in()) { + $action->showNoticeForm(); + } + + $action->showContentBlock(); + $action->showScripts(); + $action->elementEnd('body'); + return false; // No default processing + } + function noticeAsJson($notice) { // FIXME: this code should be abstracted to a neutral third // party, like Notice::asJson(). I'm not sure of the ethics // of refactoring from within a plugin, so I'm just abusing - // the TwitterApiAction method. Don't do this unless you're me! + // the ApiAction method. Don't do this unless you're me! - require_once(INSTALLDIR.'/lib/twitterapi.php'); + require_once(INSTALLDIR.'/lib/api.php'); - $act = new TwitterApiAction('/dev/null'); + $act = new ApiAction('/dev/null'); - $arr = $act->twitter_status_array($notice, true); + $arr = $act->twitterStatusArray($notice, true); $arr['url'] = $notice->bestUrl(); $arr['html'] = htmlspecialchars($notice->rendered); $arr['source'] = htmlspecialchars($arr['source']); @@ -224,4 +319,41 @@ class RealtimePlugin extends Plugin { return ''; } + + function _getTimeline($action) + { + $path = null; + $timeline = null; + + $action_name = $action->trimmed('action'); + + switch ($action_name) { + case 'public': + $path = array('public'); + break; + case 'tag': + $tag = $action->trimmed('tag'); + if (!empty($tag)) { + $path = array('tag', $tag); + } + break; + case 'showstream': + case 'all': + case 'replies': + case 'showgroup': + $nickname = common_canonical_nickname($action->trimmed('nickname')); + if (!empty($nickname)) { + $path = array($action_name, $nickname); + } + break; + default: + break; + } + + if (!empty($path)) { + $timeline = $this->_pathToChannel($path); + } + + return $timeline; + } } diff --git a/plugins/Realtime/icon_external.gif b/plugins/Realtime/icon_external.gif Binary files differnew file mode 100644 index 000000000..c4118d53b --- /dev/null +++ b/plugins/Realtime/icon_external.gif diff --git a/plugins/Realtime/realtimeupdate.js b/plugins/Realtime/realtimeupdate.js index d55db5859..a75f17d8c 100644 --- a/plugins/Realtime/realtimeupdate.js +++ b/plugins/Realtime/realtimeupdate.js @@ -1,8 +1,8 @@ // add a notice encoded as JSON into the current timeline // +// TODO: i18n RealtimeUpdate = { - _userid: 0, _replyurl: '', _favorurl: '', @@ -10,27 +10,42 @@ RealtimeUpdate = { init: function(userid, replyurl, favorurl, deleteurl) { - RealtimeUpdate._userid = userid; - RealtimeUpdate._replyurl = replyurl; - RealtimeUpdate._favorurl = favorurl; - RealtimeUpdate._deleteurl = deleteurl; + RealtimeUpdate._userid = userid; + RealtimeUpdate._replyurl = replyurl; + RealtimeUpdate._favorurl = favorurl; + RealtimeUpdate._deleteurl = deleteurl; + + $(window).blur(function() { + $('#notices_primary .notice').css({ + 'border-top-color':$('#notices_primary .notice:last').css('border-top-color'), + 'border-top-style':'dotted' + }); + + $('#notices_primary .notice:first').css({ + 'border-top-color':'#AAAAAA', + 'border-top-style':'solid' + }); + + return false; + }); }, receive: function(data) { - id = data.id; - - // Don't add it if it already exists - - if ($("#notice-"+id).length > 0) { - return; - } - - var noticeItem = RealtimeUpdate.makeNoticeItem(data); - $("#notices_primary .notices").prepend(noticeItem, true); - $("#notices_primary .notice:first").css({display:"none"}); - $("#notices_primary .notice:first").fadeIn(1000); - NoticeReply(); + setTimeout(function() { + id = data.id; + + // Don't add it if it already exists + if ($("#notice-"+id).length > 0) { + return; + } + + var noticeItem = RealtimeUpdate.makeNoticeItem(data); + $("#notices_primary .notices").prepend(noticeItem); + $("#notices_primary .notice:first").css({display:"none"}); + $("#notices_primary .notice:first").fadeIn(1000); + NoticeReply(); + }, 500); }, makeNoticeItem: function(data) @@ -50,30 +65,19 @@ RealtimeUpdate = { "<p class=\"entry-content\">"+html+"</p>"+ "</div>"+ "<div class=\"entry-content\">"+ - "<dl class=\"timestamp\">"+ - "<dt>Published</dt>"+ - "<dd>"+ - "<a rel=\"bookmark\" href=\""+data['url']+"\" >"+ + "<a class=\"timestamp\" rel=\"bookmark\" href=\""+data['url']+"\" >"+ "<abbr class=\"published\" title=\""+data['created_at']+"\">a few seconds ago</abbr>"+ "</a> "+ - "</dd>"+ - "</dl>"+ - "<dl class=\"device\">"+ - "<dt>From</dt> "+ - "<dd>"+source+"</dd>"+ // may have a link, I think - "</dl>"; - + "<span class=\"source\">"+ + "from "+ + "<span class=\"device\">"+source+"</span>"+ // may have a link + "</span>"; if (data['in_reply_to_status_id']) { - ni = ni+" <dl class=\"response\">"+ - "<dt>To</dt>"+ - "<dd>"+ - "<a href=\""+data['in_reply_to_status_url']+"\" rel=\"in-reply-to\">in reply to</a>"+ - "</dd>"+ - "</dl>"; + ni = ni+" <a class=\"response\" href=\""+data['in_reply_to_status_url']+"\">in context</a>"; } ni = ni+"</div>"+ - "<div class=\"notice-options\">"; + "<div class=\"notice-options\">"; if (RealtimeUpdate._userid != 0) { var input = $("form#form_notice fieldset input#token"); @@ -95,12 +99,12 @@ RealtimeUpdate = { var ff; ff = "<form id=\"favor-"+id+"\" class=\"form_favor\" method=\"post\" action=\""+RealtimeUpdate._favorurl+"\">"+ - "<fieldset>"+ - "<legend>Favor this notice</legend>"+ // XXX: i18n + "<fieldset>"+ + "<legend>Favor this notice</legend>"+ "<input name=\"token-"+id+"\" type=\"hidden\" id=\"token-"+id+"\" value=\""+session_key+"\"/>"+ "<input name=\"notice\" type=\"hidden\" id=\"notice-n"+id+"\" value=\""+id+"\"/>"+ "<input type=\"submit\" id=\"favor-submit-"+id+"\" name=\"favor-submit-"+id+"\" class=\"submit\" value=\"Favor\" title=\"Favor this notice\"/>"+ - "</fieldset>"+ + "</fieldset>"+ "</form>"; return ff; }, @@ -108,28 +112,71 @@ RealtimeUpdate = { makeReplyLink: function(id, nickname) { var rl; - rl = "<dl class=\"notice_reply\">"+ - "<dt>Reply to this notice</dt>"+ - "<dd>"+ - "<a href=\""+RealtimeUpdate._replyurl+"?replyto="+nickname+"\" title=\"Reply to this notice\">Reply <span class=\"notice_id\">"+id+"</span>"+ - "</a>"+ - "</dd>"+ - "</dl>"; + rl = "<a class=\"notice_reply\" href=\""+RealtimeUpdate._replyurl+"?replyto="+nickname+"\" title=\"Reply to this notice\">Reply <span class=\"notice_id\">"+id+"</span></a>"; return rl; - }, + }, makeDeleteLink: function(id) { var dl, delurl; delurl = RealtimeUpdate._deleteurl.replace("0000000000", id); - dl = "<dl class=\"notice_delete\">"+ - "<dt>Delete this notice</dt>"+ - "<dd>"+ - "<a href=\""+delurl+"\" title=\"Delete this notice\">Delete</a>"+ - "</dd>"+ - "</dl>"; + dl = "<a class=\"notice_delete\" href=\""+delurl+"\" title=\"Delete this notice\">Delete</a>"; return dl; }, + + addPopup: function(url, timeline, iconurl) + { + $('#notices_primary').css({'position':'relative'}); + $('#notices_primary').prepend('<button id="realtime_timeline" title="Pop up in a window">Pop up</button>'); + + $('#realtime_timeline').css({ + 'margin':'0 0 11px 0', + 'background':'transparent url('+ iconurl + ') no-repeat 0% 30%', + 'padding':'0 0 0 20px', + 'display':'block', + 'position':'absolute', + 'top':'-20px', + 'right':'0', + 'border':'none', + 'cursor':'pointer', + 'color':$("a").css("color"), + 'font-weight':'bold', + 'font-size':'1em' + }); + + $('#realtime_timeline').click(function() { + window.open(url, + timeline, + 'toolbar=no,resizable=yes,scrollbars=yes,status=yes'); + + return false; + }); + }, + + initPopupWindow: function() + { + window.resizeTo(500, 550); + $('address').hide(); + $('#content').css({'width':'93.5%'}); + + $('#form_notice').css({ + 'margin':'18px 0 18px 1.795%', + 'width':'93%', + 'max-width':'451px' + }); + + $('#form_notice label[for=notice_data-text], h1').css({'display': 'none'}); + + $('.notices li:first-child').css({'border-top-color':'transparent'}); + + $('#form_notice label[for="notice_data-attach"], #form_notice #notice_data-attach').css({'top':'0'}); + + $('#form_notice #notice_data-attach').css({ + 'left':'auto', + 'right':'0' + }); + } } + diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index b49e2e119..afc3f55ba 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -23,7 +23,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1 -function update_twitter_user($twitter_id, $screen_name) +function updateTwitter_user($twitter_id, $screen_name) { $uri = 'http://twitter.com/' . $screen_name; $fuser = new Foreign_user(); @@ -115,7 +115,7 @@ function save_twitter_user($twitter_id, $screen_name) // Only update if Twitter screen name has changed if ($fuser->nickname != $screen_name) { - $result = update_twitter_user($twitter_id, $screen_name); + $result = updateTwitter_user($twitter_id, $screen_name); common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' . "$fuser->id to $screen_name, was $fuser->nickname"); diff --git a/plugins/TwitterBridge/twitterbasicauthclient.php b/plugins/TwitterBridge/twitterbasicauthclient.php index fd331fbdc..1040d72fb 100644 --- a/plugins/TwitterBridge/twitterbasicauthclient.php +++ b/plugins/TwitterBridge/twitterbasicauthclient.php @@ -36,8 +36,14 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * * @category Integration * @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 + * @author Adrian Lang <mail@adrianlang.de> + * @author Brenda Wallace <shiny@cpan.org> + * @author Craig Andrews <candrews@integralblue.com> + * @author Dan Moore <dan@moore.cx> + * @author Evan Prodromou <evan@status.net> + * @author mEDI <medi@milaro.net> + * @author Sarven Capadisli <csarven@status.net> + * @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/ * */ diff --git a/plugins/recaptcha/README b/plugins/recaptcha/README index ce23a2695..b996f96cc 100644 --- a/plugins/recaptcha/README +++ b/plugins/recaptcha/README @@ -6,7 +6,7 @@ Use: 1. Get an API key from http://recaptcha.net 2. In config.php add: -include_once('plugins/recaptcha.php'); +include_once('plugins/recaptcha/recaptcha.php'); $captcha = new recaptcha(publickey, privatekey, showErrors); Changelog |