diff options
author | Brion Vibber <brion@pobox.com> | 2010-10-26 15:39:31 -0700 |
---|---|---|
committer | Brion Vibber <brion@pobox.com> | 2010-10-26 15:39:31 -0700 |
commit | 8ff44a1fb9f54ce61a91987ca44cbd4fccf0a012 (patch) | |
tree | 15b95f9a9b3abc52ad063f6ce808507a7fae256e /lib | |
parent | 22047f6412811cb340aee6ba49d15257581b5aa9 (diff) | |
parent | c09487f272e247d7c6fd8cd3d785454d2385fe3d (diff) |
Merge branch '0.9.x' into twitstream
Diffstat (limited to 'lib')
-rw-r--r-- | lib/action.php | 69 | ||||
-rw-r--r-- | lib/apiaction.php | 10 | ||||
-rw-r--r-- | lib/apiauth.php | 74 | ||||
-rw-r--r-- | lib/apioauthstore.php | 218 | ||||
-rw-r--r-- | lib/applicationlist.php | 247 | ||||
-rw-r--r-- | lib/common.php | 8 | ||||
-rw-r--r-- | lib/dberroraction.php | 1 | ||||
-rw-r--r-- | lib/default.php | 6 | ||||
-rw-r--r-- | lib/dofollowlistitem.php | 88 | ||||
-rw-r--r-- | lib/feed.php | 1 | ||||
-rw-r--r-- | lib/feedlist.php | 1 | ||||
-rw-r--r-- | lib/groupsbymemberssection.php | 2 | ||||
-rw-r--r-- | lib/groupsbypostssection.php | 2 | ||||
-rw-r--r-- | lib/groupsection.php | 1 | ||||
-rw-r--r-- | lib/grouptagcloudsection.php | 3 | ||||
-rw-r--r-- | lib/htmloutputter.php | 76 | ||||
-rw-r--r-- | lib/installer.php | 42 | ||||
-rw-r--r-- | lib/mail.php | 109 | ||||
-rw-r--r-- | lib/router.php | 37 | ||||
-rw-r--r-- | lib/statusnet.php | 15 | ||||
-rw-r--r-- | lib/theme.php | 78 | ||||
-rw-r--r-- | lib/util.php | 29 | ||||
-rw-r--r-- | lib/webcolor.php | 15 | ||||
-rw-r--r-- | lib/xmppmanager.php | 15 | ||||
-rw-r--r-- | lib/xmppoutqueuehandler.php | 1 |
25 files changed, 830 insertions, 318 deletions
diff --git a/lib/action.php b/lib/action.php index ddc058d41..01bb0f7e9 100644 --- a/lib/action.php +++ b/lib/action.php @@ -175,8 +175,9 @@ class Action extends HTMLOutputter // lawsuit $this->element('link', array('rel' => 'shortcut icon', 'href' => Theme::path('favicon.ico'))); } else { + // favicon.ico should be HTTPS if the rest of the page is $this->element('link', array('rel' => 'shortcut icon', - 'href' => common_path('favicon.ico'))); + 'href' => common_path('favicon.ico', StatusNet::isHTTPS()))); } if (common_config('site', 'mobile')) { @@ -361,9 +362,9 @@ class Action extends HTMLOutputter // lawsuit */ function showBody() { - $this->elementStart('body', (common_current_user()) ? array('id' => $this->trimmed('action'), + $this->elementStart('body', (common_current_user()) ? array('id' => strtolower($this->trimmed('action')), 'class' => 'user_in') - : array('id' => $this->trimmed('action'))); + : array('id' => strtolower($this->trimmed('action')))); $this->elementStart('div', array('id' => 'wrap')); if (Event::handle('StartShowHeader', array($this))) { $this->showHeader(); @@ -397,7 +398,10 @@ class Action extends HTMLOutputter // lawsuit Event::handle('EndShowSiteNotice', array($this)); } if (common_logged_in()) { - $this->showNoticeForm(); + if (Event::handle('StartShowNoticeForm', array($this))) { + $this->showNoticeForm(); + Event::handle('EndShowNoticeForm', array($this)); + } } else { $this->showAnonymousMessage(); } @@ -415,18 +419,43 @@ class Action extends HTMLOutputter // lawsuit 'class' => 'vcard')); if (Event::handle('StartAddressData', array($this))) { if (common_config('singleuser', 'enabled')) { + $user = User::singleUser(); $url = common_local_url('showstream', - array('nickname' => common_config('singleuser', 'nickname'))); + array('nickname' => $user->nickname)); } else { $url = common_local_url('public'); } $this->elementStart('a', array('class' => 'url home bookmark', 'href' => $url)); - if (common_config('site', 'logo') || file_exists(Theme::file('logo.png'))) { + + if (StatusNet::isHTTPS()) { + $logoUrl = common_config('site', 'ssllogo'); + if (empty($logoUrl)) { + // if logo is an uploaded file, try to fall back to HTTPS file URL + $httpUrl = common_config('site', 'logo'); + if (!empty($httpUrl)) { + $f = File::staticGet('url', $httpUrl); + if (!empty($f) && !empty($f->filename)) { + // this will handle the HTTPS case + $logoUrl = File::url($f->filename); + } + } + } + } else { + $logoUrl = common_config('site', 'logo'); + } + + if (empty($logoUrl) && file_exists(Theme::file('logo.png'))) { + // This should handle the HTTPS case internally + $logoUrl = Theme::path('logo.png'); + } + + if (!empty($logoUrl)) { $this->element('img', array('class' => 'logo photo', - 'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png'), + 'src' => $logoUrl, 'alt' => common_config('site', 'name'))); } + $this->text(' '); $this->element('span', array('class' => 'fn org'), common_config('site', 'name')); $this->elementEnd('a'); @@ -498,20 +527,20 @@ class Action extends HTMLOutputter // lawsuit } // TRANS: Tooltip for main menu option "Login" $tooltip = _m('TOOLTIP', 'Login to the site'); - // TRANS: Main menu option when not logged in to log in $this->menuItem(common_local_url('login'), + // TRANS: Main menu option when not logged in to log in _m('MENU', 'Login'), $tooltip, false, 'nav_login'); } // TRANS: Tooltip for main menu option "Help" $tooltip = _m('TOOLTIP', 'Help me!'); - // TRANS: Main menu option for help on the StatusNet site $this->menuItem(common_local_url('doc', array('title' => 'help')), + // TRANS: Main menu option for help on the StatusNet site _m('MENU', 'Help'), $tooltip, false, 'nav_help'); if ($user || !common_config('site', 'private')) { // TRANS: Tooltip for main menu option "Search" $tooltip = _m('TOOLTIP', 'Search for people or text'); - // TRANS: Main menu option when logged in or when the StatusNet instance is not private $this->menuItem(common_local_url('peoplesearch'), + // TRANS: Main menu option when logged in or when the StatusNet instance is not private _m('MENU', 'Search'), $tooltip, false, 'nav_search'); } Event::handle('EndPrimaryNav', array($this)); @@ -891,8 +920,26 @@ class Action extends HTMLOutputter // lawsuit case 'cc': // fall through default: $this->elementStart('p'); + + $image = common_config('license', 'image'); + $sslimage = common_config('license', 'sslimage'); + + if (StatusNet::isHTTPS()) { + if (!empty($sslimage)) { + $url = $sslimage; + } else if (preg_match('#^http://i.creativecommons.org/#', $image)) { + // CC support HTTPS on their images + $url = preg_replace('/^http/', 'https', $image); + } else { + // Better to show mixed content than no content + $url = $image; + } + } else { + $url = $image; + } + $this->element('img', array('id' => 'license_cc', - 'src' => common_config('license', 'image'), + 'src' => $url, 'alt' => common_config('license', 'title'), 'width' => '80', 'height' => '15')); diff --git a/lib/apiaction.php b/lib/apiaction.php index afba8ab63..4e9dbb310 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -1398,8 +1398,10 @@ class ApiAction extends Action if (is_numeric($this->arg('id'))) { return Profile::staticGet($this->arg('id')); } else if ($this->arg('id')) { + // Screen names currently can only uniquely identify a local user. $nickname = common_canonical_nickname($this->arg('id')); - return Profile::staticGet('nickname', $nickname); + $user = User::staticGet('nickname', $nickname); + return $user ? $user->getProfile() : null; } else if ($this->arg('user_id')) { // This is to ensure that a non-numeric user_id still // overrides screen_name even if it doesn't get used @@ -1408,13 +1410,15 @@ class ApiAction extends Action } } else if ($this->arg('screen_name')) { $nickname = common_canonical_nickname($this->arg('screen_name')); - return Profile::staticGet('nickname', $nickname); + $user = User::staticGet('nickname', $nickname); + return $user ? $user->getProfile() : null; } } else if (is_numeric($id)) { return Profile::staticGet($id); } else { $nickname = common_canonical_nickname($id); - return Profile::staticGet('nickname', $nickname); + $user = User::staticGet('nickname', $nickname); + return $user ? $user->getProfile() : null; } } diff --git a/lib/apiauth.php b/lib/apiauth.php index 8b0a3da17..1dacf1409 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -168,16 +168,20 @@ class ApiAuthAction extends ApiAction $app = Oauth_application::getByConsumerKey($consumer); if (empty($app)) { - common_log(LOG_WARNING, - 'Couldn\'t find the OAuth app for consumer key: ' . - $consumer); + common_log( + LOG_WARNING, + 'API OAuth - Couldn\'t find the OAuth app for consumer key: ' . + $consumer + ); // TRANS: OAuth exception thrown when no application is found for a given consumer key. throw new OAuthException(_('No application for that consumer key.')); } // set the source attr + if ($app->name != 'anonymous') { + $this->source = $app->name; + } - $this->source = $app->name; $appUser = Oauth_application_user::staticGet('token', $access_token); @@ -197,16 +201,19 @@ class ApiAuthAction extends ApiAction } $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' - )); + "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' + ) + ); } else { // TRANS: OAuth exception given when an incorrect access token was given for a user. throw new OAuthException(_('Bad access token.')); @@ -218,6 +225,7 @@ class ApiAuthAction extends ApiAction } } catch (OAuthException $e) { + $this->logAuthFailure($e->getMessage()); common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); $this->clientError($e->getMessage(), 401, $this->format); exit; @@ -255,7 +263,7 @@ class ApiAuthAction extends ApiAction // show error if the user clicks 'cancel' // TRANS: Client error thrown when authentication fails becaus a user clicked "Cancel". - $this->clientError(_("Could not authenticate you."), 401, $this->format); + $this->clientError(_('Could not authenticate you.'), 401, $this->format); exit; } else { @@ -276,18 +284,13 @@ class ApiAuthAction extends ApiAction $this->access = self::READ_WRITE; if (empty($this->auth_user) && ($required || isset($_SERVER['PHP_AUTH_USER']))) { - - // basic authentication failed - list($proxy, $ip) = common_client_ip(); - - $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); + $msg = sprintf( + "basic auth nickname = %s", + $this->auth_user_nickname + ); + $this->logAuthFailure($msg); // TRANS: Client error thrown when authentication fails. - $this->clientError(_("Could not authenticate you."), 401, $this->format); + $this->clientError(_('Could not authenticate you.'), 401, $this->format); exit; } } @@ -332,4 +335,23 @@ class ApiAuthAction extends ApiAction } } } + + /** + * Log an API authentication failer. Collect the proxy and IP + * and log them + * + * @param string $logMsg additional log message + */ + function logAuthFailure($logMsg) + { + list($proxy, $ip) = common_client_ip(); + + $msg = sprintf( + 'API auth failure (proxy = %1$s, ip = %2$s) - ', + $proxy, + $ip + ); + + common_log(LOG_WARNING, $msg . $logMsg); + } } diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index f3bf0b857..2a65fffc4 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -23,24 +23,71 @@ require_once INSTALLDIR . '/lib/oauthstore.php'; class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore { - function lookup_consumer($consumer_key) + function lookup_consumer($consumerKey) { - $con = Consumer::staticGet('consumer_key', $consumer_key); + $con = Consumer::staticGet('consumer_key', $consumerKey); if (!$con) { - return null; + + // Create an anon consumer and anon application if one + // doesn't exist already + if ($consumerKey == 'anonymous') { + + common_debug("API OAuth - creating anonymous consumer"); + $con = new Consumer(); + $con->consumer_key = $consumerKey; + $con->consumer_secret = $consumerKey; + $con->created = common_sql_now(); + + $result = $con->insert(); + if (!$result) { + // TRANS: Server error displayed when trying to create an anynymous OAuth consumer. + $this->serverError(_('Could not create anonymous consumer.')); + } + + $app = Oauth_application::getByConsumerKey('anonymous'); + + if (!$app) { + common_debug("API OAuth - creating anonymous application"); + $app = new OAuth_application(); + $app->owner = 1; // XXX: What to do here? + $app->consumer_key = $con->consumer_key; + $app->name = 'anonymous'; + $app->icon = 'default-avatar-stream.png'; // XXX: Fix this! + $app->description = "An anonymous application"; + // XXX: allow the user to set the access type when + // authorizing? Currently we default to r+w for anonymous + // OAuth client applications + $app->access_type = 3; // read + write + $app->type = 2; // desktop + $app->created = common_sql_now(); + + $id = $app->insert(); + + if (!$id) { + // TRANS: Server error displayed when trying to create an anynymous OAuth application. + $this->serverError(_("Could not create anonymous OAuth application.")); + } + } + } else { + return null; + } } - return new OAuthConsumer($con->consumer_key, - $con->consumer_secret); + 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); + // Look up the full req token + $req_token = $this->lookup_token( + null, + 'request', + $token_key + ); if (empty($req_token)) { common_debug("couldn't get request token from oauth datastore"); @@ -58,7 +105,6 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore } // Look up the app - $app = new Oauth_application(); $app->consumer_key = $token->consumer_key; $result = $app->find(true); @@ -74,8 +120,13 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore function new_access_token($token, $consumer, $verifier) { common_debug( - 'new_access_token("' . $token->key . '","' . $consumer->key. '","' . $verifier . '")', - __FILE__ + sprintf( + "New access token from request token %s, consumer %s and verifier %s ", + $token, + $consumer, + $verifier + ), + __FILE__ ); $rt = new Token(); @@ -89,73 +140,123 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized - common_debug('request token found.', __FILE__); + common_debug('Request token found.', __FILE__); - // find the associated user of the app + // find the app and profile associated with this token + $tokenAssoc = Oauth_token_association::staticGet('token', $rt->tok); + + if (!$tokenAssoc) { + throw new Exception( + // TRANS: Exception thrown when no token association could be found. + _('Could not find a profile and application associated with the request token.') + ); + } + + // check to see if we have previously issued an access token for this application + // and profile $appUser = new Oauth_application_user(); $appUser->application_id = $app->id; - $appUser->token = $rt->tok; + $appUser->profile_id = $tokenAssoc->profile_id; $result = $appUser->find(true); if (!empty($result)) { - common_debug("Ouath 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 + common_log(LOG_INFO, + sprintf( + "Existing access token found for application %s, profile %s.", + $app->id, + $tokenAssoc->profile_id + ) + ); - $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->verifier = $verifier; - $at->verified_callback = $rt->verified_callback; // 1.0a - $at->created = DB_DataObject_Cast::dateTime(); + $at = new Token(); - 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; + // fetch the full access token + $at->consumer_key = $consumer->key; + $at->tok = $appUser->token; + + $result = $at->find(true); + + if (!$result) { + throw new Exception( + // TRANS: Exception thrown when no access token can be issued. + _('Could not issue access token.') + ); } - common_debug('request token "'.$rt->tok.'" updated', __FILE__); - // update the token from req to access for the user + // Yay, we can re-issue the access token + return new OAuthToken($at->tok, $at->secret); + + } else { - $orig = clone($appUser); - $appUser->token = $at->tok; + common_log(LOG_INFO, + sprintf( + "Creating new access token for application %s, profile %s.", + $app->id, + $tokenAssoc->profile_id + ) + ); + + // make a brand new 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->verifier = $verifier; + $at->verified_callback = $rt->verified_callback; // 1.0a + $at->created = common_sql_now(); + + 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__); + } - // 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. + // insert a new Oauth_application_user record w/access token + $appUser = new Oauth_application_user(); - $appUser->access_type = $app->access_type; + $appUser->profile_id = $tokenAssoc->profile_id;; + $appUser->application_id = $app->id; + $appUser->access_type = $app->access_type; + $appUser->token = $at->tok; + $appUser->created = common_sql_now(); - $result = $appUser->update($orig); + $result = $appUser->insert(); - if (empty($result)) { - common_debug('couldn\'t update OAuth app user.'); - return null; + if (!$result) { + common_log_db_error($appUser, 'INSERT', __FILE__); + // TRANS: Server error displayed when a database error occurs. + $this->serverError(_('Database error inserting OAuth application user.')); } // Okay, good return new OAuthToken($at->tok, $at->secret); } + } else { + + // the token was not authorized or not verfied + common_log( + LOG_INFO, + sprintf( + "API OAuth - Attempt to exchange unauthorized or unverified request token %s for an access token.", + $rt->tok + ) + ); return null; } } @@ -174,9 +275,9 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore * @return void */ public function revoke_token($token_key, $type = 0) { - $rt = new Token(); - $rt->tok = $token_key; - $rt->type = $type; + $rt = new Token(); + $rt->tok = $token_key; + $rt->type = $type; $rt->state = 0; if (!$rt->find(true)) { @@ -198,7 +299,6 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore * * @return OAuthToken $token a new unauthorized OAuth request token */ - function new_request_token($consumer, $callback) { $t = new Token(); @@ -223,6 +323,4 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore return new OAuthToken($t->tok, $t->secret); } } - - } diff --git a/lib/applicationlist.php b/lib/applicationlist.php index 8b6e3a8ad..3f50f110b 100644 --- a/lib/applicationlist.php +++ b/lib/applicationlist.php @@ -22,7 +22,7 @@ * @category Application * @package StatusNet * @author Zach Copley <zach@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/ */ @@ -55,14 +55,13 @@ class ApplicationList extends Widget /** Action object using us. */ var $action = null; - function __construct($application, $owner=null, $action=null, $connections = false) + function __construct($application, $owner=null, $action=null) { parent::__construct($action); $this->application = $application; $this->owner = $owner; $this->action = $action; - $this->connections = $connections; } function show() @@ -88,24 +87,34 @@ class ApplicationList extends Widget { $user = common_current_user(); - $this->out->elementStart('li', array('class' => 'application', - 'id' => 'oauthclient-' . $this->application->id)); + $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')); - } + + $this->out->elementStart( + 'a', + array( + 'href' => common_local_url( + 'showapplication', + array('id' => $this->application->id)), + 'class' => 'url' + ) + ); if (!empty($this->application->icon)) { - $this->out->element('img', array('src' => $this->application->icon, - 'class' => 'photo avatar')); + $this->out->element( + 'img', + array( + 'src' => $this->application->icon, + 'class' => 'photo avatar' + ) + ); } $this->out->element('span', 'fn', $this->application->name); @@ -114,51 +123,56 @@ class ApplicationList extends Widget $this->out->raw(' by '); - $this->out->element('a', array('href' => $this->application->homepage, - 'class' => 'url'), - $this->application->organization); + $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!"); - } + /* Override this in subclasses. */ + function showOwnerControls() + { + return; + } +} - $this->out->elementStart('li'); - - // TRANS: Application access type - $readWriteText = _('read-write'); - // TRANS: Application access type - $readOnlyText = _('read-only'); - - $access = ($this->application->access_type & Oauth_application::$writeAccess) - ? $readWriteText : $readOnlyText; - $modifiedDate = common_date_string($appUser->modified); - // TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only") - $txt = sprintf(_('Approved %1$s - "%2$s" access.'),$modifiedDate,$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()); - // TRANS: Button label - $this->out->submit('revoke', _m('BUTTON','Revoke')); - $this->out->elementEnd('fieldset'); - $this->out->elementEnd('form'); - $this->out->elementEnd('li'); - } +/** + * Widget to show a list of connected OAuth clients + * + * @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 ConnectedAppsList extends Widget +{ + /** Current connected application query */ + var $connection = null; + + /** Owner of this list */ + var $owner = null; + + /** Action object using us. */ + var $action = null; + + function __construct($connection, $owner=null, $action=null) + { + parent::__construct($action); + + common_debug("ConnectedAppsList constructor"); + + $this->connection = $connection; + $this->owner = $owner; + $this->action = $action; } /* Override this in subclasses. */ @@ -166,4 +180,125 @@ class ApplicationList extends Widget { return; } + + function show() + { + $this->out->elementStart('ul', 'applications'); + + $cnt = 0; + + while ($this->connection->fetch()) { + $cnt++; + if($cnt > APPS_PER_PAGE) { + break; + } + $this->showConnection(); + } + + $this->out->elementEnd('ul'); + + return $cnt; + } + + function showConnection() + { + $app = Oauth_application::staticGet('id', $this->connection->application_id); + + $this->out->elementStart( + 'li', + array( + 'class' => 'application', + 'id' => 'oauthclient-' . $app->id + ) + ); + + $this->out->elementStart('span', 'vcard author'); + + $this->out->elementStart( + 'a', + array( + 'href' => $app->source_url, + 'class' => 'url' + ) + ); + + if (!empty($app->icon)) { + $this->out->element( + 'img', + array( + 'src' => $app->icon, + 'class' => 'photo avatar' + ) + ); + } + if ($app->name != 'anonymous') { + $this->out->element('span', 'fn', $app->name); + } + $this->out->elementEnd('a'); + + if ($app->name == 'anonymous') { + $this->out->element('span', 'fn', "Unknown application"); + } + + $this->out->elementEnd('span'); + + if ($app->name != 'anonymous') { + // @todo FIXME: i18n trouble. + $this->out->raw(_(' by ')); + + $this->out->element( + 'a', + array( + 'href' => $app->homepage, + 'class' => 'url' + ), + $app->organization + ); + } + + // TRANS: Application access type + $readWriteText = _('read-write'); + // TRANS: Application access type + $readOnlyText = _('read-only'); + + $access = ($this->connection->access_type & Oauth_application::$writeAccess) + ? $readWriteText : $readOnlyText; + $modifiedDate = common_date_string($this->connection->modified); + // TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only") + $txt = sprintf(_('Approved %1$s - "%2$s" access.'), $modifiedDate, $access); + + $this->out->raw(" - $txt"); + if (!empty($app->description)) { + $this->out->element( + 'p', array('class' => 'application_description'), + $app->description + ); + } + $this->out->element( + 'p', array( + 'class' => 'access_token'), + // TRANS: Access token in the application list. + // TRANS: %s are the first 7 characters of the access token. + sprintf(_('Access token starting with: %s'), substr($this->connection->token, 0, 7)) + ); + + $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('oauth_token', $this->connection->token); + $this->out->hidden('token', common_session_token()); + // TRANS: Button label + $this->out->submit('revoke', _m('BUTTON','Revoke')); + $this->out->elementEnd('fieldset'); + $this->out->elementEnd('form'); + + $this->out->elementEnd('li'); + } } diff --git a/lib/common.php b/lib/common.php index 236f2d68a..cd4fbfb15 100644 --- a/lib/common.php +++ b/lib/common.php @@ -22,10 +22,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } //exit with 200 response, if this is checking fancy from the installer if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; } -define('STATUSNET_VERSION', '0.9.5'); +define('STATUSNET_VERSION', '0.9.6'); define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility -define('STATUSNET_CODENAME', 'What\'s The Frequency, Kenneth?'); +define('STATUSNET_CODENAME', 'Man on the Moon'); define('AVATAR_PROFILE_SIZE', 96); define('AVATAR_STREAM_SIZE', 48); @@ -133,10 +133,10 @@ try { // XXX: Throw a conniption if database not installed // XXX: Find a way to use htmlwriter for this instead of handcoded markup // TRANS: Error message displayed when no configuration file was found for a StatusNet installation. - echo '<p>'. _('No configuration file found. ') .'</p>'; + echo '<p>'. _('No configuration file found.') .'</p>'; // TRANS: Error message displayed when no configuration file was found for a StatusNet installation. // TRANS: Is followed by a list of directories (separated by HTML breaks). - echo '<p>'. _('I looked for configuration files in the following places: ') .'<br /> '; + echo '<p>'. _('I looked for configuration files in the following places:') .'<br /> '; echo implode($e->configFiles, '<br />'); // TRANS: Error message displayed when no configuration file was found for a StatusNet installation. echo '<p>'. _('You may wish to run the installer to fix this.') .'</p>'; diff --git a/lib/dberroraction.php b/lib/dberroraction.php index 2cb66a022..0a6fce100 100644 --- a/lib/dberroraction.php +++ b/lib/dberroraction.php @@ -47,7 +47,6 @@ require_once INSTALLDIR.'/lib/servererroraction.php'; * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @link http://status.net/ */ - class DBErrorAction extends ServerErrorAction { function __construct($message='Error', $code=500) diff --git a/lib/default.php b/lib/default.php index 45e35e83d..a19453fce 100644 --- a/lib/default.php +++ b/lib/default.php @@ -37,6 +37,7 @@ $default = 'path' => $_path, 'logfile' => null, 'logo' => null, + 'ssllogo' => null, 'logdebug' => false, 'fancy' => false, 'locale_path' => INSTALLDIR.'/locale', @@ -210,6 +211,8 @@ $default = array('server' => null, 'dir' => INSTALLDIR . '/file/', 'path' => $_path . '/file/', + 'sslserver' => null, + 'sslpath' => null, 'ssl' => null, 'supported' => array('image/png', 'image/jpeg', @@ -314,7 +317,8 @@ $default = 'nofollow' => array('subscribers' => true, 'members' => true, - 'peopletag' => true), + 'peopletag' => true, + 'external' => 'sometimes'), // Options: 'sometimes', 'never', default = 'sometimes' 'http' => // HTTP client settings when contacting other sites array('ssl_cafile' => false, // To enable SSL cert validation, point to a CA bundle (eg '/usr/lib/ssl/certs/ca-certificates.crt') 'curl' => false, // Use CURL backend for HTTP fetches if available. (If not, PHP's socket streams will be used.) diff --git a/lib/dofollowlistitem.php b/lib/dofollowlistitem.php new file mode 100644 index 000000000..80e2d0b0a --- /dev/null +++ b/lib/dofollowlistitem.php @@ -0,0 +1,88 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * widget for displaying a list of notices + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category UI + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/noticelist.php'; + +/** + * StatusNet, the distributed open-source microblogging tool + * + * Widget superclass for notice list items that remove rel=nofollow + * + * When nofollow|external = 'sometimes', notices get rendered and saved + * with rel=nofollow for external links. We want to remove that relationship + * on some pages (profile, single notice, faves). This superclass for + * some noticelistitems will strip that bit of code out when showing + * notice content + * + * @category UI + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 + * @link http://status.net/ + */ + +class DoFollowListItem extends NoticeListItem +{ + /** + * show the content of the notice + * + * Trims out the rel=nofollow for external links + * if nofollow|external = 'sometimes' + * + * @return void + */ + + function showContent() + { + // FIXME: URL, image, video, audio + $this->out->elementStart('p', array('class' => 'entry-content')); + + if (!empty($this->notice->rendered)) { + $html = $this->notice->rendered; + } else { + $html = common_render_content($this->notice->content, $this->notice); + } + + if (common_config('nofollow', 'external') == 'sometimes') { + // remove the nofollow part + // XXX: cache the results here + + $html = preg_replace('/rel="(.*)nofollow ?/', 'rel="\1', $html); + } + + $this->out->raw($html); + + $this->out->elementEnd('p'); + } +}
\ No newline at end of file diff --git a/lib/feed.php b/lib/feed.php index e9fb6fdff..590265367 100644 --- a/lib/feed.php +++ b/lib/feed.php @@ -43,7 +43,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class Feed { const RSS1 = 1; diff --git a/lib/feedlist.php b/lib/feedlist.php index 4aacf0b3d..076576028 100644 --- a/lib/feedlist.php +++ b/lib/feedlist.php @@ -46,7 +46,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * * @see Action::showExportList() */ - class FeedList extends Widget { var $action = null; diff --git a/lib/groupsbymemberssection.php b/lib/groupsbymemberssection.php index 19b35eddb..5cf1a563c 100644 --- a/lib/groupsbymemberssection.php +++ b/lib/groupsbymemberssection.php @@ -40,7 +40,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class GroupsByMembersSection extends GroupSection { function getGroups() @@ -68,6 +67,7 @@ class GroupsByMembersSection extends GroupSection function title() { + // TRANS: Title for groups with the most members section. return _('Groups with most members'); } diff --git a/lib/groupsbypostssection.php b/lib/groupsbypostssection.php index 45d49aba6..50d60e87c 100644 --- a/lib/groupsbypostssection.php +++ b/lib/groupsbypostssection.php @@ -40,7 +40,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class GroupsByPostsSection extends GroupSection { function getGroups() @@ -68,6 +67,7 @@ class GroupsByPostsSection extends GroupSection function title() { + // TRANS: Title for groups with the most posts section. return _('Groups with most posts'); } diff --git a/lib/groupsection.php b/lib/groupsection.php index 3b0b3029d..019b13135 100644 --- a/lib/groupsection.php +++ b/lib/groupsection.php @@ -45,7 +45,6 @@ define('GROUPS_PER_SECTION', 6); * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class GroupSection extends Section { function showContent() diff --git a/lib/grouptagcloudsection.php b/lib/grouptagcloudsection.php index f1106cc7b..5b914c007 100644 --- a/lib/grouptagcloudsection.php +++ b/lib/grouptagcloudsection.php @@ -40,7 +40,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class GroupTagCloudSection extends TagCloudSection { var $group = null; @@ -53,6 +52,8 @@ class GroupTagCloudSection extends TagCloudSection function title() { + // TRANS: Title for group tag cloud section. + // TRANS: %s is a group name. return sprintf(_('Tags in %s group\'s notices'), $this->group->nickname); } diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php index 44b029604..42bff4490 100644 --- a/lib/htmloutputter.php +++ b/lib/htmloutputter.php @@ -352,58 +352,74 @@ class HTMLOutputter extends XMLOutputter */ function script($src, $type='text/javascript') { - if(Event::handle('StartScriptElement', array($this,&$src,&$type))) { + 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'])) - { + if (empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) { + + // XXX: this seems like a big assumption + if (strpos($src, 'plugins/') === 0 || strpos($src, 'local/') === 0) { - $src = common_path($src) . '?version=' . STATUSNET_VERSION; + $src = common_path($src, StatusNet::isHTTPS()) . '?version=' . STATUSNET_VERSION; - }else{ + } else { - $path = common_config('javascript', 'path'); + if (StatusNet::isHTTPS()) { - if (empty($path)) { - $path = common_config('site', 'path') . '/js/'; - } + $sslserver = common_config('javascript', 'sslserver'); - if ($path[strlen($path)-1] != '/') { - $path .= '/'; - } + if (empty($sslserver)) { + if (is_string(common_config('site', 'sslserver')) && + mb_strlen(common_config('site', 'sslserver')) > 0) { + $server = common_config('site', 'sslserver'); + } else if (common_config('site', 'server')) { + $server = common_config('site', 'server'); + } + $path = common_config('site', 'path') . '/js/'; + } else { + $server = $sslserver; + $path = common_config('javascript', 'sslpath'); + if (empty($path)) { + $path = common_config('javascript', 'path'); + } + } - if ($path[0] != '/') { - $path = '/'.$path; - } + $protocol = 'https'; - $server = common_config('javascript', 'server'); + } else { - if (empty($server)) { - $server = common_config('site', 'server'); - } + $path = common_config('javascript', 'path'); - $ssl = common_config('javascript', 'ssl'); + if (empty($path)) { + $path = common_config('site', 'path') . '/js/'; + } - if (is_null($ssl)) { // null -> guess - if (common_config('site', 'ssl') == 'always' && - !common_config('javascript', 'server')) { - $ssl = true; - } else { - $ssl = false; + $server = common_config('javascript', 'server'); + + if (empty($server)) { + $server = common_config('site', 'server'); } + + $protocol = 'http'; } - $protocol = ($ssl) ? 'https' : 'http'; + if ($path[strlen($path)-1] != '/') { + $path .= '/'; + } + + if ($path[0] != '/') { + $path = '/'.$path; + } $src = $protocol.'://'.$server.$path.$src . '?version=' . STATUSNET_VERSION; } } $this->element('script', array('type' => $type, - 'src' => $src), - ' '); + 'src' => $src), + ' '); Event::handle('EndScriptElement', array($this,$src,$type)); } @@ -453,7 +469,7 @@ class HTMLOutputter extends XMLOutputter if(file_exists(Theme::file($src,$theme))){ $src = Theme::path($src, $theme); }else{ - $src = common_path($src); + $src = common_path($src, StatusNet::isHTTPS()); } $src.= '?version=' . STATUSNET_VERSION; } diff --git a/lib/installer.php b/lib/installer.php index c046eadea..a9d809011 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -392,6 +392,30 @@ abstract class Installer } /** + * Return a parseable PHP literal for the given value. + * This will include quotes for strings, etc. + * + * @param mixed $val + * @return string + */ + function phpVal($val) + { + return var_export($val, true); + } + + /** + * Return an array of parseable PHP literal for the given values. + * These will include quotes for strings, etc. + * + * @param mixed $val + * @return array + */ + function phpVals($map) + { + return array_map(array($this, 'phpVal'), $map); + } + + /** * Write a stock configuration file. * * @return boolean success @@ -400,24 +424,32 @@ abstract class Installer */ function writeConf() { + $vals = $this->phpVals(array( + 'sitename' => $this->sitename, + 'server' => $this->server, + 'path' => $this->path, + 'db_database' => $this->db['database'], + 'db_type' => $this->db['type'], + )); + // assemble configuration file in a string $cfg = "<?php\n". "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n". // site name - "\$config['site']['name'] = '{$this->sitename}';\n\n". + "\$config['site']['name'] = {$vals['sitename']};\n\n". // site location - "\$config['site']['server'] = '{$this->server}';\n". - "\$config['site']['path'] = '{$this->path}'; \n\n". + "\$config['site']['server'] = {$vals['server']};\n". + "\$config['site']['path'] = {$vals['path']}; \n\n". // checks if fancy URLs are enabled ($this->fancy ? "\$config['site']['fancy'] = true;\n\n":''). // database - "\$config['db']['database'] = '{$this->db['database']}';\n\n". + "\$config['db']['database'] = {$vals['db_database']};\n\n". ($this->db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":''). - "\$config['db']['type'] = '{$this->db['type']}';\n\n"; + "\$config['db']['type'] = {$vals['db_type']};\n\n"; // Normalize line endings for Windows servers $cfg = str_replace("\n", PHP_EOL, $cfg); diff --git a/lib/mail.php b/lib/mail.php index ab5742e33..30d743848 100644 --- a/lib/mail.php +++ b/lib/mail.php @@ -170,19 +170,21 @@ function mail_to_user(&$user, $subject, $body, $headers=array(), $address=null) function mail_confirm_address($user, $code, $nickname, $address) { - // TRANS: Subject for address confirmation email + // TRANS: Subject for address confirmation email. $subject = _('Email address confirmation'); // TRANS: Body for address confirmation email. - $body = sprintf(_("Hey, %s.\n\n". - "Someone just entered this email address on %s.\n\n" . + // TRANS: %1$s is the addressed user's nickname, %2$s is the StatusNet sitename, + // TRANS: %3$s is the URL to confirm at. + $body = sprintf(_("Hey, %1\$s.\n\n". + "Someone just entered this email address on %2\$s.\n\n" . "If it was you, and you want to confirm your entry, ". - "use the URL below:\n\n\t%s\n\n" . + "use the URL below:\n\n\t%3\$s\n\n" . "If not, just ignore this message.\n\n". - "Thanks for your time, \n%s\n"), - $nickname, common_config('site', 'name'), - common_local_url('confirmaddress', array('code' => $code)), - common_config('site', 'name')); + "Thanks for your time, \n%2\$s\n"), + $nickname, + common_config('site', 'name'), + common_local_url('confirmaddress', array('code' => $code))); $headers = array(); return mail_to_user($user, $subject, $body, $headers, $address); @@ -239,41 +241,50 @@ function mail_subscribe_notify_profile($listenee, $other) $headers = _mail_prepare_headers('subscribe', $listenee->nickname, $other->nickname); $headers['From'] = mail_notify_from(); $headers['To'] = $name . ' <' . $listenee->email . '>'; - // TRANS: Subject of new-subscriber notification e-mail + // TRANS: Subject of new-subscriber notification e-mail. + // TRANS: %1$s is the subscribing user's nickname, %2$s is the StatusNet sitename. $headers['Subject'] = sprintf(_('%1$s is now listening to '. 'your notices on %2$s.'), $other->getBestName(), common_config('site', 'name')); + // TRANS: This is a paragraph in a new-subscriber e-mail. + // TRANS: %s is a URL where the subscriber can be reported as abusive. $blocklink = sprintf(_("If you believe this account is being used abusively, " . "you can block them from your subscribers list and " . "report as spam to site administrators at %s"), common_local_url('block', array('profileid' => $other->id))); - // TRANS: Main body of new-subscriber notification e-mail + // TRANS: Main body of new-subscriber notification e-mail. + // TRANS: %1$s is the subscriber's long name, %2$s is the StatusNet sitename, + // TRANS: %3$s is the subscriber's profile URL, %4$s is the subscriber's location (or empty) + // TRANS: %5$s is the subscriber's homepage URL (or empty), %6%s is the subscriber's bio (or empty) + // TRANS: %7$s is a link to the addressed user's e-mail settings. $body = sprintf(_('%1$s is now listening to your notices on %2$s.'."\n\n". "\t".'%3$s'."\n\n". '%4$s'. '%5$s'. '%6$s'. - "\n".'Faithfully yours,'."\n".'%7$s.'."\n\n". + "\n".'Faithfully yours,'."\n".'%2$s.'."\n\n". "----\n". "Change your email address or ". - "notification options at ".'%8$s' ."\n"), + "notification options at ".'%7$s' ."\n"), $long_name, common_config('site', 'name'), $other->profileurl, ($other->location) ? - // TRANS: Profile info line in new-subscriber notification e-mail + // TRANS: Profile info line in new-subscriber notification e-mail. + // TRANS: %s is a location. sprintf(_("Location: %s"), $other->location) . "\n" : '', ($other->homepage) ? - // TRANS: Profile info line in new-subscriber notification e-mail + // TRANS: Profile info line in new-subscriber notification e-mail. + // TRANS: %s is a homepage. sprintf(_("Homepage: %s"), $other->homepage) . "\n" : '', (($other->bio) ? - // TRANS: Profile info line in new-subscriber notification e-mail + // TRANS: Profile info line in new-subscriber notification e-mail. + // TRANS: %s is biographical information. sprintf(_("Bio: %s"), $other->bio) . "\n" : '') . "\n\n" . $blocklink . "\n", - common_config('site', 'name'), common_local_url('emailsettings')); // reset localization @@ -291,7 +302,6 @@ function mail_subscribe_notify_profile($listenee, $other) * * @return void */ - function mail_new_incoming_notify($user) { $profile = $user->getProfile(); @@ -300,19 +310,21 @@ function mail_new_incoming_notify($user) $headers['From'] = $user->incomingemail; $headers['To'] = $name . ' <' . $user->email . '>'; - // TRANS: Subject of notification mail for new posting email address + // TRANS: Subject of notification mail for new posting email address. + // TRANS: %s is the StatusNet sitename. $headers['Subject'] = sprintf(_('New email address for posting to %s'), common_config('site', 'name')); - // TRANS: Body of notification mail for new posting email address + // TRANS: Body of notification mail for new posting email address. + // TRANS: %1$s is the StatusNet sitename, %2$s is the e-mail address to send + // TRANS: to to post by e-mail, %3$s is a URL to more instructions. $body = sprintf(_("You have a new posting address on %1\$s.\n\n". "Send email to %2\$s to post new messages.\n\n". "More email instructions at %3\$s.\n\n". - "Faithfully yours,\n%4\$s"), + "Faithfully yours,\n%1\$s"), common_config('site', 'name'), $user->incomingemail, - common_local_url('doc', array('title' => 'email')), - common_config('site', 'name')); + common_local_url('doc', array('title' => 'email'))); mail_send($user->email, $headers, $body); } @@ -324,7 +336,6 @@ function mail_new_incoming_notify($user) * * @return string new email address for incoming messages */ - function mail_new_incoming_address() { $prefix = common_confirmation_code(64); @@ -343,7 +354,6 @@ function mail_new_incoming_address() * * @return success flag */ - function mail_broadcast_notice_sms($notice) { // Now, get users subscribed to this profile @@ -395,7 +405,6 @@ function mail_broadcast_notice_sms($notice) * * @return boolean success flag */ - function mail_send_sms_notice($notice, $user) { return mail_send_sms_notice_address($notice, @@ -415,7 +424,6 @@ function mail_send_sms_notice($notice, $user) * * @return boolean success flag */ - function mail_send_sms_notice_address($notice, $smsemail, $incomingemail) { $to = $nickname . ' <' . $smsemail . '>'; @@ -429,7 +437,8 @@ function mail_send_sms_notice_address($notice, $smsemail, $incomingemail) $headers['From'] = ($incomingemail) ? $incomingemail : mail_notify_from(); $headers['To'] = $to; - // TRANS: Subject line for SMS-by-email notification messages + // TRANS: Subject line for SMS-by-email notification messages. + // TRANS: %s is the posting user's nickname. $headers['Subject'] = sprintf(_('%s status'), $other->getBestName()); @@ -449,17 +458,17 @@ function mail_send_sms_notice_address($notice, $smsemail, $incomingemail) * * @return void */ - function mail_confirm_sms($code, $nickname, $address) { $recipients = $address; $headers['From'] = mail_notify_from(); $headers['To'] = $nickname . ' <' . $address . '>'; - // TRANS: Subject line for SMS-by-email address confirmation message + // TRANS: Subject line for SMS-by-email address confirmation message. $headers['Subject'] = _('SMS confirmation'); - // TRANS: Main body heading for SMS-by-email address confirmation message + // TRANS: Main body heading for SMS-by-email address confirmation message. + // TRANS: %s is the addressed user's nickname. $body = sprintf(_("%s: confirm you own this phone number with this code:"), $nickname); $body .= "\n\n"; $body .= $code; @@ -476,16 +485,18 @@ function mail_confirm_sms($code, $nickname, $address) * * @return boolean success flag */ - function mail_notify_nudge($from, $to) { common_switch_locale($to->language); - // TRANS: Subject for 'nudge' notification email + // TRANS: Subject for 'nudge' notification email. + // TRANS: %s is the nudging user. $subject = sprintf(_('You\'ve been nudged by %s'), $from->nickname); $from_profile = $from->getProfile(); - // TRANS: Body for 'nudge' notification email + // TRANS: Body for 'nudge' notification email. + // TRANS: %1$s is the nuding user's long name, $2$s is the nudging user's nickname, + // TRANS: %3$s is a URL to post notices at, %4$s is the StatusNet sitename. $body = sprintf(_("%1\$s (%2\$s) is wondering what you are up to ". "these days and is inviting you to post some news.\n\n". "So let's hear from you :)\n\n". @@ -516,7 +527,6 @@ function mail_notify_nudge($from, $to) * * @return boolean success code */ - function mail_notify_message($message, $from=null, $to=null) { if (is_null($from)) { @@ -532,12 +542,16 @@ function mail_notify_message($message, $from=null, $to=null) } common_switch_locale($to->language); - // TRANS: Subject for direct-message notification email + // TRANS: Subject for direct-message notification email. + // TRANS: %s is the sending user's nickname. $subject = sprintf(_('New private message from %s'), $from->nickname); $from_profile = $from->getProfile(); - // TRANS: Body for direct-message notification email + // TRANS: Body for direct-message notification email. + // TRANS: %1$s is the sending user's long name, %2$s is the sending user's nickname, + // TRANS: %3$s is the message content, %4$s a URL to the message, + // TRANS: %5$s is the StatusNet sitename. $body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n". "------------------------------------------------------\n". "%3\$s\n". @@ -572,7 +586,6 @@ function mail_notify_message($message, $from=null, $to=null) * * @return void */ - function mail_notify_fave($other, $user, $notice) { if (!$user->hasRight(Right::EMAILONFAVE)) { @@ -585,10 +598,15 @@ function mail_notify_fave($other, $user, $notice) common_switch_locale($other->language); - // TRANS: Subject for favorite notification email - $subject = sprintf(_('%s (@%s) added your notice as a favorite'), $bestname, $user->nickname); + // TRANS: Subject for favorite notification e-mail. + // TRANS: %1$s is the adding user's long name, %2$s is the adding user's nickname. + $subject = sprintf(_('%1$s (@%2$s) added your notice as a favorite'), $bestname, $user->nickname); - // TRANS: Body for favorite notification email + // TRANS: Body for favorite notification e-mail. + // TRANS: %1$s is the adding user's long name, $2$s is the date the notice was created, + // TRANS: %3$s is a URL to the faved notice, %4$s is the faved notice text, + // TRANS: %5$s is a URL to all faves of the adding user, %6$s is the StatusNet sitename, + // TRANS: %7$s is the adding user's nickname. $body = sprintf(_("%1\$s (@%7\$s) just added your notice from %2\$s". " as one of their favorites.\n\n" . "The URL of your notice is:\n\n" . @@ -623,7 +641,6 @@ function mail_notify_fave($other, $user, $notice) * * @return void */ - function mail_notify_attn($user, $notice) { if (!$user->email || !$user->emailnotifyattn) { @@ -654,9 +671,16 @@ function mail_notify_attn($user, $notice) $conversationEmailText = ''; } - $subject = sprintf(_('%s (@%s) sent a notice to your attention'), $bestname, $sender->nickname); + // TRANS: E-mail subject for notice notification. + // TRANS: %1$s is the sending user's long name, %2$s is the adding user's nickname. + $subject = sprintf(_('%1$s (@%2$s) sent a notice to your attention'), $bestname, $sender->nickname); // TRANS: Body of @-reply notification e-mail. + // TRANS: %1$s is the sending user's long name, $2$s is the StatusNet sitename, + // TRANS: %3$s is a URL to the notice, %4$s is the notice text, + // TRANS: %5$s is a URL to the full conversion if it exists (otherwise empty), + // TRANS: %6$s is a URL to reply to the notice, %7$s is a URL to all @-replied for the addressed user, + // TRANS: %8$s is a URL to the addressed user's e-mail settings, %9$s is the sender's nickname. $body = sprintf(_("%1\$s (@%9\$s) just sent a notice to your attention (an '@-reply') on %2\$s.\n\n". "The notice is here:\n\n". "\t%3\$s\n\n" . @@ -709,4 +733,3 @@ function _mail_prepare_headers($msg_type, $to, $from) return $headers; } - diff --git a/lib/router.php b/lib/router.php index b1cc8d529..9aaac7dfe 100644 --- a/lib/router.php +++ b/lib/router.php @@ -34,7 +34,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { require_once 'Net/URL/Mapper.php'; class StatusNet_URL_Mapper extends Net_URL_Mapper { - private static $_singleton = null; private function __construct() @@ -71,7 +70,6 @@ class StatusNet_URL_Mapper extends Net_URL_Mapper { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ - class Router { var $m = null; @@ -553,11 +551,19 @@ class Router 'format' => '(xml|json)')); // blocks + $m->connect('api/blocks/create.:format', + array('action' => 'ApiBlockCreate', + 'format' => '(xml|json)')); + $m->connect('api/blocks/create/:id.:format', array('action' => 'ApiBlockCreate', 'id' => '[a-zA-Z0-9]+', 'format' => '(xml|json)')); + $m->connect('api/blocks/destroy.:format', + array('action' => 'ApiBlockDestroy', + 'format' => '(xml|json)')); + $m->connect('api/blocks/destroy/:id.:format', array('action' => 'ApiBlockDestroy', 'id' => '[a-zA-Z0-9]+', @@ -692,7 +698,6 @@ class Router $m->connect('admin/snapshot', array('action' => 'snapshotadminpanel')); $m->connect('admin/license', array('action' => 'licenseadminpanel')); - $m->connect('getfile/:filename', array('action' => 'getfile'), array('filename' => '[A-Za-z0-9._-]+')); @@ -701,16 +706,8 @@ class Router if (common_config('singleuser', 'enabled')) { - $user = User::siteOwner(); - - if (!empty($user)) { - $nickname = $user->nickname; - } else { - $nickname = common_config('singleuser', 'nickname'); - if (empty($nickname)) { - throw new ServerException(_("No single user defined for single-user mode.")); - } - } + $user = User::singleUser(); + $nickname = $user->nickname; foreach (array('subscriptions', 'subscribers', 'all', 'foaf', 'xrds', @@ -765,9 +762,7 @@ class Router $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')); @@ -848,7 +843,8 @@ class Router } catch (Net_URL_Mapper_InvalidException $e) { common_log(LOG_ERR, "Problem getting route for $path - " . $e->getMessage()); - $cac = new ClientErrorAction("Page not found.", 404); + // TRANS: Client error on action trying to visit a non-existing page. + $cac = new ClientErrorAction(_('Page not found.'), 404); $cac->showPage(); } @@ -875,7 +871,16 @@ class Router if ($qpos !== false) { $url = substr($url, 0, $qpos+1) . str_replace('?', '&', substr($url, $qpos+1)); + + // @fixme this is a hacky workaround for http_build_query in the + // lower-level code and bad configs that set the default separator + // to & instead of &. Encoded &s in parameters will not be + // affected. + $url = substr($url, 0, $qpos+1) . + str_replace('&', '&', substr($url, $qpos+1)); + } + return $url; } } diff --git a/lib/statusnet.php b/lib/statusnet.php index 7cb831696..33bf32b10 100644 --- a/lib/statusnet.php +++ b/lib/statusnet.php @@ -169,7 +169,6 @@ class StatusNet return $sites; } - /** * Fire initialization events for all instantiated plugins. */ @@ -220,7 +219,7 @@ class StatusNet { return self::$is_api; } - + public function setApi($mode) { self::$is_api = $mode; @@ -368,6 +367,18 @@ class StatusNet } } } + + /** + * Are we running from the web with HTTPS? + * + * @return boolean true if we're running with HTTPS; else false + */ + + static function isHTTPS() + { + // There are some exceptions to this; add them here! + return !empty($_SERVER['HTTPS']); + } } class NoConfigException extends Exception diff --git a/lib/theme.php b/lib/theme.php index 992fce870..95b7c1de4 100644 --- a/lib/theme.php +++ b/lib/theme.php @@ -38,7 +38,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * Themes are directories with some expected sub-directories and files * in them. They're found in either local/theme (for locally-installed themes) * or theme/ subdir of installation dir. - * + * * Note that the 'local' directory can be overridden as $config['local']['path'] * and $config['local']['dir'] etc. * @@ -104,56 +104,72 @@ class Theme /** * Build a full URL to the given theme's base directory, possibly * using an offsite theme server path. - * + * * @param string $group configuration section name to pull paths from * @param string $fallbackSubdir default subdirectory under INSTALLDIR * @param string $name theme name - * + * * @return string URL - * + * * @todo consolidate code with that for other customizable paths */ protected function relativeThemePath($group, $fallbackSubdir, $name) { - $path = common_config($group, 'path'); + if (StatusNet::isHTTPS()) { - if (empty($path)) { - $path = common_config('site', 'path') . '/'; - if ($fallbackSubdir) { - $path .= $fallbackSubdir . '/'; + $sslserver = common_config($group, 'sslserver'); + + if (empty($sslserver)) { + if (is_string(common_config('site', 'sslserver')) && + mb_strlen(common_config('site', 'sslserver')) > 0) { + $server = common_config('site', 'sslserver'); + } else if (common_config('site', 'server')) { + $server = common_config('site', 'server'); + } + $path = common_config('site', 'path') . '/'; + if ($fallbackSubdir) { + $path .= $fallbackSubdir . '/'; + } + } else { + $server = $sslserver; + $path = common_config($group, 'sslpath'); + if (empty($path)) { + $path = common_config($group, 'path'); + } } - } - if ($path[strlen($path)-1] != '/') { - $path .= '/'; - } + $protocol = 'https'; - if ($path[0] != '/') { - $path = '/'.$path; - } + } else { - $server = common_config($group, 'server'); + $path = common_config($group, 'path'); - if (empty($server)) { - $server = common_config('site', 'server'); - } + if (empty($path)) { + $path = common_config('site', 'path') . '/'; + if ($fallbackSubdir) { + $path .= $fallbackSubdir . '/'; + } + } - $ssl = common_config($group, 'ssl'); + $server = common_config($group, 'server'); - if (is_null($ssl)) { // null -> guess - if (common_config('site', 'ssl') == 'always' && - !common_config($group, 'server')) { - $ssl = true; - } else { - $ssl = false; + if (empty($server)) { + $server = common_config('site', 'server'); } + + $protocol = 'http'; } - $protocol = ($ssl) ? 'https' : 'http'; + if ($path[strlen($path)-1] != '/') { + $path .= '/'; + } - $path = $protocol . '://'.$server.$path.$name; - return $path; + if ($path[0] != '/') { + $path = '/'.$path; + } + + return $protocol.'://'.$server.$path.$name; } /** @@ -221,7 +237,7 @@ class Theme /** * Pull data from the theme's theme.ini file. * @fixme calling getFile will fall back to default theme, this may be unsafe. - * + * * @return associative array of strings */ function getMetadata() diff --git a/lib/util.php b/lib/util.php index c05fcf15a..6044fdd92 100644 --- a/lib/util.php +++ b/lib/util.php @@ -145,7 +145,6 @@ function common_switch_locale($language=null) textdomain("statusnet"); } - function common_timezone() { if (common_logged_in()) { @@ -860,7 +859,8 @@ function common_linkify($url) { $longurl = $url; } } - $attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external'); + + $attrs = array('href' => $canon, 'title' => $longurl); $is_attachment = false; $attachment_id = null; @@ -896,6 +896,16 @@ function common_linkify($url) { $attrs['id'] = "attachment-{$attachment_id}"; } + // Whether to nofollow + + $nf = common_config('nofollow', 'external'); + + if ($nf == 'never') { + $attrs['rel'] = 'external'; + } else { + $attrs['rel'] = 'nofollow external'; + } + return XMLStringer::estring('a', $attrs, $url); } @@ -964,8 +974,9 @@ function common_tag_link($tag) $canonical = common_canonical_tag($tag); if (common_config('singleuser', 'enabled')) { // regular TagAction isn't set up in 1user mode + $user = User::singleUser(); $url = common_local_url('showstream', - array('nickname' => common_config('singleuser', 'nickname'), + array('nickname' => $user->nickname, 'tag' => $canonical)); } else { $url = common_local_url('tag', array('tag' => $canonical)); @@ -1069,7 +1080,17 @@ function common_local_url($action, $args=null, $params=null, $fragment=null, $ad function common_is_sensitive($action) { - static $sensitive = array('login', 'register', 'passwordsettings', 'api'); + static $sensitive = array( + 'login', + 'register', + 'passwordsettings', + 'api', + 'ApiOauthRequestToken', + 'ApiOauthAccessToken', + 'ApiOauthAuthorize', + 'ApiOauthPin', + 'showapplication' + ); $ssl = null; if (Event::handle('SensitiveAction', array($action, &$ssl))) { diff --git a/lib/webcolor.php b/lib/webcolor.php index 6fa603fa2..7f264c674 100644 --- a/lib/webcolor.php +++ b/lib/webcolor.php @@ -32,7 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { } class WebColor { - // XXX: Maybe make getters and setters for r,g,b values and tuples, // e.g.: to support this kinda CSS representation: rgb(255,0,0) // http://www.w3.org/TR/CSS21/syndata.html#color-units @@ -65,7 +64,6 @@ class WebColor { * * @return nothing */ - function parseColor($color) { if (is_numeric($color)) { @@ -90,13 +88,11 @@ class WebColor { * * @return nothing */ - function setNamedColor($name) { // XXX Implement this } - /** * Sets the RGB color values from a a hex tuple * @@ -104,7 +100,6 @@ class WebColor { * * @return nothing */ - function setHexColor($hexcolor) { if ($hexcolor[0] == '#') { @@ -120,7 +115,9 @@ class WebColor { $hexcolor[1].$hexcolor[1], $hexcolor[2].$hexcolor[2]); } else { - $errmsg = _('%s is not a valid color! Use 3 or 6 hex chars.'); + // TRANS: Validation error for a web colour. + // TRANS: %s is the provided (invalid) text for colour. + $errmsg = _('%s is not a valid color! Use 3 or 6 hex characters.'); throw new WebColorException(sprintf($errmsg, $hexcolor)); } @@ -137,7 +134,6 @@ class WebColor { * * @return nothing */ - function setIntColor($intcolor) { // We could do 32 bit and have an alpha channel because @@ -154,7 +150,6 @@ class WebColor { * * @return string */ - function hexValue() { $hexcolor = (strlen(dechex($this->red)) < 2 ? '0' : '' ) . @@ -165,7 +160,6 @@ class WebColor { dechex($this->blue); return strtoupper($hexcolor); - } /** @@ -176,7 +170,6 @@ class WebColor { * * @return int */ - function intValue() { $intcolor = 256 * 256 * $this->red + 256 * $this->green + $this->blue; @@ -188,5 +181,3 @@ class WebColor { class WebColorException extends Exception { } - -?>
\ No newline at end of file diff --git a/lib/xmppmanager.php b/lib/xmppmanager.php index 829eaa36c..7acd7663a 100644 --- a/lib/xmppmanager.php +++ b/lib/xmppmanager.php @@ -30,7 +30,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } * In a multi-site queuedaemon.php run, one connection will be instantiated * for each site being handled by the current process that has XMPP enabled. */ - class XmppManager extends IoManager { protected $site = null; @@ -102,6 +101,7 @@ class XmppManager extends IoManager $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this); $this->conn->setReconnectTimeout(600); + // @todo Needs i18n? jabber_send_presence("Send me a message to post a notice", 'available', null, 'available', 100); return !is_null($this->conn); @@ -281,9 +281,9 @@ class XmppManager extends IoManager $_cur = $user; if (!$user) { - $this->from_site($from, 'Unknown user; go to ' . - common_local_url('imsettings') . - ' to add your address to your account'); + // TRANS: %s is the URL to the StatusNet site's Instant Messaging settings. + $this->from_site($from, sprintf(_('Unknown user. Go to %s ' . + 'to add your address to your account'),common_local_url('imsettings'))); $this->log(LOG_WARNING, 'Message from unknown user ' . $from); return; } @@ -314,7 +314,6 @@ class XmppManager extends IoManager unset($pl); } - function is_self($from) { return preg_match('/^'.strtolower(jabber_daemon_address()).'/', strtolower($from)); @@ -400,7 +399,11 @@ class XmppManager extends IoManager $content_shortened = common_shorten_links($body); if (Notice::contentTooLong($content_shortened)) { $from = jabber_normalize_jid($pl['from']); - $this->from_site($from, sprintf(_('Message too long - maximum is %1$d characters, you sent %2$d.'), + // TRANS: Response to XMPP source when it sent too long a message. + // TRANS: %1$d the maximum number of allowed characters (used for plural), %2$d is the sent number. + $this->from_site($from, sprintf(_m('Message too long. Maximum is %1$d character, you sent %2$d.', + 'Message too long. Maximum is %1$d characters, you sent %2$d.', + Notice::maxContent()), Notice::maxContent(), mb_strlen($content_shortened))); return; diff --git a/lib/xmppoutqueuehandler.php b/lib/xmppoutqueuehandler.php index 2afa260f1..a4c9bbc4d 100644 --- a/lib/xmppoutqueuehandler.php +++ b/lib/xmppoutqueuehandler.php @@ -52,4 +52,3 @@ class XmppOutQueueHandler extends QueueHandler return $ok; } } - |