Merge branch '1.0.x' into schema-x

This commit is contained in:
Brion Vibber 2010-10-18 15:26:20 -07:00
commit f1bfbece06
18 changed files with 492 additions and 198 deletions

32
README
View File

@ -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
---

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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
{
}

View File

@ -305,7 +305,7 @@ class ShownoticeAction extends OwnerDesignAction
}
}
class SingleNoticeItem extends NoticeListItem
class SingleNoticeItem extends DoFollowListItem
{
/**
* recipe function for displaying a single notice.

View File

@ -269,7 +269,7 @@ class ProfileNoticeList extends NoticeList
}
}
class ProfileNoticeListItem extends NoticeListItem
class ProfileNoticeListItem extends DoFollowListItem
{
function showAuthor()
{

View File

@ -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);

View File

@ -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,

88
lib/dofollowlistitem.php Normal file
View File

@ -0,0 +1,88 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* widget for displaying a list of notices
*
* 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 UI
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @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 <evan@status.net>
* @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');
}
}

View File

@ -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 = "<?php\n".
"if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
// site name
"\$config['site']['name'] = '{$this->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);

View File

@ -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);
}

View File

@ -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

View File

@ -75,9 +75,10 @@ END;
'<body><pre><LINK rel=alternate hRef=http://example.com/feed/rss type=application/rss+xml><fnork',
'http://example.com/feed/rss'),
// 'rel' attribute must be lowercase, alone per http://www.rssboard.org/rss-autodiscovery
// but we're going to be liberal in what we receive.
array('http://example.com/tagsoup2',
'<body><pre><LINK rel=" feeders alternate 467" hRef=http://example.com/feed/rss type=application/rss+xml><fnork',
false),
'http://example.com/feed/rss'),
array('http://example.com/tagsoup3',
'<body><pre><LINK rel=ALTERNATE hRef=http://example.com/feed/rss type=application/rss+xml><fnork',
false),
@ -87,18 +88,20 @@ END;
array('http://example.com/relative/link2',
'<html><link rel="alternate" href="../feed/rss" type="application/rss+xml">',
'http://example.com/feed/rss'),
// This one can't resolve correctly; relative link is bogus.
array('http://example.com/relative/link3',
'<html><link rel="alternate" href="http:/feed/rss" type="application/rss+xml">',
'http://example.com/feed/rss'),
'http:/feed/rss'),
array('http://example.com/base/link1',
'<html><link rel="alternate" href="/feed/rss" type="application/rss+xml"><base href="http://target.example.com/">',
'http://target.example.com/feed/rss'),
array('http://example.com/base/link2',
'<html><link rel="alternate" href="feed/rss" type="application/rss+xml"><base href="http://target.example.com/">',
'http://target.example.com/feed/rss'),
// This one can't resolve; relative link is bogus.
array('http://example.com/base/link3',
'<html><link rel="alternate" href="http:/feed/rss" type="application/rss+xml"><base href="http://target.example.com/">',
'http://target.example.com/feed/rss'),
'http:/feed/rss'),
// Trick question! There's a <base> but no href on it
array('http://example.com/relative/fauxbase',
'<html><link rel="alternate" href="../feed/rss" type="application/rss+xml"><base target="top">',

View File

@ -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 <<<END_HELP
remote-tests.php <url1> <url2>
remote-tests.php [options] <url1> <url2>
--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();

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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 <a href="http://www.somesite.com/xyz/35637563@N00/52803365/" title="http://www.somesite.com/xyz/35637563@N00/52803365/" rel="external">http://www.somesite.com/xyz/35637563@N00/52803365/</a> link'),
'link <a href="http://www.somesite.com/xyz/35637563@N00/52803365/" title="http://www.somesite.com/xyz/35637563@N00/52803365/" rel="nofollow external">http://www.somesite.com/xyz/35637563@N00/52803365/</a> link'),
array('http://127.0.0.1',
'<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
'<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="nofollow external">http://127.0.0.1</a>'),
array('127.0.0.1',
'<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="external">127.0.0.1</a>'),
'<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="nofollow external">127.0.0.1</a>'),
array('127.0.0.1:99',
'<a href="http://127.0.0.1:99/" title="http://127.0.0.1:99/" rel="external">127.0.0.1:99</a>'),
'<a href="http://127.0.0.1:99/" title="http://127.0.0.1:99/" rel="nofollow external">127.0.0.1:99</a>'),
array('127.0.0.1/Name:test.php',
'<a href="http://127.0.0.1/Name:test.php" title="http://127.0.0.1/Name:test.php" rel="external">127.0.0.1/Name:test.php</a>'),
'<a href="http://127.0.0.1/Name:test.php" title="http://127.0.0.1/Name:test.php" rel="nofollow external">127.0.0.1/Name:test.php</a>'),
array('127.0.0.1/~test',
'<a href="http://127.0.0.1/~test" title="http://127.0.0.1/~test" rel="external">127.0.0.1/~test</a>'),
'<a href="http://127.0.0.1/~test" title="http://127.0.0.1/~test" rel="nofollow external">127.0.0.1/~test</a>'),
array('127.0.0.1/+test',
'<a href="http://127.0.0.1/+test" title="http://127.0.0.1/+test" rel="external">127.0.0.1/+test</a>'),
'<a href="http://127.0.0.1/+test" title="http://127.0.0.1/+test" rel="nofollow external">127.0.0.1/+test</a>'),
array('127.0.0.1/$test',
'<a href="http://127.0.0.1/$test" title="http://127.0.0.1/$test" rel="external">127.0.0.1/$test</a>'),
'<a href="http://127.0.0.1/$test" title="http://127.0.0.1/$test" rel="nofollow external">127.0.0.1/$test</a>'),
array('127.0.0.1/\'test',
'<a href="http://127.0.0.1/\'test" title="http://127.0.0.1/\'test" rel="external">127.0.0.1/\'test</a>'),
'<a href="http://127.0.0.1/\'test" title="http://127.0.0.1/\'test" rel="nofollow external">127.0.0.1/\'test</a>'),
array('127.0.0.1/"test',
'<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="external">127.0.0.1/</a>&quot;test'),
'<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="nofollow external">127.0.0.1/</a>&quot;test'),
array('127.0.0.1/test"test',
'<a href="http://127.0.0.1/test" title="http://127.0.0.1/test" rel="external">127.0.0.1/test</a>&quot;test'),
'<a href="http://127.0.0.1/test" title="http://127.0.0.1/test" rel="nofollow external">127.0.0.1/test</a>&quot;test'),
array('127.0.0.1/-test',
'<a href="http://127.0.0.1/-test" title="http://127.0.0.1/-test" rel="external">127.0.0.1/-test</a>'),
'<a href="http://127.0.0.1/-test" title="http://127.0.0.1/-test" rel="nofollow external">127.0.0.1/-test</a>'),
array('127.0.0.1/_test',
'<a href="http://127.0.0.1/_test" title="http://127.0.0.1/_test" rel="external">127.0.0.1/_test</a>'),
'<a href="http://127.0.0.1/_test" title="http://127.0.0.1/_test" rel="nofollow external">127.0.0.1/_test</a>'),
array('127.0.0.1/!test',
'<a href="http://127.0.0.1/!test" title="http://127.0.0.1/!test" rel="external">127.0.0.1/!test</a>'),
'<a href="http://127.0.0.1/!test" title="http://127.0.0.1/!test" rel="nofollow external">127.0.0.1/!test</a>'),
array('127.0.0.1/*test',
'<a href="http://127.0.0.1/*test" title="http://127.0.0.1/*test" rel="external">127.0.0.1/*test</a>'),
'<a href="http://127.0.0.1/*test" title="http://127.0.0.1/*test" rel="nofollow external">127.0.0.1/*test</a>'),
array('127.0.0.1/test%20stuff',
'<a href="http://127.0.0.1/test%20stuff" title="http://127.0.0.1/test%20stuff" rel="external">127.0.0.1/test%20stuff</a>'),
'<a href="http://127.0.0.1/test%20stuff" title="http://127.0.0.1/test%20stuff" rel="nofollow external">127.0.0.1/test%20stuff</a>'),
array('http://[::1]:99/test.php',
'<a href="http://[::1]:99/test.php" title="http://[::1]:99/test.php" rel="external">http://[::1]:99/test.php</a>'),
'<a href="http://[::1]:99/test.php" title="http://[::1]:99/test.php" rel="nofollow external">http://[::1]:99/test.php</a>'),
array('http://::1/test.php',
'<a href="http://::1/test.php" title="http://::1/test.php" rel="external">http://::1/test.php</a>'),
'<a href="http://::1/test.php" title="http://::1/test.php" rel="nofollow external">http://::1/test.php</a>'),
array('http://::1',
'<a href="http://::1/" title="http://::1/" rel="external">http://::1</a>'),
'<a href="http://::1/" title="http://::1/" rel="nofollow external">http://::1</a>'),
array('2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php',
'<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" title="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php</a>'),
'<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" title="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php" rel="nofollow external">2001:4978:1b5:0:21d:e0ff:fe66:59ab/test.php</a>'),
array('[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php',
'<a href="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" title="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" rel="external">[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php</a>'),
'<a href="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" title="http://[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php" rel="nofollow external">[2001:4978:1b5:0:21d:e0ff:fe66:59ab]:99/test.php</a>'),
array('2001:4978:1b5:0:21d:e0ff:fe66:59ab',
'<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" title="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" rel="external">2001:4978:1b5:0:21d:e0ff:fe66:59ab</a>'),
'<a href="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" title="http://2001:4978:1b5:0:21d:e0ff:fe66:59ab/" rel="nofollow external">2001:4978:1b5:0:21d:e0ff:fe66:59ab</a>'),
array('http://127.0.0.1',
'<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'),
'<a href="http://127.0.0.1/" title="http://127.0.0.1/" rel="nofollow external">http://127.0.0.1</a>'),
array('example.com',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>'),
array('example.com',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>'),
array('http://example.com',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>'),
array('http://example.com.',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>.'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>.'),
array('/var/lib/example.so',
'/var/lib/example.so'),
array('example',
'example'),
array('user@example.com',
'<a href="mailto:user@example.com" title="mailto:user@example.com" rel="external">user@example.com</a>'),
'<a href="mailto:user@example.com" title="mailto:user@example.com" rel="nofollow external">user@example.com</a>'),
array('user_name+other@example.com',
'<a href="mailto:user_name+other@example.com" title="mailto:user_name+other@example.com" rel="external">user_name+other@example.com</a>'),
'<a href="mailto:user_name+other@example.com" title="mailto:user_name+other@example.com" rel="nofollow external">user_name+other@example.com</a>'),
array('mailto:user@example.com',
'<a href="mailto:user@example.com" title="mailto:user@example.com" rel="external">mailto:user@example.com</a>'),
'<a href="mailto:user@example.com" title="mailto:user@example.com" rel="nofollow external">mailto:user@example.com</a>'),
array('mailto:user@example.com?subject=test',
'<a href="mailto:user@example.com?subject=test" title="mailto:user@example.com?subject=test" rel="external">mailto:user@example.com?subject=test</a>'),
'<a href="mailto:user@example.com?subject=test" title="mailto:user@example.com?subject=test" rel="nofollow external">mailto:user@example.com?subject=test</a>'),
array('xmpp:user@example.com',
'<a href="xmpp:user@example.com" title="xmpp:user@example.com" rel="external">xmpp:user@example.com</a>'),
'<a href="xmpp:user@example.com" title="xmpp:user@example.com" rel="nofollow external">xmpp:user@example.com</a>'),
array('#example',
'#<span class="tag"><a href="' . common_local_url('tag', array('tag' => common_canonical_tag('example'))) . '" rel="tag">example</a></span>'),
array('#example.com',
@ -103,167 +103,167 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
array('#.net',
'#<span class="tag"><a href="' . common_local_url('tag', array('tag' => common_canonical_tag('.net'))) . '" rel="tag">.net</a></span>'),
array('http://example',
'<a href="http://example/" title="http://example/" rel="external">http://example</a>'),
'<a href="http://example/" title="http://example/" rel="nofollow external">http://example</a>'),
array('http://3xampl3',
'<a href="http://3xampl3/" title="http://3xampl3/" rel="external">http://3xampl3</a>'),
'<a href="http://3xampl3/" title="http://3xampl3/" rel="nofollow external">http://3xampl3</a>'),
array('http://example/',
'<a href="http://example/" title="http://example/" rel="external">http://example/</a>'),
'<a href="http://example/" title="http://example/" rel="nofollow external">http://example/</a>'),
array('http://example/path',
'<a href="http://example/path" title="http://example/path" rel="external">http://example/path</a>'),
'<a href="http://example/path" title="http://example/path" rel="nofollow external">http://example/path</a>'),
array('http://example.com',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>'),
array('https://example.com',
'<a href="https://example.com/" title="https://example.com/" rel="external">https://example.com</a>'),
'<a href="https://example.com/" title="https://example.com/" rel="nofollow external">https://example.com</a>'),
array('ftp://example.com',
'<a href="ftp://example.com/" title="ftp://example.com/" rel="external">ftp://example.com</a>'),
'<a href="ftp://example.com/" title="ftp://example.com/" rel="nofollow external">ftp://example.com</a>'),
array('ftps://example.com',
'<a href="ftps://example.com/" title="ftps://example.com/" rel="external">ftps://example.com</a>'),
'<a href="ftps://example.com/" title="ftps://example.com/" rel="nofollow external">ftps://example.com</a>'),
array('http://user@example.com',
'<a href="http://user@example.com/" title="http://user@example.com/" rel="external">http://user@example.com</a>'),
'<a href="http://user@example.com/" title="http://user@example.com/" rel="nofollow external">http://user@example.com</a>'),
array('http://user:pass@example.com',
'<a href="http://user:pass@example.com/" title="http://user:pass@example.com/" rel="external">http://user:pass@example.com</a>'),
'<a href="http://user:pass@example.com/" title="http://user:pass@example.com/" rel="nofollow external">http://user:pass@example.com</a>'),
array('http://example.com:8080',
'<a href="http://example.com:8080/" title="http://example.com:8080/" rel="external">http://example.com:8080</a>'),
'<a href="http://example.com:8080/" title="http://example.com:8080/" rel="nofollow external">http://example.com:8080</a>'),
array('http://example.com:8080/test.php',
'<a href="http://example.com:8080/test.php" title="http://example.com:8080/test.php" rel="external">http://example.com:8080/test.php</a>'),
'<a href="http://example.com:8080/test.php" title="http://example.com:8080/test.php" rel="nofollow external">http://example.com:8080/test.php</a>'),
array('example.com:8080/test.php',
'<a href="http://example.com:8080/test.php" title="http://example.com:8080/test.php" rel="external">example.com:8080/test.php</a>'),
'<a href="http://example.com:8080/test.php" title="http://example.com:8080/test.php" rel="nofollow external">example.com:8080/test.php</a>'),
array('http://www.example.com',
'<a href="http://www.example.com/" title="http://www.example.com/" rel="external">http://www.example.com</a>'),
'<a href="http://www.example.com/" title="http://www.example.com/" rel="nofollow external">http://www.example.com</a>'),
array('http://example.com/',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com/</a>'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com/</a>'),
array('http://example.com/path',
'<a href="http://example.com/path" title="http://example.com/path" rel="external">http://example.com/path</a>'),
'<a href="http://example.com/path" title="http://example.com/path" rel="nofollow external">http://example.com/path</a>'),
array('http://example.com/path.html',
'<a href="http://example.com/path.html" title="http://example.com/path.html" rel="external">http://example.com/path.html</a>'),
'<a href="http://example.com/path.html" title="http://example.com/path.html" rel="nofollow external">http://example.com/path.html</a>'),
array('http://example.com/path.html#fragment',
'<a href="http://example.com/path.html#fragment" title="http://example.com/path.html#fragment" rel="external">http://example.com/path.html#fragment</a>'),
'<a href="http://example.com/path.html#fragment" title="http://example.com/path.html#fragment" rel="nofollow external">http://example.com/path.html#fragment</a>'),
array('http://example.com/path.php?foo=bar&bar=foo',
'<a href="http://example.com/path.php?foo=bar&amp;bar=foo" title="http://example.com/path.php?foo=bar&amp;bar=foo" rel="external">http://example.com/path.php?foo=bar&amp;bar=foo</a>'),
'<a href="http://example.com/path.php?foo=bar&amp;bar=foo" title="http://example.com/path.php?foo=bar&amp;bar=foo" rel="nofollow external">http://example.com/path.php?foo=bar&amp;bar=foo</a>'),
array('http://example.com.',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>.'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>.'),
array('http://müllärör.de',
'<a href="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" title="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" rel="external">http://müllärör.de</a>'),
'<a href="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" title="http://m&#xFC;ll&#xE4;r&#xF6;r.de/" rel="nofollow external">http://müllärör.de</a>'),
array('http://ﺱﺲﺷ.com',
'<a href="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" title="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" rel="external">http://ﺱﺲﺷ.com</a>'),
'<a href="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" title="http://&#xFEB1;&#xFEB2;&#xFEB7;.com/" rel="nofollow external">http://ﺱﺲﺷ.com</a>'),
array('http://сделаткартинки.com',
'<a href="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" title="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" rel="external">http://сделаткартинки.com</a>'),
'<a href="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" title="http://&#x441;&#x434;&#x435;&#x43B;&#x430;&#x442;&#x43A;&#x430;&#x440;&#x442;&#x438;&#x43D;&#x43A;&#x438;.com/" rel="nofollow external">http://сделаткартинки.com</a>'),
array('http://tūdaliņ.lv',
'<a href="http://t&#x16B;dali&#x146;.lv/" title="http://t&#x16B;dali&#x146;.lv/" rel="external">http://tūdaliņ.lv</a>'),
'<a href="http://t&#x16B;dali&#x146;.lv/" title="http://t&#x16B;dali&#x146;.lv/" rel="nofollow external">http://tūdaliņ.lv</a>'),
array('http://brændendekærlighed.com',
'<a href="http://br&#xE6;ndendek&#xE6;rlighed.com/" title="http://br&#xE6;ndendek&#xE6;rlighed.com/" rel="external">http://brændendekærlighed.com</a>'),
'<a href="http://br&#xE6;ndendek&#xE6;rlighed.com/" title="http://br&#xE6;ndendek&#xE6;rlighed.com/" rel="nofollow external">http://brændendekærlighed.com</a>'),
array('http://あーるいん.com',
'<a href="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" title="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" rel="external">http://あーるいん.com</a>'),
'<a href="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" title="http://&#x3042;&#x30FC;&#x308B;&#x3044;&#x3093;.com/" rel="nofollow external">http://あーるいん.com</a>'),
array('http://예비교사.com',
'<a href="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" title="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" rel="external">http://예비교사.com</a>'),
'<a href="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" title="http://&#xC608;&#xBE44;&#xAD50;&#xC0AC;.com/" rel="nofollow external">http://예비교사.com</a>'),
array('http://example.com.',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>.'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>.'),
array('http://example.com?',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>?'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>?'),
array('http://example.com!',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>!'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>!'),
array('http://example.com,',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>,'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>,'),
array('http://example.com;',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>;'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>;'),
array('http://example.com:',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>:'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>:'),
array('\'http://example.com\'',
'\'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>\''),
'\'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>\''),
array('"http://example.com"',
'&quot;<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>&quot;'),
'&quot;<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>&quot;'),
array('"http://example.com/"',
'&quot;<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com/</a>&quot;'),
'&quot;<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com/</a>&quot;'),
array('http://example.com',
'<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>'),
array('(http://example.com)',
'(<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>)'),
'(<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>)'),
array('[http://example.com]',
'[<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>]'),
'[<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>]'),
array('<http://example.com>',
'&lt;<a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a>&gt;'),
'&lt;<a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a>&gt;'),
array('http://example.com/path/(foo)/bar',
'<a href="http://example.com/path/(foo)/bar" title="http://example.com/path/(foo)/bar" rel="external">http://example.com/path/(foo)/bar</a>'),
'<a href="http://example.com/path/(foo)/bar" title="http://example.com/path/(foo)/bar" rel="nofollow external">http://example.com/path/(foo)/bar</a>'),
array('http://example.com/path/[foo]/bar',
'<a href="http://example.com/path/" title="http://example.com/path/" rel="external">http://example.com/path/</a>[foo]/bar'),
'<a href="http://example.com/path/" title="http://example.com/path/" rel="nofollow external">http://example.com/path/</a>[foo]/bar'),
array('http://example.com/path/foo/(bar)',
'<a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>'),
'<a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="nofollow external">http://example.com/path/foo/(bar)</a>'),
//Not a valid url - urls cannot contain unencoded square brackets
array('http://example.com/path/foo/[bar]',
'<a href="http://example.com/path/foo/" title="http://example.com/path/foo/" rel="external">http://example.com/path/foo/</a>[bar]'),
'<a href="http://example.com/path/foo/" title="http://example.com/path/foo/" rel="nofollow external">http://example.com/path/foo/</a>[bar]'),
array('Hey, check out my cool site http://example.com okay?',
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">http://example.com</a> okay?'),
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="nofollow external">http://example.com</a> okay?'),
array('What about parens (e.g. http://example.com/path/foo/(bar))?',
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)?'),
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="nofollow external">http://example.com/path/foo/(bar)</a>)?'),
array('What about parens (e.g. http://example.com/path/foo/(bar)?',
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>?'),
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="nofollow external">http://example.com/path/foo/(bar)</a>?'),
array('What about parens (e.g. http://example.com/path/foo/(bar).)?',
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>.)?'),
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="nofollow external">http://example.com/path/foo/(bar)</a>.)?'),
//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. <a href="http://example.com/path/(foo,bar)" title="http://example.com/path/(foo,bar)" rel="external">http://example.com/path/(foo,bar)</a>?'),
'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" title="http://example.com/path/(foo,bar)" rel="nofollow external">http://example.com/path/(foo,bar)</a>?'),
array('Unbalanced too (e.g. http://example.com/path/((((foo)/bar)?',
'Unbalanced too (e.g. <a href="http://example.com/path/((((foo)/bar)" title="http://example.com/path/((((foo)/bar)" rel="external">http://example.com/path/((((foo)/bar)</a>?'),
'Unbalanced too (e.g. <a href="http://example.com/path/((((foo)/bar)" title="http://example.com/path/((((foo)/bar)" rel="nofollow external">http://example.com/path/((((foo)/bar)</a>?'),
array('Unbalanced too (e.g. http://example.com/path/(foo))))/bar)?',
'Unbalanced too (e.g. <a href="http://example.com/path/(foo))))/bar" title="http://example.com/path/(foo))))/bar" rel="external">http://example.com/path/(foo))))/bar</a>)?'),
'Unbalanced too (e.g. <a href="http://example.com/path/(foo))))/bar" title="http://example.com/path/(foo))))/bar" rel="nofollow external">http://example.com/path/(foo))))/bar</a>)?'),
array('Unbalanced too (e.g. http://example.com/path/foo/((((bar)?',
'Unbalanced too (e.g. <a href="http://example.com/path/foo/((((bar)" title="http://example.com/path/foo/((((bar)" rel="external">http://example.com/path/foo/((((bar)</a>?'),
'Unbalanced too (e.g. <a href="http://example.com/path/foo/((((bar)" title="http://example.com/path/foo/((((bar)" rel="nofollow external">http://example.com/path/foo/((((bar)</a>?'),
array('Unbalanced too (e.g. http://example.com/path/foo/(bar))))?',
'Unbalanced too (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">http://example.com/path/foo/(bar)</a>)))?'),
'Unbalanced too (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="nofollow external">http://example.com/path/foo/(bar)</a>)))?'),
array('example.com',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>'),
array('example.org',
'<a href="http://example.org/" title="http://example.org/" rel="external">example.org</a>'),
'<a href="http://example.org/" title="http://example.org/" rel="nofollow external">example.org</a>'),
array('example.co.uk',
'<a href="http://example.co.uk/" title="http://example.co.uk/" rel="external">example.co.uk</a>'),
'<a href="http://example.co.uk/" title="http://example.co.uk/" rel="nofollow external">example.co.uk</a>'),
array('www.example.co.uk',
'<a href="http://www.example.co.uk/" title="http://www.example.co.uk/" rel="external">www.example.co.uk</a>'),
'<a href="http://www.example.co.uk/" title="http://www.example.co.uk/" rel="nofollow external">www.example.co.uk</a>'),
array('farm1.images.example.co.uk',
'<a href="http://farm1.images.example.co.uk/" title="http://farm1.images.example.co.uk/" rel="external">farm1.images.example.co.uk</a>'),
'<a href="http://farm1.images.example.co.uk/" title="http://farm1.images.example.co.uk/" rel="nofollow external">farm1.images.example.co.uk</a>'),
array('example.museum',
'<a href="http://example.museum/" title="http://example.museum/" rel="external">example.museum</a>'),
'<a href="http://example.museum/" title="http://example.museum/" rel="nofollow external">example.museum</a>'),
array('example.travel',
'<a href="http://example.travel/" title="http://example.travel/" rel="external">example.travel</a>'),
'<a href="http://example.travel/" title="http://example.travel/" rel="nofollow external">example.travel</a>'),
array('example.com.',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>.'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>.'),
array('example.com?',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>?'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>?'),
array('example.com!',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>!'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>!'),
array('example.com,',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>,'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>,'),
array('example.com;',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>;'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>;'),
array('example.com:',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>:'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>:'),
array('\'example.com\'',
'\'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>\''),
'\'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>\''),
array('"example.com"',
'&quot;<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>&quot;'),
'&quot;<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>&quot;'),
array('example.com',
'<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>'),
'<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>'),
array('(example.com)',
'(<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>)'),
'(<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>)'),
array('[example.com]',
'[<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>]'),
'[<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>]'),
array('<example.com>',
'&lt;<a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>&gt;'),
'&lt;<a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>&gt;'),
array('Hey, check out my cool site example.com okay?',
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">example.com</a> okay?'),
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a> okay?'),
array('Hey, check out my cool site example.com.I made it.',
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>.I made it.'),
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>.I made it.'),
array('Hey, check out my cool site example.com.Funny thing...',
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>.Funny thing...'),
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>.Funny thing...'),
array('Hey, check out my cool site example.com.You will love it.',
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="external">example.com</a>.You will love it.'),
'Hey, check out my cool site <a href="http://example.com/" title="http://example.com/" rel="nofollow external">example.com</a>.You will love it.'),
array('What about parens (e.g. example.com/path/foo/(bar))?',
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>)?'),
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="nofollow external">example.com/path/foo/(bar)</a>)?'),
array('What about parens (e.g. example.com/path/foo/(bar)?',
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>?'),
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="nofollow external">example.com/path/foo/(bar)</a>?'),
array('What about parens (e.g. example.com/path/foo/(bar).)?',
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="external">example.com/path/foo/(bar)</a>.)?'),
'What about parens (e.g. <a href="http://example.com/path/foo/(bar)" title="http://example.com/path/foo/(bar)" rel="nofollow external">example.com/path/foo/(bar)</a>.)?'),
array('What about parens (e.g. example.com/path/(foo,bar)?',
'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" title="http://example.com/path/(foo,bar)" rel="external">example.com/path/(foo,bar)</a>?'),
'What about parens (e.g. <a href="http://example.com/path/(foo,bar)" title="http://example.com/path/(foo,bar)" rel="nofollow external">example.com/path/(foo,bar)</a>?'),
array('file.ext',
'file.ext'),
array('file.html',