Merge branch '0.9.x' into twitstream

This commit is contained in:
Brion Vibber 2010-10-26 15:39:31 -07:00
commit 8ff44a1fb9
359 changed files with 61876 additions and 28097 deletions

View File

@ -1136,3 +1136,25 @@ StartShowFeedLink: before showing an individual feed item
EndShowFeedLink: after showing an individual feed EndShowFeedLink: after showing an individual feed
- $action: action being executed - $action: action being executed
- $feed: feed to show - $feed: feed to show
StartShowNoticeForm: before showing the notice form (before <form>)
- $action: action being executed
EndShowNoticeForm: after showing the notice form (after <form>)
- $action: action being executed
StartGrantRole: when a role is being assigned
- $profile: profile that will have the role
- $role: string name of the role
EndGrantRole: when a role has been successfully assigned
- $profile: profile that will have the role
- $role: string name of the role
StartRevokeRole: when a role is being revoked
- $profile: profile that will lose the role
- $role: string name of the role
EndRevokeRole: when a role has been revoked
- $profile: profile that lost the role
- $role: string name of the role

113
README
View File

@ -2,8 +2,8 @@
README README
------ ------
StatusNet 0.9.5 "What's The Frequency, Kenneth?" StatusNet 0.9.6 "Man on the Moon"
10 September 2010 25 October 2010 - RELEASE CANDIDATE
This is the README file for StatusNet, the Open Source microblogging This is the README file for StatusNet, the Open Source microblogging
platform. It includes installation instructions, descriptions of platform. It includes installation instructions, descriptions of
@ -96,8 +96,8 @@ for additional terms.
New this version New this version
================ ================
This is a security, bug and feature release since version 0.9.4 released on This is a security, bug and feature release since version 0.9.5 released on
16 August 2010. 10 September 2010.
For best compatibility with client software and site federation, and a lot of For best compatibility with client software and site federation, and a lot of
bug fixes, it is highly recommended that all public sites upgrade to the new bug fixes, it is highly recommended that all public sites upgrade to the new
@ -105,24 +105,24 @@ version.
Notable changes this version: Notable changes this version:
- Change of license for default themes and documentation from - Site moderators can now delete groups.
AGPLv3 to CC-By 3.0 Unported. - New themes: clean, shiny, mnml, victorian
- An experimental TinyMCE plugin to do in-browser rich editing of - New YammerImport plugin allows site admins to import non-private profiles and
status updates. Does not support StatusNet syntax like @-replies or message from an authenticated Yammer site.
#hashtags very well. - New experimental plugins: AnonFavorites, SlicedFavorites, GroupFavorited,
- An experimental plugin to add titles to notices. ForceGroup, ShareNotice
- A plugin to support the Echo <http://aboutecho.com/> commenting - OAuth upgraded to 1.0a
system. - Localization updates now include plugins, thanks to translatewiki.net!
- A plugin to support the Disqus <http://disqus.com/> commenting system. - SSL link generation should be more consistent; alternate SSL URLs can be
- Changes to OStatus support to make StatusNet work for the Social Web set in the admin UI for more parts of the system.
Acid Test Level 0 <http://federatedsocialweb.net/wiki/SWAT0>. - Experimental backupuser.php, restoreuser.php command-line scripts to
- Themes now support a theme.ini file for theme configuration, including dump/restore a user's complete activity stream. Can be used to transfer
defining a "base" theme. accounts manually between sites, or to save a backup before deleting.
- Improved two-way Twitter integration, including support for - Unicode fixes for OStatus notices
repeats and retweets, replies, and faves going both ways across the - Header metadata on notice pages to aid in manual reposting on Facebook
bridge, as well as better parsing of Twitter statuses. - Lots of little fixes...
A full changelog is available at http://status.net/wiki/StatusNet_0.9.5. A full changelog is available at http://status.net/wiki/StatusNet_0.9.6.
Prerequisites Prerequisites
============= =============
@ -235,9 +235,9 @@ especially if you've previously installed PHP/MySQL packages.
1. Unpack the tarball you downloaded on your Web server. Usually a 1. Unpack the tarball you downloaded on your Web server. Usually a
command like this will work: command like this will work:
tar zxf statusnet-0.9.5.tar.gz tar zxf statusnet-0.9.6.tar.gz
...which will make a statusnet-0.9.5 subdirectory in your current ...which will make a statusnet-0.9.6 subdirectory in your current
directory. (If you don't have shell access on your Web server, you directory. (If you don't have shell access on your Web server, you
may have to unpack the tarball on your local computer and FTP the may have to unpack the tarball on your local computer and FTP the
files to the server.) files to the server.)
@ -245,7 +245,7 @@ especially if you've previously installed PHP/MySQL packages.
2. Move the tarball to a directory of your choosing in your Web root 2. Move the tarball to a directory of your choosing in your Web root
directory. Usually something like this will work: directory. Usually something like this will work:
mv statusnet-0.9.5 /var/www/statusnet mv statusnet-0.9.6 /var/www/statusnet
This will make your StatusNet instance available in the statusnet path of This will make your StatusNet instance available in the statusnet path of
your server, like "http://example.net/statusnet". "microblog" or your server, like "http://example.net/statusnet". "microblog" or
@ -604,7 +604,7 @@ subdirectory to add a new language to your system. You'll need to
compile the ".po" files into ".mo" files, however. compile the ".po" files into ".mo" files, however.
Contributions of translation information to StatusNet are very easy: Contributions of translation information to StatusNet are very easy:
you can use the Web interface at TranslateWiki.net to add one you can use the Web interface at translatewiki.net to add one
or a few or lots of new translations -- or even new languages. You can or a few or lots of new translations -- or even new languages. You can
also download more up-to-date .po files there, if you so desire. also download more up-to-date .po files there, if you so desire.
@ -660,7 +660,7 @@ with this situation.
If you've been using StatusNet 0.7, 0.6, 0.5 or lower, or if you've If you've been using StatusNet 0.7, 0.6, 0.5 or lower, or if you've
been tracking the "git" version of the software, you will probably been tracking the "git" version of the software, you will probably
want to upgrade and keep your existing data. There is no automated want to upgrade and keep your existing data. There is no automated
upgrade procedure in StatusNet 0.9.5. Try these step-by-step upgrade procedure in StatusNet 0.9.6. Try these step-by-step
instructions; read to the end first before trying them. instructions; read to the end first before trying them.
0. Download StatusNet and set up all the prerequisites as if you were 0. Download StatusNet and set up all the prerequisites as if you were
@ -681,10 +681,11 @@ instructions; read to the end first before trying them.
5. Once all writing processes to your site are turned off, make a 5. Once all writing processes to your site are turned off, make a
final backup of the Web directory and database. final backup of the Web directory and database.
6. Move your StatusNet directory to a backup spot, like "statusnet.bak". 6. Move your StatusNet directory to a backup spot, like "statusnet.bak".
7. Unpack your StatusNet 0.9.5 tarball and move it to "statusnet" or 7. Unpack your StatusNet 0.9.6 tarball and move it to "statusnet" or
wherever your code used to be. wherever your code used to be.
8. Copy the config.php file and avatar directory from your old 8. Copy the config.php file and the contents of the avatar/, background/,
directory to your new directory. file/, and local/ subdirectories from your old directory to your new
directory.
9. Copy htaccess.sample to .htaccess in the new directory. Change the 9. Copy htaccess.sample to .htaccess in the new directory. Change the
RewriteBase to use the correct path. RewriteBase to use the correct path.
10. Rebuild the database. (You can safely skip this step and go to #12 10. Rebuild the database. (You can safely skip this step and go to #12
@ -852,6 +853,8 @@ notice: A plain string that will appear on every page. A good place
be escaped. be escaped.
logo: URL of an image file to use as the logo for the site. Overrides logo: URL of an image file to use as the logo for the site. Overrides
the logo in the theme, if any. the logo in the theme, if any.
ssllogo: URL of an image file to use as the logo on SSL pages. If unset,
theme logo is used instead.
ssl: Whether to use SSL and https:// URLs for some or all pages. ssl: Whether to use SSL and https:// URLs for some or all pages.
Possible values are 'always' (use it for all pages), 'never' Possible values are 'always' (use it for all pages), 'never'
(don't use it for any pages), or 'sometimes' (use it for (don't use it for any pages), or 'sometimes' (use it for
@ -1111,6 +1114,9 @@ path: Path part of theme URLs, before the theme name. Relative to the
which means to use the site path + '/theme'. which means to use the site path + '/theme'.
ssl: Whether to use SSL for theme elements. Default is null, which means ssl: Whether to use SSL for theme elements. Default is null, which means
guess based on site SSL settings. guess based on site SSL settings.
sslserver: SSL server to use when page is HTTPS-encrypted. If
unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
javascript javascript
---------- ----------
@ -1122,6 +1128,9 @@ path: Path part of Javascript URLs. Defaults to null,
which means to use the site path + '/js/'. which means to use the site path + '/js/'.
ssl: Whether to use SSL for JavaScript files. Default is null, which means ssl: Whether to use SSL for JavaScript files. Default is null, which means
guess based on site SSL settings. guess based on site SSL settings.
sslserver: SSL server to use when page is HTTPS-encrypted. If
unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
xmpp xmpp
---- ----
@ -1349,6 +1358,11 @@ ssl: whether to use HTTPS for file URLs. Defaults to null, meaning to
filecommand: command to use for determining the type of a file. May be filecommand: command to use for determining the type of a file. May be
skipped if fileinfo extension is installed. Defaults to skipped if fileinfo extension is installed. Defaults to
'/usr/bin/file'. '/usr/bin/file'.
sslserver: if specified, this server will be used when creating HTTPS
URLs. Otherwise, the site SSL server will be used, with /file/ path.
sslpath: if this and the sslserver are specified, this path will be used
when creating HTTPS URLs. Otherwise, the attachments|path value
will be used.
group group
----- -----
@ -1405,8 +1419,9 @@ dir: directory to write backgrounds too. Default is '/background/'
subdir of install dir. subdir of install dir.
path: path to backgrounds. Default is sub-path of install path; note path: path to backgrounds. Default is sub-path of install path; note
that you may need to change this if you change site-path too. that you may need to change this if you change site-path too.
ssl: Whether or not to use HTTPS for background files. Defaults to sslserver: SSL server to use when page is HTTPS-encrypted. If
null, meaning to guess from site-wide SSL settings. unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
ping ping
---- ----
@ -1471,7 +1486,8 @@ 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. interface. It also makes the user's profile the root URL.
enabled: Whether to run in "single user mode". Default false. enabled: Whether to run in "single user mode". Default false.
nickname: nickname of the single user. nickname: nickname of the single user. If no nickname is specified,
the site owner account will be used (if present).
robotstxt robotstxt
--------- ---------
@ -1487,6 +1503,33 @@ disallow: Array of (virtual) directories to disallow. Default is 'main',
'search', 'message', 'settings', 'admin'. Ignored when site 'search', 'message', 'settings', 'admin'. Ignored when site
is private, in which case the entire site ('/') is disallowed. is private, in which case the entire site ('/') is disallowed.
api
---
Options for the Twitter-like API.
realm: HTTP Basic Auth realm (see http://tools.ietf.org/html/rfc2617
for details). Some third-party tools like ping.fm want this to be
'Identi.ca API', so set it to that if you want to. default = null,
meaning 'something based on the site name'.
nofollow
--------
We optionally put 'rel="nofollow"' on some links in some pages. The
following configuration settings let you fine-tune how or when things
are nofollowed. See http://en.wikipedia.org/wiki/Nofollow for more
information on what 'nofollow' means.
subscribers: whether to nofollow links to subscribers on the profile
and personal pages. Default is true.
members: links to members on the group page. Default true.
peopletag: links to people listed in the peopletag page. Default true.
external: external links in notices. One of three values: 'sometimes',
'always', 'never'. If 'sometimes', then external links are not
nofollowed on profile, notice, and favorites page. Default is
'sometimes'.
Plugins Plugins
======= =======
@ -1544,7 +1587,7 @@ repository (see below), and you get a compilation error ("unexpected
T_STRING") in the browser, check to see that you don't have any T_STRING") in the browser, check to see that you don't have any
conflicts in your code. conflicts in your code.
If you upgraded to StatusNet 0.9.5 without reading the "Notice If you upgraded to StatusNet 0.9.x without reading the "Notice
inboxes" section above, and all your users' 'Personal' tabs are empty, inboxes" section above, and all your users' 'Personal' tabs are empty,
read the "Notice inboxes" section above. read the "Notice inboxes" section above.
@ -1655,6 +1698,10 @@ if anyone's been overlooked in error.
* mEDI * mEDI
* Brett Taylor * Brett Taylor
* Brigitte Schuster * Brigitte Schuster
* Siebrand Mazeland and the amazing volunteer translators at translatewiki.net
* Brion Vibber, StatusNet, Inc.
* James Walker, StatusNet, Inc.
* Samantha Doherty, designer, StatusNet, Inc.
Thanks also to the developers of our upstream library code and to the Thanks also to the developers of our upstream library code and to the
thousands of people who have tried out Identi.ca, installed StatusNet, thousands of people who have tried out Identi.ca, installed StatusNet,

View File

@ -89,7 +89,7 @@ class AllAction extends ProfileAction
// TRANS: Page title. %1$s is user nickname, %2$d is page number // TRANS: Page title. %1$s is user nickname, %2$d is page number
return sprintf(_('%1$s and friends, page %2$d'), $this->user->nickname, $this->page); return sprintf(_('%1$s and friends, page %2$d'), $this->user->nickname, $this->page);
} else { } else {
// TRANS: Page title. %1$s is user nickname // TRANS: Page title. %s is user nickname
return sprintf(_("%s and friends"), $this->user->nickname); return sprintf(_("%s and friends"), $this->user->nickname);
} }
} }
@ -103,7 +103,7 @@ class AllAction extends ProfileAction
'nickname' => 'nickname' =>
$this->user->nickname) $this->user->nickname)
), ),
// TRANS: %1$s is user nickname // TRANS: %s is user nickname
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)), sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
new Feed(Feed::RSS2, new Feed(Feed::RSS2,
common_local_url( common_local_url(
@ -112,7 +112,7 @@ class AllAction extends ProfileAction
'id' => $this->user->nickname 'id' => $this->user->nickname
) )
), ),
// TRANS: %1$s is user nickname // TRANS: %s is user nickname
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)), sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
new Feed(Feed::ATOM, new Feed(Feed::ATOM,
common_local_url( common_local_url(
@ -121,7 +121,7 @@ class AllAction extends ProfileAction
'id' => $this->user->nickname 'id' => $this->user->nickname
) )
), ),
// TRANS: %1$s is user nickname // TRANS: %s is user nickname
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname)) sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))
); );
} }
@ -134,18 +134,23 @@ class AllAction extends ProfileAction
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: %1$s is user nickname // TRANS: %s is user nickname
$message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->user->nickname) . ' '; $message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->user->nickname) . ' ';
if (common_logged_in()) { if (common_logged_in()) {
$current_user = common_current_user(); $current_user = common_current_user();
if ($this->user->id === $current_user->id) { if ($this->user->id === $current_user->id) {
// TRANS: Encouragement displayed on logged in user's empty timeline.
// TRANS: This message contains Markdown links. Keep "](" together.
$message .= _('Try subscribing to more people, [join a group](%%action.groups%%) or post something yourself.'); $message .= _('Try subscribing to more people, [join a group](%%action.groups%%) or post something yourself.');
} else { } else {
// TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@" // TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@"
// TRANS: This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname); $message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
} }
} else { } else {
// TRANS: Encoutagement displayed on empty timeline user pages for anonymous users.
// TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname); $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname);
} }
@ -181,7 +186,7 @@ class AllAction extends ProfileAction
// TRANS: H1 text // TRANS: H1 text
$this->element('h1', null, _("You and friends")); $this->element('h1', null, _("You and friends"));
} else { } else {
// TRANS: H1 text. %1$s is user nickname // TRANS: H1 text. %s is a user nickname
$this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname)); $this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname));
} }
} }

View File

