Merge commit 'mainline/0.9.x' into 0.9.x

This commit is contained in:
Brenda Wallace 2010-01-18 11:48:11 +13:00
commit 02a6006baf
230 changed files with 40752 additions and 21998 deletions

View File

@ -655,3 +655,35 @@ StartUnblockProfile: when we're about to unblock
EndUnblockProfile: when an unblock has succeeded
- $user: the person doing the unblock
- $profile: the person unblocked, can be remote
StartSubscribe: when a subscription is starting
- $user: the person subscribing
- $other: the person being subscribed to
EndSubscribe: when a subscription is finished
- $user: the person subscribing
- $other: the person being subscribed to
StartUnsubscribe: when an unsubscribe is starting
- $user: the person unsubscribing
- $other: the person being unsubscribed from
EndUnsubscribe: when an unsubscribe is done
- $user: the person unsubscribing
- $other: the person being unsubscribed to
StartJoinGroup: when a user is joining a group
- $group: the group being joined
- $user: the user joining
EndJoinGroup: when a user finishes joining a group
- $group: the group being joined
- $user: the user joining
StartLeaveGroup: when a user is leaving a group
- $group: the group being left
- $user: the user leaving
EndLeaveGroup: when a user has left a group
- $group: the group being left
- $user: the user leaving

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -133,7 +133,7 @@ class ApiGroupCreateAction extends ApiAuthAction
break;
default:
$this->clientError(
_('API method not found!'),
_('API method not found.'),
404,
$this->format
);

View File

@ -111,7 +111,7 @@ class ApiGroupIsMemberAction extends ApiBareAuthAction
break;
default:
$this->clientError(
_('API method not found!'),
_('API method not found.'),
400,
$this->format
);

View File

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

View File

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

View File

@ -129,7 +129,7 @@ class ApiGroupListAction extends ApiBareAuthAction
break;
default:
$this->clientError(
_('API method not found!'),
_('API method not found.'),
404,
$this->format
);

View File

@ -117,7 +117,7 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction
break;
default:
$this->clientError(
_('API method not found!'),
_('API method not found.'),
404,
$this->format
);

View File

@ -103,7 +103,7 @@ class ApiGroupMembershipAction extends ApiPrivateAuthAction
break;
default:
$this->clientError(
_('API method not found!'),
_('API method not found.'),
404,
$this->format
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -130,7 +130,7 @@ class ApiStatusnetConfigAction extends ApiAction
break;
default:
$this->clientError(
_('API method not found!'),
_('API method not found.'),
404,
$this->format
);

View File

@ -90,7 +90,7 @@ class ApiStatusnetVersionAction extends ApiPrivateAuthAction
break;
default:
$this->clientError(
_('API method not found!'),
_('API method not found.'),
404,
$this->format
);

View File

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

View File

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

View File

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

View File

@ -147,7 +147,7 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
break;
default:
$this->clientError(
_('API method not found!'),
_('API method not found.'),
404,
$this->format
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -141,7 +141,7 @@ class ConfirmaddressAction extends Action
function title()
{
return _('Confirm Address');
return _('Confirm address');
}
/**

View File

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

264
actions/editapplication.php Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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". '.

View File

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

View File

@ -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')) {

View File

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

View File

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

277
actions/newapplication.php Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -57,7 +57,7 @@ class OthersettingsAction extends AccountSettingsAction
function title()
{
return _('Other Settings');
return _('Other settings');
}
/**

145
actions/otp.php Normal file
View File

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

View File

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

View File

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

327
actions/showapplication.php Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -266,5 +266,6 @@ class VersionAction extends Action
'Craig Andrews',
'mEDI',
'Brett Taylor',
'Brigitte Schuster');
'Brigitte Schuster',
'Brion Vibber');
}

View File

@ -4,16 +4,17 @@
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Consumer extends Memcached_DataObject
class Consumer extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'consumer'; // table name
public $consumer_key; // varchar(255) primary_key not_null
public $consumer_secret; // varchar(255) not_null
public $seed; // char(32) not_null
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
/* Static get */
function staticGet($k,$v=null)
@ -21,4 +22,18 @@ class Consumer extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
static function generateNew()
{
$cons = new Consumer();
$rand = common_good_rand(16);
$cons->seed = $rand;
$cons->consumer_key = md5(time() + $rand);
$cons->consumer_secret = md5(md5(time() + time() + $rand));
$cons->created = common_sql_now();
return $cons;
}
}

View File

@ -80,7 +80,14 @@ class File extends Memcached_DataObject
if (isset($redir_data['type'])
&& (('text/html' === substr($redir_data['type'], 0, 9) || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21)))
&& ($oembed_data = File_oembed::_getOembed($given_url))) {
$fo = File_oembed::staticGet('file_id', $file_id);
if (empty($fo)) {
File_oembed::saveNew($oembed_data, $file_id);
} else {
common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file $file_id", __FILE__);
}
}
return $x;
}

View File

@ -115,7 +115,13 @@ class File_oembed extends Memcached_DataObject
}
$file_oembed->insert();
if (!empty($data->thumbnail_url)) {
File_thumbnail::saveNew($data, $file_id);
$ft = File_thumbnail::staticGet('file_id', $file_id);
if (!empty($ft)) {
common_log(LOG_WARNING, "Strangely, a File_thumbnail object exists for new file $file_id",
__FILE__);
} else {
File_thumbnail::saveNew($data, $file_id);
}
}
}
}

View File

@ -25,4 +25,41 @@ class Group_member extends Memcached_DataObject
{
return Memcached_DataObject::pkeyGet('Group_member', $kv);
}
static function join($group_id, $profile_id)
{
$member = new Group_member();
$member->group_id = $group_id;
$member->profile_id = $profile_id;
$member->created = common_sql_now();
$result = $member->insert();
if (!$result) {
common_log_db_error($member, 'INSERT', __FILE__);
throw new Exception(_("Group join failed."));
}
return true;
}
static function leave($group_id, $profile_id)
{
$member = Group_member::pkeyGet(array('group_id' => $group_id,
'profile_id' => $profile_id));
if (empty($member)) {
throw new Exception(_("Not part of group."));
}
$result = $member->delete();
if (!$result) {
common_log_db_error($member, 'INSERT', __FILE__);
throw new Exception(_("Group leave failed."));
}
return true;
}
}

180
classes/Inbox.php Normal file
View File

@ -0,0 +1,180 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Data class for user location preferences
*
* 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 Data
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2009 StatusNet Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Inbox extends Memcached_DataObject
{
const BOXCAR = 128;
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'inbox'; // table name
public $user_id; // int(4) primary_key not_null
public $notice_ids; // blob
/* Static get */
function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Inbox',$k,$v); }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
function sequenceKey()
{
return array(false, false, false);
}
/**
* Create a new inbox from existing Notice_inbox stuff
*/
static function initialize($user_id)
{
$inbox = Inbox::fromNoticeInbox($user_id);
unset($inbox->fake);
$result = $inbox->insert();
if (!$result) {
common_log_db_error($inbox, 'INSERT', __FILE__);
return null;
}
return $inbox;
}
static function fromNoticeInbox($user_id)
{
$ids = array();
$ni = new Notice_inbox();
$ni->user_id = $user_id;
$ni->selectAdd();
$ni->selectAdd('notice_id');
$ni->orderBy('notice_id DESC');
$ni->limit(0, 1024);
if ($ni->find()) {
while($ni->fetch()) {
$ids[] = $ni->notice_id;
}
}
$ni->free();
unset($ni);
$inbox = new Inbox();
$inbox->user_id = $user_id;
$inbox->notice_ids = call_user_func_array('pack', array_merge(array('N*'), $ids));
$inbox->fake = true;
return $inbox;
}
static function insertNotice($user_id, $notice_id)
{
$inbox = DB_DataObject::staticGet('inbox', 'user_id', $user_id);
if (empty($inbox)) {
$inbox = Inbox::initialize($user_id);
}
if (empty($inbox)) {
return false;
}
$result = $inbox->query(sprintf('UPDATE inbox '.
'set notice_ids = concat(cast(0x%08x as binary(4)), '.
'substr(notice_ids, 1, 4092)) '.
'WHERE user_id = %d',
$notice_id, $user_id));
if ($result) {
$c = self::memcache();
if (!empty($c)) {
$c->delete(self::cacheKey('inbox', 'user_id', $user_id));
}
}
return $result;
}
static function bulkInsert($notice_id, $user_ids)
{
foreach ($user_ids as $user_id)
{
Inbox::insertNotice($user_id, $notice_id);
}
}
function stream($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false)
{
$inbox = Inbox::staticGet('user_id', $user_id);
if (empty($inbox)) {
$inbox = Inbox::fromNoticeInbox($user_id);
if (empty($inbox)) {
return array();
} else {
$inbox->encache();
}
}
$ids = unpack('N*', $inbox->notice_ids);
if (!empty($since_id)) {
$newids = array();
foreach ($ids as $id) {
if ($id > $since_id) {
$newids[] = $id;
}
}
$ids = $newids;
}
if (!empty($max_id)) {
$newids = array();
foreach ($ids as $id) {
if ($id <= $max_id) {
$newids[] = $id;
}
}
$ids = $newids;
}
$ids = array_slice($ids, $offset, $limit);
return $ids;
}
}

