summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Shorturl_api.php66
-rw-r--r--lib/accountsettingsaction.php61
-rw-r--r--lib/action.php39
-rw-r--r--lib/command.php16
-rw-r--r--lib/commandinterpreter.php42
-rw-r--r--lib/common.php252
-rw-r--r--lib/curlclient.php179
-rw-r--r--lib/default.php232
-rw-r--r--lib/deleteaction.php74
-rw-r--r--lib/facebookaction.php30
-rw-r--r--lib/facebookutil.php7
-rw-r--r--lib/groupeditform.php24
-rw-r--r--lib/htmloutputter.php2
-rw-r--r--lib/httpclient.php122
-rw-r--r--lib/logingroupnav.php35
-rw-r--r--lib/messageform.php17
-rw-r--r--lib/noticeform.php65
-rw-r--r--lib/noticelist.php14
-rw-r--r--lib/oauthstore.php348
-rw-r--r--lib/omb.php329
-rw-r--r--lib/openid.php280
-rw-r--r--lib/plugin.php14
-rw-r--r--lib/right.php50
-rw-r--r--lib/router.php13
-rw-r--r--lib/rssaction.php59
-rw-r--r--lib/schema.php680
-rw-r--r--lib/settingsaction.php4
-rw-r--r--lib/twitter.php7
-rw-r--r--lib/twitterapi.php19
-rw-r--r--lib/twitteroauthclient.php8
-rw-r--r--lib/unqueuemanager.php9
-rw-r--r--lib/util.php136
32 files changed, 2102 insertions, 1131 deletions
diff --git a/lib/Shorturl_api.php b/lib/Shorturl_api.php
index 6402dbc09..18ae7719b 100644
--- a/lib/Shorturl_api.php
+++ b/lib/Shorturl_api.php
@@ -19,7 +19,7 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-class ShortUrlApi
+abstract class ShortUrlApi
{
protected $service_url;
protected $long_limit = 27;
@@ -35,11 +35,9 @@ class ShortUrlApi
return $url;
}
- protected function shorten_imp($url) {
- return "To Override";
- }
+ protected abstract function shorten_imp($url);
- private function is_long($url) {
+ protected function is_long($url) {
return strlen($url) >= common_config('site', 'shorturllength');
}
@@ -71,61 +69,3 @@ class ShortUrlApi
}
}
-class LilUrl extends ShortUrlApi
-{
- function __construct()
- {
- parent::__construct('http://ur1.ca/');
- }
-
- protected function shorten_imp($url) {
- $data['longurl'] = $url;
- $response = $this->http_post($data);
- if (!$response) return $url;
- $y = @simplexml_load_string($response);
- if (!isset($y->body)) return $url;
- $x = $y->body->p[0]->a->attributes();
- if (isset($x['href'])) return $x['href'];
- return $url;
- }
-}
-
-
-class PtitUrl extends ShortUrlApi
-{
- function __construct()
- {
- parent::__construct('http://ptiturl.com/?creer=oui&action=Reduire&url=');
- }
-
- protected function shorten_imp($url) {
- $response = $this->http_get($url);
- if (!$response) return $url;
- $response = $this->tidy($response);
- $y = @simplexml_load_string($response);
- if (!isset($y->body)) return $url;
- $xml = $y->body->center->table->tr->td->pre->a->attributes();
- if (isset($xml['href'])) return $xml['href'];
- return $url;
- }
-}
-
-class TightUrl extends ShortUrlApi
-{
- function __construct()
- {
- parent::__construct('http://2tu.us/?save=y&url=');
- }
-
- protected function shorten_imp($url) {
- $response = $this->http_get($url);
- if (!$response) return $url;
- $response = $this->tidy($response);
- $y = @simplexml_load_string($response);
- if (!isset($y->body)) return $url;
- $xml = $y->body->p[0]->code[0]->a->attributes();
- if (isset($xml['href'])) return $xml['href'];
- return $url;
- }
-}
-
diff --git a/lib/accountsettingsaction.php b/lib/accountsettingsaction.php
index 798116163..a004a3ed9 100644
--- a/lib/accountsettingsaction.php
+++ b/lib/accountsettingsaction.php
@@ -98,42 +98,39 @@ class AccountSettingsNav extends Widget
function show()
{
- # action => array('prompt', 'title')
- $menu =
- array('profilesettings' =>
- array(_('Profile'),
- _('Change your profile settings')),
- 'avatarsettings' =>
- array(_('Avatar'),
- _('Upload an avatar')),
- 'passwordsettings' =>
- array(_('Password'),
- _('Change your password')),
- 'emailsettings' =>
- array(_('Email'),
- _('Change email handling')),
- 'openidsettings' =>
- array(_('OpenID'),
- _('Add or remove OpenIDs')),
- 'userdesignsettings' =>
- array(_('Design'),
- _('Design your profile')),
- 'othersettings' =>
- array(_('Other'),
- _('Other options')));
-
$action_name = $this->action->trimmed('action');
$this->action->elementStart('ul', array('class' => 'nav'));
- foreach ($menu as $menuaction => $menudesc) {
- if ($menuaction == 'openidsettings' &&
- !common_config('openid', 'enabled')) {
- continue;
+ if (Event::handle('StartAccountSettingsNav', array(&$this->action))) {
+
+ $menu =
+ array('profilesettings' =>
+ array(_('Profile'),
+ _('Change your profile settings')),
+ 'avatarsettings' =>
+ array(_('Avatar'),
+ _('Upload an avatar')),
+ 'passwordsettings' =>
+ array(_('Password'),
+ _('Change your password')),
+ 'emailsettings' =>
+ array(_('Email'),
+ _('Change email handling')),
+ 'userdesignsettings' =>
+ array(_('Design'),
+ _('Design your profile')),
+ 'othersettings' =>
+ array(_('Other'),
+ _('Other options')));
+
+ foreach ($menu as $menuaction => $menudesc) {
+ $this->action->menuItem(common_local_url($menuaction),
+ $menudesc[0],
+ $menudesc[1],
+ $action_name === $menuaction);
}
- $this->action->menuItem(common_local_url($menuaction),
- $menudesc[0],
- $menudesc[1],
- $action_name === $menuaction);
+
+ Event::handle('EndAccountSettingsNav', array(&$this->action));
}
$this->action->elementEnd('ul');
diff --git a/lib/action.php b/lib/action.php
index 670eb498c..1b2f73752 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -120,14 +120,16 @@ class Action extends HTMLOutputter // lawsuit
{
// XXX: attributes (profile?)
$this->elementStart('head');
- $this->showTitle();
- $this->showShortcutIcon();
- $this->showStylesheets();
- $this->showScripts();
- $this->showOpenSearch();
- $this->showFeeds();
- $this->showDescription();
- $this->extraHead();
+ if (Event::handle('StartShowHeadElements', array($this))) {
+ $this->showTitle();
+ $this->showShortcutIcon();
+ $this->showStylesheets();
+ $this->showOpenSearch();
+ $this->showFeeds();
+ $this->showDescription();
+ $this->extraHead();
+ Event::handle('EndShowHeadElements', array($this));
+ }
$this->elementEnd('head');
}
@@ -352,6 +354,7 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowFooter', array($this));
}
$this->elementEnd('div');
+ $this->showScripts();
$this->elementEnd('body');
}
@@ -442,17 +445,12 @@ class Action extends HTMLOutputter // lawsuit
_('Logout'), _('Logout from the site'), false, 'nav_logout');
}
else {
- if (!common_config('site', 'openidonly')) {
- if (!common_config('site', 'closed')) {
- $this->menuItem(common_local_url('register'),
- _('Register'), _('Create an account'), false, 'nav_register');
- }
- $this->menuItem(common_local_url('login'),
- _('Login'), _('Login to the site'), false, 'nav_login');
- } else {
- $this->menuItem(common_local_url('openidlogin'),
- _('OpenID'), _('Login with OpenID'), false, 'nav_openid');
+ if (!common_config('site', 'closed')) {
+ $this->menuItem(common_local_url('register'),
+ _('Register'), _('Create an account'), false, 'nav_register');
}
+ $this->menuItem(common_local_url('login'),
+ _('Login'), _('Login to the site'), false, 'nav_login');
}
$this->menuItem(common_local_url('doc', array('title' => 'help')),
_('Help'), _('Help me!'), false, 'nav_help');
@@ -530,7 +528,10 @@ class Action extends HTMLOutputter // lawsuit
$this->showContentBlock();
Event::handle('EndShowContentBlock', array($this));
}
- $this->showAside();
+ if (Event::handle('StartShowAside', array($this))) {
+ $this->showAside();
+ Event::handle('EndShowAside', array($this));
+ }
$this->elementEnd('div');
}
diff --git a/lib/command.php b/lib/command.php
index 01b14f83e..11d40b8e1 100644
--- a/lib/command.php
+++ b/lib/command.php
@@ -312,16 +312,20 @@ class MessageCommand extends Command
function execute($channel)
{
$other = User::staticGet('nickname', common_canonical_nickname($this->other));
+
$len = mb_strlen($this->text);
+
if ($len == 0) {
$channel->error($this->user, _('No content!'));
return;
- } else if ($len > 140) {
- $content = common_shorten_links($content);
- if (mb_strlen($content) > 140) {
- $channel->error($this->user, sprintf(_('Message too long - maximum is 140 characters, you sent %d'), $len));
- return;
- }
+ }
+
+ $this->text = common_shorten_links($this->text);
+
+ if (Message::contentTooLong($this->text)) {
+ $channel->error($this->user, sprintf(_('Message too long - maximum is %d characters, you sent %d'),
+ Message::maxContent(), mb_strlen($this->text)));
+ return;
}
if (!$other) {
diff --git a/lib/commandinterpreter.php b/lib/commandinterpreter.php
index 6e4340e5d..60fc4c3c4 100644
--- a/lib/commandinterpreter.php
+++ b/lib/commandinterpreter.php
@@ -28,7 +28,7 @@ class CommandInterpreter
# XXX: localise
$text = preg_replace('/\s+/', ' ', trim($text));
- list($cmd, $arg) = explode(' ', $text, 2);
+ list($cmd, $arg) = $this->split_arg($text);
# We try to support all the same commands as Twitter, see
# http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands
@@ -43,7 +43,7 @@ class CommandInterpreter
return new HelpCommand($user);
case 'on':
if ($arg) {
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -54,7 +54,7 @@ class CommandInterpreter
}
case 'off':
if ($arg) {
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -74,7 +74,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -84,7 +84,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -95,7 +95,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -106,7 +106,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -117,7 +117,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -128,7 +128,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if (!$extra) {
return null;
} else {
@@ -138,7 +138,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -148,7 +148,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -158,7 +158,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -173,7 +173,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($other, $extra) = explode(' ', $arg, 2);
+ list($other, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else {
@@ -183,7 +183,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($word, $extra) = explode(' ', $arg, 2);
+ list($word, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else if ($word == 'off') {
@@ -195,7 +195,7 @@ class CommandInterpreter
if (!$arg) {
return null;
}
- list($word, $extra) = explode(' ', $arg, 2);
+ list($word, $extra) = $this->split_arg($arg);
if ($extra) {
return null;
} else if ($word == 'all') {
@@ -213,5 +213,17 @@ class CommandInterpreter
return false;
}
}
+
+ /**
+ * Split arguments without triggering a PHP notice warning
+ */
+ function split_arg($text)
+ {
+ $pieces = explode(' ', $text, 2);
+ if (count($pieces) == 1) {
+ $pieces[] = null;
+ }
+ return $pieces;
+ }
}
diff --git a/lib/common.php b/lib/common.php
index 0b4e03184..ce33c871b 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -19,10 +19,10 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-define('STATUSNET_VERSION', '0.8.2dev');
+define('STATUSNET_VERSION', '0.9.0dev');
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
-define('STATUSNET_CODENAME', 'Second Guessing');
+define('STATUSNET_CODENAME', 'Stand');
define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48);
@@ -53,6 +53,7 @@ require_once('DB/DataObject/Cast.php'); # for dates
if (!function_exists('gettext')) {
require_once("php-gettext/gettext.inc");
}
+
require_once(INSTALLDIR.'/lib/language.php');
// This gets included before the config file, so that admin code and plugins
@@ -93,206 +94,17 @@ if (isset($path)) {
null;
}
-// default configuration, overwritten in config.php
+require_once(INSTALLDIR.'/lib/default.php');
+
+// Set config values initially to default values
-$config =
- array('site' =>
- array('name' => 'Just another StatusNet microblog',
- 'server' => $_server,
- 'theme' => 'default',
- 'path' => $_path,
- 'logfile' => null,
- 'logo' => null,
- 'logdebug' => false,
- 'fancy' => false,
- 'locale_path' => INSTALLDIR.'/locale',
- 'language' => 'en_US',
- 'languages' => get_all_languages(),
- 'email' =>
- array_key_exists('SERVER_ADMIN', $_SERVER) ? $_SERVER['SERVER_ADMIN'] : null,
- 'broughtby' => null,
- 'timezone' => 'UTC',
- 'broughtbyurl' => null,
- 'closed' => false,
- 'inviteonly' => false,
- 'openidonly' => false,
- 'private' => false,
- 'ssl' => 'never',
- 'sslserver' => null,
- 'shorturllength' => 30,
- 'dupelimit' => 60), # default for same person saying the same thing
- 'syslog' =>
- array('appname' => 'statusnet', # for syslog
- 'priority' => 'debug', # XXX: currently ignored
- 'facility' => LOG_USER),
- 'queue' =>
- array('enabled' => false,
- 'subsystem' => 'db', # default to database, or 'stomp'
- 'stomp_server' => null,
- 'queue_basename' => 'statusnet',
- 'stomp_username' => null,
- 'stomp_password' => null,
- ),
- 'license' =>
- array('url' => 'http://creativecommons.org/licenses/by/3.0/',
- 'title' => 'Creative Commons Attribution 3.0',
- 'image' => 'http://i.creativecommons.org/l/by/3.0/80x15.png'),
- 'mail' =>
- array('backend' => 'mail',
- 'params' => null),
- 'nickname' =>
- array('blacklist' => array(),
- 'featured' => array()),
- 'profile' =>
- array('banned' => array()),
- 'avatar' =>
- array('server' => null,
- 'dir' => INSTALLDIR . '/avatar/',
- 'path' => $_path . '/avatar/'),
- 'background' =>
- array('server' => null,
- 'dir' => INSTALLDIR . '/background/',
- 'path' => $_path . '/background/'),
- 'public' =>
- array('localonly' => true,
- 'blacklist' => array(),
- 'autosource' => array()),
- 'theme' =>
- array('server' => null,
- 'dir' => null,
- 'path'=> null),
- 'throttle' =>
- array('enabled' => false, // whether to throttle edits; false by default
- 'count' => 20, // number of allowed messages in timespan
- 'timespan' => 600), // timespan for throttling
- 'xmpp' =>
- array('enabled' => false,
- 'server' => 'INVALID SERVER',
- 'port' => 5222,
- 'user' => 'update',
- 'encryption' => true,
- 'resource' => 'uniquename',
- 'password' => 'blahblahblah',
- 'host' => null, # only set if != server
- 'debug' => false, # print extra debug info
- 'public' => array()), # JIDs of users who want to receive the public stream
- 'openid' =>
- array('enabled' => true),
- 'invite' =>
- array('enabled' => true),
- 'sphinx' =>
- array('enabled' => false,
- 'server' => 'localhost',
- 'port' => 3312),
- 'tag' =>
- array('dropoff' => 864000.0),
- 'popular' =>
- array('dropoff' => 864000.0),
- 'daemon' =>
- array('piddir' => '/var/run',
- 'user' => false,
- 'group' => false),
- 'emailpost' =>
- array('enabled' => true),
- 'sms' =>
- array('enabled' => true),
- 'twitterbridge' =>
- array('enabled' => false),
- 'integration' =>
- array('source' => 'StatusNet', # source attribute for Twitter
- 'taguri' => $_server.',2009'), # base for tag URIs
- 'twitter' =>
- array('enabled' => true,
- 'consumer_key' => null,
- 'consumer_secret' => null),
- 'memcached' =>
- array('enabled' => false,
- 'server' => 'localhost',
- 'base' => null,
- 'port' => 11211),
- 'ping' =>
- array('notify' => array()),
- 'inboxes' =>
- array('enabled' => true), # on by default for new sites
- 'newuser' =>
- array('default' => null,
- 'welcome' => null),
- 'snapshot' =>
- array('run' => 'web',
- 'frequency' => 10000,
- 'reporturl' => 'http://status.net/stats/report'),
- 'attachments' =>
- array('server' => null,
- 'dir' => INSTALLDIR . '/file/',
- 'path' => $_path . '/file/',
- 'supported' => array('image/png',
- 'image/jpeg',
- 'image/gif',
- 'image/svg+xml',
- 'audio/mpeg',
- 'audio/x-speex',
- 'application/ogg',
- 'application/pdf',
- 'application/vnd.oasis.opendocument.text',
- 'application/vnd.oasis.opendocument.text-template',
- 'application/vnd.oasis.opendocument.graphics',
- 'application/vnd.oasis.opendocument.graphics-template',
- 'application/vnd.oasis.opendocument.presentation',
- 'application/vnd.oasis.opendocument.presentation-template',
- 'application/vnd.oasis.opendocument.spreadsheet',
- 'application/vnd.oasis.opendocument.spreadsheet-template',
- 'application/vnd.oasis.opendocument.chart',
- 'application/vnd.oasis.opendocument.chart-template',
- 'application/vnd.oasis.opendocument.image',
- 'application/vnd.oasis.opendocument.image-template',
- 'application/vnd.oasis.opendocument.formula',
- 'application/vnd.oasis.opendocument.formula-template',
- 'application/vnd.oasis.opendocument.text-master',
- 'application/vnd.oasis.opendocument.text-web',
- 'application/x-zip',
- 'application/zip',
- 'text/plain',
- 'video/mpeg',
- 'video/mp4',
- 'video/quicktime',
- 'video/mpeg'),
- 'file_quota' => 5000000,
- 'user_quota' => 50000000,
- 'monthly_quota' => 15000000,
- 'uploads' => true,
- 'filecommand' => '/usr/bin/file',
- ),
- 'group' =>
- array('maxaliases' => 3),
- 'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
- 'search' =>
- array('type' => 'fulltext'),
- 'sessions' =>
- array('handle' => false, // whether to handle sessions ourselves
- 'debug' => false), // debugging output for sessions
- 'design' =>
- array('backgroundcolor' => null, // null -> 'use theme default'
- 'contentcolor' => null,
- 'sidebarcolor' => null,
- 'textcolor' => null,
- 'linkcolor' => null,
- 'backgroundimage' => null,
- 'disposition' => null),
- );
+$config = $default;
+
+// default configuration, overwritten in config.php
$config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');
-$config['db'] =
- array('database' => 'YOU HAVE TO SET THIS IN config.php',
- 'schema_location' => INSTALLDIR . '/classes',
- 'class_location' => INSTALLDIR . '/classes',
- 'require_prefix' => 'classes/',
- 'class_prefix' => '',
- 'mirror' => null,
- 'utf8' => true,
- 'db_driver' => 'DB', # XXX: JanRain libs only work with DB
- 'quote_identifiers' => false,
- 'type' => 'mysql' );
+$config['db'] = $default['db'];
// Backward compatibility
@@ -382,13 +194,25 @@ if ($_db_name != 'statusnet' && !array_key_exists('ini_'.$_db_name, $config['db'
$config['db']['ini_'.$_db_name] = INSTALLDIR.'/classes/statusnet.ini';
}
-// Ignore openidonly if OpenID is disabled
-
-if (!$config['openid']['enabled']) {
- $config['site']['openidonly'] = false;
+function __autoload($cls)
+{
+ if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
+ require_once(INSTALLDIR.'/classes/' . $cls . '.php');
+ } else if (file_exists(INSTALLDIR.'/lib/' . strtolower($cls) . '.php')) {
+ require_once(INSTALLDIR.'/lib/' . strtolower($cls) . '.php');
+ } else if (mb_substr($cls, -6) == 'Action' &&
+ file_exists(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php')) {
+ require_once(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
+ } else if ($cls == 'OAuthRequest') {
+ require_once('OAuth.php');
+ } else {
+ Event::handle('Autoload', array(&$cls));
+ }
}
// XXX: how many of these could be auto-loaded on use?
+// XXX: note that these files should not use config options
+// at compile time since DB config options are not yet loaded.
require_once 'Validate.php';
require_once 'markdown.php';
@@ -404,24 +228,20 @@ require_once INSTALLDIR.'/lib/twitter.php';
require_once INSTALLDIR.'/lib/clientexception.php';
require_once INSTALLDIR.'/lib/serverexception.php';
-// XXX: other formats here
+// Load settings from database; note we need autoload for this
-define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
+Config::loadSettings();
-function __autoload($class)
-{
- if ($class == 'OAuthRequest') {
- require_once('OAuth.php');
- } else if (file_exists(INSTALLDIR.'/classes/' . $class . '.php')) {
- require_once(INSTALLDIR.'/classes/' . $class . '.php');
- } else if (file_exists(INSTALLDIR.'/lib/' . strtolower($class) . '.php')) {
- require_once(INSTALLDIR.'/lib/' . strtolower($class) . '.php');
- } else if (mb_substr($class, -6) == 'Action' &&
- file_exists(INSTALLDIR.'/actions/' . strtolower(mb_substr($class, 0, -6)) . '.php')) {
- require_once(INSTALLDIR.'/actions/' . strtolower(mb_substr($class, 0, -6)) . '.php');
- }
+// XXX: if plugins should check the schema at runtime, do that here.
+
+if ($config['db']['schemacheck'] == 'runtime') {
+ Event::handle('CheckSchema');
}
+// XXX: other formats here
+
+define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
+
// Give plugins a chance to initialize in a fully-prepared environment
Event::handle('InitializePlugin');
diff --git a/lib/curlclient.php b/lib/curlclient.php
new file mode 100644
index 000000000..36fc7d157
--- /dev/null
+++ b/lib/curlclient.php
@@ -0,0 +1,179 @@
+n<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Utility class for wrapping Curl
+ *
+ * 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 HTTP
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+define(CURLCLIENT_VERSION, "0.1");
+
+/**
+ * Wrapper for Curl
+ *
+ * Makes Curl HTTP client calls within our HTTPClient framework
+ *
+ * @category HTTP
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class CurlClient extends HTTPClient
+{
+ function __construct()
+ {
+ }
+
+ function head($url, $headers=null)
+ {
+ $ch = curl_init($url);
+
+ $this->setup($ch);
+
+ curl_setopt_array($ch,
+ array(CURLOPT_NOBODY => true));
+
+ if (!is_null($headers)) {
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ }
+
+ $result = curl_exec($ch);
+
+ curl_close($ch);
+
+ return $this->parseResults($result);
+ }
+
+ function get($url, $headers=null)
+ {
+ $ch = curl_init($url);
+
+ $this->setup($ch);
+
+ if (!is_null($headers)) {
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ }
+
+ $result = curl_exec($ch);
+
+ curl_close($ch);
+
+ return $this->parseResults($result);
+ }
+
+ function post($url, $headers=null, $body=null)
+ {
+ $ch = curl_init($url);
+
+ $this->setup($ch);
+
+ curl_setopt($ch, CURLOPT_POST, true);
+
+ if (!is_null($body)) {
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ }
+
+ if (!is_null($headers)) {
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ }
+
+ $result = curl_exec($ch);
+
+ curl_close($ch);
+
+ return $this->parseResults($result);
+ }
+
+ function setup($ch)
+ {
+ curl_setopt_array($ch,
+ array(CURLOPT_USERAGENT => $this->userAgent(),
+ CURLOPT_HEADER => true,
+ CURLOPT_RETURNTRANSFER => true));
+ }
+
+ function userAgent()
+ {
+ $version = curl_version();
+ return parent::userAgent() . " CurlClient/".CURLCLIENT_VERSION . " cURL/" . $version['version'];
+ }
+
+ function parseResults($results)
+ {
+ $resp = new HTTPResponse();
+
+ $lines = explode("\r\n", $results);
+
+ if (preg_match("#^HTTP/1.[01] (\d\d\d) .+$#", $lines[0], $match)) {
+ $resp->code = $match[1];
+ } else {
+ throw Exception("Bad format: initial line is not HTTP status line");
+ }
+
+ $lastk = null;
+
+ for ($i = 1; $i < count($lines); $i++) {
+ $l =& $lines[$i];
+ if (mb_strlen($l) == 0) {
+ $resp->body = implode("\r\n", array_slice($lines, $i + 1));
+ break;
+ }
+ if (preg_match("#^(\S+):\s+(.*)$#", $l, $match)) {
+ $k = $match[1];
+ $v = $match[2];
+
+ if (array_key_exists($k, $resp->headers)) {
+ if (is_array($resp->headers[$k])) {
+ $resp->headers[$k][] = $v;
+ } else {
+ $resp->headers[$k] = array($resp->headers[$k], $v);
+ }
+ } else {
+ $resp->headers[$k] = $v;
+ }
+ $lastk = $k;
+ } else if (preg_match("#^\s+(.*)$#", $l, $match)) {
+ // continuation line
+ if (is_null($lastk)) {
+ throw Exception("Bad format: initial whitespace in headers");
+ }
+ $h =& $resp->headers[$lastk];
+ if (is_array($h)) {
+ $n = count($h);
+ $h[$n-1] .= $match[1];
+ } else {
+ $h .= $match[1];
+ }
+ }
+ }
+
+ return $resp;
+ }
+}
diff --git a/lib/default.php b/lib/default.php
new file mode 100644
index 000000000..329b041e9
--- /dev/null
+++ b/lib/default.php
@@ -0,0 +1,232 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Default settings for core configuration
+ *
+ * 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 Config
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2008-9 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+$default =
+ array('site' =>
+ array('name' => 'Just another StatusNet microblog',
+ 'server' => $_server,
+ 'theme' => 'default',
+ 'path' => $_path,
+ 'logfile' => null,
+ 'logo' => null,
+ 'logdebug' => false,
+ 'fancy' => false,
+ 'locale_path' => INSTALLDIR.'/locale',
+ 'language' => 'en_US',
+ 'languages' => get_all_languages(),
+ 'email' =>
+ array_key_exists('SERVER_ADMIN', $_SERVER) ? $_SERVER['SERVER_ADMIN'] : null,
+ 'broughtby' => null,
+ 'timezone' => 'UTC',
+ 'broughtbyurl' => null,
+ 'closed' => false,
+ 'inviteonly' => false,
+ 'private' => false,
+ 'ssl' => 'never',
+ 'sslserver' => null,
+ 'shorturllength' => 30,
+ 'dupelimit' => 60, # default for same person saying the same thing
+ 'textlimit' => 140,
+ ),
+ 'db' =>
+ array('database' => 'YOU HAVE TO SET THIS IN config.php',
+ 'schema_location' => INSTALLDIR . '/classes',
+ 'class_location' => INSTALLDIR . '/classes',
+ 'require_prefix' => 'classes/',
+ 'class_prefix' => '',
+ 'mirror' => null,
+ 'utf8' => true,
+ 'db_driver' => 'DB', # XXX: JanRain libs only work with DB
+ 'quote_identifiers' => false,
+ 'type' => 'mysql',
+ 'schemacheck' => 'runtime'), // 'runtime' or 'script'
+ 'syslog' =>
+ array('appname' => 'statusnet', # for syslog
+ 'priority' => 'debug', # XXX: currently ignored
+ 'facility' => LOG_USER),
+ 'queue' =>
+ array('enabled' => false,
+ 'subsystem' => 'db', # default to database, or 'stomp'
+ 'stomp_server' => null,
+ 'queue_basename' => 'statusnet',
+ 'stomp_username' => null,
+ 'stomp_password' => null,
+ ),
+ 'license' =>
+ array('url' => 'http://creativecommons.org/licenses/by/3.0/',
+ 'title' => 'Creative Commons Attribution 3.0',
+ 'image' => 'http://i.creativecommons.org/l/by/3.0/80x15.png'),
+ 'mail' =>
+ array('backend' => 'mail',
+ 'params' => null),
+ 'nickname' =>
+ array('blacklist' => array(),
+ 'featured' => array()),
+ 'profile' =>
+ array('banned' => array(),
+ 'biolimit' => null),
+ 'avatar' =>
+ array('server' => null,
+ 'dir' => INSTALLDIR . '/avatar/',
+ 'path' => $_path . '/avatar/'),
+ 'background' =>
+ array('server' => null,
+ 'dir' => INSTALLDIR . '/background/',
+ 'path' => $_path . '/background/'),
+ 'public' =>
+ array('localonly' => true,
+ 'blacklist' => array(),
+ 'autosource' => array()),
+ 'theme' =>
+ array('server' => null,
+ 'dir' => null,
+ 'path'=> null),
+ 'throttle' =>
+ array('enabled' => false, // whether to throttle edits; false by default
+ 'count' => 20, // number of allowed messages in timespan
+ 'timespan' => 600), // timespan for throttling
+ 'xmpp' =>
+ array('enabled' => false,
+ 'server' => 'INVALID SERVER',
+ 'port' => 5222,
+ 'user' => 'update',
+ 'encryption' => true,
+ 'resource' => 'uniquename',
+ 'password' => 'blahblahblah',
+ 'host' => null, # only set if != server
+ 'debug' => false, # print extra debug info
+ 'public' => array()), # JIDs of users who want to receive the public stream
+ 'invite' =>
+ array('enabled' => true),
+ 'sphinx' =>
+ array('enabled' => false,
+ 'server' => 'localhost',
+ 'port' => 3312),
+ 'tag' =>
+ array('dropoff' => 864000.0),
+ 'popular' =>
+ array('dropoff' => 864000.0),
+ 'daemon' =>
+ array('piddir' => '/var/run',
+ 'user' => false,
+ 'group' => false),
+ 'emailpost' =>
+ array('enabled' => true),
+ 'sms' =>
+ array('enabled' => true),
+ 'twitterbridge' =>
+ array('enabled' => false),
+ 'integration' =>
+ array('source' => 'StatusNet', # source attribute for Twitter
+ 'taguri' => $_server.',2009'), # base for tag URIs
+ 'twitter' =>
+ array('enabled' => true,
+ 'consumer_key' => null,
+ 'consumer_secret' => null),
+ 'memcached' =>
+ array('enabled' => false,
+ 'server' => 'localhost',
+ 'base' => null,
+ 'port' => 11211),
+ 'ping' =>
+ array('notify' => array()),
+ 'inboxes' =>
+ array('enabled' => true), # on by default for new sites
+ 'newuser' =>
+ array('default' => null,
+ 'welcome' => null),
+ 'snapshot' =>
+ array('run' => 'web',
+ 'frequency' => 10000,
+ 'reporturl' => 'http://status.net/stats/report'),
+ 'attachments' =>
+ array('server' => null,
+ 'dir' => INSTALLDIR . '/file/',
+ 'path' => $_path . '/file/',
+ 'supported' => array('image/png',
+ 'image/jpeg',
+ 'image/gif',
+ 'image/svg+xml',
+ 'audio/mpeg',
+ 'audio/x-speex',
+ 'application/ogg',
+ 'application/pdf',
+ 'application/vnd.oasis.opendocument.text',
+ 'application/vnd.oasis.opendocument.text-template',
+ 'application/vnd.oasis.opendocument.graphics',
+ 'application/vnd.oasis.opendocument.graphics-template',
+ 'application/vnd.oasis.opendocument.presentation',
+ 'application/vnd.oasis.opendocument.presentation-template',
+ 'application/vnd.oasis.opendocument.spreadsheet',
+ 'application/vnd.oasis.opendocument.spreadsheet-template',
+ 'application/vnd.oasis.opendocument.chart',
+ 'application/vnd.oasis.opendocument.chart-template',
+ 'application/vnd.oasis.opendocument.image',
+ 'application/vnd.oasis.opendocument.image-template',
+ 'application/vnd.oasis.opendocument.formula',
+ 'application/vnd.oasis.opendocument.formula-template',
+ 'application/vnd.oasis.opendocument.text-master',
+ 'application/vnd.oasis.opendocument.text-web',
+ 'application/x-zip',
+ 'application/zip',
+ 'text/plain',
+ 'video/mpeg',
+ 'video/mp4',
+ 'video/quicktime',
+ 'video/mpeg'),
+ 'file_quota' => 5000000,
+ 'user_quota' => 50000000,
+ 'monthly_quota' => 15000000,
+ 'uploads' => true,
+ 'filecommand' => '/usr/bin/file',
+ ),
+ 'group' =>
+ array('maxaliases' => 3,
+ 'desclimit' => null),
+ 'oohembed' => array('endpoint' => 'http://oohembed.com/oohembed/'),
+ 'search' =>
+ array('type' => 'fulltext'),
+ 'sessions' =>
+ array('handle' => false, // whether to handle sessions ourselves
+ 'debug' => false), // debugging output for sessions
+ 'design' =>
+ array('backgroundcolor' => null, // null -> 'use theme default'
+ 'contentcolor' => null,
+ 'sidebarcolor' => null,
+ 'textcolor' => null,
+ 'linkcolor' => null,
+ 'backgroundimage' => null,
+ 'disposition' => null),
+ 'notice' =>
+ array('contentlimit' => null),
+ 'message' =>
+ array('contentlimit' => null),
+ 'http' =>
+ array('client' => 'curl'), // XXX: should this be the default?
+ );
diff --git a/lib/deleteaction.php b/lib/deleteaction.php
deleted file mode 100644
index f702820c6..000000000
--- a/lib/deleteaction.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * Base class for deleting things
- *
- * 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 Personal
- * @package StatusNet
- * @author Evan Prodromou <evan@status.net>
- * @author Sarven Capadisli <csarven@status.net>
- * @copyright 2008 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-class DeleteAction extends Action
-{
- var $user = null;
- var $notice = null;
- var $profile = null;
- var $user_profile = null;
-
- function prepare($args)
- {
- parent::prepare($args);
-
- $this->user = common_current_user();
- $notice_id = $this->trimmed('notice');
- $this->notice = Notice::staticGet($notice_id);
-
- if (!$this->notice) {
- common_user_error(_('No such notice.'));
- exit;
- }
-
- $this->profile = $this->notice->getProfile();
- $this->user_profile = $this->user->getProfile();
-
- return true;
- }
-
- function handle($args)
- {
- parent::handle($args);
-
- if (!common_logged_in()) {
- common_user_error(_('Not logged in.'));
- exit;
- } else if ($this->notice->profile_id != $this->user_profile->id) {
- common_user_error(_('Can\'t delete this notice.'));
- exit;
- }
- }
-
-}
diff --git a/lib/facebookaction.php b/lib/facebookaction.php
index 5cbb9be53..3f3a8d3b0 100644
--- a/lib/facebookaction.php
+++ b/lib/facebookaction.php
@@ -35,7 +35,6 @@ if (!defined('STATUSNET') && !defined('LACONICA'))
require_once INSTALLDIR.'/lib/facebookutil.php';
require_once INSTALLDIR.'/lib/noticeform.php';
-
class FacebookAction extends Action
{
@@ -181,7 +180,6 @@ class FacebookAction extends Action
}
-
// Make this into a widget later
function showLocalNav()
{
@@ -241,7 +239,6 @@ class FacebookAction extends Action
$this->endHTML();
}
-
function showInstructions()
{
@@ -257,13 +254,8 @@ class FacebookAction extends Action
$this->elementStart('dd');
$this->elementStart('p');
$this->text(sprintf($loginmsg_part1, common_config('site', 'name')));
- if (!common_config('site', 'openidonly')) {
- $this->element('a',
- array('href' => common_local_url('register')), _('Register'));
- } else {
- $this->element('a',
- array('href' => common_local_url('openidlogin')), _('Register'));
- }
+ $this->element('a',
+ array('href' => common_local_url('register')), _('Register'));
$this->text($loginmsg_part2);
$this->elementEnd('p');
$this->elementEnd('dd');
@@ -272,7 +264,6 @@ class FacebookAction extends Action
$this->elementEnd('div');
}
-
function showLoginForm($msg = null)
{
@@ -317,7 +308,6 @@ class FacebookAction extends Action
}
-
function updateProfileBox($notice)
{
@@ -399,7 +389,6 @@ class FacebookAction extends Action
$this->xw->openURI('php://output');
}
-
/**
* Generate pagination links
*
@@ -458,8 +447,9 @@ class FacebookAction extends Action
} else {
$content_shortened = common_shorten_links($content);
- if (mb_strlen($content_shortened) > 140) {
- $this->showPage(_('That\'s too long. Max notice size is 140 chars.'));
+ if (Notice::contentTooLong($content_shortened)) {
+ $this->showPage(sprintf(_('That\'s too long. Max notice size is %d chars.'),
+ Notice::maxContent()));
return;
}
}
@@ -478,11 +468,11 @@ class FacebookAction extends Action
$replyto = $this->trimmed('inreplyto');
- $notice = Notice::saveNew($user->id, $content,
- 'web', 1, ($replyto == 'false') ? null : $replyto);
-
- if (is_string($notice)) {
- $this->showPage($notice);
+ try {
+ $notice = Notice::saveNew($user->id, $content,
+ 'web', 1, ($replyto == 'false') ? null : $replyto);
+ } catch (Exception $e) {
+ $this->showPage($e->getMessage());
return;
}
diff --git a/lib/facebookutil.php b/lib/facebookutil.php
index ad61b6f0a..f5121609d 100644
--- a/lib/facebookutil.php
+++ b/lib/facebookutil.php
@@ -109,7 +109,6 @@ function facebookBroadcastNotice($notice)
$can_update = $facebook->api_client->users_hasAppPermission('status_update',
$fbuid);
-
if (!empty($attachments) && $can_publish == 1) {
$fbattachment = format_attachments($attachments);
$facebook->api_client->stream_publish($status, $fbattachment,
@@ -180,7 +179,11 @@ function format_attachments($attachments)
foreach($attachments as $attachment)
{
- $fbmedia = get_fbmedia_for_attachment($attachment);
+ if($enclosure = $attachment->getEnclosure()){
+ $fbmedia = get_fbmedia_for_attachment($enclosure);
+ }else{
+ $fbmedia = get_fbmedia_for_attachment($attachment);
+ }
if($fbmedia){
$fbattachment['media'][]=$fbmedia;
}else{
diff --git a/lib/groupeditform.php b/lib/groupeditform.php
index a649c2ee3..433f6a138 100644
--- a/lib/groupeditform.php
+++ b/lib/groupeditform.php
@@ -150,27 +150,33 @@ class GroupEditForm extends Form
$this->out->elementStart('li');
$this->out->hidden('groupid', $id);
$this->out->input('nickname', _('Nickname'),
- ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
- _('1-64 lowercase letters or numbers, no punctuation or spaces'));
+ ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
+ _('1-64 lowercase letters or numbers, no punctuation or spaces'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('fullname', _('Full name'),
- ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
+ ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('homepage', _('Homepage'),
- ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
- _('URL of the homepage or blog of the group or topic'));
+ ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
+ _('URL of the homepage or blog of the group or topic'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
+ $desclimit = User_group::maxDescription();
+ if ($desclimit == 0) {
+ $descinstr = _('Describe the group or topic');
+ } else {
+ $descinstr = sprintf(_('Describe the group or topic in %d characters'), $desclimit);
+ }
$this->out->textarea('description', _('Description'),
- ($this->out->arg('description')) ? $this->out->arg('description') : $description,
- _('Describe the group or topic in 140 chars'));
+ ($this->out->arg('description')) ? $this->out->arg('description') : $description,
+ $descinstr);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('location', _('Location'),
- ($this->out->arg('location')) ? $this->out->arg('location') : $location,
- _('Location for the group, if any, like "City, State (or Region), Country"'));
+ ($this->out->arg('location')) ? $this->out->arg('location') : $location,
+ _('Location for the group, if any, like "City, State (or Region), Country"'));
$this->out->elementEnd('li');
if (common_config('group', 'maxaliases') > 0) {
$aliases = (empty($this->group)) ? array() : $this->group->getAliases();
diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php
index 2ff9380cc..ce83295fb 100644
--- a/lib/htmloutputter.php
+++ b/lib/htmloutputter.php
@@ -106,7 +106,7 @@ class HTMLOutputter extends XMLOutputter
}
}
- header('Content-Type: '.$type.'; charset=UTF-8');
+ header('Content-Type: '.$type);
$this->extraHeaders();
if (preg_match("/.*\/.*xml/", $type)) {
diff --git a/lib/httpclient.php b/lib/httpclient.php
new file mode 100644
index 000000000..c8c8ae5b2
--- /dev/null
+++ b/lib/httpclient.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Utility for doing HTTP-related things
+ *
+ * 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 Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Useful structure for HTTP responses
+ *
+ * We make HTTP calls in several places, and we have several different
+ * ways of doing them. This class hides the specifics of what underlying
+ * library (curl or PHP-HTTP or whatever) that's used.
+ *
+ * @category HTTP
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class HTTPResponse
+{
+ public $code = null;
+ public $headers = null;
+ public $body = null;
+}
+
+/**
+ * Utility class for doing HTTP client stuff
+ *
+ * We make HTTP calls in several places, and we have several different
+ * ways of doing them. This class hides the specifics of what underlying
+ * library (curl or PHP-HTTP or whatever) that's used.
+ *
+ * @category HTTP
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class HTTPClient
+{
+ static $_client = null;
+
+ static function start()
+ {
+ if (!is_null(self::$_client)) {
+ return self::$_client;
+ }
+
+ $type = common_config('http', 'client');
+
+ switch ($type) {
+ case 'curl':
+ self::$_client = new CurlClient();
+ break;
+ default:
+ throw new Exception("Unknown HTTP client type '$type'");
+ break;
+ }
+
+ return self::$_client;
+ }
+
+ function head($url, $headers)
+ {
+ throw new Exception("HEAD method unimplemented");
+ }
+
+ function get($url, $headers)
+ {
+ throw new Exception("GET method unimplemented");
+ }
+
+ function post($url, $headers, $body)
+ {
+ throw new Exception("POST method unimplemented");
+ }
+
+ function put($url, $headers, $body)
+ {
+ throw new Exception("PUT method unimplemented");
+ }
+
+ function delete($url, $headers)
+ {
+ throw new Exception("DELETE method unimplemented");
+ }
+
+ function userAgent()
+ {
+ return "StatusNet/".STATUSNET_VERSION." (".STATUSNET_CODENAME.")";
+ }
+}
diff --git a/lib/logingroupnav.php b/lib/logingroupnav.php
index f740e329a..b545fbf26 100644
--- a/lib/logingroupnav.php
+++ b/lib/logingroupnav.php
@@ -69,30 +69,25 @@ class LoginGroupNav extends Widget
function show()
{
- // action => array('prompt', 'title')
- $menu = array();
+ $action_name = $this->action->trimmed('action');
+
+ $this->action->elementStart('ul', array('class' => 'nav'));
+
+ if (Event::handle('StartLoginGroupNav', array(&$this->action))) {
+
+ $this->action->menuItem(common_local_url('login'),
+ _('Login'),
+ _('Login with a username and password'),
+ $action_name === 'login');
- if (!common_config('site','openidonly')) {
- $menu['login'] = array(_('Login'),
- _('Login with a username and password'));
if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
- $menu['register'] = array(_('Register'),
- _('Sign up for a new account'));
+ $this->action->menuItem(common_local_url('register'),
+ _('Register'),
+ _('Sign up for a new account'),
+ $action_name === 'register');
}
- }
- if (common_config('openid', 'enabled')) {
- $menu['openidlogin'] = array(_('OpenID'),
- _('Login or register with OpenID'));
- }
-
- $action_name = $this->action->trimmed('action');
- $this->action->elementStart('ul', array('class' => 'nav'));
- foreach ($menu as $menuaction => $menudesc) {
- $this->action->menuItem(common_local_url($menuaction),
- $menudesc[0],
- $menudesc[1],
- $action_name === $menuaction);
+ Event::handle('EndLoginGroupNav', array(&$this->action));
}
$this->action->elementEnd('ul');
diff --git a/lib/messageform.php b/lib/messageform.php
index 6431bfdcc..e25ebfa08 100644
--- a/lib/messageform.php
+++ b/lib/messageform.php
@@ -140,12 +140,19 @@ class MessageForm extends Form
'rows' => 4,
'name' => 'content'),
($this->content) ? $this->content : '');
- $this->out->elementStart('dl', 'form_note');
- $this->out->element('dt', null, _('Available characters'));
- $this->out->element('dd', array('id' => 'notice_text-count'),
- '140');
- $this->out->elementEnd('dl');
+ $contentLimit = Message::maxContent();
+
+ $this->out->element('script', array('type' => 'text/javascript'),
+ 'maxLength = ' . $contentLimit . ';');
+
+ if ($contentLimit > 0) {
+ $this->out->elementStart('dl', 'form_note');
+ $this->out->element('dt', null, _('Available characters'));
+ $this->out->element('dd', array('id' => 'notice_text-count'),
+ $contentLimit);
+ $this->out->elementEnd('dl');
+ }
}
/**
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 350e37db8..9864d15eb 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -90,7 +90,7 @@ class NoticeForm extends Form
$this->action = $action;
$this->content = $content;
$this->inreplyto = $inreplyto;
-
+
if ($user) {
$this->user = $user;
} else {
@@ -124,7 +124,6 @@ class NoticeForm extends Form
return common_local_url('newnotice');
}
-
/**
* Legend of the Form
*
@@ -135,7 +134,6 @@ class NoticeForm extends Form
$this->out->element('legend', null, _('Send a notice'));
}
-
/**
* Data elements
*
@@ -144,31 +142,44 @@ class NoticeForm extends Form
function formData()
{
- $this->out->element('label', array('for' => 'notice_data-text'),
- sprintf(_('What\'s up, %s?'), $this->user->nickname));
- // XXX: vary by defined max size
- $this->out->element('textarea', array('id' => 'notice_data-text',
- 'cols' => 35,
- 'rows' => 4,
- 'name' => 'status_textarea'),
- ($this->content) ? $this->content : '');
- $this->out->elementStart('dl', 'form_note');
- $this->out->element('dt', null, _('Available characters'));
- $this->out->element('dd', array('id' => 'notice_text-count'),
- '140');
- $this->out->elementEnd('dl');
- if (common_config('attachments', 'uploads')) {
- $this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
- $this->out->element('input', array('id' => 'notice_data-attach',
- 'type' => 'file',
- 'name' => 'attach',
- 'title' => _('Attach a file')));
- $this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
- }
- if ($this->action) {
- $this->out->hidden('notice_return-to', $this->action, 'returnto');
+ if (Event::handle('StartShowNoticeFormData', array($this))) {
+ $this->out->element('label', array('for' => 'notice_data-text'),
+ sprintf(_('What\'s up, %s?'), $this->user->nickname));
+ // XXX: vary by defined max size
+ $this->out->element('textarea', array('id' => 'notice_data-text',
+ 'cols' => 35,
+ 'rows' => 4,
+ 'name' => 'status_textarea'),
+ ($this->content) ? $this->content : '');
+
+ $contentLimit = Notice::maxContent();
+
+ $this->out->element('script', array('type' => 'text/javascript'),
+ 'maxLength = ' . $contentLimit . ';');
+
+ if ($contentLimit > 0) {
+ $this->out->elementStart('dl', 'form_note');
+ $this->out->element('dt', null, _('Available characters'));
+ $this->out->element('dd', array('id' => 'notice_text-count'),
+ $contentLimit);
+ $this->out->elementEnd('dl');
+ }
+
+ if (common_config('attachments', 'uploads')) {
+ $this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
+ $this->out->element('input', array('id' => 'notice_data-attach',
+ 'type' => 'file',
+ 'name' => 'attach',
+ 'title' => _('Attach a file')));
+ $this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
+ }
+ if ($this->action) {
+ $this->out->hidden('notice_return-to', $this->action, 'returnto');
+ }
+ $this->out->hidden('notice_in-reply-to', $this->inreplyto, 'inreplyto');
+
+ Event::handle('StartShowNoticeFormData', array($this));
}
- $this->out->hidden('notice_in-reply-to', $this->inreplyto, 'inreplyto');
}
/**
diff --git a/lib/noticelist.php b/lib/noticelist.php
index d4cd3ff6e..6c296f82a 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -178,9 +178,12 @@ class NoticeListItem extends Widget
function show()
{
$this->showStart();
- $this->showNotice();
- $this->showNoticeInfo();
- $this->showNoticeOptions();
+ if (Event::handle('StartShowNoticeItem', array($this))) {
+ $this->showNotice();
+ $this->showNoticeInfo();
+ $this->showNoticeOptions();
+ Event::handle('EndShowNoticeItem', array($this));
+ }
$this->showEnd();
}
@@ -469,7 +472,10 @@ class NoticeListItem extends Widget
function showDeleteLink()
{
$user = common_current_user();
- if ($user && $this->notice->profile_id == $user->id) {
+
+ if (!empty($user) &&
+ ($this->notice->profile_id == $user->id || $user->hasRight(Right::deleteOthersNotice))) {
+
$deleteurl = common_local_url('deletenotice',
array('notice' => $this->notice->id));
$this->out->element('a', array('href' => $deleteurl,
diff --git a/lib/oauthstore.php b/lib/oauthstore.php
index 6db07b20f..d617a7df7 100644
--- a/lib/oauthstore.php
+++ b/lib/oauthstore.php
@@ -19,13 +19,12 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once(INSTALLDIR.'/lib/omb.php');
+require_once 'libomb/datastore.php';
class StatusNetOAuthDataStore extends OAuthDataStore
{
// We keep a record of who's contacted us
-
function lookup_consumer($consumer_key)
{
$con = Consumer::staticGet('consumer_key', $consumer_key);
@@ -44,7 +43,9 @@ class StatusNetOAuthDataStore extends OAuthDataStore
function lookup_token($consumer, $token_type, $token_key)
{
$t = new Token();
- $t->consumer_key = $consumer->key;
+ if (!is_null($consumer)) {
+ $t->consumer_key = $consumer->key;
+ }
$t->tok = $token_key;
$t->type = ($token_type == 'access') ? 1 : 0;
if ($t->find(true)) {
@@ -154,4 +155,345 @@ class StatusNetOAuthDataStore extends OAuthDataStore
{
return $this->new_access_token($consumer);
}
+
+ /**
+ * Revoke specified OAuth token
+ *
+ * Revokes the authorization token specified by $token_key.
+ * Throws exceptions in case of error.
+ *
+ * @param string $token_key The token to be revoked
+ *
+ * @access public
+ **/
+ public function revoke_token($token_key) {
+ $rt = new Token();
+ $rt->tok = $token_key;
+ $rt->type = 0;
+ $rt->state = 0;
+ if (!$rt->find(true)) {
+ throw new Exception('Tried to revoke unknown token');
+ }
+ if (!$rt->delete()) {
+ throw new Exception('Failed to delete revoked token');
+ }
+ }
+
+ /**
+ * Authorize specified OAuth token
+ *
+ * Authorizes the authorization token specified by $token_key.
+ * Throws exceptions in case of error.
+ *
+ * @param string $token_key The token to be authorized
+ *
+ * @access public
+ **/
+ public function authorize_token($token_key) {
+ $rt = new Token();
+ $rt->tok = $token_key;
+ $rt->type = 0;
+ $rt->state = 0;
+ if (!$rt->find(true)) {
+ throw new Exception('Tried to authorize unknown token');
+ }
+ $orig_rt = clone($rt);
+ $rt->state = 1; # Authorized but not used
+ if (!$rt->update($orig_rt)) {
+ throw new Exception('Failed to authorize token');
+ }
+ }
+
+ /**
+ * Get profile by identifying URI
+ *
+ * Returns an OMB_Profile object representing the OMB profile identified by
+ * $identifier_uri.
+ * Returns null if there is no such OMB profile.
+ * Throws exceptions in case of other error.
+ *
+ * @param string $identifier_uri The OMB identifier URI specifying the
+ * requested profile
+ *
+ * @access public
+ *
+ * @return OMB_Profile The corresponding profile
+ **/
+ public function getProfile($identifier_uri) {
+ /* getProfile is only used for remote profiles by libomb.
+ TODO: Make it work with local ones anyway. */
+ $remote = Remote_profile::staticGet('uri', $identifier_uri);
+ if (!$remote) throw new Exception('No such remote profile');
+ $profile = Profile::staticGet('id', $remote->id);
+ if (!$profile) throw new Exception('No profile for remote user');
+
+ require_once INSTALLDIR.'/lib/omb.php';
+ return profile_to_omb_profile($identifier_uri, $profile);
+ }
+
+ /**
+ * Save passed profile
+ *
+ * Stores the OMB profile $profile. Overwrites an existing entry.
+ * Throws exceptions in case of error.
+ *
+ * @param OMB_Profile $profile The OMB profile which should be saved
+ *
+ * @access public
+ **/
+ public function saveProfile($omb_profile) {
+ if (common_profile_url($omb_profile->getNickname()) ==
+ $omb_profile->getProfileURL()) {
+ throw new Exception('Not implemented');
+ } else {
+ $remote = Remote_profile::staticGet('uri', $omb_profile->getIdentifierURI());
+
+ if ($remote) {
+ $exists = true;
+ $profile = Profile::staticGet($remote->id);
+ $orig_remote = clone($remote);
+ $orig_profile = clone($profile);
+ # XXX: compare current postNotice and updateProfile URLs to the ones
+ # stored in the DB to avoid (possibly...) above attack
+ } else {
+ $exists = false;
+ $remote = new Remote_profile();
+ $remote->uri = $omb_profile->getIdentifierURI();
+ $profile = new Profile();
+ }
+
+ $profile->nickname = $omb_profile->getNickname();
+ $profile->profileurl = $omb_profile->getProfileURL();
+
+ $fullname = $omb_profile->getFullname();
+ $profile->fullname = is_null($fullname) ? '' : $fullname;
+ $homepage = $omb_profile->getHomepage();
+ $profile->homepage = is_null($homepage) ? '' : $homepage;
+ $bio = $omb_profile->getBio();
+ $profile->bio = is_null($bio) ? '' : $bio;
+ $location = $omb_profile->getLocation();
+ $profile->location = is_null($location) ? '' : $location;
+
+ if ($exists) {
+ $profile->update($orig_profile);
+ } else {
+ $profile->created = DB_DataObject_Cast::dateTime(); # current time
+ $id = $profile->insert();
+ if (!$id) {
+ throw new Exception(_('Error inserting new profile'));
+ }
+ $remote->id = $id;
+ }
+
+ $avatar_url = $omb_profile->getAvatarURL();
+ if ($avatar_url) {
+ if (!$this->add_avatar($profile, $avatar_url)) {
+ throw new Exception(_('Error inserting avatar'));
+ }
+ } else {
+ $avatar = $profile->getOriginalAvatar();
+ if($avatar) $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
+ if($avatar) $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE);
+ if($avatar) $avatar->delete();
+ $avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
+ if($avatar) $avatar->delete();
+ }
+
+ if ($exists) {
+ if (!$remote->update($orig_remote)) {
+ throw new Exception(_('Error updating remote profile'));
+ }
+ } else {
+ $remote->created = DB_DataObject_Cast::dateTime(); # current time
+ if (!$remote->insert()) {
+ throw new Exception(_('Error inserting remote profile'));
+ }
+ }
+ }
+ }
+
+ function add_avatar($profile, $url)
+ {
+ $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
+ copy($url, $temp_filename);
+ $imagefile = new ImageFile($profile->id, $temp_filename);
+ $filename = Avatar::filename($profile->id,
+ image_type_to_extension($imagefile->type),
+ null,
+ common_timestamp());
+ rename($temp_filename, Avatar::path($filename));
+ return $profile->setOriginal($filename);
+ }
+
+ /**
+ * Save passed notice
+ *
+ * Stores the OMB notice $notice. The datastore may change the passed notice.
+ * This might by neccessary for URIs depending on a database key. Note that
+ * it is the user’s duty to present a mechanism for his OMB_Datastore to
+ * appropriately change his OMB_Notice.
+ * Throws exceptions in case of error.
+ *
+ * @param OMB_Notice $notice The OMB notice which should be saved
+ *
+ * @access public
+ **/
+ public function saveNotice(&$omb_notice) {
+ if (Notice::staticGet('uri', $omb_notice->getIdentifierURI())) {
+ throw new Exception(_('Duplicate notice'));
+ }
+ $author_uri = $omb_notice->getAuthor()->getIdentifierURI();
+ common_log(LOG_DEBUG, $author_uri, __FILE__);
+ $author = Remote_profile::staticGet('uri', $author_uri);
+ if (!$author) {
+ $author = User::staticGet('uri', $author_uri);
+ }
+ if (!$author) {
+ throw new Exception('No such user');
+ }
+
+ common_log(LOG_DEBUG, print_r($author, true), __FILE__);
+
+ $notice = Notice::saveNew($author->id,
+ $omb_notice->getContent(),
+ 'omb',
+ false,
+ null,
+ $omb_notice->getIdentifierURI());
+
+ common_broadcast_notice($notice, true);
+ }
+
+ /**
+ * Get subscriptions of a given profile
+ *
+ * Returns an array containing subscription informations for the specified
+ * profile. Every array entry should in turn be an array with keys
+ * 'uri´: The identifier URI of the subscriber
+ * 'token´: The subscribe token
+ * 'secret´: The secret token
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying the
+ * subscribed profile
+ *
+ * @access public
+ *
+ * @return mixed An array containing the subscriptions or 0 if no
+ * subscription has been found.
+ **/
+ public function getSubscriptions($subscribed_user_uri) {
+ $sub = new Subscription();
+
+ $user = $this->_getAnyProfile($subscribed_user_uri);
+
+ $sub->subscribed = $user->id;
+
+ if (!$sub->find(true)) {
+ return 0;
+ }
+
+ /* Since we do not use OMB_Service_Provider’s action methods, there
+ is no need to actually return the subscriptions. */
+ return 1;
+ }
+
+ private function _getAnyProfile($uri)
+ {
+ $user = Remote_profile::staticGet('uri', $uri);
+ if (!$user) {
+ $user = User::staticGet('uri', $uri);
+ }
+ if (!$user) {
+ throw new Exception('No such user');
+ }
+ return $user;
+ }
+
+ /**
+ * Delete a subscription
+ *
+ * Deletes the subscription from $subscriber_uri to $subscribed_user_uri.
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscriber_uri The OMB identifier URI specifying the
+ * subscribing profile
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying the
+ * subscribed profile
+ *
+ * @access public
+ **/
+ public function deleteSubscription($subscriber_uri, $subscribed_user_uri)
+ {
+ $sub = new Subscription();
+
+ $subscribed = $this->_getAnyProfile($subscribed_user_uri);
+ $subscriber = $this->_getAnyProfile($subscriber_uri);
+
+ $sub->subscribed = $subscribed->id;
+ $sub->subscriber = $subscriber->id;
+
+ $sub->delete();
+ }
+
+ /**
+ * Save a subscription
+ *
+ * Saves the subscription from $subscriber_uri to $subscribed_user_uri.
+ * Throws exceptions in case of error.
+ *
+ * @param string $subscriber_uri The OMB identifier URI specifying
+ * the subscribing profile
+ *
+ * @param string $subscribed_user_uri The OMB identifier URI specifying
+ * the subscribed profile
+ * @param OAuthToken $token The access token
+ *
+ * @access public
+ **/
+ public function saveSubscription($subscriber_uri, $subscribed_user_uri,
+ $token)
+ {
+ $sub = new Subscription();
+
+ $subscribed = $this->_getAnyProfile($subscribed_user_uri);
+ $subscriber = $this->_getAnyProfile($subscriber_uri);
+
+ $sub->subscribed = $subscribed->id;
+ $sub->subscriber = $subscriber->id;
+
+ $sub_exists = $sub->find(true);
+
+ if ($sub_exists) {
+ $orig_sub = clone($sub);
+ } else {
+ $sub->created = DB_DataObject_Cast::dateTime();
+ }
+
+ $sub->token = $token->key;
+ $sub->secret = $token->secret;
+
+ if ($sub_exists) {
+ $result = $sub->update($orig_sub);
+ } else {
+ $result = $sub->insert();
+ }
+
+ if (!$result) {
+ common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
+ throw new Exception(_('Couldn\'t insert new subscription.'));
+ return;
+ }
+
+ /* Notify user, if necessary. */
+
+ if ($subscribed instanceof User) {
+ mail_subscribe_notify_profile($subscribed,
+ Profile::staticGet($subscriber->id));
+ }
+ }
}
+?>
diff --git a/lib/omb.php b/lib/omb.php
index 7dca760c6..0566701ff 100644
--- a/lib/omb.php
+++ b/lib/omb.php
@@ -19,34 +19,18 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-require_once('OAuth.php');
-require_once(INSTALLDIR.'/lib/oauthstore.php');
-
-require_once(INSTALLDIR.'/classes/Consumer.php');
-require_once(INSTALLDIR.'/classes/Nonce.php');
-require_once(INSTALLDIR.'/classes/Token.php');
-
-require_once('Auth/Yadis/Yadis.php');
-
-define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
-define('OMB_NAMESPACE', 'http://openmicroblogging.org/protocol/0.1');
-define('OMB_VERSION_01', 'http://openmicroblogging.org/protocol/0.1');
-define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
-
-define('OMB_ENDPOINT_UPDATEPROFILE', OMB_NAMESPACE.'/updateProfile');
-define('OMB_ENDPOINT_POSTNOTICE', OMB_NAMESPACE.'/postNotice');
-define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
-define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
-define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
-define('OAUTH_ENDPOINT_RESOURCE', OAUTH_NAMESPACE.'endpoint/resource');
-define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header');
-define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
-define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
+require_once INSTALLDIR.'/lib/oauthstore.php';
+require_once 'OAuth.php';
+require_once 'libomb/constants.php';
+require_once 'libomb/service_consumer.php';
+require_once 'libomb/notice.php';
+require_once 'libomb/profile.php';
+require_once 'Auth/Yadis/Yadis.php';
function omb_oauth_consumer()
{
static $con = null;
- if (!$con) {
+ if (is_null($con)) {
$con = new OAuthConsumer(common_root_url(), '');
}
return $con;
@@ -55,7 +39,7 @@ function omb_oauth_consumer()
function omb_oauth_server()
{
static $server = null;
- if (!$server) {
+ if (is_null($server)) {
$server = new OAuthServer(omb_oauth_datastore());
$server->add_signature_method(omb_hmac_sha1());
}
@@ -65,7 +49,7 @@ function omb_oauth_server()
function omb_oauth_datastore()
{
static $store = null;
- if (!$store) {
+ if (is_null($store)) {
$store = new StatusNetOAuthDataStore();
}
return $store;
@@ -74,57 +58,18 @@ function omb_oauth_datastore()
function omb_hmac_sha1()
{
static $hmac_method = null;
- if (!$hmac_method) {
+ if (is_null($hmac_method)) {
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
}
return $hmac_method;
}
-function omb_get_services($xrd, $type)
+function omb_broadcast_notice($notice)
{
- return $xrd->services(array(omb_service_filter($type)));
-}
-
-function omb_service_filter($type)
-{
- return create_function('$s',
- 'return omb_match_service($s, \''.$type.'\');');
-}
-
-function omb_match_service($service, $type)
-{
- return in_array($type, $service->getTypes());
-}
-
-function omb_service_uri($service)
-{
- if (!$service) {
- return null;
- }
- $uris = $service->getURIs();
- if (!$uris) {
- return null;
- }
- return $uris[0];
-}
-
-function omb_local_id($service)
-{
- if (!$service) {
- return null;
- }
- $els = $service->getElements('xrd:LocalID');
- if (!$els) {
- return null;
- }
- $el = $els[0];
- return $service->parser->content($el);
-}
-function omb_broadcast_remote_subscribers($notice)
-{
+ $omb_notice = notice_to_omb_notice($notice);
- # First, get remote users subscribed to this profile
+ /* Get remote users subscribed to this profile. */
$rp = new Remote_profile();
$rp->query('SELECT postnoticeurl, token, secret ' .
@@ -135,170 +80,148 @@ function omb_broadcast_remote_subscribers($notice)
$posted = array();
while ($rp->fetch()) {
- if (!array_key_exists($rp->postnoticeurl, $posted)) {
- common_log(LOG_DEBUG, 'Posting to ' . $rp->postnoticeurl);
- if (omb_post_notice_keys($notice, $rp->postnoticeurl, $rp->token, $rp->secret)) {
- common_log(LOG_DEBUG, 'Finished to ' . $rp->postnoticeurl);
- $posted[$rp->postnoticeurl] = true;
- } else {
- common_log(LOG_DEBUG, 'Failed posting to ' . $rp->postnoticeurl);
- }
+ if (isset($posted[$rp->postnoticeurl])) {
+ /* We already posted to this url. */
+ continue;
}
- }
-
- $rp->free();
- unset($rp);
+ common_debug('Posting to ' . $rp->postnoticeurl, __FILE__);
+
+ /* Post notice. */
+ $service = new Laconica_OMB_Service_Consumer(
+ array(OMB_ENDPOINT_POSTNOTICE => $rp->postnoticeurl));
+ try {
+ $service->setToken($rp->token, $rp->secret);
+ $service->postNotice($omb_notice);
+ } catch (Exception $e) {
+ common_log(LOG_ERR, 'Failed posting to ' . $rp->postnoticeurl);
+ common_log(LOG_ERR, 'Error status '.$e);
+ continue;
+ }
+ $posted[$rp->postnoticeurl] = true;
- return true;
-}
+ common_debug('Finished to ' . $rp->postnoticeurl, __FILE__);
+ }
-function omb_post_notice($notice, $remote_profile, $subscription)
-{
- return omb_post_notice_keys($notice, $remote_profile->postnoticeurl, $subscription->token, $subscription->secret);
+ return;
}
-function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret)
+function omb_broadcast_profile($profile)
{
- $user = User::staticGet('id', $notice->profile_id);
+ $user = User::staticGet('id', $profile->id);
if (!$user) {
return false;
}
- $con = omb_oauth_consumer();
+ $profile = $user->getProfile();
- $token = new OAuthToken($tk, $secret);
-
- $url = $postnoticeurl;
- $parsed = parse_url($url);
- $params = array();
- parse_str($parsed['query'], $params);
-
- $req = OAuthRequest::from_consumer_and_token($con, $token,
- 'POST', $url, $params);
-
- $req->set_parameter('omb_version', OMB_VERSION_01);
- $req->set_parameter('omb_listenee', $user->uri);
- $req->set_parameter('omb_notice', $notice->uri);
- $req->set_parameter('omb_notice_content', $notice->content);
- $req->set_parameter('omb_notice_url', common_local_url('shownotice',
- array('notice' =>
- $notice->id)));
- $req->set_parameter('omb_notice_license', common_config('license', 'url'));
+ $omb_profile = profile_to_omb_profile($user->uri, $profile, true);
- $user->free();
- unset($user);
+ /* Get remote users subscribed to this profile. */
+ $rp = new Remote_profile();
- $req->sign_request(omb_hmac_sha1(), $con, $token);
+ $rp->query('SELECT updateprofileurl, token, secret ' .
+ 'FROM subscription JOIN remote_profile ' .
+ 'ON subscription.subscriber = remote_profile.id ' .
+ 'WHERE subscription.subscribed = ' . $profile->id . ' ');
- # We re-use this tool's fetcher, since it's pretty good
+ $posted = array();
- $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+ while ($rp->fetch()) {
+ if (isset($posted[$rp->updateprofileurl])) {
+ /* We already posted to this url. */
+ continue;
+ }
+ common_debug('Posting to ' . $rp->updateprofileurl, __FILE__);
+
+ /* Update profile. */
+ $service = new StatusNet_OMB_Service_Consumer(
+ array(OMB_ENDPOINT_UPDATEPROFILE => $rp->updateprofileurl));
+ try {
+ $service->setToken($rp->token, $rp->secret);
+ $service->updateProfile($omb_profile);
+ } catch (Exception $e) {
+ common_log(LOG_ERR, 'Failed posting to ' . $rp->updateprofileurl);
+ common_log(LOG_ERR, 'Error status '.$e);
+ continue;
+ }
+ $posted[$rp->updateprofileurl] = true;
- if (!$fetcher) {
- common_log(LOG_WARNING, 'Failed to initialize Yadis fetcher.', __FILE__);
- return false;
+ common_debug('Finished to ' . $rp->updateprofileurl, __FILE__);
}
- $result = $fetcher->post($req->get_normalized_http_url(),
- $req->to_postdata(),
- array('User-Agent: StatusNet/' . STATUSNET_VERSION));
-
- if ($result->status == 403) { # not authorized, don't send again
- common_debug('403 result, deleting subscription', __FILE__);
- # FIXME: figure out how to delete this
- # $subscription->delete();
- return false;
- } else if ($result->status != 200) {
- common_debug('Error status '.$result->status, __FILE__);
- return false;
- } else { # success!
- parse_str($result->body, $return);
- if ($return['omb_version'] == OMB_VERSION_01) {
- return true;
- } else {
- return false;
- }
- }
+ return;
}
-function omb_broadcast_profile($profile)
-{
- # First, get remote users subscribed to this profile
- # XXX: use a join here rather than looping through results
- $sub = new Subscription();
- $sub->subscribed = $profile->id;
- if ($sub->find()) {
- $updated = array();
- while ($sub->fetch()) {
- $rp = Remote_profile::staticGet('id', $sub->subscriber);
- if ($rp) {
- if (!array_key_exists($rp->updateprofileurl, $updated)) {
- if (omb_update_profile($profile, $rp, $sub)) {
- $updated[$rp->updateprofileurl] = true;
- }
- }
- }
- }
+class StatusNet_OMB_Service_Consumer extends OMB_Service_Consumer {
+ public function __construct($urls)
+ {
+ $this->services = $urls;
+ $this->datastore = omb_oauth_datastore();
+ $this->oauth_consumer = omb_oauth_consumer();
+ $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
}
+
}
-function omb_update_profile($profile, $remote_profile, $subscription)
+function profile_to_omb_profile($uri, $profile, $force = false)
{
- $user = User::staticGet($profile->id);
- $con = omb_oauth_consumer();
- $token = new OAuthToken($subscription->token, $subscription->secret);
- $url = $remote_profile->updateprofileurl;
- $parsed = parse_url($url);
- $params = array();
- parse_str($parsed['query'], $params);
- $req = OAuthRequest::from_consumer_and_token($con, $token,
- "POST", $url, $params);
- $req->set_parameter('omb_version', OMB_VERSION_01);
- $req->set_parameter('omb_listenee', $user->uri);
- $req->set_parameter('omb_listenee_profile', common_profile_url($profile->nickname));
- $req->set_parameter('omb_listenee_nickname', $profile->nickname);
-
- # We use blanks to force emptying any existing values in these optional fields
-
- $req->set_parameter('omb_listenee_fullname',
- ($profile->fullname) ? $profile->fullname : '');
- $req->set_parameter('omb_listenee_homepage',
- ($profile->homepage) ? $profile->homepage : '');
- $req->set_parameter('omb_listenee_bio',
- ($profile->bio) ? $profile->bio : '');
- $req->set_parameter('omb_listenee_location',
- ($profile->location) ? $profile->location : '');
+ $omb_profile = new OMB_Profile($uri);
+ $omb_profile->setNickname($profile->nickname);
+ $omb_profile->setLicenseURL(common_config('license', 'url'));
+ if (!is_null($profile->fullname)) {
+ $omb_profile->setFullname($profile->fullname);
+ } elseif ($force) {
+ $omb_profile->setFullname('');
+ }
+ if (!is_null($profile->homepage)) {
+ $omb_profile->setHomepage($profile->homepage);
+ } elseif ($force) {
+ $omb_profile->setHomepage('');
+ }
+ if (!is_null($profile->bio)) {
+ $omb_profile->setBio($profile->bio);
+ } elseif ($force) {
+ $omb_profile->setBio('');
+ }
+ if (!is_null($profile->location)) {
+ $omb_profile->setLocation($profile->location);
+ } elseif ($force) {
+ $omb_profile->setLocation('');
+ }
+ if (!is_null($profile->profileurl)) {
+ $omb_profile->setProfileURL($profile->profileurl);
+ } elseif ($force) {
+ $omb_profile->setProfileURL('');
+ }
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
- $req->set_parameter('omb_listenee_avatar',
- ($avatar) ? $avatar->url : '');
+ if ($avatar) {
+ $omb_profile->setAvatarURL($avatar->url);
+ } elseif ($force) {
+ $omb_profile->setAvatarURL('');
+ }
+ return $omb_profile;
+}
- $req->sign_request(omb_hmac_sha1(), $con, $token);
+function notice_to_omb_notice($notice)
+{
+ /* Create an OMB_Notice for $notice. */
+ $user = User::staticGet('id', $notice->profile_id);
- # We re-use this tool's fetcher, since it's pretty good
+ if (!$user) {
+ return null;
+ }
- $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+ $profile = $user->getProfile();
- $result = $fetcher->post($req->get_normalized_http_url(),
- $req->to_postdata(),
- array('User-Agent: StatusNet/' . STATUSNET_VERSION));
+ $omb_notice = new OMB_Notice(profile_to_omb_profile($user->uri, $profile),
+ $notice->uri,
+ $notice->content);
+ $omb_notice->setURL(common_local_url('shownotice', array('notice' =>
+ $notice->id)));
+ $omb_notice->setLicenseURL(common_config('license', 'url'));
- if (empty($result) || !$result) {
- common_debug("Unable to contact " . $req->get_normalized_http_url());
- } else if ($result->status == 403) { # not authorized, don't send again
- common_debug('403 result, deleting subscription', __FILE__);
- $subscription->delete();
- return false;
- } else if ($result->status != 200) {
- common_debug('Error status '.$result->status, __FILE__);
- return false;
- } else { # success!
- parse_str($result->body, $return);
- if (isset($return['omb_version']) && $return['omb_version'] === OMB_VERSION_01) {
- return true;
- } else {
- return false;
- }
- }
+ return $omb_notice;
}
+?>
diff --git a/lib/openid.php b/lib/openid.php
deleted file mode 100644
index 7a2c46f00..000000000
--- a/lib/openid.php
+++ /dev/null
@@ -1,280 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/classes/User_openid.php');
-
-require_once('Auth/OpenID.php');
-require_once('Auth/OpenID/Consumer.php');
-require_once('Auth/OpenID/SReg.php');
-require_once('Auth/OpenID/MySQLStore.php');
-
-# About one year cookie expiry
-
-define('OPENID_COOKIE_EXPIRY', round(365.25 * 24 * 60 * 60));
-define('OPENID_COOKIE_KEY', 'lastusedopenid');
-
-function oid_store()
-{
- static $store = null;
- if (!$store) {
- # Can't be called statically
- $user = new User();
- $conn = $user->getDatabaseConnection();
- $store = new Auth_OpenID_MySQLStore($conn);
- }
- return $store;
-}
-
-function oid_consumer()
-{
- $store = oid_store();
- $consumer = new Auth_OpenID_Consumer($store);
- return $consumer;
-}
-
-function oid_clear_last()
-{
- oid_set_last('');
-}
-
-function oid_set_last($openid_url)
-{
- common_set_cookie(OPENID_COOKIE_KEY,
- $openid_url,
- time() + OPENID_COOKIE_EXPIRY);
-}
-
-function oid_get_last()
-{
- if (empty($_COOKIE[OPENID_COOKIE_KEY])) {
- return null;
- }
- $openid_url = $_COOKIE[OPENID_COOKIE_KEY];
- if ($openid_url && strlen($openid_url) > 0) {
- return $openid_url;
- } else {
- return null;
- }
-}
-
-function oid_link_user($id, $canonical, $display)
-{
-
- $oid = new User_openid();
- $oid->user_id = $id;
- $oid->canonical = $canonical;
- $oid->display = $display;
- $oid->created = DB_DataObject_Cast::dateTime();
-
- if (!$oid->insert()) {
- $err = PEAR::getStaticProperty('DB_DataObject','lastError');
- common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__);
- return false;
- }
-
- return true;
-}
-
-function oid_get_user($openid_url)
-{
- $user = null;
- $oid = User_openid::staticGet('canonical', $openid_url);
- if ($oid) {
- $user = User::staticGet('id', $oid->user_id);
- }
- return $user;
-}
-
-function oid_check_immediate($openid_url, $backto=null)
-{
- if (!$backto) {
- $action = $_REQUEST['action'];
- $args = common_copy_args($_GET);
- unset($args['action']);
- $backto = common_local_url($action, $args);
- }
- common_debug('going back to "' . $backto . '"', __FILE__);
-
- common_ensure_session();
-
- $_SESSION['openid_immediate_backto'] = $backto;
- common_debug('passed-in variable is "' . $backto . '"', __FILE__);
- common_debug('session variable is "' . $_SESSION['openid_immediate_backto'] . '"', __FILE__);
-
- oid_authenticate($openid_url,
- 'finishimmediate',
- true);
-}
-
-function oid_authenticate($openid_url, $returnto, $immediate=false)
-{
-
- $consumer = oid_consumer();
-
- if (!$consumer) {
- common_server_error(_('Cannot instantiate OpenID consumer object.'));
- return false;
- }
-
- common_ensure_session();
-
- $auth_request = $consumer->begin($openid_url);
-
- // Handle failure status return values.
- if (!$auth_request) {
- return _('Not a valid OpenID.');
- } else if (Auth_OpenID::isFailure($auth_request)) {
- return sprintf(_('OpenID failure: %s'), $auth_request->message);
- }
-
- $sreg_request = Auth_OpenID_SRegRequest::build(// Required
- array(),
- // Optional
- array('nickname',
- 'email',
- 'fullname',
- 'language',
- 'timezone',
- 'postcode',
- 'country'));
-
- if ($sreg_request) {
- $auth_request->addExtension($sreg_request);
- }
-
- $trust_root = common_root_url(true);
- $process_url = common_local_url($returnto);
-
- if ($auth_request->shouldSendRedirect()) {
- $redirect_url = $auth_request->redirectURL($trust_root,
- $process_url,
- $immediate);
- if (!$redirect_url) {
- } else if (Auth_OpenID::isFailure($redirect_url)) {
- return sprintf(_('Could not redirect to server: %s'), $redirect_url->message);
- } else {
- common_redirect($redirect_url, 303);
- }
- } else {
- // Generate form markup and render it.
- $form_id = 'openid_message';
- $form_html = $auth_request->formMarkup($trust_root, $process_url,
- $immediate, array('id' => $form_id));
-
- # XXX: This is cheap, but things choke if we don't escape ampersands
- # in the HTML attributes
-
- $form_html = preg_replace('/&/', '&amp;', $form_html);
-
- // Display an error if the form markup couldn't be generated;
- // otherwise, render the HTML.
- if (Auth_OpenID::isFailure($form_html)) {
- common_server_error(sprintf(_('Could not create OpenID form: %s'), $form_html->message));
- } else {
- $action = new AutosubmitAction(); // see below
- $action->form_html = $form_html;
- $action->form_id = $form_id;
- $action->prepare(array('action' => 'autosubmit'));
- $action->handle(array('action' => 'autosubmit'));
- }
- }
-}
-
-# Half-assed attempt at a module-private function
-
-function _oid_print_instructions()
-{
- common_element('div', 'instructions',
- _('This form should automatically submit itself. '.
- 'If not, click the submit button to go to your '.
- 'OpenID provider.'));
-}
-
-# update a user from sreg parameters
-
-function oid_update_user(&$user, &$sreg)
-{
-
- $profile = $user->getProfile();
-
- $orig_profile = clone($profile);
-
- if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) {
- $profile->fullname = $sreg['fullname'];
- }
-
- if ($sreg['country']) {
- if ($sreg['postcode']) {
- # XXX: use postcode to get city and region
- # XXX: also, store postcode somewhere -- it's valuable!
- $profile->location = $sreg['postcode'] . ', ' . $sreg['country'];
- } else {
- $profile->location = $sreg['country'];
- }
- }
-
- # XXX save language if it's passed
- # XXX save timezone if it's passed
-
- if (!$profile->update($orig_profile)) {
- common_server_error(_('Error saving the profile.'));
- return false;
- }
-
- $orig_user = clone($user);
-
- if ($sreg['email'] && Validate::email($sreg['email'], true)) {
- $user->email = $sreg['email'];
- }
-
- if (!$user->update($orig_user)) {
- common_server_error(_('Error saving the user.'));
- return false;
- }
-
- return true;
-}
-
-class AutosubmitAction extends Action
-{
- var $form_html = null;
- var $form_id = null;
-
- function handle($args)
- {
- parent::handle($args);
- $this->showPage();
- }
-
- function title()
- {
- return _('OpenID Auto-Submit');
- }
-
- function showContent()
- {
- $this->raw($this->form_html);
- $this->element('script', null,
- '$(document).ready(function() { ' .
- ' $(\'#'. $this->form_id .'\').submit(); '.
- '});');
- }
-}
diff --git a/lib/plugin.php b/lib/plugin.php
index 87d7be5a7..59bf3ba9d 100644
--- a/lib/plugin.php
+++ b/lib/plugin.php
@@ -76,4 +76,18 @@ class Plugin
{
return true;
}
+
+ /*
+ * the name of the shortener
+ * shortenerInfo associative array with additional information. One possible element is 'freeService' which can be true or false
+ * shortener array, first element is the name of the class, second element is an array to be passed as constructor parameters to the class
+ */
+ function registerUrlShortener($name, $shortenerInfo, $shortener)
+ {
+ global $_shorteners;
+ if(!is_array($_shorteners)){
+ $_shorteners=array();
+ }
+ $_shorteners[$name]=array('info'=>$shortenerInfo, 'callInfo'=>$shortener);
+ }
}
diff --git a/lib/right.php b/lib/right.php
new file mode 100644
index 000000000..4e0096d46
--- /dev/null
+++ b/lib/right.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Class for user rights
+ *
+ * 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 Authorization
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * class for rights
+ *
+ * Mostly for holding the rights constants
+ *
+ * @category Authorization
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class Right
+{
+ const deleteOthersNotice = 'deleteothersnotice';
+}
+
diff --git a/lib/router.php b/lib/router.php
index 5529e60ac..91f886bce 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -50,8 +50,7 @@ class Router
var $m = null;
static $inst = null;
static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
- 'postnotice', 'updateprofile', 'finishremotesubscribe',
- 'finishopenidlogin', 'finishaddopenid');
+ 'postnotice', 'updateprofile', 'finishremotesubscribe');
static function get()
{
@@ -76,7 +75,6 @@ class Router
$m->connect('', array('action' => 'public'));
$m->connect('rss', array('action' => 'publicrss'));
- $m->connect('xrds', array('action' => 'publicxrds'));
$m->connect('featuredrss', array('action' => 'featuredrss'));
$m->connect('favoritedrss', array('action' => 'favoritedrss'));
$m->connect('opensearch/people', array('action' => 'opensearch',
@@ -128,7 +126,6 @@ class Router
// exceptional
- $m->connect('main/openid', array('action' => 'openidlogin'));
$m->connect('main/remote', array('action' => 'remotesubscribe'));
$m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
@@ -138,7 +135,7 @@ class Router
// settings
- foreach (array('profile', 'avatar', 'password', 'openid', 'im',
+ foreach (array('profile', 'avatar', 'password', 'im',
'email', 'sms', 'twitter', 'userdesign', 'other') as $s) {
$m->connect('settings/'.$s, array('action' => $s.'settings'));
}
@@ -244,6 +241,10 @@ class Router
array('nickname' => '[a-zA-Z0-9]+'));
}
+ $m->connect('group/:nickname/foaf',
+ array('action' => 'foafgroup'),
+ array('nickname' => '[a-zA-Z0-9]+'));
+
$m->connect('group/:nickname/blocked',
array('action' => 'blockedfromgroup'),
array('nickname' => '[a-zA-Z0-9]+'));
@@ -463,7 +464,7 @@ class Router
// user stuff
foreach (array('subscriptions', 'subscribers',
- 'nudge', 'xrds', 'all', 'foaf',
+ 'nudge', 'all', 'foaf', 'xrds',
'replies', 'inbox', 'outbox', 'microsummary') as $a) {
$m->connect(':nickname/'.$a,
array('action' => $a),
diff --git a/lib/rssaction.php b/lib/rssaction.php
index 60611e48d..faf6bec7d 100644
--- a/lib/rssaction.php
+++ b/lib/rssaction.php
@@ -78,25 +78,12 @@ class Rss10Action extends Action
function prepare($args)
{
parent::prepare($args);
+
$this->limit = (int) $this->trimmed('limit');
+
if ($this->limit == 0) {
$this->limit = DEFAULT_RSS_LIMIT;
}
- return true;
- }
-
- /**
- * Handle a request
- *
- * @param array $args Arguments from $_REQUEST
- *
- * @return void
- */
-
- function handle($args)
- {
- // Parent handling, including cache check
- parent::handle($args);
if (common_config('site', 'private')) {
if (!isset($_SERVER['PHP_AUTH_USER'])) {
@@ -122,8 +109,21 @@ class Rss10Action extends Action
}
}
- // Get the list of notices
- $this->notices = $this->getNotices($this->limit);
+ return true;
+ }
+
+ /**
+ * Handle a request
+ *
+ * @param array $args Arguments from $_REQUEST
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ // Parent handling, including cache check
+ parent::handle($args);
$this->showRss();
}
@@ -140,7 +140,7 @@ class Rss10Action extends Action
}
/**
- * Get the notices to output in this stream
+ * Get the notices to output in this stream.
*
* @return array an array of Notice objects sorted in reverse chron
*/
@@ -258,26 +258,27 @@ class Rss10Action extends Action
$attachments = $notice->attachments();
if($attachments){
foreach($attachments as $attachment){
- if ($attachment->isEnclosure()) {
+ $enclosure=$attachment->getEnclosure();
+ if ($enclosure) {
// DO NOT move xmlns declaration to root element. Making it
// the default namespace here improves compatibility with
// real-world feed readers.
$attribs = array(
- 'rdf:resource' => $attachment->url,
- 'url' => $attachment->url,
+ 'rdf:resource' => $enclosure->url,
+ 'url' => $enclosure->url,
'xmlns' => 'http://purl.oclc.org/net/rss_2.0/enc#'
);
- if ($attachment->title) {
- $attribs['dc:title'] = $attachment->title;
+ if ($enclosure->title) {
+ $attribs['dc:title'] = $enclosure->title;
}
- if ($attachment->modified) {
- $attribs['dc:date'] = common_date_w3dtf($attachment->modified);
+ if ($enclosure->modified) {
+ $attribs['dc:date'] = common_date_w3dtf($enclosure->modified);
}
- if ($attachment->size) {
- $attribs['length'] = $attachment->size;
+ if ($enclosure->size) {
+ $attribs['length'] = $enclosure->size;
}
- if ($attachment->mimetype) {
- $attribs['type'] = $attachment->mimetype;
+ if ($enclosure->mimetype) {
+ $attribs['type'] = $enclosure->mimetype;
}
$this->element('enclosure', $attribs);
}
diff --git a/lib/schema.php b/lib/schema.php
new file mode 100644
index 000000000..1e0c1f3e9
--- /dev/null
+++ b/lib/schema.php
@@ -0,0 +1,680 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Database schema utilities
+ *
+ * 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 Database
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Class representing the database schema
+ *
+ * A class representing the database schema. Can be used to
+ * manipulate the schema -- especially for plugins and upgrade
+ * utilities.
+ *
+ * @category Database
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class Schema
+{
+ static $_single = null;
+ protected $conn = null;
+
+ /**
+ * Constructor. Only run once for singleton object.
+ */
+
+ protected function __construct()
+ {
+ // XXX: there should be an easier way to do this.
+ $user = new User();
+
+ $this->conn = $user->getDatabaseConnection();
+
+ $user->free();
+
+ unset($user);
+ }
+
+ /**
+ * Main public entry point. Use this to get
+ * the singleton object.
+ *
+ * @return Schema the (single) Schema object
+ */
+
+ static function get()
+ {
+ if (empty(self::$_single)) {
+ self::$_single = new Schema();
+ }
+ return self::$_single;
+ }
+
+ /**
+ * Returns a TableDef object for the table
+ * in the schema with the given name.
+ *
+ * Throws an exception if the table is not found.
+ *
+ * @param string $name Name of the table to get
+ *
+ * @return TableDef tabledef for that table.
+ */
+
+ public function getTableDef($name)
+ {
+ $res =& $this->conn->query('DESCRIBE ' . $name);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ $td = new TableDef();
+
+ $td->name = $name;
+ $td->columns = array();
+
+ $row = array();
+
+ while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
+
+ $cd = new ColumnDef();
+
+ $cd->name = $row['Field'];
+
+ $packed = $row['Type'];
+
+ if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) {
+ $cd->type = $match[1];
+ $cd->size = $match[2];
+ } else {
+ $cd->type = $packed;
+ }
+
+ $cd->nullable = ($row['Null'] == 'YES') ? true : false;
+ $cd->key = $row['Key'];
+ $cd->default = $row['Default'];
+ $cd->extra = $row['Extra'];
+
+ $td->columns[] = $cd;
+ }
+
+ return $td;
+ }
+
+ /**
+ * Gets a ColumnDef object for a single column.
+ *
+ * Throws an exception if the table is not found.
+ *
+ * @param string $table name of the table
+ * @param string $column name of the column
+ *
+ * @return ColumnDef definition of the column or null
+ * if not found.
+ */
+
+ public function getColumnDef($table, $column)
+ {
+ $td = $this->getTableDef($table);
+
+ foreach ($td->columns as $cd) {
+ if ($cd->name == $column) {
+ return $cd;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a table with the given names and columns.
+ *
+ * @param string $name Name of the table
+ * @param array $columns Array of ColumnDef objects
+ * for new table.
+ *
+ * @return boolean success flag
+ */
+
+ public function createTable($name, $columns)
+ {
+ $uniques = array();
+ $primary = array();
+ $indices = array();
+
+ $sql = "CREATE TABLE $name (\n";
+
+ for ($i = 0; $i < count($columns); $i++) {
+
+ $cd =& $columns[$i];
+
+ if ($i > 0) {
+ $sql .= ",\n";
+ }
+
+ $sql .= $this->_columnSql($cd);
+
+ switch ($cd->key) {
+ case 'UNI':
+ $uniques[] = $cd->name;
+ break;
+ case 'PRI':
+ $primary[] = $cd->name;
+ break;
+ case 'MUL':
+ $indices[] = $cd->name;
+ break;
+ }
+ }
+
+ if (count($primary) > 0) { // it really should be...
+ $sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")";
+ }
+
+ foreach ($uniques as $u) {
+ $sql .= ",\nunique index {$name}_{$u}_idx ($u)";
+ }
+
+ foreach ($indices as $i) {
+ $sql .= ",\nindex {$name}_{$i}_idx ($i)";
+ }
+
+ $sql .= "); ";
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Drops a table from the schema
+ *
+ * Throws an exception if the table is not found.
+ *
+ * @param string $name Name of the table to drop
+ *
+ * @return boolean success flag
+ */
+
+ public function dropTable($name)
+ {
+ $res =& $this->conn->query("DROP TABLE $name");
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Adds an index to a table.
+ *
+ * If no name is provided, a name will be made up based
+ * on the table name and column names.
+ *
+ * Throws an exception on database error, esp. if the table
+ * does not exist.
+ *
+ * @param string $table Name of the table
+ * @param array $columnNames Name of columns to index
+ * @param string $name (Optional) name of the index
+ *
+ * @return boolean success flag
+ */
+
+ public function createIndex($table, $columnNames, $name=null)
+ {
+ if (!is_array($columnNames)) {
+ $columnNames = array($columnNames);
+ }
+
+ if (empty($name)) {
+ $name = "$table_".implode("_", $columnNames)."_idx";
+ }
+
+ $res =& $this->conn->query("ALTER TABLE $table ".
+ "ADD INDEX $name (".
+ implode(",", $columnNames).")");
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Drops a named index from a table.
+ *
+ * @param string $table name of the table the index is on.
+ * @param string $name name of the index
+ *
+ * @return boolean success flag
+ */
+
+ public function dropIndex($table, $name)
+ {
+ $res =& $this->conn->query("ALTER TABLE $table DROP INDEX $name");
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Adds a column to a table
+ *
+ * @param string $table name of the table
+ * @param ColumnDef $columndef Definition of the new
+ * column.
+ *
+ * @return boolean success flag
+ */
+
+ public function addColumn($table, $columndef)
+ {
+ $sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef);
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Modifies a column in the schema.
+ *
+ * The name must match an existing column and table.
+ *
+ * @param string $table name of the table
+ * @param ColumnDef $columndef new definition of the column.
+ *
+ * @return boolean success flag
+ */
+
+ public function modifyColumn($table, $columndef)
+ {
+ $sql = "ALTER TABLE $table MODIFY COLUMN " .
+ $this->_columnSql($columndef);
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Drops a column from a table
+ *
+ * The name must match an existing column.
+ *
+ * @param string $table name of the table
+ * @param string $columnName name of the column to drop
+ *
+ * @return boolean success flag
+ */
+
+ public function dropColumn($table, $columnName)
+ {
+ $sql = "ALTER TABLE $table DROP COLUMN $columnName";
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Ensures that a table exists with the given
+ * name and the given column definitions.
+ *
+ * If the table does not yet exist, it will
+ * create the table. If it does exist, it will
+ * alter the table to match the column definitions.
+ *
+ * @param string $tableName name of the table
+ * @param array $columns array of ColumnDef
+ * objects for the table
+ *
+ * @return boolean success flag
+ */
+
+ public function ensureTable($tableName, $columns)
+ {
+ // XXX: DB engine portability -> toilet
+
+ try {
+ $td = $this->getTableDef($tableName);
+ } catch (Exception $e) {
+ if (preg_match('/no such table/', $e->getMessage())) {
+ return $this->createTable($tableName, $columns);
+ } else {
+ throw $e;
+ }
+ }
+
+ $cur = $this->_names($td->columns);
+ $new = $this->_names($columns);
+
+ $toadd = array_diff($new, $cur);
+ $todrop = array_diff($cur, $new);
+ $same = array_intersect($new, $cur);
+ $tomod = array();
+
+ foreach ($same as $m) {
+ $curCol = $this->_byName($td->columns, $m);
+ $newCol = $this->_byName($columns, $m);
+
+ if (!$newCol->equals($curCol)) {
+ $tomod[] = $newCol->name;
+ }
+ }
+
+ if (count($toadd) + count($todrop) + count($tomod) == 0) {
+ // nothing to do
+ return true;
+ }
+
+ // For efficiency, we want this all in one
+ // query, instead of using our methods.
+
+ $phrase = array();
+
+ foreach ($toadd as $columnName) {
+ $cd = $this->_byName($columns, $columnName);
+
+ $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
+ }
+
+ foreach ($todrop as $columnName) {
+ $phrase[] = 'DROP COLUMN ' . $columnName;
+ }
+
+ foreach ($tomod as $columnName) {
+ $cd = $this->_byName($columns, $columnName);
+
+ $phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
+ }
+
+ $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
+
+ $res =& $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the array of names from an array of
+ * ColumnDef objects.
+ *
+ * @param array $cds array of ColumnDef objects
+ *
+ * @return array strings for name values
+ */
+
+ private function _names($cds)
+ {
+ $names = array();
+
+ foreach ($cds as $cd) {
+ $names[] = $cd->name;
+ }
+
+ return $names;
+ }
+
+ /**
+ * Get a ColumnDef from an array matching
+ * name.
+ *
+ * @param array $cds Array of ColumnDef objects
+ * @param string $name Name of the column
+ *
+ * @return ColumnDef matching item or null if no match.
+ */
+
+ private function _byName($cds, $name)
+ {
+ foreach ($cds as $cd) {
+ if ($cd->name == $name) {
+ return $cd;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Return the proper SQL for creating or
+ * altering a column.
+ *
+ * Appropriate for use in CREATE TABLE or
+ * ALTER TABLE statements.
+ *
+ * @param ColumnDef $cd column to create
+ *
+ * @return string correct SQL for that column
+ */
+
+ private function _columnSql($cd)
+ {
+ $sql = "{$cd->name} ";
+
+ if (!empty($cd->size)) {
+ $sql .= "{$cd->type}({$cd->size}) ";
+ } else {
+ $sql .= "{$cd->type} ";
+ }
+
+ if (!empty($cd->default)) {
+ $sql .= "default {$cd->default} ";
+ } else {
+ $sql .= ($cd->nullable) ? "null " : "not null ";
+ }
+
+ return $sql;
+ }
+}
+
+/**
+ * A class encapsulating the structure of a table.
+ *
+ * @category Database
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class TableDef
+{
+ /** name of the table */
+ public $name;
+ /** array of ColumnDef objects for the columns. */
+ public $columns;
+}
+
+/**
+ * A class encapsulating the structure of a column in a table.
+ *
+ * @category Database
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ColumnDef
+{
+ /** name of the column. */
+ public $name;
+ /** type of column, e.g. 'int', 'varchar' */
+ public $type;
+ /** size of the column. */
+ public $size;
+ /** boolean flag; can it be null? */
+ public $nullable;
+ /**
+ * type of key: null = no key; 'PRI' => primary;
+ * 'UNI' => unique key; 'MUL' => multiple values.
+ */
+ public $key;
+ /** default value if any. */
+ public $default;
+ /** 'extra' stuff. Returned by MySQL, largely
+ * unused. */
+ public $extra;
+
+ /**
+ * Constructor.
+ *
+ * @param string $name name of the column
+ * @param string $type type of the column
+ * @param int $size size of the column
+ * @param boolean $nullable can this be null?
+ * @param string $key type of key
+ * @param value $default default value
+ * @param value $extra unused
+ */
+
+ function __construct($name=null, $type=null, $size=null,
+ $nullable=true, $key=null, $default=null,
+ $extra=null)
+ {
+ $this->name = strtolower($name);
+ $this->type = strtolower($type);
+ $this->size = $size+0;
+ $this->nullable = $nullable;
+ $this->key = $key;
+ $this->default = $default;
+ $this->extra = $extra;
+ }
+
+ /**
+ * Compares this columndef with another to see
+ * if they're functionally equivalent.
+ *
+ * @param ColumnDef $other column to compare
+ *
+ * @return boolean true if equivalent, otherwise false.
+ */
+
+ function equals($other)
+ {
+ return ($this->name == $other->name &&
+ $this->_typeMatch($other) &&
+ $this->_defaultMatch($other) &&
+ $this->_nullMatch($other) &&
+ $this->key == $other->key);
+ }
+
+ /**
+ * Does the type of this column match the
+ * type of the other column?
+ *
+ * Checks the type and size of a column. Tries
+ * to ignore differences between synonymous
+ * data types, like 'integer' and 'int'.
+ *
+ * @param ColumnDef $other other column to check
+ *
+ * @return boolean true if they're about equivalent
+ */
+
+ private function _typeMatch($other)
+ {
+ switch ($this->type) {
+ case 'integer':
+ case 'int':
+ return ($other->type == 'integer' ||
+ $other->type == 'int');
+ break;
+ default:
+ return ($this->type == $other->type &&
+ $this->size == $other->size);
+ }
+ }
+
+ /**
+ * Does the default behaviour of this column match
+ * the other?
+ *
+ * @param ColumnDef $other other column to check
+ *
+ * @return boolean true if defaults are effectively the same.
+ */
+
+ private function _defaultMatch($other)
+ {
+ return ((is_null($this->default) && is_null($other->default)) ||
+ ($this->default == $other->default));
+ }
+
+ /**
+ * Does the null behaviour of this column match
+ * the other?
+ *
+ * @param ColumnDef $other other column to check
+ *
+ * @return boolean true if these columns 'null' the same.
+ */
+
+ private function _nullMatch($other)
+ {
+ return ((!is_null($this->default) && !is_null($other->default) &&
+ $this->default == $other->default) ||
+ ($this->nullable == $other->nullable));
+ }
+}
diff --git a/lib/settingsaction.php b/lib/settingsaction.php
index a1f305f5b..c3669868d 100644
--- a/lib/settingsaction.php
+++ b/lib/settingsaction.php
@@ -77,9 +77,7 @@ class SettingsAction extends CurrentUserDesignAction
// _all_ our settings are important
common_set_returnto($this->selfUrl());
$user = common_current_user();
- if ($user->hasOpenID()) {
- common_redirect(common_local_url('openidlogin'), 303);
- } else {
+ if (Event::handle('RedirectToLogin', array($this, $user))) {
common_redirect(common_local_url('login'), 303);
}
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
diff --git a/lib/twitter.php b/lib/twitter.php
index 676c9b20a..b49e2e119 100644
--- a/lib/twitter.php
+++ b/lib/twitter.php
@@ -165,9 +165,10 @@ function broadcast_twitter($notice)
}
function broadcast_oauth($notice, $flink) {
-
$user = $flink->getUser();
$statustxt = format_status($notice);
+ // Convert !groups to #hashes
+ $statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt);
$token = TwitterOAuthClient::unpackToken($flink->credentials);
$client = new TwitterOAuthClient($token->key, $token->secret);
$status = null;
@@ -222,6 +223,10 @@ function broadcast_basicauth($notice, $flink)
$user->nickname, $user->id);
common_log(LOG_WARNING, $errmsg);
+ $errmsg = sprintf('No data returned by Twitter API when ' .
+ 'trying to send update for %1$s (user id %2$s).',
+ $user->nickname, $user->id);
+ common_log(LOG_WARNING, $errmsg);
return false;
}
diff --git a/lib/twitterapi.php b/lib/twitterapi.php
index 3bac400e2..4a5de6ab3 100644
--- a/lib/twitterapi.php
+++ b/lib/twitterapi.php
@@ -274,11 +274,12 @@ class TwitterapiAction extends Action
$enclosures = array();
foreach ($attachments as $attachment) {
- if ($attachment->isEnclosure()) {
+ $enclosure_o=$attachment->getEnclosure();
+ if ($enclosure_o) {
$enclosure = array();
- $enclosure['url'] = $attachment->url;
- $enclosure['mimetype'] = $attachment->mimetype;
- $enclosure['size'] = $attachment->size;
+ $enclosure['url'] = $enclosure_o->url;
+ $enclosure['mimetype'] = $enclosure_o->mimetype;
+ $enclosure['size'] = $enclosure_o->size;
$enclosures[] = $enclosure;
}
}
@@ -594,7 +595,6 @@ class TwitterapiAction extends Action
$this->init_document('rss');
- $this->elementStart('channel');
$this->element('title', null, $title);
$this->element('link', null, $link);
if (!is_null($suplink)) {
@@ -620,7 +620,6 @@ class TwitterapiAction extends Action
}
}
- $this->elementEnd('channel');
$this->end_twitter_rss();
}
@@ -667,7 +666,6 @@ class TwitterapiAction extends Action
$this->init_document('rss');
- $this->elementStart('channel');
$this->element('title', null, $title);
$this->element('link', null, $link);
$this->element('description', null, $subtitle);
@@ -686,7 +684,6 @@ class TwitterapiAction extends Action
}
}
- $this->elementEnd('channel');
$this->end_twitter_rss();
}
@@ -999,11 +996,14 @@ class TwitterapiAction extends Action
function init_twitter_rss()
{
$this->startXML();
- $this->elementStart('rss', array('version' => '2.0'));
+ $this->elementStart('rss', array('version' => '2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom'));
+ $this->elementStart('channel');
+ Event::handle('StartApiRss', array($this));
}
function end_twitter_rss()
{
+ $this->elementEnd('channel');
$this->elementEnd('rss');
$this->endXML();
}
@@ -1015,6 +1015,7 @@ class TwitterapiAction extends Action
$this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom',
'xml:lang' => 'en-US',
'xmlns:thr' => 'http://purl.org/syndication/thread/1.0'));
+ Event::handle('StartApiAtom', array($this));
}
function end_twitter_atom()
diff --git a/lib/twitteroauthclient.php b/lib/twitteroauthclient.php
index e37fa05f0..bad2b74ca 100644
--- a/lib/twitteroauthclient.php
+++ b/lib/twitteroauthclient.php
@@ -118,7 +118,7 @@ class TwitterOAuthClient extends OAuthClient
}
/**
- * Calls Twitter's /stutuses/update API method
+ * Calls Twitter's /statuses/update API method
*
* @param string $status text of the status
* @param int $in_reply_to_status_id optional id of the status it's
@@ -137,7 +137,7 @@ class TwitterOAuthClient extends OAuthClient
}
/**
- * Calls Twitter's /stutuses/friends_timeline API method
+ * Calls Twitter's /statuses/friends_timeline API method
*
* @param int $since_id show statuses after this id
* @param int $max_id show statuses before this id
@@ -167,7 +167,7 @@ class TwitterOAuthClient extends OAuthClient
}
/**
- * Calls Twitter's /stutuses/friends API method
+ * Calls Twitter's /statuses/friends API method
*
* @param int $id id of the user whom you wish to see friends of
* @param int $user_id numerical user id
@@ -197,7 +197,7 @@ class TwitterOAuthClient extends OAuthClient
}
/**
- * Calls Twitter's /stutuses/friends/ids API method
+ * Calls Twitter's /statuses/friends/ids API method
*
* @param int $id id of the user whom you wish to see friends of
* @param int $user_id numerical user id
diff --git a/lib/unqueuemanager.php b/lib/unqueuemanager.php
index 3cdad0b54..6cfe5bcbd 100644
--- a/lib/unqueuemanager.php
+++ b/lib/unqueuemanager.php
@@ -39,7 +39,7 @@ class UnQueueManager
case 'omb':
if ($this->_isLocal($notice)) {
require_once(INSTALLDIR.'/lib/omb.php');
- omb_broadcast_remote_subscribers($notice);
+ omb_broadcast_notice($notice);
}
break;
case 'public':
@@ -72,8 +72,13 @@ class UnQueueManager
require_once(INSTALLDIR.'/lib/jabber.php');
jabber_broadcast_notice($notice);
break;
+ case 'plugin':
+ Event::handle('HandleQueuedNotice', array(&$notice));
+ break;
default:
- throw ServerException("UnQueueManager: Unknown queue: $type");
+ if (Event::handle('UnqueueHandleNotice', array(&$notice, $queue))) {
+ throw ServerException("UnQueueManager: Unknown queue: $queue");
+ }
}
}
diff --git a/lib/util.php b/lib/util.php
index 9b299cb14..be10647fc 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -391,10 +391,10 @@ function common_render_content($text, $notice)
{
$r = common_render_text($text);
$id = $notice->profile_id;
- $r = preg_replace('/(^|\s+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r);
+ $r = preg_replace('/(^|[\s\.\,\:\;]+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r);
$r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r);
- $r = preg_replace('/(^|\s+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r);
- $r = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
+ $r = preg_replace('/(^|[\s\.\,\:\;]+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r);
+ $r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
return $r;
}
@@ -493,7 +493,7 @@ function callback_helper($matches, $callback, $notice_id) {
}while($original_url!=$url);
if(empty($notice_id)){
- $result = call_user_func_array($callback,$url);
+ $result = call_user_func_array($callback, array($url));
}else{
$result = call_user_func_array($callback, array(array($url,$notice_id)) );
}
@@ -522,21 +522,22 @@ function common_linkify($url) {
if(strpos($url, '@') !== false && strpos($url, ':') === false) {
//url is an email address without the mailto: protocol
- return XMLStringer::estring('a', array('href' => "mailto:$url", 'rel' => 'external'), $url);
- }
+ $canon = "mailto:$url";
+ $longurl = "mailto:$url";
+ }else{
- $canon = File_redirection::_canonUrl($url);
+ $canon = File_redirection::_canonUrl($url);
- $longurl_data = File_redirection::where($url);
- if (is_array($longurl_data)) {
- $longurl = $longurl_data['url'];
- } elseif (is_string($longurl_data)) {
- $longurl = $longurl_data;
- } else {
- throw new ServerException("Can't linkify url '$url'");
+ $longurl_data = File_redirection::where($canon);
+ if (is_array($longurl_data)) {
+ $longurl = $longurl_data['url'];
+ } elseif (is_string($longurl_data)) {
+ $longurl = $longurl_data;
+ } else {
+ throw new ServerException("Can't linkify url '$url'");
+ }
}
-
- $attrs = array('href' => $canon, 'rel' => 'external');
+ $attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external');
$is_attachment = false;
$attachment_id = null;
@@ -584,7 +585,8 @@ function common_linkify($url) {
function common_shorten_links($text)
{
- if (mb_strlen($text) <= 140) return $text;
+ $maxLength = Notice::maxContent();
+ if ($maxLength == 0 || mb_strlen($text) <= $maxLength) return $text;
return common_replace_urls_callback($text, array('File_redirection', 'makeShort'));
}
@@ -729,14 +731,10 @@ function common_relative_profile($sender, $nickname, $dt=null)
function common_local_url($action, $args=null, $params=null, $fragment=null)
{
- static $sensitive = array('login', 'register', 'passwordsettings',
- 'twittersettings', 'finishopenidlogin',
- 'finishaddopenid', 'api');
-
$r = Router::get();
$path = $r->build($action, $args, $params, $fragment);
- $ssl = in_array($action, $sensitive);
+ $ssl = common_is_sensitive($action);
if (common_config('site','fancy')) {
$url = common_path(mb_substr($path, 1), $ssl);
@@ -750,6 +748,19 @@ function common_local_url($action, $args=null, $params=null, $fragment=null)
return $url;
}
+function common_is_sensitive($action)
+{
+ static $sensitive = array('login', 'register', 'passwordsettings',
+ 'twittersettings', 'api');
+ $ssl = null;
+
+ if (Event::handle('SensitiveAction', array($action, &$ssl))) {
+ $ssl = in_array($action, $sensitive);
+ }
+
+ return $ssl;
+}
+
function common_path($relative, $ssl=false)
{
$pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
@@ -888,7 +899,8 @@ function common_enqueue_notice($notice)
'twitter',
'facebook',
'ping');
- static $allTransports = array('sms');
+
+ static $allTransports = array('sms', 'plugin');
$transports = $allTransports;
@@ -906,11 +918,16 @@ function common_enqueue_notice($notice)
}
}
- $qm = QueueManager::get();
+ if (Event::handle('StartEnqueueNotice', array($notice, &$transports))) {
+
+ $qm = QueueManager::get();
+
+ foreach ($transports as $transport)
+ {
+ $qm->enqueue($notice, $transport);
+ }
- foreach ($transports as $transport)
- {
- $qm->enqueue($notice, $transport);
+ Event::handle('EndEnqueueNotice', array($notice, $transports));
}
return true;
@@ -1148,7 +1165,7 @@ function common_negotiate_type($cprefs, $sprefs)
}
if ('text/html' === $besttype) {
- return "text/html";
+ return "text/html; charset=utf-8";
}
return $besttype;
}
@@ -1156,7 +1173,8 @@ function common_negotiate_type($cprefs, $sprefs)
function common_config($main, $sub)
{
global $config;
- return isset($config[$main][$sub]) ? $config[$main][$sub] : false;
+ return (array_key_exists($main, $config) &&
+ array_key_exists($sub, $config[$main])) ? $config[$main][$sub] : false;
}
function common_copy_args($from)
@@ -1363,57 +1381,19 @@ function common_shorten_url($long_url)
} else {
$svc = $user->urlshorteningservice;
}
-
- $curlh = curl_init();
- curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait
- curl_setopt($curlh, CURLOPT_USERAGENT, 'StatusNet');
- curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
-
- switch($svc) {
- case 'ur1.ca':
- require_once INSTALLDIR.'/lib/Shorturl_api.php';
- $short_url_service = new LilUrl;
- $short_url = $short_url_service->shorten($long_url);
- break;
-
- case '2tu.us':
- $short_url_service = new TightUrl;
- require_once INSTALLDIR.'/lib/Shorturl_api.php';
- $short_url = $short_url_service->shorten($long_url);
- break;
-
- case 'ptiturl.com':
- require_once INSTALLDIR.'/lib/Shorturl_api.php';
- $short_url_service = new PtitUrl;
- $short_url = $short_url_service->shorten($long_url);
- break;
-
- case 'bit.ly':
- curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($long_url));
- $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl;
- break;
-
- case 'is.gd':
- curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($long_url));
- $short_url = curl_exec($curlh);
- break;
- case 'snipr.com':
- curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($long_url));
- $short_url = curl_exec($curlh);
- break;
- case 'metamark.net':
- curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($long_url));
- $short_url = curl_exec($curlh);
- break;
- case 'tinyurl.com':
- curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($long_url));
- $short_url = curl_exec($curlh);
- break;
- default:
- $short_url = false;
+ global $_shorteners;
+ if (!isset($_shorteners[$svc])) {
+ //the user selected service doesn't exist, so default to ur1.ca
+ $svc = 'ur1.ca';
+ }
+ if (!isset($_shorteners[$svc])) {
+ // no shortener plugins installed.
+ return $long_url;
}
- curl_close($curlh);
+ $reflectionObj = new ReflectionClass($_shorteners[$svc]['callInfo'][0]);
+ $short_url_service = $reflectionObj->newInstanceArgs($_shorteners[$svc]['callInfo'][1]);
+ $short_url = $short_url_service->shorten($long_url);
return $short_url;
}