diff --git a/README b/README index 0ee83a9ee8..d69ec8fdd0 100644 --- a/README +++ b/README @@ -683,8 +683,9 @@ instructions; read to the end first before trying them. 6. Move your StatusNet directory to a backup spot, like "statusnet.bak". 7. Unpack your StatusNet 0.9.5 tarball and move it to "statusnet" or wherever your code used to be. -8. Copy the config.php file and avatar directory from your old - directory to your new directory. +8. Copy the config.php file and the contents of the avatar/, background/, + file/, and local/ subdirectories from your old directory to your new + directory. 9. Copy htaccess.sample to .htaccess in the new directory. Change the RewriteBase to use the correct path. 10. Rebuild the database. (You can safely skip this step and go to #12 @@ -1499,6 +1500,33 @@ disallow: Array of (virtual) directories to disallow. Default is 'main', 'search', 'message', 'settings', 'admin'. Ignored when site is private, in which case the entire site ('/') is disallowed. +api +--- + +Options for the Twitter-like API. + +realm: HTTP Basic Auth realm (see http://tools.ietf.org/html/rfc2617 + for details). Some third-party tools like ping.fm want this to be + 'Identi.ca API', so set it to that if you want to. default = null, + meaning 'something based on the site name'. + +nofollow +-------- + +We optionally put 'rel="nofollow"' on some links in some pages. The +following configuration settings let you fine-tune how or when things +are nofollowed. See http://en.wikipedia.org/wiki/Nofollow for more +information on what 'nofollow' means. + +subscribers: whether to nofollow links to subscribers on the profile + and personal pages. Default is true. +members: links to members on the group page. Default true. +peopletag: links to people listed in the peopletag page. Default true. +external: external links in notices. One of three values: 'sometimes', + 'always', 'never'. If 'sometimes', then external links are not + nofollowed on profile, notice, and favorites page. Default is + 'sometimes'. + url --- diff --git a/actions/apidirectmessage.php b/actions/apidirectmessage.php index e7ea38dfa1..4e2ec5eb08 100644 --- a/actions/apidirectmessage.php +++ b/actions/apidirectmessage.php @@ -74,6 +74,7 @@ class ApiDirectMessageAction extends ApiAuthAction $this->user = $this->auth_user; if (empty($this->user)) { + // TRANS: Client error given when a user was not found (404). $this->clientError(_('No such user.'), 404, $this->format); return; } @@ -86,10 +87,12 @@ class ApiDirectMessageAction extends ApiAuthAction // Action was called by /api/direct_messages/sent.format $this->title = sprintf( + // TRANS: %s is a user nickname. _("Direct messages from %s"), $this->user->nickname ); $this->subtitle = sprintf( + // TRANS: %s is a user nickname. _("All the direct messages sent from %s"), $this->user->nickname ); @@ -98,10 +101,12 @@ class ApiDirectMessageAction extends ApiAuthAction $this->id = "tag:$taguribase:SentDirectMessages:" . $this->user->id; } else { $this->title = sprintf( + // TRANS: %s is a user nickname. _("Direct messages to %s"), $this->user->nickname ); $this->subtitle = sprintf( + // TRANS: %s is a user nickname. _("All the direct messages sent to %s"), $this->user->nickname ); @@ -153,6 +158,7 @@ class ApiDirectMessageAction extends ApiAuthAction $this->showJsonDirectMessages(); break; default: + // TRANS: Client error given when an API method was not found (404). $this->clientError(_('API method not found.'), $code = 404); break; } diff --git a/actions/apidirectmessagenew.php b/actions/apidirectmessagenew.php index 44e205ebb0..c126cd312f 100644 --- a/actions/apidirectmessagenew.php +++ b/actions/apidirectmessagenew.php @@ -49,7 +49,6 @@ require_once INSTALLDIR . '/lib/apiauth.php'; * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class ApiDirectMessageNewAction extends ApiAuthAction { var $other = null; @@ -63,7 +62,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction * @return boolean success flag * */ - function prepare($args) { parent::prepare($args); @@ -99,7 +97,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction * * @return void */ - function handle($args) { parent::handle($args); @@ -116,6 +113,7 @@ class ApiDirectMessageNewAction extends ApiAuthAction if (empty($this->content)) { $this->clientError( + // TRANS: Client error (406). _('No message text!'), 406, $this->format @@ -123,9 +121,10 @@ class ApiDirectMessageNewAction extends ApiAuthAction } else { $content_shortened = common_shorten_links($this->content); if (Message::contentTooLong($content_shortened)) { + // TRANS: Client error displayed when message content is too long. + // TRANS: %d is the maximum number of characters for a message. $this->clientError( - sprintf( - _('That\'s too long. Max message size is %d chars.'), + sprintf(_m('That\'s too long. Maximum message size is %d character.', 'That\'s too long. Maximum message size is %d characters.', Message::maxContent()), Message::maxContent() ), 406, @@ -136,10 +135,12 @@ class ApiDirectMessageNewAction extends ApiAuthAction } if (empty($this->other)) { + // TRANS: Client error displayed if a recipient user could not be found (403). $this->clientError(_('Recipient user not found.'), 403, $this->format); return; } else if (!$this->user->mutuallySubscribed($this->other)) { $this->clientError( + // TRANS: Client error displayed trying to direct message another user who's not a friend (403). _('Can\'t send direct messages to users who aren\'t your friend.'), 403, $this->format @@ -149,10 +150,9 @@ class ApiDirectMessageNewAction extends ApiAuthAction // Note: sending msgs to yourself is allowed by Twitter - $errmsg = 'Don\'t send a message to yourself; ' . - 'just say it to yourself quietly instead.'; - - $this->clientError(_($errmsg), 403, $this->format); + // TRANS: Client error displayed trying to direct message self (403). + $this->clientError(_('Do not send a message to yourself; ' . + 'just say it to yourself quietly instead.'), 403, $this->format); return; } @@ -176,6 +176,4 @@ class ApiDirectMessageNewAction extends ApiAuthAction $this->showSingleJsondirectMessage($message); } } - } - diff --git a/actions/pathsadminpanel.php b/actions/pathsadminpanel.php index 0c83aa29ec..e073b0a2aa 100644 --- a/actions/pathsadminpanel.php +++ b/actions/pathsadminpanel.php @@ -92,16 +92,17 @@ class PathsadminpanelAction extends AdminPanelAction function saveSettings() { static $settings = array( - 'site' => array('path', 'locale_path', 'ssl', 'sslserver'), - 'theme' => array('server', 'dir', 'path'), - 'avatar' => array('server', 'dir', 'path'), - 'background' => array('server', 'dir', 'path') - ); + 'site' => array('path', 'locale_path', 'ssl', 'sslserver'), + 'theme' => array('server', 'dir', 'path', 'sslserver', 'sslpath'), + 'avatar' => array('server', 'dir', 'path'), + 'background' => array('server', 'dir', 'path', 'sslserver', 'sslpath'), + 'attachments' => array('server', 'dir', 'path', 'sslserver', 'sslpath') + ); - // XXX: If we're only going to have one boolean on thi page we - // can remove some of the boolean processing code --Z + // XXX: If we're only going to have one boolean on thi page we + // can remove some of the boolean processing code --Z - static $booleans = array('site' => array('fancy')); + static $booleans = array('site' => array('fancy')); $values = array(); @@ -131,13 +132,13 @@ class PathsadminpanelAction extends AdminPanelAction } } - foreach ($booleans as $section => $parts) { - foreach ($parts as $setting) { + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { Config::save($section, $setting, $values[$section][$setting]); } - } + } - $config->query('COMMIT'); + $config->query('COMMIT'); return; } @@ -230,11 +231,11 @@ class PathsAdminPanelForm extends AdminForm function formData() { - $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale')); + $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale')); $this->out->element('legend', null, _('Site'), 'site'); $this->out->elementStart('ul', 'form_data'); - $this->li(); + $this->li(); $this->input('server', _('Server'), _('Site\'s server hostname.')); $this->unli(); @@ -243,14 +244,14 @@ class PathsAdminPanelForm extends AdminForm $this->unli(); $this->li(); - $this->input('locale_path', _('Path to locales'), _('Directory path to locales'), 'site'); + $this->input('locale_path', _('Locale Directory'), _('Directory path to locales'), 'site'); $this->unli(); - $this->li(); + $this->li(); $this->out->checkbox('fancy', _('Fancy URLs'), (bool) $this->value('fancy'), _('Use fancy (more readable and memorable) URLs?')); - $this->unli(); + $this->unli(); $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); @@ -261,15 +262,23 @@ class PathsAdminPanelForm extends AdminForm $this->out->elementStart('ul', 'form_data'); $this->li(); - $this->input('server', _('Theme server'), 'Server for themes', 'theme'); + $this->input('server', _('Server'), _('Server for themes'), 'theme'); $this->unli(); $this->li(); - $this->input('path', _('Theme path'), 'Web path to themes', 'theme'); + $this->input('path', _('Path'), _('Web path to themes'), 'theme'); $this->unli(); $this->li(); - $this->input('dir', _('Theme directory'), 'Directory where themes are located', 'theme'); + $this->input('sslserver', _('SSL server'), _('SSL server for themes (default: SSL server)'), 'theme'); + $this->unli(); + + $this->li(); + $this->input('sslpath', _('SSL path'), _('SSL path to themes (default: /theme/)'), 'theme'); + $this->unli(); + + $this->li(); + $this->input('dir', _('Directory'), _('Directory where themes are located'), 'theme'); $this->unli(); $this->out->elementEnd('ul'); @@ -297,20 +306,57 @@ class PathsAdminPanelForm extends AdminForm $this->out->elementEnd('fieldset'); $this->out->elementStart('fieldset', array('id' => - 'settings_design_background-paths')); + 'settings_design_background-paths')); $this->out->element('legend', null, _('Backgrounds')); $this->out->elementStart('ul', 'form_data'); $this->li(); - $this->input('server', _('Background server'), 'Server for backgrounds', 'background'); + $this->input('server', _('Server'), 'Server for backgrounds', 'background'); $this->unli(); $this->li(); - $this->input('path', _('Background path'), 'Web path to backgrounds', 'background'); + $this->input('path', _('Path'), 'Web path to backgrounds', 'background'); $this->unli(); $this->li(); - $this->input('dir', _('Background directory'), 'Directory where backgrounds are located', 'background'); + $this->input('sslserver', _('SSL server'), 'Server for backgrounds on SSL pages', 'background'); + $this->unli(); + + $this->li(); + $this->input('sslpath', _('SSL path'), 'Web path to backgrounds on SSL pages', 'background'); + $this->unli(); + + $this->li(); + $this->input('dir', _('Directory'), 'Directory where backgrounds are located', 'background'); + $this->unli(); + + $this->out->elementEnd('ul'); + $this->out->elementEnd('fieldset'); + + $this->out->elementStart('fieldset', array('id' => + 'settings_design_attachments-paths')); + + $this->out->element('legend', null, _('Attachments')); + $this->out->elementStart('ul', 'form_data'); + + $this->li(); + $this->input('server', _('Server'), 'Server for attachments', 'attachments'); + $this->unli(); + + $this->li(); + $this->input('path', _('Path'), 'Web path to attachments', 'attachments'); + $this->unli(); + + $this->li(); + $this->input('sslserver', _('SSL server'), 'Server for attachments on SSL pages', 'attachments'); + $this->unli(); + + $this->li(); + $this->input('sslpath', _('SSL path'), 'Web path to attachments on SSL pages', 'attachments'); + $this->unli(); + + $this->li(); + $this->input('dir', _('Directory'), 'Directory where attachments are located', 'attachments'); $this->unli(); $this->out->elementEnd('ul'); @@ -320,12 +366,11 @@ class PathsAdminPanelForm extends AdminForm $this->out->element('legend', null, _('SSL')); $this->out->elementStart('ul', 'form_data'); $this->li(); + $ssl = array('never' => _('Never'), 'sometimes' => _('Sometimes'), 'always' => _('Always')); - common_debug("site ssl = " . $this->value('site', 'ssl')); - $this->out->dropdown('site-ssl', _('Use SSL'), $ssl, _('When to use SSL'), false, $this->value('ssl', 'site')); @@ -349,7 +394,7 @@ class PathsAdminPanelForm extends AdminForm function formActions() { $this->out->submit('save', _('Save'), 'submit', - 'save', _('Save paths')); + 'save', _('Save paths')); } /** @@ -370,5 +415,4 @@ class PathsAdminPanelForm extends AdminForm { $this->out->input("$section-$setting", $title, $this->value($setting, $section), $instructions); } - } diff --git a/actions/showfavorites.php b/actions/showfavorites.php index d8042e91c7..77b73711d2 100644 --- a/actions/showfavorites.php +++ b/actions/showfavorites.php @@ -227,7 +227,7 @@ class ShowfavoritesAction extends OwnerDesignAction function showContent() { - $nl = new NoticeList($this->notice, $this); + $nl = new FavoritesNoticeList($this->notice, $this); $cnt = $nl->show(); if (0 == $cnt) { @@ -244,3 +244,15 @@ class ShowfavoritesAction extends OwnerDesignAction } } +class FavoritesNoticeList extends NoticeList +{ + function newListItem($notice) + { + return new FavoritesNoticeListItem($notice, $this->out); + } +} + +// All handled by superclass +class FavoritesNoticeListItem extends DoFollowListItem +{ +} diff --git a/actions/shownotice.php b/actions/shownotice.php index 93d056acac..5b3ec93de1 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -305,7 +305,7 @@ class ShownoticeAction extends OwnerDesignAction } } -class SingleNoticeItem extends NoticeListItem +class SingleNoticeItem extends DoFollowListItem { /** * recipe function for displaying a single notice. diff --git a/actions/showstream.php b/actions/showstream.php index b2082eb4b5..8178dafb17 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -269,7 +269,7 @@ class ProfileNoticeList extends NoticeList } } -class ProfileNoticeListItem extends NoticeListItem +class ProfileNoticeListItem extends DoFollowListItem { function showAuthor() { diff --git a/install.php b/install.php index 158d51fa33..9b0d19882c 100644 --- a/install.php +++ b/install.php @@ -45,14 +45,62 @@ require INSTALLDIR . '/lib/installer.php'; * Helper class for building form */ class Posted { + /** + * HTML-friendly escaped string for the POST param of given name, or empty. + * @param string $name + * @return string + */ function value($name) + { + return htmlspecialchars($this->string($name)); + } + + /** + * The given POST parameter value, forced to a string. + * Missing value will give ''. + * + * @param string $name + * @return string + */ + function string($name) + { + return strval($this->raw($name)); + } + + /** + * The given POST parameter value, in its original form. + * Magic quotes are stripped, if provided. + * Missing value will give null. + * + * @param string $name + * @return mixed + */ + function raw($name) { if (isset($_POST[$name])) { - return htmlspecialchars(strval($_POST[$name])); + return $this->dequote($_POST[$name]); } else { - return ''; + return null; } } + + /** + * If necessary, strip magic quotes from the given value. + * + * @param mixed $val + * @return mixed + */ + function dequote($val) + { + if (get_magic_quotes_gpc()) { + if (is_string($val)) { + return stripslashes($val); + } else if (is_array($val)) { + return array_map(array($this, 'dequote'), $val); + } + } + return $val; + } } /** @@ -107,11 +155,7 @@ class WebInstaller extends Installer global $dbModules; $post = new Posted(); $dbRadios = ''; - if (isset($_POST['dbtype'])) { - $dbtype = $_POST['dbtype']; - } else { - $dbtype = null; - } + $dbtype = $post->raw('dbtype'); foreach (self::$dbModules as $type => $info) { if ($this->checkExtension($info['check_module'])) { if ($dbtype == null || $dbtype == $type) { @@ -245,19 +289,20 @@ STR; */ function prepare() { - $this->host = $_POST['host']; - $this->dbtype = $_POST['dbtype']; - $this->database = $_POST['database']; - $this->username = $_POST['dbusername']; - $this->password = $_POST['dbpassword']; - $this->sitename = $_POST['sitename']; - $this->fancy = !empty($_POST['fancy']); + $post = new Posted(); + $this->host = $post->string('host'); + $this->dbtype = $post->string('dbtype'); + $this->database = $post->string('database'); + $this->username = $post->string('dbusername'); + $this->password = $post->string('dbpassword'); + $this->sitename = $post->string('sitename'); + $this->fancy = (bool)$post->string('fancy'); - $this->adminNick = strtolower($_POST['admin_nickname']); - $this->adminPass = $_POST['admin_password']; - $adminPass2 = $_POST['admin_password2']; - $this->adminEmail = $_POST['admin_email']; - $this->adminUpdates = $_POST['admin_updates']; + $this->adminNick = strtolower($post->string('admin_nickname')); + $this->adminPass = $post->string('admin_password'); + $adminPass2 = $post->string('admin_password2'); + $this->adminEmail = $post->string('admin_email'); + $this->adminUpdates = $post->string('admin_updates'); $this->server = $_SERVER['HTTP_HOST']; $this->path = substr(dirname($_SERVER['PHP_SELF']), 1); diff --git a/lib/default.php b/lib/default.php index 279a7a3d11..a1dac7b5e1 100644 --- a/lib/default.php +++ b/lib/default.php @@ -319,7 +319,8 @@ $default = 'nofollow' => array('subscribers' => true, 'members' => true, - 'peopletag' => true), + 'peopletag' => true, + 'external' => 'sometimes'), // Options: 'sometimes', 'never', default = 'sometimes' 'url' => array('shortener' => 'ur1.ca', 'maxlength' => 25, diff --git a/lib/dofollowlistitem.php b/lib/dofollowlistitem.php new file mode 100644 index 0000000000..80e2d0b0a7 --- /dev/null +++ b/lib/dofollowlistitem.php @@ -0,0 +1,88 @@ +. + * + * @category UI + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/noticelist.php'; + +/** + * StatusNet, the distributed open-source microblogging tool + * + * Widget superclass for notice list items that remove rel=nofollow + * + * When nofollow|external = 'sometimes', notices get rendered and saved + * with rel=nofollow for external links. We want to remove that relationship + * on some pages (profile, single notice, faves). This superclass for + * some noticelistitems will strip that bit of code out when showing + * notice content + * + * @category UI + * @package StatusNet + * @author Evan Prodromou + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +class DoFollowListItem extends NoticeListItem +{ + /** + * show the content of the notice + * + * Trims out the rel=nofollow for external links + * if nofollow|external = 'sometimes' + * + * @return void + */ + + function showContent() + { + // FIXME: URL, image, video, audio + $this->out->elementStart('p', array('class' => 'entry-content')); + + if (!empty($this->notice->rendered)) { + $html = $this->notice->rendered; + } else { + $html = common_render_content($this->notice->content, $this->notice); + } + + if (common_config('nofollow', 'external') == 'sometimes') { + // remove the nofollow part + // XXX: cache the results here + + $html = preg_replace('/rel="(.*)nofollow ?/', 'rel="\1', $html); + } + + $this->out->raw($html); + + $this->out->elementEnd('p'); + } +} \ No newline at end of file diff --git a/lib/installer.php b/lib/installer.php index a5588fbed6..441f726606 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -355,6 +355,30 @@ abstract class Installer return $schema; } + /** + * Return a parseable PHP literal for the given value. + * This will include quotes for strings, etc. + * + * @param mixed $val + * @return string + */ + function phpVal($val) + { + return var_export($val, true); + } + + /** + * Return an array of parseable PHP literal for the given values. + * These will include quotes for strings, etc. + * + * @param mixed $val + * @return array + */ + function phpVals($map) + { + return array_map(array($this, 'phpVal'), $map); + } + /** * Write a stock configuration file. * @@ -364,24 +388,32 @@ abstract class Installer */ function writeConf() { + $vals = $this->phpVals(array( + 'sitename' => $this->sitename, + 'server' => $this->server, + 'path' => $this->path, + 'db_database' => $this->db['database'], + 'db_type' => $this->db['type'], + )); + // assemble configuration file in a string $cfg = "sitename}';\n\n". + "\$config['site']['name'] = {$vals['sitename']};\n\n". // site location - "\$config['site']['server'] = '{$this->server}';\n". - "\$config['site']['path'] = '{$this->path}'; \n\n". + "\$config['site']['server'] = {$vals['server']};\n". + "\$config['site']['path'] = {$vals['path']}; \n\n". // checks if fancy URLs are enabled ($this->fancy ? "\$config['site']['fancy'] = true;\n\n":''). // database - "\$config['db']['database'] = '{$this->db['database']}';\n\n". + "\$config['db']['database'] = {$vals['db_database']};\n\n". ($this->db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":''). - "\$config['db']['type'] = '{$this->db['type']}';\n\n"; + "\$config['db']['type'] = {$vals['db_type']};\n\n"; // Normalize line endings for Windows servers $cfg = str_replace("\n", PHP_EOL, $cfg); diff --git a/lib/util.php b/lib/util.php index 85d7c72f45..1d7cffe419 100644 --- a/lib/util.php +++ b/lib/util.php @@ -875,7 +875,8 @@ function common_linkify($url) { $longurl = $url; } } - $attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external'); + + $attrs = array('href' => $canon, 'title' => $longurl); $is_attachment = false; $attachment_id = null; @@ -911,6 +912,16 @@ function common_linkify($url) { $attrs['id'] = "attachment-{$attachment_id}"; } + // Whether to nofollow + + $nf = common_config('nofollow', 'external'); + + if ($nf == 'never') { + $attrs['rel'] = 'external'; + } else { + $attrs['rel'] = 'nofollow external'; + } + return XMLStringer::estring('a', $attrs, $url); } diff --git a/plugins/DirectionDetector/DirectionDetectorPlugin.php b/plugins/DirectionDetector/DirectionDetectorPlugin.php index 1473c386fa..4a38f390f1 100644 --- a/plugins/DirectionDetector/DirectionDetectorPlugin.php +++ b/plugins/DirectionDetector/DirectionDetectorPlugin.php @@ -81,6 +81,9 @@ class DirectionDetectorPlugin extends Plugin { * @return boolean */ public static function startsWithRTLCharacter($str){ + if (strlen($str) < 1) { + return false; + } if( is_array($cc = self::utf8ToUnicode(mb_substr($str, 0, 1, 'utf-8'))) ) $cc = $cc[0]; else diff --git a/plugins/OStatus/tests/FeedDiscoveryTest.php b/plugins/OStatus/tests/FeedDiscoveryTest.php index 3be4bf736c..ef17efe7cf 100644 --- a/plugins/OStatus/tests/FeedDiscoveryTest.php +++ b/plugins/OStatus/tests/FeedDiscoveryTest.php @@ -75,9 +75,10 @@ END; '
',
                            'http://example.com/feed/rss'),
