Merge branch '0.9.x'

This commit is contained in:
Brion Vibber 2010-10-18 15:21:02 -07:00
commit 53d45d7ffb
994 changed files with 50779 additions and 23738 deletions

View File

@ -1136,3 +1136,9 @@ 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

108
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"
19 October 2010
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
@ -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
----
@ -1487,6 +1502,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 +1586,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 +1697,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

@ -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
@ -123,9 +121,10 @@ class ApiDirectMessageNewAction extends ApiAuthAction
} else {
$content_shortened = common_shorten_links($this->content);
if (Message::contentTooLong($content_shortened)) {
// TRANS: Client error displayed when message content is too long.
// TRANS: %d is the maximum number of characters for a message.
$this->clientError(
sprintf(
_('That\'s too long. Max message size is %d chars.'),
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

@ -2,7 +2,8 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
* Exchange an authorized OAuth request token for an access token
* Action for getting OAuth token credentials (exchange an authorized
* request token for an access token)
*
* PHP version 5
*
@ -34,7 +35,8 @@ if (!defined('STATUSNET')) {
require_once INSTALLDIR . '/lib/apioauth.php';
/**
* Exchange an authorized OAuth request token for an access token
* Action for getting OAuth token credentials (exchange an authorized
* request token for an access token)
*
* @category API
* @package StatusNet
@ -45,6 +47,8 @@ require_once INSTALLDIR . '/lib/apioauth.php';
class ApiOauthAccessTokenAction extends ApiOauthAction
{
protected $reqToken = null;
protected $verifier = null;
/**
* Class handler.
@ -65,30 +69,58 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
$atok = 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');
$atok = $server->fetch_access_token($req);
} catch (OAuthException $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
common_debug(var_export($req, true));
$this->outputError($e->getMessage());
return;
$code = $e->getCode();
$this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text');
}
if (empty($atok)) {
common_debug('couldn\'t get access token.');
print "Token exchange failed. Has the request token been authorized?\n";
// 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',
$this->reqToken,
$this->verifier,
$ip,
$proxy
);
common_log(LOG_WARNING, $msg);
$this->clientError(_("Invalid request token or verifier.", 400, 'text'));
} else {
print $atok;
$this->showAccessToken($atok);
}
}
function outputError($msg)
/*
* Display OAuth token credentials
*
* @param OAuthToken token the access token
*/
function showAccessToken($token)
{
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: text/html; charset=utf-8');
print $msg . "\n";
header('Content-Type: application/x-www-form-urlencoded');
print $token;
}
}

View File

@ -32,6 +32,7 @@ if (!defined('STATUSNET')) {
}
require_once INSTALLDIR . '/lib/apioauth.php';
require_once INSTALLDIR . '/lib/info.php';
/**
* Authorize an OAuth request token
@ -43,9 +44,10 @@ require_once INSTALLDIR . '/lib/apioauth.php';
* @link http://status.net/
*/
class ApiOauthAuthorizeAction extends ApiOauthAction
class ApiOauthAuthorizeAction extends Action
{
var $oauth_token;
var $oauthTokenParam;
var $reqToken;
var $callback;
var $app;
var $nickname;
@ -67,12 +69,17 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
{
parent::prepare($args);
$this->nickname = $this->trimmed('nickname');
$this->password = $this->arg('password');
$this->oauth_token = $this->arg('oauth_token');
$this->callback = $this->arg('oauth_callback');
$this->store = new ApiStatusNetOAuthDataStore();
$this->app = $this->store->getAppByRequestToken($this->oauth_token);
$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();
try {
$this->app = $this->store->getAppByRequestToken($this->oauthTokenParam);
} catch (Exception $e) {
$this->clientError($e->getMessage());
}
return true;
}
@ -97,14 +104,30 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
} else {
if (empty($this->oauth_token)) {
// Make sure a oauth_token parameter was provided
if (empty($this->oauthTokenParam)) {
$this->clientError(_('No oauth_token parameter provided.'));
return;
} else {
// Check to make sure the token exists
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
if (empty($this->reqToken)) {
$this->serverError(
_('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.");
}
}
}
// make sure there's an app associated with this token
if (empty($this->app)) {
$this->clientError(_('Invalid token.'));
return;
$this->clientError(_('Invalid request token.'));
}
$name = $this->app->name;
@ -120,8 +143,8 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->showForm(_('There was a problem with your session token. '.
'Try again, please.'));
$this->showForm(
_('There was a problem with your session token. Try again, please.'));
return;
}
@ -130,6 +153,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$user = null;
if (!common_logged_in()) {
// XXX Force credentials check?
// XXX OpenID
$user = common_check_user($this->nickname, $this->password);
if (empty($user)) {
$this->showForm(_("Invalid nickname / password!"));
@ -141,9 +169,15 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if ($this->arg('allow')) {
// mark the req token as authorized
// fetch the token
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
$this->store->authorize_token($this->oauth_token);
// mark the req token as authorized
try {
$this->store->authorize_token($this->oauthTokenParam);
} catch (Exception $e) {
$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
@ -156,8 +190,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!$result) {
common_log_db_error($appUser, 'DELETE', __FILE__);
throw new ServerException(_('Database error deleting OAuth application user.'));
return;
$this->serverError(_('Database error deleting OAuth application user.'));
}
}
@ -175,20 +208,19 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
// granted. The OAuth app user record then gets updated
// with the new access token and access type.
$appUser->token = $this->oauth_token;
$appUser->token = $this->oauthTokenParam;
$appUser->created = common_sql_now();
$result = $appUser->insert();
if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__);
throw new ServerException(_('Database error inserting OAuth application user.'));
return;
$this->serverError(_('Database error inserting OAuth application user.'));
}
// if we have a callback redirect and provide the token
// If we have a callback redirect and provide the token
// A callback specified in the app setup overrides whatever
// Note: A callback specified in the app setup overrides whatever
// is passed in with the request.
if (!empty($this->app->callback_url)) {
@ -197,40 +229,40 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!empty($this->callback)) {
$target_url = $this->getCallback($this->callback,
array('oauth_token' => $this->oauth_token));
$targetUrl = $this->getCallback(
$this->callback,
array(
'oauth_token' => $this->oauthTokenParam,
'oauth_verifier' => $this->reqToken->verifier // 1.0a
)
);
// Redirect the user to the provided OAuth callback
common_redirect($targetUrl, 303);
common_redirect($target_url, 303);
} else {
common_debug("callback was empty!");
common_log(
LOG_INFO,
"No oauth_callback parameter provided for application ID "
. $this->app->id
. " when authorizing request token."
);
}
// otherwise inform the user that the rt was authorized
// Otherwise, inform the user that the rt was authorized
$this->showAuthorized();
$this->elementStart('p');
} else if ($this->arg('cancel')) {
// XXX: Do OAuth 1.0a verifier code
try {
$this->store->revoke_token($this->oauthTokenParam, 0);
$this->showCanceled();
} catch (Exception $e) {
$this->ServerError($e->getMessage());
}
$this->raw(sprintf(_("The request token %s has been authorized. " .
'Please exchange it for an access token.'),
$this->oauth_token));
$this->elementEnd('p');
} else if ($this->arg('deny')) {
$datastore = new ApiStatusNetOAuthDataStore();
$datastore->revoke_token($this->oauth_token, 0);
$this->elementStart('p');
$this->raw(sprintf(_("The request token %s has been denied and revoked."),
$this->oauth_token));
$this->elementEnd('p');
} else {
$this->clientError(_('Unexpected form submission.'));
return;
}
}
@ -276,7 +308,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
_('Allow or deny access'));
$this->hidden('token', common_session_token());
$this->hidden('oauth_token', $this->oauth_token);
$this->hidden('oauth_token', $this->oauthTokenParam);
$this->hidden('oauth_callback', $this->callback);
$this->elementStart('ul', 'form_data');
@ -321,11 +353,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
}
$this->element('input', array('id' => 'deny_submit',
$this->element('input', array('id' => 'cancel_submit',
'class' => 'submit submit form_action-primary',
'name' => 'deny',
'name' => 'cancel',
'type' => 'submit',
'value' => _('Deny')));
'value' => _('Cancel')));
$this->element('input', array('id' => 'allow_submit',
'class' => 'submit submit form_action-secondary',
@ -348,7 +380,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
function getInstructions()
{
return _('Allow or deny access to your account information.');
return _('Authorize access to your account information.');
}
/**
@ -388,4 +420,107 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
// NOP
}
/*
* Show a nice message confirming the authorization
* operation was canceled.
*
* @return nothing
*/
function showCanceled()
{
$info = new InfoAction(
_('Authorization canceled.'),
sprintf(
_('The request token %s has been revoked.'),
$this->oauthTokenParm
)
);
$info->showPage();
}
/*
* Show a nice message that the authorization was successful.
* If the operation is out-of-band, show a pin.
*
* @return nothing
*/
function showAuthorized()
{
$title = sprintf(
_("You have successfully authorized %s."),
$this->app->name
);
$msg = sprintf(
_('Please return to %s and enter the following security code to complete the process.'),
$this->app->name
);
if ($this->reqToken->verified_callback == 'oob') {
$pin = new ApiOauthPinAction($title, $msg, $this->reqToken->verifier);
$pin->showPage();
} else {
// NOTE: This would only happen if an application registered as
// a web application but sent in 'oob' for the oauth_callback
// parameter. Usually web apps will send in a callback and
// not use the pin-based workflow.
$info = new InfoAction(
$title,
$msg,
$this->oauthTokenParam,
$this->reqToken->verifier
);
$info->showPage();
}
}
/*
* Properly format the callback URL and parameters so it's
* suitable for a redirect in the OAuth dance
*
* @param string $url the URL
* @param array $params an array of parameters
*
* @return string $url a URL to use for redirecting to
*/
function getCallback($url, $params)
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar(
$url,
OAuthUtil::urlencode_rfc3986($k),
OAuthUtil::urlencode_rfc3986($v)
);
}
return $url;
}
/*
* Append a new query parameter after any existing query
* parameters.
*
* @param string $url the URL
* @prarm string $k the parameter name
* @param string $v value of the paramter
*
* @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);
if (strpos($url, '?') === false) {
return ($url . '?' . $k . '=' . $v);
} else {
return ($url . '&' . $k . '=' . $v);
}
}
}

67
actions/apioauthpin.php Normal file
View File

@ -0,0 +1,67 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action for displaying an OAuth verifier pin
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR . '/lib/info.php';
/**
* Class for displaying an OAuth verifier pin
*
* XXX: I'm pretty sure we don't need to check the logged in state here. -- Zach
*
* @category Action
* @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 ApiOauthPinAction extends InfoAction
{
function __construct($title, $message, $verifier)
{
$this->verifier = $verifier;
$this->title = $title;
parent::__construct($title, $message);
}
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->element('div', array('class' => 'info'), $this->message);
$this->element('div', array('id' => 'oauth_pin'), $this->verifier);
}
}

View File

@ -2,7 +2,7 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
* Get an OAuth request token
* Issue temporary OAuth credentials (a request token)
*
* PHP version 5
*
@ -34,7 +34,7 @@ if (!defined('STATUSNET')) {
require_once INSTALLDIR . '/lib/apioauth.php';
/**
* Get an OAuth request token
* Issue temporary OAuth credentials (a request token)
*
* @category API
* @package StatusNet
@ -58,22 +58,23 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
{
parent::prepare($args);
$this->callback = $this->arg('oauth_callback');
if (!empty($this->callback)) {
common_debug("callback: $this->callback");
}
// XXX: support "force_login" parameter like Twitter? (Forces the user to enter
// their credentials to ensure the correct users account is authorized.)
return true;
}
/**
* Class handler.
* Handle a request for temporary OAuth credentials
*
* Make sure the request is kosher, then emit a set of temporary
* credentials -- AKA an unauthorized request token.
*
* @param array $args array of arguments
*
* @return void
*/
function handle($args)
{
parent::handle($args);
@ -85,14 +86,78 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
$server->add_signature_method($hmac_method);
try {
$req = OAuthRequest::from_request();
$req = OAuthRequest::from_request();
// verify callback
if (!$this->verifyCallback($req->get_parameter('oauth_callback'))) {
throw new OAuthException(
"You must provide a valid URL or 'oob' in oauth_callback.",
400
);
}
// check signature and issue a new request token
$token = $server->fetch_request_token($req);
print $token;
common_log(
LOG_INFO,
sprintf(
"API OAuth - Issued request token %s for consumer %s with oauth_callback %s",
$token->key,
$req->get_parameter('oauth_consumer_key'),
"'" . $req->get_parameter('oauth_callback') ."'"
)
);
// return token to the client
$this->showRequestToken($token);
} catch (OAuthException $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: text/html; charset=utf-8');
print $e->getMessage() . "\n";
// Return 401 for for bad credentials or signature problems,
// and 400 for missing or unsupported parameters
$code = $e->getCode();
$this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text');
}
}
/*
* Display temporary OAuth credentials
*/
function showRequestToken($token)
{
header('Content-Type: application/x-www-form-urlencoded');
print $token;
print '&oauth_callback_confirmed=true';
}
/* Make sure the callback parameter contains either a real URL
* or the string 'oob'.
*
* @todo Check for evil/banned URLs here
*
* @return boolean true or false
*/
function verifyCallback($callback)
{
if ($callback == "oob") {
common_debug("OAuth request token requested for out of bounds 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
// allowing the workflow to proceed and issuing a pin. --Zach
return true;
} else {
return Validate::uri(
$callback,
array('allowed_schemes' => array('http', 'https'))
);
}
}

235
actions/deletegroup.php Normal file
View File

@ -0,0 +1,235 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Delete a group
*
* 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 Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Brion Vibber <brion@status.net>
* @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/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Delete a group
*
* This is the action for deleting a group.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Brion Vibber <brion@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/
* @fixme merge more of this code with related variants
*/
class DeletegroupAction extends RedirectingAction
{
var $group = null;
/**
* Prepare to run
*
* @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;
}
$nickname_arg = $this->trimmed('nickname');
$id = intval($this->arg('id'));
if ($id) {
$this->group = User_group::staticGet('id', $id);
} else if ($nickname_arg) {
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
common_redirect(common_local_url('leavegroup', $args), 301);
return false;
}
$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;
}
return true;
}
/**
* Handle the request
*
* On POST, delete the group.
*
* @param array $args unused
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) {
$this->returnToPrevious();
return;
} elseif ($this->arg('yes')) {
$this->handlePost();
return;
}
}
$this->showPage();
}
function handlePost()
{
$cur = common_current_user();
try {
if (Event::handle('StartDeleteGroup', array($this->group))) {
$this->group->delete();
Event::handle('EndDeleteGroup', array($this->group));
}
} catch (Exception $e) {
// 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');
// 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');
// @fixme add a sensible AJAX response form!
$this->elementEnd('body');
$this->elementEnd('html');
} else {
// @fixme if we could direct to the page on which this group
// would have shown... that would be awesome
common_redirect(common_local_url('groups'),
303);
}
}
function title() {
// TRANS: Title.
return _('Delete group');
}
function showContent() {
$this->areYouSureForm();
}
/**
* Confirm with user.
* Ripped from DeleteuserAction
*
* Shows a confirmation form.
*
* @fixme refactor common code for things like this
* @return void
*/
function areYouSureForm()
{
$id = $this->group->id;
$this->elementStart('form', array('id' => 'deletegroup-' . $id,
'method' => 'post',
'class' => 'form_settings form_entity_block',
'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 '.
'database, without a backup. ' .
'Public posts to this group will still appear in ' .
'individual timelines.'));
foreach ($this->args as $k => $v) {
if (substr($k, 0, 9) == 'returnto-') {
$this->hidden($k, $v);
}
}
Event::handle('EndDeleteGroupForm', array($this, $this->group));
}
$this->submit('form_action-no',
// TRANS: Button label on the delete group form.
_m('BUTTON','No'),
'submit form_action-primary',
'no',
// TRANS: Submit button title for 'No' when deleting a group.
_('Do not delete this group'));
$this->submit('form_action-yes',
// TRANS: Button label on the delete group form.
_m('BUTTON','Yes'),
'submit form_action-secondary',
'yes',
// TRANS: Submit button title for 'Yes' when deleting a group.
_('Delete this group'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
}

View File

@ -45,6 +45,12 @@ class DeletenoticeAction extends Action
parent::prepare($args);
$this->user = common_current_user();
if (!$this->user) {
common_user_error(_('Not logged in.'));
exit;
}
$notice_id = $this->trimmed('notice');
$this->notice = Notice::staticGet($notice_id);
@ -63,10 +69,7 @@ class DeletenoticeAction extends Action
{
parent::handle($args);
if (!common_logged_in()) {
common_user_error(_('Not logged in.'));
exit;
} else if ($this->notice->profile_id != $this->user_profile->id &&
if ($this->notice->profile_id != $this->user_profile->id &&
!$this->user->hasRight(Right::DELETEOTHERSNOTICE)) {
common_user_error(_('Can\'t delete this notice.'));
exit;

View File

@ -140,7 +140,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 +230,7 @@ class DesignadminpanelAction extends AdminPanelAction
function restoreDefaults()
{
$this->deleteSetting('site', 'logo');
$this->deleteSetting('site', 'ssllogo');
$this->deleteSetting('site', 'theme');
$settings = array(
@ -293,7 +294,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 +332,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 +450,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

@ -92,16 +92,17 @@ class PathsadminpanelAction extends AdminPanelAction
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 +132,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;
}
@ -230,11 +231,11 @@ class PathsAdminPanelForm extends AdminForm
function formData()
{
$this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
$this->out->elementStart('fieldset', array('id' => 'settings_paths_locale'));
$this->out->element('legend', null, _('Site'), 'site');
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->li();
$this->input('server', _('Server'), _('Site\'s server hostname.'));
$this->unli();
@ -243,14 +244,14 @@ class PathsAdminPanelForm extends AdminForm
$this->unli();
$this->li();
$this->input('locale_path', _('Path to locales'), _('Directory path to locales'), 'site');
$this->input('locale_path', _('Locale Directory'), _('Directory path to locales'), 'site');
$this->unli();
$this->li();
$this->li();
$this->out->checkbox('fancy', _('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,15 +262,23 @@ class PathsAdminPanelForm extends AdminForm
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->input('server', _('Theme server'), 'Server for themes', 'theme');
$this->input('server', _('Server'), _('Server for themes'), 'theme');
$this->unli();
$this->li();
$this->input('path', _('Theme path'), 'Web path to themes', 'theme');
$this->input('path', _('Path'), _('Web path to themes'), 'theme');
$this->unli();
$this->li();
$this->input('dir', _('Theme directory'), 'Directory where themes are located', 'theme');
$this->input('sslserver', _('SSL server'), _('SSL server for themes (default: SSL server)'), 'theme');
$this->unli();
$this->li();
$this->input('sslpath', _('SSL path'), _('SSL path to themes (default: /theme/)'), 'theme');
$this->unli();
$this->li();
$this->input('dir', _('Directory'), _('Directory where themes are located'), 'theme');
$this->unli();
$this->out->elementEnd('ul');
@ -297,20 +306,57 @@ class PathsAdminPanelForm extends AdminForm
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' =>
'settings_design_background-paths'));
'settings_design_background-paths'));
$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', _('Server'), 'Server for backgrounds', 'background');
$this->unli();
$this->li();
$this->input('path', _('Background path'), 'Web path to backgrounds', 'background');
$this->input('path', _('Path'), 'Web path to backgrounds', 'background');
$this->unli();
$this->li();
$this->input('dir', _('Background directory'), 'Directory where backgrounds are located', 'background');
$this->input('sslserver', _('SSL server'), 'Server for backgrounds on SSL pages', 'background');
$this->unli();
$this->li();
$this->input('sslpath', _('SSL path'), 'Web path to backgrounds on SSL pages', 'background');
$this->unli();
$this->li();
$this->input('dir', _('Directory'), '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'));
$this->out->element('legend', null, _('Attachments'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$this->input('server', _('Server'), 'Server for attachments', 'attachments');
$this->unli();
$this->li();
$this->input('path', _('Path'), 'Web path to attachments', 'attachments');
$this->unli();
$this->li();
$this->input('sslserver', _('SSL server'), 'Server for attachments on SSL pages', 'attachments');
$this->unli();
$this->li();
$this->input('sslpath', _('SSL path'), 'Web path to attachments on SSL pages', 'attachments');
$this->unli();
$this->li();
$this->input('dir', _('Directory'), 'Directory where attachments are located', 'attachments');
$this->unli();
$this->out->elementEnd('ul');
@ -320,12 +366,11 @@ class PathsAdminPanelForm extends AdminForm
$this->out->element('legend', null, _('SSL'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$ssl = array('never' => _('Never'),
'sometimes' => _('Sometimes'),
'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'));
@ -349,7 +394,7 @@ class PathsAdminPanelForm extends AdminForm
function formActions()
{
$this->out->submit('save', _('Save'), 'submit',
'save', _('Save paths'));
'save', _('Save paths'));
}
/**
@ -370,5 +415,4 @@ class PathsAdminPanelForm extends AdminForm
{
$this->out->input("$section-$setting", $title, $this->value($setting, $section), $instructions);
}
}

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

@ -316,6 +316,12 @@ class ShowgroupAction extends GroupDesignAction
Event::handle('EndGroupSubscribe', array($this, $this->group));
}
$this->elementEnd('li');
if ($cur->hasRight(Right::DELETEGROUP)) {
$this->elementStart('li', 'entity_delete');
$df = new DeleteGroupForm($this, $this->group);
$df->show();
$this->elementEnd('li');
}
$this->elementEnd('ul');
$this->elementEnd('div');
}

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

@ -275,7 +275,7 @@ class ProfileNoticeList extends NoticeList
}
}
class ProfileNoticeListItem extends NoticeListItem
class ProfileNoticeListItem extends DoFollowListItem
{
function showAuthor()
{

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

@ -46,12 +46,19 @@ class Oauth_application extends Memcached_DataObject
static function maxDesc()
{
$desclimit = common_config('application', 'desclimit');
// null => use global limit (distinct from 0!)
if (is_null($desclimit)) {
$desclimit = common_config('site', 'textlimit');
// This used to default to textlimit or allow unlimited descriptions,
// but this isn't part of a notice and the field's limited to 255 chars
// in the DB, so those seem silly.
//
// Now just defaulting to 255 max unless a smaller application desclimit
// is actually set. Setting to 0 will use the maximum.
$max = 255;
$desclimit = intval(common_config('application', 'desclimit'));
if ($desclimit > 0 && $desclimit < $max) {
return $desclimit;
} else {
return $max;
}
return $desclimit;
}
static function descriptionTooLong($desc)

View File

@ -854,6 +854,7 @@ class Profile extends Memcached_DataObject
case Right::SANDBOXUSER:
case Right::SILENCEUSER:
case Right::DELETEUSER:
case Right::DELETEGROUP:
$result = $this->hasRole(Profile_role::MODERATOR);
break;
case Right::CONFIGURESITE:

View File

@ -547,4 +547,61 @@ class User_group extends Memcached_DataObject
$group->query('COMMIT');
return $group;
}
/**
* Handle cascading deletion, on the model of notice and profile.
*
* This should handle freeing up cached entries for the group's
* id, nickname, URI, and aliases. There may be other areas that
* are not de-cached in the UI, including the sidebar lists on
* GroupsAction
*/
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;
if ($inst->find()) {
while ($inst->fetch()) {
$dup = clone($inst);
$dup->delete();
}
}
}
// And related groups in the other direction...
$inst = new Related_group();
$inst->related_group_id = $this->id;
$inst->delete();
// Aliases and the local_group entry need to be cleared explicitly
// or we'll miss clearing some cache keys; that can make it hard
// to create a new group with one of those names or aliases.
$this->setAliases(array());
$local = Local_group::staticGet('group_id', $this->id);
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.");
}
parent::delete();
}
}

File diff suppressed because it is too large Load Diff

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')) {
@ -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();
}
@ -422,11 +426,35 @@ class Action extends HTMLOutputter // lawsuit
}
$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');
@ -891,8 +919,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

@ -1244,23 +1244,29 @@ class ApiAction extends Action
// Do not emit error header for JSONP
if (!isset($this->callback)) {
header('HTTP/1.1 '.$code.' '.$status_string);
header('HTTP/1.1 ' . $code . ' ' . $status_string);
}
if ($format == 'xml') {
switch($format) {
case 'xml':
$this->initDocument('xml');
$this->elementStart('hash');
$this->element('error', null, $msg);
$this->element('request', null, $_SERVER['REQUEST_URI']);
$this->elementEnd('hash');
$this->endDocument('xml');
} elseif ($format == 'json'){
break;
case 'json':
$this->initDocument('json');
$error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
print(json_encode($error_array));
$this->endDocument('json');
} else {
break;
case 'text':
header('Content-Type: text/plain; charset=utf-8');
print $msg;
break;
default:
// If user didn't request a useful format, throw a regular client error
throw new ClientException($msg, $code);
}

View File

@ -30,13 +30,12 @@
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR . '/lib/apiaction.php';
require_once INSTALLDIR . '/lib/apioauthstore.php';
/**
* Base action for API OAuth enpoints. Clean up the
* the request, and possibly some other common things
* here.
* Base action for API OAuth enpoints. Clean up the
* request. Some other common functions.
*
* @category API
* @package StatusNet
@ -44,7 +43,7 @@ require_once INSTALLDIR . '/lib/apioauthstore.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 ApiOauthAction extends Action
class ApiOauthAction extends ApiAction
{
/**
* Is this a read-only action?
@ -77,6 +76,12 @@ class ApiOauthAction extends Action
self::cleanRequest();
}
/*
* Clean up the request so the OAuth library doesn't find
* any extra parameters or anything else it's not expecting.
* I'm looking at you, p parameter.
*/
static function cleanRequest()
{
// kill evil effects of magical slashing
@ -86,31 +91,19 @@ class ApiOauthAction extends Action
}
// strip out the p param added in index.php
// XXX: should we strip anything else? Or alternatively
// only allow a known list of params?
unset($_GET['p']);
unset($_POST['p']);
}
unset($_REQUEST['p']);
function getCallback($url, $params)
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar($url,
OAuthUtil::urlencode_rfc3986($k),
OAuthUtil::urlencode_rfc3986($v));
$queryArray = explode('&', $_SERVER['QUERY_STRING']);
for ($i = 0; $i < sizeof($queryArray); $i++) {
if (substr($queryArray[$i], 0, 2) == 'p=') {
unset($queryArray[$i]);
}
}
return $url;
$_SERVER['QUERY_STRING'] = implode('&', $queryArray);
}
function appendQueryVar($url, $k, $v) {
$url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&');
$url = substr($url, 0, -1);
if (strpos($url, '?') === false) {
return ($url . '?' . $k . '=' . $v);
} else {
return ($url . '&' . $k . '=' . $v);
}
}
}

View File

@ -71,33 +71,37 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
}
}
function new_access_token($token, $consumer)
function new_access_token($token, $consumer, $verifier)
{
common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__);
common_debug(
'new_access_token("' . $token->key . '","' . $consumer->key. '","' . $verifier . '")',
__FILE__
);
$rt = new Token();
$rt->consumer_key = $consumer->key;
$rt->tok = $token->key;
$rt->type = 0; // request
$rt->tok = $token->key;
$rt->type = 0; // request
$app = Oauth_application::getByConsumerKey($consumer->key);
assert(!empty($app));
if (empty($app)) {
common_debug("empty app!");
}
if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized
if ($rt->find(true) && $rt->state == 1) { // authorized
common_debug('request token found.', __FILE__);
// find the associated user of the app
$appUser = new Oauth_application_user();
$appUser->application_id = $app->id;
$appUser->token = $rt->tok;
$appUser->token = $rt->tok;
$result = $appUser->find(true);
if (!empty($result)) {
common_debug("Oath app user found.");
common_debug("Ouath app user found.");
} else {
common_debug("Oauth app user not found. app id $app->id token $rt->tok");
return null;
@ -106,10 +110,12 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
// go ahead and make the access token
$at = new Token();
$at->consumer_key = $consumer->key;
$at->tok = common_good_rand(16);
$at->secret = common_good_rand(16);
$at->type = 1; // access
$at->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();
if (!$at->insert()) {
@ -183,4 +189,40 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
throw new Exception(_('Failed to delete revoked token.'));
}
}
/*
* Create a new request token. Overrided to support OAuth 1.0a callback
*
* @param OAuthConsumer $consumer the OAuth Consumer for this token
* @param string $callback the verified OAuth callback URL
*
* @return OAuthToken $token a new unauthorized OAuth request token
*/
function new_request_token($consumer, $callback)
{
$t = new Token();
$t->consumer_key = $consumer->key;
$t->tok = common_good_rand(16);
$t->secret = common_good_rand(16);
$t->type = 0; // request
$t->state = 0; // unauthorized
$t->verified_callback = $callback;
if ($callback === 'oob') {
// six digit pin
$t->verifier = mt_rand(0, 9999999);
} else {
$t->verifier = common_good_rand(8);
}
$t->created = DB_DataObject_Cast::dateTime();
if (!$t->insert()) {
return null;
} else {
return new OAuthToken($t->tok, $t->secret);
}
}
}

