summaryrefslogtreecommitdiff
path: root/actions
diff options
context:
space:
mode:
authorBrion Vibber <brion@pobox.com>2010-10-18 15:21:02 -0700
committerBrion Vibber <brion@pobox.com>2010-10-18 15:21:02 -0700
commit53d45d7ffbe6bcdf336a0e666942557c11cf909b (patch)
tree7c26b636624162fd1036529ecc08ae63caef59f6 /actions
parent9a35e48ee2a15ded31dd1dba8e0af9071e8a28ac (diff)
parent39cfdf0d8b333fec0c35e02d3ce9abb4f4338bf5 (diff)
Merge branch '0.9.x'
Diffstat (limited to 'actions')
-rw-r--r--actions/apidirectmessage.php6
-rw-r--r--actions/apidirectmessagenew.php20
-rw-r--r--actions/apioauthaccesstoken.php56
-rw-r--r--actions/apioauthauthorize.php241
-rw-r--r--actions/apioauthpin.php67
-rw-r--r--actions/apioauthrequesttoken.php91
-rw-r--r--actions/deletegroup.php235
-rw-r--r--actions/deletenotice.php11
-rw-r--r--actions/designadminpanel.php14
-rw-r--r--actions/pathsadminpanel.php100
-rw-r--r--actions/showfavorites.php14
-rw-r--r--actions/showgroup.php6
-rw-r--r--actions/shownotice.php2
-rw-r--r--actions/showstream.php2
14 files changed, 739 insertions, 126 deletions
diff --git a/actions/apidirectmessage.php b/actions/apidirectmessage.php
index e7ea38dfa..4e2ec5eb0 100644
--- a/actions/apidirectmessage.php
+++ b/actions/apidirectmessage.php
@@ -74,6 +74,7 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->user = $this->auth_user;
if (empty($this->user)) {
+ // TRANS: Client error given when a user was not found (404).
$this->clientError(_('No such user.'), 404, $this->format);
return;
}
@@ -86,10 +87,12 @@ class ApiDirectMessageAction extends ApiAuthAction
// Action was called by /api/direct_messages/sent.format
$this->title = sprintf(
+ // TRANS: %s is a user nickname.
_("Direct messages from %s"),
$this->user->nickname
);
$this->subtitle = sprintf(
+ // TRANS: %s is a user nickname.
_("All the direct messages sent from %s"),
$this->user->nickname
);
@@ -98,10 +101,12 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->id = "tag:$taguribase:SentDirectMessages:" . $this->user->id;
} else {
$this->title = sprintf(
+ // TRANS: %s is a user nickname.
_("Direct messages to %s"),
$this->user->nickname
);
$this->subtitle = sprintf(
+ // TRANS: %s is a user nickname.
_("All the direct messages sent to %s"),
$this->user->nickname
);
@@ -153,6 +158,7 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->showJsonDirectMessages();
break;
default:
+ // TRANS: Client error given when an API method was not found (404).
$this->clientError(_('API method not found.'), $code = 404);
break;
}
diff --git a/actions/apidirectmessagenew.php b/actions/apidirectmessagenew.php
index 44e205ebb..c126cd312 100644
--- a/actions/apidirectmessagenew.php
+++ b/actions/apidirectmessagenew.php
@@ -49,7 +49,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class ApiDirectMessageNewAction extends ApiAuthAction
{
var $other = null;
@@ -63,7 +62,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
* @return boolean success flag
*
*/
-
function prepare($args)
{
parent::prepare($args);
@@ -99,7 +97,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
*
* @return void
*/
-
function handle($args)
{
parent::handle($args);
@@ -116,6 +113,7 @@ class ApiDirectMessageNewAction extends ApiAuthAction
if (empty($this->content)) {
$this->clientError(
+ // TRANS: Client error (406).
_('No message text!'),
406,
$this->format
@@ -123,9 +121,10 @@ class ApiDirectMessageNewAction extends ApiAuthAction
} else {
$content_shortened = common_shorten_links($this->content);
if (Message::contentTooLong($content_shortened)) {
+ // TRANS: Client error displayed when message content is too long.
+ // TRANS: %d is the maximum number of characters for a message.
$this->clientError(
- sprintf(
- _('That\'s too long. Max message size is %d chars.'),
+ sprintf(_m('That\'s too long. Maximum message size is %d character.', 'That\'s too long. Maximum message size is %d characters.', Message::maxContent()),
Message::maxContent()
),
406,
@@ -136,10 +135,12 @@ class ApiDirectMessageNewAction extends ApiAuthAction
}
if (empty($this->other)) {
+ // TRANS: Client error displayed if a recipient user could not be found (403).
$this->clientError(_('Recipient user not found.'), 403, $this->format);
return;
} else if (!$this->user->mutuallySubscribed($this->other)) {
$this->clientError(
+ // TRANS: Client error displayed trying to direct message another user who's not a friend (403).
_('Can\'t send direct messages to users who aren\'t your friend.'),
403,
$this->format
@@ -149,10 +150,9 @@ class ApiDirectMessageNewAction extends ApiAuthAction
// Note: sending msgs to yourself is allowed by Twitter
- $errmsg = 'Don\'t send a message to yourself; ' .
- 'just say it to yourself quietly instead.';
-
- $this->clientError(_($errmsg), 403, $this->format);
+ // TRANS: Client error displayed trying to direct message self (403).
+ $this->clientError(_('Do not send a message to yourself; ' .
+ 'just say it to yourself quietly instead.'), 403, $this->format);
return;
}
@@ -176,6 +176,4 @@ class ApiDirectMessageNewAction extends ApiAuthAction
$this->showSingleJsondirectMessage($message);
}
}
-
}
-
diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php
index 887df4c20..663a7a2bb 100644
--- a/actions/apioauthaccesstoken.php
+++ b/actions/apioauthaccesstoken.php
@@ -2,7 +2,8 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
- * Exchange an authorized OAuth request token for an access token
+ * Action for getting OAuth token credentials (exchange an authorized
+ * request token for an access token)
*
* PHP version 5
*
@@ -34,7 +35,8 @@ if (!defined('STATUSNET')) {
require_once INSTALLDIR . '/lib/apioauth.php';
/**
- * Exchange an authorized OAuth request token for an access token
+ * Action for getting OAuth token credentials (exchange an authorized
+ * request token for an access token)
*
* @category API
* @package StatusNet
@@ -45,6 +47,8 @@ require_once INSTALLDIR . '/lib/apioauth.php';
class ApiOauthAccessTokenAction extends ApiOauthAction
{
+ protected $reqToken = null;
+ protected $verifier = null;
/**
* Class handler.
@@ -65,30 +69,58 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
$atok = null;
+ // XXX: Insist that oauth_token and oauth_verifier be populated?
+ // Spec doesn't say they MUST be.
+
try {
+
$req = OAuthRequest::from_request();
+
+ $this->reqToken = $req->get_parameter('oauth_token');
+ $this->verifier = $req->get_parameter('oauth_verifier');
+
$atok = $server->fetch_access_token($req);
} catch (OAuthException $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
common_debug(var_export($req, true));
- $this->outputError($e->getMessage());
- return;
+ $code = $e->getCode();
+ $this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text');
}
if (empty($atok)) {
- common_debug('couldn\'t get access token.');
- print "Token exchange failed. Has the request token been authorized?\n";
+
+ // Token exchange failed -- log it
+
+ list($proxy, $ip) = common_client_ip();
+
+ $msg = sprintf(
+ 'API OAuth - Failure exchanging request token for access token, '
+ . 'request token = %s, verifier = %s, IP = %s, proxy = %s',
+ $this->reqToken,
+ $this->verifier,
+ $ip,
+ $proxy
+ );
+
+ common_log(LOG_WARNING, $msg);
+
+ $this->clientError(_("Invalid request token or verifier.", 400, 'text'));
+
} else {
- print $atok;
+ $this->showAccessToken($atok);
}
}
- function outputError($msg)
+ /*
+ * Display OAuth token credentials
+ *
+ * @param OAuthToken token the access token
+ */
+
+ function showAccessToken($token)
{
- header('HTTP/1.1 401 Unauthorized');
- header('Content-Type: text/html; charset=utf-8');
- print $msg . "\n";
+ header('Content-Type: application/x-www-form-urlencoded');
+ print $token;
}
}
-
diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php
index c2fbbcdd8..ea5c30c2a 100644
--- a/actions/apioauthauthorize.php
+++ b/actions/apioauthauthorize.php
@@ -32,6 +32,7 @@ if (!defined('STATUSNET')) {
}
require_once INSTALLDIR . '/lib/apioauth.php';
+require_once INSTALLDIR . '/lib/info.php';
/**
* Authorize an OAuth request token
@@ -43,9 +44,10 @@ require_once INSTALLDIR . '/lib/apioauth.php';
* @link http://status.net/
*/
-class ApiOauthAuthorizeAction extends ApiOauthAction
+class ApiOauthAuthorizeAction extends Action
{
- var $oauth_token;
+ var $oauthTokenParam;
+ var $reqToken;
var $callback;
var $app;
var $nickname;
@@ -67,12 +69,17 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
{
parent::prepare($args);
- $this->nickname = $this->trimmed('nickname');
- $this->password = $this->arg('password');
- $this->oauth_token = $this->arg('oauth_token');
- $this->callback = $this->arg('oauth_callback');
- $this->store = new ApiStatusNetOAuthDataStore();
- $this->app = $this->store->getAppByRequestToken($this->oauth_token);
+ $this->nickname = $this->trimmed('nickname');
+ $this->password = $this->arg('password');
+ $this->oauthTokenParam = $this->arg('oauth_token');
+ $this->callback = $this->arg('oauth_callback');
+ $this->store = new ApiStatusNetOAuthDataStore();
+
+ try {
+ $this->app = $this->store->getAppByRequestToken($this->oauthTokenParam);
+ } catch (Exception $e) {
+ $this->clientError($e->getMessage());
+ }
return true;
}
@@ -97,14 +104,30 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
} else {
- if (empty($this->oauth_token)) {
+ // Make sure a oauth_token parameter was provided
+ if (empty($this->oauthTokenParam)) {
$this->clientError(_('No oauth_token parameter provided.'));
- return;
+ } else {
+
+ // Check to make sure the token exists
+ $this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
+
+ if (empty($this->reqToken)) {
+ $this->serverError(
+ _('Invalid request token.')
+ );
+ } else {
+
+ // Check to make sure we haven't already authorized the token
+ if ($this->reqToken->state != 0) {
+ $this->clientError("Invalid request token.");
+ }
+ }
}
+ // make sure there's an app associated with this token
if (empty($this->app)) {
- $this->clientError(_('Invalid token.'));
- return;
+ $this->clientError(_('Invalid request token.'));
}
$name = $this->app->name;
@@ -120,8 +143,8 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
+ $this->showForm(
+ _('There was a problem with your session token. Try again, please.'));
return;
}
@@ -130,6 +153,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$user = null;
if (!common_logged_in()) {
+
+ // XXX Force credentials check?
+
+ // XXX OpenID
+
$user = common_check_user($this->nickname, $this->password);
if (empty($user)) {
$this->showForm(_("Invalid nickname / password!"));
@@ -141,9 +169,15 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if ($this->arg('allow')) {
- // mark the req token as authorized
+ // fetch the token
+ $this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
- $this->store->authorize_token($this->oauth_token);
+ // mark the req token as authorized
+ try {
+ $this->store->authorize_token($this->oauthTokenParam);
+ } catch (Exception $e) {
+ $this->serverError($e->getMessage());
+ }
// Check to see if there was a previous token associated
// with this user/app and kill it. If the user is doing this she
@@ -156,8 +190,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!$result) {
common_log_db_error($appUser, 'DELETE', __FILE__);
- throw new ServerException(_('Database error deleting OAuth application user.'));
- return;
+ $this->serverError(_('Database error deleting OAuth application user.'));
}
}
@@ -175,20 +208,19 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
// granted. The OAuth app user record then gets updated
// with the new access token and access type.
- $appUser->token = $this->oauth_token;
+ $appUser->token = $this->oauthTokenParam;
$appUser->created = common_sql_now();
$result = $appUser->insert();
if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__);
- throw new ServerException(_('Database error inserting OAuth application user.'));
- return;
+ $this->serverError(_('Database error inserting OAuth application user.'));
}
- // if we have a callback redirect and provide the token
+ // If we have a callback redirect and provide the token
- // A callback specified in the app setup overrides whatever
+ // Note: A callback specified in the app setup overrides whatever
// is passed in with the request.
if (!empty($this->app->callback_url)) {
@@ -197,40 +229,40 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!empty($this->callback)) {
- $target_url = $this->getCallback($this->callback,
- array('oauth_token' => $this->oauth_token));
+ $targetUrl = $this->getCallback(
+ $this->callback,
+ array(
+ 'oauth_token' => $this->oauthTokenParam,
+ 'oauth_verifier' => $this->reqToken->verifier // 1.0a
+ )
+ );
+
+ // Redirect the user to the provided OAuth callback
+ common_redirect($targetUrl, 303);
- common_redirect($target_url, 303);
} else {
- common_debug("callback was empty!");
+ common_log(
+ LOG_INFO,
+ "No oauth_callback parameter provided for application ID "
+ . $this->app->id
+ . " when authorizing request token."
+ );
}
- // otherwise inform the user that the rt was authorized
-
- $this->elementStart('p');
-
- // XXX: Do OAuth 1.0a verifier code
-
- $this->raw(sprintf(_("The request token %s has been authorized. " .
- 'Please exchange it for an access token.'),
- $this->oauth_token));
-
- $this->elementEnd('p');
-
- } else if ($this->arg('deny')) {
-
- $datastore = new ApiStatusNetOAuthDataStore();
- $datastore->revoke_token($this->oauth_token, 0);
+ // Otherwise, inform the user that the rt was authorized
+ $this->showAuthorized();
- $this->elementStart('p');
+ } else if ($this->arg('cancel')) {
- $this->raw(sprintf(_("The request token %s has been denied and revoked."),
- $this->oauth_token));
+ try {
+ $this->store->revoke_token($this->oauthTokenParam, 0);
+ $this->showCanceled();
+ } catch (Exception $e) {
+ $this->ServerError($e->getMessage());
+ }
- $this->elementEnd('p');
} else {
$this->clientError(_('Unexpected form submission.'));
- return;
}
}
@@ -276,7 +308,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
_('Allow or deny access'));
$this->hidden('token', common_session_token());
- $this->hidden('oauth_token', $this->oauth_token);
+ $this->hidden('oauth_token', $this->oauthTokenParam);
$this->hidden('oauth_callback', $this->callback);
$this->elementStart('ul', 'form_data');
@@ -321,11 +353,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
}
- $this->element('input', array('id' => 'deny_submit',
+ $this->element('input', array('id' => 'cancel_submit',
'class' => 'submit submit form_action-primary',
- 'name' => 'deny',
+ 'name' => 'cancel',
'type' => 'submit',
- 'value' => _('Deny')));
+ 'value' => _('Cancel')));
$this->element('input', array('id' => 'allow_submit',
'class' => 'submit submit form_action-secondary',
@@ -348,7 +380,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
function getInstructions()
{
- return _('Allow or deny access to your account information.');
+ return _('Authorize access to your account information.');
}
/**
@@ -388,4 +420,107 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
// NOP
}
+ /*
+ * Show a nice message confirming the authorization
+ * operation was canceled.
+ *
+ * @return nothing
+ */
+
+ function showCanceled()
+ {
+ $info = new InfoAction(
+ _('Authorization canceled.'),
+ sprintf(
+ _('The request token %s has been revoked.'),
+ $this->oauthTokenParm
+ )
+ );
+
+ $info->showPage();
+ }
+
+ /*
+ * Show a nice message that the authorization was successful.
+ * If the operation is out-of-band, show a pin.
+ *
+ * @return nothing
+ */
+
+ function showAuthorized()
+ {
+ $title = sprintf(
+ _("You have successfully authorized %s."),
+ $this->app->name
+ );
+
+ $msg = sprintf(
+ _('Please return to %s and enter the following security code to complete the process.'),
+ $this->app->name
+ );
+
+ if ($this->reqToken->verified_callback == 'oob') {
+ $pin = new ApiOauthPinAction($title, $msg, $this->reqToken->verifier);
+ $pin->showPage();
+ } else {
+
+ // NOTE: This would only happen if an application registered as
+ // a web application but sent in 'oob' for the oauth_callback
+ // parameter. Usually web apps will send in a callback and
+ // not use the pin-based workflow.
+
+ $info = new InfoAction(
+ $title,
+ $msg,
+ $this->oauthTokenParam,
+ $this->reqToken->verifier
+ );
+
+ $info->showPage();
+ }
+ }
+
+ /*
+ * Properly format the callback URL and parameters so it's
+ * suitable for a redirect in the OAuth dance
+ *
+ * @param string $url the URL
+ * @param array $params an array of parameters
+ *
+ * @return string $url a URL to use for redirecting to
+ */
+
+ function getCallback($url, $params)
+ {
+ foreach ($params as $k => $v) {
+ $url = $this->appendQueryVar(
+ $url,
+ OAuthUtil::urlencode_rfc3986($k),
+ OAuthUtil::urlencode_rfc3986($v)
+ );
+ }
+
+ return $url;
+ }
+
+ /*
+ * Append a new query parameter after any existing query
+ * parameters.
+ *
+ * @param string $url the URL
+ * @prarm string $k the parameter name
+ * @param string $v value of the paramter
+ *
+ * @return string $url the new URL with added parameter
+ */
+
+ function appendQueryVar($url, $k, $v) {
+ $url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&');
+ $url = substr($url, 0, -1);
+ if (strpos($url, '?') === false) {
+ return ($url . '?' . $k . '=' . $v);
+ } else {
+ return ($url . '&' . $k . '=' . $v);
+ }
+ }
}
diff --git a/actions/apioauthpin.php b/actions/apioauthpin.php
new file mode 100644
index 000000000..5e6713a54
--- /dev/null
+++ b/actions/apioauthpin.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Action for displaying an OAuth verifier pin
+ *
+ * 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 Zach Copley <zach@status.net>
+ * @copyright 2010 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/info.php';
+
+/**
+ * Class for displaying an OAuth verifier pin
+ *
+ * XXX: I'm pretty sure we don't need to check the logged in state here. -- Zach
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class ApiOauthPinAction extends InfoAction
+{
+ function __construct($title, $message, $verifier)
+ {
+ $this->verifier = $verifier;
+ $this->title = $title;
+ parent::__construct($title, $message);
+ }
+
+ /**
+ * Display content.
+ *
+ * @return nothing
+ */
+ function showContent()
+ {
+ $this->element('div', array('class' => 'info'), $this->message);
+ $this->element('div', array('id' => 'oauth_pin'), $this->verifier);
+ }
+}
diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php
index 4fa626d86..478d2dbfc 100644
--- a/actions/apioauthrequesttoken.php
+++ b/actions/apioauthrequesttoken.php
@@ -2,7 +2,7 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
- * Get an OAuth request token
+ * Issue temporary OAuth credentials (a request token)
*
* PHP version 5
*
@@ -34,7 +34,7 @@ if (!defined('STATUSNET')) {
require_once INSTALLDIR . '/lib/apioauth.php';
/**
- * Get an OAuth request token
+ * Issue temporary OAuth credentials (a request token)
*
* @category API
* @package StatusNet
@@ -58,22 +58,23 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
{
parent::prepare($args);
- $this->callback = $this->arg('oauth_callback');
-
- if (!empty($this->callback)) {
- common_debug("callback: $this->callback");
- }
+ // XXX: support "force_login" parameter like Twitter? (Forces the user to enter
+ // their credentials to ensure the correct users account is authorized.)
return true;
}
/**
- * Class handler.
+ * Handle a request for temporary OAuth credentials
+ *
+ * Make sure the request is kosher, then emit a set of temporary
+ * credentials -- AKA an unauthorized request token.
*
* @param array $args array of arguments
*
* @return void
*/
+
function handle($args)
{
parent::handle($args);
@@ -85,14 +86,78 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
$server->add_signature_method($hmac_method);
try {
- $req = OAuthRequest::from_request();
+
+ $req = OAuthRequest::from_request();
+
+ // verify callback
+ if (!$this->verifyCallback($req->get_parameter('oauth_callback'))) {
+ throw new OAuthException(
+ "You must provide a valid URL or 'oob' in oauth_callback.",
+ 400
+ );
+ }
+
+ // check signature and issue a new request token
$token = $server->fetch_request_token($req);
- print $token;
+
+ common_log(
+ LOG_INFO,
+ sprintf(
+ "API OAuth - Issued request token %s for consumer %s with oauth_callback %s",
+ $token->key,
+ $req->get_parameter('oauth_consumer_key'),
+ "'" . $req->get_parameter('oauth_callback') ."'"
+ )
+ );
+
+ // return token to the client
+ $this->showRequestToken($token);
+
} catch (OAuthException $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
- header('HTTP/1.1 401 Unauthorized');
- header('Content-Type: text/html; charset=utf-8');
- print $e->getMessage() . "\n";
+
+ // Return 401 for for bad credentials or signature problems,
+ // and 400 for missing or unsupported parameters
+
+ $code = $e->getCode();
+ $this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text');
+ }
+ }
+
+ /*
+ * Display temporary OAuth credentials
+ */
+
+ function showRequestToken($token)
+ {
+ header('Content-Type: application/x-www-form-urlencoded');
+ print $token;
+ print '&oauth_callback_confirmed=true';
+ }
+
+ /* Make sure the callback parameter contains either a real URL
+ * or the string 'oob'.
+ *
+ * @todo Check for evil/banned URLs here
+ *
+ * @return boolean true or false
+ */
+
+ function verifyCallback($callback)
+ {
+ if ($callback == "oob") {
+ common_debug("OAuth request token requested for out of bounds client.");
+
+ // XXX: Should we throw an error if a client is registered as a
+ // web application but requests the pin based workflow? For now I'm
+ // allowing the workflow to proceed and issuing a pin. --Zach
+
+ return true;
+ } else {
+ return Validate::uri(
+ $callback,
+ array('allowed_schemes' => array('http', 'https'))
+ );
}
}
diff --git a/actions/deletegroup.php b/actions/deletegroup.php
new file mode 100644
index 000000000..62fff00c4
--- /dev/null
+++ b/actions/deletegroup.php
@@ -0,0 +1,235 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Delete a group
+ *
+ * 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 Group
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @author Brion Vibber <brion@status.net>
+ * @copyright 2008-2010 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);
+}
+
+/**
+ * Delete a group
+ *
+ * This is the action for deleting a group.
+ *
+ * @category Group
+ * @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/
+ * @fixme merge more of this code with related variants
+ */
+class DeletegroupAction extends RedirectingAction
+{
+ var $group = null;
+
+ /**
+ * Prepare to run
+ *
+ * @fixme merge common setup code with other group actions
+ * @fixme allow group admins to delete their own groups
+ */
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ if (!common_logged_in()) {
+ // TRANS: Client error when trying to delete group while not logged in.
+ $this->clientError(_('You must be logged in to delete a group.'));
+ return false;
+ }
+
+ $nickname_arg = $this->trimmed('nickname');
+ $id = intval($this->arg('id'));
+ if ($id) {
+ $this->group = User_group::staticGet('id', $id);
+ } else if ($nickname_arg) {
+ $nickname = common_canonical_nickname($nickname_arg);
+
+ // Permanent redirect on non-canonical nickname
+
+ if ($nickname_arg != $nickname) {
+ $args = array('nickname' => $nickname);
+ common_redirect(common_local_url('leavegroup', $args), 301);
+ return false;
+ }
+
+ $local = Local_group::staticGet('nickname', $nickname);
+
+ if (!$local) {
+ // TRANS: Client error when trying to delete a non-local group.
+ $this->clientError(_('No such group.'), 404);
+ return false;
+ }
+
+ $this->group = User_group::staticGet('id', $local->group_id);
+ } else {
+ // TRANS: Client error when trying to delete a group without providing a nickname or ID for the group.
+ $this->clientError(_('No nickname or ID.'), 404);
+ return false;
+ }
+
+ if (!$this->group) {
+ // TRANS: Client error when trying to delete a non-existing group.
+ $this->clientError(_('No such group.'), 404);
+ return false;
+ }
+
+ $cur = common_current_user();
+ if (!$cur->hasRight(Right::DELETEGROUP)) {
+ // TRANS: Client error when trying to delete a group without having the rights to delete it.
+ $this->clientError(_('You are not allowed to delete this group.'), 403);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle the request
+ *
+ * On POST, delete the group.
+ *
+ * @param array $args unused
+ *
+ * @return void
+ */
+ function handle($args)
+ {
+ parent::handle($args);
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ if ($this->arg('no')) {
+ $this->returnToPrevious();
+ return;
+ } elseif ($this->arg('yes')) {
+ $this->handlePost();
+ return;
+ }
+ }
+ $this->showPage();
+ }
+
+ function handlePost()
+ {
+ $cur = common_current_user();
+
+ try {
+ if (Event::handle('StartDeleteGroup', array($this->group))) {
+ $this->group->delete();
+ Event::handle('EndDeleteGroup', array($this->group));
+ }
+ } catch (Exception $e) {
+ // TRANS: Server error displayed if a group could not be deleted.
+ // TRANS: %s is the name of the group that could not be deleted.
+ $this->serverError(sprintf(_('Could not delete group %s.'),
+ $this->group->nickname));
+ }
+
+ if ($this->boolean('ajax')) {
+ $this->startHTML('text/xml;charset=utf-8');
+ $this->elementStart('head');
+ // TRANS: Message given after deleting a group.
+ // TRANS: %s is the deleted group's name.
+ $this->element('title', null, sprintf(_('Deleted group %s'),
+ $this->group->nickname));
+ $this->elementEnd('head');
+ $this->elementStart('body');
+ // @fixme add a sensible AJAX response form!
+ $this->elementEnd('body');
+ $this->elementEnd('html');
+ } else {
+ // @fixme if we could direct to the page on which this group
+ // would have shown... that would be awesome
+ common_redirect(common_local_url('groups'),
+ 303);
+ }
+ }
+
+ function title() {
+ // TRANS: Title.
+ return _('Delete group');
+ }
+
+ function showContent() {
+ $this->areYouSureForm();
+ }
+
+ /**
+ * Confirm with user.
+ * Ripped from DeleteuserAction
+ *
+ * Shows a confirmation form.
+ *
+ * @fixme refactor common code for things like this
+ * @return void
+ */
+ function areYouSureForm()
+ {
+ $id = $this->group->id;
+ $this->elementStart('form', array('id' => 'deletegroup-' . $id,
+ 'method' => 'post',
+ 'class' => 'form_settings form_entity_block',
+ 'action' => common_local_url('deletegroup', array('id' => $this->group->id))));
+ $this->elementStart('fieldset');
+ $this->hidden('token', common_session_token());
+ // TRANS: Form legend for deleting a group.
+ $this->element('legend', _('Delete group'));
+ if (Event::handle('StartDeleteGroupForm', array($this, $this->group))) {
+ // TRANS: Warning in form for deleleting a group.
+ $this->element('p', null,
+ _('Are you sure you want to delete this group? '.
+ 'This will clear all data about the group from the '.
+ 'database, without a backup. ' .
+ 'Public posts to this group will still appear in ' .
+ 'individual timelines.'));
+ foreach ($this->args as $k => $v) {
+ if (substr($k, 0, 9) == 'returnto-') {
+ $this->hidden($k, $v);
+ }
+ }
+ Event::handle('EndDeleteGroupForm', array($this, $this->group));
+ }
+ $this->submit('form_action-no',
+ // TRANS: Button label on the delete group form.
+ _m('BUTTON','No'),
+ 'submit form_action-primary',
+ 'no',
+ // TRANS: Submit button title for 'No' when deleting a group.
+ _('Do not delete this group'));
+ $this->submit('form_action-yes',
+ // TRANS: Button label on the delete group form.
+ _m('BUTTON','Yes'),
+ 'submit form_action-secondary',
+ 'yes',
+ // TRANS: Submit button title for 'Yes' when deleting a group.
+ _('Delete this group'));
+ $this->elementEnd('fieldset');
+ $this->elementEnd('form');
+ }
+}
diff --git a/actions/deletenotice.php b/actions/deletenotice.php
index 68c43040b..2879faa5d 100644
--- a/actions/deletenotice.php
+++ b/actions/deletenotice.php
@@ -45,6 +45,12 @@ class DeletenoticeAction extends Action
parent::prepare($args);
$this->user = common_current_user();
+
+ if (!$this->user) {
+ common_user_error(_('Not logged in.'));
+ exit;
+ }
+
$notice_id = $this->trimmed('notice');
$this->notice = Notice::staticGet($notice_id);
@@ -63,10 +69,7 @@ class DeletenoticeAction extends Action
{
parent::handle($args);
- if (!common_logged_in()) {
- common_user_error(_('Not logged in.'));
- exit;
- } else if ($this->notice->profile_id != $this->user_profile->id &&
+ if ($this->notice->profile_id != $this->user_profile->id &&
!$this->user->hasRight(Right::DELETEOTHERSNOTICE)) {
common_user_error(_('Can\'t delete this notice.'));
exit;
diff --git a/actions/designadminpanel.php b/actions/designadminpanel.php
index 4285f7d73..587333e06 100644
--- a/actions/designadminpanel.php
+++ b/actions/designadminpanel.php
@@ -140,7 +140,7 @@ class DesignadminpanelAction extends AdminPanelAction
$themeChanged = ($this->trimmed('theme') != $oldtheme);
}
- static $settings = array('theme', 'logo');
+ static $settings = array('theme', 'logo', 'ssllogo');
$values = array();
@@ -230,6 +230,7 @@ class DesignadminpanelAction extends AdminPanelAction
function restoreDefaults()
{
$this->deleteSetting('site', 'logo');
+ $this->deleteSetting('site', 'ssllogo');
$this->deleteSetting('site', 'theme');
$settings = array(
@@ -293,7 +294,7 @@ class DesignadminpanelAction extends AdminPanelAction
/**
* Save the custom theme if the user uploaded one.
- *
+ *
* @return mixed custom theme name, if succesful, or null if no theme upload.
* @throws ClientException for invalid theme archives
* @throws ServerException if trouble saving the theme files
@@ -331,6 +332,11 @@ class DesignadminpanelAction extends AdminPanelAction
$this->clientError(_('Invalid logo URL.'));
}
+ if (!empty($values['ssllogo']) &&
+ !Validate::uri($values['ssllogo'], array('allowed_schemes' => array('https')))) {
+ $this->clientError(_('Invalid SSL logo URL.'));
+ }
+
if (!in_array($values['theme'], Theme::listAvailable())) {
$this->clientError(sprintf(_("Theme not available: %s."), $values['theme']));
}
@@ -444,6 +450,10 @@ class DesignAdminPanelForm extends AdminForm
$this->input('logo', _('Site logo'), 'Logo for the site (full URL)');
$this->unli();
+ $this->li();
+ $this->input('ssllogo', _('SSL logo'), 'Logo to show on SSL pages');
+ $this->unli();
+
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
diff --git a/actions/pathsadminpanel.php b/actions/pathsadminpanel.php
index 0c83aa29e..e073b0a2a 100644
--- a/actions/pathsadminpanel.php
+++ b/actions/pathsadminpanel.php
@@ -92,16 +92,17 @@ class PathsadminpanelAction extends AdminPanelAction
function saveSettings()
{
static $settings = array(
- 'site' => array('path', 'locale_path', 'ssl', 'sslserver'),
- 'theme' => array('server', 'dir', 'path'),
- 'avatar' => array('server', 'dir', 'path'),
- 'background' => array('server', 'dir', 'path')
- );
+ 'site' => array('path', 'locale_path', 'ssl', 'sslserver'),
+ 'theme' => array('server', 'dir', 'path', 'sslserver', 'sslpath'),
+ 'avatar' => array('server', 'dir', 'path'),
+ 'background' => array('server', 'dir', 'path', 'sslserver', 'sslpath'),
+ 'attachments' => array('server', 'dir', 'path', 'sslserver', 'sslpath')
+ );
- // XXX: If we're only going to have one boolean on thi page we
- // can remove some of the boolean processing code --Z
+ // XXX: If we're only going to have one boolean on thi page we
+ // can remove some of the boolean processing code --Z
- static $booleans = array('site' => array('fancy'));
+ static $booleans = array('site' => array('fancy'));
$values = array();
@@ -131,13 +132,13 @@ class PathsadminpanelAction extends AdminPanelAction
}
}
- foreach ($booleans as $section => $parts) {
- foreach ($parts as $setting) {
+ foreach ($booleans as $section => $parts) {
+ foreach ($parts as $setting) {
Config::save($section, $setting, $values[$section][$setting]);
}
- }
+ }
- $config->query('COMMIT');
+ $config->query('COMMIT');
return;
}
@@ -230,11 +231,11 @@ class PathsAdminPanelForm extends AdminForm
function formData()
{
- $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
+ $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
$this->out->element('legend', null, _('Site'), 'site');
$this->out->elementStart('ul', 'form_data');
- $this->li();
+ $this->li();
$this->input('server', _('Server'), _('Site\'s server hostname.'));
$this->unli();
@@ -243,14 +244,14 @@ class PathsAdminPanelForm extends AdminForm
$this->unli();
$this->li();
- $this->input('locale_path', _('Path to locales'), _('Directory path to locales'), 'site');
+ $this->input('locale_path', _('Locale Directory'), _('Directory path to locales'), 'site');
$this->unli();
- $this->li();
+ $this->li();
$this->out->checkbox('fancy', _('Fancy URLs'),
(bool) $this->value('fancy'),
_('Use fancy (more readable and memorable) URLs?'));
- $this->unli();
+ $this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
@@ -261,15 +262,23 @@ class PathsAdminPanelForm extends AdminForm
$this->out->elementStart('ul', 'form_data');
$this->li();
- $this->input('server', _('Theme server'), 'Server for themes', 'theme');
+ $this->input('server', _('Server'), _('Server for themes'), 'theme');
+ $this->unli();
+
+ $this->li();
+ $this->input('path', _('Path'), _('Web path to themes'), 'theme');
+ $this->unli();
+
+ $this->li();
+ $this->input('sslserver', _('SSL server'), _('SSL server for themes (default: SSL server)'), 'theme');
$this->unli();
$this->li();
- $this->input('path', _('Theme path'), 'Web path to themes', 'theme');
+ $this->input('sslpath', _('SSL path'), _('SSL path to themes (default: /theme/)'), 'theme');
$this->unli();
$this->li();
- $this->input('dir', _('Theme directory'), 'Directory where themes are located', 'theme');
+ $this->input('dir', _('Directory'), _('Directory where themes are located'), 'theme');
$this->unli();
$this->out->elementEnd('ul');
@@ -297,20 +306,57 @@ class PathsAdminPanelForm extends AdminForm
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' =>
- 'settings_design_background-paths'));
+ 'settings_design_background-paths'));
$this->out->element('legend', null, _('Backgrounds'));
$this->out->elementStart('ul', 'form_data');
$this->li();
- $this->input('server', _('Background server'), 'Server for backgrounds', 'background');
+ $this->input('server', _('Server'), 'Server for backgrounds', 'background');
+ $this->unli();
+
+ $this->li();
+ $this->input('path', _('Path'), 'Web path to backgrounds', 'background');
$this->unli();
$this->li();
- $this->input('path', _('Background path'), 'Web path to backgrounds', 'background');
+ $this->input('sslserver', _('SSL server'), 'Server for backgrounds on SSL pages', 'background');
$this->unli();
$this->li();
- $this->input('dir', _('Background directory'), 'Directory where backgrounds are located', 'background');
+ $this->input('sslpath', _('SSL path'), 'Web path to backgrounds on SSL pages', 'background');
+ $this->unli();
+
+ $this->li();
+ $this->input('dir', _('Directory'), 'Directory where backgrounds are located', 'background');
+ $this->unli();
+
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('fieldset');
+
+ $this->out->elementStart('fieldset', array('id' =>
+ 'settings_design_attachments-paths'));
+
+ $this->out->element('legend', null, _('Attachments'));
+ $this->out->elementStart('ul', 'form_data');
+
+ $this->li();
+ $this->input('server', _('Server'), 'Server for attachments', 'attachments');
+ $this->unli();
+
+ $this->li();
+ $this->input('path', _('Path'), 'Web path to attachments', 'attachments');
+ $this->unli();
+
+ $this->li();
+ $this->input('sslserver', _('SSL server'), 'Server for attachments on SSL pages', 'attachments');
+ $this->unli();
+
+ $this->li();
+ $this->input('sslpath', _('SSL path'), 'Web path to attachments on SSL pages', 'attachments');
+ $this->unli();
+
+ $this->li();
+ $this->input('dir', _('Directory'), 'Directory where attachments are located', 'attachments');
$this->unli();
$this->out->elementEnd('ul');
@@ -320,12 +366,11 @@ class PathsAdminPanelForm extends AdminForm
$this->out->element('legend', null, _('SSL'));
$this->out->elementStart('ul', 'form_data');
$this->li();
+
$ssl = array('never' => _('Never'),
'sometimes' => _('Sometimes'),
'always' => _('Always'));
- common_debug("site ssl = " . $this->value('site', 'ssl'));
-
$this->out->dropdown('site-ssl', _('Use SSL'),
$ssl, _('When to use SSL'),
false, $this->value('ssl', 'site'));
@@ -349,7 +394,7 @@ class PathsAdminPanelForm extends AdminForm
function formActions()
{
$this->out->submit('save', _('Save'), 'submit',
- 'save', _('Save paths'));
+ 'save', _('Save paths'));
}
/**
@@ -370,5 +415,4 @@ class PathsAdminPanelForm extends AdminForm
{
$this->out->input("$section-$setting", $title, $this->value($setting, $section), $instructions);
}
-
}
diff --git a/actions/showfavorites.php b/actions/showfavorites.php
index d8042e91c..77b73711d 100644
--- a/actions/showfavorites.php
+++ b/actions/showfavorites.php
@@ -227,7 +227,7 @@ class ShowfavoritesAction extends OwnerDesignAction
function showContent()
{
- $nl = new NoticeList($this->notice, $this);
+ $nl = new FavoritesNoticeList($this->notice, $this);
$cnt = $nl->show();
if (0 == $cnt) {
@@ -244,3 +244,15 @@ class ShowfavoritesAction extends OwnerDesignAction
}
}
+class FavoritesNoticeList extends NoticeList
+{
+ function newListItem($notice)
+ {
+ return new FavoritesNoticeListItem($notice, $this->out);
+ }
+}
+
+// All handled by superclass
+class FavoritesNoticeListItem extends DoFollowListItem
+{
+}
diff --git a/actions/showgroup.php b/actions/showgroup.php
index 17c37e4d7..9a12bafaf 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -316,6 +316,12 @@ class ShowgroupAction extends GroupDesignAction
Event::handle('EndGroupSubscribe', array($this, $this->group));
}
$this->elementEnd('li');
+ if ($cur->hasRight(Right::DELETEGROUP)) {
+ $this->elementStart('li', 'entity_delete');
+ $df = new DeleteGroupForm($this, $this->group);
+ $df->show();
+ $this->elementEnd('li');
+ }
$this->elementEnd('ul');
$this->elementEnd('div');
}
diff --git a/actions/shownotice.php b/actions/shownotice.php
index c5180568b..5fc863486 100644
--- a/actions/shownotice.php
+++ b/actions/shownotice.php
@@ -311,7 +311,7 @@ class ShownoticeAction extends OwnerDesignAction
}
}
-class SingleNoticeItem extends NoticeListItem
+class SingleNoticeItem extends DoFollowListItem
{
/**
* recipe function for displaying a single notice.
diff --git a/actions/showstream.php b/actions/showstream.php
index e9f117afc..be61a7ce0 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -275,7 +275,7 @@ class ProfileNoticeList extends NoticeList
}
}
-class ProfileNoticeListItem extends NoticeListItem
+class ProfileNoticeListItem extends DoFollowListItem
{
function showAuthor()
{