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
- $action: action being executed
- $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
------
StatusNet 0.9.5 "What's The Frequency, Kenneth?"
10 September 2010
StatusNet 0.9.6 "Man on the Moon"
25 October 2010 - RELEASE CANDIDATE
This is the README file for StatusNet, the Open Source microblogging
platform. It includes installation instructions, descriptions of
@ -96,8 +96,8 @@ for additional terms.
New this version
================
This is a security, bug and feature release since version 0.9.4 released on
16 August 2010.
This is a security, bug and feature release since version 0.9.5 released on
10 September 2010.
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
@ -105,24 +105,24 @@ version.
Notable changes this version:
- Change of license for default themes and documentation from
AGPLv3 to CC-By 3.0 Unported.
- An experimental TinyMCE plugin to do in-browser rich editing of
status updates. Does not support StatusNet syntax like @-replies or
#hashtags very well.
- An experimental plugin to add titles to notices.
- A plugin to support the Echo <http://aboutecho.com/> commenting
system.
- A plugin to support the Disqus <http://disqus.com/> commenting system.
- Changes to OStatus support to make StatusNet work for the Social Web
Acid Test Level 0 <http://federatedsocialweb.net/wiki/SWAT0>.
- Themes now support a theme.ini file for theme configuration, including
defining a "base" theme.
- Improved two-way Twitter integration, including support for
repeats and retweets, replies, and faves going both ways across the
bridge, as well as better parsing of Twitter statuses.
- Site moderators can now delete groups.
- New themes: clean, shiny, mnml, victorian
- New YammerImport plugin allows site admins to import non-private profiles and
message from an authenticated Yammer site.
- New experimental plugins: AnonFavorites, SlicedFavorites, GroupFavorited,
ForceGroup, ShareNotice
- OAuth upgraded to 1.0a
- Localization updates now include plugins, thanks to translatewiki.net!
- SSL link generation should be more consistent; alternate SSL URLs can be
set in the admin UI for more parts of the system.
- Experimental backupuser.php, restoreuser.php command-line scripts to
dump/restore a user's complete activity stream. Can be used to transfer
accounts manually between sites, or to save a backup before deleting.
- Unicode fixes for OStatus notices
- Header metadata on notice pages to aid in manual reposting on Facebook
- 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
=============
@ -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
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
may have to unpack the tarball on your local computer and FTP the
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
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
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.
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
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
been tracking the "git" version of the software, you will probably
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.
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
final backup of the Web directory and database.
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.
8. Copy the config.php file and avatar directory from your old
directory to your new directory.
8. Copy the config.php file and the contents of the avatar/, background/,
file/, and local/ subdirectories from your old directory to your new
directory.
9. Copy htaccess.sample to .htaccess in the new directory. Change the
RewriteBase to use the correct path.
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.
logo: URL of an image file to use as the logo for the site. Overrides
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.
Possible values are 'always' (use it for all pages), 'never'
(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'.
ssl: Whether to use SSL for theme elements. Default is null, which means
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
----------
@ -1122,6 +1128,9 @@ path: Path part of Javascript URLs. Defaults to null,
which means to use the site path + '/js/'.
ssl: Whether to use SSL for JavaScript files. Default is null, which means
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
----
@ -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
skipped if fileinfo extension is installed. Defaults to
'/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
-----
@ -1405,8 +1419,9 @@ dir: directory to write backgrounds too. Default is '/background/'
subdir of install dir.
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.
ssl: Whether or not to use HTTPS for background files. Defaults to
null, meaning to guess from site-wide 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.
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.
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
---------
@ -1487,6 +1503,33 @@ disallow: Array of (virtual) directories to disallow. Default is 'main',
'search', 'message', 'settings', 'admin'. Ignored when site
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
=======
@ -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
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,
read the "Notice inboxes" section above.
@ -1655,6 +1698,10 @@ if anyone's been overlooked in error.
* mEDI
* Brett Taylor
* 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
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
return sprintf(_('%1$s and friends, page %2$d'), $this->user->nickname, $this->page);
} else {
// TRANS: Page title. %1$s is user nickname
// TRANS: Page title. %s is user nickname
return sprintf(_("%s and friends"), $this->user->nickname);
}
}
@ -103,7 +103,7 @@ class AllAction extends ProfileAction
'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)),
new Feed(Feed::RSS2,
common_local_url(
@ -112,7 +112,7 @@ class AllAction extends ProfileAction
'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)),
new Feed(Feed::ATOM,
common_local_url(
@ -121,7 +121,7 @@ class AllAction extends ProfileAction
'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))
);
}
@ -134,18 +134,23 @@ class AllAction extends ProfileAction
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) . ' ';
if (common_logged_in()) {
$current_user = common_current_user();
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.');
} else {
// 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);
}
} 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);
}
@ -181,7 +186,7 @@ class AllAction extends ProfileAction
// TRANS: H1 text
$this->element('h1', null, _("You and friends"));
} 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));
}
}

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
* @link http://status.net/
*/
class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
{
/**
@ -56,7 +55,6 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
@ -76,7 +74,6 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -92,6 +89,7 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
// TRANS: Client error displayed handling a non-existing API method.
_('API method not found.'),
404,
$this->format
@ -102,16 +100,14 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
// Note: Twitter no longer supports IM
if (!in_array(strtolower($this->device), array('sms', 'im', 'none'))) {
$this->clientError(
_(
'You must specify a parameter named ' .
'\'device\' with a value of one of: sms, im, none.'
)
);
// 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 ' .
'\'device\' with a value of one of: sms, im, none.' ));
return;
}
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);
return;
}
@ -131,6 +127,7 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
if ($result === false) {
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.'));
return;
}
@ -155,5 +152,4 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
$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
* @link http://status.net/
*/
class ApiAccountUpdateProfileAction extends ApiAuthAction
{
/**
* Take arguments for running
*
@ -55,7 +53,6 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
@ -79,7 +76,6 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -103,6 +99,7 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
}
if (empty($this->user)) {
// TRANS: Client error displayed if a user could not be found.
$this->clientError(_('No such user.'), 404, $this->format);
return;
}
@ -110,6 +107,7 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$profile = $this->user->getProfile();
if (empty($profile)) {
// TRANS: Client error displayed if a user profile could not be found.
$this->clientError(_('User has no profile.'));
return;
}
@ -145,6 +143,7 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
if (!$result) {
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.'));
return;
}
@ -163,5 +162,4 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$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
* @link http://status.net/
*/
class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
{
var $tile = false;
/**
@ -56,7 +54,6 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
@ -76,7 +73,6 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -92,6 +88,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
// TRANS: Client error displayed when trying to handle an unknown API method.
_('API method not found.'),
404,
$this->format
@ -106,8 +103,11 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
$msg = _('The server was unable to handle that much POST ' .
'data (%s bytes) due to its current configuration.');
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// 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']));
return;
@ -125,7 +125,6 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
// is part of the img filename.
if (empty($design)) {
$this->user->query('BEGIN');
// save new design
@ -134,6 +133,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
if (empty($id)) {
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.'));
return;
}
@ -144,6 +144,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
if (empty($result)) {
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->user->query('ROLLBACK');
return;
@ -185,6 +186,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
if ($result === false) {
common_log_db_error($design, 'UPDATE', __FILE__);
// TRANS: Error displayed when updating design settings fails.
$this->showForm(_('Could not update your design.'));
return;
}
@ -192,6 +194,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
$profile = $this->user->getProfile();
if (empty($profile)) {
// TRANS: Client error displayed when a user has no profile.
$this->clientError(_('User has no profile.'));
return;
}
@ -208,5 +211,4 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction
$this->endDocument('json');
}
}
}

View File

@ -95,9 +95,11 @@ class ApiAccountUpdateProfileImageAction extends ApiAuthAction
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
$msg = _('The server was unable to handle that much POST ' .
'data (%s bytes) due to its current configuration.');
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// 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']));
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
* @link http://status.net/
*/
class ApiBlockCreateAction extends ApiAuthAction
{
var $other = null;
@ -59,7 +58,6 @@ class ApiBlockCreateAction extends ApiAuthAction
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
@ -79,7 +77,6 @@ class ApiBlockCreateAction extends ApiAuthAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -103,6 +100,7 @@ class ApiBlockCreateAction extends ApiAuthAction
if ($this->user->id == $this->other->id) {
$this->clientError(
// TRANS: Client error displayed when users try to block themselves.
_("You cannot block yourself!"),
403,
$this->format
@ -124,10 +122,8 @@ class ApiBlockCreateAction extends ApiAuthAction
$this->showProfile($this->other, $this->format);
$this->endDocument($this->format);
} else {
// TRANS: Server error displayed when blocking a user has failed.
$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
* @link http://status.net/
*/
class ApiBlockDestroyAction extends ApiAuthAction
{
var $other = null;
@ -58,7 +57,6 @@ class ApiBlockDestroyAction extends ApiAuthAction
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
@ -78,7 +76,6 @@ class ApiBlockDestroyAction extends ApiAuthAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -112,10 +109,8 @@ class ApiBlockDestroyAction extends ApiAuthAction
$this->showProfile($this->other, $this->format);
$this->endDocument($this->format);
} else {
// TRANS: Server error displayed when unblocking a user has failed.
$this->serverError(_('Unblock user failed.'));
}
}
}

View File

@ -74,6 +74,7 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->user = $this->auth_user;
if (empty($this->user)) {
// TRANS: Client error given when a user was not found (404).
$this->clientError(_('No such user.'), 404, $this->format);
return;
}
@ -86,10 +87,12 @@ class ApiDirectMessageAction extends ApiAuthAction
// Action was called by /api/direct_messages/sent.format
$this->title = sprintf(
// TRANS: %s is a user nickname.
_("Direct messages from %s"),
$this->user->nickname
);
$this->subtitle = sprintf(
// TRANS: %s is a user nickname.
_("All the direct messages sent from %s"),
$this->user->nickname
);
@ -98,10 +101,12 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->id = "tag:$taguribase:SentDirectMessages:" . $this->user->id;
} else {
$this->title = sprintf(
// TRANS: %s is a user nickname.
_("Direct messages to %s"),
$this->user->nickname
);
$this->subtitle = sprintf(
// TRANS: %s is a user nickname.
_("All the direct messages sent to %s"),
$this->user->nickname
);
@ -153,6 +158,7 @@ class ApiDirectMessageAction extends ApiAuthAction
$this->showJsonDirectMessages();
break;
default:
// TRANS: Client error given when an API method was not found (404).
$this->clientError(_('API method not found.'), $code = 404);
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
* @link http://status.net/
*/
class ApiDirectMessageNewAction extends ApiAuthAction
{
var $other = null;
@ -63,7 +62,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
@ -99,7 +97,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -116,6 +113,7 @@ class ApiDirectMessageNewAction extends ApiAuthAction
if (empty($this->content)) {
$this->clientError(
// TRANS: Client error (406).
_('No message text!'),
406,
$this->format
@ -124,8 +122,9 @@ class ApiDirectMessageNewAction extends ApiAuthAction
$content_shortened = common_shorten_links($this->content);
if (Message::contentTooLong($content_shortened)) {
$this->clientError(
sprintf(
_('That\'s too long. Max message size is %d chars.'),
// TRANS: Client error displayed when message content is too long.
// 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()
),
406,
@ -136,10 +135,12 @@ class ApiDirectMessageNewAction extends ApiAuthAction
}
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);
return;
} else if (!$this->user->mutuallySubscribed($this->other)) {
$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.'),
403,
$this->format
@ -149,10 +150,9 @@ class ApiDirectMessageNewAction extends ApiAuthAction
// Note: sending msgs to yourself is allowed by Twitter
$errmsg = 'Don\'t send a message to yourself; ' .
'just say it to yourself quietly instead.';
$this->clientError(_($errmsg), 403, $this->format);
// TRANS: Client error displayed trying to direct message self (403).
$this->clientError(_('Do not send a message to yourself; ' .
'just say it to yourself quietly instead.'), 403, $this->format);
return;
}
@ -176,6 +176,4 @@ class ApiDirectMessageNewAction extends ApiAuthAction
$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
* @link http://status.net/
*/
class ApiFavoriteCreateAction extends ApiAuthAction
{
var $notice = null;
@ -61,7 +60,6 @@ class ApiFavoriteCreateAction extends ApiAuthAction
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
@ -81,7 +79,6 @@ class ApiFavoriteCreateAction extends ApiAuthAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -107,6 +104,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction
if (empty($this->notice)) {
$this->clientError(
// TRANS: Client error displayed when requesting a status with a non-existing ID.
_('No status found with that ID.'),
404,
$this->format
@ -118,6 +116,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction
if ($this->user->hasFave($this->notice)) {
$this->clientError(
// TRANS: Client error displayed when trying to mark a notice favourite that already is a favourite.
_('This status is already a favorite.'),
403,
$this->format
@ -129,6 +128,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction
if (empty($fave)) {
$this->clientError(
// TRANS: Client error displayed when marking a notice as favourite fails.
_('Could not create favorite.'),
403,
$this->format
@ -166,5 +166,4 @@ class ApiFavoriteCreateAction extends ApiAuthAction
// 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
* @link http://status.net/
*/
class ApiGroupCreateAction extends ApiAuthAction
{
var $group = null;
@ -95,7 +94,6 @@ class ApiGroupCreateAction extends ApiAuthAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -111,6 +109,7 @@ class ApiGroupCreateAction extends ApiAuthAction
}
if (empty($this->user)) {
// TRANS: Client error given when a user was not found (404).
$this->clientError(_('No such user.'), 404, $this->format);
return;
}
@ -137,13 +136,13 @@ class ApiGroupCreateAction extends ApiAuthAction
break;
default:
$this->clientError(
// TRANS: Client error given when an API method was not found (404).
_('API method not found.'),
404,
$this->format
);
break;
}
}
/**
@ -164,6 +163,7 @@ class ApiGroupCreateAction extends ApiAuthAction
if (!$valid) {
$this->clientError(
// TRANS: Validation error in form for group creation.
_(
'Nickname must have only lowercase letters ' .
'and numbers and no spaces.'
@ -174,6 +174,7 @@ class ApiGroupCreateAction extends ApiAuthAction
return false;
} elseif ($this->groupNicknameExists($this->nickname)) {
$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.'),
403,
$this->format
@ -181,6 +182,7 @@ class ApiGroupCreateAction extends ApiAuthAction
return false;
} else if (!User_group::allowedNickname($this->nickname)) {
$this->clientError(
// TRANS: Client error in form for group creation.
_('Not a valid nickname.'),
403,
$this->format
@ -197,6 +199,7 @@ class ApiGroupCreateAction extends ApiAuthAction
)
)) {
$this->clientError(
// TRANS: Client error in form for group creation.
_('Homepage is not a valid URL.'),
403,
$this->format
@ -206,7 +209,8 @@ class ApiGroupCreateAction extends ApiAuthAction
!is_null($this->fullname)
&& mb_strlen($this->fullname) > 255) {
$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,
$this->format
);
@ -225,7 +229,7 @@ class ApiGroupCreateAction extends ApiAuthAction
!is_null($this->location)
&& mb_strlen($this->location) > 255) {
$this->clientError(
_('Location is too long (max 255 chars).'),
_('Location is too long (maximum 255 characters).'),
403,
$this->format
);

View File

@ -78,9 +78,11 @@ class ApiMediaUploadAction extends ApiAuthAction
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
$msg = _('The server was unable to handle that much POST ' .
'data (%s bytes) due to its current configuration.');
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// 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']));
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
* @link http://status.net/
*/
class ApiOauthAccessTokenAction extends ApiOauthAction
{
protected $reqToken = null;
@ -67,21 +66,21 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
$server->add_signature_method($hmac_method);
$atok = null;
$atok = $app = null;
// XXX: Insist that oauth_token and oauth_verifier be populated?
// Spec doesn't say they MUST be.
try {
$req = OAuthRequest::from_request();
$this->reqToken = $req->get_parameter('oauth_token');
$this->verifier = $req->get_parameter('oauth_verifier');
$app = $datastore->getAppByRequestToken($this->reqToken);
$atok = $server->fetch_access_token($req);
} catch (OAuthException $e) {
} catch (Exception $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
common_debug(var_export($req, true));
$code = $e->getCode();
@ -92,22 +91,27 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
// Token exchange failed -- log it
list($proxy, $ip) = common_client_ip();
$msg = sprintf(
'API OAuth - Failure exchanging request token for access token, '
. 'request token = %s, verifier = %s, IP = %s, proxy = %s',
'API OAuth - Failure exchanging OAuth request token for access token, '
. 'request token = %s, verifier = %s',
$this->reqToken,
$this->verifier,
$ip,
$proxy
$this->verifier
);
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'));
} else {
common_log(
LOG_INFO,
sprintf(
"Issued access token '%s' for application %d (%s).",
$atok->key,
$app->id,
$app->name
)
);
$this->showAccessToken($atok);
}
}

View File

@ -35,7 +35,7 @@ require_once INSTALLDIR . '/lib/apioauth.php';
require_once INSTALLDIR . '/lib/info.php';
/**
* Authorize an OAuth request token
* Authorize an Oputh request token
*
* @category API
* @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
* @link http://status.net/
*/
class ApiOauthAuthorizeAction extends Action
{
var $oauthTokenParam;
@ -69,11 +68,11 @@ class ApiOauthAuthorizeAction extends Action
{
parent::prepare($args);
$this->nickname = $this->trimmed('nickname');
$this->password = $this->arg('password');
$this->oauthTokenParam = $this->arg('oauth_token');
$this->callback = $this->arg('oauth_callback');
$this->store = new ApiStatusNetOAuthDataStore();
$this->nickname = $this->trimmed('nickname');
$this->password = $this->arg('password');
$this->oauthTokenParam = $this->arg('oauth_token');
$this->mode = $this->arg('mode');
$this->store = new ApiStatusNetOAuthDataStore();
try {
$this->app = $this->store->getAppByRequestToken($this->oauthTokenParam);
@ -93,7 +92,6 @@ class ApiOauthAuthorizeAction extends Action
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -106,6 +104,7 @@ class ApiOauthAuthorizeAction extends Action
// Make sure a oauth_token parameter was provided
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.'));
} else {
@ -113,20 +112,21 @@ class ApiOauthAuthorizeAction extends Action
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
if (empty($this->reqToken)) {
$this->serverError(
_('Invalid request token.')
);
// TRANS: Client error given when an invalid request token was passed to the OAuth API.
$this->clientError(_('Invalid request token.'));
} else {
// Check to make sure we haven't already authorized the token
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
if (empty($this->app)) {
// TRANS: Client error given when an invalid request token was passed to the OAuth API.
$this->clientError(_('Invalid request token.'));
}
@ -156,10 +156,15 @@ class ApiOauthAuthorizeAction extends Action
// 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);
}
Event::handle('EndOAuthLoginCheck', array($this, &$user));
$user = common_check_user($this->nickname, $this->password);
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!"));
return;
}
@ -167,10 +172,11 @@ class ApiOauthAuthorizeAction extends Action
$user = common_current_user();
}
if ($this->arg('allow')) {
// fetch the token
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
assert(!empty($this->reqToken));
// fetch the token
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
if ($this->arg('allow')) {
// mark the req token as authorized
try {
@ -179,73 +185,66 @@ class ApiOauthAuthorizeAction extends Action
$this->serverError($e->getMessage());
}
// Check to see if there was a previous token associated
// with this user/app and kill it. If the user is doing this she
// probably doesn't want any old tokens anyway.
common_log(
LOG_INFO,
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)) {
$result = $appUser->delete();
$tokenAssoc = new Oauth_token_association();
if (!$result) {
common_log_db_error($appUser, 'DELETE', __FILE__);
$this->serverError(_('Database error deleting OAuth application user.'));
}
}
$tokenAssoc->profile_id = $user->id;
$tokenAssoc->application_id = $this->app->id;
$tokenAssoc->token = $this->oauthTokenParam;
$tokenAssoc->created = common_sql_now();
// associated the authorized req token with the user and the app
$appUser = new Oauth_application_user();
$appUser->profile_id = $user->id;
$appUser->application_id = $this->app->id;
// Note: do not copy the access type from the application.
// The access type should always be 0 when the OAuth app
// user record has a request token associated with it.
// Access type gets assigned once an access token has been
// granted. The OAuth app user record then gets updated
// with the new access token and access type.
$appUser->token = $this->oauthTokenParam;
$appUser->created = common_sql_now();
$result = $appUser->insert();
$result = $tokenAssoc->insert();
if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__);
$this->serverError(_('Database error inserting OAuth application user.'));
common_log_db_error($tokenAssoc, 'INSERT', __FILE__);
// TRANS: Server error displayed when a database action fails.
$this->serverError(_('Database error inserting oauth_token_association.'));
}
// If we have a callback redirect and provide the token
$callback = $this->getCallback();
// 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,
if (!empty($callback) && $this->reqToken->verified_callback != 'oob') {
$targetUrl = $this->buildCallbackUrl(
$callback,
array(
'oauth_token' => $this->oauthTokenParam,
'oauth_verifier' => $this->reqToken->verifier // 1.0a
)
);
common_log(LOG_INFO, "Redirecting to callback: $targetUrl");
// Redirect the user to the provided OAuth callback
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(
LOG_INFO,
"No oauth_callback parameter provided for application ID "
. $this->app->id
. " when authorizing request token."
LOG_WARNING,
sprintf(
"API OAuth - No callback provided for OAuth web client ID %s (%s) "
. "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')) {
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 {
$this->store->revoke_token($this->oauthTokenParam, 0);
$this->showCanceled();
} catch (Exception $e) {
$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 {
// TRANS: Client error given on when invalid data was passed through a form in the OAuth API.
$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)
{
$this->error = $error;
@ -285,9 +384,9 @@ class ApiOauthAuthorizeAction extends Action
*
* @return string title of the page
*/
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');
}
@ -296,7 +395,6 @@ class ApiOauthAuthorizeAction extends Action
*
* @return void
*/
function showContent()
{
$this->elementStart('form', array('method' => 'post',
@ -305,27 +403,40 @@ class ApiOauthAuthorizeAction extends Action
'action' => common_local_url('ApiOauthAuthorize')));
$this->elementStart('fieldset');
$this->element('legend', array('id' => 'apioauthauthorize_allowdeny'),
// TRANS: Fieldset legend.
_('Allow or deny access'));
$this->hidden('token', common_session_token());
$this->hidden('mode', $this->mode);
$this->hidden('oauth_token', $this->oauthTokenParam);
$this->hidden('oauth_callback', $this->callback);
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$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));
}
$access = ($this->app->access_type & Oauth_application::$writeAccess) ?
'access and update' : 'access';
$msg = _('The application <strong>%1$s</strong> by ' .
'<strong>%2$s</strong> would like the ability ' .
// 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 ' .
'<strong>%2$s</strong> 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.');
}
$this->raw(sprintf($msg,
$this->app->name,
@ -336,34 +447,43 @@ class ApiOauthAuthorizeAction extends Action
$this->elementEnd('li');
$this->elementEnd('ul');
// quickie hack
$button = false;
if (!common_logged_in()) {
if (Event::handle('StartOAuthLoginForm', array($this, &$button))) {
$this->elementStart('fieldset');
// TRANS: Fieldset legend.
$this->element('legend', null, _m('LEGEND','Account'));
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
// TRANS: Field label on OAuth API authorisation form.
$this->input('nickname', _('Nickname'));
$this->elementEnd('li');
$this->elementStart('li');
// TRANS: Field label on OAuth API authorisation form.
$this->password('password', _('Password'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementStart('fieldset');
$this->element('legend', null, _('Account'));
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->input('nickname', _('Nickname'));
$this->elementEnd('li');
$this->elementStart('li');
$this->password('password', _('Password'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
$this->elementEnd('fieldset');
}
Event::handle('EndOAuthLoginForm', array($this, &$button));
}
$this->element('input', array('id' => 'cancel_submit',
'class' => 'submit submit form_action-primary',
'name' => 'cancel',
'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',
'class' => 'submit submit form_action-secondary',
'name' => 'allow',
'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('form');
@ -377,9 +497,9 @@ class ApiOauthAuthorizeAction extends Action
*
* @return void
*/
function getInstructions()
{
// TRANS: Form instructions.
return _('Authorize access to your account information.');
}
@ -390,18 +510,61 @@ class ApiOauthAuthorizeAction extends Action
*
* @return void
*/
function showLocalNav()
{
// 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.
*
* @return nothing
*/
function showSiteNotice()
{
// NOP
@ -414,7 +577,6 @@ class ApiOauthAuthorizeAction extends Action
*
* @return nothing
*/
function showNoticeForm()
{
// NOP
@ -426,14 +588,16 @@ class ApiOauthAuthorizeAction extends Action
*
* @return nothing
*/
function showCanceled()
{
$info = new InfoAction(
// TRANS: Header for user notification after revoking OAuth access to an application.
_('Authorization canceled.'),
sprintf(
// TRANS: User notification after revoking OAuth access to an application.
// TRANS: %s is an OAuth token.
_('The request token %s has been revoked.'),
$this->oauthTokenParm
$this->oauthTokenParam
)
);
@ -446,21 +610,29 @@ class ApiOauthAuthorizeAction extends Action
*
* @return nothing
*/
function showAuthorized()
{
$title = sprintf(
_("You have successfully authorized %s."),
$this->app->name
// TRANS: Header of user notification after authorising an application access to a profile.
// TRANS: %s is the authorised application name.
_('You have successfully authorized %s.'),
($this->app->name == 'anonymous') ? 'the application' : $this->app->name
);
$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.'),
$this->app->name
($this->app->name == 'anonymous') ? 'the application' : $this->app->name
);
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();
} 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
* 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
*/
function getCallback($url, $params)
function buildCallbackUrl($url, $params)
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar(
@ -513,7 +714,6 @@ class ApiOauthAuthorizeAction extends Action
*
* @return string $url the new URL with added parameter
*/
function appendQueryVar($url, $k, $v) {
$url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&');
$url = substr($url, 0, -1);

View File

@ -31,8 +31,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR . '/lib/info.php';
/**
* Class for displaying an OAuth verifier pin
*
@ -47,13 +45,121 @@ require_once INSTALLDIR . '/lib/info.php';
class ApiOauthPinAction extends InfoAction
{
function __construct($title, $message, $verifier)
function __construct($title, $message, $verifier, $desktopMode = false)
{
$this->verifier = $verifier;
$this->title = $title;
$this->verifier = $verifier;
$this->title = $title;
$this->desktopMode = $desktopMode;
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.
*

View File

@ -146,7 +146,7 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
function verifyCallback($callback)
{
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
// web application but requests the pin based workflow? For now I'm
@ -154,10 +154,7 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
return true;
} else {
return Validate::uri(
$callback,
array('allowed_schemes' => array('http', 'https'))
);
return Validate::uri($callback);
}
}

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

View File

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

View File

@ -45,7 +45,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @link http://status.net/
* @fixme merge more of this code with related variants
*/
class DeletegroupAction extends RedirectingAction
{
var $group = null;
@ -56,12 +55,12 @@ class DeletegroupAction extends RedirectingAction
* @fixme merge common setup code with other group actions
* @fixme allow group admins to delete their own groups
*/
function prepare($args)
{
parent::prepare($args);
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.'));
return false;
}
@ -84,23 +83,27 @@ class DeletegroupAction extends RedirectingAction
$local = Local_group::staticGet('nickname', $nickname);
if (!$local) {
// TRANS: Client error when trying to delete a non-local group.
$this->clientError(_('No such group.'), 404);
return false;
}
$this->group = User_group::staticGet('id', $local->group_id);
} 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);
return false;
}
if (!$this->group) {
// TRANS: Client error when trying to delete a non-existing group.
$this->clientError(_('No such group.'), 404);
return false;
}
$cur = common_current_user();
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);
return false;
}
@ -117,7 +120,6 @@ class DeletegroupAction extends RedirectingAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -143,14 +145,18 @@ class DeletegroupAction extends RedirectingAction
Event::handle('EndDeleteGroup', array($this->group));
}
} 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));
}
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$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->elementEnd('head');
$this->elementStart('body');
@ -166,6 +172,7 @@ class DeletegroupAction extends RedirectingAction
}
function title() {
// TRANS: Title.
return _('Delete group');
}
@ -191,8 +198,10 @@ class DeletegroupAction extends RedirectingAction
'action' => common_local_url('deletegroup', array('id' => $this->group->id))));
$this->elementStart('fieldset');
$this->hidden('token', common_session_token());
// TRANS: Form legend for deleting a group.
$this->element('legend', _('Delete group'));
if (Event::handle('StartDeleteGroupForm', array($this, $this->group))) {
// TRANS: Warning in form for deleleting a group.
$this->element('p', null,
_('Are you sure you want to delete this group? '.
'This will clear all data about the group from the '.
@ -223,4 +232,4 @@ class DeletegroupAction extends RedirectingAction
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
}
}

View File

@ -120,8 +120,11 @@ class DesignadminpanelAction extends AdminPanelAction
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
$msg = _('The server was unable to handle that much POST ' .
'data (%s bytes) due to its current configuration.');
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// 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']));
return;
}
@ -140,7 +143,7 @@ class DesignadminpanelAction extends AdminPanelAction
$themeChanged = ($this->trimmed('theme') != $oldtheme);
}
static $settings = array('theme', 'logo');
static $settings = array('theme', 'logo', 'ssllogo');
$values = array();
@ -230,6 +233,7 @@ class DesignadminpanelAction extends AdminPanelAction
function restoreDefaults()
{
$this->deleteSetting('site', 'logo');
$this->deleteSetting('site', 'ssllogo');
$this->deleteSetting('site', 'theme');
$settings = array(
@ -257,8 +261,8 @@ class DesignadminpanelAction extends AdminPanelAction
function saveBackgroundImage()
{
$filename = null;
if ($_FILES['design_background-image_file']['error'] ==
if (isset($_FILES['design_background-image_file']['error']) &&
$_FILES['design_background-image_file']['error'] ==
UPLOAD_ERR_OK) {
$filepath = null;
@ -293,7 +297,7 @@ class DesignadminpanelAction extends AdminPanelAction
/**
* Save the custom theme if the user uploaded one.
*
*
* @return mixed custom theme name, if succesful, or null if no theme upload.
* @throws ClientException for invalid theme archives
* @throws ServerException if trouble saving the theme files
@ -331,6 +335,11 @@ class DesignadminpanelAction extends AdminPanelAction
$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())) {
$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->unli();
$this->li();
$this->input('ssllogo', _('SSL logo'), 'Logo to show on SSL pages');
$this->unli();
$this->out->elementEnd('ul');
$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
* @link http://status.net/
*/
class EditApplicationAction extends OwnerDesignAction
{
var $msg = null;
@ -51,18 +50,19 @@ class EditApplicationAction extends OwnerDesignAction
function title()
{
return _('Edit Application');
// TRANS: Title for "Edit application" form.
return _('Edit application');
}
/**
* Prepare to run
*/
function prepare($args)
{
parent::prepare($args);
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.'));
return false;
}
@ -74,10 +74,12 @@ class EditApplicationAction extends OwnerDesignAction
$cur = common_current_user();
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);
}
if (!$this->app) {
// TRANS: Client error displayed trying to edit an application that does not exist.
$this->clientError(_('No such application.'));
return false;
}
@ -94,7 +96,6 @@ class EditApplicationAction extends OwnerDesignAction
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -115,8 +116,11 @@ class EditApplicationAction extends OwnerDesignAction
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
$msg = _('The server was unable to handle that much POST ' .
'data (%s bytes) due to its current configuration.');
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// 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']));
return;
}
@ -136,6 +140,7 @@ class EditApplicationAction extends OwnerDesignAction
} elseif ($this->arg('save')) {
$this->trySave();
} else {
// TRANS: Client error displayed submitting invalid form data for edit application.
$this->clientError(_('Unexpected form submission.'));
}
}
@ -158,6 +163,7 @@ class EditApplicationAction extends OwnerDesignAction
$this->element('p', 'error', $this->msg);
} else {
$this->element('p', 'instructions',
// TRANS: Instructions for "Edit application" form.
_('Use this form to edit your application.'));
}
}
@ -174,36 +180,47 @@ class EditApplicationAction extends OwnerDesignAction
$access_type = $this->arg('default_access_type');
if (empty($name)) {
// TRANS: Validation error shown when not providing a name in the "Edit application" form.
$this->showForm(_('Name is required.'));
return;
} 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;
} 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.'));
return;
} elseif (empty($description)) {
// TRANS: Validation error shown when not providing a description in the "Edit application" form.
$this->showForm(_('Description is required.'));
return;
} elseif (Oauth_application::descriptionTooLong($description)) {
$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()));
return;
} 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.'));
return;
} elseif ((mb_strlen($source_url) > 0)
&& !Validate::uri($source_url,
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.'));
return;
} elseif (empty($organization)) {
// TRANS: Validation error shown when not providing an organisation in the "Edit application" form.
$this->showForm(_('Organization is required.'));
return;
} 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;
} elseif (empty($homepage)) {
$this->showForm(_('Organization homepage is required.'));
@ -212,9 +229,11 @@ class EditApplicationAction extends OwnerDesignAction
&& !Validate::uri($homepage,
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.'));
return;
} 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.'));
return;
} elseif (mb_strlen($callback_url) > 0
@ -222,6 +241,7 @@ class EditApplicationAction extends OwnerDesignAction
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.'));
return;
}
@ -258,6 +278,7 @@ class EditApplicationAction extends OwnerDesignAction
// the next step.
if ($result === false) {
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.'));
}
@ -276,7 +297,6 @@ class EditApplicationAction extends OwnerDesignAction
*
* @return boolean true if the name already exists
*/
function nameExists($name)
{
$newapp = Oauth_application::staticGet('name', $name);
@ -286,6 +306,4 @@ class EditApplicationAction extends OwnerDesignAction
return $newapp->id != $this->app->id;
}
}
}

View File

@ -199,13 +199,13 @@ class EditgroupAction extends GroupDesignAction
$this->showForm(_('Homepage is not a valid URL.'));
return;
} 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;
} 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;
} 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;
}