View File

@ -12,7 +12,7 @@
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
* Copyright (C) 2008-2010 StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -32,7 +32,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/error.php';
require_once INSTALLDIR . '/lib/error.php';
/**
* Class for displaying HTTP client errors
@ -90,4 +90,26 @@ class ClientErrorAction extends ErrorAction
$this->showPage();
}
/**
* To specify additional HTTP headers for the action
*
* @return void
*/
function extraHeaders()
{
$status_string = @self::$status[$this->code];
header('HTTP/1.1 '.$this->code.' '.$status_string);
}
/**
* Page title.
*
* @return page title
*/
function title()
{
return @self::$status[$this->code];
}
}

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

View File

@ -116,9 +116,9 @@ class ConnectSettingsNav extends Widget
}
$menu['oauthconnectionssettings'] = array(
// TRANS: Menu item for OAth connection settings.
// TRANS: Menu item for OuAth connection settings.
_m('MENU','Connections'),
// TRANS: Tooltip for connected applications (Connections through OAth) menu item.
// TRANS: Tooltip for connected applications (Connections through OAuth) menu item.
_('Authorized connected applications')
);

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.)

123
lib/deletegroupform.php Normal file
View File

@ -0,0 +1,123 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Form for joining a group
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @author Brion Vibber <brion@status.net>
* @copyright 2009, 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Form for deleting a group
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @author Brion Vibber <brion@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*
* @see UnsubscribeForm
* @fixme merge a bunch of this stuff with similar form types to reduce boilerplate
*/
class DeleteGroupForm extends Form
{
/**
* group for user to delete
*/
var $group = null;
/**
* Constructor
*
* @param HTMLOutputter $out output channel
* @param group $group group to join
*/
function __construct($out=null, $group=null)
{
parent::__construct($out);
$this->group = $group;
}
/**
* ID of the form
*
* @return string ID of the form
*/
function id()
{
return 'group-delete-' . $this->group->id;
}
/**
* class of the form
*
* @return string of the form class
*/
function formClass()
{
return 'form_group_delete';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
return common_local_url('deletegroup',
array('id' => $this->group->id));
}
function formData()
{
$this->out->hidden($this->id() . '-returnto-action', 'groupbyid', 'returnto-action');
$this->out->hidden($this->id() . '-returnto-id', $this->group->id, 'returnto-id');
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
$this->out->submit('submit', _('Delete'));
}
}

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