@ -45,7 +45,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
{ {
/** /**
@ -56,7 +55,6 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
@ -76,7 +74,6 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -92,6 +89,7 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
if (!in_array($this->format, array('xml', 'json'))) { if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed handling a non-existing API method.
_('API method not found.'), _('API method not found.'),
404, 404,
$this->format $this->format
@ -102,16 +100,14 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
// Note: Twitter no longer supports IM // Note: Twitter no longer supports IM
if (!in_array(strtolower($this->device), array('sms', 'im', 'none'))) { if (!in_array(strtolower($this->device), array('sms', 'im', 'none'))) {
$this->clientError( // TRANS: Client error displayed when no valid device parameter is provided for a user's delivery device setting.
_( $this->clientError(_( 'You must specify a parameter named ' .
'You must specify a parameter named ' . '\'device\' with a value of one of: sms, im, none.' ));
'\'device\' with a value of one of: sms, im, none.'
)
);
return; return;
} }
if (empty($this->user)) { if (empty($this->user)) {
// TRANS: Client error displayed when no existing user is provided for a user's delivery device setting.
$this->clientError(_('No such user.'), 404, $this->format); $this->clientError(_('No such user.'), 404, $this->format);
return; return;
} }
@ -131,6 +127,7 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
if ($result === false) { if ($result === false) {
common_log_db_error($this->user, 'UPDATE', __FILE__); common_log_db_error($this->user, 'UPDATE', __FILE__);
// TRANS: Server error displayed when a user's delivery device cannot be updated.
$this->serverError(_('Could not update user.')); $this->serverError(_('Could not update user.'));
return; return;
} }
@ -155,5 +152,4 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
$this->endDocument('json'); $this->endDocument('json');
} }
} }
} }

View File

@ -43,10 +43,8 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiAccountUpdateProfileAction extends ApiAuthAction class ApiAccountUpdateProfileAction extends ApiAuthAction
{ {
/** /**
* Take arguments for running * Take arguments for running
* *
@ -55,7 +53,6 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
@ -79,7 +76,6 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -103,6 +99,7 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
} }
if (empty($this->user)) { if (empty($this->user)) {
// TRANS: Client error displayed if a user could not be found.
$this->clientError(_('No such user.'), 404, $this->format); $this->clientError(_('No such user.'), 404, $this->format);
return; return;
} }
@ -110,6 +107,7 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$profile = $this->user->getProfile(); $profile = $this->user->getProfile();
if (empty($profile)) { if (empty($profile)) {
// TRANS: Client error displayed if a user profile could not be found.
$this->clientError(_('User has no profile.')); $this->clientError(_('User has no profile.'));
return; return;
} }
@ -145,6 +143,7 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
if (!$result) { if (!$result) {
common_log_db_error($profile, 'UPDATE', __FILE__); common_log_db_error($profile, 'UPDATE', __FILE__);
// TRANS: Server error displayed if a user profile could not be saved.
$this->serverError(_('Could not save profile.')); $this->serverError(_('Could not save profile.'));
return; return;
} }
@ -163,5 +162,4 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$this->endDocument('json'); $this->endDocument('json');
} }
} }
} }

View File

@ -42,10 +42,8 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
{ {
var $tile = false; var $tile = false;
/** /**
@ -56,7 +54,6 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
@ -76,7 +73,6 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -92,6 +88,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
if (!in_array($this->format, array('xml', 'json'))) { if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when trying to handle an unknown API method.
_('API method not found.'), _('API method not found.'),
404, 404,
$this->format $this->format
@ -106,8 +103,11 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
&& empty($_POST) && empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0) && ($_SERVER['CONTENT_LENGTH'] > 0)
) { ) {
$msg = _('The server was unable to handle that much POST ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'data (%s bytes) due to its current configuration.'); // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); $this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return; return;
@ -125,7 +125,6 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
// is part of the img filename. // is part of the img filename.
if (empty($design)) { if (empty($design)) {
$this->user->query('BEGIN'); $this->user->query('BEGIN');
// save new design // save new design
@ -134,6 +133,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
if (empty($id)) { if (empty($id)) {
common_log_db_error($id, 'INSERT', __FILE__); common_log_db_error($id, 'INSERT', __FILE__);
// TRANS: Client error displayed when saving design settings fails because of an empty id.
$this->clientError(_('Unable to save your design settings.')); $this->clientError(_('Unable to save your design settings.'));
return; return;
} }
@ -144,6 +144,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
if (empty($result)) { if (empty($result)) {
common_log_db_error($original, 'UPDATE', __FILE__); common_log_db_error($original, 'UPDATE', __FILE__);
// TRANS: Client error displayed when saving design settings fails because of an empty result.
$this->clientError(_('Unable to save your design settings.')); $this->clientError(_('Unable to save your design settings.'));
$this->user->query('ROLLBACK'); $this->user->query('ROLLBACK');
return; return;
@ -185,6 +186,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
if ($result === false) { if ($result === false) {
common_log_db_error($design, 'UPDATE', __FILE__); common_log_db_error($design, 'UPDATE', __FILE__);
// TRANS: Error displayed when updating design settings fails.
$this->showForm(_('Could not update your design.')); $this->showForm(_('Could not update your design.'));
return; return;
} }
@ -192,6 +194,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
$profile = $this->user->getProfile(); $profile = $this->user->getProfile();
if (empty($profile)) { if (empty($profile)) {
// TRANS: Client error displayed when a user has no profile.
$this->clientError(_('User has no profile.')); $this->clientError(_('User has no profile.'));
return; return;
} }
@ -208,5 +211,4 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
$this->endDocument('json'); $this->endDocument('json');
} }
} }
} }

View File

@ -95,9 +95,11 @@ class ApiAccountUpdateProfileImageAction extends ApiAuthAction
&& empty($_POST) && empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0) && ($_SERVER['CONTENT_LENGTH'] > 0)
) { ) {
$msg = _('The server was unable to handle that much POST ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'data (%s bytes) due to its current configuration.'); // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); $this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return; return;
} }

View File

@ -46,7 +46,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiBlockCreateAction extends ApiAuthAction class ApiBlockCreateAction extends ApiAuthAction
{ {
var $other = null; var $other = null;
@ -59,7 +58,6 @@ class ApiBlockCreateAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
@ -79,7 +77,6 @@ class ApiBlockCreateAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -103,6 +100,7 @@ class ApiBlockCreateAction extends ApiAuthAction
if ($this->user->id == $this->other->id) { if ($this->user->id == $this->other->id) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when users try to block themselves.
_("You cannot block yourself!"), _("You cannot block yourself!"),
403, 403,
$this->format $this->format
@ -124,10 +122,8 @@ class ApiBlockCreateAction extends ApiAuthAction
$this->showProfile($this->other, $this->format); $this->showProfile($this->other, $this->format);
$this->endDocument($this->format); $this->endDocument($this->format);
} else { } else {
// TRANS: Server error displayed when blocking a user has failed.
$this->serverError(_('Block user failed.'), 500, $this->format); $this->serverError(_('Block user failed.'), 500, $this->format);
} }
} }
} }

View File

@ -45,7 +45,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiBlockDestroyAction extends ApiAuthAction class ApiBlockDestroyAction extends ApiAuthAction
{ {
var $other = null; var $other = null;
@ -58,7 +57,6 @@ class ApiBlockDestroyAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
@ -78,7 +76,6 @@ class ApiBlockDestroyAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -112,10 +109,8 @@ class ApiBlockDestroyAction extends ApiAuthAction
$this->showProfile($this->other, $this->format); $this->showProfile($this->other, $this->format);
$this->endDocument($this->format); $this->endDocument($this->format);
} else { } else {
// TRANS: Server error displayed when unblocking a user has failed.
$this->serverError(_('Unblock user failed.')); $this->serverError(_('Unblock user failed.'));
} }
} }
} }

View File

@ -74,6 +74,7 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->user = $this->auth_user; $this->user = $this->auth_user;
if (empty($this->user)) { if (empty($this->user)) {
// TRANS: Client error given when a user was not found (404).
$this->clientError(_('No such user.'), 404, $this->format); $this->clientError(_('No such user.'), 404, $this->format);
return; return;
} }
@ -86,10 +87,12 @@ class ApiDirectMessageAction extends ApiAuthAction
// Action was called by /api/direct_messages/sent.format // Action was called by /api/direct_messages/sent.format
$this->title = sprintf( $this->title = sprintf(
// TRANS: %s is a user nickname.
_("Direct messages from %s"), _("Direct messages from %s"),
$this->user->nickname $this->user->nickname
); );
$this->subtitle = sprintf( $this->subtitle = sprintf(
// TRANS: %s is a user nickname.
_("All the direct messages sent from %s"), _("All the direct messages sent from %s"),
$this->user->nickname $this->user->nickname
); );
@ -98,10 +101,12 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->id = "tag:$taguribase:SentDirectMessages:" . $this->user->id; $this->id = "tag:$taguribase:SentDirectMessages:" . $this->user->id;
} else { } else {
$this->title = sprintf( $this->title = sprintf(
// TRANS: %s is a user nickname.
_("Direct messages to %s"), _("Direct messages to %s"),
$this->user->nickname $this->user->nickname
); );
$this->subtitle = sprintf( $this->subtitle = sprintf(
// TRANS: %s is a user nickname.
_("All the direct messages sent to %s"), _("All the direct messages sent to %s"),
$this->user->nickname $this->user->nickname
); );
@ -153,6 +158,7 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->showJsonDirectMessages(); $this->showJsonDirectMessages();
break; break;
default: default:
// TRANS: Client error given when an API method was not found (404).
$this->clientError(_('API method not found.'), $code = 404); $this->clientError(_('API method not found.'), $code = 404);
break; break;
} }

View File

@ -49,7 +49,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiDirectMessageNewAction extends ApiAuthAction class ApiDirectMessageNewAction extends ApiAuthAction
{ {
var $other = null; var $other = null;
@ -63,7 +62,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
@ -99,7 +97,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -116,6 +113,7 @@ class ApiDirectMessageNewAction extends ApiAuthAction
if (empty($this->content)) { if (empty($this->content)) {
$this->clientError( $this->clientError(
// TRANS: Client error (406).
_('No message text!'), _('No message text!'),
406, 406,
$this->format $this->format
@ -124,8 +122,9 @@ class ApiDirectMessageNewAction extends ApiAuthAction
$content_shortened = common_shorten_links($this->content); $content_shortened = common_shorten_links($this->content);
if (Message::contentTooLong($content_shortened)) { if (Message::contentTooLong($content_shortened)) {
$this->clientError( $this->clientError(
sprintf( // TRANS: Client error displayed when message content is too long.
_('That\'s too long. Max message size is %d chars.'), // TRANS: %d is the maximum number of characters for a message.
sprintf(_m('That\'s too long. Maximum message size is %d character.', 'That\'s too long. Maximum message size is %d characters.', Message::maxContent()),
Message::maxContent() Message::maxContent()
), ),
406, 406,
@ -136,10 +135,12 @@ class ApiDirectMessageNewAction extends ApiAuthAction
} }
if (empty($this->other)) { if (empty($this->other)) {
// TRANS: Client error displayed if a recipient user could not be found (403).
$this->clientError(_('Recipient user not found.'), 403, $this->format); $this->clientError(_('Recipient user not found.'), 403, $this->format);
return; return;
} else if (!$this->user->mutuallySubscribed($this->other)) { } else if (!$this->user->mutuallySubscribed($this->other)) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed trying to direct message another user who's not a friend (403).
_('Can\'t send direct messages to users who aren\'t your friend.'), _('Can\'t send direct messages to users who aren\'t your friend.'),
403, 403,
$this->format $this->format
@ -149,10 +150,9 @@ class ApiDirectMessageNewAction extends ApiAuthAction
// Note: sending msgs to yourself is allowed by Twitter // Note: sending msgs to yourself is allowed by Twitter
$errmsg = 'Don\'t send a message to yourself; ' . // TRANS: Client error displayed trying to direct message self (403).
'just say it to yourself quietly instead.'; $this->clientError(_('Do not send a message to yourself; ' .
'just say it to yourself quietly instead.'), 403, $this->format);
$this->clientError(_($errmsg), 403, $this->format);
return; return;
} }
@ -176,6 +176,4 @@ class ApiDirectMessageNewAction extends ApiAuthAction
$this->showSingleJsondirectMessage($message); $this->showSingleJsondirectMessage($message);
} }
} }
} }

View File

@ -48,7 +48,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiFavoriteCreateAction extends ApiAuthAction class ApiFavoriteCreateAction extends ApiAuthAction
{ {
var $notice = null; var $notice = null;
@ -61,7 +60,6 @@ class ApiFavoriteCreateAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
@ -81,7 +79,6 @@ class ApiFavoriteCreateAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -107,6 +104,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction
if (empty($this->notice)) { if (empty($this->notice)) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when requesting a status with a non-existing ID.
_('No status found with that ID.'), _('No status found with that ID.'),
404, 404,
$this->format $this->format
@ -118,6 +116,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction
if ($this->user->hasFave($this->notice)) { if ($this->user->hasFave($this->notice)) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when trying to mark a notice favourite that already is a favourite.
_('This status is already a favorite.'), _('This status is already a favorite.'),
403, 403,
$this->format $this->format
@ -129,6 +128,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction
if (empty($fave)) { if (empty($fave)) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when marking a notice as favourite fails.
_('Could not create favorite.'), _('Could not create favorite.'),
403, 403,
$this->format $this->format
@ -166,5 +166,4 @@ class ApiFavoriteCreateAction extends ApiAuthAction
// XXX: notify by SMS // XXX: notify by SMS
} }
} }
} }

View File

@ -49,7 +49,6 @@ require_once INSTALLDIR . '/lib/apiauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiGroupCreateAction extends ApiAuthAction class ApiGroupCreateAction extends ApiAuthAction
{ {
var $group = null; var $group = null;
@ -95,7 +94,6 @@ class ApiGroupCreateAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -111,6 +109,7 @@ class ApiGroupCreateAction extends ApiAuthAction
} }
if (empty($this->user)) { if (empty($this->user)) {
// TRANS: Client error given when a user was not found (404).
$this->clientError(_('No such user.'), 404, $this->format); $this->clientError(_('No such user.'), 404, $this->format);
return; return;
} }
@ -137,13 +136,13 @@ class ApiGroupCreateAction extends ApiAuthAction
break; break;
default: default:
$this->clientError( $this->clientError(
// TRANS: Client error given when an API method was not found (404).
_('API method not found.'), _('API method not found.'),
404, 404,
$this->format $this->format
); );
break; break;
} }
} }
/** /**
@ -164,6 +163,7 @@ class ApiGroupCreateAction extends ApiAuthAction
if (!$valid) { if (!$valid) {
$this->clientError( $this->clientError(
// TRANS: Validation error in form for group creation.
_( _(
'Nickname must have only lowercase letters ' . 'Nickname must have only lowercase letters ' .
'and numbers and no spaces.' 'and numbers and no spaces.'
@ -174,6 +174,7 @@ class ApiGroupCreateAction extends ApiAuthAction
return false; return false;
} elseif ($this->groupNicknameExists($this->nickname)) { } elseif ($this->groupNicknameExists($this->nickname)) {
$this->clientError( $this->clientError(
// TRANS: Client error trying to create a group with a nickname this is already in use.
_('Nickname already in use. Try another one.'), _('Nickname already in use. Try another one.'),
403, 403,
$this->format $this->format
@ -181,6 +182,7 @@ class ApiGroupCreateAction extends ApiAuthAction
return false; return false;
} else if (!User_group::allowedNickname($this->nickname)) { } else if (!User_group::allowedNickname($this->nickname)) {
$this->clientError( $this->clientError(
// TRANS: Client error in form for group creation.
_('Not a valid nickname.'), _('Not a valid nickname.'),
403, 403,
$this->format $this->format
@ -197,6 +199,7 @@ class ApiGroupCreateAction extends ApiAuthAction
) )
)) { )) {
$this->clientError( $this->clientError(
// TRANS: Client error in form for group creation.
_('Homepage is not a valid URL.'), _('Homepage is not a valid URL.'),
403, 403,
$this->format $this->format
@ -206,7 +209,8 @@ class ApiGroupCreateAction extends ApiAuthAction
!is_null($this->fullname) !is_null($this->fullname)
&& mb_strlen($this->fullname) > 255) { && mb_strlen($this->fullname) > 255) {
$this->clientError( $this->clientError(
_('Full name is too long (max 255 chars).'), // TRANS: Client error in form for group creation.
_('Full name is too long (maximum 255 characters).'),
403, 403,
$this->format $this->format
); );
@ -225,7 +229,7 @@ class ApiGroupCreateAction extends ApiAuthAction
!is_null($this->location) !is_null($this->location)
&& mb_strlen($this->location) > 255) { && mb_strlen($this->location) > 255) {
$this->clientError( $this->clientError(
_('Location is too long (max 255 chars).'), _('Location is too long (maximum 255 characters).'),
403, 403,
$this->format $this->format
); );

View File

@ -78,9 +78,11 @@ class ApiMediaUploadAction extends ApiAuthAction
&& empty($_POST) && empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0) && ($_SERVER['CONTENT_LENGTH'] > 0)
) { ) {
$msg = _('The server was unable to handle that much POST ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'data (%s bytes) due to its current configuration.'); // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); $this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return; return;
} }

View File

@ -44,7 +44,6 @@ require_once INSTALLDIR . '/lib/apioauth.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiOauthAccessTokenAction extends ApiOauthAction class ApiOauthAccessTokenAction extends ApiOauthAction
{ {
protected $reqToken = null; protected $reqToken = null;
@ -67,21 +66,21 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
$server->add_signature_method($hmac_method); $server->add_signature_method($hmac_method);
$atok = null; $atok = $app = null;
// XXX: Insist that oauth_token and oauth_verifier be populated? // XXX: Insist that oauth_token and oauth_verifier be populated?
// Spec doesn't say they MUST be. // Spec doesn't say they MUST be.
try { try {
$req = OAuthRequest::from_request(); $req = OAuthRequest::from_request();
$this->reqToken = $req->get_parameter('oauth_token'); $this->reqToken = $req->get_parameter('oauth_token');
$this->verifier = $req->get_parameter('oauth_verifier'); $this->verifier = $req->get_parameter('oauth_verifier');
$app = $datastore->getAppByRequestToken($this->reqToken);
$atok = $server->fetch_access_token($req); $atok = $server->fetch_access_token($req);
} catch (OAuthException $e) { } catch (Exception $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
common_debug(var_export($req, true)); common_debug(var_export($req, true));
$code = $e->getCode(); $code = $e->getCode();
@ -92,22 +91,27 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
// Token exchange failed -- log it // Token exchange failed -- log it
list($proxy, $ip) = common_client_ip();
$msg = sprintf( $msg = sprintf(
'API OAuth - Failure exchanging request token for access token, ' 'API OAuth - Failure exchanging OAuth request token for access token, '
. 'request token = %s, verifier = %s, IP = %s, proxy = %s', . 'request token = %s, verifier = %s',
$this->reqToken, $this->reqToken,
$this->verifier, $this->verifier
$ip,
$proxy
); );
common_log(LOG_WARNING, $msg); common_log(LOG_WARNING, $msg);
// TRANS: Client error given from the OAuth API when the request token or verifier is invalid.
$this->clientError(_("Invalid request token or verifier.", 400, 'text')); $this->clientError(_("Invalid request token or verifier.", 400, 'text'));
} else { } else {
common_log(
LOG_INFO,
sprintf(
"Issued access token '%s' for application %d (%s).",
$atok->key,
$app->id,
$app->name
)
);
$this->showAccessToken($atok); $this->showAccessToken($atok);
} }
} }

View File

@ -35,7 +35,7 @@ require_once INSTALLDIR . '/lib/apioauth.php';
require_once INSTALLDIR . '/lib/info.php'; require_once INSTALLDIR . '/lib/info.php';
/** /**
* Authorize an OAuth request token * Authorize an Oputh request token
* *
* @category API * @category API
* @package StatusNet * @package StatusNet
@ -43,7 +43,6 @@ require_once INSTALLDIR . '/lib/info.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiOauthAuthorizeAction extends Action class ApiOauthAuthorizeAction extends Action
{ {
var $oauthTokenParam; var $oauthTokenParam;
@ -72,7 +71,7 @@ class ApiOauthAuthorizeAction extends Action
$this->nickname = $this->trimmed('nickname'); $this->nickname = $this->trimmed('nickname');
$this->password = $this->arg('password'); $this->password = $this->arg('password');
$this->oauthTokenParam = $this->arg('oauth_token'); $this->oauthTokenParam = $this->arg('oauth_token');
$this->callback = $this->arg('oauth_callback'); $this->mode = $this->arg('mode');
$this->store = new ApiStatusNetOAuthDataStore(); $this->store = new ApiStatusNetOAuthDataStore();
try { try {
@ -93,7 +92,6 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -106,6 +104,7 @@ class ApiOauthAuthorizeAction extends Action
// Make sure a oauth_token parameter was provided // Make sure a oauth_token parameter was provided
if (empty($this->oauthTokenParam)) { if (empty($this->oauthTokenParam)) {
// TRANS: Client error given when no oauth_token was passed to the OAuth API.
$this->clientError(_('No oauth_token parameter provided.')); $this->clientError(_('No oauth_token parameter provided.'));
} else { } else {
@ -113,20 +112,21 @@ class ApiOauthAuthorizeAction extends Action
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam); $this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
if (empty($this->reqToken)) { if (empty($this->reqToken)) {
$this->serverError( // TRANS: Client error given when an invalid request token was passed to the OAuth API.
_('Invalid request token.') $this->clientError(_('Invalid request token.'));
);
} else { } else {
// Check to make sure we haven't already authorized the token // Check to make sure we haven't already authorized the token
if ($this->reqToken->state != 0) { if ($this->reqToken->state != 0) {
$this->clientError("Invalid request token."); // TRANS: Client error given when an invalid request token was passed to the OAuth API.
$this->clientError(_('Request token already authorized.'));
} }
} }
} }
// make sure there's an app associated with this token // make sure there's an app associated with this token
if (empty($this->app)) { if (empty($this->app)) {
// TRANS: Client error given when an invalid request token was passed to the OAuth API.
$this->clientError(_('Invalid request token.')); $this->clientError(_('Invalid request token.'));
} }
@ -156,10 +156,15 @@ class ApiOauthAuthorizeAction extends Action
// XXX Force credentials check? // XXX Force credentials check?
// XXX OpenID // @fixme this should probably use a unified login form handler
$user = null;
if (Event::handle('StartOAuthLoginCheck', array($this, &$user))) {
$user = common_check_user($this->nickname, $this->password); $user = common_check_user($this->nickname, $this->password);
}
Event::handle('EndOAuthLoginCheck', array($this, &$user));
if (empty($user)) { if (empty($user)) {
// TRANS: Form validation error given when an invalid username and/or password was passed to the OAuth API.
$this->showForm(_("Invalid nickname / password!")); $this->showForm(_("Invalid nickname / password!"));
return; return;
} }
@ -167,10 +172,11 @@ class ApiOauthAuthorizeAction extends Action
$user = common_current_user(); $user = common_current_user();
} }
if ($this->arg('allow')) {
// fetch the token // fetch the token
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam); $this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
assert(!empty($this->reqToken));
if ($this->arg('allow')) {
// mark the req token as authorized // mark the req token as authorized
try { try {
@ -179,73 +185,66 @@ class ApiOauthAuthorizeAction extends Action
$this->serverError($e->getMessage()); $this->serverError($e->getMessage());
} }
// Check to see if there was a previous token associated common_log(
// with this user/app and kill it. If the user is doing this she LOG_INFO,
// probably doesn't want any old tokens anyway. sprintf(
"API OAuth - User %d (%s) has authorized request token %s for OAuth application %d (%s).",
$user->id,
$user->nickname,
$this->reqToken->tok,
$this->app->id,
$this->app->name
)
);
$appUser = Oauth_application_user::getByKeys($user, $this->app); // XXX: Make sure we have a oauth_token_association table. The table
// is now in the main schema, but because it is being added with
// a point release, it's unlikely to be there. This code can be
// removed as of 1.0.
$this->ensureOauthTokenAssociationTable();
if (!empty($appUser)) { $tokenAssoc = new Oauth_token_association();
$result = $appUser->delete();
$tokenAssoc->profile_id = $user->id;
$tokenAssoc->application_id = $this->app->id;
$tokenAssoc->token = $this->oauthTokenParam;
$tokenAssoc->created = common_sql_now();
$result = $tokenAssoc->insert();
if (!$result) { if (!$result) {
common_log_db_error($appUser, 'DELETE', __FILE__); common_log_db_error($tokenAssoc, 'INSERT', __FILE__);
$this->serverError(_('Database error deleting OAuth application user.')); // TRANS: Server error displayed when a database action fails.
} $this->serverError(_('Database error inserting oauth_token_association.'));
} }
// associated the authorized req token with the user and the app $callback = $this->getCallback();
$appUser = new Oauth_application_user(); if (!empty($callback) && $this->reqToken->verified_callback != 'oob') {
$targetUrl = $this->buildCallbackUrl(
$appUser->profile_id = $user->id; $callback,
$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->oauthTokenParam;
$appUser->created = common_sql_now();
$result = $appUser->insert();
if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__);
$this->serverError(_('Database error inserting OAuth application user.'));
}
// If we have a callback redirect and provide the token
// Note: A callback specified in the app setup overrides whatever
// is passed in with the request.
if (!empty($this->app->callback_url)) {
$this->callback = $this->app->callback_url;
}
if (!empty($this->callback)) {
$targetUrl = $this->getCallback(
$this->callback,
array( array(
'oauth_token' => $this->oauthTokenParam, 'oauth_token' => $this->oauthTokenParam,
'oauth_verifier' => $this->reqToken->verifier // 1.0a 'oauth_verifier' => $this->reqToken->verifier // 1.0a
) )
); );
common_log(LOG_INFO, "Redirecting to callback: $targetUrl");
// Redirect the user to the provided OAuth callback // Redirect the user to the provided OAuth callback
common_redirect($targetUrl, 303); common_redirect($targetUrl, 303);
} else { } elseif ($this->app->type == 2) {
// Strangely, a web application seems to want to do the OOB
// workflow. Because no callback was specified anywhere.
common_log( common_log(
LOG_INFO, LOG_WARNING,
"No oauth_callback parameter provided for application ID " sprintf(
. $this->app->id "API OAuth - No callback provided for OAuth web client ID %s (%s) "
. " when authorizing request token." . "during authorization step. Falling back to OOB workflow.",
$this->app->id,
$this->app->name
)
); );
} }
@ -254,18 +253,118 @@ class ApiOauthAuthorizeAction extends Action
} else if ($this->arg('cancel')) { } else if ($this->arg('cancel')) {
common_log(
LOG_INFO,
sprintf(
"API OAuth - User %d (%s) refused to authorize request token %s for OAuth application %d (%s).",
$user->id,
$user->nickname,
$this->reqToken->tok,
$this->app->id,
$this->app->name
)
);
try { try {
$this->store->revoke_token($this->oauthTokenParam, 0); $this->store->revoke_token($this->oauthTokenParam, 0);
$this->showCanceled();
} catch (Exception $e) { } catch (Exception $e) {
$this->ServerError($e->getMessage()); $this->ServerError($e->getMessage());
} }
$callback = $this->getCallback();
// If there's a callback available, inform the consumer the user
// has refused authorization
if (!empty($callback) && $this->reqToken->verified_callback != 'oob') {
$targetUrl = $this->buildCallbackUrl(
$callback,
array(
'oauth_problem' => 'user_refused',
)
);
common_log(LOG_INFO, "Redirecting to callback: $targetUrl");
// Redirect the user to the provided OAuth callback
common_redirect($targetUrl, 303);
}
// otherwise inform the user that authorization for the rt was declined
$this->showCanceled();
} else { } else {
// TRANS: Client error given on when invalid data was passed through a form in the OAuth API.
$this->clientError(_('Unexpected form submission.')); $this->clientError(_('Unexpected form submission.'));
} }
} }
// XXX Remove this function when we hit 1.0
function ensureOauthTokenAssociationTable()
{
$schema = Schema::get();
$reqTokenCols = array(
new ColumnDef('profile_id', 'integer', null, true, 'PRI'),
new ColumnDef('application_id', 'integer', null, true, 'PRI'),
new ColumnDef('token', 'varchar', 255, true, 'PRI'),
new ColumnDef('created', 'datetime', null, false),
new ColumnDef(
'modified',
'timestamp',
null,
false,
null,
'CURRENT_TIMESTAMP',
'on update CURRENT_TIMESTAMP'
)
);
$schema->ensureTable('oauth_token_association', $reqTokenCols);
}
/**
* Show body - override to add a special CSS class for the authorize
* page's "desktop mode" (minimal display)
*
* Calls template methods
*
* @return nothing
*/
function showBody()
{
$bodyClasses = array();
if ($this->desktopMode()) {
$bodyClasses[] = 'oauth-desktop-mode';
}
if (common_current_user()) {
$bodyClasses[] = 'user_in';
}
$attrs = array('id' => strtolower($this->trimmed('action')));
if (!empty($bodyClasses)) {
$attrs['class'] = implode(' ', $bodyClasses);
}
$this->elementStart('body', $attrs);
$this->elementStart('div', array('id' => 'wrap'));
if (Event::handle('StartShowHeader', array($this))) {
$this->showHeader();
Event::handle('EndShowHeader', array($this));
}
$this->showCore();
if (Event::handle('StartShowFooter', array($this))) {
$this->showFooter();
Event::handle('EndShowFooter', array($this));
}
$this->elementEnd('div');
$this->showScripts();
$this->elementEnd('body');
}
function showForm($error=null) function showForm($error=null)
{ {
$this->error = $error; $this->error = $error;
@ -285,9 +384,9 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return string title of the page * @return string title of the page
*/ */
function title() function title()
{ {
// TRANS: Title for a page where a user can confirm/deny account access by an external application.
return _('An application would like to connect to your account'); return _('An application would like to connect to your account');
} }
@ -296,7 +395,6 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return void * @return void
*/ */
function showContent() function showContent()
{ {
$this->elementStart('form', array('method' => 'post', $this->elementStart('form', array('method' => 'post',
@ -305,27 +403,40 @@ class ApiOauthAuthorizeAction extends Action
'action' => common_local_url('ApiOauthAuthorize'))); 'action' => common_local_url('ApiOauthAuthorize')));
$this->elementStart('fieldset'); $this->elementStart('fieldset');
$this->element('legend', array('id' => 'apioauthauthorize_allowdeny'), $this->element('legend', array('id' => 'apioauthauthorize_allowdeny'),
// TRANS: Fieldset legend.
_('Allow or deny access')); _('Allow or deny access'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->hidden('mode', $this->mode);
$this->hidden('oauth_token', $this->oauthTokenParam); $this->hidden('oauth_token', $this->oauthTokenParam);
$this->hidden('oauth_callback', $this->callback); $this->hidden('oauth_callback', $this->callback);
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
$this->elementStart('p'); $this->elementStart('p');
if (!empty($this->app->icon)) { if (!empty($this->app->icon) && $this->app->name != 'anonymous') {
$this->element('img', array('src' => $this->app->icon)); $this->element('img', array('src' => $this->app->icon));
} }
$access = ($this->app->access_type & Oauth_application::$writeAccess) ? $access = ($this->app->access_type & Oauth_application::$writeAccess) ?
'access and update' : 'access'; 'access and update' : 'access';
// TRANS: User notification of external application requesting account access.
// TRANS: %1$s is the application name requesting access, %2$s is the organisation behind the application,
// TRANS: %3$s is the access type requested, %4$s is the StatusNet sitename.
if ($this->app->name == 'anonymous') {
// Special message for the anonymous app and consumer
$msg = _('An application would like the ability ' .
'to <strong>%3$s</strong> your %4$s account data. ' .
'You should only give access to your %4$s account ' .
'to third parties you trust.');
} else {
$msg = _('The application <strong>%1$s</strong> by ' . $msg = _('The application <strong>%1$s</strong> by ' .
'<strong>%2$s</strong> would like the ability ' . '<strong>%2$s</strong> would like the ability ' .
'to <strong>%3$s</strong> your %4$s account data. ' . 'to <strong>%3$s</strong> your %4$s account data. ' .
'You should only give access to your %4$s account ' . 'You should only give access to your %4$s account ' .
'to third parties you trust.'); 'to third parties you trust.');
}
$this->raw(sprintf($msg, $this->raw(sprintf($msg,
$this->app->name, $this->app->name,
@ -336,34 +447,43 @@ class ApiOauthAuthorizeAction extends Action
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementEnd('ul'); $this->elementEnd('ul');
// quickie hack
$button = false;
if (!common_logged_in()) { if (!common_logged_in()) {
if (Event::handle('StartOAuthLoginForm', array($this, &$button))) {
$this->elementStart('fieldset'); $this->elementStart('fieldset');
$this->element('legend', null, _('Account')); // TRANS: Fieldset legend.
$this->element('legend', null, _m('LEGEND','Account'));
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label on OAuth API authorisation form.
$this->input('nickname', _('Nickname')); $this->input('nickname', _('Nickname'));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label on OAuth API authorisation form.
$this->password('password', _('Password')); $this->password('password', _('Password'));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementEnd('ul'); $this->elementEnd('ul');
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
}
Event::handle('EndOAuthLoginForm', array($this, &$button));
} }
$this->element('input', array('id' => 'cancel_submit', $this->element('input', array('id' => 'cancel_submit',
'class' => 'submit submit form_action-primary', 'class' => 'submit submit form_action-primary',
'name' => 'cancel', 'name' => 'cancel',
'type' => 'submit', 'type' => 'submit',
'value' => _('Cancel'))); // TRANS: Button text that when clicked will cancel the process of allowing access to an account
// TRANS: by an external application.
'value' => _m('BUTTON','Cancel')));
$this->element('input', array('id' => 'allow_submit', $this->element('input', array('id' => 'allow_submit',
'class' => 'submit submit form_action-secondary', 'class' => 'submit submit form_action-secondary',
'name' => 'allow', 'name' => 'allow',
'type' => 'submit', 'type' => 'submit',
'value' => _('Allow'))); // TRANS: Button text that when clicked will allow access to an account by an external application.
'value' => $button ? $button : _m('BUTTON','Allow')));
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
$this->elementEnd('form'); $this->elementEnd('form');
@ -377,9 +497,9 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return void * @return void
*/ */
function getInstructions() function getInstructions()
{ {
// TRANS: Form instructions.
return _('Authorize access to your account information.'); return _('Authorize access to your account information.');
} }
@ -390,18 +510,61 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return void * @return void
*/ */
function showLocalNav() function showLocalNav()
{ {
// NOP // NOP
} }
/*
* Checks to see if a the "mode" parameter is present in the request
* and set to "desktop". If it is, the page is meant to be displayed in
* a small frame of another application, and we should suppress the
* header, aside, and footer.
*/
function desktopMode()
{
if (isset($this->mode) && $this->mode == 'desktop') {
return true;
} else {
return false;
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showHeader()
{
if ($this->desktopMode() == false) {
parent::showHeader();
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showAside()
{
if ($this->desktopMode() == false) {
parent::showAside();
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showFooter()
{
if ($this->desktopMode() == false) {
parent::showFooter();
}
}
/** /**
* Show site notice. * Show site notice.
* *
* @return nothing * @return nothing
*/ */
function showSiteNotice() function showSiteNotice()
{ {
// NOP // NOP
@ -414,7 +577,6 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return nothing * @return nothing
*/ */
function showNoticeForm() function showNoticeForm()
{ {
// NOP // NOP
@ -426,14 +588,16 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return nothing * @return nothing
*/ */
function showCanceled() function showCanceled()
{ {
$info = new InfoAction( $info = new InfoAction(
// TRANS: Header for user notification after revoking OAuth access to an application.
_('Authorization canceled.'), _('Authorization canceled.'),
sprintf( sprintf(
// TRANS: User notification after revoking OAuth access to an application.
// TRANS: %s is an OAuth token.
_('The request token %s has been revoked.'), _('The request token %s has been revoked.'),
$this->oauthTokenParm $this->oauthTokenParam
) )
); );
@ -446,21 +610,29 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return nothing * @return nothing
*/ */
function showAuthorized() function showAuthorized()
{ {
$title = sprintf( $title = sprintf(
_("You have successfully authorized %s."), // TRANS: Header of user notification after authorising an application access to a profile.
$this->app->name // TRANS: %s is the authorised application name.
_('You have successfully authorized %s.'),
($this->app->name == 'anonymous') ? 'the application' : $this->app->name
); );
$msg = sprintf( $msg = sprintf(
// TRANS: Uer notification after authorising an application access to a profile.
// TRANS: %s is the authorised application name.
_('Please return to %s and enter the following security code to complete the process.'), _('Please return to %s and enter the following security code to complete the process.'),
$this->app->name ($this->app->name == 'anonymous') ? 'the application' : $this->app->name
); );
if ($this->reqToken->verified_callback == 'oob') { if ($this->reqToken->verified_callback == 'oob') {
$pin = new ApiOauthPinAction($title, $msg, $this->reqToken->verifier); $pin = new ApiOauthPinAction(
$title,
$msg,
$this->reqToken->verifier,
$this->desktopMode()
);
$pin->showPage(); $pin->showPage();
} else { } else {
@ -480,6 +652,36 @@ class ApiOauthAuthorizeAction extends Action
} }
} }
/*
* Figure out what the callback should be
*/
function getCallback()
{
$callback = null;
// Return the verified callback if we have one
if ($this->reqToken->verified_callback != 'oob') {
$callback = $this->reqToken->verified_callback;
// Otherwise return the callback that was provided when
// registering the app
if (empty($callback)) {
common_debug(
"No verified callback found for request token, using application callback: "
. $this->app->callback_url,
__FILE__
);
$callback = $this->app->callback_url;
}
}
return $callback;
}
/* /*
* Properly format the callback URL and parameters so it's * Properly format the callback URL and parameters so it's
* suitable for a redirect in the OAuth dance * suitable for a redirect in the OAuth dance
@ -489,8 +691,7 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return string $url a URL to use for redirecting to * @return string $url a URL to use for redirecting to
*/ */
function buildCallbackUrl($url, $params)
function getCallback($url, $params)
{ {
foreach ($params as $k => $v) { foreach ($params as $k => $v) {
$url = $this->appendQueryVar( $url = $this->appendQueryVar(
@ -513,7 +714,6 @@ class ApiOauthAuthorizeAction extends Action
* *
* @return string $url the new URL with added parameter * @return string $url the new URL with added parameter
*/ */
function appendQueryVar($url, $k, $v) { function appendQueryVar($url, $k, $v) {
$url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&'); $url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&');
$url = substr($url, 0, -1); $url = substr($url, 0, -1);

View File

@ -31,8 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1); exit(1);
} }
require_once INSTALLDIR . '/lib/info.php';
/** /**
* Class for displaying an OAuth verifier pin * Class for displaying an OAuth verifier pin
* *
@ -47,13 +45,121 @@ require_once INSTALLDIR . '/lib/info.php';
class ApiOauthPinAction extends InfoAction class ApiOauthPinAction extends InfoAction
{ {
function __construct($title, $message, $verifier) function __construct($title, $message, $verifier, $desktopMode = false)
{ {
$this->verifier = $verifier; $this->verifier = $verifier;
$this->title = $title; $this->title = $title;
$this->desktopMode = $desktopMode;
parent::__construct($title, $message); parent::__construct($title, $message);
} }
/**
* Show body - override to add a special CSS class for the pin pages's
* "desktop mode" (minimal display)
*
* Calls template methods
*
* @return nothing
*/
function showBody()
{
$bodyClasses = array();
if ($this->desktopMode) {
$bodyClasses[] = 'oauth-desktop-mode';
}
if (common_current_user()) {
$bodyClasses[] = 'user_in';
}
$attrs = array('id' => strtolower($this->trimmed('action')));
if (!empty($bodyClasses)) {
$attrs['class'] = implode(' ', $bodyClasses);
}
$this->elementStart('body', $attrs);
$this->elementStart('div', array('id' => 'wrap'));
if (Event::handle('StartShowHeader', array($this))) {
$this->showHeader();
Event::handle('EndShowHeader', array($this));
}
$this->showCore();
if (Event::handle('StartShowFooter', array($this))) {
$this->showFooter();
Event::handle('EndShowFooter', array($this));
}
$this->elementEnd('div');
$this->showScripts();
$this->elementEnd('body');
}
/**
* A local menu
*
* Shows different login/register actions.
*
* @return void
*/
function showLocalNav()
{
// NOP
}
/*
* Override - suppress output in "desktop" mode
*/
function showHeader()
{
if ($this->desktopMode == false) {
parent::showHeader();
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showAside()
{
if ($this->desktopMode == false) {
parent::showAside();
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showFooter()
{
if ($this->desktopMode == false) {
parent::showFooter();
}
}
/**
* Show site notice.
*
* @return nothing
*/
function showSiteNotice()
{
// NOP
}
/**
* Show notice form.
*
* Show the form for posting a new notice
*
* @return nothing
*/
function showNoticeForm()
{
// NOP
}
/** /**
* Display content. * Display content.
* *

View File

@ -146,7 +146,7 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
function verifyCallback($callback) function verifyCallback($callback)
{ {
if ($callback == "oob") { if ($callback == "oob") {
common_debug("OAuth request token requested for out of bounds client."); common_debug("OAuth request token requested for out of band client.");
// XXX: Should we throw an error if a client is registered as a // XXX: Should we throw an error if a client is registered as a
// web application but requests the pin based workflow? For now I'm // web application but requests the pin based workflow? For now I'm
@ -154,10 +154,7 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
return true; return true;
} else { } else {
return Validate::uri( return Validate::uri($callback);
$callback,
array('allowed_schemes' => array('http', 'https'))
);
} }
} }

View File

@ -147,10 +147,8 @@ require_once INSTALLDIR . '/lib/mediafile.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiStatusesUpdateAction extends ApiAuthAction class ApiStatusesUpdateAction extends ApiAuthAction
{ {
var $source = null;
var $status = null; var $status = null;
var $in_reply_to_status_id = null; var $in_reply_to_status_id = null;
var $lat = null; var $lat = null;
@ -164,7 +162,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
* @return boolean success flag * @return boolean success flag
* *
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
@ -188,7 +185,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -210,8 +206,11 @@ class ApiStatusesUpdateAction extends ApiAuthAction
&& empty($_POST) && empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0) && ($_SERVER['CONTENT_LENGTH'] > 0)
) { ) {
$msg = _('The server was unable to handle that much POST ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'data (%s bytes) due to its current configuration.'); // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); $this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return; return;
@ -219,6 +218,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
if (empty($this->status)) { if (empty($this->status)) {
$this->clientError( $this->clientError(
// TRANS: Client error displayed when the parameter "status" is missing.
_('Client must provide a \'status\' parameter with a value.'), _('Client must provide a \'status\' parameter with a value.'),
400, 400,
$this->format $this->format
@ -240,7 +240,11 @@ class ApiStatusesUpdateAction extends ApiAuthAction
$this->clientError( $this->clientError(
sprintf( sprintf(
_('That\'s too long. Max notice size is %d chars.'), // TRANS: Client error displayed when the parameter "status" is missing.
// TRANS: %d is the maximum number of character for a notice.
_m('That\'s too long. Maximum notice size is %d character.',
'That\'s too long. Maximum notice size is %d characters.',
Notice::maxContent()),
Notice::maxContent() Notice::maxContent()
), ),
406, 406,
@ -256,7 +260,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
$cmd = $inter->handle_command($this->auth_user, $status_shortened); $cmd = $inter->handle_command($this->auth_user, $status_shortened);
if ($cmd) { if ($cmd) {
if ($this->supported($cmd)) { if ($this->supported($cmd)) {
$cmd->execute(new Channel()); $cmd->execute(new Channel());
} }
@ -266,13 +269,10 @@ class ApiStatusesUpdateAction extends ApiAuthAction
// or not! // or not!
$this->notice = $this->auth_user->getCurrentNotice(); $this->notice = $this->auth_user->getCurrentNotice();
} else { } else {
$reply_to = null; $reply_to = null;
if (!empty($this->in_reply_to_status_id)) { if (!empty($this->in_reply_to_status_id)) {
// Check whether notice actually exists // Check whether notice actually exists
$reply = Notice::staticGet($this->in_reply_to_status_id); $reply = Notice::staticGet($this->in_reply_to_status_id);
@ -281,7 +281,8 @@ class ApiStatusesUpdateAction extends ApiAuthAction
$reply_to = $this->in_reply_to_status_id; $reply_to = $this->in_reply_to_status_id;
} else { } else {
$this->clientError( $this->clientError(
_('Not found.'), // TRANS: Client error displayed when replying to a non-existing notice.
_('Parent notice not found.'),
$code = 404, $code = 404,
$this->format $this->format
); );
@ -303,10 +304,9 @@ class ApiStatusesUpdateAction extends ApiAuthAction
if (Notice::contentTooLong($status_shortened)) { if (Notice::contentTooLong($status_shortened)) {
$upload->delete(); $upload->delete();
$msg = _( $msg = _m('Maximum notice size is %d character, including attachment URL.',
'Max notice size is %d chars, ' . 'Maximum notice size is %d characters, including attachment URL.',
'including attachment URL.' Notice::maxContent());
);
$this->clientError( $this->clientError(
sprintf($msg, Notice::maxContent()), sprintf($msg, Notice::maxContent()),
400, 400,
@ -345,7 +345,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
if (isset($upload)) { if (isset($upload)) {
$upload->attachToNotice($this->notice); $upload->attachToNotice($this->notice);
} }
} }
$this->showNotice(); $this->showNotice();
@ -356,7 +355,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
* *
* @return void * @return void
*/ */
function showNotice() function showNotice()
{ {
if (!empty($this->notice)) { if (!empty($this->notice)) {
@ -375,7 +373,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction
* *
* @return boolean true or false * @return boolean true or false
*/ */
function supported($cmd) function supported($cmd)
{ {
static $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand', static $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand',
@ -387,5 +384,4 @@ class ApiStatusesUpdateAction extends ApiAuthAction
return false; return false;
} }
} }

View File

@ -254,9 +254,11 @@ class AvatarsettingsAction extends AccountSettingsAction
&& empty($_POST) && empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0) && ($_SERVER['CONTENT_LENGTH'] > 0)
) { ) {
$msg = _('The server was unable to handle that much POST ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'data (%s bytes) due to its current configuration.'); // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); $this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return; return;
} }

View File

@ -45,7 +45,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @link http://status.net/ * @link http://status.net/
* @fixme merge more of this code with related variants * @fixme merge more of this code with related variants
*/ */
class DeletegroupAction extends RedirectingAction class DeletegroupAction extends RedirectingAction
{ {
var $group = null; var $group = null;
@ -56,12 +55,12 @@ class DeletegroupAction extends RedirectingAction
* @fixme merge common setup code with other group actions * @fixme merge common setup code with other group actions
* @fixme allow group admins to delete their own groups * @fixme allow group admins to delete their own groups
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
if (!common_logged_in()) { if (!common_logged_in()) {
// TRANS: Client error when trying to delete group while not logged in.
$this->clientError(_('You must be logged in to delete a group.')); $this->clientError(_('You must be logged in to delete a group.'));
return false; return false;
} }
@ -84,23 +83,27 @@ class DeletegroupAction extends RedirectingAction
$local = Local_group::staticGet('nickname', $nickname); $local = Local_group::staticGet('nickname', $nickname);
if (!$local) { if (!$local) {
// TRANS: Client error when trying to delete a non-local group.
$this->clientError(_('No such group.'), 404); $this->clientError(_('No such group.'), 404);
return false; return false;
} }
$this->group = User_group::staticGet('id', $local->group_id); $this->group = User_group::staticGet('id', $local->group_id);
} else { } else {
// TRANS: Client error when trying to delete a group without providing a nickname or ID for the group.
$this->clientError(_('No nickname or ID.'), 404); $this->clientError(_('No nickname or ID.'), 404);
return false; return false;
} }
if (!$this->group) { if (!$this->group) {
// TRANS: Client error when trying to delete a non-existing group.
$this->clientError(_('No such group.'), 404); $this->clientError(_('No such group.'), 404);
return false; return false;
} }
$cur = common_current_user(); $cur = common_current_user();
if (!$cur->hasRight(Right::DELETEGROUP)) { if (!$cur->hasRight(Right::DELETEGROUP)) {
// TRANS: Client error when trying to delete a group without having the rights to delete it.
$this->clientError(_('You are not allowed to delete this group.'), 403); $this->clientError(_('You are not allowed to delete this group.'), 403);
return false; return false;
} }
@ -117,7 +120,6 @@ class DeletegroupAction extends RedirectingAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -143,14 +145,18 @@ class DeletegroupAction extends RedirectingAction
Event::handle('EndDeleteGroup', array($this->group)); Event::handle('EndDeleteGroup', array($this->group));
} }
} catch (Exception $e) { } catch (Exception $e) {
$this->serverError(sprintf(_('Could not delete group %2$s.'), // TRANS: Server error displayed if a group could not be deleted.
// TRANS: %s is the name of the group that could not be deleted.
$this->serverError(sprintf(_('Could not delete group %s.'),
$this->group->nickname)); $this->group->nickname));
} }
if ($this->boolean('ajax')) { if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8'); $this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head'); $this->elementStart('head');
$this->element('title', null, sprintf(_('Deleted group %2$s'), // TRANS: Message given after deleting a group.
// TRANS: %s is the deleted group's name.
$this->element('title', null, sprintf(_('Deleted group %s'),
$this->group->nickname)); $this->group->nickname));
$this->elementEnd('head'); $this->elementEnd('head');
$this->elementStart('body'); $this->elementStart('body');
@ -166,6 +172,7 @@ class DeletegroupAction extends RedirectingAction
} }
function title() { function title() {
// TRANS: Title.
return _('Delete group'); return _('Delete group');
} }
@ -191,8 +198,10 @@ class DeletegroupAction extends RedirectingAction
'action' => common_local_url('deletegroup', array('id' => $this->group->id)))); 'action' => common_local_url('deletegroup', array('id' => $this->group->id))));
$this->elementStart('fieldset'); $this->elementStart('fieldset');
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
// TRANS: Form legend for deleting a group.
$this->element('legend', _('Delete group')); $this->element('legend', _('Delete group'));
if (Event::handle('StartDeleteGroupForm', array($this, $this->group))) { if (Event::handle('StartDeleteGroupForm', array($this, $this->group))) {
// TRANS: Warning in form for deleleting a group.
$this->element('p', null, $this->element('p', null,
_('Are you sure you want to delete this group? '. _('Are you sure you want to delete this group? '.
'This will clear all data about the group from the '. 'This will clear all data about the group from the '.

View File

@ -120,8 +120,11 @@ class DesignadminpanelAction extends AdminPanelAction
&& empty($_POST) && empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0) && ($_SERVER['CONTENT_LENGTH'] > 0)
) { ) {
$msg = _('The server was unable to handle that much POST ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'data (%s bytes) due to its current configuration.'); // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); $this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return; return;
} }
@ -140,7 +143,7 @@ class DesignadminpanelAction extends AdminPanelAction
$themeChanged = ($this->trimmed('theme') != $oldtheme); $themeChanged = ($this->trimmed('theme') != $oldtheme);
} }
static $settings = array('theme', 'logo'); static $settings = array('theme', 'logo', 'ssllogo');
$values = array(); $values = array();
@ -230,6 +233,7 @@ class DesignadminpanelAction extends AdminPanelAction
function restoreDefaults() function restoreDefaults()
{ {
$this->deleteSetting('site', 'logo'); $this->deleteSetting('site', 'logo');
$this->deleteSetting('site', 'ssllogo');
$this->deleteSetting('site', 'theme'); $this->deleteSetting('site', 'theme');
$settings = array( $settings = array(
@ -257,8 +261,8 @@ class DesignadminpanelAction extends AdminPanelAction
function saveBackgroundImage() function saveBackgroundImage()
{ {
$filename = null; $filename = null;
if (isset($_FILES['design_background-image_file']['error']) &&
if ($_FILES['design_background-image_file']['error'] == $_FILES['design_background-image_file']['error'] ==
UPLOAD_ERR_OK) { UPLOAD_ERR_OK) {
$filepath = null; $filepath = null;
@ -331,6 +335,11 @@ class DesignadminpanelAction extends AdminPanelAction
$this->clientError(_('Invalid logo URL.')); $this->clientError(_('Invalid logo URL.'));
} }
if (!empty($values['ssllogo']) &&
!Validate::uri($values['ssllogo'], array('allowed_schemes' => array('https')))) {
$this->clientError(_('Invalid SSL logo URL.'));
}
if (!in_array($values['theme'], Theme::listAvailable())) { if (!in_array($values['theme'], Theme::listAvailable())) {
$this->clientError(sprintf(_("Theme not available: %s."), $values['theme'])); $this->clientError(sprintf(_("Theme not available: %s."), $values['theme']));
} }
@ -444,6 +453,10 @@ class DesignAdminPanelForm extends AdminForm
$this->input('logo', _('Site logo'), 'Logo for the site (full URL)'); $this->input('logo', _('Site logo'), 'Logo for the site (full URL)');
$this->unli(); $this->unli();
$this->li();
$this->input('ssllogo', _('SSL logo'), 'Logo to show on SSL pages');
$this->unli();
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
$this->out->elementEnd('fieldset'); $this->out->elementEnd('fieldset');

View File

@ -42,7 +42,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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class EditApplicationAction extends OwnerDesignAction class EditApplicationAction extends OwnerDesignAction
{ {
var $msg = null; var $msg = null;
@ -51,18 +50,19 @@ class EditApplicationAction extends OwnerDesignAction
function title() function title()
{ {
return _('Edit Application'); // TRANS: Title for "Edit application" form.
return _('Edit application');
} }
/** /**
* Prepare to run * Prepare to run
*/ */
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
if (!common_logged_in()) { if (!common_logged_in()) {
// TRANS: Client error displayed trying to edit an application while not logged in.
$this->clientError(_('You must be logged in to edit an application.')); $this->clientError(_('You must be logged in to edit an application.'));
return false; return false;
} }
@ -74,10 +74,12 @@ class EditApplicationAction extends OwnerDesignAction
$cur = common_current_user(); $cur = common_current_user();
if ($cur->id != $this->owner->id) { if ($cur->id != $this->owner->id) {
// TRANS: Client error displayed trying to edit an application while not being its owner.
$this->clientError(_('You are not the owner of this application.'), 401); $this->clientError(_('You are not the owner of this application.'), 401);
} }
if (!$this->app) { if (!$this->app) {
// TRANS: Client error displayed trying to edit an application that does not exist.
$this->clientError(_('No such application.')); $this->clientError(_('No such application.'));
return false; return false;
} }
@ -94,7 +96,6 @@ class EditApplicationAction extends OwnerDesignAction
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -115,8 +116,11 @@ class EditApplicationAction extends OwnerDesignAction
&& empty($_POST) && empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0) && ($_SERVER['CONTENT_LENGTH'] > 0)
) { ) {
$msg = _('The server was unable to handle that much POST ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'data (%s bytes) due to its current configuration.'); // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); $this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return; return;
} }
@ -136,6 +140,7 @@ class EditApplicationAction extends OwnerDesignAction
} elseif ($this->arg('save')) { } elseif ($this->arg('save')) {
$this->trySave(); $this->trySave();
} else { } else {
// TRANS: Client error displayed submitting invalid form data for edit application.
$this->clientError(_('Unexpected form submission.')); $this->clientError(_('Unexpected form submission.'));
} }
} }
@ -158,6 +163,7 @@ class EditApplicationAction extends OwnerDesignAction
$this->element('p', 'error', $this->msg); $this->element('p', 'error', $this->msg);
} else { } else {
$this->element('p', 'instructions', $this->element('p', 'instructions',
// TRANS: Instructions for "Edit application" form.
_('Use this form to edit your application.')); _('Use this form to edit your application.'));
} }
} }
@ -174,36 +180,47 @@ class EditApplicationAction extends OwnerDesignAction
$access_type = $this->arg('default_access_type'); $access_type = $this->arg('default_access_type');
if (empty($name)) { if (empty($name)) {
// TRANS: Validation error shown when not providing a name in the "Edit application" form.
$this->showForm(_('Name is required.')); $this->showForm(_('Name is required.'));
return; return;
} elseif (mb_strlen($name) > 255) { } elseif (mb_strlen($name) > 255) {
$this->showForm(_('Name is too long (max 255 chars).')); // TRANS: Validation error shown when providing too long a name in the "Edit application" form.
$this->showForm(_('Name is too long (max 255 characters).'));
return; return;
} else if ($this->nameExists($name)) { } else if ($this->nameExists($name)) {
// TRANS: Validation error shown when providing a name for an application that already exists in the "Edit application" form.
$this->showForm(_('Name already in use. Try another one.')); $this->showForm(_('Name already in use. Try another one.'));
return; return;
} elseif (empty($description)) { } elseif (empty($description)) {
// TRANS: Validation error shown when not providing a description in the "Edit application" form.
$this->showForm(_('Description is required.')); $this->showForm(_('Description is required.'));
return; return;
} elseif (Oauth_application::descriptionTooLong($description)) { } elseif (Oauth_application::descriptionTooLong($description)) {
$this->showForm(sprintf( $this->showForm(sprintf(
_('Description is too long (max %d chars).'), // TRANS: Validation error shown when providing too long a description in the "Edit application" form.
_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
Oauth_application::maxDesc()),
Oauth_application::maxDesc())); Oauth_application::maxDesc()));
return; return;
} elseif (mb_strlen($source_url) > 255) { } elseif (mb_strlen($source_url) > 255) {
// TRANS: Validation error shown when providing too long a source URL in the "Edit application" form.
$this->showForm(_('Source URL is too long.')); $this->showForm(_('Source URL is too long.'));
return; return;
} elseif ((mb_strlen($source_url) > 0) } elseif ((mb_strlen($source_url) > 0)
&& !Validate::uri($source_url, && !Validate::uri($source_url,
array('allowed_schemes' => array('http', 'https')))) array('allowed_schemes' => array('http', 'https'))))
{ {
// TRANS: Validation error shown when providing an invalid source URL in the "Edit application" form.
$this->showForm(_('Source URL is not valid.')); $this->showForm(_('Source URL is not valid.'));
return; return;
} elseif (empty($organization)) { } elseif (empty($organization)) {
// TRANS: Validation error shown when not providing an organisation in the "Edit application" form.
$this->showForm(_('Organization is required.')); $this->showForm(_('Organization is required.'));
return; return;
} elseif (mb_strlen($organization) > 255) { } elseif (mb_strlen($organization) > 255) {
$this->showForm(_('Organization is too long (max 255 chars).')); // TRANS: Validation error shown when providing too long an arganisation name in the "Edit application" form.
$this->showForm(_('Organization is too long (maximum 255 characters).'));
return; return;
} elseif (empty($homepage)) { } elseif (empty($homepage)) {
$this->showForm(_('Organization homepage is required.')); $this->showForm(_('Organization homepage is required.'));
@ -212,9 +229,11 @@ class EditApplicationAction extends OwnerDesignAction
&& !Validate::uri($homepage, && !Validate::uri($homepage,
array('allowed_schemes' => array('http', 'https')))) array('allowed_schemes' => array('http', 'https'))))
{ {
// TRANS: Validation error shown when providing an invalid homepage URL in the "Edit application" form.
$this->showForm(_('Homepage is not a valid URL.')); $this->showForm(_('Homepage is not a valid URL.'));
return; return;
} elseif (mb_strlen($callback_url) > 255) { } elseif (mb_strlen($callback_url) > 255) {
// TRANS: Validation error shown when providing too long a callback URL in the "Edit application" form.
$this->showForm(_('Callback is too long.')); $this->showForm(_('Callback is too long.'));
return; return;
} elseif (mb_strlen($callback_url) > 0 } elseif (mb_strlen($callback_url) > 0
@ -222,6 +241,7 @@ class EditApplicationAction extends OwnerDesignAction
array('allowed_schemes' => array('http', 'https')) array('allowed_schemes' => array('http', 'https'))
)) ))
{ {
// TRANS: Validation error shown when providing an invalid callback URL in the "Edit application" form.
$this->showForm(_('Callback URL is not valid.')); $this->showForm(_('Callback URL is not valid.'));
return; return;
} }
@ -258,6 +278,7 @@ class EditApplicationAction extends OwnerDesignAction
// the next step. // the next step.
if ($result === false) { if ($result === false) {
common_log_db_error($this->app, 'UPDATE', __FILE__); common_log_db_error($this->app, 'UPDATE', __FILE__);
// TRANS: Server error occuring when an application could not be updated from the "Edit application" form.
$this->serverError(_('Could not update application.')); $this->serverError(_('Could not update application.'));
} }
@ -276,7 +297,6 @@ class EditApplicationAction extends OwnerDesignAction
* *
* @return boolean true if the name already exists * @return boolean true if the name already exists
*/ */
function nameExists($name) function nameExists($name)
{ {
$newapp = Oauth_application::staticGet('name', $name); $newapp = Oauth_application::staticGet('name', $name);
@ -286,6 +306,4 @@ class EditApplicationAction extends OwnerDesignAction
return $newapp->id != $this->app->id; return $newapp->id != $this->app->id;
} }
} }
} }

View File

@ -199,13 +199,13 @@ class EditgroupAction extends GroupDesignAction
$this->showForm(_('Homepage is not a valid URL.')); $this->showForm(_('Homepage is not a valid URL.'));
return; return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) { } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
$this->showForm(_('Full name is too long (max 255 chars).')); $this->showForm(_('Full name is too long (maximum 255 characters).'));
return; return;
} else if (User_group::descriptionTooLong($description)) { } else if (User_group::descriptionTooLong($description)) {
$this->showForm(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription())); $this->showForm(sprintf(_('Description is too long (max %d chars).'), User_group::maxDescription()));
return; return;
} else if (!is_null($location) && mb_strlen($location) > 255) { } else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).')); $this->showForm(_('Location is too long (maximum 255 characters).'));
return; return;
} }

View File

@ -36,8 +36,11 @@ class InviteAction extends CurrentUserDesignAction
{ {
parent::handle($args); parent::handle($args);
if (!common_config('invite', 'enabled')) { if (!common_config('invite', 'enabled')) {
// TRANS: Client error displayed when trying to sent invites while they have been disabled.
$this->clientError(_('Invites have been disabled.')); $this->clientError(_('Invites have been disabled.'));
} else if (!common_logged_in()) { } else if (!common_logged_in()) {
// TRANS: Client error displayed when trying to sent invites while not logged in.
// TRANS: %s is the StatusNet site name.
$this->clientError(sprintf(_('You must be logged in to invite other users to use %s.'), $this->clientError(sprintf(_('You must be logged in to invite other users to use %s.'),
common_config('site', 'name'))); common_config('site', 'name')));
return; return;
@ -69,7 +72,9 @@ class InviteAction extends CurrentUserDesignAction
foreach ($addresses as $email) { foreach ($addresses as $email) {
$email = trim($email); $email = trim($email);
if (!Validate::email($email, common_config('email', 'check_domain'))) { if (!Validate::email($email, common_config('email', 'check_domain'))) {
$this->showForm(sprintf(_('Invalid email address: %s'), $email)); // TRANS: Form validation message when providing an e-mail address that does not validate.
// TRANS: %s is an invalid e-mail address.
$this->showForm(sprintf(_('Invalid email address: %s.'), $email));
return; return;
} }
} }
@ -107,8 +112,10 @@ class InviteAction extends CurrentUserDesignAction
function title() function title()
{ {
if ($this->mode == 'sent') { if ($this->mode == 'sent') {
return _('Invitation(s) sent'); // TRANS: Page title when invitations have been sent.
return _('Invitations sent');
} else { } else {
// TRANS: Page title when inviting potential users.
return _('Invite new users'); return _('Invite new users');
} }
} }
@ -125,28 +132,48 @@ class InviteAction extends CurrentUserDesignAction
function showInvitationSuccess() function showInvitationSuccess()
{ {
if ($this->already) { if ($this->already) {
$this->element('p', null, _('You are already subscribed to these users:')); // TRANS: Message displayed inviting users to use a StatusNet site while the inviting user
// TRANS: is already subscribed to one or more users with the given e-mail address(es).
// TRANS: Plural form is based on the number of reported already subscribed e-mail addresses.
// TRANS: Followed by a bullet list.
$this->element('p', null, _m('You are already subscribed to this user:',
'You are already subscribed to these users:',
count($this->already)));
$this->elementStart('ul'); $this->elementStart('ul');
foreach ($this->already as $other) { foreach ($this->already as $other) {
// TRANS: Used as list item for already subscribed users (%1$s is nickname, %2$s is e-mail address).
$this->element('li', null, sprintf(_('%1$s (%2$s)'), $other->nickname, $other->email)); $this->element('li', null, sprintf(_('%1$s (%2$s)'), $other->nickname, $other->email));
} }
$this->elementEnd('ul'); $this->elementEnd('ul');
} }
if ($this->subbed) { if ($this->subbed) {
$this->element('p', null, _('These people are already users and you were automatically subscribed to them:')); // TRANS: Message displayed inviting users to use a StatusNet site while the invited user
// TRANS: already uses a this StatusNet site. Plural form is based on the number of
// TRANS: reported already present people. Followed by a bullet list.
$this->element('p', null, _m('This person is already a user and you were automatically subscribed:',
'These people are already users and you were automatically subscribed to them:',
count($this->subbed)));
$this->elementStart('ul'); $this->elementStart('ul');
foreach ($this->subbed as $other) { foreach ($this->subbed as $other) {
// TRANS: Used as list item for already registered people (%1$s is nickname, %2$s is e-mail address).
$this->element('li', null, sprintf(_('%1$s (%2$s)'), $other->nickname, $other->email)); $this->element('li', null, sprintf(_('%1$s (%2$s)'), $other->nickname, $other->email));
} }
$this->elementEnd('ul'); $this->elementEnd('ul');
} }
if ($this->sent) { if ($this->sent) {
$this->element('p', null, _('Invitation(s) sent to the following people:')); // TRANS: Message displayed inviting users to use a StatusNet site. Plural form is
// TRANS: based on the number of invitations sent. Followed by a bullet list of
// TRANS: e-mail addresses to which invitations were sent.
$this->element('p', null, _m('Invitation sent to the following person:',
'Invitations sent to the following people:',
count($this->sent)));
$this->elementStart('ul'); $this->elementStart('ul');
foreach ($this->sent as $other) { foreach ($this->sent as $other) {
$this->element('li', null, $other); $this->element('li', null, $other);
} }
$this->elementEnd('ul'); $this->elementEnd('ul');
// TRANS: Generic message displayed after sending out one or more invitations to
// TRANS: people to join a StatusNet site.
$this->element('p', null, _('You will be notified when your invitees accept the invitation and register on the site. Thanks for growing the community!')); $this->element('p', null, _('You will be notified when your invitees accept the invitation and register on the site. Thanks for growing the community!'));
} }
} }
@ -159,6 +186,7 @@ class InviteAction extends CurrentUserDesignAction
} else { } else {
$this->elementStart('div', 'instructions'); $this->elementStart('div', 'instructions');
$this->element('p', null, $this->element('p', null,
// TRANS: Form instructions.
_('Use this form to invite your friends and colleagues to use this service.')); _('Use this form to invite your friends and colleagues to use this service.'));
$this->elementEnd('div'); $this->elementEnd('div');
} }
@ -179,18 +207,23 @@ class InviteAction extends CurrentUserDesignAction
'class' => 'form_settings', 'class' => 'form_settings',
'action' => common_local_url('invite'))); 'action' => common_local_url('invite')));
$this->elementStart('fieldset'); $this->elementStart('fieldset');
// TRANS: Form legend.
$this->element('legend', null, 'Send an invitation'); $this->element('legend', null, 'Send an invitation');
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label for a list of e-mail addresses.
$this->textarea('addresses', _('Email addresses'), $this->textarea('addresses', _('Email addresses'),
$this->trimmed('addresses'), $this->trimmed('addresses'),
// TRANS: Tooltip for field label for a list of e-mail addresses.
_('Addresses of friends to invite (one per line)')); _('Addresses of friends to invite (one per line)'));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label for a personal message to send to invitees.
$this->textarea('personal', _('Personal message'), $this->textarea('personal', _('Personal message'),
$this->trimmed('personal'), $this->trimmed('personal'),
// TRANS: Tooltip for field label for a personal message to send to invitees.
_('Optionally add a personal message to the invitation.')); _('Optionally add a personal message to the invitation.'));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementEnd('ul'); $this->elementEnd('ul');
@ -224,10 +257,16 @@ class InviteAction extends CurrentUserDesignAction
$headers['From'] = mail_notify_from(); $headers['From'] = mail_notify_from();
$headers['To'] = trim($email); $headers['To'] = trim($email);
// TRANS: Subject for invitation email. Note that 'them' is correct as a gender-neutral singular 3rd-person pronoun in English. // TRANS: Subject for invitation email. Note that 'them' is correct as a gender-neutral
// TRANS: singular 3rd-person pronoun in English. %1$s is the inviting user, $2$s is
// TRANS: the StatusNet sitename.
$headers['Subject'] = sprintf(_('%1$s has invited you to join them on %2$s'), $bestname, $sitename); $headers['Subject'] = sprintf(_('%1$s has invited you to join them on %2$s'), $bestname, $sitename);
// TRANS: Body text for invitation email. Note that 'them' is correct as a gender-neutral singular 3rd-person pronoun in English. // TRANS: Body text for invitation email. Note that 'them' is correct as a gender-neutral
// TRANS: singular 3rd-person pronoun in English. %1$s is the inviting user, %2$s is the
// TRANS: StatusNet sitename, %3$s is the site URL, %4$s is the personal message from the
// TRANS: inviting user, %s%5 a link to the timeline for the inviting user, %s$6 is a link
// TRANS: to register with the StatusNet site.
$body = sprintf(_("%1\$s has invited you to join them on %2\$s (%3\$s).\n\n". $body = sprintf(_("%1\$s has invited you to join them on %2\$s (%3\$s).\n\n".
"%2\$s is a micro-blogging service that lets you keep up-to-date with people you know and people who interest you.\n\n". "%2\$s is a micro-blogging service that lets you keep up-to-date with people you know and people who interest you.\n\n".
"You can also share news about yourself, your thoughts, or your life online with people who know about you. ". "You can also share news about yourself, your thoughts, or your life online with people who know about you. ".

View File

@ -148,7 +148,7 @@ class MakeadminAction extends RedirectingAction
$this->group->getBestName()); $this->group->getBestName());
} }
$this->returnToArgs(); $this->returnToPrevious();
} }
/** /**

View File

@ -42,14 +42,14 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class NewApplicationAction extends OwnerDesignAction class NewApplicationAction extends OwnerDesignAction
{ {
var $msg; var $msg;
function title() function title()
{ {
return _('New Application'); // TRANS: This is the title of the form for adding a new application.
return _('New application');
} }
/** /**
@ -61,6 +61,7 @@ class NewApplicationAction extends OwnerDesignAction
parent::prepare($args); parent::prepare($args);
if (!common_logged_in()) { if (!common_logged_in()) {
// TRANS: Client error displayed trying to add a new application while not logged in.
$this->clientError(_('You must be logged in to register an application.')); $this->clientError(_('You must be logged in to register an application.'));
return false; return false;
} }
@ -98,8 +99,11 @@ class NewApplicationAction extends OwnerDesignAction
&& empty($_POST) && empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0) && ($_SERVER['CONTENT_LENGTH'] > 0)
) { ) {
$msg = _('The server was unable to handle that much POST ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'data (%s bytes) due to its current configuration.'); // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); $this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
return; return;
} }
@ -162,14 +166,18 @@ class NewApplicationAction extends OwnerDesignAction
$this->showForm(_('Name already in use. Try another one.')); $this->showForm(_('Name already in use. Try another one.'));
return; return;
} elseif (mb_strlen($name) > 255) { } elseif (mb_strlen($name) > 255) {
$this->showForm(_('Name is too long (max 255 chars).')); $this->showForm(_('Name is too long (maximum 255 chars).'));
return; return;
} elseif (empty($description)) { } elseif (empty($description)) {
$this->showForm(_('Description is required.')); $this->showForm(_('Description is required.'));
return; return;
} elseif (Oauth_application::descriptionTooLong($description)) { } elseif (Oauth_application::descriptionTooLong($description)) {
$this->showForm(sprintf( $this->showForm(sprintf(
_('Description is too long (max %d chars).'), // TRANS: Form validation error in New application form.
// TRANS: %d is the maximum number of characters for the description.
_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
Oauth_application::maxDesc()),
Oauth_application::maxDesc())); Oauth_application::maxDesc()));
return; return;
} elseif (empty($source_url)) { } elseif (empty($source_url)) {
@ -188,7 +196,7 @@ class NewApplicationAction extends OwnerDesignAction
$this->showForm(_('Organization is required.')); $this->showForm(_('Organization is required.'));
return; return;
} elseif (mb_strlen($organization) > 255) { } elseif (mb_strlen($organization) > 255) {
$this->showForm(_('Organization is too long (max 255 chars).')); $this->showForm(_('Organization is too long (maximum 255 chars).'));
return; return;
} elseif (empty($homepage)) { } elseif (empty($homepage)) {
$this->showForm(_('Organization homepage is required.')); $this->showForm(_('Organization homepage is required.'));

View File

@ -139,13 +139,13 @@ class NewgroupAction extends Action
$this->showForm(_('Homepage is not a valid URL.')); $this->showForm(_('Homepage is not a valid URL.'));
return; return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) { } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
$this->showForm(_('Full name is too long (max 255 chars).')); $this->showForm(_('Full name is too long (maximum 255 characters).'));
return; return;
} else if (User_group::descriptionTooLong($description)) { } else if (User_group::descriptionTooLong($description)) {
$this->showForm(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription())); $this->showForm(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription()));
return; return;
} else if (!is_null($location) && mb_strlen($location) > 255) { } else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).')); $this->showForm(_('Location is too long (maximum 255 characters).'));
return; return;
} }

View File

@ -147,8 +147,11 @@ class NewmessageAction extends Action
$content_shortened = common_shorten_links($this->content); $content_shortened = common_shorten_links($this->content);
if (Message::contentTooLong($content_shortened)) { if (Message::contentTooLong($content_shortened)) {
$this->showForm(sprintf(_('That\'s too long. ' . // TRANS: Form validation error displayed when message content is too long.
'Max message size is %d chars.'), // TRANS: %d is the maximum number of characters for a message.
$this->showForm(sprintf(_m('That\'s too long. Maximum message size is %d character.',
'That\'s too long. Maximum message size is %d characters.',
Message::maxContent()),
Message::maxContent())); Message::maxContent()));
return; return;
} }

View File

@ -82,7 +82,6 @@ class NewnoticeAction extends Action
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
if (!common_logged_in()) { if (!common_logged_in()) {
@ -91,9 +90,12 @@ class NewnoticeAction extends Action
// check for this before token since all POST and FILES data // check for this before token since all POST and FILES data
// is losts when size is exceeded // is losts when size is exceeded
if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) { if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) {
$this->clientError(sprintf(_('The server was unable to handle ' . // TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
'that much POST data (%s bytes) due to its current configuration.'), // TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$_SERVER['CONTENT_LENGTH'])); $msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg,$_SERVER['CONTENT_LENGTH']));
} }
parent::handle($args); parent::handle($args);
@ -352,4 +354,3 @@ class NewnoticeAction extends Action
$nli->show(); $nli->show();
} }
} }

View File

@ -82,7 +82,7 @@ class NudgeAction extends Action
} }
if (!$other->email || !$other->emailnotifynudge) { if (!$other->email || !$other->emailnotifynudge) {
$this->clientError(_('This user doesn\'t allow nudges or hasn\'t confirmed or set their email yet.')); $this->clientError(_('This user doesn\'t allow nudges or hasn\'t confirmed or set their email address yet.'));
return; return;
} }

View File

@ -56,6 +56,7 @@ class OauthappssettingsAction extends SettingsAction
$this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
if (!common_logged_in()) { if (!common_logged_in()) {
// TRANS: Message displayed to an anonymous user trying to view OAuth application list.
$this->clientError(_('You must be logged in to list your applications.')); $this->clientError(_('You must be logged in to list your applications.'));
return false; return false;
} }
@ -71,6 +72,7 @@ class OauthappssettingsAction extends SettingsAction
function title() function title()
{ {
// TRANS: Page title for OAuth applications
return _('OAuth applications'); return _('OAuth applications');
} }
@ -82,6 +84,7 @@ class OauthappssettingsAction extends SettingsAction
function getInstructions() function getInstructions()
{ {
// TRANS: Page instructions for OAuth applications
return _('Applications you have registered'); return _('Applications you have registered');
} }
@ -100,6 +103,7 @@ class OauthappssettingsAction extends SettingsAction
$application = new Oauth_application(); $application = new Oauth_application();
$application->owner = $user->id; $application->owner = $user->id;
$application->whereAdd("name != 'anonymous'");
$application->limit($offset, $limit); $application->limit($offset, $limit);
$application->orderBy('created DESC'); $application->orderBy('created DESC');
$application->find(); $application->find();
@ -119,6 +123,7 @@ class OauthappssettingsAction extends SettingsAction
array('href' => common_local_url('newapplication'), array('href' => common_local_url('newapplication'),
'class' => 'more' 'class' => 'more'
), ),
// TRANS: Link description to add a new OAuth application.
'Register a new application'); 'Register a new application');
$this->elementEnd('p'); $this->elementEnd('p');
@ -132,6 +137,7 @@ class OauthappssettingsAction extends SettingsAction
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: Empty list message on page with OAuth applications.
$message = sprintf(_('You have not registered any applications yet.')); $message = sprintf(_('You have not registered any applications yet.'));
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
@ -162,5 +168,4 @@ class OauthappssettingsAction extends SettingsAction
} }
} }
} }

View File

@ -22,7 +22,7 @@
* @category Settings * @category Settings
* @package StatusNet * @package StatusNet
* @author Zach Copley <zach@status.net> * @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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -46,17 +46,15 @@ require_once INSTALLDIR . '/lib/apioauthstore.php';
* *
* @see SettingsAction * @see SettingsAction
*/ */
class OauthconnectionssettingsAction extends ConnectSettingsAction class OauthconnectionssettingsAction extends ConnectSettingsAction
{ {
var $page = null; var $page = null;
var $id = null; var $oauth_token = null;
function prepare($args) function prepare($args)
{ {
parent::prepare($args); parent::prepare($args);
$this->id = (int)$this->arg('id'); $this->oauth_token = $this->arg('oauth_token');
$this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
return true; return true;
} }
@ -69,6 +67,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
function title() function title()
{ {
// TRANS: Title for OAuth connection settings.
return _('Connected applications'); return _('Connected applications');
} }
@ -80,7 +79,8 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
function getInstructions() function getInstructions()
{ {
return _('You have allowed the following applications to access your account.'); // TRANS: Instructions for OAuth connection settings.
return _('The following connections exist for your account.');
} }
/** /**
@ -97,22 +97,26 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
$offset = ($this->page - 1) * APPS_PER_PAGE; $offset = ($this->page - 1) * APPS_PER_PAGE;
$limit = APPS_PER_PAGE + 1; $limit = APPS_PER_PAGE + 1;
$application = $profile->getApplications($offset, $limit); $connection = $profile->getConnectedApps($offset, $limit);
$cnt = 0; $cnt = 0;
if (!empty($application)) { if (!empty($connection)) {
$al = new ApplicationList($application, $user, $this, true); $cal = new ConnectedAppsList($connection, $user, $this);
$cnt = $al->show(); $cnt = $cal->show();
} }
if ($cnt == 0) { if ($cnt == 0) {
$this->showEmptyListMessage(); $this->showEmptyListMessage();
} }
$this->pagination($this->page > 1, $cnt > APPS_PER_PAGE, $this->pagination(
$this->page, 'connectionssettings', $this->page > 1,
array('nickname' => $user->nickname)); $cnt > APPS_PER_PAGE,
$this->page,
'connectionssettings',
array('nickname' => $user->nickname)
);
} }
/** /**
@ -125,7 +129,6 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
* *
* @return void * @return void
*/ */
function handlePost() function handlePost()
{ {
// CSRF protection // CSRF protection
@ -138,44 +141,36 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
} }
if ($this->arg('revoke')) { if ($this->arg('revoke')) {
$this->revokeAccess($this->id); $this->revokeAccess($this->oauth_token);
// XXX: Show some indicator to the user of what's been done.
$this->showPage();
} else { } else {
// TRANS: Client error when submitting a form with unexpected information.
$this->clientError(_('Unexpected form submission.'), 401); $this->clientError(_('Unexpected form submission.'), 401);
return false; return false;
} }
} }
/** /**
* Revoke access to an authorized OAuth application * Revoke an access token
*
* XXX: Confirm revoke before doing it
* *
* @param int $appId the ID of the application * @param int $appId the ID of the application
* *
*/ */
function revokeAccess($token)
function revokeAccess($appId)
{ {
$cur = common_current_user(); $cur = common_current_user();
$app = Oauth_application::staticGet('id', $appId); $appUser = Oauth_application_user::getByUserAndToken($cur, $token);
if (empty($app)) {
$this->clientError(_('No such application.'), 404);
return false;
}
// XXX: Transaction here?
$appUser = Oauth_application_user::getByKeys($cur, $app);
if (empty($appUser)) { if (empty($appUser)) {
// TRANS: Client error when trying to revoke access for an application while not being a user of it.
$this->clientError(_('You are not a user of that application.'), 401); $this->clientError(_('You are not a user of that application.'), 401);
return false; return false;
} }
$app = Oauth_application::staticGet('id', $appUser->application_id);
$datastore = new ApiStatusNetOAuthDataStore(); $datastore = new ApiStatusNetOAuthDataStore();
$datastore->revoke_token($appUser->token, 1); $datastore->revoke_token($appUser->token, 1);
@ -183,18 +178,38 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
if (!$result) { if (!$result) {
common_log_db_error($orig, 'DELETE', __FILE__); common_log_db_error($orig, 'DELETE', __FILE__);
$this->clientError(sprintf(_('Unable to revoke access for app: %s.'), $app->id)); // TRANS: Client error when revoking access has failed for some reason.
// TRANS: %s is the application ID revoking access failed for.
$this->clientError(sprintf(_('Unable to revoke access for application: %s.'), $app->id));
return false; return false;
} }
$msg = 'User %s (id: %d) revoked access to app %s (id: %d)'; $msg = 'API OAuth - user %s (id: %d) revoked access token %s for app id %d';
common_log(LOG_INFO, sprintf($msg, $cur->nickname, common_log(
$cur->id, $app->name, $app->id)); LOG_INFO,
sprintf(
$msg,
$cur->nickname,
$cur->id,
$appUser->token,
$appUser->application_id
)
);
$msg = sprintf(
// TRANS: Success message after revoking access for an application.
// TRANS: %1$s is the application name, %2$s is the first part of the user token.
_('You have successfully revoked access for %1$s and the access token starting with %2$s.'),
$app->name,
substr($appUser->token, 0, 7)
);
$this->showForm($msg, true);
} }
function showEmptyListMessage() function showEmptyListMessage()
{ {
// TRANS: Empty list message when no applications have been authorised yet.
$message = _('You have not authorized any applications to use your account.'); $message = _('You have not authorized any applications to use your account.');
$this->elementStart('div', 'guide'); $this->elementStart('div', 'guide');
@ -206,13 +221,24 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
{ {
$cur = common_current_user(); $cur = common_current_user();
$this->elementStart('div', array('id' => 'developer-help', 'class' => 'section'));
$this->element('h2', null, 'Developers'); $this->element('h2', null, 'Developers');
$this->elementStart('p'); $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');
}
$devMsg = sprintf(
// TRANS: Note for developers in the OAuth connection settings form.
// TRANS: This message contains a Markdown link. Do not separate "](".
// TRANS: %s is the URL to the OAuth settings.
_('Are you a developer? [Register an OAuth client application](%s) to use with this instance of StatusNet.'),
common_local_url('oauthappssettings')
);
$output = common_markup_to_html($devMsg);
$this->raw($output);
$this->elementEnd('p');
$this->elementEnd('section');
}
} }

View File

@ -44,10 +44,8 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class PathsadminpanelAction extends AdminPanelAction class PathsadminpanelAction extends AdminPanelAction
{ {
/** /**
* Returns the page title * Returns the page title
* *
@ -56,6 +54,7 @@ class PathsadminpanelAction extends AdminPanelAction
function title() function title()
{ {
// TRANS: Title for Paths admin panel.
return _('Paths'); return _('Paths');
} }
@ -64,9 +63,9 @@ class PathsadminpanelAction extends AdminPanelAction
* *
* @return string instructions * @return string instructions
*/ */
function getInstructions() function getInstructions()
{ {
// TRANS: Form instructions for Path admin panel.
return _('Path and server settings for this StatusNet site'); return _('Path and server settings for this StatusNet site');
} }
@ -75,7 +74,6 @@ class PathsadminpanelAction extends AdminPanelAction
* *
* @return void * @return void
*/ */
function showForm() function showForm()
{ {
$form = new PathsAdminPanelForm($this); $form = new PathsAdminPanelForm($this);
@ -88,14 +86,14 @@ class PathsadminpanelAction extends AdminPanelAction
* *
* @return void * @return void
*/ */
function saveSettings() function saveSettings()
{ {
static $settings = array( static $settings = array(
'site' => array('path', 'locale_path', 'ssl', 'sslserver'), 'site' => array('path', 'locale_path', 'ssl', 'sslserver'),
'theme' => array('server', 'dir', 'path'), 'theme' => array('server', 'dir', 'path', 'sslserver', 'sslpath'),
'avatar' => array('server', 'dir', 'path'), 'avatar' => array('server', 'dir', 'path'),
'background' => array('server', 'dir', 'path') 'background' => array('server', 'dir', 'path', 'sslserver', 'sslpath'),
'attachments' => array('server', 'dir', 'path', 'sslserver', 'sslpath')
); );
// XXX: If we're only going to have one boolean on thi page we // XXX: If we're only going to have one boolean on thi page we
@ -147,25 +145,29 @@ class PathsadminpanelAction extends AdminPanelAction
* *
* @return void * @return void
*/ */
function validate(&$values) function validate(&$values)
{ {
// Validate theme dir // Validate theme dir
if (!empty($values['theme']['dir']) && !is_readable($values['theme']['dir'])) { if (!empty($values['theme']['dir']) && !is_readable($values['theme']['dir'])) {
// TRANS: Client error in Paths admin panel.
// TRANS: %s is the directory that could not be read from.
$this->clientError(sprintf(_("Theme directory not readable: %s."), $values['theme']['dir'])); $this->clientError(sprintf(_("Theme directory not readable: %s."), $values['theme']['dir']));
} }
// Validate avatar dir // Validate avatar dir
if (empty($values['avatar']['dir']) || !is_writable($values['avatar']['dir'])) { if (empty($values['avatar']['dir']) || !is_writable($values['avatar']['dir'])) {
// TRANS: Client error in Paths admin panel.
// TRANS: %s is the avatar directory that could not be written to.
$this->clientError(sprintf(_("Avatar directory not writable: %s."), $values['avatar']['dir'])); $this->clientError(sprintf(_("Avatar directory not writable: %s."), $values['avatar']['dir']));
} }
// Validate background dir // Validate background dir
if (empty($values['background']['dir']) || !is_writable($values['background']['dir'])) { if (empty($values['background']['dir']) || !is_writable($values['background']['dir'])) {
// TRANS: Client error in Paths admin panel.
// TRANS: %s is the background directory that could not be written to.
$this->clientError(sprintf(_("Background directory not writable: %s."), $values['background']['dir'])); $this->clientError(sprintf(_("Background directory not writable: %s."), $values['background']['dir']));
} }
@ -174,27 +176,28 @@ class PathsadminpanelAction extends AdminPanelAction
// XXX: What else do we need to validate for lacales path here? --Z // XXX: What else do we need to validate for lacales path here? --Z
if (!empty($values['site']['locale_path']) && !is_readable($values['site']['locale_path'])) { if (!empty($values['site']['locale_path']) && !is_readable($values['site']['locale_path'])) {
// TRANS: Client error in Paths admin panel.
// TRANS: %s is the locales directory that could not be read from.
$this->clientError(sprintf(_("Locales directory not readable: %s."), $values['site']['locale_path'])); $this->clientError(sprintf(_("Locales directory not readable: %s."), $values['site']['locale_path']));
} }
// Validate SSL setup // Validate SSL setup
if (mb_strlen($values['site']['sslserver']) > 255) { if (mb_strlen($values['site']['sslserver']) > 255) {
// TRANS: Client error in Paths admin panel.
// TRANS: %s is the SSL server URL that is too long.
$this->clientError(_('Invalid SSL server. The maximum length is 255 characters.')); $this->clientError(_('Invalid SSL server. The maximum length is 255 characters.'));
} }
} }
} }
class PathsAdminPanelForm extends AdminForm class PathsAdminPanelForm extends AdminForm
{ {
/** /**
* ID of the form * ID of the form
* *
* @return int ID of the form * @return int ID of the form
*/ */
function id() function id()
{ {
return 'form_paths_admin_panel'; return 'form_paths_admin_panel';
@ -205,7 +208,6 @@ class PathsAdminPanelForm extends AdminForm
* *
* @return string class of the form * @return string class of the form
*/ */
function formClass() function formClass()
{ {
return 'form_settings'; return 'form_settings';
@ -216,7 +218,6 @@ class PathsAdminPanelForm extends AdminForm
* *
* @return string URL of the action * @return string URL of the action
*/ */
function action() function action()
{ {
return common_local_url('pathsadminpanel'); return common_local_url('pathsadminpanel');
@ -227,27 +228,39 @@ class PathsAdminPanelForm extends AdminForm
* *
* @return void * @return void
*/ */
function formData() function formData()
{ {
$this->out->elementStart('fieldset', array('id' => 'settings_paths_locale')); $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
// TRANS: Fieldset legend in Paths admin panel.
$this->out->element('legend', null, _('Site'), 'site'); $this->out->element('legend', null, _('Site'), 'site');
$this->out->elementStart('ul', 'form_data'); $this->out->elementStart('ul', 'form_data');
$this->li(); $this->li();
$this->input('server', _('Server'), _('Site\'s server hostname.')); $this->input('server',
// TRANS: Field label in Paths admin panel.
_('Server'),
_('Site\'s server hostname.'));
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('path', _('Path'), _('Site path')); $this->input('path',
// TRANS: Field label in Paths admin panel.
_('Path'),
_('Site path.'));
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('locale_path', _('Path to locales'), _('Directory path to locales'), 'site'); $this->input('locale_path',
// TRANS: Field label in Paths admin panel.
_('Locale directory'),
_('Directory path to locales.'),
'site');
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->out->checkbox('fancy', _('Fancy URLs'), $this->out->checkbox('fancy',
// TRANS: Checkbox label in Paths admin panel.
_('Fancy URLs'),
(bool) $this->value('fancy'), (bool) $this->value('fancy'),
_('Use fancy (more readable and memorable) URLs?')); _('Use fancy (more readable and memorable) URLs?'));
$this->unli(); $this->unli();
@ -261,35 +274,84 @@ class PathsAdminPanelForm extends AdminForm
$this->out->elementStart('ul', 'form_data'); $this->out->elementStart('ul', 'form_data');
$this->li(); $this->li();
$this->input('server', _('Theme server'), 'Server for themes', 'theme'); $this->input('server',
// TRANS: Field label in Paths admin panel.
_('Server'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Server for themes.'),
'theme');
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('path', _('Theme path'), 'Web path to themes', 'theme'); $this->input('path',
// TRANS: Field label in Paths admin panel.
_('Path'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Web path to themes.'),
'theme');
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('dir', _('Theme directory'), 'Directory where themes are located', 'theme'); $this->input('sslserver',
// TRANS: Field label in Paths admin panel.
_('SSL server'),
// TRANS: Tooltip for field label in Paths admin panel.
_('SSL server for themes (default: SSL server).'),
'theme');
$this->unli();
$this->li();
$this->input('sslpath',
// TRANS: Field label in Paths admin panel.
_('SSL path'),
// TRANS: Tooltip for field label in Paths admin panel.
_('SSL path to themes (default: /theme/).'),
'theme');
$this->unli();
$this->li();
$this->input('dir',
// TRANS: Field label in Paths admin panel.
_('Directory'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Directory where themes are located.'),
'theme');
$this->unli(); $this->unli();
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
$this->out->elementEnd('fieldset'); $this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_avatar-paths')); $this->out->elementStart('fieldset', array('id' => 'settings_avatar-paths'));
// TRANS: Fieldset legend in Paths admin panel.
$this->out->element('legend', null, _('Avatars')); $this->out->element('legend', null, _('Avatars'));
$this->out->elementStart('ul', 'form_data'); $this->out->elementStart('ul', 'form_data');
$this->li(); $this->li();
$this->input('server', _('Avatar server'), 'Server for avatars', 'avatar'); $this->input('server',
// TRANS: Field label in Paths admin panel.
_('Avatar server'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Server for avatars.'),
'avatar');
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('path', _('Avatar path'), 'Web path to avatars', 'avatar'); $this->input('path',
// TRANS: Field label in Paths admin panel.
_('Avatar path'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Web path to avatars.'),
'avatar');
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('dir', _('Avatar directory'), 'Directory where avatars are located', 'avatar'); $this->input('dir',
// TRANS: Field label in Paths admin panel.
_('Avatar directory'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Directory where avatars are located.'),
'avatar');
$this->unli(); $this->unli();
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
@ -298,46 +360,145 @@ class PathsAdminPanelForm extends AdminForm
$this->out->elementStart('fieldset', array('id' => $this->out->elementStart('fieldset', array('id' =>
'settings_design_background-paths')); 'settings_design_background-paths'));
// TRANS: Fieldset legend in Paths admin panel.
$this->out->element('legend', null, _('Backgrounds')); $this->out->element('legend', null, _('Backgrounds'));
$this->out->elementStart('ul', 'form_data'); $this->out->elementStart('ul', 'form_data');
$this->li(); $this->li();
$this->input('server', _('Background server'), 'Server for backgrounds', 'background'); $this->input('server',
// TRANS: Field label in Paths admin panel.
_('Server'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Server for backgrounds.'),
'background');
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('path', _('Background path'), 'Web path to backgrounds', 'background'); $this->input('path',
// TRANS: Field label in Paths admin panel.
_('Path'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Web path to backgrounds.'),
'background');
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('dir', _('Background directory'), 'Directory where backgrounds are located', 'background'); $this->input('sslserver',
// TRANS: Field label in Paths admin panel.
_('SSL server'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Server for backgrounds on SSL pages.'),
'background');
$this->unli();
$this->li();
$this->input('sslpath',
// TRANS: Field label in Paths admin panel.
_('SSL path'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Web path to backgrounds on SSL pages.'),
'background');
$this->unli();
$this->li();
$this->input('dir',
// TRANS: Field label in Paths admin panel.
_('Directory'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Directory where backgrounds are located.'),
'background');
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' =>
'settings_design_attachments-paths'));
// TRANS: Fieldset legens in Paths admin panel.
$this->out->element('legend', null, _('Attachments'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->input('server',
// TRANS: Field label in Paths admin panel.
_('Server'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Server for attachments.'),
'attachments');
$this->unli();
$this->li();
$this->input('path',
// TRANS: Field label in Paths admin panel.
_('Path'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Web path to attachments.'),
'attachments');
$this->unli();
$this->li();
$this->input('sslserver',
// TRANS: Field label in Paths admin panel.
_('SSL server'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Server for attachments on SSL pages.'),
'attachments');
$this->unli();
$this->li();
$this->input('sslpath',
// TRANS: Field label in Paths admin panel.
_('SSL path'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Web path to attachments on SSL pages.'),
'attachments');
$this->unli();
$this->li();
$this->input('dir',
// TRANS: Field label in Paths admin panel.
_('Directory'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Directory where attachments are located.'),
'attachments');
$this->unli(); $this->unli();
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
$this->out->elementEnd('fieldset'); $this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_admin_ssl')); $this->out->elementStart('fieldset', array('id' => 'settings_admin_ssl'));
// TRANS: Fieldset legend in Paths admin panel.
$this->out->element('legend', null, _('SSL')); $this->out->element('legend', null, _('SSL'));
$this->out->elementStart('ul', 'form_data'); $this->out->elementStart('ul', 'form_data');
$this->li(); $this->li();
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
$ssl = array('never' => _('Never'), $ssl = array('never' => _('Never'),
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
'sometimes' => _('Sometimes'), 'sometimes' => _('Sometimes'),
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
'always' => _('Always')); 'always' => _('Always'));
common_debug("site ssl = " . $this->value('site', 'ssl')); // TRANS: Drop down label in Paths admin panel.
$this->out->dropdown('site-ssl',
$this->out->dropdown('site-ssl', _('Use SSL'), _('Use SSL'),
$ssl, _('When to use SSL'), // TRANS: Tooltip for field label in Paths admin panel.
false, $this->value('ssl', 'site')); $ssl, _('When to use SSL.'),
false,
$this->value('ssl', 'site'));
$this->unli(); $this->unli();
$this->li(); $this->li();
$this->input('sslserver', _('SSL server'), $this->input('sslserver',
_('Server to direct SSL requests to'), 'site'); // TRANS: Field label in Paths admin panel.
_('SSL server'),
// TRANS: Tooltip for field label in Paths admin panel.
_('Server to direct SSL requests to.'),
'site');
$this->unli(); $this->unli();
$this->out->elementEnd('ul'); $this->out->elementEnd('ul');
$this->out->elementEnd('fieldset'); $this->out->elementEnd('fieldset');
} }
/** /**
@ -345,10 +506,11 @@ class PathsAdminPanelForm extends AdminForm
* *
* @return void * @return void
*/ */
function formActions() function formActions()
{ {
$this->out->submit('save', _('Save'), 'submit', // TRANS: Button text to store form data in the Paths admin panel.
$this->out->submit('save', _m('BUTTON','Save'), 'submit',
// TRANS: Button title text to store form data in the Paths admin panel.
'save', _('Save paths')); 'save', _('Save paths'));
} }
@ -365,10 +527,8 @@ class PathsAdminPanelForm extends AdminForm
* *
* @return void * @return void
*/ */
function input($setting, $title, $instructions, $section='site') function input($setting, $title, $instructions, $section='site')
{ {
$this->out->input("$section-$setting", $title, $this->value($setting, $section), $instructions); $this->out->input("$section-$setting", $title, $this->value($setting, $section), $instructions);
} }
} }

View File

@ -57,6 +57,7 @@ class ProfilesettingsAction extends AccountSettingsAction
function title() function title()
{ {
// TRANS: Page title for profile settings.
return _('Profile settings'); return _('Profile settings');
} }
@ -68,6 +69,7 @@ class ProfilesettingsAction extends AccountSettingsAction
function getInstructions() function getInstructions()
{ {
// TRANS: Usage instructions for profile settings.
return _('You can update your personal profile info here '. return _('You can update your personal profile info here '.
'so people know more about you.'); 'so people know more about you.');
} }
@ -96,6 +98,7 @@ class ProfilesettingsAction extends AccountSettingsAction
'class' => 'form_settings', 'class' => 'form_settings',
'action' => common_local_url('profilesettings'))); 'action' => common_local_url('profilesettings')));
$this->elementStart('fieldset'); $this->elementStart('fieldset');
// TRANS: Profile settings form legend.
$this->element('legend', null, _('Profile information')); $this->element('legend', null, _('Profile information'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
@ -103,38 +106,54 @@ class ProfilesettingsAction extends AccountSettingsAction
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
if (Event::handle('StartProfileFormData', array($this))) { if (Event::handle('StartProfileFormData', array($this))) {
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('nickname', _('Nickname'), $this->input('nickname', _('Nickname'),
($this->arg('nickname')) ? $this->arg('nickname') : $profile->nickname, ($this->arg('nickname')) ? $this->arg('nickname') : $profile->nickname,
_('1-64 lowercase letters or numbers, no punctuation or spaces')); // TRANS: Tooltip for field label in form for profile settings.
_('1-64 lowercase letters or numbers, no punctuation or spaces.'));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('fullname', _('Full name'), $this->input('fullname', _('Full name'),
($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname); ($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('homepage', _('Homepage'), $this->input('homepage', _('Homepage'),
($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage, ($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage,
_('URL of your homepage, blog, or profile on another site')); // TRANS: Tooltip for field label in form for profile settings.
_('URL of your homepage, blog, or profile on another site.'));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
$maxBio = Profile::maxBio(); $maxBio = Profile::maxBio();
if ($maxBio > 0) { if ($maxBio > 0) {
$bioInstr = sprintf(_('Describe yourself and your interests in %d chars'), // TRANS: Tooltip for field label in form for profile settings. Plural
// TRANS: is decided by the number of characters available for the
// TRANS: biography (%d).
$bioInstr = sprintf(_m('Describe yourself and your interests in %d character',
'Describe yourself and your interests in %d characters',
$maxBio),
$maxBio); $maxBio);
} else { } else {
// TRANS: Tooltip for field label in form for profile settings.
$bioInstr = _('Describe yourself and your interests'); $bioInstr = _('Describe yourself and your interests');
} }
// TRANS: Text area label in form for profile settings where users can provide.
// TRANS: their biography.
$this->textarea('bio', _('Bio'), $this->textarea('bio', _('Bio'),
($this->arg('bio')) ? $this->arg('bio') : $profile->bio, ($this->arg('bio')) ? $this->arg('bio') : $profile->bio,
$bioInstr); $bioInstr);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('location', _('Location'), $this->input('location', _('Location'),
($this->arg('location')) ? $this->arg('location') : $profile->location, ($this->arg('location')) ? $this->arg('location') : $profile->location,
// TRANS: Tooltip for field label in form for profile settings.
_('Where you are, like "City, State (or Region), Country"')); _('Where you are, like "City, State (or Region), Country"'));
$this->elementEnd('li'); $this->elementEnd('li');
if (common_config('location', 'share') == 'user') { if (common_config('location', 'share') == 'user') {
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Checkbox label in form for profile settings.
$this->checkbox('sharelocation', _('Share my current location when posting notices'), $this->checkbox('sharelocation', _('Share my current location when posting notices'),
($this->arg('sharelocation')) ? ($this->arg('sharelocation')) ?
$this->arg('sharelocation') : $user->shareLocation()); $this->arg('sharelocation') : $user->shareLocation());
@ -142,13 +161,17 @@ class ProfilesettingsAction extends AccountSettingsAction
} }
Event::handle('EndProfileFormData', array($this)); Event::handle('EndProfileFormData', array($this));
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('tags', _('Tags'), $this->input('tags', _('Tags'),
($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()), ($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()),
// TRANS: Tooltip for field label in form for profile settings.
_('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated')); _('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated'));
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
$language = common_language(); $language = common_language();
// TRANS: Dropdownlist label in form for profile settings.
$this->dropdown('language', _('Language'), $this->dropdown('language', _('Language'),
// TRANS: Tooltip for dropdown list label in form for profile settings.
get_nice_language_list(), _('Preferred language'), get_nice_language_list(), _('Preferred language'),
false, $language); false, $language);
$this->elementEnd('li'); $this->elementEnd('li');
@ -158,12 +181,15 @@ class ProfilesettingsAction extends AccountSettingsAction
$timezones[$v] = $v; $timezones[$v] = $v;
} }
$this->elementStart('li'); $this->elementStart('li');
// TRANS: Dropdownlist label in form for profile settings.
$this->dropdown('timezone', _('Timezone'), $this->dropdown('timezone', _('Timezone'),
// TRANS: Tooltip for dropdown list label in form for profile settings.
$timezones, _('What timezone are you normally in?'), $timezones, _('What timezone are you normally in?'),
true, $timezone); true, $timezone);
$this->elementEnd('li'); $this->elementEnd('li');
$this->elementStart('li'); $this->elementStart('li');
$this->checkbox('autosubscribe', $this->checkbox('autosubscribe',
// TRANS: Checkbox label in form for profile settings.
_('Automatically subscribe to whoever '. _('Automatically subscribe to whoever '.
'subscribes to me (best for non-humans)'), 'subscribes to me (best for non-humans)'),
($this->arg('autosubscribe')) ? ($this->arg('autosubscribe')) ?
@ -171,7 +197,8 @@ class ProfilesettingsAction extends AccountSettingsAction
$this->elementEnd('li'); $this->elementEnd('li');
} }
$this->elementEnd('ul'); $this->elementEnd('ul');
$this->submit('save', _('Save')); // TRANS: Button to save input in profile settings.
$this->submit('save', _m('BUTTON','Save'));
$this->elementEnd('fieldset'); $this->elementEnd('fieldset');
$this->elementEnd('form'); $this->elementEnd('form');
@ -212,33 +239,46 @@ class ProfilesettingsAction extends AccountSettingsAction
if (!Validate::string($nickname, array('min_length' => 1, if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64, 'max_length' => 64,
'format' => NICKNAME_FMT))) { 'format' => NICKNAME_FMT))) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.')); $this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.'));
return; return;
} else if (!User::allowed_nickname($nickname)) { } else if (!User::allowed_nickname($nickname)) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Not a valid nickname.')); $this->showForm(_('Not a valid nickname.'));
return; return;
} else if (!is_null($homepage) && (strlen($homepage) > 0) && } else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) { !Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Homepage is not a valid URL.')); $this->showForm(_('Homepage is not a valid URL.'));
return; return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) { } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
$this->showForm(_('Full name is too long (max 255 chars).')); // TRANS: Validation error in form for profile settings.
$this->showForm(_('Full name is too long (maximum 255 characters).'));
return; return;
} else if (Profile::bioTooLong($bio)) { } else if (Profile::bioTooLong($bio)) {
$this->showForm(sprintf(_('Bio is too long (max %d chars).'), // TRANS: Validation error in form for profile settings.
// TRANS: Plural form is used based on the maximum number of allowed
// TRANS: characters for the biography (%d).
$this->showForm(sprintf(_m('Bio is too long (maximum %d character).',
'Bio is too long (maximum %d characters).',
Profile::maxBio()),
Profile::maxBio())); Profile::maxBio()));
return; return;
} else if (!is_null($location) && mb_strlen($location) > 255) { } else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).')); // TRANS: Validation error in form for profile settings.
$this->showForm(_('Location is too long (maximum 255 characters).'));
return; return;
} else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) { } else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Timezone not selected.')); $this->showForm(_('Timezone not selected.'));
return; return;
} else if ($this->nicknameExists($nickname)) { } else if ($this->nicknameExists($nickname)) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Nickname already in use. Try another one.')); $this->showForm(_('Nickname already in use. Try another one.'));
return; return;
} else if (!is_null($language) && strlen($language) > 50) { } else if (!is_null($language) && strlen($language) > 50) {
$this->showForm(_('Language is too long (max 50 chars).')); // TRANS: Validation error in form for profile settings.
$this->showForm(_('Language is too long (maximum 50 characters).'));
return; return;
} }
@ -250,6 +290,8 @@ class ProfilesettingsAction extends AccountSettingsAction
foreach ($tags as $tag) { foreach ($tags as $tag) {
if (!common_valid_profile_tag($tag)) { if (!common_valid_profile_tag($tag)) {
// TRANS: Validation error in form for profile settings.
// TRANS: %s is an invalid tag.
$this->showForm(sprintf(_('Invalid tag: "%s"'), $tag)); $this->showForm(sprintf(_('Invalid tag: "%s"'), $tag));
return; return;
} }
@ -280,6 +322,7 @@ class ProfilesettingsAction extends AccountSettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
// TRANS: Server error thrown when user profile settings could not be updated.
$this->serverError(_('Couldn\'t update user.')); $this->serverError(_('Couldn\'t update user.'));
return; return;
} else { } else {
@ -303,6 +346,8 @@ class ProfilesettingsAction extends AccountSettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__); common_log_db_error($user, 'UPDATE', __FILE__);
// TRANS: Server error thrown when user profile settings could not be updated to
// TRANS: automatically subscribe to any subscriber.
$this->serverError(_('Couldn\'t update user for autosubscribe.')); $this->serverError(_('Couldn\'t update user for autosubscribe.'));
return; return;
} }
@ -360,6 +405,7 @@ class ProfilesettingsAction extends AccountSettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__); common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__);
// TRANS: Server error thrown when user profile location preference settings could not be updated.
$this->serverError(_('Couldn\'t save location prefs.')); $this->serverError(_('Couldn\'t save location prefs.'));
return; return;
} }
@ -372,6 +418,7 @@ class ProfilesettingsAction extends AccountSettingsAction
if ($result === false) { if ($result === false) {
common_log_db_error($profile, 'UPDATE', __FILE__); common_log_db_error($profile, 'UPDATE', __FILE__);
// TRANS: Server error thrown when user profile settings could not be saved.
$this->serverError(_('Couldn\'t save profile.')); $this->serverError(_('Couldn\'t save profile.'));
return; return;
} }
@ -380,6 +427,7 @@ class ProfilesettingsAction extends AccountSettingsAction
$result = $user->setSelfTags($tags); $result = $user->setSelfTags($tags);
if (!$result) { if (!$result) {
// TRANS: Server error thrown when user profile settings tags could not be saved.
$this->serverError(_('Couldn\'t save tags.')); $this->serverError(_('Couldn\'t save tags.'));
return; return;
} }
@ -388,6 +436,7 @@ class ProfilesettingsAction extends AccountSettingsAction
Event::handle('EndProfileSaveForm', array($this)); Event::handle('EndProfileSaveForm', array($this));
common_broadcast_profile($profile); common_broadcast_profile($profile);
// TRANS: Confirmation shown when user profile settings are saved.
$this->showForm(_('Settings saved.'), true); $this->showForm(_('Settings saved.'), true);
} }

View File

@ -44,7 +44,6 @@ define('TAGS_PER_PAGE', 100);
* @copyright 2008-2009 StatusNet, Inc. * @copyright 2008-2009 StatusNet, Inc.
* @link http://status.net/ * @link http://status.net/
*/ */
class PublictagcloudAction extends Action class PublictagcloudAction extends Action
{ {
function isReadOnly($args) function isReadOnly($args)
@ -54,24 +53,37 @@ class PublictagcloudAction extends Action
function title() function title()
{ {
// TRANS: Title for public tag cloud.
return _('Public tag cloud'); return _('Public tag cloud');
} }
function showPageNotice() function showPageNotice()
{ {
$this->element('p', 'instructions', $this->element('p', 'instructions',
// TRANS: Instructions (more used like an explanation/header).
// TRANS: %s is the StatusNet sitename.
sprintf(_('These are most popular recent tags on %s'), sprintf(_('These are most popular recent tags on %s'),
common_config('site', 'name'))); common_config('site', 'name')));
} }
function showEmptyList() function showEmptyList()
{ {
// TRANS: This message contains a Markdown URL. The link description is between
// TRANS: square brackets, and the link between parentheses. Do not separate "]("
// TRANS: and do not change the URL part.
$message = _('No one has posted a notice with a [hashtag](%%doc.tags%%) yet.') . ' '; $message = _('No one has posted a notice with a [hashtag](%%doc.tags%%) yet.') . ' ';
if (common_logged_in()) { if (common_logged_in()) {
// TRANS: Message shown to a logged in user for the public tag cloud
// TRANS: while no tags exist yet. "One" refers to the non-existing hashtag.
$message .= _('Be the first to post one!'); $message .= _('Be the first to post one!');
} }
else { else {
// TRANS: Message shown to a anonymous user for the public tag cloud
// TRANS: while no tags exist yet. "One" refers to the non-existing hashtag.
// TRANS: This message contains a Markdown URL. The link description is between
// TRANS: square brackets, and the link between parentheses. Do not separate "]("
// TRANS: and do not change the URL part.
$message .= _('Why not [register an account](%%action.register%%) and be the first to post one!'); $message .= _('Why not [register an account](%%action.register%%) and be the first to post one!');
} }

View File

@ -224,14 +224,16 @@ class RegisterAction extends Action
$this->showForm(_('Homepage is not a valid URL.')); $this->showForm(_('Homepage is not a valid URL.'));
return; return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) { } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
$this->showForm(_('Full name is too long (max 255 chars).')); $this->showForm(_('Full name is too long (maximum 255 characters).'));
return; return;
} else if (Profile::bioTooLong($bio)) { } else if (Profile::bioTooLong($bio)) {
$this->showForm(sprintf(_('Bio is too long (max %d chars).'), $this->showForm(sprintf(_m('Bio is too long (maximum %d character).',
'Bio is too long (maximum %d characters).',
Profile::maxBio()),
Profile::maxBio())); Profile::maxBio()));
return; return;
} else if (!is_null($location) && mb_strlen($location) > 255) { } else if (!is_null($location) && mb_strlen($location) > 255) {
$this->showForm(_('Location is too long (max 255 chars).')); $this->showForm(_('Location is too long (maximum 255 characters).'));
return; return;
} else if (strlen($password) < 6) { } else if (strlen($password) < 6) {
$this->showForm(_('Password must be 6 or more characters.')); $this->showForm(_('Password must be 6 or more characters.'));
@ -465,7 +467,12 @@ class RegisterAction extends Action
$this->elementStart('li'); $this->elementStart('li');
$maxBio = Profile::maxBio(); $maxBio = Profile::maxBio();
if ($maxBio > 0) { if ($maxBio > 0) {
$bioInstr = sprintf(_('Describe yourself and your interests in %d chars'), // TRANS: Tooltip for field label in form for profile settings. Plural
// TRANS: is decided by the number of characters available for the
// TRANS: biography (%d).
$bioInstr = sprintf(_m('Describe yourself and your interests in %d character',
'Describe yourself and your interests in %d characters',
$maxBio),
$maxBio); $maxBio);
} else { } else {
$bioInstr = _('Describe yourself and your interests'); $bioInstr = _('Describe yourself and your interests');

View File

@ -227,7 +227,7 @@ class ShowfavoritesAction extends OwnerDesignAction
function showContent() function showContent()
{ {
$nl = new NoticeList($this->notice, $this); $nl = new FavoritesNoticeList($this->notice, $this);
$cnt = $nl->show(); $cnt = $nl->show();
if (0 == $cnt) { if (0 == $cnt) {
@ -244,3 +244,15 @@ class ShowfavoritesAction extends OwnerDesignAction
} }
} }
class FavoritesNoticeList extends NoticeList
{
function newListItem($notice)
{
return new FavoritesNoticeListItem($notice, $this->out);
}
}
// All handled by superclass
class FavoritesNoticeListItem extends DoFollowListItem
{
}

View File

@ -311,7 +311,7 @@ class ShownoticeAction extends OwnerDesignAction
} }
} }
class SingleNoticeItem extends NoticeListItem class SingleNoticeItem extends DoFollowListItem
{ {
/** /**
* recipe function for displaying a single notice. * recipe function for displaying a single notice.

View File

@ -54,7 +54,6 @@ require_once INSTALLDIR.'/lib/feedlist.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ShowstreamAction extends ProfileAction class ShowstreamAction extends ProfileAction
{ {
function isReadOnly($args) function isReadOnly($args)
@ -84,7 +83,6 @@ class ShowstreamAction extends ProfileAction
function handle($args) function handle($args)
{ {
// Looks like we're good; start output // Looks like we're good; start output
// For YADIS discovery, we also have a <meta> tag // For YADIS discovery, we also have a <meta> tag
@ -186,7 +184,6 @@ class ShowstreamAction extends ProfileAction
$this->element('link', array('rel' => 'EditURI', $this->element('link', array('rel' => 'EditURI',
'type' => 'application/rsd+xml', 'type' => 'application/rsd+xml',
'href' => $rsd)); 'href' => $rsd));
} }
function showProfile() function showProfile()
@ -275,7 +272,7 @@ class ProfileNoticeList extends NoticeList
} }
} }
class ProfileNoticeListItem extends NoticeListItem class ProfileNoticeListItem extends DoFollowListItem
{ {
function showAuthor() function showAuthor()
{ {

View File

@ -41,14 +41,17 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class SubscribersAction extends GalleryAction class SubscribersAction extends GalleryAction
{ {
function title() function title()
{ {
if ($this->page == 1) { if ($this->page == 1) {
// TRANS: Header for list of subscribers for a user (first page).
// TRANS: %s is the user's nickname.
return sprintf(_('%s subscribers'), $this->user->nickname); return sprintf(_('%s subscribers'), $this->user->nickname);
} else { } else {
// TRANS: Header for list of subscribers for a user (not first page).
// TRANS: %1$s is the user's nickname, $2$d is the page number.
return sprintf(_('%1$s subscribers, page %2$d'), return sprintf(_('%1$s subscribers, page %2$d'),
$this->user->nickname, $this->user->nickname,
$this->page); $this->page);
@ -60,10 +63,14 @@ class SubscribersAction extends GalleryAction
$user = common_current_user(); $user = common_current_user();
if ($user && ($user->id == $this->profile->id)) { if ($user && ($user->id == $this->profile->id)) {
$this->element('p', null, $this->element('p', null,
// TRANS: Page notice for page with an overview of all subscribers
// TRANS: of the logged in user's own profile.
_('These are the people who listen to '. _('These are the people who listen to '.
'your notices.')); 'your notices.'));
} else { } else {
$this->element('p', null, $this->element('p', null,
// TRANS: Page notice for page with an overview of all subscribers of a user other
// TRANS: than the logged in user. %s is the user nickname.
sprintf(_('These are the people who '. sprintf(_('These are the people who '.
'listen to %s\'s notices.'), 'listen to %s\'s notices.'),
$this->profile->nickname)); $this->profile->nickname));
@ -105,12 +112,20 @@ class SubscribersAction extends GalleryAction
if (common_logged_in()) { if (common_logged_in()) {
$current_user = common_current_user(); $current_user = common_current_user();
if ($this->user->id === $current_user->id) { if ($this->user->id === $current_user->id) {
$message = _('You have no subscribers. Try subscribing to people you know and they might return the favor'); // TRANS: Subscriber list text when the logged in user has no subscribers.
$message = _('You have no subscribers. Try subscribing to people you know and they might return the favor.');
} else { } else {
// TRANS: Subscriber list text when looking at the subscribers for a of a user other
// TRANS: than the logged in user that has no subscribers. %s is the user nickname.
$message = sprintf(_('%s has no subscribers. Want to be the first?'), $this->user->nickname); $message = sprintf(_('%s has no subscribers. Want to be the first?'), $this->user->nickname);
} }
} }
else { else {
// TRANS: Subscriber list text when looking at the subscribers for a of a user that has none
// TRANS: as an anonymous user. %s is the user nickname.
// TRANS: This message contains a Markdown URL. The link description is between
// TRANS: square brackets, and the link between parentheses. Do not separate "]("
// TRANS: and do not change the URL part.
$message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.register%%%%) and be the first?'), $this->user->nickname); $message = sprintf(_('%s has no subscribers. Why not [register an account](%%%%action.register%%%%) and be the first?'), $this->user->nickname);
} }

View File

@ -41,16 +41,17 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
class SubscriptionsAction extends GalleryAction class SubscriptionsAction extends GalleryAction
{ {
function title() function title()
{ {
if ($this->page == 1) { if ($this->page == 1) {
// TRANS: Header for subscriptions overview for a user (first page).
// TRANS: %s is a user nickname.
return sprintf(_('%s subscriptions'), $this->user->nickname); return sprintf(_('%s subscriptions'), $this->user->nickname);
} else { } else {
// TRANS: Header for subscriptions overview for a user (not first page).
// TRANS: %1$s is a user nickname, %2$d is the page number.
return sprintf(_('%1$s subscriptions, page %2$d'), return sprintf(_('%1$s subscriptions, page %2$d'),
$this->user->nickname, $this->user->nickname,
$this->page); $this->page);
@ -62,10 +63,14 @@ class SubscriptionsAction extends GalleryAction
$user = common_current_user(); $user = common_current_user();
if ($user && ($user->id == $this->profile->id)) { if ($user && ($user->id == $this->profile->id)) {
$this->element('p', null, $this->element('p', null,
// TRANS: Page notice for page with an overview of all subscriptions
// TRANS: of the logged in user's own profile.
_('These are the people whose notices '. _('These are the people whose notices '.
'you listen to.')); 'you listen to.'));
} else { } else {
$this->element('p', null, $this->element('p', null,
// TRANS: Page notice for page with an overview of all subscriptions of a user other
// TRANS: than the logged in user. %s is the user nickname.
sprintf(_('These are the people whose '. sprintf(_('These are the people whose '.
'notices %s listens to.'), 'notices %s listens to.'),
$this->profile->nickname)); $this->profile->nickname));
@ -123,12 +128,24 @@ class SubscriptionsAction extends GalleryAction
if (common_logged_in()) { if (common_logged_in()) {
$current_user = common_current_user(); $current_user = common_current_user();
if ($this->user->id === $current_user->id) { if ($this->user->id === $current_user->id) {
$message = _('You\'re not listening to anyone\'s notices right now, try subscribing to people you know. Try [people search](%%action.peoplesearch%%), look for members in groups you\'re interested in and in our [featured users](%%action.featured%%). If you\'re a [Twitter user](%%action.twittersettings%%), you can automatically subscribe to people you already follow there.'); // TRANS: Subscription list text when the logged in user has no subscriptions.
// TRANS: This message contains Markdown URLs. The link description is between
// TRANS: square brackets, and the link between parentheses. Do not separate "]("
// TRANS: and do not change the URL part.
$message = _('You\'re not listening to anyone\'s notices right now, try subscribing to people you know. '.
'Try [people search](%%action.peoplesearch%%), look for members in groups you\'re interested '.
'in and in our [featured users](%%action.featured%%). '.
'If you\'re a [Twitter user](%%action.twittersettings%%), you can automatically subscribe to '.
'people you already follow there.');
} else { } else {
// TRANS: Subscription list text when looking at the subscriptions for a of a user other
// TRANS: than the logged in user that has no subscriptions. %s is the user nickname.
$message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname); $message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname);
} }
} }
else { else {
// TRANS: Subscription list text when looking at the subscriptions for a of a user that has none
// TRANS: as an anonymous user. %s is the user nickname.
$message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname); $message = sprintf(_('%s is not listening to anyone.'), $this->user->nickname);
} }
@ -205,6 +222,7 @@ class SubscriptionsListItem extends SubscriptionListItem
} }
$this->out->element('input', $attrs); $this->out->element('input', $attrs);
// TRANS: Checkbox label for enabling Jabber messages for a profile in a subscriptions list.
$this->out->element('label', array('for' => 'jabber-'.$this->profile->id), _('Jabber')); $this->out->element('label', array('for' => 'jabber-'.$this->profile->id), _('Jabber'));
} else { } else {
$this->out->hidden('jabber', $sub->jabber); $this->out->hidden('jabber', $sub->jabber);
@ -219,11 +237,13 @@ class SubscriptionsListItem extends SubscriptionListItem
} }
$this->out->element('input', $attrs); $this->out->element('input', $attrs);
// TRANS: Checkbox label for enabling SMS messages for a profile in a subscriptions list.
$this->out->element('label', array('for' => 'sms-'.$this->profile->id), _('SMS')); $this->out->element('label', array('for' => 'sms-'.$this->profile->id), _('SMS'));
} else { } else {
$this->out->hidden('sms', $sub->sms); $this->out->hidden('sms', $sub->sms);
} }
$this->out->submit('save', _('Save')); // TRANS: Save button for settings for a profile in a subscriptions list.
$this->out->submit('save', _m('BUTTON','Save'));
$this->out->elementEnd('form'); $this->out->elementEnd('form');
return; return;
} }

View File

@ -139,7 +139,42 @@ class Design extends Memcached_DataObject
static function url($filename) static function url($filename)
{ {
if (StatusNet::isHTTPS()) {
$sslserver = common_config('background', 'sslserver');
if (empty($sslserver)) {
// XXX: this assumes that background dir == site dir + /background/
// not true if there's another server
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') . '/background/';
} else {
$server = $sslserver;
$path = common_config('background', 'sslpath');
if (empty($path)) {
$path = common_config('background', 'path'); $path = common_config('background', 'path');
}
}
$protocol = 'https';
} else {
$path = common_config('background', 'path');
$server = common_config('background', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$protocol = 'http';
}
if ($path[strlen($path)-1] != '/') { if ($path[strlen($path)-1] != '/') {
$path .= '/'; $path .= '/';
@ -149,25 +184,6 @@ class Design extends Memcached_DataObject
$path = '/'.$path; $path = '/'.$path;
} }
$server = common_config('background', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config('background', 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('background', 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
return $protocol.'://'.$server.$path.$filename; return $protocol.'://'.$server.$path.$filename;
} }

View File

@ -261,13 +261,51 @@ class File extends Memcached_DataObject
// TRANS: Client exception thrown if a file upload does not have a valid name. // TRANS: Client exception thrown if a file upload does not have a valid name.
throw new ClientException(_("Invalid filename.")); throw new ClientException(_("Invalid filename."));
} }
if (common_config('site','private')) { if (common_config('site','private')) {
return common_local_url('getfile', return common_local_url('getfile',
array('filename' => $filename)); array('filename' => $filename));
}
if (StatusNet::isHTTPS()) {
$sslserver = common_config('attachments', 'sslserver');
if (empty($sslserver)) {
// XXX: this assumes that background dir == site dir + /file/
// not true if there's another server
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') . '/file/';
} else { } else {
$server = $sslserver;
$path = common_config('attachments', 'sslpath');
if (empty($path)) {
$path = common_config('attachments', 'path'); $path = common_config('attachments', 'path');
}
}
$protocol = 'https';
} else {
$path = common_config('attachments', 'path');
$server = common_config('attachments', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config('attachments', 'ssl');
$protocol = ($ssl) ? 'https' : 'http';
}
if ($path[strlen($path)-1] != '/') { if ($path[strlen($path)-1] != '/') {
$path .= '/'; $path .= '/';
@ -277,28 +315,8 @@ class File extends Memcached_DataObject
$path = '/'.$path; $path = '/'.$path;
} }
$server = common_config('attachments', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config('attachments', 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('attachments', 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
return $protocol.'://'.$server.$path.$filename; return $protocol.'://'.$server.$path.$filename;
} }
}
function getEnclosure(){ function getEnclosure(){
$enclosure = (object) array(); $enclosure = (object) array();

View File

@ -60,7 +60,7 @@ class Notice_tag extends Memcached_DataObject
} }
if ($max_id != 0) { if ($max_id != 0) {
$nt->whereAdd('notice_id < ' . $max_id); $nt->whereAdd('notice_id <= ' . $max_id);
} }
$nt->orderBy('notice_id DESC'); $nt->orderBy('notice_id DESC');

View File

@ -24,20 +24,51 @@ class Oauth_application_user extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###END_AUTOCODE
static function getByKeys($user, $app) static function getByUserAndToken($user, $token)
{ {
if (empty($user) || empty($app)) { if (empty($user) || empty($token)) {
return null; return null;
} }
$oau = new Oauth_application_user(); $oau = new Oauth_application_user();
$oau->profile_id = $user->id; $oau->profile_id = $user->id;
$oau->application_id = $app->id; $oau->token = $token;
$oau->limit(1); $oau->limit(1);
$result = $oau->find(true); $result = $oau->find(true);
return empty($result) ? null : $oau; return empty($result) ? null : $oau;
} }
function updateKeys(&$orig)
{
$this->_connect();
$parts = array();
foreach (array('profile_id', 'application_id', 'token', 'access_type') as $k) {
if (strcmp($this->$k, $orig->$k) != 0) {
$parts[] = $k . ' = ' . $this->_quote($this->$k);
}
}
if (count($parts) == 0) {
# No changes
return true;
}
$toupdate = implode(', ', $parts);
$table = $this->tableName();
if(common_config('db','quote_identifiers')) {
$table = '"' . $table . '"';
}
$qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
' WHERE profile_id = ' . $orig->profile_id
. ' AND application_id = ' . $orig->application_id
. " AND token = '$orig->token'";
$orig->decache();
$result = $this->query($qry);
if ($result) {
$this->encache();
}
return $result;
}
} }

View File

@ -0,0 +1,42 @@
<?php
/**
* Table Definition for oauth_association
*/
require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
class Oauth_token_association extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'oauth_token_association'; // table name
public $profile_id; // int(4) primary_key not_null
public $application_id; // int(4) primary_key not_null
public $token; // varchar(255) primary key not null
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_token_association', $k, $v);
}
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
static function getByUserAndToken($user, $token)
{
if (empty($user) || empty($token)) {
return null;
}
$oau = new oauth_request_token();
$oau->profile_id = $user->id;
$oau->token = $token;
$oau->limit(1);
$result = $oau->find(true);
return empty($result) ? null : $oau;
}
}

View File

@ -199,7 +199,7 @@ class Profile extends Memcached_DataObject
} }
if ($max_id != 0) { if ($max_id != 0) {
$query .= " and id < $max_id"; $query .= " and id <= $max_id";
} }
$query .= ' order by id DESC'; $query .= ' order by id DESC';
@ -240,7 +240,7 @@ class Profile extends Memcached_DataObject
} }
if ($max_id != 0) { if ($max_id != 0) {
$query .= " and id < $max_id"; $query .= " and id <= $max_id";
} }
$query .= ' order by id DESC'; $query .= ' order by id DESC';
@ -401,10 +401,10 @@ class Profile extends Memcached_DataObject
return $profile; return $profile;
} }
function getApplications($offset = 0, $limit = null) function getConnectedApps($offset = 0, $limit = null)
{ {
$qry = $qry =
'SELECT a.* ' . 'SELECT u.* ' .
'FROM oauth_application_user u, oauth_application a ' . 'FROM oauth_application_user u, oauth_application a ' .
'WHERE u.profile_id = %d ' . 'WHERE u.profile_id = %d ' .
'AND a.id = u.application_id ' . 'AND a.id = u.application_id ' .
@ -419,11 +419,11 @@ class Profile extends Memcached_DataObject
} }
} }
$application = new Oauth_application(); $apps = new Oauth_application_user();
$cnt = $application->query(sprintf($qry, $this->id)); $cnt = $apps->query(sprintf($qry, $this->id));
return $application; return $apps;
} }
function subscriptionCount() function subscriptionCount()
@ -758,6 +758,8 @@ class Profile extends Memcached_DataObject
function grantRole($name) function grantRole($name)
{ {
if (Event::handle('StartGrantRole', array($this, $name))) {
$role = new Profile_role(); $role = new Profile_role();
$role->profile_id = $this->id; $role->profile_id = $this->id;
@ -767,15 +769,19 @@ class Profile extends Memcached_DataObject
$result = $role->insert(); $result = $role->insert();
if (!$result) { if (!$result) {
common_log_db_error($role, 'INSERT', __FILE__); throw new Exception("Can't save role '$name' for profile '{$this->id}'");
return false;
} }
return true; Event::handle('EndGrantRole', array($this, $name));
}
return $result;
} }
function revokeRole($name) function revokeRole($name)
{ {
if (Event::handle('StartRevokeRole', array($this, $name))) {
$role = Profile_role::pkeyGet(array('profile_id' => $this->id, $role = Profile_role::pkeyGet(array('profile_id' => $this->id,
'role' => $name)); 'role' => $name));
@ -794,8 +800,11 @@ class Profile extends Memcached_DataObject
throw new Exception(sprintf(_('Cannot revoke role "%1$s" for user #%2$d; database error.'),$name, $this->id)); throw new Exception(sprintf(_('Cannot revoke role "%1$s" for user #%2$d; database error.'),$name, $this->id));
} }
Event::handle('EndRevokeRole', array($this, $name));
return true; return true;
} }
}
function isSandboxed() function isSandboxed()
{ {

View File

@ -55,7 +55,7 @@ class Reply extends Memcached_DataObject
} }
if ($max_id != 0) { if ($max_id != 0) {
$reply->whereAdd('notice_id < ' . $max_id); $reply->whereAdd('notice_id <= ' . $max_id);
} }
$reply->orderBy('notice_id DESC'); $reply->orderBy('notice_id DESC');

View File

@ -43,7 +43,6 @@ class Status_network_tag extends Safe_DataObject
$this->_connect(); $this->_connect();
} }
/* Static get */ /* Static get */
function staticGet($k,$v=null) function staticGet($k,$v=null)
{ {
@ -99,7 +98,7 @@ class Status_network_tag extends Safe_DataObject
if (Status_network::$cache) { if (Status_network::$cache) {
$packed = implode('|', $result); $packed = implode('|', $result);
Status_network::$cache->set($key, $packed, 3600); Status_network::$cache->set($key, $packed, 0, 3600);
} }
return $result; return $result;

View File

@ -875,4 +875,35 @@ class User extends Memcached_DataObject
return $owner; return $owner;
} }
/**
* Pull the primary site account to use in single-user mode.
* If a valid user nickname is listed in 'singleuser':'nickname'
* in the config, this will be used; otherwise the site owner
* account is taken by default.
*
* @return User
* @throws ServerException if no valid single user account is present
* @throws ServerException if called when not in single-user mode
*/
static function singleUser()
{
if (common_config('singleuser', 'enabled')) {
$nickname = common_config('singleuser', 'nickname');
if ($nickname) {
$user = User::staticGet('nickname', $nickname);
} else {
$user = User::siteOwner();
}
if ($user) {
return $user;
} else {
// TRANS: Server exception.
throw new ServerException(_('No single user defined for single-user mode.'));
}
} else {
// TRANS: Server exception.
throw new ServerException(_('Single-user mode code called when not enabled.'));
}
}
} }

