summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/accountsettingsaction.php30
-rw-r--r--lib/action.php72
-rw-r--r--lib/activity.php3
-rw-r--r--lib/adminpanelaction.php11
-rw-r--r--lib/apiaction.php4
-rw-r--r--lib/apiauth.php5
-rw-r--r--lib/applicationeditform.php41
-rw-r--r--lib/applicationlist.php17
-rw-r--r--lib/atomgroupnoticefeed.php3
-rw-r--r--lib/atomusernoticefeed.php3
-rw-r--r--lib/attachmentlist.php3
-rw-r--r--lib/command.php36
-rw-r--r--lib/deluserqueuehandler.php10
-rw-r--r--lib/htmloutputter.php1
-rw-r--r--lib/implugin.php4
-rw-r--r--lib/installer.php578
-rw-r--r--lib/language.php3
-rw-r--r--lib/mail.php45
-rw-r--r--lib/noticeform.php4
-rw-r--r--lib/noticelist.php25
-rw-r--r--lib/profileformaction.php25
-rw-r--r--lib/redirectingaction.php96
-rw-r--r--lib/statusnet.php15
-rw-r--r--lib/util.php49
24 files changed, 973 insertions, 110 deletions
diff --git a/lib/accountsettingsaction.php b/lib/accountsettingsaction.php
index c79a1f5d7..57740f8b8 100644
--- a/lib/accountsettingsaction.php
+++ b/lib/accountsettingsaction.php
@@ -105,27 +105,45 @@ class AccountSettingsNav extends Widget
$user = common_current_user();
if(Event::handle('StartAccountSettingsProfileMenuItem', array($this, &$menu))){
- $this->showMenuItem('profilesettings',_('Profile'),_('Change your profile settings'));
+ // TRANS: Link title attribute in user account settings menu.
+ $title = _('Change your profile settings');
+ // TRANS: Link description in user account settings menu.
+ $this->showMenuItem('profilesettings',_('Profile'),$title);
Event::handle('EndAccountSettingsProfileMenuItem', array($this, &$menu));
}
if(Event::handle('StartAccountSettingsAvatarMenuItem', array($this, &$menu))){
- $this->showMenuItem('avatarsettings',_('Avatar'),_('Upload an avatar'));
+ // TRANS: Link title attribute in user account settings menu.
+ $title = _('Upload an avatar');
+ // TRANS: Link description in user account settings menu.
+ $this->showMenuItem('avatarsettings',_('Avatar'),$title);
Event::handle('EndAccountSettingsAvatarMenuItem', array($this, &$menu));
}
if(Event::handle('StartAccountSettingsPasswordMenuItem', array($this, &$menu))){
- $this->showMenuItem('passwordsettings',_('Password'),_('Change your password'));
+ // TRANS: Link title attribute in user account settings menu.
+ $title = _('Change your password');
+ // TRANS: Link description in user account settings menu.
+ $this->showMenuItem('passwordsettings',_('Password'),$title);
Event::handle('EndAccountSettingsPasswordMenuItem', array($this, &$menu));
}
if(Event::handle('StartAccountSettingsEmailMenuItem', array($this, &$menu))){
- $this->showMenuItem('emailsettings',_('Email'),_('Change email handling'));
+ // TRANS: Link title attribute in user account settings menu.
+ $title = _('Change email handling');
+ // TRANS: Link description in user account settings menu.
+ $this->showMenuItem('emailsettings',_('Email'),$title);
Event::handle('EndAccountSettingsEmailMenuItem', array($this, &$menu));
}
if(Event::handle('StartAccountSettingsDesignMenuItem', array($this, &$menu))){
- $this->showMenuItem('userdesignsettings',_('Design'),_('Design your profile'));
+ // TRANS: Link title attribute in user account settings menu.
+ $title = _('Design your profile');
+ // TRANS: Link description in user account settings menu.
+ $this->showMenuItem('userdesignsettings',_('Design'),$title);
Event::handle('EndAccountSettingsDesignMenuItem', array($this, &$menu));
}
if(Event::handle('StartAccountSettingsOtherMenuItem', array($this, &$menu))){
- $this->showMenuItem('othersettings',_('Other'),_('Other options'));
+ // TRANS: Link title attribute in user account settings menu.
+ $title = _('Other options');
+ // TRANS: Link description in user account settings menu.
+ $this->showMenuItem('othersettings',_('Other'),$title);
Event::handle('EndAccountSettingsOtherMenuItem', array($this, &$menu));
}
diff --git a/lib/action.php b/lib/action.php
index 09113a598..4296ae7de 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -141,6 +141,7 @@ class Action extends HTMLOutputter // lawsuit
function showTitle()
{
$this->element('title', null,
+ // TRANS: Page title. %1$s is the title, %2$s is the site name.
sprintf(_("%1\$s - %2\$s"),
$this->title(),
common_config('site', 'name')));
@@ -156,6 +157,7 @@ class Action extends HTMLOutputter // lawsuit
function title()
{
+ // TRANS: Page title for a page without a title set.
return _("Untitled page");
}
@@ -420,6 +422,7 @@ class Action extends HTMLOutputter // lawsuit
{
$user = common_current_user();
$this->elementStart('dl', array('id' => 'site_nav_global_primary'));
+ // TRANS: DT element for primary navigation menu. String is hidden in default CSS.
$this->element('dt', null, _('Primary site navigation'));
$this->elementStart('dd');
$this->elementStart('ul', array('class' => 'nav'));
@@ -427,31 +430,31 @@ class Action extends HTMLOutputter // lawsuit
if ($user) {
// TRANS: Tooltip for main menu option "Personal"
$tooltip = _m('TOOLTIP', 'Personal profile and friends timeline');
- // TRANS: Main menu option when logged in for access to personal profile and friends timeline
$this->menuItem(common_local_url('all', array('nickname' => $user->nickname)),
+ // TRANS: Main menu option when logged in for access to personal profile and friends timeline
_m('MENU', 'Personal'), $tooltip, false, 'nav_home');
// TRANS: Tooltip for main menu option "Account"
$tooltip = _m('TOOLTIP', 'Change your email, avatar, password, profile');
- // TRANS: Main menu option when logged in for access to user settings
$this->menuItem(common_local_url('profilesettings'),
+ // TRANS: Main menu option when logged in for access to user settings
_('Account'), $tooltip, false, 'nav_account');
// TRANS: Tooltip for main menu option "Services"
$tooltip = _m('TOOLTIP', 'Connect to services');
- // TRANS: Main menu option when logged in and connection are possible for access to options to connect to other services
$this->menuItem(common_local_url('oauthconnectionssettings'),
+ // TRANS: Main menu option when logged in and connection are possible for access to options to connect to other services
_('Connect'), $tooltip, false, 'nav_connect');
if ($user->hasRight(Right::CONFIGURESITE)) {
// TRANS: Tooltip for menu option "Admin"
$tooltip = _m('TOOLTIP', 'Change site configuration');
- // TRANS: Main menu option when logged in and site admin for access to site configuration
$this->menuItem(common_local_url('siteadminpanel'),
+ // TRANS: Main menu option when logged in and site admin for access to site configuration
_m('MENU', 'Admin'), $tooltip, false, 'nav_admin');
}
if (common_config('invite', 'enabled')) {
// TRANS: Tooltip for main menu option "Invite"
$tooltip = _m('TOOLTIP', 'Invite friends and colleagues to join you on %s');
- // TRANS: Main menu option when logged in and invitations are allowed for inviting new users
$this->menuItem(common_local_url('invite'),
+ // TRANS: Main menu option when logged in and invitations are allowed for inviting new users
_m('MENU', 'Invite'),
sprintf($tooltip,
common_config('site', 'name')),
@@ -459,16 +462,16 @@ class Action extends HTMLOutputter // lawsuit
}
// TRANS: Tooltip for main menu option "Logout"
$tooltip = _m('TOOLTIP', 'Logout from the site');
- // TRANS: Main menu option when logged in to log out the current user
$this->menuItem(common_local_url('logout'),
+ // TRANS: Main menu option when logged in to log out the current user
_m('MENU', 'Logout'), $tooltip, false, 'nav_logout');
}
else {
if (!common_config('site', 'closed')) {
// TRANS: Tooltip for main menu option "Register"
$tooltip = _m('TOOLTIP', 'Create an account');
- // TRANS: Main menu option when not logged in to register a new account
$this->menuItem(common_local_url('register'),
+ // TRANS: Main menu option when not logged in to register a new account
_m('MENU', 'Register'), $tooltip, false, 'nav_register');
}
// TRANS: Tooltip for main menu option "Login"
@@ -575,6 +578,7 @@ class Action extends HTMLOutputter // lawsuit
function showLocalNavBlock()
{
$this->elementStart('dl', array('id' => 'site_nav_local_views'));
+ // TRANS: DT element for local views block. String is hidden in default CSS.
$this->element('dt', null, _('Local views'));
$this->elementStart('dd');
$this->showLocalNav();
@@ -641,6 +645,7 @@ class Action extends HTMLOutputter // lawsuit
$this->elementStart('dl', array('id' => 'page_notice',
'class' => 'system_notice'));
+ // TRANS: DT element for page notice. String is hidden in default CSS.
$this->element('dt', null, _('Page notice'));
$this->elementStart('dd');
if (Event::handle('StartShowPageNotice', array($this))) {
@@ -743,28 +748,37 @@ class Action extends HTMLOutputter // lawsuit
function showSecondaryNav()
{
$this->elementStart('dl', array('id' => 'site_nav_global_secondary'));
+ // TRANS: DT element for secondary navigation menu. String is hidden in default CSS.
$this->element('dt', null, _('Secondary site navigation'));
$this->elementStart('dd', null);
$this->elementStart('ul', array('class' => 'nav'));
if (Event::handle('StartSecondaryNav', array($this))) {
$this->menuItem(common_local_url('doc', array('title' => 'help')),
+ // TRANS: Secondary navigation menu option leading to help on StatusNet.
_('Help'));
$this->menuItem(common_local_url('doc', array('title' => 'about')),
+ // TRANS: Secondary navigation menu option leading to text about StatusNet site.
_('About'));
$this->menuItem(common_local_url('doc', array('title' => 'faq')),
+ // TRANS: Secondary navigation menu option leading to Frequently Asked Questions.
_('FAQ'));
$bb = common_config('site', 'broughtby');
if (!empty($bb)) {
$this->menuItem(common_local_url('doc', array('title' => 'tos')),
+ // TRANS: Secondary navigation menu option leading to Terms of Service.
_('TOS'));
}
$this->menuItem(common_local_url('doc', array('title' => 'privacy')),
+ // TRANS: Secondary navigation menu option leading to privacy policy.
_('Privacy'));
$this->menuItem(common_local_url('doc', array('title' => 'source')),
+ // TRANS: Secondary navigation menu option.
_('Source'));
$this->menuItem(common_local_url('version'),
+ // TRANS: Secondary navigation menu option leading to version information on the StatusNet site.
_('Version'));
$this->menuItem(common_local_url('doc', array('title' => 'contact')),
+ // TRANS: Secondary navigation menu option leading to contact information on the StatusNet site.
_('Contact'));
$this->menuItem(common_local_url('doc', array('title' => 'badge')),
_('Badge'));
@@ -795,16 +809,18 @@ class Action extends HTMLOutputter // lawsuit
*/
function showStatusNetLicense()
{
+ // TRANS: DT element for StatusNet software license.
$this->element('dt', array('id' => 'site_statusnet_license'), _('StatusNet software license'));
$this->elementStart('dd', null);
- // @fixme drop the final spaces in the messages when at good spot
- // to let translations get updated.
if (common_config('site', 'broughtby')) {
- $instr = _('**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%). ');
+ // TRANS: First sentence of the StatusNet site license. Used if 'broughtby' is set.
+ $instr = _('**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%).');
} else {
- $instr = _('**%%site.name%%** is a microblogging service. ');
+ // TRANS: First sentence of the StatusNet site license. Used if 'broughtby' is not set.
+ $instr = _('**%%site.name%%** is a microblogging service.');
}
$instr .= ' ';
+ // TRANS: Second sentence of the StatusNet site license. Mentions the StatusNet source code license.
$instr .= sprintf(_('It runs the [StatusNet](http://status.net/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), STATUSNET_VERSION);
$output = common_markup_to_html($instr);
$this->raw($output);
@@ -820,19 +836,25 @@ class Action extends HTMLOutputter // lawsuit
function showContentLicense()
{
if (Event::handle('StartShowContentLicense', array($this))) {
+ // TRANS: DT element for StatusNet site content license.
$this->element('dt', array('id' => 'site_content_license'), _('Site content license'));
$this->elementStart('dd', array('id' => 'site_content_license_cc'));
switch (common_config('license', 'type')) {
case 'private':
+ // TRANS: Content license displayed when license is set to 'private'.
+ // TRANS: %1$s is the site name.
$this->element('p', null, sprintf(_('Content and data of %1$s are private and confidential.'),
common_config('site', 'name')));
// fall through
case 'allrightsreserved':
if (common_config('license', 'owner')) {
+ // TRANS: Content license displayed when license is set to 'allrightsreserved'.
+ // TRANS: %1$s is the copyright owner.
$this->element('p', null, sprintf(_('Content and data copyright by %1$s. All rights reserved.'),
common_config('license', 'owner')));
} else {
+ // TRANS: Content license displayed when license is set to 'allrightsreserved' and no owner is set.
$this->element('p', null, _('Content and data copyright by contributors. All rights reserved.'));
}
break;
@@ -845,14 +867,16 @@ class Action extends HTMLOutputter // lawsuit
'width' => '80',
'height' => '15'));
$this->text(' ');
- //TODO: This is dirty: i18n
- $this->text(_('All '.common_config('site', 'name').' content and data are available under the '));
- $this->element('a', array('class' => 'license',
- 'rel' => 'external license',
- 'href' => common_config('license', 'url')),
- common_config('license', 'title'));
- $this->text(' ');
- $this->text(_('license.'));
+ // TRANS: license message in footer. %1$s is the site name, %2$s is a link to the license URL, with a licence name set in configuration.
+ $notice = _('All %1$s content and data are available under the %2$s license.');
+ $link = "<a class=\"license\" rel=\"external license\" href=\"" .
+ htmlspecialchars(common_config('license', 'url')) .
+ "\">" .
+ htmlspecialchars(common_config('license', 'title')) .
+ "</a>";
+ $this->raw(sprintf(htmlspecialchars($notice),
+ htmlspecialchars(common_config('site', 'name')),
+ $link));
$this->elementEnd('p');
break;
}
@@ -1146,11 +1170,15 @@ class Action extends HTMLOutputter // lawsuit
*
* @return nothing
*/
+ // XXX: The messages in this pagination method only tailor to navigating
+ // notices. In other lists, "Previous"/"Next" type navigation is
+ // desirable, but not available.
function pagination($have_before, $have_after, $page, $action, $args=null)
{
// Does a little before-after block for next/prev page
if ($have_before || $have_after) {
$this->elementStart('dl', 'pagination');
+ // TRANS: DT element for pagination (previous/next, etc.).
$this->element('dt', null, _('Pagination'));
$this->elementStart('dd', null);
$this->elementStart('ul', array('class' => 'nav'));
@@ -1160,6 +1188,8 @@ class Action extends HTMLOutputter // lawsuit
$this->elementStart('li', array('class' => 'nav_prev'));
$this->element('a', array('href' => common_local_url($action, $args, $pargs),
'rel' => 'prev'),
+ // TRANS: Pagination message to go to a page displaying information more in the
+ // TRANS: present than the currently displayed information.
_('After'));
$this->elementEnd('li');
}
@@ -1168,6 +1198,8 @@ class Action extends HTMLOutputter // lawsuit
$this->elementStart('li', array('class' => 'nav_next'));
$this->element('a', array('href' => common_local_url($action, $args, $pargs),
'rel' => 'next'),
+ // TRANS: Pagination message to go to a page displaying information more in the
+ // TRANS: past than the currently displayed information.
_('Before'));
$this->elementEnd('li');
}
@@ -1211,6 +1243,8 @@ class Action extends HTMLOutputter // lawsuit
* @return void
*/
+ // XXX: Finding this type of check with the same message about 50 times.
+ // Possible to refactor?
function checkSessionToken()
{
// CSRF protection
diff --git a/lib/activity.php b/lib/activity.php
index 5d6230c6d..365bb6258 100644
--- a/lib/activity.php
+++ b/lib/activity.php
@@ -117,7 +117,8 @@ class Activity
// Insist on a feed's root DOMElement; don't allow a DOMDocument
if ($feed instanceof DOMDocument) {
throw new ClientException(
- _("Expecting a root feed element but got a whole XML document.")
+ // TRANS: Client exception thrown when a feed instance is a DOMDocument.
+ _('Expecting a root feed element but got a whole XML document.')
);
}
diff --git a/lib/adminpanelaction.php b/lib/adminpanelaction.php
index d87981b6a..6c9947608 100644
--- a/lib/adminpanelaction.php
+++ b/lib/adminpanelaction.php
@@ -69,7 +69,7 @@ class AdminPanelAction extends Action
// User must be logged in.
if (!common_logged_in()) {
- // TRANS: Client error message
+ // TRANS: Client error message thrown when trying to access the admin panel while not logged in.
$this->clientError(_('Not logged in.'));
return false;
}
@@ -94,7 +94,7 @@ class AdminPanelAction extends Action
// User must have the right to change admin settings
if (!$user->hasRight(Right::CONFIGURESITE)) {
- // TRANS: Client error message
+ // TRANS: Client error message thrown when a user tries to change admin settings but has no access rights.
$this->clientError(_('You cannot make changes to this site.'));
return false;
}
@@ -106,7 +106,7 @@ class AdminPanelAction extends Action
$name = mb_substr($name, 0, -10);
if (!self::canAdmin($name)) {
- // TRANS: Client error message
+ // TRANS: Client error message throw when a certain panel's settings cannot be changed.
$this->clientError(_('Changes to that panel are not allowed.'), 403);
return false;
}
@@ -225,7 +225,7 @@ class AdminPanelAction extends Action
function showForm()
{
- // TRANS: Client error message
+ // TRANS: Client error message.
$this->clientError(_('showForm() not implemented.'));
return;
}
@@ -279,7 +279,8 @@ class AdminPanelAction extends Action
$result = $config->delete();
if (!$result) {
common_log_db_error($config, 'DELETE', __FILE__);
- // TRANS: Client error message
+ // TRANS: Client error message thrown if design settings could not be deleted in
+ // TRANS: the admin panel Design.
$this->clientError(_("Unable to delete design setting."));
return null;
}
diff --git a/lib/apiaction.php b/lib/apiaction.php
index 59dc47c23..d5580abd3 100644
--- a/lib/apiaction.php
+++ b/lib/apiaction.php
@@ -102,6 +102,7 @@ class ApiAction extends Action
function handle($args)
{
+ header('Access-Control-Allow-Origin: *');
parent::handle($args);
}
@@ -1065,6 +1066,7 @@ class ApiAction extends Action
$this->initTwitterAtom();
break;
default:
+ // TRANS: Client error on an API request with an unsupported data format.
$this->clientError(_('Not a supported data format.'));
break;
}
@@ -1093,6 +1095,7 @@ class ApiAction extends Action
$this->endTwitterRss();
break;
default:
+ // TRANS: Client error on an API request with an unsupported data format.
$this->clientError(_('Not a supported data format.'));
break;
}
@@ -1209,6 +1212,7 @@ class ApiAction extends Action
$this->showJsonObjects($profile_array);
break;
default:
+ // TRANS: Client error on an API request with an unsupported data format.
$this->clientError(_('Not a supported data format.'));
return;
}
diff --git a/lib/apiauth.php b/lib/apiauth.php
index e78de618e..d6ad7e021 100644
--- a/lib/apiauth.php
+++ b/lib/apiauth.php
@@ -91,6 +91,7 @@ class ApiAuthAction extends ApiAction
if ($this->isReadOnly($args) == false) {
if ($this->access != self::READ_WRITE) {
+ // TRANS: Client error 401.
$msg = _('API resource requires read-write access, ' .
'but you only have read access.');
$this->clientError($msg, 401, $this->format);
@@ -273,8 +274,8 @@ class ApiAuthAction extends ApiAction
list($proxy, $ip) = common_client_ip();
- $msg = sprintf(_('Failed API auth attempt, nickname = %1$s, ' .
- 'proxy = %2$s, ip = %3$s'),
+ $msg = sprintf( 'Failed API auth attempt, nickname = %1$s, ' .
+ 'proxy = %2$s, ip = %3$s',
$this->auth_user_nickname,
$proxy,
$ip);
diff --git a/lib/applicationeditform.php b/lib/applicationeditform.php
index 9b7d05861..81c8fb018 100644
--- a/lib/applicationeditform.php
+++ b/lib/applicationeditform.php
@@ -133,6 +133,7 @@ class ApplicationEditForm extends Form
function formLegend()
{
+ // TRANS: Form legend.
$this->out->element('legend', null, _('Edit application'));
}
@@ -177,10 +178,12 @@ class ApplicationEditForm extends Form
}
$this->out->element('label', array('for' => 'app_icon'),
+ // TRANS: Form input field label for application icon.
_('Icon'));
$this->out->element('input', array('name' => 'app_icon',
'type' => 'file',
'id' => 'app_icon'));
+ // TRANS: Form guide.
$this->out->element('p', 'form_guide', _('Icon for this application'));
$this->out->element('input', array('name' => 'MAX_FILE_SIZE',
'type' => 'hidden',
@@ -192,6 +195,7 @@ class ApplicationEditForm extends Form
$this->out->hidden('application_id', $id);
+ // TRANS: Form input field label for application name.
$this->out->input('name', _('Name'),
($this->out->arg('name')) ? $this->out->arg('name') : $name);
@@ -201,11 +205,14 @@ class ApplicationEditForm extends Form
$maxDesc = Oauth_application::maxDesc();
if ($maxDesc > 0) {
+ // TRANS: Form input field instructions.
$descInstr = sprintf(_('Describe your application in %d characters'),
$maxDesc);
} else {
+ // TRANS: Form input field instructions.
$descInstr = _('Describe your application');
}
+ // TRANS: Form input field label.
$this->out->textarea('description', _('Description'),
($this->out->arg('description')) ? $this->out->arg('description') : $description,
$descInstr);
@@ -213,27 +220,39 @@ class ApplicationEditForm extends Form
$this->out->elementEnd('li');
$this->out->elementStart('li');
+ // TRANS: Form input field instructions.
+ $instruction = _('URL of the homepage of this application');
+ // TRANS: Form input field label.
$this->out->input('source_url', _('Source URL'),
($this->out->arg('source_url')) ? $this->out->arg('source_url') : $source_url,
- _('URL of the homepage of this application'));
+ $instruction);
$this->out->elementEnd('li');
$this->out->elementStart('li');
+ // TRANS: Form input field instructions.
+ $instruction = _('Organization responsible for this application');
+ // TRANS: Form input field label.
$this->out->input('organization', _('Organization'),
($this->out->arg('organization')) ? $this->out->arg('organization') : $organization,
- _('Organization responsible for this application'));
+ $instruction);
$this->out->elementEnd('li');
$this->out->elementStart('li');
+ // TRANS: Form input field instructions.
+ $instruction = _('URL for the homepage of the organization');
+ // TRANS: Form input field label.
$this->out->input('homepage', _('Homepage'),
($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
- _('URL for the homepage of the organization'));
+ $instruction);
$this->out->elementEnd('li');
$this->out->elementStart('li');
+ // TRANS: Form input field instructions.
+ $instruction = _('URL to redirect to after authentication');
+ // TRANS: Form input field label.
$this->out->input('callback_url', ('Callback URL'),
($this->out->arg('callback_url')) ? $this->out->arg('callback_url') : $callback_url,
- _('URL to redirect to after authentication'));
+ $instruction);
$this->out->elementEnd('li');
$this->out->elementStart('li', array('id' => 'application_types'));
@@ -255,6 +274,7 @@ class ApplicationEditForm extends Form
$this->out->element('label', array('for' => 'app_type-browser',
'class' => 'radio'),
+ // TRANS: Radio button label for application type
_('Browser'));
$attrs = array('name' => 'app_type',
@@ -271,7 +291,9 @@ class ApplicationEditForm extends Form
$this->out->element('label', array('for' => 'app_type-desktop',
'class' => 'radio'),
+ // TRANS: Radio button label for application type
_('Desktop'));
+ // TRANS: Form guide.
$this->out->element('p', 'form_guide', _('Type of application, browser or desktop'));
$this->out->elementEnd('li');
@@ -294,6 +316,7 @@ class ApplicationEditForm extends Form
$this->out->element('label', array('for' => 'default_access_type-ro',
'class' => 'radio'),
+ // TRANS: Radio button label for access type.
_('Read-only'));
$attrs = array('name' => 'default_access_type',
@@ -312,7 +335,9 @@ class ApplicationEditForm extends Form
$this->out->element('label', array('for' => 'default_access_type-rw',
'class' => 'radio'),
+ // TRANS: Radio button label for access type.
_('Read-write'));
+ // TRANS: Form guide.
$this->out->element('p', 'form_guide', _('Default access for this application: read-only, or read-write'));
$this->out->elementEnd('li');
@@ -328,9 +353,13 @@ class ApplicationEditForm extends Form
function formActions()
{
- $this->out->submit('cancel', _('Cancel'), 'submit form_action-primary',
+ // TRANS: Button label
+ $this->out->submit('cancel', _m('BUTTON','Cancel'), 'submit form_action-primary',
+ // TRANS: Submit button title
'cancel', _('Cancel'));
- $this->out->submit('save', _('Save'), 'submit form_action-secondary',
+ // TRANS: Button label
+ $this->out->submit('save', _m('BUTTON','Save'), 'submit form_action-secondary',
+ // TRANS: Submit button title
'save', _('Save'));
}
}
diff --git a/lib/applicationlist.php b/lib/applicationlist.php
index 3abb1f8aa..904f8981d 100644
--- a/lib/applicationlist.php
+++ b/lib/applicationlist.php
@@ -88,7 +88,6 @@ class ApplicationList extends Widget
function showApplication()
{
-
$user = common_current_user();
$this->out->elementStart('li', array('class' => 'application',
@@ -133,11 +132,16 @@ class ApplicationList extends Widget
$this->out->elementStart('li');
- $access = ($this->application->access_type & Oauth_application::$writeAccess)
- ? 'read-write' : 'read-only';
+ // TRANS: Application access type
+ $readWriteText = _('read-write');
+ // TRANS: Application access type
+ $readOnlyText = _('read-only');
- $txt = 'Approved ' . common_date_string($appUser->modified) .
- " - $access access.";
+ $access = ($this->application->access_type & Oauth_application::$writeAccess)
+ ? $readWriteText : $readOnlyText;
+ $modifiedDate = common_date_string($appUser->modified);
+ // TRANS: Used in application list. %1$s is a modified date, %2$s is access type (read-write or read-only)
+ $txt = sprintf(_('Approved %1$s - "%2$s" access.'),$modifiedDate,$access);
$this->out->raw($txt);
$this->out->elementEnd('li');
@@ -151,7 +155,8 @@ class ApplicationList extends Widget
$this->out->elementStart('fieldset');
$this->out->hidden('id', $this->application->id);
$this->out->hidden('token', common_session_token());
- $this->out->submit('revoke', _('Revoke'));
+ // TRANS: Button label
+ $this->out->submit('revoke', _m('BUTTON','Revoke'));
$this->out->elementEnd('fieldset');
$this->out->elementEnd('form');
$this->out->elementEnd('li');
diff --git a/lib/atomgroupnoticefeed.php b/lib/atomgroupnoticefeed.php
index 08c1c707c..b4810d04a 100644
--- a/lib/atomgroupnoticefeed.php
+++ b/lib/atomgroupnoticefeed.php
@@ -58,11 +58,14 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed
parent::__construct($indent);
$this->group = $group;
+ // TRANS: Title in atom group notice feed. %s is a group name.
$title = sprintf(_("%s timeline"), $group->nickname);
$this->setTitle($title);
$sitename = common_config('site', 'name');
$subtitle = sprintf(
+ // TRANS: Message is used as a subtitle in atom group notice feed.
+ // TRANS: %1$s is a group name, %2$s is a site name.
_('Updates from %1$s on %2$s!'),
$group->nickname,
$sitename
diff --git a/lib/atomusernoticefeed.php b/lib/atomusernoticefeed.php
index 428cc2de2..acfcbd75f 100644
--- a/lib/atomusernoticefeed.php
+++ b/lib/atomusernoticefeed.php
@@ -64,11 +64,14 @@ class AtomUserNoticeFeed extends AtomNoticeFeed
$this->setActivitySubject($profile->asActivityNoun('subject'));
}
+ // TRANS: Title in atom user notice feed. %s is a user name.
$title = sprintf(_("%s timeline"), $user->nickname);
$this->setTitle($title);
$sitename = common_config('site', 'name');
$subtitle = sprintf(
+ // TRANS: Message is used as a subtitle in atom user notice feed.
+ // TRANS: %1$s is a user name, %2$s is a site name.
_('Updates from %1$s on %2$s!'),
$user->nickname, $sitename
);
diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php
index 43f836e15..59cab9532 100644
--- a/lib/attachmentlist.php
+++ b/lib/attachmentlist.php
@@ -84,6 +84,7 @@ class AttachmentList extends Widget
if (empty($att)) return 0;
$this->out->elementStart('dl', array('id' =>'attachments',
'class' => 'entry-content'));
+ // TRANS: DT element label in attachment list.
$this->out->element('dt', null, _('Attachments'));
$this->out->elementStart('dd');
$this->out->elementStart('ol', array('class' => 'attachments'));
@@ -260,6 +261,7 @@ class Attachment extends AttachmentListItem
'class' => 'entry-content'));
if (!empty($this->oembed->author_name)) {
$this->out->elementStart('dl', 'vcard author');
+ // TRANS: DT element label in attachment list item.
$this->out->element('dt', null, _('Author'));
$this->out->elementStart('dd', 'fn');
if (empty($this->oembed->author_url)) {
@@ -273,6 +275,7 @@ class Attachment extends AttachmentListItem
}
if (!empty($this->oembed->provider)) {
$this->out->elementStart('dl', 'vcard');
+ // TRANS: DT element label in attachment list item.
$this->out->element('dt', null, _('Provider'));
$this->out->elementStart('dd', 'fn');
if (empty($this->oembed->provider_url)) {
diff --git a/lib/command.php b/lib/command.php
index 084c61fd1..30db9d069 100644
--- a/lib/command.php
+++ b/lib/command.php
@@ -122,6 +122,8 @@ class Command
}
Event::handle('EndCommandGetProfile', array($this, $arg, &$profile));
if (!$profile) {
+ // TRANS: Message given requesting a profile for a non-existing user.
+ // TRANS: %s is the nickname of the user for which the profile could not be found.
throw new CommandException(sprintf(_('Could not find a user with nickname %s'), $arg));
}
return $profile;
@@ -140,6 +142,8 @@ class Command
}
Event::handle('EndCommandGetUser', array($this, $arg, &$user));
if (!$user){
+ // TRANS: Message given getting a non-existing user.
+ // TRANS: %s is the nickname of the user that could not be found.
throw new CommandException(sprintf(_('Could not find a local user with nickname %s'),
$arg));
}
@@ -225,6 +229,8 @@ class NudgeCommand extends Command
}
// XXX: notify by IM
// XXX: notify by SMS
+ // TRANS: Message given having nudged another user.
+ // TRANS: %s is the nickname of the user that was nudged.
$channel->output($this->user, sprintf(_('Nudge sent to %s'),
$recipient->nickname));
}
@@ -328,12 +334,16 @@ class JoinCommand extends Command
Event::handle('EndJoinGroup', array($group, $cur));
}
} catch (Exception $e) {
- $channel->error($cur, sprintf(_('Could not join user %s to group %s'),
+ // TRANS: Message given having failed to add a user to a group.
+ // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
+ $channel->error($cur, sprintf(_('Could not join user %1$s to group %2$s'),
$cur->nickname, $group->nickname));
return;
}
- $channel->output($cur, sprintf(_('%s joined group %s'),
+ // TRANS: Message given having added a user to a group.
+ // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
+ $channel->output($cur, sprintf(_('%1$s joined group %2$s'),
$cur->nickname,
$group->nickname));
}
@@ -370,12 +380,16 @@ class DropCommand extends Command
Event::handle('EndLeaveGroup', array($group, $cur));
}
} catch (Exception $e) {
- $channel->error($cur, sprintf(_('Could not remove user %s to group %s'),
+ // TRANS: Message given having failed to remove a user from a group.
+ // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
+ $channel->error($cur, sprintf(_('Could not remove user %1$s from group %2$s'),
$cur->nickname, $group->nickname));
return;
}
- $channel->output($cur, sprintf(_('%s left group %s'),
+ // TRANS: Message given having removed a user from a group.
+ // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
+ $channel->output($cur, sprintf(_('%1$s left group %2$s'),
$cur->nickname,
$group->nickname));
}
@@ -395,18 +409,24 @@ class WhoisCommand extends Command
{
$recipient = $this->getProfile($this->other);
+ // TRANS: Whois output.
+ // TRANS: %1$s nickname of the queried user, %2$s is their profile URL.
$whois = sprintf(_("%1\$s (%2\$s)"), $recipient->nickname,
$recipient->profileurl);
if ($recipient->fullname) {
+ // TRANS: Whois output. %s is the full name of the queried user.
$whois .= "\n" . sprintf(_('Fullname: %s'), $recipient->fullname);
}
if ($recipient->location) {
+ // TRANS: Whois output. %s is the location of the queried user.
$whois .= "\n" . sprintf(_('Location: %s'), $recipient->location);
}
if ($recipient->homepage) {
+ // TRANS: Whois output. %s is the homepage of the queried user.
$whois .= "\n" . sprintf(_('Homepage: %s'), $recipient->homepage);
}
if ($recipient->bio) {
+ // TRANS: Whois output. %s is the bio information of the queried user.
$whois .= "\n" . sprintf(_('About: %s'), $recipient->bio);
}
$channel->output($this->user, $whois);
@@ -447,7 +467,9 @@ class MessageCommand extends Command
$this->text = common_shorten_links($this->text);
if (Message::contentTooLong($this->text)) {
- $channel->error($this->user, sprintf(_('Message too long - maximum is %d characters, you sent %d'),
+ // TRANS: Message given if content is too long.
+ // TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
+ $channel->error($this->user, sprintf(_('Message too long - maximum is %1$d characters, you sent %2$d'),
Message::maxContent(), mb_strlen($this->text)));
return;
}
@@ -465,6 +487,8 @@ class MessageCommand extends Command
$message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source());
if ($message) {
$message->notify();
+ // TRANS: Message given have sent a direct message to another user.
+ // TRANS: %s is the name of the other user.
$channel->output($this->user, sprintf(_('Direct message to %s sent'), $this->other));
} else {
$channel->error($this->user, _('Error sending direct message.'));
@@ -500,6 +524,8 @@ class RepeatCommand extends Command
if ($repeat) {
+ // TRANS: Message given having repeated a notice from another user.
+ // TRANS: %s is the name of the user for which the notice was repeated.
$channel->output($this->user, sprintf(_('Notice from %s repeated'), $recipient->nickname));
} else {
$channel->error($this->user, _('Error repeating notice.'));
diff --git a/lib/deluserqueuehandler.php b/lib/deluserqueuehandler.php
index 4a1233a5e..710303938 100644
--- a/lib/deluserqueuehandler.php
+++ b/lib/deluserqueuehandler.php
@@ -49,9 +49,13 @@ class DelUserQueueHandler extends QueueHandler
return true;
}
- if (!$user->hasRole(Profile_role::DELETED)) {
- common_log(LOG_INFO, "User {$user->nickname} is not pending deletion; aborting.");
- return true;
+ try {
+ if (!$user->hasRole(Profile_role::DELETED)) {
+ common_log(LOG_INFO, "User {$user->nickname} is not pending deletion; aborting.");
+ return true;
+ }
+ } catch (UserNoProfileException $unp) {
+ common_log(LOG_INFO, "Deleting user {$user->nickname} with no profile... probably a good idea!");
}
$notice = $this->getNextBatch($user);
diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php
index 7786b5941..5dc2b38da 100644
--- a/lib/htmloutputter.php
+++ b/lib/htmloutputter.php
@@ -100,6 +100,7 @@ class HTMLOutputter extends XMLOutputter
$type = common_negotiate_type($cp, $sp);
if (!$type) {
+ // TRANS: Client exception 406
throw new ClientException(_('This page is not available in a '.
'media type you accept'), 406);
}
diff --git a/lib/implugin.php b/lib/implugin.php
index 018b0ecee..7302859a4 100644
--- a/lib/implugin.php
+++ b/lib/implugin.php
@@ -586,7 +586,9 @@ abstract class ImPlugin extends Plugin
function onGetImTransports(&$transports)
{
- $transports[$this->transport] = array('display' => $this->getDisplayName());
+ $transports[$this->transport] = array(
+ 'display' => $this->getDisplayName(),
+ 'daemon_screenname' => $this->daemon_screenname());
}
function onSendImConfirmationCode($transport, $screenname, $code, $user)
diff --git a/lib/installer.php b/lib/installer.php
new file mode 100644
index 000000000..d0e46f95c
--- /dev/null
+++ b/lib/installer.php
@@ -0,0 +1,578 @@
+<?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' => 'mysql', // 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 be consistent about using mysqli vs mysql!
+ * @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 = mysql_connect($host, $username, $password);
+ if (!$conn) {
+ $this->updateStatus("Can't connect to server '$host' as '$username'.", true);
+ return false;
+ }
+ $this->updateStatus("Changing to database...");
+ $res = mysql_select_db($database, $conn);
+ if (!$res) {
+ $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 = mysql_query($stmt, $conn);
+ if ($res === false) {
+ $error = mysql_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);
+
+}
diff --git a/lib/language.php b/lib/language.php
index 6fb6222a1..8009adc9b 100644
--- a/lib/language.php
+++ b/lib/language.php
@@ -305,7 +305,8 @@ function get_all_languages() {
'fi' => array('q' => 1, 'lang' => 'fi', 'name' => 'Finnish', 'direction' => 'ltr'),
'fa' => array('q' => 1, 'lang' => 'fa', 'name' => 'Persian', 'direction' => 'rtl'),
'fr-fr' => array('q' => 1, 'lang' => 'fr', 'name' => 'French', 'direction' => 'ltr'),
- 'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Galician', 'direction' => 'ltr'),
+ 'ga' => array('q' => 0.5, 'lang' => 'ga', 'name' => 'Irish', 'direction' => 'ltr'),
+ 'gl' => array('q' => 0.8, 'lang' => 'gl', 'name' => 'Galician', 'direction' => 'ltr'),
'he' => array('q' => 0.5, 'lang' => 'he', 'name' => 'Hebrew', 'direction' => 'rtl'),
'hsb' => array('q' => 0.8, 'lang' => 'hsb', 'name' => 'Upper Sorbian', 'direction' => 'ltr'),
'ia' => array('q' => 0.8, 'lang' => 'ia', 'name' => 'Interlingua', 'direction' => 'ltr'),
diff --git a/lib/mail.php b/lib/mail.php
index 807b6a363..c38d9f2f5 100644
--- a/lib/mail.php
+++ b/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_init_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',
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 7278c41a9..84c20a5b3 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -212,8 +212,8 @@ class NoticeForm extends Form
$this->out->checkbox('notice_data-geo', _('Share my location'), true);
$this->out->elementEnd('div');
$this->out->inlineScript(' var NoticeDataGeo_text = {'.
- 'ShareDisable: "'._('Do not share my location').'",'.
- 'ErrorTimeout: "'._('Sorry, retrieving your geo location is taking longer than expected, please try again later').'"'.
+ 'ShareDisable: ' .json_encode(_('Do not share my location')).','.
+ 'ErrorTimeout: ' .json_encode(_('Sorry, retrieving your geo location is taking longer than expected, please try again later')).
'}');
}
diff --git a/lib/noticelist.php b/lib/noticelist.php
index 83c8de9f6..5265326b2 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -426,10 +426,18 @@ class NoticeListItem extends Widget
if (empty($name)) {
$latdms = $this->decimalDegreesToDMS(abs($lat));
$londms = $this->decimalDegreesToDMS(abs($lon));
+ // TRANS: Used in coordinates as abbreviation of north
+ $north = _('N');
+ // TRANS: Used in coordinates as abbreviation of south
+ $south = _('S');
+ // TRANS: Used in coordinates as abbreviation of east
+ $east = _('E');
+ // TRANS: Used in coordinates as abbreviation of west
+ $west = _('W');
$name = sprintf(
_('%1$u°%2$u\'%3$u"%4$s %5$u°%6$u\'%7$u"%8$s'),
- $latdms['deg'],$latdms['min'], $latdms['sec'],($lat>0?_('N'):_('S')),
- $londms['deg'],$londms['min'], $londms['sec'],($lon>0?_('E'):_('W')));
+ $latdms['deg'],$latdms['min'], $latdms['sec'],($lat>0? $north:$south),
+ $londms['deg'],$londms['min'], $londms['sec'],($lon>0? $east:$west));
}
$url = $location->getUrl();
@@ -543,18 +551,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
diff --git a/lib/profileformaction.php b/lib/profileformaction.php
index 8a934666e..0ffafe5fb 100644
--- a/lib/profileformaction.php
+++ b/lib/profileformaction.php
@@ -41,7 +41,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @link http://status.net/
*/
-class ProfileFormAction extends Action
+class ProfileFormAction extends RedirectingAction
{
var $profile = null;
@@ -102,29 +102,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
*
* sub-classes should overload this request
diff --git a/lib/redirectingaction.php b/lib/redirectingaction.php
new file mode 100644
index 000000000..f11585274
--- /dev/null
+++ b/lib/redirectingaction.php
@@ -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."));
+ }
+}
diff --git a/lib/statusnet.php b/lib/statusnet.php
index 98f25c8a0..ac5d10134 100644
--- a/lib/statusnet.php
+++ b/lib/statusnet.php
@@ -31,6 +31,7 @@ class StatusNet
{
protected static $have_config;
protected static $is_api;
+ protected static $plugins = array();
/**
* Configure and instantiate a plugin into the current configuration.
@@ -74,10 +75,23 @@ class StatusNet
$inst->$aname = $avalue;
}
}
+
+ // Record activated plugins for later display/config dump
+ self::$plugins[] = array($name, $attrs);
+
return true;
}
/**
+ * Get a list of activated plugins in this process.
+ * @return array of (string $name, array $args) pairs
+ */
+ public static function getActivePlugins()
+ {
+ return self::$plugins;
+ }
+
+ /**
* Initialize, or re-initialize, StatusNet global configuration
* and plugins.
*
@@ -237,6 +251,7 @@ class StatusNet
global $_server, $_path, $config;
Event::clearHandlers();
+ self::$plugins = array();
// try to figure out where we are. $server and $path
// can be set by including module, else we guess based
diff --git a/lib/util.php b/lib/util.php
index 96d21bc59..2e987a23a 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -862,7 +862,14 @@ function common_xml_safe_str($str)
function common_tag_link($tag)
{
$canonical = common_canonical_tag($tag);
- $url = common_local_url('tag', array('tag' => $canonical));
+ if (common_config('singleuser', 'enabled')) {
+ // regular TagAction isn't set up in 1user mode
+ $url = common_local_url('showstream',
+ array('nickname' => common_config('singleuser', 'nickname'),
+ 'tag' => $canonical));
+ } else {
+ $url = common_local_url('tag', array('tag' => $canonical));
+ }
$xs = new XMLStringer();
$xs->elementStart('span', 'tag');
$xs->element('a', array('href' => $url,
@@ -1043,24 +1050,38 @@ function common_date_string($dt)
if ($now < $t) { // that shouldn't happen!
return common_exact_date($dt);
} else if ($diff < 60) {
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return _('a few seconds ago');
} else if ($diff < 92) {
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about a minute ago');
} else if ($diff < 3300) {
+ // XXX: should support plural.
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return sprintf(_('about %d minutes ago'), round($diff/60));
} else if ($diff < 5400) {
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about an hour ago');
} else if ($diff < 22 * 3600) {
+ // XXX: should support plural.
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return sprintf(_('about %d hours ago'), round($diff/3600));
} else if ($diff < 37 * 3600) {
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about a day ago');
} else if ($diff < 24 * 24 * 3600) {
+ // XXX: should support plural.
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return sprintf(_('about %d days ago'), round($diff/(24*3600)));
} else if ($diff < 46 * 24 * 3600) {
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about a month ago');
} else if ($diff < 330 * 24 * 3600) {
+ // XXX: should support plural.
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return sprintf(_('about %d months ago'), round($diff/(30*24*3600)));
} else if ($diff < 480 * 24 * 3600) {
+ // TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about a year ago');
} else {
return common_exact_date($dt);
@@ -1263,12 +1284,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();