@ -33,6 +33,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR . '/lib/info.php';
/**
* Base class for displaying HTTP errors
*
@ -42,7 +44,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class ErrorAction extends Action
class ErrorAction extends InfoAction
{
static $status = array();
@ -52,7 +54,7 @@ class ErrorAction extends Action
function __construct($message, $code, $output='php://output', $indent=null)
{
parent::__construct($output, $indent);
parent::__construct(null, $message, $output, $indent);
$this->code = $code;
$this->message = $message;
@ -64,43 +66,6 @@ class ErrorAction extends Action
$this->prepare($_REQUEST);
}
/**
* To specify additional HTTP headers for the action
*
* @return void
*/
function extraHeaders()
{
$status_string = @self::$status[$this->code];
header('HTTP/1.1 '.$this->code.' '.$status_string);
}
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->element('div', array('class' => 'error'), $this->message);
}
/**
* Page title.
*
* @return page title
*/
function title()
{
return @self::$status[$this->code];
}
function isReadOnly($args)
{
return true;
}
function showPage()
{
if ($this->minimal) {
@ -116,32 +81,16 @@ class ErrorAction extends Action
exit();
}
// Overload a bunch of stuff so the page isn't too bloated
function showBody()
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->elementStart('body', array('id' => 'error'));
$this->elementStart('div', array('id' => 'wrap'));
$this->showHeader();
$this->showCore();
$this->showFooter();
$this->elementEnd('div');
$this->elementEnd('body');
$this->element('div', array('class' => 'error'), $this->message);
}
function showCore()
{
$this->elementStart('div', array('id' => 'core'));
$this->showContentBlock();
$this->elementEnd('div');
}
function showHeader()
{
$this->elementStart('div', array('id' => 'header'));
$this->showLogo();
$this->showPrimaryNav();
$this->elementEnd('div');
}
}

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

