EndAddressData: At the end of <address>
- $action: the current action
+StartShowSiteNotice: Before showing site notice
+- $action: the current action
+
+EndShowSiteNotice: After showing site notice
+- $action: the current action
+
StartLoginGroupNav: Before showing the login and register navigation menu
- $action: the current action
should enable it only after you've convinced yourself that
it is safe. Default is 'false'.
+singleuser
+----------
+
+If an installation has only one user, this can simplify a lot of the
+interface. It also makes the user's profile the root URL.
+
+enabled: Whether to run in "single user mode". Default false.
+nickname: nickname of the single user.
+
Plugins
=======
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Site access administration panel
+ *
+ * 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 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);
+}
+
+/**
+ * Administer site access settings
+ *
+ * @category Admin
+ * @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 AccessadminpanelAction extends AdminPanelAction
+{
+ /**
+ * Returns the page title
+ *
+ * @return string page title
+ */
+
+ function title()
+ {
+ return _('Access');
+ }
+
+ /**
+ * Instructions for using this form.
+ *
+ * @return string instructions
+ */
+
+ function getInstructions()
+ {
+ return _('Site access settings');
+ }
+
+ /**
+ * Show the site admin panel form
+ *
+ * @return void
+ */
+
+ function showForm()
+ {
+ $form = new AccessAdminPanelForm($this);
+ $form->show();
+ return;
+ }
+
+ /**
+ * Save settings from the form
+ *
+ * @return void
+ */
+
+ function saveSettings()
+ {
+ static $booleans = array('site' => array('private', 'inviteonly', 'closed'));
+
+ foreach ($booleans as $section => $parts) {
+ foreach ($parts as $setting) {
+ $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0;
+ }
+ }
+
+ $config = new Config();
+
+ $config->query('BEGIN');
+
+ foreach ($booleans as $section => $parts) {
+ foreach ($parts as $setting) {
+ Config::save($section, $setting, $values[$section][$setting]);
+ }
+ }
+
+ $config->query('COMMIT');
+
+ return;
+ }
+
+}
+
+class AccessAdminPanelForm extends AdminForm
+{
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ return 'form_site_admin_panel';
+ }
+
+ /**
+ * class of the form
+ *
+ * @return string class of the form
+ */
+
+ function formClass()
+ {
+ return 'form_settings';
+ }
+
+ /**
+ * Action of the form
+ *
+ * @return string URL of the action
+ */
+
+ function action()
+ {
+ return common_local_url('accessadminpanel');
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->out->elementStart('fieldset', array('id' => 'settings_admin_access'));
+ $this->out->element('legend', null, _('Registration'));
+ $this->out->elementStart('ul', 'form_data');
+ $this->li();
+ $this->out->checkbox('private', _('Private'),
+ (bool) $this->value('private'),
+ _('Prohibit anonymous users (not logged in) from viewing site?'));
+ $this->unli();
+
+ $this->li();
+ $this->out->checkbox('inviteonly', _('Invite only'),
+ (bool) $this->value('inviteonly'),
+ _('Make registration invitation only.'));
+ $this->unli();
+
+ $this->li();
+ $this->out->checkbox('closed', _('Closed'),
+ (bool) $this->value('closed'),
+ _('Disable new registrations.'));
+ $this->unli();
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('fieldset');
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->out->submit('submit', _('Save'), 'submit', null, _('Save access settings'));
+ }
+
+}
print json_encode($out);
}
- $this->endDocument($this->format);
+ $this->endDocument($this->format);
+ }
+
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
}
}
}
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ *
+ **/
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
}
}
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
return true;
}
-
/**
* Determines whether this API resource requires auth. Overloaded to look
* return true in case source_id and source_screen_name are both empty
}
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
}
}
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
return null;
}
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
}
}
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
--- /dev/null
+<?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_WARNING, '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";
+ }
+}
+
--- /dev/null
+<?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>%s</strong> by <strong>%s</strong> would like " .
+ "the ability to <strong>%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()
+ {
+ }
+
+}
--- /dev/null
+<?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_WARNING, 'API OAuthException - ' . $e->getMessage());
+ header('HTTP/1.1 401 Unauthorized');
+ header('Content-Type: text/html; charset=utf-8');
+ print $e->getMessage() . "\n";
+ }
+ }
+
+}
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
- * @copyright 2009 StatusNet, Inc.
+ * @copyright 2009-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/
*/
{
parent::prepare($args);
- $this->user = $this->auth_user;
$this->status = $this->trimmed('status');
$this->source = $this->trimmed('source');
$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';
}
return;
}
- if (empty($this->user)) {
+ if (empty($this->auth_user)) {
$this->clientError(_('No such user.'), 404, $this->format);
return;
}
// Check for commands
$inter = new CommandInterpreter();
- $cmd = $inter->handle_command($this->user, $status_shortened);
+ $cmd = $inter->handle_command($this->auth_user, $status_shortened);
if ($cmd) {
// And, it returns your last status whether the cmd was successful
// or not!
- $this->notice = $this->user->getCurrentNotice();
+ $this->notice = $this->auth_user->getCurrentNotice();
} else {
$upload = null;
try {
- $upload = MediaFile::fromUpload('media', $this->user);
+ $upload = MediaFile::fromUpload('media', $this->auth_user);
} catch (ClientException $ce) {
$this->clientError($ce->getMessage());
return;
$options = array('reply_to' => $reply_to);
- if ($this->user->shareLocation()) {
+ if ($this->auth_user->shareLocation()) {
$locOptions = Notice::locationOptions($this->lat,
$this->lon,
null,
null,
- $this->user->getProfile());
+ $this->auth_user->getProfile());
$options = array_merge($options, $locOptions);
}
$this->notice =
- Notice::saveNew($this->user->id,
+ Notice::saveNew($this->auth_user->id,
$content,
$this->source,
$options);
$upload->attachToNotice($this->notice);
}
-
}
$this->showNotice();
}
}
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
}
}
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
}
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return true;
+ }
+
}
parent::showScripts();
if ($this->mode == 'crop') {
- $this->script('js/jcrop/jquery.Jcrop.min.js');
- $this->script('js/jcrop/jquery.Jcrop.go.js');
+ $this->script('jcrop/jquery.Jcrop.min.js');
+ $this->script('jcrop/jquery.Jcrop.go.js');
}
$this->autofocus('avatarfile');
{
parent::showScripts();
- $this->script('js/farbtastic/farbtastic.js');
- $this->script('js/userdesign.go.js');
+ $this->script('farbtastic/farbtastic.js');
+ $this->script('userdesign.go.js');
$this->autofocus('design_background-image_file');
}
*/
class DocAction extends Action
{
- var $filename;
- var $title;
+ var $output = null;
+ var $filename = null;
+ var $title = null;
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->title = $this->trimmed('title');
+ $this->output = null;
+
+ $this->loadDoc();
+ return true;
+ }
/**
- * Class handler.
+ * Handle a request
*
* @param array $args array of arguments
*
function handle($args)
{
parent::handle($args);
-
- $this->title = $this->trimmed('title');
- $this->output = null;
-
- if (Event::handle('StartLoadDoc', array(&$this->title, &$this->output))) {
-
- $this->filename = INSTALLDIR.'/doc-src/'.$this->title;
- if (!file_exists($this->filename)) {
- $this->clientError(_('No such document.'));
- return;
- }
-
- $c = file_get_contents($this->filename);
- $this->output = common_markup_to_html($c);
-
- Event::handle('EndLoadDoc', array($this->title, &$this->output));
- }
-
$this->showPage();
}
- // overrrided to add entry-title class
- function showPageTitle() {
+ /**
+ * Page title
+ *
+ * Gives the page title of the document. Override default for hAtom entry.
+ *
+ * @return void
+ */
+
+ function showPageTitle()
+ {
$this->element('h1', array('class' => 'entry-title'), $this->title());
}
- // overrided to add hentry, and content-inner classes
+ /**
+ * Block for content.
+ *
+ * Overrides default from Action to wrap everything in an hAtom entry.
+ *
+ * @return void.
+ */
+
function showContentBlock()
- {
- $this->elementStart('div', array('id' => 'content', 'class' => 'hentry'));
- $this->showPageTitle();
- $this->showPageNoticeBlock();
- $this->elementStart('div', array('id' => 'content_inner',
- 'class' => 'entry-content'));
- // show the actual content (forms, lists, whatever)
- $this->showContent();
- $this->elementEnd('div');
- $this->elementEnd('div');
- }
+ {
+ $this->elementStart('div', array('id' => 'content', 'class' => 'hentry'));
+ $this->showPageTitle();
+ $this->showPageNoticeBlock();
+ $this->elementStart('div', array('id' => 'content_inner',
+ 'class' => 'entry-content'));
+ // show the actual content (forms, lists, whatever)
+ $this->showContent();
+ $this->elementEnd('div');
+ $this->elementEnd('div');
+ }
/**
* Display content.
*
- * @return nothing
+ * Shows the content of the document.
+ *
+ * @return void
*/
+
function showContent()
{
$this->raw($this->output);
/**
* Page title.
*
+ * Uses the title of the document.
+ *
* @return page title
*/
function title()
return ucfirst($this->title);
}
+ /**
+ * These pages are read-only.
+ *
+ * @param array $args unused.
+ *
+ * @return boolean read-only flag (false)
+ */
+
function isReadOnly($args)
{
return true;
}
+
+ function loadDoc()
+ {
+ if (Event::handle('StartLoadDoc', array(&$this->title, &$this->output))) {
+
+ $this->filename = $this->getFilename();
+
+ if (empty($this->filename)) {
+ throw new ClientException(sprintf(_('No such document "%s"'), $this->title), 404);
+ }
+
+ $c = file_get_contents($this->filename);
+
+ $this->output = common_markup_to_html($c);
+
+ Event::handle('EndLoadDoc', array($this->title, &$this->output));
+ }
+ }
+
+ function getFilename()
+ {
+ if (file_exists(INSTALLDIR.'/local/doc-src/'.$this->title)) {
+ $localDef = INSTALLDIR.'/local/doc-src/'.$this->title;
+ }
+
+ $local = glob(INSTALLDIR.'/local/doc-src/'.$this->title.'.*');
+
+ if (count($local) || isset($localDef)) {
+ return $this->negotiateLanguage($local, $localDef);
+ }
+
+ if (file_exists(INSTALLDIR.'/doc-src/'.$this->title)) {
+ $distDef = INSTALLDIR.'/doc-src/'.$this->title;
+ }
+
+ $dist = glob(INSTALLDIR.'/doc-src/'.$this->title.'.*');
+
+ if (count($dist) || isset($distDef)) {
+ return $this->negotiateLanguage($dist, $distDef);
+ }
+
+ return null;
+ }
+
+ function negotiateLanguage($filenames, $defaultFilename=null)
+ {
+ // XXX: do this better
+
+ $langcode = common_language();
+
+ foreach ($filenames as $filename) {
+ if (preg_match('/\.'.$langcode.'$/', $filename)) {
+ return $filename;
+ }
+ }
+
+ return $defaultFilename;
+ }
}
--- /dev/null
+<?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);
+ }
+
+}
+
parent::showScripts();
if ($this->mode == 'crop') {
- $this->script('js/jcrop/jquery.Jcrop.min.js');
- $this->script('js/jcrop/jquery.Jcrop.go.js');
+ $this->script('jcrop/jquery.Jcrop.min.js');
+ $this->script('jcrop/jquery.Jcrop.go.js');
}
$this->autofocus('avatarfile');
function title()
{
if ($this->page > 1) {
- return sprintf(_("Inbox for %1$s - page %2$d"), $this->user->nickname,
+ return sprintf(_('Inbox for %1$s - page %2$d'), $this->user->nickname,
$this->page);
} else {
- return sprintf(_("Inbox for %s"), $this->user->nickname);
+ return sprintf(_('Inbox for %s'), $this->user->nickname);
}
}
--- /dev/null
+<?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);
+
+ }
+
+}
+
--- /dev/null
+<?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;
+ }
+
+ }
+
+}
--- /dev/null
+<?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');
+ }
+
+}
function title()
{
if ($this->page > 1) {
- return sprintf(_("Outbox for %1$s - page %2$d"),
+ return sprintf(_('Outbox for %1$s - page %2$d'),
$this->user->nickname, $page);
} else {
- return sprintf(_("Outbox for %s"), $this->user->nickname);
+ return sprintf(_('Outbox for %s'), $this->user->nickname);
}
}
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @author Sarven Capadisli <csarven@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
'background' => array('server', 'dir', 'path')
);
+ // XXX: If we're only going to have one boolean on thi page we
+ // can remove some of the boolean processing code --Z
+
+ static $booleans = array('site' => array('fancy'));
+
$values = array();
foreach ($settings as $section => $parts) {
}
}
+ foreach ($booleans as $section => $parts) {
+ foreach ($parts as $setting) {
+ $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0;
+ }
+ }
+
$this->validate($values);
// assert(all values are valid);
}
}
- $config->query('COMMIT');
+ foreach ($booleans as $section => $parts) {
+ foreach ($parts as $setting) {
+ Config::save($section, $setting, $values[$section][$setting]);
+ }
+ }
+
+ $config->query('COMMIT');
return;
}
function formData()
{
- $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
+ $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
$this->out->element('legend', null, _('Site'), 'site');
$this->out->elementStart('ul', 'form_data');
+ $this->li();
+ $this->input('server', _('Server'), _('Site\'s server hostname.'));
+ $this->unli();
+
$this->li();
$this->input('path', _('Path'), _('Site path'));
$this->unli();
$this->input('locale_path', _('Path to locales'), _('Directory path to locales'), 'site');
$this->unli();
+ $this->li();
+ $this->out->checkbox('fancy', _('Fancy URLs'),
+ (bool) $this->value('fancy'),
+ _('Use fancy (more readable and memorable) URLs?'));
+ $this->unli();
+
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
if ($this->page == 1) {
return sprintf(_("Replies to %s"), $this->user->nickname);
} else {
- return sprintf(_("Replies to %1$s, page %2$d"),
+ return sprintf(_('Replies to %1$s, page %2$d'),
$this->user->nickname,
$this->page);
}
--- /dev/null
+<?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();
+ }
+
+}
function title()
{
if ($this->page == 1) {
- return sprintf(_("%s's favorite notices"), $this->user->nickname);
+ return sprintf(_('%s\'s favorite notices'), $this->user->nickname);
} else {
- return sprintf(_("%1$s's favorite notices, page %2$d"),
+ return sprintf(_('%1$s\'s favorite notices, page %2$d'),
$this->user->nickname,
$this->page);
}
}
if ($this->page == 1) {
- return sprintf(_("%s group"), $base);
+ return sprintf(_('%s group'), $base);
} else {
- return sprintf(_("%1$s group, page %2$d"),
+ return sprintf(_('%1$s group, page %2$d'),
$base,
$this->page);
}
if ($this->page == 1) {
return $base;
} else {
- return sprintf(_("%1$s, page %2$d"),
+ return sprintf(_('%1$s, page %2$d'),
$base,
$this->page);
}
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @author Sarven Capadisli <csarven@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
'site', 'textlimit', 'dupelimit'),
'snapshot' => array('run', 'reporturl', 'frequency'));
- static $booleans = array('site' => array('private', 'inviteonly', 'closed', 'fancy'));
-
$values = array();
foreach ($settings as $section => $parts) {
}
}
- foreach ($booleans as $section => $parts) {
- foreach ($parts as $setting) {
- $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0;
- }
- }
-
// This throws an exception on validation errors
$this->validate($values);
}
}
- foreach ($booleans as $section => $parts) {
- foreach ($parts as $setting) {
- Config::save($section, $setting, $values[$section][$setting]);
- }
- }
-
$config->query('COMMIT');
return;
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
- $this->out->elementStart('fieldset', array('id' => 'settings_admin_urls'));
- $this->out->element('legend', null, _('URLs'));
- $this->out->elementStart('ul', 'form_data');
- $this->li();
- $this->input('server', _('Server'), _('Site\'s server hostname.'));
- $this->unli();
-
- $this->li();
- $this->out->checkbox('fancy', _('Fancy URLs'),
- (bool) $this->value('fancy'),
- _('Use fancy (more readable and memorable) URLs?'));
- $this->unli();
- $this->out->elementEnd('ul');
- $this->out->elementEnd('fieldset');
-
- $this->out->elementStart('fieldset', array('id' => 'settings_admin_access'));
- $this->out->element('legend', null, _('Access'));
- $this->out->elementStart('ul', 'form_data');
- $this->li();
- $this->out->checkbox('private', _('Private'),
- (bool) $this->value('private'),
- _('Prohibit anonymous users (not logged in) from viewing site?'));
- $this->unli();
-
- $this->li();
- $this->out->checkbox('inviteonly', _('Invite only'),
- (bool) $this->value('inviteonly'),
- _('Make registration invitation only.'));
- $this->unli();
-
- $this->li();
- $this->out->checkbox('closed', _('Closed'),
- (bool) $this->value('closed'),
- _('Disable new registrations.'));
- $this->unli();
- $this->out->elementEnd('ul');
- $this->out->elementEnd('fieldset');
-
$this->out->elementStart('fieldset', array('id' => 'settings_admin_snapshots'));
$this->out->element('legend', null, _('Snapshots'));
$this->out->elementStart('ul', 'form_data');
function title()
{
if ($this->page == 1) {
- return sprintf(_("Notices tagged with %s"), $this->tag);
+ return sprintf(_('Notices tagged with %s'), $this->tag);
} else {
- return sprintf(_("Notices tagged with %1$s, page %2$d"),
+ return sprintf(_('Notices tagged with %1$s, page %2$d'),
$this->tag,
$this->page);
}
array('tag' => $this->tag)),
sprintf(_('Notice feed for tag %s (RSS 1.0)'),
$this->tag)),
- new Feed(Feed::RSS2,
+ new Feed(Feed::RSS2,
common_local_url('ApiTimelineTag',
array('format' => 'rss',
'tag' => $this->tag)),
function title()
{
if ($this->page == 1) {
- return sprintf(_("%s groups"), $this->user->nickname);
+ return sprintf(_('%s groups'), $this->user->nickname);
} else {
- return sprintf(_("%1$s groups, page %2$d"),
+ return sprintf(_('%1$s groups, page %2$d'),
$this->user->nickname,
$this->page);
}
*/
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)
/* 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;
+ }
+
}
// 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;
}
unset($i);
}
$i = Memcached_DataObject::getcached($cls, $k, $v);
- if ($i) {
- return $i;
- } else {
+ if ($i === false) { // false == cache miss
$i = DB_DataObject::factory($cls);
if (empty($i)) {
$i = false;
}
$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;
}
- function &pkeyGet($cls, $kv)
+ /**
+ * @fixme Should this return false on lookup fail to match staticGet?
+ */
+ function pkeyGet($cls, $kv)
{
$i = Memcached_DataObject::multicache($cls, $kv);
- if ($i) {
+ if ($i !== false) { // false == cache miss
return $i;
} else {
- $i = new $cls();
+ $i = DB_DataObject::factory($cls);
+ if (empty($i)) {
+ return false;
+ }
foreach ($kv as $k => $v) {
$i->$k = $v;
}
$i->encache();
} else {
$i = null;
+ $c = self::memcache();
+ if (!empty($c)) {
+ $ck = self::multicacheKey($cls, $kv);
+ $c->set($ck, null);
+ }
}
return $i;
}
function insert()
{
$result = parent::insert();
+ if ($result) {
+ $this->encache(); // in case of cached negative lookups
+ }
return $result;
}
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();
function encache()
{
$c = $this->memcache();
+
if (!$c) {
return false;
} else if ($this->tableName() == 'user' && is_object($this->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);
+ $keys = $this->_allCacheKeys();
+
+ foreach ($keys as $key) {
+ $c->set($key, $this);
+ }
}
}
function decache()
{
$c = $this->memcache();
+
if (!$c) {
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->delete($this->cacheKey($this->tableName(), $key, $this->$key));
+ }
+
+ $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;
}
+ $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 = Memcached_DataObject::memcache();
+ $c = self::memcache();
if (!$c) {
return false;
} else {
- $pkeys = implode(',', array_keys($kv));
- $pvals = implode(',', array_values($kv));
- return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals));
+ return $c->get(self::multicacheKey($cls, $kv));
}
}
+ 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';
$key_part = common_keyize($cls).':'.md5($qry);
$ckey = common_cache_key($key_part);
$stored = $c->get($ckey);
- if ($stored) {
+
+ if ($stored !== false) {
return new ArrayWrapper($stored);
}
return new ArrayWrapper($cached);
}
+ /**
+ * sends query to database - this is the private one that must work
+ * - internal functions use this rather than $this->query()
+ *
+ * Overridden to do logging.
+ *
+ * @param string $string
+ * @access private
+ * @return mixed none or PEAR_Error
+ */
+ function _query($string)
+ {
+ $start = microtime(true);
+ $result = parent::_query($string);
+ $delta = microtime(true) - $start;
+
+ $limit = common_config('db', 'log_slow_queries');
+ if (($limit > 0 && $delta >= $limit) || common_config('db', 'log_queries')) {
+ $clean = $this->sanitizeQuery($string);
+ common_log(LOG_DEBUG, sprintf("DB query (%0.3fs): %s", $delta, $clean));
+ }
+ return $result;
+ }
+
+ // Sanitize a query for logging
+ // @fixme don't trim spaces in string literals
+ function sanitizeQuery($string)
+ {
+ $string = preg_replace('/\s+/', ' ', $string);
+ $string = trim($string);
+ return $string;
+ }
+
// We overload so that 'SET NAMES "utf8"' is called for
// each connection
--- /dev/null
+<?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);
+ }
+ }
+
+}
--- /dev/null
+<?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;
+ }
+
+}
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();
public $logo; // varchar(255)
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
+ public $tags; // text
/* Static get */
- function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('Status_network',$k,$v); }
+ function staticGet($k,$v=NULL) {
+ $i = DB_DataObject::staticGet('Status_network',$k,$v);
+
+ // Don't use local process cache; if we're fetching multiple
+ // times it's because we're reloading it in a long-running
+ // process; we need a fresh copy!
+ global $_DB_DATAOBJECT;
+ unset($_DB_DATAOBJECT['CACHE']['status_network']);
+ return $i;
+ }
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
return $this->nickname . '.' . self::$wildcard;
}
}
+
+ /**
+ * Return site meta-info tags as an array
+ * @return array of strings
+ */
+ function getTags()
+ {
+ return array_filter(explode("|", strval($this->tags)));
+ }
+
+ /**
+ * Check if this site record has a particular meta-info tag attached.
+ * @param string $tag
+ * @return bool
+ */
+ function hasTag($tag)
+ {
+ return in_array($tag, $this->getTags());
+ }
}
*/
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 */
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
[consumer]
consumer_key = 130
+consumer_secret = 130
seed = 130
created = 142
modified = 384
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
secret = 130
type = 145
state = 17
+verifier = 2
+verified_callback = 2
created = 142
modified = 384
+++ /dev/null
-create table queue_item_new (
- id integer auto_increment primary key comment 'unique identifier',
- frame blob not null comment 'data: object reference or opaque string',
- transport varchar(8) not null comment 'queue for what? "email", "jabber", "sms", "irc", ...',
- created datetime not null comment 'date this record was created',
- claimed datetime comment 'date this item was claimed',
-
- index queue_item_created_idx (created)
-
-) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
-
-insert into queue_item_new (frame,transport,created,claimed)
- select notice_id,transport,created,claimed from queue_item;
-alter table queue_item rename to queue_item_old;
-alter table queue_item_new rename to queue_item;
-
--- /dev/null
+create table queue_item_new (
+ id integer auto_increment primary key comment 'unique identifier',
+ frame blob not null comment 'data: object reference or opaque string',
+ transport varchar(8) not null comment 'queue for what? "email", "jabber", "sms", "irc", ...',
+ created datetime not null comment 'date this record was created',
+ claimed datetime comment 'date this item was claimed',
+
+ index queue_item_created_idx (created)
+
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+insert into queue_item_new (frame,transport,created,claimed)
+ select notice_id,transport,created,claimed from queue_item;
+alter table queue_item rename to queue_item_old;
+alter table queue_item_new rename to queue_item;
+
+alter table consumer
+ add consumer_secret varchar(255) not null comment 'secret value';
+
+alter table token
+ add verifier varchar(255) comment 'verifier string for OAuth 1.0a',
+ add verified_callback varchar(255) comment 'verified callback URL for OAuth 1.0a';
+
+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;
+
sitename varchar(255) comment 'display name',
theme varchar(255) comment 'theme name',
logo varchar(255) comment 'site logo',
+
+ tags text comment 'site meta-info tags (pipe-separated)',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified'
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',
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',
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 (
-// 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() {
}
} 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;
return function(position) {
callback(position);
self.lastPosition = position;
- }
- }
+ };
+ };
// -- PUBLIC
return {
};
});
-// 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();
})();
+}
{
if (Event::handle('StartShowScripts', array($this))) {
if (Event::handle('StartShowJQueryScripts', array($this))) {
- $this->script('js/jquery.min.js');
- $this->script('js/jquery.form.js');
- $this->script('js/jquery.cookie.js');
- $this->script('js/json2.js');
- $this->script('js/jquery.joverlay.min.js');
+ $this->script('jquery.min.js');
+ $this->script('jquery.form.js');
+ $this->script('jquery.cookie.js');
+ $this->script('json2.js');
+ $this->script('jquery.joverlay.min.js');
Event::handle('EndShowJQueryScripts', array($this));
}
if (Event::handle('StartShowStatusNetScripts', array($this)) &&
Event::handle('StartShowLaconicaScripts', array($this))) {
- $this->script('js/xbImportNode.js');
- $this->script('js/util.js');
- $this->script('js/geometa.js');
+ $this->script('xbImportNode.js');
+ $this->script('util.js');
+ $this->script('geometa.js');
// Frame-busting code to avoid clickjacking attacks.
$this->element('script', array('type' => 'text/javascript'),
'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }');
$this->elementStart('div', array('id' => 'header'));
$this->showLogo();
$this->showPrimaryNav();
- $this->showSiteNotice();
+ if (Event::handle('StartShowSiteNotice', array($this))) {
+ $this->showSiteNotice();
+
+ Event::handle('EndShowSiteNotice', array($this));
+ }
if (common_logged_in()) {
$this->showNoticeForm();
} else {
$this->elementStart('address', array('id' => 'site_contact',
'class' => 'vcard'));
if (Event::handle('StartAddressData', array($this))) {
+ if (common_config('singleuser', 'enabled')) {
+ $url = common_local_url('showstream',
+ array('nickname' => common_config('singleuser', 'nickname')));
+ } else {
+ $url = common_local_url('public');
+ }
$this->elementStart('a', array('class' => 'url home bookmark',
- 'href' => common_local_url('public')));
+ 'href' => $url));
if (common_config('site', 'logo') || file_exists(Theme::file('logo.png'))) {
$this->element('img', array('class' => 'logo photo',
'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png'),
if ($this->canAdmin('user')) {
$this->out->menuItem(common_local_url('useradminpanel'), _('User'),
- _('Paths configuration'), $action_name == 'useradminpanel', 'nav_design_admin_panel');
+ _('User configuration'), $action_name == 'useradminpanel', 'nav_design_admin_panel');
}
- if ($this->canAdmin('paths')) {
- $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
- _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel');
+ if ($this->canAdmin('access')) {
+ $this->out->menuItem(common_local_url('accessadminpanel'), _('Access'),
+ _('Access configuration'), $action_name == 'accessadminpanel', 'nav_design_admin_panel');
+ }
+
+ if ($this->canAdmin('paths')) {
+ $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
+ _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel');
}
Event::handle('EndAdminPanelNav', array($this));
class ApiAction extends Action
{
+ const READ_ONLY = 1;
+ const READ_WRITE = 2;
+
var $format = null;
var $user = null;
var $auth_user = null;
var $since_id = null;
var $since = null;
+ var $access = self::READ_ONLY; // read (default) or read-write
+
/**
* Initialization.
*
* @author Evan Prodromou <evan@status.net>
* @author mEDI <medi@milaro.net>
* @author Sarven Capadisli <csarven@status.net>
- * @author Zach Copley <zach@status.net>
- * @copyright 2009 StatusNet, Inc.
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2009-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/
*/
}
require_once INSTALLDIR . '/lib/api.php';
+require_once INSTALLDIR . '/lib/apioauth.php';
/**
* Actions extending this class will require auth
class ApiAuthAction extends ApiAction
{
+ var $auth_user_nickname = null;
+ var $auth_user_password = null;
+ var $access_token = null;
+ var $oauth_source = null;
/**
* Take arguments for running, and output basic auth header if needed
{
parent::prepare($args);
+ $this->consumer_key = $this->arg('oauth_consumer_key');
+ $this->access_token = $this->arg('oauth_token');
+
+ // NOTE: $this->auth_user has to get set in prepare(), not handle(),
+ // because subclasses do stuff with it in their prepares.
+
if ($this->requiresAuth()) {
- $this->checkBasicAuthUser();
+ if (!empty($this->access_token)) {
+ $this->checkOAuthRequest();
+ } else {
+ $this->checkBasicAuthUser(true);
+ }
+ } else {
+
+ // Check to see if a basic auth user is there even
+ // if one's not required
+
+ if (empty($this->access_token)) {
+ $this->checkBasicAuthUser(false);
+ }
+ }
+
+ // Reject API calls with the wrong access level
+
+ if ($this->isReadOnly($args) == false) {
+ if ($this->access != self::READ_WRITE) {
+ $msg = _('API resource requires read-write access, ' .
+ 'but you only have read access.');
+ $this->clientError($msg, 401, $this->format);
+ exit;
+ }
}
return true;
}
+ function handle($args)
+ {
+ parent::handle($args);
+ }
+
+ function checkOAuthRequest()
+ {
+ $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 probably not happen
+ common_log(LOG_WARNING,
+ '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)) {
+
+ // If access_type == 0 we have either a request token
+ // or a bad / revoked access token
+
+ if ($appUser->access_type != 0) {
+
+ // Set the access level for the api call
+
+ $this->access = ($appUser->access_type & Oauth_application::$writeAccess)
+ ? self::READ_WRITE : self::READ_ONLY;
+
+ if (Event::handle('StartSetApiUser', array(&$user))) {
+ $this->auth_user = User::staticGet('id', $appUser->profile_id);
+ Event::handle('EndSetApiUser', array($user));
+ }
+
+ $msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " .
+ "application '%s' (id: %d) with %s access.";
+
+ common_log(LOG_INFO, sprintf($msg,
+ $this->auth_user->nickname,
+ $this->auth_user->id,
+ $app->name,
+ $app->id,
+ ($this->access = self::READ_WRITE) ?
+ 'read-write' : 'read-only'
+ ));
+ return;
+ } 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_WARNING, 'API OAuthException - ' . $e->getMessage());
+ $this->showAuthError();
+ exit;
+ }
+ }
+
/**
* Does this API resource require authentication?
*
* @return boolean true or false
*/
- function checkBasicAuthUser()
+ function checkBasicAuthUser($required = true)
{
$this->basicAuthProcessHeader();
$realm = common_config('site', 'name') . ' API';
- if (!isset($this->auth_user)) {
+ if (!isset($this->auth_user_nickname) && $required) {
header('WWW-Authenticate: Basic realm="' . $realm . '"');
// show error if the user clicks 'cancel'
- $this->showBasicAuthError();
+ $this->showAuthError();
exit;
} else {
- $nickname = $this->auth_user;
- $password = $this->auth_pw;
- $user = common_check_user($nickname, $password);
+
+ $user = common_check_user($this->auth_user_nickname,
+ $this->auth_user_password);
+
if (Event::handle('StartSetApiUser', array(&$user))) {
- $this->auth_user = $user;
+
+ if (!empty($user)) {
+ $this->auth_user = $user;
+ }
+
Event::handle('EndSetApiUser', array($user));
}
- if (empty($this->auth_user)) {
+ // By default, basic auth users have rw access
+
+ $this->access = self::READ_WRITE;
+
+ if (empty($this->auth_user) && $required) {
// basic authentication failed
list($proxy, $ip) = common_client_ip();
- common_log(
- LOG_WARNING,
- 'Failed API auth attempt, nickname = ' .
- "$nickname, proxy = $proxy, ip = $ip."
- );
- $this->showBasicAuthError();
+
+ $msg = sprintf(_('Failed API auth attempt, nickname = %1$s, ' .
+ 'proxy = %2$s, ip = %3$s'),
+ $this->auth_user_nickname,
+ $proxy,
+ $ip);
+ common_log(LOG_WARNING, $msg);
+ $this->showAuthError();
exit;
}
}
- return true;
}
/**
{
if (isset($_SERVER['AUTHORIZATION'])
|| isset($_SERVER['HTTP_AUTHORIZATION'])
- ) {
- $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])
- ? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION'];
+ ) {
+ $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])
+ ? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION'];
}
if (isset($_SERVER['PHP_AUTH_USER'])) {
- $this->auth_user = $_SERVER['PHP_AUTH_USER'];
- $this->auth_pw = $_SERVER['PHP_AUTH_PW'];
+ $this->auth_user_nickname = $_SERVER['PHP_AUTH_USER'];
+ $this->auth_user_password = $_SERVER['PHP_AUTH_PW'];
} elseif (isset($authorization_header)
&& strstr(substr($authorization_header, 0, 5), 'Basic')) {
- // decode the HTTP_AUTHORIZATION header on php-cgi server self
+ // Decode the HTTP_AUTHORIZATION header on php-cgi server self
// on fcgid server the header name is AUTHORIZATION
$auth_hash = base64_decode(substr($authorization_header, 6));
- list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash);
+ list($this->auth_user_nickname,
+ $this->auth_user_password) = explode(':', $auth_hash);
- // set all to null on a empty basic auth request
+ // Set all to null on a empty basic auth request
- if ($this->auth_user == "") {
- $this->auth_user = null;
- $this->auth_pw = null;
+ if (empty($this->auth_user_nickname)) {
+ $this->auth_user_nickname = null;
+ $this->auth_password = null;
}
- } else {
- $this->auth_user = null;
- $this->auth_pw = null;
}
}
* @return void
*/
- function showBasicAuthError()
+ function showAuthError()
{
header('HTTP/1.1 401 Unauthorized');
$msg = 'Could not authenticate you.';
--- /dev/null
+<?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);
+ }
+ }
+
+}
--- /dev/null
+<?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;
+ }
+ }
+
+}
+
--- /dev/null
+<?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'));
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Widget to show a list of OAuth 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 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);
+}
+
+require_once INSTALLDIR . '/lib/widget.php';
+
+define('APPS_PER_PAGE', 20);
+
+/**
+ * Widget to show a list of OAuth applications
+ *
+ * @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 ApplicationList extends Widget
+{
+ /** Current application, application query */
+ var $application = null;
+
+ /** Owner of this list */
+ var $owner = null;
+
+ /** Action object using us. */
+ var $action = null;
+
+ function __construct($application, $owner=null, $action=null, $connections = false)
+ {
+ parent::__construct($action);
+
+ $this->application = $application;
+ $this->owner = $owner;
+ $this->action = $action;
+ $this->connections = $connections;
+ }
+
+ function show()
+ {
+ $this->out->elementStart('ul', 'applications');
+
+ $cnt = 0;
+
+ while ($this->application->fetch()) {
+ $cnt++;
+ if($cnt > APPS_PER_PAGE) {
+ break;
+ }
+ $this->showapplication();
+ }
+
+ $this->out->elementEnd('ul');
+
+ return $cnt;
+ }
+
+ function showApplication()
+ {
+
+ $user = common_current_user();
+
+ $this->out->elementStart('li', array('class' => 'application',
+ 'id' => 'oauthclient-' . $this->application->id));
+
+ $this->out->elementStart('span', 'vcard author');
+ if (!$this->connections) {
+ $this->out->elementStart('a',
+ array('href' => common_local_url('showapplication',
+ array('id' => $this->application->id)),
+ 'class' => 'url'));
+
+ } else {
+ $this->out->elementStart('a', array('href' => $this->application->source_url,
+ 'class' => 'url'));
+ }
+
+ if (!empty($this->application->icon)) {
+ $this->out->element('img', array('src' => $this->application->icon,
+ 'class' => 'photo avatar'));
+ }
+
+ $this->out->element('span', 'fn', $this->application->name);
+ $this->out->elementEnd('a');
+ $this->out->elementEnd('span');
+
+ $this->out->raw(' by ');
+
+ $this->out->element('a', array('href' => $this->application->homepage,
+ 'class' => 'url'),
+ $this->application->organization);
+
+ $this->out->element('p', 'note', $this->application->description);
+ $this->out->elementEnd('li');
+
+ if ($this->connections) {
+ $appUser = Oauth_application_user::getByKeys($this->owner, $this->application);
+
+ if (empty($appUser)) {
+ common_debug("empty appUser!");
+ }
+
+ $this->out->elementStart('li');
+
+ $access = ($this->application->access_type & Oauth_application::$writeAccess)
+ ? 'read-write' : 'read-only';
+
+ $txt = 'Approved ' . common_date_string($appUser->modified) .
+ " - $access access.";
+
+ $this->out->raw($txt);
+ $this->out->elementEnd('li');
+
+ $this->out->elementStart('li', 'entity_revoke');
+ $this->out->elementStart('form', array('id' => 'form_revoke_app',
+ 'class' => 'form_revoke_app',
+ 'method' => 'POST',
+ 'action' =>
+ common_local_url('oauthconnectionssettings')));
+ $this->out->elementStart('fieldset');
+ $this->out->hidden('id', $this->application->id);
+ $this->out->hidden('token', common_session_token());
+ $this->out->submit('revoke', _('Revoke'));
+ $this->out->elementEnd('fieldset');
+ $this->out->elementEnd('form');
+ $this->out->elementEnd('li');
+ }
+ }
+
+ /* Override this in subclasses. */
+
+ function showOwnerControls()
+ {
+ return;
+ }
+
+}
array(_('SMS'),
_('Updates by SMS'));
}
+
+ $menu['oauthconnectionssettings'] = array(
+ _('Connections'),
+ _('Authorized connected applications')
+ );
foreach ($menu as $menuaction => $menudesc) {
$this->action->menuItem(common_local_url($menuaction),
}
-
'dupelimit' => 60, # default for same person saying the same thing
'textlimit' => 140,
'indent' => true,
- 'use_x_sendfile' => false,
+ 'use_x_sendfile' => false
),
'db' =>
array('database' => 'YOU HAVE TO SET THIS IN config.php',
'db_driver' => 'DB', # XXX: JanRain libs only work with DB
'quote_identifiers' => false,
'type' => 'mysql',
- 'schemacheck' => 'runtime'), // 'runtime' or 'script'
+ 'schemacheck' => 'runtime', // 'runtime' or 'script'
+ 'log_queries' => false, // true to log all DB queries
+ 'log_slow_queries' => 0), // if set, log queries taking over N seconds
'syslog' =>
array('appname' => 'statusnet', # for syslog
'priority' => 'debug', # XXX: currently ignored
'subsystem' => 'db', # default to database, or 'stomp'
'stomp_server' => null,
'queue_basename' => '/queue/statusnet/',
+ 'control_channel' => '/topic/statusnet-control', // broadcasts to all queue daemons
'stomp_username' => null,
'stomp_password' => null,
'monitor' => null, // URL to monitor ping endpoint (work in progress)
array('server' => null,
'dir' => null,
'path'=> null),
+ 'javascript' =>
+ array('server' => null,
+ 'path'=> null),
'throttle' =>
array('enabled' => false, // whether to throttle edits; false by default
'count' => 20, // number of allowed messages in timespan
'uploads' => true,
'filecommand' => '/usr/bin/file',
),
+ 'application' =>
+ array('desclimit' => null),
'group' =>
array('maxaliases' => 3,
'desclimit' => null),
'OpenID' => null),
),
'admin' =>
- array('panels' => array('design', 'site', 'user', 'paths')),
+ array('panels' => array('design', 'site', 'user', 'paths', 'access')),
+ 'singleuser' =>
+ array('enabled' => false,
+ 'nickname' => null),
);
{
parent::showScripts();
- $this->script('js/farbtastic/farbtastic.js');
- $this->script('js/userdesign.go.js');
+ $this->script('farbtastic/farbtastic.js');
+ $this->script('userdesign.go.js');
$this->autofocus('design_background-image_file');
}
function script($src, $type='text/javascript')
{
if(Event::handle('StartScriptElement', array($this,&$src,&$type))) {
+
$url = parse_url($src);
+
if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment']))
{
- $src = common_path($src) . '?version=' . STATUSNET_VERSION;
+ $path = common_config('javascript', 'path');
+
+ if (empty($path)) {
+ $path = common_config('site', 'path') . '/js/';
+ }
+
+ if ($path[strlen($path)-1] != '/') {
+ $path .= '/';
+ }
+
+ if ($path[0] != '/') {
+ $path = '/'.$path;
+ }
+
+ $server = common_config('javascript', 'server');
+
+ if (empty($server)) {
+ $server = common_config('site', 'server');
+ }
+
+ // XXX: protocol
+
+ $src = 'http://'.$server.$path.$src . '?version=' . STATUSNET_VERSION;
}
+
$this->element('script', array('type' => $type,
'src' => $src),
' ');
+
Event::handle('EndScriptElement', array($this,$src,$type));
}
}
protected $pollTimeouts = array();
protected $lastPoll = array();
+ public $shutdown = false; // Did we do a graceful shutdown?
+ public $respawn = true; // Should we respawn after shutdown?
+
/**
* @param string $id process ID to use in logging/monitoring
*/
$this->logState('init');
$this->start();
- while (true) {
+ while (!$this->shutdown) {
$timeouts = array_values($this->pollTimeouts);
$timeouts[] = 60; // default max timeout
$this->logState('idle');
$this->idle();
- $memoryLimit = $this->softMemoryLimit();
- if ($memoryLimit > 0) {
- $usage = memory_get_usage();
- if ($usage > $memoryLimit) {
- common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
- break;
- } else if (common_config('queue', 'debug_memory')) {
- common_log(LOG_DEBUG, "Memory usage $usage");
- }
- }
+ $this->checkMemory();
}
$this->logState('shutdown');
$this->finish();
}
+ /**
+ * Check runtime memory usage, possibly triggering a graceful shutdown
+ * and thread respawn if we've crossed the soft limit.
+ */
+ protected function checkMemory()
+ {
+ $memoryLimit = $this->softMemoryLimit();
+ if ($memoryLimit > 0) {
+ $usage = memory_get_usage();
+ if ($usage > $memoryLimit) {
+ common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
+ $this->requestRestart();
+ } else if (common_config('queue', 'debug_memory')) {
+ common_log(LOG_DEBUG, "Memory usage $usage");
+ }
+ }
+ }
+
/**
* Return fully-parsed soft memory limit in bytes.
* @return intval 0 or -1 if not set
$owners[] = "thread:" . $this->id;
$this->monitor->stats($key, $owners);
}
+
+ /**
+ * For IoManagers to request a graceful shutdown at end of event loop.
+ */
+ public function requestShutdown()
+ {
+ $this->shutdown = true;
+ $this->respawn = false;
+ }
+
+ /**
+ * For IoManagers to request a graceful restart at end of event loop.
+ */
+ public function requestRestart()
+ {
+ $this->shutdown = true;
+ $this->respawn = true;
+ }
+
}
function show()
{
$user = null;
-
+
// FIXME: we should probably pass this in
-
+
$action = $this->action->trimmed('action');
$nickname = $this->action->trimmed('nickname');
$cur = common_current_user();
- if ($cur && $cur->id == $user->id) {
+ if ($cur && $cur->id == $user->id &&
+ !common_config('singleuser', 'enabled')) {
$this->out->menuItem(common_local_url('inbox', array('nickname' =>
$nickname)),
$this->initialize();
}
+ /**
+ * Optional; ping any running queue handler daemons with a notification
+ * such as announcing a new site to handle or requesting clean shutdown.
+ * This avoids having to restart all the daemons manually to update configs
+ * and such.
+ *
+ * Called from scripts/queuectl.php controller utility.
+ *
+ * @param string $event event key
+ * @param string $param optional parameter to append to key
+ * @return boolean success
+ */
+ public function sendControlSignal($event, $param='')
+ {
+ throw new Exception(get_class($this) . " does not support control signals.");
+ }
+
/**
* Store an object (usually/always a Notice) into the given queue
* for later processing. No guarantee is made on when it will be
// XMPP output handlers...
$this->connect('jabber', 'JabberQueueHandler');
$this->connect('public', 'PublicQueueHandler');
-
// @fixme this should get an actual queue
//$this->connect('confirm', 'XmppConfirmHandler');
if (Event::handle('StartInitializeRouter', array(&$m))) {
- // In the "root"
-
- $m->connect('', array('action' => 'public'));
- $m->connect('rss', array('action' => 'publicrss'));
- $m->connect('featuredrss', array('action' => 'featuredrss'));
- $m->connect('favoritedrss', array('action' => 'favoritedrss'));
$m->connect('opensearch/people', array('action' => 'opensearch',
'type' => 'people'));
$m->connect('opensearch/notice', array('action' => 'opensearch',
// settings
- foreach (array('profile', 'avatar', 'password', 'im',
- 'email', 'sms', 'userdesign', 'other') as $s) {
+ foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections',
+ 'oauthapps', 'email', 'sms', 'userdesign', 'other') as $s) {
$m->connect('settings/'.$s, array('action' => $s.'settings'));
}
+ $m->connect('settings/oauthapps/show/:id',
+ array('action' => 'showapplication'),
+ array('id' => '[0-9]+')
+ );
+ $m->connect('settings/oauthapps/new',
+ array('action' => 'newapplication')
+ );
+ $m->connect('settings/oauthapps/edit/:id',
+ array('action' => 'editapplication'),
+ array('id' => '[0-9]+')
+ );
+
// search
foreach (array('group', 'people', 'notice') as $s) {
array('action' => 'peopletag'),
array('tag' => '[a-zA-Z0-9]+'));
- $m->connect('featured/', array('action' => 'featured'));
- $m->connect('featured', array('action' => 'featured'));
- $m->connect('favorited/', array('action' => 'favorited'));
- $m->connect('favorited', array('action' => 'favorited'));
-
// groups
$m->connect('group/new', array('action' => 'newgroup'));
$m->connect('api/search.json', array('action' => 'twitapisearchjson'));
$m->connect('api/trends.json', array('action' => 'twitapitrends'));
+ $m->connect('api/oauth/request_token',
+ array('action' => 'apioauthrequesttoken'));
+
+ $m->connect('api/oauth/access_token',
+ array('action' => 'apioauthaccesstoken'));
+
+ $m->connect('api/oauth/authorize',
+ array('action' => 'apioauthauthorize'));
+
+ // Admin
+
$m->connect('admin/site', array('action' => 'siteadminpanel'));
$m->connect('admin/design', array('action' => 'designadminpanel'));
$m->connect('admin/user', array('action' => 'useradminpanel'));
+ $m->connect('admin/access', array('action' => 'accessadminpanel'));
$m->connect('admin/paths', array('action' => 'pathsadminpanel'));
$m->connect('getfile/:filename',
array('action' => 'getfile'),
array('filename' => '[A-Za-z0-9._-]+'));
- // user stuff
+ // In the "root"
- foreach (array('subscriptions', 'subscribers',
- 'nudge', 'all', 'foaf', 'xrds',
- 'replies', 'inbox', 'outbox', 'microsummary') as $a) {
- $m->connect(':nickname/'.$a,
- array('action' => $a),
+ if (common_config('singleuser', 'enabled')) {
+
+ $nickname = common_config('singleuser', 'nickname');
+
+ foreach (array('subscriptions', 'subscribers',
+ 'all', 'foaf', 'xrds',
+ 'replies', 'microsummary') as $a) {
+ $m->connect($a,
+ array('action' => $a,
+ 'nickname' => $nickname));
+ }
+
+ foreach (array('subscriptions', 'subscribers') as $a) {
+ $m->connect($a.'/:tag',
+ array('action' => $a,
+ 'nickname' => $nickname),
+ array('tag' => '[a-zA-Z0-9]+'));
+ }
+
+ foreach (array('rss', 'groups') as $a) {
+ $m->connect($a,
+ array('action' => 'user'.$a,
+ 'nickname' => $nickname));
+ }
+
+ foreach (array('all', 'replies', 'favorites') as $a) {
+ $m->connect($a.'/rss',
+ array('action' => $a.'rss',
+ 'nickname' => $nickname));
+ }
+
+ $m->connect('favorites',
+ array('action' => 'showfavorites',
+ 'nickname' => $nickname));
+
+ $m->connect('avatar/:size',
+ array('action' => 'avatarbynickname',
+ 'nickname' => $nickname),
+ array('size' => '(original|96|48|24)'));
+
+ $m->connect('tag/:tag/rss',
+ array('action' => 'userrss',
+ 'nickname' => $nickname),
+ array('tag' => '[a-zA-Z0-9]+'));
+
+ $m->connect('tag/:tag',
+ array('action' => 'showstream',
+ 'nickname' => $nickname),
+ array('tag' => '[a-zA-Z0-9]+'));
+
+ $m->connect('',
+ array('action' => 'showstream',
+ 'nickname' => $nickname));
+
+ } else {
+
+ $m->connect('', array('action' => 'public'));
+ $m->connect('rss', array('action' => 'publicrss'));
+ $m->connect('featuredrss', array('action' => 'featuredrss'));
+ $m->connect('favoritedrss', array('action' => 'favoritedrss'));
+ $m->connect('featured/', array('action' => 'featured'));
+ $m->connect('featured', array('action' => 'featured'));
+ $m->connect('favorited/', array('action' => 'favorited'));
+ $m->connect('favorited', array('action' => 'favorited'));
+
+ foreach (array('subscriptions', 'subscribers',
+ 'nudge', 'all', 'foaf', 'xrds',
+ 'replies', 'inbox', 'outbox', 'microsummary') as $a) {
+ $m->connect(':nickname/'.$a,
+ array('action' => $a),
+ array('nickname' => '[a-zA-Z0-9]{1,64}'));
+ }
+
+ foreach (array('subscriptions', 'subscribers') as $a) {
+ $m->connect(':nickname/'.$a.'/:tag',
+ array('action' => $a),
+ array('tag' => '[a-zA-Z0-9]+',
+ 'nickname' => '[a-zA-Z0-9]{1,64}'));
+ }
+
+ foreach (array('rss', 'groups') as $a) {
+ $m->connect(':nickname/'.$a,
+ array('action' => 'user'.$a),
+ array('nickname' => '[a-zA-Z0-9]{1,64}'));
+ }
+
+ foreach (array('all', 'replies', 'favorites') as $a) {
+ $m->connect(':nickname/'.$a.'/rss',
+ array('action' => $a.'rss'),
+ array('nickname' => '[a-zA-Z0-9]{1,64}'));
+ }
+
+ $m->connect(':nickname/favorites',
+ array('action' => 'showfavorites'),
array('nickname' => '[a-zA-Z0-9]{1,64}'));
- }
- foreach (array('subscriptions', 'subscribers') as $a) {
- $m->connect(':nickname/'.$a.'/:tag',
- array('action' => $a),
- array('tag' => '[a-zA-Z0-9]+',
+ $m->connect(':nickname/avatar/:size',
+ array('action' => 'avatarbynickname'),
+ array('size' => '(original|96|48|24)',
'nickname' => '[a-zA-Z0-9]{1,64}'));
- }
- foreach (array('rss', 'groups') as $a) {
- $m->connect(':nickname/'.$a,
- array('action' => 'user'.$a),
- array('nickname' => '[a-zA-Z0-9]{1,64}'));
- }
+ $m->connect(':nickname/tag/:tag/rss',
+ array('action' => 'userrss'),
+ array('nickname' => '[a-zA-Z0-9]{1,64}'),
+ array('tag' => '[a-zA-Z0-9]+'));
- foreach (array('all', 'replies', 'favorites') as $a) {
- $m->connect(':nickname/'.$a.'/rss',
- array('action' => $a.'rss'),
+ $m->connect(':nickname/tag/:tag',
+ array('action' => 'showstream'),
+ array('nickname' => '[a-zA-Z0-9]{1,64}'),
+ array('tag' => '[a-zA-Z0-9]+'));
+
+ $m->connect(':nickname',
+ array('action' => 'showstream'),
array('nickname' => '[a-zA-Z0-9]{1,64}'));
}
- $m->connect(':nickname/favorites',
- array('action' => 'showfavorites'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'));
-
- $m->connect(':nickname/avatar/:size',
- array('action' => 'avatarbynickname'),
- array('size' => '(original|96|48|24)',
- 'nickname' => '[a-zA-Z0-9]{1,64}'));
-
- $m->connect(':nickname/tag/:tag/rss',
- array('action' => 'userrss'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'),
- array('tag' => '[a-zA-Z0-9]+'));
-
- $m->connect(':nickname/tag/:tag',
- array('action' => 'showstream'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'),
- array('tag' => '[a-zA-Z0-9]+'));
-
- $m->connect(':nickname',
- array('action' => 'showstream'),
- array('nickname' => '[a-zA-Z0-9]{1,64}'));
+ // user stuff
Event::handle('RouterInitialized', array($m));
}
{
protected $threads=1;
+ const EXIT_OK = 0;
+ const EXIT_ERR = 1;
+ const EXIT_SHUTDOWN = 100;
+ const EXIT_RESTART = 101;
+
function __construct($id=null, $daemonize=true, $threads=1)
{
parent::__construct($daemonize);
/**
* Perform some actual work!
*
- * @return boolean true on success, false on failure
+ * @return int exit code; use self::EXIT_SHUTDOWN to request not to respawn.
*/
public abstract function runThread();
while (count($children) > 0) {
$status = null;
$pid = pcntl_wait($status);
- if ($pid > 0) {
+ if ($pid > 0 && pcntl_wifexited($status)) {
+ $exitCode = pcntl_wexitstatus($status);
+
$i = array_search($pid, $children);
if ($i === false) {
- $this->log(LOG_ERR, "Unrecognized child pid $pid exited!");
+ $this->log(LOG_ERR, "Unrecognized child pid $pid exited with status $exitCode");
continue;
}
unset($children[$i]);
- $this->log(LOG_INFO, "Thread $i pid $pid exited.");
-
- $pid = pcntl_fork();
- if ($pid < 0) {
- $this->log(LOG_ERROR, "Couldn't fork to respawn thread $i; aborting thread.\n");
- } else if ($pid == 0) {
- $this->initAndRunChild($i);
+
+ if ($this->shouldRespawn($exitCode)) {
+ $this->log(LOG_INFO, "Thread $i pid $pid exited with status $exitCode; respawing.");
+
+ $pid = pcntl_fork();
+ if ($pid < 0) {
+ $this->log(LOG_ERROR, "Couldn't fork to respawn thread $i; aborting thread.\n");
+ } else if ($pid == 0) {
+ $this->initAndRunChild($i);
+ } else {
+ $this->log(LOG_INFO, "Respawned thread $i as pid $pid");
+ $children[$i] = $pid;
+ }
} else {
- $this->log(LOG_INFO, "Respawned thread $i as pid $pid");
- $children[$i] = $pid;
+ $this->log(LOG_INFO, "Thread $i pid $pid exited with status $exitCode; closing out thread.");
}
}
}
return true;
}
+ /**
+ * Determine whether to respawn an exited subprocess based on its exit code.
+ * Otherwise we'll respawn all exits by default.
+ *
+ * @param int $exitCode
+ * @return boolean true to respawn
+ */
+ protected function shouldRespawn($exitCode)
+ {
+ if ($exitCode == self::EXIT_SHUTDOWN) {
+ // Thread requested a clean shutdown.
+ return false;
+ } else {
+ // Otherwise we should always respawn!
+ return true;
+ }
+ }
+
/**
* Initialize things for a fresh thread, call runThread(), and
* exit at completion with appropriate return value.
{
$this->set_id($this->get_id() . "." . $thread);
$this->resetDb();
- $ok = $this->runThread();
- exit($ok ? 0 : 1);
+ $exitCode = $this->runThread();
+ exit($exitCode);
}
/**
var $password = null;
var $base = null;
var $con = null;
+ protected $control;
protected $sites = array();
+ protected $subscriptions = array();
protected $useTransactions = true;
protected $transaction = null;
$this->username = common_config('queue', 'stomp_username');
$this->password = common_config('queue', 'stomp_password');
$this->base = common_config('queue', 'queue_basename');
+ $this->control = common_config('queue', 'control_channel');
}
/**
$this->initialize();
}
+ /**
+ * Optional; ping any running queue handler daemons with a notification
+ * such as announcing a new site to handle or requesting clean shutdown.
+ * This avoids having to restart all the daemons manually to update configs
+ * and such.
+ *
+ * Currently only relevant for multi-site queue managers such as Stomp.
+ *
+ * @param string $event event key
+ * @param string $param optional parameter to append to key
+ * @return boolean success
+ */
+ public function sendControlSignal($event, $param='')
+ {
+ $message = $event;
+ if ($param != '') {
+ $message .= ':' . $param;
+ }
+ $this->_connect();
+ $result = $this->con->send($this->control,
+ $message,
+ array ('created' => common_sql_now()));
+ if ($result) {
+ $this->_log(LOG_INFO, "Sent control ping to queue daemons: $message");
+ return true;
+ } else {
+ $this->_log(LOG_ERR, "Failed sending control ping to queue daemons: $message");
+ return false;
+ }
+ }
/**
* Instantiate the appropriate QueueHandler class for the given queue.
*/
function getHandler($queue)
{
- $handlers = $this->handlers[common_config('site', 'server')];
+ $handlers = $this->handlers[$this->currentSite()];
if (isset($handlers[$queue])) {
$class = $handlers[$queue];
if (class_exists($class)) {
function getQueues()
{
$group = $this->activeGroup();
- $site = common_config('site', 'server');
+ $site = $this->currentSite();
if (empty($this->groups[$site][$group])) {
return array();
} else {
*/
public function connect($transport, $class, $group='queuedaemon')
{
- $this->handlers[common_config('site', 'server')][$transport] = $class;
- $this->groups[common_config('site', 'server')][$group][$transport] = $class;
+ $this->handlers[$this->currentSite()][$transport] = $class;
+ $this->groups[$this->currentSite()][$group][$transport] = $class;
}
/**
$result = $this->con->send($this->queueName($queue),
$msg, // BODY of the message
- array ('created' => common_sql_now()));
+ array ('created' => common_sql_now(),
+ 'persistent' => 'true'));
if (!$result) {
common_log(LOG_ERR, "Error sending $rep to $queue queue");
$ok = true;
$frames = $this->con->readFrames();
foreach ($frames as $frame) {
- $ok = $ok && $this->_handleItem($frame);
+ $dest = $frame->headers['destination'];
+ if ($dest == $this->control) {
+ if (!$this->handleControlSignal($frame)) {
+ // We got a control event that requests a shutdown;
+ // close out and stop handling anything else!
+ break;
+ }
+ } else {
+ $ok = $ok && $this->handleItem($frame);
+ }
}
return $ok;
}
public function start($master)
{
parent::start($master);
+ $this->_connect();
+
+ $this->con->subscribe($this->control);
if ($this->sites) {
foreach ($this->sites as $server) {
StatusNet::init($server);
// If there are any outstanding delivered messages we haven't processed,
// free them for another thread to take.
$this->rollback();
+ $this->con->unsubscribe($this->control);
if ($this->sites) {
foreach ($this->sites as $server) {
StatusNet::init($server);
}
return true;
}
-
+
+ /**
+ * Get identifier of the currently active site configuration
+ * @return string
+ */
+ protected function currentSite()
+ {
+ return common_config('site', 'server'); // @fixme switch to nickname
+ }
+
/**
* Lazy open connection to Stomp queue server.
*/
*/
protected function doSubscribe()
{
+ $site = $this->currentSite();
$this->_connect();
foreach ($this->getQueues() as $queue) {
$rawqueue = $this->queueName($queue);
+ $this->subscriptions[$site][$queue] = $rawqueue;
$this->_log(LOG_INFO, "Subscribing to $rawqueue");
$this->con->subscribe($rawqueue);
}
}
-
+
/**
* Subscribe from all enabled notice queues for the current site.
*/
protected function doUnsubscribe()
{
+ $site = $this->currentSite();
$this->_connect();
- foreach ($this->getQueues() as $queue) {
- $this->con->unsubscribe($this->queueName($queue));
+ if (!empty($this->subscriptions[$site])) {
+ foreach ($this->subscriptions[$site] as $queue => $rawqueue) {
+ $this->_log(LOG_INFO, "Unsubscribing from $rawqueue");
+ $this->con->unsubscribe($rawqueue);
+ unset($this->subscriptions[$site][$queue]);
+ }
}
}
* @param StompFrame $frame
* @return bool
*/
- protected function _handleItem($frame)
+ protected function handleItem($frame)
{
list($site, $queue) = $this->parseDestination($frame->headers['destination']);
- if ($site != common_config('site', 'server')) {
+ if ($site != $this->currentSite()) {
$this->stats('switch');
StatusNet::init($site);
}
$handler = $this->getHandler($queue);
if (!$handler) {
- $this->_log(LOG_ERROR, "Missing handler class; skipping $info");
+ $this->_log(LOG_ERR, "Missing handler class; skipping $info");
$this->ack($frame);
$this->commit();
$this->begin();
return true;
}
+ /**
+ * Process a control signal broadcast.
+ *
+ * @param array $frame Stomp frame
+ * @return bool true to continue; false to stop further processing.
+ */
+ protected function handleControlSignal($frame)
+ {
+ $message = trim($frame->body);
+ if (strpos($message, ':') !== false) {
+ list($event, $param) = explode(':', $message, 2);
+ } else {
+ $event = $message;
+ $param = '';
+ }
+
+ $shutdown = false;
+
+ if ($event == 'shutdown') {
+ $this->master->requestShutdown();
+ $shutdown = true;
+ } else if ($event == 'restart') {
+ $this->master->requestRestart();
+ $shutdown = true;
+ } else if ($event == 'update') {
+ $this->updateSiteConfig($param);
+ } else {
+ $this->_log(LOG_ERR, "Ignoring unrecognized control message: $message");
+ }
+
+ $this->ack($frame);
+ $this->commit();
+ $this->begin();
+ return $shutdown;
+ }
+
+ /**
+ * Set us up with queue subscriptions for a new site added at runtime,
+ * triggered by a broadcast to the 'statusnet-control' topic.
+ *
+ * @param array $frame Stomp frame
+ * @return bool true to continue; false to stop further processing.
+ */
+ protected function updateSiteConfig($nickname)
+ {
+ if (empty($this->sites)) {
+ if ($nickname == common_config('site', 'nickname')) {
+ StatusNet::init(common_config('site', 'server'));
+ $this->doUnsubscribe();
+ $this->doSubscribe();
+ } else {
+ $this->_log(LOG_INFO, "Ignoring update ping for other site $nickname");
+ }
+ } else {
+ $sn = Status_network::staticGet($nickname);
+ if ($sn) {
+ $server = $sn->getServerName(); // @fixme do config-by-nick
+ StatusNet::init($server);
+ if (empty($this->sites[$server])) {
+ $this->addSite($server);
+ }
+ $this->_log(LOG_INFO, "(Re)subscribing to queues for site $nickname / $server");
+ $this->doUnsubscribe();
+ $this->doSubscribe();
+ $this->stats('siteupdate');
+ } else {
+ $this->_log(LOG_ERR, "Ignoring ping for unrecognized new site $nickname");
+ }
+ }
+ }
+
/**
* Combines the queue_basename from configuration with the
* site server name and queue name to give eg:
protected function queueName($queue)
{
return common_config('queue', 'queue_basename') .
- common_config('site', 'server') . '/' . $queue;
+ $this->currentSite() . '/' . $queue;
}
/**
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * UAP (Universal Ad Package) plugin
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Sarven Capadisli <csarven@status.net>
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Abstract superclass for advertising plugins
+ *
+ * Plugins for showing ads should derive from this plugin.
+ *
+ * Outputs the following ad types (based on UAP):
+ *
+ * Medium Rectangle 300x250
+ * Rectangle 180x150
+ * Leaderboard 728x90
+ * Wide Skyscraper 160x600
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Sarven Capadisli <csarven@status.net>
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+abstract class UAPPlugin extends Plugin
+{
+ public $mediumRectangle = null;
+ public $rectangle = null;
+ public $leaderboard = null;
+ public $wideSkyscraper = null;
+
+ /**
+ * Output our dedicated stylesheet
+ *
+ * @param Action $action Action being shown
+ *
+ * @return boolean hook flag
+ */
+
+ function onEndShowStatusNetStyles($action)
+ {
+ // XXX: allow override by theme
+ $action->cssLink('css/uap.css', 'base', 'screen, projection, tv');
+ return true;
+ }
+
+ /**
+ * Add a medium rectangle ad at the beginning of sidebar
+ *
+ * @param Action $action Action being shown
+ *
+ * @return boolean hook flag
+ */
+
+ function onStartShowAside($action)
+ {
+ if (!is_null($this->mediumRectangle)) {
+
+ $action->elementStart('div',
+ array('id' => 'ad_medium-rectangle',
+ 'class' => 'ad'));
+
+ $this->showMediumRectangle($action);
+
+ $action->elementEnd('div');
+ }
+
+ return true;
+ }
+
+ /**
+ * Add a leaderboard in the header
+ *
+ * @param Action $action Action being shown
+ *
+ * @return boolean hook flag
+ */
+
+ function onEndShowHeader($action)
+ {
+ if (!is_null($this->leaderboard)) {
+ $action->elementStart('div',
+ array('id' => 'ad_leaderboard',
+ 'class' => 'ad'));
+ $this->showLeaderboard($action);
+ $action->elementEnd('div');
+ }
+
+ return true;
+ }
+
+ /**
+ * Add a rectangle before aside sections
+ *
+ * @param Action $action Action being shown
+ *
+ * @return boolean hook flag
+ */
+
+ function onStartShowSections($action)
+ {
+ if (!is_null($this->rectangle)) {
+ $action->elementStart('div',
+ array('id' => 'ad_rectangle',
+ 'class' => 'ad'));
+ $this->showRectangle($action);
+ $action->elementEnd('div');
+ }
+
+ return true;
+ }
+
+ /**
+ * Add a wide skyscraper after the aside
+ *
+ * @param Action $action Action being shown
+ *
+ * @return boolean hook flag
+ */
+
+ function onEndShowAside($action)
+ {
+ if (!is_null($this->wideSkyscraper)) {
+ $action->elementStart('div',
+ array('id' => 'ad_wide-skyscraper',
+ 'class' => 'ad'));
+
+ $this->showWideSkyscraper($action);
+
+ $action->elementEnd('div');
+ }
+ return true;
+ }
+
+ /**
+ * Show a medium rectangle ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ abstract protected function showMediumRectangle($action);
+
+ /**
+ * Show a rectangle ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ abstract protected function showRectangle($action);
+
+ /**
+ * Show a wide skyscraper ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ abstract protected function showWideSkyscraper($action);
+
+ /**
+ * Show a leaderboard ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ abstract protected function showLeaderboard($action);
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin for Google Adsense
+ *
+ * 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 Ads
+ * @package StatusNet
+ * @author Evan Prodromou <evan@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);
+}
+
+/**
+ * Plugin to add Google Adsense to StatusNet sites
+ *
+ * This plugin lets you add Adsense ad units to your StatusNet site.
+ *
+ * We support the 4 ad sizes for the Universal Ad Platform (UAP):
+ *
+ * Medium Rectangle
+ * (Small) Rectangle
+ * Leaderboard
+ * Wide Skyscraper
+ *
+ * They fit in different places on the default theme. Some themes
+ * might interact quite poorly with this plugin.
+ *
+ * To enable advertising, you must sign up with Google Adsense and
+ * get a client ID.
+ *
+ * https://www.google.com/adsense/
+ *
+ * You'll also need to create an Adsense for Content unit in one
+ * of the four sizes described above. At the end of the process,
+ * note the "google_ad_client" and "google_ad_slot" values in the
+ * resultant Javascript.
+ *
+ * Add the plugin to config.php like so:
+ *
+ * addPlugin('Adsense', array('client' => 'Your client ID',
+ * 'rectangle' => 'slot'));
+ *
+ * Here, your client ID is the value of google_ad_client and the
+ * slot is the value of google_ad_slot. Note that if you create
+ * a different size, you'll need to provide different arguments:
+ * 'mediumRectangle', 'leaderboard', or 'wideSkyscraper'.
+ *
+ * If for some reason your ad server is different from the default,
+ * use the 'adScript' parameter to set the full path to the ad script.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @seeAlso UAPPlugin
+ */
+
+class AdsensePlugin extends UAPPlugin
+{
+ public $adScript = 'http://pagead2.googlesyndication.com/pagead/show_ads.js';
+ public $client = null;
+
+ /**
+ * Show a medium rectangle 'ad'
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showMediumRectangle($action)
+ {
+ $this->showAdsenseCode($action, 300, 250, $this->mediumRectangle);
+ }
+
+ /**
+ * Show a rectangle 'ad'
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showRectangle($action)
+ {
+ $this->showAdsenseCode($action, 180, 150, $this->rectangle);
+ }
+
+ /**
+ * Show a wide skyscraper ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showWideSkyscraper($action)
+ {
+ $this->showAdsenseCode($action, 160, 600, $this->wideSkyscraper);
+ }
+
+ /**
+ * Show a leaderboard ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showLeaderboard($action)
+ {
+ $this->showAdsenseCode($action, 728, 90, $this->leaderboard);
+ }
+
+ /**
+ * Output the bits of JavaScript code to show Adsense
+ *
+ * @param Action $action Action being shown
+ * @param integer $width Width of the block
+ * @param integer $height Height of the block
+ * @param string $slot Slot identifier
+ *
+ * @return void
+ */
+
+ protected function showAdsenseCode($action, $width, $height, $slot)
+ {
+ $code = 'google_ad_client = "'.$this->client.'"; ';
+ $code .= 'google_ad_slot = "'.$slot.'"; ';
+ $code .= 'google_ad_width = '.$width.'; ';
+ $code .= 'google_ad_height = '.$height.'; ';
+
+ $action->inlineScript($code);
+
+ $action->script($this->adScript);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin for testing ad layout
+ *
+ * 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 Ads
+ * @package StatusNet
+ * @author Evan Prodromou <evan@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);
+}
+
+/**
+ * Plugin for testing ad layout
+ *
+ * This plugin uses the UAPPlugin framework to output ad content. However,
+ * its ad content is just images with one red pixel stretched to the
+ * right size. It's mostly useful for debugging theme layout.
+ *
+ * To use this plugin, set the parameter for the ad size you want to use
+ * to true (or anything non-null). For example, to make a leaderboard:
+ *
+ * addPlugin('BlankAd', array('leaderboard' => true));
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @seeAlso Location
+ */
+
+class BlankAdPlugin extends UAPPlugin
+{
+ /**
+ * Show a medium rectangle 'ad'
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showMediumRectangle($action)
+ {
+ $action->element('img',
+ array('width' => 300,
+ 'height' => 250,
+ 'src' => common_path('plugins/BlankAd/redpixel.png')),
+ '');
+ }
+
+ /**
+ * Show a rectangle 'ad'
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showRectangle($action)
+ {
+ $action->element('img',
+ array('width' => 180,
+ 'height' => 150,
+ 'src' => common_path('plugins/BlankAd/redpixel.png')),
+ '');
+ }
+
+ /**
+ * Show a wide skyscraper ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showWideSkyscraper($action)
+ {
+ $action->element('img',
+ array('width' => 160,
+ 'height' => 600,
+ 'src' => common_path('plugins/BlankAd/redpixel.png')),
+ '');
+ }
+
+ /**
+ * Show a leaderboard ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showLeaderboard($action)
+ {
+ $action->element('img',
+ array('width' => 728,
+ 'height' => 90,
+ 'src' => common_path('plugins/BlankAd/redpixel.png')),
+ '');
+ }
+}
\ No newline at end of file
function showScripts()
{
- $this->script('js/facebookapp.js');
+ $this->script('facebookapp.js');
}
/**
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Plugin for OpenX ad server
+ *
+ * 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 Ads
+ * @package StatusNet
+ * @author Evan Prodromou <evan@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);
+}
+
+/**
+ * Plugin for OpenX Ad Server
+ *
+ * This plugin supports the OpenX ad server, http://www.openx.org/
+ *
+ * We support the 4 ad sizes for the Universal Ad Platform (UAP):
+ *
+ * Medium Rectangle
+ * (Small) Rectangle
+ * Leaderboard
+ * Wide Skyscraper
+ *
+ * They fit in different places on the default theme. Some themes
+ * might interact quite poorly with this plugin.
+ *
+ * To enable advertising, you will need an OpenX server. You'll need
+ * to set up a "zone" for your StatusNet site that identifies a
+ * kind of ad you want to place (of the above 4 sizes).
+ *
+ * Add the plugin to config.php like so:
+ *
+ * addPlugin('OpenX', array('adScript' => 'full path to script',
+ * 'rectangle' => 1));
+ *
+ * Here, the 'adScript' parameter is the full path to the OpenX
+ * ad script, like 'http://example.com/www/delivery/ajs.php'. Note
+ * that we don't do any magic to swap between HTTP and HTTPS, so
+ * if you want HTTPS, say so.
+ *
+ * The 'rectangle' parameter is the zone ID for that ad space on
+ * your site. If you've configured another size, try 'mediumRectangle',
+ * 'leaderboard', or 'wideSkyscraper'.
+ *
+ * If for some reason your ad server is different from the default,
+ * use the 'adScript' parameter to set the full path to the ad script.
+ *
+ * @category Ads
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @seeAlso UAPPlugin
+ */
+
+class OpenXPlugin extends UAPPlugin
+{
+ public $adScript = null;
+
+ /**
+ * Show a medium rectangle 'ad'
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showMediumRectangle($action)
+ {
+ $this->showAd($action, $this->mediumRectangle);
+ }
+
+ /**
+ * Show a rectangle 'ad'
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showRectangle($action)
+ {
+ $this->showAd($action, $this->rectangle);
+ }
+
+ /**
+ * Show a wide skyscraper ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showWideSkyscraper($action)
+ {
+ $this->showAd($action, $this->wideSkyscraper);
+ }
+
+ /**
+ * Show a leaderboard ad
+ *
+ * @param Action $action Action being shown
+ *
+ * @return void
+ */
+
+ protected function showLeaderboard($action)
+ {
+ $this->showAd($action, $this->leaderboard);
+ }
+
+ /**
+ * Show an ad using OpenX
+ *
+ * @param Action $action Action being shown
+ * @param integer $zone Zone to show
+ *
+ * @return void
+ */
+
+ protected function showAd($action, $zone)
+ {
+$scr = <<<ENDOFSCRIPT
+var m3_u = '%s';
+var m3_r = Math.floor(Math.random()*99999999999);
+if (!document.MAX_used) document.MAX_used = ',';
+document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
+document.write ("?zoneid=%d");
+document.write ('&cb=' + m3_r);
+if (document.MAX_used != ',') document.write ("&exclude=" + document.MAX_used);
+document.write (document.charset ? '&charset='+document.charset : (document.characterSet ? '&charset='+document.characterSet : ''));
+document.write ("&loc=" + escape(window.location));
+if (document.referrer) document.write ("&referer=" + escape(document.referrer));
+if (document.context) document.write ("&context=" + escape(document.context));
+if (document.mmm_fo) document.write ("&mmm_fo=1");
+document.write ("'><\/scr"+"ipt>");
+ENDOFSCRIPT;
+
+ $action->inlineScript(sprintf($scr, $this->adScript, $zone));
+ return true;
+ }
+}
\ No newline at end of file
function onEndAddressData($action)
{
$action->elementStart('span', 'poweredby');
- $action->text(_('powered by'));
- $action->element('a', array('href' => 'http://status.net/'), 'StatusNet');
+ $action->raw(sprintf(_m('powered by %s'),
+ sprintf('<a href="http://status.net/">%s</a>',
+ _m('StatusNet'))));
$action->elementEnd('span');
return true;
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-01-22 15:03-0800\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: PoweredByStatusNetPlugin.php:49
+#, php-format
+msgid "powered by %s"
+msgstr ""
+
+#: PoweredByStatusNetPlugin.php:51
+msgid "StatusNet"
+msgstr ""
+
+#: PoweredByStatusNetPlugin.php:64
+msgid ""
+"Outputs powered by <a href=\"http://status.net/\">StatusNet</a> after site "
+"name."
+msgstr ""
parent::__construct();
}
+ /**
+ * Check if plugin should be active; may be mass-enabled.
+ * @return boolean
+ */
+
+ function enabled()
+ {
+ if (common_config('site', 'private')) {
+ // PuSH relies on public feeds
+ return false;
+ }
+ // @fixme check for being on a private network?
+ return true;
+ }
+
/**
* Hooks the StartApiAtom event
*
function onStartApiAtom($action)
{
- $action->element('link', array('rel' => 'hub', 'href' => $this->hub), null);
-
+ if ($this->enabled()) {
+ $action->element('link', array('rel' => 'hub', 'href' => $this->hub), null);
+ }
return true;
}
function onStartApiRss($action)
{
- $action->element('atom:link', array('rel' => 'hub',
- 'href' => $this->hub),
- null);
+ if ($this->enabled()) {
+ $action->element('atom:link', array('rel' => 'hub',
+ 'href' => $this->hub),
+ null);
+ }
return true;
}
function onHandleQueuedNotice($notice)
{
+ if (!$this->enabled()) {
+ return false;
+ }
$publisher = new Publisher($this->hub);
$feeds = array();
'format' => 'atom'));
}
}
-
- foreach (array_unique($feeds) as $feed) {
- if (!$publisher->publish_update($feed)) {
- common_log_line(LOG_WARNING,
- $feed.' was not published to hub at '.
- $this->hub.':'.$publisher->last_response());
- }
+ $feeds = array_unique($feeds);
+
+ ob_start();
+ $ok = $publisher->publish_update($feeds);
+ $push_last_response = ob_get_clean();
+
+ if (!$ok) {
+ common_log(LOG_WARNING,
+ 'Failure publishing ' . count($feeds) . ' feeds to hub at '.
+ $this->hub.': '.$push_last_response);
+ } else {
+ common_log(LOG_INFO,
+ 'Published ' . count($feeds) . ' feeds to hub at '.
+ $this->hub.': '.$push_last_response);
}
return true;
function onPluginVersion(&$versions)
{
+ $about = _m('The PubSubHubBub plugin pushes RSS/Atom updates '.
+ 'to a <a href = "'.
+ 'http://pubsubhubbub.googlecode.com/'.
+ '">PubSubHubBub</a> hub.');
+ if (!$this->enabled()) {
+ $about = '<span class="disabled" style="color:gray">' . $about . '</span> ' .
+ _m('(inactive on private site)');
+ }
$versions[] = array('name' => 'PubSubHubBub',
'version' => STATUSNET_VERSION,
'author' => 'Craig Andrews',
'homepage' =>
'http://status.net/wiki/Plugin:PubSubHubBub',
'rawdescription' =>
- _m('The PubSubHubBub plugin pushes RSS/Atom updates '.
- 'to a <a href = "'.
- 'http://pubsubhubbub.googlecode.com/'.
- '">PubSubHubBub</a> hub.'));
+ $about);
return true;
}
$scripts = $this->_getScripts();
foreach ($scripts as $script) {
- $action->script($script);
+ $action->script(common_path($script));
}
$user = common_current_user();
* @category Plugin
* @package StatusNet
* @author Zach Copley <zach@status.net>
- * @copyright 2009 Control Yourself, Inc.
+ * @author Julien C <chaumond@gmail.com>
+ * @copyright 2009-2010 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
* @category Plugin
* @package StatusNet
* @author Zach Copley <zach@status.net>
+ * @author Julien C <chaumond@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
* @link http://twitter.com/
$m->connect('twitter/authorization',
array('action' => 'twitterauthorization'));
$m->connect('settings/twitter', array('action' => 'twittersettings'));
+ $m->connect('main/twitterlogin', array('action' => 'twitterlogin'));
+
+ return true;
+ }
+
+ /*
+ * Add a login tab for 'Sign in with Twitter'
+ *
+ * @param Action &action the current action
+ *
+ * @return void
+ */
+ function onEndLoginGroupNav(&$action)
+ {
+
+ $action_name = $action->trimmed('action');
+
+ $action->menuItem(common_local_url('twitterlogin'),
+ _('Twitter'),
+ _('Login or register using Twitter'),
+ 'twitterlogin' === $action_name);
return true;
}
switch ($cls) {
case 'TwittersettingsAction':
case 'TwitterauthorizationAction':
+ case 'TwitterloginAction':
include_once INSTALLDIR . '/plugins/TwitterBridge/' .
strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;
* 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 TwitterauthorizationAction
+ * @category Plugin
* @package StatusNet
- * @author Zach Copely <zach@status.net>
- * @copyright 2009 StatusNet, Inc.
+ * @author Zach Copley <zach@status.net>
+ * @author Julien C <chaumond@gmail.com>
+ * @copyright 2009-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/
*/
* (Foreign_link) between the StatusNet user and Twitter user and stores the
* access token and secret in the link.
*
- * @category Twitter
+ * @category Plugin
* @package StatusNet
* @author Zach Copley <zach@status.net>
+ * @author Julien C <chaumond@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*
*/
class TwitterauthorizationAction extends Action
{
+ var $twuid = null;
+ var $tw_fields = null;
+ var $access_token = null;
+ var $signin = null;
+
/**
* Initialize class members. Looks for 'oauth_token' parameter.
*
{
parent::prepare($args);
+ $this->signin = $this->boolean('signin');
$this->oauth_token = $this->arg('oauth_token');
return true;
{
parent::handle($args);
- if (!common_logged_in()) {
- $this->clientError(_m('Not logged in.'), 403);
+ if (common_logged_in()) {
+ $user = common_current_user();
+ $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
+
+ // If there's already a foreign link record, it means we already
+ // have an access token, and this is unecessary. So go back.
+
+ if (isset($flink)) {
+ common_redirect(common_local_url('twittersettings'));
+ }
}
- $user = common_current_user();
- $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- // If there's already a foreign link record, it means we already
- // have an access token, and this is unecessary. So go back.
+ // User was not logged in to StatusNet before
- if (isset($flink)) {
- common_redirect(common_local_url('twittersettings'));
- }
+ $this->twuid = $this->trimmed('twuid');
+
+ $this->tw_fields = array('screen_name' => $this->trimmed('tw_fields_screen_name'),
+ 'fullname' => $this->trimmed('tw_fields_fullname'));
+
+ $this->access_token = new OAuthToken($this->trimmed('access_token_key'), $this->trimmed('access_token_secret'));
+
+ $token = $this->trimmed('token');
- // $this->oauth_token is only populated once Twitter authorizes our
- // request token. If it's empty we're at the beginning of the auth
- // process
+ if (!$token || $token != common_session_token()) {
+ $this->showForm(_('There was a problem with your session token. Try again, please.'));
+ return;
+ }
- if (empty($this->oauth_token)) {
- $this->authorizeRequestToken();
+ if ($this->arg('create')) {
+ if (!$this->boolean('license')) {
+ $this->showForm(_('You can\'t register if you don\'t agree to the license.'),
+ $this->trimmed('newname'));
+ return;
+ }
+ $this->createNewUser();
+ } else if ($this->arg('connect')) {
+ $this->connectNewUser();
+ } else {
+ common_debug('Twitter Connect Plugin - ' .
+ print_r($this->args, true));
+ $this->showForm(_('Something weird happened.'),
+ $this->trimmed('newname'));
+ }
} else {
- $this->saveAccessToken();
+ // $this->oauth_token is only populated once Twitter authorizes our
+ // request token. If it's empty we're at the beginning of the auth
+ // process
+
+ if (empty($this->oauth_token)) {
+ $this->authorizeRequestToken();
+ } else {
+ $this->saveAccessToken();
+ }
}
}
$_SESSION['twitter_request_token'] = $req_tok->key;
$_SESSION['twitter_request_token_secret'] = $req_tok->secret;
- $auth_link = $client->getAuthorizeLink($req_tok);
+ $auth_link = $client->getAuthorizeLink($req_tok, $this->signin);
} catch (OAuthClientException $e) {
$msg = sprintf('OAuth client cURL error - code: %1s, msg: %2s',
$this->serverError(_m('Couldn\'t link your Twitter account.'));
}
+ $twitter_user = null;
+
try {
$client = new TwitterOAuthClient($_SESSION['twitter_request_token'],
$twitter_user = $client->verifyCredentials();
} catch (OAuthClientException $e) {
- $msg = sprintf('OAuth client cURL error - code: %1$s, msg: %2$s',
+ $msg = sprintf('OAuth client error - code: %1$s, msg: %2$s',
$e->getCode(), $e->getMessage());
$this->serverError(_m('Couldn\'t link your Twitter account.'));
}
- // Save the access token and Twitter user info
+ if (common_logged_in()) {
+
+ // Save the access token and Twitter user info
+
+ $user = common_current_user();
+ $this->saveForeignLink($user->id, $twitter_user->id, $atok);
+ save_twitter_user($twitter_user->id, $twitter_user->name);
+
+ } else {
- $this->saveForeignLink($atok, $twitter_user);
+ $this->twuid = $twitter_user->id;
+ $this->tw_fields = array("screen_name" => $twitter_user->screen_name,
+ "name" => $twitter_user->name);
+ $this->access_token = $atok;
+ $this->tryLogin();
+ }
// Clean up the the mess we made in the session
unset($_SESSION['twitter_request_token']);
unset($_SESSION['twitter_request_token_secret']);
- common_redirect(common_local_url('twittersettings'));
+ if (common_logged_in()) {
+ common_redirect(common_local_url('twittersettings'));
+ }
}
/**
* Saves a Foreign_link between Twitter user and local user,
* which includes the access token and secret.
*
- * @param OAuthToken $access_token the access token to save
- * @param mixed $twitter_user twitter API user object
+ * @param int $user_id StatusNet user ID
+ * @param int $twuid Twitter user ID
+ * @param OAuthToken $token the access token to save
*
* @return nothing
*/
- function saveForeignLink($access_token, $twitter_user)
+ function saveForeignLink($user_id, $twuid, $access_token)
{
- $user = common_current_user();
-
$flink = new Foreign_link();
- $flink->user_id = $user->id;
- $flink->foreign_id = $twitter_user->id;
+ $flink->user_id = $user_id;
+ $flink->foreign_id = $twuid;
$flink->service = TWITTER_SERVICE;
$creds = TwitterOAuthClient::packToken($access_token);
if (empty($flink_id)) {
common_log_db_error($flink, 'INSERT', __FILE__);
- $this->serverError(_m('Couldn\'t link your Twitter account.'));
+ $this->serverError(_('Couldn\'t link your Twitter account.'));
+ }
+
+ return $flink_id;
+ }
+
+ function showPageNotice()
+ {
+ if ($this->error) {
+ $this->element('div', array('class' => 'error'), $this->error);
+ } else {
+ $this->element('div', 'instructions',
+ sprintf(_('This is the first time you\'ve logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
+ }
+ }
+
+ function title()
+ {
+ return _('Twitter Account Setup');
+ }
+
+ function showForm($error=null, $username=null)
+ {
+ $this->error = $error;
+ $this->username = $username;
+
+ $this->showPage();
+ }
+
+ function showPage()
+ {
+ parent::showPage();
+ }
+
+ function showContent()
+ {
+ if (!empty($this->message_text)) {
+ $this->element('p', null, $this->message);
+ return;
+ }
+
+ $this->elementStart('form', array('method' => 'post',
+ 'id' => 'form_settings_twitter_connect',
+ 'class' => 'form_settings',
+ 'action' => common_local_url('twitterauthorization')));
+ $this->elementStart('fieldset', array('id' => 'settings_twitter_connect_options'));
+ $this->element('legend', null, _('Connection options'));
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+ $this->element('input', array('type' => 'checkbox',
+ 'id' => 'license',
+ 'class' => 'checkbox',
+ 'name' => 'license',
+ 'value' => 'true'));
+ $this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
+ $this->text(_('My text and files are available under '));
+ $this->element('a', array('href' => common_config('license', 'url')),
+ common_config('license', 'title'));
+ $this->text(_(' except this private data: password, email address, IM address, phone number.'));
+ $this->elementEnd('label');
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+ $this->hidden('access_token_key', $this->access_token->key);
+ $this->hidden('access_token_secret', $this->access_token->secret);
+ $this->hidden('twuid', $this->twuid);
+ $this->hidden('tw_fields_screen_name', $this->tw_fields['screen_name']);
+ $this->hidden('tw_fields_name', $this->tw_fields['name']);
+
+ $this->elementStart('fieldset');
+ $this->hidden('token', common_session_token());
+ $this->element('legend', null,
+ _('Create new account'));
+ $this->element('p', null,
+ _('Create a new user with this nickname.'));
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+ $this->input('newname', _('New nickname'),
+ ($this->username) ? $this->username : '',
+ _('1-64 lowercase letters or numbers, no punctuation or spaces'));
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+ $this->submit('create', _('Create'));
+ $this->elementEnd('fieldset');
+
+ $this->elementStart('fieldset');
+ $this->element('legend', null,
+ _('Connect existing account'));
+ $this->element('p', null,
+ _('If you already have an account, login with your username and password to connect it to your Twitter account.'));
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+ $this->input('nickname', _('Existing nickname'));
+ $this->elementEnd('li');
+ $this->elementStart('li');
+ $this->password('password', _('Password'));
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+ $this->submit('connect', _('Connect'));
+ $this->elementEnd('fieldset');
+
+ $this->elementEnd('fieldset');
+ $this->elementEnd('form');
+ }
+
+ function message($msg)
+ {
+ $this->message_text = $msg;
+ $this->showPage();
+ }
+
+ function createNewUser()
+ {
+ if (common_config('site', 'closed')) {
+ $this->clientError(_('Registration not allowed.'));
+ return;
+ }
+
+ $invite = null;
+
+ if (common_config('site', 'inviteonly')) {
+ $code = $_SESSION['invitecode'];
+ if (empty($code)) {
+ $this->clientError(_('Registration not allowed.'));
+ return;
+ }
+
+ $invite = Invitation::staticGet($code);
+
+ if (empty($invite)) {
+ $this->clientError(_('Not a valid invitation code.'));
+ return;
+ }
+ }
+
+ $nickname = $this->trimmed('newname');
+
+ if (!Validate::string($nickname, array('min_length' => 1,
+ 'max_length' => 64,
+ 'format' => NICKNAME_FMT))) {
+ $this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.'));
+ return;
+ }
+
+ if (!User::allowed_nickname($nickname)) {
+ $this->showForm(_('Nickname not allowed.'));
+ return;
+ }
+
+ if (User::staticGet('nickname', $nickname)) {
+ $this->showForm(_('Nickname already in use. Try another one.'));
+ return;
+ }
+
+ $fullname = trim($this->tw_fields['name']);
+
+ $args = array('nickname' => $nickname, 'fullname' => $fullname);
+
+ if (!empty($invite)) {
+ $args['code'] = $invite->code;
+ }
+
+ $user = User::register($args);
+
+ $result = $this->saveForeignLink($user->id,
+ $this->twuid,
+ $this->access_token);
+
+ save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
+
+ if (!$result) {
+ $this->serverError(_('Error connecting user to Twitter.'));
+ return;
+ }
+
+ common_set_user($user);
+ common_real_login(true);
+
+ common_debug('TwitterBridge Plugin - ' .
+ "Registered new user $user->id from Twitter user $this->twuid");
+
+ common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
+ 303);
+ }
+
+ function connectNewUser()
+ {
+ $nickname = $this->trimmed('nickname');
+ $password = $this->trimmed('password');
+
+ if (!common_check_user($nickname, $password)) {
+ $this->showForm(_('Invalid username or password.'));
+ return;
+ }
+
+ $user = User::staticGet('nickname', $nickname);
+
+ if (!empty($user)) {
+ common_debug('TwitterBridge Plugin - ' .
+ "Legit user to connect to Twitter: $nickname");
+ }
+
+ $result = $this->saveForeignLink($user->id,
+ $this->twuid,
+ $this->access_token);
+
+ save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
+
+ if (!$result) {
+ $this->serverError(_('Error connecting user to Twitter.'));
+ return;
+ }
+
+ common_debug('TwitterBridge Plugin - ' .
+ "Connected Twitter user $this->twuid to local user $user->id");
+
+ common_set_user($user);
+ common_real_login(true);
+
+ $this->goHome($user->nickname);
+ }
+
+ function connectUser()
+ {
+ $user = common_current_user();
+
+ $result = $this->flinkUser($user->id, $this->twuid);
+
+ if (empty($result)) {
+ $this->serverError(_('Error connecting user to Twitter.'));
+ return;
}
- save_twitter_user($twitter_user->id, $twitter_user->screen_name);
+ common_debug('TwitterBridge Plugin - ' .
+ "Connected Twitter user $this->twuid to local user $user->id");
+
+ // Return to Twitter connection settings tab
+ common_redirect(common_local_url('twittersettings'), 303);
+ }
+
+ function tryLogin()
+ {
+ common_debug('TwitterBridge Plugin - ' .
+ "Trying login for Twitter user $this->twuid.");
+
+ $flink = Foreign_link::getByForeignID($this->twuid,
+ TWITTER_SERVICE);
+
+ if (!empty($flink)) {
+ $user = $flink->getUser();
+
+ if (!empty($user)) {
+
+ common_debug('TwitterBridge Plugin - ' .
+ "Logged in Twitter user $flink->foreign_id as user $user->id ($user->nickname)");
+
+ common_set_user($user);
+ common_real_login(true);
+ $this->goHome($user->nickname);
+ }
+
+ } else {
+
+ common_debug('TwitterBridge Plugin - ' .
+ "No flink found for twuid: $this->twuid - new user");
+
+ $this->showForm(null, $this->bestNewNickname());
+ }
+ }
+
+ function goHome($nickname)
+ {
+ $url = common_get_returnto();
+ if ($url) {
+ // We don't have to return to it again
+ common_set_returnto(null);
+ } else {
+ $url = common_local_url('all',
+ array('nickname' =>
+ $nickname));
+ }
+
+ common_redirect($url, 303);
+ }
+
+ function bestNewNickname()
+ {
+ if (!empty($this->tw_fields['name'])) {
+ $nickname = $this->nicknamize($this->tw_fields['name']);
+ if ($this->isNewNickname($nickname)) {
+ return $nickname;
+ }
+ }
+
+ return null;
+ }
+
+ // Given a string, try to make it work as a nickname
+
+ function nicknamize($str)
+ {
+ $str = preg_replace('/\W/', '', $str);
+ $str = str_replace(array('-', '_'), '', $str);
+ return strtolower($str);
+ }
+
+ function isNewNickname($str)
+ {
+ if (!Validate::string($str, array('min_length' => 1,
+ 'max_length' => 64,
+ 'format' => NICKNAME_FMT))) {
+ return false;
+ }
+ if (!User::allowed_nickname($str)) {
+ return false;
+ }
+ if (User::staticGet('nickname', $str)) {
+ return false;
+ }
+ return true;
}
}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * 'Sign in with Twitter' login page
+ *
+ * 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 Julien Chaumond <chaumond@gmail.com>
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
+
+/**
+ * Page for logging in with Twitter
+ *
+ * @category Login
+ * @package StatusNet
+ * @author Julien Chaumond <chaumond@gmail.com>
+ * @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 TwitterloginAction extends Action
+{
+ function handle($args)
+ {
+ parent::handle($args);
+
+ if (common_is_real_login()) {
+ $this->clientError(_('Already logged in.'));
+ }
+
+ $this->showPage();
+ }
+
+ function title()
+ {
+ return _('Twitter Login');
+ }
+
+ function getInstructions()
+ {
+ return _('Login with your Twitter account');
+ }
+
+ function showPageNotice()
+ {
+ $instr = $this->getInstructions();
+ $output = common_markup_to_html($instr);
+ $this->elementStart('div', 'instructions');
+ $this->raw($output);
+ $this->elementEnd('div');
+ }
+
+ function showContent()
+ {
+ $this->elementStart('a', array('href' => common_local_url('twitterauthorization',
+ null,
+ array('signin' => true))));
+ $this->element('img', array('src' => common_path('plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png'),
+ 'alt' => 'Sign in with Twitter'));
+ $this->elementEnd('a');
+ }
+
+ function showLocalNav()
+ {
+ $nav = new LoginGroupNav($this);
+ $nav->show();
+ }
+}
{
public static $requestTokenURL = 'https://twitter.com/oauth/request_token';
public static $authorizeURL = 'https://twitter.com/oauth/authorize';
+ public static $signinUrl = 'https://twitter.com/oauth/authenticate';
public static $accessTokenURL = 'https://twitter.com/oauth/access_token';
/**
*
* @return the link
*/
- function getAuthorizeLink($request_token)
+ function getAuthorizeLink($request_token, $signin = false)
{
- return parent::getAuthorizeLink(self::$authorizeURL,
+ $url = ($signin) ? self::$signinUrl : self::$authorizeURL;
+
+ return parent::getAuthorizeLink($url,
$request_token,
common_local_url('twitterauthorization'));
}
$this->elementEnd('p');
$this->element('p', 'form_note',
_m('Connected Twitter account'));
+ $this->elementEnd('fieldset');
+
+ $this->elementStart('fieldset');
+
+ $this->element('legend', null, _m('Disconnect my account from Twitter'));
- $this->submit('remove', _m('Remove'));
+ if (!$user->password) {
+
+ $this->elementStart('p', array('class' => 'form_guide'));
+ $this->text(_m('Disconnecting your Twitter ' .
+ 'could make it impossible to log in! Please '));
+ $this->element('a',
+ array('href' => common_local_url('passwordsettings')),
+ _m('set a password'));
+
+ $this->text(_m(' first.'));
+ $this->elementEnd('p');
+ } else {
+
+ $note = _m('Keep your %1$s account but disconnect from Twitter. ' .
+ 'You can use your %1$s password to log in.');
+
+ $site = common_config('site', 'name');
+
+ $this->element('p', 'instructions',
+ sprintf($note, $site));
+
+ $this->submit('disconnect', _m('Disconnect'));
+ }
$this->elementEnd('fieldset');
if ($this->arg('save')) {
$this->savePreferences();
- } else if ($this->arg('remove')) {
+ } else if ($this->arg('disconnect')) {
$this->removeTwitterAccount();
} else {
$this->showForm(_m('Unexpected form submission.'));
return;
}
- $this->showForm(_m('Twitter account removed.'), true);
+ $this->showForm(_m('Twitter account disconnected.'), true);
}
/**
if (CONSOLE_INTERACTIVE) {
if (CONSOLE_READLINE) {
$line = readline($prompt);
- readline_add_history($line);
- if (defined('CONSOLE_HISTORY')) {
- // Save often; it's easy to hit fatal errors.
- readline_write_history(CONSOLE_HISTORY);
+ if (trim($line) != '') {
+ readline_add_history($line);
+ if (defined('CONSOLE_HISTORY')) {
+ // Save often; it's easy to hit fatal errors.
+ readline_write_history(CONSOLE_HISTORY);
+ }
}
return $line;
} else {
--- /dev/null
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 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
+ * 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/>.
+ */
+
+/**
+ * Sends control signals to running queue daemons.
+ *
+ * @author Brion Vibber <brion@status.net>
+ * @package QueueHandler
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'ur';
+$longoptions = array('update', 'restart', 'stop');
+
+$helptext = <<<END_OF_QUEUECTL_HELP
+Send broadcast events to control any running queue handlers.
+(Currently for Stomp queues only.)
+
+Events relating to current site (as selected with -s etc)
+ -u --update Announce new site or updated configuration. Running
+ daemons will start subscribing to any new queues needed
+ for this site.
+
+Global events:
+ -r --restart Graceful restart of all threads
+ --stop Graceful shutdown of all threads
+
+END_OF_QUEUECTL_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+function doSendControl($message, $event, $param='')
+{
+ print $message;
+ $qm = QueueManager::get();
+ if ($qm->sendControlSignal($event, $param)) {
+ print " sent.\n";
+ } else {
+ print " FAILED.\n";
+ }
+}
+
+$actions = 0;
+
+if (have_option('u') || have_option('--update')) {
+ $nickname = common_config('site', 'nickname');
+ doSendControl("Sending site update signal to queue daemons for $nickname",
+ "update", $nickname);
+ $actions++;
+}
+
+if (have_option('r') || have_option('--restart')) {
+ doSendControl("Sending graceful restart signal to queue daemons...",
+ "restart");
+ $actions++;
+}
+
+if (have_option('--stop')) {
+ doSendControl("Sending graceful shutdown signal to queue daemons...",
+ "shutdown");
+ $actions++;
+}
+
+if (!$actions) {
+ show_help();
+}
+
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
$shortoptions = 'fi:at:';
-$longoptions = array('id=', 'foreground', 'all', 'threads=', 'skip-xmpp', 'xmpp-only');
+$longoptions = array('id=', 'foreground', 'all', 'threads=');
/**
* Attempts to get a count of the processors available on the current system
$this->log(LOG_INFO, 'terminating normally');
- return true;
+ return $master->respawn ? self::EXIT_RESTART : self::EXIT_SHUTDOWN;
}
}
$daemonize = !(have_option('f') || have_option('--foreground'));
$all = have_option('a') || have_option('--all');
-if (have_option('--skip-xmpp')) {
- define('XMPP_EMERGENCY_FLAG', true);
-}
-if (have_option('--xmpp-only')) {
- define('XMPP_ONLY_FLAG', true);
-}
-
$daemon = new QueueDaemon($id, $daemonize, $threads, $all);
$daemon->runOnce();
--- /dev/null
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a 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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'i:n:';
+$longoptions = array('id=', 'nickname=', 'subject=');
+
+$helptext = <<<END_OF_USEREMAIL_HELP
+sendemail.php [options] < <message body>
+Sends given email text to user.
+
+ -i --id id of the user to query
+ -n --nickname nickname of the user to query
+ --subject mail subject line (required)
+
+END_OF_USEREMAIL_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+if (have_option('i', 'id')) {
+ $id = get_option_value('i', 'id');
+ $user = User::staticGet('id', $id);
+ if (empty($user)) {
+ print "Can't find user with ID $id\n";
+ exit(1);
+ }
+} else if (have_option('n', 'nickname')) {
+ $nickname = get_option_value('n', 'nickname');
+ $user = User::staticGet('nickname', $nickname);
+ if (empty($user)) {
+ print "Can't find user with nickname '$nickname'\n";
+ exit(1);
+ }
+} else {
+ print "You must provide a user by --id or --nickname\n";
+ exit(1);
+}
+
+if (empty($user->email)) {
+ // @fixme unconfirmed address?
+ print "No email registered for user '$user->nickname'\n";
+ exit(1);
+}
+
+if (!have_option('subject')) {
+ echo "You must provide a subject line for the mail in --subject='...' param.\n";
+ exit(1);
+}
+$subject = get_option_value('subject');
+
+if (posix_isatty(STDIN)) {
+ print "You must provide message input on stdin!\n";
+ exit(1);
+}
+$body = file_get_contents('php://stdin');
+
+print "Sending to $user->email...";
+if (mail_to_user($user, $subject, $body)) {
+ print " done\n";
+} else {
+ print " failed.\n";
+ exit(1);
+}
+
export BACKGROUNDBASE=/var/www/background.example.net
export FILEBASE=/var/www/file.example.net
export PWDGEN="pwgen 20"
-
+export PHPBASE=/var/www/statusnet
+export WILDCARD=example.net
+export MAILTEMPLATE=/etc/statusnet/newsite-mail.txt
+export MAILSUBJECT="Your new StatusNet site"
+export POSTINSTALL=/etc/statusnet/morestuff.sh
source /etc/statusnet/setup.cfg
-export nickname=$1
-export sitename=$2
+# setup_status_net.sh mysite 'My Site' '1user' 'owner@example.com' 'Firsty McLastname'
+export nickname="$1"
+export sitename="$2"
+export tags="$3"
+export email="$4"
+export fullname="$5"
+
+# Fixme: if this is changed later we need to update profile URLs
+# for the created user.
+export server="$nickname.$WILDCARD"
+
+# End-user info
+export userpass=`$PWDGEN`
+export roles="administrator moderator owner"
+
+# DB info
export password=`$PWDGEN`
export database=$nickname$DBBASE
export username=$nickname$USERBASE
GRANT ALL ON $database.* TO '$username'@'localhost' IDENTIFIED BY '$password';
GRANT ALL ON $database.* TO '$username'@'%' IDENTIFIED BY '$password';
-INSERT INTO status_network (nickname, dbhost, dbuser, dbpass, dbname, sitename, created)
-VALUES ('$nickname', '$DBHOSTNAME', '$username', '$password', '$database', '$sitename', now());
+INSERT INTO status_network (nickname, dbhost, dbuser, dbpass, dbname, sitename, created, tags)
+VALUES ('$nickname', '$DBHOSTNAME', '$username', '$password', '$database', '$sitename', now(), '$tags');
ENDOFCOMMANDS
mkdir $top/$nickname
chmod a+w $top/$nickname
done
+
+php $PHPBASE/scripts/registeruser.php \
+ -s"$server" \
+ -n"$nickname" \
+ -f"$fullname" \
+ -w"$userpass" \
+ -e"$email"
+
+for role in $roles
+do
+ php $PHPBASE/scripts/userrole.php \
+ -s"$server" \
+ -n"$nickname" \
+ -r"$role"
+done
+
+if [ -f "$MAILTEMPLATE" ]
+then
+ # fixme how safe is this? are sitenames sanitized?
+ cat $MAILTEMPLATE | \
+ sed "s/\$nickname/$nickname/" | \
+ sed "s/\$sitename/$sitename/" | \
+ sed "s/\$userpass/$userpass/" | \
+ php $PHPBASE/scripts/sendemail.php \
+ -s"$server" \
+ -n"$nickname" \
+ --subject="$MAILSUBJECT"
+else
+ echo "No mail template, not sending email."
+fi
+
+if [ -f "$POSTINSTALL" ]
+then
+ echo "Running $POSTINSTALL ..."
+ source "$POSTINSTALL"
+fi
common_log(LOG_INFO, 'terminating normally');
- return true;
+ return $master->respawn ? self::EXIT_RESTART : self::EXIT_SHUTDOWN;
}
}
--- /dev/null
+Some very rough test scripts for hitting up the OAuth endpoints.
+
+Note: this works best if you register an OAuth application, leaving
+the callback URL blank.
+
+Put your instance info and consumer key and secret in oauth.ini
+
+Example usage:
+--------------
+
+php getrequesttoken.php
+
+Gets a request token, token secret and a url to authorize it. Once
+you authorize the request token you can exchange it for an access token...
+
+php exchangetokens.php --oauth_token=b9a79548a88c1aa9a5bea73103c6d41d --token_secret=4a47d9337fc0202a14ab552e17a3b657
+
+Once you have your access token, go ahead and try a protected API
+resource:
+
+php verifycreds.php --oauth_token=cf2de7665f0dda0a82c2dc39b01be7f9 --token_secret=4524c3b712200138e1a4cff2e9ca83d8
+
--- /dev/null
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a 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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+require_once INSTALLDIR . '/extlib/OAuth.php';
+
+$ini = parse_ini_file("oauth.ini");
+
+$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
+
+$at_endpoint = $ini['apiroot'] . $ini['access_token_url'];
+
+$shortoptions = 't:s:';
+$longoptions = array('oauth_token=', 'token_secret=');
+
+$helptext = <<<END_OF_ETOKENS_HELP
+ exchangetokens.php [options]
+ Exchange an authorized OAuth request token for an access token
+
+ -t --oauth_token authorized request token
+ -s --token_secret authorized request token secret
+
+END_OF_ETOKENS_HELP;
+
+require_once INSTALLDIR . '/scripts/commandline.inc';
+
+$token = null;
+$token_secret = null;
+
+if (have_option('t', 'oauth_token')) {
+ $token = get_option_value('oauth_token');
+}
+
+if (have_option('s', 'token_secret')) {
+ $token_secret = get_option_value('s', 'token_secret');
+}
+
+if (empty($token)) {
+ print "Please specify a request token.\n";
+ exit(1);
+}
+
+if (empty($token_secret)) {
+ print "Please specify a request token secret.\n";
+ exit(1);
+}
+
+$rt = new OAuthToken($token, $token_secret);
+common_debug("Exchange request token = " . var_export($rt, true));
+
+$parsed = parse_url($at_endpoint);
+$params = array();
+parse_str($parsed['query'], $params);
+
+$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+
+$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $rt, "GET", $at_endpoint, $params);
+$req_req->sign_request($hmac_method, $test_consumer, $rt);
+
+$r = httpRequest($req_req->to_url());
+
+common_debug("Exchange request token = " . var_export($rt, true));
+common_debug("Exchange tokens URL: " . $req_req->to_url());
+
+$body = $r->getBody();
+
+$token_stuff = array();
+parse_str($body, $token_stuff);
+
+print 'Access token : ' . $token_stuff['oauth_token'] . "\n";
+print 'Access token secret : ' . $token_stuff['oauth_token_secret'] . "\n";
+
+function httpRequest($url)
+{
+ $request = HTTPClient::start();
+
+ $request->setConfig(array(
+ 'follow_redirects' => true,
+ 'connect_timeout' => 120,
+ 'timeout' => 120,
+ 'ssl_verify_peer' => false,
+ 'ssl_verify_host' => false
+ ));
+
+ return $request->get($url);
+}
+
--- /dev/null
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a 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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+require_once INSTALLDIR . '/scripts/commandline.inc';
+require_once INSTALLDIR . '/extlib/OAuth.php';
+
+$ini = parse_ini_file("oauth.ini");
+
+$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
+
+$rt_endpoint = $ini['apiroot'] . $ini['request_token_url'];
+
+$parsed = parse_url($rt_endpoint);
+$params = array();
+
+parse_str($parsed['query'], $params);
+
+$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+
+$req_req = OAuthRequest::from_consumer_and_token($test_consumer, NULL, "GET", $rt_endpoint, $params);
+$req_req->sign_request($hmac_method, $test_consumer, NULL);
+
+$r = httpRequest($req_req->to_url());
+
+$body = $r->getBody();
+
+$token_stuff = array();
+parse_str($body, $token_stuff);
+
+$authurl = $ini['apiroot'] . $ini['authorize_url'] . '?oauth_token=' . $token_stuff['oauth_token'];
+
+print 'Request token : ' . $token_stuff['oauth_token'] . "\n";
+print 'Request token secret : ' . $token_stuff['oauth_token_secret'] . "\n";
+print "Authorize URL : $authurl\n";
+
+//var_dump($req_req);
+
+function httpRequest($url)
+{
+ $request = HTTPClient::start();
+
+ $request->setConfig(array(
+ 'follow_redirects' => true,
+ 'connect_timeout' => 120,
+ 'timeout' => 120,
+ 'ssl_verify_peer' => false,
+ 'ssl_verify_host' => false
+ ));
+
+ return $request->get($url);
+}
+
--- /dev/null
+; Setup OAuth info here
+apiroot = "http://YOURSTATUSNET/api"
+
+request_token_url = "/oauth/request_token"
+authorize_url = "/oauth/authorize"
+access_token_url = "/oauth/access_token"
+
+consumer_key = "b748968e9bea81a53f3a3c15aa0c686f"
+consumer_secret = "5434e18cce05d9e53cdd48029a62fa41"
+
--- /dev/null
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 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
+ * 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/>.
+ **/
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+require_once INSTALLDIR . '/extlib/OAuth.php';
+
+$shortoptions = 'o:s:u:';
+$longoptions = array('oauth_token=', 'token_secret=', 'update=');
+
+$helptext = <<<END_OF_VERIFY_HELP
+ statusupdate.php [options]
+ Update your status using OAuth
+
+ -o --oauth_token access token
+ -s --token_secret access token secret
+ -u --update status update
+
+
+END_OF_VERIFY_HELP;
+
+$token = null;
+$token_secret = null;
+$update = null;
+
+require_once INSTALLDIR . '/scripts/commandline.inc';
+
+if (have_option('o', 'oauth_token')) {
+ $token = get_option_value('oauth_token');
+}
+
+if (have_option('s', 'token_secret')) {
+ $token_secret = get_option_value('s', 'token_secret');
+}
+
+if (have_option('u', 'update')) {
+ $update = get_option_value('u', 'update');
+}
+
+if (empty($token)) {
+ print "Please specify an access token.\n";
+ exit(1);
+}
+
+if (empty($token_secret)) {
+ print "Please specify an access token secret.\n";
+ exit(1);
+}
+
+if (empty($update)) {
+ print "You forgot to update your status!\n";
+ exit(1);
+}
+
+$ini = parse_ini_file("oauth.ini");
+
+$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
+
+$endpoint = $ini['apiroot'] . '/statuses/update.xml';
+
+print "$endpoint\n";
+
+$at = new OAuthToken($token, $token_secret);
+
+$parsed = parse_url($endpoint);
+$params = array();
+parse_str($parsed['query'], $params);
+
+$params['status'] = $update;
+
+$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+
+$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $at, 'POST', $endpoint, $params);
+$req_req->sign_request($hmac_method, $test_consumer, $at);
+
+$r = httpRequest($req_req->to_url());
+
+$body = $r->getBody();
+
+print "$body\n";
+
+//print $req_req->to_url() . "\n\n";
+
+function httpRequest($url)
+{
+ $request = HTTPClient::start();
+
+ $request->setConfig(array(
+ 'follow_redirects' => true,
+ 'connect_timeout' => 120,
+ 'timeout' => 120,
+ 'ssl_verify_peer' => false,
+ 'ssl_verify_host' => false
+ ));
+
+ return $request->post($url);
+}
+
--- /dev/null
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a 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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
+
+require_once INSTALLDIR . '/extlib/OAuth.php';
+
+$shortoptions = 'o:s:';
+$longoptions = array('oauth_token=', 'token_secret=');
+
+$helptext = <<<END_OF_VERIFY_HELP
+ verifycreds.php [options]
+ Use an access token to verify credentials thru the api
+
+ -o --oauth_token access token
+ -s --token_secret access token secret
+
+END_OF_VERIFY_HELP;
+
+$token = null;
+$token_secret = null;
+
+require_once INSTALLDIR . '/scripts/commandline.inc';
+
+if (have_option('o', 'oauth_token')) {
+ $token = get_option_value('oauth_token');
+}
+
+if (have_option('s', 'token_secret')) {
+ $token_secret = get_option_value('s', 'token_secret');
+}
+
+if (empty($token)) {
+ print "Please specify an access token.\n";
+ exit(1);
+}
+
+if (empty($token_secret)) {
+ print "Please specify an access token secret.\n";
+ exit(1);
+}
+
+$ini = parse_ini_file("oauth.ini");
+
+$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
+
+$endpoint = $ini['apiroot'] . '/account/verify_credentials.xml';
+
+print "$endpoint\n";
+
+$at = new OAuthToken($token, $token_secret);
+
+$parsed = parse_url($endpoint);
+$params = array();
+parse_str($parsed['query'], $params);
+
+$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+
+$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $at, "GET", $endpoint, $params);
+$req_req->sign_request($hmac_method, $test_consumer, $at);
+
+$r = httpRequest($req_req->to_url());
+
+$body = $r->getBody();
+
+print "$body\n";
+
+//print $req_req->to_url() . "\n\n";
+
+function httpRequest($url)
+{
+ $request = HTTPClient::start();
+
+ $request->setConfig(array(
+ 'follow_redirects' => true,
+ 'connect_timeout' => 120,
+ 'timeout' => 120,
+ 'ssl_verify_peer' => false,
+ 'ssl_verify_host' => false
+ ));
+
+ return $request->get($url);
+}
+
#form_password_recover legend,
#form_password_change legend,
.form_entity_block legend,
-#form_filter_bytag legend {
+#form_filter_bytag legend,
+#apioauthauthorize_allowdeny {
display:none;
}
margin-right:11px;
}
+/*applications*/
+.applications {
+margin-bottom:18px;
+float:left;
+width:100%;
+}
+.applications li {
+list-style-type:none;
+}
+.application img,
+#showapplication .entity_profile img,
+.form_data #application_icon img,
+#apioauthauthorize .form_data img {
+max-width:96px;
+max-height:96px;
+}
+#apioauthauthorize .form_data img {
+margin-right:18px;
+float:left;
+}
+#showapplication .entity_profile {
+width:68%;
+}
+#showapplication .entity_profile .entity_fn {
+margin-left:0;
+}
+#showapplication .entity_profile .entity_fn .fn:before,
+#showapplication .entity_profile .entity_fn .fn:after {
+content:'';
+}
+#showapplication .entity_data {
+clear:both;
+margin-bottom:18px;
+}
+#showapplication .entity_data h2 {
+display:none;
+}
+#showapplication .entity_data dl {
+margin-bottom:18px;
+}
+#showapplication .entity_data dt {
+font-weight:bold;
+}
+#showapplication .entity_data dd {
+margin-left:1.795%;
+font-family:monospace;
+font-size:1.3em;
+}
+.form_data #application_types label.radio,
+.form_data #default_access_types label.radio {
+width:14.5%;
+}
+
/* NOTICE */
.notice,
-.profile {
+.profile,
+.application {
position:relative;
padding-top:11px;
padding-bottom:11px;
--- /dev/null
+/** Universal Ad Package styles:
+ * Medium Rectangle 300x250
+ * Rectangle 180x150
+ * Leaderboard 728x90
+ * Wide Skyscraper 160x600
+ *
+ * @package StatusNet
+ * @author Sarven Capadisli <csarven@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/
+ */
+
+
+.ad {
+border:1px solid #CCC;
+float:left;
+}
+
+#ad_medium-rectangle {
+width:300px;
+height:250px;
+
+margin-left:1.35%;
+margin-bottom:18px;
+}
+
+#ad_rectangle {
+width:180px;
+height:150px;
+
+float:none;
+clear:both;
+margin:0 auto;
+margin-bottom:29px;
+}
+
+#ad_leaderboard {
+width:728px;
+height:90px;
+
+margin:0 auto 18px;
+float:none;
+clear:both;
+}
+
+#ad_wide-skyscraper {
+width:160px;
+height:600px;
+
+float:right;
+margin-top:18px;
+margin-right:8.25%;
+}
.notice,
.profile,
+.application,
#content tbody tr {
border-top-color:#C8D1D5;
}
.entity_delete input.submit,
.notice-options .repeated,
.form_notice label[for=notice_data-geo],
-button.minimize {
+button.minimize,
+.form_reset_key input.submit {
background-image:url(../../base/images/icons/icons-01.gif);
background-repeat:no-repeat;
background-color:transparent;
.entity_delete input.submit {
background-position: 5px -1511px;
}
+.form_reset_key input.submit {
+background-position: 5px -1973px;
+}
/* NOTICES */
.notice .attachment {
-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
}
#content .notices li:hover,
+#content .applications li:hover,
#content tbody tr:hover {
background-color:rgba(240, 240, 240, 0.2);
}
.notice,
.profile,
+.application,
#content tbody tr {
border-top-color:#CEE1E9;
}
.entity_delete input.submit,
.notice-options .repeated,
.form_notice label[for=notice_data-geo],
-button.minimize {
+button.minimize,
+.form_reset_key input.submit {
background-image:url(../../base/images/icons/icons-01.gif);
background-repeat:no-repeat;
background-color:transparent;
.entity_delete input.submit {
background-position: 5px -1511px;
}
+.form_reset_key input.submit {
+background-position: 5px -1973px;
+}
/* NOTICES */
.notice .attachment {
-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
}
#content .notices li:hover,
+#content .applications li:hover,
#content tbody tr:hover {
background-color:rgba(240, 240, 240, 0.2);
}