View File

@ -559,16 +559,27 @@ class User_group extends Memcached_DataObject
function delete() function delete()
{ {
if ($this->id) { if ($this->id) {
// Safe to delete in bulk for now // Safe to delete in bulk for now
$related = array('Group_inbox', $related = array('Group_inbox',
'Group_block', 'Group_block',
'Group_member', 'Group_member',
'Related_group'); 'Related_group');
Event::handle('UserGroupDeleteRelated', array($this, &$related)); Event::handle('UserGroupDeleteRelated', array($this, &$related));
foreach ($related as $cls) { foreach ($related as $cls) {
$inst = new $cls(); $inst = new $cls();
$inst->group_id = $this->id; $inst->group_id = $this->id;
$inst->delete();
if ($inst->find()) {
while ($inst->fetch()) {
$dup = clone($inst);
$dup->delete();
}
}
} }
// And related groups in the other direction... // And related groups in the other direction...
@ -584,6 +595,10 @@ class User_group extends Memcached_DataObject
if ($local) { if ($local) {
$local->delete(); $local->delete();
} }
// blow the cached ids
self::blow('user_group:notice_ids:%d', $this->id);
} else { } else {
common_log(LOG_WARN, "Ambiguous user_group->delete(); skipping related tables."); common_log(LOG_WARN, "Ambiguous user_group->delete(); skipping related tables.");
} }

View File

@ -401,6 +401,18 @@ modified = 384
profile_id = K profile_id = K
application_id = K application_id = K
[oauth_token_association]
profile_id = 129
application_id = 129
token = 130
created = 142
modified = 384
[oauth_token_association__keys]
profile_id = K
application_id = K
token = K
[profile] [profile]
id = 129 id = 129
nickname = 130 nickname = 130

View File

@ -34,7 +34,7 @@ VALUES
('mbpidgin','mbpidgin','http://code.google.com/p/microblog-purple/', now()), ('mbpidgin','mbpidgin','http://code.google.com/p/microblog-purple/', now()),
('Mobidentica', 'Mobidentica', 'http://www.substanceofcode.com/software/mobidentica/', now()), ('Mobidentica', 'Mobidentica', 'http://www.substanceofcode.com/software/mobidentica/', now()),
('moconica','Moconica','http://moconica.com/', now()), ('moconica','Moconica','http://moconica.com/', now()),
('mustard', 'MuSTArDroid', 'https://launchpad.net/mustardroid', now()), ('mustard', 'mustard', 'http://mustard.macno.org', now()),
('nambu','Nambu','http://www.nambu.com/', now()), ('nambu','Nambu','http://www.nambu.com/', now()),
('peoplebrowsr', 'PeopleBrowsr', 'http://www.peoplebrowsr.com/', now()), ('peoplebrowsr', 'PeopleBrowsr', 'http://www.peoplebrowsr.com/', now()),
('Pikchur','Pikchur','http://www.pikchur.com/', now()), ('Pikchur','Pikchur','http://www.pikchur.com/', now()),

View File

@ -237,6 +237,15 @@ create table oauth_application_user (
constraint primary key (profile_id, application_id) constraint primary key (profile_id, application_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table oauth_token_association (
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),
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, token)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
/* These are used by JanRain OpenID library */ /* These are used by JanRain OpenID library */
create table oid_associations ( create table oid_associations (

View File

@ -45,13 +45,61 @@ require INSTALLDIR . '/lib/installer.php';
* Helper class for building form * Helper class for building form
*/ */
class Posted { class Posted {
/**
* HTML-friendly escaped string for the POST param of given name, or empty.
* @param string $name
* @return string
*/
function value($name) function value($name)
{ {
if (isset($_POST[$name])) { return htmlspecialchars($this->string($name));
return htmlspecialchars(strval($_POST[$name]));
} else {
return '';
} }
/**
* The given POST parameter value, forced to a string.
* Missing value will give ''.
*
* @param string $name
* @return string
*/
function string($name)
{
return strval($this->raw($name));
}
/**
* The given POST parameter value, in its original form.
* Magic quotes are stripped, if provided.
* Missing value will give null.
*
* @param string $name
* @return mixed
*/
function raw($name)
{
if (isset($_POST[$name])) {
return $this->dequote($_POST[$name]);
} else {
return null;
}
}
/**
* If necessary, strip magic quotes from the given value.
*
* @param mixed $val
* @return mixed
*/
function dequote($val)
{
if (get_magic_quotes_gpc()) {
if (is_string($val)) {
return stripslashes($val);
} else if (is_array($val)) {
return array_map(array($this, 'dequote'), $val);
}
}
return $val;
} }
} }
@ -107,11 +155,7 @@ class WebInstaller extends Installer
global $dbModules; global $dbModules;
$post = new Posted(); $post = new Posted();
$dbRadios = ''; $dbRadios = '';
if (isset($_POST['dbtype'])) { $dbtype = $post->raw('dbtype');
$dbtype = $_POST['dbtype'];
} else {
$dbtype = null;
}
foreach (self::$dbModules as $type => $info) { foreach (self::$dbModules as $type => $info) {
if ($this->checkExtension($info['check_module'])) { if ($this->checkExtension($info['check_module'])) {
if ($dbtype == null || $dbtype == $type) { if ($dbtype == null || $dbtype == $type) {
@ -245,19 +289,20 @@ STR;
*/ */
function prepare() function prepare()
{ {
$this->host = $_POST['host']; $post = new Posted();
$this->dbtype = $_POST['dbtype']; $this->host = $post->string('host');
$this->database = $_POST['database']; $this->dbtype = $post->string('dbtype');
$this->username = $_POST['dbusername']; $this->database = $post->string('database');
$this->password = $_POST['dbpassword']; $this->username = $post->string('dbusername');
$this->sitename = $_POST['sitename']; $this->password = $post->string('dbpassword');
$this->fancy = !empty($_POST['fancy']); $this->sitename = $post->string('sitename');
$this->fancy = (bool)$post->string('fancy');
$this->adminNick = strtolower($_POST['admin_nickname']); $this->adminNick = strtolower($post->string('admin_nickname'));
$this->adminPass = $_POST['admin_password']; $this->adminPass = $post->string('admin_password');
$adminPass2 = $_POST['admin_password2']; $adminPass2 = $post->string('admin_password2');
$this->adminEmail = $_POST['admin_email']; $this->adminEmail = $post->string('admin_email');
$this->adminUpdates = $_POST['admin_updates']; $this->adminUpdates = $post->string('admin_updates');
$this->server = $_SERVER['HTTP_HOST']; $this->server = $_SERVER['HTTP_HOST'];
$this->path = substr(dirname($_SERVER['PHP_SELF']), 1); $this->path = substr(dirname($_SERVER['PHP_SELF']), 1);

View File

@ -175,8 +175,9 @@ class Action extends HTMLOutputter // lawsuit
$this->element('link', array('rel' => 'shortcut icon', $this->element('link', array('rel' => 'shortcut icon',
'href' => Theme::path('favicon.ico'))); 'href' => Theme::path('favicon.ico')));
} else { } else {
// favicon.ico should be HTTPS if the rest of the page is
$this->element('link', array('rel' => 'shortcut icon', $this->element('link', array('rel' => 'shortcut icon',
'href' => common_path('favicon.ico'))); 'href' => common_path('favicon.ico', StatusNet::isHTTPS())));
} }
if (common_config('site', 'mobile')) { if (common_config('site', 'mobile')) {
@ -361,9 +362,9 @@ class Action extends HTMLOutputter // lawsuit
*/ */
function showBody() 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') 'class' => 'user_in')
: array('id' => $this->trimmed('action'))); : array('id' => strtolower($this->trimmed('action'))));
$this->elementStart('div', array('id' => 'wrap')); $this->elementStart('div', array('id' => 'wrap'));
if (Event::handle('StartShowHeader', array($this))) { if (Event::handle('StartShowHeader', array($this))) {
$this->showHeader(); $this->showHeader();
@ -397,7 +398,10 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowSiteNotice', array($this)); Event::handle('EndShowSiteNotice', array($this));
} }
if (common_logged_in()) { if (common_logged_in()) {
if (Event::handle('StartShowNoticeForm', array($this))) {
$this->showNoticeForm(); $this->showNoticeForm();
Event::handle('EndShowNoticeForm', array($this));
}
} else { } else {
$this->showAnonymousMessage(); $this->showAnonymousMessage();
} }
@ -415,18 +419,43 @@ class Action extends HTMLOutputter // lawsuit
'class' => 'vcard')); 'class' => 'vcard'));
if (Event::handle('StartAddressData', array($this))) { if (Event::handle('StartAddressData', array($this))) {
if (common_config('singleuser', 'enabled')) { if (common_config('singleuser', 'enabled')) {
$user = User::singleUser();
$url = common_local_url('showstream', $url = common_local_url('showstream',
array('nickname' => common_config('singleuser', 'nickname'))); array('nickname' => $user->nickname));
} else { } else {
$url = common_local_url('public'); $url = common_local_url('public');
} }
$this->elementStart('a', array('class' => 'url home bookmark', $this->elementStart('a', array('class' => 'url home bookmark',
'href' => $url)); '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', $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'))); 'alt' => common_config('site', 'name')));
} }
$this->text(' '); $this->text(' ');
$this->element('span', array('class' => 'fn org'), common_config('site', 'name')); $this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
$this->elementEnd('a'); $this->elementEnd('a');
@ -498,20 +527,20 @@ class Action extends HTMLOutputter // lawsuit
} }
// TRANS: Tooltip for main menu option "Login" // TRANS: Tooltip for main menu option "Login"
$tooltip = _m('TOOLTIP', 'Login to the site'); $tooltip = _m('TOOLTIP', 'Login to the site');
// TRANS: Main menu option when not logged in to log in
$this->menuItem(common_local_url('login'), $this->menuItem(common_local_url('login'),
// TRANS: Main menu option when not logged in to log in
_m('MENU', 'Login'), $tooltip, false, 'nav_login'); _m('MENU', 'Login'), $tooltip, false, 'nav_login');
} }
// TRANS: Tooltip for main menu option "Help" // TRANS: Tooltip for main menu option "Help"
$tooltip = _m('TOOLTIP', 'Help me!'); $tooltip = _m('TOOLTIP', 'Help me!');
// TRANS: Main menu option for help on the StatusNet site
$this->menuItem(common_local_url('doc', array('title' => 'help')), $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'); _m('MENU', 'Help'), $tooltip, false, 'nav_help');
if ($user || !common_config('site', 'private')) { if ($user || !common_config('site', 'private')) {
// TRANS: Tooltip for main menu option "Search" // TRANS: Tooltip for main menu option "Search"
$tooltip = _m('TOOLTIP', 'Search for people or text'); $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'), $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'); _m('MENU', 'Search'), $tooltip, false, 'nav_search');
} }
Event::handle('EndPrimaryNav', array($this)); Event::handle('EndPrimaryNav', array($this));
@ -891,8 +920,26 @@ class Action extends HTMLOutputter // lawsuit
case 'cc': // fall through case 'cc': // fall through
default: default:
$this->elementStart('p'); $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', $this->element('img', array('id' => 'license_cc',
'src' => common_config('license', 'image'), 'src' => $url,
'alt' => common_config('license', 'title'), 'alt' => common_config('license', 'title'),
'width' => '80', 'width' => '80',
'height' => '15')); 'height' => '15'));