118
lib/info.php Normal file
View File

@ -0,0 +1,118 @@
<?php
/**
* Information action
*
* PHP version 5
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Base class for displaying dialog box like messages to the user
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* @see ErrorAction
*/
class InfoAction extends Action
{
var $message = null;
function __construct($title, $message, $output='php://output', $indent=null)
{
parent::__construct($output, $indent);
$this->message = $message;
$this->title = $title;
// XXX: hack alert: usually we aren't going to
// call this page directly, but because it's
// an action it needs an args array anyway
$this->prepare($_REQUEST);
}
/**
* Page title.
*
* @return page title
*/
function title()
{
return empty($this->title) ? '' : $this->title;
}
function isReadOnly($args)
{
return true;
}
// Overload a bunch of stuff so the page isn't too bloated
function showBody()
{
$this->elementStart('body', array('id' => 'error'));
$this->elementStart('div', array('id' => 'wrap'));
$this->showHeader();
$this->showCore();
$this->showFooter();
$this->elementEnd('div');
$this->elementEnd('body');
}
function showCore()
{
$this->elementStart('div', array('id' => 'core'));
$this->showContentBlock();
$this->elementEnd('div');
}
function showHeader()
{
$this->elementStart('div', array('id' => 'header'));
$this->showLogo();
$this->showPrimaryNav();
$this->elementEnd('div');
}
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->element('div', array('class' => 'info'), $this->message);
}
}

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

