summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Shorturl_api.php71
-rw-r--r--lib/accountsettingsaction.php58
-rw-r--r--lib/action.php57
-rw-r--r--lib/adminpanelaction.php282
-rw-r--r--lib/api.php73
-rw-r--r--lib/apiprivateauth.php82
-rw-r--r--lib/blockform.php99
-rw-r--r--lib/command.php219
-rw-r--r--lib/commandinterpreter.php35
-rw-r--r--lib/common.php25
-rw-r--r--lib/curlclient.php179
-rw-r--r--lib/default.php11
-rw-r--r--lib/deleteuserform.php79
-rw-r--r--lib/designsettings.php13
-rw-r--r--lib/error.php4
-rw-r--r--lib/grouplist.php34
-rw-r--r--lib/groupnav.php79
-rw-r--r--lib/htmloutputter.php4
-rw-r--r--lib/httpclient.php213
-rw-r--r--lib/imagefile.php7
-rw-r--r--lib/jabber.php8
-rw-r--r--lib/language.php70
-rw-r--r--lib/location.php188
-rw-r--r--lib/mail.php19
-rw-r--r--lib/mailbox.php2
-rw-r--r--lib/mediafile.php289
-rw-r--r--lib/messageform.php13
-rw-r--r--lib/noticeform.php13
-rw-r--r--lib/noticelist.php41
-rw-r--r--lib/noticesection.php2
-rw-r--r--lib/oauthclient.php65
-rw-r--r--lib/oauthstore.php8
-rw-r--r--lib/ping.php12
-rw-r--r--lib/plugin.php14
-rw-r--r--lib/profileactionform.php187
-rw-r--r--lib/profileformaction.php139
-rw-r--r--lib/profilelist.php35
-rw-r--r--lib/right.php13
-rw-r--r--lib/router.php966
-rw-r--r--lib/sandboxform.php80
-rw-r--r--lib/schema.php41
-rw-r--r--lib/search_engines.php71
-rw-r--r--lib/silenceform.php80
-rw-r--r--lib/snapshot.php21
-rw-r--r--lib/subs.php8
-rw-r--r--lib/theme.php227
-rw-r--r--lib/unblockform.php98
-rw-r--r--lib/unsandboxform.php82
-rw-r--r--lib/unsilenceform.php80
-rw-r--r--lib/userprofile.php358
-rw-r--r--lib/util.php113
-rw-r--r--lib/xrdsoutputter.php96
52 files changed, 3689 insertions, 1374 deletions
diff --git a/lib/Shorturl_api.php b/lib/Shorturl_api.php
deleted file mode 100644
index 18ae7719b..000000000
--- a/lib/Shorturl_api.php
+++ /dev/null
@@ -1,71 +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); }
-
-abstract class ShortUrlApi
-{
- protected $service_url;
- protected $long_limit = 27;
-
- function __construct($service_url)
- {
- $this->service_url = $service_url;
- }
-
- function shorten($url)
- {
- if ($this->is_long($url)) return $this->shorten_imp($url);
- return $url;
- }
-
- protected abstract function shorten_imp($url);
-
- protected function is_long($url) {
- return strlen($url) >= common_config('site', 'shorturllength');
- }
-
- protected function http_post($data) {
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $this->service_url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
- $response = curl_exec($ch);
- $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- curl_close($ch);
- if (($code < 200) || ($code >= 400)) return false;
- return $response;
- }
-
- protected function http_get($url) {
- $encoded_url = urlencode($url);
- return file_get_contents("{$this->service_url}$encoded_url");
- }
-
- protected function tidy($response) {
- $response = str_replace('&nbsp;', ' ', $response);
- $config = array('output-xhtml' => true);
- $tidy = new tidy;
- $tidy->parseString($response, $config, 'utf8');
- $tidy->cleanRepair();
- return (string)$tidy;
- }
-}
-
diff --git a/lib/accountsettingsaction.php b/lib/accountsettingsaction.php
index a004a3ed9..c79a1f5d7 100644
--- a/lib/accountsettingsaction.php
+++ b/lib/accountsettingsaction.php
@@ -102,32 +102,31 @@ class AccountSettingsNav extends Widget
$this->action->elementStart('ul', array('class' => 'nav'));
if (Event::handle('StartAccountSettingsNav', array(&$this->action))) {
+ $user = common_current_user();
- $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);
+ if(Event::handle('StartAccountSettingsProfileMenuItem', array($this, &$menu))){
+ $this->showMenuItem('profilesettings',_('Profile'),_('Change your profile settings'));
+ Event::handle('EndAccountSettingsProfileMenuItem', array($this, &$menu));
+ }
+ if(Event::handle('StartAccountSettingsAvatarMenuItem', array($this, &$menu))){
+ $this->showMenuItem('avatarsettings',_('Avatar'),_('Upload an avatar'));
+ Event::handle('EndAccountSettingsAvatarMenuItem', array($this, &$menu));
+ }
+ if(Event::handle('StartAccountSettingsPasswordMenuItem', array($this, &$menu))){
+ $this->showMenuItem('passwordsettings',_('Password'),_('Change your password'));
+ Event::handle('EndAccountSettingsPasswordMenuItem', array($this, &$menu));
+ }
+ if(Event::handle('StartAccountSettingsEmailMenuItem', array($this, &$menu))){
+ $this->showMenuItem('emailsettings',_('Email'),_('Change email handling'));
+ Event::handle('EndAccountSettingsEmailMenuItem', array($this, &$menu));
+ }
+ if(Event::handle('StartAccountSettingsDesignMenuItem', array($this, &$menu))){
+ $this->showMenuItem('userdesignsettings',_('Design'),_('Design your profile'));
+ Event::handle('EndAccountSettingsDesignMenuItem', array($this, &$menu));
+ }
+ if(Event::handle('StartAccountSettingsOtherMenuItem', array($this, &$menu))){
+ $this->showMenuItem('othersettings',_('Other'),_('Other options'));
+ Event::handle('EndAccountSettingsOtherMenuItem', array($this, &$menu));
}
Event::handle('EndAccountSettingsNav', array(&$this->action));
@@ -135,4 +134,13 @@ class AccountSettingsNav extends Widget
$this->action->elementEnd('ul');
}
+
+ function showMenuItem($menuaction, $desc1, $desc2)
+ {
+ $action_name = $this->action->trimmed('action');
+ $this->action->menuItem(common_local_url($menuaction),
+ $desc1,
+ $desc2,
+ $action_name === $menuaction);
+ }
}
diff --git a/lib/action.php b/lib/action.php
index 1b2f73752..9c7060bba 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -168,7 +168,7 @@ class Action extends HTMLOutputter // lawsuit
{
if (is_readable(INSTALLDIR . '/theme/' . common_config('site', 'theme') . '/favicon.ico')) {
$this->element('link', array('rel' => 'shortcut icon',
- 'href' => theme_path('favicon.ico')));
+ 'href' => Theme::path('favicon.ico')));
} else {
$this->element('link', array('rel' => 'shortcut icon',
'href' => common_path('favicon.ico')));
@@ -177,7 +177,7 @@ class Action extends HTMLOutputter // lawsuit
if (common_config('site', 'mobile')) {
if (is_readable(INSTALLDIR . '/theme/' . common_config('site', 'theme') . '/apple-touch-icon.png')) {
$this->element('link', array('rel' => 'apple-touch-icon',
- 'href' => theme_path('apple-touch-icon.png')));
+ 'href' => Theme::path('apple-touch-icon.png')));
} else {
$this->element('link', array('rel' => 'apple-touch-icon',
'href' => common_path('apple-touch-icon.png')));
@@ -210,16 +210,16 @@ class Action extends HTMLOutputter // lawsuit
if (Event::handle('StartShowUAStyles', array($this))) {
$this->comment('[if IE]><link rel="stylesheet" type="text/css" '.
- 'href="'.theme_path('css/ie.css', 'base').'?version='.STATUSNET_VERSION.'" /><![endif]');
+ 'href="'.Theme::path('css/ie.css', 'base').'?version='.STATUSNET_VERSION.'" /><![endif]');
foreach (array(6,7) as $ver) {
- if (file_exists(theme_file('css/ie'.$ver.'.css', 'base'))) {
+ if (file_exists(Theme::file('css/ie'.$ver.'.css', 'base'))) {
// Yes, IE people should be put in jail.
$this->comment('[if lte IE '.$ver.']><link rel="stylesheet" type="text/css" '.
- 'href="'.theme_path('css/ie'.$ver.'.css', 'base').'?version='.STATUSNET_VERSION.'" /><![endif]');
+ 'href="'.Theme::path('css/ie'.$ver.'.css', 'base').'?version='.STATUSNET_VERSION.'" /><![endif]');
}
}
$this->comment('[if IE]><link rel="stylesheet" type="text/css" '.
- 'href="'.theme_path('css/ie.css', null).'?version='.STATUSNET_VERSION.'" /><![endif]');
+ 'href="'.Theme::path('css/ie.css', null).'?version='.STATUSNET_VERSION.'" /><![endif]');
Event::handle('EndShowUAStyles', array($this));
}
@@ -391,9 +391,9 @@ class Action extends HTMLOutputter // lawsuit
if (Event::handle('StartAddressData', array($this))) {
$this->elementStart('a', array('class' => 'url home bookmark',
'href' => common_local_url('public')));
- if (common_config('site', 'logo') || file_exists(theme_file('logo.png'))) {
+ if (common_config('site', 'logo') || file_exists(Theme::file('logo.png'))) {
$this->element('img', array('class' => 'logo photo',
- 'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : theme_path('logo.png'),
+ 'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png'),
'alt' => common_config('site', 'name')));
}
$this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
@@ -434,6 +434,10 @@ class Action extends HTMLOutputter // lawsuit
$this->menuItem(common_local_url($connect),
_('Connect'), _('Connect to services'), false, 'nav_connect');
}
+ if ($user->hasRight(Right::CONFIGURESITE)) {
+ $this->menuItem(common_local_url('siteadminpanel'),
+ _('Admin'), _('Change site configuration'), false, 'nav_admin');
+ }
if (common_config('invite', 'enabled')) {
$this->menuItem(common_local_url('invite'),
_('Invite'),
@@ -986,6 +990,18 @@ class Action extends HTMLOutputter // lawsuit
function selfUrl()
{
+ list($action, $args) = $this->returnToArgs();
+ return common_local_url($action, $args);
+ }
+
+ /**
+ * Returns arguments sufficient for re-constructing URL
+ *
+ * @return array two elements: action, other args
+ */
+
+ function returnToArgs()
+ {
$action = $this->trimmed('action');
$args = $this->args;
unset($args['action']);
@@ -998,8 +1014,7 @@ class Action extends HTMLOutputter // lawsuit
foreach (array_keys($_COOKIE) as $cookie) {
unset($args[$cookie]);
}
-
- return common_local_url($action, $args);
+ return array($action, $args);
}
/**
@@ -1048,8 +1063,7 @@ class Action extends HTMLOutputter // lawsuit
{
// Does a little before-after block for next/prev page
if ($have_before || $have_after) {
- $this->elementStart('div', array('class' => 'pagination'));
- $this->elementStart('dl', null);
+ $this->elementStart('dl', 'pagination');
$this->element('dt', null, _('Pagination'));
$this->elementStart('dd', null);
$this->elementStart('ul', array('class' => 'nav'));
@@ -1074,7 +1088,6 @@ class Action extends HTMLOutputter // lawsuit
$this->elementEnd('ul');
$this->elementEnd('dd');
$this->elementEnd('dl');
- $this->elementEnd('div');
}
}
@@ -1101,4 +1114,22 @@ class Action extends HTMLOutputter // lawsuit
{
return Design::siteDesign();
}
+
+ /**
+ * Check the session token.
+ *
+ * Checks that the current form has the correct session token,
+ * and throw an exception if it does not.
+ *
+ * @return void
+ */
+
+ function checkSessionToken()
+ {
+ // CSRF protection
+ $token = $this->trimmed('token');
+ if (empty($token) || $token != common_session_token()) {
+ $this->clientError(_('There was a problem with your session token.'));
+ }
+ }
}
diff --git a/lib/adminpanelaction.php b/lib/adminpanelaction.php
new file mode 100644
index 000000000..33b210da3
--- /dev/null
+++ b/lib/adminpanelaction.php
@@ -0,0 +1,282 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Superclass for admin panel actions
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category UI
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 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);
+}
+
+/**
+ * superclass for admin panel actions
+ *
+ * Common code for all admin panel actions.
+ *
+ * @category UI
+ * @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/
+ *
+ * @todo Find some commonalities with SettingsAction and combine
+ */
+
+class AdminPanelAction extends Action
+{
+ var $success = true;
+ var $msg = null;
+
+ /**
+ * Prepare for the action
+ *
+ * We check to see that the user is logged in, has
+ * authenticated in this session, and has the right
+ * to configure the site.
+ *
+ * @param array $args Array of arguments from Web driver
+ *
+ * @return boolean success flag
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ // User must be logged in.
+
+ if (!common_logged_in()) {
+ $this->clientError(_('Not logged in.'));
+ return;
+ }
+
+ $user = common_current_user();
+
+ // ...because they're logged in
+
+ assert(!empty($user));
+
+ // It must be a "real" login, not saved cookie login
+
+ if (!common_is_real_login()) {
+ // Cookie theft is too easy; we require automatic
+ // logins to re-authenticate before admining the site
+ common_set_returnto($this->selfUrl());
+ if (Event::handle('RedirectToLogin', array($this, $user))) {
+ common_redirect(common_local_url('login'), 303);
+ }
+ }
+
+ // User must have the right to change admin settings
+
+ if (!$user->hasRight(Right::CONFIGURESITE)) {
+ $this->clientError(_('You cannot make changes to this site.'));
+ return;
+ }
+
+ return true;
+ }
+
+ /**
+ * handle the action
+ *
+ * Check session token and try to save the settings if this is a
+ * POST. Otherwise, show the form.
+ *
+ * @param array $args unused.
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ $this->checkSessionToken();
+ try {
+ $this->saveSettings();
+
+ // Reload settings
+
+ Config::loadSettings();
+
+ $this->success = true;
+ $this->msg = _('Settings saved.');
+ } catch (Exception $e) {
+ $this->success = false;
+ $this->msg = $e->getMessage();
+ }
+ }
+ $this->showPage();
+ }
+
+ /**
+ * Show tabset for this page
+ *
+ * Uses the AdminPanelNav widget
+ *
+ * @return void
+ * @see AdminPanelNav
+ */
+
+ function showLocalNav()
+ {
+ $nav = new AdminPanelNav($this);
+ $nav->show();
+ }
+
+ /**
+ * Show the content section of the page
+ *
+ * Here, we show the admin panel's form.
+ *
+ * @return void.
+ */
+
+ function showContent()
+ {
+ $this->showForm();
+ }
+
+ /**
+ * show human-readable instructions for the page, or
+ * a success/failure on save.
+ *
+ * @return void
+ */
+
+ function showPageNotice()
+ {
+ if ($this->msg) {
+ $this->element('div', ($this->success) ? 'success' : 'error',
+ $this->msg);
+ } else {
+ $inst = $this->getInstructions();
+ $output = common_markup_to_html($inst);
+
+ $this->elementStart('div', 'instructions');
+ $this->raw($output);
+ $this->elementEnd('div');
+ }
+ }
+
+ /**
+ * Show the admin panel form
+ *
+ * Sub-classes should overload this.
+ *
+ * @return void
+ */
+
+ function showForm()
+ {
+ $this->clientError(_('showForm() not implemented.'));
+ return;
+ }
+
+ /**
+ * Instructions for using this form.
+ *
+ * String with instructions for using the form.
+ *
+ * Subclasses should overload this.
+ *
+ * @return void
+ */
+
+ function getInstructions()
+ {
+ return '';
+ }
+
+ /**
+ * Save settings from the form
+ *
+ * Validate and save the settings from the user.
+ *
+ * @return void
+ */
+
+ function saveSettings()
+ {
+ $this->clientError(_('saveSettings() not implemented.'));
+ return;
+ }
+}
+
+/**
+ * Menu for public group of actions
+ *
+ * @category Output
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Sarven Capadisli <csarven@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/
+ *
+ * @see Widget
+ */
+
+class AdminPanelNav extends Widget
+{
+ var $action = null;
+
+ /**
+ * Construction
+ *
+ * @param Action $action current action, used for output
+ */
+
+ function __construct($action=null)
+ {
+ parent::__construct($action);
+ $this->action = $action;
+ }
+
+ /**
+ * Show the menu
+ *
+ * @return void
+ */
+
+ function show()
+ {
+ $action_name = $this->action->trimmed('action');
+
+ $this->action->elementStart('ul', array('class' => 'nav'));
+
+ if (Event::handle('StartAdminPanelNav', array($this))) {
+
+ $this->out->menuItem(common_local_url('siteadminpanel'), _('Site'),
+ _('Basic site configuration'), $action_name == 'siteadminpanel', 'nav_site_admin_panel');
+
+ $this->out->menuItem(common_local_url('designadminpanel'), _('Design'),
+ _('Design configuration'), $action_name == 'designadminpanel', 'nav_design_admin_panel');
+
+ Event::handle('EndAdminPanelNav', array($this));
+ }
+ $this->action->elementEnd('ul');
+ }
+}
diff --git a/lib/api.php b/lib/api.php
index 9bd2083de..e2ea87b43 100644
--- a/lib/api.php
+++ b/lib/api.php
@@ -60,7 +60,7 @@ class ApiAction extends Action
var $max_id = null;
var $since_id = null;
var $since = null;
-
+
/**
* Initialization.
*
@@ -72,14 +72,14 @@ class ApiAction extends Action
function prepare($args)
{
parent::prepare($args);
-
+
$this->format = $this->arg('format');
$this->page = (int)$this->arg('page', 1);
$this->count = (int)$this->arg('count', 20);
$this->max_id = (int)$this->arg('max_id', 0);
$this->since_id = (int)$this->arg('since_id', 0);
$this->since = $this->arg('since');
-
+
return true;
}
@@ -134,11 +134,20 @@ class ApiAction extends Action
$twitter_user['protected'] = false; # not supported by StatusNet yet
$twitter_user['followers_count'] = $profile->subscriberCount();
- // Need to pull up the user for some of this
- $user = $profile->getUser();
- $design = $user->getDesign();
$defaultDesign = Design::siteDesign();
- if (!$design) $design = $defaultDesign;
+ $design = null;
+ $user = $profile->getUser();
+
+ // Note: some profiles don't have an associated user
+
+ if (!empty($user)) {
+ $design = $user->getDesign();
+ }
+
+ if (empty($design)) {
+ $design = $defaultDesign;
+ }
+
$color = Design::toWebColor(empty($design->backgroundcolor) ? $defaultDesign->backgroundcolor : $design->backgroundcolor);
$twitter_user['profile_background_color'] = ($color == null) ? '' : '#'.$color->hexValue();
$color = Design::toWebColor(empty($design->textcolor) ? $defaultDesign->textcolor : $design->textcolor);
@@ -155,7 +164,6 @@ class ApiAction extends Action
$twitter_user['favourites_count'] = $profile->faveCount(); // British spelling!
-
$timezone = 'UTC';
if ($user->timezone) {
@@ -168,9 +176,14 @@ class ApiAction extends Action
$twitter_user['utc_offset'] = $t->format('Z');
$twitter_user['time_zone'] = $timezone;
- // To be supported some day, perhaps
- $twitter_user['profile_background_image_url'] = '';
- $twitter_user['profile_background_tile'] = false;
+ $twitter_user['profile_background_image_url']
+ = empty($design->backgroundimage)
+ ? '' : ($design->disposition & BACKGROUND_ON)
+ ? Design::url($design->backgroundimage) : '';
+
+ $twitter_user['profile_background_tile']
+ = empty($design->disposition)
+ ? '' : ($design->disposition & BACKGROUND_TILE) ? 'true' : 'false';
$twitter_user['statuses_count'] = $profile->noticeCount();
@@ -229,6 +242,15 @@ class ApiAction extends Action
$twitter_status['in_reply_to_screen_name'] =
($replier_profile) ? $replier_profile->nickname : null;
+ if (isset($notice->lat) && isset($notice->lon)) {
+ // This is the format that GeoJSON expects stuff to be in
+ $twitter_status['geo'] = array('type' => 'Point',
+ 'coordinates' => array((float) $notice->lat,
+ (float) $notice->lon));
+ } else {
+ $twitter_status['geo'] = null;
+ }
+
if (isset($this->auth_user)) {
$twitter_status['favorited'] = $this->auth_user->hasFave($notice);
} else {
@@ -353,10 +375,19 @@ class ApiAction extends Action
$entry['pubDate'] = common_date_rfc2822($notice->created);
$entry['guid'] = $entry['link'];
+ if (isset($notice->lat) && isset($notice->lon)) {
+ // This is the format that GeoJSON expects stuff to be in.
+ // showGeoRSS() below uses it for XML output, so we reuse it
+ $entry['geo'] = array('type' => 'Point',
+ 'coordinates' => array((float) $notice->lat,
+ (float) $notice->lon));
+ } else {
+ $entry['geo'] = null;
+ }
+
return $entry;
}
-
function twitterRelationshipArray($source, $target)
{
$relationship = array();
@@ -432,6 +463,9 @@ class ApiAction extends Action
case 'attachments':
$this->showXmlAttachments($twitter_status['attachments']);
break;
+ case 'geo':
+ $this->showGeoRSS($value);
+ break;
default:
$this->element($element, null, $value);
}
@@ -475,6 +509,18 @@ class ApiAction extends Action
}
}
+ function showGeoRSS($geo)
+ {
+ if (empty($geo)) {
+ // empty geo element
+ $this->element('geo');
+ } else {
+ $this->elementStart('geo', array('xmlns:georss' => 'http://www.georss.org/georss'));
+ $this->element('georss:point', null, $geo['coordinates'][0] . ' ' . $geo['coordinates'][1]);
+ $this->elementEnd('geo');
+ }
+ }
+
function showTwitterRssItem($entry)
{
$this->elementStart('item');
@@ -496,6 +542,7 @@ class ApiAction extends Action
}
}
+ $this->showGeoRSS($entry['geo']);
$this->elementEnd('item');
}
@@ -520,7 +567,6 @@ class ApiAction extends Action
$this->endDocument('json');
}
-
function showXmlTimeline($notice)
{
@@ -640,7 +686,6 @@ class ApiAction extends Action
$this->endTwitterRss();
}
-
function showTwitterAtomEntry($entry)
{
$this->elementStart('entry');
diff --git a/lib/apiprivateauth.php b/lib/apiprivateauth.php
new file mode 100644
index 000000000..5d0033005
--- /dev/null
+++ b/lib/apiprivateauth.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Base class for API actions that only require auth when a site
+ * is configured to be private
+ *
+ * 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 API
+ * @package StatusNet
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author mEDI <medi@milaro.net>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @author Zach Copley <zach@status.net>
+ * @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);
+}
+
+require_once INSTALLDIR.'/lib/apiauth.php';
+
+/**
+ * Actions extending this class will require auth only if a site is private
+ *
+ * @category API
+ * @package StatusNet
+ * @author Adrian Lang <mail@adrianlang.de>
+ * @author Brenda Wallace <shiny@cpan.org>
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @author Dan Moore <dan@moore.cx>
+ * @author Evan Prodromou <evan@status.net>
+ * @author mEDI <medi@milaro.net>
+ * @author Sarven Capadisli <csarven@status.net>
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiPrivateAuthAction extends ApiAuthAction
+{
+
+ /**
+ * Does this API resource require authentication?
+ *
+ * @return boolean true or false
+ */
+
+ function requiresAuth()
+ {
+ // If the site is "private", all API methods except statusnet/config
+ // need authentication
+
+ if (common_config('site', 'private')) {
+ return true;
+ }
+
+ return false;
+ }
+
+}
diff --git a/lib/blockform.php b/lib/blockform.php
index 4820d09af..b6652b1f6 100644
--- a/lib/blockform.php
+++ b/lib/blockform.php
@@ -32,8 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/form.php';
-
/**
* Form for blocking a user
*
@@ -47,109 +45,38 @@ require_once INSTALLDIR.'/lib/form.php';
* @see UnblockForm
*/
-class BlockForm extends Form
+class BlockForm extends ProfileActionForm
{
/**
- * Profile of user to block
- */
-
- var $profile = null;
-
- /**
- * Return-to args
- */
-
- var $args = null;
-
- /**
- * Constructor
+ * Action this form provides
*
- * @param HTMLOutputter $out output channel
- * @param Profile $profile profile of user to block
- * @param array $args return-to args
+ * @return string Name of the action, lowercased.
*/
- function __construct($out=null, $profile=null, $args=null)
+ function target()
{
- parent::__construct($out);
-
- $this->profile = $profile;
- $this->args = $args;
+ return 'block';
}
/**
- * ID of the form
- *
- * @return int ID of the form
- */
-
- function id()
- {
- return 'block-' . $this->profile->id;
- }
-
-
- /**
- * class of the form
- *
- * @return string class of the form
- */
-
- function formClass()
- {
- return 'form_user_block';
- }
-
-
- /**
- * Action of the form
- *
- * @return string URL of the action
- */
-
- function action()
- {
- return common_local_url('block');
- }
-
-
- /**
- * Legend of the Form
- *
- * @return void
- */
- function formLegend()
- {
- $this->out->element('legend', null, _('Block this user'));
- }
-
-
- /**
- * Data elements of the form
+ * Title of the form
*
- * @return void
+ * @return string Title of the form, internationalized
*/
- function formData()
+ function title()
{
- $this->out->hidden('blockto-' . $this->profile->id,
- $this->profile->id,
- 'blockto');
- if ($this->args) {
- foreach ($this->args as $k => $v) {
- $this->out->hidden('returnto-' . $k, $v);
- }
- }
+ return _('Block');
}
/**
- * Action elements
+ * Description of the form
*
- * @return void
+ * @return string description of the form, internationalized
*/
- function formActions()
+ function description()
{
- $this->out->submit('submit', _('Block'), 'submit', null, _('Block this user'));
+ return _('Block this user');
}
}
diff --git a/lib/command.php b/lib/command.php
index 11d40b8e1..7e98156b6 100644
--- a/lib/command.php
+++ b/lib/command.php
@@ -73,7 +73,7 @@ class UntrackCommand extends UnimplementedCommand
}
}
-class NudgeCommand extends UnimplementedCommand
+class NudgeCommand extends Command
{
var $other = null;
function __construct($user, $other)
@@ -81,6 +81,26 @@ class NudgeCommand extends UnimplementedCommand
parent::__construct($user);
$this->other = $other;
}
+ function execute($channel)
+ {
+ $recipient = User::staticGet('nickname', $this->other);
+ if(! $recipient){
+ $channel->error($this->user, sprintf(_('Could not find a user with nickname %s'),
+ $this->other));
+ }else{
+ if ($recipient->id == $this->user->id) {
+ $channel->error($this->user, _('It does not make a lot of sense to nudge yourself!'));
+ }else{
+ if ($recipient->email && $recipient->emailnotifynudge) {
+ mail_notify_nudge($this->user, $recipient);
+ }
+ // XXX: notify by IM
+ // XXX: notify by SMS
+ $channel->output($this->user, sprintf(_('Nudge sent to %s'),
+ $recipient->nickname));
+ }
+ }
+ }
}
class InviteCommand extends UnimplementedCommand
@@ -124,18 +144,30 @@ class FavCommand extends Command
function execute($channel)
{
+ if(substr($this->other,0,1)=='#'){
+ //favoriting a specific notice_id
- $recipient =
- common_relative_profile($this->user, common_canonical_nickname($this->other));
+ $notice = Notice::staticGet(substr($this->other,1));
+ if (!$notice) {
+ $channel->error($this->user, _('Notice with that id does not exist'));
+ return;
+ }
+ $recipient = $notice->getProfile();
+ }else{
+ //favoriting a given user's last notice
- if (!$recipient) {
- $channel->error($this->user, _('No such user.'));
- return;
- }
- $notice = $recipient->getCurrentNotice();
- if (!$notice) {
- $channel->error($this->user, _('User has no last notice'));
- return;
+ $recipient =
+ common_relative_profile($this->user, common_canonical_nickname($this->other));
+
+ if (!$recipient) {
+ $channel->error($this->user, _('No such user.'));
+ return;
+ }
+ $notice = $recipient->getCurrentNotice();
+ if (!$notice) {
+ $channel->error($this->user, _('User has no last notice'));
+ return;
+ }
}
$fave = Fave::addNew($this->user, $notice);
@@ -347,6 +379,71 @@ class MessageCommand extends Command
}
}
+class ReplyCommand extends Command
+{
+ var $other = null;
+ var $text = null;
+ function __construct($user, $other, $text)
+ {
+ parent::__construct($user);
+ $this->other = $other;
+ $this->text = $text;
+ }
+
+ function execute($channel)
+ {
+ if(substr($this->other,0,1)=='#'){
+ //replying to a specific notice_id
+
+ $notice = Notice::staticGet(substr($this->other,1));
+ if (!$notice) {
+ $channel->error($this->user, _('Notice with that id does not exist'));
+ return;
+ }
+ $recipient = $notice->getProfile();
+ }else{
+ //replying to a given user's last notice
+
+ $recipient =
+ common_relative_profile($this->user, common_canonical_nickname($this->other));
+
+ if (!$recipient) {
+ $channel->error($this->user, _('No such user.'));
+ return;
+ }
+ $notice = $recipient->getCurrentNotice();
+ if (!$notice) {
+ $channel->error($this->user, _('User has no last notice'));
+ return;
+ }
+ }
+
+ $len = mb_strlen($this->text);
+
+ if ($len == 0) {
+ $channel->error($this->user, _('No content!'));
+ return;
+ }
+
+ $this->text = common_shorten_links($this->text);
+
+ if (Notice::contentTooLong($this->text)) {
+ $channel->error($this->user, sprintf(_('Notice too long - maximum is %d characters, you sent %d'),
+ Notice::maxContent(), mb_strlen($this->text)));
+ return;
+ }
+
+ $notice = Notice::saveNew($this->user->id, $this->text, $channel->source(), 1,
+ $notice->id);
+ if ($notice) {
+ $channel->output($this->user, sprintf(_('Reply to %s sent'), $recipient->nickname));
+ } else {
+ $channel->error($this->user, _('Error saving notice.'));
+ }
+ common_broadcast_notice($notice);
+ }
+}
+
class GetCommand extends Command
{
@@ -482,6 +579,97 @@ class OnCommand extends Command
}
}
+class LoginCommand extends Command
+{
+ function execute($channel)
+ {
+ $login_token = Login_token::staticGet('user_id',$this->user->id);
+ if($login_token){
+ $login_token->delete();
+ }
+ $login_token = new Login_token();
+ $login_token->user_id = $this->user->id;
+ $login_token->token = common_good_rand(16);
+ $login_token->created = common_sql_now();
+ $result = $login_token->insert();
+ if (!$result) {
+ common_log_db_error($login_token, 'INSERT', __FILE__);
+ $channel->error($this->user, sprintf(_('Could not create login token for %s'),
+ $this->user->nickname));
+ return;
+ }
+ $channel->output($this->user,
+ sprintf(_('This link is useable only once, and is good for only 2 minutes: %s'),
+ common_local_url('login',
+ array('user_id'=>$login_token->user_id, 'token'=>$login_token->token))));
+ }
+}
+
+class SubscriptionsCommand extends Command
+{
+ function execute($channel)
+ {
+ $profile = $this->user->getSubscriptions(0);
+ $nicknames=array();
+ while ($profile->fetch()) {
+ $nicknames[]=$profile->nickname;
+ }
+ if(count($nicknames)==0){
+ $out=_('You are not subscribed to anyone.');
+ }else{
+ $out = ngettext('You are subscribed to this person:',
+ 'You are subscribed to these people:',
+ count($nicknames));
+ $out .= ' ';
+ $out .= implode(', ',$nicknames);
+ }
+ $channel->output($this->user,$out);
+ }
+}
+
+class SubscribersCommand extends Command
+{
+ function execute($channel)
+ {
+ $profile = $this->user->getSubscribers();
+ $nicknames=array();
+ while ($profile->fetch()) {
+ $nicknames[]=$profile->nickname;
+ }
+ if(count($nicknames)==0){
+ $out=_('No one is subscribed to you.');
+ }else{
+ $out = ngettext('This person is subscribed to you:',
+ 'These people are subscribed to you:',
+ count($nicknames));
+ $out .= ' ';
+ $out .= implode(', ',$nicknames);
+ }
+ $channel->output($this->user,$out);
+ }
+}
+
+class GroupsCommand extends Command
+{
+ function execute($channel)
+ {
+ $group = $this->user->getGroups();
+ $groups=array();
+ while ($group->fetch()) {
+ $groups[]=$group->nickname;
+ }
+ if(count($groups)==0){
+ $out=_('You are not a member of any groups.');
+ }else{
+ $out = ngettext('You are a member of this group:',
+ 'You are a member of these groups:',
+ count($nicknames));
+ $out.=implode(', ',$groups);
+ }
+ $channel->output($this->user,$out);
+ }
+}
+
class HelpCommand extends Command
{
function execute($channel)
@@ -492,12 +680,19 @@ class HelpCommand extends Command
"off - turn off notifications\n".
"help - show this help\n".
"follow <nickname> - subscribe to user\n".
+ "groups - lists the groups you have joined\n".
+ "subscriptions - list the people you follow\n".
+ "subscribers - list the people that follow you\n".
"leave <nickname> - unsubscribe from user\n".
"d <nickname> <text> - direct message to user\n".
"get <nickname> - get last notice from user\n".
"whois <nickname> - get profile info on user\n".
"fav <nickname> - add user's last notice as a 'fave'\n".
+ "fav #<notice_id> - add notice with the given id as a 'fave'\n".
+ "reply #<notice_id> - reply to notice with a given id\n".
+ "reply <nickname> - reply to the last notice from user\n".
"join <group> - join group\n".
+ "login - Get a link to login to the web interface\n".
"drop <group> - leave group\n".
"stats - get your stats\n".
"stop - same as 'off'\n".
@@ -507,7 +702,7 @@ class HelpCommand extends Command
"last <nickname> - same as 'get'\n".
"on <nickname> - not yet implemented.\n".
"off <nickname> - not yet implemented.\n".
- "nudge <nickname> - not yet implemented.\n".
+ "nudge <nickname> - remind a user to update.\n".
"invite <phone number> - not yet implemented.\n".
"track <word> - not yet implemented.\n".
"untrack <word> - not yet implemented.\n".
diff --git a/lib/commandinterpreter.php b/lib/commandinterpreter.php
index 60fc4c3c4..665015afc 100644
--- a/lib/commandinterpreter.php
+++ b/lib/commandinterpreter.php
@@ -41,6 +41,30 @@ class CommandInterpreter
return null;
}
return new HelpCommand($user);
+ case 'login':
+ if ($arg) {
+ return null;
+ } else {
+ return new LoginCommand($user);
+ }
+ case 'subscribers':
+ if ($arg) {
+ return null;
+ } else {
+ return new SubscribersCommand($user);
+ }
+ case 'subscriptions':
+ if ($arg) {
+ return null;
+ } else {
+ return new SubscriptionsCommand($user);
+ }
+ case 'groups':
+ if ($arg) {
+ return null;
+ } else {
+ return new GroupsCommand($user);
+ }
case 'on':
if ($arg) {
list($other, $extra) = $this->split_arg($arg);
@@ -134,6 +158,17 @@ class CommandInterpreter
} else {
return new MessageCommand($user, $other, $extra);
}
+ case 'r':
+ case 'reply':
+ if (!$arg) {
+ return null;
+ }
+ list($other, $extra) = $this->split_arg($arg);
+ if (!$extra) {
+ return null;
+ } else {
+ return new ReplyCommand($user, $other, $extra);
+ }
case 'whois':
if (!$arg) {
return null;
diff --git a/lib/common.php b/lib/common.php
index e29456ed4..063d7d9d9 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -19,6 +19,9 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+//exit with 200 response, if this is checking fancy from the installer
+if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
+
define('STATUSNET_VERSION', '0.9.0dev');
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
@@ -38,12 +41,18 @@ define('FOREIGN_NOTICE_SEND_REPLY', 4);
define('FOREIGN_FRIEND_SEND', 1);
define('FOREIGN_FRIEND_RECV', 2);
-define_syslog_variables();
-
# append our extlib dir as the last-resort place to find libs
set_include_path(get_include_path() . PATH_SEPARATOR . INSTALLDIR . '/extlib/');
+# To protect against upstream libraries which haven't updated
+# for PHP 5.3 where dl() function may not be present...
+if (!function_exists('dl')) {
+ function dl($library) {
+ return false;
+ }
+}
+
# global configuration object
require_once('PEAR.php');
@@ -169,6 +178,7 @@ if (isset($conffile)) {
$_config_files[] = INSTALLDIR.'/config.php';
}
+global $_have_a_config;
$_have_a_config = false;
foreach ($_config_files as $_config_file) {
@@ -185,7 +195,14 @@ function _have_config()
}
// XXX: Throw a conniption if database not installed
-
+// XXX: Find a way to use htmlwriter for this instead of handcoded markup
+if (!_have_config()) {
+ echo '<p>'. _('No configuration file found. ') .'</p>';
+ echo '<p>'. _('I looked for configuration files in the following places: ') .'<br/> '. implode($_config_files, '<br/>');
+ echo '<p>'. _('You may wish to run the installer to fix this.') .'</p>';
+ echo '<a href="install.php">'. _('Go to the installer.') .'</a>';
+ exit;
+}
// Fixup for statusnet.ini
$_db_name = substr($config['db']['database'], strrpos($config['db']['database'], '/') + 1);
@@ -219,10 +236,8 @@ require_once 'markdown.php';
require_once INSTALLDIR.'/lib/util.php';
require_once INSTALLDIR.'/lib/action.php';
-require_once INSTALLDIR.'/lib/theme.php';
require_once INSTALLDIR.'/lib/mail.php';
require_once INSTALLDIR.'/lib/subs.php';
-require_once INSTALLDIR.'/lib/Shorturl_api.php';
require_once INSTALLDIR.'/lib/clientexception.php';
require_once INSTALLDIR.'/lib/serverexception.php';
diff --git a/lib/curlclient.php b/lib/curlclient.php
deleted file mode 100644
index 36fc7d157..000000000
--- a/lib/curlclient.php
+++ /dev/null
@@ -1,179 +0,0 @@
-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
index 30e43eefb..95366e0b3 100644
--- a/lib/default.php
+++ b/lib/default.php
@@ -84,7 +84,8 @@ $default =
'image' => 'http://i.creativecommons.org/l/by/3.0/80x15.png'),
'mail' =>
array('backend' => 'mail',
- 'params' => null),
+ 'params' => null,
+ 'domain_check' => true),
'nickname' =>
array('blacklist' => array(),
'featured' => array()),
@@ -124,10 +125,6 @@ $default =
'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' =>
@@ -227,6 +224,6 @@ $default =
array('contentlimit' => null),
'message' =>
array('contentlimit' => null),
- 'http' =>
- array('client' => 'curl'), // XXX: should this be the default?
+ 'location' =>
+ array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth
);
diff --git a/lib/deleteuserform.php b/lib/deleteuserform.php
new file mode 100644
index 000000000..09ea8f68d
--- /dev/null
+++ b/lib/deleteuserform.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for deleting a user
+ *
+ * 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 Form
+ * @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);
+}
+
+/**
+ * Form for deleting a user
+ *
+ * @category Form
+ * @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 DeleteUserForm extends ProfileActionForm
+{
+ /**
+ * Action this form provides
+ *
+ * @return string Name of the action, lowercased.
+ */
+
+ function target()
+ {
+ return 'deleteuser';
+ }
+
+ /**
+ * Title of the form
+ *
+ * @return string Title of the form, internationalized
+ */
+
+ function title()
+ {
+ return _('Delete');
+ }
+
+ /**
+ * Description of the form
+ *
+ * @return string description of the form, internationalized
+ */
+
+ function description()
+ {
+ return _('Delete this user');
+ }
+}
diff --git a/lib/designsettings.php b/lib/designsettings.php
index 820d534f2..5ce9ddeda 100644
--- a/lib/designsettings.php
+++ b/lib/designsettings.php
@@ -271,17 +271,20 @@ class DesignSettingsAction extends AccountSettingsAction
function handlePost()
{
- // XXX: Robin's workaround for a bug in PHP where $_POST
- // and $_FILE are empty in the case that the uploaded
- // file is bigger than PHP is configured to handle.
-
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) {
+ // Workaround for PHP returning empty $_POST and $_FILES when POST
+ // length > post_max_size in php.ini
+
+ if (empty($_FILES)
+ && empty($_POST)
+ && ($_SERVER['CONTENT_LENGTH'] > 0)
+ ) {
$msg = _('The server was unable to handle that much POST ' .
'data (%s bytes) due to its current configuration.');
$this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
+ return;
}
}
diff --git a/lib/error.php b/lib/error.php
index 6a9b76be1..3162cfe65 100644
--- a/lib/error.php
+++ b/lib/error.php
@@ -70,7 +70,7 @@ class ErrorAction extends Action
*/
function extraHeaders()
{
- $status_string = $this->status[$this->code];
+ $status_string = @self::$status[$this->code];
header('HTTP/1.1 '.$this->code.' '.$status_string);
}
@@ -92,7 +92,7 @@ class ErrorAction extends Action
function title()
{
- return self::$status[$this->code];
+ return @self::$status[$this->code];
}
function isReadOnly($args)
diff --git a/lib/grouplist.php b/lib/grouplist.php
index b41c5b5f8..99bff9cdc 100644
--- a/lib/grouplist.php
+++ b/lib/grouplist.php
@@ -85,19 +85,19 @@ class GroupList extends Widget
function showGroup()
{
- $this->out->elementStart('li', array('class' => 'profile',
+ $this->out->elementStart('li', array('class' => 'profile hentry',
'id' => 'group-' . $this->group->id));
$user = common_current_user();
- $this->out->elementStart('div', 'entity_profile vcard');
+ $this->out->elementStart('div', 'entity_profile vcard entry-content');
$logo = ($this->group->stream_logo) ?
$this->group->stream_logo : User_group::defaultLogo(AVATAR_STREAM_SIZE);
$this->out->elementStart('a', array('href' => $this->group->homeUrl(),
- 'class' => 'url',
- 'rel' => 'group'));
+ 'class' => 'url entry-title',
+ 'rel' => 'contact group'));
$this->out->element('img', array('src' => $logo,
'class' => 'photo avatar',
'width' => AVATAR_STREAM_SIZE,
@@ -105,48 +105,32 @@ class GroupList extends Widget
'alt' =>
($this->group->fullname) ? $this->group->fullname :
$this->group->nickname));
- $hasFN = ($this->group->fullname) ? 'nickname url uid' : 'fn org nickname url uid';
+ $hasFN = ($this->group->fullname) ? 'nickname' : 'fn org nickname';
$this->out->elementStart('span', $hasFN);
$this->out->raw($this->highlight($this->group->nickname));
$this->out->elementEnd('span');
$this->out->elementEnd('a');
if ($this->group->fullname) {
- $this->out->elementStart('dl', 'entity_fn');
- $this->out->element('dt', null, 'Full name');
- $this->out->elementStart('dd');
$this->out->elementStart('span', 'fn org');
$this->out->raw($this->highlight($this->group->fullname));
$this->out->elementEnd('span');
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
}
if ($this->group->location) {
- $this->out->elementStart('dl', 'entity_location');
- $this->out->element('dt', null, _('Location'));
- $this->out->elementStart('dd', 'label');
+ $this->out->elementStart('span', 'label');
$this->out->raw($this->highlight($this->group->location));
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
+ $this->out->elementEnd('span');
}
if ($this->group->homepage) {
- $this->out->elementStart('dl', 'entity_url');
- $this->out->element('dt', null, _('URL'));
- $this->out->elementStart('dd');
$this->out->elementStart('a', array('href' => $this->group->homepage,
'class' => 'url'));
$this->out->raw($this->highlight($this->group->homepage));
$this->out->elementEnd('a');
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
}
if ($this->group->description) {
- $this->out->elementStart('dl', 'entity_note');
- $this->out->element('dt', null, _('Note'));
- $this->out->elementStart('dd', 'note');
+ $this->out->elementStart('p', 'note');
$this->out->raw($this->highlight($this->group->description));
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
+ $this->out->elementEnd('p');
}
# If we're on a list with an owner (subscriptions or subscribers)...
diff --git a/lib/groupnav.php b/lib/groupnav.php
index 31cf378c8..131b38fa2 100644
--- a/lib/groupnav.php
+++ b/lib/groupnav.php
@@ -79,46 +79,49 @@ class GroupNav extends Widget
$nickname = $this->group->nickname;
$this->out->elementStart('ul', array('class' => 'nav'));
- $this->out->menuItem(common_local_url('showgroup', array('nickname' =>
- $nickname)),
- _('Group'),
- sprintf(_('%s group'), $nickname),
- $action_name == 'showgroup',
- 'nav_group_group');
- $this->out->menuItem(common_local_url('groupmembers', array('nickname' =>
- $nickname)),
- _('Members'),
- sprintf(_('%s group members'), $nickname),
- $action_name == 'groupmembers',
- 'nav_group_members');
+ if (Event::handle('StartGroupGroupNav', array($this))) {
+ $this->out->menuItem(common_local_url('showgroup', array('nickname' =>
+ $nickname)),
+ _('Group'),
+ sprintf(_('%s group'), $nickname),
+ $action_name == 'showgroup',
+ 'nav_group_group');
+ $this->out->menuItem(common_local_url('groupmembers', array('nickname' =>
+ $nickname)),
+ _('Members'),
+ sprintf(_('%s group members'), $nickname),
+ $action_name == 'groupmembers',
+ 'nav_group_members');
- $cur = common_current_user();
+ $cur = common_current_user();
- if ($cur && $cur->isAdmin($this->group)) {
- $this->out->menuItem(common_local_url('blockedfromgroup', array('nickname' =>
- $nickname)),
- _('Blocked'),
- sprintf(_('%s blocked users'), $nickname),
- $action_name == 'blockedfromgroup',
- 'nav_group_blocked');
- $this->out->menuItem(common_local_url('editgroup', array('nickname' =>
- $nickname)),
- _('Admin'),
- sprintf(_('Edit %s group properties'), $nickname),
- $action_name == 'editgroup',
- 'nav_group_admin');
- $this->out->menuItem(common_local_url('grouplogo', array('nickname' =>
- $nickname)),
- _('Logo'),
- sprintf(_('Add or edit %s logo'), $nickname),
- $action_name == 'grouplogo',
- 'nav_group_logo');
- $this->out->menuItem(common_local_url('groupdesignsettings', array('nickname' =>
- $nickname)),
- _('Design'),
- sprintf(_('Add or edit %s design'), $nickname),
- $action_name == 'groupdesignsettings',
- 'nav_group_design');
+ if ($cur && $cur->isAdmin($this->group)) {
+ $this->out->menuItem(common_local_url('blockedfromgroup', array('nickname' =>
+ $nickname)),
+ _('Blocked'),
+ sprintf(_('%s blocked users'), $nickname),
+ $action_name == 'blockedfromgroup',
+ 'nav_group_blocked');
+ $this->out->menuItem(common_local_url('editgroup', array('nickname' =>
+ $nickname)),
+ _('Admin'),
+ sprintf(_('Edit %s group properties'), $nickname),
+ $action_name == 'editgroup',
+ 'nav_group_admin');
+ $this->out->menuItem(common_local_url('grouplogo', array('nickname' =>
+ $nickname)),
+ _('Logo'),
+ sprintf(_('Add or edit %s logo'), $nickname),
+ $action_name == 'grouplogo',
+ 'nav_group_logo');
+ $this->out->menuItem(common_local_url('groupdesignsettings', array('nickname' =>
+ $nickname)),
+ _('Design'),
+ sprintf(_('Add or edit %s design'), $nickname),
+ $action_name == 'groupdesignsettings',
+ 'nav_group_design');
+ }
+ Event::handle('EndGroupGroupNav', array($this));
}
$this->out->elementEnd('ul');
}
diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php
index ce83295fb..c2ec83c28 100644
--- a/lib/htmloutputter.php
+++ b/lib/htmloutputter.php
@@ -375,8 +375,8 @@ class HTMLOutputter extends XMLOutputter
$url = parse_url($src);
if( empty($url->scheme) && empty($url->host) && empty($url->query) && empty($url->fragment))
{
- if(file_exists(theme_file($src,$theme))){
- $src = theme_path($src, $theme) . '?version=' . STATUSNET_VERSION;
+ if(file_exists(Theme::file($src,$theme))){
+ $src = Theme::path($src, $theme) . '?version=' . STATUSNET_VERSION;
}else{
$src = common_path($src);
}
diff --git a/lib/httpclient.php b/lib/httpclient.php
index f16e31e10..3f8262076 100644
--- a/lib/httpclient.php
+++ b/lib/httpclient.php
@@ -31,6 +31,9 @@ if (!defined('STATUSNET')) {
exit(1);
}
+require_once 'HTTP/Request2.php';
+require_once 'HTTP/Request2/Response.php';
+
/**
* Useful structure for HTTP responses
*
@@ -38,18 +41,53 @@ if (!defined('STATUSNET')) {
* ways of doing them. This class hides the specifics of what underlying
* library (curl or PHP-HTTP or whatever) that's used.
*
+ * This extends the HTTP_Request2_Response class with methods to get info
+ * about any followed redirects.
+ *
* @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/
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Brion Vibber <brion@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
+class HTTPResponse extends HTTP_Request2_Response
{
- public $code = null;
- public $headers = array();
- public $body = null;
+ function __construct(HTTP_Request2_Response $response, $url, $redirects=0)
+ {
+ foreach (get_object_vars($response) as $key => $val) {
+ $this->$key = $val;
+ }
+ $this->url = strval($url);
+ $this->redirectCount = intval($redirects);
+ }
+
+ /**
+ * Get the count of redirects that have been followed, if any.
+ * @return int
+ */
+ function getRedirectCount()
+ {
+ return $this->redirectCount;
+ }
+
+ /**
+ * Gets the final target URL, after any redirects have been followed.
+ * @return string URL
+ */
+ function getUrl()
+ {
+ return $this->url;
+ }
+
+ /**
+ * Check if the response is OK, generally a 200 status code.
+ * @return bool
+ */
+ function isOk()
+ {
+ return ($this->getStatus() == 200);
+ }
}
/**
@@ -59,64 +97,163 @@ class HTTPResponse
* ways of doing them. This class hides the specifics of what underlying
* library (curl or PHP-HTTP or whatever) that's used.
*
+ * This extends the PEAR HTTP_Request2 package:
+ * - sends StatusNet-specific User-Agent header
+ * - 'follow_redirects' config option, defaulting off
+ * - 'max_redirs' config option, defaulting to 10
+ * - extended response class adds getRedirectCount() and getUrl() methods
+ * - get() and post() convenience functions return body content directly
+ *
* @category HTTP
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
+ * @author Brion Vibber <brion@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
+class HTTPClient extends HTTP_Request2
{
- static $_client = null;
- static function start()
+ function __construct($url=null, $method=self::METHOD_GET, $config=array())
{
- 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;
+ $this->config['max_redirs'] = 10;
+ $this->config['follow_redirects'] = true;
+ parent::__construct($url, $method, $config);
+ $this->setHeader('User-Agent', $this->userAgent());
}
- function head($url, $headers)
+ /**
+ * Convenience/back-compat instantiator
+ * @return HTTPClient
+ */
+ public static function start()
{
- throw new Exception("HEAD method unimplemented");
+ return new HTTPClient();
}
- function get($url, $headers)
+ /**
+ * Convenience function to run a GET request.
+ *
+ * @return HTTPResponse
+ * @throws HTTP_Request2_Exception
+ */
+ public function get($url, $headers=array())
{
- throw new Exception("GET method unimplemented");
+ return $this->doRequest($url, self::METHOD_GET, $headers);
}
- function post($url, $headers, $body)
+ /**
+ * Convenience function to run a HEAD request.
+ *
+ * @return HTTPResponse
+ * @throws HTTP_Request2_Exception
+ */
+ public function head($url, $headers=array())
{
- throw new Exception("POST method unimplemented");
+ return $this->doRequest($url, self::METHOD_HEAD, $headers);
}
- function put($url, $headers, $body)
+ /**
+ * Convenience function to POST form data.
+ *
+ * @param string $url
+ * @param array $headers optional associative array of HTTP headers
+ * @param array $data optional associative array or blob of form data to submit
+ * @return HTTPResponse
+ * @throws HTTP_Request2_Exception
+ */
+ public function post($url, $headers=array(), $data=array())
{
- throw new Exception("PUT method unimplemented");
+ if ($data) {
+ $this->addPostParameter($data);
+ }
+ return $this->doRequest($url, self::METHOD_POST, $headers);
}
- function delete($url, $headers)
+ /**
+ * @return HTTPResponse
+ * @throws HTTP_Request2_Exception
+ */
+ protected function doRequest($url, $method, $headers)
{
- throw new Exception("DELETE method unimplemented");
+ $this->setUrl($url);
+ $this->setMethod($method);
+ if ($headers) {
+ foreach ($headers as $header) {
+ $this->setHeader($header);
+ }
+ }
+ $response = $this->send();
+ return $response;
+ }
+
+ protected function log($level, $detail) {
+ $method = $this->getMethod();
+ $url = $this->getUrl();
+ common_log($level, __CLASS__ . ": HTTP $method $url - $detail");
}
+ /**
+ * Pulls up StatusNet's customized user-agent string, so services
+ * we hit can track down the responsible software.
+ *
+ * @return string
+ */
function userAgent()
{
return "StatusNet/".STATUSNET_VERSION." (".STATUSNET_CODENAME.")";
}
+
+ /**
+ * Actually performs the HTTP request and returns an HTTPResponse object
+ * with response body and header info.
+ *
+ * Wraps around parent send() to add logging and redirection processing.
+ *
+ * @return HTTPResponse
+ * @throw HTTP_Request2_Exception
+ */
+ public function send()
+ {
+ $maxRedirs = intval($this->config['max_redirs']);
+ if (empty($this->config['follow_redirects'])) {
+ $maxRedirs = 0;
+ }
+ $redirs = 0;
+ do {
+ try {
+ $response = parent::send();
+ } catch (HTTP_Request2_Exception $e) {
+ $this->log(LOG_ERR, $e->getMessage());
+ throw $e;
+ }
+ $code = $response->getStatus();
+ if ($code >= 200 && $code < 300) {
+ $reason = $response->getReasonPhrase();
+ $this->log(LOG_INFO, "$code $reason");
+ } elseif ($code >= 300 && $code < 400) {
+ $url = $this->getUrl();
+ $target = $response->getHeader('Location');
+
+ if (++$redirs >= $maxRedirs) {
+ common_log(LOG_ERR, __CLASS__ . ": Too many redirects: skipping $code redirect from $url to $target");
+ break;
+ }
+ try {
+ $this->setUrl($target);
+ $this->setHeader('Referer', $url);
+ common_log(LOG_INFO, __CLASS__ . ": Following $code redirect from $url to $target");
+ continue;
+ } catch (HTTP_Request2_Exception $e) {
+ common_log(LOG_ERR, __CLASS__ . ": Invalid $code redirect from $url to $target");
+ }
+ } else {
+ $reason = $response->getReasonPhrase();
+ $this->log(LOG_ERR, "$code $reason");
+ }
+ break;
+ } while ($maxRedirs);
+ return new HTTPResponse($response, $this->getUrl(), $redirs);
+ }
}
diff --git a/lib/imagefile.php b/lib/imagefile.php
index 88f461481..cf1668f20 100644
--- a/lib/imagefile.php
+++ b/lib/imagefile.php
@@ -72,14 +72,19 @@ class ImageFile
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
- throw new Exception(sprintf(_('That file is too big. The maximum file size is %d.'),
+ throw new Exception(sprintf(_('That file is too big. The maximum file size is %s.'),
ImageFile::maxFileSize()));
return;
case UPLOAD_ERR_PARTIAL:
@unlink($_FILES[$param]['tmp_name']);
throw new Exception(_('Partial upload.'));
return;
+ case UPLOAD_ERR_NO_FILE:
+ // No file; probably just a non-AJAX submission.
+ return;
default:
+ common_log(LOG_ERR, __METHOD__ . ": Unknown upload error " .
+ $_FILES[$param]['error']);
throw new Exception(_('System error uploading file.'));
return;
}
diff --git a/lib/jabber.php b/lib/jabber.php
index 3dcdce5db..a8e295ea5 100644
--- a/lib/jabber.php
+++ b/lib/jabber.php
@@ -176,6 +176,7 @@ function jabber_format_entry($profile, $notice)
$xs = new XMLStringer();
$xs->elementStart('html', array('xmlns' => 'http://jabber.org/protocol/xhtml-im'));
$xs->elementStart('body', array('xmlns' => 'http://www.w3.org/1999/xhtml'));
+ $xs->element("img", array('src'=> $profile->avatarUrl(AVATAR_MINI_SIZE)));
$xs->element('a', array('href' => $profile->profileurl),
$profile->nickname);
$xs->text(": ");
@@ -184,6 +185,11 @@ function jabber_format_entry($profile, $notice)
} else {
$xs->raw(common_render_content($notice->content, $notice));
}
+ $xs->text(" ");
+ $xs->element('a', array(
+ 'href'=>common_local_url('conversation',
+ array('id' => $notice->conversation)).'#notice-'.$notice->id
+ ),sprintf(_('[%s]'),$notice->id));
$xs->elementEnd('body');
$xs->elementEnd('html');
@@ -475,5 +481,5 @@ function jabber_public_notice($notice)
function jabber_format_notice(&$profile, &$notice)
{
- return $profile->nickname . ': ' . $notice->content;
+ return $profile->nickname . ': ' . $notice->content . ' [' . $notice->id . ']';
}
diff --git a/lib/language.php b/lib/language.php
index 7dcb808c9..2570907b7 100644
--- a/lib/language.php
+++ b/lib/language.php
@@ -100,38 +100,40 @@ function get_nice_language_list()
* @return array mapping of language codes to language info
*/
function get_all_languages() {
- return array(
- 'bg' => array('q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'),
- 'ca' => array('q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'),
- 'cs' => array('q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'),
- 'de' => array('q' => 0.8, 'lang' => 'de', 'name' => 'German', 'direction' => 'ltr'),
- 'el' => array('q' => 0.1, 'lang' => 'el', 'name' => 'Greek', 'direction' => 'ltr'),
- 'en-us' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'),
- 'en-gb' => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'),
- 'en' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'),
- 'es' => array('q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'),
- 'fi' => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'),
- 'fr-fr' => array('q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'),
- 'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Galician', 'direction' => 'ltr'),
- 'he' => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'),
- 'it' => array('q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'),
- 'jp' => array('q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'),
- 'ko' => array('q' => 0.9, 'lang' => 'ko', 'name' => 'Korean', 'direction' => 'ltr'),
- 'mk' => array('q' => 0.5, 'lang' => 'mk', 'name' => 'Macedonian', 'direction' => 'ltr'),
- 'nb' => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'),
- 'no' => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'),
- 'nn' => array('q' => 1, 'lang' => 'nn', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'),
- 'nl' => array('q' => 0.5, 'lang' => 'nl', 'name' => 'Dutch', 'direction' => 'ltr'),
- 'pl' => array('q' => 0.5, 'lang' => 'pl', 'name' => 'Polish', 'direction' => 'ltr'),
- 'pt' => array('q' => 0.1, 'lang' => 'pt', 'name' => 'Portuguese', 'direction' => 'ltr'),
- 'pt-br' => array('q' => 0.9, 'lang' => 'pt_BR', 'name' => 'Portuguese Brazil', 'direction' => 'ltr'),
- 'ru' => array('q' => 0.9, 'lang' => 'ru', 'name' => 'Russian', 'direction' => 'ltr'),
- 'sv' => array('q' => 0.8, 'lang' => 'sv', 'name' => 'Swedish', 'direction' => 'ltr'),
- 'te' => array('q' => 0.3, 'lang' => 'te', 'name' => 'Telugu', 'direction' => 'ltr'),
- 'tr' => array('q' => 0.5, 'lang' => 'tr', 'name' => 'Turkish', 'direction' => 'ltr'),
- 'uk' => array('q' => 1, 'lang' => 'uk', 'name' => 'Ukrainian', 'direction' => 'ltr'),
- 'vi' => array('q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'),
- 'zh-cn' => array('q' => 0.9, 'lang' => 'zh_CN', 'name' => 'Chinese (Simplified)', 'direction' => 'ltr'),
- 'zh-hant' => array('q' => 0.2, 'lang' => 'zh_TW', 'name' => 'Chinese (Taiwanese)', 'direction' => 'ltr'),
- );
+ return array(
+ 'ar' => array('q' => 0.8, 'lang' => 'ar', 'name' => 'Arabic', 'direction' => 'rtl'),
+ 'bg' => array('q' => 0.8, 'lang' => 'bg', 'name' => 'Bulgarian', 'direction' => 'ltr'),
+ 'ca' => array('q' => 0.5, 'lang' => 'ca', 'name' => 'Catalan', 'direction' => 'ltr'),
+ 'cs' => array('q' => 0.5, 'lang' => 'cs', 'name' => 'Czech', 'direction' => 'ltr'),
+ 'de' => array('q' => 0.8, 'lang' => 'de', 'name' => 'German', 'direction' => 'ltr'),
+ 'el' => array('q' => 0.1, 'lang' => 'el', 'name' => 'Greek', 'direction' => 'ltr'),
+ 'en-us' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'),
+ 'en-gb' => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'),
+ 'en' => array('q' => 1, 'lang' => 'en', 'name' => 'English (US)', 'direction' => 'ltr'),
+ 'es' => array('q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'),
+ 'fi' => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'),
+ 'fr-fr' => array('q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'),
+ 'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Galician', 'direction' => 'ltr'),
+ 'he' => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'),
+ 'is' => array('q' => 0.1, 'lang' => 'is', 'name' => 'Icelandic', 'direction' => 'ltr'),
+ 'it' => array('q' => 1, 'lang' => 'it', 'name' => 'Italian', 'direction' => 'ltr'),
+ 'jp' => array('q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'),
+ 'ko' => array('q' => 0.9, 'lang' => 'ko', 'name' => 'Korean', 'direction' => 'ltr'),
+ 'mk' => array('q' => 0.5, 'lang' => 'mk', 'name' => 'Macedonian', 'direction' => 'ltr'),
+ 'nb' => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'),
+ 'no' => array('q' => 0.1, 'lang' => 'nb', 'name' => 'Norwegian (Bokmål)', 'direction' => 'ltr'),
+ 'nn' => array('q' => 1, 'lang' => 'nn', 'name' => 'Norwegian (Nynorsk)', 'direction' => 'ltr'),
+ 'nl' => array('q' => 0.5, 'lang' => 'nl', 'name' => 'Dutch', 'direction' => 'ltr'),
+ 'pl' => array('q' => 0.5, 'lang' => 'pl', 'name' => 'Polish', 'direction' => 'ltr'),
+ 'pt' => array('q' => 0.1, 'lang' => 'pt', 'name' => 'Portuguese', 'direction' => 'ltr'),
+ 'pt-br' => array('q' => 0.9, 'lang' => 'pt_BR', 'name' => 'Portuguese Brazil', 'direction' => 'ltr'),
+ 'ru' => array('q' => 0.9, 'lang' => 'ru', 'name' => 'Russian', 'direction' => 'ltr'),
+ 'sv' => array('q' => 0.8, 'lang' => 'sv', 'name' => 'Swedish', 'direction' => 'ltr'),
+ 'te' => array('q' => 0.3, 'lang' => 'te', 'name' => 'Telugu', 'direction' => 'ltr'),
+ 'tr' => array('q' => 0.5, 'lang' => 'tr', 'name' => 'Turkish', 'direction' => 'ltr'),
+ 'uk' => array('q' => 1, 'lang' => 'uk', 'name' => 'Ukrainian', 'direction' => 'ltr'),
+ 'vi' => array('q' => 0.8, 'lang' => 'vi', 'name' => 'Vietnamese', 'direction' => 'ltr'),
+ 'zh-cn' => array('q' => 0.9, 'lang' => 'zh_CN', 'name' => 'Chinese (Simplified)', 'direction' => 'ltr'),
+ 'zh-hant' => array('q' => 0.2, 'lang' => 'zh_TW', 'name' => 'Chinese (Taiwanese)', 'direction' => 'ltr'),
+ );
}
diff --git a/lib/location.php b/lib/location.php
new file mode 100644
index 000000000..bbfc15a36
--- /dev/null
+++ b/lib/location.php
@@ -0,0 +1,188 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Class for locations
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Location
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * class for locations
+ *
+ * These are stored in the DB as part of notice and profile records,
+ * but since they're about the same in both, we have a separate class
+ * for them.
+ *
+ * @category Location
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class Location
+{
+ public $lat;
+ public $lon;
+ public $location_id;
+ public $location_ns;
+ private $_url;
+
+ var $names = array();
+
+ /**
+ * Constructor that makes a Location from a string name
+ *
+ * @param string $name Human-readable name (any kind)
+ * @param string $language Language, default = common_language()
+ *
+ * @return Location Location with that name (or null if not found)
+ */
+
+ static function fromName($name, $language=null)
+ {
+ if (is_null($language)) {
+ $language = common_language();
+ }
+
+ $location = null;
+
+ // Let a third-party handle it
+
+ Event::handle('LocationFromName', array($name, $language, &$location));
+
+ return $location;
+ }
+
+ /**
+ * Constructor that makes a Location from an ID
+ *
+ * @param integer $id Identifier ID
+ * @param integer $ns Namespace of the identifier
+ * @param string $language Language to return name in (default is common)
+ *
+ * @return Location The location with this ID (or null if none)
+ */
+
+ static function fromId($id, $ns, $language=null)
+ {
+ if (is_null($language)) {
+ $language = common_language();
+ }
+
+ $location = null;
+
+ // Let a third-party handle it
+
+ Event::handle('LocationFromId', array($id, $ns, $language, &$location));
+
+ return $location;
+ }
+
+ /**
+ * Constructor that finds the nearest location to a lat/lon pair
+ *
+ * @param float $lat Latitude
+ * @param float $lon Longitude
+ * @param string $language Language for results, default = current
+ *
+ * @return Location the location found, or null if none found
+ */
+
+ static function fromLatLon($lat, $lon, $language=null)
+ {
+ if (is_null($language)) {
+ $language = common_language();
+ }
+
+ $location = null;
+
+ // Let a third-party handle it
+
+ if (Event::handle('LocationFromLatLon',
+ array($lat, $lon, $language, &$location))) {
+ // Default is just the lat/lon pair
+
+ $location = new Location();
+
+ $location->lat = $lat;
+ $location->lon = $lon;
+ }
+
+ return $location;
+ }
+
+ /**
+ * Get the name for this location in the given language
+ *
+ * @param string $language language to use, default = current
+ *
+ * @return string location name or null if not found
+ */
+
+ function getName($language=null)
+ {
+ if (is_null($language)) {
+ $language = common_language();
+ }
+
+ if (array_key_exists($language, $this->names)) {
+ return $this->names[$language];
+ } else {
+ $name = null;
+ Event::handle('LocationNameLanguage', array($this, $language, &$name));
+ if (!empty($name)) {
+ $this->names[$language] = $name;
+ return $name;
+ }
+ }
+ }
+
+ /**
+ * Get an URL suitable for this location
+ *
+ * @return string URL for this location or NULL
+ */
+
+ function getURL()
+ {
+ // Keep one cached
+
+ if (is_string($this->_url)) {
+ return $this->_url;
+ }
+
+ $url = null;
+
+ Event::handle('LocationUrl', array($this, &$url));
+
+ $this->_url = $url;
+
+ return $url;
+ }
+}
diff --git a/lib/mail.php b/lib/mail.php
index 5218059e9..dffac3262 100644
--- a/lib/mail.php
+++ b/lib/mail.php
@@ -216,7 +216,8 @@ function mail_subscribe_notify($listenee, $listener)
function mail_subscribe_notify_profile($listenee, $other)
{
- if ($listenee->email && $listenee->emailnotifysub) {
+ if ($other->hasRight(Right::EMAILONSUBSCRIBE) &&
+ $listenee->email && $listenee->emailnotifysub) {
// use the recipient's localization
common_init_locale($listenee->language);
@@ -545,6 +546,10 @@ function mail_notify_message($message, $from=null, $to=null)
function mail_notify_fave($other, $user, $notice)
{
+ if (!$user->hasRight(Right::EMAILONFAVE)) {
+ return;
+ }
+
$profile = $user->getProfile();
$bestname = $profile->getBestName();
@@ -594,10 +599,14 @@ function mail_notify_attn($user, $notice)
$sender = $notice->getProfile();
+ if (!$sender->hasRight(Right::EMAILONREPLY)) {
+ return;
+ }
+
$bestname = $sender->getBestName();
common_init_locale($user->language);
-
+
if ($notice->conversation != $notice->id) {
$conversationEmailText = "The full conversation can be read here:\n\n".
"\t%5\$s\n\n ";
@@ -607,9 +616,9 @@ function mail_notify_attn($user, $notice)
$conversationEmailText = "%5\$s";
$conversationUrl = null;
}
-
+
$subject = sprintf(_('%s (@%s) sent a notice to your attention'), $bestname, $sender->nickname);
-
+
$body = sprintf(_("%1\$s (@%9\$s) just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
"The notice is here:\n\n".
"\t%3\$s\n\n" .
@@ -635,7 +644,7 @@ function mail_notify_attn($user, $notice)
array('nickname' => $user->nickname)),//%7
common_local_url('emailsettings'), //%8
$sender->nickname); //%9
-
+
common_init_locale();
mail_to_user($user, $subject, $body);
}
diff --git a/lib/mailbox.php b/lib/mailbox.php
index e1d384a06..90a58b4c4 100644
--- a/lib/mailbox.php
+++ b/lib/mailbox.php
@@ -282,7 +282,7 @@ class MailboxAction extends CurrentUserDesignAction
$ns->name);
$this->elementEnd('span');
} else {
- $this->out->element('span', 'device', $source_name);
+ $this->element('span', 'device', $source_name);
}
break;
}
diff --git a/lib/mediafile.php b/lib/mediafile.php
new file mode 100644
index 000000000..29d752f0c
--- /dev/null
+++ b/lib/mediafile.php
@@ -0,0 +1,289 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Abstraction for media files in general
+ *
+ * TODO: combine with ImageFile?
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Media
+ * @package StatusNet
+ * @author Robin Millette <robin@millette.info>
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2008-2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+class MediaFile
+{
+
+ var $filename = null;
+ var $fileRecord = null;
+ var $user = null;
+ var $fileurl = null;
+ var $short_fileurl = null;
+ var $mimetype = null;
+
+ function __construct($user = null, $filename = null, $mimetype = null)
+ {
+ if ($user == null) {
+ $this->user = common_current_user();
+ }
+
+ $this->filename = $filename;
+ $this->mimetype = $mimetype;
+ $this->fileRecord = $this->storeFile();
+
+ $this->fileurl = common_local_url('attachment',
+ array('attachment' => $this->fileRecord->id));
+
+ $this->maybeAddRedir($this->fileRecord->id, $this->fileurl);
+ $this->short_fileurl = common_shorten_url($this->fileurl);
+ $this->maybeAddRedir($this->fileRecord->id, $this->short_fileurl);
+ }
+
+ function attachToNotice($notice)
+ {
+ File_to_post::processNew($this->fileRecord->id, $notice->id);
+ $this->maybeAddRedir($this->fileRecord->id,
+ common_local_url('file', array('notice' => $notice->id)));
+ }
+
+ function shortUrl()
+ {
+ return $this->short_fileurl;
+ }
+
+ function delete()
+ {
+ $filepath = File::path($this->filename);
+ @unlink($filepath);
+ }
+
+ function storeFile() {
+
+ $file = new File;
+
+ $file->filename = $this->filename;
+ $file->url = File::url($this->filename);
+ $filepath = File::path($this->filename);
+ $file->size = filesize($filepath);
+ $file->date = time();
+ $file->mimetype = $this->mimetype;
+
+ $file_id = $file->insert();
+
+ if (!$file_id) {
+ common_log_db_error($file, "INSERT", __FILE__);
+ throw new ClientException(_('There was a database error while saving your file. Please try again.'));
+ }
+
+ return $file;
+ }
+
+ function rememberFile($file, $short)
+ {
+ $this->maybeAddRedir($file->id, $short);
+ }
+
+ function maybeAddRedir($file_id, $url)
+ {
+ $file_redir = File_redirection::staticGet('url', $url);
+
+ if (empty($file_redir)) {
+
+ $file_redir = new File_redirection;
+ $file_redir->url = $url;
+ $file_redir->file_id = $file_id;
+
+ $result = $file_redir->insert();
+
+ if (!$result) {
+ common_log_db_error($file_redir, "INSERT", __FILE__);
+ throw new ClientException(_('There was a database error while saving your file. Please try again.'));
+ }
+ }
+ }
+
+ static function fromUpload($param = 'media', $user = null)
+ {
+ if (empty($user)) {
+ $user = common_current_user();
+ }
+
+ if (!isset($_FILES[$param]['error'])){
+ return;
+ }
+
+ switch ($_FILES[$param]['error']) {
+ case UPLOAD_ERR_OK: // success, jump out
+ break;
+ case UPLOAD_ERR_INI_SIZE:
+ throw new ClientException(_('The uploaded file exceeds the ' .
+ 'upload_max_filesize directive in php.ini.'));
+ return;
+ case UPLOAD_ERR_FORM_SIZE:
+ throw new ClientException(
+ _('The uploaded file exceeds the MAX_FILE_SIZE directive' .
+ ' that was specified in the HTML form.'));
+ return;
+ case UPLOAD_ERR_PARTIAL:
+ @unlink($_FILES[$param]['tmp_name']);
+ throw new ClientException(_('The uploaded file was only' .
+ ' partially uploaded.'));
+ return;
+ case UPLOAD_ERR_NO_FILE:
+ // No file; probably just a non-AJAX submission.
+ return;
+ case UPLOAD_ERR_NO_TMP_DIR:
+ throw new ClientException(_('Missing a temporary folder.'));
+ return;
+ case UPLOAD_ERR_CANT_WRITE:
+ throw new ClientException(_('Failed to write file to disk.'));
+ return;
+ case UPLOAD_ERR_EXTENSION:
+ throw new ClientException(_('File upload stopped by extension.'));
+ return;
+ default:
+ common_log(LOG_ERR, __METHOD__ . ": Unknown upload error " .
+ $_FILES[$param]['error']);
+ throw new ClientException(_('System error uploading file.'));
+ return;
+ }
+
+ if (!MediaFile::respectsQuota($user, $_FILES['attach']['size'])) {
+
+ // Should never actually get here
+
+ @unlink($_FILES[$param]['tmp_name']);
+ throw new ClientException(_('File exceeds user\'s quota!'));
+ return;
+ }
+
+ $mimetype = MediaFile::getUploadedFileType($_FILES[$param]['tmp_name']);
+
+ $filename = null;
+
+ if (isset($mimetype)) {
+
+ $basename = basename($_FILES[$param]['name']);
+ $filename = File::filename($user->getProfile(), $basename, $mimetype);
+ $filepath = File::path($filename);
+
+ $result = move_uploaded_file($_FILES[$param]['tmp_name'], $filepath);
+
+ if (!$result) {
+ throw new ClientException(_('File could not be moved to destination directory.'));
+ return;
+ }
+
+ } else {
+ throw new ClientException(_('Could not determine file\'s mime-type!'));
+ return;
+ }
+
+ return new MediaFile($user, $filename, $mimetype);
+ }
+
+ static function fromFilehandle($fh, $user) {
+
+ $stream = stream_get_meta_data($fh);
+
+ if (!MediaFile::respectsQuota($user, filesize($stream['uri']))) {
+
+ // Should never actually get here
+
+ throw new ClientException(_('File exceeds user\'s quota!'));
+ return;
+ }
+
+ $mimetype = MediaFile::getUploadedFileType($fh);
+
+ $filename = null;
+
+ if (isset($mimetype)) {
+
+ $filename = File::filename($user->getProfile(), "email", $mimetype);
+
+ $filepath = File::path($filename);
+
+ $result = copy($stream['uri'], $filepath) && chmod($filepath, 0664);
+
+ if (!$result) {
+ throw new ClientException(_('File could not be moved to destination directory.' .
+ $stream['uri'] . ' ' . $filepath));
+ }
+ } else {
+ throw new ClientException(_('Could not determine file\'s mime-type!'));
+ return;
+ }
+
+ return new MediaFile($user, $filename, $mimetype);
+ }
+
+ static function getUploadedFileType($f) {
+ require_once 'MIME/Type.php';
+
+ $cmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd');
+ $cmd = common_config('attachments', 'filecommand');
+
+ $filetype = null;
+
+ if (is_string($f)) {
+
+ // assuming a filename
+
+ $filetype = MIME_Type::autoDetect($f);
+ } else {
+
+ // assuming a filehandle
+
+ $stream = stream_get_meta_data($f);
+ $filetype = MIME_Type::autoDetect($stream['uri']);
+ }
+
+ if (in_array($filetype, common_config('attachments', 'supported'))) {
+ return $filetype;
+ }
+ $media = MIME_Type::getMedia($filetype);
+ if ('application' !== $media) {
+ $hint = sprintf(_(' Try using another %s format.'), $media);
+ } else {
+ $hint = '';
+ }
+ throw new ClientException(sprintf(
+ _('%s is not a supported filetype on this server.'), $filetype) . $hint);
+ }
+
+ static function respectsQuota($user, $filesize)
+ {
+ $file = new File;
+ $result = $file->isRespectsQuota($user, $filesize);
+ if ($result === true) {
+ return true;
+ } else {
+ throw new ClientException($result);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/lib/messageform.php b/lib/messageform.php
index e25ebfa08..b034be312 100644
--- a/lib/messageform.php
+++ b/lib/messageform.php
@@ -80,11 +80,22 @@ class MessageForm extends Form
/**
* ID of the form
*
- * @return int ID of the form
+ * @return string ID of the form
*/
function id()
{
+ return 'form_notice-direct';
+ }
+
+ /**
+ * Class of the form
+ *
+ * @return string class of the form
+ */
+
+ function formClass()
+ {
return 'form_notice';
}
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 9864d15eb..1be011c18 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -105,7 +105,7 @@ class NoticeForm extends Form
/**
* ID of the form
*
- * @return int ID of the form
+ * @return string ID of the form
*/
function id()
@@ -113,6 +113,17 @@ class NoticeForm extends Form
return 'form_notice';
}
+ /**
+ * Class of the form
+ *
+ * @return string class of the form
+ */
+
+ function formClass()
+ {
+ return 'form_notice';
+ }
+
/**
* Action of the form
*
diff --git a/lib/noticelist.php b/lib/noticelist.php
index 6c296f82a..bf12bb73c 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -199,6 +199,7 @@ class NoticeListItem extends Widget
{
$this->out->elementStart('div', 'entry-content');
$this->showNoticeLink();
+ $this->showNoticeLocation();
$this->showNoticeSource();
$this->showContext();
$this->out->elementEnd('div');
@@ -370,6 +371,44 @@ class NoticeListItem extends Widget
}
/**
+ * show the notice location
+ *
+ * shows the notice location in the correct language.
+ *
+ * If an URL is available, makes a link. Otherwise, just a span.
+ *
+ * @return void
+ */
+
+ function showNoticeLocation()
+ {
+ $id = $this->notice->id;
+
+ $location = $this->notice->getLocation();
+
+ if (empty($location)) {
+ return;
+ }
+
+ $name = $location->getName();
+
+ if (empty($name)) {
+ // XXX: Could be a translation issue. Fall back to... something?
+ return;
+ }
+
+ $url = $location->getUrl();
+
+ if (empty($url)) {
+ $this->out->element('span', array('class' => 'location'), $name);
+ } else {
+ $this->out->element('a', array('class' => 'location',
+ 'href' => $url),
+ $name);
+ }
+ }
+
+ /**
* Show the source of the notice
*
* Either the name (and link) of the API client that posted the notice,
@@ -474,7 +513,7 @@ class NoticeListItem extends Widget
$user = common_current_user();
if (!empty($user) &&
- ($this->notice->profile_id == $user->id || $user->hasRight(Right::deleteOthersNotice))) {
+ ($this->notice->profile_id == $user->id || $user->hasRight(Right::DELETEOTHERSNOTICE))) {
$deleteurl = common_local_url('deletenotice',
array('notice' => $this->notice->id));
diff --git a/lib/noticesection.php b/lib/noticesection.php
index b223932ef..24465f8ba 100644
--- a/lib/noticesection.php
+++ b/lib/noticesection.php
@@ -114,7 +114,7 @@ class NoticeSection extends Section
$att_class = 'attachments';
}
- $clip = theme_path('images/icons/clip.png', 'base');
+ $clip = Theme::path('images/icons/clip.png', 'base');
$this->out->elementStart('a', array('class' => $att_class, 'style' => "font-style: italic;", 'href' => $href, 'title' => "# of attachments: $count"));
$this->out->raw(" ($count&nbsp");
$this->out->element('img', array('style' => 'display: inline', 'align' => 'top', 'width' => 20, 'height' => 20, 'src' => $clip, 'alt' => 'alt'));
diff --git a/lib/oauthclient.php b/lib/oauthclient.php
index f1827726e..1a86e2460 100644
--- a/lib/oauthclient.php
+++ b/lib/oauthclient.php
@@ -43,7 +43,7 @@ require_once 'OAuth.php';
* @link http://status.net/
*
*/
-class OAuthClientCurlException extends Exception
+class OAuthClientException extends Exception
{
}
@@ -97,9 +97,14 @@ class OAuthClient
function getRequestToken($url)
{
$response = $this->oAuthGet($url);
- parse_str($response);
- $token = new OAuthToken($oauth_token, $oauth_token_secret);
- return $token;
+ $arr = array();
+ parse_str($response, $arr);
+ if (isset($arr['oauth_token']) && isset($arr['oauth_token_secret'])) {
+ $token = new OAuthToken($arr['oauth_token'], @$arr['oauth_token_secret']);
+ return $token;
+ } else {
+ throw new OAuthClientException();
+ }
}
/**
@@ -177,7 +182,7 @@ class OAuthClient
}
/**
- * Make a HTTP request using cURL.
+ * Make a HTTP request.
*
* @param string $url Where to make the
* @param array $params post parameters
@@ -186,40 +191,32 @@ class OAuthClient
*/
function httpRequest($url, $params = null)
{
- $options = array(
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_FAILONERROR => true,
- CURLOPT_HEADER => false,
- CURLOPT_FOLLOWLOCATION => true,
- CURLOPT_USERAGENT => 'StatusNet',
- CURLOPT_CONNECTTIMEOUT => 120,
- CURLOPT_TIMEOUT => 120,
- CURLOPT_HTTPAUTH => CURLAUTH_ANY,
- CURLOPT_SSL_VERIFYPEER => false,
-
- // Twitter is strict about accepting invalid "Expect" headers
-
- CURLOPT_HTTPHEADER => array('Expect:')
- );
+ $request = new HTTPClient($url);
+ $request->setConfig(array(
+ 'connect_timeout' => 120,
+ 'timeout' => 120,
+ 'follow_redirects' => true,
+ 'ssl_verify_peer' => false,
+ ));
+
+ // Twitter is strict about accepting invalid "Expect" headers
+ $request->setHeader('Expect', '');
if (isset($params)) {
- $options[CURLOPT_POST] = true;
- $options[CURLOPT_POSTFIELDS] = $params;
+ $request->setMethod(HTTP_Request2::METHOD_POST);
+ $request->setBody($params);
}
- $ch = curl_init($url);
- curl_setopt_array($ch, $options);
- $response = curl_exec($ch);
-
- if ($response === false) {
- $msg = curl_error($ch);
- $code = curl_errno($ch);
- throw new OAuthClientCurlException($msg, $code);
+ try {
+ $response = $request->send();
+ $code = $response->getStatus();
+ if ($code < 200 || $code >= 400) {
+ throw new OAuthClientException($response->getBody(), $code);
+ }
+ return $response->getBody();
+ } catch (Exception $e) {
+ throw new OAuthClientException($e->getMessage(), $e->getCode());
}
-
- curl_close($ch);
-
- return $response;
}
}
diff --git a/lib/oauthstore.php b/lib/oauthstore.php
index d617a7df7..b04bcbb8b 100644
--- a/lib/oauthstore.php
+++ b/lib/oauthstore.php
@@ -351,7 +351,7 @@ class StatusNetOAuthDataStore extends OAuthDataStore
$author = User::staticGet('uri', $author_uri);
}
if (!$author) {
- throw new Exception('No such user');
+ throw new Exception('No such user.');
}
common_log(LOG_DEBUG, print_r($author, true), __FILE__);
@@ -407,7 +407,7 @@ class StatusNetOAuthDataStore extends OAuthDataStore
$user = User::staticGet('uri', $uri);
}
if (!$user) {
- throw new Exception('No such user');
+ throw new Exception('No such user.');
}
return $user;
}
@@ -462,6 +462,10 @@ class StatusNetOAuthDataStore extends OAuthDataStore
$subscribed = $this->_getAnyProfile($subscribed_user_uri);
$subscriber = $this->_getAnyProfile($subscriber_uri);
+ if (!$subscriber->hasRight(Right::SUBSCRIBE)) {
+ return _('You have been banned from subscribing.');
+ }
+
$sub->subscribed = $subscribed->id;
$sub->subscriber = $subscriber->id;
diff --git a/lib/ping.php b/lib/ping.php
index 175bf8440..5698c4038 100644
--- a/lib/ping.php
+++ b/lib/ping.php
@@ -44,20 +44,16 @@ function ping_broadcast_notice($notice) {
array('nickname' => $profile->nickname)),
$tags));
- $context = stream_context_create(array('http' => array('method' => "POST",
- 'header' =>
- "Content-Type: text/xml\r\n".
- "User-Agent: StatusNet/".STATUSNET_VERSION."\r\n",
- 'content' => $req)));
- $file = file_get_contents($notify_url, false, $context);
+ $request = HTTPClient::start();
+ $httpResponse = $request->post($notify_url, array('Content-Type: text/xml'), $req);
- if ($file === false || mb_strlen($file) == 0) {
+ if (!$httpResponse || mb_strlen($httpResponse->getBody()) == 0) {
common_log(LOG_WARNING,
"XML-RPC empty results for ping ($notify_url, $notice->id) ");
continue;
}
- $response = xmlrpc_decode($file);
+ $response = xmlrpc_decode($httpResponse->getBody());
if (is_array($response) && xmlrpc_is_fault($response)) {
common_log(LOG_WARNING,
diff --git a/lib/plugin.php b/lib/plugin.php
index 59bf3ba9d..87d7be5a7 100644
--- a/lib/plugin.php
+++ b/lib/plugin.php
@@ -76,18 +76,4 @@ 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/profileactionform.php b/lib/profileactionform.php
new file mode 100644
index 000000000..24d4595c0
--- /dev/null
+++ b/lib/profileactionform.php
@@ -0,0 +1,187 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Superclass for forms that operate on a profile
+ *
+ * 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 Form
+ * @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);
+}
+
+/**
+ * Superclass for forms that operate on a profile
+ *
+ * Certain forms (block, silence, userflag, sandbox, delete) work on
+ * a single profile and work almost the same. So, this form extracts
+ * a lot of the common code to simplify those forms.
+ *
+ * @category Form
+ * @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 ProfileActionForm extends Form
+{
+ /**
+ * Profile of user to act on
+ */
+
+ var $profile = null;
+
+ /**
+ * Return-to args
+ */
+
+ var $args = null;
+
+ /**
+ * Constructor
+ *
+ * @param HTMLOutputter $out output channel
+ * @param Profile $profile profile of user to act on
+ * @param array $args return-to args
+ */
+
+ function __construct($out=null, $profile=null, $args=null)
+ {
+ parent::__construct($out);
+
+ $this->profile = $profile;
+ $this->args = $args;
+ }
+
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ return $this->target() . '-' . $this->profile->id;
+ }
+
+ /**
+ * class of the form
+ *
+ * @return string class of the form
+ */
+
+ function formClass()
+ {
+ return 'form_user_'.$this->target();
+ }
+
+ /**
+ * Action of the form
+ *
+ * @return string URL of the action
+ */
+
+ function action()
+ {
+ return common_local_url($this->target());
+ }
+
+ /**
+ * Legend of the Form
+ *
+ * @return void
+ */
+
+ function formLegend()
+ {
+ $this->out->element('legend', null, $this->description());
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $action = $this->target();
+
+ $this->out->hidden($action.'to-' . $this->profile->id,
+ $this->profile->id,
+ 'profileid');
+
+ if ($this->args) {
+ foreach ($this->args as $k => $v) {
+ $this->out->hidden('returnto-' . $k, $v);
+ }
+ }
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->out->submit('submit', $this->title(), 'submit',
+ null, $this->description());
+ }
+
+ /**
+ * Action this form targets
+ *
+ * @return string Name of the action, lowercased.
+ */
+
+ function target()
+ {
+ return null;
+ }
+
+ /**
+ * Title of the form
+ *
+ * @return string Title of the form, internationalized
+ */
+
+ function title()
+ {
+ return null;
+ }
+
+ /**
+ * Description of the form
+ *
+ * @return string description of the form, internationalized
+ */
+
+ function description()
+ {
+ return null;
+ }
+}
diff --git a/lib/profileformaction.php b/lib/profileformaction.php
new file mode 100644
index 000000000..8cb5f6a93
--- /dev/null
+++ b/lib/profileformaction.php
@@ -0,0 +1,139 @@
+<?php
+/**
+ * Superclass for actions that operate on a user
+ *
+ * PHP version 5
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 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/>.
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Superclass for actions that operate on a user
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ */
+
+class ProfileFormAction extends Action
+{
+ var $profile = null;
+
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->checkSessionToken();
+
+ if (!common_logged_in()) {
+ $this->clientError(_('Not logged in.'));
+ return false;
+ }
+
+ $id = $this->trimmed('profileid');
+
+ if (!$id) {
+ $this->clientError(_('No profile specified.'));
+ return false;
+ }
+
+ $this->profile = Profile::staticGet('id', $id);
+
+ if (!$this->profile) {
+ $this->clientError(_('No profile with that ID.'));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle request
+ *
+ * Shows a page with list of favorite notices
+ *
+ * @param array $args $_REQUEST args; handled in prepare()
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ $this->handlePost();
+ $this->returnToArgs();
+ }
+ }
+
+ /**
+ * Return to the calling page based on hidden arguments
+ *
+ * @return void
+ */
+
+ function returnToArgs()
+ {
+ foreach ($this->args as $k => $v) {
+ if ($k == 'returnto-action') {
+ $action = $v;
+ } else if (substr($k, 0, 9) == 'returnto-') {
+ $args[substr($k, 9)] = $v;
+ }
+ }
+
+ if ($action) {
+ common_redirect(common_local_url($action, $args), 303);
+ } else {
+ $this->clientError(_("No return-to arguments"));
+ }
+ }
+
+ /**
+ * handle a POST request
+ *
+ * sub-classes should overload this request
+ *
+ * @return void
+ */
+
+ function handlePost()
+ {
+ $this->serverError(_("unimplemented method"));
+ }
+}
diff --git a/lib/profilelist.php b/lib/profilelist.php
index 5cc211e36..3412d41d1 100644
--- a/lib/profilelist.php
+++ b/lib/profilelist.php
@@ -76,7 +76,7 @@ class ProfileList extends Widget
function startList()
{
- $this->out->elementStart('ul', 'profiles');
+ $this->out->elementStart('ul', 'profiles xoxo');
}
function endList()
@@ -140,7 +140,7 @@ class ProfileListItem extends Widget
function startItem()
{
- $this->out->elementStart('li', array('class' => 'profile',
+ $this->out->elementStart('li', array('class' => 'profile hentry',
'id' => 'profile-' . $this->profile->id));
}
@@ -175,14 +175,15 @@ class ProfileListItem extends Widget
function startProfile()
{
- $this->out->elementStart('div', 'entity_profile vcard');
+ $this->out->elementStart('div', 'entity_profile vcard entry-content');
}
function showAvatar()
{
$avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE);
$this->out->elementStart('a', array('href' => $this->profile->profileurl,
- 'class' => 'url'));
+ 'class' => 'url entry-title',
+ 'rel' => 'contact'));
$this->out->element('img', array('src' => ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE),
'class' => 'photo avatar',
'width' => AVATAR_STREAM_SIZE,
@@ -190,7 +191,7 @@ class ProfileListItem extends Widget
'alt' =>
($this->profile->fullname) ? $this->profile->fullname :
$this->profile->nickname));
- $hasFN = ($this->profile->fullname !== '') ? 'nickname' : 'fn nickname';
+ $hasFN = (!empty($this->profile->fullname)) ? 'nickname' : 'fn nickname';
$this->out->elementStart('span', $hasFN);
$this->out->raw($this->highlight($this->profile->nickname));
$this->out->elementEnd('span');
@@ -200,53 +201,37 @@ class ProfileListItem extends Widget
function showFullName()
{
if (!empty($this->profile->fullname)) {
- $this->out->elementStart('dl', 'entity_fn');
- $this->out->element('dt', null, 'Full name');
- $this->out->elementStart('dd');
$this->out->elementStart('span', 'fn');
$this->out->raw($this->highlight($this->profile->fullname));
$this->out->elementEnd('span');
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
}
}
function showLocation()
{
if (!empty($this->profile->location)) {
- $this->out->elementStart('dl', 'entity_location');
- $this->out->element('dt', null, _('Location'));
- $this->out->elementStart('dd', 'label');
+ $this->out->elementStart('span', 'location');
$this->out->raw($this->highlight($this->profile->location));
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
+ $this->out->elementEnd('span');
}
}
function showHomepage()
{
if (!empty($this->profile->homepage)) {
- $this->out->elementStart('dl', 'entity_url');
- $this->out->element('dt', null, _('URL'));
- $this->out->elementStart('dd');
$this->out->elementStart('a', array('href' => $this->profile->homepage,
'class' => 'url'));
$this->out->raw($this->highlight($this->profile->homepage));
$this->out->elementEnd('a');
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
}
}
function showBio()
{
if (!empty($this->profile->bio)) {
- $this->out->elementStart('dl', 'entity_note');
- $this->out->element('dt', null, _('Note'));
- $this->out->elementStart('dd', 'note');
+ $this->out->elementStart('p', 'note');
$this->out->raw($this->highlight($this->profile->bio));
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
+ $this->out->elementEnd('p');
}
}
diff --git a/lib/right.php b/lib/right.php
index 4e0096d46..5e66eae0e 100644
--- a/lib/right.php
+++ b/lib/right.php
@@ -45,6 +45,17 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
class Right
{
- const deleteOthersNotice = 'deleteothersnotice';
+ const DELETEOTHERSNOTICE = 'deleteothersnotice';
+ const CONFIGURESITE = 'configuresite';
+ const DELETEUSER = 'deleteuser';
+ const SILENCEUSER = 'silenceuser';
+ const SANDBOXUSER = 'sandboxuser';
+ const NEWNOTICE = 'newnotice';
+ const PUBLICNOTICE = 'publicnotice';
+ const NEWMESSAGE = 'newmessage';
+ const SUBSCRIBE = 'subscribe';
+ const EMAILONREPLY = 'emailonreply';
+ const EMAILONSUBSCRIBE = 'emailonsubscribe';
+ const EMAILONFAVE = 'emailonfave';
}
diff --git a/lib/router.php b/lib/router.php
index 4fb0834fd..9629267ac 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -71,563 +71,585 @@ class Router
{
$m = Net_URL_Mapper::getInstance();
- // In the "root"
+ if (Event::handle('StartInitializeRouter', array(&$m))) {
- $m->connect('', array('action' => 'public'));
- $m->connect('rss', array('action' => 'publicrss'));
- $m->connect('featuredrss', array('action' => 'featuredrss'));
- $m->connect('favoritedrss', array('action' => 'favoritedrss'));
- $m->connect('opensearch/people', array('action' => 'opensearch',
- 'type' => 'people'));
- $m->connect('opensearch/notice', array('action' => 'opensearch',
- 'type' => 'notice'));
+ // In the "root"
- // docs
+ $m->connect('', array('action' => 'public'));
+ $m->connect('rss', array('action' => 'publicrss'));
+ $m->connect('featuredrss', array('action' => 'featuredrss'));
+ $m->connect('favoritedrss', array('action' => 'favoritedrss'));
+ $m->connect('opensearch/people', array('action' => 'opensearch',
+ 'type' => 'people'));
+ $m->connect('opensearch/notice', array('action' => 'opensearch',
+ 'type' => 'notice'));
- $m->connect('doc/:title', array('action' => 'doc'));
+ // docs
- // main stuff is repetitive
+ $m->connect('doc/:title', array('action' => 'doc'));
- $main = array('login', 'logout', 'register', 'subscribe',
- 'unsubscribe', 'confirmaddress', 'recoverpassword',
- 'invite', 'favor', 'disfavor', 'sup',
- 'block', 'unblock', 'subedit',
- 'groupblock', 'groupunblock');
+ $m->connect('main/login?user_id=:user_id&token=:token', array('action'=>'login'), array('user_id'=> '[0-9]+', 'token'=>'.+'));
- foreach ($main as $a) {
- $m->connect('main/'.$a, array('action' => $a));
- }
+ // main stuff is repetitive
- $m->connect('main/sup/:seconds', array('action' => 'sup'),
- array('seconds' => '[0-9]+'));
+ $main = array('login', 'logout', 'register', 'subscribe',
+ 'unsubscribe', 'confirmaddress', 'recoverpassword',
+ 'invite', 'favor', 'disfavor', 'sup',
+ 'block', 'unblock', 'subedit',
+ 'groupblock', 'groupunblock',
+ 'sandbox', 'unsandbox',
+ 'silence', 'unsilence',
+ 'deleteuser');
- $m->connect('main/tagother/:id', array('action' => 'tagother'));
+ foreach ($main as $a) {
+ $m->connect('main/'.$a, array('action' => $a));
+ }
- $m->connect('main/oembed',
- array('action' => 'oembed'));
+ $m->connect('main/sup/:seconds', array('action' => 'sup'),
+ array('seconds' => '[0-9]+'));
- // these take a code
+ $m->connect('main/tagother/:id', array('action' => 'tagother'));
- foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) {
- $m->connect('main/'.$c.'/:code', array('action' => $c));
- }
+ $m->connect('main/oembed',
+ array('action' => 'oembed'));
- // exceptional
+ $m->connect('main/xrds',
+ array('action' => 'publicxrds'));
- $m->connect('main/remote', array('action' => 'remotesubscribe'));
- $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
+ // these take a code
- foreach (Router::$bare as $action) {
- $m->connect('index.php?action=' . $action, array('action' => $action));
- }
+ foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) {
+ $m->connect('main/'.$c.'/:code', array('action' => $c));
+ }
- // settings
+ // exceptional
- foreach (array('profile', 'avatar', 'password', 'im',
- 'email', 'sms', 'userdesign', 'other') as $s) {
- $m->connect('settings/'.$s, array('action' => $s.'settings'));
- }
+ $m->connect('main/remote', array('action' => 'remotesubscribe'));
+ $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '['.NICKNAME_FMT.']+'));
- // search
+ foreach (Router::$bare as $action) {
+ $m->connect('index.php?action=' . $action, array('action' => $action));
+ }
- foreach (array('group', 'people', 'notice') as $s) {
- $m->connect('search/'.$s, array('action' => $s.'search'));
- $m->connect('search/'.$s.'?q=:q',
- array('action' => $s.'search'),
- array('q' => '.+'));
- }
+ // settings
- // The second of these is needed to make the link work correctly
- // when inserted into the page. The first is needed to match the
- // route on the way in. Seems to be another Net_URL_Mapper bug to me.
- $m->connect('search/notice/rss', array('action' => 'noticesearchrss'));
- $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
- array('q' => '.+'));
-
- $m->connect('attachment/:attachment',
- array('action' => 'attachment'),
- array('attachment' => '[0-9]+'));
-
- $m->connect('attachment/:attachment/ajax',
- array('action' => 'attachment_ajax'),
- array('attachment' => '[0-9]+'));
-
- $m->connect('attachment/:attachment/thumbnail',
- array('action' => 'attachment_thumbnail'),
- array('attachment' => '[0-9]+'));
-
- $m->connect('notice/new', array('action' => 'newnotice'));
- $m->connect('notice/new?replyto=:replyto',
- array('action' => 'newnotice'),
- array('replyto' => '[A-Za-z0-9_-]+'));
- $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
- array('action' => 'newnotice'),
- array('replyto' => '[A-Za-z0-9_-]+'),
- array('inreplyto' => '[0-9]+'));
-
- $m->connect('notice/:notice/file',
- array('action' => 'file'),
- array('notice' => '[0-9]+'));
-
- $m->connect('notice/:notice',
- array('action' => 'shownotice'),
- array('notice' => '[0-9]+'));
- $m->connect('notice/delete', array('action' => 'deletenotice'));
- $m->connect('notice/delete/:notice',
- array('action' => 'deletenotice'),
- array('notice' => '[0-9]+'));
-
- // conversation
-
- $m->connect('conversation/:id',
- array('action' => 'conversation'),
- array('id' => '[0-9]+'));
-
- $m->connect('message/new', array('action' => 'newmessage'));
- $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]+'));
- $m->connect('message/:message',
- array('action' => 'showmessage'),
- array('message' => '[0-9]+'));
-
- $m->connect('user/:id',
- array('action' => 'userbyid'),
- array('id' => '[0-9]+'));
-
- $m->connect('tags/', array('action' => 'publictagcloud'));
- $m->connect('tag/', array('action' => 'publictagcloud'));
- $m->connect('tags', array('action' => 'publictagcloud'));
- $m->connect('tag', array('action' => 'publictagcloud'));
- $m->connect('tag/:tag/rss',
- array('action' => 'tagrss'),
- array('tag' => '[a-zA-Z0-9]+'));
- $m->connect('tag/:tag',
- array('action' => 'tag'),
- array('tag' => '[\pL\pN_\-\.]{1,64}'));
-
- $m->connect('peopletag/:tag',
- array('action' => 'peopletag'),
- array('tag' => '[a-zA-Z0-9]+'));
-
- $m->connect('featured/', array('action' => 'featured'));
- $m->connect('featured', array('action' => 'featured'));
- $m->connect('favorited/', array('action' => 'favorited'));
- $m->connect('favorited', array('action' => 'favorited'));
-
- // groups
-
- $m->connect('group/new', array('action' => 'newgroup'));
-
- foreach (array('edit', 'join', 'leave') as $v) {
- $m->connect('group/:nickname/'.$v,
- array('action' => $v.'group'),
- array('nickname' => '[a-zA-Z0-9]+'));
- }
+ foreach (array('profile', 'avatar', 'password', 'im',
+ 'email', 'sms', 'userdesign', 'other') as $s) {
+ $m->connect('settings/'.$s, array('action' => $s.'settings'));
+ }
- foreach (array('members', 'logo', 'rss', 'designsettings') as $n) {
- $m->connect('group/:nickname/'.$n,
- array('action' => 'group'.$n),
- array('nickname' => '[a-zA-Z0-9]+'));
- }
+ // search
- $m->connect('group/:nickname/foaf',
- array('action' => 'foafgroup'),
- array('nickname' => '[a-zA-Z0-9]+'));
-
- $m->connect('group/:nickname/blocked',
- array('action' => 'blockedfromgroup'),
- array('nickname' => '[a-zA-Z0-9]+'));
-
- $m->connect('group/:nickname/makeadmin',
- array('action' => 'makeadmin'),
- array('nickname' => '[a-zA-Z0-9]+'));
-
- $m->connect('group/:id/id',
- array('action' => 'groupbyid'),
- array('id' => '[0-9]+'));
-
- $m->connect('group/:nickname',
- array('action' => 'showgroup'),
- array('nickname' => '[a-zA-Z0-9]+'));
-
- $m->connect('group/', array('action' => 'groups'));
- $m->connect('group', array('action' => 'groups'));
- $m->connect('groups/', array('action' => 'groups'));
- $m->connect('groups', array('action' => 'groups'));
-
- // Twitter-compatible API
-
- // statuses API
-
- $m->connect('api/statuses/public_timeline.:format',
- array('action' => 'ApiTimelinePublic',
- 'format' => '(xml|json|rss|atom)'));
+ foreach (array('group', 'people', 'notice') as $s) {
+ $m->connect('search/'.$s, array('action' => $s.'search'));
+ $m->connect('search/'.$s.'?q=:q',
+ array('action' => $s.'search'),
+ array('q' => '.+'));
+ }
- $m->connect('api/statuses/friends_timeline.:format',
- array('action' => 'ApiTimelineFriends',
- 'format' => '(xml|json|rss|atom)'));
+ // The second of these is needed to make the link work correctly
+ // when inserted into the page. The first is needed to match the
+ // route on the way in. Seems to be another Net_URL_Mapper bug to me.
+ $m->connect('search/notice/rss', array('action' => 'noticesearchrss'));
+ $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
+ array('q' => '.+'));
- $m->connect('api/statuses/friends_timeline/:id.:format',
- array('action' => 'ApiTimelineFriends',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json|rss|atom)'));
- $m->connect('api/statuses/home_timeline.:format',
- array('action' => 'ApiTimelineFriends',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('attachment/:attachment',
+ array('action' => 'attachment'),
+ array('attachment' => '[0-9]+'));
+
+ $m->connect('attachment/:attachment/ajax',
+ array('action' => 'attachment_ajax'),
+ array('attachment' => '[0-9]+'));
+
+ $m->connect('attachment/:attachment/thumbnail',
+ array('action' => 'attachment_thumbnail'),
+ array('attachment' => '[0-9]+'));
+
+ $m->connect('notice/new', array('action' => 'newnotice'));
+ $m->connect('notice/new?replyto=:replyto',
+ array('action' => 'newnotice'),
+ array('replyto' => '['.NICKNAME_FMT.']+'));
+ $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
+ array('action' => 'newnotice'),
+ array('replyto' => '['.NICKNAME_FMT.']+'),
+ array('inreplyto' => '[0-9]+'));
+
+ $m->connect('notice/:notice/file',
+ array('action' => 'file'),
+ array('notice' => '[0-9]+'));
+
+ $m->connect('notice/:notice',
+ array('action' => 'shownotice'),
+ array('notice' => '[0-9]+'));
+ $m->connect('notice/delete', array('action' => 'deletenotice'));
+ $m->connect('notice/delete/:notice',
+ array('action' => 'deletenotice'),
+ array('notice' => '[0-9]+'));
+
+ $m->connect('bookmarklet/new', array('action' => 'bookmarklet'));
+
+ // conversation
+
+ $m->connect('conversation/:id',
+ array('action' => 'conversation'),
+ array('id' => '[0-9]+'));
+
+ $m->connect('message/new', array('action' => 'newmessage'));
+ $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '['.NICKNAME_FMT.']+'));
+ $m->connect('message/:message',
+ array('action' => 'showmessage'),
+ array('message' => '[0-9]+'));
+
+ $m->connect('user/:id',
+ array('action' => 'userbyid'),
+ array('id' => '[0-9]+'));
+
+ $m->connect('tags/', array('action' => 'publictagcloud'));
+ $m->connect('tag/', array('action' => 'publictagcloud'));
+ $m->connect('tags', array('action' => 'publictagcloud'));
+ $m->connect('tag', array('action' => 'publictagcloud'));
+ $m->connect('tag/:tag/rss',
+ array('action' => 'tagrss'),
+ array('tag' => '[a-zA-Z0-9]+'));
+ $m->connect('tag/:tag',
+ array('action' => 'tag'),
+ array('tag' => '[\pL\pN_\-\.]{1,64}'));
+
+ $m->connect('peopletag/:tag',
+ array('action' => 'peopletag'),
+ array('tag' => '[a-zA-Z0-9]+'));
+
+ $m->connect('featured/', array('action' => 'featured'));
+ $m->connect('featured', array('action' => 'featured'));
+ $m->connect('favorited/', array('action' => 'favorited'));
+ $m->connect('favorited', array('action' => 'favorited'));
+
+ // groups
+
+ $m->connect('group/new', array('action' => 'newgroup'));
+
+ foreach (array('edit', 'join', 'leave') as $v) {
+ $m->connect('group/:nickname/'.$v,
+ array('action' => $v.'group'),
+ array('nickname' => '[a-zA-Z0-9]+'));
+ }
+
+ foreach (array('members', 'logo', 'rss', 'designsettings') as $n) {
+ $m->connect('group/:nickname/'.$n,
+ array('action' => 'group'.$n),
+ array('nickname' => '[a-zA-Z0-9]+'));
+ }
+
+ $m->connect('group/:nickname/foaf',
+ array('action' => 'foafgroup'),
+ array('nickname' => '[a-zA-Z0-9]+'));
- $m->connect('api/statuses/home_timeline/:id.:format',
- array('action' => 'ApiTimelineFriends',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('group/:nickname/blocked',
+ array('action' => 'blockedfromgroup'),
+ array('nickname' => '[a-zA-Z0-9]+'));
- $m->connect('api/statuses/user_timeline.:format',
- array('action' => 'ApiTimelineUser',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('group/:nickname/makeadmin',
+ array('action' => 'makeadmin'),
+ array('nickname' => '[a-zA-Z0-9]+'));
- $m->connect('api/statuses/user_timeline/:id.:format',
- array('action' => 'ApiTimelineUser',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('group/:id/id',
+ array('action' => 'groupbyid'),
+ array('id' => '[0-9]+'));
- $m->connect('api/statuses/mentions.:format',
- array('action' => 'ApiTimelineMentions',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('group/:nickname',
+ array('action' => 'showgroup'),
+ array('nickname' => '[a-zA-Z0-9]+'));
- $m->connect('api/statuses/mentions/:id.:format',
- array('action' => 'ApiTimelineMentions',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('group/', array('action' => 'groups'));
+ $m->connect('group', array('action' => 'groups'));
+ $m->connect('groups/', array('action' => 'groups'));
+ $m->connect('groups', array('action' => 'groups'));
+
+ // Twitter-compatible API
+
+ // statuses API
+
+ $m->connect('api/statuses/public_timeline.:format',
+ array('action' => 'ApiTimelinePublic',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/friends_timeline.:format',
+ array('action' => 'ApiTimelineFriends',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/friends_timeline/:id.:format',
+ array('action' => 'ApiTimelineFriends',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json|rss|atom)'));
+ $m->connect('api/statuses/home_timeline.:format',
+ array('action' => 'ApiTimelineFriends',
+ 'format' => '(xml|json|rss|atom)'));
- $m->connect('api/statuses/replies.:format',
- array('action' => 'ApiTimelineMentions',
- 'format' => '(xml|json|rss|atom)'));
-
- $m->connect('api/statuses/replies/:id.:format',
- array('action' => 'ApiTimelineMentions',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('api/statuses/home_timeline/:id.:format',
+ array('action' => 'ApiTimelineFriends',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json|rss|atom)'));
- $m->connect('api/statuses/friends.:format',
- array('action' => 'ApiUserFriends',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/user_timeline.:format',
+ array('action' => 'ApiTimelineUser',
+ 'format' => '(xml|json|rss|atom)'));
- $m->connect('api/statuses/friends/:id.:format',
- array('action' => 'ApiUserFriends',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/user_timeline/:id.:format',
+ array('action' => 'ApiTimelineUser',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/mentions.:format',
+ array('action' => 'ApiTimelineMentions',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/mentions/:id.:format',
+ array('action' => 'ApiTimelineMentions',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/replies.:format',
+ array('action' => 'ApiTimelineMentions',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statuses/replies/:id.:format',
+ array('action' => 'ApiTimelineMentions',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json|rss|atom)'));
- $m->connect('api/statuses/followers.:format',
- array('action' => 'ApiUserFollowers',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/friends.:format',
+ array('action' => 'ApiUserFriends',
+ 'format' => '(xml|json)'));
- $m->connect('api/statuses/followers/:id.:format',
- array('action' => 'ApiUserFollowers',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/friends/:id.:format',
+ array('action' => 'ApiUserFriends',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
- $m->connect('api/statuses/show.:format',
- array('action' => 'ApiStatusesShow',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/followers.:format',
+ array('action' => 'ApiUserFollowers',
+ 'format' => '(xml|json)'));
- $m->connect('api/statuses/show/:id.:format',
- array('action' => 'ApiStatusesShow',
- 'id' => '[0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/followers/:id.:format',
+ array('action' => 'ApiUserFollowers',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
- $m->connect('api/statuses/update.:format',
- array('action' => 'ApiStatusesUpdate',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/show.:format',
+ array('action' => 'ApiStatusesShow',
+ 'format' => '(xml|json)'));
- $m->connect('api/statuses/destroy.:format',
- array('action' => 'ApiStatusesDestroy',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/show/:id.:format',
+ array('action' => 'ApiStatusesShow',
+ 'id' => '[0-9]+',
+ 'format' => '(xml|json)'));
- $m->connect('api/statuses/destroy/:id.:format',
- array('action' => 'ApiStatusesDestroy',
- 'id' => '[0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/update.:format',
+ array('action' => 'ApiStatusesUpdate',
+ 'format' => '(xml|json)'));
- // users
+ $m->connect('api/statuses/destroy.:format',
+ array('action' => 'ApiStatusesDestroy',
+ 'format' => '(xml|json)'));
- $m->connect('api/users/show/:id.:format',
- array('action' => 'ApiUserShow',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/statuses/destroy/:id.:format',
+ array('action' => 'ApiStatusesDestroy',
+ 'id' => '[0-9]+',
+ 'format' => '(xml|json)'));
- $m->connect('api/users/:method',
- array('action' => 'api',
- 'apiaction' => 'users'),
- array('method' => 'show(\.(xml|json))?'));
+ // users
- // direct messages
+ $m->connect('api/users/show/:id.:format',
+ array('action' => 'ApiUserShow',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
+ // direct messages
- $m->connect('api/direct_messages.:format',
- array('action' => 'ApiDirectMessage',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('api/direct_messages.:format',
+ array('action' => 'ApiDirectMessage',
+ 'format' => '(xml|json|rss|atom)'));
- $m->connect('api/direct_messages/sent.:format',
- array('action' => 'ApiDirectMessage',
- 'format' => '(xml|json|rss|atom)',
- 'sent' => true));
+ $m->connect('api/direct_messages/sent.:format',
+ array('action' => 'ApiDirectMessage',
+ 'format' => '(xml|json|rss|atom)',
+ 'sent' => true));
- $m->connect('api/direct_messages/new.:format',
- array('action' => 'ApiDirectMessageNew',
- 'format' => '(xml|json)'));
+ $m->connect('api/direct_messages/new.:format',
+ array('action' => 'ApiDirectMessageNew',
+ 'format' => '(xml|json)'));
- // friendships
+ // friendships
- $m->connect('api/friendships/show.:format',
- array('action' => 'ApiFriendshipsShow',
- 'format' => '(xml|json)'));
+ $m->connect('api/friendships/show.:format',
+ array('action' => 'ApiFriendshipsShow',
+ 'format' => '(xml|json)'));
- $m->connect('api/friendships/exists.:format',
- array('action' => 'ApiFriendshipsExists',
- 'format' => '(xml|json)'));
+ $m->connect('api/friendships/exists.:format',
+ array('action' => 'ApiFriendshipsExists',
+ 'format' => '(xml|json)'));
- $m->connect('api/friendships/create.:format',
- array('action' => 'ApiFriendshipsCreate',
- 'format' => '(xml|json)'));
+ $m->connect('api/friendships/create.:format',
+ array('action' => 'ApiFriendshipsCreate',
+ 'format' => '(xml|json)'));
- $m->connect('api/friendships/destroy.:format',
- array('action' => 'ApiFriendshipsDestroy',
- 'format' => '(xml|json)'));
+ $m->connect('api/friendships/destroy.:format',
+ array('action' => 'ApiFriendshipsDestroy',
+ 'format' => '(xml|json)'));
- $m->connect('api/friendships/create/:id.:format',
- array('action' => 'ApiFriendshipsCreate',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/friendships/create/:id.:format',
+ array('action' => 'ApiFriendshipsCreate',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
- $m->connect('api/friendships/destroy/:id.:format',
- array('action' => 'ApiFriendshipsDestroy',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/friendships/destroy/:id.:format',
+ array('action' => 'ApiFriendshipsDestroy',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
- // Social graph
+ // Social graph
- $m->connect('api/friends/ids/:id.:format',
- array('action' => 'apiFriends',
- 'ids_only' => true));
+ $m->connect('api/friends/ids/:id.:format',
+ array('action' => 'apiFriends',
+ 'ids_only' => true));
- $m->connect('api/followers/ids/:id.:format',
- array('action' => 'apiFollowers',
- 'ids_only' => true));
+ $m->connect('api/followers/ids/:id.:format',
+ array('action' => 'apiFollowers',
+ 'ids_only' => true));
- $m->connect('api/friends/ids.:format',
- array('action' => 'apiFriends',
- 'ids_only' => true));
+ $m->connect('api/friends/ids.:format',
+ array('action' => 'apiFriends',
+ 'ids_only' => true));
- $m->connect('api/followers/ids.:format',
- array('action' => 'apiFollowers',
- 'ids_only' => true));
+ $m->connect('api/followers/ids.:format',
+ array('action' => 'apiFollowers',
+ 'ids_only' => true));
- // account
+ // account
- $m->connect('api/account/verify_credentials.:format',
- array('action' => 'ApiAccountVerifyCredentials'));
+ $m->connect('api/account/verify_credentials.:format',
+ array('action' => 'ApiAccountVerifyCredentials'));
- // special case where verify_credentials is called w/out a format
+ $m->connect('api/account/update_profile.:format',
+ array('action' => 'ApiAccountUpdateProfile'));
- $m->connect('api/account/verify_credentials',
- array('action' => 'ApiAccountVerifyCredentials'));
+ $m->connect('api/account/update_profile_image.:format',
+ array('action' => 'ApiAccountUpdateProfileImage'));
- $m->connect('api/account/rate_limit_status.:format',
- array('action' => 'ApiAccountRateLimitStatus'));
+ $m->connect('api/account/update_profile_background_image.:format',
+ array('action' => 'ApiAccountUpdateProfileBackgroundImage'));
- // favorites
+ $m->connect('api/account/update_profile_colors.:format',
+ array('action' => 'ApiAccountUpdateProfileColors'));
- $m->connect('api/favorites.:format',
- array('action' => 'ApiTimelineFavorites',
- 'format' => '(xml|json|rss|atom)'));
+ $m->connect('api/account/update_delivery_device.:format',
+ array('action' => 'ApiAccountUpdateDeliveryDevice'));
- $m->connect('api/favorites/:id.:format',
- array('action' => 'ApiTimelineFavorites',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xmljson|rss|atom)'));
+ // special case where verify_credentials is called w/out a format
- $m->connect('api/favorites/create/:id.:format',
- array('action' => 'ApiFavoriteCreate',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/account/verify_credentials',
+ array('action' => 'ApiAccountVerifyCredentials'));
- $m->connect('api/favorites/destroy/:id.:format',
- array('action' => 'ApiFavoriteDestroy',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/account/rate_limit_status.:format',
+ array('action' => 'ApiAccountRateLimitStatus'));
- // notifications
+ // favorites
- $m->connect('api/notifications/:method/:argument',
- array('action' => 'api',
- 'apiaction' => 'favorites'));
+ $m->connect('api/favorites.:format',
+ array('action' => 'ApiTimelineFavorites',
+ 'format' => '(xml|json|rss|atom)'));
- // blocks
+ $m->connect('api/favorites/:id.:format',
+ array('action' => 'ApiTimelineFavorites',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xmljson|rss|atom)'));
- $m->connect('api/blocks/create/:id.:format',
- array('action' => 'ApiBlockCreate',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
+ $m->connect('api/favorites/create/:id.:format',
+ array('action' => 'ApiFavoriteCreate',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
- $m->connect('api/blocks/destroy/:id.:format',
- array('action' => 'ApiBlockDestroy',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
- // help
+ $m->connect('api/favorites/destroy/:id.:format',
+ array('action' => 'ApiFavoriteDestroy',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
+ // blocks
- $m->connect('api/help/test.:format',
- array('action' => 'ApiHelpTest',
- 'format' => '(xml|json)'));
+ $m->connect('api/blocks/create/:id.:format',
+ array('action' => 'ApiBlockCreate',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
- // statusnet
+ $m->connect('api/blocks/destroy/:id.:format',
+ array('action' => 'ApiBlockDestroy',
+ 'id' => '['.NICKNAME_FMT.']+',
+ 'format' => '(xml|json)'));
+ // help
- $m->connect('api/statusnet/version.:format',
- array('action' => 'ApiStatusnetVersion',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/config.:format',
- array('action' => 'ApiStatusnetConfig',
- 'format' => '(xml|json)'));
-
- // For older methods, we provide "laconica" base action
-
- $m->connect('api/laconica/version.:format',
- array('action' => 'ApiStatusnetVersion',
- 'format' => '(xml|json)'));
-
- $m->connect('api/laconica/config.:format',
- array('action' => 'ApiStatusnetConfig',
- 'format' => '(xml|json)'));
+ $m->connect('api/help/test.:format',
+ array('action' => 'ApiHelpTest',
+ 'format' => '(xml|json)'));
- // Groups and tags are newer than 0.8.1 so no backward-compatibility
- // necessary
+ // statusnet
- // Groups
- //'list' has to be handled differently, as php will not allow a method to be named 'list'
-
- $m->connect('api/statusnet/groups/timeline/:id.:format',
- array('action' => 'ApiTimelineGroup',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xmljson|rss|atom)'));
+ $m->connect('api/statusnet/version.:format',
+ array('action' => 'ApiStatusnetVersion',
+ 'format' => '(xml|json)'));
- $m->connect('api/statusnet/groups/show.:format',
- array('action' => 'ApiGroupShow',
- 'format' => '(xml|json)'));
+ $m->connect('api/statusnet/config.:format',
+ array('action' => 'ApiStatusnetConfig',
+ 'format' => '(xml|json)'));
- $m->connect('api/statusnet/groups/show/:id.:format',
- array('action' => 'ApiGroupShow',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/groups/join.:format',
- array('action' => 'ApiGroupJoin',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/groups/join/:id.:format',
- array('action' => 'ApiGroupJoin',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/groups/leave.:format',
- array('action' => 'ApiGroupLeave',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/groups/leave/:id.:format',
- array('action' => 'ApiGroupLeave',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/groups/is_member.:format',
- array('action' => 'ApiGroupIsMember',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/groups/list.:format',
- array('action' => 'ApiGroupList',
- 'format' => '(xml|json|rss|atom)'));
-
- $m->connect('api/statusnet/groups/list/:id.:format',
- array('action' => 'ApiGroupList',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json|rss|atom)'));
-
- $m->connect('api/statusnet/groups/list_all.:format',
- array('action' => 'ApiGroupListAll',
- 'format' => '(xml|json|rss|atom)'));
-
- $m->connect('api/statusnet/groups/membership.:format',
- array('action' => 'ApiGroupMembership',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/groups/membership/:id.:format',
- array('action' => 'ApiGroupMembership',
- 'id' => '[a-zA-Z0-9]+',
- 'format' => '(xml|json)'));
-
- $m->connect('api/statusnet/groups/create.:format',
- array('action' => 'ApiGroupCreate',
- 'format' => '(xml|json)'));
- // Tags
- $m->connect('api/statusnet/tags/timeline/:tag.:format',
- array('action' => 'ApiTimelineTag',
- 'format' => '(xmljson|rss|atom)'));
-
- // search
- $m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
- $m->connect('api/search.json', array('action' => 'twitapisearchjson'));
- $m->connect('api/trends.json', array('action' => 'twitapitrends'));
-
- // user stuff
-
- foreach (array('subscriptions', 'subscribers',
- 'nudge', 'all', 'foaf', 'xrds',
- 'replies', 'inbox', 'outbox', 'microsummary') as $a) {
- $m->connect(':nickname/'.$a,
- array('action' => $a),
- array('nickname' => '[a-zA-Z0-9]{1,64}'));
- }
+ // For older methods, we provide "laconica" base action
- foreach (array('subscriptions', 'subscribers') as $a) {
- $m->connect(':nickname/'.$a.'/:tag',
- array('action' => $a),
- array('tag' => '[a-zA-Z0-9]+',
- 'nickname' => '[a-zA-Z0-9]{1,64}'));
- }
+ $m->connect('api/laconica/version.:format',
+ array('action' => 'ApiStatusnetVersion',
+ 'format' => '(xml|json)'));
- foreach (array('rss', 'groups') as $a) {
- $m->connect(':nickname/'.$a,
- array('action' => 'user'.$a),
- array('nickname' => '[a-zA-Z0-9]{1,64}'));
- }
+ $m->connect('api/laconica/config.:format',
+ array('action' => 'ApiStatusnetConfig',
+ 'format' => '(xml|json)'));
+
+ // Groups and tags are newer than 0.8.1 so no backward-compatibility
+ // necessary
- foreach (array('all', 'replies', 'favorites') as $a) {
- $m->connect(':nickname/'.$a.'/rss',
- array('action' => $a.'rss'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'));
+ // Groups
+ //'list' has to be handled differently, as php will not allow a method to be named 'list'
+
+ $m->connect('api/statusnet/groups/timeline/:id.:format',
+ array('action' => 'ApiTimelineGroup',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xmljson|rss|atom)'));
+
+ $m->connect('api/statusnet/groups/show.:format',
+ array('action' => 'ApiGroupShow',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/show/:id.:format',
+ array('action' => 'ApiGroupShow',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/join.:format',
+ array('action' => 'ApiGroupJoin',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/join/:id.:format',
+ array('action' => 'ApiGroupJoin',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/leave.:format',
+ array('action' => 'ApiGroupLeave',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/leave/:id.:format',
+ array('action' => 'ApiGroupLeave',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/is_member.:format',
+ array('action' => 'ApiGroupIsMember',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/list.:format',
+ array('action' => 'ApiGroupList',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statusnet/groups/list/:id.:format',
+ array('action' => 'ApiGroupList',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statusnet/groups/list_all.:format',
+ array('action' => 'ApiGroupListAll',
+ 'format' => '(xml|json|rss|atom)'));
+
+ $m->connect('api/statusnet/groups/membership.:format',
+ array('action' => 'ApiGroupMembership',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/membership/:id.:format',
+ array('action' => 'ApiGroupMembership',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/create.:format',
+ array('action' => 'ApiGroupCreate',
+ 'format' => '(xml|json)'));
+ // Tags
+ $m->connect('api/statusnet/tags/timeline/:tag.:format',
+ array('action' => 'ApiTimelineTag',
+ 'format' => '(xmljson|rss|atom)'));
+
+ // search
+ $m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
+ $m->connect('api/search.json', array('action' => 'twitapisearchjson'));
+ $m->connect('api/trends.json', array('action' => 'twitapitrends'));
+
+ $m->connect('admin/site', array('action' => 'siteadminpanel'));
+ $m->connect('admin/design', array('action' => 'designadminpanel'));
+
+ $m->connect('getfile/:filename',
+ array('action' => 'getfile'),
+ array('filename' => '[A-Za-z0-9._-]+'));
+
+ // user stuff
+
+ foreach (array('subscriptions', 'subscribers',
+ 'nudge', 'all', 'foaf', 'xrds',
+ 'replies', 'inbox', 'outbox', 'microsummary') as $a) {
+ $m->connect(':nickname/'.$a,
+ array('action' => $a),
+ array('nickname' => '['.NICKNAME_FMT.']{1,64}'));
+ }
+
+ foreach (array('subscriptions', 'subscribers') as $a) {
+ $m->connect(':nickname/'.$a.'/:tag',
+ array('action' => $a),
+ array('tag' => '[a-zA-Z0-9]+',
+ 'nickname' => '['.NICKNAME_FMT.']{1,64}'));
+ }
+
+ foreach (array('rss', 'groups') as $a) {
+ $m->connect(':nickname/'.$a,
+ array('action' => 'user'.$a),
+ array('nickname' => '[a-zA-Z0-9]{1,64}'));
+ }
+
+ foreach (array('all', 'replies', 'favorites') as $a) {
+ $m->connect(':nickname/'.$a.'/rss',
+ array('action' => $a.'rss'),
+ array('nickname' => '['.NICKNAME_FMT.']{1,64}'));
+ }
+
+ $m->connect(':nickname/favorites',
+ array('action' => 'showfavorites'),
+ array('nickname' => '['.NICKNAME_FMT.']{1,64}'));
+
+ $m->connect(':nickname/avatar/:size',
+ array('action' => 'avatarbynickname'),
+ array('size' => '(original|96|48|24)',
+ 'nickname' => '['.NICKNAME_FMT.']{1,64}'));
+
+ $m->connect(':nickname/tag/:tag/rss',
+ array('action' => 'userrss'),
+ array('nickname' => '['.NICKNAME_FMT.']{1,64}'),
+ array('tag' => '[a-zA-Z0-9]+'));
+
+ $m->connect(':nickname/tag/:tag',
+ array('action' => 'showstream'),
+ array('nickname' => '['.NICKNAME_FMT.']{1,64}'),
+ array('tag' => '[a-zA-Z0-9]+'));
+
+ $m->connect(':nickname',
+ array('action' => 'showstream'),
+ array('nickname' => '['.NICKNAME_FMT.']{1,64}'));
+
+ Event::handle('RouterInitialized', array($m));
}
- $m->connect(':nickname/favorites',
- array('action' => 'showfavorites'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'));
-
- $m->connect(':nickname/avatar/:size',
- array('action' => 'avatarbynickname'),
- array('size' => '(original|96|48|24)',
- 'nickname' => '[a-zA-Z0-9]{1,64}'));
-
- $m->connect(':nickname/tag/:tag/rss',
- array('action' => 'userrss'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'),
- array('tag' => '[a-zA-Z0-9]+'));
-
- $m->connect(':nickname/tag/:tag',
- array('action' => 'showstream'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'),
- array('tag' => '[a-zA-Z0-9]+'));
-
- $m->connect(':nickname',
- array('action' => 'showstream'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'));
-
- Event::handle('RouterInitialized', array($m));
-
return $m;
}
diff --git a/lib/sandboxform.php b/lib/sandboxform.php
new file mode 100644
index 000000000..7a98e0a5f
--- /dev/null
+++ b/lib/sandboxform.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for sandboxing a user
+ *
+ * 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 Form
+ * @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);
+}
+
+/**
+ * Form for sandboxing a user
+ *
+ * @category Form
+ * @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/
+ *
+ * @see UnSandboxForm
+ */
+
+class SandboxForm extends ProfileActionForm
+{
+ /**
+ * Action this form provides
+ *
+ * @return string Name of the action, lowercased.
+ */
+
+ function target()
+ {
+ return 'sandbox';
+ }
+
+ /**
+ * Title of the form
+ *
+ * @return string Title of the form, internationalized
+ */
+
+ function title()
+ {
+ return _('Sandbox');
+ }
+
+ /**
+ * Description of the form
+ *
+ * @return string description of the form, internationalized
+ */
+
+ function description()
+ {
+ return _('Sandbox this user');
+ }
+}
diff --git a/lib/schema.php b/lib/schema.php
index 1e0c1f3e9..560884d9f 100644
--- a/lib/schema.php
+++ b/lib/schema.php
@@ -373,6 +373,26 @@ class Schema
}
/**
+ * Ensures that the table that backs a given
+ * Plugin_DataObject class exists.
+ *
+ * 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 Plugin_DataObject $dataObjectClass
+ *
+ * @return boolean success flag
+ */
+
+ public function ensureDataObject($dataObjectClass)
+ {
+ $obj = new $dataObjectClass();
+ $tableDef = $obj->tableDef();
+ return $this->ensureTable($tableDef->name,$tableDef->columns);
+ }
+
+ /**
* Ensures that a table exists with the given
* name and the given column definitions.
*
@@ -544,6 +564,19 @@ class TableDef
public $name;
/** array of ColumnDef objects for the columns. */
public $columns;
+
+ /**
+ * Constructor.
+ *
+ * @param string $name name of the table
+ * @param array $columns columns in the table
+ */
+
+ function __construct($name=null,$columns=null)
+ {
+ $this->name = $name;
+ $this->columns = $columns;
+ }
}
/**
@@ -576,6 +609,8 @@ class ColumnDef
/** 'extra' stuff. Returned by MySQL, largely
* unused. */
public $extra;
+ /** auto increment this field if no value is specific for it during an insert **/
+ public $auto_increment;
/**
* Constructor.
@@ -591,7 +626,7 @@ class ColumnDef
function __construct($name=null, $type=null, $size=null,
$nullable=true, $key=null, $default=null,
- $extra=null)
+ $extra=null, $auto_increment=false)
{
$this->name = strtolower($name);
$this->type = strtolower($type);
@@ -600,6 +635,7 @@ class ColumnDef
$this->key = $key;
$this->default = $default;
$this->extra = $extra;
+ $this->auto_increment = $auto_increment;
}
/**
@@ -617,7 +653,8 @@ class ColumnDef
$this->_typeMatch($other) &&
$this->_defaultMatch($other) &&
$this->_nullMatch($other) &&
- $this->key == $other->key);
+ $this->key == $other->key &&
+ $this->auto_increment == $other->auto_increment);
}
/**
diff --git a/lib/search_engines.php b/lib/search_engines.php
index 69f6ff468..332db3f89 100644
--- a/lib/search_engines.php
+++ b/lib/search_engines.php
@@ -46,70 +46,11 @@ class SearchEngine
}
}
-class SphinxSearch extends SearchEngine
-{
- private $sphinx;
- private $connected;
-
- function __construct($target, $table)
- {
- $fp = @fsockopen(common_config('sphinx', 'server'), common_config('sphinx', 'port'));
- if (!$fp) {
- $this->connected = false;
- return;
- }
- fclose($fp);
- parent::__construct($target, $table);
- $this->sphinx = new SphinxClient;
- $this->sphinx->setServer(common_config('sphinx', 'server'), common_config('sphinx', 'port'));
- $this->connected = true;
- }
-
- function is_connected()
- {
- return $this->connected;
- }
-
- function limit($offset, $count, $rss = false)
- {
- //FIXME without LARGEST_POSSIBLE, the most recent results aren't returned
- // this probably has a large impact on performance
- $LARGEST_POSSIBLE = 1e6;
-
- if ($rss) {
- $this->sphinx->setLimits($offset, $count, $count, $LARGEST_POSSIBLE);
- }
- else {
- // return at most 50 pages of results
- $this->sphinx->setLimits($offset, $count, 50 * ($count - 1), $LARGEST_POSSIBLE);
- }
-
- return $this->target->limit(0, $count);
- }
-
- function query($q)
- {
- $result = $this->sphinx->query($q, $this->table);
- if (!isset($result['matches'])) return false;
- $id_set = join(', ', array_keys($result['matches']));
- $this->target->whereAdd("id in ($id_set)");
- return true;
- }
-
- function set_sort_mode($mode)
- {
- if ('chron' === $mode) {
- $this->sphinx->SetSortMode(SPH_SORT_ATTR_DESC, 'created_ts');
- return $this->target->orderBy('created desc');
- }
- }
-}
-
class MySQLSearch extends SearchEngine
{
function query($q)
{
- if ('identica_people' === $this->table) {
+ if ('profile' === $this->table) {
$this->target->whereAdd('MATCH(nickname, fullname, location, bio, homepage) ' .
'AGAINST (\''.addslashes($q).'\' IN BOOLEAN MODE)');
if (strtolower($q) != $q) {
@@ -117,7 +58,7 @@ class MySQLSearch extends SearchEngine
'AGAINST (\''.addslashes(strtolower($q)).'\' IN BOOLEAN MODE)', 'OR');
}
return true;
- } else if ('identica_notices' === $this->table) {
+ } else if ('notice' === $this->table) {
// Don't show imported notices
$this->target->whereAdd('notice.is_local != ' . Notice::GATEWAY);
@@ -143,13 +84,13 @@ class MySQLLikeSearch extends SearchEngine
{
function query($q)
{
- if ('identica_people' === $this->table) {
+ if ('profile' === $this->table) {
$qry = sprintf('(nickname LIKE "%%%1$s%%" OR '.
' fullname LIKE "%%%1$s%%" OR '.
' location LIKE "%%%1$s%%" OR '.
' bio LIKE "%%%1$s%%" OR '.
' homepage LIKE "%%%1$s%%")', addslashes($q));
- } else if ('identica_notices' === $this->table) {
+ } else if ('notice' === $this->table) {
$qry = sprintf('content LIKE "%%%1$s%%"', addslashes($q));
} else {
throw new ServerException('Unknown table: ' . $this->table);
@@ -165,9 +106,9 @@ class PGSearch extends SearchEngine
{
function query($q)
{
- if ('identica_people' === $this->table) {
+ if ('profile' === $this->table) {
return $this->target->whereAdd('textsearch @@ plainto_tsquery(\''.addslashes($q).'\')');
- } else if ('identica_notices' === $this->table) {
+ } else if ('notice' === $this->table) {
// XXX: We need to filter out gateway notices (notice.is_local = -2) --Zach
diff --git a/lib/silenceform.php b/lib/silenceform.php
new file mode 100644
index 000000000..9673fa120
--- /dev/null
+++ b/lib/silenceform.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for silencing a user
+ *
+ * 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 Form
+ * @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);
+}
+
+/**
+ * Form for silencing a user
+ *
+ * @category Form
+ * @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/
+ *
+ * @see UnSilenceForm
+ */
+
+class SilenceForm extends ProfileActionForm
+{
+ /**
+ * Action this form provides
+ *
+ * @return string Name of the action, lowercased.
+ */
+
+ function target()
+ {
+ return 'silence';
+ }
+
+ /**
+ * Title of the form
+ *
+ * @return string Title of the form, internationalized
+ */
+
+ function title()
+ {
+ return _('Silence');
+ }
+
+ /**
+ * Description of the form
+ *
+ * @return string description of the form, internationalized
+ */
+
+ function description()
+ {
+ return _('Silence this user');
+ }
+}
diff --git a/lib/snapshot.php b/lib/snapshot.php
index ede846e5b..2a10c6b93 100644
--- a/lib/snapshot.php
+++ b/lib/snapshot.php
@@ -172,26 +172,9 @@ class Snapshot
{
// XXX: Use OICU2 and OAuth to make authorized requests
- $postdata = http_build_query($this->stats);
-
- $opts =
- array('http' =>
- array(
- 'method' => 'POST',
- 'header' => 'Content-type: '.
- 'application/x-www-form-urlencoded',
- 'content' => $postdata,
- 'user_agent' => 'StatusNet/'.STATUSNET_VERSION
- )
- );
-
- $context = stream_context_create($opts);
-
$reporturl = common_config('snapshot', 'reporturl');
-
- $result = @file_get_contents($reporturl, false, $context);
-
- return $result;
+ $request = HTTPClient::start();
+ $request->post($reporturl, null, $this->stats);
}
/**
diff --git a/lib/subs.php b/lib/subs.php
index 68c89c842..2fc3160de 100644
--- a/lib/subs.php
+++ b/lib/subs.php
@@ -44,8 +44,12 @@ function subs_subscribe_user($user, $other_nickname)
function subs_subscribe_to($user, $other)
{
+ if (!$user->hasRight(Right::SUBSCRIBE)) {
+ return _('You have been banned from subscribing.');
+ }
+
if ($user->isSubscribed($other)) {
- return _('Already subscribed!.');
+ return _('Already subscribed!');
}
if ($other->hasBlocked($user)) {
@@ -121,7 +125,7 @@ function subs_unsubscribe_user($user, $other_nickname)
function subs_unsubscribe_to($user, $other)
{
if (!$user->isSubscribed($other))
- return _('Not subscribed!.');
+ return _('Not subscribed!');
$sub = DB_DataObject::factory('subscription');
diff --git a/lib/theme.php b/lib/theme.php
index 08e3e8538..020ce1ac4 100644
--- a/lib/theme.php
+++ b/lib/theme.php
@@ -23,7 +23,7 @@
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
- * @copyright 2008 StatusNet, Inc.
+ * @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@@ -33,62 +33,215 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
}
/**
- * Gets the full path of a file in a theme dir based on its relative name
+ * Class for querying and manipulating a theme
*
- * @param string $relative relative path within the theme directory
- * @param string $theme name of the theme; defaults to current theme
+ * Themes are directories with some expected sub-directories and files
+ * in them. They're found in either local/theme (for locally-installed themes)
+ * or theme/ subdir of installation dir.
*
- * @return string File path to the theme file
+ * This used to be a couple of functions, but for various reasons it's nice
+ * to have a class instead.
+ *
+ * @category Output
+ * @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/
*/
-function theme_file($relative, $theme=null)
+class Theme
{
- if (empty($theme)) {
- $theme = common_config('site', 'theme');
+ var $dir = null;
+ var $path = null;
+
+ /**
+ * Constructor
+ *
+ * Determines the proper directory and path for this theme.
+ *
+ * @param string $name Name of the theme; defaults to config value
+ */
+
+ function __construct($name=null)
+ {
+ if (empty($name)) {
+ $name = common_config('site', 'theme');
+ }
+
+ // Check to see if it's in the local dir
+
+ $localroot = self::localRoot();
+
+ $fulldir = $localroot.'/'.$name;
+
+ if (file_exists($fulldir) && is_dir($fulldir)) {
+ $this->dir = $fulldir;
+ $this->path = common_path('local/theme/'.$name.'/');
+ return;
+ }
+
+ // Check to see if it's in the distribution dir
+
+ $instroot = self::installRoot();
+
+ $fulldir = $instroot.'/'.$name;
+
+ if (file_exists($fulldir) && is_dir($fulldir)) {
+
+ $this->dir = $fulldir;
+
+ $path = common_config('theme', 'path');
+
+ if (empty($path)) {
+ $path = common_config('site', 'path') . '/theme/';
+ }
+
+ if ($path[strlen($path)-1] != '/') {
+ $path .= '/';
+ }
+
+ if ($path[0] != '/') {
+ $path = '/'.$path;
+ }
+
+ $server = common_config('theme', 'server');
+
+ if (empty($server)) {
+ $server = common_config('site', 'server');
+ }
+
+ // XXX: protocol
+
+ $this->path = 'http://'.$server.$path.$name;
+ }
}
- $dir = common_config('theme', 'dir');
- if (empty($dir)) {
- $dir = INSTALLDIR.'/theme';
+
+ /**
+ * Gets the full local filename of a file in this theme.
+ *
+ * @param string $relative relative name, like 'logo.png'
+ *
+ * @return string full pathname, like /var/www/mublog/theme/default/logo.png
+ */
+
+ function getFile($relative)
+ {
+ return $this->dir.'/'.$relative;
}
- return $dir.'/'.$theme.'/'.$relative;
-}
-/**
- * Gets the full URL of a file in a theme dir based on its relative name
- *
- * @param string $relative relative path within the theme directory
- * @param string $theme name of the theme; defaults to current theme
- *
- * @return string URL of the file
- */
+ /**
+ * Gets the full HTTP url of a file in this theme
+ *
+ * @param string $relative relative name, like 'logo.png'
+ *
+ * @return string full URL, like 'http://example.com/theme/default/logo.png'
+ */
-function theme_path($relative, $theme=null)
-{
- if (empty($theme)) {
- $theme = common_config('site', 'theme');
+ function getPath($relative)
+ {
+ return $this->path.'/'.$relative;
+ }
+
+ /**
+ * Gets the full path of a file in a theme dir based on its relative name
+ *
+ * @param string $relative relative path within the theme directory
+ * @param string $name name of the theme; defaults to current theme
+ *
+ * @return string File path to the theme file
+ */
+
+ static function file($relative, $name=null)
+ {
+ $theme = new Theme($name);
+ return $theme->getFile($relative);
}
- $path = common_config('theme', 'path');
+ /**
+ * Gets the full URL of a file in a theme dir based on its relative name
+ *
+ * @param string $relative relative path within the theme directory
+ * @param string $name name of the theme; defaults to current theme
+ *
+ * @return string URL of the file
+ */
- if (empty($path)) {
- $path = common_config('site', 'path') . '/theme/';
+ static function path($relative, $name=null)
+ {
+ $theme = new Theme($name);
+ return $theme->getPath($relative);
}
- if ($path[strlen($path)-1] != '/') {
- $path .= '/';
+ /**
+ * list available theme names
+ *
+ * @return array list of available theme names
+ */
+
+ static function listAvailable()
+ {
+ $local = self::subdirsOf(self::localRoot());
+ $install = self::subdirsOf(self::installRoot());
+
+ $i = array_search('base', $install);
+
+ unset($install[$i]);
+
+ return array_merge($local, $install);
}
- if ($path[0] != '/') {
- $path = '/'.$path;
+ /**
+ * Utility for getting subdirs of a directory
+ *
+ * @param string $dir full path to directory to check
+ *
+ * @return array relative filenames of subdirs, or empty array
+ */
+
+ protected static function subdirsOf($dir)
+ {
+ $subdirs = array();
+
+ if (is_dir($dir)) {
+ if ($dh = opendir($dir)) {
+ while (($filename = readdir($dh)) !== false) {
+ if ($filename != '..' && $filename !== '.' &&
+ is_dir($dir.'/'.$filename)) {
+ $subdirs[] = $filename;
+ }
+ }
+ closedir($dh);
+ }
+ }
+
+ return $subdirs;
}
- $server = common_config('theme', 'server');
+ /**
+ * Local root dir for themes
+ *
+ * @return string local root dir for themes
+ */
- if (empty($server)) {
- $server = common_config('site', 'server');
+ protected static function localRoot()
+ {
+ return INSTALLDIR.'/local/theme';
}
- // XXX: protocol
+ /**
+ * Root dir for themes that are shipped with StatusNet
+ *
+ * @return string root dir for StatusNet themes
+ */
+
+ protected static function installRoot()
+ {
+ $instroot = common_config('theme', 'dir');
- return 'http://'.$server.$path.$theme.'/'.$relative;
+ if (empty($instroot)) {
+ $instroot = INSTALLDIR.'/theme';
+ }
+
+ return $instroot;
+ }
}
diff --git a/lib/unblockform.php b/lib/unblockform.php
index f1343757c..4fe28b21a 100644
--- a/lib/unblockform.php
+++ b/lib/unblockform.php
@@ -28,12 +28,10 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
+if (!defined('STATUSNET')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/form.php';
-
/**
* Form for unblocking a user
*
@@ -47,106 +45,38 @@ require_once INSTALLDIR.'/lib/form.php';
* @see BlockForm
*/
-class UnblockForm extends Form
+class UnblockForm extends ProfileActionForm
{
/**
- * Profile of user to unblock
- */
-
- var $profile = null;
-
- /**
- * Return-to args
- */
-
- var $args = null;
-
- /**
- * Constructor
- *
- * @param HTMLOutputter $out output channel
- * @param Profile $profile profile of user to unblock
- * @param array $args return-to args
- */
-
- function __construct($out=null, $profile=null, $args=null)
- {
- parent::__construct($out);
-
- $this->profile = $profile;
- $this->args = $args;
- }
-
- /**
- * ID of the form
- *
- * @return int ID of the form
- */
-
- function id()
- {
- return 'unblock-' . $this->profile->id;
- }
-
- /**
- * class of the form
+ * Action this form provides
*
- * @return string class of the form
+ * @return string Name of the action, lowercased.
*/
- function formClass()
+ function target()
{
- return 'form_user_unblock';
+ return 'unblock';
}
/**
- * Action of the form
- *
- * @return string URL of the action
- */
-
- function action()
- {
- return common_local_url('unblock');
- }
-
- /**
- * Legend of the Form
- *
- * @return void
- */
- function formLegend()
- {
- $this->out->element('legend', null, _('Unblock this user'));
- }
-
-
- /**
- * Data elements of the form
+ * Title of the form
*
- * @return void
+ * @return string Title of the form, internationalized
*/
- function formData()
+ function title()
{
- $this->out->hidden('unblockto-' . $this->profile->id,
- $this->profile->id,
- 'unblockto');
- if ($this->args) {
- foreach ($this->args as $k => $v) {
- $this->out->hidden('returnto-' . $k, $v);
- }
- }
+ return _('Unblock');
}
/**
- * Action elements
+ * Description of the form
*
- * @return void
+ * @return string description of the form, internationalized
*/
- function formActions()
+ function description()
{
- $this->out->submit('submit', _('Unblock'), 'submit', null, _('Unblock this user'));
+ return _('Unlock this user');
}
}
diff --git a/lib/unsandboxform.php b/lib/unsandboxform.php
new file mode 100644
index 000000000..a77634244
--- /dev/null
+++ b/lib/unsandboxform.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for unsandboxing a user
+ *
+ * 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 Form
+ * @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);
+}
+
+/**
+ * Form for unsandboxing a user
+ *
+ * Removes the "sandboxed" role for a user.
+ *
+ * @category Form
+ * @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/
+ *
+ * @see UnSandboxForm
+ */
+
+class UnsandboxForm extends ProfileActionForm
+{
+ /**
+ * Action this form provides
+ *
+ * @return string Name of the action, lowercased.
+ */
+
+ function target()
+ {
+ return 'unsandbox';
+ }
+
+ /**
+ * Title of the form
+ *
+ * @return string Title of the form, internationalized
+ */
+
+ function title()
+ {
+ return _('Unsandbox');
+ }
+
+ /**
+ * Description of the form
+ *
+ * @return string description of the form, internationalized
+ */
+
+ function description()
+ {
+ return _('Unsandbox this user');
+ }
+}
diff --git a/lib/unsilenceform.php b/lib/unsilenceform.php
new file mode 100644
index 000000000..ac02b8b6c
--- /dev/null
+++ b/lib/unsilenceform.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Form for unsilencing a user
+ *
+ * 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 Form
+ * @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);
+}
+
+/**
+ * Form for unsilencing a user
+ *
+ * @category Form
+ * @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/
+ *
+ * @see SilenceForm
+ */
+
+class UnSilenceForm extends ProfileActionForm
+{
+ /**
+ * Action this form provides
+ *
+ * @return string Name of the action, lowercased.
+ */
+
+ function target()
+ {
+ return 'unsilence';
+ }
+
+ /**
+ * Title of the form
+ *
+ * @return string Title of the form, internationalized
+ */
+
+ function title()
+ {
+ return _('Unsilence');
+ }
+
+ /**
+ * Description of the form
+ *
+ * @return string description of the form, internationalized
+ */
+
+ function description()
+ {
+ return _('Unsilence this user');
+ }
+}
diff --git a/lib/userprofile.php b/lib/userprofile.php
new file mode 100644
index 000000000..ee205af85
--- /dev/null
+++ b/lib/userprofile.php
@@ -0,0 +1,358 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Profile for a particular user
+ *
+ * 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>
+ * @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);
+}
+
+require_once INSTALLDIR.'/lib/widget.php';
+
+/**
+ * Profile of a user
+ *
+ * Shows profile information about a particular user
+ *
+ * @category Output
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Sarven Capadisli <csarven@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/
+ *
+ * @see HTMLOutputter
+ */
+
+class UserProfile extends Widget
+{
+ var $user = null;
+ var $profile = null;
+
+ function __construct($action=null, $user=null, $profile=null)
+ {
+ parent::__construct($action);
+ $this->user = $user;
+ $this->profile = $profile;
+ }
+
+ function show()
+ {
+ $this->showProfileData();
+ $this->showEntityActions();
+ }
+
+ function showProfileData()
+ {
+ if (Event::handle('StartProfilePageProfileSection', array(&$this->out, $this->profile))) {
+
+ $this->out->elementStart('div', 'entity_profile vcard author');
+ $this->out->element('h2', null, _('User profile'));
+
+ if (Event::handle('StartProfilePageProfileElements', array(&$this->out, $this->profile))) {
+
+ $this->showAvatar();
+ $this->showNickname();
+ $this->showFullName();
+ $this->showLocation();
+ $this->showHomepage();
+ $this->showBio();
+ $this->showProfileTags();
+
+ Event::handle('EndProfilePageProfileElements', array(&$this->out, $this->profile));
+ }
+
+ $this->out->elementEnd('div');
+ Event::handle('EndProfilePageProfileSection', array(&$this->out, $this->profile));
+ }
+ }
+
+ function showAvatar()
+ {
+ if (Event::handle('StartProfilePageAvatar', array($this->out, $this->profile))) {
+
+ $avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE);
+
+ $this->out->elementStart('dl', 'entity_depiction');
+ $this->out->element('dt', null, _('Photo'));
+ $this->out->elementStart('dd');
+ $this->out->element('img', array('src' => ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE),
+ 'class' => 'photo avatar',
+ 'width' => AVATAR_PROFILE_SIZE,
+ 'height' => AVATAR_PROFILE_SIZE,
+ 'alt' => $this->profile->nickname));
+ $this->out->elementEnd('dd');
+
+ $user = User::staticGet('id', $this->profile->id);
+
+ $cur = common_current_user();
+ if ($cur && $cur->id == $user->id) {
+ $this->out->elementStart('dd');
+ $this->out->element('a', array('href' => common_local_url('avatarsettings')), _('Edit Avatar'));
+ $this->out->elementEnd('dd');
+ }
+
+ $this->out->elementEnd('dl');
+
+ Event::handle('EndProfilePageAvatar', array($this->out, $this->profile));
+ }
+ }
+
+ function showNickname()
+ {
+ if (Event::handle('StartProfilePageNickname', array($this->out, $this->profile))) {
+
+ $this->out->elementStart('dl', 'entity_nickname');
+ $this->out->element('dt', null, _('Nickname'));
+ $this->out->elementStart('dd');
+ $hasFN = ($this->profile->fullname) ? 'nickname url uid' : 'fn nickname url uid';
+ $this->out->element('a', array('href' => $this->profile->profileurl,
+ 'rel' => 'me', 'class' => $hasFN),
+ $this->profile->nickname);
+ $this->out->elementEnd('dd');
+ $this->out->elementEnd('dl');
+
+ Event::handle('EndProfilePageNickname', array($this->out, $this->profile));
+ }
+ }
+
+ function showFullName()
+ {
+ if (Event::handle('StartProfilePageFullName', array($this->out, $this->profile))) {
+ if ($this->profile->fullname) {
+ $this->out->elementStart('dl', 'entity_fn');
+ $this->out->element('dt', null, _('Full name'));
+ $this->out->elementStart('dd');
+ $this->out->element('span', 'fn', $this->profile->fullname);
+ $this->out->elementEnd('dd');
+ $this->out->elementEnd('dl');
+ }
+ Event::handle('EndProfilePageFullName', array($this->out, $this->profile));
+ }
+ }
+
+ function showLocation()
+ {
+ if (Event::handle('StartProfilePageLocation', array($this->out, $this->profile))) {
+ if ($this->profile->location) {
+ $this->out->elementStart('dl', 'entity_location');
+ $this->out->element('dt', null, _('Location'));
+ $this->out->element('dd', 'label', $this->profile->location);
+ $this->out->elementEnd('dl');
+ }
+ Event::handle('EndProfilePageLocation', array($this->out, $this->profile));
+ }
+ }
+
+ function showHomepage()
+ {
+ if (Event::handle('StartProfilePageHomepage', array($this->out, $this->profile))) {
+ if ($this->profile->homepage) {
+ $this->out->elementStart('dl', 'entity_url');
+ $this->out->element('dt', null, _('URL'));
+ $this->out->elementStart('dd');
+ $this->out->element('a', array('href' => $this->profile->homepage,
+ 'rel' => 'me', 'class' => 'url'),
+ $this->profile->homepage);
+ $this->out->elementEnd('dd');
+ $this->out->elementEnd('dl');
+ }
+ Event::handle('EndProfilePageHomepage', array($this->out, $this->profile));
+ }
+ }
+
+ function showBio()
+ {
+ if (Event::handle('StartProfilePageBio', array($this->out, $this->profile))) {
+ if ($this->profile->bio) {
+ $this->out->elementStart('dl', 'entity_note');
+ $this->out->element('dt', null, _('Note'));
+ $this->out->element('dd', 'note', $this->profile->bio);
+ $this->out->elementEnd('dl');
+ }
+ Event::handle('EndProfilePageBio', array($this->out, $this->profile));
+ }
+ }
+
+ function showProfileTags()
+ {
+ if (Event::handle('StartProfilePageProfileTags', array($this->out, $this->profile))) {
+ $tags = Profile_tag::getTags($this->profile->id, $this->profile->id);
+
+ if (count($tags) > 0) {
+ $this->out->elementStart('dl', 'entity_tags');
+ $this->out->element('dt', null, _('Tags'));
+ $this->out->elementStart('dd');
+ $this->out->elementStart('ul', 'tags xoxo');
+ foreach ($tags as $tag) {
+ $this->out->elementStart('li');
+ // Avoid space by using raw output.
+ $pt = '<span class="mark_hash">#</span><a rel="tag" href="' .
+ common_local_url('peopletag', array('tag' => $tag)) .
+ '">' . $tag . '</a>';
+ $this->out->raw($pt);
+ $this->out->elementEnd('li');
+ }
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('dd');
+ $this->out->elementEnd('dl');
+ }
+ Event::handle('EndProfilePageProfileTags', array($this->out, $this->profile));
+ }
+ }
+
+ function showEntityActions()
+ {
+ if (Event::handle('StartProfilePageActionsSection', array(&$this->out, $this->profile))) {
+
+ $cur = common_current_user();
+
+ $this->out->elementStart('div', 'entity_actions');
+ $this->out->element('h2', null, _('User actions'));
+ $this->out->elementStart('ul');
+
+ if (Event::handle('StartProfilePageActionsElements', array(&$this->out, $this->profile))) {
+ if (empty($cur)) { // not logged in
+ $this->out->elementStart('li', 'entity_subscribe');
+ $this->showRemoteSubscribeLink();
+ $this->out->elementEnd('li');
+ } else {
+ if ($cur->id == $this->profile->id) { // your own page
+ $this->out->elementStart('li', 'entity_edit');
+ $this->out->element('a', array('href' => common_local_url('profilesettings'),
+ 'title' => _('Edit profile settings')),
+ _('Edit'));
+ $this->out->elementEnd('li');
+ } else { // someone else's page
+
+ // subscribe/unsubscribe button
+
+ $this->out->elementStart('li', 'entity_subscribe');
+
+ if ($cur->isSubscribed($this->profile)) {
+ $usf = new UnsubscribeForm($this->out, $this->profile);
+ $usf->show();
+ } else {
+ $sf = new SubscribeForm($this->out, $this->profile);
+ $sf->show();
+ }
+ $this->out->elementEnd('li');
+
+ if ($cur->mutuallySubscribed($this->user)) {
+
+ // message
+
+ $this->out->elementStart('li', 'entity_send-a-message');
+ $this->out->element('a', array('href' => common_local_url('newmessage', array('to' => $this->user->id)),
+ 'title' => _('Send a direct message to this user')),
+ _('Message'));
+ $this->out->elementEnd('li');
+
+ // nudge
+
+ if ($this->user->email && $this->user->emailnotifynudge) {
+ $this->out->elementStart('li', 'entity_nudge');
+ $nf = new NudgeForm($this->out, $this->user);
+ $nf->show();
+ $this->out->elementEnd('li');
+ }
+ }
+
+ // return-to args, so we don't have to keep re-writing them
+
+ list($action, $r2args) = $this->out->returnToArgs();
+
+ // push the action into the list
+
+ $r2args['action'] = $action;
+
+ // block/unblock
+
+ $blocked = $cur->hasBlocked($this->profile);
+ $this->out->elementStart('li', 'entity_block');
+ if ($blocked) {
+ $ubf = new UnblockForm($this->out, $this->profile, $r2args);
+ $ubf->show();
+ } else {
+ $bf = new BlockForm($this->out, $this->profile, $r2args);
+ $bf->show();
+ }
+ $this->out->elementEnd('li');
+
+ if ($cur->hasRight(Right::SANDBOXUSER)) {
+ $this->out->elementStart('li', 'entity_sandbox');
+ if ($this->user->isSandboxed()) {
+ $usf = new UnSandboxForm($this->out, $this->profile, $r2args);
+ $usf->show();
+ } else {
+ $sf = new SandboxForm($this->out, $this->profile, $r2args);
+ $sf->show();
+ }
+ $this->out->elementEnd('li');
+ }
+
+ if ($cur->hasRight(Right::SILENCEUSER)) {
+ $this->out->elementStart('li', 'entity_silence');
+ if ($this->user->isSilenced()) {
+ $usf = new UnSilenceForm($this->out, $this->profile, $r2args);
+ $usf->show();
+ } else {
+ $sf = new SilenceForm($this->out, $this->profile, $r2args);
+ $sf->show();
+ }
+ $this->out->elementEnd('li');
+ }
+
+ if ($cur->hasRight(Right::DELETEUSER)) {
+ $this->out->elementStart('li', 'entity_delete');
+ $df = new DeleteUserForm($this->out, $this->profile, $r2args);
+ $df->show();
+ $this->out->elementEnd('li');
+ }
+ }
+ }
+
+ Event::handle('EndProfilePageActionsElements', array(&$this->out, $this->profile));
+ }
+
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('div');
+
+ Event::handle('EndProfilePageActionsSection', array(&$this->out, $this->profile));
+ }
+ }
+
+ function showRemoteSubscribeLink()
+ {
+ $url = common_local_url('remotesubscribe',
+ array('nickname' => $this->profile->nickname));
+ $this->out->element('a', array('href' => $url,
+ 'class' => 'entity_remote_subscribe'),
+ _('Subscribe'));
+ }
+}
diff --git a/lib/util.php b/lib/util.php
index b6e89f0bd..68f3520db 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -57,12 +57,11 @@ function common_init_language()
// we can set in another locale that may not be set up
// (say, ga_ES for Galego/Galician) it seems to take it.
common_init_locale("en_US");
-
+
$language = common_language();
$locale_set = common_init_locale($language);
setlocale(LC_CTYPE, 'C');
-
- // So we don't have to make people install the gettext locales
+ // So we do not have to make people install the gettext locales
$path = common_config('site','locale_path');
bindtextdomain("statusnet", $path);
bind_textdomain_codeset("statusnet", "UTF-8");
@@ -117,23 +116,26 @@ function common_munge_password($password, $id)
}
// check if a username exists and has matching password
+
function common_check_user($nickname, $password)
{
- // NEVER allow blank passwords, even if they match the DB
- if (mb_strlen($password) == 0) {
- return false;
- }
- $user = User::staticGet('nickname', $nickname);
- if (is_null($user) || $user === false) {
- return false;
- } else {
- if (0 == strcmp(common_munge_password($password, $user->id),
- $user->password)) {
- return $user;
- } else {
- return false;
+ $authenticatedUser = false;
+
+ if (Event::handle('StartCheckPassword', array($nickname, $password, &$authenticatedUser))) {
+ $user = User::staticGet('nickname', $nickname);
+ if (!empty($user)) {
+ if (!empty($password)) { // never allow login with blank password
+ if (0 == strcmp(common_munge_password($password, $user->id),
+ $user->password)) {
+ //internal checking passed
+ $authenticatedUser =& $user;
+ }
+ }
}
+ Event::handle('EndCheckPassword', array($nickname, $password, $authenticatedUser));
}
+
+ return $authenticatedUser;
}
// is the current user logged in?
@@ -348,8 +350,11 @@ function common_current_user()
common_ensure_session();
$id = isset($_SESSION['userid']) ? $_SESSION['userid'] : false;
if ($id) {
- $_cur = User::staticGet($id);
- return $_cur;
+ $user = User::staticGet($id);
+ if ($user) {
+ $_cur = $user;
+ return $_cur;
+ }
}
}
@@ -422,7 +427,7 @@ function common_render_text($text)
function common_replace_urls_callback($text, $callback, $notice_id = null) {
// Start off with a regex
$regex = '#'.
- '(?:^|[\s\(\)\[\]\{\}\\\'\\\";]+)(?![\@\!\#])'.
+ '(?:^|[\s\<\>\(\)\[\]\{\}\\\'\\\";]+)(?![\@\!\#])'.
'('.
'(?:'.
'(?:'. //Known protocols
@@ -452,9 +457,9 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
')'.
'(?:'.
'(?:\:\d+)?'. //:port
- '(?:/[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"@]*)?'. // /path
- '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"@\/]*)?'. // ?query string
- '(?:\#[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"\@/\?\#]*)?'. // #fragment
+ '(?:/[\pN\pL$\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'@]*)?'. // /path
+ '(?:\?[\pN\pL\$\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'@\/]*)?'. // ?query string
+ '(?:\#[\pN\pL$\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\@/\?\#]*)?'. // #fragment
')(?<![\?\.\,\#\,])'.
')'.
'#ixu';
@@ -480,6 +485,10 @@ function callback_helper($matches, $callback, $notice_id) {
array(
'left'=>'{',
'right'=>'}'
+ ),
+ array(
+ 'left'=>'<',
+ 'right'=>'>'
)
);
$cannotEndWith=array('.','?',',','#');
@@ -781,12 +790,18 @@ function common_path($relative, $ssl=false)
if (is_string(common_config('site', 'sslserver')) &&
mb_strlen(common_config('site', 'sslserver')) > 0) {
$serverpart = common_config('site', 'sslserver');
- } else {
+ } else if (common_config('site', 'server')) {
$serverpart = common_config('site', 'server');
+ } else {
+ common_log(LOG_ERR, 'Site Sever not configured, unable to determine site name.');
}
} else {
$proto = 'http';
- $serverpart = common_config('site', 'server');
+ if (common_config('site', 'server')) {
+ $serverpart = common_config('site', 'server');
+ } else {
+ common_log(LOG_ERR, 'Site Sever not configured, unable to determine site name.');
+ }
}
return $proto.'://'.$serverpart.'/'.$pathpart.$relative;
@@ -1074,7 +1089,11 @@ function common_log_objstring(&$object)
$arr = $object->toArray();
$fields = array();
foreach ($arr as $k => $v) {
- $fields[] = "$k='$v'";
+ if (is_object($v)) {
+ $fields[] = "$k='".get_class($v)."'";
+ } else {
+ $fields[] = "$k='$v'";
+ }
}
$objstring = $object->tableName() . '[' . implode(',', $fields) . ']';
return $objstring;
@@ -1360,9 +1379,28 @@ function common_memcache()
}
}
+function common_license_terms($uri)
+{
+ if(preg_match('/creativecommons.org\/licenses\/([^\/]+)/', $uri, $matches)) {
+ return explode('-',$matches[1]);
+ }
+ return array($uri);
+}
+
function common_compatible_license($from, $to)
{
+ $from_terms = common_license_terms($from);
+ // public domain and cc-by are compatible with everything
+ if(count($from_terms) == 1 && ($from_terms[0] == 'publicdomain' || $from_terms[0] == 'by')) {
+ return true;
+ }
+ $to_terms = common_license_terms($to);
+ // sa is compatible across versions. IANAL
+ if(in_array('sa',$from_terms) || in_array('sa',$to_terms)) {
+ return count(array_diff($from_terms, $to_terms)) == 0;
+ }
// XXX: better compatibility check needed here!
+ // Should at least normalise URIs
return ($from == $to);
}
@@ -1385,25 +1423,18 @@ function common_shorten_url($long_url)
if (empty($user)) {
// common current user does not find a user when called from the XMPP daemon
// therefore we'll set one here fix, so that XMPP given URLs may be shortened
- $svc = 'ur1.ca';
+ $shortenerName = 'ur1.ca';
} else {
- $svc = $user->urlshorteningservice;
- }
- global $_shorteners;
- if (!isset($_shorteners[$svc])) {
- //the user selected service doesn't exist, so default to ur1.ca
- $svc = 'ur1.ca';
+ $shortenerName = $user->urlshorteningservice;
}
- if (!isset($_shorteners[$svc])) {
- // no shortener plugins installed.
- return $long_url;
- }
-
- $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;
+ if(Event::handle('StartShortenUrl', array($long_url,$shortenerName,&$shortenedUrl))){
+ //URL wasn't shortened, so return the long url
+ return $long_url;
+ }else{
+ //URL was shortened, so return the result
+ return $shortenedUrl;
+ }
}
function common_client_ip()
diff --git a/lib/xrdsoutputter.php b/lib/xrdsoutputter.php
new file mode 100644
index 000000000..4b77ed5a3
--- /dev/null
+++ b/lib/xrdsoutputter.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Low-level generator for HTML
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Output
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @copyright 2008 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/xmloutputter.php';
+
+/**
+ * Low-level generator for XRDS XML
+ *
+ * @category Output
+ * @package StatusNet
+ * @author Craig Andrews <candrews@integralblue.com>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @see Action
+ * @see XMLOutputter
+ */
+class XRDSOutputter extends XMLOutputter
+{
+ public function startXRDS()
+ {
+ header('Content-Type: application/xrds+xml');
+ $this->startXML();
+ $this->elementStart('XRDS', array('xmlns' => 'xri://$xrds'));
+ }
+
+ public function endXRDS()
+ {
+ $this->elementEnd('XRDS');
+ $this->endXML();
+ }
+
+ /**
+ * Show service.
+ *
+ * @param string $type XRDS type
+ * @param string $uri URI
+ * @param array $params type parameters, null by default
+ * @param array $sigs type signatures, null by default
+ * @param string $localId local ID, null by default
+ *
+ * @return void
+ */
+ function showXrdsService($type, $uri, $params=null, $sigs=null, $localId=null)
+ {
+ $this->elementStart('Service');
+ if ($uri) {
+ $this->element('URI', null, $uri);
+ }
+ $this->element('Type', null, $type);
+ if ($params) {
+ foreach ($params as $param) {
+ $this->element('Type', null, $param);
+ }
+ }
+ if ($sigs) {
+ foreach ($sigs as $sig) {
+ $this->element('Type', null, $sig);
+ }
+ }
+ if ($localId) {
+ $this->element('LocalID', null, $localId);
+ }
+ $this->elementEnd('Service');
+ }
+}