View File

@ -36,8 +36,11 @@ class InviteAction extends CurrentUserDesignAction
{
parent::handle($args);
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.'));
} 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.'),
common_config('site', 'name')));
return;
@ -69,7 +72,9 @@ class InviteAction extends CurrentUserDesignAction
foreach ($addresses as $email) {
$email = trim($email);
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;
}
}
@ -107,8 +112,10 @@ class InviteAction extends CurrentUserDesignAction
function title()
{
if ($this->mode == 'sent') {
return _('Invitation(s) sent');
// TRANS: Page title when invitations have been sent.
return _('Invitations sent');
} else {
// TRANS: Page title when inviting potential users.
return _('Invite new users');
}
}
@ -125,28 +132,48 @@ class InviteAction extends CurrentUserDesignAction
function showInvitationSuccess()
{
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');
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->elementEnd('ul');
}
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');
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->elementEnd('ul');
}
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');
foreach ($this->sent as $other) {
$this->element('li', null, $other);
}
$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!'));
}
}
@ -159,6 +186,7 @@ class InviteAction extends CurrentUserDesignAction
} else {
$this->elementStart('div', 'instructions');
$this->element('p', null,
// TRANS: Form instructions.
_('Use this form to invite your friends and colleagues to use this service.'));
$this->elementEnd('div');
}
@ -179,18 +207,23 @@ class InviteAction extends CurrentUserDesignAction
'class' => 'form_settings',
'action' => common_local_url('invite')));
$this->elementStart('fieldset');
// TRANS: Form legend.
$this->element('legend', null, 'Send an invitation');
$this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
// TRANS: Field label for a list of e-mail addresses.
$this->textarea('addresses', _('Email addresses'),
$this->trimmed('addresses'),
// TRANS: Tooltip for field label for a list of e-mail addresses.
_('Addresses of friends to invite (one per line)'));
$this->elementEnd('li');
$this->elementStart('li');
// TRANS: Field label for a personal message to send to invitees.
$this->textarea('personal', _('Personal message'),
$this->trimmed('personal'),
// TRANS: Tooltip for field label for a personal message to send to invitees.
_('Optionally add a personal message to the invitation.'));
$this->elementEnd('li');
$this->elementEnd('ul');
@ -224,10 +257,16 @@ class InviteAction extends CurrentUserDesignAction
$headers['From'] = mail_notify_from();
$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);
// 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".
"%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. ".

