diff options
author | Brenda Wallace <shiny@cpan.org> | 2010-01-18 11:48:11 +1300 |
---|---|---|
committer | Brenda Wallace <shiny@cpan.org> | 2010-01-18 11:48:11 +1300 |
commit | 02a6006bafd663443b512c5c283b64c7dacfbbb1 (patch) | |
tree | 6d2ac830a8be8330df124da3056f891a2065eedb /actions | |
parent | c1f1d712bd4d775b24ce99b44f469ec7dcd2342e (diff) | |
parent | 4978810c81c8d7ba75daa795d66965d6b43331f3 (diff) |
Merge commit 'mainline/0.9.x' into 0.9.x
Diffstat (limited to 'actions')
71 files changed, 2112 insertions, 163 deletions
diff --git a/actions/all.php b/actions/all.php index efa4521e2..3eb185214 100644 --- a/actions/all.php +++ b/actions/all.php @@ -81,7 +81,7 @@ class AllAction extends ProfileAction function title() { if ($this->page > 1) { - return sprintf(_("%1$s and friends, page %2$d"), $this->user->nickname, $this->page); + return sprintf(_('%1$s and friends, page %2$d'), $this->user->nickname, $this->page); } else { return sprintf(_("%s and friends"), $this->user->nickname); } diff --git a/actions/apiaccountverifycredentials.php b/actions/apiaccountverifycredentials.php index 08b201dbf..1095d5162 100644 --- a/actions/apiaccountverifycredentials.php +++ b/actions/apiaccountverifycredentials.php @@ -82,4 +82,18 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction } + /** + * Is this action read only? + * + * @param array $args other arguments + * + * @return boolean true + * + **/ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apidirectmessage.php b/actions/apidirectmessage.php index 5b3f412ad..5fbc46518 100644 --- a/actions/apidirectmessage.php +++ b/actions/apidirectmessage.php @@ -153,7 +153,7 @@ class ApiDirectMessageAction extends ApiAuthAction $this->showJsonDirectMessages(); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apifavoritecreate.php b/actions/apifavoritecreate.php index 436739770..3618f9401 100644 --- a/actions/apifavoritecreate.php +++ b/actions/apifavoritecreate.php @@ -96,7 +96,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction if (!in_array($this->format, array('xml', 'json'))) { $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); @@ -116,7 +116,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction if ($this->user->hasFave($this->notice)) { $this->clientError( - _('This status is already a favorite!'), + _('This status is already a favorite.'), 403, $this->format ); diff --git a/actions/apifavoritedestroy.php b/actions/apifavoritedestroy.php index f131d1c7f..c4daf480e 100644 --- a/actions/apifavoritedestroy.php +++ b/actions/apifavoritedestroy.php @@ -97,7 +97,7 @@ class ApiFavoriteDestroyAction extends ApiAuthAction if (!in_array($this->format, array('xml', 'json'))) { $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); @@ -119,7 +119,7 @@ class ApiFavoriteDestroyAction extends ApiAuthAction if (!$fave->find(true)) { $this->clientError( - _('That status is not a favorite!'), + _('That status is not a favorite.'), 403, $this->favorite ); diff --git a/actions/apifriendshipscreate.php b/actions/apifriendshipscreate.php index a824e734b..1de2cc32e 100644 --- a/actions/apifriendshipscreate.php +++ b/actions/apifriendshipscreate.php @@ -97,7 +97,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction if (!in_array($this->format, array('xml', 'json'))) { $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apifriendshipsdestroy.php b/actions/apifriendshipsdestroy.php index 3d9b7e001..91c6fd032 100644 --- a/actions/apifriendshipsdestroy.php +++ b/actions/apifriendshipsdestroy.php @@ -97,7 +97,7 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction if (!in_array($this->format, array('xml', 'json'))) { $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); @@ -117,7 +117,7 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction if ($this->user->id == $this->other->id) { $this->clientError( - _("You cannot unfollow yourself!"), + _("You cannot unfollow yourself."), 403, $this->format ); diff --git a/actions/apifriendshipsshow.php b/actions/apifriendshipsshow.php index 8fc436738..73ecc9249 100644 --- a/actions/apifriendshipsshow.php +++ b/actions/apifriendshipsshow.php @@ -126,7 +126,7 @@ class ApiFriendshipsShowAction extends ApiBareAuthAction parent::handle($args); if (!in_array($this->format, array('xml', 'json'))) { - $this->clientError(_('API method not found!'), 404); + $this->clientError(_('API method not found.'), 404); return; } diff --git a/actions/apigroupcreate.php b/actions/apigroupcreate.php index 8827d1c5c..028d76a78 100644 --- a/actions/apigroupcreate.php +++ b/actions/apigroupcreate.php @@ -133,7 +133,7 @@ class ApiGroupCreateAction extends ApiAuthAction break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apigroupismember.php b/actions/apigroupismember.php index 08348e97b..69ead0b53 100644 --- a/actions/apigroupismember.php +++ b/actions/apigroupismember.php @@ -111,7 +111,7 @@ class ApiGroupIsMemberAction extends ApiBareAuthAction break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 400, $this->format ); diff --git a/actions/apigroupjoin.php b/actions/apigroupjoin.php index 4b718bce6..374cf83df 100644 --- a/actions/apigroupjoin.php +++ b/actions/apigroupjoin.php @@ -145,14 +145,14 @@ class ApiGroupJoinAction extends ApiAuthAction switch($this->format) { case 'xml': - $this->show_single_xml_group($this->group); + $this->showSingleXmlGroup($this->group); break; case 'json': $this->showSingleJsonGroup($this->group); break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apigroupleave.php b/actions/apigroupleave.php index 7321ff5d2..9848ece05 100644 --- a/actions/apigroupleave.php +++ b/actions/apigroupleave.php @@ -131,14 +131,14 @@ class ApiGroupLeaveAction extends ApiAuthAction switch($this->format) { case 'xml': - $this->show_single_xml_group($this->group); + $this->showSingleXmlGroup($this->group); break; case 'json': $this->showSingleJsonGroup($this->group); break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apigrouplist.php b/actions/apigrouplist.php index 4cf657579..66b67a030 100644 --- a/actions/apigrouplist.php +++ b/actions/apigrouplist.php @@ -129,7 +129,7 @@ class ApiGroupListAction extends ApiBareAuthAction break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apigrouplistall.php b/actions/apigrouplistall.php index c597839a8..1921c1f19 100644 --- a/actions/apigrouplistall.php +++ b/actions/apigrouplistall.php @@ -117,7 +117,7 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apigroupmembership.php b/actions/apigroupmembership.php index dd2843161..3c7c8e883 100644 --- a/actions/apigroupmembership.php +++ b/actions/apigroupmembership.php @@ -103,7 +103,7 @@ class ApiGroupMembershipAction extends ApiPrivateAuthAction break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apigroupshow.php b/actions/apigroupshow.php index aae4d249c..7aa49b1bf 100644 --- a/actions/apigroupshow.php +++ b/actions/apigroupshow.php @@ -102,7 +102,7 @@ class ApiGroupShowAction extends ApiPrivateAuthAction $this->showSingleJsonGroup($this->group); break; default: - $this->clientError(_('API method not found!'), 404, $this->format); + $this->clientError(_('API method not found.'), 404, $this->format); break; } diff --git a/actions/apihelptest.php b/actions/apihelptest.php index f2c459e6f..7b4017531 100644 --- a/actions/apihelptest.php +++ b/actions/apihelptest.php @@ -85,7 +85,7 @@ class ApiHelpTestAction extends ApiPrivateAuthAction $this->endDocument('json'); } else { $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php new file mode 100644 index 000000000..085ef6f0b --- /dev/null +++ b/actions/apioauthaccesstoken.php @@ -0,0 +1,94 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Exchange an authorized OAuth request token for an access token + * + * 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 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')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apioauth.php'; + +/** + * Exchange an authorized OAuth request token for an access token + * + * @category API + * @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 ApiOauthAccessTokenAction extends ApiOauthAction +{ + + /** + * Class handler. + * + * @param array $args array of arguments + * + * @return void + */ + function handle($args) + { + parent::handle($args); + + $datastore = new ApiStatusNetOAuthDataStore(); + $server = new OAuthServer($datastore); + $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); + + $server->add_signature_method($hmac_method); + + $atok = null; + + try { + $req = OAuthRequest::from_request(); + $atok = $server->fetch_access_token($req); + + } catch (OAuthException $e) { + common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); + common_debug(var_export($req, true)); + $this->outputError($e->getMessage()); + return; + } + + if (empty($atok)) { + common_debug('couldn\'t get access token.'); + print "Token exchange failed. Has the request token been authorized?\n"; + } else { + print $atok; + } + } + + function outputError($msg) + { + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/html; charset=utf-8'); + print $msg . "\n"; + } +} + diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php new file mode 100644 index 000000000..19128bdce --- /dev/null +++ b/actions/apioauthauthorize.php @@ -0,0 +1,376 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Authorize an OAuth request token + * + * 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 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')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apioauth.php'; + +/** + * Authorize an OAuth request token + * + * @category API + * @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 ApiOauthAuthorizeAction extends ApiOauthAction +{ + var $oauth_token; + var $callback; + var $app; + var $nickname; + var $password; + var $store; + + /** + * Is this a read-only action? + * + * @return boolean false + */ + + function isReadOnly($args) + { + return false; + } + + function prepare($args) + { + parent::prepare($args); + + common_debug("apioauthauthorize"); + + $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); + + return true; + } + + /** + * Handle input, produce output + * + * Switches on request method; either shows the form or handles its input. + * + * @param array $args $_REQUEST data + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + + $this->handlePost(); + + } else { + + // XXX: make better error messages + + if (empty($this->oauth_token)) { + + common_debug("No request token found."); + + $this->clientError(_('Bad request.')); + return; + } + + if (empty($this->app)) { + common_debug('No app for that token.'); + $this->clientError(_('Bad request.')); + return; + } + + $name = $this->app->name; + common_debug("Requesting auth for app: " . $name); + + $this->showForm(); + } + } + + function handlePost() + { + common_debug("handlePost()"); + + // check session token for CSRF protection. + + $token = $this->trimmed('token'); + + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + // check creds + + $user = null; + + if (!common_logged_in()) { + $user = common_check_user($this->nickname, $this->password); + if (empty($user)) { + $this->showForm(_("Invalid nickname / password!")); + return; + } + } else { + $user = common_current_user(); + } + + if ($this->arg('allow')) { + + // mark the req token as authorized + + $this->store->authorize_token($this->oauth_token); + + // Check to see if there was a previous token associated + // with this user/app and kill it. If the user is doing this she + // probably doesn't want any old tokens anyway. + + $appUser = Oauth_application_user::getByKeys($user, $this->app); + + if (!empty($appUser)) { + $result = $appUser->delete(); + + if (!$result) { + common_log_db_error($appUser, 'DELETE', __FILE__); + throw new ServerException(_('DB error deleting OAuth app user.')); + return; + } + } + + // associated the authorized req token with the user and the app + + $appUser = new Oauth_application_user(); + + $appUser->profile_id = $user->id; + $appUser->application_id = $this->app->id; + + // Note: do not copy the access type from the application. + // The access type should always be 0 when the OAuth app + // user record has a request token associated with it. + // Access type gets assigned once an access token has been + // granted. The OAuth app user record then gets updated + // with the new access token and access type. + + $appUser->token = $this->oauth_token; + $appUser->created = common_sql_now(); + + $result = $appUser->insert(); + + if (!$result) { + common_log_db_error($appUser, 'INSERT', __FILE__); + throw new ServerException(_('DB error inserting OAuth app user.')); + return; + } + + // if we have a callback redirect and provide the token + + // A callback specified in the app setup overrides whatever + // is passed in with the request. + + common_debug("Req token is authorized - doing callback"); + + if (!empty($this->app->callback_url)) { + $this->callback = $this->app->callback_url; + } + + if (!empty($this->callback)) { + + // XXX: Need better way to build this redirect url. + + $target_url = $this->getCallback($this->callback, + array('oauth_token' => $this->oauth_token)); + + common_debug("Doing callback to $target_url"); + + common_redirect($target_url, 303); + } else { + common_debug("callback was empty!"); + } + + // 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')) { + + $this->elementStart('p'); + + $this->raw(sprintf(_("The request token %s has been denied."), + $this->oauth_token)); + + $this->elementEnd('p'); + } else { + $this->clientError(_('Unexpected form submission.')); + return; + } + } + + function showForm($error=null) + { + $this->error = $error; + $this->showPage(); + } + + function showScripts() + { + parent::showScripts(); + if (!common_logged_in()) { + $this->autofocus('nickname'); + } + } + + /** + * Title of the page + * + * @return string title of the page + */ + + function title() + { + return _('An application would like to connect to your account'); + } + + /** + * Shows the authorization form. + * + * @return void + */ + + function showContent() + { + $this->elementStart('form', array('method' => 'post', + 'id' => 'form_apioauthauthorize', + 'class' => 'form_settings', + 'action' => common_local_url('apioauthauthorize'))); + $this->elementStart('fieldset'); + $this->element('legend', array('id' => 'apioauthauthorize_allowdeny'), + _('Allow or deny access')); + + $this->hidden('token', common_session_token()); + $this->hidden('oauth_token', $this->oauth_token); + $this->hidden('oauth_callback', $this->callback); + + $this->elementStart('ul', 'form_data'); + $this->elementStart('li'); + $this->elementStart('p'); + if (!empty($this->app->icon)) { + $this->element('img', array('src' => $this->app->icon)); + } + + $access = ($this->app->access_type & Oauth_application::$writeAccess) ? + 'access and update' : 'access'; + + $msg = _("The application <strong>%1$s</strong> by <strong>%2$s</strong> would like " . + "the ability to <strong>%3$s</strong> your account data."); + + $this->raw(sprintf($msg, + $this->app->name, + $this->app->organization, + $access)); + $this->elementEnd('p'); + $this->elementEnd('li'); + $this->elementEnd('ul'); + + if (!common_logged_in()) { + + $this->elementStart('fieldset'); + $this->element('legend', null, _('Account')); + $this->elementStart('ul', 'form_data'); + $this->elementStart('li'); + $this->input('nickname', _('Nickname')); + $this->elementEnd('li'); + $this->elementStart('li'); + $this->password('password', _('Password')); + $this->elementEnd('li'); + $this->elementEnd('ul'); + + $this->elementEnd('fieldset'); + + } + + $this->element('input', array('id' => 'deny_submit', + 'class' => 'submit submit form_action-primary', + 'name' => 'deny', + 'type' => 'submit', + 'value' => _('Deny'))); + + $this->element('input', array('id' => 'allow_submit', + 'class' => 'submit submit form_action-secondary', + 'name' => 'allow', + 'type' => 'submit', + 'value' => _('Allow'))); + + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + } + + /** + * Instructions for using the form + * + * For "remembered" logins, we make the user re-login when they + * try to change settings. Different instructions for this case. + * + * @return void + */ + + function getInstructions() + { + return _('Allow or deny access to your account information.'); + } + + /** + * A local menu + * + * Shows different login/register actions. + * + * @return void + */ + + function showLocalNav() + { + } + +} diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php new file mode 100644 index 000000000..467640b9a --- /dev/null +++ b/actions/apioauthrequesttoken.php @@ -0,0 +1,99 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Get an OAuth request token + * + * 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 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')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apioauth.php'; + +/** + * Get an OAuth request token + * + * @category API + * @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 ApiOauthRequestTokenAction extends ApiOauthAction +{ + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $this->callback = $this->arg('oauth_callback'); + + if (!empty($this->callback)) { + common_debug("callback: $this->callback"); + } + + return true; + } + + /** + * Class handler. + * + * @param array $args array of arguments + * + * @return void + */ + function handle($args) + { + parent::handle($args); + + $datastore = new ApiStatusNetOAuthDataStore(); + $server = new OAuthServer($datastore); + $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); + + $server->add_signature_method($hmac_method); + + try { + $req = OAuthRequest::from_request(); + $token = $server->fetch_request_token($req); + print $token; + } catch (OAuthException $e) { + common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/html; charset=utf-8'); + print $e->getMessage() . "\n"; + } + } + +} diff --git a/actions/apistatusesdestroy.php b/actions/apistatusesdestroy.php index 8dc8793b5..f7d52f020 100644 --- a/actions/apistatusesdestroy.php +++ b/actions/apistatusesdestroy.php @@ -99,7 +99,7 @@ class ApiStatusesDestroyAction extends ApiAuthAction parent::handle($args); if (!in_array($this->format, array('xml', 'json'))) { - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); return; } diff --git a/actions/apistatusesretweets.php b/actions/apistatusesretweets.php index 2efd59b37..f7a3dd60a 100644 --- a/actions/apistatusesretweets.php +++ b/actions/apistatusesretweets.php @@ -109,7 +109,7 @@ class ApiStatusesRetweetsAction extends ApiAuthAction $this->showJsonTimeline($strm); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apistatusesshow.php b/actions/apistatusesshow.php index e26c009c4..0315d2953 100644 --- a/actions/apistatusesshow.php +++ b/actions/apistatusesshow.php @@ -105,7 +105,7 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction parent::handle($args); if (!in_array($this->format, array('xml', 'json'))) { - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); return; } diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php index f594bbf39..f8bf7cf87 100644 --- a/actions/apistatusesupdate.php +++ b/actions/apistatusesupdate.php @@ -85,6 +85,11 @@ class ApiStatusesUpdateAction extends ApiAuthAction $this->lat = $this->trimmed('lat'); $this->lon = $this->trimmed('long'); + // try to set the source attr from OAuth app + if (empty($this->source)) { + $this->source = $this->oauth_source; + } + if (empty($this->source) || in_array($this->source, self::$reserved_sources)) { $this->source = 'api'; } diff --git a/actions/apistatusnetconfig.php b/actions/apistatusnetconfig.php index ed1d151bf..ab96f2e5f 100644 --- a/actions/apistatusnetconfig.php +++ b/actions/apistatusnetconfig.php @@ -130,7 +130,7 @@ class ApiStatusnetConfigAction extends ApiAction break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apistatusnetversion.php b/actions/apistatusnetversion.php index bbf891a89..5109cd806 100644 --- a/actions/apistatusnetversion.php +++ b/actions/apistatusnetversion.php @@ -90,7 +90,7 @@ class ApiStatusnetVersionAction extends ApiPrivateAuthAction break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apisubscriptions.php b/actions/apisubscriptions.php index 2c691bb84..0ba324057 100644 --- a/actions/apisubscriptions.php +++ b/actions/apisubscriptions.php @@ -108,7 +108,7 @@ class ApiSubscriptionsAction extends ApiBareAuthAction parent::handle($args); if (!in_array($this->format, array('xml', 'json'))) { - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); return; } diff --git a/actions/apitimelinefavorites.php b/actions/apitimelinefavorites.php index 700f6e0fd..1027d97d4 100644 --- a/actions/apitimelinefavorites.php +++ b/actions/apitimelinefavorites.php @@ -143,7 +143,7 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction $this->showJsonTimeline($this->notices); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelinefriends.php b/actions/apitimelinefriends.php index 9ec7447e6..4e3827bae 100644 --- a/actions/apitimelinefriends.php +++ b/actions/apitimelinefriends.php @@ -72,7 +72,6 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction function prepare($args) { parent::prepare($args); - common_debug("api friends_timeline"); $this->user = $this->getTargetUser($this->arg('id')); if (empty($this->user)) { @@ -153,7 +152,7 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction $this->showJsonTimeline($this->notices); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelinegroup.php b/actions/apitimelinegroup.php index 22c577f39..af414c680 100644 --- a/actions/apitimelinegroup.php +++ b/actions/apitimelinegroup.php @@ -147,7 +147,7 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction break; default: $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apitimelinehome.php b/actions/apitimelinehome.php index 5f5ea37b1..828eae6cf 100644 --- a/actions/apitimelinehome.php +++ b/actions/apitimelinehome.php @@ -153,7 +153,7 @@ class ApiTimelineHomeAction extends ApiBareAuthAction $this->showJsonTimeline($this->notices); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelinementions.php b/actions/apitimelinementions.php index 19f40aebc..9dc2162cc 100644 --- a/actions/apitimelinementions.php +++ b/actions/apitimelinementions.php @@ -148,7 +148,7 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction $this->showJsonTimeline($this->notices); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelinepublic.php b/actions/apitimelinepublic.php index 633f3c36e..3f4a46c0f 100644 --- a/actions/apitimelinepublic.php +++ b/actions/apitimelinepublic.php @@ -128,7 +128,7 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction $this->showJsonTimeline($this->notices); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelineretweetedbyme.php b/actions/apitimelineretweetedbyme.php index 1e65678ad..88652c3fd 100644 --- a/actions/apitimelineretweetedbyme.php +++ b/actions/apitimelineretweetedbyme.php @@ -119,7 +119,7 @@ class ApiTimelineRetweetedByMeAction extends ApiAuthAction break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelineretweetedtome.php b/actions/apitimelineretweetedtome.php index 681b0b9e9..113ab96d2 100644 --- a/actions/apitimelineretweetedtome.php +++ b/actions/apitimelineretweetedtome.php @@ -118,7 +118,7 @@ class ApiTimelineRetweetedToMeAction extends ApiAuthAction break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelineretweetsofme.php b/actions/apitimelineretweetsofme.php index 479bff431..6ca2c779c 100644 --- a/actions/apitimelineretweetsofme.php +++ b/actions/apitimelineretweetsofme.php @@ -119,7 +119,7 @@ class ApiTimelineRetweetsOfMeAction extends ApiAuthAction break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelinetag.php b/actions/apitimelinetag.php index 1a50520f4..1427d23b6 100644 --- a/actions/apitimelinetag.php +++ b/actions/apitimelinetag.php @@ -138,7 +138,7 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction $this->showJsonTimeline($this->notices); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } } diff --git a/actions/apitimelineuser.php b/actions/apitimelineuser.php index 14c62a52e..830b16941 100644 --- a/actions/apitimelineuser.php +++ b/actions/apitimelineuser.php @@ -162,7 +162,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction $this->showJsonTimeline($this->notices); break; default: - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); break; } diff --git a/actions/apiusershow.php b/actions/apiusershow.php index aa7aec5a4..a7fe0dcc1 100644 --- a/actions/apiusershow.php +++ b/actions/apiusershow.php @@ -98,7 +98,7 @@ class ApiUserShowAction extends ApiPrivateAuthAction } if (!in_array($this->format, array('xml', 'json'))) { - $this->clientError(_('API method not found!'), $code = 404); + $this->clientError(_('API method not found.'), $code = 404); return; } diff --git a/actions/blockedfromgroup.php b/actions/blockedfromgroup.php index 934f14ec4..0b4caf5bf 100644 --- a/actions/blockedfromgroup.php +++ b/actions/blockedfromgroup.php @@ -70,14 +70,14 @@ class BlockedfromgroupAction extends GroupDesignAction } if (!$nickname) { - $this->clientError(_('No nickname'), 404); + $this->clientError(_('No nickname.'), 404); return false; } $this->group = User_group::staticGet('nickname', $nickname); if (!$this->group) { - $this->clientError(_('No such group'), 404); + $this->clientError(_('No such group.'), 404); return false; } diff --git a/actions/confirmaddress.php b/actions/confirmaddress.php index 6fd74f3ff..cc8351d8d 100644 --- a/actions/confirmaddress.php +++ b/actions/confirmaddress.php @@ -141,7 +141,7 @@ class ConfirmaddressAction extends Action function title() { - return _('Confirm Address'); + return _('Confirm address'); } /** diff --git a/actions/deletenotice.php b/actions/deletenotice.php index ba8e86d0f..69cb1ebe8 100644 --- a/actions/deletenotice.php +++ b/actions/deletenotice.php @@ -155,7 +155,7 @@ class DeletenoticeAction extends Action if (!$token || $token != common_session_token()) { $this->showForm(_('There was a problem with your session token. ' . - ' Try again, please.')); + 'Try again, please.')); return; } diff --git a/actions/editapplication.php b/actions/editapplication.php new file mode 100644 index 000000000..3b120259a --- /dev/null +++ b/actions/editapplication.php @@ -0,0 +1,264 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Edit an OAuth Application + * + * 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 Applications + * @package StatusNet + * @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); +} + +/** + * Edit the details of an OAuth application + * + * This is the form for editing an application + * + * @category Application + * @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 EditApplicationAction extends OwnerDesignAction +{ + var $msg = null; + var $owner = null; + var $app = null; + + function title() + { + return _('Edit application'); + } + + /** + * Prepare to run + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to edit an application.')); + return false; + } + + $id = (int)$this->arg('id'); + + $this->app = Oauth_application::staticGet($id); + $this->owner = User::staticGet($this->app->owner); + $cur = common_current_user(); + + if ($cur->id != $this->owner->id) { + $this->clientError(_('You are not the owner of this application.'), 401); + } + + if (!$this->app) { + $this->clientError(_('No such application.')); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On GET, show the form. On POST, try to save the app. + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->handlePost($args); + } else { + $this->showForm(); + } + } + + function handlePost($args) + { + // 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->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); + return; + } + + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token.')); + return; + } + + $cur = common_current_user(); + + if ($this->arg('cancel')) { + common_redirect(common_local_url('showapplication', + array('id' => $this->app->id)), 303); + } elseif ($this->arg('save')) { + $this->trySave(); + } else { + $this->clientError(_('Unexpected form submission.')); + } + } + + function showForm($msg=null) + { + $this->msg = $msg; + $this->showPage(); + } + + function showContent() + { + $form = new ApplicationEditForm($this, $this->app); + $form->show(); + } + + function showPageNotice() + { + if (!empty($this->msg)) { + $this->element('p', 'error', $this->msg); + } else { + $this->element('p', 'instructions', + _('Use this form to edit your application.')); + } + } + + function trySave() + { + $name = $this->trimmed('name'); + $description = $this->trimmed('description'); + $source_url = $this->trimmed('source_url'); + $organization = $this->trimmed('organization'); + $homepage = $this->trimmed('homepage'); + $callback_url = $this->trimmed('callback_url'); + $type = $this->arg('app_type'); + $access_type = $this->arg('default_access_type'); + + if (empty($name)) { + $this->showForm(_('Name is required.')); + return; + } elseif (mb_strlen($name) > 255) { + $this->showForm(_('Name is too long (max 255 chars).')); + return; + } elseif (empty($description)) { + $this->showForm(_('Description is required.')); + return; + } elseif (Oauth_application::descriptionTooLong($description)) { + $this->showForm(sprintf( + _('Description is too long (max %d chars).'), + Oauth_application::maxDescription())); + return; + } elseif (mb_strlen($source_url) > 255) { + $this->showForm(_('Source URL is too long.')); + return; + } elseif ((mb_strlen($source_url) > 0) + && !Validate::uri($source_url, + array('allowed_schemes' => array('http', 'https')))) + { + $this->showForm(_('Source URL is not valid.')); + return; + } elseif (empty($organization)) { + $this->showForm(_('Organization is required.')); + return; + } elseif (mb_strlen($organization) > 255) { + $this->showForm(_('Organization is too long (max 255 chars).')); + return; + } elseif (empty($homepage)) { + $this->showForm(_('Organization homepage is required.')); + return; + } elseif ((mb_strlen($homepage) > 0) + && !Validate::uri($homepage, + array('allowed_schemes' => array('http', 'https')))) + { + $this->showForm(_('Homepage is not a valid URL.')); + return; + } elseif (mb_strlen($callback_url) > 255) { + $this->showForm(_('Callback is too long.')); + return; + } elseif (mb_strlen($callback_url) > 0 + && !Validate::uri($source_url, + array('allowed_schemes' => array('http', 'https')) + )) + { + $this->showForm(_('Callback URL is not valid.')); + return; + } + + $cur = common_current_user(); + + // Checked in prepare() above + + assert(!is_null($cur)); + assert(!is_null($this->app)); + + $orig = clone($this->app); + + $this->app->name = $name; + $this->app->description = $description; + $this->app->source_url = $source_url; + $this->app->organization = $organization; + $this->app->homepage = $homepage; + $this->app->callback_url = $callback_url; + $this->app->type = $type; + + common_debug("access_type = $access_type"); + + if ($access_type == 'r') { + $this->app->access_type = 1; + } else { + $this->app->access_type = 3; + } + + $result = $this->app->update($orig); + + if (!$result) { + common_log_db_error($this->app, 'UPDATE', __FILE__); + $this->serverError(_('Could not update application.')); + } + + $this->app->uploadLogo(); + + common_redirect(common_local_url('oauthappssettings'), 303); + } + +} + diff --git a/actions/editgroup.php b/actions/editgroup.php index cf1608035..ad0b6e185 100644 --- a/actions/editgroup.php +++ b/actions/editgroup.php @@ -81,7 +81,7 @@ class EditgroupAction extends GroupDesignAction } if (!$nickname) { - $this->clientError(_('No nickname'), 404); + $this->clientError(_('No nickname.'), 404); return false; } @@ -93,14 +93,14 @@ class EditgroupAction extends GroupDesignAction } if (!$this->group) { - $this->clientError(_('No such group'), 404); + $this->clientError(_('No such group.'), 404); return false; } $cur = common_current_user(); if (!$cur->isAdmin($this->group)) { - $this->clientError(_('You must be an admin to edit the group'), 403); + $this->clientError(_('You must be an admin to edit the group.'), 403); return false; } @@ -165,7 +165,7 @@ class EditgroupAction extends GroupDesignAction { $cur = common_current_user(); if (!$cur->isAdmin($this->group)) { - $this->clientError(_('You must be an admin to edit the group'), 403); + $this->clientError(_('You must be an admin to edit the group.'), 403); return; } diff --git a/actions/emailsettings.php b/actions/emailsettings.php index 761aaa8f3..bfef2970d 100644 --- a/actions/emailsettings.php +++ b/actions/emailsettings.php @@ -57,7 +57,7 @@ class EmailsettingsAction extends AccountSettingsAction function title() { - return _('Email Settings'); + return _('Email settings'); } /** @@ -118,7 +118,7 @@ class EmailsettingsAction extends AccountSettingsAction } else { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); - $this->input('email', _('Email Address'), + $this->input('email', _('Email address'), ($this->arg('email')) ? $this->arg('email') : null, _('Email address, like "UserName@example.org"')); $this->elementEnd('li'); @@ -328,7 +328,7 @@ class EmailsettingsAction extends AccountSettingsAction return; } if (!Validate::email($email, common_config('email', 'check_domain'))) { - $this->showForm(_('Not a valid email address')); + $this->showForm(_('Not a valid email address.')); return; } else if ($user->email == $email) { $this->showForm(_('That is already your email address.')); diff --git a/actions/groupbyid.php b/actions/groupbyid.php index f65bf511a..5af7109cb 100644 --- a/actions/groupbyid.php +++ b/actions/groupbyid.php @@ -71,7 +71,7 @@ class GroupbyidAction extends Action $id = $this->arg('id'); if (!$id) { - $this->clientError(_('No ID')); + $this->clientError(_('No ID.')); return false; } @@ -80,7 +80,7 @@ class GroupbyidAction extends Action $this->group = User_group::staticGet('id', $id); if (!$this->group) { - $this->clientError(_('No such group'), 404); + $this->clientError(_('No such group.'), 404); return false; } diff --git a/actions/groupdesignsettings.php b/actions/groupdesignsettings.php index 1c998efe1..e290ba514 100644 --- a/actions/groupdesignsettings.php +++ b/actions/groupdesignsettings.php @@ -81,7 +81,7 @@ class GroupDesignSettingsAction extends DesignSettingsAction } if (!$nickname) { - $this->clientError(_('No nickname'), 404); + $this->clientError(_('No nickname.'), 404); return false; } @@ -94,14 +94,14 @@ class GroupDesignSettingsAction extends DesignSettingsAction } if (!$this->group) { - $this->clientError(_('No such group'), 404); + $this->clientError(_('No such group.'), 404); return false; } $cur = common_current_user(); if (!$cur->isAdmin($this->group)) { - $this->clientError(_('You must be an admin to edit the group'), 403); + $this->clientError(_('You must be an admin to edit the group.'), 403); return false; } @@ -284,7 +284,7 @@ class GroupDesignSettingsAction extends DesignSettingsAction if (empty($id)) { common_log_db_error($id, 'INSERT', __FILE__); - $this->showForm(_('Unable to save your design settings!')); + $this->showForm(_('Unable to save your design settings.')); return; } @@ -294,7 +294,7 @@ class GroupDesignSettingsAction extends DesignSettingsAction if (empty($result)) { common_log_db_error($original, 'UPDATE', __FILE__); - $this->showForm(_('Unable to save your design settings!')); + $this->showForm(_('Unable to save your design settings.')); $this->group->query('ROLLBACK'); return; } diff --git a/actions/grouplogo.php b/actions/grouplogo.php index a9dc7eb1d..f197aef33 100644 --- a/actions/grouplogo.php +++ b/actions/grouplogo.php @@ -83,7 +83,7 @@ class GrouplogoAction extends GroupDesignAction } if (!$nickname) { - $this->clientError(_('No nickname'), 404); + $this->clientError(_('No nickname.'), 404); return false; } @@ -96,14 +96,14 @@ class GrouplogoAction extends GroupDesignAction } if (!$this->group) { - $this->clientError(_('No such group'), 404); + $this->clientError(_('No such group.'), 404); return false; } $cur = common_current_user(); if (!$cur->isAdmin($this->group)) { - $this->clientError(_('You must be an admin to edit the group'), 403); + $this->clientError(_('You must be an admin to edit the group.'), 403); return false; } @@ -175,7 +175,7 @@ class GrouplogoAction extends GroupDesignAction if (!$profile) { common_log_db_error($user, 'SELECT', __FILE__); - $this->serverError(_('User without matching profile')); + $this->serverError(_('User without matching profile.')); return; } diff --git a/actions/groupmembers.php b/actions/groupmembers.php index 5c59594c5..0f47c268d 100644 --- a/actions/groupmembers.php +++ b/actions/groupmembers.php @@ -73,14 +73,14 @@ class GroupmembersAction extends GroupDesignAction } if (!$nickname) { - $this->clientError(_('No nickname'), 404); + $this->clientError(_('No nickname.'), 404); return false; } $this->group = User_group::staticGet('nickname', $nickname); if (!$this->group) { - $this->clientError(_('No such group'), 404); + $this->clientError(_('No such group.'), 404); return false; } diff --git a/actions/imsettings.php b/actions/imsettings.php index f57933b43..751c6117c 100644 --- a/actions/imsettings.php +++ b/actions/imsettings.php @@ -56,7 +56,7 @@ class ImsettingsAction extends ConnectSettingsAction function title() { - return _('IM Settings'); + return _('IM settings'); } /** @@ -121,7 +121,7 @@ class ImsettingsAction extends ConnectSettingsAction } else { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); - $this->input('jabber', _('IM Address'), + $this->input('jabber', _('IM address'), ($this->arg('jabber')) ? $this->arg('jabber') : null, sprintf(_('Jabber or GTalk address, '. 'like "UserName@example.org". '. diff --git a/actions/joingroup.php b/actions/joingroup.php index 5ca34bd9c..235e5ab4c 100644 --- a/actions/joingroup.php +++ b/actions/joingroup.php @@ -73,21 +73,21 @@ class JoingroupAction extends Action } if (!$nickname) { - $this->clientError(_('No nickname'), 404); + $this->clientError(_('No nickname.'), 404); return false; } $this->group = User_group::staticGet('nickname', $nickname); if (!$this->group) { - $this->clientError(_('No such group'), 404); + $this->clientError(_('No such group.'), 404); return false; } $cur = common_current_user(); if ($cur->isMember($this->group)) { - $this->clientError(_('You are already a member of that group'), 403); + $this->clientError(_('You are already a member of that group.'), 403); return false; } @@ -115,17 +115,13 @@ class JoingroupAction extends Action $cur = common_current_user(); - $member = new Group_member(); - - $member->group_id = $this->group->id; - $member->profile_id = $cur->id; - $member->created = common_sql_now(); - - $result = $member->insert(); - - if (!$result) { - common_log_db_error($member, 'INSERT', __FILE__); - $this->serverError(sprintf(_('Could not join user %1$s to group %2$s'), + try { + if (Event::handle('StartJoinGroup', array($this->group, $cur))) { + Group_member::join($this->group->id, $cur->id); + Event::handle('EndJoinGroup', array($this->group, $cur)); + } + } catch (Exception $e) { + $this->serverError(sprintf(_('Could not join user %1$s to group %2$s.'), $cur->nickname, $this->group->nickname)); } diff --git a/actions/leavegroup.php b/actions/leavegroup.php index b0f973e1a..9b9d83b6c 100644 --- a/actions/leavegroup.php +++ b/actions/leavegroup.php @@ -110,22 +110,15 @@ class LeavegroupAction extends Action $cur = common_current_user(); - $member = new Group_member(); - - $member->group_id = $this->group->id; - $member->profile_id = $cur->id; - - if (!$member->find(true)) { - $this->serverError(_('Could not find membership record.')); - return; - } - - $result = $member->delete(); - - if (!$result) { - common_log_db_error($member, 'DELETE', __FILE__); + try { + if (Event::handle('StartLeaveGroup', array($this->group, $cur))) { + Group_member::leave($this->group->id, $cur->id); + Event::handle('EndLeaveGroup', array($this->group, $cur)); + } + } catch (Exception $e) { $this->serverError(sprintf(_('Could not remove user %1$s from group %2$s.'), $cur->nickname, $this->group->nickname)); + return; } if ($this->boolean('ajax')) { diff --git a/actions/login.php b/actions/login.php index c775fa692..8ea3c800b 100644 --- a/actions/login.php +++ b/actions/login.php @@ -76,15 +76,10 @@ class LoginAction extends Action { parent::handle($args); - $disabled = common_config('logincommand','disabled'); - $disabled = isset($disabled) && $disabled; - if (common_is_real_login()) { $this->clientError(_('Already logged in.')); } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->checkLogin(); - } else if (!$disabled && isset($args['user_id']) && isset($args['token'])){ - $this->checkLogin($args['user_id'],$args['token']); } else { common_ensure_session(); $this->showForm(); @@ -103,46 +98,30 @@ class LoginAction extends Action function checkLogin($user_id=null, $token=null) { - if(isset($token) && isset($user_id)){ - //Token based login (from the LoginCommand) - $login_token = Login_token::staticGet('user_id',$user_id); - if($login_token && $login_token->token == $token){ - if($login_token->modified > time()+2*60){ - //token has expired - //delete the token as it is useless - $login_token->delete(); - $this->showForm(_('Invalid or expired token.')); - return; - }else{ - //delete the token so it cannot be reused - $login_token->delete(); - //it's a valid token - let them log in - $user = User::staticGet('id', $user_id); - //$user = User::staticGet('nickname', "candrews"); - } - }else{ - $this->showForm(_('Invalid or expired token.')); - return; - } - }else{ - // Regular form submission login - - // XXX: login throttle - - // CSRF protection - token set in NoticeForm - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->clientError(_('There was a problem with your session token. '. - 'Try again, please.')); - return; - } - - $nickname = $this->trimmed('nickname'); - $password = $this->arg('password'); - - $user = common_check_user($nickname, $password); + // XXX: login throttle + + // CSRF protection - token set in NoticeForm + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $st = common_session_token(); + if (empty($token)) { + common_log(LOG_WARNING, 'No token provided by client.'); + } else if (empty($st)) { + common_log(LOG_WARNING, 'No session token stored.'); + } else { + common_log(LOG_WARNING, 'Token = ' . $token . ' and session token = ' . $st); + } + + $this->clientError(_('There was a problem with your session token. '. + 'Try again, please.')); + return; } + $nickname = $this->trimmed('nickname'); + $password = $this->arg('password'); + + $user = common_check_user($nickname, $password); + if (!$user) { $this->showForm(_('Incorrect username or password.')); return; @@ -165,6 +144,7 @@ class LoginAction extends Action if ($url) { // We don't have to return to it again common_set_returnto(null); + $url = common_inject_session($url); } else { $url = common_local_url('all', array('nickname' => @@ -240,9 +220,9 @@ class LoginAction extends Action function showContent() { $this->elementStart('form', array('method' => 'post', - 'id' => 'form_login', - 'class' => 'form_settings', - 'action' => common_local_url('login'))); + 'id' => 'form_login', + 'class' => 'form_settings', + 'action' => common_local_url('login'))); $this->elementStart('fieldset'); $this->element('legend', null, _('Login to site')); $this->elementStart('ul', 'form_data'); @@ -255,7 +235,7 @@ class LoginAction extends Action $this->elementStart('li'); $this->checkbox('rememberme', _('Remember me'), false, _('Automatically login in the future; ' . - 'not for shared computers!')); + 'not for shared computers!')); $this->elementEnd('li'); $this->elementEnd('ul'); $this->submit('submit', _('Login')); diff --git a/actions/makeadmin.php b/actions/makeadmin.php index 250ade221..9ad7d6e7c 100644 --- a/actions/makeadmin.php +++ b/actions/makeadmin.php @@ -129,7 +129,7 @@ class MakeadminAction extends Action 'profile_id' => $this->profile->id)); if (empty($member)) { - $this->serverError(_('Can\'t get membership record for %1$s in group %2$s'), + $this->serverError(_('Can\'t get membership record for %1$s in group %2$s.'), $this->profile->getBestName(), $this->group->getBestName()); } @@ -142,7 +142,7 @@ class MakeadminAction extends Action if (!$result) { common_log_db_error($member, 'UPDATE', __FILE__); - $this->serverError(_('Can\'t make %1$s an admin for group %2$s'), + $this->serverError(_('Can\'t make %1$s an admin for group %2$s.'), $this->profile->getBestName(), $this->group->getBestName()); } diff --git a/actions/newapplication.php b/actions/newapplication.php new file mode 100644 index 000000000..bc5b4edaf --- /dev/null +++ b/actions/newapplication.php @@ -0,0 +1,277 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Register a new OAuth Application + * + * 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 Applications + * @package StatusNet + * @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); +} + +/** + * Add a new application + * + * This is the form for adding a new application + * + * @category Application + * @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 NewApplicationAction extends OwnerDesignAction +{ + var $msg; + + function title() + { + return _('New application'); + } + + /** + * Prepare to run + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to register an application.')); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On GET, show the form. On POST, try to save the app. + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->handlePost($args); + } else { + $this->showForm(); + } + } + + function handlePost($args) + { + // 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->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); + return; + } + + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token.')); + return; + } + + $cur = common_current_user(); + + if ($this->arg('cancel')) { + common_redirect(common_local_url('oauthappssettings'), 303); + } elseif ($this->arg('save')) { + $this->trySave(); + } else { + $this->clientError(_('Unexpected form submission.')); + } + } + + function showForm($msg=null) + { + $this->msg = $msg; + $this->showPage(); + } + + function showContent() + { + $form = new ApplicationEditForm($this); + $form->show(); + } + + function showPageNotice() + { + if ($this->msg) { + $this->element('p', 'error', $this->msg); + } else { + $this->element('p', 'instructions', + _('Use this form to register a new application.')); + } + } + + function trySave() + { + $name = $this->trimmed('name'); + $description = $this->trimmed('description'); + $source_url = $this->trimmed('source_url'); + $organization = $this->trimmed('organization'); + $homepage = $this->trimmed('homepage'); + $callback_url = $this->trimmed('callback_url'); + $type = $this->arg('app_type'); + $access_type = $this->arg('default_access_type'); + + if (empty($name)) { + $this->showForm(_('Name is required.')); + return; + } elseif (mb_strlen($name) > 255) { + $this->showForm(_('Name is too long (max 255 chars).')); + return; + } elseif (empty($description)) { + $this->showForm(_('Description is required.')); + return; + } elseif (Oauth_application::descriptionTooLong($description)) { + $this->showForm(sprintf( + _('Description is too long (max %d chars).'), + Oauth_application::maxDescription())); + return; + } elseif (empty($source_url)) { + $this->showForm(_('Source URL is required.')); + return; + } elseif ((strlen($source_url) > 0) + && !Validate::uri( + $source_url, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Source URL is not valid.')); + return; + } elseif (empty($organization)) { + $this->showForm(_('Organization is required.')); + return; + } elseif (mb_strlen($organization) > 255) { + $this->showForm(_('Organization is too long (max 255 chars).')); + return; + } elseif (empty($homepage)) { + $this->showForm(_('Organization homepage is required.')); + return; + } elseif ((strlen($homepage) > 0) + && !Validate::uri( + $homepage, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Homepage is not a valid URL.')); + return; + } elseif (mb_strlen($callback_url) > 255) { + $this->showForm(_('Callback is too long.')); + return; + } elseif (strlen($callback_url) > 0 + && !Validate::uri( + $source_url, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Callback URL is not valid.')); + return; + } + + $cur = common_current_user(); + + // Checked in prepare() above + + assert(!is_null($cur)); + + $app = new Oauth_application(); + + $app->query('BEGIN'); + + $app->name = $name; + $app->owner = $cur->id; + $app->description = $description; + $app->source_url = $source_url; + $app->organization = $organization; + $app->homepage = $homepage; + $app->callback_url = $callback_url; + $app->type = $type; + + // Yeah, I dunno why I chose bit flags. I guess so I could + // copy this value directly to Oauth_application_user + // access_type which I think does need bit flags -- Z + + if ($access_type == 'r') { + $app->setAccessFlags(true, false); + } else { + $app->setAccessFlags(true, true); + } + + $app->created = common_sql_now(); + + // generate consumer key and secret + + $consumer = Consumer::generateNew(); + + $result = $consumer->insert(); + + if (!$result) { + common_log_db_error($consumer, 'INSERT', __FILE__); + $this->serverError(_('Could not create application.')); + } + + $app->consumer_key = $consumer->consumer_key; + + $this->app_id = $app->insert(); + + if (!$this->app_id) { + common_log_db_error($app, 'INSERT', __FILE__); + $this->serverError(_('Could not create application.')); + $app->query('ROLLBACK'); + } + + $app->uploadLogo(); + + $app->query('COMMIT'); + + common_redirect(common_local_url('oauthappssettings'), 303); + + } + +} + diff --git a/actions/newmessage.php b/actions/newmessage.php index 350452091..25e58feab 100644 --- a/actions/newmessage.php +++ b/actions/newmessage.php @@ -182,7 +182,7 @@ class NewmessageAction extends Action $this->elementEnd('head'); $this->elementStart('body'); $this->element('p', array('id' => 'command_result'), - sprintf(_('Direct message to %s sent'), + sprintf(_('Direct message to %s sent.'), $this->other->nickname)); $this->elementEnd('body'); $this->elementEnd('html'); diff --git a/actions/oauthappssettings.php b/actions/oauthappssettings.php new file mode 100644 index 000000000..6c0670b17 --- /dev/null +++ b/actions/oauthappssettings.php @@ -0,0 +1,166 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * List the OAuth applications that a user has registered with this instance + * + * 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 Settings + * @package StatusNet + * @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); +} + +require_once INSTALLDIR . '/lib/settingsaction.php'; +require_once INSTALLDIR . '/lib/applicationlist.php'; + +/** + * Show a user's registered OAuth applications + * + * @category Settings + * @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/ + * + * @see SettingsAction + */ + +class OauthappssettingsAction extends SettingsAction +{ + var $page = 0; + + function prepare($args) + { + parent::prepare($args); + $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to list your applications.')); + return false; + } + + return true; + } + + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _('OAuth applications'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _('Applications you have registered'); + } + + /** + * Content area of the page + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + + $offset = ($this->page - 1) * APPS_PER_PAGE; + $limit = APPS_PER_PAGE + 1; + + $application = new Oauth_application(); + $application->owner = $user->id; + $application->limit($offset, $limit); + $application->orderBy('created DESC'); + $application->find(); + + $cnt = 0; + + if ($application) { + $al = new ApplicationList($application, $user, $this); + $cnt = $al->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } + } + + $this->elementStart('p', array('id' => 'application_register')); + $this->element('a', + array('href' => common_local_url('newapplication'), + 'class' => 'more' + ), + 'Register a new application'); + $this->elementEnd('p'); + + $this->pagination( + $this->page > 1, + $cnt > APPS_PER_PAGE, + $this->page, + 'oauthappssettings' + ); + } + + function showEmptyListMessage() + { + $message = sprintf(_('You have not registered any applications yet.')); + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + } + +} diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php new file mode 100644 index 000000000..c2e8d441b --- /dev/null +++ b/actions/oauthconnectionssettings.php @@ -0,0 +1,212 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * List a user's OAuth connected applications + * + * 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 Settings + * @package StatusNet + * @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); +} + +require_once INSTALLDIR . '/lib/connectsettingsaction.php'; +require_once INSTALLDIR . '/lib/applicationlist.php'; + +/** + * Show connected OAuth applications + * + * @category Settings + * @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/ + * + * @see SettingsAction + */ + +class OauthconnectionssettingsAction extends ConnectSettingsAction +{ + + var $page = null; + var $id = null; + + function prepare($args) + { + parent::prepare($args); + $this->id = (int)$this->arg('id'); + $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; + return true; + } + + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _('Connected applications'); + } + + function isReadOnly($args) + { + return true; + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _('You have allowed the following applications to access you account.'); + } + + /** + * Content area of the page + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + $profile = $user->getProfile(); + + $offset = ($this->page - 1) * APPS_PER_PAGE; + $limit = APPS_PER_PAGE + 1; + + $application = $profile->getApplications($offset, $limit); + + $cnt == 0; + + if (!empty($application)) { + $al = new ApplicationList($application, $user, $this, true); + $cnt = $al->show(); + } + + if ($cnt == 0) { + $this->showEmptyListMessage(); + } + + $this->pagination($this->page > 1, $cnt > APPS_PER_PAGE, + $this->page, 'connectionssettings', + array('nickname' => $this->user->nickname)); + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + if ($this->arg('revoke')) { + $this->revokeAccess($this->id); + + // XXX: Show some indicator to the user of what's been done. + + $this->showPage(); + } else { + $this->clientError(_('Unexpected form submission.'), 401); + return false; + } + } + + function revokeAccess($appId) + { + $cur = common_current_user(); + + $app = Oauth_application::staticGet('id', $appId); + + if (empty($app)) { + $this->clientError(_('No such application.'), 404); + return false; + } + + $appUser = Oauth_application_user::getByKeys($cur, $app); + + if (empty($appUser)) { + $this->clientError(_('You are not a user of that application.'), 401); + return false; + } + + $orig = clone($appUser); + $appUser->access_type = 0; // No access + $result = $appUser->update(); + + if (!$result) { + common_log_db_error($orig, 'UPDATE', __FILE__); + $this->clientError(_('Unable to revoke access for app: ' . $app->id)); + return false; + } + + $msg = 'User %s (id: %d) revoked access to app %s (id: %d)'; + common_log(LOG_INFO, sprintf($msg, $cur->nickname, + $cur->id, $app->name, $app->id)); + + } + + function showEmptyListMessage() + { + $message = sprintf(_('You have not authorized any applications to use your account.')); + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + + function showSections() + { + $cur = common_current_user(); + + $this->element('h2', null, 'Developers'); + $this->elementStart('p'); + $this->raw(_('Developers can edit the registration settings for their applications ')); + $this->element('a', + array('href' => common_local_url('oauthappssettings')), + 'here.'); + $this->elementEnd('p'); + } + +} diff --git a/actions/othersettings.php b/actions/othersettings.php index 0de7cd908..10e9873b3 100644 --- a/actions/othersettings.php +++ b/actions/othersettings.php @@ -57,7 +57,7 @@ class OthersettingsAction extends AccountSettingsAction function title() { - return _('Other Settings'); + return _('Other settings'); } /** diff --git a/actions/otp.php b/actions/otp.php new file mode 100644 index 000000000..acf84aee8 --- /dev/null +++ b/actions/otp.php @@ -0,0 +1,145 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Allow one-time password login + * + * 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 Login + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Allow one-time password login + * + * This action will automatically log in the user identified by the user_id + * parameter. A login_token record must be constructed beforehand, typically + * by code where the user is already authenticated. + * + * @category Login + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +class OtpAction extends Action +{ + var $user; + var $token; + var $rememberme; + var $returnto; + var $lt; + + function prepare($args) + { + parent::prepare($args); + + if (common_is_real_login()) { + $this->clientError(_('Already logged in.')); + return false; + } + + $id = $this->trimmed('user_id'); + + if (empty($id)) { + $this->clientError(_('No user ID specified.')); + return false; + } + + $this->user = User::staticGet('id', $id); + + if (empty($this->user)) { + $this->clientError(_('No such user.')); + return false; + } + + $this->token = $this->trimmed('token'); + + if (empty($this->token)) { + $this->clientError(_('No login token specified.')); + return false; + } + + $this->lt = Login_token::staticGet('user_id', $id); + + if (empty($this->lt)) { + $this->clientError(_('No login token requested.')); + return false; + } + + if ($this->lt->token != $this->token) { + $this->clientError(_('Invalid login token specified.')); + return false; + } + + if ($this->lt->modified > time() + Login_token::TIMEOUT) { + //token has expired + //delete the token as it is useless + $this->lt->delete(); + $this->lt = null; + $this->clientError(_('Login token expired.')); + return false; + } + + $this->rememberme = $this->boolean('rememberme'); + $this->returnto = $this->trimmed('returnto'); + + return true; + } + + function handle($args) + { + parent::handle($args); + + // success! + if (!common_set_user($this->user)) { + $this->serverError(_('Error setting user. You are probably not authorized.')); + return; + } + + // We're now logged in; disable the lt + + $this->lt->delete(); + $this->lt = null; + + if ($this->rememberme) { + common_rememberme($this->user); + } + + if (!empty($this->returnto)) { + $url = $this->returnto; + // We don't have to return to it again + common_set_returnto(null); + } else { + $url = common_local_url('all', + array('nickname' => + $this->user->nickname)); + } + + common_redirect($url, 303); + } +} diff --git a/actions/pathsadminpanel.php b/actions/pathsadminpanel.php index d39c7c449..3779fcfaa 100644 --- a/actions/pathsadminpanel.php +++ b/actions/pathsadminpanel.php @@ -305,7 +305,7 @@ class PathsAdminPanelForm extends AdminForm $this->unli(); $this->li(); - $this->input('sslserver', _('SSL Server'), + $this->input('sslserver', _('SSL server'), _('Server to direct SSL requests to'), 'site'); $this->unli(); $this->out->elementEnd('ul'); diff --git a/actions/register.php b/actions/register.php index 96015c219..698137346 100644 --- a/actions/register.php +++ b/actions/register.php @@ -259,6 +259,7 @@ class RegisterAction extends Action // Re-init language env in case it changed (not yet, but soon) common_init_language(); + $this->showSuccess(); } else { $this->showForm(_('Invalid username or password.')); diff --git a/actions/showapplication.php b/actions/showapplication.php new file mode 100644 index 000000000..a6ff425c7 --- /dev/null +++ b/actions/showapplication.php @@ -0,0 +1,327 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Show an OAuth application + * + * 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 Application + * @package StatusNet + * @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); +} + +/** + * Show an OAuth application + * + * @category Application + * @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 ShowApplicationAction extends OwnerDesignAction +{ + /** + * Application to show + */ + + var $application = null; + + /** + * User who owns the app + */ + + var $owner = null; + + var $msg = null; + + var $success = null; + + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + + $id = (int)$this->arg('id'); + + $this->application = Oauth_application::staticGet($id); + $this->owner = User::staticGet($this->application->owner); + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to view an application.')); + return false; + } + + if (empty($this->application)) { + $this->clientError(_('No such application.'), 404); + return false; + } + + $cur = common_current_user(); + + if ($cur->id != $this->owner->id) { + $this->clientError(_('You are not the owner of this application.'), 401); + return false; + } + + return true; + } + + /** + * Handle the request + * + * Shows info about the app + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token.')); + return; + } + + if ($this->arg('reset')) { + $this->resetKey(); + } + } else { + $this->showPage(); + } + } + + /** + * Title of the page + * + * @return string title of the page + */ + + function title() + { + if (!empty($this->application->name)) { + return 'Application: ' . $this->application->name; + } + } + + function showPageNotice() + { + if (!empty($this->msg)) { + $this->element('div', ($this->success) ? 'success' : 'error', $this->msg); + } + } + + function showContent() + { + + $cur = common_current_user(); + + $consumer = $this->application->getConsumer(); + + $this->elementStart('div', 'entity_profile vcard'); + $this->element('h2', null, _('Application profile')); + $this->elementStart('dl', 'entity_depiction'); + $this->element('dt', null, _('Icon')); + $this->elementStart('dd'); + if (!empty($this->application->icon)) { + $this->element('img', array('src' => $this->application->icon, + 'class' => 'photo logo')); + } + $this->elementEnd('dd'); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_fn'); + $this->element('dt', null, _('Name')); + $this->elementStart('dd'); + $this->element('a', array('href' => $this->application->source_url, + 'class' => 'url fn'), + $this->application->name); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_org'); + $this->element('dt', null, _('Organization')); + $this->elementStart('dd'); + $this->element('a', array('href' => $this->application->homepage, + 'class' => 'url'), + $this->application->organization); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_note'); + $this->element('dt', null, _('Description')); + $this->element('dd', 'note', $this->application->description); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_statistics'); + $this->element('dt', null, _('Statistics')); + $this->elementStart('dd'); + $defaultAccess = ($this->application->access_type & Oauth_application::$writeAccess) + ? 'read-write' : 'read-only'; + $profile = Profile::staticGet($this->application->owner); + + $appUsers = new Oauth_application_user(); + $appUsers->application_id = $this->application->id; + $userCnt = $appUsers->count(); + + $this->raw(sprintf( + _('created by %1$s - %2$s access by default - %3$d users'), + $profile->getBestName(), + $defaultAccess, + $userCnt + )); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + $this->elementEnd('div'); + + $this->elementStart('div', 'entity_actions'); + $this->element('h2', null, _('Application actions')); + $this->elementStart('ul'); + $this->elementStart('li', 'entity_edit'); + $this->element('a', + array('href' => common_local_url('editapplication', + array('id' => $this->application->id))), + 'Edit'); + $this->elementEnd('li'); + + $this->elementStart('li', 'entity_reset_keysecret'); + $this->elementStart('form', array( + 'id' => 'forma_reset_key', + 'class' => 'form_reset_key', + 'method' => 'POST', + 'action' => common_local_url('showapplication', + array('id' => $this->application->id)))); + + $this->elementStart('fieldset'); + $this->hidden('token', common_session_token()); + $this->submit('reset', _('Reset key & secret')); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + $this->elementEnd('li'); + $this->elementEnd('ul'); + $this->elementEnd('div'); + + $this->elementStart('div', 'entity_data'); + $this->element('h2', null, _('Application info')); + $this->elementStart('dl', 'entity_consumer_key'); + $this->element('dt', null, _('Consumer key')); + $this->element('dd', null, $consumer->consumer_key); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_consumer_secret'); + $this->element('dt', null, _('Consumer secret')); + $this->element('dd', null, $consumer->consumer_secret); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_request_token_url'); + $this->element('dt', null, _('Request token URL')); + $this->element('dd', null, common_local_url('apioauthrequesttoken')); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_access_token_url'); + $this->element('dt', null, _('Access token URL')); + $this->element('dd', null, common_local_url('apioauthaccesstoken')); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_authorize_url'); + $this->element('dt', null, _('Authorize URL')); + $this->element('dd', null, common_local_url('apioauthauthorize')); + $this->elementEnd('dl'); + + $this->element('p', 'note', + _('Note: We support HMAC-SHA1 signatures. We do not support the plaintext signature method.')); + $this->elementEnd('div'); + + $this->elementStart('p', array('id' => 'application_action')); + $this->element('a', + array('href' => common_local_url('oauthappssettings'), + 'class' => 'more'), + 'View your applications'); + $this->elementEnd('p'); + } + + function resetKey() + { + $this->application->query('BEGIN'); + + $consumer = $this->application->getConsumer(); + $result = $consumer->delete(); + + if (!$result) { + common_log_db_error($consumer, 'DELETE', __FILE__); + $this->success = false; + $this->msg = ('Unable to reset consumer key and secret.'); + $this->showPage(); + return; + } + + $consumer = Consumer::generateNew(); + + $result = $consumer->insert(); + + if (!$result) { + common_log_db_error($consumer, 'INSERT', __FILE__); + $this->application->query('ROLLBACK'); + $this->success = false; + $this->msg = ('Unable to reset consumer key and secret.'); + $this->showPage(); + return; + } + + $orig = clone($this->application); + $this->application->consumer_key = $consumer->consumer_key; + $result = $this->application->update($orig); + + if (!$result) { + common_log_db_error($application, 'UPDATE', __FILE__); + $this->application->query('ROLLBACK'); + $this->success = false; + $this->msg = ('Unable to reset consumer key and secret.'); + $this->showPage(); + return; + } + + $this->application->query('COMMIT'); + + $this->success = true; + $this->msg = ('Consumer key and secret reset.'); + $this->showPage(); + } + +} diff --git a/actions/showgroup.php b/actions/showgroup.php index c0de4c653..06ae572e8 100644 --- a/actions/showgroup.php +++ b/actions/showgroup.php @@ -118,7 +118,7 @@ class ShowgroupAction extends GroupDesignAction } if (!$nickname) { - $this->clientError(_('No nickname'), 404); + $this->clientError(_('No nickname.'), 404); return false; } @@ -134,7 +134,7 @@ class ShowgroupAction extends GroupDesignAction common_redirect(common_local_url('groupbyid', $args), 301); return false; } else { - $this->clientError(_('No such group'), 404); + $this->clientError(_('No such group.'), 404); return false; } } diff --git a/actions/siteadminpanel.php b/actions/siteadminpanel.php index 5e29f4c19..dd388a18a 100644 --- a/actions/siteadminpanel.php +++ b/actions/siteadminpanel.php @@ -151,10 +151,10 @@ class SiteadminpanelAction extends AdminPanelAction $values['site']['email'] = common_canonical_email($values['site']['email']); if (empty($values['site']['email'])) { - $this->clientError(_('You must have a valid contact email address')); + $this->clientError(_('You must have a valid contact email address.')); } if (!Validate::email($values['site']['email'], common_config('email', 'check_domain'))) { - $this->clientError(_('Not a valid email address')); + $this->clientError(_('Not a valid email address.')); } // Validate timezone @@ -169,7 +169,7 @@ class SiteadminpanelAction extends AdminPanelAction if (!is_null($values['site']['language']) && !in_array($values['site']['language'], array_keys(get_nice_language_list()))) { - $this->clientError(sprintf(_('Unknown language "%s"'), $values['site']['language'])); + $this->clientError(sprintf(_('Unknown language "%s".'), $values['site']['language'])); } // Validate report URL diff --git a/actions/smssettings.php b/actions/smssettings.php index 672abcef8..751495d57 100644 --- a/actions/smssettings.php +++ b/actions/smssettings.php @@ -55,7 +55,7 @@ class SmssettingsAction extends ConnectSettingsAction function title() { - return _('SMS Settings'); + return _('SMS settings'); } /** @@ -135,7 +135,7 @@ class SmssettingsAction extends ConnectSettingsAction } else { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); - $this->input('sms', _('SMS Phone number'), + $this->input('sms', _('SMS phone number'), ($this->arg('sms')) ? $this->arg('sms') : null, _('Phone number, no punctuation or spaces, '. 'with area code')); diff --git a/actions/subscribe.php b/actions/subscribe.php index 4c46806e4..a90d7facd 100644 --- a/actions/subscribe.php +++ b/actions/subscribe.php @@ -58,7 +58,7 @@ class SubscribeAction extends Action $result = subs_subscribe_to($user, $other); - if($result != true) { + if (is_string($result)) { $this->clientError($result); return; } diff --git a/actions/tagother.php b/actions/tagother.php index e9e13b939..735d876da 100644 --- a/actions/tagother.php +++ b/actions/tagother.php @@ -163,8 +163,8 @@ class TagotherAction extends Action $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; } diff --git a/actions/unsubscribe.php b/actions/unsubscribe.php index 4a5863489..6bb10d448 100644 --- a/actions/unsubscribe.php +++ b/actions/unsubscribe.php @@ -81,13 +81,13 @@ class UnsubscribeAction extends Action $other = Profile::staticGet('id', $other_id); if (!$other) { - $this->clientError(_('No profile with that id.')); + $this->clientError(_('No profile with that ID.')); return; } $result = subs_unsubscribe_to($user, $other); - if ($result != true) { + if (is_string($result)) { $this->clientError($result); return; } diff --git a/actions/userdesignsettings.php b/actions/userdesignsettings.php index 31a097970..1cf878000 100644 --- a/actions/userdesignsettings.php +++ b/actions/userdesignsettings.php @@ -207,7 +207,7 @@ class UserDesignSettingsAction extends DesignSettingsAction if (empty($id)) { common_log_db_error($id, 'INSERT', __FILE__); - $this->showForm(_('Unable to save your design settings!')); + $this->showForm(_('Unable to save your design settings.')); return; } @@ -217,7 +217,7 @@ class UserDesignSettingsAction extends DesignSettingsAction if (empty($result)) { common_log_db_error($original, 'UPDATE', __FILE__); - $this->showForm(_('Unable to save your design settings!')); + $this->showForm(_('Unable to save your design settings.')); $user->query('ROLLBACK'); return; } @@ -260,7 +260,7 @@ class UserDesignSettingsAction extends DesignSettingsAction if (empty($id)) { common_log_db_error($id, 'INSERT', __FILE__); - $this->showForm(_('Unable to save your design settings!')); + $this->showForm(_('Unable to save your design settings.')); return; } @@ -270,7 +270,7 @@ class UserDesignSettingsAction extends DesignSettingsAction if (empty($result)) { common_log_db_error($original, 'UPDATE', __FILE__); - $this->showForm(_('Unable to save your design settings!')); + $this->showForm(_('Unable to save your design settings.')); $user->query('ROLLBACK'); return; } diff --git a/actions/version.php b/actions/version.php index c1f673c45..b6593e5ed 100644 --- a/actions/version.php +++ b/actions/version.php @@ -266,5 +266,6 @@ class VersionAction extends Action 'Craig Andrews', 'mEDI', 'Brett Taylor', - 'Brigitte Schuster'); + 'Brigitte Schuster', + 'Brion Vibber'); } |