@ -55,6 +55,17 @@ class StatusNetOAuthDataStore extends OAuthDataStore
}
}
function getTokenByKey($token_key)
{
$t = new Token();
$t->tok = $token_key;
if ($t->find(true)) {
return $t;
} else {
return null;
}
}
// http://oauth.net/core/1.0/#nonce
// "The Consumer SHALL then generate a Nonce value that is unique for
// all requests with that timestamp."
@ -317,13 +328,18 @@ class StatusNetOAuthDataStore extends OAuthDataStore
function add_avatar($profile, $url)
{
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
copy($url, $temp_filename);
$imagefile = new ImageFile($profile->id, $temp_filename);
$filename = Avatar::filename($profile->id,
image_type_to_extension($imagefile->type),
null,
common_timestamp());
rename($temp_filename, Avatar::path($filename));
try {
copy($url, $temp_filename);
$imagefile = new ImageFile($profile->id, $temp_filename);
$filename = Avatar::filename($profile->id,
image_type_to_extension($imagefile->type),
null,
common_timestamp());
rename($temp_filename, Avatar::path($filename));
} catch (Exception $e) {
unlink($temp_filename);
throw $e;
}
return $profile->setOriginal($filename);
}

View File

@ -60,5 +60,6 @@ class Right
const MAKEGROUPADMIN = 'makegroupadmin';
const GRANTROLE = 'grantrole';
const REVOKEROLE = 'revokerole';
const DELETEGROUP = 'deletegroup';
}

