------
StatusNet 0.9.6 "Man on the Moon"
- 25 October 2010 - RELEASE CANDIDATE
+ 29 October 2010
This is the README file for StatusNet, the Open Source microblogging
platform. It includes installation instructions, descriptions of
- Header metadata on notice pages to aid in manual reposting on Facebook
- Lots of little fixes...
+ Changes from 0.9.6 release candidate 1:
+ - fix for broken group pages when logged out
+ - fix for stuck ping queue entries when bad profile
+ - fix for bogus single-user nickname config entry error
+ - i18n updates
+ - nofollow updates
+ - SSL-only mode secure cookie fix
+ - experimental ApiLogger plugin for usage data gathering
+ - experimental follow-everyone plugin
+
A full changelog is available at http://status.net/wiki/StatusNet_0.9.6.
Prerequisites
parameters correctly so that both the SSL server and the
"normal" server can access the session cookie and
preferably other cookies as well.
-shorturllength: Length of URL at which URLs in a message exceeding 140
- characters will be sent to the user's chosen
- shortening service.
+shorturllength: ignored. See 'url' section below.
dupelimit: minimum time allowed for one person to say the same thing
twice. Default 60s. Anything lower is considered a user
or UI error.
nofollowed on profile, notice, and favorites page. Default is
'sometimes'.
+url
+---
+
+Everybody loves URL shorteners. These are some options for fine-tuning
+how and when the server shortens URLs.
+
+shortener: URL shortening service to use by default. Users can override
+ individually. 'ur1.ca' by default.
+maxlength: If an URL is strictly longer than this limit, it will be
+ shortened. Note that the URL shortener service may return an
+ URL longer than this limit. Defaults to 25. Users can
+ override. If set to 0, all URLs will be shortened.
+maxnoticelength: If a notice is strictly longer than this limit, all
+ URLs in the notice will be shortened. Users can override.
+ -1 means the text limit for notices.
+
Plugins
=======
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class ConfirmaddressAction extends Action
{
/** type of confirmation. */
- var $type = null;
+ var $address;
/**
* Accept a confirmation code
*
* @return void
*/
-
function handle($args)
{
parent::handle($args);
}
$code = $this->trimmed('code');
if (!$code) {
+ // TRANS: Client error displayed when not providing a confirmation code in the contact address confirmation action.
$this->clientError(_('No confirmation code.'));
return;
}
$confirm = Confirm_address::staticGet('code', $code);
if (!$confirm) {
+ // TRANS: Client error displayed when providing a non-existing confirmation code in the contact address confirmation action.
$this->clientError(_('Confirmation code not found.'));
return;
}
$cur = common_current_user();
if ($cur->id != $confirm->user_id) {
+ // TRANS: Client error displayed when not providing a confirmation code for another user in the contact address confirmation action.
$this->clientError(_('That confirmation code is not for you!'));
return;
}
$type = $confirm->address_type;
- if (!in_array($type, array('email', 'jabber', 'sms'))) {
- // TRANS: Server error for a unknow address type %s, which can be 'email', 'jabber', or 'sms'.
- $this->serverError(sprintf(_('Unrecognized address type %s.'), $type));
+ $transports = array();
+ Event::handle('GetImTransports', array(&$transports));
+ if (!in_array($type, array('email', 'sms')) && !in_array($type, array_keys($transports))) {
+ // TRANS: Server error for an unknown address type, which can be 'email', 'sms', or the name of an IM network (such as 'xmpp' or 'aim')
+ $this->serverError(sprintf(_('Unrecognized address type %s'), $type));
return;
}
- if ($cur->$type == $confirm->address) {
- // TRANS: Client error for an already confirmed email/jabber/sms address.
- $this->clientError(_('That address has already been confirmed.'));
- return;
- }
-
+ $this->address = $confirm->address;
$cur->query('BEGIN');
+ if (in_array($type, array('email', 'sms')))
+ {
+ if ($cur->$type == $confirm->address) {
++ // TRANS: Client error for an already confirmed email/jabber/sms address.
+ $this->clientError(_('That address has already been confirmed.'));
+ return;
+ }
+
+ $orig_user = clone($cur);
+
+ $cur->$type = $confirm->address;
+
+ if ($type == 'sms') {
+ $cur->carrier = ($confirm->address_extra)+0;
+ $carrier = Sms_carrier::staticGet($cur->carrier);
+ $cur->smsemail = $carrier->toEmailAddress($cur->sms);
+ }
+
+ $result = $cur->updateKeys($orig_user);
+
+ if (!$result) {
+ common_log_db_error($cur, 'UPDATE', __FILE__);
+ $this->serverError(_('Couldn\'t update user.'));
+ return;
+ }
+
+ if ($type == 'email') {
+ $cur->emailChanged();
+ }
+
+ } else {
+
+ $user_im_prefs = new User_im_prefs();
+ $user_im_prefs->transport = $confirm->address_type;
+ $user_im_prefs->user_id = $cur->id;
+ if ($user_im_prefs->find() && $user_im_prefs->fetch()) {
+ if($user_im_prefs->screenname == $confirm->address){
+ $this->clientError(_('That address has already been confirmed.'));
+ return;
+ }
+ $user_im_prefs->screenname = $confirm->address;
+ $result = $user_im_prefs->update();
+
+ if (!$result) {
+ common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
+ $this->serverError(_('Couldn\'t update user im preferences.'));
+ return;
+ }
+ }else{
+ $user_im_prefs = new User_im_prefs();
+ $user_im_prefs->screenname = $confirm->address;
+ $user_im_prefs->transport = $confirm->address_type;
+ $user_im_prefs->user_id = $cur->id;
+ $result = $user_im_prefs->insert();
+
+ if (!$result) {
+ common_log_db_error($user_im_prefs, 'INSERT', __FILE__);
+ $this->serverError(_('Couldn\'t insert user im preferences.'));
+ return;
+ }
+ }
- $orig_user = clone($cur);
-
- $cur->$type = $confirm->address;
-
- if ($type == 'sms') {
- $cur->carrier = ($confirm->address_extra)+0;
- $carrier = Sms_carrier::staticGet($cur->carrier);
- $cur->smsemail = $carrier->toEmailAddress($cur->sms);
- }
-
- $result = $cur->updateKeys($orig_user);
-
- if (!$result) {
- common_log_db_error($cur, 'UPDATE', __FILE__);
- // TRANS: Server error displayed when a user update to the database fails in the contact address confirmation action.
- $this->serverError(_('Couldn\'t update user.'));
- return;
- }
-
- if ($type == 'email') {
- $cur->emailChanged();
}
$result = $confirm->delete();
if (!$result) {
common_log_db_error($confirm, 'DELETE', __FILE__);
- $this->serverError(_('Couldn\'t delete email confirmation.'));
+ // TRANS: Server error displayed when an address confirmation code deletion from the
+ // TRANS: database fails in the contact address confirmation action.
+ $this->serverError(_('Could not delete address confirmation.'));
return;
}
$cur->query('COMMIT');
-
- $this->type = $type;
$this->showPage();
}
*
* @return string title
*/
-
function title()
{
+ // TRANS: Title for the contact address confirmation action.
return _('Confirm address');
}
*
* @return void
*/
-
function showContent()
{
$cur = common_current_user();
- $type = $this->type;
$this->element('p', null,
+ // TRANS: Success message for the contact address confirmation action.
+ // TRANS: %s can be 'email', 'jabber', or 'sms'.
sprintf(_('The address "%s" has been '.
'confirmed for your account.'),
- $cur->$type));
+ $this->address));
}
}
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class OthersettingsAction extends AccountSettingsAction
{
/**
*
* @return string Title of the page
*/
-
function title()
{
+ // Page title for a tab in user profile settings.
return _('Other settings');
}
function getInstructions()
{
+ // TRANS: Instructions for tab "Other" in user profile settings.
return _('Manage various other options.');
}
$this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data');
- $shorteners = array();
+ $shorteners = array(_('[none]') => array('freeService' => false));
+
Event::handle('GetUrlShorteners', array(&$shorteners));
+
$services = array();
foreach($shorteners as $name=>$value)
{
$services[$name]=$name;
if($value['freeService']){
+ // TRANS: Used as a suffix for free URL shorteners in a dropdown list in the tab "Other" of a
+ // TRANS: user's profile settings. This message has one space at the beginning. Use your
+ // TRANS: language's word separator here if it has one (most likely a single space).
$services[$name].=_(' (free service)');
}
}
asort($services);
$this->elementStart('li');
+ // TRANS: Label for dropdown with URL shortener services.
$this->dropdown('urlshorteningservice', _('Shorten URLs with'),
+ // TRANS: Tooltip for for dropdown with URL shortener services.
$services, _('Automatic shortening service to use.'),
false, $user->urlshorteningservice);
$this->elementEnd('li');
}
$this->elementStart('li');
+ $this->input('maxurllength',
+ _('URL longer than'),
+ (!is_null($this->arg('maxurllength'))) ?
+ $this->arg('maxurllength') : User_urlshortener_prefs::maxUrlLength($user),
+ _('URLs longer than this will be shortened.'));
+ $this->elementEnd('li');
+ $this->elementStart('li');
+ $this->input('maxnoticelength',
+ _('Text longer than'),
+ (!is_null($this->arg('maxnoticelength'))) ?
+ $this->arg('maxnoticelength') : User_urlshortener_prefs::maxNoticeLength($user),
+ _('URLs in notices longer than this will be shortened.'));
+ $this->elementEnd('li');
+ $this->elementStart('li');
+ // TRANS: Label for checkbox.
$this->checkbox('viewdesigns', _('View profile designs'),
- // TRANS: Tooltip for checkbox.
- $user->viewdesigns, _('Show or hide profile designs.'));
+ - $user->viewdesigns, _('Show or hide profile designs.'));
$this->elementEnd('li');
$this->elementEnd('ul');
- $this->submit('save', _('Save'));
+ // TRANS: Button text for saving "Other settings" in profile.
+ $this->submit('save', _m('BUTTON','Save'));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
$urlshorteningservice = $this->trimmed('urlshorteningservice');
if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) {
- $this->showForm(_('URL shortening service is too long (max 50 chars).'));
+ // TRANS: Form validation error for form "Other settings" in user profile.
+ $this->showForm(_('URL shortening service is too long (maximum 50 characters).'));
return;
}
$viewdesigns = $this->boolean('viewdesigns');
+ $maxurllength = $this->trimmed('maxurllength');
+
+ if (!Validate::number($maxurllength, array('min' => 0))) {
+ throw new ClientException(_('Invalid number for max url length.'));
+ }
+
+ $maxnoticelength = $this->trimmed('maxnoticelength');
+
+ if (!Validate::number($maxnoticelength, array('min' => 0))) {
+ throw new ClientException(_('Invalid number for max notice length.'));
+ }
+
$user = common_current_user();
assert(!is_null($user)); // should already be checked
if ($result === false) {
common_log_db_error($user, 'UPDATE', __FILE__);
+ // TRANS: Server error displayed when "Other" settings in user profile could not be updated on the server.
$this->serverError(_('Couldn\'t update user.'));
return;
}
+ $prefs = User_urlshortener_prefs::getPrefs($user);
+ $orig = null;
+
+ if (empty($prefs)) {
+ $prefs = new User_urlshortener_prefs();
+
+ $prefs->user_id = $user->id;
+ $prefs->created = common_sql_now();
+ } else {
+ $orig = clone($prefs);
+ }
+
+ $prefs->urlshorteningservice = $urlshorteningservice;
+ $prefs->maxurllength = $maxurllength;
+ $prefs->maxnoticelength = $maxnoticelength;
+
+ if (!empty($orig)) {
+ $result = $prefs->update($orig);
+ } else {
+ $result = $prefs->insert();
+ }
+
+ if (!$result) {
+ throw new ServerException(_('Error saving user URL shortening preferences.'));
+ }
+
$user->query('COMMIT');
$this->showForm(_('Preferences saved.'), true);
function title()
{
- if (!empty($this->profile->fullname)) {
- $base = $this->profile->fullname . ' (' . $this->user->nickname . ') ';
- } else {
- $base = $this->user->nickname;
- }
+ $base = $this->profile->getFancyName();
if (!empty($this->tag)) {
- $base .= sprintf(_(' tagged %s'), $this->tag);
- }
-
- if ($this->page == 1) {
- return $base;
+ if ($this->page == 1) {
+ // TRANS: Page title showing tagged notices in one user's stream. Param 1 is the username, 2 is the hash tag.
+ return sprintf(_('%1$s tagged %2$s'), $base, $this->tag);
+ } else {
+ // TRANS: Page title showing tagged notices in one user's stream. Param 1 is the username, 2 is the hash tag, 3 is the page number.
+ return sprintf(_('%1$s tagged %2$s, page %3$d'), $base, $this->tag, $this->page);
+ }
} else {
- return sprintf(_('%1$s, page %2$d'),
- $base,
- $this->page);
+ if ($this->page == 1) {
+ return $base;
+ } else {
+ // TRANS: Extended page title showing tagged notices in one user's stream. Param 1 is the username, param 2 is the page number.
+ return sprintf(_('%1$s, page %2$d'),
+ $base,
+ $this->page);
+ }
}
}
$this->selfUrl());
$this->element('meta', array('name' => 'microid',
'content' => $id->toString()));
- }
- if ($this->user->jabbermicroid && $this->user->jabber && $this->profile->profileurl) {
- $id = new Microid('xmpp:'.$this->user->jabber,
- $this->selfUrl());
- $this->element('meta', array('name' => 'microid',
- 'content' => $id->toString()));
}
// See https://wiki.mozilla.org/Microsummaries
return true;
}
+ /**
+ * Gets either the full name (if filled) or the nickname.
+ *
+ * @return string
+ */
function getBestName()
{
return ($this->fullname) ? $this->fullname : $this->nickname;
}
+ /**
+ * Gets the full name (if filled) with nickname as a parenthetical, or the nickname alone
+ * if no fullname is provided.
+ *
+ * @return string
+ */
+ function getFancyName()
+ {
+ if ($this->fullname) {
+ // TRANS: Full name of a profile or group followed by nickname in parens
+ return sprintf(_('%1$s (%2$s)'), $this->fullname, $this->nickname);
+ } else {
+ return $this->nickname;
+ }
+ }
+
/**
* Get the most recent notice posted by this user, if any.
*
function subscriptionCount()
{
- $c = common_memcache();
+ $c = Cache::instance();
if (!empty($c)) {
- $cnt = $c->get(common_cache_key('profile:subscription_count:'.$this->id));
+ $cnt = $c->get(Cache::key('profile:subscription_count:'.$this->id));
if (is_integer($cnt)) {
return (int) $cnt;
}
$cnt = ($cnt > 0) ? $cnt - 1 : $cnt;
if (!empty($c)) {
- $c->set(common_cache_key('profile:subscription_count:'.$this->id), $cnt);
+ $c->set(Cache::key('profile:subscription_count:'.$this->id), $cnt);
}
return $cnt;
function subscriberCount()
{
- $c = common_memcache();
+ $c = Cache::instance();
if (!empty($c)) {
- $cnt = $c->get(common_cache_key('profile:subscriber_count:'.$this->id));
+ $cnt = $c->get(Cache::key('profile:subscriber_count:'.$this->id));
if (is_integer($cnt)) {
return (int) $cnt;
}
$cnt = (int) $sub->count('distinct subscriber');
if (!empty($c)) {
- $c->set(common_cache_key('profile:subscriber_count:'.$this->id), $cnt);
+ $c->set(Cache::key('profile:subscriber_count:'.$this->id), $cnt);
}
return $cnt;
function hasFave($notice)
{
- $cache = common_memcache();
+ $cache = Cache::instance();
// XXX: Kind of a hack.
function faveCount()
{
- $c = common_memcache();
+ $c = Cache::instance();
if (!empty($c)) {
- $cnt = $c->get(common_cache_key('profile:fave_count:'.$this->id));
+ $cnt = $c->get(Cache::key('profile:fave_count:'.$this->id));
if (is_integer($cnt)) {
return (int) $cnt;
}
$cnt = (int) $faves->count('distinct notice_id');
if (!empty($c)) {
- $c->set(common_cache_key('profile:fave_count:'.$this->id), $cnt);
+ $c->set(Cache::key('profile:fave_count:'.$this->id), $cnt);
}
return $cnt;
function noticeCount()
{
- $c = common_memcache();
+ $c = Cache::instance();
if (!empty($c)) {
- $cnt = $c->get(common_cache_key('profile:notice_count:'.$this->id));
+ $cnt = $c->get(Cache::key('profile:notice_count:'.$this->id));
if (is_integer($cnt)) {
return (int) $cnt;
}
$cnt = (int) $notices->count('distinct id');
if (!empty($c)) {
- $c->set(common_cache_key('profile:notice_count:'.$this->id), $cnt);
+ $c->set(Cache::key('profile:notice_count:'.$this->id), $cnt);
}
return $cnt;
function blowSubscriberCount()
{
- $c = common_memcache();
+ $c = Cache::instance();
if (!empty($c)) {
- $c->delete(common_cache_key('profile:subscriber_count:'.$this->id));
+ $c->delete(Cache::key('profile:subscriber_count:'.$this->id));
}
}
function blowSubscriptionCount()
{
- $c = common_memcache();
+ $c = Cache::instance();
if (!empty($c)) {
- $c->delete(common_cache_key('profile:subscription_count:'.$this->id));
+ $c->delete(Cache::key('profile:subscription_count:'.$this->id));
}
}
function blowFaveCount()
{
- $c = common_memcache();
+ $c = Cache::instance();
if (!empty($c)) {
- $c->delete(common_cache_key('profile:fave_count:'.$this->id));
+ $c->delete(Cache::key('profile:fave_count:'.$this->id));
}
}
function blowNoticeCount()
{
- $c = common_memcache();
+ $c = Cache::instance();
if (!empty($c)) {
- $c->delete(common_cache_key('profile:notice_count:'.$this->id));
+ $c->delete(Cache::key('profile:notice_count:'.$this->id));
}
}
if (Message::contentTooLong($this->text)) {
// XXX: i18n. Needs plural support.
- // TRANS: Message given if content is too long.
+ // TRANS: Message given if content is too long. %1$sd is used for plural.
// 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.'),
+ $channel->error($this->user, sprintf(_m('Message too long - maximum is %1$d character, you sent %2$d.',
+ 'Message too long - maximum is %1$d characters, you sent %2$d.',
+ Message::maxContent()),
Message::maxContent(), mb_strlen($this->text)));
return;
}
if (Notice::contentTooLong($this->text)) {
// XXX: i18n. Needs plural support.
- // TRANS: Message given if content of a notice for a reply is too long.
+ // TRANS: Message given if content of a notice for a reply is too long. %1$d is used for plural.
// TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
- $channel->error($this->user, sprintf(_('Notice too long - maximum is %1$d characters, you sent %2$d.'),
+ $channel->error($this->user, sprintf(_m('Notice too long - maximum is %1$d character, you sent %2$d.',
+ 'Notice too long - maximum is %1$d characters, you sent %2$d.',
+ Notice::maxContent()),
Notice::maxContent(), mb_strlen($this->text)));
return;
}
}
function handle($channel)
{
- if ($other) {
+ if ($this->other) {
// TRANS: Error text shown when issuing the command "off" with a setting which has not yet been implemented.
$channel->error($this->user, _("Command not yet implemented."));
} else {
function handle($channel)
{
- if ($other) {
+ if ($this->other) {
// TRANS: Error text shown when issuing the command "on" with a setting which has not yet been implemented.
$channel->error($this->user, _("Command not yet implemented."));
} else {