View File

@ -40,6 +40,8 @@ class Login_token extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
const TIMEOUT = 120; // seconds after which to timeout the token
/*
DB_DataObject calculates the sequence key(s) by taking the first key returned by the keys() function.
In this case, the keys() function returns user_id as the first key. user_id is not a sequence, but
@ -52,4 +54,29 @@ class Login_token extends Memcached_DataObject
{
return array(false,false);
}
function makeNew($user)
{
$login_token = Login_token::staticGet('user_id', $user->id);
if (!empty($login_token)) {
$login_token->delete();
}
$login_token = new Login_token();
$login_token->user_id = $user->id;
$login_token->token = common_good_rand(16);
$login_token->created = common_sql_now();
$result = $login_token->insert();
if (!$result) {
common_log_db_error($login_token, 'INSERT', __FILE__);
throw new Exception(sprintf(_('Could not create login token for %s'),
$user->nickname));
}
return $login_token;
}
}

View File

@ -19,6 +19,8 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Memcached_DataObject extends DB_DataObject
{
/**
@ -66,6 +68,7 @@ class Memcached_DataObject extends DB_DataObject
// Clear this out so we don't accidentally break global
// state in *this* process.
$this->_DB_resultid = null;
// We don't have any local DBO refs, so clear these out.
$this->_link_loaded = false;
}
@ -90,7 +93,9 @@ class Memcached_DataObject extends DB_DataObject
unset($i);
}
$i = Memcached_DataObject::getcached($cls, $k, $v);
if ($i === false) { // false == cache miss
if ($i) {
return $i;
} else {
$i = DB_DataObject::factory($cls);
if (empty($i)) {
$i = false;
@ -98,34 +103,22 @@ class Memcached_DataObject extends DB_DataObject
}
$result = $i->get($k, $v);
if ($result) {
// Hit!
$i->encache();
return $i;
} else {
// save the fact that no such row exists
$c = self::memcache();
if (!empty($c)) {
$ck = self::cachekey($cls, $k, $v);
$c->set($ck, null);
}
$i = false;
return $i;
}
}
return $i;
}
/**
* @fixme Should this return false on lookup fail to match staticGet?
*/
function pkeyGet($cls, $kv)
function &pkeyGet($cls, $kv)
{
$i = Memcached_DataObject::multicache($cls, $kv);
if ($i !== false) { // false == cache miss
if ($i) {
return $i;
} else {
$i = DB_DataObject::factory($cls);
if (empty($i)) {
return false;
}
$i = new $cls();
foreach ($kv as $k => $v) {
$i->$k = $v;
}
@ -133,11 +126,6 @@ class Memcached_DataObject extends DB_DataObject
$i->encache();
} else {
$i = null;
$c = self::memcache();
if (!empty($c)) {
$ck = self::multicacheKey($cls, $kv);
$c->set($ck, null);
}
}
return $i;
}
@ -146,9 +134,6 @@ class Memcached_DataObject extends DB_DataObject
function insert()
{
$result = parent::insert();
if ($result) {
$this->encache(); // in case of cached negative lookups
}
return $result;
}
@ -188,23 +173,21 @@ class Memcached_DataObject extends DB_DataObject
if (!$c) {
return false;
} else {
return $c->get(Memcached_DataObject::cacheKey($cls, $k, $v));
$obj = $c->get(Memcached_DataObject::cacheKey($cls, $k, $v));
if (0 == strcasecmp($cls, 'User')) {
// Special case for User
if (is_object($obj) && is_object($obj->id)) {
common_log(LOG_ERR, "User " . $obj->nickname . " was cached with User as ID; deleting");
$c->delete(Memcached_DataObject::cacheKey($cls, $k, $v));
return false;
}
}
return $obj;
}
}
function keyTypes()
{
// ini-based classes return number-indexed arrays. handbuilt
// classes return column => keytype. Make this uniform.
$keys = $this->keys();
$keyskeys = array_keys($keys);
if (is_string($keyskeys[0])) {
return $keys;
}
global $_DB_DATAOBJECT;
if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) {
$this->databaseStructure();
@ -216,90 +199,73 @@ class Memcached_DataObject extends DB_DataObject
function encache()
{
$c = $this->memcache();
if (!$c) {
return false;
}
$keys = $this->_allCacheKeys();
foreach ($keys as $key) {
$c->set($key, $this);
} else if ($this->tableName() == 'user' && is_object($this->id)) {
// Special case for User bug
$e = new Exception();
common_log(LOG_ERR, __METHOD__ . ' caching user with User object as ID ' .
str_replace("\n", " ", $e->getTraceAsString()));
return false;
} else {
$pkey = array();
$pval = array();
$types = $this->keyTypes();
ksort($types);
foreach ($types as $key => $type) {
if ($type == 'K') {
$pkey[] = $key;
$pval[] = $this->$key;
} else {
$c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this);
}
}
# XXX: should work for both compound and scalar pkeys
$pvals = implode(',', $pval);
$pkeys = implode(',', $pkey);
$c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this);
}
}
function decache()
{
$c = $this->memcache();
if (!$c) {
return false;
}
$keys = $this->_allCacheKeys();
foreach ($keys as $key) {
$c->delete($key, $this);
}
}
function _allCacheKeys()
{
$ckeys = array();
$types = $this->keyTypes();
ksort($types);
$pkey = array();
$pval = array();
foreach ($types as $key => $type) {
assert(!empty($key));
if ($type == 'U') {
if (empty($this->$key)) {
continue;
} else {
$pkey = array();
$pval = array();
$types = $this->keyTypes();
ksort($types);
foreach ($types as $key => $type) {
if ($type == 'K') {
$pkey[] = $key;
$pval[] = $this->$key;
} else {
$c->delete($this->cacheKey($this->tableName(), $key, $this->$key));
}
$ckeys[] = $this->cacheKey($this->tableName(), $key, $this->$key);
} else if ($type == 'K' || $type == 'N') {
$pkey[] = $key;
$pval[] = $this->$key;
} else {
throw new Exception("Unknown key type $key => $type for " . $this->tableName());
}
# should work for both compound and scalar pkeys
# XXX: comma works for now but may not be safe separator for future keys
$pvals = implode(',', $pval);
$pkeys = implode(',', $pkey);
$c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals));
}
assert(count($pkey) > 0);
// XXX: should work for both compound and scalar pkeys
$pvals = implode(',', $pval);
$pkeys = implode(',', $pkey);
$ckeys[] = $this->cacheKey($this->tableName(), $pkeys, $pvals);
return $ckeys;
}
function multicache($cls, $kv)
{
ksort($kv);
$c = self::memcache();
$c = Memcached_DataObject::memcache();
if (!$c) {
return false;
} else {
return $c->get(self::multicacheKey($cls, $kv));
$pkeys = implode(',', array_keys($kv));
$pvals = implode(',', array_values($kv));
return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals));
}
}
static function multicacheKey($cls, $kv)
{
ksort($kv);
$pkeys = implode(',', array_keys($kv));
$pvals = implode(',', array_values($kv));
return self::cacheKey($cls, $pkeys, $pvals);
}
function getSearchEngine($table)
{
require_once INSTALLDIR.'/lib/search_engines.php';
@ -334,8 +300,7 @@ class Memcached_DataObject extends DB_DataObject
$key_part = common_keyize($cls).':'.md5($qry);
$ckey = common_cache_key($key_part);
$stored = $c->get($ckey);
if ($stored !== false) {
if ($stored) {
return new ArrayWrapper($stored);
}
@ -366,6 +331,29 @@ class Memcached_DataObject extends DB_DataObject
$exists = false;
}
// @fixme horrible evil hack!
//
// In multisite configuration we don't want to keep around a separate
// connection for every database; we could end up with thousands of
// connections open per thread. In an ideal world we might keep
// a connection per server and select different databases, but that'd
// be reliant on having the same db username/pass as well.
//
// MySQL connections are cheap enough we're going to try just
// closing out the old connection and reopening when we encounter
// a new DSN.
//
// WARNING WARNING if we end up actually using multiple DBs at a time
// we'll need some fancier logic here.
if (!$exists && !empty($_DB_DATAOBJECT['CONNECTIONS'])) {
foreach ($_DB_DATAOBJECT['CONNECTIONS'] as $index => $conn) {
if (!empty($conn)) {
$conn->disconnect();
}
unset($_DB_DATAOBJECT['CONNECTIONS'][$index]);
}
}
$result = parent::_connect();
if ($result && !$exists) {

View File

@ -125,8 +125,7 @@ class Notice extends Memcached_DataObject
'Fave',
'Notice_tag',
'Group_inbox',
'Queue_item',
'Notice_inbox');
'Queue_item');
foreach ($related as $cls) {
$inst = new $cls();
@ -276,7 +275,6 @@ class Notice extends Memcached_DataObject
if (isset($repeat_of)) {
$notice->repeat_of = $repeat_of;
$notice->reply_to = $repeat_of;
} else {
$notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final);
}
@ -300,8 +298,6 @@ class Notice extends Memcached_DataObject
// XXX: some of these functions write to the DB
$notice->query('BEGIN');
$id = $notice->insert();
if (!$id) {
@ -339,12 +335,14 @@ class Notice extends Memcached_DataObject
$notice->saveTags();
$notice->addToInboxes();
$groups = $notice->saveGroups();
$recipients = $notice->saveReplies();
$notice->addToInboxes($groups, $recipients);
$notice->saveUrls();
$notice->query('COMMIT');
Event::handle('EndNoticeSave', array($notice));
}
@ -503,20 +501,6 @@ class Notice extends Memcached_DataObject
$original->free();
unset($original);
}
$ni = new Notice_inbox();
$ni->notice_id = $this->id;
if ($ni->find()) {
while ($ni->fetch()) {
$tmk = common_cache_key('user:repeated_to_me:'.$ni->user_id);
$cache->delete($tmk);
}
}
$ni->free();
unset($ni);
}
}
}
@ -842,11 +826,28 @@ class Notice extends Memcached_DataObject
return $ids;
}
function addToInboxes()
/**
* @param $groups array of Group *objects*
* @param $recipients array of profile *ids*
*/
function whoGets($groups=null, $recipients=null)
{
// XXX: loads constants
$c = self::memcache();
$inbox = new Notice_inbox();
if (!empty($c)) {
$ni = $c->get(common_cache_key('notice:who_gets:'.$this->id));
if ($ni !== false) {
return $ni;
}
}
if (is_null($groups)) {
$groups = $this->getGroups();
}
if (is_null($recipients)) {
$recipients = $this->getReplies();
}
$users = $this->getSubscribedUsers();
@ -860,7 +861,6 @@ class Notice extends Memcached_DataObject
$ni[$id] = NOTICE_INBOX_SOURCE_SUB;
}
$groups = $this->saveGroups();
$profile = $this->getProfile();
foreach ($groups as $group) {
@ -875,8 +875,6 @@ class Notice extends Memcached_DataObject
}
}
$recipients = $this->saveReplies();
foreach ($recipients as $recipient) {
if (!array_key_exists($recipient, $ni)) {
@ -887,7 +885,19 @@ class Notice extends Memcached_DataObject
}
}
Notice_inbox::bulkInsert($this->id, $this->created, $ni);
if (!empty($c)) {
// XXX: pack this data better
$c->set(common_cache_key('notice:who_gets:'.$this->id), $ni);
}
return $ni;
}
function addToInboxes($groups, $recipients)
{
$ni = $this->whoGets($groups, $recipients);
Inbox::bulkInsert($this->id, array_keys($ni));
return;
}
@ -919,8 +929,17 @@ class Notice extends Memcached_DataObject
return $ids;
}
/**
* @return array of Group objects
*/
function saveGroups()
{
// Don't save groups for repeats
if (!empty($this->repeat_of)) {
return array();
}
$groups = array();
/* extract all !group */
@ -991,6 +1010,12 @@ class Notice extends Memcached_DataObject
*/
function saveReplies()
{
// Don't save reply data for repeats
if (!empty($this->repeat_of)) {
return array();
}
// Alternative reply format
$tname = false;
if (preg_match('/^T ([A-Z0-9]{1,64}) /', $this->content, $match)) {
@ -1077,6 +1102,63 @@ class Notice extends Memcached_DataObject
return $recipientIds;
}
function getReplies()
{
// XXX: cache me
$ids = array();
$reply = new Reply();
$reply->selectAdd();
$reply->selectAdd('profile_id');
$reply->notice_id = $this->id;
if ($reply->find()) {
while($reply->fetch()) {
$ids[] = $reply->profile_id;
}
}
$reply->free();
return $ids;
}
/**
* Same calculation as saveGroups but without the saving
* @fixme merge the functions
* @return array of Group objects
*/
function getGroups()
{
// Don't save groups for repeats
if (!empty($this->repeat_of)) {
return array();
}
// XXX: cache me
$groups = array();
$gi = new Group_inbox();
$gi->selectAdd();
$gi->selectAdd('group_id');
$gi->notice_id = $this->id;
if ($gi->find()) {
while ($gi->fetch()) {
$groups[] = clone($gi);
}
}
$gi->free();
return $groups;
}
function asAtomEntry($namespace=false, $source=false)
{
$profile = $this->getProfile();

View File

@ -1,7 +1,7 @@
<?php
/*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
* Copyright (C) 2008-2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -17,7 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
@ -29,12 +31,6 @@ define('NOTICE_INBOX_GC_MAX', 12800);
define('NOTICE_INBOX_LIMIT', 1000);
define('NOTICE_INBOX_SOFT_LIMIT', 1000);
define('NOTICE_INBOX_SOURCE_SUB', 1);
define('NOTICE_INBOX_SOURCE_GROUP', 2);
define('NOTICE_INBOX_SOURCE_REPLY', 3);
define('NOTICE_INBOX_SOURCE_FORWARD', 4);
define('NOTICE_INBOX_SOURCE_GATEWAY', -1);
class Notice_inbox extends Memcached_DataObject
{
###START_AUTOCODE
@ -55,139 +51,31 @@ class Notice_inbox extends Memcached_DataObject
function stream($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false)
{
return Notice::stream(array('Notice_inbox', '_streamDirect'),
array($user_id, $own),
($own) ? 'notice_inbox:by_user:'.$user_id :
'notice_inbox:by_user_own:'.$user_id,
$offset, $limit, $since_id, $max_id, $since);
throw new Exception('Notice_inbox no longer used; use Inbox');
}
function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id, $since)
{
$inbox = new Notice_inbox();
$inbox->user_id = $user_id;
if (!$own) {
$inbox->whereAdd('source != ' . NOTICE_INBOX_SOURCE_GATEWAY);
}
if ($since_id != 0) {
$inbox->whereAdd('notice_id > ' . $since_id);
}
if ($max_id != 0) {
$inbox->whereAdd('notice_id <= ' . $max_id);
}
if (!is_null($since)) {
$inbox->whereAdd('created > \'' . date('Y-m-d H:i:s', $since) . '\'');
}
$inbox->orderBy('created DESC');
if (!is_null($offset)) {
$inbox->limit($offset, $limit);
}
$ids = array();
if ($inbox->find()) {
while ($inbox->fetch()) {
$ids[] = $inbox->notice_id;
}
}
return $ids;
throw new Exception('Notice_inbox no longer used; use Inbox');
}
function pkeyGet($kv)
function &pkeyGet($kv)
{
return Memcached_DataObject::pkeyGet('Notice_inbox', $kv);
}
/**
* Trim inbox for a given user to latest NOTICE_INBOX_LIMIT items
* (up to NOTICE_INBOX_GC_MAX will be deleted).
*
* @param int $user_id
* @return int count of notices dropped from the inbox, if any
*/
static function gc($user_id)
{
$entry = new Notice_inbox();
$entry->user_id = $user_id;
$entry->orderBy('created DESC');
$entry->limit(NOTICE_INBOX_LIMIT - 1, NOTICE_INBOX_GC_MAX);
$total = $entry->find();
if ($total > 0) {
$notices = array();
$cnt = 0;
while ($entry->fetch()) {
$notices[] = $entry->notice_id;
$cnt++;
if ($cnt >= NOTICE_INBOX_GC_BOXCAR) {
self::deleteMatching($user_id, $notices);
$notices = array();
$cnt = 0;
}
}
if ($cnt > 0) {
self::deleteMatching($user_id, $notices);
$notices = array();
}
}
return $total;
throw new Exception('Notice_inbox no longer used; use Inbox');
}
static function deleteMatching($user_id, $notices)
{
$entry = new Notice_inbox();
return $entry->query('DELETE FROM notice_inbox '.
'WHERE user_id = ' . $user_id . ' ' .
'AND notice_id in ('.implode(',', $notices).')');
throw new Exception('Notice_inbox no longer used; use Inbox');
}
static function bulkInsert($notice_id, $created, $ni)
{
$cnt = 0;
$qryhdr = 'INSERT INTO notice_inbox (user_id, notice_id, source, created) VALUES ';
$qry = $qryhdr;
foreach ($ni as $id => $source) {
if ($cnt > 0) {
$qry .= ', ';
}
$qry .= '('.$id.', '.$notice_id.', '.$source.", '".$created. "') ";
$cnt++;
if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) {
// FIXME: Causes lag in replicated servers
// Notice_inbox::gc($id);
}
if ($cnt >= MAX_BOXCARS) {
$inbox = new Notice_inbox();
$result = $inbox->query($qry);
if (PEAR::isError($result)) {
common_log_db_error($inbox, $qry);
}
$qry = $qryhdr;
$cnt = 0;
}
}
if ($cnt > 0) {
$inbox = new Notice_inbox();
$result = $inbox->query($qry);
if (PEAR::isError($result)) {
common_log_db_error($inbox, $qry);
}
}
return;
throw new Exception('Notice_inbox no longer used; use Inbox');
}
}

View File

@ -0,0 +1,140 @@
<?php
/**
* Table Definition for oauth_application
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Oauth_application extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'oauth_application'; // table name
public $id; // int(4) primary_key not_null
public $owner; // int(4) not_null
public $consumer_key; // varchar(255) not_null
public $name; // varchar(255) not_null
public $description; // varchar(255)
public $icon; // varchar(255) not_null
public $source_url; // varchar(255)
public $organization; // varchar(255)
public $homepage; // varchar(255)
public $callback_url; // varchar(255) not_null
public $type; // tinyint(1)
public $access_type; // tinyint(1)
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
/* Static get */
function staticGet($k,$v=NULL) {
return Memcached_DataObject::staticGet('Oauth_application',$k,$v);
}
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
// Bit flags
public static $readAccess = 1;
public static $writeAccess = 2;
public static $browser = 1;
public static $desktop = 2;
function getConsumer()
{
return Consumer::staticGet('consumer_key', $this->consumer_key);
}
static function maxDesc()
{
$desclimit = common_config('application', 'desclimit');
// null => use global limit (distinct from 0!)
if (is_null($desclimit)) {
$desclimit = common_config('site', 'textlimit');
}
return $desclimit;
}
static function descriptionTooLong($desc)
{
$desclimit = self::maxDesc();
return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit));
}
function setAccessFlags($read, $write)
{
if ($read) {
$this->access_type |= self::$readAccess;
} else {
$this->access_type &= ~self::$readAccess;
}
if ($write) {
$this->access_type |= self::$writeAccess;
} else {
$this->access_type &= ~self::$writeAccess;
}
}
function setOriginal($filename)
{
$imagefile = new ImageFile($this->id, Avatar::path($filename));
// XXX: Do we want to have a bunch of different size icons? homepage, stream, mini?
// or just one and control size via CSS? --Zach
$orig = clone($this);
$this->icon = Avatar::url($filename);
common_debug(common_log_objstring($this));
return $this->update($orig);
}
static function getByConsumerKey($key)
{
if (empty($key)) {
return null;
}
$app = new Oauth_application();
$app->consumer_key = $key;
$app->limit(1);
$result = $app->find(true);
return empty($result) ? null : $app;
}
/**
* Handle an image upload
*
* Does all the magic for handling an image upload, and crops the
* image by default.
*
* @return void
*/
function uploadLogo()
{
if ($_FILES['app_icon']['error'] ==
UPLOAD_ERR_OK) {
try {
$imagefile = ImageFile::fromUpload('app_icon');
} catch (Exception $e) {
common_debug("damn that sucks");
$this->showForm($e->getMessage());
return;
}
$filename = Avatar::filename($this->id,
image_type_to_extension($imagefile->type),
null,
'oauth-app-icon-'.common_timestamp());
$filepath = Avatar::path($filename);
move_uploaded_file($imagefile->filepath, $filepath);
$this->setOriginal($filename);
}
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* Table Definition for oauth_application_user
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Oauth_application_user extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'oauth_application_user'; // table name
public $profile_id; // int(4) primary_key not_null
public $application_id; // int(4) primary_key not_null
public $access_type; // tinyint(1)
public $token; // varchar(255)
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
/* Static get */
function staticGet($k,$v=NULL) {
return Memcached_DataObject::staticGet('Oauth_application_user',$k,$v);
}
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
static function getByKeys($user, $app)
{
if (empty($user) || empty($app)) {
return null;
}
$oau = new Oauth_application_user();
$oau->profile_id = $user->id;
$oau->application_id = $app->id;
$oau->limit(1);
$result = $oau->find(true);
return empty($result) ? null : $oau;
}
}