View File

@ -148,7 +148,7 @@ class MakeadminAction extends RedirectingAction
$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
* @link http://status.net/
*/
class NewApplicationAction extends OwnerDesignAction
{
var $msg;
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);
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.'));
return false;
}
@ -91,35 +92,38 @@ class NewApplicationAction extends OwnerDesignAction
function handlePost($args)
{
// Workaround for PHP returning empty $_POST and $_FILES when POST
// Workaround for PHP returning empty $_POST and $_FILES when POST
// length > post_max_size in php.ini
if (empty($_FILES)
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
$msg = _('The server was unable to handle that much POST ' .
'data (%s bytes) due to its current configuration.');
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// 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']));
return;
}
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->clientError(_('There was a problem with your session token.'));
return;
}
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->clientError(_('There was a problem with your session token.'));
return;
}
$cur = common_current_user();
$cur = common_current_user();
if ($this->arg('cancel')) {
common_redirect(common_local_url('oauthappssettings'), 303);
} elseif ($this->arg('save')) {
$this->trySave();
} else {
$this->clientError(_('Unexpected form submission.'));
}
if ($this->arg('cancel')) {
common_redirect(common_local_url('oauthappssettings'), 303);
} elseif ($this->arg('save')) {
$this->trySave();
} else {
$this->clientError(_('Unexpected form submission.'));
}
}
function showForm($msg=null)
@ -162,14 +166,18 @@ class NewApplicationAction extends OwnerDesignAction
$this->showForm(_('Name already in use. Try another one.'));
return;
} elseif (mb_strlen($name) > 255) {
$this->showForm(_('Name is too long (max 255 chars).'));
$this->showForm(_('Name is too long (maximum 255 chars).'));
return;
} elseif (empty($description)) {
$this->showForm(_('Description is required.'));
return;
} elseif (Oauth_application::descriptionTooLong($description)) {
$this->showForm(sprintf(
_('Description is too long (max %d chars).'),
// 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()));
return;
} elseif (empty($source_url)) {
@ -188,7 +196,7 @@ class NewApplicationAction extends OwnerDesignAction
$this->showForm(_('Organization is required.'));
return;
} elseif (mb_strlen($organization) > 255) {
$this->showForm(_('Organization is too long (max 255 chars).'));
$this->showForm(_('Organization is too long (maximum 255 chars).'));
return;
} elseif (empty($homepage)) {
$this->showForm(_('Organization homepage is required.'));

View File

@ -139,13 +139,13 @@ class NewgroupAction extends Action
$this->showForm(_('Homepage is not a valid URL.'));
return;
} 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;
} else if (User_group::descriptionTooLong($description)) {
$this->showForm(sprintf(_('description is too long (max %d chars).'), User_group::maxDescription()));
return;
} 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;
}