View File

@ -276,7 +276,7 @@ class Router
$m->connect('group/new', array('action' => 'newgroup'));
foreach (array('edit', 'join', 'leave') as $v) {
foreach (array('edit', 'join', 'leave', 'delete') as $v) {
$m->connect('group/:nickname/'.$v,
array('action' => $v.'group'),
array('nickname' => '[a-zA-Z0-9]+'));

View File

@ -96,4 +96,27 @@ class ServerErrorAction extends ErrorAction
$this->showPage();
}
/**
* To specify additional HTTP headers for the action
*
* @return void
*/
function extraHeaders()
{
$status_string = @self::$status[$this->code];
header('HTTP/1.1 '.$this->code.' '.$status_string);
}
/**
* Page title.
*
* @return page title
*/
function title()
{
return @self::$status[$this->code];
}
}

View File

@ -22,7 +22,7 @@
* @category Exception
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008 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/
*/

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

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

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

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-22 22:34+0000\n"
"POT-Creation-Date: 2010-10-09 14:04+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

View File

@ -0,0 +1,30 @@
# Translation of StatusNet - APC to Breton (Brezhoneg)
# Expored from translatewiki.net
#
# Author: Fulup
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Breton <http://translatewiki.net/wiki/Portal:br>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: br\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: APCPlugin.php:115
msgid ""
"Use the <a href=\"http://pecl.php.net/package/apc\">APC</a> variable cache "
"to cache query results."
msgstr ""
"Ober gant an <a href=\"http://pecl.php.net/package/apc\">APC</a> grubuilh "
"kemm-digemm evit krubuilhañ disoc'hoù ar rekedoù."

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Spanish <http://translatewiki.net/wiki/Portal:es>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: es\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: French <http://translatewiki.net/wiki/Portal:fr>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: fr\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -0,0 +1,30 @@
# Translation of StatusNet - APC to Galician (Galego)
# Expored from translatewiki.net
#
# Author: Toliño
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Galician <http://translatewiki.net/wiki/Portal:gl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: gl\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: APCPlugin.php:115
msgid ""
"Use the <a href=\"http://pecl.php.net/package/apc\">APC</a> variable cache "
"to cache query results."
msgstr ""
"Use a caché variable <a href=\"http://pecl.php.net/package/apc\">APC</a> "
"para memorizar os resultados da pescuda."

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Interlingua <http://translatewiki.net/wiki/Portal:ia>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: ia\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -0,0 +1,30 @@
# Translation of StatusNet - APC to Indonesian (Bahasa Indonesia)
# Expored from translatewiki.net
#
# Author: Farras
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-10-09 14:04+0000\n"
"PO-Revision-Date: 2010-10-09 14:07:14+0000\n"
"Language-Team: Indonesian <http://translatewiki.net/wiki/Portal:id>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2010-10-03 20:54:23+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74529); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: id\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: APCPlugin.php:115
msgid ""
"Use the <a href=\"http://pecl.php.net/package/apc\">APC</a> variable cache "
"to cache query results."
msgstr ""
"Gunakan singgahan variabel <a href=\"http://pecl.php.net/package/apc\">APC</"
"a> untuk menyinggah hasil pencarian."

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Macedonian <http://translatewiki.net/wiki/Portal:mk>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: mk\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Norwegian (bokmål) <http://translatewiki.net/wiki/Portal:no>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: no\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Dutch <http://translatewiki.net/wiki/Portal:nl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: nl\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -0,0 +1,31 @@
# Translation of StatusNet - APC to Polish (Polski)
# Expored from translatewiki.net
#
# Author: Sp5uhe
# --
# This file is distributed under the same license as the StatusNet package.
#
msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Polish <http://translatewiki.net/wiki/Portal:pl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: pl\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"
"Plural-Forms: nplurals=3; plural=(n == 1) ? 0 : ( (n%10 >= 2 && n%10 <= 4 && "
"(n%100 < 10 || n%100 >= 20)) ? 1 : 2 );\n"
#: APCPlugin.php:115
msgid ""
"Use the <a href=\"http://pecl.php.net/package/apc\">APC</a> variable cache "
"to cache query results."
msgstr ""
"Korzystaj z <a href=\"http://pecl.php.net/package/apc\">APC</a> pamięci "
"podręcznej zmiennych do przechowywania wyników zapytań."

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Portuguese <http://translatewiki.net/wiki/Portal:pt>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: pt\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -9,14 +9,14 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Brazilian Portuguese <http://translatewiki.net/wiki/Portal:pt-"
"br>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: pt-br\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Russian <http://translatewiki.net/wiki/Portal:ru>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: ru\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Tagalog <http://translatewiki.net/wiki/Portal:tl>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: tl\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -9,13 +9,13 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Ukrainian <http://translatewiki.net/wiki/Portal:uk>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: uk\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

View File

@ -9,14 +9,14 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - APC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-09-27 22:20+0000\n"
"PO-Revision-Date: 2010-09-27 22:41:44+0000\n"
"POT-Creation-Date: 2010-10-03 19:53+0000\n"
"PO-Revision-Date: 2010-10-03 19:56:23+0000\n"
"Language-Team: Simplified Chinese <http://translatewiki.net/wiki/Portal:zh-"
"hans>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-POT-Import-Date: 1285-19-54 31::+0000\n"
"X-Generator: MediaWiki 1.17alpha (r73828); Translate extension (2010-09-17)\n"
"X-POT-Import-Date: 2010-10-01 20:37:50+0000\n"
"X-Generator: MediaWiki 1.17alpha (r74231); Translate extension (2010-09-17)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: zh-hans\n"
"X-Message-Group: #out-statusnet-plugin-apc\n"

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