View File

@ -1398,8 +1398,10 @@ class ApiAction extends Action
if (is_numeric($this->arg('id'))) { if (is_numeric($this->arg('id'))) {
return Profile::staticGet($this->arg('id')); return Profile::staticGet($this->arg('id'));
} else if ($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')); $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')) { } else if ($this->arg('user_id')) {
// This is to ensure that a non-numeric user_id still // This is to ensure that a non-numeric user_id still
// overrides screen_name even if it doesn't get used // overrides screen_name even if it doesn't get used
@ -1408,13 +1410,15 @@ class ApiAction extends Action
} }
} else if ($this->arg('screen_name')) { } else if ($this->arg('screen_name')) {
$nickname = common_canonical_nickname($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)) { } else if (is_numeric($id)) {
return Profile::staticGet($id); return Profile::staticGet($id);
} else { } else {
$nickname = common_canonical_nickname($id); $nickname = common_canonical_nickname($id);
return Profile::staticGet('nickname', $nickname); $user = User::staticGet('nickname', $nickname);
return $user ? $user->getProfile() : null;
} }
} }

View File

@ -168,16 +168,20 @@ class ApiAuthAction extends ApiAction
$app = Oauth_application::getByConsumerKey($consumer); $app = Oauth_application::getByConsumerKey($consumer);
if (empty($app)) { if (empty($app)) {
common_log(LOG_WARNING, common_log(
'Couldn\'t find the OAuth app for consumer key: ' . LOG_WARNING,
$consumer); '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. // TRANS: OAuth exception thrown when no application is found for a given consumer key.
throw new OAuthException(_('No application for that consumer key.')); throw new OAuthException(_('No application for that consumer key.'));
} }
// set the source attr // set the source attr
if ($app->name != 'anonymous') {
$this->source = $app->name; $this->source = $app->name;
}
$appUser = Oauth_application_user::staticGet('token', $access_token); $appUser = Oauth_application_user::staticGet('token', $access_token);
@ -199,14 +203,17 @@ class ApiAuthAction extends ApiAction
$msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " . $msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " .
"application '%s' (id: %d) with %s access."; "application '%s' (id: %d) with %s access.";
common_log(LOG_INFO, sprintf($msg, common_log(
LOG_INFO,
sprintf(
$msg,
$this->auth_user->nickname, $this->auth_user->nickname,
$this->auth_user->id, $this->auth_user->id,
$app->name, $app->name,
$app->id, $app->id,
($this->access = self::READ_WRITE) ? ($this->access = self::READ_WRITE) ? 'read-write' : 'read-only'
'read-write' : 'read-only' )
)); );
} else { } else {
// TRANS: OAuth exception given when an incorrect access token was given for a user. // TRANS: OAuth exception given when an incorrect access token was given for a user.
throw new OAuthException(_('Bad access token.')); throw new OAuthException(_('Bad access token.'));
@ -218,6 +225,7 @@ class ApiAuthAction extends ApiAction
} }
} catch (OAuthException $e) { } catch (OAuthException $e) {
$this->logAuthFailure($e->getMessage());
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
$this->clientError($e->getMessage(), 401, $this->format); $this->clientError($e->getMessage(), 401, $this->format);
exit; exit;
@ -255,7 +263,7 @@ class ApiAuthAction extends ApiAction
// show error if the user clicks 'cancel' // show error if the user clicks 'cancel'
// TRANS: Client error thrown when authentication fails becaus a user clicked "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; exit;
} else { } else {
@ -276,18 +284,13 @@ class ApiAuthAction extends ApiAction
$this->access = self::READ_WRITE; $this->access = self::READ_WRITE;
if (empty($this->auth_user) && ($required || isset($_SERVER['PHP_AUTH_USER']))) { if (empty($this->auth_user) && ($required || isset($_SERVER['PHP_AUTH_USER']))) {
$msg = sprintf(
// basic authentication failed "basic auth nickname = %s",
list($proxy, $ip) = common_client_ip(); $this->auth_user_nickname
);
$msg = sprintf( 'Failed API auth attempt, nickname = %1$s, ' . $this->logAuthFailure($msg);
'proxy = %2$s, ip = %3$s',
$this->auth_user_nickname,
$proxy,
$ip);
common_log(LOG_WARNING, $msg);
// TRANS: Client error thrown when authentication fails. // 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; 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);
}
} }