View File

@ -147,8 +147,11 @@ class NewmessageAction extends Action
$content_shortened = common_shorten_links($this->content);
if (Message::contentTooLong($content_shortened)) {
$this->showForm(sprintf(_('That\'s too long. ' .
'Max message size is %d chars.'),
// TRANS: Form validation error displayed when message content is too long.
// 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()));
return;
}

View File

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

View File

@ -82,7 +82,7 @@ class NudgeAction extends Action
}
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;
}

View File

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

View File

@ -22,7 +22,7 @@
* @category Settings
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@ -46,17 +46,15 @@ require_once INSTALLDIR . '/lib/apioauthstore.php';
*
* @see SettingsAction
*/
class OauthconnectionssettingsAction extends ConnectSettingsAction
{
var $page = null;
var $id = null;
var $page = null;
var $oauth_token = null;
function 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;
return true;
}
@ -69,6 +67,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
function title()
{
// TRANS: Title for OAuth connection settings.
return _('Connected applications');
}
@ -80,7 +79,8 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
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;
$limit = APPS_PER_PAGE + 1;
$application = $profile->getApplications($offset, $limit);
$connection = $profile->getConnectedApps($offset, $limit);
$cnt = 0;
if (!empty($application)) {
$al = new ApplicationList($application, $user, $this, true);
$cnt = $al->show();
if (!empty($connection)) {
$cal = new ConnectedAppsList($connection, $user, $this);
$cnt = $cal->show();
}
if ($cnt == 0) {
$this->showEmptyListMessage();
}
$this->pagination($this->page > 1, $cnt > APPS_PER_PAGE,
$this->page, 'connectionssettings',
array('nickname' => $user->nickname));
$this->pagination(
$this->page > 1,
$cnt > APPS_PER_PAGE,
$this->page,
'connectionssettings',
array('nickname' => $user->nickname)
);
}
/**
@ -125,7 +129,6 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
*
* @return void
*/
function handlePost()
{
// CSRF protection
@ -138,44 +141,36 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
}
if ($this->arg('revoke')) {
$this->revokeAccess($this->id);
// XXX: Show some indicator to the user of what's been done.
$this->showPage();
$this->revokeAccess($this->oauth_token);
} else {
// TRANS: Client error when submitting a form with unexpected information.
$this->clientError(_('Unexpected form submission.'), 401);
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
*
*/
function revokeAccess($appId)
function revokeAccess($token)
{
$cur = common_current_user();
$app = Oauth_application::staticGet('id', $appId);
if (empty($app)) {
$this->clientError(_('No such application.'), 404);
return false;
}
// XXX: Transaction here?
$appUser = Oauth_application_user::getByKeys($cur, $app);
$appUser = Oauth_application_user::getByUserAndToken($cur, $token);
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);
return false;
}
$app = Oauth_application::staticGet('id', $appUser->application_id);
$datastore = new ApiStatusNetOAuthDataStore();
$datastore->revoke_token($appUser->token, 1);
@ -183,18 +178,38 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
if (!$result) {
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;
}
$msg = 'User %s (id: %d) revoked access to app %s (id: %d)';
common_log(LOG_INFO, sprintf($msg, $cur->nickname,
$cur->id, $app->name, $app->id));
$msg = 'API OAuth - user %s (id: %d) revoked access token %s for app id %d';
common_log(
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()
{
// TRANS: Empty list message when no applications have been authorised yet.
$message = _('You have not authorized any applications to use your account.');
$this->elementStart('div', 'guide');
@ -204,15 +219,26 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
function showSections()
{
$cur = common_current_user();
$cur = common_current_user();
$this->element('h2', null, 'Developers');
$this->elementStart('p');
$this->raw(_('Developers can edit the registration settings for their applications '));
$this->element('a',
array('href' => common_local_url('oauthappssettings')),
'here.');
$this->elementEnd('p');
$this->elementStart('div', array('id' => 'developer-help', 'class' => 'section'));
$this->element('h2', null, 'Developers');
$this->elementStart('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
* @link http://status.net/
*/
class PathsadminpanelAction extends AdminPanelAction
{
/**
* Returns the page title
*
@ -56,6 +54,7 @@ class PathsadminpanelAction extends AdminPanelAction
function title()
{
// TRANS: Title for Paths admin panel.
return _('Paths');
}
@ -64,9 +63,9 @@ class PathsadminpanelAction extends AdminPanelAction
*
* @return string instructions
*/
function getInstructions()
{
// TRANS: Form instructions for Path admin panel.
return _('Path and server settings for this StatusNet site');
}
@ -75,7 +74,6 @@ class PathsadminpanelAction extends AdminPanelAction
*
* @return void
*/
function showForm()
{
$form = new PathsAdminPanelForm($this);
@ -88,20 +86,20 @@ class PathsadminpanelAction extends AdminPanelAction
*
* @return void
*/
function saveSettings()
{
static $settings = array(
'site' => array('path', 'locale_path', 'ssl', 'sslserver'),
'theme' => array('server', 'dir', 'path'),
'avatar' => array('server', 'dir', 'path'),
'background' => array('server', 'dir', 'path')
);
'site' => array('path', 'locale_path', 'ssl', 'sslserver'),
'theme' => array('server', 'dir', 'path', 'sslserver', 'sslpath'),
'avatar' => 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
// can remove some of the boolean processing code --Z
// XXX: If we're only going to have one boolean on thi page we
// can remove some of the boolean processing code --Z
static $booleans = array('site' => array('fancy'));
static $booleans = array('site' => array('fancy'));
$values = array();
@ -131,13 +129,13 @@ class PathsadminpanelAction extends AdminPanelAction
}
}
foreach ($booleans as $section => $parts) {
foreach ($parts as $setting) {
foreach ($booleans as $section => $parts) {
foreach ($parts as $setting) {
Config::save($section, $setting, $values[$section][$setting]);
}
}
}
$config->query('COMMIT');
$config->query('COMMIT');
return;
}
@ -147,25 +145,29 @@ class PathsadminpanelAction extends AdminPanelAction
*
* @return void
*/
function validate(&$values)
{
// Validate 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']));
}
// Validate 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']));
}
// Validate 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']));
}
@ -174,27 +176,28 @@ class PathsadminpanelAction extends AdminPanelAction
// 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'])) {
// 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']));
}
// Validate SSL setup
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.'));
}
}
}
class PathsAdminPanelForm extends AdminForm
{
/**
* ID of the form
*
* @return int ID of the form
*/
function id()
{
return 'form_paths_admin_panel';
@ -205,7 +208,6 @@ class PathsAdminPanelForm extends AdminForm
*
* @return string class of the form
*/
function formClass()
{
return 'form_settings';
@ -216,7 +218,6 @@ class PathsAdminPanelForm extends AdminForm
*
* @return string URL of the action
*/
function action()
{
return common_local_url('pathsadminpanel');
@ -227,30 +228,42 @@ class PathsAdminPanelForm extends AdminForm
*
* @return void
*/
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->elementStart('ul', 'form_data');
$this->li();
$this->input('server', _('Server'), _('Site\'s server hostname.'));
$this->li();
$this->input('server',
// TRANS: Field label in Paths admin panel.
_('Server'),
_('Site\'s server hostname.'));
$this->unli();
$this->li();
$this->input('path', _('Path'), _('Site path'));
$this->input('path',
// TRANS: Field label in Paths admin panel.
_('Path'),
_('Site path.'));
$this->unli();
$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->li();
$this->out->checkbox('fancy', _('Fancy URLs'),
$this->li();
$this->out->checkbox('fancy',
// TRANS: Checkbox label in Paths admin panel.
_('Fancy URLs'),
(bool) $this->value('fancy'),
_('Use fancy (more readable and memorable) URLs?'));
$this->unli();
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
@ -261,35 +274,84 @@ class PathsAdminPanelForm extends AdminForm
$this->out->elementStart('ul', 'form_data');
$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->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->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->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_avatar-paths'));
// TRANS: Fieldset legend in Paths admin panel.
$this->out->element('legend', null, _('Avatars'));
$this->out->elementStart('ul', 'form_data');
$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->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->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->out->elementEnd('ul');
@ -297,47 +359,146 @@ class PathsAdminPanelForm extends AdminForm
$this->out->elementEnd('fieldset');
$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->elementStart('ul', 'form_data');
$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->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->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->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_admin_ssl'));
// TRANS: Fieldset legend in Paths admin panel.
$this->out->element('legend', null, _('SSL'));
$this->out->elementStart('ul', 'form_data');
$this->li();
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
$ssl = array('never' => _('Never'),
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
'sometimes' => _('Sometimes'),
// TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
'always' => _('Always'));
common_debug("site ssl = " . $this->value('site', 'ssl'));
$this->out->dropdown('site-ssl', _('Use SSL'),
$ssl, _('When to use SSL'),
false, $this->value('ssl', 'site'));
// TRANS: Drop down label in Paths admin panel.
$this->out->dropdown('site-ssl',
_('Use SSL'),
// TRANS: Tooltip for field label in Paths admin panel.
$ssl, _('When to use SSL.'),
false,
$this->value('ssl', 'site'));
$this->unli();
$this->li();
$this->input('sslserver', _('SSL server'),
_('Server to direct SSL requests to'), 'site');
$this->input('sslserver',
// 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->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
}
/**
@ -345,11 +506,12 @@ class PathsAdminPanelForm extends AdminForm
*
* @return void
*/
function formActions()
{
$this->out->submit('save', _('Save'), 'submit',
'save', _('Save paths'));
// 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'));
}
/**
@ -365,10 +527,8 @@ class PathsAdminPanelForm extends AdminForm
*
* @return void
*/
function input($setting, $title, $instructions, $section='site')
{
$this->out->input("$section-$setting", $title, $this->value($setting, $section), $instructions);
}
}

View File

@ -57,6 +57,7 @@ class ProfilesettingsAction extends AccountSettingsAction
function title()
{
// TRANS: Page title for profile settings.
return _('Profile settings');
}
@ -68,6 +69,7 @@ class ProfilesettingsAction extends AccountSettingsAction
function getInstructions()
{
// TRANS: Usage instructions for profile settings.
return _('You can update your personal profile info here '.
'so people know more about you.');
}
@ -96,6 +98,7 @@ class ProfilesettingsAction extends AccountSettingsAction
'class' => 'form_settings',
'action' => common_local_url('profilesettings')));
$this->elementStart('fieldset');
// TRANS: Profile settings form legend.
$this->element('legend', null, _('Profile information'));
$this->hidden('token', common_session_token());
@ -103,38 +106,54 @@ class ProfilesettingsAction extends AccountSettingsAction
$this->elementStart('ul', 'form_data');
if (Event::handle('StartProfileFormData', array($this))) {
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('nickname', _('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->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('fullname', _('Full name'),
($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname);
$this->elementEnd('li');
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('homepage', _('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->elementStart('li');
$maxBio = Profile::maxBio();
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);
} else {
// TRANS: Tooltip for field label in form for profile settings.
$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->arg('bio')) ? $this->arg('bio') : $profile->bio,
$bioInstr);
$this->elementEnd('li');
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('location', _('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"'));
$this->elementEnd('li');
if (common_config('location', 'share') == 'user') {
$this->elementStart('li');
// TRANS: Checkbox label in form for profile settings.
$this->checkbox('sharelocation', _('Share my current location when posting notices'),
($this->arg('sharelocation')) ?
$this->arg('sharelocation') : $user->shareLocation());
@ -142,13 +161,17 @@ class ProfilesettingsAction extends AccountSettingsAction
}
Event::handle('EndProfileFormData', array($this));
$this->elementStart('li');
// TRANS: Field label in form for profile settings.
$this->input('tags', _('Tags'),
($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'));
$this->elementEnd('li');
$this->elementStart('li');
$language = common_language();
// TRANS: Dropdownlist label in form for profile settings.
$this->dropdown('language', _('Language'),
// TRANS: Tooltip for dropdown list label in form for profile settings.
get_nice_language_list(), _('Preferred language'),
false, $language);
$this->elementEnd('li');
@ -158,12 +181,15 @@ class ProfilesettingsAction extends AccountSettingsAction
$timezones[$v] = $v;
}
$this->elementStart('li');
// TRANS: Dropdownlist label in form for profile settings.
$this->dropdown('timezone', _('Timezone'),
// TRANS: Tooltip for dropdown list label in form for profile settings.
$timezones, _('What timezone are you normally in?'),
true, $timezone);
$this->elementEnd('li');
$this->elementStart('li');
$this->checkbox('autosubscribe',
// TRANS: Checkbox label in form for profile settings.
_('Automatically subscribe to whoever '.
'subscribes to me (best for non-humans)'),
($this->arg('autosubscribe')) ?
@ -171,7 +197,8 @@ class ProfilesettingsAction extends AccountSettingsAction
$this->elementEnd('li');
}
$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('form');
@ -212,33 +239,46 @@ class ProfilesettingsAction extends AccountSettingsAction
if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64,
'format' => NICKNAME_FMT))) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.'));
return;
} else if (!User::allowed_nickname($nickname)) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Not a valid nickname.'));
return;
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!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.'));
return;
} 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;
} 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()));
return;
} 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;
} else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Timezone not selected.'));
return;
} else if ($this->nicknameExists($nickname)) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Nickname already in use. Try another one.'));
return;
} 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;
}
@ -250,6 +290,8 @@ class ProfilesettingsAction extends AccountSettingsAction
foreach ($tags as $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));
return;
}
@ -280,6 +322,7 @@ class ProfilesettingsAction extends AccountSettingsAction
if ($result === false) {
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.'));
return;
} else {
@ -303,6 +346,8 @@ class ProfilesettingsAction extends AccountSettingsAction
if ($result === false) {
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.'));
return;
}
@ -360,6 +405,7 @@ class ProfilesettingsAction extends AccountSettingsAction
if ($result === false) {
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.'));
return;
}
@ -372,6 +418,7 @@ class ProfilesettingsAction extends AccountSettingsAction
if ($result === false) {
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.'));
return;
}
@ -380,6 +427,7 @@ class ProfilesettingsAction extends AccountSettingsAction
$result = $user->setSelfTags($tags);
if (!$result) {
// TRANS: Server error thrown when user profile settings tags could not be saved.
$this->serverError(_('Couldn\'t save tags.'));
return;
}
@ -388,6 +436,7 @@ class ProfilesettingsAction extends AccountSettingsAction
Event::handle('EndProfileSaveForm', array($this));
common_broadcast_profile($profile);
// TRANS: Confirmation shown when user profile settings are saved.
$this->showForm(_('Settings saved.'), true);
}

