summaryrefslogtreecommitdiff
path: root/actions
diff options
context:
space:
mode:
Diffstat (limited to 'actions')
-rw-r--r--actions/apiatomservice.php132
-rw-r--r--actions/apidirectmessagenew.php2
-rw-r--r--actions/apifriendshipsexists.php2
-rw-r--r--actions/apigroupcreate.php33
-rw-r--r--actions/apioauthauthorize.php2
-rw-r--r--actions/apistatusesshow.php62
-rw-r--r--actions/apistatusesupdate.php6
-rw-r--r--actions/apitimelineuser.php310
-rw-r--r--actions/atompubfavoritefeed.php374
-rw-r--r--actions/atompubmembershipfeed.php355
-rw-r--r--actions/atompubshowfavorite.php228
-rw-r--r--actions/atompubshowmembership.php235
-rw-r--r--actions/atompubshowsubscription.php224
-rw-r--r--actions/atompubsubscriptionfeed.php335
-rw-r--r--actions/attachment.php8
-rw-r--r--actions/attachment_ajax.php2
-rw-r--r--actions/attachment_thumbnail.php4
-rw-r--r--actions/avatarbynickname.php8
-rw-r--r--actions/avatarsettings.php49
-rw-r--r--actions/block.php12
-rw-r--r--actions/blockedfromgroup.php25
-rw-r--r--actions/bookmarklet.php5
-rw-r--r--actions/confirmaddress.php19
-rw-r--r--actions/conversation.php17
-rw-r--r--actions/deleteapplication.php11
-rw-r--r--actions/deletegroup.php4
-rw-r--r--actions/deletenotice.php11
-rw-r--r--actions/editapplication.php2
-rw-r--r--actions/editgroup.php49
-rw-r--r--actions/emailsettings.php205
-rw-r--r--actions/groupunblock.php14
-rw-r--r--actions/hostmeta.php65
-rw-r--r--actions/invite.php4
-rw-r--r--actions/licenseadminpanel.php2
-rw-r--r--actions/newapplication.php4
-rw-r--r--actions/newgroup.php37
-rw-r--r--actions/newmessage.php2
-rw-r--r--actions/newnotice.php19
-rw-r--r--actions/oauthconnectionssettings.php2
-rw-r--r--actions/oembed.php25
-rw-r--r--actions/othersettings.php18
-rw-r--r--actions/profilesettings.php16
-rw-r--r--actions/recoverpassword.php2
-rw-r--r--actions/register.php11
-rw-r--r--actions/rsd.php14
-rw-r--r--actions/showgroup.php65
-rw-r--r--actions/showmessage.php57
-rw-r--r--actions/shownotice.php36
-rw-r--r--actions/showstream.php55
-rw-r--r--actions/sitenoticeadminpanel.php19
-rw-r--r--actions/subedit.php6
-rw-r--r--actions/subscriptions.php17
-rw-r--r--actions/useradminpanel.php45
-rw-r--r--actions/userxrd.php65
54 files changed, 2912 insertions, 419 deletions
diff --git a/actions/apiatomservice.php b/actions/apiatomservice.php
new file mode 100644
index 000000000..b60b312fc
--- /dev/null
+++ b/actions/apiatomservice.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * An AtomPub service document for a user
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link http://status.net/
+ */
+
+require_once INSTALLDIR.'/lib/apibareauth.php';
+
+/**
+ * Shows an AtomPub service document for a user
+ *
+ * @category API
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
+ * @link http://status.net/
+ */
+
+class ApiAtomServiceAction extends ApiBareAuthAction
+{
+ /**
+ * Take arguments for running
+ *
+ * @param array $args $_REQUEST args
+ *
+ * @return boolean success flag
+ *
+ */
+
+ function prepare($args)
+ {
+ parent::prepare($args);
+ $this->user = $this->getTargetUser($this->arg('id'));
+
+ if (empty($this->user)) {
+ $this->clientError(_('No such user.'), 404, $this->format);
+ return;
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle the arguments. In our case, show a service document.
+ *
+ * @param Array $args unused.
+ *
+ * @return void
+ */
+
+ function handle($args)
+ {
+ parent::handle($args);
+
+ header('Content-Type: application/atomsvc+xml');
+
+ $this->startXML();
+ $this->elementStart('service', array('xmlns' => 'http://www.w3.org/2007/app',
+ 'xmlns:atom' => 'http://www.w3.org/2005/Atom',
+ 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/'));
+ $this->elementStart('workspace');
+ $this->element('atom:title', null, _('Main'));
+ $this->elementStart('collection',
+ array('href' => common_local_url('ApiTimelineUser',
+ array('id' => $this->user->id,
+ 'format' => 'atom'))));
+ $this->element('atom:title',
+ null,
+ sprintf(_("%s timeline"),
+ $this->user->nickname));
+ $this->element('accept', null, 'application/atom+xml;type=entry');
+ $this->element('activity:verb', null, ActivityVerb::POST);
+ $this->elementEnd('collection');
+ $this->elementStart('collection',
+ array('href' => common_local_url('AtomPubSubscriptionFeed',
+ array('subscriber' => $this->user->id))));
+ $this->element('atom:title',
+ null,
+ sprintf(_("%s subscriptions"),
+ $this->user->nickname));
+ $this->element('accept', null, 'application/atom+xml;type=entry');
+ $this->element('activity:verb', null, ActivityVerb::FOLLOW);
+ $this->elementEnd('collection');
+ $this->elementStart('collection',
+ array('href' => common_local_url('AtomPubFavoriteFeed',
+ array('profile' => $this->user->id))));
+ $this->element('atom:title',
+ null,
+ sprintf(_("%s favorites"),
+ $this->user->nickname));
+ $this->element('accept', null, 'application/atom+xml;type=entry');
+ $this->element('activity:verb', null, ActivityVerb::FAVORITE);
+ $this->elementEnd('collection');
+ $this->elementStart('collection',
+ array('href' => common_local_url('AtomPubMembershipFeed',
+ array('profile' => $this->user->id))));
+ $this->element('atom:title',
+ null,
+ sprintf(_("%s memberships"),
+ $this->user->nickname));
+ $this->element('accept', null, 'application/atom+xml;type=entry');
+ $this->element('activity:verb', null, ActivityVerb::JOIN);
+ $this->elementEnd('collection');
+ $this->elementEnd('workspace');
+ $this->elementEnd('service');
+ $this->endXML();
+ }
+}
diff --git a/actions/apidirectmessagenew.php b/actions/apidirectmessagenew.php
index b335a9c93..978c75353 100644
--- a/actions/apidirectmessagenew.php
+++ b/actions/apidirectmessagenew.php
@@ -119,7 +119,7 @@ class ApiDirectMessageNewAction extends ApiAuthAction
$this->format
);
} else {
- $content_shortened = common_shorten_links($this->content);
+ $content_shortened = $this->auth_user->shortenLinks($this->content);
if (Message::contentTooLong($content_shortened)) {
$this->clientError(
// TRANS: Client error displayed when message content is too long.
diff --git a/actions/apifriendshipsexists.php b/actions/apifriendshipsexists.php
index c8766633b..43b1daf4f 100644
--- a/actions/apifriendshipsexists.php
+++ b/actions/apifriendshipsexists.php
@@ -85,7 +85,7 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
if (empty($this->profile_a) || empty($this->profile_b)) {
$this->clientError(
// TRANS: Client error displayed when supplying invalid parameters to an API call checking if a friendship exists.
- _('Two valid IDs or screen_names must be supplied.'),
+ _('Two valid IDs or nick names must be supplied.'),
400,
$this->format
);
diff --git a/actions/apigroupcreate.php b/actions/apigroupcreate.php
index 54875a718..d01504bc8 100644
--- a/actions/apigroupcreate.php
+++ b/actions/apigroupcreate.php
@@ -73,7 +73,7 @@ class ApiGroupCreateAction extends ApiAuthAction
$this->user = $this->auth_user;
- $this->nickname = $this->arg('nickname');
+ $this->nickname = Nickname::normalize($this->arg('nickname'));
$this->fullname = $this->arg('full_name');
$this->homepage = $this->arg('homepage');
$this->description = $this->arg('description');
@@ -150,26 +150,7 @@ class ApiGroupCreateAction extends ApiAuthAction
*/
function validateParams()
{
- $valid = Validate::string(
- $this->nickname, array(
- 'min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT
- )
- );
-
- if (!$valid) {
- $this->clientError(
- // TRANS: Validation error in form for group creation.
- _(
- 'Nickname must have only lowercase letters ' .
- 'and numbers and no spaces.'
- ),
- 403,
- $this->format
- );
- return false;
- } elseif ($this->groupNicknameExists($this->nickname)) {
+ if ($this->groupNicknameExists($this->nickname)) {
$this->clientError(
// TRANS: Client error trying to create a group with a nickname this is already in use.
_('Nickname already in use. Try another one.'),
@@ -265,15 +246,7 @@ class ApiGroupCreateAction extends ApiAuthAction
foreach ($this->aliases as $alias) {
- $valid = Validate::string(
- $alias, array(
- 'min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT
- )
- );
-
- if (!$valid) {
+ if (!Nickname::isValid($alias)) {
$this->clientError(
// TRANS: Client error shown when providing an invalid alias during group creation.
// TRANS: %s is the invalid alias.
diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php
index b2c0de719..d76ae060f 100644
--- a/actions/apioauthauthorize.php
+++ b/actions/apioauthauthorize.php
@@ -421,7 +421,7 @@ class ApiOauthAuthorizeAction extends Action
if ($this->app->name == 'anonymous') {
// Special message for the anonymous app and consumer.
// TRANS: User notification of external application requesting account access.
- // TRANS: %3$s is the access type requested, %4$s is the StatusNet sitename.
+ // TRANS: %3$s is the access type requested (read-write or read-only), %4$s is the StatusNet sitename.
$msg = _('An application would like the ability ' .
'to <strong>%3$s</strong> your %4$s account data. ' .
'You should only give access to your %4$s account ' .
diff --git a/actions/apistatusesshow.php b/actions/apistatusesshow.php
index a98e45f79..e684a07ee 100644
--- a/actions/apistatusesshow.php
+++ b/actions/apistatusesshow.php
@@ -100,13 +100,23 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
{
parent::handle($args);
- if (!in_array($this->format, array('xml', 'json'))) {
+ if (!in_array($this->format, array('xml', 'json', 'atom'))) {
// TRANS: Client error displayed when trying to handle an unknown API method.
- $this->clientError(_('API method not found.'), $code = 404);
+ $this->clientError(_('API method not found.'), 404);
return;
}
- $this->showNotice();
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'GET':
+ $this->showNotice();
+ break;
+ case 'DELETE':
+ $this->deleteNotice();
+ break;
+ default:
+ $this->clientError(_('HTTP method not supported.'), 405);
+ return;
+ }
}
/**
@@ -117,10 +127,18 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
function showNotice()
{
if (!empty($this->notice)) {
- if ($this->format == 'xml') {
+ switch ($this->format) {
+ case 'xml':
$this->showSingleXmlStatus($this->notice);
- } elseif ($this->format == 'json') {
+ break;
+ case 'json':
$this->show_single_json_status($this->notice);
+ break;
+ case 'atom':
+ $this->showSingleAtomStatus($this->notice);
+ break;
+ default:
+ throw new Exception(sprintf(_("Unsupported format: %s"), $this->format));
}
} else {
// XXX: Twitter just sets a 404 header and doens't bother
@@ -153,9 +171,14 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
*
* @return boolean true
*/
+
function isReadOnly($args)
{
- return true;
+ if ($_SERVER['REQUEST_METHOD'] == 'GET') {
+ return true;
+ } else {
+ return false;
+ }
}
/**
@@ -197,4 +220,31 @@ class ApiStatusesShowAction extends ApiPrivateAuthAction
return null;
}
+
+ function deleteNotice()
+ {
+ if ($this->format != 'atom') {
+ $this->clientError(_("Can only delete using the Atom format."));
+ return;
+ }
+
+ if (empty($this->auth_user) ||
+ ($this->notice->profile_id != $this->auth_user->id &&
+ !$this->auth_user->hasRight(Right::DELETEOTHERSNOTICE))) {
+ $this->clientError(_('Can\'t delete this notice.'), 403);
+ return;
+ }
+
+ if (Event::handle('StartDeleteOwnNotice', array($this->auth_user, $this->notice))) {
+ $this->notice->delete();
+ Event::handle('EndDeleteOwnNotice', array($this->auth_user, $this->notice));
+ }
+
+ // @fixme is there better output we could do here?
+
+ header('HTTP/1.1 200 OK');
+ header('Content-Type: text/plain');
+ print(sprintf(_('Deleted notice %d'), $this->notice->id));
+ print("\n");
+ }
}
diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php
index 666ed9fa3..a8ec7f8bb 100644
--- a/actions/apistatusesupdate.php
+++ b/actions/apistatusesupdate.php
@@ -55,7 +55,7 @@
Yes
@param status (Required) The URL-encoded text of the status update.
- @param source (Optional) The source of the status.
+ @param source (Optional) The source application name, if using HTTP authentication or an anonymous OAuth consumer.
@param in_reply_to_status_id (Optional) The ID of an existing status that the update is in reply to.
@param lat (Optional) The latitude the status refers to.
@param long (Optional) The longitude the status refers to.
@@ -67,7 +67,7 @@
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
- @li If the @e source parameter is not supplied the source of the status will default to 'api'.
+ @li If the @e source parameter is not supplied the source of the status will default to 'api'. When authenticated via a registered OAuth application, the application's registered name and URL will always override the source parameter.
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
to encode the latitude and longitude (see example response below <georss:point>).
@li Data uploaded via the @e media parameter should be multipart/form-data encoded.
@@ -231,7 +231,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
return;
}
- $status_shortened = common_shorten_links($this->status);
+ $status_shortened = $this->auth_user->shortenlinks($this->status);
if (Notice::contentTooLong($status_shortened)) {
// Note: Twitter truncates anything over 140, flags the status
diff --git a/actions/apitimelineuser.php b/actions/apitimelineuser.php
index 0046c462d..d90507aa4 100644
--- a/actions/apitimelineuser.php
+++ b/actions/apitimelineuser.php
@@ -97,7 +97,12 @@ class ApiTimelineUserAction extends ApiBareAuthAction
function handle($args)
{
parent::handle($args);
- $this->showTimeline();
+
+ if ($this->isPost()) {
+ $this->handlePost();
+ } else {
+ $this->showTimeline();
+ }
}
/**
@@ -114,9 +119,9 @@ class ApiTimelineUserAction extends ApiBareAuthAction
$atom = new AtomUserNoticeFeed($this->user, $this->auth_user);
$link = common_local_url(
- 'showstream',
- array('nickname' => $this->user->nickname)
- );
+ 'showstream',
+ array('nickname' => $this->user->nickname)
+ );
$self = $this->getSelfUri();
@@ -132,20 +137,63 @@ class ApiTimelineUserAction extends ApiBareAuthAction
break;
case 'rss':
$this->showRssTimeline(
- $this->notices,
- $atom->title,
- $link,
- $atom->subtitle,
- $suplink,
- $atom->logo,
- $self
- );
+ $this->notices,
+ $atom->title,
+ $link,
+ $atom->subtitle,
+ $suplink,
+ $atom->logo,
+ $self
+ );
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom->setId($self);
$atom->setSelfLink($self);
+
+ // Add navigation links: next, prev, first
+ // Note: we use IDs rather than pages for navigation; page boundaries
+ // change too quickly!
+
+ if (!empty($this->next_id)) {
+ $nextUrl = common_local_url('ApiTimelineUser',
+ array('format' => 'atom',
+ 'id' => $this->user->id),
+ array('max_id' => $this->next_id));
+
+ $atom->addLink($nextUrl,
+ array('rel' => 'next',
+ 'type' => 'application/atom+xml'));
+ }
+
+ if (($this->page > 1 || !empty($this->max_id)) && !empty($this->notices)) {
+
+ $lastNotice = $this->notices[0];
+ $lastId = $lastNotice->id;
+
+ $prevUrl = common_local_url('ApiTimelineUser',
+ array('format' => 'atom',
+ 'id' => $this->user->id),
+ array('since_id' => $lastId));
+
+ $atom->addLink($prevUrl,
+ array('rel' => 'prev',
+ 'type' => 'application/atom+xml'));
+ }
+
+ if ($this->page > 1 || !empty($this->since_id) || !empty($this->max_id)) {
+
+ $firstUrl = common_local_url('ApiTimelineUser',
+ array('format' => 'atom',
+ 'id' => $this->user->id));
+
+ $atom->addLink($firstUrl,
+ array('rel' => 'first',
+ 'type' => 'application/atom+xml'));
+
+ }
+
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
@@ -169,13 +217,18 @@ class ApiTimelineUserAction extends ApiBareAuthAction
{
$notices = array();
- $notice = $this->user->getNotices(
- ($this->page-1) * $this->count, $this->count,
- $this->since_id, $this->max_id
- );
+ $notice = $this->user->getNotices(($this->page-1) * $this->count,
+ $this->count + 1,
+ $this->since_id,
+ $this->max_id);
while ($notice->fetch()) {
- $notices[] = clone($notice);
+ if (count($notices) < $this->count) {
+ $notices[] = clone($notice);
+ } else {
+ $this->next_id = $notice->id;
+ break;
+ }
}
return $notices;
@@ -188,9 +241,14 @@ class ApiTimelineUserAction extends ApiBareAuthAction
*
* @return boolean true
*/
+
function isReadOnly($args)
{
- return true;
+ if ($_SERVER['REQUEST_METHOD'] == 'GET') {
+ return true;
+ } else {
+ return false;
+ }
}
/**
@@ -221,17 +279,215 @@ class ApiTimelineUserAction extends ApiBareAuthAction
$last = count($this->notices) - 1;
return '"' . implode(
- ':',
- array($this->arg('action'),
- common_user_cache_hash($this->auth_user),
- common_language(),
- $this->user->id,
- strtotime($this->notices[0]->created),
- strtotime($this->notices[$last]->created))
- )
- . '"';
+ ':',
+ array($this->arg('action'),
+ common_user_cache_hash($this->auth_user),
+ common_language(),
+ $this->user->id,
+ strtotime($this->notices[0]->created),
+ strtotime($this->notices[$last]->created))
+ )
+ . '"';
}
return null;
}
+
+ function handlePost()
+ {
+ if (empty($this->auth_user) ||
+ $this->auth_user->id != $this->user->id) {
+ // TRANS: Client error displayed trying to add a notice to another user's timeline.
+ $this->clientError(_('Only the user can add to their own timeline.'));
+ return;
+ }
+
+ // Only handle posts for Atom
+ if ($this->format != 'atom') {
+ // TRANS: Client error displayed when using another format than AtomPub.
+ $this->clientError(_('Only accept AtomPub for Atom feeds.'));
+ return;
+ }
+
+ $xml = file_get_contents('php://input');
+
+ $dom = DOMDocument::loadXML($xml);
+
+ if ($dom->documentElement->namespaceURI != Activity::ATOM ||
+ $dom->documentElement->localName != 'entry') {
+ // TRANS: Client error displayed when not using an Atom entry.
+ $this->clientError(_('Atom post must be an Atom entry.'));
+ return;
+ }
+
+ $activity = new Activity($dom->documentElement);
+
+ if (Event::handle('StartAtomPubNewActivity', array(&$activity))) {
+
+ if ($activity->verb != ActivityVerb::POST) {
+ // TRANS: Client error displayed when not using the POST verb.
+ // TRANS: Do not translate POST.
+ $this->clientError(_('Can only handle POST activities.'));
+ return;
+ }
+
+ $note = $activity->objects[0];
+
+ if (!in_array($note->type, array(ActivityObject::NOTE,
+ ActivityObject::BLOGENTRY,
+ ActivityObject::STATUS))) {
+ // TRANS: Client error displayed when using an unsupported activity object type.
+ // TRANS: %s is the unsupported activity object type.
+ $this->clientError(sprintf(_('Cannot handle activity object type "%s".'),
+ $note->type));
+ return;
+ }
+
+ $saved = $this->postNote($activity);
+
+ Event::handle('EndAtomPubNewActivity', array($activity, $saved));
+ }
+
+ if (!empty($saved)) {
+ header("Location: " . common_local_url('ApiStatusesShow', array('notice_id' => $saved->id,
+ 'format' => 'atom')));
+ $this->showSingleAtomStatus($saved);
+ }
+ }
+
+ function postNote($activity)
+ {
+ $note = $activity->objects[0];
+
+ // Use summary as fallback for content
+
+ if (!empty($note->content)) {
+ $sourceContent = $note->content;
+ } else if (!empty($note->summary)) {
+ $sourceContent = $note->summary;
+ } else if (!empty($note->title)) {
+ $sourceContent = $note->title;
+ } else {
+ // @fixme fetch from $sourceUrl?
+ // TRANS: Client error displayed when posting a notice without content through the API.
+ $this->clientError(sprintf(_('No content for notice %d.'),
+ $note->id));
+ return;
+ }
+
+ // Get (safe!) HTML and text versions of the content
+
+ $rendered = $this->purify($sourceContent);
+ $content = html_entity_decode(strip_tags($rendered), ENT_QUOTES, 'UTF-8');
+
+ $shortened = $this->auth_user->shortenLinks($content);
+
+ $options = array('is_local' => Notice::LOCAL_PUBLIC,
+ 'rendered' => $rendered,
+ 'replies' => array(),
+ 'groups' => array(),
+ 'tags' => array(),
+ 'urls' => array());
+
+ // accept remote URI (not necessarily a good idea)
+
+ common_debug("Note ID is {$note->id}");
+
+ if (!empty($note->id)) {
+ $notice = Notice::staticGet('uri', trim($note->id));
+
+ if (!empty($notice)) {
+ // TRANS: Client error displayed when using another format than AtomPub.
+ $this->clientError(sprintf(_('Notice with URI "%s" already exists.'),
+ $note->id));
+ return;
+ }
+ common_log(LOG_NOTICE, "Saving client-supplied notice URI '$note->id'");
+ $options['uri'] = $note->id;
+ }
+
+ // accept remote create time (also maybe not such a good idea)
+
+ if (!empty($activity->time)) {
+ common_log(LOG_NOTICE, "Saving client-supplied create time {$activity->time}");
+ $options['created'] = common_sql_date($activity->time);
+ }
+
+ // Check for optional attributes...
+
+ if (!empty($activity->context)) {
+
+ foreach ($activity->context->attention as $uri) {
+
+ $profile = Profile::fromURI($uri);
+
+ if (!empty($profile)) {
+ $options['replies'] = $uri;
+ } else {
+ $group = User_group::staticGet('uri', $uri);
+ if (!empty($group)) {
+ $options['groups'] = $uri;
+ } else {
+ // @fixme: hook for discovery here
+ common_log(LOG_WARNING, sprintf(_('AtomPub post with unknown attention URI %s'), $uri));
+ }
+ }
+ }
+
+ // Maintain direct reply associations
+ // @fixme what about conversation ID?
+
+ if (!empty($activity->context->replyToID)) {
+ $orig = Notice::staticGet('uri',
+ $activity->context->replyToID);
+ if (!empty($orig)) {
+ $options['reply_to'] = $orig->id;
+ }
+ }
+
+ $location = $activity->context->location;
+
+ if ($location) {
+ $options['lat'] = $location->lat;
+ $options['lon'] = $location->lon;
+ if ($location->location_id) {
+ $options['location_ns'] = $location->location_ns;
+ $options['location_id'] = $location->location_id;
+ }
+ }
+ }
+
+ // Atom categories <-> hashtags
+
+ foreach ($activity->categories as $cat) {
+ if ($cat->term) {
+ $term = common_canonical_tag($cat->term);
+ if ($term) {
+ $options['tags'][] = $term;
+ }
+ }
+ }
+
+ // Atom enclosures -> attachment URLs
+ foreach ($activity->enclosures as $href) {
+ // @fixme save these locally or....?
+ $options['urls'][] = $href;
+ }
+
+ $saved = Notice::saveNew($this->user->id,
+ $content,
+ 'atompub', // TODO: deal with this
+ $options);
+
+ return $saved;
+ }
+
+ function purify($content)
+ {
+ require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
+
+ $config = array('safe' => 1,
+ 'deny_attribute' => 'id,style,on*');
+ return htmLawed($content, $config);
+ }
}
diff --git a/actions/atompubfavoritefeed.php b/actions/atompubfavoritefeed.php
new file mode 100644
index 000000000..478a01b7c
--- /dev/null
+++ b/actions/atompubfavoritefeed.php
@@ -0,0 +1,374 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Feed of ActivityStreams 'favorite' actions
+ *
+ * PHP version 5
+ *
+ * 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 AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+require_once INSTALLDIR . '/lib/apiauth.php';
+
+/**
+ * Feed of ActivityStreams 'favorite' actions
+ *
+ * @category AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class AtompubfavoritefeedAction extends ApiAuthAction
+{
+ private $_profile = null;
+ private $_faves = null;
+
+ /**
+ * For initializing members of the class.
+ *
+ * @param array $argarray misc. arguments
+ *
+ * @return boolean true
+ */
+
+ function prepare($argarray)
+ {
+ parent::prepare($argarray);
+
+ $this->_profile = Profile::staticGet('id', $this->trimmed('profile'));
+
+ if (empty($this->_profile)) {
+ throw new ClientException(_('No such profile'), 404);
+ }
+
+ $offset = ($this->page-1) * $this->count;
+ $limit = $this->count + 1;
+
+ $this->_faves = Fave::byProfile($this->_profile->id,
+ $offset,
+ $limit);
+
+ return true;
+ }
+
+ /**
+ * Handler method
+ *
+ * @param array $argarray is ignored since it's now passed in in prepare()
+ *
+ * @return void
+ */
+
+ function handle($argarray=null)
+ {
+ parent::handle($argarray);
+
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'HEAD':
+ case 'GET':
+ $this->showFeed();
+ break;
+ case 'POST':
+ $this->addFavorite();
+ break;
+ default:
+ throw new ClientException(_('HTTP method not supported.'), 405);
+ return;
+ }
+
+ return;
+ }
+
+ /**
+ * Show a feed of favorite activity streams objects
+ *
+ * @return void
+ */
+
+ function showFeed()
+ {
+ header('Content-Type: application/atom+xml; charset=utf-8');
+
+ $url = common_local_url('AtomPubFavoriteFeed',
+ array('profile' => $this->_profile->id));
+
+ $feed = new Atom10Feed(true);
+
+ $feed->addNamespace('activity',
+ 'http://activitystrea.ms/spec/1.0/');
+
+ $feed->addNamespace('poco',
+ 'http://portablecontacts.net/spec/1.0');
+
+ $feed->addNamespace('media',
+ 'http://purl.org/syndication/atommedia');
+
+ $feed->id = $url;
+
+ $feed->setUpdated('now');
+
+ $feed->addAuthor($this->_profile->getBestName(),
+ $this->_profile->getURI());
+
+ $feed->setTitle(sprintf(_("%s favorites"),
+ $this->_profile->getBestName()));
+
+ $feed->setSubtitle(sprintf(_("Notices %s has favorited to on %s"),
+ $this->_profile->getBestName(),
+ common_config('site', 'name')));
+
+ $feed->addLink(common_local_url('showfavorites',
+ array('nickname' =>
+ $this->_profile->nickname)));
+
+ $feed->addLink($url,
+ array('rel' => 'self',
+ 'type' => 'application/atom+xml'));
+
+ // If there's more...
+
+ if ($this->page > 1) {
+ $feed->addLink($url,
+ array('rel' => 'first',
+ 'type' => 'application/atom+xml'));
+
+ $feed->addLink(common_local_url('AtomPubFavoriteFeed',
+ array('profile' =>
+ $this->_profile->id),
+ array('page' =>
+ $this->page - 1)),
+ array('rel' => 'prev',
+ 'type' => 'application/atom+xml'));
+ }
+
+ if ($this->_faves->N > $this->count) {
+
+ $feed->addLink(common_local_url('AtomPubFavoriteFeed',
+ array('profile' =>
+ $this->_profile->id),
+ array('page' =>
+ $this->page + 1)),
+ array('rel' => 'next',
+ 'type' => 'application/atom+xml'));
+ }
+
+ $i = 0;
+
+ while ($this->_faves->fetch()) {
+
+ // We get one more than needed; skip that one
+
+ $i++;
+
+ if ($i > $this->count) {
+ break;
+ }
+
+ $act = $this->_faves->asActivity();
+ $feed->addEntryRaw($act->asString(false, false, false));
+ }
+
+ $this->raw($feed->getString());
+ }
+
+ /**
+ * add a new favorite
+ *
+ * @return void
+ */
+
+ function addFavorite()
+ {
+ // XXX: Refactor this; all the same for atompub
+
+ if (empty($this->auth_user) ||
+ $this->auth_user->id != $this->_profile->id) {
+ throw new ClientException(_("Can't add someone else's".
+ " subscription"), 403);
+ }
+
+ $xml = file_get_contents('php://input');
+
+ $dom = DOMDocument::loadXML($xml);
+
+ if ($dom->documentElement->namespaceURI != Activity::ATOM ||
+ $dom->documentElement->localName != 'entry') {
+ // TRANS: Client error displayed when not using an Atom entry.
+ throw new ClientException(_('Atom post must be an Atom entry.'));
+ return;
+ }
+
+ $activity = new Activity($dom->documentElement);
+
+ $fave = null;
+
+ if (Event::handle('StartAtomPubNewActivity', array(&$activity))) {
+
+ if ($activity->verb != ActivityVerb::FAVORITE) {
+ // TRANS: Client error displayed when not using the POST verb.
+ // TRANS: Do not translate POST.
+ throw new ClientException(_('Can only handle Favorite activities.'));
+ return;
+ }
+
+ $note = $activity->objects[0];
+
+ if (!in_array($note->type, array(ActivityObject::NOTE,
+ ActivityObject::BLOGENTRY,
+ ActivityObject::STATUS))) {
+ throw new ClientException(_('Can only fave notices.'));
+ return;
+ }
+
+ $notice = Notice::staticGet('uri', $note->id);
+
+ if (empty($notice)) {
+ // XXX: import from listed URL or something
+ throw new ClientException(_('Unknown note.'));
+ }
+
+ $old = Fave::pkeyGet(array('user_id' => $this->auth_user->id,
+ 'notice_id' => $notice->id));
+
+ if (!empty($old)) {
+ throw new ClientException(_('Already a favorite.'));
+ }
+
+ $profile = $this->auth_user->getProfile();
+
+ $fave = Fave::addNew($profile, $notice);
+
+ if (!empty($fave)) {
+ $this->_profile->blowFavesCache();
+ $this->notify($fave, $notice, $this->auth_user);
+ }
+
+ Event::handle('EndAtomPubNewActivity', array($activity, $fave));
+ }
+
+ if (!empty($fave)) {
+ $act = $fave->asActivity();
+
+ header('Content-Type: application/atom+xml; charset=utf-8');
+ header('Content-Location: ' . $act->selfLink);
+
+ $this->startXML();
+ $this->raw($act->asString(true, true, true));
+ $this->endXML();
+ }
+ }
+
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+ $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return last modified, if applicable.
+ *
+ * MAY override
+ *
+ * @return string last modified http header
+ */
+ function lastModified()
+ {
+ // For comparison with If-Last-Modified
+ // If not applicable, return null
+ return null;
+ }
+
+ /**
+ * Return etag, if applicable.
+ *
+ * MAY override
+ *
+ * @return string etag http header
+ */
+
+ function etag()
+ {
+ return null;
+ }
+
+ /**
+ * Does this require authentication?
+ *
+ * @return boolean true if delete, else false
+ */
+
+ function requiresAuth()
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+ $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Notify the author of the favorite that the user likes their notice
+ *
+ * @param Favorite $fave the favorite in question
+ * @param Notice $notice the notice that's been faved
+ * @param User $user the user doing the favoriting
+ *
+ * @return void
+ */
+
+ function notify($fave, $notice, $user)
+ {
+ $other = User::staticGet('id', $notice->profile_id);
+ if ($other && $other->id != $user->id) {
+ if ($other->email && $other->emailnotifyfav) {
+ mail_notify_fave($other, $user, $notice);
+ }
+ // XXX: notify by IM
+ // XXX: notify by SMS
+ }
+ }
+}
diff --git a/actions/atompubmembershipfeed.php b/actions/atompubmembershipfeed.php
new file mode 100644
index 000000000..3002576c1
--- /dev/null
+++ b/actions/atompubmembershipfeed.php
@@ -0,0 +1,355 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Feed of group memberships for a user, in ActivityStreams format
+ *
+ * PHP version 5
+ *
+ * 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 AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+require_once INSTALLDIR . '/lib/apiauth.php';
+
+/**
+ * Feed of group memberships for a user, in ActivityStreams format
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class AtompubmembershipfeedAction extends ApiAuthAction
+{
+ private $_profile = null;
+ private $_memberships = null;
+
+ /**
+ * For initializing members of the class.
+ *
+ * @param array $argarray misc. arguments
+ *
+ * @return boolean true
+ */
+
+ function prepare($argarray)
+ {
+ parent::prepare($argarray);
+
+ $profileId = $this->trimmed('profile');
+
+ $this->_profile = Profile::staticGet('id', $profileId);
+
+ if (empty($this->_profile)) {
+ throw new ClientException(_('No such profile.'), 404);
+ }
+
+ $offset = ($this->page-1) * $this->count;
+ $limit = $this->count + 1;
+
+ $this->_memberships = Group_member::byMember($this->_profile->id,
+ $offset,
+ $limit);
+
+ return true;
+ }
+
+ /**
+ * Handler method
+ *
+ * @param array $argarray is ignored since it's now passed in in prepare()
+ *
+ * @return void
+ */
+
+ function handle($argarray=null)
+ {
+ parent::handle($argarray);
+
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'HEAD':
+ case 'GET':
+ $this->showFeed();
+ break;
+ case 'POST':
+ $this->addMembership();
+ break;
+ default:
+ throw new ClientException(_('HTTP method not supported.'), 405);
+ return;
+ }
+
+ return;
+ }
+
+ /**
+ * Show a feed of favorite activity streams objects
+ *
+ * @return void
+ */
+
+ function showFeed()
+ {
+ header('Content-Type: application/atom+xml; charset=utf-8');
+
+ $url = common_local_url('AtomPubMembershipFeed',
+ array('profile' => $this->_profile->id));
+
+ $feed = new Atom10Feed(true);
+
+ $feed->addNamespace('activity',
+ 'http://activitystrea.ms/spec/1.0/');
+
+ $feed->addNamespace('poco',
+ 'http://portablecontacts.net/spec/1.0');
+
+ $feed->addNamespace('media',
+ 'http://purl.org/syndication/atommedia');
+
+ $feed->id = $url;
+
+ $feed->setUpdated('now');
+
+ $feed->addAuthor($this->_profile->getBestName(),
+ $this->_profile->getURI());
+
+ $feed->setTitle(sprintf(_("%s group memberships"),
+ $this->_profile->getBestName()));
+
+ $feed->setSubtitle(sprintf(_("Groups %s is a member of on %s"),
+ $this->_profile->getBestName(),
+ common_config('site', 'name')));
+
+ $feed->addLink(common_local_url('usergroups',
+ array('nickname' =>
+ $this->_profile->nickname)));
+
+ $feed->addLink($url,
+ array('rel' => 'self',
+ 'type' => 'application/atom+xml'));
+
+ // If there's more...
+
+ if ($this->page > 1) {
+ $feed->addLink($url,
+ array('rel' => 'first',
+ 'type' => 'application/atom+xml'));
+
+ $feed->addLink(common_local_url('AtomPubMembershipFeed',
+ array('profile' =>
+ $this->_profile->id),
+ array('page' =>
+ $this->page - 1)),
+ array('rel' => 'prev',
+ 'type' => 'application/atom+xml'));
+ }
+
+ if ($this->_memberships->N > $this->count) {
+
+ $feed->addLink(common_local_url('AtomPubMembershipFeed',
+ array('profile' =>
+ $this->_profile->id),
+ array('page' =>
+ $this->page + 1)),
+ array('rel' => 'next',
+ 'type' => 'application/atom+xml'));
+ }
+
+ $i = 0;
+
+ while ($this->_memberships->fetch()) {
+
+ // We get one more than needed; skip that one
+
+ $i++;
+
+ if ($i > $this->count) {
+ break;
+ }
+
+ $act = $this->_memberships->asActivity();
+ $feed->addEntryRaw($act->asString(false, false, false));
+ }
+
+ $this->raw($feed->getString());
+ }
+
+ /**
+ * add a new favorite
+ *
+ * @return void
+ */
+
+ function addMembership()
+ {
+ // XXX: Refactor this; all the same for atompub
+
+ if (empty($this->auth_user) ||
+ $this->auth_user->id != $this->_profile->id) {
+ throw new ClientException(_("Can't add someone else's".
+ " membership"), 403);
+ }
+
+ $xml = file_get_contents('php://input');
+
+ $dom = DOMDocument::loadXML($xml);
+
+ if ($dom->documentElement->namespaceURI != Activity::ATOM ||
+ $dom->documentElement->localName != 'entry') {
+ // TRANS: Client error displayed when not using an Atom entry.
+ throw new ClientException(_('Atom post must be an Atom entry.'));
+ return;
+ }
+
+ $activity = new Activity($dom->documentElement);
+
+ $membership = null;
+
+ if (Event::handle('StartAtomPubNewActivity', array(&$activity))) {
+
+ if ($activity->verb != ActivityVerb::JOIN) {
+ // TRANS: Client error displayed when not using the POST verb.
+ // TRANS: Do not translate POST.
+ throw new ClientException(_('Can only handle Join activities.'));
+ return;
+ }
+
+ $groupObj = $activity->objects[0];
+
+ if ($groupObj->type != ActivityObject::GROUP) {
+ throw new ClientException(_('Can only fave notices.'));
+ return;
+ }
+
+ $group = User_group::staticGet('uri', $groupObj->id);
+
+ if (empty($group)) {
+ // XXX: import from listed URL or something
+ throw new ClientException(_('Unknown group.'));
+ }
+
+ $old = Group_member::pkeyGet(array('profile_id' => $this->auth_user->id,
+ 'group_id' => $group->id));
+
+ if (!empty($old)) {
+ throw new ClientException(_('Already a member.'));
+ }
+
+ $profile = $this->auth_user->getProfile();
+
+ if (Group_block::isBlocked($group, $profile)) {
+ // XXX: import from listed URL or something
+ throw new ClientException(_('Blocked by admin.'));
+ }
+
+ if (Event::handle('StartJoinGroup', array($group, $this->auth_user))) {
+ $membership = Group_member::join($group->id, $this->auth_user->id);
+ Event::handle('EndJoinGroup', array($group, $this->auth_user));
+ }
+
+ Event::handle('EndAtomPubNewActivity', array($activity, $membership));
+ }
+
+ if (!empty($membership)) {
+ $act = $membership->asActivity();
+
+ header('Content-Type: application/atom+xml; charset=utf-8');
+ header('Content-Location: ' . $act->selfLink);
+
+ $this->startXML();
+ $this->raw($act->asString(true, true, true));
+ $this->endXML();
+ }
+ }
+
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+ $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return last modified, if applicable.
+ *
+ * MAY override
+ *
+ * @return string last modified http header
+ */
+ function lastModified()
+ {
+ // For comparison with If-Last-Modified
+ // If not applicable, return null
+ return null;
+ }
+
+ /**
+ * Return etag, if applicable.
+ *
+ * MAY override
+ *
+ * @return string etag http header
+ */
+
+ function etag()
+ {
+ return null;
+ }
+
+ /**
+ * Does this require authentication?
+ *
+ * @return boolean true if delete, else false
+ */
+
+ function requiresAuth()
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+ $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/actions/atompubshowfavorite.php b/actions/atompubshowfavorite.php
new file mode 100644
index 000000000..5fe680bb7
--- /dev/null
+++ b/actions/atompubshowfavorite.php
@@ -0,0 +1,228 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Show a single favorite in Atom Activity Streams format
+ *
+ * PHP version 5
+ *
+ * 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 AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+require_once INSTALLDIR . '/lib/apiauth.php';
+
+/**
+ * Show a single favorite in Atom Activity Streams format.
+ *
+ * Can also be used to delete a favorite.
+ *
+ * @category Action
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class AtompubshowfavoriteAction extends ApiAuthAction
+{
+ private $_profile = null;
+ private $_notice = null;
+ private $_fave = null;
+
+ /**
+ * For initializing members of the class.
+ *
+ * @param array $argarray misc. arguments
+ *
+ * @return boolean true
+ */
+
+ function prepare($argarray)
+ {
+ parent::prepare($argarray);
+
+ $profileId = $this->trimmed('profile');
+ $noticeId = $this->trimmed('notice');
+
+ $this->_profile = Profile::staticGet('id', $profileId);
+
+ if (empty($this->_profile)) {
+ throw new ClientException(_('No such profile.'), 404);
+ }
+
+ $this->_notice = Notice::staticGet('id', $noticeId);
+
+ if (empty($this->_notice)) {
+ throw new ClientException(_('No such notice.'), 404);
+ }
+
+ $this->_fave = Fave::pkeyGet(array('user_id' => $profileId,
+ 'notice_id' => $noticeId));
+
+ if (empty($this->_fave)) {
+ throw new ClientException(_('No such favorite.'), 404);
+ }
+
+ return true;
+ }
+
+ /**
+ * Handler method
+ *
+ * @param array $argarray is ignored since it's now passed in in prepare()
+ *
+ * @return void
+ */
+
+ function handle($argarray=null)
+ {
+ parent::handle($argarray);
+
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case GET:
+ case HEAD:
+ $this->showFave();
+ break;
+ case DELETE:
+ $this->deleteFave();
+ break;
+ default:
+ throw new ClientException(_('HTTP method not supported.'),
+ 405);
+ }
+ return true;
+ }
+
+ /**
+ * Show a single favorite, in ActivityStreams format
+ *
+ * @return void
+ */
+
+ function showFave()
+ {
+ $activity = $this->_fave->asActivity();
+
+ header('Content-Type: application/atom+xml; charset=utf-8');
+
+ $this->startXML();
+ $this->raw($activity->asString(true, true, true));
+ $this->endXML();
+
+ return;
+ }
+
+ /**
+ * Delete the favorite
+ *
+ * @return void
+ */
+
+ function deleteFave()
+ {
+ if (empty($this->auth_user) ||
+ $this->auth_user->id != $this->_profile->id) {
+ throw new ClientException(_("Can't delete someone else's".
+ " favorite"), 403);
+ }
+
+ $this->_fave->delete();
+
+ return;
+ }
+
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+ $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return last modified, if applicable.
+ *
+ * MAY override
+ *
+ * @return string last modified http header
+ */
+
+ function lastModified()
+ {
+ return max(strtotime($this->_profile->modified),
+ strtotime($this->_notice->modified),
+ strtotime($this->_fave->modified));
+ }
+
+ /**
+ * Return etag, if applicable.
+ *
+ * MAY override
+ *
+ * @return string etag http header
+ */
+
+ function etag()
+ {
+ $mtime = strtotime($this->_fave->modified);
+
+ return 'W/"' . implode(':', array('AtomPubShowFavorite',
+ $this->_profile->id,
+ $this->_notice->id,
+ $mtime)) . '"';
+ }
+
+ /**
+ * Does this require authentication?
+ *
+ * @return boolean true if delete, else false
+ */
+
+ function requiresAuth()
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+ $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/actions/atompubshowmembership.php b/actions/atompubshowmembership.php
new file mode 100644
index 000000000..6d848a229
--- /dev/null
+++ b/actions/atompubshowmembership.php
@@ -0,0 +1,235 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Show a single membership as an Activity Streams entry
+ *
+ * PHP version 5
+ *
+ * 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 AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+require_once INSTALLDIR . '/lib/apiauth.php';
+
+/**
+ * Show (or delete) a single membership event as an ActivityStreams entry
+ *
+ * @category AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class AtompubshowmembershipAction extends ApiAuthAction
+{
+ private $_profile = null;
+ private $_group = null;
+ private $_membership = null;
+
+ /**
+ * For initializing members of the class.
+ *
+ * @param array $argarray misc. arguments
+ *
+ * @return boolean true
+ */
+
+ function prepare($argarray)
+ {
+ parent::prepare($argarray);
+
+ $profileId = $this->trimmed('profile');
+
+ $this->_profile = Profile::staticGet('id', $profileId);
+
+ if (empty($this->_profile)) {
+ throw new ClientException(_('No such profile.'), 404);
+ }
+
+ $groupId = $this->trimmed('group');
+
+ $this->_group = User_group::staticGet('id', $groupId);
+
+ if (empty($this->_group)) {
+ throw new ClientException(_('No such group'), 404);
+ }
+
+ $kv = array('group_id' => $groupId,
+ 'profile_id' => $profileId);
+
+ $this->_membership = Group_member::pkeyGet($kv);
+
+ if (empty($this->_membership)) {
+ throw new ClientException(_('Not a member'), 404);
+ }
+
+ return true;
+ }
+
+ /**
+ * Handler method
+ *
+ * @param array $argarray is ignored since it's now passed in in prepare()
+ *
+ * @return void
+ */
+
+ function handle($argarray=null)
+ {
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'GET':
+ case 'HEAD':
+ $this->showMembership();
+ break;
+ case 'DELETE':
+ $this->deleteMembership();
+ break;
+ default:
+ throw new ClientException(_('Method not supported'), 405);
+ break;
+ }
+ return;
+ }
+
+ /**
+ * show a single membership
+ *
+ * @return void
+ */
+
+ function showMembership()
+ {
+ $activity = $this->_membership->asActivity();
+
+ header('Content-Type: application/atom+xml; charset=utf-8');
+
+ $this->startXML();
+ $this->raw($activity->asString(true, true, true));
+ $this->endXML();
+
+ return;
+ }
+
+ /**
+ * Delete the membership (leave the group)
+ *
+ * @return void
+ */
+
+ function deleteMembership()
+ {
+ if (empty($this->auth_user) ||
+ $this->auth_user->id != $this->_profile->id) {
+ throw new ClientException(_("Can't delete someone else's".
+ " membership"), 403);
+ }
+
+ if (Event::handle('StartLeaveGroup', array($this->_group, $this->auth_user))) {
+ Group_member::leave($this->_group->id, $this->auth_user->id);
+ Event::handle('EndLeaveGroup', array($this->_group, $this->auth_user));
+ }
+
+ return;
+ }
+
+ /**
+ * Return true if read only.
+ *
+ * MAY override
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+ $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return last modified, if applicable.
+ *
+ * Because the representation depends on the profile and group,
+ * our last modified value is the maximum of their mod time
+ * with the actual membership's mod time.
+ *
+ * @return string last modified http header
+ */
+ function lastModified()
+ {
+ return max(strtotime($this->_profile->modified),
+ strtotime($this->_group->modified),
+ strtotime($this->_membership->modified));
+ }
+
+ /**
+ * Return etag, if applicable.
+ *
+ * A "weak" Etag including the profile and group id as well as
+ * the admin flag and ctime of the membership.
+ *
+ * @return string etag http header
+ */
+
+ function etag()
+ {
+ $ctime = strtotime($this->_membership->created);
+
+ $adminflag = ($this->_membership->is_admin) ? 't' : 'f';
+
+ return 'W/"' . implode(':', array('AtomPubShowMembership',
+ $this->_profile->id,
+ $this->_group->id,
+ $adminflag,
+ $ctime)) . '"';
+ }
+
+ /**
+ * Does this require authentication?
+ *
+ * @return boolean true if delete, else false
+ */
+
+ function requiresAuth()
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
+ $_SERVER['REQUEST_METHOD'] == 'HEAD') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/actions/atompubshowsubscription.php b/actions/atompubshowsubscription.php
new file mode 100644
index 000000000..55ba68adc
--- /dev/null
+++ b/actions/atompubshowsubscription.php
@@ -0,0 +1,224 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Single subscription
+ *
+ * PHP version 5
+ *
+ * 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 AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+require_once INSTALLDIR . '/lib/apiauth.php';
+
+/**
+ * Show a single subscription
+ *
+ * @category AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class AtompubshowsubscriptionAction extends ApiAuthAction
+{
+ private $_subscriber = null;
+ private $_subscribed = null;
+ private $_subscription = null;
+
+ /**
+ * For initializing members of the class.
+ *
+ * @param array $argarray misc. arguments
+ *
+ * @return boolean true
+ */
+
+ function prepare($argarray)
+ {
+ parent::prepare($argarray);
+ $subscriberId = $this->trimmed('subscriber');
+
+ $this->_subscriber = Profile::staticGet('id', $subscriberId);
+
+ if (empty($this->_subscriber)) {
+ throw new ClientException(sprintf(_('No such profile id: %d'),
+ $subscriberId), 404);
+ }
+
+ $subscribedId = $this->trimmed('subscribed');
+
+ $this->_subscribed = Profile::staticGet('id', $subscribedId);
+
+ if (empty($this->_subscribed)) {
+ throw new ClientException(sprintf(_('No such profile id: %d'),
+ $subscribedId), 404);
+ }
+
+ $this->_subscription =
+ Subscription::pkeyGet(array('subscriber' => $subscriberId,
+ 'subscribed' => $subscribedId));
+
+ if (empty($this->_subscription)) {
+ $msg = sprintf(_('Profile %d not subscribed to profile %d'),
+ $subscriberId, $subscribedId);
+ throw new ClientException($msg, 404);
+ }
+
+ return true;
+ }
+
+ /**
+ * Handler method
+ *
+ * @param array $argarray is ignored since it's now passed in in prepare()
+ *
+ * @return void
+ */
+
+ function handle($argarray=null)
+ {
+ parent::handle($argarray);
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'HEAD':
+ case 'GET':
+ $this->showSubscription();
+ break;
+ case 'DELETE':
+ $this->deleteSubscription();
+ break;
+ default:
+ $this->clientError(_('HTTP method not supported.'), 405);
+ return;
+ }
+
+ return;
+ }
+
+ /**
+ * Show the subscription in ActivityStreams Atom format.
+ *
+ * @return void
+ */
+
+ function showSubscription()
+ {
+ $activity = $this->_subscription->asActivity();
+
+ header('Content-Type: application/atom+xml; charset=utf-8');
+
+ $this->startXML();
+ $this->raw($activity->asString(true, true, true));
+ $this->endXML();
+
+ return;
+ }
+
+ /**
+ * Delete the subscription
+ *
+ * @return void
+ */
+
+ function deleteSubscription()
+ {
+ if (empty($this->auth_user) ||
+ $this->auth_user->id != $this->_subscriber->id) {
+ throw new ClientException(_("Can't delete someone else's".
+ " subscription"), 403);
+ }
+
+ Subscription::cancel($this->_subscriber,
+ $this->_subscribed);
+
+ return;
+ }
+
+ /**
+ * Is this action read only?
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean true
+ */
+
+ function isReadOnly($args)
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'DELETE') {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Return last modified, if applicable.
+ *
+ * @return string last modified http header
+ */
+
+ function lastModified()
+ {
+ return max(strtotime($this->_subscriber->modified),
+ strtotime($this->_subscribed->modified),
+ strtotime($this->_subscription->modified));
+ }
+
+ /**
+ * Etag for this object
+ *
+ * @return string etag http header
+ */
+
+ function etag()
+ {
+ $mtime = strtotime($this->_subscription->modified);
+
+ return 'W/"' . implode(':', array('AtomPubShowSubscription',
+ $this->_subscriber->id,
+ $this->_subscribed->id,
+ $mtime)) . '"';
+ }
+
+ /**
+ * Does this require authentication?
+ *
+ * @return boolean true if delete, else false
+ */
+
+ function requiresAuth()
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'DELETE') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/actions/atompubsubscriptionfeed.php b/actions/atompubsubscriptionfeed.php
new file mode 100644
index 000000000..15ae79f6a
--- /dev/null
+++ b/actions/atompubsubscriptionfeed.php
@@ -0,0 +1,335 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * AtomPub subscription feed
+ *
+ * PHP version 5
+ *
+ * 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 Cache
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+require_once INSTALLDIR . '/lib/apiauth.php';
+
+/**
+ * Subscription feed class for AtomPub
+ *
+ * Generates a list of the user's subscriptions
+ *
+ * @category AtomPub
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class AtompubsubscriptionfeedAction extends ApiAuthAction
+{
+ private $_profile = null;
+ private $_subscriptions = null;
+
+ /**
+ * For initializing members of the class.
+ *
+ * @param array $argarray misc. arguments
+ *
+ * @return boolean true
+ */
+
+ function prepare($argarray)
+ {
+ parent::prepare($argarray);
+
+ $subscriber = $this->trimmed('subscriber');
+
+ $this->_profile = Profile::staticGet('id', $subscriber);
+
+ if (empty($this->_profile)) {
+ throw new ClientException(sprintf(_('No such profile id: %d'),
+ $subscriber), 404);
+ }
+
+ // page and count from ApiAction
+
+ $offset = ($this->page-1) * $this->count;
+
+ $this->_subscriptions = Subscription::bySubscriber($subscriber,
+ $offset,
+ $this->count + 1);
+
+ return true;
+ }
+
+ /**
+ * Handler method
+ *
+ * @param array $argarray is ignored since it's now passed in in prepare()
+ *
+ * @return void
+ */
+
+ function handle($argarray=null)
+ {
+ parent::handle($argarray);
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'HEAD':
+ case 'GET':
+ $this->showFeed();
+ break;
+ case 'POST':
+ $this->addSubscription();
+ break;
+ default:
+ $this->clientError(_('HTTP method not supported.'), 405);
+ return;
+ }
+
+ return;
+ }
+
+ /**
+ * Show the feed of subscriptions
+ *
+ * @return void
+ */
+
+ function showFeed()
+ {
+ header('Content-Type: application/atom+xml; charset=utf-8');
+
+ $url = common_local_url('AtomPubSubscriptionFeed',
+ array('subscriber' => $this->_profile->id));
+
+ $feed = new Atom10Feed(true);
+
+ $feed->addNamespace('activity',
+ 'http://activitystrea.ms/spec/1.0/');
+
+ $feed->addNamespace('poco',
+ 'http://portablecontacts.net/spec/1.0');
+
+ $feed->addNamespace('media',
+ 'http://purl.org/syndication/atommedia');
+
+ $feed->id = $url;
+
+ $feed->setUpdated('now');
+
+ $feed->addAuthor($this->_profile->getBestName(),
+ $this->_profile->getURI());
+
+ $feed->setTitle(sprintf(_("%s subscriptions"),
+ $this->_profile->getBestName()));
+
+ $feed->setSubtitle(sprintf(_("People %s has subscribed to on %s"),
+ $this->_profile->getBestName(),
+ common_config('site', 'name')));
+
+ $feed->addLink(common_local_url('subscriptions',
+ array('nickname' =>
+ $this->_profile->nickname)));
+
+ $feed->addLink($url,
+ array('rel' => 'self',
+ 'type' => 'application/atom+xml'));
+
+ // If there's more...
+
+ if ($this->page > 1) {
+ $feed->addLink($url,
+ array('rel' => 'first',
+ 'type' => 'application/atom+xml'));
+
+ $feed->addLink(common_local_url('AtomPubSubscriptionFeed',
+ array('subscriber' =>
+ $this->_profile->id),
+ array('page' =>
+ $this->page - 1)),
+ array('rel' => 'prev',
+ 'type' => 'application/atom+xml'));
+ }
+
+ if ($this->_subscriptions->N > $this->count) {
+
+ $feed->addLink(common_local_url('AtomPubSubscriptionFeed',
+ array('subscriber' =>
+ $this->_profile->id),
+ array('page' =>
+ $this->page + 1)),
+ array('rel' => 'next',
+ 'type' => 'application/atom+xml'));
+ }
+
+ $i = 0;
+
+ // XXX: This is kind of inefficient
+
+ while ($this->_subscriptions->fetch()) {
+
+ // We get one more than needed; skip that one
+
+ $i++;
+
+ if ($i > $this->count) {
+ break;
+ }
+
+ $act = $this->_subscriptions->asActivity();
+ $feed->addEntryRaw($act->asString(false, false, false));
+ }
+
+ $this->raw($feed->getString());
+ }
+
+ /**
+ * Add a new subscription
+ *
+ * Handling the POST method for AtomPub
+ *
+ * @return void
+ */
+
+ function addSubscription()
+ {
+ if (empty($this->auth_user) ||
+ $this->auth_user->id != $this->_profile->id) {
+ throw new ClientException(_("Can't add someone else's".
+ " subscription"), 403);
+ }
+
+ $xml = file_get_contents('php://input');
+
+ $dom = DOMDocument::loadXML($xml);
+
+ if ($dom->documentElement->namespaceURI != Activity::ATOM ||
+ $dom->documentElement->localName != 'entry') {
+ // TRANS: Client error displayed when not using an Atom entry.
+ $this->clientError(_('Atom post must be an Atom entry.'));
+ return;
+ }
+
+ $activity = new Activity($dom->documentElement);
+
+ $sub = null;
+
+ if (Event::handle('StartAtomPubNewActivity', array(&$activity))) {
+
+ if ($activity->verb != ActivityVerb::FOLLOW) {
+ // TRANS: Client error displayed when not using the POST verb.
+ // TRANS: Do not translate POST.
+ $this->clientError(_('Can only handle Follow activities.'));
+ return;
+ }
+
+ $person = $activity->objects[0];
+
+ if ($person->type != ActivityObject::PERSON) {
+ $this->clientError(_('Can only follow people.'));
+ return;
+ }
+
+ // XXX: OStatus discovery (maybe)
+
+ $profile = Profile::fromURI($person->id);
+
+ if (empty($profile)) {
+ $this->clientError(sprintf(_('Unknown profile %s'), $person->id));
+ return;
+ }
+
+ if (Subscription::start($this->_profile, $profile)) {
+ $sub = Subscription::pkeyGet(array('subscriber' => $this->_profile->id,
+ 'subscribed' => $profile->id));
+ }
+
+ Event::handle('EndAtomPubNewActivity', array($activity, $sub));
+ }
+
+ if (!empty($sub)) {
+ $act = $sub->asActivity();
+
+ header('Content-Type: application/atom+xml; charset=utf-8');
+ header('Content-Location: ' . $act->selfLink);
+
+ $this->startXML();
+ $this->raw($act->asString(true, true, true));
+ $this->endXML();
+ }
+ }
+
+ /**
+ * Return true if read only.
+ *
+ * @param array $args other arguments
+ *
+ * @return boolean is read only action?
+ */
+
+ function isReadOnly($args)
+ {
+ return $_SERVER['REQUEST_METHOD'] != 'POST';
+ }
+
+ /**
+ * Return last modified, if applicable.
+ *
+ * @return string last modified http header
+ */
+
+ function lastModified()
+ {
+ return null;
+ }
+
+ /**
+ * Return etag, if applicable.
+ *
+ * @return string etag http header
+ */
+
+ function etag()
+ {
+ return null;
+ }
+
+ /**
+ * Does this require authentication?
+ *
+ * @return boolean true if delete, else false
+ */
+
+ function requiresAuth()
+ {
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/actions/attachment.php b/actions/attachment.php
index 6981354d1..45aa78728 100644
--- a/actions/attachment.php
+++ b/actions/attachment.php
@@ -42,7 +42,6 @@ require_once INSTALLDIR.'/lib/attachmentlist.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 AttachmentAction extends Action
{
/**
@@ -70,6 +69,7 @@ class AttachmentAction extends Action
}
if (empty($this->attachment)) {
+ // TRANS: Client error displayed trying to get a non-existing attachment.
$this->clientError(_('No such attachment.'), 404);
return false;
}
@@ -81,7 +81,6 @@ class AttachmentAction extends Action
*
* @return boolean true
*/
-
function isReadOnly($args)
{
return true;
@@ -129,7 +128,6 @@ class AttachmentAction extends Action
*
* @return void
*/
-
function handle($args)
{
parent::handle($args);
@@ -150,7 +148,6 @@ class AttachmentAction extends Action
*
* @return void
*/
-
function showLocalNavBlock()
{
}
@@ -162,7 +159,6 @@ class AttachmentAction extends Action
*
* @return void
*/
-
function showContent()
{
$ali = new Attachment($this->attachment, $this);
@@ -174,7 +170,6 @@ class AttachmentAction extends Action
*
* @return void
*/
-
function showPageNoticeBlock()
{
}
@@ -191,4 +186,3 @@ class AttachmentAction extends Action
$atcs->show();
}
}
-
diff --git a/actions/attachment_ajax.php b/actions/attachment_ajax.php
index 1e0728075..fb7d15f8a 100644
--- a/actions/attachment_ajax.php
+++ b/actions/attachment_ajax.php
@@ -42,7 +42,6 @@ require_once INSTALLDIR.'/actions/attachment.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 Attachment_ajaxAction extends AttachmentAction
{
/**
@@ -80,4 +79,3 @@ class Attachment_ajaxAction extends AttachmentAction
$this->elementEnd('div');
}
}
-
diff --git a/actions/attachment_thumbnail.php b/actions/attachment_thumbnail.php
index 7d0ac97a6..38648b8be 100644
--- a/actions/attachment_thumbnail.php
+++ b/actions/attachment_thumbnail.php
@@ -42,10 +42,8 @@ require_once INSTALLDIR.'/actions/attachment.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 Attachment_thumbnailAction extends AttachmentAction
{
-
function handle($args)
{
$this->showPage();
@@ -79,6 +77,4 @@ class Attachment_thumbnailAction extends AttachmentAction
}
$this->element('img', array('src' => $file_thumbnail->url, 'alt' => 'Thumbnail'));
}
-
}
-
diff --git a/actions/avatarbynickname.php b/actions/avatarbynickname.php
index 537950792..fa97a86eb 100644
--- a/actions/avatarbynickname.php
+++ b/actions/avatarbynickname.php
@@ -48,7 +48,7 @@ class AvatarbynicknameAction extends Action
* Class handler.
*
* @param array $args query arguments
- *
+ *
* @return boolean false if nickname or user isn't found
*/
function handle($args)
@@ -56,27 +56,32 @@ class AvatarbynicknameAction extends Action
parent::handle($args);
$nickname = $this->trimmed('nickname');
if (!$nickname) {
+ // TRANS: Client error displayed trying to get an avatar without providing a nickname.
$this->clientError(_('No nickname.'));
return;
}
$size = $this->trimmed('size');
if (!$size) {
+ // TRANS: Client error displayed trying to get an avatar without providing an avatar size.
$this->clientError(_('No size.'));
return;
}
$size = strtolower($size);
if (!in_array($size, array('original', '96', '48', '24'))) {
+ // TRANS: Client error displayed trying to get an avatar providing an invalid avatar size.
$this->clientError(_('Invalid size.'));
return;
}
$user = User::staticGet('nickname', $nickname);
if (!$user) {
+ // TRANS: Client error displayed trying to get an avatar for a non-existing user.
$this->clientError(_('No such user.'));
return;
}
$profile = $user->getProfile();
if (!$profile) {
+ // TRANS: Client error displayed trying to get an avatar for a user without a profile.
$this->clientError(_('User has no profile.'));
return;
}
@@ -103,4 +108,3 @@ class AvatarbynicknameAction extends Action
return true;
}
}
-
diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php
index 9d4040e75..375420c5c 100644
--- a/actions/avatarsettings.php
+++ b/actions/avatarsettings.php
@@ -49,7 +49,6 @@ define('MAX_ORIGINAL', 480);
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class AvatarsettingsAction extends AccountSettingsAction
{
var $mode = null;
@@ -61,9 +60,9 @@ class AvatarsettingsAction extends AccountSettingsAction
*
* @return string Title of the page
*/
-
function title()
{
+ // TRANS: Title for avatar upload page.
return _('Avatar');
}
@@ -72,10 +71,12 @@ class AvatarsettingsAction extends AccountSettingsAction
*
* @return instructions for use
*/
-
function getInstructions()
{
- return sprintf(_('You can upload your personal avatar. The maximum file size is %s.'), ImageFile::maxFileSize());
+ // TRANS: Instruction for avatar upload page.
+ // TRANS: %s is the maximum file size, for example "500b", "10kB" or "2MB".
+ return sprintf(_('You can upload your personal avatar. The maximum file size is %s.'),
+ ImageFile::maxFileSize());
}
/**
@@ -103,6 +104,7 @@ class AvatarsettingsAction extends AccountSettingsAction
if (!$profile) {
common_log_db_error($user, 'SELECT', __FILE__);
+ // TRANS: Server error displayed in avatar upload page when no matching profile can be found for a user.
$this->serverError(_('User without matching profile.'));
return;
}
@@ -116,14 +118,16 @@ class AvatarsettingsAction extends AccountSettingsAction
'action' =>
common_local_url('avatarsettings')));
$this->elementStart('fieldset');
+ // TRANS: Avatar upload page form legend.
$this->element('legend', null, _('Avatar settings'));
$this->hidden('token', common_session_token());
-
+
if (Event::handle('StartAvatarFormData', array($this))) {
$this->elementStart('ul', 'form_data');
if ($original) {
$this->elementStart('li', array('id' => 'avatar_original',
'class' => 'avatar_view'));
+ // TRANS: Header on avatar upload page for thumbnail of originally uploaded avatar (h2).
$this->element('h2', null, _("Original"));
$this->elementStart('div', array('id'=>'avatar_original_view'));
$this->element('img', array('src' => $original->url,
@@ -139,6 +143,7 @@ class AvatarsettingsAction extends AccountSettingsAction
if ($avatar) {
$this->elementStart('li', array('id' => 'avatar_preview',
'class' => 'avatar_view'));
+ // TRANS: Header on avatar upload page for thumbnail of to be used rendition of uploaded avatar (h2).
$this->element('h2', null, _("Preview"));
$this->elementStart('div', array('id'=>'avatar_preview_view'));
$this->element('img', array('src' => $original->url,
@@ -146,7 +151,8 @@ class AvatarsettingsAction extends AccountSettingsAction
'height' => AVATAR_PROFILE_SIZE,
'alt' => $user->nickname));
$this->elementEnd('div');
- $this->submit('delete', _('Delete'));
+ // TRANS: Button on avatar upload page to delete current avatar.
+ $this->submit('delete', _m('BUTTON','Delete'));
$this->elementEnd('li');
}
@@ -163,7 +169,8 @@ class AvatarsettingsAction extends AccountSettingsAction
$this->elementStart('ul', 'form_actions');
$this->elementStart('li');
- $this->submit('upload', _('Upload'));
+ // TRANS: Button on avatar upload page to upload an avatar.
+ $this->submit('upload', _m('BUTTON','Upload'));
$this->elementEnd('li');
$this->elementEnd('ul');
}
@@ -171,7 +178,6 @@ class AvatarsettingsAction extends AccountSettingsAction
$this->elementEnd('fieldset');
$this->elementEnd('form');
-
}
function showCropForm()
@@ -182,6 +188,7 @@ class AvatarsettingsAction extends AccountSettingsAction
if (!$profile) {
common_log_db_error($user, 'SELECT', __FILE__);
+ // TRANS: Server error displayed in avatar upload page when no matching profile can be found for a user.
$this->serverError(_('User without matching profile.'));
return;
}
@@ -194,6 +201,7 @@ class AvatarsettingsAction extends AccountSettingsAction
'action' =>
common_local_url('avatarsettings')));
$this->elementStart('fieldset');
+ // TRANS: Avatar upload page crop form legend.
$this->element('legend', null, _('Avatar settings'));
$this->hidden('token', common_session_token());
@@ -202,6 +210,7 @@ class AvatarsettingsAction extends AccountSettingsAction
$this->elementStart('li',
array('id' => 'avatar_original',
'class' => 'avatar_view'));
+ // TRANS: Header on avatar upload crop form for thumbnail of originally uploaded avatar (h2).
$this->element('h2', null, _("Original"));
$this->elementStart('div', array('id'=>'avatar_original_view'));
$this->element('img', array('src' => Avatar::url($this->filedata['filename']),
@@ -214,6 +223,7 @@ class AvatarsettingsAction extends AccountSettingsAction
$this->elementStart('li',
array('id' => 'avatar_preview',
'class' => 'avatar_view'));
+ // TRANS: Header on avatar upload crop form for thumbnail of to be used rendition of uploaded avatar (h2).
$this->element('h2', null, _("Preview"));
$this->elementStart('div', array('id'=>'avatar_preview_view'));
$this->element('img', array('src' => Avatar::url($this->filedata['filename']),
@@ -228,13 +238,14 @@ class AvatarsettingsAction extends AccountSettingsAction
'type' => 'hidden',
'id' => $crop_info));
}
- $this->submit('crop', _('Crop'));
+
+ // TRANS: Button on avatar upload crop form to confirm a selected crop as avatar.
+ $this->submit('crop', _m('BUTTON','Crop'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
$this->elementEnd('form');
-
}
/**
@@ -244,7 +255,6 @@ class AvatarsettingsAction extends AccountSettingsAction
*
* @return void
*/
-
function handlePost()
{
// Workaround for PHP returning empty $_POST and $_FILES when POST
@@ -271,7 +281,7 @@ class AvatarsettingsAction extends AccountSettingsAction
'Try again, please.'));
return;
}
-
+
if (Event::handle('StartAvatarSaveForm', array($this))) {
if ($this->arg('upload')) {
$this->uploadAvatar();
@@ -280,6 +290,7 @@ class AvatarsettingsAction extends AccountSettingsAction
} else if ($this->arg('delete')) {
$this->deleteAvatar();
} else {
+ // TRANS: Unexpected validation error on avatar upload form.
$this->showForm(_('Unexpected form submission.'));
}
Event::handle('EndAvatarSaveForm', array($this));
@@ -294,7 +305,6 @@ class AvatarsettingsAction extends AccountSettingsAction
*
* @return void
*/
-
function uploadAvatar()
{
try {
@@ -304,6 +314,7 @@ class AvatarsettingsAction extends AccountSettingsAction
return;
}
if ($imagefile === null) {
+ // TRANS: Validation error on avatar upload form when no file was uploaded.
$this->showForm(_('No file uploaded.'));
return;
}
@@ -331,6 +342,7 @@ class AvatarsettingsAction extends AccountSettingsAction
$this->mode = 'crop';
+ // TRANS: Avatar upload form unstruction after uploading a file.
$this->showForm(_('Pick a square area of the image to be your avatar'),
true);
}
@@ -340,12 +352,12 @@ class AvatarsettingsAction extends AccountSettingsAction
*
* @return void
*/
-
function cropAvatar()
{
$filedata = $_SESSION['FILEDATA'];
if (!$filedata) {
+ // TRANS: Server error displayed if an avatar upload went wrong somehow server side.
$this->serverError(_('Lost our file data.'));
return;
}
@@ -369,24 +381,25 @@ class AvatarsettingsAction extends AccountSettingsAction
@unlink($filedata['filepath']);
unset($_SESSION['FILEDATA']);
$this->mode = 'upload';
+ // TRANS: Success message for having updated a user avatar.
$this->showForm(_('Avatar updated.'), true);
common_broadcast_profile($profile);
} else {
+ // TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
$this->showForm(_('Failed updating avatar.'));
}
}
-
+
/**
* Get rid of the current avatar.
*
* @return void
*/
-
function deleteAvatar()
{
$user = common_current_user();
$profile = $user->getProfile();
-
+
$avatar = $profile->getOriginalAvatar();
if($avatar) $avatar->delete();
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
@@ -396,6 +409,7 @@ class AvatarsettingsAction extends AccountSettingsAction
$avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
if($avatar) $avatar->delete();
+ // TRANS: Success message for deleting a user avatar.
$this->showForm(_('Avatar deleted.'), true);
}
@@ -416,7 +430,6 @@ class AvatarsettingsAction extends AccountSettingsAction
*
* @return void
*/
-
function showScripts()
{
parent::showScripts();
diff --git a/actions/block.php b/actions/block.php
index 93f8ec937..e87353b4e 100644
--- a/actions/block.php
+++ b/actions/block.php
@@ -42,7 +42,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-
class BlockAction extends ProfileFormAction
{
var $profile = null;
@@ -54,7 +53,6 @@ class BlockAction extends ProfileFormAction
*
* @return boolean success flag
*/
-
function prepare($args)
{
if (!parent::prepare($args)) {
@@ -66,6 +64,7 @@ class BlockAction extends ProfileFormAction
assert(!empty($cur)); // checked by parent
if ($cur->hasBlocked($this->profile)) {
+ // TRANS: Client error displayed when blocking a user that has already been blocked.
$this->clientError(_('You already blocked that user.'));
return false;
}
@@ -82,7 +81,6 @@ class BlockAction extends ProfileFormAction
*
* @return void
*/
-
function handle($args)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
@@ -104,6 +102,7 @@ class BlockAction extends ProfileFormAction
}
function title() {
+ // TRANS: Title for block user page.
return _('Block user');
}
@@ -133,8 +132,10 @@ class BlockAction extends ProfileFormAction
'action' => common_local_url('block')));
$this->elementStart('fieldset');
$this->hidden('token', common_session_token());
+ // TRANS: Legend for block user form.
$this->element('legend', _('Block user'));
$this->element('p', null,
+ // TRANS: Explanation of consequences when blocking a user on the block user page.
_('Are you sure you want to block this user? '.
'Afterwards, they will be unsubscribed from you, '.
'unable to subscribe to you in the future, and '.
@@ -184,6 +185,7 @@ class BlockAction extends ProfileFormAction
}
if (!$result) {
+ // TRANS: Server error displayed when blocking a user fails.
$this->serverError(_('Failed to save block information.'));
return;
}
@@ -199,7 +201,7 @@ class BlockAction extends ProfileFormAction
* Override for form session token checks; on our first hit we're just
* requesting confirmation, which doesn't need a token. We need to be
* able to take regular GET requests from email!
- *
+ *
* @throws ClientException if token is bad on POST request or if we have
* confirmation parameters which could trigger something.
*/
@@ -216,7 +218,7 @@ class BlockAction extends ProfileFormAction
/**
* If we reached this form without returnto arguments, return to the
* current user's subscription list.
- *
+ *
* @return string URL
*/
function defaultReturnTo()
diff --git a/actions/blockedfromgroup.php b/actions/blockedfromgroup.php
index a0598db27..6ff572c05 100644
--- a/actions/blockedfromgroup.php
+++ b/actions/blockedfromgroup.php
@@ -40,7 +40,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class BlockedfromgroupAction extends GroupDesignAction
{
var $page = null;
@@ -70,6 +69,7 @@ class BlockedfromgroupAction extends GroupDesignAction
}
if (!$nickname) {
+ // TRANS: Client error displayed when requesting a list of blocked users for a group without providing a group nickname.
$this->clientError(_('No nickname.'), 404);
return false;
}
@@ -77,6 +77,7 @@ class BlockedfromgroupAction extends GroupDesignAction
$local = Local_group::staticGet('nickname', $nickname);
if (!$local) {
+ // TRANS: Client error displayed when requesting a list of blocked users for a non-local group.
$this->clientError(_('No such group.'), 404);
return false;
}
@@ -84,6 +85,7 @@ class BlockedfromgroupAction extends GroupDesignAction
$this->group = User_group::staticGet('id', $local->group_id);
if (!$this->group) {
+ // TRANS: Client error displayed when requesting a list of blocked users for a non-existing group.
$this->clientError(_('No such group.'), 404);
return false;
}
@@ -94,9 +96,13 @@ class BlockedfromgroupAction extends GroupDesignAction
function title()
{
if ($this->page == 1) {
+ // TRANS: Title for first page with list of users blocked from a group.
+ // TRANS: %s is a group nickname.
return sprintf(_('%s blocked profiles'),
$this->group->nickname);
} else {
+ // TRANS: Title for any but the first page with list of users blocked from a group.
+ // TRANS: %1$s is a group nickname, %2$d is a page number.
return sprintf(_('%1$s blocked profiles, page %2$d'),
$this->group->nickname,
$this->page);
@@ -112,6 +118,7 @@ class BlockedfromgroupAction extends GroupDesignAction
function showPageNotice()
{
$this->element('p', 'instructions',
+ // TRANS: Instructions for list of users blocked from a group.
_('A list of the users blocked from joining this group.'));
}
@@ -205,7 +212,6 @@ class GroupBlockListItem extends ProfileListItem
*
* @see UnblockForm
*/
-
class GroupUnblockForm extends Form
{
/**
@@ -234,7 +240,6 @@ class GroupUnblockForm extends Form
* @param User_group $group group to block user from
* @param array $args return-to args
*/
-
function __construct($out=null, $profile=null, $group=null, $args=null)
{
parent::__construct($out);
@@ -249,7 +254,6 @@ class GroupUnblockForm extends Form
*
* @return int ID of the form
*/
-
function id()
{
// This should be unique for the page.
@@ -261,7 +265,6 @@ class GroupUnblockForm extends Form
*
* @return string class of the form
*/
-
function formClass()
{
return 'form_group_unblock';
@@ -272,7 +275,6 @@ class GroupUnblockForm extends Form
*
* @return string URL of the action
*/
-
function action()
{
return common_local_url('groupunblock');
@@ -285,6 +287,7 @@ class GroupUnblockForm extends Form
*/
function formLegend()
{
+ // TRANS: Form legend for unblocking a user from a group.
$this->out->element('legend', null, _('Unblock user from group'));
}
@@ -293,7 +296,6 @@ class GroupUnblockForm extends Form
*
* @return void
*/
-
function formData()
{
$this->out->hidden('unblockto-' . $this->profile->id,
@@ -314,9 +316,14 @@ class GroupUnblockForm extends Form
*
* @return void
*/
-
function formActions()
{
- $this->out->submit('submit', _('Unblock'), 'submit', null, _('Unblock this user'));
+ $this->out->submit('submit',
+ // TRANS: Button text for unblocking a user from a group.
+ _m('BUTTON','Unblock'),
+ 'submit',
+ null,
+ // TRANS: Tooltip for button for unblocking a user from a group.
+ _('Unblock this user'));
}
}
diff --git a/actions/bookmarklet.php b/actions/bookmarklet.php
index 041c2e894..9cf4e58f8 100644
--- a/actions/bookmarklet.php
+++ b/actions/bookmarklet.php
@@ -34,7 +34,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
require_once INSTALLDIR . '/actions/newnotice.php';
/**
- * Action for posting a notice
+ * Action for posting a notice
*
* @category Bookmarklet
* @package StatusNet
@@ -42,12 +42,12 @@ require_once INSTALLDIR . '/actions/newnotice.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 BookmarkletAction extends NewnoticeAction
{
function showTitle()
{
// TRANS: Title for mini-posting window loaded from bookmarklet.
+ // TRANS: %s is the StatusNet site name.
$this->element('title', null, sprintf(_('Post to %s'), common_config('site', 'name')));
}
@@ -73,4 +73,3 @@ class BookmarkletAction extends NewnoticeAction
{
}
}
-
diff --git a/actions/confirmaddress.php b/actions/confirmaddress.php
index 8bf8c8c4d..5617c5339 100644
--- a/actions/confirmaddress.php
+++ b/actions/confirmaddress.php
@@ -44,7 +44,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class ConfirmaddressAction extends Action
{
/** type of confirmation. */
@@ -61,7 +60,6 @@ class ConfirmaddressAction extends Action
*
* @return void
*/
-
function handle($args)
{
parent::handle($args);
@@ -72,27 +70,30 @@ class ConfirmaddressAction extends Action
}
$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 an unknow address type, which can be 'email', 'jabber', or '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));
return;
}
if ($cur->$type == $confirm->address) {
- // TRANS: Client error for an already confirmed email/jabbel/sms address.
+ // TRANS: Client error for an already confirmed email/jabber/sms address.
$this->clientError(_('That address has already been confirmed.'));
return;
}
@@ -113,6 +114,7 @@ class ConfirmaddressAction extends Action
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;
}
@@ -125,7 +127,9 @@ class ConfirmaddressAction extends Action
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;
}
@@ -140,9 +144,9 @@ class ConfirmaddressAction extends Action
*
* @return string title
*/
-
function title()
{
+ // TRANS: Title for the contact address confirmation action.
return _('Confirm address');
}
@@ -151,13 +155,14 @@ class ConfirmaddressAction extends Action
*
* @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));
diff --git a/actions/conversation.php b/actions/conversation.php
index 900a724ef..8d11df37b 100644
--- a/actions/conversation.php
+++ b/actions/conversation.php
@@ -45,7 +45,6 @@ require_once INSTALLDIR.'/lib/noticelist.php';
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-
class ConversationAction extends Action
{
var $id = null;
@@ -58,7 +57,6 @@ class ConversationAction extends Action
*
* @return boolean false if id not passed in
*/
-
function prepare($args)
{
parent::prepare($args);
@@ -81,7 +79,6 @@ class ConversationAction extends Action
*
* @return void
*/
-
function handle($args)
{
parent::handle($args);
@@ -93,10 +90,10 @@ class ConversationAction extends Action
*
* @return string page title
*/
-
function title()
{
- return _("Conversation");
+ // TRANS: Title for page with a conversion (multiple notices in context).
+ return _('Conversation');
}
/**
@@ -107,7 +104,6 @@ class ConversationAction extends Action
*
* @return void
*/
-
function showContent()
{
$notices = Notice::conversationStream($this->id, null, null);
@@ -134,7 +130,6 @@ class ConversationAction extends Action
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-
class ConversationTree extends NoticeList
{
var $tree = null;
@@ -145,12 +140,12 @@ class ConversationTree extends NoticeList
*
* @return void
*/
-
function show()
{
$cnt = $this->_buildTree();
$this->out->elementStart('div', array('id' =>'notices_primary'));
+ // TRANS: Header on conversation page. Hidden by default (h2).
$this->out->element('h2', null, _('Notices'));
$this->out->elementStart('ol', array('class' => 'notices xoxo'));
@@ -200,7 +195,6 @@ class ConversationTree extends NoticeList
*
* @return void
*/
-
function showNoticePlus($id)
{
$notice = $this->table[$id];
@@ -237,7 +231,6 @@ class ConversationTree extends NoticeList
*
* @return NoticeListItem a list item to show
*/
-
function newListItem($notice)
{
return new ConversationTreeItem($notice, $this->out);
@@ -255,7 +248,6 @@ class ConversationTree extends NoticeList
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-
class ConversationTreeItem extends NoticeListItem
{
/**
@@ -266,7 +258,6 @@ class ConversationTreeItem extends NoticeListItem
*
* @return void
*/
-
function showStart()
{
return;
@@ -280,7 +271,6 @@ class ConversationTreeItem extends NoticeListItem
*
* @return void
*/
-
function showEnd()
{
return;
@@ -293,7 +283,6 @@ class ConversationTreeItem extends NoticeListItem
*
* @return void
*/
-
function showContext()
{
return;
diff --git a/actions/deleteapplication.php b/actions/deleteapplication.php
index 806de0be6..272a91762 100644
--- a/actions/deleteapplication.php
+++ b/actions/deleteapplication.php
@@ -40,7 +40,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-
class DeleteapplicationAction extends Action
{
var $app = null;
@@ -52,7 +51,6 @@ class DeleteapplicationAction extends Action
*
* @return boolean success flag
*/
-
function prepare($args)
{
if (!parent::prepare($args)) {
@@ -60,6 +58,7 @@ class DeleteapplicationAction extends Action
}
if (!common_logged_in()) {
+ // TRANS: Client error displayed trying to delete an application while not logged in.
$this->clientError(_('You must be logged in to delete an application.'));
return false;
}
@@ -68,6 +67,7 @@ class DeleteapplicationAction extends Action
$this->app = Oauth_application::staticGet('id', $id);
if (empty($this->app)) {
+ // TRANS: Client error displayed trying to delete an application that does not exist.
$this->clientError(_('Application not found.'));
return false;
}
@@ -75,6 +75,7 @@ class DeleteapplicationAction extends Action
$cur = common_current_user();
if ($cur->id != $this->app->owner) {
+ // TRANS: Client error displayed trying to delete an application the current user does not own.
$this->clientError(_('You are not the owner of this application.'), 401);
return false;
}
@@ -91,7 +92,6 @@ class DeleteapplicationAction extends Action
*
* @return void
*/
-
function handle($args)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
@@ -120,6 +120,7 @@ class DeleteapplicationAction extends Action
}
function title() {
+ // TRANS: Title for delete application page.
return _('Delete application');
}
@@ -144,8 +145,10 @@ class DeleteapplicationAction extends Action
array('id' => $this->app->id))));
$this->elementStart('fieldset');
$this->hidden('token', common_session_token());
+ // TRANS: Fieldset legend on delete application page.
$this->element('legend', _('Delete application'));
$this->element('p', null,
+ // TRANS: Confirmation text on delete application page.
_('Are you sure you want to delete this application? '.
'This will clear all data about the application from the '.
'database, including all existing user connections.'));
@@ -171,10 +174,8 @@ class DeleteapplicationAction extends Action
*
* @return void
*/
-
function handlePost()
{
$this->app->delete();
}
}
-
diff --git a/actions/deletegroup.php b/actions/deletegroup.php
index 62fff00c4..4e9b9851f 100644
--- a/actions/deletegroup.php
+++ b/actions/deletegroup.php
@@ -172,7 +172,7 @@ class DeletegroupAction extends RedirectingAction
}
function title() {
- // TRANS: Title.
+ // TRANS: Title of delete group page.
return _('Delete group');
}
@@ -201,8 +201,8 @@ class DeletegroupAction extends RedirectingAction
// TRANS: Form legend for deleting a group.
$this->element('legend', _('Delete group'));
if (Event::handle('StartDeleteGroupForm', array($this, $this->group))) {
- // TRANS: Warning in form for deleleting a group.
$this->element('p', null,
+ // TRANS: Warning in form for deleleting a group.
_('Are you sure you want to delete this group? '.
'This will clear all data about the group from the '.
'database, without a backup. ' .
diff --git a/actions/deletenotice.php b/actions/deletenotice.php
index 2879faa5d..a7ac28e19 100644
--- a/actions/deletenotice.php
+++ b/actions/deletenotice.php
@@ -32,6 +32,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
+// @todo FIXME: documentation needed.
class DeletenoticeAction extends Action
{
var $error = null;
@@ -47,6 +48,7 @@ class DeletenoticeAction extends Action
$this->user = common_current_user();
if (!$this->user) {
+ // TRANS: Error message displayed trying to delete a notice while not logged in.
common_user_error(_('Not logged in.'));
exit;
}
@@ -55,6 +57,7 @@ class DeletenoticeAction extends Action
$this->notice = Notice::staticGet($notice_id);
if (!$this->notice) {
+ // TRANS: Error message displayed trying to delete a non-existing notice.
common_user_error(_('No such notice.'));
exit;
}
@@ -71,6 +74,7 @@ class DeletenoticeAction extends Action
if ($this->notice->profile_id != $this->user_profile->id &&
!$this->user->hasRight(Right::DELETEOTHERSNOTICE)) {
+ // TRANS: Error message displayed trying to delete a notice that was not made by the current user.
common_user_error(_('Can\'t delete this notice.'));
exit;
}
@@ -90,7 +94,6 @@ class DeletenoticeAction extends Action
*
* @return void
*/
-
function showPageNotice()
{
$instr = $this->getInstructions();
@@ -103,12 +106,14 @@ class DeletenoticeAction extends Action
function getInstructions()
{
+ // TRANS: Instructions for deleting a notice.
return _('You are about to permanently delete a notice. ' .
'Once this is done, it cannot be undone.');
}
function title()
{
+ // TRANS: Page title when deleting a notice.
return _('Delete notice');
}
@@ -121,7 +126,6 @@ class DeletenoticeAction extends Action
*
* @return void
*/
-
function showForm($error = null)
{
$this->error = $error;
@@ -133,7 +137,6 @@ class DeletenoticeAction extends Action
*
* @return void
*/
-
function showContent()
{
$this->elementStart('form', array('id' => 'form_notice_delete',
@@ -141,9 +144,11 @@ class DeletenoticeAction extends Action
'method' => 'post',
'action' => common_local_url('deletenotice')));
$this->elementStart('fieldset');
+ // TRANS: Fieldset legend for the delete notice form.
$this->element('legend', null, _('Delete notice'));
$this->hidden('token', common_session_token());
$this->hidden('notice', $this->trimmed('notice'));
+ // TRANS: Message for the delete notice form.
$this->element('p', null, _('Are you sure you want to delete this notice?'));
$this->submit('form_action-no',
// TRANS: Button label on the delete notice form.
diff --git a/actions/editapplication.php b/actions/editapplication.php
index d1c7a5c3d..760b1d284 100644
--- a/actions/editapplication.php
+++ b/actions/editapplication.php
@@ -185,7 +185,7 @@ class EditApplicationAction extends OwnerDesignAction
return;
} elseif (mb_strlen($name) > 255) {
// TRANS: Validation error shown when providing too long a name in the "Edit application" form.
- $this->showForm(_('Name is too long (max 255 characters).'));
+ $this->showForm(_('Name is too long (maximum 255 characters).'));
return;
} else if ($this->nameExists($name)) {
// TRANS: Validation error shown when providing a name for an application that already exists in the "Edit application" form.
diff --git a/actions/editgroup.php b/actions/editgroup.php
index eaadbabe4..ab4dbb283 100644
--- a/actions/editgroup.php
+++ b/actions/editgroup.php
@@ -45,14 +45,13 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class EditgroupAction extends GroupDesignAction
{
-
var $msg;
function title()
{
+ // TRANS: Title for form to edit a group. %s is a group nickname.
return sprintf(_('Edit %s group'), $this->group->nickname);
}
@@ -65,6 +64,7 @@ class EditgroupAction extends GroupDesignAction
parent::prepare($args);
if (!common_logged_in()) {
+ // TRANS: Client error displayed trying to edit a group while not logged in.
$this->clientError(_('You must be logged in to create a group.'));
return false;
}
@@ -81,6 +81,7 @@ class EditgroupAction extends GroupDesignAction
}
if (!$nickname) {
+ // TRANS: Client error displayed trying to edit a group while not proving a nickname for the group to edit.
$this->clientError(_('No nickname.'), 404);
return false;
}
@@ -97,6 +98,7 @@ class EditgroupAction extends GroupDesignAction
}
if (!$this->group) {
+ // TRANS: Client error displayed trying to edit a non-existing group.
$this->clientError(_('No such group.'), 404);
return false;
}
@@ -104,6 +106,7 @@ class EditgroupAction extends GroupDesignAction
$cur = common_current_user();
if (!$cur->isAdmin($this->group)) {
+ // TRANS: Client error displayed trying to edit a group while not being a group admin.
$this->clientError(_('You must be an admin to edit the group.'), 403);
return false;
}
@@ -120,7 +123,6 @@ class EditgroupAction extends GroupDesignAction
*
* @return void
*/
-
function handle($args)
{
parent::handle($args);
@@ -155,6 +157,7 @@ class EditgroupAction extends GroupDesignAction
$this->element('p', 'error', $this->msg);
} else {
$this->element('p', 'instructions',
+ // TRANS: Form instructions for group edit form.
_('Use this form to edit the group.'));
}
}
@@ -169,42 +172,47 @@ class EditgroupAction extends GroupDesignAction
{
$cur = common_current_user();
if (!$cur->isAdmin($this->group)) {
+ // TRANS: Client error displayed trying to edit a group while not being a group admin.
$this->clientError(_('You must be an admin to edit the group.'), 403);
return;
}
- $nickname = common_canonical_nickname($this->trimmed('nickname'));
+ $nickname = Nickname::normalize($this->trimmed('nickname'));
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$description = $this->trimmed('description');
$location = $this->trimmed('location');
$aliasstring = $this->trimmed('aliases');
- if (!Validate::string($nickname, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
- $this->showForm(_('Nickname must have only lowercase letters '.
- 'and numbers and no spaces.'));
- return;
- } else if ($this->nicknameExists($nickname)) {
+ if ($this->nicknameExists($nickname)) {
+ // TRANS: Group edit form validation error.
$this->showForm(_('Nickname already in use. Try another one.'));
return;
} else if (!User_group::allowedNickname($nickname)) {
+ // TRANS: Group edit form validation error.
$this->showForm(_('Not a valid nickname.'));
return;
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage,
array('allowed_schemes' =>
array('http', 'https')))) {
+ // TRANS: Group edit form validation error.
$this->showForm(_('Homepage is not a valid URL.'));
return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
+ // TRANS: Group edit form validation error.
$this->showForm(_('Full name is too long (maximum 255 characters).'));
return;
} else if (User_group::descriptionTooLong($description)) {
- $this->showForm(sprintf(_('Description is too long (max %d chars).'), User_group::maxDescription()));
+ $this->showForm(sprintf(
+ // TRANS: Group edit form validation error.
+ _m('Description is too long (maximum %d character).',
+ 'Description is too long (maximum %d characters).',
+ User_group::maxDescription()),
+ User_group::maxDescription()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
+ // TRANS: Group edit form validation error.
$this->showForm(_('Location is too long (maximum 255 characters).'));
return;
}
@@ -216,25 +224,30 @@ class EditgroupAction extends GroupDesignAction
}
if (count($aliases) > common_config('group', 'maxaliases')) {
- $this->showForm(sprintf(_('Too many aliases! Maximum %d.'),
+ // TRANS: Group edit form validation error.
+ // TRANS: %d is the maximum number of allowed aliases.
+ $this->showForm(sprintf(_m('Too many aliases! Maximum %d allowed.',
+ 'Too many aliases! Maximum %d allowed.',
+ common_config('group', 'maxaliases')),
common_config('group', 'maxaliases')));
return;
}
foreach ($aliases as $alias) {
- if (!Validate::string($alias, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
+ if (!Nickname::isValid($alias)) {
+ // TRANS: Group edit form validation error.
$this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
return;
}
if ($this->nicknameExists($alias)) {
+ // TRANS: Group edit form validation error.
$this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
$alias));
return;
}
// XXX assumes alphanum nicknames
if (strcmp($alias, $nickname) == 0) {
+ // TRANS: Group edit form validation error.
$this->showForm(_('Alias can\'t be the same as nickname.'));
return;
}
@@ -255,12 +268,14 @@ class EditgroupAction extends GroupDesignAction
if (!$result) {
common_log_db_error($this->group, 'UPDATE', __FILE__);
+ // TRANS: Server error displayed when editing a group fails.
$this->serverError(_('Could not update group.'));
}
$result = $this->group->setAliases($aliases);
if (!$result) {
+ // TRANS: Server error displayed when group aliases could not be added.
$this->serverError(_('Could not create aliases.'));
}
@@ -277,6 +292,7 @@ class EditgroupAction extends GroupDesignAction
array('nickname' => $nickname)),
303);
} else {
+ // TRANS: Group edit form success message.
$this->showForm(_('Options saved.'));
}
}
@@ -300,4 +316,3 @@ class EditgroupAction extends GroupDesignAction
return false;
}
}
-
diff --git a/actions/emailsettings.php b/actions/emailsettings.php
index 9c250fc8a..4a7dc1b87 100644
--- a/actions/emailsettings.php
+++ b/actions/emailsettings.php
@@ -79,6 +79,7 @@ class EmailsettingsAction extends AccountSettingsAction
function showScripts()
{
parent::showScripts();
+ $this->script('emailsettings.js');
$this->autofocus('email');
}
@@ -149,6 +150,26 @@ class EmailsettingsAction extends AccountSettingsAction
$this->elementStart('fieldset', array('id' => 'settings_email_incoming'));
// TRANS: Form legend for incoming e-mail settings form.
$this->element('legend', null, _('Incoming email'));
+
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+ $this->checkbox('emailpost',
+ // TRANS: Checkbox label in e-mail preferences form.
+ _('I want to post notices by email.'),
+ $user->emailpost);
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+
+ // Our stylesheets make the form_data list items all floats, which
+ // creates lots of problems with trying to wrap divs around things.
+ // This should force a break before the next section, which needs
+ // to be separate so we can disable the things in it when the
+ // checkbox is off.
+ $this->elementStart('div', array('style' => 'clear: both'));
+ $this->elementEnd('div');
+
+ $this->elementStart('div', array('id' => 'emailincoming'));
+
if ($user->incomingemail) {
$this->elementStart('p');
$this->element('span', 'address', $user->incomingemail);
@@ -163,13 +184,22 @@ class EmailsettingsAction extends AccountSettingsAction
}
$this->elementStart('p');
- $this->element('span', 'input_instructions',
- // TRANS: Instructions for incoming e-mail address input form.
- _('Make a new email address for posting to; '.
- 'cancels the old one.'));
+ if ($user->incomingemail) {
+ // TRANS: Instructions for incoming e-mail address input form, when an address has already been assigned.
+ $msg = _('Make a new email address for posting to; '.
+ 'cancels the old one.');
+ } else {
+ // TRANS: Instructions for incoming e-mail address input form.
+ $msg = _('To send notices via email, we need to create a unique email address for you on this server:');
+ }
+ $this->element('span', 'input_instructions', $msg);
$this->elementEnd('p');
+
// TRANS: Button label for adding an e-mail address to send notices from.
$this->submit('newincoming', _m('BUTTON','New'));
+
+ $this->elementEnd('div'); // div#emailincoming
+
$this->elementEnd('fieldset');
}
@@ -178,51 +208,47 @@ class EmailsettingsAction extends AccountSettingsAction
$this->element('legend', null, _('Email preferences'));
$this->elementStart('ul', 'form_data');
- $this->elementStart('li');
- $this->checkbox('emailnotifysub',
- // TRANS: Checkbox label in e-mail preferences form.
- _('Send me notices of new subscriptions through email.'),
- $user->emailnotifysub);
- $this->elementEnd('li');
- $this->elementStart('li');
- $this->checkbox('emailnotifyfav',
- // TRANS: Checkbox label in e-mail preferences form.
- _('Send me email when someone '.
- 'adds my notice as a favorite.'),
- $user->emailnotifyfav);
- $this->elementEnd('li');
- $this->elementStart('li');
- $this->checkbox('emailnotifymsg',
- // TRANS: Checkbox label in e-mail preferences form.
- _('Send me email when someone sends me a private message.'),
- $user->emailnotifymsg);
- $this->elementEnd('li');
- $this->elementStart('li');
- $this->checkbox('emailnotifyattn',
- // TRANS: Checkbox label in e-mail preferences form.
- _('Send me email when someone sends me an "@-reply".'),
- $user->emailnotifyattn);
- $this->elementEnd('li');
- $this->elementStart('li');
- $this->checkbox('emailnotifynudge',
- // TRANS: Checkbox label in e-mail preferences form.
- _('Allow friends to nudge me and send me an email.'),
- $user->emailnotifynudge);
- $this->elementEnd('li');
- if (common_config('emailpost', 'enabled')) {
- $this->elementStart('li');
- $this->checkbox('emailpost',
- // TRANS: Checkbox label in e-mail preferences form.
- _('I want to post notices by email.'),
- $user->emailpost);
- $this->elementEnd('li');
- }
- $this->elementStart('li');
- $this->checkbox('emailmicroid',
- // TRANS: Checkbox label in e-mail preferences form.
- _('Publish a MicroID for my email address.'),
- $user->emailmicroid);
- $this->elementEnd('li');
+
+ if (Event::handle('StartEmailFormData', array($this))) {
+ $this->elementStart('li');
+ $this->checkbox('emailnotifysub',
+ // TRANS: Checkbox label in e-mail preferences form.
+ _('Send me notices of new subscriptions through email.'),
+ $user->emailnotifysub);
+ $this->elementEnd('li');
+ $this->elementStart('li');
+ $this->checkbox('emailnotifyfav',
+ // TRANS: Checkbox label in e-mail preferences form.
+ _('Send me email when someone '.
+ 'adds my notice as a favorite.'),
+ $user->emailnotifyfav);
+ $this->elementEnd('li');
+ $this->elementStart('li');
+ $this->checkbox('emailnotifymsg',
+ // TRANS: Checkbox label in e-mail preferences form.
+ _('Send me email when someone sends me a private message.'),
+ $user->emailnotifymsg);
+ $this->elementEnd('li');
+ $this->elementStart('li');
+ $this->checkbox('emailnotifyattn',
+ // TRANS: Checkbox label in e-mail preferences form.
+ _('Send me email when someone sends me an "@-reply".'),
+ $user->emailnotifyattn);
+ $this->elementEnd('li');
+ $this->elementStart('li');
+ $this->checkbox('emailnotifynudge',
+ // TRANS: Checkbox label in e-mail preferences form.
+ _('Allow friends to nudge me and send me an email.'),
+ $user->emailnotifynudge);
+ $this->elementEnd('li');
+ $this->elementStart('li');
+ $this->checkbox('emailmicroid',
+ // TRANS: Checkbox label in e-mail preferences form.
+ _('Publish a MicroID for my email address.'),
+ $user->emailmicroid);
+ $this->elementEnd('li');
+ Event::handle('EndEmailFormData', array($this));
+ }
$this->elementEnd('ul');
// TRANS: Button label to save e-mail preferences.
$this->submit('save', _m('BUTTON','Save'));
@@ -299,43 +325,48 @@ class EmailsettingsAction extends AccountSettingsAction
function savePreferences()
{
- $emailnotifysub = $this->boolean('emailnotifysub');
- $emailnotifyfav = $this->boolean('emailnotifyfav');
- $emailnotifymsg = $this->boolean('emailnotifymsg');
- $emailnotifynudge = $this->boolean('emailnotifynudge');
- $emailnotifyattn = $this->boolean('emailnotifyattn');
- $emailmicroid = $this->boolean('emailmicroid');
- $emailpost = $this->boolean('emailpost');
-
- $user = common_current_user();
-
- assert(!is_null($user)); // should already be checked
-
- $user->query('BEGIN');
-
- $original = clone($user);
-
- $user->emailnotifysub = $emailnotifysub;
- $user->emailnotifyfav = $emailnotifyfav;
- $user->emailnotifymsg = $emailnotifymsg;
- $user->emailnotifynudge = $emailnotifynudge;
- $user->emailnotifyattn = $emailnotifyattn;
- $user->emailmicroid = $emailmicroid;
- $user->emailpost = $emailpost;
-
- $result = $user->update($original);
-
- if ($result === false) {
- common_log_db_error($user, 'UPDATE', __FILE__);
- // TRANS: Server error thrown on database error updating e-mail preferences.
- $this->serverError(_('Couldn\'t update user.'));
- return;
- }
-
- $user->query('COMMIT');
-
- // TRANS: Confirmation message for successful e-mail preferences save.
- $this->showForm(_('Email preferences saved.'), true);
+ $user = common_current_user();
+
+ if (Event::handle('StartEmailSaveForm', array($this, &$user))) {
+
+ $emailnotifysub = $this->boolean('emailnotifysub');
+ $emailnotifyfav = $this->boolean('emailnotifyfav');
+ $emailnotifymsg = $this->boolean('emailnotifymsg');
+ $emailnotifynudge = $this->boolean('emailnotifynudge');
+ $emailnotifyattn = $this->boolean('emailnotifyattn');
+ $emailmicroid = $this->boolean('emailmicroid');
+ $emailpost = $this->boolean('emailpost');
+
+ assert(!is_null($user)); // should already be checked
+
+ $user->query('BEGIN');
+
+ $original = clone($user);
+
+ $user->emailnotifysub = $emailnotifysub;
+ $user->emailnotifyfav = $emailnotifyfav;
+ $user->emailnotifymsg = $emailnotifymsg;
+ $user->emailnotifynudge = $emailnotifynudge;
+ $user->emailnotifyattn = $emailnotifyattn;
+ $user->emailmicroid = $emailmicroid;
+ $user->emailpost = $emailpost;
+
+ $result = $user->update($original);
+
+ if ($result === false) {
+ common_log_db_error($user, 'UPDATE', __FILE__);
+ // TRANS: Server error thrown on database error updating e-mail preferences.
+ $this->serverError(_('Couldn\'t update user.'));
+ return;
+ }
+
+ $user->query('COMMIT');
+
+ Event::handle('EndEmailSaveForm', array($this));
+
+ // TRANS: Confirmation message for successful e-mail preferences save.
+ $this->showForm(_('Email preferences saved.'), true);
+ }
}
/**
@@ -501,6 +532,7 @@ class EmailsettingsAction extends AccountSettingsAction
$orig = clone($user);
$user->incomingemail = null;
+ $user->emailpost = 0;
if (!$user->updateKeys($orig)) {
common_log_db_error($user, 'UPDATE', __FILE__);
@@ -525,6 +557,7 @@ class EmailsettingsAction extends AccountSettingsAction
$orig = clone($user);
$user->incomingemail = mail_new_incoming_address();
+ $user->emailpost = 1;
if (!$user->updateKeys($orig)) {
common_log_db_error($user, 'UPDATE', __FILE__);
diff --git a/actions/groupunblock.php b/actions/groupunblock.php
index dd6b15c26..ef2380725 100644
--- a/actions/groupunblock.php
+++ b/actions/groupunblock.php
@@ -32,7 +32,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
}
/**
- * Unlock a user from a group
+ * Unblock a user from a group
*
* @category Action
* @package StatusNet
@@ -40,7 +40,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
-
class GroupunblockAction extends Action
{
var $profile = null;
@@ -53,11 +52,11 @@ class GroupunblockAction extends Action
*
* @return boolean success flag
*/
-
function prepare($args)
{
parent::prepare($args);
if (!common_logged_in()) {
+ // TRANS: Client error displayed when trying to unblock a user from a group while not logged in.
$this->clientError(_('Not logged in.'));
return false;
}
@@ -68,11 +67,13 @@ class GroupunblockAction extends Action
}
$id = $this->trimmed('unblockto');
if (empty($id)) {
+ // TRANS: Client error displayed when trying to unblock a user from a group without providing a profile.
$this->clientError(_('No profile specified.'));
return false;
}
$this->profile = Profile::staticGet('id', $id);
if (empty($this->profile)) {
+ // TRANS: Client error displayed when trying to unblock a user from a group without providing an existing profile.
$this->clientError(_('No profile with that ID.'));
return false;
}
@@ -83,15 +84,18 @@ class GroupunblockAction extends Action
}
$this->group = User_group::staticGet('id', $group_id);
if (empty($this->group)) {
+ // TRANS: Client error displayed when trying to unblock a user from a non-existing group.
$this->clientError(_('No such group.'));
return false;
}
$user = common_current_user();
if (!$user->isAdmin($this->group)) {
+ // TRANS: Client error displayed when trying to unblock a user from a group without being an administrator for the group.
$this->clientError(_('Only an admin can unblock group members.'), 401);
return false;
}
if (!Group_block::isBlocked($this->group, $this->profile)) {
+ // TRANS: Client error displayed when trying to unblock a non-blocked user from a group.
$this->clientError(_('User is not blocked from group.'));
return false;
}
@@ -105,7 +109,6 @@ class GroupunblockAction extends Action
*
* @return void
*/
-
function handle($args)
{
parent::handle($args);
@@ -119,12 +122,12 @@ class GroupunblockAction extends Action
*
* @return void
*/
-
function unblockProfile()
{
$result = Group_block::unblockProfile($this->group, $this->profile);
if (!$result) {
+ // TRANS: Server error displayed when unblocking a user from a group fails because of an unknown error.
$this->serverError(_('Error removing the block.'));
return;
}
@@ -146,4 +149,3 @@ class GroupunblockAction extends Action
}
}
}
-
diff --git a/actions/hostmeta.php b/actions/hostmeta.php
new file mode 100644
index 000000000..2587bb6b9
--- /dev/null
+++ b/actions/hostmeta.php
@@ -0,0 +1,65 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @category Action
+ * @package StatusNet
+ * @maintainer James Walker <james@status.net>
+ * @author Craig Andrews <candrews@integralblue.com>
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+class HostMetaAction extends Action
+{
+
+ /**
+ * Is read only?
+ *
+ * @return boolean true
+ */
+ function isReadOnly()
+ {
+ return true;
+ }
+
+ function handle()
+ {
+ parent::handle();
+
+ $domain = common_config('site', 'server');
+
+ $xrd = new XRD();
+ $xrd->host = $domain;
+
+ if(Event::handle('StartHostMetaLinks', array(&$xrd->links))) {
+ $url = common_local_url('userxrd');
+ $url.= '?uri={uri}';
+ $xrd->links[] = array('rel' => Discovery::LRDD_REL,
+ 'template' => $url,
+ 'title' => array('Resource Descriptor'));
+ Event::handle('EndHostMetaLinks', array(&$xrd->links));
+ }
+
+ header('Content-type: application/xrd+xml');
+ print $xrd->toXML();
+ }
+}
diff --git a/actions/invite.php b/actions/invite.php
index 2779437e0..e9adb3b7f 100644
--- a/actions/invite.php
+++ b/actions/invite.php
@@ -142,7 +142,7 @@ class InviteAction extends CurrentUserDesignAction
$this->elementStart('ul');
foreach ($this->already as $other) {
// TRANS: Used as list item for already subscribed users (%1$s is nickname, %2$s is e-mail address).
- $this->element('li', null, sprintf(_('%1$s (%2$s)'), $other->nickname, $other->email));
+ $this->element('li', null, sprintf(_m('INVITE','%1$s (%2$s)'), $other->nickname, $other->email));
}
$this->elementEnd('ul');
}
@@ -156,7 +156,7 @@ class InviteAction extends CurrentUserDesignAction
$this->elementStart('ul');
foreach ($this->subbed as $other) {
// TRANS: Used as list item for already registered people (%1$s is nickname, %2$s is e-mail address).
- $this->element('li', null, sprintf(_('%1$s (%2$s)'), $other->nickname, $other->email));
+ $this->element('li', null, sprintf(_m('INVITE','%1$s (%2$s)'), $other->nickname, $other->email));
}
$this->elementEnd('ul');
}
diff --git a/actions/licenseadminpanel.php b/actions/licenseadminpanel.php
index 9165ca19d..95ac48cc8 100644
--- a/actions/licenseadminpanel.php
+++ b/actions/licenseadminpanel.php
@@ -153,7 +153,7 @@ class LicenseadminpanelAction extends AdminPanelAction
// Make sure the license title is not too long
if (mb_strlen($values['license']['type']) > 255) {
$this->clientError(
- _("Invalid license title. Max length is 255 characters.")
+ _('Invalid license title. Maximum length is 255 characters.')
);
}
diff --git a/actions/newapplication.php b/actions/newapplication.php
index 033c0852d..ae1754558 100644
--- a/actions/newapplication.php
+++ b/actions/newapplication.php
@@ -166,7 +166,7 @@ class NewApplicationAction extends OwnerDesignAction
$this->showForm(_('Name already in use. Try another one.'));
return;
} elseif (mb_strlen($name) > 255) {
- $this->showForm(_('Name is too long (maximum 255 chars).'));
+ $this->showForm(_('Name is too long (maximum 255 characters).'));
return;
} elseif (empty($description)) {
$this->showForm(_('Description is required.'));
@@ -196,7 +196,7 @@ class NewApplicationAction extends OwnerDesignAction
$this->showForm(_('Organization is required.'));
return;
} elseif (mb_strlen($organization) > 255) {
- $this->showForm(_('Organization is too long (maximum 255 chars).'));
+ $this->showForm(_('Organization is too long (maximum 255 characters).'));
return;
} elseif (empty($homepage)) {
$this->showForm(_('Organization homepage is required.'));
diff --git a/actions/newgroup.php b/actions/newgroup.php
index 05520223c..95af6415e 100644
--- a/actions/newgroup.php
+++ b/actions/newgroup.php
@@ -43,25 +43,25 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class NewgroupAction extends Action
{
var $msg;
function title()
{
+ // TRANS: Title for form to create a group.
return _('New group');
}
/**
* Prepare to run
*/
-
function prepare($args)
{
parent::prepare($args);
if (!common_logged_in()) {
+ // TRANS: Client error displayed trying to create a group while not logged in.
$this->clientError(_('You must be logged in to create a group.'));
return false;
}
@@ -78,7 +78,6 @@ class NewgroupAction extends Action
*
* @return void
*/
-
function handle($args)
{
parent::handle($args);
@@ -107,42 +106,45 @@ class NewgroupAction extends Action
$this->element('p', 'error', $this->msg);
} else {
$this->element('p', 'instructions',
+ // TRANS: Form instructions for group create form.
_('Use this form to create a new group.'));
}
}
function trySave()
{
- $nickname = $this->trimmed('nickname');
+ try {
+ $nickname = Nickname::normalize($this->trimmed('nickname'));
+ } catch (NicknameException $e) {
+ $this->showForm($e->getMessage());
+ }
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$description = $this->trimmed('description');
$location = $this->trimmed('location');
$aliasstring = $this->trimmed('aliases');
- if (!Validate::string($nickname, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
- $this->showForm(_('Nickname must have only lowercase letters '.
- 'and numbers and no spaces.'));
- return;
- } else if ($this->nicknameExists($nickname)) {
+ if ($this->nicknameExists($nickname)) {
+ // TRANS: Group create form validation error.
$this->showForm(_('Nickname already in use. Try another one.'));
return;
} else if (!User_group::allowedNickname($nickname)) {
+ // TRANS: Group create form validation error.
$this->showForm(_('Not a valid nickname.'));
return;
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!Validate::uri($homepage,
array('allowed_schemes' =>
array('http', 'https')))) {
+ // TRANS: Group create form validation error.
$this->showForm(_('Homepage is not a valid URL.'));
return;
} else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
+ // TRANS: Group create form validation error.
$this->showForm(_('Full name is too long (maximum 255 characters).'));
return;
} else if (User_group::descriptionTooLong($description)) {
- // TRANS: Form validation error creating a new group because the description is too long.
+ // TRANS: Group create form validation error.
// TRANS: %d is the maximum number of allowed characters.
$this->showForm(sprintf(_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
@@ -150,6 +152,7 @@ class NewgroupAction extends Action
User_group::maxDescription()));
return;
} else if (!is_null($location) && mb_strlen($location) > 255) {
+ // TRANS: Group create form validation error.
$this->showForm(_('Location is too long (maximum 255 characters).'));
return;
}
@@ -161,7 +164,7 @@ class NewgroupAction extends Action
}
if (count($aliases) > common_config('group', 'maxaliases')) {
- // TRANS: Client error shown when providing too many aliases during group creation.
+ // TRANS: Group create form validation error.
// TRANS: %d is the maximum number of allowed aliases.
$this->showForm(sprintf(_m('Too many aliases! Maximum %d allowed.',
'Too many aliases! Maximum %d allowed.',
@@ -171,19 +174,20 @@ class NewgroupAction extends Action
}
foreach ($aliases as $alias) {
- if (!Validate::string($alias, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
+ if (!Nickname::isValid($alias)) {
+ // TRANS: Group create form validation error.
$this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
return;
}
if ($this->nicknameExists($alias)) {
+ // TRANS: Group create form validation error.
$this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
$alias));
return;
}
// XXX assumes alphanum nicknames
if (strcmp($alias, $nickname) == 0) {
+ // TRANS: Group create form validation error.
$this->showForm(_('Alias can\'t be the same as nickname.'));
return;
}
@@ -227,4 +231,3 @@ class NewgroupAction extends Action
return false;
}
}
-
diff --git a/actions/newmessage.php b/actions/newmessage.php
index c58ed3849..447a00580 100644
--- a/actions/newmessage.php
+++ b/actions/newmessage.php
@@ -144,7 +144,7 @@ class NewmessageAction extends Action
$this->showForm(_('No content!'));
return;
} else {
- $content_shortened = common_shorten_links($this->content);
+ $content_shortened = $user->shortenLinks($this->content);
if (Message::contentTooLong($content_shortened)) {
// TRANS: Form validation error displayed when message content is too long.
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 57cd847c6..faafd9551 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -154,10 +154,13 @@ class NewnoticeAction extends Action
return;
}
- $content_shortened = common_shorten_links($content);
+ $content_shortened = $user->shortenLinks($content);
if (Notice::contentTooLong($content_shortened)) {
- $this->clientError(sprintf(_('That\'s too long. '.
- 'Max notice size is %d chars.'),
+ // TRANS: Client error displayed when the parameter "status" is missing.
+ // TRANS: %d is the maximum number of character for a notice.
+ $this->clientError(sprintf(_m('That\'s too long. Maximum notice size is %d character.',
+ 'That\'s too long. Maximum notice size is %d characters.',
+ Notice::maxContent()),
Notice::maxContent()));
}
@@ -178,12 +181,10 @@ class NewnoticeAction extends Action
if (Notice::contentTooLong($content_shortened)) {
$upload->delete();
- $this->clientError(
- sprintf(
- _('Max notice size is %d chars, including attachment URL.'),
- Notice::maxContent()
- )
- );
+ $this->clientError(sprintf(_m('Maximum notice size is %d character, including attachment URL.',
+ 'Maximum notice size is %d characters, including attachment URL.',
+ Notice::maxContent()),
+ Notice::maxContent()));
}
}
diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php
index 9a7cda924..cdb73203f 100644
--- a/actions/oauthconnectionssettings.php
+++ b/actions/oauthconnectionssettings.php
@@ -97,7 +97,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
$offset = ($this->page - 1) * APPS_PER_PAGE;
$limit = APPS_PER_PAGE + 1;
- $connection = $profile->getConnectedApps($offset, $limit);
+ $connection = $user->getConnectedApps($offset, $limit);
$cnt = 0;
diff --git a/actions/oembed.php b/actions/oembed.php
index e25e4cb25..09d68a446 100644
--- a/actions/oembed.php
+++ b/actions/oembed.php
@@ -79,11 +79,7 @@ class OembedAction extends Action
if (empty($profile)) {
$this->serverError(_('Notice has no profile.'), 500);
}
- if (!empty($profile->fullname)) {
- $authorname = $profile->fullname . ' (' . $profile->nickname . ')';
- } else {
- $authorname = $profile->nickname;
- }
+ $authorname = $profile->getFancyName();
$oembed['title'] = sprintf(_('%1$s\'s status on %2$s'),
$authorname,
common_exact_date($notice->created));
@@ -112,10 +108,23 @@ class OembedAction extends Action
$oembed['url']=$file_oembed->url;
}else if(substr($attachment->mimetype,0,strlen('image/'))=='image/'){
$oembed['type']='photo';
- //TODO set width and height
- //$oembed['width']=
- //$oembed['height']=
+ if ($attachment->filename) {
+ $filepath = File::path($attachment->filename);
+ $gis = @getimagesize($filepath);
+ if ($gis) {
+ $oembed['width'] = $gis[0];
+ $oembed['height'] = $gis[1];
+ } else {
+ // TODO Either throw an error or find a fallback?
+ }
+ }
$oembed['url']=$attachment->url;
+ $thumb = $attachment->getThumbnail();
+ if ($thumb) {
+ $oembed['thumbnail_url'] = $thumb->url;
+ $oembed['thumbnail_width'] = $thumb->width;
+ $oembed['thumbnail_height'] = $thumb->height;
+ }
}else{
$oembed['type']='link';
$oembed['url']=common_local_url('attachment',
diff --git a/actions/othersettings.php b/actions/othersettings.php
index 10e9873b3..13460a4bf 100644
--- a/actions/othersettings.php
+++ b/actions/othersettings.php
@@ -46,7 +46,6 @@ require_once INSTALLDIR.'/lib/accountsettingsaction.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 OthersettingsAction extends AccountSettingsAction
{
/**
@@ -54,9 +53,9 @@ class OthersettingsAction extends AccountSettingsAction
*
* @return string Title of the page
*/
-
function title()
{
+ // Page title for a tab in user profile settings.
return _('Other settings');
}
@@ -68,6 +67,7 @@ class OthersettingsAction extends AccountSettingsAction
function getInstructions()
{
+ // TRANS: Instructions for tab "Other" in user profile settings.
return _('Manage various other options.');
}
@@ -105,6 +105,9 @@ class OthersettingsAction extends AccountSettingsAction
{
$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)');
}
}
@@ -113,17 +116,22 @@ class OthersettingsAction extends AccountSettingsAction
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');
+ // TRANS: Label for checkbox.
$this->checkbox('viewdesigns', _('View profile designs'),
+ // TRANS: Tooltip for checkbox.
$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');
}
@@ -150,7 +158,8 @@ class OthersettingsAction extends AccountSettingsAction
$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;
}
@@ -171,6 +180,7 @@ class OthersettingsAction extends AccountSettingsAction
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;
}
diff --git a/actions/profilesettings.php b/actions/profilesettings.php
index e1a0f8b6d..28b1d20f3 100644
--- a/actions/profilesettings.php
+++ b/actions/profilesettings.php
@@ -225,7 +225,13 @@ class ProfilesettingsAction extends AccountSettingsAction
if (Event::handle('StartProfileSaveForm', array($this))) {
- $nickname = $this->trimmed('nickname');
+ try {
+ $nickname = Nickname::normalize($this->trimmed('nickname'));
+ } catch (NicknameException $e) {
+ $this->showForm($e->getMessage());
+ return;
+ }
+
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$bio = $this->trimmed('bio');
@@ -236,13 +242,7 @@ class ProfilesettingsAction extends AccountSettingsAction
$tagstring = $this->trimmed('tags');
// Some validation
- if (!Validate::string($nickname, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
- // TRANS: Validation error in form for profile settings.
- $this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.'));
- return;
- } else if (!User::allowed_nickname($nickname)) {
+ if (!User::allowed_nickname($nickname)) {
// TRANS: Validation error in form for profile settings.
$this->showForm(_('Not a valid nickname.'));
return;
diff --git a/actions/recoverpassword.php b/actions/recoverpassword.php
index f9956897f..33b0440e4 100644
--- a/actions/recoverpassword.php
+++ b/actions/recoverpassword.php
@@ -362,7 +362,7 @@ class RecoverpasswordAction extends Action
$confirm = $this->trimmed('confirm');
if (!$newpassword || strlen($newpassword) < 6) {
- $this->showPasswordForm(_('Password must be 6 chars or more.'));
+ $this->showPasswordForm(_('Password must be 6 characters or more.'));
return;
}
if ($newpassword != $confirm) {
diff --git a/actions/register.php b/actions/register.php
index 3ae3f56f6..5d91aef70 100644
--- a/actions/register.php
+++ b/actions/register.php
@@ -198,7 +198,11 @@ class RegisterAction extends Action
}
// Input scrubbing
- $nickname = common_canonical_nickname($nickname);
+ try {
+ $nickname = Nickname::normalize($nickname);
+ } catch (NicknameException $e) {
+ $this->showForm($e->getMessage());
+ }
$email = common_canonical_email($email);
if (!$this->boolean('license')) {
@@ -206,11 +210,6 @@ class RegisterAction extends Action
'agree to the license.'));
} else if ($email && !Validate::email($email, common_config('email', 'check_domain'))) {
$this->showForm(_('Not a valid email address.'));
- } else if (!Validate::string($nickname, array('min_length' => 1,
- 'max_length' => 64,
- 'format' => NICKNAME_FMT))) {
- $this->showForm(_('Nickname must have only lowercase letters '.
- 'and numbers and no spaces.'));
} else if ($this->nicknameExists($nickname)) {
$this->showForm(_('Nickname already in use. Try another one.'));
} else if (!User::allowed_nickname($nickname)) {
diff --git a/actions/rsd.php b/actions/rsd.php
index f88bf2e9a..e02c85c41 100644
--- a/actions/rsd.php
+++ b/actions/rsd.php
@@ -162,6 +162,20 @@ class RsdAction extends Action
'true');
$this->elementEnd('settings');
$this->elementEnd('api');
+
+ // Atom API
+
+ if (empty($this->user)) {
+ $service = common_local_url('ApiAtomService');
+ } else {
+ $service = common_local_url('ApiAtomService', array('id' => $this->user->nickname));
+ }
+
+ $this->element('api', array('name' => 'Atom',
+ 'preferred' => 'false',
+ 'apiLink' => $service,
+ 'blogID' => $blogID));
+
Event::handle('EndRsdListApis', array($this, $this->user));
}
$this->elementEnd('apis');
diff --git a/actions/showgroup.php b/actions/showgroup.php
index 8e8ff717c..f38cd420a 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -46,10 +46,8 @@ define('MEMBERS_PER_SECTION', 27);
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class ShowgroupAction extends GroupDesignAction
{
-
/** page we're viewing. */
var $page = null;
@@ -58,7 +56,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return boolean true
*/
-
function isReadOnly($args)
{
return true;
@@ -69,18 +66,16 @@ class ShowgroupAction extends GroupDesignAction
*
* @return string page title, with page number
*/
-
function title()
{
- if (!empty($this->group->fullname)) {
- $base = $this->group->fullname . ' (' . $this->group->nickname . ')';
- } else {
- $base = $this->group->nickname;
- }
+ $base = $this->group->getFancyName();
if ($this->page == 1) {
+ // TRANS: Page title for first group page. %s is a group name.
return sprintf(_('%s group'), $base);
} else {
+ // TRANS: Page title for any but first group page.
+ // TRANS: %1$s is a group name, $2$s is a page number.
return sprintf(_('%1$s group, page %2$d'),
$base,
$this->page);
@@ -96,7 +91,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return boolean success flag
*/
-
function prepare($args)
{
parent::prepare($args);
@@ -118,6 +112,7 @@ class ShowgroupAction extends GroupDesignAction
}
if (!$nickname) {
+ // TRANS: Client error displayed if no nickname argument was given requesting a group page.
$this->clientError(_('No nickname.'), 404);
return false;
}
@@ -135,6 +130,7 @@ class ShowgroupAction extends GroupDesignAction
return false;
} else {
common_log(LOG_NOTICE, "Couldn't find local group for nickname '$nickname'");
+ // TRANS: Client error displayed if no remote group with a given name was found requesting group page.
$this->clientError(_('No such group.'), 404);
return false;
}
@@ -143,6 +139,7 @@ class ShowgroupAction extends GroupDesignAction
$this->group = User_group::staticGet('id', $local->group_id);
if (!$this->group) {
+ // TRANS: Client error displayed if no local group with a given name was found requesting group page.
$this->clientError(_('No such group.'), 404);
return false;
}
@@ -160,7 +157,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function handle($args)
{
$this->showPage();
@@ -171,7 +167,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function showLocalNav()
{
$nav = new GroupNav($this, $this->group);
@@ -183,7 +178,6 @@ class ShowgroupAction extends GroupDesignAction
*
* Shows a group profile and a list of group notices
*/
-
function showContent()
{
$this->showGroupProfile();
@@ -195,7 +189,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function showGroupNotices()
{
$notice = $this->group->getNotices(($this->page-1)*NOTICES_PER_PAGE,
@@ -218,15 +211,16 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function showGroupProfile()
{
$this->elementStart('div', array('id' => 'i',
'class' => 'entity_profile vcard author'));
+ // TRANS: Group profile header (h2). Text hidden by default.
$this->element('h2', null, _('Group profile'));
$this->elementStart('dl', 'entity_depiction');
+ // TRANS: Label for group avatar (dt). Text hidden by default.
$this->element('dt', null, _('Avatar'));
$this->elementStart('dd');
@@ -242,6 +236,7 @@ class ShowgroupAction extends GroupDesignAction
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_nickname');
+ // TRANS: Label for group nickname (dt). Text hidden by default.
$this->element('dt', null, _('Nickname'));
$this->elementStart('dd');
$hasFN = ($this->group->fullname) ? 'nickname url uid' : 'fn org nickname url uid';
@@ -253,6 +248,7 @@ class ShowgroupAction extends GroupDesignAction
if ($this->group->fullname) {
$this->elementStart('dl', 'entity_fn');
+ // TRANS: Label for full group name (dt). Text hidden by default.
$this->element('dt', null, _('Full name'));
$this->elementStart('dd');
$this->element('span', 'fn org', $this->group->fullname);
@@ -262,6 +258,7 @@ class ShowgroupAction extends GroupDesignAction
if ($this->group->location) {
$this->elementStart('dl', 'entity_location');
+ // TRANS: Label for group location (dt). Text hidden by default.
$this->element('dt', null, _('Location'));
$this->element('dd', 'label', $this->group->location);
$this->elementEnd('dl');
@@ -269,6 +266,7 @@ class ShowgroupAction extends GroupDesignAction
if ($this->group->homepage) {
$this->elementStart('dl', 'entity_url');
+ // TRANS: Label for group URL (dt). Text hidden by default.
$this->element('dt', null, _('URL'));
$this->elementStart('dd');
$this->element('a', array('href' => $this->group->homepage,
@@ -280,6 +278,7 @@ class ShowgroupAction extends GroupDesignAction
if ($this->group->description) {
$this->elementStart('dl', 'entity_note');
+ // TRANS: Label for group description or group note (dt). Text hidden by default.
$this->element('dt', null, _('Note'));
$this->element('dd', 'note', $this->group->description);
$this->elementEnd('dl');
@@ -290,6 +289,7 @@ class ShowgroupAction extends GroupDesignAction
if (!empty($aliases)) {
$this->elementStart('dl', 'entity_aliases');
+ // TRANS: Label for group aliases (dt). Text hidden by default.
$this->element('dt', null, _('Aliases'));
$this->element('dd', 'aliases', implode(' ', $aliases));
$this->elementEnd('dl');
@@ -300,6 +300,7 @@ class ShowgroupAction extends GroupDesignAction
$cur = common_current_user();
$this->elementStart('div', 'entity_actions');
+ // TRANS: Group actions header (h2). Text hidden by default.
$this->element('h2', null, _('Group actions'));
$this->elementStart('ul');
$this->elementStart('li', 'entity_subscribe');
@@ -331,7 +332,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function getFeeds()
{
$url =
@@ -341,23 +341,27 @@ class ShowgroupAction extends GroupDesignAction
return array(new Feed(Feed::RSS1,
common_local_url('grouprss',
array('nickname' => $this->group->nickname)),
+ // TRANS: Tooltip for feed link. %s is a group nickname.
sprintf(_('Notice feed for %s group (RSS 1.0)'),
$this->group->nickname)),
new Feed(Feed::RSS2,
common_local_url('ApiTimelineGroup',
array('format' => 'rss',
'id' => $this->group->id)),
+ // TRANS: Tooltip for feed link. %s is a group nickname.
sprintf(_('Notice feed for %s group (RSS 2.0)'),
$this->group->nickname)),
new Feed(Feed::ATOM,
common_local_url('ApiTimelineGroup',
array('format' => 'atom',
'id' => $this->group->id)),
+ // TRANS: Tooltip for feed link. %s is a group nickname.
sprintf(_('Notice feed for %s group (Atom)'),
$this->group->nickname)),
new Feed(Feed::FOAF,
common_local_url('foafgroup',
array('nickname' => $this->group->nickname)),
+ // TRANS: Tooltip for feed link. %s is a group nickname.
sprintf(_('FOAF for %s group'),
$this->group->nickname)));
}
@@ -367,7 +371,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function showSections()
{
$this->showMembers();
@@ -382,7 +385,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function showMembers()
{
$member = $this->group->getMembers(0, MEMBERS_PER_SECTION);
@@ -396,17 +398,22 @@ class ShowgroupAction extends GroupDesignAction
if (Event::handle('StartShowGroupMembersMiniList', array($this))) {
+ // TRANS: Header for mini list of group members on a group page (h2).
$this->element('h2', null, _('Members'));
$gmml = new GroupMembersMiniList($member, $this);
$cnt = $gmml->show();
if ($cnt == 0) {
+ // TRANS: Description for mini list of group members on a group page when the group has no members.
$this->element('p', null, _('(None)'));
}
+ // @todo FIXME: Should be shown if a group has more than 27 members, but I do not see it displayed at
+ // for example http://identi.ca/group/statusnet. Broken?
if ($cnt > MEMBERS_PER_SECTION) {
$this->element('a', array('href' => common_local_url('groupmembers',
array('nickname' => $this->group->nickname))),
+ // TRANS: Link to all group members from mini list of group members if group has more than n members.
_('All members'));
}
@@ -421,7 +428,6 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function showAdmins()
{
$adminSection = new GroupAdminSection($this, $this->group);
@@ -433,22 +439,26 @@ class ShowgroupAction extends GroupDesignAction
*
* @return void
*/
-
function showStatistics()
{
$this->elementStart('div', array('id' => 'entity_statistics',
'class' => 'section'));
+ // TRANS: Header for group statistics on a group page (h2).
$this->element('h2', null, _('Statistics'));
$this->elementStart('dl', 'entity_created');
- $this->element('dt', null, _('Created'));
+ // @todo FIXME: i18n issue. This label gets a colon added from somewhere. Should be part of the message.
+ // TRANS: Label for creation date in statistics on group page.
+ $this->element('dt', null, _m('LABEL','Created'));
$this->element('dd', null, date('j M Y',
strtotime($this->group->created)));
$this->elementEnd('dl');
$this->elementStart('dl', 'entity_members');
- $this->element('dt', null, _('Members'));
+ // @todo FIXME: i18n issue. This label gets a colon added from somewhere. Should be part of the message.
+ // TRANS: Label for member count in statistics on group page.
+ $this->element('dt', null, _m('LABEL','Members'));
$this->element('dd', null, $this->group->getMemberCount());
$this->elementEnd('dl');
@@ -458,12 +468,21 @@ class ShowgroupAction extends GroupDesignAction
function showAnonymousMessage()
{
if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
+ // @todo FIXME: use group full name here if available instead of (uglier) primary alias.
+ // TRANS: Notice on group pages for anonymous users for StatusNet sites that accept new registrations.
+ // TRANS: **%s** is the group alias, %%%%site.name%%%% is the site name,
+ // TRANS: %%%%action.register%%%% is the URL for registration, %%%%doc.help%%%% is a URL to help.
+ // TRANS: This message contains Markdown links. Ensure they are formatted correctly: [Description](link).
$m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [StatusNet](http://status.net/) tool. Its members share ' .
'short messages about their life and interests. '.
'[Join now](%%%%action.register%%%%) to become part of this group and many more! ([Read more](%%%%doc.help%%%%))'),
$this->group->nickname);
} else {
+ // @todo FIXME: use group full name here if available instead of (uglier) primary alias.
+ // TRANS: Notice on group pages for anonymous users for StatusNet sites that accept no new registrations.
+ // TRANS: **%s** is the group alias, %%%%site.name%%%% is the site name,
+ // TRANS: This message contains Markdown links. Ensure they are formatted correctly: [Description](link).
$m = sprintf(_('**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [StatusNet](http://status.net/) tool. Its members share ' .
'short messages about their life and interests. '),
@@ -492,6 +511,7 @@ class GroupAdminSection extends ProfileSection
function title()
{
+ // TRANS: Header for list of group administrators on a group page (h2).
return _('Admins');
}
@@ -527,4 +547,3 @@ class GroupMembersMiniListItem extends ProfileMiniListItem
return $aAttrs;
}
}
-
diff --git a/actions/showmessage.php b/actions/showmessage.php
index db757948b..d737f85d3 100644
--- a/actions/showmessage.php
+++ b/actions/showmessage.php
@@ -26,8 +26,8 @@
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
}
require_once INSTALLDIR.'/lib/mailbox.php';
@@ -36,26 +36,24 @@ require_once INSTALLDIR.'/lib/mailbox.php';
* Show a single message
*
* // XXX: It is totally weird how this works!
- *
+ *
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class ShowmessageAction extends MailboxAction
{
/**
* Message object to show
*/
-
var $message = null;
-
+
/**
* The current user
*/
-
+
var $user = null;
/**
@@ -67,17 +65,17 @@ class ShowmessageAction extends MailboxAction
*
* @return success flag
*/
-
function prepare($args)
{
parent::prepare($args);
-
+
$this->page = 1;
-
+
$id = $this->trimmed('message');
$this->message = Message::staticGet('id', $id);
if (!$this->message) {
+ // TRANS: Client error displayed requesting a single message that does not exist.
$this->clientError(_('No such message.'), 404);
return false;
}
@@ -90,40 +88,47 @@ class ShowmessageAction extends MailboxAction
function handle($args)
{
Action::handle($args);
-
- if ($this->user && ($this->user->id == $this->message->from_profile ||
+
+ if ($this->user && ($this->user->id == $this->message->from_profile ||
$this->user->id == $this->message->to_profile)) {
$this->showPage();
} else {
+ // TRANS: Client error displayed requesting a single direct message the requesting user was not a party in.
$this->clientError(_('Only the sender and recipient ' .
'may read this message.'), 403);
return;
}
}
-
+
function title()
- {
+ {
if ($this->user->id == $this->message->from_profile) {
$to = $this->message->getTo();
- return sprintf(_("Message to %1\$s on %2\$s"),
+ // @todo FIXME: Might be nice if the timestamp could be localised.
+ // TRANS: Page title for single direct message display when viewing user is the sender.
+ // TRANS: %1$s is the addressed user's nickname, $2$s is a timestamp.
+ return sprintf(_('Message to %1$s on %2$s'),
$to->nickname,
common_exact_date($this->message->created));
} else if ($this->user->id == $this->message->to_profile) {
$from = $this->message->getFrom();
- return sprintf(_("Message from %1\$s on %2\$s"),
+ // @todo FIXME: Might be nice if the timestamp could be localised.
+ // TRANS: Page title for single message display.
+ // TRANS: %1$s is the sending user's nickname, $2$s is a timestamp.
+ return sprintf(_('Message from %1$s on %2$s'),
$from->nickname,
common_exact_date($this->message->created));
}
}
-
- function getMessages()
- {
+
+ function getMessages()
+ {
$message = new Message();
$message->id = $this->message->id;
$message->find();
return $message;
}
-
+
function getMessageProfile()
{
if ($this->user->id == $this->message->from_profile) {
@@ -135,23 +140,21 @@ class ShowmessageAction extends MailboxAction
return null;
}
}
-
+
/**
* Don't show local navigation
*
* @return void
*/
-
function showLocalNavBlock()
{
}
-
+
/**
* Don't show page notice
*
* @return void
*/
-
function showPageNoticeBlock()
{
}
@@ -161,17 +164,15 @@ class ShowmessageAction extends MailboxAction
*
* @return void
*/
-
- function showAside()
+ function showAside()
{
}
-
+
/**
* Don't show any instructions
*
* @return string
*/
-
function getInstructions()
{
return '';
diff --git a/actions/shownotice.php b/actions/shownotice.php
index 5fc863486..b4af7dbaa 100644
--- a/actions/shownotice.php
+++ b/actions/shownotice.php
@@ -167,11 +167,7 @@ class ShownoticeAction extends OwnerDesignAction
function title()
{
- if (!empty($this->profile->fullname)) {
- $base = $this->profile->fullname . ' (' . $this->profile->nickname . ')';
- } else {
- $base = $this->profile->nickname;
- }
+ $base = $this->profile->getFancyName();
return sprintf(_('%1$s\'s status on %2$s'),
$base,
@@ -335,8 +331,38 @@ class SingleNoticeItem extends DoFollowListItem
$this->showEnd();
}
+ /**
+ * For our zoomed-in special case we'll use a fuller list
+ * for the attachment info.
+ */
function showNoticeAttachments() {
$al = new AttachmentList($this->notice, $this->out);
$al->show();
}
+
+ /**
+ * show the avatar of the notice's author
+ *
+ * We use the larger size for single notice page.
+ *
+ * @return void
+ */
+
+ function showAvatar()
+ {
+ $avatar_size = AVATAR_PROFILE_SIZE;
+
+ $avatar = $this->profile->getAvatar($avatar_size);
+
+ $this->out->element('img', array('src' => ($avatar) ?
+ $avatar->displayUrl() :
+ Avatar::defaultImage($avatar_size),
+ 'class' => 'avatar photo',
+ 'width' => $avatar_size,
+ 'height' => $avatar_size,
+ 'alt' =>
+ ($this->profile->fullname) ?
+ $this->profile->fullname :
+ $this->profile->nickname));
+ }
}
diff --git a/actions/showstream.php b/actions/showstream.php
index fb5b061fb..5a22bdf28 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -63,21 +63,26 @@ class ShowstreamAction extends ProfileAction
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. %1$s is the username, %2$s 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.
+ // TRANS: %1$s is the username, %2$s is the hash tag, %1$d 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.
+ // TRANS: %1$s is the username, %2$d is the page number.
+ return sprintf(_('%1$s, page %2$d'),
+ $base,
+ $this->page);
+ }
}
}
@@ -117,6 +122,8 @@ class ShowstreamAction extends ProfileAction
common_local_url('userrss',
array('nickname' => $this->user->nickname,
'tag' => $this->tag)),
+ // TRANS: Title for link to notice feed.
+ // TRANS: %1$s is a user nickname, %2$s is a hashtag.
sprintf(_('Notice feed for %1$s tagged %2$s (RSS 1.0)'),
$this->user->nickname, $this->tag)));
}
@@ -124,6 +131,8 @@ class ShowstreamAction extends ProfileAction
return array(new Feed(Feed::RSS1,
common_local_url('userrss',
array('nickname' => $this->user->nickname)),
+ // TRANS: Title for link to notice feed.
+ // TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (RSS 1.0)'),
$this->user->nickname)),
new Feed(Feed::RSS2,
@@ -131,6 +140,8 @@ class ShowstreamAction extends ProfileAction
array(
'id' => $this->user->id,
'format' => 'rss')),
+ // TRANS: Title for link to notice feed.
+ // TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (RSS 2.0)'),
$this->user->nickname)),
new Feed(Feed::ATOM,
@@ -143,6 +154,8 @@ class ShowstreamAction extends ProfileAction
new Feed(Feed::FOAF,
common_local_url('foaf', array('nickname' =>
$this->user->nickname)),
+ // TRANS: Title for link to notice feed. FOAF stands for Friend of a Friend.
+ // TRANS: More information at http://www.foaf-project.org. %s is a user nickname.
sprintf(_('FOAF for %s'), $this->user->nickname)));
}
@@ -194,17 +207,23 @@ class ShowstreamAction extends ProfileAction
function showEmptyListMessage()
{
- $message = sprintf(_('This is the timeline for %1$s but %2$s hasn\'t posted anything yet.'), $this->user->nickname, $this->user->nickname) . ' ';
+ // TRANS: First sentence of empty list message for a stream. $1%s is a user nickname.
+ $message = sprintf(_('This is the timeline for %1$s, but %1$s hasn\'t posted anything yet.'), $this->user->nickname) . ' ';
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
+ // TRANS: Second sentence of empty list message for a stream for the user themselves.
$message .= _('Seen anything interesting recently? You haven\'t posted any notices yet, now would be a good time to start :)');
} else {
+ // TRANS: Second sentence of empty list message for a non-self stream. %1$s is a user nickname, %2$s is a part of a URL.
+ // TRANS: This message contains a Markdown link. Keep "](" together.
$message .= sprintf(_('You can try to nudge %1$s or [post something to them](%%%%action.newnotice%%%%?status_textarea=%2$s).'), $this->user->nickname, '@' . $this->user->nickname);
}
}
else {
+ // TRANS: Second sentence of empty message for anonymous users. %s is a user nickname.
+ // TRANS: This message contains a Markdown link. Keep "](" together.
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname);
}
@@ -240,11 +259,15 @@ class ShowstreamAction extends ProfileAction
function showAnonymousMessage()
{
if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
+ // TRANS: Announcement for anonymous users showing a stream if site registrations are open.
+ // TRANS: This message contains a Markdown link. Keep "](" together.
$m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [StatusNet](http://status.net/) tool. ' .
'[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
$this->user->nickname, $this->user->nickname);
} else {
+ // TRANS: Announcement for anonymous users showing a stream if site registrations are closed or invite only.
+ // TRANS: This message contains a Markdown link. Keep "](" together.
$m = sprintf(_('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
'based on the Free Software [StatusNet](http://status.net/) tool. '),
$this->user->nickname, $this->user->nickname);
@@ -284,7 +307,6 @@ class ProfileNoticeListItem extends DoFollowListItem
*
* @return void
*/
-
function showRepeat()
{
if (!empty($this->repeat)) {
@@ -295,13 +317,14 @@ class ProfileNoticeListItem extends DoFollowListItem
'class' => 'url');
if (!empty($this->profile->fullname)) {
- $attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ')';
+ $attrs['title'] = $this->getFancyName();
}
$this->out->elementStart('span', 'repeat');
$text_link = XMLStringer::estring('a', $attrs, $this->profile->nickname);
+ // TRANS: Link to the author of a repeated notice. %s is a linked nickname.
$this->out->raw(sprintf(_('Repeat of %s'), $text_link));
$this->out->elementEnd('span');
diff --git a/actions/sitenoticeadminpanel.php b/actions/sitenoticeadminpanel.php
index bdcaa2355..797a6c4f4 100644
--- a/actions/sitenoticeadminpanel.php
+++ b/actions/sitenoticeadminpanel.php
@@ -42,7 +42,6 @@ require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.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 SitenoticeadminpanelAction extends AdminPanelAction
{
/**
@@ -50,9 +49,9 @@ class SitenoticeadminpanelAction extends AdminPanelAction
*
* @return string page title
*/
-
function title()
{
+ // TRANS: Page title for site-wide notice tab in admin panel.
return _('Site Notice');
}
@@ -61,9 +60,9 @@ class SitenoticeadminpanelAction extends AdminPanelAction
*
* @return string instructions
*/
-
function getInstructions()
{
+ // TRANS: Instructions for site-wide notice tab in admin panel.
return _('Edit site-wide message');
}
@@ -72,7 +71,6 @@ class SitenoticeadminpanelAction extends AdminPanelAction
*
* @return void
*/
-
function showForm()
{
$form = new SiteNoticeAdminPanelForm($this);
@@ -85,7 +83,6 @@ class SitenoticeadminpanelAction extends AdminPanelAction
*
* @return void
*/
-
function saveSettings()
{
$siteNotice = $this->trimmed('site-notice');
@@ -100,6 +97,7 @@ class SitenoticeadminpanelAction extends AdminPanelAction
$result = Config::save('site', 'notice', $siteNotice);
if (!$result) {
+ // TRANS: Server error displayed when saving a site-wide notice was impossible.
$this->ServerError(_("Unable to save site notice."));
}
}
@@ -110,7 +108,8 @@ class SitenoticeadminpanelAction extends AdminPanelAction
if (mb_strlen($siteNotice) > 255) {
$this->clientError(
- _('Max length for the site-wide notice is 255 chars.')
+ // TRANS: Client error displayed when a site-wide notice was longer than allowed.
+ _('Maximum length for the site-wide notice is 255 characters.')
);
}
@@ -173,9 +172,11 @@ class SiteNoticeAdminPanelForm extends AdminForm
$this->out->elementStart('li');
$this->out->textarea(
'site-notice',
+ // TRANS: Label for site-wide notice text field in admin panel.
_('Site notice text'),
common_config('site', 'notice'),
- _('Site-wide notice text (255 chars max; HTML okay)')
+ // TRANS: Tooltip for site-wide notice text field in admin panel.
+ _('Site-wide notice text (255 characters maximum; HTML allowed)')
);
$this->out->elementEnd('li');
@@ -192,9 +193,11 @@ class SiteNoticeAdminPanelForm extends AdminForm
{
$this->out->submit(
'submit',
- _('Save'),
+ // TRANS: Button text for saving site notice in admin panel.
+ _m('BUTTON','Save'),
'submit',
null,
+ // TRANS: Title for button to save site notice in admin panel.
_('Save site notice')
);
}
diff --git a/actions/subedit.php b/actions/subedit.php
index cf6589e50..3b77aff58 100644
--- a/actions/subedit.php
+++ b/actions/subedit.php
@@ -19,6 +19,7 @@
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+// @todo FIXME: Documentation needed.
class SubeditAction extends Action
{
var $profile = null;
@@ -28,6 +29,7 @@ class SubeditAction extends Action
parent::prepare($args);
if (!common_logged_in()) {
+ // TRANS: Client error displayed trying a change a subscription while not logged in.
$this->clientError(_('Not logged in.'));
return false;
}
@@ -43,6 +45,7 @@ class SubeditAction extends Action
$id = $this->trimmed('profile');
if (!$id) {
+ // TRANS: Client error displayed trying a change a subscription without providing a profile.
$this->clientError(_('No profile specified.'));
return false;
}
@@ -50,6 +53,7 @@ class SubeditAction extends Action
$this->profile = Profile::staticGet('id', $id);
if (!$this->profile) {
+ // TRANS: Client error displayed trying a change a subscription for a non-existant profile ID.
$this->clientError(_('No profile with that ID.'));
return false;
}
@@ -67,6 +71,7 @@ class SubeditAction extends Action
'subscribed' => $this->profile->id));
if (!$sub) {
+ // TRANS: Client error displayed trying a change a subscription for a non-subscribed profile.
$this->clientError(_('You are not subscribed to that profile.'));
return false;
}
@@ -80,6 +85,7 @@ class SubeditAction extends Action
if (!$result) {
common_log_db_error($sub, 'UPDATE', __FILE__);
+ // TRANS: Server error displayed when updating a subscription fails with a database error.
$this->serverError(_('Could not save subscription.'));
return false;
}
diff --git a/actions/subscriptions.php b/actions/subscriptions.php
index ba2f67f2d..a814a4f35 100644
--- a/actions/subscriptions.php
+++ b/actions/subscriptions.php
@@ -163,6 +163,22 @@ class SubscriptionsAction extends GalleryAction
$cloud2 = new SubscriptionsPeopleSelfTagCloudSection($this);
$cloud2->show();
}
+
+ /**
+ * Link to feeds of subscriptions
+ *
+ * @return array of Feed objects
+ */
+
+ function getFeeds()
+ {
+ return array(new Feed(Feed::ATOM,
+ common_local_url('AtomPubSubscriptionFeed',
+ array('subscriber' => $this->profile->id)),
+ sprintf(_('Subscription feed for %s (Atom)'),
+ $this->profile->nickname)));
+
+ }
}
// XXX SubscriptionsList and SubscriptionList are dangerously close
@@ -247,4 +263,5 @@ class SubscriptionsListItem extends SubscriptionListItem
$this->out->elementEnd('form');
return;
}
+
}
diff --git a/actions/useradminpanel.php b/actions/useradminpanel.php
index 04e0ca3e7..fc75e83b2 100644
--- a/actions/useradminpanel.php
+++ b/actions/useradminpanel.php
@@ -45,7 +45,6 @@ if (!defined('STATUSNET')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-
class UseradminpanelAction extends AdminPanelAction
{
/**
@@ -53,7 +52,6 @@ class UseradminpanelAction extends AdminPanelAction
*
* @return string page title
*/
-
function title()
{
// TRANS: User admin panel title
@@ -65,9 +63,9 @@ class UseradminpanelAction extends AdminPanelAction
*
* @return string instructions
*/
-
function getInstructions()
{
+ // TRANS: Instruction for user admin panel.
return _('User settings for this StatusNet site');
}
@@ -76,7 +74,6 @@ class UseradminpanelAction extends AdminPanelAction
*
* @return void
*/
-
function showForm()
{
$form = new UserAdminPanelForm($this);
@@ -89,7 +86,6 @@ class UseradminpanelAction extends AdminPanelAction
*
* @return void
*/
-
function saveSettings()
{
static $settings = array(
@@ -147,13 +143,15 @@ class UseradminpanelAction extends AdminPanelAction
// Validate biolimit
if (!Validate::number($values['profile']['biolimit'])) {
- $this->clientError(_("Invalid bio limit. Must be numeric."));
+ // TRANS: Form validation error in user admin panel when a non-numeric character limit was set.
+ $this->clientError(_('Invalid bio limit. Must be numeric.'));
}
// Validate welcome text
if (mb_strlen($values['newuser']['welcome']) > 255) {
- $this->clientError(_("Invalid welcome text. Max length is 255 characters."));
+ // TRANS: Form validation error in user admin panel when welcome text is too long.
+ $this->clientError(_('Invalid welcome text. Maximum length is 255 characters.'));
}
// Validate default subscription
@@ -163,7 +161,9 @@ class UseradminpanelAction extends AdminPanelAction
if (empty($defuser)) {
$this->clientError(
sprintf(
- _('Invalid default subscripton: \'%1$s\' is not user.'),
+ // TRANS: Client error displayed when trying to set a non-existing user as default subscription for new
+ // TRANS: users in user admin panel. %1$s is the invalid nickname.
+ _('Invalid default subscripton: \'%1$s\' is not a user.'),
$values['newuser']['default']
)
);
@@ -179,7 +179,6 @@ class UserAdminPanelForm extends AdminForm
*
* @return int ID of the form
*/
-
function id()
{
return 'useradminpanel';
@@ -190,7 +189,6 @@ class UserAdminPanelForm extends AdminForm
*
* @return string class of the form
*/
-
function formClass()
{
return 'form_settings';
@@ -201,7 +199,6 @@ class UserAdminPanelForm extends AdminForm
*
* @return string URL of the action
*/
-
function action()
{
return common_local_url('useradminpanel');
@@ -212,7 +209,6 @@ class UserAdminPanelForm extends AdminForm
*
* @return void
*/
-
function formData()
{
$this->out->elementStart('fieldset', array('id' => 'settings_user-profile'));
@@ -220,7 +216,9 @@ class UserAdminPanelForm extends AdminForm
$this->out->elementStart('ul', 'form_data');
$this->li();
+ // TRANS: Field label in user admin panel for setting the character limit for the bio field.
$this->input('biolimit', _('Bio Limit'),
+ // TRANS: Tooltip in user admin panel for setting the character limit for the bio field.
_('Maximum length of a profile bio in characters.'),
'profile');
$this->unli();
@@ -229,17 +227,22 @@ class UserAdminPanelForm extends AdminForm
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_user-newusers'));
+ // TRANS: Form legend in user admin panel.
$this->out->element('legend', null, _('New users'));
$this->out->elementStart('ul', 'form_data');
$this->li();
+ // TRANS: Field label in user admin panel for setting new user welcome text.
$this->input('welcome', _('New user welcome'),
- _('Welcome text for new users (Max 255 chars).'),
+ // TRANS: Tooltip in user admin panel for setting new user welcome text.
+ _('Welcome text for new users (maximum 255 characters).'),
'newuser');
$this->unli();
$this->li();
+ // TRANS: Field label in user admin panel for setting default subscription for new users.
$this->input('default', _('Default subscription'),
+ // TRANS: Tooltip in user admin panel for setting default subscription for new users.
_('Automatically subscribe new users to this user.'),
'newuser');
$this->unli();
@@ -249,21 +252,21 @@ class UserAdminPanelForm extends AdminForm
$this->out->elementEnd('fieldset');
$this->out->elementStart('fieldset', array('id' => 'settings_user-invitations'));
+ // TRANS: Form legend in user admin panel.
$this->out->element('legend', null, _('Invitations'));
$this->out->elementStart('ul', 'form_data');
$this->li();
+ // TRANS: Field label for checkbox in user admin panel for allowing users to invite friend using site e-mail.
$this->out->checkbox('invite-enabled', _('Invitations enabled'),
(bool) $this->value('enabled', 'invite'),
+ // TRANS: Tooltip for checkbox in user admin panel for allowing users to invite friend using site e-mail.
_('Whether to allow users to invite new users.'));
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
-
-
-
}
/**
@@ -278,7 +281,6 @@ class UserAdminPanelForm extends AdminForm
*
* @return void
*/
-
function input($setting, $title, $instructions, $section='site')
{
$this->out->input("$section-$setting", $title, $this->value($setting, $section), $instructions);
@@ -289,9 +291,14 @@ class UserAdminPanelForm extends AdminForm
*
* @return void
*/
-
function formActions()
{
- $this->out->submit('submit', _('Save'), 'submit', null, _('Save user settings'));
+ $this->out->submit('submit',
+ // TRANS: Button text to save user settings in user admin panel.
+ _m('BUTTON','Save'),
+ 'submit',
+ null,
+ // TRANS: Title for button to save user settings in user admin panel.
+ _('Save user settings'));
}
}
diff --git a/actions/userxrd.php b/actions/userxrd.php
new file mode 100644
index 000000000..582f7a35e
--- /dev/null
+++ b/actions/userxrd.php
@@ -0,0 +1,65 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * @package OStatusPlugin
+ * @maintainer James Walker <james@status.net>
+ */
+class UserxrdAction extends XrdAction
+{
+ function prepare($args)
+ {
+ parent::prepare($args);
+
+ $this->uri = $this->trimmed('uri');
+ $this->uri = self::normalize($this->uri);
+
+ if (self::isWebfinger($this->uri)) {
+ $parts = explode('@', substr(urldecode($this->uri), 5));
+ if (count($parts) == 2) {
+ list($nick, $domain) = $parts;
+ // @fixme confirm the domain too
+ // @fixme if domain checking is added, ensure that it will not
+ // cause problems with sites that have changed domains!
+ $nick = common_canonical_nickname($nick);
+ $this->user = User::staticGet('nickname', $nick);
+ }
+ } else {
+ $this->user = User::staticGet('uri', $this->uri);
+ if (empty($this->user)) {
+ // try and get it by profile url
+ $profile = Profile::staticGet('profileurl', $this->uri);
+ if (!empty($profile)) {
+ $this->user = User::staticGet('id', $profile->id);
+ }
+ }
+ }
+
+ if (!$this->user) {
+ $this->clientError(_m('No such user.'), 404);
+ return false;
+ }
+
+ return true;
+ }
+}