View File

@ -23,24 +23,71 @@ require_once INSTALLDIR . '/lib/oauthstore.php';
class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore 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) { 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.'));
} }
return new OAuthConsumer($con->consumer_key, $app = Oauth_application::getByConsumerKey('anonymous');
$con->consumer_secret);
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
);
} }
function getAppByRequestToken($token_key) function getAppByRequestToken($token_key)
{ {
// Look up the full req tokenx // Look up the full req token
$req_token = $this->lookup_token(null, $req_token = $this->lookup_token(
null,
'request', 'request',
$token_key); $token_key
);
if (empty($req_token)) { if (empty($req_token)) {
common_debug("couldn't get request token from oauth datastore"); common_debug("couldn't get request token from oauth datastore");
@ -58,7 +105,6 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
} }
// Look up the app // Look up the app
$app = new Oauth_application(); $app = new Oauth_application();
$app->consumer_key = $token->consumer_key; $app->consumer_key = $token->consumer_key;
$result = $app->find(true); $result = $app->find(true);
@ -74,7 +120,12 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
function new_access_token($token, $consumer, $verifier) function new_access_token($token, $consumer, $verifier)
{ {
common_debug( common_debug(
'new_access_token("' . $token->key . '","' . $consumer->key. '","' . $verifier . '")', sprintf(
"New access token from request token %s, consumer %s and verifier %s ",
$token,
$consumer,
$verifier
),
__FILE__ __FILE__
); );
@ -89,34 +140,76 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized 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 = new Oauth_application_user();
$appUser->application_id = $app->id; $appUser->application_id = $app->id;
$appUser->token = $rt->tok; $appUser->profile_id = $tokenAssoc->profile_id;
$result = $appUser->find(true); $result = $appUser->find(true);
if (!empty($result)) { 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 = new Token();
// 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.')
);
}
// Yay, we can re-issue the access token
return new OAuthToken($at->tok, $at->secret);
} else {
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->consumer_key = $consumer->key;
$at->tok = common_good_rand(16); $at->tok = common_good_rand(16);
$at->secret = common_good_rand(16); $at->secret = common_good_rand(16);
$at->type = 1; // access $at->type = 1; // access
$at->verifier = $verifier; $at->verifier = $verifier;
$at->verified_callback = $rt->verified_callback; // 1.0a $at->verified_callback = $rt->verified_callback; // 1.0a
$at->created = DB_DataObject_Cast::dateTime(); $at->created = common_sql_now();
if (!$at->insert()) { if (!$at->insert()) {
$e = $at->_lastError; $e = $at->_lastError;
@ -131,31 +224,39 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
return null; return null;
} }
common_debug('request token "' . $rt->tok . '" updated', __FILE__); common_debug('request token "' . $rt->tok . '" updated', __FILE__);
}
// update the token from req to access for the user // insert a new Oauth_application_user record w/access token
$appUser = new Oauth_application_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->profile_id = $tokenAssoc->profile_id;;
$appUser->application_id = $app->id;
$appUser->access_type = $app->access_type; $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)) { if (!$result) {
common_debug('couldn\'t update OAuth app user.'); common_log_db_error($appUser, 'INSERT', __FILE__);
return null; // TRANS: Server error displayed when a database error occurs.
$this->serverError(_('Database error inserting OAuth application user.'));
} }
// Okay, good // Okay, good
return new OAuthToken($at->tok, $at->secret); return new OAuthToken($at->tok, $at->secret);
} }
} else { } 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; return null;
} }
} }
@ -198,7 +299,6 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
* *
* @return OAuthToken $token a new unauthorized OAuth request token * @return OAuthToken $token a new unauthorized OAuth request token
*/ */
function new_request_token($consumer, $callback) function new_request_token($consumer, $callback)
{ {
$t = new Token(); $t = new Token();
@ -223,6 +323,4 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
return new OAuthToken($t->tok, $t->secret); return new OAuthToken($t->tok, $t->secret);
} }
} }
} }