View File

@ -44,7 +44,6 @@ define('TAGS_PER_PAGE', 100);
* @copyright 2008-2009 StatusNet, Inc.
* @link http://status.net/
*/
class PublictagcloudAction extends Action
{
function isReadOnly($args)
@ -54,24 +53,37 @@ class PublictagcloudAction extends Action
function title()
{
// TRANS: Title for public tag cloud.
return _('Public tag cloud');
}
function showPageNotice()
{
$this->element('p', 'instructions',
sprintf(_('These are most popular recent tags on %s '),
// TRANS: Instructions (more used like an explanation/header).
// TRANS: %s is the StatusNet sitename.
sprintf(_('These are most popular recent tags on %s'),
common_config('site', 'name')));
}
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.') . ' ';
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!');
}
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!');
}

View File

@ -224,14 +224,16 @@ class RegisterAction extends Action
$this->showForm(_('Homepage is not a valid URL.'));
return;
} 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;
} 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()));
return;
} 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;
} else if (strlen($password) < 6) {
$this->showForm(_('Password must be 6 or more characters.'));
@ -465,7 +467,12 @@ class RegisterAction extends Action
$this->elementStart('li');
$maxBio = Profile::maxBio();
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);
} else {
$bioInstr = _('Describe yourself and your interests');

View File

@ -227,7 +227,7 @@ class ShowfavoritesAction extends OwnerDesignAction
function showContent()
{
$nl = new NoticeList($this->notice, $this);
$nl = new FavoritesNoticeList($this->notice, $this);
$cnt = $nl->show();
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.

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
* @link http://status.net/
*/
class ShowstreamAction extends ProfileAction
{
function isReadOnly($args)
@ -84,7 +83,6 @@ class ShowstreamAction extends ProfileAction
function handle($args)
{
// Looks like we're good; start output
// For YADIS discovery, we also have a <meta> tag
@ -186,7 +184,6 @@ class ShowstreamAction extends ProfileAction
$this->element('link', array('rel' => 'EditURI',
'type' => 'application/rsd+xml',
'href' => $rsd));
}
function showProfile()
@ -275,7 +272,7 @@ class ProfileNoticeList extends NoticeList
}
}
class ProfileNoticeListItem extends NoticeListItem
class ProfileNoticeListItem extends DoFollowListItem
{
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
* @link http://status.net/
*/
class SubscribersAction extends GalleryAction
{
function title()
{
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);
} 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'),
$this->user->nickname,
$this->page);
@ -60,10 +63,14 @@ class SubscribersAction extends GalleryAction
$user = common_current_user();
if ($user && ($user->id == $this->profile->id)) {
$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 '.
'your notices.'));
} else {
$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 '.
'listen to %s\'s notices.'),
$this->profile->nickname));
@ -105,12 +112,20 @@ class SubscribersAction extends GalleryAction
if (common_logged_in()) {
$current_user = common_current_user();
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 {
// 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);
}
}
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);
}

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
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
class SubscriptionsAction extends GalleryAction
{
function title()
{
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);
} 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'),
$this->user->nickname,
$this->page);
@ -62,10 +63,14 @@ class SubscriptionsAction extends GalleryAction
$user = common_current_user();
if ($user && ($user->id == $this->profile->id)) {
$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 '.
'you listen to.'));
} else {
$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 '.
'notices %s listens to.'),
$this->profile->nickname));
@ -123,12 +128,24 @@ class SubscriptionsAction extends GalleryAction
if (common_logged_in()) {
$current_user = common_current_user();
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 {
// 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);
}
}
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);
}
@ -205,6 +222,7 @@ class SubscriptionsListItem extends SubscriptionListItem
}
$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'));
} else {
$this->out->hidden('jabber', $sub->jabber);
@ -219,11 +237,13 @@ class SubscriptionsListItem extends SubscriptionListItem
}
$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'));
} else {
$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');
return;
}

View File

@ -139,7 +139,42 @@ class Design extends Memcached_DataObject
static function url($filename)
{
$path = common_config('background', 'path');
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');
}
}
$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] != '/') {
$path .= '/';
@ -149,25 +184,6 @@ class Design extends Memcached_DataObject
$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;
}

View File