View File

@ -352,6 +352,31 @@ class Profile extends Memcached_DataObject
return $profile;
}
function getApplications($offset = 0, $limit = null)
{
$qry =
'SELECT a.* ' .
'FROM oauth_application_user u, oauth_application a ' .
'WHERE u.profile_id = %d ' .
'AND a.id = u.application_id ' .
'AND u.access_type > 0 ' .
'ORDER BY u.created DESC ';
if ($offset > 0) {
if (common_config('db','type') == 'pgsql') {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
} else {
$qry .= ' LIMIT ' . $offset . ', ' . $limit;
}
}
$application = new Oauth_application();
$cnt = $application->query(sprintf($qry, $this->id));
return $application;
}
function subscriptionCount()
{
$c = common_memcache();

View File

@ -25,10 +25,12 @@ class Queue_item extends Memcached_DataObject
function sequenceKey()
{ return array(false, false); }
static function top($transport) {
static function top($transport=null) {
$qi = new Queue_item();
$qi->transport = $transport;
if ($transport) {
$qi->transport = $transport;
}
$qi->orderBy('created');
$qi->whereAdd('claimed is null');
@ -40,7 +42,8 @@ class Queue_item extends Memcached_DataObject
# XXX: potential race condition
# can we force it to only update if claimed is still null
# (or old)?
common_log(LOG_INFO, 'claiming queue item = ' . $qi->notice_id . ' for transport ' . $transport);
common_log(LOG_INFO, 'claiming queue item = ' . $qi->notice_id .
' for transport ' . $qi->transport);
$orig = clone($qi);
$qi->claimed = common_sql_now();
$result = $qi->update($orig);

View File

@ -49,6 +49,13 @@ class Status_network extends DB_DataObject
static $cache = null;
static $base = null;
/**
* @param string $dbhost
* @param string $dbuser
* @param string $dbpass
* @param string $dbname
* @param array $servers memcached servers to use for caching config info
*/
static function setupDB($dbhost, $dbuser, $dbpass, $dbname, $servers)
{
global $config;
@ -60,12 +67,17 @@ class Status_network extends DB_DataObject
if (class_exists('Memcache')) {
self::$cache = new Memcache();
// Can't close persistent connections, making forking painful.
//
// @fixme only do this in *parent* CLI processes.
// single-process and child-processes *should* use persistent.
$persist = php_sapi_name() != 'cli';
if (is_array($servers)) {
foreach($servers as $server) {
self::$cache->addServer($server);
self::$cache->addServer($server, 11211, $persist);
}
} else {
self::$cache->addServer($servers);
self::$cache->addServer($servers, 11211, $persist);
}
}
@ -89,7 +101,7 @@ class Status_network extends DB_DataObject
if (empty($sn)) {
$sn = self::staticGet($k, $v);
if (!empty($sn)) {
self::$cache->set($ck, $sn);
self::$cache->set($ck, clone($sn));
}
}
@ -121,6 +133,11 @@ class Status_network extends DB_DataObject
return parent::delete();
}
/**
* @param string $servername hostname
* @param string $pathname URL base path
* @param string $wildcard hostname suffix to match wildcard config
*/
static function setupSite($servername, $pathname, $wildcard)
{
global $config;
@ -150,9 +167,19 @@ class Status_network extends DB_DataObject
}
if (!empty($sn)) {
if (!empty($sn->hostname) && 0 != strcasecmp($sn->hostname, $servername)) {
$sn->redirectToHostname();
// Redirect to the right URL
if (!empty($sn->hostname) &&
empty($_SERVER['HTTPS']) &&
0 != strcasecmp($sn->hostname, $servername)) {
$sn->redirectTo('http://'.$sn->hostname.$_SERVER['REQUEST_URI']);
} else if (!empty($_SERVER['HTTPS']) &&
0 != strcasecmp($sn->hostname, $servername) &&
0 != strcasecmp($sn->nickname.'.'.$wildcard, $servername)) {
$sn->redirectTo('https://'.$sn->nickname.'.'.$wildcard.$_SERVER['REQUEST_URI']);
}
$dbhost = (empty($sn->dbhost)) ? 'localhost' : $sn->dbhost;
$dbuser = (empty($sn->dbuser)) ? $sn->nickname : $sn->dbuser;
$dbpass = $sn->dbpass;
@ -160,7 +187,11 @@ class Status_network extends DB_DataObject
$config['db']['database'] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname";
$config['site']['name'] = $sn->sitename;
$config['site']['name'] = $sn->sitename;
if (!empty($sn->hostname)) {
$config['site']['server'] = $sn->hostname;
}
if (!empty($sn->theme)) {
$config['site']['theme'] = $sn->theme;
@ -179,11 +210,8 @@ class Status_network extends DB_DataObject
// (C) 2006 by Heiko Richler http://www.richler.de/
// LGPL
function redirectToHostname()
function redirectTo($destination)
{
$destination = 'http://'.$this->hostname;
$destination .= $_SERVER['REQUEST_URI'];
$old = 'http'.
(($_SERVER['HTTPS'] == 'on') ? 'S' : '').
'://'.

View File

@ -4,7 +4,7 @@
*/
require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
class Token extends Memcached_DataObject
class Token extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
@ -14,7 +14,9 @@ class Token extends Memcached_DataObject
public $tok; // char(32) primary_key not_null
public $secret; // char(32) not_null
public $type; // tinyint(1) not_null
public $state; // tinyint(1)
public $state; // tinyint(1)
public $verifier; // varchar(255)
public $verified_callback; // varchar(255)
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP

View File

@ -291,6 +291,20 @@ class User extends Memcached_DataObject
return false;
}
// Everyone gets an inbox
$inbox = new Inbox();
$inbox->user_id = $user->id;
$inbox->notice_ids = '';
$result = $inbox->insert();
if (!$result) {
common_log_db_error($inbox, 'INSERT', __FILE__);
return false;
}
// Everyone is subscribed to themself
$subscription = new Subscription();
@ -482,89 +496,30 @@ class User extends Memcached_DataObject
function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
{
$ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false);
$ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false);
return Notice::getStreamByIds($ids);
}
function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
{
$ids = Notice_inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true);
$ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true);
return Notice::getStreamByIds($ids);
}
function friendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
{
$ids = Notice::stream(array($this, '_friendsTimelineDirect'),
array(false),
'user:friends_timeline:'.$this->id,
$offset, $limit, $since_id, $before_id, $since);
$ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false);
return Notice::getStreamByIds($ids);
}
function ownFriendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
{
$ids = Notice::stream(array($this, '_friendsTimelineDirect'),
array(true),
'user:friends_timeline_own:'.$this->id,
$offset, $limit, $since_id, $before_id, $since);
$ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true);
return Notice::getStreamByIds($ids);
}
function _friendsTimelineDirect($own, $offset, $limit, $since_id, $max_id, $since)
{
$qry =
'SELECT notice.id AS id ' .
'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' .
'WHERE notice_inbox.user_id = ' . $this->id . ' ' .
'AND notice.repeat_of IS NULL ';
if (!$own) {
// XXX: autoload notice inbox for constant
$inbox = new Notice_inbox();
$qry .= 'AND notice_inbox.source != ' . NOTICE_INBOX_SOURCE_GATEWAY . ' ';
}
if ($since_id != 0) {
$qry .= 'AND notice.id > ' . $since_id . ' ';
}
if ($max_id != 0) {
$qry .= 'AND notice.id <= ' . $max_id . ' ';
}
if (!is_null($since)) {
$qry .= 'AND notice.modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
}
// NOTE: we sort by fave time, not by notice time!
$qry .= 'ORDER BY notice_id DESC ';
if (!is_null($offset)) {
$qry .= "LIMIT $limit OFFSET $offset";
}
$ids = array();
$notice = new Notice();
$notice->query($qry);
while ($notice->fetch()) {
$ids[] = $notice->id;
}
$notice->free();
$notice = NULL;
return $ids;
}
function blowFavesCache()
{
$cache = common_memcache();
@ -777,7 +732,6 @@ class User extends Memcached_DataObject
'Remember_me',
'Foreign_link',
'Invitation',
'Notice_inbox',
);
Event::handle('UserDeleteRelated', array($this, &$related));
@ -945,56 +899,7 @@ class User extends Memcached_DataObject
function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
$ids = Notice::stream(array($this, '_repeatedToMeDirect'),
array(),
'user:repeated_to_me:'.$this->id,
$offset, $limit, $since_id, $max_id, null);
return Notice::getStreamByIds($ids);
}
function _repeatedToMeDirect($offset, $limit, $since_id, $max_id, $since)
{
$qry =
'SELECT notice.id AS id ' .
'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' .
'WHERE notice_inbox.user_id = ' . $this->id . ' ' .
'AND notice.repeat_of IS NOT NULL ';
if ($since_id != 0) {
$qry .= 'AND notice.id > ' . $since_id . ' ';
}
if ($max_id != 0) {
$qry .= 'AND notice.id <= ' . $max_id . ' ';
}
if (!is_null($since)) {
$qry .= 'AND notice.modified > \'' . date('Y-m-d H:i:s', $since) . '\' ';
}
// NOTE: we sort by fave time, not by notice time!
$qry .= 'ORDER BY notice.id DESC ';
if (!is_null($offset)) {
$qry .= "LIMIT $limit OFFSET $offset";
}
$ids = array();
$notice = new Notice();
$notice->query($qry);
while ($notice->fetch()) {
$ids[] = $notice->id;
}
$notice->free();
$notice = NULL;
return $ids;
throw new Exception("Not implemented since inbox change.");
}
function shareLocation()