+                     // This one can't resolve correctly; relative link is bogus.
                      array('http://example.com/relative/link3',
                            '',
-                           'http://example.com/feed/rss'),
+                           'http:/feed/rss'),
                      array('http://example.com/base/link1',
                            '',
                            'http://target.example.com/feed/rss'),
                      array('http://example.com/base/link2',
                            '',
                            'http://target.example.com/feed/rss'),
+                     // This one can't resolve; relative link is bogus.
                      array('http://example.com/base/link3',
                            '',
-                           'http://target.example.com/feed/rss'),
+                           'http:/feed/rss'),
                      // Trick question! There's a  but no href on it
                      array('http://example.com/relative/fauxbase',
                            '',
diff --git a/plugins/OStatus/tests/remote-tests.php b/plugins/OStatus/tests/remote-tests.php
index 64c60a8a4c..7888ec7c5d 100644
--- a/plugins/OStatus/tests/remote-tests.php
+++ b/plugins/OStatus/tests/remote-tests.php
@@ -4,7 +4,7 @@ if (php_sapi_name() != 'cli') {
     die('not for web');
 }
 
-define('TIMEOUT', 60); // ssslllloowwwww salmon if queues are off
+define('HTTP_TIMEOUT', 60); // ssslllloowwwww salmon if queues are off
 
 define('INSTALLDIR', dirname(dirname(dirname(dirname(__FILE__)))));
 set_include_path(INSTALLDIR . '/extlib' . PATH_SEPARATOR . get_include_path());
@@ -63,14 +63,15 @@ class OStatusTester extends TestBase
     /**
      * @param string $a base URL of test site A (eg http://localhost/mublog)
      * @param string $b base URL of test site B (eg http://localhost/mublog2)
+     * @param int $timeout HTTP timeout value (needs to be long if Salmon is slow)
      */
-    function __construct($a, $b) {
+    function __construct($a, $b, $timeout=60) {
         $this->a = $a;
         $this->b = $b;
 
         $base = 'test' . mt_rand(1, 1000000);
-        $this->pub = new SNTestClient($this->a, 'pub' . $base, 'pw-' . mt_rand(1, 1000000));
-        $this->sub = new SNTestClient($this->b, 'sub' . $base, 'pw-' . mt_rand(1, 1000000));
+        $this->pub = new SNTestClient($this->a, 'pub' . $base, 'pw-' . mt_rand(1, 1000000), $timeout);
+        $this->sub = new SNTestClient($this->b, 'sub' . $base, 'pw-' . mt_rand(1, 1000000), $timeout);
     }
 
     function run()
@@ -166,11 +167,12 @@ class OStatusTester extends TestBase
 
 class SNTestClient extends TestBase
 {
-    function __construct($base, $username, $password)
+    function __construct($base, $username, $password, $timeout=60)
     {
         $this->basepath = $base;
         $this->username = $username;
         $this->password = $password;
+        $this->timeout = $timeout;
 
         $this->fullname = ucfirst($username) . ' Smith';
         $this->homepage = 'http://example.org/' . $username;
@@ -190,7 +192,7 @@ class SNTestClient extends TestBase
     {
         $url = $this->basepath . '/' . $path;
 
-        $http = new HTTP_Request2($url, 'POST', array('timeout' => TIMEOUT));
+        $http = new HTTP_Request2($url, 'POST', array('timeout' => $this->timeout));
         if ($auth) {
             $http->setAuth($this->username, $this->password, HTTP_Request2::AUTH_BASIC);
         }
@@ -217,7 +219,7 @@ class SNTestClient extends TestBase
     protected function web($path, $form, $params=array())
     {
         $url = $this->basepath . '/' . $path;
-        $http = new HTTP_Request2($url, 'GET', array('timeout' => TIMEOUT));
+        $http = new HTTP_Request2($url, 'GET', array('timeout' => $this->timeout));
         $response = $http->send();
 
         $dom = $this->checkWeb($url, 'GET', $response);
@@ -534,10 +536,29 @@ class SNTestClient extends TestBase
 
 }
 
-$args = array_slice($_SERVER['argv'], 1);
+// @fixme switch to commandline.inc?
+$timeout = HTTP_TIMEOUT;
+
+$args = array();
+$options = array();
+foreach (array_slice($_SERVER['argv'], 1) as $arg) {
+    if (substr($arg, 0, 2) == '--') {
+        $bits = explode('=', substr($arg, 2), 2);
+        if (count($bits == 2)) {
+            list($key, $val) = $bits;
+            $options[$key] = $val;
+        } else {
+            list($key) = $bits;
+            $options[$key] = true;
+        }
+    } else {
+        $args[] = $arg;
+    }
+}
 if (count($args) < 2) {
     print << 
+remote-tests.php [options]  
+  --timeout=## change HTTP timeout from default {$timeout}s
   url1: base URL of a StatusNet instance
   url2: base URL of another StatusNet instance
 
@@ -551,6 +572,9 @@ exit(1);
 
 $a = $args[0];
 $b = $args[1];
+if (isset($options['timeout'])) {
+    $timeout = intval($options['timeout']);
+}
 
-$tester = new OStatusTester($a, $b);
+$tester = new OStatusTester($a, $b, $timeout);
 $tester->run();
diff --git a/plugins/OpenExternalLinkTarget/OpenExternalLinkTargetPlugin.php b/plugins/OpenExternalLinkTarget/OpenExternalLinkTargetPlugin.php
index 611f5e5c7b..f0f6144e01 100644
--- a/plugins/OpenExternalLinkTarget/OpenExternalLinkTargetPlugin.php
+++ b/plugins/OpenExternalLinkTarget/OpenExternalLinkTargetPlugin.php
@@ -40,7 +40,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://status.net/
  */
-
 class OpenExternalLinkTargetPlugin extends Plugin
 {
     function onEndShowScripts($action)
@@ -57,7 +56,7 @@ class OpenExternalLinkTargetPlugin extends Plugin
                             'author' => 'Sarven Capadisli',
                             'homepage' => 'http://status.net/wiki/Plugin:OpenExternalLinkTarget',
                             'rawdescription' =>
-                            _m('Opens external links (e.g., with rel=external) on a new window or tab.'));
+                            _m('Opens external links (i.e. with rel=external) on a new window or tab.'));
         return true;
     }
 }
diff --git a/plugins/OpenID/openidadminpanel.php b/plugins/OpenID/openidadminpanel.php
index ce4806cc89..38df183fef 100644
--- a/plugins/OpenID/openidadminpanel.php
+++ b/plugins/OpenID/openidadminpanel.php
@@ -257,7 +257,7 @@ class OpenIDAdminPanelForm extends AdminForm
         $this->out->checkbox(
             'openidonly', _m('Enable OpenID-only mode'),
             (bool) $this->value('openidonly', 'site'),
-            _m('Require all users to login via OpenID. WARNING: disables password authentication for all users!'),
+            _m('Require all users to login via OpenID. Warning: disables password authentication for all users!'),
             'true'
         );
         $this->unli();
diff --git a/tests/URLDetectionTest.php b/tests/URLDetectionTest.php
index d83f9faf58..eac7ba3f5c 100644
--- a/tests/URLDetectionTest.php
+++ b/tests/URLDetectionTest.php
@@ -29,73 +29,73 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
                      array('not a link :: no way',
                            'not a link :: no way'),
                      array('link http://www.somesite.com/xyz/35637563@N00/52803365/ link',
-                           'link http://www.somesite.com/xyz/35637563@N00/52803365/ link'),
+                           'link http://www.somesite.com/xyz/35637563@N00/52803365/ link'),
                      array('http://127.0.0.1',
-                           'http://127.0.0.1'),
+                           'http://127.0.0.1'),
                      array('127.0.0.1',
-                           '127.0.0.1'),
+                           '127.0.0.1'),
                      array('127.0.0.1:99',
-                           '127.0.0.1:99'),
+                           '127.0.0.1:99'),
                      array('127.0.0.1/Name:test.php',
-                           '127.0.0.1/Name:test.php'),
+                           '127.0.0.1/Name:test.php'),
                      array('127.0.0.1/~test',
-                           '127.0.0.1/~test'),
+                           '127.0.0.1/~test'),
                      array('127.0.0.1/+test',
-                           '127.0.0.1/+test'),
+                           '127.0.0.1/+test'),
                      array('127.0.0.1/$test',
-                           '127.0.0.1/$test'),
+                           '127.0.0.1/$test'),
                      array('127.0.0.1/\'test',
-                           '127.0.0.1/\'test'),
+                           '127.0.0.1/\'test'),
                      array('127.0.0.1/"test',
-                           '127.0.0.1/"test'),
+                           '127.0.0.1/"test'),
                      array('127.0.0.1/test"test',
-                           '127.0.0.1/test"test'),
+                           '127.0.0.1/test"test'),
                      array('127.0.0.1/-test',
-                           '127.0.0.1/-test'),
+                           '127.0.0.1/-test'),
                      array('127.0.0.1/_test',
-                           '127.0.0.1/_test'),
+                           '127.0.0.1/_test'),
                      array('127.0.0.1/!test',
-                           '127.0.0.1/!test'),
+                           '127.0.0.1/!test'),
                      array('127.0.0.1/*test',
-                           '127.0.0.1/*test'),
+                           '127.0.0.1/*test'),
                      array('127.0.0.1/test%20stuff',
-                           '127.0.0.1/test%20stuff'),
+                           '127.0.0.1/test%20stuff'),
                      array('http://[::1]:99/test.php',
-                           'http://[::1]:99/test.php'),
+                           'http://[::1]:99/test.php'),
                      array('http://::1/test.php',
-                           'http://::1/test.php'),
+                           'http://::1/test.php'),
                      array('http://::1',
-                           'http://::1'),
+                           'http://::1'),
                      array('2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php',
-                           '2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php'),
+                           '2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php'),
                      array('[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php',
-                           '[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php'),
+                           '[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php'),
                      array('2001:4978:1b5:0:21d:e0ff:fe66:59ab',
-                           '2001:4978:1b5:0:21d:e0ff:fe66:59ab'),
+                           '2001:4978:1b5:0:21d:e0ff:fe66:59ab'),
                      array('http://127.0.0.1',
-                           'http://127.0.0.1'),
+                           'http://127.0.0.1'),
                      array('example.com',
-                           'example.com'),
+                           'example.com'),
                      array('example.com',
-                           'example.com'),
+                           'example.com'),
                      array('http://example.com',
-                           'http://example.com'),
+                           'http://example.com'),
                      array('http://example.com.',
-                           'http://example.com.'),
+                           'http://example.com.'),
                      array('/var/lib/example.so',
                            '/var/lib/example.so'),
                      array('example',
                            'example'),
                      array('user@example.com',
-                           'user@example.com'),
+                           'user@example.com'),
                      array('user_name+other@example.com',
-                           'user_name+other@example.com'),
+                           'user_name+other@example.com'),
                      array('mailto:user@example.com',
-                           'mailto:user@example.com'),
+                           'mailto:user@example.com'),
                      array('mailto:user@example.com?subject=test',
-                           'mailto:user@example.com?subject=test'),
+                           'mailto:user@example.com?subject=test'),
                      array('xmpp:user@example.com',
-                           'xmpp:user@example.com'),
+                           'xmpp:user@example.com'),
                      array('#example',
                            '#'),
                      array('#example.com',
@@ -103,167 +103,167 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
                      array('#.net',
                            '#'),
                      array('http://example',
-                           'http://example'),
+                           'http://example'),
                      array('http://3xampl3',
-                           'http://3xampl3'),
+                           'http://3xampl3'),
                      array('http://example/',
-                           'http://example/'),
+                           'http://example/'),
                      array('http://example/path',
-                           'http://example/path'),
+                           'http://example/path'),
                      array('http://example.com',
-                           'http://example.com'),
+                           'http://example.com'),
                      array('https://example.com',
-                           'https://example.com'),
+                           'https://example.com'),
                      array('ftp://example.com',
-                           'ftp://example.com'),
+                           'ftp://example.com'),
                      array('ftps://example.com',
-                           'ftps://example.com'),
+                           'ftps://example.com'),
                      array('http://user@example.com',
-                           'http://user@example.com'),
+                           'http://user@example.com'),
                      array('http://user:pass@example.com',
-                           'http://user:pass@example.com'),
+                           'http://user:pass@example.com'),
                      array('http://example.com:8080',
-                           'http://example.com:8080'),
+                           'http://example.com:8080'),
                      array('http://example.com:8080/test.php',
-                           'http://example.com:8080/test.php'),
+                           'http://example.com:8080/test.php'),
                      array('example.com:8080/test.php',
-                           'example.com:8080/test.php'),
+                           'example.com:8080/test.php'),
                      array('http://www.example.com',
-                           'http://www.example.com'),
+                           'http://www.example.com'),
                      array('http://example.com/',
-                           'http://example.com/'),
+                           'http://example.com/'),
                      array('http://example.com/path',
-                           'http://example.com/path'),
+                           'http://example.com/path'),
                      array('http://example.com/path.html',
-                           'http://example.com/path.html'),
+                           'http://example.com/path.html'),
                      array('http://example.com/path.html#fragment',
-                           'http://example.com/path.html#fragment'),
+                           'http://example.com/path.html#fragment'),
                      array('http://example.com/path.php?foo=bar&bar=foo',
-                           'http://example.com/path.php?foo=bar&bar=foo'),
+                           'http://example.com/path.php?foo=bar&bar=foo'),
                      array('http://example.com.',
-                           'http://example.com.'),
+                           'http://example.com.'),
                      array('http://müllärör.de',
-                           'http://müllärör.de'),
+                           'http://müllärör.de'),
                      array('http://ﺱﺲﺷ.com',
-                           'http://ﺱﺲﺷ.com'),
+                           'http://ﺱﺲﺷ.com'),
                      array('http://сделаткартинки.com',
-                           'http://сделаткартинки.com'),
+                           'http://сделаткартинки.com'),
                      array('http://tūdaliņ.lv',
-                           'http://tūdaliņ.lv'),
+                           'http://tūdaliņ.lv'),
                      array('http://brændendekærlighed.com',
-                           'http://brændendekærlighed.com'),
+                           'http://brændendekærlighed.com'),
                      array('http://あーるいん.com',
-                           'http://あーるいん.com'),
+                           'http://あーるいん.com'),
                      array('http://예비교사.com',
-                           'http://예비교사.com'),
+                           'http://예비교사.com'),
                      array('http://example.com.',
-                           'http://example.com.'),
+                           'http://example.com.'),
                      array('http://example.com?',
-                           'http://example.com?'),
+                           'http://example.com?'),
                      array('http://example.com!',
-                           'http://example.com!'),
+                           'http://example.com!'),
                      array('http://example.com,',
-                           'http://example.com,'),
+                           'http://example.com,'),
                      array('http://example.com;',
-                           'http://example.com;'),
+                           'http://example.com;'),
                      array('http://example.com:',
-                           'http://example.com:'),
+                           'http://example.com:'),
                      array('\'http://example.com\'',
-                           '\'http://example.com\''),
+                           '\'http://example.com\''),
                      array('"http://example.com"',
-                           '"http://example.com"'),
+                           '"http://example.com"'),
                      array('"http://example.com/"',
-                           '"http://example.com/"'),
+                           '"http://example.com/"'),
                      array('http://example.com',
-                           'http://example.com'),
+                           'http://example.com'),
                      array('(http://example.com)',
-                           '(http://example.com)'),
+                           '(http://example.com)'),
                      array('[http://example.com]',
-                           '[http://example.com]'),
+                           '[http://example.com]'),
                      array('',
-                           '<http://example.com>'),
+                           '<http://example.com>'),
                      array('http://example.com/path/(foo)/bar',
-                           'http://example.com/path/(foo)/bar'),
+                           'http://example.com/path/(foo)/bar'),
                      array('http://example.com/path/[foo]/bar',
-                           'http://example.com/path/[foo]/bar'),
+                           'http://example.com/path/[foo]/bar'),
                      array('http://example.com/path/foo/(bar)',
-                           'http://example.com/path/foo/(bar)'),
+                           'http://example.com/path/foo/(bar)'),
                      //Not a valid url - urls cannot contain unencoded square brackets
                      array('http://example.com/path/foo/[bar]',
-                           'http://example.com/path/foo/[bar]'),
+                           'http://example.com/path/foo/[bar]'),
                      array('Hey, check out my cool site http://example.com okay?',
-                           'Hey, check out my cool site http://example.com okay?'),
+                           'Hey, check out my cool site http://example.com okay?'),
                      array('What about parens (e.g. http://example.com/path/foo/(bar))?',
-                           'What about parens (e.g. http://example.com/path/foo/(bar))?'),
+                           'What about parens (e.g. http://example.com/path/foo/(bar))?'),
                      array('What about parens (e.g. http://example.com/path/foo/(bar)?',
-                           'What about parens (e.g. http://example.com/path/foo/(bar)?'),
+                           'What about parens (e.g. http://example.com/path/foo/(bar)?'),
                      array('What about parens (e.g. http://example.com/path/foo/(bar).)?',
-                           'What about parens (e.g. http://example.com/path/foo/(bar).)?'),
+                           'What about parens (e.g. http://example.com/path/foo/(bar).)?'),
                      //Not a valid url - urls cannot contain unencoded commas
                      array('What about parens (e.g. http://example.com/path/(foo,bar)?',
-                           'What about parens (e.g. http://example.com/path/(foo,bar)?'),
+                           'What about parens (e.g. http://example.com/path/(foo,bar)?'),
                      array('Unbalanced too (e.g. http://example.com/path/((((foo)/bar)?',
-                           'Unbalanced too (e.g. http://example.com/path/((((foo)/bar)?'),
+                           'Unbalanced too (e.g. http://example.com/path/((((foo)/bar)?'),
                      array('Unbalanced too (e.g. http://example.com/path/(foo))))/bar)?',
-                           'Unbalanced too (e.g. http://example.com/path/(foo))))/bar)?'),
+                           'Unbalanced too (e.g. http://example.com/path/(foo))))/bar)?'),
                      array('Unbalanced too (e.g. http://example.com/path/foo/((((bar)?',
-                           'Unbalanced too (e.g. http://example.com/path/foo/((((bar)?'),
+                           'Unbalanced too (e.g. http://example.com/path/foo/((((bar)?'),
                      array('Unbalanced too (e.g. http://example.com/path/foo/(bar))))?',
-                           'Unbalanced too (e.g. http://example.com/path/foo/(bar))))?'),
+                           'Unbalanced too (e.g. http://example.com/path/foo/(bar))))?'),
                      array('example.com',
-                           'example.com'),
+                           'example.com'),
                      array('example.org',
-                           'example.org'),
+                           'example.org'),
                      array('example.co.uk',
-                           'example.co.uk'),
+                           'example.co.uk'),
                      array('www.example.co.uk',
-                           'www.example.co.uk'),
+                           'www.example.co.uk'),
                      array('farm1.images.example.co.uk',
-                           'farm1.images.example.co.uk'),
+                           'farm1.images.example.co.uk'),
                      array('example.museum',
-                           'example.museum'),
+                           'example.museum'),
                      array('example.travel',
-                           'example.travel'),
+                           'example.travel'),
                      array('example.com.',
-                           'example.com.'),
+                           'example.com.'),
                      array('example.com?',
-                           'example.com?'),
+                           'example.com?'),
                      array('example.com!',
-                           'example.com!'),
+                           'example.com!'),
                      array('example.com,',
-                           'example.com,'),
+                           'example.com,'),
                      array('example.com;',
-                           'example.com;'),
+                           'example.com;'),
                      array('example.com:',
-                           'example.com:'),
+                           'example.com:'),
                      array('\'example.com\'',
-                           '\'example.com\''),
+                           '\'example.com\''),
                      array('"example.com"',
-                           '"example.com"'),
+                           '"example.com"'),
                      array('example.com',
-                           'example.com'),
+                           'example.com'),
                      array('(example.com)',
-                           '(example.com)'),
+                           '(example.com)'),
                      array('[example.com]',
-                           '[example.com]'),
+                           '[example.com]'),
                      array('',
-                           '<example.com>'),
+                           '<example.com>'),
                      array('Hey, check out my cool site example.com okay?',
-                           'Hey, check out my cool site example.com okay?'),
+                           'Hey, check out my cool site example.com okay?'),
                      array('Hey, check out my cool site example.com.I made it.',
-                           'Hey, check out my cool site example.com.I made it.'),
+                           'Hey, check out my cool site example.com.I made it.'),
                      array('Hey, check out my cool site example.com.Funny thing...',
-                           'Hey, check out my cool site example.com.Funny thing...'),
+                           'Hey, check out my cool site example.com.Funny thing...'),
                      array('Hey, check out my cool site example.com.You will love it.',
-                           'Hey, check out my cool site example.com.You will love it.'),
+                           'Hey, check out my cool site example.com.You will love it.'),
                      array('What about parens (e.g. example.com/path/foo/(bar))?',
-                           'What about parens (e.g. example.com/path/foo/(bar))?'),
+                           'What about parens (e.g. example.com/path/foo/(bar))?'),
                      array('What about parens (e.g. example.com/path/foo/(bar)?',
-                           'What about parens (e.g. example.com/path/foo/(bar)?'),
+                           'What about parens (e.g. example.com/path/foo/(bar)?'),
                      array('What about parens (e.g. example.com/path/foo/(bar).)?',
-                           'What about parens (e.g. example.com/path/foo/(bar).)?'),
+                           'What about parens (e.g. example.com/path/foo/(bar).)?'),
                      array('What about parens (e.g. example.com/path/(foo,bar)?',
-                           'What about parens (e.g. example.com/path/(foo,bar)?'),
+                           'What about parens (e.g. example.com/path/(foo,bar)?'),
                      array('file.ext',
                            'file.ext'),
                      array('file.html',