@ -261,22 +261,41 @@ class File extends Memcached_DataObject
// TRANS: Client exception thrown if a file upload does not have a valid name.
throw new ClientException(_("Invalid filename."));
}
if(common_config('site','private')) {
if (common_config('site','private')) {
return common_local_url('getfile',
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 {
$server = $sslserver;
$path = common_config('attachments', 'sslpath');
if (empty($path)) {
$path = common_config('attachments', 'path');
}
}
$protocol = 'https';
} else {
$path = common_config('attachments', 'path');
if ($path[strlen($path)-1] != '/') {
$path .= '/';
}
if ($path[0] != '/') {
$path = '/'.$path;
}
$server = common_config('attachments', 'server');
if (empty($server)) {
@ -285,19 +304,18 @@ class File extends Memcached_DataObject
$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;
}
if ($path[strlen($path)-1] != '/') {
$path .= '/';
}
if ($path[0] != '/') {
$path = '/'.$path;
}
return $protocol.'://'.$server.$path.$filename;
}
function getEnclosure(){

View File

@ -60,7 +60,7 @@ class Notice_tag extends Memcached_DataObject
}
if ($max_id != 0) {
$nt->whereAdd('notice_id < ' . $max_id);
$nt->whereAdd('notice_id <= ' . $max_id);
}
$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 */
###END_AUTOCODE
static function getByKeys($user, $app)
static function getByUserAndToken($user, $token)
{
if (empty($user) || empty($app)) {
if (empty($user) || empty($token)) {
return null;
}
$oau = new Oauth_application_user();
$oau->profile_id = $user->id;
$oau->application_id = $app->id;
$oau->profile_id = $user->id;
$oau->token = $token;
$oau->limit(1);
$result = $oau->find(true);
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) {
$query .= " and id < $max_id";
$query .= " and id <= $max_id";
}
$query .= ' order by id DESC';
@ -240,7 +240,7 @@ class Profile extends Memcached_DataObject
}
if ($max_id != 0) {
$query .= " and id < $max_id";
$query .= " and id <= $max_id";
}
$query .= ' order by id DESC';
@ -401,10 +401,10 @@ class Profile extends Memcached_DataObject
return $profile;
}
function getApplications($offset = 0, $limit = null)
function getConnectedApps($offset = 0, $limit = null)
{
$qry =
'SELECT a.* ' .
'SELECT u.* ' .
'FROM oauth_application_user u, oauth_application a ' .
'WHERE u.profile_id = %d ' .
'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()
@ -758,43 +758,52 @@ class Profile extends Memcached_DataObject
function grantRole($name)
{
$role = new Profile_role();
if (Event::handle('StartGrantRole', array($this, $name))) {
$role->profile_id = $this->id;
$role->role = $name;
$role->created = common_sql_now();
$role = new Profile_role();
$result = $role->insert();
$role->profile_id = $this->id;
$role->role = $name;
$role->created = common_sql_now();
if (!$result) {
common_log_db_error($role, 'INSERT', __FILE__);
return false;
$result = $role->insert();
if (!$result) {
throw new Exception("Can't save role '$name' for profile '{$this->id}'");
}
Event::handle('EndGrantRole', array($this, $name));
}
return true;
return $result;
}
function revokeRole($name)
{
$role = Profile_role::pkeyGet(array('profile_id' => $this->id,
'role' => $name));
if (Event::handle('StartRevokeRole', array($this, $name))) {
if (empty($role)) {
// TRANS: Exception thrown when trying to revoke an existing role for a user that does not exist.
// TRANS: %1$s is the role name, %2$s is the user ID (number).
throw new Exception(sprintf(_('Cannot revoke role "%1$s" for user #%2$d; does not exist.'),$name, $this->id));
$role = Profile_role::pkeyGet(array('profile_id' => $this->id,
'role' => $name));
if (empty($role)) {
// TRANS: Exception thrown when trying to revoke an existing role for a user that does not exist.
// TRANS: %1$s is the role name, %2$s is the user ID (number).
throw new Exception(sprintf(_('Cannot revoke role "%1$s" for user #%2$d; does not exist.'),$name, $this->id));
}
$result = $role->delete();
if (!$result) {
common_log_db_error($role, 'DELETE', __FILE__);
// TRANS: Exception thrown when trying to revoke a role for a user with a failing database query.
// TRANS: %1$s is the role name, %2$s is the user ID (number).
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;
}
$result = $role->delete();
if (!$result) {
common_log_db_error($role, 'DELETE', __FILE__);
// TRANS: Exception thrown when trying to revoke a role for a user with a failing database query.
// TRANS: %1$s is the role name, %2$s is the user ID (number).
throw new Exception(sprintf(_('Cannot revoke role "%1$s" for user #%2$d; database error.'),$name, $this->id));
}
return true;
}
function isSandboxed()

View File

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

View File

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

View File

@ -875,4 +875,35 @@ class User extends Memcached_DataObject
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()
{
if ($this->id) {
// Safe to delete in bulk for now
$related = array('Group_inbox',
'Group_block',
'Group_member',
'Related_group');
Event::handle('UserGroupDeleteRelated', array($this, &$related));
foreach ($related as $cls) {
$inst = new $cls();
$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...
@ -584,6 +595,10 @@ class User_group extends Memcached_DataObject
if ($local) {
$local->delete();
}
// blow the cached ids
self::blow('user_group:notice_ids:%d', $this->id);
} else {
common_log(LOG_WARN, "Ambiguous user_group->delete(); skipping related tables.");
}

View File

@ -401,6 +401,18 @@ modified = 384
profile_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]
id = 129
nickname = 130

View File

@ -34,7 +34,7 @@ VALUES
('mbpidgin','mbpidgin','http://code.google.com/p/microblog-purple/', now()),
('Mobidentica', 'Mobidentica', 'http://www.substanceofcode.com/software/mobidentica/', 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()),
('peoplebrowsr', 'PeopleBrowsr', 'http://www.peoplebrowsr.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)
) 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 */
create table oid_associations (

View File

@ -45,14 +45,62 @@ require INSTALLDIR . '/lib/installer.php';
* Helper class for building form
*/
class Posted {
/**
* HTML-friendly escaped string for the POST param of given name, or empty.
* @param string $name
* @return string
*/
function value($name)
{
return htmlspecialchars($this->string($name));
}
/**
* 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 htmlspecialchars(strval($_POST[$name]));
return $this->dequote($_POST[$name]);
} else {
return '';
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;
$post = new Posted();
$dbRadios = '';
if (isset($_POST['dbtype'])) {
$dbtype = $_POST['dbtype'];
} else {
$dbtype = null;
}
$dbtype = $post->raw('dbtype');
foreach (self::$dbModules as $type => $info) {
if ($this->checkExtension($info['check_module'])) {
if ($dbtype == null || $dbtype == $type) {
@ -245,19 +289,20 @@ STR;
*/
function prepare()
{
$this->host = $_POST['host'];
$this->dbtype = $_POST['dbtype'];
$this->database = $_POST['database'];
$this->username = $_POST['dbusername'];
$this->password = $_POST['dbpassword'];
$this->sitename = $_POST['sitename'];
$this->fancy = !empty($_POST['fancy']);
$post = new Posted();
$this->host = $post->string('host');
$this->dbtype = $post->string('dbtype');
$this->database = $post->string('database');
$this->username = $post->string('dbusername');
$this->password = $post->string('dbpassword');
$this->sitename = $post->string('sitename');
$this->fancy = (bool)$post->string('fancy');
$this->adminNick = strtolower($_POST['admin_nickname']);
$this->adminPass = $_POST['admin_password'];
$adminPass2 = $_POST['admin_password2'];
$this->adminEmail = $_POST['admin_email'];
$this->adminUpdates = $_POST['admin_updates'];
$this->adminNick = strtolower($post->string('admin_nickname'));
$this->adminPass = $post->string('admin_password');
$adminPass2 = $post->string('admin_password2');
$this->adminEmail = $post->string('admin_email');
$this->adminUpdates = $post->string('admin_updates');
$this->server = $_SERVER['HTTP_HOST'];
$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',
'href' => Theme::path('favicon.ico')));
} else {
// favicon.ico should be HTTPS if the rest of the page is
$this->element('link', array('rel' => 'shortcut icon',
'href' => common_path('favicon.ico')));
'href' => common_path('favicon.ico', StatusNet::isHTTPS())));
}
if (common_config('site', 'mobile')) {
@ -361,9 +362,9 @@ class Action extends HTMLOutputter // lawsuit
*/
function showBody()
{
$this->elementStart('body', (common_current_user()) ? array('id' => $this->trimmed('action'),
$this->elementStart('body', (common_current_user()) ? array('id' => strtolower($this->trimmed('action')),
'class' => 'user_in')
: array('id' => $this->trimmed('action')));
: array('id' => strtolower($this->trimmed('action'))));
$this->elementStart('div', array('id' => 'wrap'));
if (Event::handle('StartShowHeader', array($this))) {
$this->showHeader();
@ -397,7 +398,10 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowSiteNotice', array($this));
}
if (common_logged_in()) {
$this->showNoticeForm();
if (Event::handle('StartShowNoticeForm', array($this))) {
$this->showNoticeForm();
Event::handle('EndShowNoticeForm', array($this));
}
} else {
$this->showAnonymousMessage();
}
@ -415,18 +419,43 @@ class Action extends HTMLOutputter // lawsuit
'class' => 'vcard'));
if (Event::handle('StartAddressData', array($this))) {
if (common_config('singleuser', 'enabled')) {
$user = User::singleUser();
$url = common_local_url('showstream',
array('nickname' => common_config('singleuser', 'nickname')));
array('nickname' => $user->nickname));
} else {
$url = common_local_url('public');
}
$this->elementStart('a', array('class' => 'url home bookmark',
'href' => $url));
if (common_config('site', 'logo') || file_exists(Theme::file('logo.png'))) {
if (StatusNet::isHTTPS()) {
$logoUrl = common_config('site', 'ssllogo');
if (empty($logoUrl)) {
// if logo is an uploaded file, try to fall back to HTTPS file URL
$httpUrl = common_config('site', 'logo');
if (!empty($httpUrl)) {
$f = File::staticGet('url', $httpUrl);
if (!empty($f) && !empty($f->filename)) {
// this will handle the HTTPS case
$logoUrl = File::url($f->filename);
}
}
}
} else {
$logoUrl = common_config('site', 'logo');
}
if (empty($logoUrl) && file_exists(Theme::file('logo.png'))) {
// This should handle the HTTPS case internally
$logoUrl = Theme::path('logo.png');
}
if (!empty($logoUrl)) {
$this->element('img', array('class' => 'logo photo',
'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png'),
'src' => $logoUrl,
'alt' => common_config('site', 'name')));
}
$this->text(' ');
$this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
$this->elementEnd('a');
@ -498,20 +527,20 @@ class Action extends HTMLOutputter // lawsuit
}
// TRANS: Tooltip for main menu option "Login"
$tooltip = _m('TOOLTIP', 'Login to the site');
// TRANS: Main menu option when not logged in to log in
$this->menuItem(common_local_url('login'),
// TRANS: Main menu option when not logged in to log in
_m('MENU', 'Login'), $tooltip, false, 'nav_login');
}
// TRANS: Tooltip for main menu option "Help"
$tooltip = _m('TOOLTIP', 'Help me!');
// TRANS: Main menu option for help on the StatusNet site
$this->menuItem(common_local_url('doc', array('title' => 'help')),
// TRANS: Main menu option for help on the StatusNet site
_m('MENU', 'Help'), $tooltip, false, 'nav_help');
if ($user || !common_config('site', 'private')) {
// TRANS: Tooltip for main menu option "Search"
$tooltip = _m('TOOLTIP', 'Search for people or text');
// TRANS: Main menu option when logged in or when the StatusNet instance is not private
$this->menuItem(common_local_url('peoplesearch'),
// TRANS: Main menu option when logged in or when the StatusNet instance is not private
_m('MENU', 'Search'), $tooltip, false, 'nav_search');
}
Event::handle('EndPrimaryNav', array($this));
@ -891,8 +920,26 @@ class Action extends HTMLOutputter // lawsuit
case 'cc': // fall through
default:
$this->elementStart('p');
$image = common_config('license', 'image');
$sslimage = common_config('license', 'sslimage');
if (StatusNet::isHTTPS()) {
if (!empty($sslimage)) {
$url = $sslimage;
} else if (preg_match('#^http://i.creativecommons.org/#', $image)) {
// CC support HTTPS on their images
$url = preg_replace('/^http/', 'https', $image);
} else {
// Better to show mixed content than no content
$url = $image;
}
} else {
$url = $image;
}
$this->element('img', array('id' => 'license_cc',
'src' => common_config('license', 'image'),
'src' => $url,
'alt' => common_config('license', 'title'),
'width' => '80',
'height' => '15'));

View File

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

View File

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

View File

@ -23,24 +23,71 @@ require_once INSTALLDIR . '/lib/oauthstore.php';
class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
{
function lookup_consumer($consumer_key)
function lookup_consumer($consumerKey)
{
$con = Consumer::staticGet('consumer_key', $consumer_key);
$con = Consumer::staticGet('consumer_key', $consumerKey);
if (!$con) {
return null;
// Create an anon consumer and anon application if one
// doesn't exist already
if ($consumerKey == 'anonymous') {
common_debug("API OAuth - creating anonymous consumer");
$con = new Consumer();
$con->consumer_key = $consumerKey;
$con->consumer_secret = $consumerKey;
$con->created = common_sql_now();
$result = $con->insert();
if (!$result) {
// TRANS: Server error displayed when trying to create an anynymous OAuth consumer.
$this->serverError(_('Could not create anonymous consumer.'));
}
$app = Oauth_application::getByConsumerKey('anonymous');
if (!$app) {
common_debug("API OAuth - creating anonymous application");
$app = new OAuth_application();
$app->owner = 1; // XXX: What to do here?
$app->consumer_key = $con->consumer_key;
$app->name = 'anonymous';
$app->icon = 'default-avatar-stream.png'; // XXX: Fix this!
$app->description = "An anonymous application";
// XXX: allow the user to set the access type when
// authorizing? Currently we default to r+w for anonymous
// OAuth client applications
$app->access_type = 3; // read + write
$app->type = 2; // desktop
$app->created = common_sql_now();
$id = $app->insert();
if (!$id) {
// TRANS: Server error displayed when trying to create an anynymous OAuth application.
$this->serverError(_("Could not create anonymous OAuth application."));
}
}
} else {
return null;
}
}
return new OAuthConsumer($con->consumer_key,
$con->consumer_secret);
return new OAuthConsumer(
$con->consumer_key,
$con->consumer_secret
);
}
function getAppByRequestToken($token_key)
{
// Look up the full req tokenx
$req_token = $this->lookup_token(null,
'request',
$token_key);
// Look up the full req token
$req_token = $this->lookup_token(
null,
'request',
$token_key
);
if (empty($req_token)) {
common_debug("couldn't get request token from oauth datastore");
@ -58,7 +105,6 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
}
// Look up the app
$app = new Oauth_application();
$app->consumer_key = $token->consumer_key;
$result = $app->find(true);
@ -74,8 +120,13 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
function new_access_token($token, $consumer, $verifier)
{
common_debug(
'new_access_token("' . $token->key . '","' . $consumer->key. '","' . $verifier . '")',
__FILE__
sprintf(
"New access token from request token %s, consumer %s and verifier %s ",
$token,
$consumer,
$verifier
),
__FILE__
);
$rt = new Token();
@ -89,73 +140,123 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized
common_debug('request token found.', __FILE__);
common_debug('Request token found.', __FILE__);
// find the associated user of the app
// find the app and profile associated with this token
$tokenAssoc = Oauth_token_association::staticGet('token', $rt->tok);
if (!$tokenAssoc) {
throw new Exception(
// TRANS: Exception thrown when no token association could be found.
_('Could not find a profile and application associated with the request token.')
);
}
// check to see if we have previously issued an access token for this application
// and profile
$appUser = new Oauth_application_user();
$appUser->application_id = $app->id;
$appUser->token = $rt->tok;
$appUser->profile_id = $tokenAssoc->profile_id;
$result = $appUser->find(true);
if (!empty($result)) {
common_debug("Ouath app user found.");
} else {
common_debug("Oauth app user not found. app id $app->id token $rt->tok");
return null;
}
// go ahead and make the access token
common_log(LOG_INFO,
sprintf(
"Existing access token found for application %s, profile %s.",
$app->id,
$tokenAssoc->profile_id
)
);
$at = new Token();
$at->consumer_key = $consumer->key;
$at->tok = common_good_rand(16);
$at->secret = common_good_rand(16);
$at->type = 1; // access
$at->verifier = $verifier;
$at->verified_callback = $rt->verified_callback; // 1.0a
$at->created = DB_DataObject_Cast::dateTime();
$at = new Token();
if (!$at->insert()) {
$e = $at->_lastError;
common_debug('access token "'.$at->tok.'" not inserted: "'.$e->message.'"', __FILE__);
return null;
} else {
common_debug('access token "'.$at->tok.'" inserted', __FILE__);
// burn the old one
$orig_rt = clone($rt);
$rt->state = 2; // used
if (!$rt->update($orig_rt)) {
return null;
// fetch the full access token
$at->consumer_key = $consumer->key;
$at->tok = $appUser->token;
$result = $at->find(true);
if (!$result) {
throw new Exception(
// TRANS: Exception thrown when no access token can be issued.
_('Could not issue access token.')
);
}
common_debug('request token "'.$rt->tok.'" updated', __FILE__);
// update the token from req to access for the user
// Yay, we can re-issue the access token
return new OAuthToken($at->tok, $at->secret);
$orig = clone($appUser);
$appUser->token = $at->tok;
} else {
// 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.
common_log(LOG_INFO,
sprintf(
"Creating new access token for application %s, profile %s.",
$app->id,
$tokenAssoc->profile_id
)
);
$appUser->access_type = $app->access_type;
// make a brand new access token
$at = new Token();
$result = $appUser->update($orig);
$at->consumer_key = $consumer->key;
$at->tok = common_good_rand(16);
$at->secret = common_good_rand(16);
$at->type = 1; // access
$at->verifier = $verifier;
$at->verified_callback = $rt->verified_callback; // 1.0a
$at->created = common_sql_now();
if (empty($result)) {
common_debug('couldn\'t update OAuth app user.');
if (!$at->insert()) {
$e = $at->_lastError;
common_debug('access token "' . $at->tok . '" not inserted: "' . $e->message . '"', __FILE__);
return null;
} else {
common_debug('access token "' . $at->tok . '" inserted', __FILE__);
// burn the old one
$orig_rt = clone($rt);
$rt->state = 2; // used
if (!$rt->update($orig_rt)) {
return null;
}
common_debug('request token "' . $rt->tok . '" updated', __FILE__);
}
// insert a new Oauth_application_user record w/access token
$appUser = new Oauth_application_user();
$appUser->profile_id = $tokenAssoc->profile_id;;
$appUser->application_id = $app->id;
$appUser->access_type = $app->access_type;
$appUser->token = $at->tok;
$appUser->created = common_sql_now();
$result = $appUser->insert();
if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__);
// TRANS: Server error displayed when a database error occurs.
$this->serverError(_('Database error inserting OAuth application user.'));
}
// Okay, good
return new OAuthToken($at->tok, $at->secret);
}
} else {
// the token was not authorized or not verfied
common_log(
LOG_INFO,
sprintf(
"API OAuth - Attempt to exchange unauthorized or unverified request token %s for an access token.",
$rt->tok
)
);
return null;
}
}
@ -174,9 +275,9 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
* @return void
*/
public function revoke_token($token_key, $type = 0) {
$rt = new Token();
$rt->tok = $token_key;
$rt->type = $type;
$rt = new Token();
$rt->tok = $token_key;
$rt->type = $type;
$rt->state = 0;
if (!$rt->find(true)) {
@ -198,7 +299,6 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
*
* @return OAuthToken $token a new unauthorized OAuth request token
*/
function new_request_token($consumer, $callback)
{
$t = new Token();
@ -223,6 +323,4 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
return new OAuthToken($t->tok, $t->secret);
}
}
}

View File

@ -22,7 +22,7 @@
* @category Application
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@ -55,14 +55,13 @@ class ApplicationList extends Widget
/** Action object using us. */
var $action = null;
function __construct($application, $owner=null, $action=null, $connections = false)
function __construct($application, $owner=null, $action=null)
{
parent::__construct($action);
$this->application = $application;
$this->owner = $owner;
$this->action = $action;
$this->connections = $connections;
}
function show()
@ -88,24 +87,34 @@ class ApplicationList extends Widget
{
$user = common_current_user();
$this->out->elementStart('li', array('class' => 'application',
'id' => 'oauthclient-' . $this->application->id));
$this->out->elementStart(
'li',
array(
'class' => 'application',
'id' => 'oauthclient-' . $this->application->id
)
);
$this->out->elementStart('span', 'vcard author');
if (!$this->connections) {
$this->out->elementStart('a',
array('href' => common_local_url('showapplication',
array('id' => $this->application->id)),
'class' => 'url'));
} else {
$this->out->elementStart('a', array('href' => $this->application->source_url,
'class' => 'url'));
}
$this->out->elementStart(
'a',
array(
'href' => common_local_url(
'showapplication',
array('id' => $this->application->id)),
'class' => 'url'
)
);
if (!empty($this->application->icon)) {
$this->out->element('img', array('src' => $this->application->icon,
'class' => 'photo avatar'));
$this->out->element(
'img',
array(
'src' => $this->application->icon,
'class' => 'photo avatar'
)
);
}
$this->out->element('span', 'fn', $this->application->name);
@ -114,51 +123,18 @@ class ApplicationList extends Widget
$this->out->raw(' by ');
$this->out->element('a', array('href' => $this->application->homepage,
'class' => 'url'),
$this->application->organization);
$this->out->element(
'a',
array(
'href' => $this->application->homepage,
'class' => 'url'
),
$this->application->organization
);
$this->out->element('p', 'note', $this->application->description);
$this->out->elementEnd('li');
if ($this->connections) {
$appUser = Oauth_application_user::getByKeys($this->owner, $this->application);
if (empty($appUser)) {
common_debug("empty appUser!");
}
$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. */
@ -167,3 +143,162 @@ class ApplicationList extends Widget
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
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
define('STATUSNET_VERSION', '0.9.5');
define('STATUSNET_VERSION', '0.9.6');
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
define('STATUSNET_CODENAME', 'What\'s The Frequency, Kenneth?');
define('STATUSNET_CODENAME', 'Man on the Moon');
define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48);
@ -133,10 +133,10 @@ try {
// XXX: Throw a conniption if database not installed
// XXX: Find a way to use htmlwriter for this instead of handcoded markup
// TRANS: Error message displayed when no configuration file was found for a StatusNet installation.
echo '<p>'. _('No configuration file found. ') .'</p>';
echo '<p>'. _('No configuration file found.') .'</p>';
// TRANS: Error message displayed when no configuration file was found for a StatusNet installation.
// TRANS: Is followed by a list of directories (separated by HTML breaks).
echo '<p>'. _('I looked for configuration files in the following places: ') .'<br /> ';
echo '<p>'. _('I looked for configuration files in the following places:') .'<br /> ';
echo implode($e->configFiles, '<br />');
// TRANS: Error message displayed when no configuration file was found for a StatusNet installation.
echo '<p>'. _('You may wish to run the installer to fix this.') .'</p>';

View File

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

View File

@ -37,6 +37,7 @@ $default =
'path' => $_path,
'logfile' => null,
'logo' => null,
'ssllogo' => null,
'logdebug' => false,
'fancy' => false,
'locale_path' => INSTALLDIR.'/locale',
@ -210,6 +211,8 @@ $default =
array('server' => null,
'dir' => INSTALLDIR . '/file/',
'path' => $_path . '/file/',
'sslserver' => null,
'sslpath' => null,
'ssl' => null,
'supported' => array('image/png',
'image/jpeg',
@ -314,7 +317,8 @@ $default =
'nofollow' =>
array('subscribers' => true,
'members' => true,
'peopletag' => true),
'peopletag' => true,
'external' => 'sometimes'), // Options: 'sometimes', 'never', default = 'sometimes'
'http' => // HTTP client settings when contacting other sites
array('ssl_cafile' => false, // To enable SSL cert validation, point to a CA bundle (eg '/usr/lib/ssl/certs/ca-certificates.crt')
'curl' => false, // Use CURL backend for HTTP fetches if available. (If not, PHP's socket streams will be used.)

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
* @link http://status.net/
*/
class Feed
{
const RSS1 = 1;

View File

@ -46,7 +46,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
*
* @see Action::showExportList()
*/
class FeedList extends Widget
{
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
* @link http://status.net/
*/
class GroupsByMembersSection extends GroupSection
{
function getGroups()
@ -68,6 +67,7 @@ class GroupsByMembersSection extends GroupSection
function title()
{
// TRANS: Title for groups with the most members section.
return _('Groups with most members');
}

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
* @link http://status.net/
*/
class GroupsByPostsSection extends GroupSection
{
function getGroups()
@ -68,6 +67,7 @@ class GroupsByPostsSection extends GroupSection
function title()
{
// TRANS: Title for groups with the most posts section.
return _('Groups with most posts');
}

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
* @link http://status.net/
*/
class GroupSection extends Section
{
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
* @link http://status.net/
*/
class GroupTagCloudSection extends TagCloudSection
{
var $group = null;
@ -53,6 +52,8 @@ class GroupTagCloudSection extends TagCloudSection
function title()
{
// TRANS: Title for group tag cloud section.
// TRANS: %s is a group name.
return sprintf(_('Tags in %s group\'s notices'), $this->group->nickname);
}

View File

@ -352,22 +352,57 @@ class HTMLOutputter extends XMLOutputter
*/
function script($src, $type='text/javascript')
{
if(Event::handle('StartScriptElement', array($this,&$src,&$type))) {
if (Event::handle('StartScriptElement', array($this,&$src,&$type))) {
$url = parse_url($src);
if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment']))
{
if (empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) {
// XXX: this seems like a big assumption
if (strpos($src, 'plugins/') === 0 || strpos($src, 'local/') === 0) {
$src = common_path($src) . '?version=' . STATUSNET_VERSION;
$src = common_path($src, StatusNet::isHTTPS()) . '?version=' . STATUSNET_VERSION;
}else{
} else {
$path = common_config('javascript', 'path');
if (StatusNet::isHTTPS()) {
if (empty($path)) {
$path = common_config('site', 'path') . '/js/';
$sslserver = common_config('javascript', 'sslserver');
if (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 {
$path = common_config('javascript', 'path');
if (empty($path)) {
$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] != '/') {
@ -378,32 +413,13 @@ class HTMLOutputter extends XMLOutputter
$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;
}
}
$this->element('script', array('type' => $type,
'src' => $src),
' ');
'src' => $src),
' ');
Event::handle('EndScriptElement', array($this,$src,$type));
}
@ -453,7 +469,7 @@ class HTMLOutputter extends XMLOutputter
if(file_exists(Theme::file($src,$theme))){
$src = Theme::path($src, $theme);
}else{
$src = common_path($src);
$src = common_path($src, StatusNet::isHTTPS());
}
$src.= '?version=' . STATUSNET_VERSION;
}

View File

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

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

View File

@ -34,7 +34,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
require_once 'Net/URL/Mapper.php';
class StatusNet_URL_Mapper extends Net_URL_Mapper {
private static $_singleton = null;
private function __construct()
@ -71,7 +70,6 @@ class StatusNet_URL_Mapper extends Net_URL_Mapper {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class Router
{
var $m = null;
@ -553,11 +551,19 @@ class Router
'format' => '(xml|json)'));
// blocks
$m->connect('api/blocks/create.:format',
array('action' => 'ApiBlockCreate',
'format' => '(xml|json)'));
$m->connect('api/blocks/create/:id.:format',
array('action' => 'ApiBlockCreate',
'id' => '[a-zA-Z0-9]+',
'format' => '(xml|json)'));
$m->connect('api/blocks/destroy.:format',
array('action' => 'ApiBlockDestroy',
'format' => '(xml|json)'));
$m->connect('api/blocks/destroy/:id.:format',
array('action' => 'ApiBlockDestroy',
'id' => '[a-zA-Z0-9]+',
@ -692,7 +698,6 @@ class Router
$m->connect('admin/snapshot', array('action' => 'snapshotadminpanel'));
$m->connect('admin/license', array('action' => 'licenseadminpanel'));
$m->connect('getfile/:filename',
array('action' => 'getfile'),
array('filename' => '[A-Za-z0-9._-]+'));
@ -701,16 +706,8 @@ class Router
if (common_config('singleuser', 'enabled')) {
$user = User::siteOwner();
if (!empty($user)) {
$nickname = $user->nickname;
} else {
$nickname = common_config('singleuser', 'nickname');
if (empty($nickname)) {
throw new ServerException(_("No single user defined for single-user mode."));
}
}
$user = User::singleUser();
$nickname = $user->nickname;
foreach (array('subscriptions', 'subscribers',
'all', 'foaf', 'xrds',
@ -765,9 +762,7 @@ class Router
$m->connect('',
array('action' => 'showstream',
'nickname' => $nickname));
} else {
$m->connect('', array('action' => 'public'));
$m->connect('rss', array('action' => 'publicrss'));
$m->connect('featuredrss', array('action' => 'featuredrss'));
@ -848,7 +843,8 @@ class Router
} catch (Net_URL_Mapper_InvalidException $e) {
common_log(LOG_ERR, "Problem getting route for $path - " .
$e->getMessage());
$cac = new ClientErrorAction("Page not found.", 404);
// TRANS: Client error on action trying to visit a non-existing page.
$cac = new ClientErrorAction(_('Page not found.'), 404);
$cac->showPage();
}
@ -875,7 +871,16 @@ class Router
if ($qpos !== false) {
$url = substr($url, 0, $qpos+1) .
str_replace('?', '&', substr($url, $qpos+1));
// @fixme this is a hacky workaround for http_build_query in the
// lower-level code and bad configs that set the default separator
// to &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;
}
}

View File

@ -169,7 +169,6 @@ class StatusNet
return $sites;
}
/**
* Fire initialization events for all instantiated plugins.
*/
@ -220,7 +219,7 @@ class StatusNet
{
return self::$is_api;
}
public function setApi($mode)
{
self::$is_api = $mode;
@ -368,6 +367,18 @@ class StatusNet
}
}
}
/**
* Are we running from the web with HTTPS?
*
* @return boolean true if we're running with HTTPS; else false
*/
static function isHTTPS()
{
// There are some exceptions to this; add them here!
return !empty($_SERVER['HTTPS']);
}
}
class NoConfigException extends Exception

View File

@ -38,7 +38,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* Themes are directories with some expected sub-directories and files
* in them. They're found in either local/theme (for locally-installed themes)
* or theme/ subdir of installation dir.
*
*
* Note that the 'local' directory can be overridden as $config['local']['path']
* and $config['local']['dir'] etc.
*
@ -104,25 +104,61 @@ class Theme
/**
* Build a full URL to the given theme's base directory, possibly
* using an offsite theme server path.
*
*
* @param string $group configuration section name to pull paths from
* @param string $fallbackSubdir default subdirectory under INSTALLDIR
* @param string $name theme name
*
*
* @return string URL
*
*
* @todo consolidate code with that for other customizable paths
*/
protected function relativeThemePath($group, $fallbackSubdir, $name)
{
$path = common_config($group, 'path');
if (StatusNet::isHTTPS()) {
if (empty($path)) {
$path = common_config('site', 'path') . '/';
if ($fallbackSubdir) {
$path .= $fallbackSubdir . '/';
$sslserver = common_config($group, 'sslserver');
if (empty($sslserver)) {
if (is_string(common_config('site', 'sslserver')) &&
mb_strlen(common_config('site', 'sslserver')) > 0) {
$server = common_config('site', 'sslserver');
} else if (common_config('site', 'server')) {
$server = common_config('site', 'server');
}
$path = common_config('site', 'path') . '/';
if ($fallbackSubdir) {
$path .= $fallbackSubdir . '/';
}
} else {
$server = $sslserver;
$path = common_config($group, 'sslpath');
if (empty($path)) {
$path = common_config($group, 'path');
}
}
$protocol = 'https';
} else {
$path = common_config($group, 'path');
if (empty($path)) {
$path = common_config('site', 'path') . '/';
if ($fallbackSubdir) {
$path .= $fallbackSubdir . '/';
}
}
$server = common_config($group, 'server');
if (empty($server)) {
$server = common_config('site', 'server');
}
$protocol = 'http';
}
if ($path[strlen($path)-1] != '/') {
@ -133,27 +169,7 @@ class Theme
$path = '/'.$path;
}
$server = common_config($group, 'server');
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;
return $protocol.'://'.$server.$path.$name;
}
/**
@ -221,7 +237,7 @@ class Theme
/**
* Pull data from the theme's theme.ini file.
* @fixme calling getFile will fall back to default theme, this may be unsafe.
*
*
* @return associative array of strings
*/
function getMetadata()

View File

@ -145,7 +145,6 @@ function common_switch_locale($language=null)
textdomain("statusnet");
}
function common_timezone()
{
if (common_logged_in()) {
@ -860,7 +859,8 @@ function common_linkify($url) {
$longurl = $url;
}
}
$attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external');
$attrs = array('href' => $canon, 'title' => $longurl);
$is_attachment = false;
$attachment_id = null;
@ -896,6 +896,16 @@ function common_linkify($url) {
$attrs['id'] = "attachment-{$attachment_id}";
}
// Whether to nofollow
$nf = common_config('nofollow', 'external');
if ($nf == 'never') {
$attrs['rel'] = 'external';
} else {
$attrs['rel'] = 'nofollow external';
}
return XMLStringer::estring('a', $attrs, $url);
}
@ -964,8 +974,9 @@ function common_tag_link($tag)
$canonical = common_canonical_tag($tag);
if (common_config('singleuser', 'enabled')) {
// regular TagAction isn't set up in 1user mode
$user = User::singleUser();
$url = common_local_url('showstream',
array('nickname' => common_config('singleuser', 'nickname'),
array('nickname' => $user->nickname,
'tag' => $canonical));
} else {
$url = common_local_url('tag', array('tag' => $canonical));
@ -1069,7 +1080,17 @@ function common_local_url($action, $args=null, $params=null, $fragment=null, $ad
function common_is_sensitive($action)
{
static $sensitive = array('login', 'register', 'passwordsettings', 'api');
static $sensitive = array(
'login',
'register',
'passwordsettings',
'api',
'ApiOauthRequestToken',
'ApiOauthAccessToken',
'ApiOauthAuthorize',
'ApiOauthPin',
'showapplication'
);
$ssl = null;
if (Event::handle('SensitiveAction', array($action, &$ssl))) {

View File

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

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
* for each site being handled by the current process that has XMPP enabled.
*/
class XmppManager extends IoManager
{
protected $site = null;
@ -102,6 +101,7 @@ class XmppManager extends IoManager
$this->conn->addEventHandler('reconnect', 'handle_reconnect', $this);
$this->conn->setReconnectTimeout(600);
// @todo Needs i18n?
jabber_send_presence("Send me a message to post a notice", 'available', null, 'available', 100);
return !is_null($this->conn);
@ -281,9 +281,9 @@ class XmppManager extends IoManager
$_cur = $user;
if (!$user) {
$this->from_site($from, 'Unknown user; go to ' .
common_local_url('imsettings') .
' to add your address to your account');
// TRANS: %s is the URL to the StatusNet site's Instant Messaging settings.
$this->from_site($from, sprintf(_('Unknown user. Go to %s ' .
'to add your address to your account'),common_local_url('imsettings')));
$this->log(LOG_WARNING, 'Message from unknown user ' . $from);
return;
}
@ -314,7 +314,6 @@ class XmppManager extends IoManager
unset($pl);
}
function is_self($from)
{
return preg_match('/^'.strtolower(jabber_daemon_address()).'/', strtolower($from));
@ -400,7 +399,11 @@ class XmppManager extends IoManager
$content_shortened = common_shorten_links($body);
if (Notice::contentTooLong($content_shortened)) {
$from = jabber_normalize_jid($pl['from']);
$this->from_site($from, sprintf(_('Message too long - maximum is %1$d characters, you sent %2$d.'),
// TRANS: Response to XMPP source when it sent too long a message.
// TRANS: %1$d the maximum number of allowed characters (used for plural), %2$d is the sent number.
$this->from_site($from, sprintf(_m('Message too long. Maximum is %1$d character, you sent %2$d.',
'Message too long. Maximum is %1$d characters, you sent %2$d.',
Notice::maxContent()),
Notice::maxContent(),
mb_strlen($content_shortened)));
return;

View File

@ -52,4 +52,3 @@ class XmppOutQueueHandler extends QueueHandler
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