Merge branch 'master' of gitorious.org:statusnet/mainline
This commit is contained in:
commit
3f2c805652
79
README
79
README
|
@ -2,8 +2,8 @@
|
|||
README
|
||||
------
|
||||
|
||||
StatusNet 0.9.1 ("Everybody Hurts")
|
||||
28 Mar 2010
|
||||
StatusNet 0.9.2 ("King of Birds")
|
||||
21 Apr 2010
|
||||
|
||||
This is the README file for StatusNet, the Open Source microblogging
|
||||
platform. It includes installation instructions, descriptions of
|
||||
|
@ -77,7 +77,7 @@ for additional terms.
|
|||
New this version
|
||||
================
|
||||
|
||||
This is a minor bug and feature release since version 0.9.0 released 4
|
||||
This is a minor bug and feature release since version 0.9.1 released 28
|
||||
March 2010.
|
||||
|
||||
Because of fixes to OStatus bugs, it is highly recommended that all
|
||||
|
@ -85,26 +85,23 @@ public sites upgrade to the new version immediately.
|
|||
|
||||
Notable changes this version:
|
||||
|
||||
- Twitter bridge truncates and links back to original for long
|
||||
notices.
|
||||
- Changed "Home" link in main menu to "Personal".
|
||||
- A new memcached plugin (using pecl/memcached versus pecl/memcache)
|
||||
- Opt-in subscription to update@status.net
|
||||
- Script to run commands on behalf of a user.
|
||||
- Better Web UI for long notices.
|
||||
- A plugin to open external links in their own window or tab
|
||||
- Fixes to Salmon protocol for compatibility with other systems.
|
||||
- Updates to latest ActivityStreams definition.
|
||||
- Twitpic-compatible API for image upload.
|
||||
- Background deletion of user accounts.
|
||||
- Better support for HTTP basic authentication with CGI/FastCGI
|
||||
- Better discovery on OStatus
|
||||
- Support for PuSH-enabled RSS 2.0 feeds
|
||||
- OpenID-only mode
|
||||
- OpenID blacklist/whitelist
|
||||
- OStatus unit tests
|
||||
- Fixed email notifications for @-replies that come in via OStatus
|
||||
- OStatus related Fixes to the cloudy theme
|
||||
- Pass geo locations over Twitter bridge (will only be used if enabled on the Twitter side)
|
||||
- scripts/showplugins.php - script to dump the list of activated plugins and their settings
|
||||
- scripts/fixup_blocks.php - script to finds any stray subscriptions in violation of blocks, and removes them
|
||||
- Allow blocking someone who's not currently subscribed to you (prevents seeing @-replies from them, or them subbing to you in future)
|
||||
- Default 2-second timeout on Geonames web service lookups
|
||||
- Improved localization for plugins
|
||||
- New anti-spam measures: added nofollow rels to group members list, subscribers list
|
||||
- Shared cache key option for Geonames plugin (lets multi-instance sites share their cached geoname lookups)
|
||||
- Stability fixes to the TwitterStatusFetcher
|
||||
- If user allows location sharing but turned off browser location use profile location
|
||||
- Improved group listing via the API
|
||||
- Improved FOAF output
|
||||
- Several other bugfixes
|
||||
|
||||
A full changelog is available at http://status.net/wiki/StatusNet_0.9.1.
|
||||
A full changelog is available at http://status.net/wiki/StatusNet_0.9.2.
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
@ -216,9 +213,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.1.tar.gz
|
||||
tar zxf statusnet-0.9.2.tar.gz
|
||||
|
||||
...which will make a statusnet-0.9.1 subdirectory in your current
|
||||
...which will make a statusnet-0.9.2 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.)
|
||||
|
@ -226,7 +223,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.1 /var/www/statusnet
|
||||
mv statusnet-0.9.2 /var/www/statusnet
|
||||
|
||||
This will make your StatusNet instance available in the statusnet path of
|
||||
your server, like "http://example.net/statusnet". "microblog" or
|
||||
|
@ -641,7 +638,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.1. Try these step-by-step
|
||||
upgrade procedure in StatusNet 0.9.2. 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
|
||||
|
@ -662,7 +659,7 @@ 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.1 tarball and move it to "statusnet" or
|
||||
7. Unpack your StatusNet 0.9.2 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.
|
||||
|
@ -942,6 +939,26 @@ stomp_password: password for connecting to the stomp server; defaults
|
|||
to null.
|
||||
|
||||
stomp_persistent: keep items across queue server restart, if enabled.
|
||||
Under ActiveMQ, the server configuration determines if and how
|
||||
persistent storage is actually saved.
|
||||
|
||||
If using a message queue server other than ActiveMQ, you may
|
||||
need to disable this if it does not support persistence.
|
||||
|
||||
stomp_transactions: use transactions to aid in error detection.
|
||||
A broken transaction will be seen quickly, allowing a message
|
||||
to be redelivered immediately if a daemon crashes.
|
||||
|
||||
If using a message queue server other than ActiveMQ, you may
|
||||
need to disable this if it does not support transactions.
|
||||
|
||||
stomp_acks: send acknowledgements to aid in flow control.
|
||||
An acknowledgement of successful processing tells the server
|
||||
we're ready for more and can help keep things moving smoothly.
|
||||
|
||||
This should *not* be turned off when running with ActiveMQ, but
|
||||
if using another message queue server that does not support
|
||||
acknowledgements you might need to disable this.
|
||||
|
||||
softlimit: an absolute or relative "soft memory limit"; daemons will
|
||||
restart themselves gracefully when they find they've hit
|
||||
|
@ -970,6 +987,12 @@ max_retries: for stomp, drop messages after N failed attempts to process.
|
|||
dead_letter_dir: for stomp, optional directory to dump data on failed
|
||||
queue processing events after discarding them.
|
||||
|
||||
stomp_no_transactions: for stomp, the server does not support transactions,
|
||||
so do not try to user them. This is needed for http://www.morbidq.com/.
|
||||
|
||||
stomp_no_acks: for stomp, the server does not support acknowledgements.
|
||||
so do not try to user them. This is needed for http://www.morbidq.com/.
|
||||
|
||||
license
|
||||
-------
|
||||
|
||||
|
@ -1499,7 +1522,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.1 without reading the "Notice
|
||||
If you upgraded to StatusNet 0.9.2 without reading the "Notice
|
||||
inboxes" section above, and all your users' 'Personal' tabs are empty,
|
||||
read the "Notice inboxes" section above.
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ class AllAction extends ProfileAction
|
|||
|
||||
if ($this->page > 1 && $this->notice->N == 0) {
|
||||
// TRANS: Server error when page not found (404)
|
||||
$this->serverError(_('No such page'), $code = 404);
|
||||
$this->serverError(_('No such page.'), $code = 404);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -103,7 +103,7 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
|
|||
$this->clientError(
|
||||
_(
|
||||
'You must specify a parameter named ' .
|
||||
'\'device\' with a value of one of: sms, im, none'
|
||||
'\'device\' with a value of one of: sms, im, none.'
|
||||
)
|
||||
);
|
||||
return;
|
||||
|
|
|
@ -263,7 +263,7 @@ class ApiGroupCreateAction extends ApiAuthAction
|
|||
|
||||
if (!$valid) {
|
||||
$this->clientError(
|
||||
sprintf(_('Invalid alias: "%s"'), $alias),
|
||||
sprintf(_('Invalid alias: "%s".'), $alias),
|
||||
403,
|
||||
$this->format
|
||||
);
|
||||
|
|
|
@ -92,7 +92,7 @@ class ApiGroupIsMemberAction extends ApiBareAuthAction
|
|||
}
|
||||
|
||||
if (empty($this->group)) {
|
||||
$this->clientError(_('Group not found!'), 404, $this->format);
|
||||
$this->clientError(_('Group not found.'), 404, $this->format);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ class ApiGroupJoinAction extends ApiAuthAction
|
|||
}
|
||||
|
||||
if (empty($this->group)) {
|
||||
$this->clientError(_('Group not found!'), 404, $this->format);
|
||||
$this->clientError(_('Group not found.'), 404, $this->format);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ class ApiGroupLeaveAction extends ApiAuthAction
|
|||
}
|
||||
|
||||
if (empty($this->group)) {
|
||||
$this->clientError(_('Group not found!'), 404, $this->format);
|
||||
$this->clientError(_('Group not found.'), 404, $this->format);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ class ApiGroupMembershipAction extends ApiPrivateAuthAction
|
|||
parent::handle($args);
|
||||
|
||||
if (empty($this->group)) {
|
||||
$this->clientError(_('Group not found!'), 404, $this->format);
|
||||
$this->clientError(_('Group not found.'), 404, $this->format);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ class ApiGroupShowAction extends ApiPrivateAuthAction
|
|||
common_redirect(common_local_url('ApiGroupShow', $args), 301);
|
||||
} else {
|
||||
$this->clientError(
|
||||
_('Group not found!'),
|
||||
_('Group not found.'),
|
||||
404,
|
||||
$this->format
|
||||
);
|
||||
|
|
|
@ -199,7 +199,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
|||
$reply_to = $this->in_reply_to_status_id;
|
||||
} else {
|
||||
$this->clientError(
|
||||
_('Not found'),
|
||||
_('Not found.'),
|
||||
$code = 404,
|
||||
$this->format
|
||||
);
|
||||
|
|
|
@ -88,7 +88,7 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction
|
|||
parent::handle($args);
|
||||
|
||||
if (empty($this->group)) {
|
||||
$this->clientError(_('Group not found!'), 404, $this->format);
|
||||
$this->clientError(_('Group not found.'), 404, $this->format);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class ApiTimelineRetweetedByMeAction extends ApiAuthAction
|
|||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$this->serverError('Unimplemented', 503);
|
||||
$this->serverError('Unimplemented.', 503);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ class AvatarsettingsAction extends AccountSettingsAction
|
|||
|
||||
if (!$profile) {
|
||||
common_log_db_error($user, 'SELECT', __FILE__);
|
||||
$this->serverError(_('User without matching profile'));
|
||||
$this->serverError(_('User without matching profile.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ class AvatarsettingsAction extends AccountSettingsAction
|
|||
|
||||
if (!$profile) {
|
||||
common_log_db_error($user, 'SELECT', __FILE__);
|
||||
$this->serverError(_('User without matching profile'));
|
||||
$this->serverError(_('User without matching profile.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ class BlockAction extends ProfileFormAction
|
|||
assert(!empty($cur)); // checked by parent
|
||||
|
||||
if ($cur->hasBlocked($this->profile)) {
|
||||
$this->clientError(_("You already blocked that user."));
|
||||
$this->clientError(_('You already blocked that user.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@ class BookmarkletAction extends NewnoticeAction
|
|||
{
|
||||
function showTitle()
|
||||
{
|
||||
$this->element('title', null, _('Post to ').common_config('site', 'name'));
|
||||
// TRANS: Title for mini-posting window loaded from bookmarklet.
|
||||
$this->element('title', null, sprintf(_('Post to %s'), common_config('site', 'name')));
|
||||
}
|
||||
|
||||
function showHeader()
|
||||
|
|
|
@ -87,7 +87,7 @@ class ConfirmaddressAction extends Action
|
|||
}
|
||||
$type = $confirm->address_type;
|
||||
if (!in_array($type, array('email', 'jabber', 'sms'))) {
|
||||
$this->serverError(sprintf(_('Unrecognized address type %s'), $type));
|
||||
$this->serverError(sprintf(_('Unrecognized address type %s.'), $type));
|
||||
return;
|
||||
}
|
||||
if ($cur->$type == $confirm->address) {
|
||||
|
|
|
@ -64,14 +64,14 @@ class DeleteuserAction extends ProfileFormAction
|
|||
assert(!empty($cur)); // checked by parent
|
||||
|
||||
if (!$cur->hasRight(Right::DELETEUSER)) {
|
||||
$this->clientError(_("You cannot delete users."));
|
||||
$this->clientError(_('You cannot delete users.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->user = User::staticGet('id', $this->profile->id);
|
||||
|
||||
if (empty($this->user)) {
|
||||
$this->clientError(_("You can only delete local users."));
|
||||
$this->clientError(_('You can only delete local users.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -272,11 +272,11 @@ class DesignadminpanelAction extends AdminPanelAction
|
|||
{
|
||||
if (!empty($values['logo']) &&
|
||||
!Validate::uri($values['logo'], array('allowed_schemes' => array('http', 'https')))) {
|
||||
$this->clientError(_("Invalid logo URL."));
|
||||
$this->clientError(_('Invalid logo URL.'));
|
||||
}
|
||||
|
||||
if (!in_array($values['theme'], Theme::listAvailable())) {
|
||||
$this->clientError(sprintf(_("Theme not available: %s"), $values['theme']));
|
||||
$this->clientError(sprintf(_("Theme not available: %s."), $values['theme']));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ class DisfavorAction extends Action
|
|||
$notice = Notice::staticGet($id);
|
||||
$token = $this->trimmed('token-'.$notice->id);
|
||||
if (!$token || $token != common_session_token()) {
|
||||
$this->clientError(_("There was a problem with your session token. Try again, please."));
|
||||
$this->clientError(_('There was a problem with your session token. Try again, please.'));
|
||||
return;
|
||||
}
|
||||
$fave = new Fave();
|
||||
|
|
|
@ -72,7 +72,7 @@ class FavorAction extends Action
|
|||
$notice = Notice::staticGet($id);
|
||||
$token = $this->trimmed('token-'.$notice->id);
|
||||
if (!$token || $token != common_session_token()) {
|
||||
$this->clientError(_("There was a problem with your session token. Try again, please."));
|
||||
$this->clientError(_('There was a problem with your session token. Try again, please.'));
|
||||
return;
|
||||
}
|
||||
if ($user->hasFave($notice)) {
|
||||
|
|
|
@ -135,7 +135,7 @@ class FinishremotesubscribeAction extends Action
|
|||
$service->getServiceURI(OMB_ENDPOINT_UPDATEPROFILE);
|
||||
|
||||
if (!$remote->update($orig_remote)) {
|
||||
$this->serverError(_('Error updating remote profile'));
|
||||
$this->serverError(_('Error updating remote profile.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,11 +59,11 @@ class GrantRoleAction extends ProfileFormAction
|
|||
|
||||
$this->role = $this->arg('role');
|
||||
if (!Profile_role::isValid($this->role)) {
|
||||
$this->clientError(_("Invalid role."));
|
||||
$this->clientError(_('Invalid role.'));
|
||||
return false;
|
||||
}
|
||||
if (!Profile_role::isSettable($this->role)) {
|
||||
$this->clientError(_("This role is reserved and cannot be set."));
|
||||
$this->clientError(_('This role is reserved and cannot be set.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -72,14 +72,14 @@ class GrantRoleAction extends ProfileFormAction
|
|||
assert(!empty($cur)); // checked by parent
|
||||
|
||||
if (!$cur->hasRight(Right::GRANTROLE)) {
|
||||
$this->clientError(_("You cannot grant user roles on this site."));
|
||||
$this->clientError(_('You cannot grant user roles on this site.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(!empty($this->profile)); // checked by parent
|
||||
|
||||
if ($this->profile->hasRole($this->role)) {
|
||||
$this->clientError(_("User already has this role."));
|
||||
$this->clientError(_('User already has this role.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class GroupblockAction extends Action
|
||||
class GroupblockAction extends RedirectingAction
|
||||
{
|
||||
var $profile = null;
|
||||
var $group = null;
|
||||
|
@ -117,9 +117,7 @@ class GroupblockAction extends Action
|
|||
parent::handle($args);
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
if ($this->arg('no')) {
|
||||
common_redirect(common_local_url('groupmembers',
|
||||
array('nickname' => $this->group->nickname)),
|
||||
303);
|
||||
$this->returnToArgs();
|
||||
} elseif ($this->arg('yes')) {
|
||||
$this->blockProfile();
|
||||
} elseif ($this->arg('blockto')) {
|
||||
|
@ -196,23 +194,20 @@ class GroupblockAction extends Action
|
|||
$this->serverError(_("Database error blocking user from group."));
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->returnToArgs();
|
||||
}
|
||||
|
||||
// Now, gotta figure where we go back to
|
||||
foreach ($this->args as $k => $v) {
|
||||
if ($k == 'returnto-action') {
|
||||
$action = $v;
|
||||
} elseif (substr($k, 0, 9) == 'returnto-') {
|
||||
$args[substr($k, 9)] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if ($action) {
|
||||
common_redirect(common_local_url($action, $args), 303);
|
||||
} else {
|
||||
common_redirect(common_local_url('groupmembers',
|
||||
array('nickname' => $this->group->nickname)),
|
||||
303);
|
||||
}
|
||||
/**
|
||||
* If we reached this form without returnto arguments, default to
|
||||
* the top of the group's member list.
|
||||
*
|
||||
* @return string URL
|
||||
*/
|
||||
function defaultReturnTo()
|
||||
{
|
||||
return common_local_url('groupmembers',
|
||||
array('nickname' => $this->group->nickname));
|
||||
}
|
||||
|
||||
function showScripts()
|
||||
|
|
|
@ -205,8 +205,7 @@ class GroupMemberListItem extends ProfileListItem
|
|||
!$this->profile->isAdmin($this->group)) {
|
||||
$this->out->elementStart('li', 'entity_make_admin');
|
||||
$maf = new MakeAdminForm($this->out, $this->profile, $this->group,
|
||||
array('action' => 'groupmembers',
|
||||
'nickname' => $this->group->nickname));
|
||||
$this->returnToArgs());
|
||||
$maf->show();
|
||||
$this->out->elementEnd('li');
|
||||
}
|
||||
|
@ -220,8 +219,7 @@ class GroupMemberListItem extends ProfileListItem
|
|||
if (!empty($user) && $user->id != $this->profile->id && $user->isAdmin($this->group)) {
|
||||
$this->out->elementStart('li', 'entity_block');
|
||||
$bf = new GroupBlockForm($this->out, $this->profile, $this->group,
|
||||
array('action' => 'groupmembers',
|
||||
'nickname' => $this->group->nickname));
|
||||
$this->returnToArgs());
|
||||
$bf->show();
|
||||
$this->out->elementEnd('li');
|
||||
}
|
||||
|
@ -248,6 +246,23 @@ class GroupMemberListItem extends ProfileListItem
|
|||
|
||||
return $aAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch necessary return-to arguments for the profile forms
|
||||
* to return to this list when they're done.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function returnToArgs()
|
||||
{
|
||||
$args = array('action' => 'groupmembers',
|
||||
'nickname' => $this->group->nickname);
|
||||
$page = $this->out->arg('page');
|
||||
if ($page) {
|
||||
$args['param-page'] = $page;
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -292,7 +292,7 @@ class ImsettingsAction extends ConnectSettingsAction
|
|||
$this->showForm(_('Cannot normalize that Jabber ID'));
|
||||
return;
|
||||
}
|
||||
if (!jabber_valid_base_jid($jabber)) {
|
||||
if (!jabber_valid_base_jid($jabber, common_config('email', 'domain_check'))) {
|
||||
$this->showForm(_('Not a valid Jabber ID'));
|
||||
return;
|
||||
} else if ($user->jabber == $jabber) {
|
||||
|
|
|
@ -38,7 +38,7 @@ class InviteAction extends CurrentUserDesignAction
|
|||
if (!common_config('invite', 'enabled')) {
|
||||
$this->clientError(_('Invites have been disabled.'));
|
||||
} else if (!common_logged_in()) {
|
||||
$this->clientError(sprintf(_('You must be logged in to invite other users to use %s'),
|
||||
$this->clientError(sprintf(_('You must be logged in to invite other users to use %s.'),
|
||||
common_config('site', 'name')));
|
||||
return;
|
||||
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
|
|
|
@ -41,7 +41,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class MakeadminAction extends Action
|
||||
class MakeadminAction extends RedirectingAction
|
||||
{
|
||||
var $profile = null;
|
||||
var $group = null;
|
||||
|
@ -148,20 +148,19 @@ class MakeadminAction extends Action
|
|||
$this->group->getBestName());
|
||||
}
|
||||
|
||||
foreach ($this->args as $k => $v) {
|
||||
if ($k == 'returnto-action') {
|
||||
$action = $v;
|
||||
} else if (substr($k, 0, 9) == 'returnto-') {
|
||||
$args[substr($k, 9)] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if ($action) {
|
||||
common_redirect(common_local_url($action, $args), 303);
|
||||
} else {
|
||||
common_redirect(common_local_url('groupmembers',
|
||||
array('nickname' => $this->group->nickname)),
|
||||
303);
|
||||
}
|
||||
$this->returnToArgs();
|
||||
}
|
||||
|
||||
/**
|
||||
* If we reached this form without returnto arguments, default to
|
||||
* the top of the group's member list.
|
||||
*
|
||||
* @return string URL
|
||||
*/
|
||||
function defaultReturnTo()
|
||||
{
|
||||
return common_local_url('groupmembers',
|
||||
array('nickname' => $this->group->nickname));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ class MicrosummaryAction extends Action
|
|||
$notice = $user->getCurrentNotice();
|
||||
|
||||
if (!$notice) {
|
||||
$this->clientError(_('No current status'), 404);
|
||||
$this->clientError(_('No current status.'), 404);
|
||||
}
|
||||
|
||||
header('Content-Type: text/plain');
|
||||
|
|
|
@ -183,7 +183,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
|
|||
|
||||
if (!$result) {
|
||||
common_log_db_error($orig, 'DELETE', __FILE__);
|
||||
$this->clientError(_('Unable to revoke access for app: ' . $app->id));
|
||||
$this->clientError(sprintf(_('Unable to revoke access for app: %s.'), $app->id));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
|
|||
|
||||
function showEmptyListMessage()
|
||||
{
|
||||
$message = sprintf(_('You have not authorized any applications to use your account.'));
|
||||
$message = _('You have not authorized any applications to use your account.');
|
||||
|
||||
$this->elementStart('div', 'guide');
|
||||
$this->raw(common_markup_to_html($message));
|
||||
|
|
|
@ -60,7 +60,7 @@ class OembedAction extends Action
|
|||
$proxy_args = $r->map($path);
|
||||
|
||||
if (!$proxy_args) {
|
||||
$this->serverError(_("$path not found"), 404);
|
||||
$this->serverError(_("$path not found."), 404);
|
||||
}
|
||||
$oembed=array();
|
||||
$oembed['version']='1.0';
|
||||
|
@ -72,11 +72,11 @@ class OembedAction extends Action
|
|||
$id = $proxy_args['notice'];
|
||||
$notice = Notice::staticGet($id);
|
||||
if(empty($notice)){
|
||||
$this->serverError(_("notice $id not found"), 404);
|
||||
$this->serverError(_("Notice $id not found."), 404);
|
||||
}
|
||||
$profile = $notice->getProfile();
|
||||
if (empty($profile)) {
|
||||
$this->serverError(_('Notice has no profile'), 500);
|
||||
$this->serverError(_('Notice has no profile.'), 500);
|
||||
}
|
||||
if (!empty($profile->fullname)) {
|
||||
$authorname = $profile->fullname . ' (' . $profile->nickname . ')';
|
||||
|
@ -95,7 +95,7 @@ class OembedAction extends Action
|
|||
$id = $proxy_args['attachment'];
|
||||
$attachment = File::staticGet($id);
|
||||
if(empty($attachment)){
|
||||
$this->serverError(_("attachment $id not found"), 404);
|
||||
$this->serverError(_("Attachment $id not found."), 404);
|
||||
}
|
||||
if(empty($attachment->filename) && $file_oembed = File_oembed::staticGet('file_id', $attachment->id)){
|
||||
// Proxy the existing oembed information
|
||||
|
@ -123,7 +123,7 @@ class OembedAction extends Action
|
|||
if($attachment->title) $oembed['title']=$attachment->title;
|
||||
break;
|
||||
default:
|
||||
$this->serverError(_("$path not supported for oembed requests"), 501);
|
||||
$this->serverError(_("$path not supported for oembed requests."), 501);
|
||||
}
|
||||
switch($args['format']){
|
||||
case 'xml':
|
||||
|
@ -154,10 +154,12 @@ class OembedAction extends Action
|
|||
$this->end_document('json');
|
||||
break;
|
||||
default:
|
||||
$this->serverError(_('content type ' . $apidata['content-type'] . ' not supported'), 501);
|
||||
// TRANS: Error message displaying attachments. %s is a raw MIME type (eg 'image/png')
|
||||
$this->serverError(sprintf(_('Content type %s not supported.'), $apidata['content-type']), 501);
|
||||
}
|
||||
}else{
|
||||
$this->serverError(_('Only ' . common_root_url() . ' urls over plain http please'), 404);
|
||||
// TRANS: Error message displaying attachments. %s is the site's base URL.
|
||||
$this->serverError(sprintf(_('Only %s URLs over plain HTTP please.'), common_root_url()), 404);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -154,19 +154,19 @@ class PathsadminpanelAction extends AdminPanelAction
|
|||
// Validate theme dir
|
||||
|
||||
if (!empty($values['theme']['dir']) && !is_readable($values['theme']['dir'])) {
|
||||
$this->clientError(sprintf(_("Theme directory not readable: %s"), $values['theme']['dir']));
|
||||
$this->clientError(sprintf(_("Theme directory not readable: %s."), $values['theme']['dir']));
|
||||
}
|
||||
|
||||
// Validate avatar dir
|
||||
|
||||
if (empty($values['avatar']['dir']) || !is_writable($values['avatar']['dir'])) {
|
||||
$this->clientError(sprintf(_("Avatar directory not writable: %s"), $values['avatar']['dir']));
|
||||
$this->clientError(sprintf(_("Avatar directory not writable: %s."), $values['avatar']['dir']));
|
||||
}
|
||||
|
||||
// Validate background dir
|
||||
|
||||
if (empty($values['background']['dir']) || !is_writable($values['background']['dir'])) {
|
||||
$this->clientError(sprintf(_("Background directory not writable: %s"), $values['background']['dir']));
|
||||
$this->clientError(sprintf(_("Background directory not writable: %s."), $values['background']['dir']));
|
||||
}
|
||||
|
||||
// Validate locales dir
|
||||
|
@ -174,13 +174,13 @@ class PathsadminpanelAction extends AdminPanelAction
|
|||
// XXX: What else do we need to validate for lacales path here? --Z
|
||||
|
||||
if (!empty($values['site']['locale_path']) && !is_readable($values['site']['locale_path'])) {
|
||||
$this->clientError(sprintf(_("Locales directory not readable: %s"), $values['site']['locale_path']));
|
||||
$this->clientError(sprintf(_("Locales directory not readable: %s."), $values['site']['locale_path']));
|
||||
}
|
||||
|
||||
// Validate SSL setup
|
||||
|
||||
if (mb_strlen($values['site']['sslserver']) > 255) {
|
||||
$this->clientError(_("Invalid SSL server. The maximum length is 255 characters."));
|
||||
$this->clientError(_('Invalid SSL server. The maximum length is 255 characters.'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ class PeopletagAction extends Action
|
|||
$this->tag = $this->trimmed('tag');
|
||||
|
||||
if (!common_valid_profile_tag($this->tag)) {
|
||||
$this->clientError(sprintf(_('Not a valid people tag: %s'),
|
||||
$this->clientError(sprintf(_('Not a valid people tag: %s.'),
|
||||
$this->tag));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ class PostnoticeAction extends Action
|
|||
{
|
||||
$content = common_shorten_links($_POST['omb_notice_content']);
|
||||
if (Notice::contentTooLong($content)) {
|
||||
$this->clientError(_('Invalid notice content'), 400);
|
||||
$this->clientError(_('Invalid notice content.'), 400);
|
||||
return false;
|
||||
}
|
||||
$license = $_POST['omb_notice_license'];
|
||||
|
|
|
@ -80,7 +80,7 @@ class PublicAction extends Action
|
|||
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
|
||||
|
||||
if ($this->page > MAX_PUBLIC_PAGE) {
|
||||
$this->clientError(sprintf(_("Beyond the page limit (%s)"), MAX_PUBLIC_PAGE));
|
||||
$this->clientError(sprintf(_("Beyond the page limit (%s)."), MAX_PUBLIC_PAGE));
|
||||
}
|
||||
|
||||
common_set_returnto($this->selfUrl());
|
||||
|
@ -95,7 +95,7 @@ class PublicAction extends Action
|
|||
|
||||
if($this->page > 1 && $this->notice->N == 0){
|
||||
// TRANS: Server error when page not found (404)
|
||||
$this->serverError(_('No such page'),$code=404);
|
||||
$this->serverError(_('No such page.'),$code=404);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -491,11 +491,15 @@ class RegisterAction extends Action
|
|||
$this->elementStart('li');
|
||||
$this->element('input', $attrs);
|
||||
$this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
|
||||
$this->text(_('My text and files are available under '));
|
||||
$this->element('a', array('href' => common_config('license', 'url')),
|
||||
common_config('license', 'title'), _("Creative Commons Attribution 3.0"));
|
||||
$this->text(_(' except this private data: password, '.
|
||||
'email address, IM address, and phone number.'));
|
||||
$message = _('My text and files are available under %s ' .
|
||||
'except this private data: password, ' .
|
||||
'email address, IM address, and phone number.');
|
||||
$link = '<a href="' .
|
||||
htmlspecialchars(common_config('license', 'url')) .
|
||||
'">' .
|
||||
htmlspecialchars(common_config('license', 'title')) .
|
||||
'</a>';
|
||||
$this->raw(sprintf(htmlspecialchars($message), $link));
|
||||
$this->elementEnd('label');
|
||||
$this->elementEnd('li');
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ class RemotesubscribeAction extends Action
|
|||
$profile = $user->getProfile();
|
||||
if (!$profile) {
|
||||
common_log_db_error($user, 'SELECT', __FILE__);
|
||||
$this->serverError(_('User without matching profile'));
|
||||
$this->serverError(_('User without matching profile.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,21 +54,21 @@ class RepeatAction extends Action
|
|||
$this->user = common_current_user();
|
||||
|
||||
if (empty($this->user)) {
|
||||
$this->clientError(_("Only logged-in users can repeat notices."));
|
||||
$this->clientError(_('Only logged-in users can repeat notices.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = $this->trimmed('notice');
|
||||
|
||||
if (empty($id)) {
|
||||
$this->clientError(_("No notice specified."));
|
||||
$this->clientError(_('No notice specified.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->notice = Notice::staticGet('id', $id);
|
||||
|
||||
if (empty($this->notice)) {
|
||||
$this->clientError(_("No notice specified."));
|
||||
$this->clientError(_('No notice specified.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -80,14 +80,14 @@ class RepeatAction extends Action
|
|||
$token = $this->trimmed('token-'.$id);
|
||||
|
||||
if (empty($token) || $token != common_session_token()) {
|
||||
$this->clientError(_("There was a problem with your session token. Try again, please."));
|
||||
$this->clientError(_('There was a problem with your session token. Try again, please.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
$profile = $this->user->getProfile();
|
||||
|
||||
if ($profile->hasRepeated($id)) {
|
||||
$this->clientError(_("You already repeated that notice."));
|
||||
$this->clientError(_('You already repeated that notice.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ class RepliesAction extends OwnerDesignAction
|
|||
|
||||
if($this->page > 1 && $this->notice->N == 0){
|
||||
// TRANS: Server error when page not found (404)
|
||||
$this->serverError(_('No such page'),$code=404);
|
||||
$this->serverError(_('No such page.'),$code=404);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -59,11 +59,11 @@ class RevokeRoleAction extends ProfileFormAction
|
|||
|
||||
$this->role = $this->arg('role');
|
||||
if (!Profile_role::isValid($this->role)) {
|
||||
$this->clientError(_("Invalid role."));
|
||||
$this->clientError(_('Invalid role.'));
|
||||
return false;
|
||||
}
|
||||
if (!Profile_role::isSettable($this->role)) {
|
||||
$this->clientError(_("This role is reserved and cannot be set."));
|
||||
$this->clientError(_('This role is reserved and cannot be set.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ class RevokeRoleAction extends ProfileFormAction
|
|||
assert(!empty($cur)); // checked by parent
|
||||
|
||||
if (!$cur->hasRight(Right::REVOKEROLE)) {
|
||||
$this->clientError(_("You cannot revoke user roles on this site."));
|
||||
$this->clientError(_('You cannot revoke user roles on this site.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,14 +62,14 @@ class SandboxAction extends ProfileFormAction
|
|||
assert(!empty($cur)); // checked by parent
|
||||
|
||||
if (!$cur->hasRight(Right::SANDBOXUSER)) {
|
||||
$this->clientError(_("You cannot sandbox users on this site."));
|
||||
$this->clientError(_('You cannot sandbox users on this site.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(!empty($this->profile)); // checked by parent
|
||||
|
||||
if ($this->profile->isSandboxed()) {
|
||||
$this->clientError(_("User is already sandboxed."));
|
||||
$this->clientError(_('User is already sandboxed.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ class ShowfavoritesAction extends OwnerDesignAction
|
|||
|
||||
if($this->page > 1 && $this->notice->N == 0){
|
||||
// TRANS: Server error when page not found (404)
|
||||
$this->serverError(_('No such page'),$code=404);
|
||||
$this->serverError(_('No such page.'),$code=404);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -97,7 +97,7 @@ class ShownoticeAction extends OwnerDesignAction
|
|||
$this->profile = $this->notice->getProfile();
|
||||
|
||||
if (empty($this->profile)) {
|
||||
$this->serverError(_('Notice has no profile'), 500);
|
||||
$this->serverError(_('Notice has no profile.'), 500);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,14 +62,14 @@ class SilenceAction extends ProfileFormAction
|
|||
assert(!empty($cur)); // checked by parent
|
||||
|
||||
if (!$cur->hasRight(Right::SILENCEUSER)) {
|
||||
$this->clientError(_("You cannot silence users on this site."));
|
||||
$this->clientError(_('You cannot silence users on this site.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(!empty($this->profile)); // checked by parent
|
||||
|
||||
if ($this->profile->isSilenced()) {
|
||||
$this->clientError(_("User is already silenced."));
|
||||
$this->clientError(_('User is already silenced.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ class SiteadminpanelAction extends AdminPanelAction
|
|||
// Validate site name
|
||||
|
||||
if (empty($values['site']['name'])) {
|
||||
$this->clientError(_("Site name must have non-zero length."));
|
||||
$this->clientError(_('Site name must have non-zero length.'));
|
||||
}
|
||||
|
||||
// Validate email
|
||||
|
@ -168,7 +168,7 @@ class SiteadminpanelAction extends AdminPanelAction
|
|||
// Validate dupe limit
|
||||
|
||||
if (!Validate::number($values['site']['dupelimit'], array('min' => 1))) {
|
||||
$this->clientError(_("Dupe limit must 1 or more seconds."));
|
||||
$this->clientError(_("Dupe limit must be one or more seconds."));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ class SitenoticeadminpanelAction extends AdminPanelAction
|
|||
|
||||
if (mb_strlen($siteNotice) > 255) {
|
||||
$this->clientError(
|
||||
_('Max length for the site-wide notice is 255 chars')
|
||||
_('Max length for the site-wide notice is 255 chars.')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -124,13 +124,13 @@ class SnapshotadminpanelAction extends AdminPanelAction
|
|||
// Validate snapshot run value
|
||||
|
||||
if (!in_array($values['snapshot']['run'], array('web', 'cron', 'never'))) {
|
||||
$this->clientError(_("Invalid snapshot run value."));
|
||||
$this->clientError(_('Invalid snapshot run value.'));
|
||||
}
|
||||
|
||||
// Validate snapshot frequency value
|
||||
|
||||
if (!Validate::number($values['snapshot']['frequency'])) {
|
||||
$this->clientError(_("Snapshot frequency must be a number."));
|
||||
$this->clientError(_('Snapshot frequency must be a number.'));
|
||||
}
|
||||
|
||||
// Validate report URL
|
||||
|
@ -141,7 +141,7 @@ class SnapshotadminpanelAction extends AdminPanelAction
|
|||
array('allowed_schemes' => array('http', 'https')
|
||||
)
|
||||
)) {
|
||||
$this->clientError(_("Invalid snapshot report URL."));
|
||||
$this->clientError(_('Invalid snapshot report URL.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,9 +157,13 @@ class SubscribersListItem extends SubscriptionListItem
|
|||
$user = common_current_user();
|
||||
|
||||
if (!empty($user) && $this->owner->id == $user->id) {
|
||||
$bf = new BlockForm($this->out, $this->profile,
|
||||
array('action' => 'subscribers',
|
||||
'nickname' => $this->owner->nickname));
|
||||
$returnto = array('action' => 'subscribers',
|
||||
'nickname' => $this->owner->nickname);
|
||||
$page = $this->out->arg('page');
|
||||
if ($page) {
|
||||
$returnto['param-page'] = $page;
|
||||
}
|
||||
$bf = new BlockForm($this->out, $this->profile, $returnto);
|
||||
$bf->show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ class TagAction extends Action
|
|||
|
||||
if($this->page > 1 && $this->notice->N == 0){
|
||||
// TRANS: Server error when page not found (404)
|
||||
$this->serverError(_('No such page'),$code=404);
|
||||
$this->serverError(_('No such page.'),$code=404);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -62,14 +62,14 @@ class UnsandboxAction extends ProfileFormAction
|
|||
assert(!empty($cur)); // checked by parent
|
||||
|
||||
if (!$cur->hasRight(Right::SANDBOXUSER)) {
|
||||
$this->clientError(_("You cannot sandbox users on this site."));
|
||||
$this->clientError(_('You cannot sandbox users on this site.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(!empty($this->profile)); // checked by parent
|
||||
|
||||
if (!$this->profile->isSandboxed()) {
|
||||
$this->clientError(_("User is not sandboxed."));
|
||||
$this->clientError(_('User is not sandboxed.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,14 +62,14 @@ class UnsilenceAction extends ProfileFormAction
|
|||
assert(!empty($cur)); // checked by parent
|
||||
|
||||
if (!$cur->hasRight(Right::SILENCEUSER)) {
|
||||
$this->clientError(_("You cannot silence users on this site."));
|
||||
$this->clientError(_('You cannot silence users on this site.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(!empty($this->profile)); // checked by parent
|
||||
|
||||
if (!$this->profile->isSilenced()) {
|
||||
$this->clientError(_("User is not silenced."));
|
||||
$this->clientError(_('User is not silenced.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ class UnsubscribeAction extends Action
|
|||
$other_id = $this->arg('unsubscribeto');
|
||||
|
||||
if (!$other_id) {
|
||||
$this->clientError(_('No profile id in request.'));
|
||||
$this->clientError(_('No profile ID in request.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class UserauthorizationAction extends Action
|
|||
$profile = $user->getProfile();
|
||||
if (!$profile) {
|
||||
common_log_db_error($user, 'SELECT', __FILE__);
|
||||
$this->serverError(_('User without matching profile'));
|
||||
$this->serverError(_('User without matching profile.'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ class UserrssAction extends Rss10Action
|
|||
$profile = $user->getProfile();
|
||||
if (!$profile) {
|
||||
common_log_db_error($user, 'SELECT', __FILE__);
|
||||
$this->serverError(_('User without matching profile'));
|
||||
$this->serverError(_('User without matching profile.'));
|
||||
return null;
|
||||
}
|
||||
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||
|
|
|
@ -330,6 +330,10 @@ class Memcached_DataObject extends Safe_DataObject
|
|||
*/
|
||||
function _query($string)
|
||||
{
|
||||
if (common_config('db', 'annotate_queries')) {
|
||||
$string = $this->annotateQuery($string);
|
||||
}
|
||||
|
||||
$start = microtime(true);
|
||||
$result = parent::_query($string);
|
||||
$delta = microtime(true) - $start;
|
||||
|
@ -342,6 +346,70 @@ class Memcached_DataObject extends Safe_DataObject
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first caller in the stack trace that's not a
|
||||
* low-level database function and add a comment to the
|
||||
* query string. This should then be visible in process lists
|
||||
* and slow query logs, to help identify problem areas.
|
||||
*
|
||||
* Also marks whether this was a web GET/POST or which daemon
|
||||
* was running it.
|
||||
*
|
||||
* @param string $string SQL query string
|
||||
* @return string SQL query string, with a comment in it
|
||||
*/
|
||||
function annotateQuery($string)
|
||||
{
|
||||
$ignore = array('annotateQuery',
|
||||
'_query',
|
||||
'query',
|
||||
'get',
|
||||
'insert',
|
||||
'delete',
|
||||
'update',
|
||||
'find');
|
||||
$ignoreStatic = array('staticGet',
|
||||
'pkeyGet',
|
||||
'cachedQuery');
|
||||
$here = get_class($this); // if we get confused
|
||||
$bt = debug_backtrace();
|
||||
|
||||
// Find the first caller that's not us?
|
||||
foreach ($bt as $frame) {
|
||||
$func = $frame['function'];
|
||||
if (isset($frame['type']) && $frame['type'] == '::') {
|
||||
if (in_array($func, $ignoreStatic)) {
|
||||
continue;
|
||||
}
|
||||
$here = $frame['class'] . '::' . $func;
|
||||
break;
|
||||
} else if (isset($frame['type']) && $frame['type'] == '->') {
|
||||
if ($frame['object'] === $this && in_array($func, $ignore)) {
|
||||
continue;
|
||||
}
|
||||
if (in_array($func, $ignoreStatic)) {
|
||||
continue; // @fixme this shouldn't be needed?
|
||||
}
|
||||
$here = get_class($frame['object']) . '->' . $func;
|
||||
break;
|
||||
}
|
||||
$here = $func;
|
||||
break;
|
||||
}
|
||||
|
||||
if (php_sapi_name() == 'cli') {
|
||||
$context = basename($_SERVER['PHP_SELF']);
|
||||
} else {
|
||||
$context = $_SERVER['REQUEST_METHOD'];
|
||||
}
|
||||
|
||||
// Slip the comment in after the first command,
|
||||
// or DB_DataObject gets confused about handling inserts and such.
|
||||
$parts = explode(' ', $string, 2);
|
||||
$parts[0] .= " /* $context $here */";
|
||||
return implode(' ', $parts);
|
||||
}
|
||||
|
||||
// Sanitize a query for logging
|
||||
// @fixme don't trim spaces in string literals
|
||||
function sanitizeQuery($string)
|
||||
|
|
|
@ -701,6 +701,27 @@ class Notice extends Memcached_DataObject
|
|||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this notice part of an active conversation?
|
||||
*
|
||||
* @return boolean true if other messages exist in the same
|
||||
* conversation, false if this is the only one
|
||||
*/
|
||||
function hasConversation()
|
||||
{
|
||||
if (!empty($this->conversation)) {
|
||||
$conversation = Notice::conversationStream(
|
||||
$this->conversation,
|
||||
1,
|
||||
1
|
||||
);
|
||||
if ($conversation->N > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $groups array of Group *objects*
|
||||
* @param $recipients array of profile *ids*
|
||||
|
|
100
extlib/Net/IDNA.php
Normal file
100
extlib/Net/IDNA.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
// {{{ license
|
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
|
||||
//
|
||||
// +----------------------------------------------------------------------+
|
||||
// | This library is free software; you can redistribute it and/or modify |
|
||||
// | it under the terms of the GNU Lesser General Public License as |
|
||||
// | published by the Free Software Foundation; either version 2.1 of the |
|
||||
// | License, or (at your option) any later version. |
|
||||
// | |
|
||||
// | This library 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 |
|
||||
// | Lesser General Public License for more details. |
|
||||
// | |
|
||||
// | You should have received a copy of the GNU Lesser General Public |
|
||||
// | License along with this library; if not, write to the Free Software |
|
||||
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
|
||||
// | USA. |
|
||||
// +----------------------------------------------------------------------+
|
||||
//
|
||||
|
||||
// }}}
|
||||
|
||||
|
||||
/**
|
||||
* Encode/decode Internationalized Domain Names.
|
||||
* Factory class to get correct implementation either for php4 or php5.
|
||||
*
|
||||
* @author Markus Nix <mnix@docuverse.de>
|
||||
* @author Matthias Sommerfeld <mso@phlylabs.de>
|
||||
* @package Net
|
||||
* @version $Id: IDNA.php 284681 2009-07-24 04:24:27Z clockwerx $
|
||||
*/
|
||||
|
||||
class Net_IDNA
|
||||
{
|
||||
// {{{ factory
|
||||
/**
|
||||
* Attempts to return a concrete IDNA instance for either php4 or php5.
|
||||
*
|
||||
* @param array $params Set of paramaters
|
||||
* @return object IDNA The newly created concrete Log instance, or an
|
||||
* false on an error.
|
||||
* @access public
|
||||
*/
|
||||
function getInstance($params = array())
|
||||
{
|
||||
$version = explode( '.', phpversion() );
|
||||
$handler = ((int)$version[0] > 4) ? 'php5' : 'php4';
|
||||
$class = 'Net_IDNA_' . $handler;
|
||||
$classfile = 'Net/IDNA/' . $handler . '.php';
|
||||
|
||||
/*
|
||||
* Attempt to include our version of the named class, but don't treat
|
||||
* a failure as fatal. The caller may have already included their own
|
||||
* version of the named class.
|
||||
*/
|
||||
@include_once $classfile;
|
||||
|
||||
/* If the class exists, return a new instance of it. */
|
||||
if (class_exists($class)) {
|
||||
return new $class($params);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// }}}
|
||||
|
||||
// {{{ singleton
|
||||
/**
|
||||
* Attempts to return a concrete IDNA instance for either php4 or php5,
|
||||
* only creating a new instance if no IDNA instance with the same
|
||||
* parameters currently exists.
|
||||
*
|
||||
* @param array $params Set of paramaters
|
||||
* @return object IDNA The newly created concrete Log instance, or an
|
||||
* false on an error.
|
||||
* @access public
|
||||
*/
|
||||
function singleton($params = array())
|
||||
{
|
||||
static $instances;
|
||||
if (!isset($instances)) {
|
||||
$instances = array();
|
||||
}
|
||||
|
||||
$signature = serialize($params);
|
||||
if (!isset($instances[$signature])) {
|
||||
$instances[$signature] = Net_IDNA::getInstance($params);
|
||||
}
|
||||
|
||||
return $instances[$signature];
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
|
||||
?>
|
3269
extlib/Net/IDNA/php5.php
Normal file
3269
extlib/Net/IDNA/php5.php
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -185,7 +185,7 @@ function checkMirror($action_obj, $args)
|
|||
|
||||
function isLoginAction($action)
|
||||
{
|
||||
static $loginActions = array('login', 'recoverpassword', 'api', 'doc', 'register', 'publicxrds', 'otp');
|
||||
static $loginActions = array('login', 'recoverpassword', 'api', 'doc', 'register', 'publicxrds', 'otp', 'rsd');
|
||||
|
||||
$login = null;
|
||||
|
||||
|
|
1046
install.php
1046
install.php
File diff suppressed because it is too large
Load Diff
|
@ -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.1');
|
||||
define('STATUSNET_VERSION', '0.9.2');
|
||||
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
|
||||
|
||||
define('STATUSNET_CODENAME', 'Everybody Hurts');
|
||||
define('STATUSNET_CODENAME', 'King of Birds');
|
||||
|
||||
define('AVATAR_PROFILE_SIZE', 96);
|
||||
define('AVATAR_STREAM_SIZE', 48);
|
||||
|
|
|
@ -72,6 +72,7 @@ $default =
|
|||
'quote_identifiers' => false,
|
||||
'type' => 'mysql',
|
||||
'schemacheck' => 'runtime', // 'runtime' or 'script'
|
||||
'annotate_queries' => false, // true to add caller comments to queries, eg /* POST Notice::saveNew */
|
||||
'log_queries' => false, // true to log all DB queries
|
||||
'log_slow_queries' => 0), // if set, log queries taking over N seconds
|
||||
'syslog' =>
|
||||
|
@ -87,6 +88,8 @@ $default =
|
|||
'stomp_username' => null,
|
||||
'stomp_password' => null,
|
||||
'stomp_persistent' => true, // keep items across queue server restart, if persistence is enabled
|
||||
'stomp_transactions' => true, // use STOMP transactions to aid in detecting failures (supported by ActiveMQ, but not by all)
|
||||
'stomp_acks' => true, // send acknowledgements after successful processing (supported by ActiveMQ, but not by all)
|
||||
'stomp_manual_failover' => true, // if multiple servers are listed, treat them as separate (enqueue on one randomly, listen on all)
|
||||
'monitor' => null, // URL to monitor ping endpoint (work in progress)
|
||||
'softlimit' => '90%', // total size or % of memory_limit at which to restart queue threads gracefully
|
||||
|
|
|
@ -43,6 +43,9 @@ require_once 'HTTP/Request2/Response.php';
|
|||
*
|
||||
* This extends the HTTP_Request2_Response class with methods to get info
|
||||
* about any followed redirects.
|
||||
*
|
||||
* Originally used the name 'HTTPResponse' to match earlier code, but
|
||||
* this conflicts with a class in in the PECL HTTP extension.
|
||||
*
|
||||
* @category HTTP
|
||||
* @package StatusNet
|
||||
|
@ -51,7 +54,7 @@ require_once 'HTTP/Request2/Response.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 HTTPResponse extends HTTP_Request2_Response
|
||||
class StatusNet_HTTPResponse extends HTTP_Request2_Response
|
||||
{
|
||||
function __construct(HTTP_Request2_Response $response, $url, $redirects=0)
|
||||
{
|
||||
|
@ -146,7 +149,7 @@ class HTTPClient extends HTTP_Request2
|
|||
/**
|
||||
* Convenience function to run a GET request.
|
||||
*
|
||||
* @return HTTPResponse
|
||||
* @return StatusNet_HTTPResponse
|
||||
* @throws HTTP_Request2_Exception
|
||||
*/
|
||||
public function get($url, $headers=array())
|
||||
|
@ -157,7 +160,7 @@ class HTTPClient extends HTTP_Request2
|
|||
/**
|
||||
* Convenience function to run a HEAD request.
|
||||
*
|
||||
* @return HTTPResponse
|
||||
* @return StatusNet_HTTPResponse
|
||||
* @throws HTTP_Request2_Exception
|
||||
*/
|
||||
public function head($url, $headers=array())
|
||||
|
@ -171,7 +174,7 @@ class HTTPClient extends HTTP_Request2
|
|||
* @param string $url
|
||||
* @param array $headers optional associative array of HTTP headers
|
||||
* @param array $data optional associative array or blob of form data to submit
|
||||
* @return HTTPResponse
|
||||
* @return StatusNet_HTTPResponse
|
||||
* @throws HTTP_Request2_Exception
|
||||
*/
|
||||
public function post($url, $headers=array(), $data=array())
|
||||
|
@ -183,7 +186,7 @@ class HTTPClient extends HTTP_Request2
|
|||
}
|
||||
|
||||
/**
|
||||
* @return HTTPResponse
|
||||
* @return StatusNet_HTTPResponse
|
||||
* @throws HTTP_Request2_Exception
|
||||
*/
|
||||
protected function doRequest($url, $method, $headers)
|
||||
|
@ -217,12 +220,12 @@ class HTTPClient extends HTTP_Request2
|
|||
}
|
||||
|
||||
/**
|
||||
* Actually performs the HTTP request and returns an HTTPResponse object
|
||||
* with response body and header info.
|
||||
* Actually performs the HTTP request and returns a
|
||||
* StatusNet_HTTPResponse object with response body and header info.
|
||||
*
|
||||
* Wraps around parent send() to add logging and redirection processing.
|
||||
*
|
||||
* @return HTTPResponse
|
||||
* @return StatusNet_HTTPResponse
|
||||
* @throw HTTP_Request2_Exception
|
||||
*/
|
||||
public function send()
|
||||
|
@ -265,6 +268,6 @@ class HTTPClient extends HTTP_Request2
|
|||
}
|
||||
break;
|
||||
} while ($maxRedirs);
|
||||
return new HTTPResponse($response, $this->getUrl(), $redirs);
|
||||
return new StatusNet_HTTPResponse($response, $this->getUrl(), $redirs);
|
||||
}
|
||||
}
|
||||
|
|
576
lib/installer.php
Normal file
576
lib/installer.php
Normal file
|
@ -0,0 +1,576 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2009, StatusNet, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Installation
|
||||
* @package Installation
|
||||
*
|
||||
* @author Adrian Lang <mail@adrianlang.de>
|
||||
* @author Brenda Wallace <shiny@cpan.org>
|
||||
* @author Brett Taylor <brett@webfroot.co.nz>
|
||||
* @author Brion Vibber <brion@pobox.com>
|
||||
* @author CiaranG <ciaran@ciarang.com>
|
||||
* @author Craig Andrews <candrews@integralblue.com>
|
||||
* @author Eric Helgeson <helfire@Erics-MBP.local>
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Robin Millette <millette@controlyourself.ca>
|
||||
* @author Sarven Capadisli <csarven@status.net>
|
||||
* @author Tom Adams <tom@holizz.com>
|
||||
* @author Zach Copley <zach@status.net>
|
||||
* @license GNU Affero General Public License http://www.gnu.org/licenses/
|
||||
* @version 0.9.x
|
||||
* @link http://status.net
|
||||
*/
|
||||
|
||||
abstract class Installer
|
||||
{
|
||||
/** Web site info */
|
||||
public $sitename, $server, $path, $fancy;
|
||||
/** DB info */
|
||||
public $host, $dbname, $dbtype, $username, $password, $db;
|
||||
/** Administrator info */
|
||||
public $adminNick, $adminPass, $adminEmail, $adminUpdates;
|
||||
/** Should we skip writing the configuration file? */
|
||||
public $skipConfig = false;
|
||||
|
||||
public static $dbModules = array(
|
||||
'mysql' => array(
|
||||
'name' => 'MySQL',
|
||||
'check_module' => 'mysqli',
|
||||
'installer' => 'mysql_db_installer',
|
||||
),
|
||||
'pgsql' => array(
|
||||
'name' => 'PostgreSQL',
|
||||
'check_module' => 'pgsql',
|
||||
'installer' => 'pgsql_db_installer',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Attempt to include a PHP file and report if it worked, while
|
||||
* suppressing the annoying warning messages on failure.
|
||||
*/
|
||||
private function haveIncludeFile($filename) {
|
||||
$old = error_reporting(error_reporting() & ~E_WARNING);
|
||||
$ok = include_once($filename);
|
||||
error_reporting($old);
|
||||
return $ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all is ready for installation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function checkPrereqs()
|
||||
{
|
||||
$pass = true;
|
||||
|
||||
if (file_exists(INSTALLDIR.'/config.php')) {
|
||||
$this->warning('Config file "config.php" already exists.');
|
||||
$pass = false;
|
||||
}
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.2.3', '<')) {
|
||||
$errors[] = 'Require PHP version 5.2.3 or greater.';
|
||||
$pass = false;
|
||||
}
|
||||
|
||||
// Look for known library bugs
|
||||
$str = "abcdefghijklmnopqrstuvwxyz";
|
||||
$replaced = preg_replace('/[\p{Cc}\p{Cs}]/u', '*', $str);
|
||||
if ($str != $replaced) {
|
||||
$this->warning('PHP is linked to a version of the PCRE library ' .
|
||||
'that does not support Unicode properties. ' .
|
||||
'If you are running Red Hat Enterprise Linux / ' .
|
||||
'CentOS 5.4 or earlier, see <a href="' .
|
||||
'http://status.net/wiki/Red_Hat_Enterprise_Linux#PCRE_library' .
|
||||
'">our documentation page</a> on fixing this.');
|
||||
$pass = false;
|
||||
}
|
||||
|
||||
$reqs = array('gd', 'curl',
|
||||
'xmlwriter', 'mbstring', 'xml', 'dom', 'simplexml');
|
||||
|
||||
foreach ($reqs as $req) {
|
||||
if (!$this->checkExtension($req)) {
|
||||
$this->warning(sprintf('Cannot load required extension: <code>%s</code>', $req));
|
||||
$pass = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we have at least one database module available
|
||||
$missingExtensions = array();
|
||||
foreach (self::$dbModules as $type => $info) {
|
||||
if (!$this->checkExtension($info['check_module'])) {
|
||||
$missingExtensions[] = $info['check_module'];
|
||||
}
|
||||
}
|
||||
|
||||
if (count($missingExtensions) == count(self::$dbModules)) {
|
||||
$req = implode(', ', $missingExtensions);
|
||||
$this->warning(sprintf('Cannot find a database extension. You need at least one of %s.', $req));
|
||||
$pass = false;
|
||||
}
|
||||
|
||||
if (!is_writable(INSTALLDIR)) {
|
||||
$this->warning(sprintf('Cannot write config file to: <code>%s</code></p>', INSTALLDIR),
|
||||
sprintf('On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR));
|
||||
$pass = false;
|
||||
}
|
||||
|
||||
// Check the subdirs used for file uploads
|
||||
$fileSubdirs = array('avatar', 'background', 'file');
|
||||
foreach ($fileSubdirs as $fileSubdir) {
|
||||
$fileFullPath = INSTALLDIR."/$fileSubdir/";
|
||||
if (!is_writable($fileFullPath)) {
|
||||
$this->warning(sprintf('Cannot write to %s directory: <code>%s</code>', $fileSubdir, $fileFullPath),
|
||||
sprintf('On your server, try this command: <code>chmod a+w %s</code>', $fileFullPath));
|
||||
$pass = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a php extension is both installed and loaded
|
||||
*
|
||||
* @param string $name of extension to check
|
||||
*
|
||||
* @return boolean whether extension is installed and loaded
|
||||
*/
|
||||
function checkExtension($name)
|
||||
{
|
||||
if (extension_loaded($name)) {
|
||||
return true;
|
||||
} elseif (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode')) {
|
||||
// dl will throw a fatal error if it's disabled or we're in safe mode.
|
||||
// More fun, it may not even exist under some SAPIs in 5.3.0 or later...
|
||||
$soname = $name . '.' . PHP_SHLIB_SUFFIX;
|
||||
if (PHP_SHLIB_SUFFIX == 'dll') {
|
||||
$soname = "php_" . $soname;
|
||||
}
|
||||
return @dl($soname);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic validation on the database paramters
|
||||
* Side effects: error output if not valid
|
||||
*
|
||||
* @return boolean success
|
||||
*/
|
||||
function validateDb()
|
||||
{
|
||||
$fail = false;
|
||||
|
||||
if (empty($this->host)) {
|
||||
$this->updateStatus("No hostname specified.", true);
|
||||
$fail = true;
|
||||
}
|
||||
|
||||
if (empty($this->database)) {
|
||||
$this->updateStatus("No database specified.", true);
|
||||
$fail = true;
|
||||
}
|
||||
|
||||
if (empty($this->username)) {
|
||||
$this->updateStatus("No username specified.", true);
|
||||
$fail = true;
|
||||
}
|
||||
|
||||
if (empty($this->sitename)) {
|
||||
$this->updateStatus("No sitename specified.", true);
|
||||
$fail = true;
|
||||
}
|
||||
|
||||
return !$fail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic validation on the administrator user paramters
|
||||
* Side effects: error output if not valid
|
||||
*
|
||||
* @return boolean success
|
||||
*/
|
||||
function validateAdmin()
|
||||
{
|
||||
$fail = false;
|
||||
|
||||
if (empty($this->adminNick)) {
|
||||
$this->updateStatus("No initial StatusNet user nickname specified.", true);
|
||||
$fail = true;
|
||||
}
|
||||
if ($this->adminNick && !preg_match('/^[0-9a-z]{1,64}$/', $this->adminNick)) {
|
||||
$this->updateStatus('The user nickname "' . htmlspecialchars($this->adminNick) .
|
||||
'" is invalid; should be plain letters and numbers no longer than 64 characters.', true);
|
||||
$fail = true;
|
||||
}
|
||||
// @fixme hardcoded list; should use User::allowed_nickname()
|
||||
// if/when it's safe to have loaded the infrastructure here
|
||||
$blacklist = array('main', 'admin', 'twitter', 'settings', 'rsd.xml', 'favorited', 'featured', 'favoritedrss', 'featuredrss', 'rss', 'getfile', 'api', 'groups', 'group', 'peopletag', 'tag', 'user', 'message', 'conversation', 'bookmarklet', 'notice', 'attachment', 'search', 'index.php', 'doc', 'opensearch', 'robots.txt', 'xd_receiver.html', 'facebook');
|
||||
if (in_array($this->adminNick, $blacklist)) {
|
||||
$this->updateStatus('The user nickname "' . htmlspecialchars($this->adminNick) .
|
||||
'" is reserved.', true);
|
||||
$fail = true;
|
||||
}
|
||||
|
||||
if (empty($this->adminPass)) {
|
||||
$this->updateStatus("No initial StatusNet user password specified.", true);
|
||||
$fail = true;
|
||||
}
|
||||
|
||||
return !$fail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the database with the appropriate function for the selected type...
|
||||
* Saves database info into $this->db.
|
||||
*
|
||||
* @return mixed array of database connection params on success, false on failure
|
||||
*/
|
||||
function setupDatabase()
|
||||
{
|
||||
if ($this->db) {
|
||||
throw new Exception("Bad order of operations: DB already set up.");
|
||||
}
|
||||
$method = self::$dbModules[$this->dbtype]['installer'];
|
||||
$db = call_user_func(array($this, $method),
|
||||
$this->host,
|
||||
$this->database,
|
||||
$this->username,
|
||||
$this->password);
|
||||
$this->db = $db;
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a database on PostgreSQL.
|
||||
* Will output status updates during the operation.
|
||||
*
|
||||
* @param string $host
|
||||
* @param string $database
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return mixed array of database connection params on success, false on failure
|
||||
*
|
||||
* @fixme escape things in the connection string in case we have a funny pass etc
|
||||
*/
|
||||
function Pgsql_Db_installer($host, $database, $username, $password)
|
||||
{
|
||||
$connstring = "dbname=$database host=$host user=$username";
|
||||
|
||||
//No password would mean trust authentication used.
|
||||
if (!empty($password)) {
|
||||
$connstring .= " password=$password";
|
||||
}
|
||||
$this->updateStatus("Starting installation...");
|
||||
$this->updateStatus("Checking database...");
|
||||
$conn = pg_connect($connstring);
|
||||
|
||||
if ($conn ===false) {
|
||||
$this->updateStatus("Failed to connect to database: $connstring");
|
||||
return false;
|
||||
}
|
||||
|
||||
//ensure database encoding is UTF8
|
||||
$record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
|
||||
if ($record->server_encoding != 'UTF8') {
|
||||
$this->updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->updateStatus("Running database script...");
|
||||
//wrap in transaction;
|
||||
pg_query($conn, 'BEGIN');
|
||||
$res = $this->runDbScript('statusnet_pg.sql', $conn, 'pgsql');
|
||||
|
||||
if ($res === false) {
|
||||
$this->updateStatus("Can't run database script.", true);
|
||||
return false;
|
||||
}
|
||||
foreach (array('sms_carrier' => 'SMS carrier',
|
||||
'notice_source' => 'notice source',
|
||||
'foreign_services' => 'foreign service')
|
||||
as $scr => $name) {
|
||||
$this->updateStatus(sprintf("Adding %s data to database...", $name));
|
||||
$res = $this->runDbScript($scr.'.sql', $conn, 'pgsql');
|
||||
if ($res === false) {
|
||||
$this->updateStatus(sprintf("Can't run %d script.", $name), true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pg_query($conn, 'COMMIT');
|
||||
|
||||
if (empty($password)) {
|
||||
$sqlUrl = "pgsql://$username@$host/$database";
|
||||
} else {
|
||||
$sqlUrl = "pgsql://$username:$password@$host/$database";
|
||||
}
|
||||
|
||||
$db = array('type' => 'pgsql', 'database' => $sqlUrl);
|
||||
|
||||
return $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a database on MySQL.
|
||||
* Will output status updates during the operation.
|
||||
*
|
||||
* @param string $host
|
||||
* @param string $database
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return mixed array of database connection params on success, false on failure
|
||||
*
|
||||
* @fixme escape things in the connection string in case we have a funny pass etc
|
||||
*/
|
||||
function Mysql_Db_installer($host, $database, $username, $password)
|
||||
{
|
||||
$this->updateStatus("Starting installation...");
|
||||
$this->updateStatus("Checking database...");
|
||||
|
||||
$conn = mysqli_init();
|
||||
if (!$conn->real_connect($host, $username, $password)) {
|
||||
$this->updateStatus("Can't connect to server '$host' as '$username'.", true);
|
||||
return false;
|
||||
}
|
||||
$this->updateStatus("Changing to database...");
|
||||
if (!$conn->select_db($database)) {
|
||||
$this->updateStatus("Can't change to database.", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->updateStatus("Running database script...");
|
||||
$res = $this->runDbScript('statusnet.sql', $conn);
|
||||
if ($res === false) {
|
||||
$this->updateStatus("Can't run database script.", true);
|
||||
return false;
|
||||
}
|
||||
foreach (array('sms_carrier' => 'SMS carrier',
|
||||
'notice_source' => 'notice source',
|
||||
'foreign_services' => 'foreign service')
|
||||
as $scr => $name) {
|
||||
$this->updateStatus(sprintf("Adding %s data to database...", $name));
|
||||
$res = $this->runDbScript($scr.'.sql', $conn);
|
||||
if ($res === false) {
|
||||
$this->updateStatus(sprintf("Can't run %d script.", $name), true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$sqlUrl = "mysqli://$username:$password@$host/$database";
|
||||
$db = array('type' => 'mysql', 'database' => $sqlUrl);
|
||||
return $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a stock configuration file.
|
||||
*
|
||||
* @return boolean success
|
||||
*
|
||||
* @fixme escape variables in output in case we have funny chars, apostrophes etc
|
||||
*/
|
||||
function writeConf()
|
||||
{
|
||||
// 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".
|
||||
|
||||
// site location
|
||||
"\$config['site']['server'] = '{$this->server}';\n".
|
||||
"\$config['site']['path'] = '{$this->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".
|
||||
($this->db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
|
||||
"\$config['db']['type'] = '{$this->db['type']}';\n\n";
|
||||
// write configuration file out to install directory
|
||||
$res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install schema into the database
|
||||
*
|
||||
* @param string $filename location of database schema file
|
||||
* @param dbconn $conn connection to database
|
||||
* @param string $type type of database, currently mysql or pgsql
|
||||
*
|
||||
* @return boolean - indicating success or failure
|
||||
*/
|
||||
function runDbScript($filename, $conn, $type = 'mysqli')
|
||||
{
|
||||
$sql = trim(file_get_contents(INSTALLDIR . '/db/' . $filename));
|
||||
$stmts = explode(';', $sql);
|
||||
foreach ($stmts as $stmt) {
|
||||
$stmt = trim($stmt);
|
||||
if (!mb_strlen($stmt)) {
|
||||
continue;
|
||||
}
|
||||
// FIXME: use PEAR::DB or PDO instead of our own switch
|
||||
switch ($type) {
|
||||
case 'mysqli':
|
||||
$res = $conn->query($stmt);
|
||||
if ($res === false) {
|
||||
$error = $conn->error();
|
||||
}
|
||||
break;
|
||||
case 'pgsql':
|
||||
$res = pg_query($conn, $stmt);
|
||||
if ($res === false) {
|
||||
$error = pg_last_error();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
|
||||
}
|
||||
if ($res === false) {
|
||||
$this->updateStatus("ERROR ($error) for SQL '$stmt'");
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the initial admin user account.
|
||||
* Side effect: may load portions of StatusNet framework.
|
||||
* Side effect: outputs program info
|
||||
*/
|
||||
function registerInitialUser()
|
||||
{
|
||||
define('STATUSNET', true);
|
||||
define('LACONICA', true); // compatibility
|
||||
|
||||
require_once INSTALLDIR . '/lib/common.php';
|
||||
|
||||
$data = array('nickname' => $this->adminNick,
|
||||
'password' => $this->adminPass,
|
||||
'fullname' => $this->adminNick);
|
||||
if ($this->adminEmail) {
|
||||
$data['email'] = $this->adminEmail;
|
||||
}
|
||||
$user = User::register($data);
|
||||
|
||||
if (empty($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// give initial user carte blanche
|
||||
|
||||
$user->grantRole('owner');
|
||||
$user->grantRole('moderator');
|
||||
$user->grantRole('administrator');
|
||||
|
||||
// Attempt to do a remote subscribe to update@status.net
|
||||
// Will fail if instance is on a private network.
|
||||
|
||||
if ($this->adminUpdates && class_exists('Ostatus_profile')) {
|
||||
try {
|
||||
$oprofile = Ostatus_profile::ensureProfileURL('http://update.status.net/');
|
||||
Subscription::start($user->getProfile(), $oprofile->localProfile());
|
||||
$this->updateStatus("Set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
|
||||
} catch (Exception $e) {
|
||||
$this->updateStatus("Could not set up subscription to <a href='http://update.status.net/'>update@status.net</a>.", true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The beef of the installer!
|
||||
* Create database, config file, and admin user.
|
||||
*
|
||||
* Prerequisites: validation of input data.
|
||||
*
|
||||
* @return boolean success
|
||||
*/
|
||||
function doInstall()
|
||||
{
|
||||
$this->db = $this->setupDatabase();
|
||||
|
||||
if (!$this->db) {
|
||||
// database connection failed, do not move on to create config file.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->skipConfig) {
|
||||
$this->updateStatus("Writing config file...");
|
||||
$res = $this->writeConf();
|
||||
|
||||
if (!$res) {
|
||||
$this->updateStatus("Can't write config file.", true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->adminNick)) {
|
||||
// Okay, cross fingers and try to register an initial user
|
||||
if ($this->registerInitialUser()) {
|
||||
$this->updateStatus(
|
||||
"An initial user with the administrator role has been created."
|
||||
);
|
||||
} else {
|
||||
$this->updateStatus(
|
||||
"Could not create initial StatusNet user (administrator).",
|
||||
true
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TODO https needs to be considered
|
||||
*/
|
||||
$link = "http://".$this->server.'/'.$this->path;
|
||||
|
||||
$this->updateStatus("StatusNet has been installed at $link");
|
||||
$this->updateStatus(
|
||||
"<strong>DONE!</strong> You can visit your <a href='$link'>new StatusNet site</a> (login as '$this->adminNick'). If this is your first StatusNet install, you may want to poke around our <a href='http://status.net/wiki/Getting_started'>Getting Started guide</a>."
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output a pre-install-time warning message
|
||||
* @param string $message HTML ok, but should be plaintext-able
|
||||
* @param string $submessage HTML ok, but should be plaintext-able
|
||||
*/
|
||||
abstract function warning($message, $submessage='');
|
||||
|
||||
/**
|
||||
* Output an install-time progress message
|
||||
* @param string $message HTML ok, but should be plaintext-able
|
||||
* @param boolean $error true if this should be marked as an error condition
|
||||
*/
|
||||
abstract function updateStatus($status, $error=false);
|
||||
|
||||
}
|
181
lib/jabber.php
181
lib/jabber.php
|
@ -34,38 +34,197 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||
require_once 'XMPPHP/XMPP.php';
|
||||
|
||||
/**
|
||||
* checks whether a string is a syntactically valid Jabber ID (JID)
|
||||
* Splits a Jabber ID (JID) into node, domain, and resource portions.
|
||||
*
|
||||
* Based on validation routine submitted by:
|
||||
* @copyright 2009 Patrick Georgi <patrick@georgi-clan.de>
|
||||
* @license Licensed under ISC-L, which is compatible with everything else that keeps the copyright notice intact.
|
||||
*
|
||||
* @param string $jid string to check
|
||||
*
|
||||
* @return boolean whether the string is a valid JID
|
||||
* @return array with "node", "domain", and "resource" indices
|
||||
* @throws Exception if input is not valid
|
||||
*/
|
||||
|
||||
function jabber_valid_base_jid($jid)
|
||||
function jabber_split_jid($jid)
|
||||
{
|
||||
// Cheap but effective
|
||||
return Validate::email($jid);
|
||||
$chars = '';
|
||||
/* the following definitions come from stringprep, Appendix C,
|
||||
which is used in its entirety by nodeprop, Chapter 5, "Prohibited Output" */
|
||||
/* C1.1 ASCII space characters */
|
||||
$chars .= "\x{20}";
|
||||
/* C1.2 Non-ASCII space characters */
|
||||
$chars .= "\x{a0}\x{1680}\x{2000}-\x{200b}\x{202f}\x{205f}\x{3000a}";
|
||||
/* C2.1 ASCII control characters */
|
||||
$chars .= "\x{00}-\x{1f}\x{7f}";
|
||||
/* C2.2 Non-ASCII control characters */
|
||||
$chars .= "\x{80}-\x{9f}\x{6dd}\x{70f}\x{180e}\x{200c}\x{200d}\x{2028}\x{2029}\x{2060}-\x{2063}\x{206a}-\x{206f}\x{feff}\x{fff9}-\x{fffc}\x{1d173}-\x{1d17a}";
|
||||
/* C3 - Private Use */
|
||||
$chars .= "\x{e000}-\x{f8ff}\x{f0000}-\x{ffffd}\x{100000}-\x{10fffd}";
|
||||
/* C4 - Non-character code points */
|
||||
$chars .= "\x{fdd0}-\x{fdef}\x{fffe}\x{ffff}\x{1fffe}\x{1ffff}\x{2fffe}\x{2ffff}\x{3fffe}\x{3ffff}\x{4fffe}\x{4ffff}\x{5fffe}\x{5ffff}\x{6fffe}\x{6ffff}\x{7fffe}\x{7ffff}\x{8fffe}\x{8ffff}\x{9fffe}\x{9ffff}\x{afffe}\x{affff}\x{bfffe}\x{bffff}\x{cfffe}\x{cffff}\x{dfffe}\x{dffff}\x{efffe}\x{effff}\x{ffffe}\x{fffff}\x{10fffe}\x{10ffff}";
|
||||
/* C5 - Surrogate codes */
|
||||
$chars .= "\x{d800}-\x{dfff}";
|
||||
/* C6 - Inappropriate for plain text */
|
||||
$chars .= "\x{fff9}-\x{fffd}";
|
||||
/* C7 - Inappropriate for canonical representation */
|
||||
$chars .= "\x{2ff0}-\x{2ffb}";
|
||||
/* C8 - Change display properties or are deprecated */
|
||||
$chars .= "\x{340}\x{341}\x{200e}\x{200f}\x{202a}-\x{202e}\x{206a}-\x{206f}";
|
||||
/* C9 - Tagging characters */
|
||||
$chars .= "\x{e0001}\x{e0020}-\x{e007f}";
|
||||
|
||||
/* Nodeprep forbids some more characters */
|
||||
$nodeprepchars = $chars;
|
||||
$nodeprepchars .= "\x{22}\x{26}\x{27}\x{2f}\x{3a}\x{3c}\x{3e}\x{40}";
|
||||
|
||||
$parts = explode("/", $jid, 2);
|
||||
if (count($parts) > 1) {
|
||||
$resource = $parts[1];
|
||||
if ($resource == '') {
|
||||
// Warning: empty resource isn't legit.
|
||||
// But if we're normalizing, we may as well take it...
|
||||
}
|
||||
} else {
|
||||
$resource = null;
|
||||
}
|
||||
|
||||
$node = explode("@", $parts[0]);
|
||||
if ((count($node) > 2) || (count($node) == 0)) {
|
||||
throw new Exception("Invalid JID: too many @s");
|
||||
} else if (count($node) == 1) {
|
||||
$domain = $node[0];
|
||||
$node = null;
|
||||
} else {
|
||||
$domain = $node[1];
|
||||
$node = $node[0];
|
||||
if ($node == '') {
|
||||
throw new Exception("Invalid JID: @ but no node");
|
||||
}
|
||||
}
|
||||
|
||||
// Length limits per http://xmpp.org/rfcs/rfc3920.html#addressing
|
||||
if ($node !== null) {
|
||||
if (strlen($node) > 1023) {
|
||||
throw new Exception("Invalid JID: node too long.");
|
||||
}
|
||||
if (preg_match("/[".$nodeprepchars."]/u", $node)) {
|
||||
throw new Exception("Invalid JID node '$node'");
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($domain) > 1023) {
|
||||
throw new Exception("Invalid JID: domain too long.");
|
||||
}
|
||||
if (!common_valid_domain($domain)) {
|
||||
throw new Exception("Invalid JID domain name '$domain'");
|
||||
}
|
||||
|
||||
if ($resource !== null) {
|
||||
if (strlen($resource) > 1023) {
|
||||
throw new Exception("Invalid JID: resource too long.");
|
||||
}
|
||||
if (preg_match("/[".$chars."]/u", $resource)) {
|
||||
throw new Exception("Invalid JID resource '$resource'");
|
||||
}
|
||||
}
|
||||
|
||||
return array('node' => is_null($node) ? null : mb_strtolower($node),
|
||||
'domain' => is_null($domain) ? null : mb_strtolower($domain),
|
||||
'resource' => $resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* normalizes a Jabber ID for comparison
|
||||
* Checks whether a string is a syntactically valid Jabber ID (JID),
|
||||
* either with or without a resource.
|
||||
*
|
||||
* Note that a bare domain can be a valid JID.
|
||||
*
|
||||
* @param string $jid string to check
|
||||
* @param bool $check_domain whether we should validate that domain...
|
||||
*
|
||||
* @return boolean whether the string is a valid JID
|
||||
*/
|
||||
function jabber_valid_full_jid($jid, $check_domain=false)
|
||||
{
|
||||
try {
|
||||
$parts = jabber_split_jid($jid);
|
||||
if ($check_domain) {
|
||||
if (!jabber_check_domain($parts['domain'])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $parts['resource'] !== ''; // missing or present; empty ain't kosher
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a string is a syntactically valid base Jabber ID (JID).
|
||||
* A base JID won't include a resource specifier on the end; since we
|
||||
* take it off when reading input we can't really use them reliably
|
||||
* to direct outgoing messages yet (sorry guys!)
|
||||
*
|
||||
* Note that a bare domain can be a valid JID.
|
||||
*
|
||||
* @param string $jid string to check
|
||||
* @param bool $check_domain whether we should validate that domain...
|
||||
*
|
||||
* @return boolean whether the string is a valid JID
|
||||
*/
|
||||
function jabber_valid_base_jid($jid, $check_domain=false)
|
||||
{
|
||||
try {
|
||||
$parts = jabber_split_jid($jid);
|
||||
if ($check_domain) {
|
||||
if (!jabber_check_domain($parts['domain'])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return ($parts['resource'] === null); // missing; empty ain't kosher
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a Jabber ID for comparison, dropping the resource component if any.
|
||||
*
|
||||
* @param string $jid JID to check
|
||||
* @param bool $check_domain if true, reject if the domain isn't findable
|
||||
*
|
||||
* @return string an equivalent JID in normalized (lowercase) form
|
||||
*/
|
||||
|
||||
function jabber_normalize_jid($jid)
|
||||
{
|
||||
if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) {
|
||||
$node = $matches[1];
|
||||
$server = $matches[2];
|
||||
return strtolower($node.'@'.$server);
|
||||
} else {
|
||||
try {
|
||||
$parts = jabber_split_jid($jid);
|
||||
if ($parts['node'] !== null) {
|
||||
return $parts['node'] . '@' . $parts['domain'];
|
||||
} else {
|
||||
return $parts['domain'];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this domain's got some legit DNS record
|
||||
*/
|
||||
function jabber_check_domain($domain)
|
||||
{
|
||||
if (checkdnsrr("_xmpp-server._tcp." . $domain, "SRV")) {
|
||||
return true;
|
||||
}
|
||||
if (checkdnsrr($domain, "ANY")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* the JID of the Jabber daemon for this StatusNet instance
|
||||
*
|
||||
|
|
45
lib/mail.php
45
lib/mail.php
|
@ -170,8 +170,10 @@ function mail_to_user(&$user, $subject, $body, $headers=array(), $address=null)
|
|||
|
||||
function mail_confirm_address($user, $code, $nickname, $address)
|
||||
{
|
||||
// TRANS: Subject for address confirmation email
|
||||
$subject = _('Email address confirmation');
|
||||
|
||||
// TRANS: Body for address confirmation email.
|
||||
$body = sprintf(_("Hey, %s.\n\n".
|
||||
"Someone just entered this email address on %s.\n\n" .
|
||||
"If it was you, and you want to confirm your entry, ".
|
||||
|
@ -237,11 +239,13 @@ function mail_subscribe_notify_profile($listenee, $other)
|
|||
$headers = _mail_prepare_headers('subscribe', $listenee->nickname, $other->nickname);
|
||||
$headers['From'] = mail_notify_from();
|
||||
$headers['To'] = $name . ' <' . $listenee->email . '>';
|
||||
// TRANS: Subject of new-subscriber notification e-mail
|
||||
$headers['Subject'] = sprintf(_('%1$s is now listening to '.
|
||||
'your notices on %2$s.'),
|
||||
$other->getBestName(),
|
||||
common_config('site', 'name'));
|
||||
|
||||
// TRANS: Main body of new-subscriber notification e-mail
|
||||
$body = sprintf(_('%1$s is now listening to your notices on %2$s.'."\n\n".
|
||||
"\t".'%3$s'."\n\n".
|
||||
'%4$s'.
|
||||
|
@ -255,10 +259,13 @@ function mail_subscribe_notify_profile($listenee, $other)
|
|||
common_config('site', 'name'),
|
||||
$other->profileurl,
|
||||
($other->location) ?
|
||||
// TRANS: Profile info line in new-subscriber notification e-mail
|
||||
sprintf(_("Location: %s"), $other->location) . "\n" : '',
|
||||
($other->homepage) ?
|
||||
// TRANS: Profile info line in new-subscriber notification e-mail
|
||||
sprintf(_("Homepage: %s"), $other->homepage) . "\n" : '',
|
||||
($other->bio) ?
|
||||
// TRANS: Profile info line in new-subscriber notification e-mail
|
||||
sprintf(_("Bio: %s"), $other->bio) . "\n\n" : '',
|
||||
common_config('site', 'name'),
|
||||
common_local_url('emailsettings'));
|
||||
|
@ -287,9 +294,11 @@ function mail_new_incoming_notify($user)
|
|||
|
||||
$headers['From'] = $user->incomingemail;
|
||||
$headers['To'] = $name . ' <' . $user->email . '>';
|
||||
// TRANS: Subject of notification mail for new posting email address
|
||||
$headers['Subject'] = sprintf(_('New email address for posting to %s'),
|
||||
common_config('site', 'name'));
|
||||
|
||||
// TRANS: Body of notification mail for new posting email address
|
||||
$body = sprintf(_("You have a new posting address on %1\$s.\n\n".
|
||||
"Send email to %2\$s to post new messages.\n\n".
|
||||
"More email instructions at %3\$s.\n\n".
|
||||
|
@ -414,6 +423,7 @@ function mail_send_sms_notice_address($notice, $smsemail, $incomingemail)
|
|||
|
||||
$headers['From'] = ($incomingemail) ? $incomingemail : mail_notify_from();
|
||||
$headers['To'] = $to;
|
||||
// TRANS: Subject line for SMS-by-email notification messages
|
||||
$headers['Subject'] = sprintf(_('%s status'),
|
||||
$other->getBestName());
|
||||
|
||||
|
@ -440,11 +450,11 @@ function mail_confirm_sms($code, $nickname, $address)
|
|||
|
||||
$headers['From'] = mail_notify_from();
|
||||
$headers['To'] = $nickname . ' <' . $address . '>';
|
||||
// TRANS: Subject line for SMS-by-email address confirmation message
|
||||
$headers['Subject'] = _('SMS confirmation');
|
||||
|
||||
// FIXME: I18N
|
||||
|
||||
$body = "$nickname: confirm you own this phone number with this code:";
|
||||
// TRANS: Main body heading for SMS-by-email address confirmation message
|
||||
$body = sprintf(_("%s: confirm you own this phone number with this code:"), $nickname);
|
||||
$body .= "\n\n";
|
||||
$body .= $code;
|
||||
$body .= "\n\n";
|
||||
|
@ -464,10 +474,12 @@ function mail_confirm_sms($code, $nickname, $address)
|
|||
function mail_notify_nudge($from, $to)
|
||||
{
|
||||
common_init_locale($to->language);
|
||||
// TRANS: Subject for 'nudge' notification email
|
||||
$subject = sprintf(_('You\'ve been nudged by %s'), $from->nickname);
|
||||
|
||||
$from_profile = $from->getProfile();
|
||||
|
||||
// TRANS: Body for 'nudge' notification email
|
||||
$body = sprintf(_("%1\$s (%2\$s) is wondering what you are up to ".
|
||||
"these days and is inviting you to post some news.\n\n".
|
||||
"So let's hear from you :)\n\n".
|
||||
|
@ -514,10 +526,12 @@ function mail_notify_message($message, $from=null, $to=null)
|
|||
}
|
||||
|
||||
common_init_locale($to->language);
|
||||
// TRANS: Subject for direct-message notification email
|
||||
$subject = sprintf(_('New private message from %s'), $from->nickname);
|
||||
|
||||
$from_profile = $from->getProfile();
|
||||
|
||||
// TRANS: Body for direct-message notification email
|
||||
$body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n".
|
||||
"------------------------------------------------------\n".
|
||||
"%3\$s\n".
|
||||
|
@ -565,8 +579,10 @@ function mail_notify_fave($other, $user, $notice)
|
|||
|
||||
common_init_locale($other->language);
|
||||
|
||||
// TRANS: Subject for favorite notification email
|
||||
$subject = sprintf(_('%s (@%s) added your notice as a favorite'), $bestname, $user->nickname);
|
||||
|
||||
// TRANS: Body for favorite notification email
|
||||
$body = sprintf(_("%1\$s (@%7\$s) just added your notice from %2\$s".
|
||||
" as one of their favorites.\n\n" .
|
||||
"The URL of your notice is:\n\n" .
|
||||
|
@ -622,24 +638,25 @@ function mail_notify_attn($user, $notice)
|
|||
|
||||
common_switch_locale($user->language);
|
||||
|
||||
if ($notice->conversation != $notice->id) {
|
||||
$conversationEmailText = "The full conversation can be read here:\n\n".
|
||||
"\t%5\$s\n\n ";
|
||||
$conversationUrl = common_local_url('conversation',
|
||||
array('id' => $notice->conversation)).'#notice-'.$notice->id;
|
||||
} else {
|
||||
$conversationEmailText = "%5\$s";
|
||||
$conversationUrl = null;
|
||||
}
|
||||
if ($notice->hasConversation()) {
|
||||
$conversationUrl = common_local_url('conversation',
|
||||
array('id' => $notice->conversation)).'#notice-'.$notice->id;
|
||||
// TRANS: Line in @-reply notification e-mail. %s is conversation URL.
|
||||
$conversationEmailText = sprintf(_("The full conversation can be read here:\n\n".
|
||||
"\t%s"), $conversationUrl) . "\n\n";
|
||||
} else {
|
||||
$conversationEmailText = '';
|
||||
}
|
||||
|
||||
$subject = sprintf(_('%s (@%s) sent a notice to your attention'), $bestname, $sender->nickname);
|
||||
|
||||
// TRANS: Body of @-reply notification e-mail.
|
||||
$body = sprintf(_("%1\$s (@%9\$s) just sent a notice to your attention (an '@-reply') on %2\$s.\n\n".
|
||||
"The notice is here:\n\n".
|
||||
"\t%3\$s\n\n" .
|
||||
"It reads:\n\n".
|
||||
"\t%4\$s\n\n" .
|
||||
$conversationEmailText .
|
||||
"%5\$s" .
|
||||
"You can reply back here:\n\n".
|
||||
"\t%6\$s\n\n" .
|
||||
"The list of all @-replies for you here:\n\n" .
|
||||
|
@ -652,7 +669,7 @@ function mail_notify_attn($user, $notice)
|
|||
common_local_url('shownotice',
|
||||
array('notice' => $notice->id)),//%3
|
||||
$notice->content,//%4
|
||||
$conversationUrl,//%5
|
||||
$conversationEmailText,//%5
|
||||
common_local_url('newnotice',
|
||||
array('replyto' => $sender->nickname, 'inreplyto' => $notice->id)),//%6
|
||||
common_local_url('replies',
|
||||
|
|
|
@ -543,18 +543,7 @@ class NoticeListItem extends Widget
|
|||
|
||||
function showContext()
|
||||
{
|
||||
$hasConversation = false;
|
||||
if (!empty($this->notice->conversation)) {
|
||||
$conversation = Notice::conversationStream(
|
||||
$this->notice->conversation,
|
||||
1,
|
||||
1
|
||||
);
|
||||
if ($conversation->N > 0) {
|
||||
$hasConversation = true;
|
||||
}
|
||||
}
|
||||
if ($hasConversation) {
|
||||
if ($this->notice->hasConversation()) {
|
||||
$conv = Conversation::staticGet(
|
||||
'id',
|
||||
$this->notice->conversation
|
||||
|
|
|
@ -91,6 +91,7 @@ class Plugin
|
|||
$path = INSTALLDIR . "/plugins/$name/locale";
|
||||
if (file_exists($path) && is_dir($path)) {
|
||||
bindtextdomain($name, $path);
|
||||
bind_textdomain_codeset($name, 'UTF-8');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
class ProfileFormAction extends Action
|
||||
class ProfileFormAction extends RedirectingAction
|
||||
{
|
||||
var $profile = null;
|
||||
|
||||
|
@ -101,29 +101,6 @@ class ProfileFormAction extends Action
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return to the calling page based on hidden arguments
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function returnToArgs()
|
||||
{
|
||||
foreach ($this->args as $k => $v) {
|
||||
if ($k == 'returnto-action') {
|
||||
$action = $v;
|
||||
} else if (substr($k, 0, 9) == 'returnto-') {
|
||||
$args[substr($k, 9)] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if ($action) {
|
||||
common_redirect(common_local_url($action, $args), 303);
|
||||
} else {
|
||||
$this->clientError(_("No return-to arguments."));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle a POST request
|
||||
*
|
||||
|
|
96
lib/redirectingaction.php
Normal file
96
lib/redirectingaction.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
/**
|
||||
* Superclass for actions that redirect to a given return-to page on completion.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2009-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/>.
|
||||
*
|
||||
* @category Action
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Brion Vibber <brion@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Superclass for actions that redirect to a given return-to page on completion.
|
||||
*
|
||||
* @category Action
|
||||
* @package StatusNet
|
||||
* @author Evan Prodromou <evan@status.net>
|
||||
* @author Brion Vibber <brion@status.net>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
|
||||
class RedirectingAction extends Action
|
||||
{
|
||||
|
||||
/**
|
||||
* Redirect browser to the page our hidden parameters requested,
|
||||
* or if none given, to the url given by $this->defaultReturnTo().
|
||||
*
|
||||
* To be called only after successful processing.
|
||||
*
|
||||
* @fixme rename this -- it obscures Action::returnToArgs() which
|
||||
* returns a list of arguments, and is a bit confusing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function returnToArgs()
|
||||
{
|
||||
// Now, gotta figure where we go back to
|
||||
$action = false;
|
||||
$args = array();
|
||||
$params = array();
|
||||
foreach ($this->args as $k => $v) {
|
||||
if ($k == 'returnto-action') {
|
||||
$action = $v;
|
||||
} else if (substr($k, 0, 15) == 'returnto-param-') {
|
||||
$params[substr($k, 15)] = $v;
|
||||
} elseif (substr($k, 0, 9) == 'returnto-') {
|
||||
$args[substr($k, 9)] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if ($action) {
|
||||
common_redirect(common_local_url($action, $args, $params), 303);
|
||||
} else {
|
||||
$url = $this->defaultReturnToUrl();
|
||||
}
|
||||
common_redirect($url, 303);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we reached this form without returnto arguments, where should
|
||||
* we go? May be overridden by subclasses to a reasonable destination
|
||||
* for that action; default implementation throws an exception.
|
||||
*
|
||||
* @return string URL
|
||||
*/
|
||||
function defaultReturnTo()
|
||||
{
|
||||
$this->clientError(_("No return-to arguments."));
|
||||
}
|
||||
}
|
|
@ -39,7 +39,8 @@ class StompQueueManager extends QueueManager
|
|||
protected $base;
|
||||
protected $control;
|
||||
|
||||
protected $useTransactions = true;
|
||||
protected $useTransactions;
|
||||
protected $useAcks;
|
||||
|
||||
protected $sites = array();
|
||||
protected $subscriptions = array();
|
||||
|
@ -59,11 +60,13 @@ class StompQueueManager extends QueueManager
|
|||
} else {
|
||||
$this->servers = array($server);
|
||||
}
|
||||
$this->username = common_config('queue', 'stomp_username');
|
||||
$this->password = common_config('queue', 'stomp_password');
|
||||
$this->base = common_config('queue', 'queue_basename');
|
||||
$this->control = common_config('queue', 'control_channel');
|
||||
$this->breakout = common_config('queue', 'breakout');
|
||||
$this->username = common_config('queue', 'stomp_username');
|
||||
$this->password = common_config('queue', 'stomp_password');
|
||||
$this->base = common_config('queue', 'queue_basename');
|
||||
$this->control = common_config('queue', 'control_channel');
|
||||
$this->breakout = common_config('queue', 'breakout');
|
||||
$this->useTransactions = common_config('queue', 'stomp_transactions');
|
||||
$this->useAcks = common_config('queue', 'stomp_acks');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -703,13 +706,15 @@ class StompQueueManager extends QueueManager
|
|||
|
||||
protected function ack($idx, $frame)
|
||||
{
|
||||
if ($this->useTransactions) {
|
||||
if (empty($this->transaction[$idx])) {
|
||||
throw new Exception("Tried to ack but not in a transaction");
|
||||
if ($this->useAcks) {
|
||||
if ($this->useTransactions) {
|
||||
if (empty($this->transaction[$idx])) {
|
||||
throw new Exception("Tried to ack but not in a transaction");
|
||||
}
|
||||
$this->cons[$idx]->ack($frame, $this->transaction[$idx]);
|
||||
} else {
|
||||
$this->cons[$idx]->ack($frame);
|
||||
}
|
||||
$this->cons[$idx]->ack($frame, $this->transaction[$idx]);
|
||||
} else {
|
||||
$this->cons[$idx]->ack($frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
75
lib/util.php
75
lib/util.php
|
@ -1308,12 +1308,38 @@ function common_mtrand($bytes)
|
|||
return $enc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the given URL as the return destination for a future
|
||||
* form submission, to be read by common_get_returnto().
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @fixme as a session-global setting, this can allow multiple forms
|
||||
* to conflict and overwrite each others' returnto destinations if
|
||||
* the user has multiple tabs or windows open.
|
||||
*
|
||||
* Should refactor to index with a token or otherwise only pass the
|
||||
* data along its intended path.
|
||||
*/
|
||||
function common_set_returnto($url)
|
||||
{
|
||||
common_ensure_session();
|
||||
$_SESSION['returnto'] = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a return-destination URL previously recorded by
|
||||
* common_set_returnto().
|
||||
*
|
||||
* @return mixed URL string or null
|
||||
*
|
||||
* @fixme as a session-global setting, this can allow multiple forms
|
||||
* to conflict and overwrite each others' returnto destinations if
|
||||
* the user has multiple tabs or windows open.
|
||||
*
|
||||
* Should refactor to index with a token or otherwise only pass the
|
||||
* data along its intended path.
|
||||
*/
|
||||
function common_get_returnto()
|
||||
{
|
||||
common_ensure_session();
|
||||
|
@ -1433,6 +1459,55 @@ function common_valid_tag($tag)
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if given domain or address literal is valid
|
||||
* eg for use in JIDs and URLs. Does not check if the domain
|
||||
* exists!
|
||||
*
|
||||
* @param string $domain
|
||||
* @return boolean valid or not
|
||||
*/
|
||||
function common_valid_domain($domain)
|
||||
{
|
||||
$octet = "(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])";
|
||||
$ipv4 = "(?:$octet(?:\.$octet){3})";
|
||||
if (preg_match("/^$ipv4$/u", $domain)) return true;
|
||||
|
||||
$group = "(?:[0-9a-f]{1,4})";
|
||||
$ipv6 = "(?:\[($group(?::$group){0,7})?(::)?($group(?::$group){0,7})?\])"; // http://tools.ietf.org/html/rfc3513#section-2.2
|
||||
|
||||
if (preg_match("/^$ipv6$/ui", $domain, $matches)) {
|
||||
$before = explode(":", $matches[1]);
|
||||
$zeroes = $matches[2];
|
||||
$after = explode(":", $matches[3]);
|
||||
if ($zeroes) {
|
||||
$min = 0;
|
||||
$max = 7;
|
||||
} else {
|
||||
$min = 1;
|
||||
$max = 8;
|
||||
}
|
||||
$explicit = count($before) + count($after);
|
||||
if ($explicit < $min || $explicit > $max) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
require_once "Net/IDNA.php";
|
||||
$idn = Net_IDNA::getInstance();
|
||||
$domain = $idn->encode($domain);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$subdomain = "(?:[a-z0-9][a-z0-9-]*)"; // @fixme
|
||||
$fqdn = "(?:$subdomain(?:\.$subdomain)*\.?)";
|
||||
|
||||
return preg_match("/^$fqdn$/ui", $domain);
|
||||
}
|
||||
|
||||
/* Following functions are copied from MediaWiki GlobalFunctions.php
|
||||
* and written by Evan Prodromou. */
|
||||
|
||||
|
|
6544
locale/af/LC_MESSAGES/statusnet.po
Normal file
6544
locale/af/LC_MESSAGES/statusnet.po
Normal file
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
6972
locale/gl/LC_MESSAGES/statusnet.po
Normal file
6972
locale/gl/LC_MESSAGES/statusnet.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user