View File

@ -22,7 +22,7 @@
* @category Application * @category Application
* @package StatusNet * @package StatusNet
* @author Zach Copley <zach@status.net> * @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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -55,14 +55,13 @@ class ApplicationList extends Widget
/** Action object using us. */ /** Action object using us. */
var $action = null; var $action = null;
function __construct($application, $owner=null, $action=null, $connections = false) function __construct($application, $owner=null, $action=null)
{ {
parent::__construct($action); parent::__construct($action);
$this->application = $application; $this->application = $application;
$this->owner = $owner; $this->owner = $owner;
$this->action = $action; $this->action = $action;
$this->connections = $connections;
} }
function show() function show()
@ -88,24 +87,34 @@ class ApplicationList extends Widget
{ {
$user = common_current_user(); $user = common_current_user();
$this->out->elementStart('li', array('class' => 'application', $this->out->elementStart(
'id' => 'oauthclient-' . $this->application->id)); 'li',
array(
'class' => 'application',
'id' => 'oauthclient-' . $this->application->id
)
);
$this->out->elementStart('span', 'vcard author'); $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(
$this->out->elementStart('a', array('href' => $this->application->source_url, 'a',
'class' => 'url')); array(
} 'href' => common_local_url(
'showapplication',
array('id' => $this->application->id)),
'class' => 'url'
)
);
if (!empty($this->application->icon)) { if (!empty($this->application->icon)) {
$this->out->element('img', array('src' => $this->application->icon, $this->out->element(
'class' => 'photo avatar')); 'img',
array(
'src' => $this->application->icon,
'class' => 'photo avatar'
)
);
} }
$this->out->element('span', 'fn', $this->application->name); $this->out->element('span', 'fn', $this->application->name);
@ -114,51 +123,18 @@ class ApplicationList extends Widget
$this->out->raw(' by '); $this->out->raw(' by ');
$this->out->element('a', array('href' => $this->application->homepage, $this->out->element(
'class' => 'url'), 'a',
$this->application->organization); array(
'href' => $this->application->homepage,
'class' => 'url'
),
$this->application->organization
);
$this->out->element('p', 'note', $this->application->description); $this->out->element('p', 'note', $this->application->description);
$this->out->elementEnd('li'); $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');
// 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');
}
} }
/* Override this in subclasses. */ /* Override this in subclasses. */
@ -167,3 +143,162 @@ class ApplicationList extends Widget
return; return;
} }
} }
/**
* 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. */
function showOwnerControls()
{
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');
}
}