View File

@ -39,6 +39,7 @@ code = K
[consumer]
consumer_key = 130
consumer_secret = 130
seed = 130
created = 142
modified = 384
@ -241,6 +242,13 @@ address = 130
address_type = 130
created = 142
[inbox]
user_id = 129
notice_ids = 66
[inbox__keys]
user_id = K
[invitation__keys]
code = K
@ -341,6 +349,37 @@ created = 142
tag = K
notice_id = K
[oauth_application]
id = 129
owner = 129
consumer_key = 130
name = 130
description = 2
icon = 130
source_url = 2
organization = 2
homepage = 2
callback_url = 130
type = 17
access_type = 17
created = 142
modified = 384
[oauth_application__keys]
id = N
[oauth_application_user]
profile_id = 129
application_id = 129
access_type = 17
token = 2
created = 142
modified = 384
[oauth_application_user__keys]
profile_id = K
application_id = K
[profile]
id = 129
nickname = 130
@ -477,6 +516,8 @@ tok = 130
secret = 130
type = 145
state = 17
verifier = 2
verified_callback = 2
created = 142
modified = 384

17
db/rc2torc3.sql Normal file
View File

@ -0,0 +1,17 @@
create table user_location_prefs (
user_id integer not null comment 'user who has the preference' references user (id),
share_location tinyint default 1 comment 'Whether to share location data',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
constraint primary key (user_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table inbox (
user_id integer not null comment 'user receiving the notice' references user (id),
notice_ids blob comment 'packed list of notice ids',
constraint primary key (user_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;

View File

@ -176,6 +176,7 @@ create table fave (
create table consumer (
consumer_key varchar(255) primary key comment 'unique identifier, root URL',
consumer_secret varchar(255) not null comment 'secret value',
seed char(32) not null comment 'seed for new tokens by this consumer',
created datetime not null comment 'date this record was created',
@ -188,6 +189,8 @@ create table token (
secret char(32) not null comment 'secret value',
type tinyint not null default 0 comment 'request or access',
state tinyint default 0 comment 'for requests, 0 = initial, 1 = authorized, 2 = used',
verifier varchar(255) comment 'verifier string for OAuth 1.0a',
verified_callback varchar(255) comment 'verified callback URL for OAuth 1.0a',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
@ -207,6 +210,33 @@ create table nonce (
constraint primary key (consumer_key, ts, nonce)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table oauth_application (
id integer auto_increment primary key comment 'unique identifier',
owner integer not null comment 'owner of the application' references profile (id),
consumer_key varchar(255) not null comment 'application consumer key' references consumer (consumer_key),
name varchar(255) not null comment 'name of the application',
description varchar(255) comment 'description of the application',
icon varchar(255) not null comment 'application icon',
source_url varchar(255) comment 'application homepage - used for source link',
organization varchar(255) comment 'name of the organization running the application',
homepage varchar(255) comment 'homepage for the organization',
callback_url varchar(255) comment 'url to redirect to after authentication',
type tinyint default 0 comment 'type of app, 1 = browser, 2 = desktop',
access_type tinyint default 0 comment 'default access type, bit 1 = read, bit 2 = write',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified'
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table oauth_application_user (
profile_id integer not null comment 'user of the application' references profile (id),
application_id integer not null comment 'id of the application' references oauth_application (id),
access_type tinyint default 0 comment 'access type, bit 1 = read, bit 2 = write, bit 3 = revoked',
token varchar(255) comment 'request or access token',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
constraint primary key (profile_id, application_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
/* These are used by JanRain OpenID library */
create table oid_associations (
@ -596,3 +626,11 @@ create table user_location_prefs (
constraint primary key (user_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table inbox (
user_id integer not null comment 'user receiving the notice' references user (id),
notice_ids blob comment 'packed list of notice ids',
constraint primary key (user_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;

View File

@ -29,7 +29,7 @@
* @author Robin Millette <millette@controlyourself.ca>
* @author Sarven Capadisli <csarven@controlyourself.ca>
* @author Tom Adams <tom@holizz.com>
*
*
* @license GNU Affero General Public License http://www.gnu.org/licenses/
*/

View File

@ -1,5 +1,5 @@
// A shim to implement the W3C Geolocation API Specification using Gears
if (typeof navigator.geolocation == "undefined" || navigator.geolocation.shim ) (function(){
// A shim to implement the W3C Geolocation API Specification using Gears or the Ajax API
if (typeof navigator.geolocation == "undefined" || navigator.geolocation.shim ) { (function(){
// -- BEGIN GEARS_INIT
(function() {
@ -23,8 +23,7 @@ if (typeof navigator.geolocation == "undefined" || navigator.geolocation.shim )
}
} catch (e) {
// Safari
if ((typeof navigator.mimeTypes != 'undefined')
&& navigator.mimeTypes["application/x-googlegears"]) {
if ((typeof navigator.mimeTypes != 'undefined') && navigator.mimeTypes["application/x-googlegears"]) {
factory = document.createElement("object");
factory.style.display = "none";
factory.width = 0;
@ -64,8 +63,8 @@ var GearsGeoLocation = (function() {
return function(position) {
callback(position);
self.lastPosition = position;
}
}
};
};
// -- PUBLIC
return {
@ -96,9 +95,123 @@ var GearsGeoLocation = (function() {
};
});
// If you have Gears installed use that
if (window.google && google.gears) {
navigator.geolocation = GearsGeoLocation();
}
var AjaxGeoLocation = (function() {
// -- PRIVATE
var loading = false;
var loadGoogleLoader = function() {
if (!hasGoogleLoader() && !loading) {
loading = true;
var s = document.createElement('script');
s.src = (document.location.protocol == "https:"?"https://":"http://") + 'www.google.com/jsapi?callback=_google_loader_apiLoaded';
s.type = "text/javascript";
document.getElementsByTagName('body')[0].appendChild(s);
}
};
var queue = [];
var addLocationQueue = function(callback) {
queue.push(callback);
};
var runLocationQueue = function() {
if (hasGoogleLoader()) {
while (queue.length > 0) {
var call = queue.pop();
call();
}
}
};
window['_google_loader_apiLoaded'] = function() {
runLocationQueue();
};
var hasGoogleLoader = function() {
return (window['google'] && google['loader']);
};
var checkGoogleLoader = function(callback) {
if (hasGoogleLoader()) { return true; }
addLocationQueue(callback);
loadGoogleLoader();
return false;
};
loadGoogleLoader(); // start to load as soon as possible just in case
// -- PUBLIC
return {
shim: true,
type: "ClientLocation",
lastPosition: null,
getCurrentPosition: function(successCallback, errorCallback, options) {
var self = this;
if (!checkGoogleLoader(function() {
self.getCurrentPosition(successCallback, errorCallback, options);
})) { return; }
if (google.loader.ClientLocation) {
var cl = google.loader.ClientLocation;
var position = {
coords: {
latitude: cl.latitude,
longitude: cl.longitude,
altitude: null,
accuracy: 43000, // same as Gears accuracy over wifi?
altitudeAccuracy: null,
heading: null,
speed: null
},
// extra info that is outside of the bounds of the core API
address: {
city: cl.address.city,
country: cl.address.country,
country_code: cl.address.country_code,
region: cl.address.region
},
timestamp: new Date()
};
successCallback(position);
this.lastPosition = position;
} else if (errorCallback === "function") {
errorCallback({ code: 3, message: "Using the Google ClientLocation API and it is not able to calculate a location."});
}
},
watchPosition: function(successCallback, errorCallback, options) {
this.getCurrentPosition(successCallback, errorCallback, options);
var self = this;
var watchId = setInterval(function() {
self.getCurrentPosition(successCallback, errorCallback, options);
}, 10000);
return watchId;
},
clearWatch: function(watchId) {
clearInterval(watchId);
},
getPermission: function(siteName, imageUrl, extraMessage) {
// for now just say yes :)
return true;
}
};
});
// If you have Gears installed use that, else use Ajax ClientLocation
navigator.geolocation = (window.google && google.gears) ? GearsGeoLocation() : AjaxGeoLocation();
})();
}

View File

@ -289,6 +289,7 @@ var SN = { // StatusNet
}
}
$('#'+form_id).resetForm();
$('#'+form_id+' #'+SN.C.S.NoticeInReplyTo).val('');
$('#'+form_id+' #'+SN.C.S.NoticeDataAttachSelected).remove();
SN.U.FormNoticeEnhancements($('#'+form_id));
}
@ -480,8 +481,9 @@ var SN = { // StatusNet
var NDGe = $('#'+SN.C.S.NoticeDataGeo);
function removeNoticeDataGeo() {
$('label[for='+SN.C.S.NoticeDataGeo+']').removeClass('checked').attr('title', jQuery.trim($('label[for='+SN.C.S.NoticeDataGeo+']').text()));
$('#'+SN.C.S.NoticeDataGeoSelected).hide();
$('label[for='+SN.C.S.NoticeDataGeo+']')
.attr('title', jQuery.trim($('label[for='+SN.C.S.NoticeDataGeo+']').text()))
.removeClass('checked');
$('#'+SN.C.S.NoticeLat).val('');
$('#'+SN.C.S.NoticeLon).val('');
@ -492,7 +494,7 @@ var SN = { // StatusNet
$.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled');
}
function getJSONgeocodeURL(geocodeURL, data) {
function getJSONgeocodeURL(geocodeURL, data, position) {
$.getJSON(geocodeURL, data, function(location) {
var lns, lid;
@ -513,17 +515,8 @@ var SN = { // StatusNet
NLN_text = location.name;
}
$('#'+SN.C.S.NoticeGeoName)
.replaceWith('<a id="notice_data-geo_name"/>');
$('#'+SN.C.S.NoticeGeoName)
.attr('href', location.url)
.text(NLN_text)
.click(function() {
window.open(location.url);
return false;
});
$('label[for='+SN.C.S.NoticeDataGeo+']')
.attr('title', NoticeDataGeo_text.ShareDisable + ' (' + NLN_text + ')');
$('#'+SN.C.S.NoticeLat).val(data.lat);
$('#'+SN.C.S.NoticeLon).val(data.lon);
@ -532,14 +525,13 @@ var SN = { // StatusNet
$('#'+SN.C.S.NoticeDataGeo).attr('checked', true);
var cookieValue = {
'NLat': data.lat,
'NLon': data.lon,
'NLNS': lns,
'NLID': lid,
'NLN': NLN_text,
'NLNU': location.url,
'NDG': true,
'NDGSM': false
NLat: data.lat,
NLon: data.lon,
NLNS: lns,
NLID: lid,
NLN: NLN_text,
NLNU: location.url,
NDG: true
};
$.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue));
});
@ -557,62 +549,14 @@ var SN = { // StatusNet
var geocodeURL = NGW.attr('title');
NGW.removeAttr('title');
$('label[for='+SN.C.S.NoticeDataGeo+']').attr('title', jQuery.trim($('label[for='+SN.C.S.NoticeDataGeo+']').text()));
$('label[for='+SN.C.S.NoticeDataGeo+']')
.attr('title', jQuery.trim($('label[for='+SN.C.S.NoticeDataGeo+']').text()));
NDGe.change(function() {
var NLN = $('#'+SN.C.S.NoticeGeoName);
if (NLN.length > 0) {
NLN.remove();
}
if ($('#'+SN.C.S.NoticeDataGeo).attr('checked') === true || $.cookie(SN.C.S.NoticeDataGeoCookie) === null) {
$('label[for='+SN.C.S.NoticeDataGeo+']').addClass('checked').attr('title', NoticeDataGeoShareDisable_text);
var S = '<div id="'+SN.C.S.NoticeDataGeoSelected+'" class="'+SN.C.S.Success+'"/>';
var NDGS = $('#'+SN.C.S.NoticeDataGeoSelected);
if (NDGS.length > 0) {
NDGS.replaceWith(S);
}
else {
$('#'+SN.C.S.FormNotice).append(S);
}
NDGS = $('#'+SN.C.S.NoticeDataGeoSelected);
NDGS.prepend('<span id="'+SN.C.S.NoticeGeoName+'">Geo</span> <button class="minimize" title="'+NoticeDataGeoInfoMinimize_text+'">&#95;</button> <button class="close" title="'+NoticeDataGeoShareDisable_text+'">&#215;</button>');
var NLN = $('#'+SN.C.S.NoticeGeoName);
NLN.addClass('processing');
$('#'+SN.C.S.NoticeDataGeoSelected+' button.close').click(function(){
removeNoticeDataGeo();
$('#'+SN.C.S.NoticeDataGeoSelected).remove();
$('#'+SN.C.S.NoticeDataText).focus();
return false;
});
$('#'+SN.C.S.NoticeDataGeoSelected+' button.minimize').click(function(){
$('#'+SN.C.S.NoticeDataGeoSelected).hide();
var cookieValue = {
'NLat': $('#'+SN.C.S.NoticeLat).val(),
'NLon': $('#'+SN.C.S.NoticeLat).val(),
'NLNS': $('#'+SN.C.S.NoticeLocationNs).val(),
'NLID': $('#'+SN.C.S.NoticeLocationId).val(),
'NLN': $('#'+SN.C.S.NoticeGeoName).text(),
'NLNU': $('#'+SN.C.S.NoticeGeoName).attr('href'),
'NDG': true,
'NDGSM': true
};
$.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue));
$('#'+SN.C.S.NoticeDataText).focus();
return false;
});
$('label[for='+SN.C.S.NoticeDataGeo+']')
.attr('title', NoticeDataGeo_text.ShareDisable)
.addClass('checked');
if ($.cookie(SN.C.S.NoticeDataGeoCookie) === null || $.cookie(SN.C.S.NoticeDataGeoCookie) == 'disabled') {
if (navigator.geolocation) {
@ -622,18 +566,27 @@ var SN = { // StatusNet
$('#'+SN.C.S.NoticeLon).val(position.coords.longitude);
var data = {
'lat': position.coords.latitude,
'lon': position.coords.longitude,
'token': $('#token').val()
lat: position.coords.latitude,
lon: position.coords.longitude,
token: $('#token').val()
};
getJSONgeocodeURL(geocodeURL, data);
getJSONgeocodeURL(geocodeURL, data, position);
},
function(error) {
if (error.PERMISSION_DENIED == 1) {
removeNoticeDataGeo();
switch(error.code) {
case error.PERMISSION_DENIED:
removeNoticeDataGeo();
break;
case error.TIMEOUT:
$('#'+SN.C.S.NoticeDataGeo).attr('checked', false);
break;
}
},
{
timeout: 10000
}
);
}
@ -645,7 +598,7 @@ var SN = { // StatusNet
'token': $('#token').val()
};
getJSONgeocodeURL(geocodeURL, data);
getJSONgeocodeURL(geocodeURL, data, position);
}
else {
removeNoticeDataGeo();
@ -657,34 +610,20 @@ var SN = { // StatusNet
else {
var cookieValue = JSON.parse($.cookie(SN.C.S.NoticeDataGeoCookie));
if (cookieValue.NDGSM === true) {
$('#'+SN.C.S.NoticeDataGeoSelected).hide();
}
$('#'+SN.C.S.NoticeLat).val(cookieValue.NLat);
$('#'+SN.C.S.NoticeLon).val(cookieValue.NLon);
$('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS);
$('#'+SN.C.S.NoticeLocationId).val(cookieValue.NLID);
$('#'+SN.C.S.NoticeDataGeo).attr('checked', cookieValue.NDG);
$('#'+SN.C.S.NoticeGeoName)
.replaceWith('<a id="notice_data-geo_name"/>');
$('#'+SN.C.S.NoticeGeoName)
.attr('href', cookieValue.NLNU)
.text(cookieValue.NLN)
.click(function() {
window.open($(this).attr('href'));
return false;
});
$('label[for='+SN.C.S.NoticeDataGeo+']')
.attr('title', NoticeDataGeo_text.ShareDisable + ' (' + cookieValue.NLN + ')')
.addClass('checked');
}
}
else {
removeNoticeDataGeo();
}
$('#'+SN.C.S.NoticeDataText).focus();
}).change();
}
},

View File

@ -141,7 +141,7 @@ class Action extends HTMLOutputter // lawsuit
function showTitle()
{
$this->element('title', null,
sprintf(_("%1$s - %2$s"),
sprintf(_("%1\$s - %2\$s"),
$this->title(),
common_config('site', 'name')));
}

View File

@ -53,6 +53,9 @@ if (!defined('STATUSNET')) {
class ApiAction extends Action
{
const READ_ONLY = 1;
const READ_WRITE = 2;
var $format = null;
var $user = null;
var $auth_user = null;
@ -62,6 +65,8 @@ class ApiAction extends Action
var $since_id = null;
var $since = null;
var $access = self::READ_ONLY; // read (default) or read-write
/**
* Initialization.
*
@ -140,12 +145,14 @@ class ApiAction extends Action
// Note: some profiles don't have an associated user
$defaultDesign = Design::siteDesign();
if (!empty($user)) {
$design = $user->getDesign();
}
if (empty($design)) {
$design = Design::siteDesign();
$design = $defaultDesign;
}
$color = Design::toWebColor(empty($design->backgroundcolor) ? $defaultDesign->backgroundcolor : $design->backgroundcolor);
@ -166,7 +173,7 @@ class ApiAction extends Action
$timezone = 'UTC';
if ($user->timezone) {
if (!empty($user) && $user->timezone) {
$timezone = $user->timezone;
}

View File

@ -28,7 +28,7 @@
* @author Evan Prodromou <evan@status.net>
* @author mEDI <medi@milaro.net>
* @author Sarven Capadisli <csarven@status.net>
* @author Zach Copley <zach@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
@ -39,6 +39,7 @@ if (!defined('STATUSNET')) {
}
require_once INSTALLDIR . '/lib/api.php';
require_once INSTALLDIR . '/lib/apioauth.php';
/**
* Actions extending this class will require auth
@ -52,6 +53,9 @@ require_once INSTALLDIR . '/lib/api.php';
class ApiAuthAction extends ApiAction
{
var $access_token;
var $oauth_access_type;
var $oauth_source;
/**
* Take arguments for running, and output basic auth header if needed
@ -67,12 +71,115 @@ class ApiAuthAction extends ApiAction
parent::prepare($args);
if ($this->requiresAuth()) {
$this->checkBasicAuthUser();
$this->consumer_key = $this->arg('oauth_consumer_key');
$this->access_token = $this->arg('oauth_token');
if (!empty($this->access_token)) {
$this->checkOAuthRequest();
} else {
$this->checkBasicAuthUser();
// By default, all basic auth users have read and write access
$this->access = self::READ_WRITE;
}
}
return true;
}
function handle($args)
{
parent::handle($args);
}
function checkOAuthRequest()
{
common_debug("We have an OAuth request.");
$datastore = new ApiStatusNetOAuthDataStore();
$server = new OAuthServer($datastore);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$server->add_signature_method($hmac_method);
ApiOauthAction::cleanRequest();
try {
$req = OAuthRequest::from_request();
$server->verify_request($req);
$app = Oauth_application::getByConsumerKey($this->consumer_key);
if (empty($app)) {
// this should really not happen
common_log(LOG_WARN,
"Couldn't find the OAuth app for consumer key: $this->consumer_key");
throw new OAuthException('No application for that consumer key.');
}
// set the source attr
$this->oauth_source = $app->name;
$appUser = Oauth_application_user::staticGet('token',
$this->access_token);
// XXX: check that app->id and appUser->application_id and consumer all
// match?
if (!empty($appUser)) {
// read or read-write
$this->oauth_access_type = $appUser->access_type;
// If access_type == 0 we have either a request token
// or a bad / revoked access token
if ($this->oauth_access_type != 0) {
// Set the read or read-write access for the api call
$this->access = ($appUser->access_type & Oauth_application::$writeAccess)
? self::READ_WRITE : self::READ_ONLY;
$this->auth_user = User::staticGet('id', $appUser->profile_id);
$msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " .
"application '%s' (id: %d).";
common_log(LOG_INFO, sprintf($msg,
$this->auth_user->nickname,
$this->auth_user->id,
$app->name,
$app->id));
return true;
} else {
throw new OAuthException('Bad access token.');
}
} else {
// also should not happen
throw new OAuthException('No user for that token.');
}
} catch (OAuthException $e) {
common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage());
common_debug(var_export($req, true));
$this->showOAuthError($e->getMessage());
exit();
}
}
function showOAuthError($msg)
{
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: text/html; charset=utf-8');
print $msg . "\n";
}
/**
* Does this API resource require authentication?
*
@ -128,6 +235,7 @@ class ApiAuthAction extends ApiAction
exit;
}
}
return true;
}

122
lib/apioauth.php Normal file
View File

@ -0,0 +1,122 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Base action for OAuth API endpoints
*
* 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/apioauthstore.php';
/**
* Base action for API OAuth enpoints. Clean up the
* the request, and possibly some other common things
* here.
*
* @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 ApiOauthAction extends Action
{
/**
* Is this a read-only action?
*
* @return boolean false
*/
function isReadOnly($args)
{
return false;
}
function prepare($args)
{
parent::prepare($args);
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);
self::cleanRequest();
}
static function cleanRequest()
{
// kill evil effects of magical slashing
if (get_magic_quotes_gpc() == 1) {
$_POST = array_map('stripslashes', $_POST);
$_GET = array_map('stripslashes', $_GET);
}
// strip out the p param added in index.php
// XXX: should we strip anything else? Or alternatively
// only allow a known list of params?
unset($_GET['p']);
unset($_POST['p']);
}
function getCallback($url, $params)
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar($url,
OAuthUtil::urlencode_rfc3986($k),
OAuthUtil::urlencode_rfc3986($v));
}
return $url;
}
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);
}
}
}

163
lib/apioauthstore.php Normal file
View File

@ -0,0 +1,163 @@
<?php
/*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
require_once INSTALLDIR . '/lib/oauthstore.php';
class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
{
function lookup_consumer($consumer_key)
{
$con = Consumer::staticGet('consumer_key', $consumer_key);
if (!$con) {
return null;
}
return new OAuthConsumer($con->consumer_key,
$con->consumer_secret);
}
function getAppByRequestToken($token_key)
{
// Look up the full req tokenx
$req_token = $this->lookup_token(null,
'request',
$token_key);
if (empty($req_token)) {
common_debug("couldn't get request token from oauth datastore");
return null;
}
// Look up the full Token
$token = new Token();
$token->tok = $req_token->key;
$result = $token->find(true);
if (empty($result)) {
common_debug('Couldn\'t find req token in the token table.');
return null;
}
// Look up the app
$app = new Oauth_application();
$app->consumer_key = $token->consumer_key;
$result = $app->find(true);
if (!empty($result)) {
return $app;
} else {
common_debug("Couldn't find the app!");
return null;
}
}
function new_access_token($token, $consumer)
{
common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__);
$rt = new Token();
$rt->consumer_key = $consumer->key;
$rt->tok = $token->key;
$rt->type = 0; // request
$app = Oauth_application::getByConsumerKey($consumer->key);
if (empty($app)) {
common_debug("empty app!");
}
if ($rt->find(true) && $rt->state == 1) { // authorized
common_debug('request token found.', __FILE__);
// find the associated user of the app
$appUser = new Oauth_application_user();
$appUser->application_id = $app->id;
$appUser->token = $rt->tok;
$result = $appUser->find(true);
if (!empty($result)) {
common_debug("Oath app user found.");
} else {
common_debug("Oauth app user not found. app id $app->id token $rt->tok");
return null;
}
// go ahead and make the access token
$at = new Token();
$at->consumer_key = $consumer->key;
$at->tok = common_good_rand(16);
$at->secret = common_good_rand(16);
$at->type = 1; // access
$at->created = DB_DataObject_Cast::dateTime();
if (!$at->insert()) {
$e = $at->_lastError;
common_debug('access token "'.$at->tok.'" not inserted: "'.$e->message.'"', __FILE__);
return null;
} else {
common_debug('access token "'.$at->tok.'" inserted', __FILE__);
// burn the old one
$orig_rt = clone($rt);
$rt->state = 2; // used
if (!$rt->update($orig_rt)) {
return null;
}
common_debug('request token "'.$rt->tok.'" updated', __FILE__);
// update the token from req to access for the user
$orig = clone($appUser);
$appUser->token = $at->tok;
// It's at this point that we change the access type
// to whatever the application's access is. Request
// tokens should always have an access type of 0, and
// therefore be unuseable for making requests for
// protected resources.
$appUser->access_type = $app->access_type;
$result = $appUser->update($orig);
if (empty($result)) {
common_debug('couldn\'t update OAuth app user.');
return null;
}
// Okay, good
return new OAuthToken($at->tok, $at->secret);
}
} else {
return null;
}
}
}

338
lib/applicationeditform.php Normal file
View File

@ -0,0 +1,338 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Form for editing an 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 Form
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR . '/lib/form.php';
/**
* Form for editing an application
*
* @category Form
* @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 ApplicationEditForm extends Form
{
/**
* group for user to join
*/
var $application = null;
/**
* Constructor
*
* @param Action $out output channel
* @param User_group $group group to join
*/
function __construct($out=null, $application=null)
{
parent::__construct($out);
$this->application = $application;
}
/**
* ID of the form
*
* @return string ID of the form
*/
function id()
{
if ($this->application) {
return 'form_application_edit-' . $this->application->id;
} else {
return 'form_application_add';
}
}
/**
* HTTP method used to submit the form
*
* For image data we need to send multipart/form-data
* so we set that here too
*
* @return string the method to use for submitting
*/
function method()
{
$this->enctype = 'multipart/form-data';
return 'post';
}
/**
* class of the form
*
* @return string of the form class
*/
function formClass()
{
return 'form_settings';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
$cur = common_current_user();
if (!empty($this->application)) {
return common_local_url('editapplication',
array('id' => $this->application->id));
} else {
return common_local_url('newapplication');
}
}
/**
* Name of the form
*
* @return void
*/
function formLegend()
{
$this->out->element('legend', null, _('Edit application'));
}
/**
* Data elements of the form
*
* @return void
*/
function formData()
{
if ($this->application) {
$id = $this->application->id;
$icon = $this->application->icon;
$name = $this->application->name;
$description = $this->application->description;
$source_url = $this->application->source_url;
$organization = $this->application->organization;
$homepage = $this->application->homepage;
$callback_url = $this->application->callback_url;
$this->type = $this->application->type;
$this->access_type = $this->application->access_type;
} else {
$id = '';
$icon = '';
$name = '';
$description = '';
$source_url = '';
$organization = '';
$homepage = '';
$callback_url = '';
$this->type = '';
$this->access_type = '';
}
$this->out->hidden('token', common_session_token());
$this->out->elementStart('ul', 'form_data');
$this->out->elementStart('li', array('id' => 'application_icon'));
if (!empty($icon)) {
$this->out->element('img', array('src' => $icon));
}
$this->out->element('label', array('for' => 'app_icon'),
_('Icon'));
$this->out->element('input', array('name' => 'app_icon',
'type' => 'file',
'id' => 'app_icon'));
$this->out->element('p', 'form_guide', _('Icon for this application'));
$this->out->element('input', array('name' => 'MAX_FILE_SIZE',
'type' => 'hidden',
'id' => 'MAX_FILE_SIZE',
'value' => ImageFile::maxFileSizeInt()));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->hidden('application_id', $id);
$this->out->input('name', _('Name'),
($this->out->arg('name')) ? $this->out->arg('name') : $name);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$maxDesc = Oauth_application::maxDesc();
if ($maxDesc > 0) {
$descInstr = sprintf(_('Describe your application in %d characters'),
$maxDesc);
} else {
$descInstr = _('Describe your application');
}
$this->out->textarea('description', _('Description'),
($this->out->arg('description')) ? $this->out->arg('description') : $description,
$descInstr);
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('source_url', _('Source URL'),
($this->out->arg('source_url')) ? $this->out->arg('source_url') : $source_url,
_('URL of the homepage of this application'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('organization', _('Organization'),
($this->out->arg('organization')) ? $this->out->arg('organization') : $organization,
_('Organization responsible for this application'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('homepage', _('Homepage'),
($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
_('URL for the homepage of the organization'));
$this->out->elementEnd('li');
$this->out->elementStart('li');
$this->out->input('callback_url', ('Callback URL'),
($this->out->arg('callback_url')) ? $this->out->arg('callback_url') : $callback_url,
_('URL to redirect to after authentication'));
$this->out->elementEnd('li');
$this->out->elementStart('li', array('id' => 'application_types'));
$attrs = array('name' => 'app_type',
'type' => 'radio',
'id' => 'app_type-browser',
'class' => 'radio',
'value' => Oauth_application::$browser);
// Default to Browser
if ($this->application->type == Oauth_application::$browser
|| empty($this->application->type)) {
$attrs['checked'] = 'checked';
}
$this->out->element('input', $attrs);
$this->out->element('label', array('for' => 'app_type-browser',
'class' => 'radio'),
_('Browser'));
$attrs = array('name' => 'app_type',
'type' => 'radio',
'id' => 'app_type-dekstop',
'class' => 'radio',
'value' => Oauth_application::$desktop);
if ($this->application->type == Oauth_application::$desktop) {
$attrs['checked'] = 'checked';
}
$this->out->element('input', $attrs);
$this->out->element('label', array('for' => 'app_type-desktop',
'class' => 'radio'),
_('Desktop'));
$this->out->element('p', 'form_guide', _('Type of application, browser or desktop'));
$this->out->elementEnd('li');
$this->out->elementStart('li', array('id' => 'default_access_types'));
$attrs = array('name' => 'default_access_type',
'type' => 'radio',
'id' => 'default_access_type-r',
'class' => 'radio',
'value' => 'r');
// default to read-only access
if ($this->application->access_type & Oauth_application::$readAccess
|| empty($this->application->access_type)) {
$attrs['checked'] = 'checked';
}
$this->out->element('input', $attrs);
$this->out->element('label', array('for' => 'default_access_type-ro',
'class' => 'radio'),
_('Read-only'));
$attrs = array('name' => 'default_access_type',
'type' => 'radio',
'id' => 'default_access_type-rw',
'class' => 'radio',
'value' => 'rw');
if ($this->application->access_type & Oauth_application::$readAccess
&& $this->application->access_type & Oauth_application::$writeAccess
) {
$attrs['checked'] = 'checked';
}
$this->out->element('input', $attrs);
$this->out->element('label', array('for' => 'default_access_type-rw',
'class' => 'radio'),
_('Read-write'));
$this->out->element('p', 'form_guide', _('Default access for this application: read-only, or read-write'));
$this->out->elementEnd('li');
$this->out->elementEnd('ul');
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
$this->out->submit('cancel', _('Cancel'), 'submit form_action-primary',
'cancel', _('Cancel'));
$this->out->submit('save', _('Save'), 'submit form_action-secondary',
'save', _('Save'));
}
}

Some files were not shown because too many files have changed in this diff Show More