diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/api.php | 20 | ||||
-rw-r--r-- | lib/command.php | 124 | ||||
-rw-r--r-- | lib/commandinterpreter.php | 11 | ||||
-rw-r--r-- | lib/common.php | 10 | ||||
-rw-r--r-- | lib/connectsettingsaction.php | 49 | ||||
-rw-r--r-- | lib/curlclient.php | 2 | ||||
-rw-r--r-- | lib/default.php | 29 | ||||
-rw-r--r-- | lib/facebookaction.php | 672 | ||||
-rw-r--r-- | lib/facebookutil.php | 260 | ||||
-rw-r--r-- | lib/httpclient.php | 2 | ||||
-rw-r--r-- | lib/jabber.php | 6 | ||||
-rw-r--r-- | lib/language.php | 47 | ||||
-rw-r--r-- | lib/location.php | 188 | ||||
-rw-r--r-- | lib/mail.php | 72 | ||||
-rw-r--r-- | lib/mediafile.php | 284 | ||||
-rw-r--r-- | lib/messageform.php | 13 | ||||
-rw-r--r-- | lib/noticeform.php | 13 | ||||
-rw-r--r-- | lib/noticelist.php | 39 | ||||
-rw-r--r-- | lib/omb.php | 2 | ||||
-rw-r--r-- | lib/profilelist.php | 62 | ||||
-rw-r--r-- | lib/queuehandler.php | 77 | ||||
-rw-r--r-- | lib/router.php | 961 | ||||
-rw-r--r-- | lib/twitter.php | 311 | ||||
-rw-r--r-- | lib/twitterbasicauthclient.php | 242 | ||||
-rw-r--r-- | lib/twitteroauthclient.php | 229 | ||||
-rw-r--r-- | lib/unqueuemanager.php | 13 | ||||
-rw-r--r-- | lib/util.php | 30 | ||||
-rw-r--r-- | lib/xrdsoutputter.php | 96 |
28 files changed, 1479 insertions, 2385 deletions
diff --git a/lib/api.php b/lib/api.php index 7a63a4a78..9bd2083de 100644 --- a/lib/api.php +++ b/lib/api.php @@ -134,11 +134,19 @@ class ApiAction extends Action $twitter_user['protected'] = false; # not supported by StatusNet yet $twitter_user['followers_count'] = $profile->subscriberCount(); - // To be supported soon... - $twitter_user['profile_background_color'] = ''; - $twitter_user['profile_text_color'] = ''; - $twitter_user['profile_link_color'] = ''; - $twitter_user['profile_sidebar_fill_color'] = ''; + // Need to pull up the user for some of this + $user = $profile->getUser(); + $design = $user->getDesign(); + $defaultDesign = Design::siteDesign(); + if (!$design) $design = $defaultDesign; + $color = Design::toWebColor(empty($design->backgroundcolor) ? $defaultDesign->backgroundcolor : $design->backgroundcolor); + $twitter_user['profile_background_color'] = ($color == null) ? '' : '#'.$color->hexValue(); + $color = Design::toWebColor(empty($design->textcolor) ? $defaultDesign->textcolor : $design->textcolor); + $twitter_user['profile_text_color'] = ($color == null) ? '' : '#'.$color->hexValue(); + $color = Design::toWebColor(empty($design->linkcolor) ? $defaultDesign->linkcolor : $design->linkcolor); + $twitter_user['profile_link_color'] = ($color == null) ? '' : '#'.$color->hexValue(); + $color = Design::toWebColor(empty($design->sidebarcolor) ? $defaultDesign->sidebarcolor : $design->sidebarcolor); + $twitter_user['profile_sidebar_fill_color'] = ($color == null) ? '' : '#'.$color->hexValue(); $twitter_user['profile_sidebar_border_color'] = ''; $twitter_user['friends_count'] = $profile->subscriptionCount(); @@ -147,8 +155,6 @@ class ApiAction extends Action $twitter_user['favourites_count'] = $profile->faveCount(); // British spelling! - // Need to pull up the user for some of this - $user = User::staticGet($profile->id); $timezone = 'UTC'; diff --git a/lib/command.php b/lib/command.php index 11d40b8e1..9efa40696 100644 --- a/lib/command.php +++ b/lib/command.php @@ -73,7 +73,7 @@ class UntrackCommand extends UnimplementedCommand } } -class NudgeCommand extends UnimplementedCommand +class NudgeCommand extends Command { var $other = null; function __construct($user, $other) @@ -81,6 +81,26 @@ class NudgeCommand extends UnimplementedCommand parent::__construct($user); $this->other = $other; } + function execute($channel) + { + $recipient = User::staticGet('nickname', $this->other); + if(! $recipient){ + $channel->error($this->user, sprintf(_('Could not find a user with nickname %s'), + $this->other)); + }else{ + if ($recipient->id == $this->user->id) { + $channel->error($this->user, _('It does not make a lot of sense to nudge yourself!')); + }else{ + if ($recipient->email && $recipient->emailnotifynudge) { + mail_notify_nudge($this->user, $recipient); + } + // XXX: notify by IM + // XXX: notify by SMS + $channel->output($this->user, sprintf(_('Nudge sent to %s'), + $recipient->nickname)); + } + } + } } class InviteCommand extends UnimplementedCommand @@ -124,18 +144,30 @@ class FavCommand extends Command function execute($channel) { + if(substr($this->other,0,1)=='#'){ + //favoriting a specific notice_id - $recipient = - common_relative_profile($this->user, common_canonical_nickname($this->other)); + $notice = Notice::staticGet(substr($this->other,1)); + if (!$notice) { + $channel->error($this->user, _('Notice with that id does not exist')); + return; + } + $recipient = $notice->getProfile(); + }else{ + //favoriting a given user's last notice - if (!$recipient) { - $channel->error($this->user, _('No such user.')); - return; - } - $notice = $recipient->getCurrentNotice(); - if (!$notice) { - $channel->error($this->user, _('User has no last notice')); - return; + $recipient = + common_relative_profile($this->user, common_canonical_nickname($this->other)); + + if (!$recipient) { + $channel->error($this->user, _('No such user.')); + return; + } + $notice = $recipient->getCurrentNotice(); + if (!$notice) { + $channel->error($this->user, _('User has no last notice')); + return; + } } $fave = Fave::addNew($this->user, $notice); @@ -347,6 +379,71 @@ class MessageCommand extends Command } } +class ReplyCommand extends Command +{ + var $other = null; + var $text = null; + function __construct($user, $other, $text) + { + parent::__construct($user); + $this->other = $other; + $this->text = $text; + } + + function execute($channel) + { + if(substr($this->other,0,1)=='#'){ + //replying to a specific notice_id + + $notice = Notice::staticGet(substr($this->other,1)); + if (!$notice) { + $channel->error($this->user, _('Notice with that id does not exist')); + return; + } + $recipient = $notice->getProfile(); + }else{ + //replying to a given user's last notice + + $recipient = + common_relative_profile($this->user, common_canonical_nickname($this->other)); + + if (!$recipient) { + $channel->error($this->user, _('No such user.')); + return; + } + $notice = $recipient->getCurrentNotice(); + if (!$notice) { + $channel->error($this->user, _('User has no last notice')); + return; + } + } + + $len = mb_strlen($this->text); + + if ($len == 0) { + $channel->error($this->user, _('No content!')); + return; + } + + $this->text = common_shorten_links($this->text); + + if (Notice::contentTooLong($this->text)) { + $channel->error($this->user, sprintf(_('Notice too long - maximum is %d characters, you sent %d'), + Notice::maxContent(), mb_strlen($this->text))); + return; + } + + $notice = Notice::saveNew($this->user->id, $this->text, $channel->source(), 1, + $notice->id); + if ($notice) { + $channel->output($this->user, sprintf(_('Reply to %s sent'), $recipient->nickname)); + } else { + $channel->error($this->user, _('Error saving notice.')); + } + common_broadcast_notice($notice); + } +} + class GetCommand extends Command { @@ -497,6 +594,9 @@ class HelpCommand extends Command "get <nickname> - get last notice from user\n". "whois <nickname> - get profile info on user\n". "fav <nickname> - add user's last notice as a 'fave'\n". + "fav #<notice_id> - add notice with the given id as a 'fave'\n". + "reply #<notice_id> - reply to notice with a given id\n". + "reply <nickname> - reply to the last notice from user\n". "join <group> - join group\n". "drop <group> - leave group\n". "stats - get your stats\n". @@ -507,7 +607,7 @@ class HelpCommand extends Command "last <nickname> - same as 'get'\n". "on <nickname> - not yet implemented.\n". "off <nickname> - not yet implemented.\n". - "nudge <nickname> - not yet implemented.\n". + "nudge <nickname> - remind a user to update.\n". "invite <phone number> - not yet implemented.\n". "track <word> - not yet implemented.\n". "untrack <word> - not yet implemented.\n". diff --git a/lib/commandinterpreter.php b/lib/commandinterpreter.php index 60fc4c3c4..b921a17cc 100644 --- a/lib/commandinterpreter.php +++ b/lib/commandinterpreter.php @@ -134,6 +134,17 @@ class CommandInterpreter } else { return new MessageCommand($user, $other, $extra); } + case 'r': + case 'reply': + if (!$arg) { + return null; + } + list($other, $extra) = $this->split_arg($arg); + if (!$extra) { + return null; + } else { + return new ReplyCommand($user, $other, $extra); + } case 'whois': if (!$arg) { return null; diff --git a/lib/common.php b/lib/common.php index ce33c871b..2c2f6869e 100644 --- a/lib/common.php +++ b/lib/common.php @@ -185,7 +185,14 @@ function _have_config() } // XXX: Throw a conniption if database not installed - +// XXX: Find a way to use htmlwriter for this instead of handcoded markup +if (!_have_config()) { + echo '<p>'. _('No configuation file found. ') .'</p>'; + echo '<p>'. _('I looked for configuration files in the following places: ') .'<br/> '. implode($_config_files, '<br/>'); + echo '<p>'. _('You may wish to run the installer to fix this.') .'</p>'; + echo '<a href="install.php">'. _('Go to the installer.') .'</a>'; + exit; +} // Fixup for statusnet.ini $_db_name = substr($config['db']['database'], strrpos($config['db']['database'], '/') + 1); @@ -223,7 +230,6 @@ require_once INSTALLDIR.'/lib/theme.php'; require_once INSTALLDIR.'/lib/mail.php'; require_once INSTALLDIR.'/lib/subs.php'; require_once INSTALLDIR.'/lib/Shorturl_api.php'; -require_once INSTALLDIR.'/lib/twitter.php'; require_once INSTALLDIR.'/lib/clientexception.php'; require_once INSTALLDIR.'/lib/serverexception.php'; diff --git a/lib/connectsettingsaction.php b/lib/connectsettingsaction.php index 2095a7ceb..e5fb8727b 100644 --- a/lib/connectsettingsaction.php +++ b/lib/connectsettingsaction.php @@ -98,34 +98,37 @@ class ConnectSettingsNav extends Widget function show() { - # action => array('prompt', 'title') - $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')); - } - $action_name = $this->action->trimmed('action'); $this->action->elementStart('ul', array('class' => 'nav')); - foreach ($menu as $menuaction => $menudesc) { - $this->action->menuItem(common_local_url($menuaction), - $menudesc[0], - $menudesc[1], - $action_name === $menuaction); + if (Event::handle('StartConnectSettingsNav', array(&$this->action))) { + + # action => array('prompt', 'title') + $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')); + } + + foreach ($menu as $menuaction => $menudesc) { + $this->action->menuItem(common_local_url($menuaction), + $menudesc[0], + $menudesc[1], + $action_name === $menuaction); + } + + Event::handle('EndConnectSettingsNav', array(&$this->action)); } $this->action->elementEnd('ul'); } + } + + diff --git a/lib/curlclient.php b/lib/curlclient.php index 36fc7d157..c307c2984 100644 --- a/lib/curlclient.php +++ b/lib/curlclient.php @@ -1,4 +1,4 @@ -n<?php +<?php /** * StatusNet, the distributed open-source microblogging tool * diff --git a/lib/default.php b/lib/default.php index 9f3d4b1f9..7ec8558b0 100644 --- a/lib/default.php +++ b/lib/default.php @@ -84,7 +84,8 @@ $default = 'image' => 'http://i.creativecommons.org/l/by/3.0/80x15.png'), 'mail' => array('backend' => 'mail', - 'params' => null), + 'params' => null, + 'domain_check' => true), 'nickname' => array('blacklist' => array(), 'featured' => array()), @@ -140,21 +141,21 @@ $default = array('enabled' => true), 'sms' => array('enabled' => true), - 'twitterbridge' => + 'twitterimport' => array('enabled' => false), 'integration' => array('source' => 'StatusNet', # source attribute for Twitter 'taguri' => $_server.',2009'), # base for tag URIs - 'twitter' => - array('enabled' => true, - 'consumer_key' => null, - 'consumer_secret' => null), + 'twitter' => + array('enabled' => true, + 'consumer_key' => null, + 'consumer_secret' => null), 'memcached' => array('enabled' => false, 'server' => 'localhost', 'base' => null, 'port' => 11211), - 'ping' => + 'ping' => array('notify' => array()), 'inboxes' => array('enabled' => true), # ignored after 0.9.x @@ -200,12 +201,12 @@ $default = 'video/mp4', 'video/quicktime', 'video/mpeg'), - 'file_quota' => 5000000, - 'user_quota' => 50000000, - 'monthly_quota' => 15000000, - 'uploads' => true, - 'filecommand' => '/usr/bin/file', - ), + 'file_quota' => 5000000, + 'user_quota' => 50000000, + 'monthly_quota' => 15000000, + 'uploads' => true, + 'filecommand' => '/usr/bin/file', + ), 'group' => array('maxaliases' => 3, 'desclimit' => null), @@ -229,4 +230,6 @@ $default = array('contentlimit' => null), 'http' => array('client' => 'curl'), // XXX: should this be the default? + 'location' => + array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth ); diff --git a/lib/facebookaction.php b/lib/facebookaction.php deleted file mode 100644 index 3f3a8d3b0..000000000 --- a/lib/facebookaction.php +++ /dev/null @@ -1,672 +0,0 @@ -<?php -/** - * StatusNet, the distributed open-source microblogging tool - * - * Low-level generator for HTML - * - * 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 Faceboook - * @package StatusNet - * @author Zach Copley <zach@status.net> - * @copyright 2008 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); -} - -require_once INSTALLDIR.'/lib/facebookutil.php'; -require_once INSTALLDIR.'/lib/noticeform.php'; - -class FacebookAction extends Action -{ - - var $facebook = null; - var $fbuid = null; - var $flink = null; - var $action = null; - var $app_uri = null; - var $app_name = null; - - /** - * Constructor - * - * Just wraps the HTMLOutputter constructor. - * - * @param string $output URI to output to, default = stdout - * @param boolean $indent Whether to indent output, default true - * - * @see XMLOutputter::__construct - * @see HTMLOutputter::__construct - */ - function __construct($output='php://output', $indent=true, $facebook=null, $flink=null) - { - parent::__construct($output, $indent); - - $this->facebook = $facebook; - $this->flink = $flink; - - if ($this->flink) { - $this->fbuid = $flink->foreign_id; - $this->user = $flink->getUser(); - } - - $this->args = array(); - } - - function prepare($argarray) - { - parent::prepare($argarray); - - $this->facebook = getFacebook(); - $this->fbuid = $this->facebook->require_login(); - - $this->action = $this->trimmed('action'); - - $app_props = $this->facebook->api_client->Admin_getAppProperties( - array('canvas_name', 'application_name')); - - $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name']; - $this->app_name = $app_props['application_name']; - - $this->flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE); - - return true; - - } - - function showStylesheets() - { - $this->cssLink('css/display.css', 'base'); - $this->cssLink('css/display.css',null,'screen, projection, tv'); - $this->cssLink('css/facebookapp.css', 'base'); - } - - function showScripts() - { - $this->script('js/facebookapp.js'); - } - - /** - * Start an Facebook ready HTML document - * - * For Facebook we don't want to actually output any headers, - * DTD info, etc. Just Stylesheet and JavaScript links. - * - * If $type isn't specified, will attempt to do content negotiation. - * - * @param string $type MIME type to use; default is to do negotation. - * - * @return void - */ - - function startHTML($type=null) - { - $this->showStylesheets(); - $this->showScripts(); - - $this->elementStart('div', array('class' => 'facebook-page')); - } - - /** - * Ends a Facebook ready HTML document - * - * @return void - */ - function endHTML() - { - $this->elementEnd('div'); - $this->endXML(); - } - - /** - * Show notice form. - * - * MAY overload if no notice form needed... or direct message box???? - * - * @return nothing - */ - function showNoticeForm() - { - // don't do it for most of the Facebook pages - } - - function showBody() - { - $this->elementStart('div', array('id' => 'wrap')); - $this->showHeader(); - $this->showCore(); - $this->showFooter(); - $this->elementEnd('div'); - } - - function showAside() - { - } - - function showHead($error, $success) - { - - if ($error) { - $this->element("h1", null, $error); - } - - if ($success) { - $this->element("h1", null, $success); - } - - $this->elementStart('fb:if-section-not-added', array('section' => 'profile')); - $this->elementStart('span', array('id' => 'add_to_profile')); - $this->element('fb:add-section-button', array('section' => 'profile')); - $this->elementEnd('span'); - $this->elementEnd('fb:if-section-not-added'); - - } - - // Make this into a widget later - function showLocalNav() - { - $this->elementStart('ul', array('class' => 'nav')); - - $this->elementStart('li', array('class' => - ($this->action == 'facebookhome') ? 'current' : 'facebook_home')); - $this->element('a', - array('href' => 'index.php', 'title' => _('Home')), _('Home')); - $this->elementEnd('li'); - - if (common_config('invite', 'enabled')) { - $this->elementStart('li', - array('class' => - ($this->action == 'facebookinvite') ? 'current' : 'facebook_invite')); - $this->element('a', - array('href' => 'invite.php', 'title' => _('Invite')), _('Invite')); - $this->elementEnd('li'); - } - - $this->elementStart('li', - array('class' => - ($this->action == 'facebooksettings') ? 'current' : 'facebook_settings')); - $this->element('a', - array('href' => 'settings.php', - 'title' => _('Settings')), _('Settings')); - $this->elementEnd('li'); - - $this->elementEnd('ul'); - } - - /** - * Show header of the page. - * - * Calls template methods - * - * @return nothing - */ - function showHeader() - { - $this->elementStart('div', array('id' => 'header')); - $this->showLogo(); - $this->showNoticeForm(); - $this->elementEnd('div'); - } - - /** - * Show page, a template method. - * - * @return nothing - */ - function showPage($error = null, $success = null) - { - $this->startHTML(); - $this->showHead($error, $success); - $this->showBody(); - $this->endHTML(); - } - - function showInstructions() - { - - $this->elementStart('div', array('class' => 'facebook_guide')); - - $this->elementStart('dl', array('class' => 'system_notice')); - $this->element('dt', null, 'Page Notice'); - - $loginmsg_part1 = _('To use the %s Facebook Application you need to login ' . - 'with your username and password. Don\'t have a username yet? '); - $loginmsg_part2 = _(' a new account.'); - - $this->elementStart('dd'); - $this->elementStart('p'); - $this->text(sprintf($loginmsg_part1, common_config('site', 'name'))); - $this->element('a', - array('href' => common_local_url('register')), _('Register')); - $this->text($loginmsg_part2); - $this->elementEnd('p'); - $this->elementEnd('dd'); - - $this->elementEnd('dl'); - $this->elementEnd('div'); - } - - function showLoginForm($msg = null) - { - - $this->elementStart('div', array('id' => 'content')); - $this->element('h1', null, _('Login')); - - if ($msg) { - $this->element('fb:error', array('message' => $msg)); - } - - $this->showInstructions(); - - $this->elementStart('div', array('id' => 'content_inner')); - - $this->elementStart('form', array('method' => 'post', - 'class' => 'form_settings', - 'id' => 'login', - 'action' => 'index.php')); - - $this->elementStart('fieldset'); - - $this->elementStart('ul', array('class' => 'form_datas')); - $this->elementStart('li'); - $this->input('nickname', _('Nickname')); - $this->elementEnd('li'); - $this->elementStart('li'); - $this->password('password', _('Password')); - $this->elementEnd('li'); - $this->elementEnd('ul'); - - $this->submit('submit', _('Login')); - $this->elementEnd('fieldset'); - $this->elementEnd('form'); - - $this->elementStart('p'); - $this->element('a', array('href' => common_local_url('recoverpassword')), - _('Lost or forgotten password?')); - $this->elementEnd('p'); - - $this->elementEnd('div'); - $this->elementEnd('div'); - - } - - function updateProfileBox($notice) - { - - // Need to include inline CSS for styling the Profile box - - $app_props = $this->facebook->api_client->Admin_getAppProperties(array('icon_url')); - $icon_url = $app_props['icon_url']; - - $style = '<style> - .entry-title *, - .entry-content * { - font-size:14px; - font-family:"Lucida Sans Unicode", "Lucida Grande", sans-serif; - } - .entry-title a, - .entry-content a { - color:#002E6E; - } - - .entry-title .vcard .photo { - float:left; - display:inline; - margin-right:11px; - margin-bottom:11px - } - .entry-title { - margin-bottom:11px; - } - .entry-title p.entry-content { - display:inline; - margin-left:5px; - } - - div.entry-content { - clear:both; - } - div.entry-content dl, - div.entry-content dt, - div.entry-content dd { - display:inline; - text-transform:lowercase; - } - - div.entry-content dd, - div.entry-content .device dt { - margin-left:0; - margin-right:5px; - } - div.entry-content dl.timestamp dt, - div.entry-content dl.response dt { - display:none; - } - div.entry-content dd a { - display:inline-block; - } - - #facebook_statusnet_app { - text-indent:-9999px; - height:16px; - width:16px; - display:block; - background:url('.$icon_url.') no-repeat 0 0; - float:right; - } - </style>'; - - $this->xw->openMemory(); - - $item = new FacebookProfileBoxNotice($notice, $this); - $item->show(); - - $fbml = "<fb:wide>$style " . $this->xw->outputMemory(false) . "</fb:wide>"; - $fbml .= "<fb:narrow>$style " . $this->xw->outputMemory(false) . "</fb:narrow>"; - - $fbml_main = "<fb:narrow>$style " . $this->xw->outputMemory(false) . "</fb:narrow>"; - - $this->facebook->api_client->profile_setFBML(null, $this->fbuid, $fbml, null, null, $fbml_main); - - $this->xw->openURI('php://output'); - } - - /** - * Generate pagination links - * - * @param boolean $have_before is there something before? - * @param boolean $have_after is there something after? - * @param integer $page current page - * @param string $action current action - * @param array $args rest of query arguments - * - * @return nothing - */ - function pagination($have_before, $have_after, $page, $action, $args=null) - { - // Does a little before-after block for next/prev page - if ($have_before || $have_after) { - $this->elementStart('div', array('class' => 'pagination')); - $this->elementStart('dl', null); - $this->element('dt', null, _('Pagination')); - $this->elementStart('dd', null); - $this->elementStart('ul', array('class' => 'nav')); - } - if ($have_before) { - $pargs = array('page' => $page-1); - $newargs = $args ? array_merge($args, $pargs) : $pargs; - $this->elementStart('li', array('class' => 'nav_prev')); - $this->element('a', array('href' => "$this->app_uri/$action?page=$newargs[page]", 'rel' => 'prev'), - _('After')); - $this->elementEnd('li'); - } - if ($have_after) { - $pargs = array('page' => $page+1); - $newargs = $args ? array_merge($args, $pargs) : $pargs; - $this->elementStart('li', array('class' => 'nav_next')); - $this->element('a', array('href' => "$this->app_uri/$action?page=$newargs[page]", 'rel' => 'next'), - _('Before')); - $this->elementEnd('li'); - } - if ($have_before || $have_after) { - $this->elementEnd('ul'); - $this->elementEnd('dd'); - $this->elementEnd('dl'); - $this->elementEnd('div'); - } - } - - function saveNewNotice() - { - - $user = $this->flink->getUser(); - - $content = $this->trimmed('status_textarea'); - - if (!$content) { - $this->showPage(_('No notice content!')); - return; - } else { - $content_shortened = common_shorten_links($content); - - if (Notice::contentTooLong($content_shortened)) { - $this->showPage(sprintf(_('That\'s too long. Max notice size is %d chars.'), - Notice::maxContent())); - return; - } - } - - $inter = new CommandInterpreter(); - - $cmd = $inter->handle_command($user, $content_shortened); - - if ($cmd) { - - // XXX fix this - - $cmd->execute(new WebChannel()); - return; - } - - $replyto = $this->trimmed('inreplyto'); - - try { - $notice = Notice::saveNew($user->id, $content, - 'web', 1, ($replyto == 'false') ? null : $replyto); - } catch (Exception $e) { - $this->showPage($e->getMessage()); - return; - } - - common_broadcast_notice($notice); - - // Also update the user's Facebook status - facebookBroadcastNotice($notice); - - } - -} - -class FacebookNoticeForm extends NoticeForm -{ - - var $post_action = null; - - /** - * Constructor - * - * @param HTMLOutputter $out output channel - * @param string $action action to return to, if any - * @param string $content content to pre-fill - */ - - function __construct($out=null, $action=null, $content=null, - $post_action=null, $user=null) - { - parent::__construct($out, $action, $content, $user); - $this->post_action = $post_action; - } - - /** - * Action of the form - * - * @return string URL of the action - */ - - function action() - { - return $this->post_action; - } - -} - -class FacebookNoticeList extends NoticeList -{ - - /** - * constructor - * - * @param Notice $notice stream of notices from DB_DataObject - */ - - function __construct($notice, $out=null) - { - parent::__construct($notice, $out); - } - - /** - * show the list of notices - * - * "Uses up" the stream by looping through it. So, probably can't - * be called twice on the same list. - * - * @return int count of notices listed. - */ - - function show() - { - $this->out->elementStart('div', array('id' =>'notices_primary')); - $this->out->element('h2', null, _('Notices')); - $this->out->elementStart('ul', array('class' => 'notices')); - - $cnt = 0; - - while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) { - $cnt++; - - if ($cnt > NOTICES_PER_PAGE) { - break; - } - - $item = $this->newListItem($this->notice); - $item->show(); - } - - $this->out->elementEnd('ul'); - $this->out->elementEnd('div'); - - return $cnt; - } - - /** - * returns a new list item for the current notice - * - * Overridden to return a Facebook specific list item. - * - * @param Notice $notice the current notice - * - * @return FacebookNoticeListItem a list item for displaying the notice - * formatted for display in the Facebook App. - */ - - function newListItem($notice) - { - return new FacebookNoticeListItem($notice, $this); - } - -} - -class FacebookNoticeListItem extends NoticeListItem -{ - - /** - * constructor - * - * Also initializes the profile attribute. - * - * @param Notice $notice The notice we'll display - */ - - function __construct($notice, $out=null) - { - parent::__construct($notice, $out); - } - - /** - * recipe function for displaying a single notice in the Facebook App. - * - * Overridden to strip out some of the controls that we don't - * want to be available. - * - * @return void - */ - - function show() - { - $this->showStart(); - $this->showNotice(); - $this->showNoticeInfo(); - - // XXX: Need to update to show attachements and controls - - $this->showEnd(); - } - -} - -class FacebookProfileBoxNotice extends FacebookNoticeListItem -{ - - /** - * constructor - * - * Also initializes the profile attribute. - * - * @param Notice $notice The notice we'll display - */ - - function __construct($notice, $out=null) - { - parent::__construct($notice, $out); - } - - /** - * Recipe function for displaying a single notice in the - * Facebook App profile notice box - * - * @return void - */ - - function show() - { - $this->showNotice(); - $this->showNoticeInfo(); - $this->showAppLink(); - } - - function showAppLink() - { - - $this->facebook = getFacebook(); - - $app_props = $this->facebook->api_client->Admin_getAppProperties( - array('canvas_name', 'application_name')); - - $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name']; - $this->app_name = $app_props['application_name']; - - $this->out->elementStart('a', array('id' => 'facebook_statusnet_app', - 'href' => $this->app_uri)); - $this->out->text($this->app_name); - $this->out->elementEnd('a'); - } - -} diff --git a/lib/facebookutil.php b/lib/facebookutil.php deleted file mode 100644 index f5121609d..000000000 --- a/lib/facebookutil.php +++ /dev/null @@ -1,260 +0,0 @@ -<?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/>. - */ - -require_once INSTALLDIR.'/extlib/facebook/facebook.php'; -require_once INSTALLDIR.'/lib/facebookaction.php'; -require_once INSTALLDIR.'/lib/noticelist.php'; - -define("FACEBOOK_SERVICE", 2); // Facebook is foreign_service ID 2 -define("FACEBOOK_NOTICE_PREFIX", 1); -define("FACEBOOK_PROMPTED_UPDATE_PREF", 2); - -function getFacebook() -{ - static $facebook = null; - - $apikey = common_config('facebook', 'apikey'); - $secret = common_config('facebook', 'secret'); - - if ($facebook === null) { - $facebook = new Facebook($apikey, $secret); - } - - if (empty($facebook)) { - common_log(LOG_ERR, 'Could not make new Facebook client obj!', - __FILE__); - } - - return $facebook; -} - -function isFacebookBound($notice, $flink) { - - if (empty($flink)) { - return false; - } - - // Avoid a loop - - if ($notice->source == 'Facebook') { - common_log(LOG_INFO, "Skipping notice $notice->id because its " . - 'source is Facebook.'); - return false; - } - - // If the user does not want to broadcast to Facebook, move along - - if (!($flink->noticesync & FOREIGN_NOTICE_SEND == FOREIGN_NOTICE_SEND)) { - common_log(LOG_INFO, "Skipping notice $notice->id " . - 'because user has FOREIGN_NOTICE_SEND bit off.'); - return false; - } - - // If it's not a reply, or if the user WANTS to send @-replies, - // then, yeah, it can go to Facebook. - - if (!preg_match('/@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) || - ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) { - return true; - } - - return false; - -} - -function facebookBroadcastNotice($notice) -{ - $facebook = getFacebook(); - $flink = Foreign_link::getByUserID($notice->profile_id, FACEBOOK_SERVICE); - - if (isFacebookBound($notice, $flink)) { - - // Okay, we're good to go, update the FB status - - $status = null; - $fbuid = $flink->foreign_id; - $user = $flink->getUser(); - $attachments = $notice->attachments(); - - try { - - // Get the status 'verb' (prefix) the user has set - - // XXX: Does this call count against our per user FB request limit? - // If so we should consider storing verb elsewhere or not storing - - $prefix = $facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, - $fbuid); - - $status = "$prefix $notice->content"; - - $can_publish = $facebook->api_client->users_hasAppPermission('publish_stream', - $fbuid); - - $can_update = $facebook->api_client->users_hasAppPermission('status_update', - $fbuid); - if (!empty($attachments) && $can_publish == 1) { - $fbattachment = format_attachments($attachments); - $facebook->api_client->stream_publish($status, $fbattachment, - null, null, $fbuid); - common_log(LOG_INFO, - "Posted notice $notice->id w/attachment " . - "to Facebook user's stream (fbuid = $fbuid)."); - } elseif ($can_update == 1 || $can_publish == 1) { - $facebook->api_client->users_setStatus($status, $fbuid, false, true); - common_log(LOG_INFO, - "Posted notice $notice->id to Facebook " . - "as a status update (fbuid = $fbuid)."); - } else { - $msg = "Not sending notice $notice->id to Facebook " . - "because user $user->nickname hasn't given the " . - 'Facebook app \'status_update\' or \'publish_stream\' permission.'; - common_log(LOG_WARNING, $msg); - } - - // Finally, attempt to update the user's profile box - - if ($can_publish == 1 || $can_update == 1) { - updateProfileBox($facebook, $flink, $notice); - } - - } catch (FacebookRestClientException $e) { - - $code = $e->getCode(); - - common_log(LOG_WARNING, 'Facebook returned error code ' . - $code . ': ' . $e->getMessage()); - common_log(LOG_WARNING, - 'Unable to update Facebook status for ' . - "$user->nickname (user id: $user->id)!"); - - if ($code == 200 || $code == 250) { - - // 200 The application does not have permission to operate on the passed in uid parameter. - // 250 Updating status requires the extended permission status_update or publish_stream. - // see: http://wiki.developers.facebook.com/index.php/Users.setStatus#Example_Return_XML - - remove_facebook_app($flink); - - } else { - - // Try sending again later. - - return false; - } - - } - } - - return true; - -} - -function updateProfileBox($facebook, $flink, $notice) { - $fbaction = new FacebookAction($output = 'php://output', - $indent = true, $facebook, $flink); - $fbaction->updateProfileBox($notice); -} - -function format_attachments($attachments) -{ - $fbattachment = array(); - $fbattachment['media'] = array(); - - foreach($attachments as $attachment) - { - if($enclosure = $attachment->getEnclosure()){ - $fbmedia = get_fbmedia_for_attachment($enclosure); - }else{ - $fbmedia = get_fbmedia_for_attachment($attachment); - } - if($fbmedia){ - $fbattachment['media'][]=$fbmedia; - }else{ - $fbattachment['name'] = ($attachment->title ? - $attachment->title : $attachment->url); - $fbattachment['href'] = $attachment->url; - } - } - if(count($fbattachment['media'])>0){ - unset($fbattachment['name']); - unset($fbattachment['href']); - } - return $fbattachment; -} - -/** -* given an File objects, returns an associative array suitable for Facebook media -*/ -function get_fbmedia_for_attachment($attachment) -{ - $fbmedia = array(); - - if (strncmp($attachment->mimetype, 'image/', strlen('image/')) == 0) { - $fbmedia['type'] = 'image'; - $fbmedia['src'] = $attachment->url; - $fbmedia['href'] = $attachment->url; - } else if ($attachment->mimetype == 'audio/mpeg') { - $fbmedia['type'] = 'mp3'; - $fbmedia['src'] = $attachment->url; - }else if ($attachment->mimetype == 'application/x-shockwave-flash') { - $fbmedia['type'] = 'flash'; - - // http://wiki.developers.facebook.com/index.php/Attachment_%28Streams%29 - // says that imgsrc is required... but we have no value to put in it - // $fbmedia['imgsrc']=''; - - $fbmedia['swfsrc'] = $attachment->url; - }else{ - return false; - } - return $fbmedia; -} - -function remove_facebook_app($flink) -{ - - $user = $flink->getUser(); - - common_log(LOG_INFO, 'Removing Facebook App Foreign link for ' . - "user $user->nickname (user id: $user->id)."); - - $result = $flink->delete(); - - if (empty($result)) { - common_log(LOG_ERR, 'Could not remove Facebook App ' . - "Foreign_link for $user->nickname (user id: $user->id)!"); - common_log_db_error($flink, 'DELETE', __FILE__); - } - - // Notify the user that we are removing their FB app access - - $result = mail_facebook_app_removed($user); - - if (!$result) { - - $msg = 'Unable to send email to notify ' . - "$user->nickname (user id: $user->id) " . - 'that their Facebook app link was ' . - 'removed!'; - - common_log(LOG_WARNING, $msg); - } - -} diff --git a/lib/httpclient.php b/lib/httpclient.php index c8c8ae5b2..f16e31e10 100644 --- a/lib/httpclient.php +++ b/lib/httpclient.php @@ -48,7 +48,7 @@ if (!defined('STATUSNET')) { class HTTPResponse { public $code = null; - public $headers = null; + public $headers = array(); public $body = null; } diff --git a/lib/jabber.php b/lib/jabber.php index 3dcdce5db..73f2ec660 100644 --- a/lib/jabber.php +++ b/lib/jabber.php @@ -176,6 +176,7 @@ function jabber_format_entry($profile, $notice) $xs = new XMLStringer(); $xs->elementStart('html', array('xmlns' => 'http://jabber.org/protocol/xhtml-im')); $xs->elementStart('body', array('xmlns' => 'http://www.w3.org/1999/xhtml')); + $xs->element("img", array('src'=> $profile->avatarUrl(AVATAR_MINI_SIZE) , 'alt' => $profile->nickname)); $xs->element('a', array('href' => $profile->profileurl), $profile->nickname); $xs->text(": "); @@ -184,6 +185,11 @@ function jabber_format_entry($profile, $notice) } else { $xs->raw(common_render_content($notice->content, $notice)); } + $xs->raw(" "); + $xs->element('a', array( + 'href'=>common_local_url('conversation', + array('id' => $notice->conversation)).'#notice-'.$notice->id + ),sprintf(_('notice id: %s'),$notice->id)); $xs->elementEnd('body'); $xs->elementEnd('html'); diff --git a/lib/language.php b/lib/language.php index 561a4ddb8..7dcb808c9 100644 --- a/lib/language.php +++ b/lib/language.php @@ -101,35 +101,36 @@ function get_nice_language_list() */ function get_all_languages() { return array( - 'bg' => array('q' => 0.8, 'lang' => 'bg_BG', 'name' => 'Bulgarian', 'direction' => 'ltr'), - 'ca' => array('q' => 0.5, 'lang' => 'ca_ES', 'name' => 'Catalan', 'direction' => 'ltr'), - 'cs' => array('q' => 0.5, 'lang' => 'cs_CZ', 'name' => 'Czech', 'direction' => 'ltr'), - 'de' => array('q' => 0.8, 'lang' => 'de_DE', 'name' => 'German', 'direction' => 'ltr'), + 'bg' => array('q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'), + 'ca' => array('q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'), + 'cs' => array('q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'), + 'de' => array('q' => 0.8, 'lang' => 'de', 'name' => 'German', 'direction' => 'ltr'), 'el' => array('q' => 0.1, 'lang' => 'el', 'name' => 'Greek', 'direction' => 'ltr'), - 'en-us' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'), + 'en-us' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'), 'en-gb' => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'), - 'en' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'), + 'en' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'), 'es' => array('q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'), 'fi' => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'), - 'fr-fr' => array('q' => 1, 'lang' => 'fr_FR', 'name' => 'French', 'direction' => 'ltr'), - 'he' => array('q' => 0.5, 'lang' => 'he_IL', 'name' => 'Hebrew', 'direction' => 'rtl'), - 'it' => array('q' => 1, 'lang' => 'it_IT', 'name' => 'Italian', 'direction' => 'ltr'), - 'jp' => array('q' => 0.5, 'lang' => 'ja_JP', 'name' => 'Japanese', 'direction' => 'ltr'), - 'ko' => array('q' => 0.9, 'lang' => 'ko_KR', 'name' => 'Korean', 'direction' => 'ltr'), - 'mk' => array('q' => 0.5, 'lang' => 'mk_MK', 'name' => 'Macedonian', 'direction' => 'ltr'), - 'nb' => array('q' => 0.1, 'lang' => 'nb_NO', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'), - 'no' => array('q' => 0.1, 'lang' => 'nb_NO', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'), - 'nn' => array('q' => 1, 'lang' => 'nn_NO', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'), - 'nl' => array('q' => 0.5, 'lang' => 'nl_NL', 'name' => 'Dutch', 'direction' => 'ltr'), - 'pl' => array('q' => 0.5, 'lang' => 'pl_PL', 'name' => 'Polish', 'direction' => 'ltr'), + 'fr-fr' => array('q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'), + 'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Galician', 'direction' => 'ltr'), + 'he' => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'), + 'it' => array('q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'), + 'jp' => array('q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'), + 'ko' => array('q' => 0.9, 'lang' => 'ko', 'name' => 'Korean', 'direction' => 'ltr'), + 'mk' => array('q' => 0.5, 'lang' => 'mk', 'name' => 'Macedonian', 'direction' => 'ltr'), + 'nb' => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'), + 'no' => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'), + 'nn' => array('q' => 1, 'lang' => 'nn', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'), + 'nl' => array('q' => 0.5, 'lang' => 'nl', 'name' => 'Dutch', 'direction' => 'ltr'), + 'pl' => array('q' => 0.5, 'lang' => 'pl', 'name' => 'Polish', 'direction' => 'ltr'), 'pt' => array('q' => 0.1, 'lang' => 'pt', 'name' => 'Portuguese', 'direction' => 'ltr'), 'pt-br' => array('q' => 0.9, 'lang' => 'pt_BR', 'name' => 'Portuguese Brazil', 'direction' => 'ltr'), - 'ru' => array('q' => 0.9, 'lang' => 'ru_RU', 'name' => 'Russian', 'direction' => 'ltr'), - 'sv' => array('q' => 0.8, 'lang' => 'sv_SE', 'name' => 'Swedish', 'direction' => 'ltr'), - 'te' => array('q' => 0.3, 'lang' => 'te_IN', 'name' => 'Telugu', 'direction' => 'ltr'), - 'tr' => array('q' => 0.5, 'lang' => 'tr_TR', 'name' => 'Turkish', 'direction' => 'ltr'), - 'uk' => array('q' => 1, 'lang' => 'uk_UA', 'name' => 'Ukrainian', 'direction' => 'ltr'), - 'vi' => array('q' => 0.8, 'lang' => 'vi_VN', 'name' => 'Vietnamese', 'direction' => 'ltr'), + 'ru' => array('q' => 0.9, 'lang' => 'ru', 'name' => 'Russian', 'direction' => 'ltr'), + 'sv' => array('q' => 0.8, 'lang' => 'sv', 'name' => 'Swedish', 'direction' => 'ltr'), + 'te' => array('q' => 0.3, 'lang' => 'te', 'name' => 'Telugu', 'direction' => 'ltr'), + 'tr' => array('q' => 0.5, 'lang' => 'tr', 'name' => 'Turkish', 'direction' => 'ltr'), + 'uk' => array('q' => 1, 'lang' => 'uk', 'name' => 'Ukrainian', 'direction' => 'ltr'), + 'vi' => array('q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'), 'zh-cn' => array('q' => 0.9, 'lang' => 'zh_CN', 'name' => 'Chinese (Simplified)', 'direction' => 'ltr'), 'zh-hant' => array('q' => 0.2, 'lang' => 'zh_TW', 'name' => 'Chinese (Taiwanese)', 'direction' => 'ltr'), ); diff --git a/lib/location.php b/lib/location.php new file mode 100644 index 000000000..bbfc15a36 --- /dev/null +++ b/lib/location.php @@ -0,0 +1,188 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Class for locations + * + * 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 Location + * @package StatusNet + * @author Evan Prodromou <evan@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') && !defined('LACONICA')) { + exit(1); +} + +/** + * class for locations + * + * These are stored in the DB as part of notice and profile records, + * but since they're about the same in both, we have a separate class + * for them. + * + * @category Location + * @package StatusNet + * @author Evan Prodromou <evan@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 Location +{ + public $lat; + public $lon; + public $location_id; + public $location_ns; + private $_url; + + var $names = array(); + + /** + * Constructor that makes a Location from a string name + * + * @param string $name Human-readable name (any kind) + * @param string $language Language, default = common_language() + * + * @return Location Location with that name (or null if not found) + */ + + static function fromName($name, $language=null) + { + if (is_null($language)) { + $language = common_language(); + } + + $location = null; + + // Let a third-party handle it + + Event::handle('LocationFromName', array($name, $language, &$location)); + + return $location; + } + + /** + * Constructor that makes a Location from an ID + * + * @param integer $id Identifier ID + * @param integer $ns Namespace of the identifier + * @param string $language Language to return name in (default is common) + * + * @return Location The location with this ID (or null if none) + */ + + static function fromId($id, $ns, $language=null) + { + if (is_null($language)) { + $language = common_language(); + } + + $location = null; + + // Let a third-party handle it + + Event::handle('LocationFromId', array($id, $ns, $language, &$location)); + + return $location; + } + + /** + * Constructor that finds the nearest location to a lat/lon pair + * + * @param float $lat Latitude + * @param float $lon Longitude + * @param string $language Language for results, default = current + * + * @return Location the location found, or null if none found + */ + + static function fromLatLon($lat, $lon, $language=null) + { + if (is_null($language)) { + $language = common_language(); + } + + $location = null; + + // Let a third-party handle it + + if (Event::handle('LocationFromLatLon', + array($lat, $lon, $language, &$location))) { + // Default is just the lat/lon pair + + $location = new Location(); + + $location->lat = $lat; + $location->lon = $lon; + } + + return $location; + } + + /** + * Get the name for this location in the given language + * + * @param string $language language to use, default = current + * + * @return string location name or null if not found + */ + + function getName($language=null) + { + if (is_null($language)) { + $language = common_language(); + } + + if (array_key_exists($language, $this->names)) { + return $this->names[$language]; + } else { + $name = null; + Event::handle('LocationNameLanguage', array($this, $language, &$name)); + if (!empty($name)) { + $this->names[$language] = $name; + return $name; + } + } + } + + /** + * Get an URL suitable for this location + * + * @return string URL for this location or NULL + */ + + function getURL() + { + // Keep one cached + + if (is_string($this->_url)) { + return $this->_url; + } + + $url = null; + + Event::handle('LocationUrl', array($this, &$url)); + + $this->_url = $url; + + return $url; + } +} diff --git a/lib/mail.php b/lib/mail.php index 5bf4d7425..5218059e9 100644 --- a/lib/mail.php +++ b/lib/mail.php @@ -640,75 +640,3 @@ function mail_notify_attn($user, $notice) mail_to_user($user, $subject, $body); } -/** - * Send a mail message to notify a user that her Twitter bridge link - * has stopped working, and therefore has been removed. This can - * happen when the user changes her Twitter password, or otherwise - * revokes access. - * - * @param User $user user whose Twitter bridge link has been removed - * - * @return boolean success flag - */ - -function mail_twitter_bridge_removed($user) -{ - common_init_locale($user->language); - - $profile = $user->getProfile(); - - $subject = sprintf(_('Your Twitter bridge has been disabled.')); - - $site_name = common_config('site', 'name'); - - $body = sprintf(_('Hi, %1$s. We\'re sorry to inform you that your ' . - 'link to Twitter has been disabled. We no longer seem to have ' . - 'permission to update your Twitter status. (Did you revoke ' . - '%3$s\'s access?)' . "\n\n" . - 'You can re-enable your Twitter bridge by visiting your ' . - "Twitter settings page:\n\n\t%2\$s\n\n" . - "Regards,\n%3\$s\n"), - $profile->getBestName(), - common_local_url('twittersettings'), - common_config('site', 'name')); - - common_init_locale(); - return mail_to_user($user, $subject, $body); -} - -/** - * Send a mail message to notify a user that her Facebook Application - * access has been removed. - * - * @param User $user user whose Facebook app link has been removed - * - * @return boolean success flag - */ - -function mail_facebook_app_removed($user) -{ - common_init_locale($user->language); - - $profile = $user->getProfile(); - - $site_name = common_config('site', 'name'); - - $subject = sprintf( - _('Your %1$s Facebook application access has been disabled.', - $site_name)); - - $body = sprintf(_("Hi, %1\$s. We're sorry to inform you that we are " . - 'unable to update your Facebook status from %2$s, and have disabled ' . - 'the Facebook application for your account. This may be because ' . - 'you have removed the Facebook application\'s authorization, or ' . - 'have deleted your Facebook account. You can re-enable the ' . - 'Facebook application and automatic status updating by ' . - "re-installing the %2\$s Facebook application.\n\nRegards,\n\n%2\$s"), - $user->nickname, $site_name); - - common_init_locale(); - return mail_to_user($user, $subject, $body); - -} - - diff --git a/lib/mediafile.php b/lib/mediafile.php new file mode 100644 index 000000000..d4d184dd0 --- /dev/null +++ b/lib/mediafile.php @@ -0,0 +1,284 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Abstraction for media files in general + * + * TODO: combine with ImageFile? + * + * 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 Media + * @package StatusNet + * @author Robin Millette <robin@millette.info> + * @author Zach Copley <zach@status.net> + * @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); +} + +class MediaFile +{ + + var $filename = null; + var $fileRecord = null; + var $user = null; + var $fileurl = null; + var $short_fileurl = null; + var $mimetype = null; + + function __construct($user = null, $filename = null, $mimetype = null) + { + if ($user == null) { + $this->user = common_current_user(); + } + + $this->filename = $filename; + $this->mimetype = $mimetype; + $this->fileRecord = $this->storeFile(); + + $this->fileurl = common_local_url('attachment', + array('attachment' => $this->fileRecord->id)); + + $this->maybeAddRedir($this->fileRecord->id, $this->fileurl); + $this->short_fileurl = common_shorten_url($this->fileurl); + $this->maybeAddRedir($this->fileRecord->id, $this->short_fileurl); + } + + function attachToNotice($notice) + { + File_to_post::processNew($this->fileRecord->id, $notice->id); + $this->maybeAddRedir($this->fileRecord->id, + common_local_url('file', array('notice' => $notice->id))); + } + + function shortUrl() + { + return $this->short_fileurl; + } + + function delete() + { + $filepath = File::path($this->filename); + @unlink($filepath); + } + + function storeFile() { + + $file = new File; + + $file->filename = $this->filename; + $file->url = File::url($this->filename); + $filepath = File::path($this->filename); + $file->size = filesize($filepath); + $file->date = time(); + $file->mimetype = $this->mimetype; + + $file_id = $file->insert(); + + if (!$file_id) { + common_log_db_error($file, "INSERT", __FILE__); + throw new ClientException(_('There was a database error while saving your file. Please try again.')); + } + + return $file; + } + + function rememberFile($file, $short) + { + $this->maybeAddRedir($file->id, $short); + } + + function maybeAddRedir($file_id, $url) + { + $file_redir = File_redirection::staticGet('url', $url); + + if (empty($file_redir)) { + + $file_redir = new File_redirection; + $file_redir->url = $url; + $file_redir->file_id = $file_id; + + $result = $file_redir->insert(); + + if (!$result) { + common_log_db_error($file_redir, "INSERT", __FILE__); + throw new ClientException(_('There was a database error while saving your file. Please try again.')); + } + } + } + + static function fromUpload($param = 'media', $user = null) + { + if (empty($user)) { + $user = common_current_user(); + } + + if (!isset($_FILES[$param]['error'])){ + return; + } + + switch ($_FILES[$param]['error']) { + case UPLOAD_ERR_OK: // success, jump out + break; + case UPLOAD_ERR_INI_SIZE: + throw new ClientException(_('The uploaded file exceeds the ' . + 'upload_max_filesize directive in php.ini.')); + return; + case UPLOAD_ERR_FORM_SIZE: + throw new ClientException( + _('The uploaded file exceeds the MAX_FILE_SIZE directive' . + ' that was specified in the HTML form.')); + return; + case UPLOAD_ERR_PARTIAL: + @unlink($_FILES[$param]['tmp_name']); + throw new ClientException(_('The uploaded file was only' . + ' partially uploaded.')); + return; + case UPLOAD_ERR_NO_TMP_DIR: + throw new ClientException(_('Missing a temporary folder.')); + return; + case UPLOAD_ERR_CANT_WRITE: + throw new ClientException(_('Failed to write file to disk.')); + return; + case UPLOAD_ERR_EXTENSION: + throw new ClientException(_('File upload stopped by extension.')); + return; + default: + throw new ClientException(_('System error uploading file.')); + return; + } + + if (!MediaFile::respectsQuota($user, $_FILES['attach']['size'])) { + + // Should never actually get here + + @unlink($_FILES[$param]['tmp_name']); + throw new ClientException(_('File exceeds user\'s quota!')); + return; + } + + $mimetype = MediaFile::getUploadedFileType($_FILES[$param]['tmp_name']); + + $filename = null; + + if (isset($mimetype)) { + + $basename = basename($_FILES[$param]['name']); + $filename = File::filename($user->getProfile(), $basename, $mimetype); + $filepath = File::path($filename); + + $result = move_uploaded_file($_FILES[$param]['tmp_name'], $filepath); + + if (!$result) { + throw new ClientException(_('File could not be moved to destination directory.')); + return; + } + + } else { + throw new ClientException(_('Could not determine file\'s mime-type!')); + return; + } + + return new MediaFile($user, $filename, $mimetype); + } + + static function fromFilehandle($fh, $user) { + + $stream = stream_get_meta_data($fh); + + if (!MediaFile::respectsQuota($user, filesize($stream['uri']))) { + + // Should never actually get here + + throw new ClientException(_('File exceeds user\'s quota!')); + return; + } + + $mimetype = MediaFile::getUploadedFileType($fh); + + $filename = null; + + if (isset($mimetype)) { + + $filename = File::filename($user->getProfile(), "email", $mimetype); + + $filepath = File::path($filename); + + $result = copy($stream['uri'], $filepath) && chmod($filepath, 0664); + + if (!$result) { + throw new ClientException(_('File could not be moved to destination directory.' . + $stream['uri'] . ' ' . $filepath)); + } + } else { + throw new ClientException(_('Could not determine file\'s mime-type!')); + return; + } + + return new MediaFile($user, $filename, $mimetype); + } + + static function getUploadedFileType($f) { + require_once 'MIME/Type.php'; + + $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); + $cmd = common_config('attachments', 'filecommand'); + + $filetype = null; + + if (is_string($f)) { + + // assuming a filename + + $filetype = MIME_Type::autoDetect($f); + } else { + + // assuming a filehandle + + $stream = stream_get_meta_data($f); + $filetype = MIME_Type::autoDetect($stream['uri']); + } + + if (in_array($filetype, common_config('attachments', 'supported'))) { + return $filetype; + } + $media = MIME_Type::getMedia($filetype); + if ('application' !== $media) { + $hint = sprintf(_(' Try using another %s format.'), $media); + } else { + $hint = ''; + } + throw new ClientException(sprintf( + _('%s is not a supported filetype on this server.'), $filetype) . $hint); + } + + static function respectsQuota($user, $filesize) + { + $file = new File; + $result = $file->isRespectsQuota($user, $filesize); + if ($result === true) { + return true; + } else { + throw new ClientException($result); + } + } + +}
\ No newline at end of file diff --git a/lib/messageform.php b/lib/messageform.php index e25ebfa08..b034be312 100644 --- a/lib/messageform.php +++ b/lib/messageform.php @@ -80,11 +80,22 @@ class MessageForm extends Form /** * ID of the form * - * @return int ID of the form + * @return string ID of the form */ function id() { + return 'form_notice-direct'; + } + + /** + * Class of the form + * + * @return string class of the form + */ + + function formClass() + { return 'form_notice'; } diff --git a/lib/noticeform.php b/lib/noticeform.php index 9864d15eb..1be011c18 100644 --- a/lib/noticeform.php +++ b/lib/noticeform.php @@ -105,7 +105,7 @@ class NoticeForm extends Form /** * ID of the form * - * @return int ID of the form + * @return string ID of the form */ function id() @@ -113,6 +113,17 @@ class NoticeForm extends Form return 'form_notice'; } + /** + * Class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_notice'; + } + /** * Action of the form * diff --git a/lib/noticelist.php b/lib/noticelist.php index 6c296f82a..8b3015cc3 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -199,6 +199,7 @@ class NoticeListItem extends Widget { $this->out->elementStart('div', 'entry-content'); $this->showNoticeLink(); + $this->showNoticeLocation(); $this->showNoticeSource(); $this->showContext(); $this->out->elementEnd('div'); @@ -370,6 +371,44 @@ class NoticeListItem extends Widget } /** + * show the notice location + * + * shows the notice location in the correct language. + * + * If an URL is available, makes a link. Otherwise, just a span. + * + * @return void + */ + + function showNoticeLocation() + { + $id = $this->notice->id; + + $location = $this->notice->getLocation(); + + if (empty($location)) { + return; + } + + $name = $location->getName(); + + if (empty($name)) { + // XXX: Could be a translation issue. Fall back to... something? + return; + } + + $url = $location->getUrl(); + + if (empty($url)) { + $this->out->element('span', array('class' => 'location'), $name); + } else { + $this->out->element('a', array('class' => 'location', + 'href' => $url), + $name); + } + } + + /** * Show the source of the notice * * Either the name (and link) of the API client that posted the notice, diff --git a/lib/omb.php b/lib/omb.php index 0566701ff..cd6d6e1b2 100644 --- a/lib/omb.php +++ b/lib/omb.php @@ -87,7 +87,7 @@ function omb_broadcast_notice($notice) common_debug('Posting to ' . $rp->postnoticeurl, __FILE__); /* Post notice. */ - $service = new Laconica_OMB_Service_Consumer( + $service = new StatusNet_OMB_Service_Consumer( array(OMB_ENDPOINT_POSTNOTICE => $rp->postnoticeurl)); try { $service->setToken($rp->token, $rp->secret); diff --git a/lib/profilelist.php b/lib/profilelist.php index 331430b3e..5cc211e36 100644 --- a/lib/profilelist.php +++ b/lib/profilelist.php @@ -62,9 +62,15 @@ class ProfileList extends Widget function show() { - $this->startList(); - $cnt = $this->showProfiles(); - $this->endList(); + $cnt = 0; + + if (Event::handle('StartProfileList', array($this))) { + $this->startList(); + $cnt = $this->showProfiles(); + $this->endList(); + Event::handle('EndProfileList', array($this)); + } + return $cnt; } @@ -117,10 +123,19 @@ class ProfileListItem extends Widget function show() { - $this->startItem(); - $this->showProfile(); - $this->showActions(); - $this->endItem(); + if (Event::handle('StartProfileListItem', array($this))) { + $this->startItem(); + if (Event::handle('StartProfileListItemProfile', array($this))) { + $this->showProfile(); + Event::handle('EndProfileListItemProfile', array($this)); + } + if (Event::handle('StartProfileListItemActions', array($this))) { + $this->showActions(); + Event::handle('EndProfileListItemActions', array($this)); + } + $this->endItem(); + Event::handle('EndProfileListItem', array($this)); + } } function startItem() @@ -132,11 +147,29 @@ class ProfileListItem extends Widget function showProfile() { $this->startProfile(); - $this->showAvatar(); - $this->showFullName(); - $this->showLocation(); - $this->showHomepage(); - $this->showBio(); + if (Event::handle('StartProfileListItemProfileElements', array($this))) { + if (Event::handle('StartProfileListItemAvatar', array($this))) { + $this->showAvatar(); + Event::handle('EndProfileListItemAvatar', array($this)); + } + if (Event::handle('StartProfileListItemFullName', array($this))) { + $this->showFullName(); + Event::handle('EndProfileListItemFullName', array($this)); + } + if (Event::handle('StartProfileListItemLocation', array($this))) { + $this->showLocation(); + Event::handle('EndProfileListItemLocation', array($this)); + } + if (Event::handle('StartProfileListItemHomepage', array($this))) { + $this->showHomepage(); + Event::handle('EndProfileListItemHomepage', array($this)); + } + if (Event::handle('StartProfileListItemBio', array($this))) { + $this->showBio(); + Event::handle('EndProfileListItemBio', array($this)); + } + Event::handle('EndProfileListItemProfileElements', array($this)); + } $this->endProfile(); } @@ -225,7 +258,10 @@ class ProfileListItem extends Widget function showActions() { $this->startActions(); - $this->showSubscribeButton(); + if (Event::handle('StartProfileListItemActionElements', array($this))) { + $this->showSubscribeButton(); + Event::handle('EndProfileListItemActionElements', array($this)); + } $this->endActions(); } diff --git a/lib/queuehandler.php b/lib/queuehandler.php index 8c65a97c6..cd43b1e09 100644 --- a/lib/queuehandler.php +++ b/lib/queuehandler.php @@ -27,6 +27,22 @@ define('CLAIM_TIMEOUT', 1200); define('QUEUE_HANDLER_MISS_IDLE', 10); define('QUEUE_HANDLER_HIT_IDLE', 0); +/** + * Base class for queue handlers. + * + * As extensions of the Daemon class, each queue handler has the ability + * to launch itself in the background, at which point it'll pass control + * to the configured QueueManager class to poll for updates. + * + * Subclasses must override at least the following methods: + * - transport + * - start + * - finish + * - handle_notice + * + * Some subclasses will also want to override the idle handler: + * - idle + */ class QueueHandler extends Daemon { @@ -39,6 +55,14 @@ class QueueHandler extends Daemon } } + /** + * How many seconds a polling-based queue manager should wait between + * checks for new items to handle. + * + * Defaults to 60 seconds; override to speed up or slow down. + * + * @return int timeout in seconds + */ function timeout() { return 60; @@ -54,24 +78,69 @@ class QueueHandler extends Daemon return strtolower($this->class_name().'.'.$this->get_id()); } + /** + * Return transport keyword which identifies items this queue handler + * services; must be defined for all subclasses. + * + * Must be 8 characters or less to fit in the queue_item database. + * ex "email", "jabber", "sms", "irc", ... + * + * @return string + */ function transport() { return null; } + /** + * Initialization, run when the queue handler starts. + * If this function indicates failure, the handler run will be aborted. + * + * @fixme run() will abort if this doesn't return true, + * but some subclasses don't bother. + * @return boolean true on success, false on failure + */ function start() { } + /** + * Cleanup, run when the queue handler ends. + * If this function indicates failure, a warning will be logged. + * + * @fixme run() will throw warnings if this doesn't return true, + * but many subclasses don't bother. + * @return boolean true on success, false on failure + */ function finish() { } + /** + * Here's the meat of your queue handler -- you're handed a Notice + * object, which you may do as you will with. + * + * If this function indicates failure, a warning will be logged + * and the item is placed back in the queue to be re-run. + * + * @param Notice $notice + * @return boolean true on success, false on failure + */ function handle_notice($notice) { return true; } + /** + * Setup and start of run loop for this queue handler as a daemon. + * Most of the heavy lifting is passed on to the QueueManager's service() + * method, which passes control back to our handle_notice() method for + * each notice that comes in on the queue. + * + * Most of the time this won't need to be overridden in a subclass. + * + * @return boolean true on success, false on failure + */ function run() { if (!$this->start()) { @@ -100,6 +169,14 @@ class QueueHandler extends Daemon return true; } + /** + * Called by QueueHandler after each handled item or empty polling cycle. + * This is a good time to e.g. service your XMPP connection. + * + * Doesn't need to be overridden if there's no maintenance to do. + * + * @param int $timeout seconds to sleep if there's nothing to do + */ function idle($timeout=0) { if ($timeout > 0) { diff --git a/lib/router.php b/lib/router.php index b9a45d867..64853e419 100644 --- a/lib/router.php +++ b/lib/router.php @@ -71,574 +71,573 @@ class Router { $m = Net_URL_Mapper::getInstance(); - // In the "root" + if (Event::handle('StartInitializeRouter', array(&$m))) { - $m->connect('', array('action' => 'public')); - $m->connect('rss', array('action' => 'publicrss')); - $m->connect('featuredrss', array('action' => 'featuredrss')); - $m->connect('favoritedrss', array('action' => 'favoritedrss')); - $m->connect('opensearch/people', array('action' => 'opensearch', - 'type' => 'people')); - $m->connect('opensearch/notice', array('action' => 'opensearch', - 'type' => 'notice')); + // In the "root" - // docs + $m->connect('', array('action' => 'public')); + $m->connect('rss', array('action' => 'publicrss')); + $m->connect('featuredrss', array('action' => 'featuredrss')); + $m->connect('favoritedrss', array('action' => 'favoritedrss')); + $m->connect('opensearch/people', array('action' => 'opensearch', + 'type' => 'people')); + $m->connect('opensearch/notice', array('action' => 'opensearch', + 'type' => 'notice')); - $m->connect('doc/:title', array('action' => 'doc')); + // docs - // Twitter + $m->connect('doc/:title', array('action' => 'doc')); - $m->connect('twitter/authorization', array('action' => 'twitterauthorization')); + // main stuff is repetitive - // facebook + $main = array('login', 'logout', 'register', 'subscribe', + 'unsubscribe', 'confirmaddress', 'recoverpassword', + 'invite', 'favor', 'disfavor', 'sup', + 'block', 'unblock', 'subedit', + 'groupblock', 'groupunblock'); - $m->connect('facebook', array('action' => 'facebookhome')); - $m->connect('facebook/index.php', array('action' => 'facebookhome')); - $m->connect('facebook/settings.php', array('action' => 'facebooksettings')); - $m->connect('facebook/invite.php', array('action' => 'facebookinvite')); - $m->connect('facebook/remove', array('action' => 'facebookremove')); + foreach ($main as $a) { + $m->connect('main/'.$a, array('action' => $a)); + } - // main stuff is repetitive + $m->connect('main/sup/:seconds', array('action' => 'sup'), + array('seconds' => '[0-9]+')); - $main = array('login', 'logout', 'register', 'subscribe', - 'unsubscribe', 'confirmaddress', 'recoverpassword', - 'invite', 'favor', 'disfavor', 'sup', - 'block', 'unblock', 'subedit', - 'groupblock', 'groupunblock'); + $m->connect('main/tagother/:id', array('action' => 'tagother')); - foreach ($main as $a) { - $m->connect('main/'.$a, array('action' => $a)); - } - - $m->connect('main/sup/:seconds', array('action' => 'sup'), - array('seconds' => '[0-9]+')); + $m->connect('main/oembed', + array('action' => 'oembed')); - $m->connect('main/tagother/:id', array('action' => 'tagother')); + $m->connect('main/xrds', + array('action' => 'publicxrds')); - $m->connect('main/oembed', - array('action' => 'oembed')); + // these take a code - // these take a code + foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) { + $m->connect('main/'.$c.'/:code', array('action' => $c)); + } - foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) { - $m->connect('main/'.$c.'/:code', array('action' => $c)); - } + // exceptional - // exceptional + $m->connect('main/remote', array('action' => 'remotesubscribe')); + $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+')); - $m->connect('main/remote', array('action' => 'remotesubscribe')); - $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+')); + foreach (Router::$bare as $action) { + $m->connect('index.php?action=' . $action, array('action' => $action)); + } - foreach (Router::$bare as $action) { - $m->connect('index.php?action=' . $action, array('action' => $action)); - } + // settings - // settings + foreach (array('profile', 'avatar', 'password', 'im', + 'email', 'sms', 'userdesign', 'other') as $s) { + $m->connect('settings/'.$s, array('action' => $s.'settings')); + } - foreach (array('profile', 'avatar', 'password', 'im', - 'email', 'sms', 'twitter', 'userdesign', 'other') as $s) { - $m->connect('settings/'.$s, array('action' => $s.'settings')); - } + // search - // search + foreach (array('group', 'people', 'notice') as $s) { + $m->connect('search/'.$s, array('action' => $s.'search')); + $m->connect('search/'.$s.'?q=:q', + array('action' => $s.'search'), + array('q' => '.+')); + } - foreach (array('group', 'people', 'notice') as $s) { - $m->connect('search/'.$s, array('action' => $s.'search')); - $m->connect('search/'.$s.'?q=:q', - array('action' => $s.'search'), + // The second of these is needed to make the link work correctly + // when inserted into the page. The first is needed to match the + // route on the way in. Seems to be another Net_URL_Mapper bug to me. + $m->connect('search/notice/rss', array('action' => 'noticesearchrss')); + $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'), array('q' => '.+')); - } - // The second of these is needed to make the link work correctly - // when inserted into the page. The first is needed to match the - // route on the way in. Seems to be another Net_URL_Mapper bug to me. - $m->connect('search/notice/rss', array('action' => 'noticesearchrss')); - $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'), - array('q' => '.+')); - - $m->connect('attachment/:attachment', - array('action' => 'attachment'), - array('attachment' => '[0-9]+')); - - $m->connect('attachment/:attachment/ajax', - array('action' => 'attachment_ajax'), - array('attachment' => '[0-9]+')); - - $m->connect('attachment/:attachment/thumbnail', - array('action' => 'attachment_thumbnail'), - array('attachment' => '[0-9]+')); - - $m->connect('notice/new', array('action' => 'newnotice')); - $m->connect('notice/new?replyto=:replyto', - array('action' => 'newnotice'), - array('replyto' => '[A-Za-z0-9_-]+')); - $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto', - array('action' => 'newnotice'), - array('replyto' => '[A-Za-z0-9_-]+'), - array('inreplyto' => '[0-9]+')); - - $m->connect('notice/:notice/file', - array('action' => 'file'), - array('notice' => '[0-9]+')); - - $m->connect('notice/:notice', - array('action' => 'shownotice'), - array('notice' => '[0-9]+')); - $m->connect('notice/delete', array('action' => 'deletenotice')); - $m->connect('notice/delete/:notice', - array('action' => 'deletenotice'), - array('notice' => '[0-9]+')); - - // conversation - - $m->connect('conversation/:id', - array('action' => 'conversation'), - array('id' => '[0-9]+')); - - $m->connect('message/new', array('action' => 'newmessage')); - $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]+')); - $m->connect('message/:message', - array('action' => 'showmessage'), - array('message' => '[0-9]+')); - - $m->connect('user/:id', - array('action' => 'userbyid'), - array('id' => '[0-9]+')); - - $m->connect('tags/', array('action' => 'publictagcloud')); - $m->connect('tag/', array('action' => 'publictagcloud')); - $m->connect('tags', array('action' => 'publictagcloud')); - $m->connect('tag', array('action' => 'publictagcloud')); - $m->connect('tag/:tag/rss', - array('action' => 'tagrss'), - array('tag' => '[a-zA-Z0-9]+')); - $m->connect('tag/:tag', - array('action' => 'tag'), - array('tag' => '[\pL\pN_\-\.]{1,64}')); - - $m->connect('peopletag/:tag', - array('action' => 'peopletag'), - array('tag' => '[a-zA-Z0-9]+')); - - $m->connect('featured/', array('action' => 'featured')); - $m->connect('featured', array('action' => 'featured')); - $m->connect('favorited/', array('action' => 'favorited')); - $m->connect('favorited', array('action' => 'favorited')); - - // groups - - $m->connect('group/new', array('action' => 'newgroup')); - - foreach (array('edit', 'join', 'leave') as $v) { - $m->connect('group/:nickname/'.$v, - array('action' => $v.'group'), + $m->connect('attachment/:attachment', + array('action' => 'attachment'), + array('attachment' => '[0-9]+')); + + $m->connect('attachment/:attachment/ajax', + array('action' => 'attachment_ajax'), + array('attachment' => '[0-9]+')); + + $m->connect('attachment/:attachment/thumbnail', + array('action' => 'attachment_thumbnail'), + array('attachment' => '[0-9]+')); + + $m->connect('notice/new', array('action' => 'newnotice')); + $m->connect('notice/new?replyto=:replyto', + array('action' => 'newnotice'), + array('replyto' => '[A-Za-z0-9_-]+')); + $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto', + array('action' => 'newnotice'), + array('replyto' => '[A-Za-z0-9_-]+'), + array('inreplyto' => '[0-9]+')); + + $m->connect('notice/:notice/file', + array('action' => 'file'), + array('notice' => '[0-9]+')); + + $m->connect('notice/:notice', + array('action' => 'shownotice'), + array('notice' => '[0-9]+')); + $m->connect('notice/delete', array('action' => 'deletenotice')); + $m->connect('notice/delete/:notice', + array('action' => 'deletenotice'), + array('notice' => '[0-9]+')); + + $m->connect('bookmarklet/new', array('action' => 'bookmarklet')); + + // conversation + + $m->connect('conversation/:id', + array('action' => 'conversation'), + array('id' => '[0-9]+')); + + $m->connect('message/new', array('action' => 'newmessage')); + $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]+')); + $m->connect('message/:message', + array('action' => 'showmessage'), + array('message' => '[0-9]+')); + + $m->connect('user/:id', + array('action' => 'userbyid'), + array('id' => '[0-9]+')); + + $m->connect('tags/', array('action' => 'publictagcloud')); + $m->connect('tag/', array('action' => 'publictagcloud')); + $m->connect('tags', array('action' => 'publictagcloud')); + $m->connect('tag', array('action' => 'publictagcloud')); + $m->connect('tag/:tag/rss', + array('action' => 'tagrss'), + array('tag' => '[a-zA-Z0-9]+')); + $m->connect('tag/:tag', + array('action' => 'tag'), + array('tag' => '[\pL\pN_\-\.]{1,64}')); + + $m->connect('peopletag/:tag', + array('action' => 'peopletag'), + array('tag' => '[a-zA-Z0-9]+')); + + $m->connect('featured/', array('action' => 'featured')); + $m->connect('featured', array('action' => 'featured')); + $m->connect('favorited/', array('action' => 'favorited')); + $m->connect('favorited', array('action' => 'favorited')); + + // groups + + $m->connect('group/new', array('action' => 'newgroup')); + + foreach (array('edit', 'join', 'leave') as $v) { + $m->connect('group/:nickname/'.$v, + array('action' => $v.'group'), + array('nickname' => '[a-zA-Z0-9]+')); + } + + foreach (array('members', 'logo', 'rss', 'designsettings') as $n) { + $m->connect('group/:nickname/'.$n, + array('action' => 'group'.$n), + array('nickname' => '[a-zA-Z0-9]+')); + } + + $m->connect('group/:nickname/foaf', + array('action' => 'foafgroup'), array('nickname' => '[a-zA-Z0-9]+')); - } - foreach (array('members', 'logo', 'rss', 'designsettings') as $n) { - $m->connect('group/:nickname/'.$n, - array('action' => 'group'.$n), + $m->connect('group/:nickname/blocked', + array('action' => 'blockedfromgroup'), array('nickname' => '[a-zA-Z0-9]+')); - } - - $m->connect('group/:nickname/foaf', - array('action' => 'foafgroup'), - array('nickname' => '[a-zA-Z0-9]+')); - - $m->connect('group/:nickname/blocked', - array('action' => 'blockedfromgroup'), - array('nickname' => '[a-zA-Z0-9]+')); - - $m->connect('group/:nickname/makeadmin', - array('action' => 'makeadmin'), - array('nickname' => '[a-zA-Z0-9]+')); - - $m->connect('group/:id/id', - array('action' => 'groupbyid'), - array('id' => '[0-9]+')); - - $m->connect('group/:nickname', - array('action' => 'showgroup'), - array('nickname' => '[a-zA-Z0-9]+')); - - $m->connect('group/', array('action' => 'groups')); - $m->connect('group', array('action' => 'groups')); - $m->connect('groups/', array('action' => 'groups')); - $m->connect('groups', array('action' => 'groups')); - - // Twitter-compatible API - - // statuses API - - $m->connect('api/statuses/public_timeline.:format', - array('action' => 'ApiTimelinePublic', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/friends_timeline.:format', - array('action' => 'ApiTimelineFriends', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/friends_timeline/:id.:format', - array('action' => 'ApiTimelineFriends', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - $m->connect('api/statuses/home_timeline.:format', - array('action' => 'ApiTimelineFriends', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/home_timeline/:id.:format', - array('action' => 'ApiTimelineFriends', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/user_timeline.:format', - array('action' => 'ApiTimelineUser', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/user_timeline/:id.:format', - array('action' => 'ApiTimelineUser', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/mentions.:format', - array('action' => 'ApiTimelineMentions', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/mentions/:id.:format', - array('action' => 'ApiTimelineMentions', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - $m->connect('api/statuses/replies.:format', - array('action' => 'ApiTimelineMentions', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/replies/:id.:format', - array('action' => 'ApiTimelineMentions', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statuses/friends.:format', - array('action' => 'ApiUserFriends', - 'format' => '(xml|json)')); + $m->connect('group/:nickname/makeadmin', + array('action' => 'makeadmin'), + array('nickname' => '[a-zA-Z0-9]+')); - $m->connect('api/statuses/friends/:id.:format', - array('action' => 'ApiUserFriends', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('group/:id/id', + array('action' => 'groupbyid'), + array('id' => '[0-9]+')); - $m->connect('api/statuses/followers.:format', - array('action' => 'ApiUserFollowers', - 'format' => '(xml|json)')); + $m->connect('group/:nickname', + array('action' => 'showgroup'), + array('nickname' => '[a-zA-Z0-9]+')); - $m->connect('api/statuses/followers/:id.:format', - array('action' => 'ApiUserFollowers', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('group/', array('action' => 'groups')); + $m->connect('group', array('action' => 'groups')); + $m->connect('groups/', array('action' => 'groups')); + $m->connect('groups', array('action' => 'groups')); + + // Twitter-compatible API + + // statuses API + + $m->connect('api/statuses/public_timeline.:format', + array('action' => 'ApiTimelinePublic', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/friends_timeline.:format', + array('action' => 'ApiTimelineFriends', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/friends_timeline/:id.:format', + array('action' => 'ApiTimelineFriends', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + $m->connect('api/statuses/home_timeline.:format', + array('action' => 'ApiTimelineFriends', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/home_timeline/:id.:format', + array('action' => 'ApiTimelineFriends', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); - $m->connect('api/statuses/show.:format', - array('action' => 'ApiStatusesShow', - 'format' => '(xml|json)')); + $m->connect('api/statuses/user_timeline.:format', + array('action' => 'ApiTimelineUser', + 'format' => '(xml|json|rss|atom)')); - $m->connect('api/statuses/show/:id.:format', - array('action' => 'ApiStatusesShow', - 'id' => '[0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/statuses/user_timeline/:id.:format', + array('action' => 'ApiTimelineUser', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/mentions.:format', + array('action' => 'ApiTimelineMentions', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/mentions/:id.:format', + array('action' => 'ApiTimelineMentions', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/replies.:format', + array('action' => 'ApiTimelineMentions', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/replies/:id.:format', + array('action' => 'ApiTimelineMentions', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statuses/friends.:format', + array('action' => 'ApiUserFriends', + 'format' => '(xml|json)')); + + $m->connect('api/statuses/friends/:id.:format', + array('action' => 'ApiUserFriends', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/statuses/update.:format', - array('action' => 'ApiStatusesUpdate', - 'format' => '(xml|json)')); + $m->connect('api/statuses/followers.:format', + array('action' => 'ApiUserFollowers', + 'format' => '(xml|json)')); - $m->connect('api/statuses/destroy.:format', - array('action' => 'ApiStatusesDestroy', - 'format' => '(xml|json)')); + $m->connect('api/statuses/followers/:id.:format', + array('action' => 'ApiUserFollowers', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/statuses/destroy/:id.:format', - array('action' => 'ApiStatusesDestroy', - 'id' => '[0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/statuses/show.:format', + array('action' => 'ApiStatusesShow', + 'format' => '(xml|json)')); - // users + $m->connect('api/statuses/show/:id.:format', + array('action' => 'ApiStatusesShow', + 'id' => '[0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/users/show/:id.:format', - array('action' => 'ApiUserShow', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/statuses/update.:format', + array('action' => 'ApiStatusesUpdate', + 'format' => '(xml|json)')); - $m->connect('api/users/:method', - array('action' => 'api', - 'apiaction' => 'users'), - array('method' => 'show(\.(xml|json))?')); + $m->connect('api/statuses/destroy.:format', + array('action' => 'ApiStatusesDestroy', + 'format' => '(xml|json)')); - // direct messages + $m->connect('api/statuses/destroy/:id.:format', + array('action' => 'ApiStatusesDestroy', + 'id' => '[0-9]+', + 'format' => '(xml|json)')); + // users - $m->connect('api/direct_messages.:format', - array('action' => 'ApiDirectMessage', - 'format' => '(xml|json|rss|atom)')); + $m->connect('api/users/show/:id.:format', + array('action' => 'ApiUserShow', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/direct_messages/sent.:format', - array('action' => 'ApiDirectMessage', - 'format' => '(xml|json|rss|atom)', - 'sent' => true)); + $m->connect('api/users/:method', + array('action' => 'api', + 'apiaction' => 'users'), + array('method' => 'show(\.(xml|json))?')); - $m->connect('api/direct_messages/new.:format', - array('action' => 'ApiDirectMessageNew', - 'format' => '(xml|json)')); + // direct messages - // friendships + $m->connect('api/direct_messages.:format', + array('action' => 'ApiDirectMessage', + 'format' => '(xml|json|rss|atom)')); - $m->connect('api/friendships/show.:format', - array('action' => 'ApiFriendshipsShow', - 'format' => '(xml|json)')); + $m->connect('api/direct_messages/sent.:format', + array('action' => 'ApiDirectMessage', + 'format' => '(xml|json|rss|atom)', + 'sent' => true)); - $m->connect('api/friendships/exists.:format', - array('action' => 'ApiFriendshipsExists', - 'format' => '(xml|json)')); + $m->connect('api/direct_messages/new.:format', + array('action' => 'ApiDirectMessageNew', + 'format' => '(xml|json)')); - $m->connect('api/friendships/create.:format', - array('action' => 'ApiFriendshipsCreate', - 'format' => '(xml|json)')); + // friendships - $m->connect('api/friendships/destroy.:format', - array('action' => 'ApiFriendshipsDestroy', - 'format' => '(xml|json)')); + $m->connect('api/friendships/show.:format', + array('action' => 'ApiFriendshipsShow', + 'format' => '(xml|json)')); - $m->connect('api/friendships/create/:id.:format', - array('action' => 'ApiFriendshipsCreate', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/friendships/exists.:format', + array('action' => 'ApiFriendshipsExists', + 'format' => '(xml|json)')); - $m->connect('api/friendships/destroy/:id.:format', - array('action' => 'ApiFriendshipsDestroy', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/friendships/create.:format', + array('action' => 'ApiFriendshipsCreate', + 'format' => '(xml|json)')); - // Social graph + $m->connect('api/friendships/destroy.:format', + array('action' => 'ApiFriendshipsDestroy', + 'format' => '(xml|json)')); - $m->connect('api/friends/ids/:id.:format', - array('action' => 'apiFriends', - 'ids_only' => true)); + $m->connect('api/friendships/create/:id.:format', + array('action' => 'ApiFriendshipsCreate', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/followers/ids/:id.:format', - array('action' => 'apiFollowers', - 'ids_only' => true)); + $m->connect('api/friendships/destroy/:id.:format', + array('action' => 'ApiFriendshipsDestroy', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/friends/ids.:format', - array('action' => 'apiFriends', - 'ids_only' => true)); + // Social graph - $m->connect('api/followers/ids.:format', - array('action' => 'apiFollowers', - 'ids_only' => true)); + $m->connect('api/friends/ids/:id.:format', + array('action' => 'apiFriends', + 'ids_only' => true)); - // account + $m->connect('api/followers/ids/:id.:format', + array('action' => 'apiFollowers', + 'ids_only' => true)); - $m->connect('api/account/verify_credentials.:format', - array('action' => 'ApiAccountVerifyCredentials')); + $m->connect('api/friends/ids.:format', + array('action' => 'apiFriends', + 'ids_only' => true)); - // special case where verify_credentials is called w/out a format + $m->connect('api/followers/ids.:format', + array('action' => 'apiFollowers', + 'ids_only' => true)); - $m->connect('api/account/verify_credentials', - array('action' => 'ApiAccountVerifyCredentials')); + // account - $m->connect('api/account/rate_limit_status.:format', - array('action' => 'ApiAccountRateLimitStatus')); + $m->connect('api/account/verify_credentials.:format', + array('action' => 'ApiAccountVerifyCredentials')); - // favorites + // special case where verify_credentials is called w/out a format - $m->connect('api/favorites.:format', - array('action' => 'ApiTimelineFavorites', - 'format' => '(xml|json|rss|atom)')); + $m->connect('api/account/verify_credentials', + array('action' => 'ApiAccountVerifyCredentials')); - $m->connect('api/favorites/:id.:format', - array('action' => 'ApiTimelineFavorites', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xmljson|rss|atom)')); + $m->connect('api/account/rate_limit_status.:format', + array('action' => 'ApiAccountRateLimitStatus')); - $m->connect('api/favorites/create/:id.:format', - array('action' => 'ApiFavoriteCreate', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + // favorites - $m->connect('api/favorites/destroy/:id.:format', - array('action' => 'ApiFavoriteDestroy', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + $m->connect('api/favorites.:format', + array('action' => 'ApiTimelineFavorites', + 'format' => '(xml|json|rss|atom)')); - // notifications + $m->connect('api/favorites/:id.:format', + array('action' => 'ApiTimelineFavorites', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xmljson|rss|atom)')); - $m->connect('api/notifications/:method/:argument', - array('action' => 'api', - 'apiaction' => 'favorites')); + $m->connect('api/favorites/create/:id.:format', + array('action' => 'ApiFavoriteCreate', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - // blocks + $m->connect('api/favorites/destroy/:id.:format', + array('action' => 'ApiFavoriteDestroy', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/blocks/create/:id.:format', - array('action' => 'ApiBlockCreate', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); + // notifications - $m->connect('api/blocks/destroy/:id.:format', - array('action' => 'ApiBlockDestroy', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - // help + $m->connect('api/notifications/:method/:argument', + array('action' => 'api', + 'apiaction' => 'favorites')); - $m->connect('api/help/test.:format', - array('action' => 'ApiHelpTest', - 'format' => '(xml|json)')); + // blocks - // statusnet + $m->connect('api/blocks/create/:id.:format', + array('action' => 'ApiBlockCreate', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); - $m->connect('api/statusnet/version.:format', - array('action' => 'ApiStatusnetVersion', - 'format' => '(xml|json)')); + $m->connect('api/blocks/destroy/:id.:format', + array('action' => 'ApiBlockDestroy', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + // help - $m->connect('api/statusnet/config.:format', - array('action' => 'ApiStatusnetConfig', - 'format' => '(xml|json)')); + $m->connect('api/help/test.:format', + array('action' => 'ApiHelpTest', + 'format' => '(xml|json)')); - // For older methods, we provide "laconica" base action - - $m->connect('api/laconica/version.:format', - array('action' => 'ApiStatusnetVersion', - 'format' => '(xml|json)')); - - $m->connect('api/laconica/config.:format', - array('action' => 'ApiStatusnetConfig', - 'format' => '(xml|json)')); + // statusnet - // Groups and tags are newer than 0.8.1 so no backward-compatibility - // necessary + $m->connect('api/statusnet/version.:format', + array('action' => 'ApiStatusnetVersion', + 'format' => '(xml|json)')); - // Groups - //'list' has to be handled differently, as php will not allow a method to be named 'list' + $m->connect('api/statusnet/config.:format', + array('action' => 'ApiStatusnetConfig', + 'format' => '(xml|json)')); - $m->connect('api/statusnet/groups/timeline/:id.:format', - array('action' => 'ApiTimelineGroup', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xmljson|rss|atom)')); + // For older methods, we provide "laconica" base action - $m->connect('api/statusnet/groups/show.:format', - array('action' => 'ApiGroupShow', - 'format' => '(xml|json)')); + $m->connect('api/laconica/version.:format', + array('action' => 'ApiStatusnetVersion', + 'format' => '(xml|json)')); + + $m->connect('api/laconica/config.:format', + array('action' => 'ApiStatusnetConfig', + 'format' => '(xml|json)')); - $m->connect('api/statusnet/groups/show/:id.:format', - array('action' => 'ApiGroupShow', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/join.:format', - array('action' => 'ApiGroupJoin', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/join/:id.:format', - array('action' => 'ApiGroupJoin', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/leave.:format', - array('action' => 'ApiGroupLeave', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/leave/:id.:format', - array('action' => 'ApiGroupLeave', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/is_member.:format', - array('action' => 'ApiGroupIsMember', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/list.:format', - array('action' => 'ApiGroupList', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statusnet/groups/list/:id.:format', - array('action' => 'ApiGroupList', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statusnet/groups/list_all.:format', - array('action' => 'ApiGroupListAll', - 'format' => '(xml|json|rss|atom)')); - - $m->connect('api/statusnet/groups/membership.:format', - array('action' => 'ApiGroupMembership', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/membership/:id.:format', - array('action' => 'ApiGroupMembership', - 'id' => '[a-zA-Z0-9]+', - 'format' => '(xml|json)')); - - $m->connect('api/statusnet/groups/create.:format', - array('action' => 'ApiGroupCreate', - 'format' => '(xml|json)')); - // Tags - $m->connect('api/statusnet/tags/timeline/:tag.:format', - array('action' => 'ApiTimelineTag', - 'format' => '(xmljson|rss|atom)')); - - // search - $m->connect('api/search.atom', array('action' => 'twitapisearchatom')); - $m->connect('api/search.json', array('action' => 'twitapisearchjson')); - $m->connect('api/trends.json', array('action' => 'twitapitrends')); - - // user stuff - - foreach (array('subscriptions', 'subscribers', - 'nudge', 'all', 'foaf', 'xrds', - 'replies', 'inbox', 'outbox', 'microsummary') as $a) { - $m->connect(':nickname/'.$a, - array('action' => $a), + // Groups and tags are newer than 0.8.1 so no backward-compatibility + // necessary + + // Groups + //'list' has to be handled differently, as php will not allow a method to be named 'list' + + $m->connect('api/statusnet/groups/timeline/:id.:format', + array('action' => 'ApiTimelineGroup', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xmljson|rss|atom)')); + + $m->connect('api/statusnet/groups/show.:format', + array('action' => 'ApiGroupShow', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/show/:id.:format', + array('action' => 'ApiGroupShow', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/join.:format', + array('action' => 'ApiGroupJoin', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/join/:id.:format', + array('action' => 'ApiGroupJoin', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/leave.:format', + array('action' => 'ApiGroupLeave', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/leave/:id.:format', + array('action' => 'ApiGroupLeave', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/is_member.:format', + array('action' => 'ApiGroupIsMember', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/list.:format', + array('action' => 'ApiGroupList', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statusnet/groups/list/:id.:format', + array('action' => 'ApiGroupList', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statusnet/groups/list_all.:format', + array('action' => 'ApiGroupListAll', + 'format' => '(xml|json|rss|atom)')); + + $m->connect('api/statusnet/groups/membership.:format', + array('action' => 'ApiGroupMembership', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/membership/:id.:format', + array('action' => 'ApiGroupMembership', + 'id' => '[a-zA-Z0-9]+', + 'format' => '(xml|json)')); + + $m->connect('api/statusnet/groups/create.:format', + array('action' => 'ApiGroupCreate', + 'format' => '(xml|json)')); + // Tags + $m->connect('api/statusnet/tags/timeline/:tag.:format', + array('action' => 'ApiTimelineTag', + 'format' => '(xmljson|rss|atom)')); + + // search + $m->connect('api/search.atom', array('action' => 'twitapisearchatom')); + $m->connect('api/search.json', array('action' => 'twitapisearchjson')); + $m->connect('api/trends.json', array('action' => 'twitapitrends')); + + $m->connect('getfile/:filename', + array('action' => 'getfile'), + array('filename' => '[A-Za-z0-9._-]+')); + + // user stuff + + foreach (array('subscriptions', 'subscribers', + 'nudge', 'all', 'foaf', 'xrds', + 'replies', 'inbox', 'outbox', 'microsummary') as $a) { + $m->connect(':nickname/'.$a, + array('action' => $a), + array('nickname' => '[a-zA-Z0-9]{1,64}')); + } + + foreach (array('subscriptions', 'subscribers') as $a) { + $m->connect(':nickname/'.$a.'/:tag', + array('action' => $a), + array('tag' => '[a-zA-Z0-9]+', + 'nickname' => '[a-zA-Z0-9]{1,64}')); + } + + foreach (array('rss', 'groups') as $a) { + $m->connect(':nickname/'.$a, + array('action' => 'user'.$a), + array('nickname' => '[a-zA-Z0-9]{1,64}')); + } + + foreach (array('all', 'replies', 'favorites') as $a) { + $m->connect(':nickname/'.$a.'/rss', + array('action' => $a.'rss'), + array('nickname' => '[a-zA-Z0-9]{1,64}')); + } + + $m->connect(':nickname/favorites', + array('action' => 'showfavorites'), array('nickname' => '[a-zA-Z0-9]{1,64}')); - } - foreach (array('subscriptions', 'subscribers') as $a) { - $m->connect(':nickname/'.$a.'/:tag', - array('action' => $a), - array('tag' => '[a-zA-Z0-9]+', + $m->connect(':nickname/avatar/:size', + array('action' => 'avatarbynickname'), + array('size' => '(original|96|48|24)', 'nickname' => '[a-zA-Z0-9]{1,64}')); - } - foreach (array('rss', 'groups') as $a) { - $m->connect(':nickname/'.$a, - array('action' => 'user'.$a), - array('nickname' => '[a-zA-Z0-9]{1,64}')); - } + $m->connect(':nickname/tag/:tag/rss', + array('action' => 'userrss'), + array('nickname' => '[a-zA-Z0-9]{1,64}'), + array('tag' => '[a-zA-Z0-9]+')); - foreach (array('all', 'replies', 'favorites') as $a) { - $m->connect(':nickname/'.$a.'/rss', - array('action' => $a.'rss'), - array('nickname' => '[a-zA-Z0-9]{1,64}')); - } - - $m->connect(':nickname/favorites', - array('action' => 'showfavorites'), - array('nickname' => '[a-zA-Z0-9]{1,64}')); - - $m->connect(':nickname/avatar/:size', - array('action' => 'avatarbynickname'), - array('size' => '(original|96|48|24)', - 'nickname' => '[a-zA-Z0-9]{1,64}')); - - $m->connect(':nickname/tag/:tag/rss', - array('action' => 'userrss'), - array('nickname' => '[a-zA-Z0-9]{1,64}'), - array('tag' => '[a-zA-Z0-9]+')); + $m->connect(':nickname/tag/:tag', + array('action' => 'showstream'), + array('nickname' => '[a-zA-Z0-9]{1,64}'), + array('tag' => '[a-zA-Z0-9]+')); - $m->connect(':nickname/tag/:tag', - array('action' => 'showstream'), - array('nickname' => '[a-zA-Z0-9]{1,64}'), - array('tag' => '[a-zA-Z0-9]+')); - - $m->connect(':nickname', - array('action' => 'showstream'), - array('nickname' => '[a-zA-Z0-9]{1,64}')); + $m->connect(':nickname', + array('action' => 'showstream'), + array('nickname' => '[a-zA-Z0-9]{1,64}')); - Event::handle('RouterInitialized', array($m)); + Event::handle('RouterInitialized', array($m)); + } return $m; } diff --git a/lib/twitter.php b/lib/twitter.php deleted file mode 100644 index afc3f55ba..000000000 --- a/lib/twitter.php +++ /dev/null @@ -1,311 +0,0 @@ -<?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); -} - -define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1 - -function updateTwitter_user($twitter_id, $screen_name) -{ - $uri = 'http://twitter.com/' . $screen_name; - $fuser = new Foreign_user(); - - $fuser->query('BEGIN'); - - // Dropping down to SQL because regular DB_DataObject udpate stuff doesn't seem - // to work so good with tables that have multiple column primary keys - - // Any time we update the uri for a forein user we have to make sure there - // are no dupe entries first -- unique constraint on the uri column - - $qry = 'UPDATE foreign_user set uri = \'\' WHERE uri = '; - $qry .= '\'' . $uri . '\'' . ' AND service = ' . TWITTER_SERVICE; - - $fuser->query($qry); - - // Update the user - - $qry = 'UPDATE foreign_user SET nickname = '; - $qry .= '\'' . $screen_name . '\'' . ', uri = \'' . $uri . '\' '; - $qry .= 'WHERE id = ' . $twitter_id . ' AND service = ' . TWITTER_SERVICE; - - $fuser->query('COMMIT'); - - $fuser->free(); - unset($fuser); - - return true; -} - -function add_twitter_user($twitter_id, $screen_name) -{ - - $new_uri = 'http://twitter.com/' . $screen_name; - - // Clear out any bad old foreign_users with the new user's legit URL - // This can happen when users move around or fakester accounts get - // repoed, and things like that. - - $luser = new Foreign_user(); - $luser->uri = $new_uri; - $luser->service = TWITTER_SERVICE; - $result = $luser->delete(); - - if (empty($result)) { - common_log(LOG_WARNING, - "Twitter bridge - removed invalid Twitter user squatting on uri: $new_uri"); - } - - $luser->free(); - unset($luser); - - // Otherwise, create a new Twitter user - - $fuser = new Foreign_user(); - - $fuser->nickname = $screen_name; - $fuser->uri = 'http://twitter.com/' . $screen_name; - $fuser->id = $twitter_id; - $fuser->service = TWITTER_SERVICE; - $fuser->created = common_sql_now(); - $result = $fuser->insert(); - - if (empty($result)) { - common_log(LOG_WARNING, - "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name."); - common_log_db_error($fuser, 'INSERT', __FILE__); - } else { - common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id)."); - } - - return $result; -} - -// Creates or Updates a Twitter user -function save_twitter_user($twitter_id, $screen_name) -{ - - // Check to see whether the Twitter user is already in the system, - // and update its screen name and uri if so. - - $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE); - - if (!empty($fuser)) { - - $result = true; - - // Only update if Twitter screen name has changed - - if ($fuser->nickname != $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"); - } - - return $result; - - } else { - return add_twitter_user($twitter_id, $screen_name); - } - - $fuser->free(); - unset($fuser); - - return true; -} - -function is_twitter_bound($notice, $flink) { - - // Check to see if notice should go to Twitter - if (!empty($flink) && ($flink->noticesync & FOREIGN_NOTICE_SEND)) { - - // If it's not a Twitter-style reply, or if the user WANTS to send replies. - if (!preg_match('/^@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) || - ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY)) { - return true; - } - } - - return false; -} - -function broadcast_twitter($notice) -{ - $flink = Foreign_link::getByUserID($notice->profile_id, - TWITTER_SERVICE); - - if (is_twitter_bound($notice, $flink)) { - if (TwitterOAuthClient::isPackedToken($flink->credentials)) { - return broadcast_oauth($notice, $flink); - } else { - return broadcast_basicauth($notice, $flink); - } - } - - return true; -} - -function broadcast_oauth($notice, $flink) { - $user = $flink->getUser(); - $statustxt = format_status($notice); - // Convert !groups to #hashes - $statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt); - $token = TwitterOAuthClient::unpackToken($flink->credentials); - $client = new TwitterOAuthClient($token->key, $token->secret); - $status = null; - - try { - $status = $client->statusesUpdate($statustxt); - } catch (OAuthClientCurlException $e) { - return process_error($e, $flink); - } - - if (empty($status)) { - - // This could represent a failure posting, - // or the Twitter API might just be behaving flakey. - - $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - return false; - } - - // Notice crossed the great divide - - $msg = sprintf('Twitter bridge - posted notice %s to Twitter using OAuth.', - $notice->id); - common_log(LOG_INFO, $msg); - - return true; -} - -function broadcast_basicauth($notice, $flink) -{ - $user = $flink->getUser(); - - $statustxt = format_status($notice); - - $client = new TwitterBasicAuthClient($flink); - $status = null; - - try { - $status = $client->statusesUpdate($statustxt); - } catch (BasicAuthCurlException $e) { - return process_error($e, $flink); - } - - if (empty($status)) { - - $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - $errmsg = sprintf('No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - return false; - } - - $msg = sprintf('Twitter bridge - posted notice %s to Twitter using basic auth.', - $notice->id); - common_log(LOG_INFO, $msg); - - return true; -} - -function process_error($e, $flink) -{ - $user = $flink->getUser(); - $errmsg = $e->getMessage(); - $delivered = false; - - switch($errmsg) { - case 'The requested URL returned error: 401': - $logmsg = sprintf('Twiter bridge - User %1$s (user id: %2$s) has an invalid ' . - 'Twitter screen_name/password combo or an invalid acesss token.', - $user->nickname, $user->id); - $delivered = true; - remove_twitter_link($flink); - break; - case 'The requested URL returned error: 403': - $logmsg = sprintf('Twitter bridge - User %1$s (user id: %2$s) has exceeded ' . - 'his/her Twitter request limit.', - $user->nickname, $user->id); - break; - default: - $logmsg = sprintf('Twitter bridge - cURL error trying to send notice to Twitter ' . - 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: %4$s.', - $user->nickname, $user->id, - $e->getCode(), $e->getMessage()); - break; - } - - common_log(LOG_WARNING, $logmsg); - - return $delivered; -} - -function format_status($notice) -{ - // XXX: Hack to get around PHP cURL's use of @ being a a meta character - return preg_replace('/^@/', ' @', $notice->content); -} - -function remove_twitter_link($flink) -{ - $user = $flink->getUser(); - - common_log(LOG_INFO, 'Removing Twitter bridge Foreign link for ' . - "user $user->nickname (user id: $user->id)."); - - $result = $flink->delete(); - - if (empty($result)) { - common_log(LOG_ERR, 'Could not remove Twitter bridge ' . - "Foreign_link for $user->nickname (user id: $user->id)!"); - common_log_db_error($flink, 'DELETE', __FILE__); - } - - // Notify the user that her Twitter bridge is down - - if (isset($user->email)) { - - $result = mail_twitter_bridge_removed($user); - - if (!$result) { - - $msg = 'Unable to send email to notify ' . - "$user->nickname (user id: $user->id) " . - 'that their Twitter bridge link was ' . - 'removed!'; - - common_log(LOG_WARNING, $msg); - } - } - -} diff --git a/lib/twitterbasicauthclient.php b/lib/twitterbasicauthclient.php deleted file mode 100644 index 1040d72fb..000000000 --- a/lib/twitterbasicauthclient.php +++ /dev/null @@ -1,242 +0,0 @@ -<?php -/** - * StatusNet, the distributed open-source microblogging tool - * - * Class for doing OAuth calls against Twitter - * - * 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 Integration - * @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') && !defined('LACONICA')) { - exit(1); -} - -/** - * Exception wrapper for cURL errors - * - * @category Integration - * @package StatusNet - * @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/ - * - */ -class BasicAuthCurlException extends Exception -{ -} - -/** - * Class for talking to the Twitter API with HTTP Basic Auth. - * - * @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 - * @link http://status.net/ - * - */ -class TwitterBasicAuthClient -{ - var $screen_name = null; - var $password = null; - - /** - * constructor - * - * @param Foreign_link $flink a Foreign_link storing the - * Twitter user's password, etc. - */ - function __construct($flink) - { - $fuser = $flink->getForeignUser(); - $this->screen_name = $fuser->nickname; - $this->password = $flink->credentials; - } - - /** - * Calls Twitter's /statuses/update API method - * - * @param string $status text of the status - * @param int $in_reply_to_status_id optional id of the status it's - * a reply to - * - * @return mixed the status - */ - function statusesUpdate($status, $in_reply_to_status_id = null) - { - $url = 'https://twitter.com/statuses/update.json'; - $params = array('status' => $status, - 'source' => common_config('integration', 'source'), - 'in_reply_to_status_id' => $in_reply_to_status_id); - $response = $this->httpRequest($url, $params); - $status = json_decode($response); - return $status; - } - - /** - * Calls Twitter's /statuses/friends_timeline API method - * - * @param int $since_id show statuses after this id - * @param int $max_id show statuses before this id - * @param int $cnt number of statuses to show - * @param int $page page number - * - * @return mixed an array of statuses - */ - function statusesFriendsTimeline($since_id = null, $max_id = null, - $cnt = null, $page = null) - { - $url = 'https://twitter.com/statuses/friends_timeline.json'; - $params = array('since_id' => $since_id, - 'max_id' => $max_id, - 'count' => $cnt, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->httpRequest($url); - $statuses = json_decode($response); - return $statuses; - } - - /** - * Calls Twitter's /statuses/friends API method - * - * @param int $id id of the user whom you wish to see friends of - * @param int $user_id numerical user id - * @param int $screen_name screen name - * @param int $page page number - * - * @return mixed an array of twitter users and their latest status - */ - function statusesFriends($id = null, $user_id = null, $screen_name = null, - $page = null) - { - $url = "https://twitter.com/statuses/friends.json"; - - $params = array('id' => $id, - 'user_id' => $user_id, - 'screen_name' => $screen_name, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->httpRequest($url); - $friends = json_decode($response); - return $friends; - } - - /** - * Calls Twitter's /statuses/friends/ids API method - * - * @param int $id id of the user whom you wish to see friends of - * @param int $user_id numerical user id - * @param int $screen_name screen name - * @param int $page page number - * - * @return mixed a list of ids, 100 per page - */ - function friendsIds($id = null, $user_id = null, $screen_name = null, - $page = null) - { - $url = "https://twitter.com/friends/ids.json"; - - $params = array('id' => $id, - 'user_id' => $user_id, - 'screen_name' => $screen_name, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->httpRequest($url); - $ids = json_decode($response); - return $ids; - } - - /** - * Make a HTTP request using cURL. - * - * @param string $url Where to make the request - * @param array $params post parameters - * - * @return mixed the request - */ - function httpRequest($url, $params = null, $auth = true) - { - $options = array( - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FAILONERROR => true, - CURLOPT_HEADER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_USERAGENT => 'StatusNet', - CURLOPT_CONNECTTIMEOUT => 120, - CURLOPT_TIMEOUT => 120, - CURLOPT_HTTPAUTH => CURLAUTH_ANY, - CURLOPT_SSL_VERIFYPEER => false, - - // Twitter is strict about accepting invalid "Expect" headers - - CURLOPT_HTTPHEADER => array('Expect:') - ); - - if (isset($params)) { - $options[CURLOPT_POST] = true; - $options[CURLOPT_POSTFIELDS] = $params; - } - - if ($auth) { - $options[CURLOPT_USERPWD] = $this->screen_name . - ':' . $this->password; - } - - $ch = curl_init($url); - curl_setopt_array($ch, $options); - $response = curl_exec($ch); - - if ($response === false) { - $msg = curl_error($ch); - $code = curl_errno($ch); - throw new BasicAuthCurlException($msg, $code); - } - - curl_close($ch); - - return $response; - } - -} diff --git a/lib/twitteroauthclient.php b/lib/twitteroauthclient.php deleted file mode 100644 index bad2b74ca..000000000 --- a/lib/twitteroauthclient.php +++ /dev/null @@ -1,229 +0,0 @@ -<?php -/** - * StatusNet, the distributed open-source microblogging tool - * - * Class for doing OAuth calls against Twitter - * - * 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 Integration - * @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') && !defined('LACONICA')) { - exit(1); -} - -/** - * Class for talking to the Twitter API with OAuth. - * - * @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 - * @link http://status.net/ - * - */ -class TwitterOAuthClient extends OAuthClient -{ - public static $requestTokenURL = 'https://twitter.com/oauth/request_token'; - public static $authorizeURL = 'https://twitter.com/oauth/authorize'; - public static $accessTokenURL = 'https://twitter.com/oauth/access_token'; - - /** - * Constructor - * - * @param string $oauth_token the user's token - * @param string $oauth_token_secret the user's token secret - * - * @return nothing - */ - function __construct($oauth_token = null, $oauth_token_secret = null) - { - $consumer_key = common_config('twitter', 'consumer_key'); - $consumer_secret = common_config('twitter', 'consumer_secret'); - - parent::__construct($consumer_key, $consumer_secret, - $oauth_token, $oauth_token_secret); - } - - // XXX: the following two functions are to support the horrible hack - // of using the credentils field in Foreign_link to store both - // the access token and token secret. This hack should go away with - // 0.9, in which we can make DB changes and add a new column for the - // token itself. - - static function packToken($token) - { - return implode(chr(0), array($token->key, $token->secret)); - } - - static function unpackToken($str) - { - $vals = explode(chr(0), $str); - return new OAuthToken($vals[0], $vals[1]); - } - - static function isPackedToken($str) - { - if (strpos($str, chr(0)) === false) { - return false; - } else { - return true; - } - } - - /** - * Builds a link to Twitter's endpoint for authorizing a request token - * - * @param OAuthToken $request_token token to authorize - * - * @return the link - */ - function getAuthorizeLink($request_token) - { - return parent::getAuthorizeLink(self::$authorizeURL, - $request_token, - common_local_url('twitterauthorization')); - } - - /** - * Calls Twitter's /account/verify_credentials API method - * - * @return mixed the Twitter user - */ - function verifyCredentials() - { - $url = 'https://twitter.com/account/verify_credentials.json'; - $response = $this->oAuthGet($url); - $twitter_user = json_decode($response); - return $twitter_user; - } - - /** - * Calls Twitter's /statuses/update API method - * - * @param string $status text of the status - * @param int $in_reply_to_status_id optional id of the status it's - * a reply to - * - * @return mixed the status - */ - function statusesUpdate($status, $in_reply_to_status_id = null) - { - $url = 'https://twitter.com/statuses/update.json'; - $params = array('status' => $status, - 'in_reply_to_status_id' => $in_reply_to_status_id); - $response = $this->oAuthPost($url, $params); - $status = json_decode($response); - return $status; - } - - /** - * Calls Twitter's /statuses/friends_timeline API method - * - * @param int $since_id show statuses after this id - * @param int $max_id show statuses before this id - * @param int $cnt number of statuses to show - * @param int $page page number - * - * @return mixed an array of statuses - */ - function statusesFriendsTimeline($since_id = null, $max_id = null, - $cnt = null, $page = null) - { - - $url = 'https://twitter.com/statuses/friends_timeline.json'; - $params = array('since_id' => $since_id, - 'max_id' => $max_id, - 'count' => $cnt, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->oAuthGet($url); - $statuses = json_decode($response); - return $statuses; - } - - /** - * Calls Twitter's /statuses/friends API method - * - * @param int $id id of the user whom you wish to see friends of - * @param int $user_id numerical user id - * @param int $screen_name screen name - * @param int $page page number - * - * @return mixed an array of twitter users and their latest status - */ - function statusesFriends($id = null, $user_id = null, $screen_name = null, - $page = null) - { - $url = "https://twitter.com/statuses/friends.json"; - - $params = array('id' => $id, - 'user_id' => $user_id, - 'screen_name' => $screen_name, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->oAuthGet($url); - $friends = json_decode($response); - return $friends; - } - - /** - * Calls Twitter's /statuses/friends/ids API method - * - * @param int $id id of the user whom you wish to see friends of - * @param int $user_id numerical user id - * @param int $screen_name screen name - * @param int $page page number - * - * @return mixed a list of ids, 100 per page - */ - function friendsIds($id = null, $user_id = null, $screen_name = null, - $page = null) - { - $url = "https://twitter.com/friends/ids.json"; - - $params = array('id' => $id, - 'user_id' => $user_id, - 'screen_name' => $screen_name, - 'page' => $page); - $qry = http_build_query($params); - - if (!empty($qry)) { - $url .= "?$qry"; - } - - $response = $this->oAuthGet($url); - $ids = json_decode($response); - return $ids; - } - -} diff --git a/lib/unqueuemanager.php b/lib/unqueuemanager.php index 6cfe5bcbd..72dbc4eed 100644 --- a/lib/unqueuemanager.php +++ b/lib/unqueuemanager.php @@ -48,17 +48,6 @@ class UnQueueManager jabber_public_notice($notice); } break; - case 'twitter': - if ($this->_isLocal($notice)) { - broadcast_twitter($notice); - } - break; - case 'facebook': - if ($this->_isLocal($notice)) { - require_once INSTALLDIR . '/lib/facebookutil.php'; - return facebookBroadcastNotice($notice); - } - break; case 'ping': if ($this->_isLocal($notice)) { require_once INSTALLDIR . '/lib/ping.php'; @@ -77,7 +66,7 @@ class UnQueueManager break; default: if (Event::handle('UnqueueHandleNotice', array(&$notice, $queue))) { - throw ServerException("UnQueueManager: Unknown queue: $queue"); + throw new ServerException("UnQueueManager: Unknown queue: $queue"); } } } diff --git a/lib/util.php b/lib/util.php index be10647fc..d159c583e 100644 --- a/lib/util.php +++ b/lib/util.php @@ -51,13 +51,23 @@ function common_init_locale($language=null) function common_init_language() { mb_internal_encoding('UTF-8'); + + // gettext seems very picky... We first need to setlocale() + // to a locale which _does_ exist on the system, and _then_ + // we can set in another locale that may not be set up + // (say, ga_ES for Galego/Galician) it seems to take it. + common_init_locale("en_US"); + $language = common_language(); - // So we don't have to make people install the gettext locales $locale_set = common_init_locale($language); - bindtextdomain("statusnet", common_config('site','locale_path')); + setlocale(LC_CTYPE, 'C'); + + // So we don't have to make people install the gettext locales + $path = common_config('site','locale_path'); + bindtextdomain("statusnet", $path); bind_textdomain_codeset("statusnet", "UTF-8"); textdomain("statusnet"); - setlocale(LC_CTYPE, 'C'); + if(!$locale_set) { common_log(LOG_INFO, 'Language requested:' . $language . ' - locale could not be set. Perhaps that system locale is not installed.', __FILE__); } @@ -391,7 +401,7 @@ function common_render_content($text, $notice) { $r = common_render_text($text); $id = $notice->profile_id; - $r = preg_replace('/(^|[\s\.\,\:\;]+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r); + $r = preg_replace('/(^|\s+)@(['.NICKNAME_FMT.']{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r); $r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r); $r = preg_replace('/(^|[\s\.\,\:\;]+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r); $r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r); @@ -771,12 +781,18 @@ function common_path($relative, $ssl=false) if (is_string(common_config('site', 'sslserver')) && mb_strlen(common_config('site', 'sslserver')) > 0) { $serverpart = common_config('site', 'sslserver'); - } else { + } else if (common_config('site', 'server')) { $serverpart = common_config('site', 'server'); + } else { + common_log(LOG_ERR, 'Site Sever not configured, unable to determine site name.'); } } else { $proto = 'http'; - $serverpart = common_config('site', 'server'); + if (common_config('site', 'server')) { + $serverpart = common_config('site', 'server'); + } else { + common_log(LOG_ERR, 'Site Sever not configured, unable to determine site name.'); + } } return $proto.'://'.$serverpart.'/'.$pathpart.$relative; @@ -896,8 +912,6 @@ function common_broadcast_notice($notice, $remote=false) function common_enqueue_notice($notice) { static $localTransports = array('omb', - 'twitter', - 'facebook', 'ping'); static $allTransports = array('sms', 'plugin'); diff --git a/lib/xrdsoutputter.php b/lib/xrdsoutputter.php new file mode 100644 index 000000000..4b77ed5a3 --- /dev/null +++ b/lib/xrdsoutputter.php @@ -0,0 +1,96 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Low-level generator for HTML + * + * 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 Output + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2008 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); +} + +require_once INSTALLDIR.'/lib/xmloutputter.php'; + +/** + * Low-level generator for XRDS XML + * + * @category Output + * @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/ + * + * @see Action + * @see XMLOutputter + */ +class XRDSOutputter extends XMLOutputter +{ + public function startXRDS() + { + header('Content-Type: application/xrds+xml'); + $this->startXML(); + $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds')); + } + + public function endXRDS() + { + $this->elementEnd('XRDS'); + $this->endXML(); + } + + /** + * Show service. + * + * @param string $type XRDS type + * @param string $uri URI + * @param array $params type parameters, null by default + * @param array $sigs type signatures, null by default + * @param string $localId local ID, null by default + * + * @return void + */ + function showXrdsService($type, $uri, $params=null, $sigs=null, $localId=null) + { + $this->elementStart('Service'); + if ($uri) { + $this->element('URI', null, $uri); + } + $this->element('Type', null, $type); + if ($params) { + foreach ($params as $param) { + $this->element('Type', null, $param); + } + } + if ($sigs) { + foreach ($sigs as $sig) { + $this->element('Type', null, $sig); + } + } + if ($localId) { + $this->element('LocalID', null, $localId); + } + $this->elementEnd('Service'); + } +} |