View File

@ -22,10 +22,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
//exit with 200 response, if this is checking fancy from the installer //exit with 200 response, if this is checking fancy from the installer
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; } 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('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_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48); define('AVATAR_STREAM_SIZE', 48);

View File

@ -47,7 +47,6 @@ require_once INSTALLDIR.'/lib/servererroraction.php';
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class DBErrorAction extends ServerErrorAction class DBErrorAction extends ServerErrorAction
{ {
function __construct($message='Error', $code=500) function __construct($message='Error', $code=500)

View File

@ -37,6 +37,7 @@ $default =
'path' => $_path, 'path' => $_path,
'logfile' => null, 'logfile' => null,
'logo' => null, 'logo' => null,
'ssllogo' => null,
'logdebug' => false, 'logdebug' => false,
'fancy' => false, 'fancy' => false,
'locale_path' => INSTALLDIR.'/locale', 'locale_path' => INSTALLDIR.'/locale',
@ -210,6 +211,8 @@ $default =
array('server' => null, array('server' => null,
'dir' => INSTALLDIR . '/file/', 'dir' => INSTALLDIR . '/file/',
'path' => $_path . '/file/', 'path' => $_path . '/file/',
'sslserver' => null,
'sslpath' => null,
'ssl' => null, 'ssl' => null,
'supported' => array('image/png', 'supported' => array('image/png',
'image/jpeg', 'image/jpeg',
@ -314,7 +317,8 @@ $default =
'nofollow' => 'nofollow' =>
array('subscribers' => true, array('subscribers' => true,
'members' => true, 'members' => true,
'peopletag' => true), 'peopletag' => true,
'external' => 'sometimes'), // Options: 'sometimes', 'never', default = 'sometimes'
'http' => // HTTP client settings when contacting other sites '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') 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.) 'curl' => false, // Use CURL backend for HTTP fetches if available. (If not, PHP's socket streams will be used.)

88
lib/dofollowlistitem.php Normal file
View File

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

View File

@ -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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class Feed class Feed
{ {
const RSS1 = 1; const RSS1 = 1;

View File

@ -46,7 +46,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* *
* @see Action::showExportList() * @see Action::showExportList()
*/ */
class FeedList extends Widget class FeedList extends Widget
{ {
var $action = null; var $action = null;

View File

@ -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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class GroupsByMembersSection extends GroupSection class GroupsByMembersSection extends GroupSection
{ {
function getGroups() function getGroups()
@ -68,6 +67,7 @@ class GroupsByMembersSection extends GroupSection
function title() function title()
{ {
// TRANS: Title for groups with the most members section.
return _('Groups with most members'); return _('Groups with most members');
} }

View File

@ -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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class GroupsByPostsSection extends GroupSection class GroupsByPostsSection extends GroupSection
{ {
function getGroups() function getGroups()
@ -68,6 +67,7 @@ class GroupsByPostsSection extends GroupSection
function title() function title()
{ {
// TRANS: Title for groups with the most posts section.
return _('Groups with most posts'); return _('Groups with most posts');
} }

View File

@ -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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class GroupSection extends Section class GroupSection extends Section
{ {
function showContent() function showContent()

View File

@ -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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class GroupTagCloudSection extends TagCloudSection class GroupTagCloudSection extends TagCloudSection
{ {
var $group = null; var $group = null;
@ -53,6 +52,8 @@ class GroupTagCloudSection extends TagCloudSection
function title() 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); return sprintf(_('Tags in %s group\'s notices'), $this->group->nickname);
} }

View File

@ -356,11 +356,37 @@ class HTMLOutputter extends XMLOutputter
$url = parse_url($src); $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) { 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 {
if (StatusNet::isHTTPS()) {
$sslserver = common_config('javascript', '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') . '/js/';
} else {
$server = $sslserver;
$path = common_config('javascript', 'sslpath');
if (empty($path)) {
$path = common_config('javascript', 'path');
}
}
$protocol = 'https';
} else { } else {
@ -370,6 +396,15 @@ class HTMLOutputter extends XMLOutputter
$path = common_config('site', 'path') . '/js/'; $path = common_config('site', 'path') . '/js/';
} }
$server = common_config('javascript', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$protocol = 'http';
}
if ($path[strlen($path)-1] != '/') { if ($path[strlen($path)-1] != '/') {
$path .= '/'; $path .= '/';
} }
@ -378,25 +413,6 @@ class HTMLOutputter extends XMLOutputter
$path = '/'.$path; $path = '/'.$path;
} }
$server = common_config('javascript', 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config('javascript', 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config('javascript', 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
$src = $protocol.'://'.$server.$path.$src . '?version=' . STATUSNET_VERSION; $src = $protocol.'://'.$server.$path.$src . '?version=' . STATUSNET_VERSION;
} }
} }
@ -453,7 +469,7 @@ class HTMLOutputter extends XMLOutputter
if(file_exists(Theme::file($src,$theme))){ if(file_exists(Theme::file($src,$theme))){
$src = Theme::path($src, $theme); $src = Theme::path($src, $theme);
}else{ }else{
$src = common_path($src); $src = common_path($src, StatusNet::isHTTPS());
} }
$src.= '?version=' . STATUSNET_VERSION; $src.= '?version=' . STATUSNET_VERSION;
} }

View File

@ -391,6 +391,30 @@ abstract class Installer
return $db; return $db;
} }
/**
* 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. * Write a stock configuration file.
* *
@ -400,24 +424,32 @@ abstract class Installer
*/ */
function writeConf() 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 // assemble configuration file in a string
$cfg = "<?php\n". $cfg = "<?php\n".
"if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n". "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
// site name // site name
"\$config['site']['name'] = '{$this->sitename}';\n\n". "\$config['site']['name'] = {$vals['sitename']};\n\n".
// site location // site location
"\$config['site']['server'] = '{$this->server}';\n". "\$config['site']['server'] = {$vals['server']};\n".
"\$config['site']['path'] = '{$this->path}'; \n\n". "\$config['site']['path'] = {$vals['path']}; \n\n".
// checks if fancy URLs are enabled // checks if fancy URLs are enabled
($this->fancy ? "\$config['site']['fancy'] = true;\n\n":''). ($this->fancy ? "\$config['site']['fancy'] = true;\n\n":'').
// database // 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":''). ($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 // Normalize line endings for Windows servers
$cfg = str_replace("\n", PHP_EOL, $cfg); $cfg = str_replace("\n", PHP_EOL, $cfg);

View File

@ -170,19 +170,21 @@ function mail_to_user(&$user, $subject, $body, $headers=array(), $address=null)
function mail_confirm_address($user, $code, $nickname, $address) function mail_confirm_address($user, $code, $nickname, $address)
{ {
// TRANS: Subject for address confirmation email // TRANS: Subject for address confirmation email.
$subject = _('Email address confirmation'); $subject = _('Email address confirmation');
// TRANS: Body for address confirmation email. // TRANS: Body for address confirmation email.
$body = sprintf(_("Hey, %s.\n\n". // TRANS: %1$s is the addressed user's nickname, %2$s is the StatusNet sitename,
"Someone just entered this email address on %s.\n\n" . // 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, ". "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". "If not, just ignore this message.\n\n".
"Thanks for your time, \n%s\n"), "Thanks for your time, \n%2\$s\n"),
$nickname, common_config('site', 'name'), $nickname,
common_local_url('confirmaddress', array('code' => $code)), common_config('site', 'name'),
common_config('site', 'name')); common_local_url('confirmaddress', array('code' => $code)));
$headers = array(); $headers = array();
return mail_to_user($user, $subject, $body, $headers, $address); 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 = _mail_prepare_headers('subscribe', $listenee->nickname, $other->nickname);
$headers['From'] = mail_notify_from(); $headers['From'] = mail_notify_from();
$headers['To'] = $name . ' <' . $listenee->email . '>'; $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 '. $headers['Subject'] = sprintf(_('%1$s is now listening to '.
'your notices on %2$s.'), 'your notices on %2$s.'),
$other->getBestName(), $other->getBestName(),
common_config('site', 'name')); 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, " . $blocklink = sprintf(_("If you believe this account is being used abusively, " .
"you can block them from your subscribers list and " . "you can block them from your subscribers list and " .
"report as spam to site administrators at %s"), "report as spam to site administrators at %s"),
common_local_url('block', array('profileid' => $other->id))); 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". $body = sprintf(_('%1$s is now listening to your notices on %2$s.'."\n\n".
"\t".'%3$s'."\n\n". "\t".'%3$s'."\n\n".
'%4$s'. '%4$s'.
'%5$s'. '%5$s'.
'%6$s'. '%6$s'.
"\n".'Faithfully yours,'."\n".'%7$s.'."\n\n". "\n".'Faithfully yours,'."\n".'%2$s.'."\n\n".
"----\n". "----\n".
"Change your email address or ". "Change your email address or ".
"notification options at ".'%8$s' ."\n"), "notification options at ".'%7$s' ."\n"),
$long_name, $long_name,
common_config('site', 'name'), common_config('site', 'name'),
$other->profileurl, $other->profileurl,
($other->location) ? ($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" : '', sprintf(_("Location: %s"), $other->location) . "\n" : '',
($other->homepage) ? ($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" : '', sprintf(_("Homepage: %s"), $other->homepage) . "\n" : '',
(($other->bio) ? (($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" : '') . sprintf(_("Bio: %s"), $other->bio) . "\n" : '') .
"\n\n" . $blocklink . "\n", "\n\n" . $blocklink . "\n",
common_config('site', 'name'),
common_local_url('emailsettings')); common_local_url('emailsettings'));
// reset localization // reset localization
@ -291,7 +302,6 @@ function mail_subscribe_notify_profile($listenee, $other)
* *
* @return void * @return void
*/ */
function mail_new_incoming_notify($user) function mail_new_incoming_notify($user)
{ {
$profile = $user->getProfile(); $profile = $user->getProfile();
@ -300,19 +310,21 @@ function mail_new_incoming_notify($user)
$headers['From'] = $user->incomingemail; $headers['From'] = $user->incomingemail;
$headers['To'] = $name . ' <' . $user->email . '>'; $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'), $headers['Subject'] = sprintf(_('New email address for posting to %s'),
common_config('site', 'name')); 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". $body = sprintf(_("You have a new posting address on %1\$s.\n\n".
"Send email to %2\$s to post new messages.\n\n". "Send email to %2\$s to post new messages.\n\n".
"More email instructions at %3\$s.\n\n". "More email instructions at %3\$s.\n\n".
"Faithfully yours,\n%4\$s"), "Faithfully yours,\n%1\$s"),
common_config('site', 'name'), common_config('site', 'name'),
$user->incomingemail, $user->incomingemail,
common_local_url('doc', array('title' => 'email')), common_local_url('doc', array('title' => 'email')));
common_config('site', 'name'));
mail_send($user->email, $headers, $body); mail_send($user->email, $headers, $body);
} }
@ -324,7 +336,6 @@ function mail_new_incoming_notify($user)
* *
* @return string new email address for incoming messages * @return string new email address for incoming messages
*/ */
function mail_new_incoming_address() function mail_new_incoming_address()
{ {
$prefix = common_confirmation_code(64); $prefix = common_confirmation_code(64);
@ -343,7 +354,6 @@ function mail_new_incoming_address()
* *
* @return success flag * @return success flag
*/ */
function mail_broadcast_notice_sms($notice) function mail_broadcast_notice_sms($notice)
{ {
// Now, get users subscribed to this profile // Now, get users subscribed to this profile
@ -395,7 +405,6 @@ function mail_broadcast_notice_sms($notice)
* *
* @return boolean success flag * @return boolean success flag
*/ */
function mail_send_sms_notice($notice, $user) function mail_send_sms_notice($notice, $user)
{ {
return mail_send_sms_notice_address($notice, return mail_send_sms_notice_address($notice,
@ -415,7 +424,6 @@ function mail_send_sms_notice($notice, $user)
* *
* @return boolean success flag * @return boolean success flag
*/ */
function mail_send_sms_notice_address($notice, $smsemail, $incomingemail) function mail_send_sms_notice_address($notice, $smsemail, $incomingemail)
{ {
$to = $nickname . ' <' . $smsemail . '>'; $to = $nickname . ' <' . $smsemail . '>';
@ -429,7 +437,8 @@ function mail_send_sms_notice_address($notice, $smsemail, $incomingemail)
$headers['From'] = ($incomingemail) ? $incomingemail : mail_notify_from(); $headers['From'] = ($incomingemail) ? $incomingemail : mail_notify_from();
$headers['To'] = $to; $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'), $headers['Subject'] = sprintf(_('%s status'),
$other->getBestName()); $other->getBestName());
@ -449,17 +458,17 @@ function mail_send_sms_notice_address($notice, $smsemail, $incomingemail)
* *
* @return void * @return void
*/ */
function mail_confirm_sms($code, $nickname, $address) function mail_confirm_sms($code, $nickname, $address)
{ {
$recipients = $address; $recipients = $address;
$headers['From'] = mail_notify_from(); $headers['From'] = mail_notify_from();
$headers['To'] = $nickname . ' <' . $address . '>'; $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'); $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 = sprintf(_("%s: confirm you own this phone number with this code:"), $nickname);
$body .= "\n\n"; $body .= "\n\n";
$body .= $code; $body .= $code;
@ -476,16 +485,18 @@ function mail_confirm_sms($code, $nickname, $address)
* *
* @return boolean success flag * @return boolean success flag
*/ */
function mail_notify_nudge($from, $to) function mail_notify_nudge($from, $to)
{ {
common_switch_locale($to->language); 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); $subject = sprintf(_('You\'ve been nudged by %s'), $from->nickname);
$from_profile = $from->getProfile(); $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 ". $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". "these days and is inviting you to post some news.\n\n".
"So let's hear from you :)\n\n". "So let's hear from you :)\n\n".
@ -516,7 +527,6 @@ function mail_notify_nudge($from, $to)
* *
* @return boolean success code * @return boolean success code
*/ */
function mail_notify_message($message, $from=null, $to=null) function mail_notify_message($message, $from=null, $to=null)
{ {
if (is_null($from)) { if (is_null($from)) {
@ -532,12 +542,16 @@ function mail_notify_message($message, $from=null, $to=null)
} }
common_switch_locale($to->language); 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); $subject = sprintf(_('New private message from %s'), $from->nickname);
$from_profile = $from->getProfile(); $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". $body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n".
"------------------------------------------------------\n". "------------------------------------------------------\n".
"%3\$s\n". "%3\$s\n".
@ -572,7 +586,6 @@ function mail_notify_message($message, $from=null, $to=null)
* *
* @return void * @return void
*/ */
function mail_notify_fave($other, $user, $notice) function mail_notify_fave($other, $user, $notice)
{ {
if (!$user->hasRight(Right::EMAILONFAVE)) { if (!$user->hasRight(Right::EMAILONFAVE)) {
@ -585,10 +598,15 @@ function mail_notify_fave($other, $user, $notice)
common_switch_locale($other->language); common_switch_locale($other->language);
// TRANS: Subject for favorite notification email // TRANS: Subject for favorite notification e-mail.
$subject = sprintf(_('%s (@%s) added your notice as a favorite'), $bestname, $user->nickname); // 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". $body = sprintf(_("%1\$s (@%7\$s) just added your notice from %2\$s".
" as one of their favorites.\n\n" . " as one of their favorites.\n\n" .
"The URL of your notice is:\n\n" . "The URL of your notice is:\n\n" .
@ -623,7 +641,6 @@ function mail_notify_fave($other, $user, $notice)
* *
* @return void * @return void
*/ */
function mail_notify_attn($user, $notice) function mail_notify_attn($user, $notice)
{ {
if (!$user->email || !$user->emailnotifyattn) { if (!$user->email || !$user->emailnotifyattn) {
@ -654,9 +671,16 @@ function mail_notify_attn($user, $notice)
$conversationEmailText = ''; $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: 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". $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". "The notice is here:\n\n".
"\t%3\$s\n\n" . "\t%3\$s\n\n" .
@ -709,4 +733,3 @@ function _mail_prepare_headers($msg_type, $to, $from)
return $headers; return $headers;
} }

View File

@ -34,7 +34,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
require_once 'Net/URL/Mapper.php'; require_once 'Net/URL/Mapper.php';
class StatusNet_URL_Mapper extends Net_URL_Mapper { class StatusNet_URL_Mapper extends Net_URL_Mapper {
private static $_singleton = null; private static $_singleton = null;
private function __construct() 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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class Router class Router
{ {
var $m = null; var $m = null;
@ -553,11 +551,19 @@ class Router
'format' => '(xml|json)')); 'format' => '(xml|json)'));
// blocks // blocks
$m->connect('api/blocks/create.:format',
array('action' => 'ApiBlockCreate',
'format' => '(xml|json)'));
$m->connect('api/blocks/create/:id.:format', $m->connect('api/blocks/create/:id.:format',
array('action' => 'ApiBlockCreate', array('action' => 'ApiBlockCreate',
'id' => '[a-zA-Z0-9]+', 'id' => '[a-zA-Z0-9]+',
'format' => '(xml|json)')); 'format' => '(xml|json)'));
$m->connect('api/blocks/destroy.:format',
array('action' => 'ApiBlockDestroy',
'format' => '(xml|json)'));
$m->connect('api/blocks/destroy/:id.:format', $m->connect('api/blocks/destroy/:id.:format',
array('action' => 'ApiBlockDestroy', array('action' => 'ApiBlockDestroy',
'id' => '[a-zA-Z0-9]+', 'id' => '[a-zA-Z0-9]+',
@ -692,7 +698,6 @@ class Router
$m->connect('admin/snapshot', array('action' => 'snapshotadminpanel')); $m->connect('admin/snapshot', array('action' => 'snapshotadminpanel'));
$m->connect('admin/license', array('action' => 'licenseadminpanel')); $m->connect('admin/license', array('action' => 'licenseadminpanel'));
$m->connect('getfile/:filename', $m->connect('getfile/:filename',
array('action' => 'getfile'), array('action' => 'getfile'),
array('filename' => '[A-Za-z0-9._-]+')); array('filename' => '[A-Za-z0-9._-]+'));
@ -701,16 +706,8 @@ class Router
if (common_config('singleuser', 'enabled')) { if (common_config('singleuser', 'enabled')) {
$user = User::siteOwner(); $user = User::singleUser();
if (!empty($user)) {
$nickname = $user->nickname; $nickname = $user->nickname;
} else {
$nickname = common_config('singleuser', 'nickname');
if (empty($nickname)) {
throw new ServerException(_("No single user defined for single-user mode."));
}
}
foreach (array('subscriptions', 'subscribers', foreach (array('subscriptions', 'subscribers',
'all', 'foaf', 'xrds', 'all', 'foaf', 'xrds',
@ -765,9 +762,7 @@ class Router
$m->connect('', $m->connect('',
array('action' => 'showstream', array('action' => 'showstream',
'nickname' => $nickname)); 'nickname' => $nickname));
} else { } else {
$m->connect('', array('action' => 'public')); $m->connect('', array('action' => 'public'));
$m->connect('rss', array('action' => 'publicrss')); $m->connect('rss', array('action' => 'publicrss'));
$m->connect('featuredrss', array('action' => 'featuredrss')); $m->connect('featuredrss', array('action' => 'featuredrss'));
@ -848,7 +843,8 @@ class Router
} catch (Net_URL_Mapper_InvalidException $e) { } catch (Net_URL_Mapper_InvalidException $e) {
common_log(LOG_ERR, "Problem getting route for $path - " . common_log(LOG_ERR, "Problem getting route for $path - " .
$e->getMessage()); $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(); $cac->showPage();
} }
@ -875,7 +871,16 @@ class Router
if ($qpos !== false) { if ($qpos !== false) {
$url = substr($url, 0, $qpos+1) . $url = substr($url, 0, $qpos+1) .
str_replace('?', '&', substr($url, $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 &amp; instead of &. Encoded &s in parameters will not be
// affected.
$url = substr($url, 0, $qpos+1) .
str_replace('&amp;', '&', substr($url, $qpos+1));
} }
return $url; return $url;
} }
} }

View File

@ -169,7 +169,6 @@ class StatusNet
return $sites; return $sites;
} }
/** /**
* Fire initialization events for all instantiated plugins. * Fire initialization events for all instantiated plugins.
*/ */
@ -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 class NoConfigException extends Exception

View File

@ -116,6 +116,33 @@ class Theme
protected function relativeThemePath($group, $fallbackSubdir, $name) protected function relativeThemePath($group, $fallbackSubdir, $name)
{ {
if (StatusNet::isHTTPS()) {
$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');
}
}
$protocol = 'https';
} else {
$path = common_config($group, 'path'); $path = common_config($group, 'path');
if (empty($path)) { if (empty($path)) {
@ -125,6 +152,15 @@ class Theme
} }
} }
$server = common_config($group, 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$protocol = 'http';
}
if ($path[strlen($path)-1] != '/') { if ($path[strlen($path)-1] != '/') {
$path .= '/'; $path .= '/';
} }
@ -133,27 +169,7 @@ class Theme
$path = '/'.$path; $path = '/'.$path;
} }
$server = common_config($group, 'server'); return $protocol.'://'.$server.$path.$name;
if (empty($server)) {
$server = common_config('site', 'server');
}
$ssl = common_config($group, 'ssl');
if (is_null($ssl)) { // null -> guess
if (common_config('site', 'ssl') == 'always' &&
!common_config($group, 'server')) {
$ssl = true;
} else {
$ssl = false;
}
}
$protocol = ($ssl) ? 'https' : 'http';
$path = $protocol . '://'.$server.$path.$name;
return $path;
} }
/** /**

View File

@ -145,7 +145,6 @@ function common_switch_locale($language=null)
textdomain("statusnet"); textdomain("statusnet");
} }
function common_timezone() function common_timezone()
{ {
if (common_logged_in()) { if (common_logged_in()) {
@ -860,7 +859,8 @@ function common_linkify($url) {
$longurl = $url; $longurl = $url;
} }
} }
$attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external');
$attrs = array('href' => $canon, 'title' => $longurl);
$is_attachment = false; $is_attachment = false;
$attachment_id = null; $attachment_id = null;
@ -896,6 +896,16 @@ function common_linkify($url) {
$attrs['id'] = "attachment-{$attachment_id}"; $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); return XMLStringer::estring('a', $attrs, $url);
} }
@ -964,8 +974,9 @@ function common_tag_link($tag)
$canonical = common_canonical_tag($tag); $canonical = common_canonical_tag($tag);
if (common_config('singleuser', 'enabled')) { if (common_config('singleuser', 'enabled')) {
// regular TagAction isn't set up in 1user mode // regular TagAction isn't set up in 1user mode
$user = User::singleUser();
$url = common_local_url('showstream', $url = common_local_url('showstream',
array('nickname' => common_config('singleuser', 'nickname'), array('nickname' => $user->nickname,
'tag' => $canonical)); 'tag' => $canonical));
} else { } else {
$url = common_local_url('tag', array('tag' => $canonical)); $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) 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; $ssl = null;
if (Event::handle('SensitiveAction', array($action, &$ssl))) { if (Event::handle('SensitiveAction', array($action, &$ssl))) {

View File

@ -32,7 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
} }
class WebColor { class WebColor {
// XXX: Maybe make getters and setters for r,g,b values and tuples, // 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) // e.g.: to support this kinda CSS representation: rgb(255,0,0)
// http://www.w3.org/TR/CSS21/syndata.html#color-units // http://www.w3.org/TR/CSS21/syndata.html#color-units
@ -65,7 +64,6 @@ class WebColor {
* *
* @return nothing * @return nothing
*/ */
function parseColor($color) { function parseColor($color) {
if (is_numeric($color)) { if (is_numeric($color)) {
@ -90,13 +88,11 @@ class WebColor {
* *
* @return nothing * @return nothing
*/ */
function setNamedColor($name) function setNamedColor($name)
{ {
// XXX Implement this // XXX Implement this
} }
/** /**
* Sets the RGB color values from a a hex tuple * Sets the RGB color values from a a hex tuple
* *
@ -104,7 +100,6 @@ class WebColor {
* *
* @return nothing * @return nothing
*/ */
function setHexColor($hexcolor) { function setHexColor($hexcolor) {
if ($hexcolor[0] == '#') { if ($hexcolor[0] == '#') {
@ -120,7 +115,9 @@ class WebColor {
$hexcolor[1].$hexcolor[1], $hexcolor[1].$hexcolor[1],
$hexcolor[2].$hexcolor[2]); $hexcolor[2].$hexcolor[2]);
} else { } 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)); throw new WebColorException(sprintf($errmsg, $hexcolor));
} }
@ -137,7 +134,6 @@ class WebColor {
* *
* @return nothing * @return nothing
*/ */
function setIntColor($intcolor) function setIntColor($intcolor)
{ {
// We could do 32 bit and have an alpha channel because // We could do 32 bit and have an alpha channel because
@ -154,7 +150,6 @@ class WebColor {
* *
* @return string * @return string
*/ */
function hexValue() { function hexValue() {
$hexcolor = (strlen(dechex($this->red)) < 2 ? '0' : '' ) . $hexcolor = (strlen(dechex($this->red)) < 2 ? '0' : '' ) .
@ -165,7 +160,6 @@ class WebColor {
dechex($this->blue); dechex($this->blue);
return strtoupper($hexcolor); return strtoupper($hexcolor);
} }
/** /**
@ -176,7 +170,6 @@ class WebColor {
* *
* @return int * @return int
*/ */
function intValue() function intValue()
{ {
$intcolor = 256 * 256 * $this->red + 256 * $this->green + $this->blue; $intcolor = 256 * 256 * $this->red + 256 * $this->green + $this->blue;
@ -188,5 +181,3 @@ class WebColor {
class WebColorException extends Exception class WebColorException extends Exception
{ {
} }
?>

View File

@ -30,7 +30,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
* In a multi-site queuedaemon.php run, one connection will be instantiated * 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. * for each site being handled by the current process that has XMPP enabled.
*/ */
class XmppManager extends IoManager class XmppManager extends IoManager
{ {
protected $site = null; protected $site = null;
@ -102,6 +101,7 @@ class XmppManager extends IoManager
$this->conn->addEventHandler('reconnect', 'handle_reconnect', $this); $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this);
$this->conn->setReconnectTimeout(600); $this->conn->setReconnectTimeout(600);
// @todo Needs i18n?
jabber_send_presence("Send me a message to post a notice", 'available', null, 'available', 100); jabber_send_presence("Send me a message to post a notice", 'available', null, 'available', 100);
return !is_null($this->conn); return !is_null($this->conn);
@ -281,9 +281,9 @@ class XmppManager extends IoManager
$_cur = $user; $_cur = $user;
if (!$user) { if (!$user) {
$this->from_site($from, 'Unknown user; go to ' . // TRANS: %s is the URL to the StatusNet site's Instant Messaging settings.
common_local_url('imsettings') . $this->from_site($from, sprintf(_('Unknown user. Go to %s ' .
' to add your address to your account'); 'to add your address to your account'),common_local_url('imsettings')));
$this->log(LOG_WARNING, 'Message from unknown user ' . $from); $this->log(LOG_WARNING, 'Message from unknown user ' . $from);
return; return;
} }
@ -314,7 +314,6 @@ class XmppManager extends IoManager
unset($pl); unset($pl);
} }
function is_self($from) function is_self($from)
{ {
return preg_match('/^'.strtolower(jabber_daemon_address()).'/', strtolower($from)); return preg_match('/^'.strtolower(jabber_daemon_address()).'/', strtolower($from));
@ -400,7 +399,11 @@ class XmppManager extends IoManager
$content_shortened = common_shorten_links($body); $content_shortened = common_shorten_links($body);
if (Notice::contentTooLong($content_shortened)) { if (Notice::contentTooLong($content_shortened)) {
$from = jabber_normalize_jid($pl['from']); $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(), Notice::maxContent(),
mb_strlen($content_shortened))); mb_strlen($content_shortened)));
return; return;

View File

@ -52,4 +52,3 @@ class XmppOutQueueHandler extends QueueHandler
return $ok; return $ok;
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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