diff options
Diffstat (limited to 'actions')
105 files changed, 2364 insertions, 749 deletions
diff --git a/actions/all.php b/actions/all.php index 8c22e6f5f..9c01b6393 100644 --- a/actions/all.php +++ b/actions/all.php @@ -18,15 +18,19 @@ * * @category Actions * @package Actions - * @author Evan Prodromou <evan@status.net> - * @author Mike Cochrane <mikec@mikenz.geek.nz> - * @author Robin Millette <millette@controlyourself.ca> * @author Adrian Lang <mail@adrianlang.de> - * @author Meitar Moscovitz <meitarm@gmail.com> - * @author Sarven Capadisli <csarven@status.net> + * @author Brenda Wallace <shiny@cpan.org> + * @author Brion Vibber <brion@pobox.com> * @author Craig Andrews <candrews@integralblue.com> + * @author Evan Prodromou <evan@status.net> * @author Jeffery To <jeffery.to@gmail.com> - * @author Zach Copley <zach@controlyourself.ca> + * @author Meitar Moscovitz <meitarm@gmail.com> + * @author Mike Cochrane <mikec@mikenz.geek.nz> + * @author Robin Millette <millette@status.net> + * @author Sarven Capadisli <csarven@status.net> + * @author Siebrand Mazeland <s.mazeland@xs4all.nl> + * @author Zach Copley <zach@status.net> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license GNU Affero General Public License http://www.gnu.org/licenses/ * @link http://status.net */ @@ -61,7 +65,7 @@ class AllAction extends ProfileAction if ($this->page > 1 && $this->notice->N == 0) { // TRANS: Server error when page not found (404) - $this->serverError(_('No such page'), $code = 404); + $this->serverError(_('No such page.'), $code = 404); } return true; diff --git a/actions/allrss.php b/actions/allrss.php index 28b1be27d..7df0b1ef7 100644 --- a/actions/allrss.php +++ b/actions/allrss.php @@ -83,6 +83,7 @@ class AllrssAction extends Rss10Action function getNotices($limit=0) { $cur = common_current_user(); + $user = $this->user; if (!empty($cur) && $cur->id == $user->id) { $notice = $this->user->noticeInbox(0, $limit); @@ -90,7 +91,6 @@ class AllrssAction extends Rss10Action $notice = $this->user->noticesWithFriends(0, $limit); } - $user = $this->user; $notice = $user->noticesWithFriends(0, $limit); $notices = array(); @@ -112,10 +112,12 @@ class AllrssAction extends Rss10Action $c = array('url' => common_local_url('allrss', array('nickname' => $user->nickname)), + // TRANS: Message is used as link title. %s is a user nickname. 'title' => sprintf(_('%s and friends'), $user->nickname), 'link' => common_local_url('all', array('nickname' => $user->nickname)), + // TRANS: Message is used as link description. %1$s is a username, %2$s is a site name. 'description' => sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, common_config('site', 'name'))); return $c; diff --git a/actions/apiaccountratelimitstatus.php b/actions/apiaccountratelimitstatus.php index f19e315bf..e2dff2db9 100644 --- a/actions/apiaccountratelimitstatus.php +++ b/actions/apiaccountratelimitstatus.php @@ -21,8 +21,10 @@ * * @category API * @package StatusNet + * @author Brion Vibber <brion@pobox.com> * @author Evan Prodromou <evan@status.net> * @author Robin Millette <robin@millette.info> + * @author Siebrand Mazeland <s.mazeland@xs4all.nl> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 diff --git a/actions/apiaccountupdatedeliverydevice.php b/actions/apiaccountupdatedeliverydevice.php index 684906fe9..295378aa6 100644 --- a/actions/apiaccountupdatedeliverydevice.php +++ b/actions/apiaccountupdatedeliverydevice.php @@ -21,6 +21,7 @@ * * @category API * @package StatusNet + * @author Siebrand Mazeland <s.mazeland@xs4all.nl> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 @@ -103,7 +104,7 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction $this->clientError( _( 'You must specify a parameter named ' . - '\'device\' with a value of one of: sms, im, none' + '\'device\' with a value of one of: sms, im, none.' ) ); return; diff --git a/actions/apiaccountverifycredentials.php b/actions/apiaccountverifycredentials.php index ea61a3205..79416e9b2 100644 --- a/actions/apiaccountverifycredentials.php +++ b/actions/apiaccountverifycredentials.php @@ -75,7 +75,7 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction if ($this->format == 'xml') { $this->initDocument('xml'); - $this->showTwitterXmlUser($twitter_user); + $this->showTwitterXmlUser($twitter_user, 'user', true); $this->endDocument('xml'); } elseif ($this->format == 'json') { $this->initDocument('json'); diff --git a/actions/apiblockcreate.php b/actions/apiblockcreate.php index c26485f59..b355cd1c7 100644 --- a/actions/apiblockcreate.php +++ b/actions/apiblockcreate.php @@ -23,7 +23,7 @@ * @package StatusNet * @author Evan Prodromou <evan@status.net> * @author Zach Copley <zach@status.net> - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -65,7 +65,7 @@ class ApiBlockCreateAction extends ApiAuthAction parent::prepare($args); $this->user = $this->auth_user; - $this->other = $this->getTargetUser($this->arg('id')); + $this->other = $this->getTargetProfile($this->arg('id')); return true; } diff --git a/actions/apiblockdestroy.php b/actions/apiblockdestroy.php index 666f308f4..7ea201677 100644 --- a/actions/apiblockdestroy.php +++ b/actions/apiblockdestroy.php @@ -23,7 +23,7 @@ * @package StatusNet * @author Evan Prodromou <evan@status.net> * @author Zach Copley <zach@status.net> - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -64,7 +64,7 @@ class ApiBlockDestroyAction extends ApiAuthAction parent::prepare($args); $this->user = $this->auth_user; - $this->other = $this->getTargetUser($this->arg('id')); + $this->other = $this->getTargetProfile($this->arg('id')); return true; } diff --git a/actions/apidirectmessage.php b/actions/apidirectmessage.php index 53da9e0c6..7a0f46274 100644 --- a/actions/apidirectmessage.php +++ b/actions/apidirectmessage.php @@ -232,7 +232,8 @@ class ApiDirectMessageAction extends ApiAuthAction function showXmlDirectMessages() { $this->initDocument('xml'); - $this->elementStart('direct-messages', array('type' => 'array')); + $this->elementStart('direct-messages', array('type' => 'array', + 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); foreach ($this->messages as $m) { $dm_array = $this->directMessageArray($m); diff --git a/actions/apidirectmessagenew.php b/actions/apidirectmessagenew.php index b9ac92d77..65d065648 100644 --- a/actions/apidirectmessagenew.php +++ b/actions/apidirectmessagenew.php @@ -52,7 +52,6 @@ require_once INSTALLDIR . '/lib/apiauth.php'; class ApiDirectMessageNewAction extends ApiAuthAction { - var $source = null; var $other = null; var $content = null; @@ -76,13 +75,6 @@ class ApiDirectMessageNewAction extends ApiAuthAction return; } - $this->source = $this->trimmed('source'); // Not supported by Twitter. - - $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api'); - if (empty($this->source) || in_array($this->source, $reserved_sources)) { - $source = 'api'; - } - $this->content = $this->trimmed('text'); $this->user = $this->auth_user; diff --git a/actions/apifavoritecreate.php b/actions/apifavoritecreate.php index 3618f9401..0447a92ba 100644 --- a/actions/apifavoritecreate.php +++ b/actions/apifavoritecreate.php @@ -25,6 +25,7 @@ * @author Evan Prodromou <evan@status.net> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -123,7 +124,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction return; } - $fave = Fave::addNew($this->user, $this->notice); + $fave = Fave::addNew($this->user->getProfile(), $this->notice); if (empty($fave)) { $this->clientError( diff --git a/actions/apifavoritedestroy.php b/actions/apifavoritedestroy.php index c4daf480e..9f2efdd00 100644 --- a/actions/apifavoritedestroy.php +++ b/actions/apifavoritedestroy.php @@ -25,6 +25,7 @@ * @author Evan Prodromou <evan@status.net> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/actions/apifriendshipscreate.php b/actions/apifriendshipscreate.php index 1de2cc32e..a7ec5b28a 100644 --- a/actions/apifriendshipscreate.php +++ b/actions/apifriendshipscreate.php @@ -24,7 +24,7 @@ * @author Dan Moore <dan@moore.cx> * @author Evan Prodromou <evan@status.net> * @author Zach Copley <zach@status.net> - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -67,7 +67,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction parent::prepare($args); $this->user = $this->auth_user; - $this->other = $this->getTargetUser($id); + $this->other = $this->getTargetProfile($this->arg('id')); return true; } @@ -106,7 +106,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction if (empty($this->other)) { $this->clientError( - _('Could not follow user: User not found.'), + _('Could not follow user: profile not found.'), 403, $this->format ); diff --git a/actions/apifriendshipsdestroy.php b/actions/apifriendshipsdestroy.php index d48a57756..551d01682 100644 --- a/actions/apifriendshipsdestroy.php +++ b/actions/apifriendshipsdestroy.php @@ -24,7 +24,7 @@ * @author Dan Moore <dan@moore.cx> * @author Evan Prodromou <evan@status.net> * @author Zach Copley <zach@status.net> - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -67,7 +67,7 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction parent::prepare($args); $this->user = $this->auth_user; - $this->other = $this->getTargetUser($id); + $this->other = $this->getTargetProfile($this->arg('id')); return true; } @@ -125,8 +125,7 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction } // throws an exception on error - Subscription::cancel($this->user->getProfile(), - $this->other->getProfile()); + Subscription::cancel($this->user->getProfile(), $this->other); $this->initDocument($this->format); $this->showProfile($this->other, $this->format); diff --git a/actions/apifriendshipsexists.php b/actions/apifriendshipsexists.php index ca62b5f51..725178fd4 100644 --- a/actions/apifriendshipsexists.php +++ b/actions/apifriendshipsexists.php @@ -24,7 +24,7 @@ * @author Dan Moore <dan@moore.cx> * @author Evan Prodromou <evan@status.net> * @author Zach Copley <zach@status.net> - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -50,8 +50,8 @@ require_once INSTALLDIR . '/lib/apiprivateauth.php'; class ApiFriendshipsExistsAction extends ApiPrivateAuthAction { - var $user_a = null; - var $user_b = null; + var $profile_a = null; + var $profile_b = null; /** * Take arguments for running @@ -66,11 +66,8 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction { parent::prepare($args); - $user_a_id = $this->trimmed('user_a'); - $user_b_id = $this->trimmed('user_b'); - - $this->user_a = $this->getTargetUser($user_a_id); - $this->user_b = $this->getTargetUser($user_b_id); + $this->profile_a = $this->getTargetProfile($this->trimmed('user_a')); + $this->profile_b = $this->getTargetProfile($this->trimmed('user_b')); return true; } @@ -89,16 +86,16 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction { parent::handle($args); - if (empty($this->user_a) || empty($this->user_b)) { + if (empty($this->profile_a) || empty($this->profile_b)) { $this->clientError( - _('Two user ids or screen_names must be supplied.'), + _('Two valid IDs or screen_names must be supplied.'), 400, $this->format ); return; } - $result = $this->user_a->isSubscribed($this->user_b); + $result = Subscription::exists($this->profile_a, $this->profile_b); switch ($this->format) { case 'xml': diff --git a/actions/apigroupcreate.php b/actions/apigroupcreate.php index 145806356..d216c15cd 100644 --- a/actions/apigroupcreate.php +++ b/actions/apigroupcreate.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -263,7 +264,7 @@ class ApiGroupCreateAction extends ApiAuthAction if (!$valid) { $this->clientError( - sprintf(_('Invalid alias: "%s"'), $alias), + sprintf(_('Invalid alias: "%s".'), $alias), 403, $this->format ); diff --git a/actions/apigroupismember.php b/actions/apigroupismember.php index 97f843561..eaa4769f3 100644 --- a/actions/apigroupismember.php +++ b/actions/apigroupismember.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -92,7 +93,7 @@ class ApiGroupIsMemberAction extends ApiBareAuthAction } if (empty($this->group)) { - $this->clientError(_('Group not found!'), 404, $this->format); + $this->clientError(_('Group not found.'), 404, $this->format); return false; } diff --git a/actions/apigroupjoin.php b/actions/apigroupjoin.php index 374cf83df..5265ec629 100644 --- a/actions/apigroupjoin.php +++ b/actions/apigroupjoin.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -101,7 +102,7 @@ class ApiGroupJoinAction extends ApiAuthAction } if (empty($this->group)) { - $this->clientError(_('Group not found!'), 404, $this->format); + $this->clientError(_('Group not found.'), 404, $this->format); return false; } diff --git a/actions/apigroupleave.php b/actions/apigroupleave.php index 9848ece05..8c100d58a 100644 --- a/actions/apigroupleave.php +++ b/actions/apigroupleave.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -101,7 +102,7 @@ class ApiGroupLeaveAction extends ApiAuthAction } if (empty($this->group)) { - $this->clientError(_('Group not found!'), 404, $this->format); + $this->clientError(_('Group not found.'), 404, $this->format); return false; } diff --git a/actions/apigrouplist.php b/actions/apigrouplist.php index 98fdb0497..148c802f4 100644 --- a/actions/apigrouplist.php +++ b/actions/apigrouplist.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -66,7 +67,13 @@ class ApiGroupListAction extends ApiBareAuthAction { parent::prepare($args); - $this->user = $this->getTargetUser($id); + $this->user = $this->getTargetUser(null); + + if (empty($this->user)) { + $this->clientError(_('No such user.'), 404, $this->format); + return false; + } + $this->groups = $this->getGroups(); return true; @@ -86,12 +93,8 @@ class ApiGroupListAction extends ApiBareAuthAction { parent::handle($args); - if (empty($this->user)) { - $this->clientError(_('No such user.'), 404, $this->format); - return; - } - $sitename = common_config('site', 'name'); + // TRANS: %s is a user name $title = sprintf(_("%s's groups"), $this->user->nickname); $taguribase = TagURI::base(); $id = "tag:$taguribase:Groups"; @@ -99,10 +102,12 @@ class ApiGroupListAction extends ApiBareAuthAction 'usergroups', array('nickname' => $this->user->nickname) ); + $subtitle = sprintf( - _("Groups %1$s is a member of on %2$s."), - $this->user->nickname, - $sitename + // TRANS: Meant to convey the user %2$s is a member of each of the groups listed on site %1$s + _("%1\$s groups %2\$s is a member of."), + $sitename, + $this->user->nickname ); switch($this->format) { diff --git a/actions/apigrouplistall.php b/actions/apigrouplistall.php index e1b54a832..a8317608d 100644 --- a/actions/apigrouplistall.php +++ b/actions/apigrouplistall.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -66,7 +67,7 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction { parent::prepare($args); - $this->user = $this->getTargetUser($id); + $this->user = $this->getTargetUser(null); $this->groups = $this->getGroups(); return true; @@ -87,6 +88,7 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction parent::handle($args); $sitename = common_config('site', 'name'); + // TRANS: Message is used as a title. %s is a site name. $title = sprintf(_("%s groups"), $sitename); $taguribase = TagURI::base(); $id = "tag:$taguribase:Groups"; @@ -137,11 +139,18 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction $qry = 'SELECT user_group.* '. 'from user_group join local_group on user_group.id = local_group.group_id '. 'order by created desc '; - + $offset = intval($this->page - 1) * intval($this->count); + $limit = intval($this->count); + if (common_config('db', 'type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } $group = new User_group(); $group->query($qry); + $groups = array(); while ($group->fetch()) { $groups[] = clone($group); } diff --git a/actions/apigroupmembership.php b/actions/apigroupmembership.php index 9f72b527c..ffd5c7c7d 100644 --- a/actions/apigroupmembership.php +++ b/actions/apigroupmembership.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -88,7 +89,7 @@ class ApiGroupMembershipAction extends ApiPrivateAuthAction parent::handle($args); if (empty($this->group)) { - $this->clientError(_('Group not found!'), 404, $this->format); + $this->clientError(_('Group not found.'), 404, $this->format); return false; } diff --git a/actions/apigroupshow.php b/actions/apigroupshow.php index 5745a81f4..2998e505e 100644 --- a/actions/apigroupshow.php +++ b/actions/apigroupshow.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -79,7 +80,7 @@ class ApiGroupShowAction extends ApiPrivateAuthAction common_redirect(common_local_url('ApiGroupShow', $args), 301); } else { $this->clientError( - _('Group not found!'), + _('Group not found.'), 404, $this->format ); diff --git a/actions/apimediaupload.php b/actions/apimediaupload.php new file mode 100644 index 000000000..ec316edc8 --- /dev/null +++ b/actions/apimediaupload.php @@ -0,0 +1,141 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Upload an image via the API + * + * 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 + * @author Zach Copley <zach@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; +require_once INSTALLDIR . '/lib/mediafile.php'; + +/** + * Upload an image via the API. Returns a shortened URL for the image + * to the user. + * + * @category API + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiMediaUploadAction extends ApiAuthAction +{ + /** + * Handle the request + * + * Grab the file from the 'media' param, then store, and shorten + * + * @todo Upload throttle! + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError( + _('This method requires a POST.'), + 400, $this->format + ); + return; + } + + // Workaround for PHP returning empty $_POST and $_FILES when POST + // length > post_max_size in php.ini + + if (empty($_FILES) + && empty($_POST) + && ($_SERVER['CONTENT_LENGTH'] > 0) + ) { + $msg = _('The server was unable to handle that much POST ' . + 'data (%s bytes) due to its current configuration.'); + + $this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); + return; + } + + $upload = null; + + try { + $upload = MediaFile::fromUpload('media', $this->auth_user); + } catch (ClientException $ce) { + $this->clientError($ce->getMessage()); + return; + } + + if (isset($upload)) { + $this->showResponse($upload); + } else { + $this->clientError('Upload failed.'); + return; + } + } + + /** + * Show a Twitpic-like response with the ID of the media file + * and a (hopefully) shortened URL for it. + * + * @param File $upload the uploaded file + * + * @return void + */ + function showResponse($upload) + { + $this->initDocument(); + $this->elementStart('rsp', array('stat' => 'ok')); + $this->element('mediaid', null, $upload->fileRecord->id); + $this->element('mediaurl', null, $upload->shortUrl()); + $this->elementEnd('rsp'); + $this->endDocument(); + } + + /** + * Overrided clientError to show a more Twitpic-like error + * + * @param String $msg an error message + * + */ + function clientError($msg) + { + $this->initDocument(); + $this->elementStart('rsp', array('stat' => 'fail')); + + // @todo add in error code + $errAttr = array('msg' => $msg); + + $this->element('err', $errAttr, null); + $this->elementEnd('rsp'); + $this->endDocument(); + } + +} diff --git a/actions/twitapisearchatom.php b/actions/apisearchatom.php index 24aa619bd..60bb8b040 100644 --- a/actions/twitapisearchatom.php +++ b/actions/apisearchatom.php @@ -22,7 +22,7 @@ * @category Search * @package StatusNet * @author Zach Copley <zach@status.net> - * @copyright 2008-2009 StatusNet, Inc. + * @copyright 2008-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +require_once INSTALLDIR.'/lib/apiprivateauth.php'; + /** * Action for outputting search results in Twitter compatible Atom * format. @@ -44,10 +46,10 @@ 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/ * - * @see ApiAction + * @see ApiPrivateAuthAction */ -class TwitapisearchatomAction extends ApiAction +class ApiSearchAtomAction extends ApiPrivateAuthAction { var $cnt; @@ -96,8 +98,11 @@ class TwitapisearchatomAction extends ApiAction function prepare($args) { + common_debug("in apisearchatom prepare()"); + parent::prepare($args); + $this->query = $this->trimmed('q'); $this->lang = $this->trimmed('lang'); $this->rpp = $this->trimmed('rpp'); @@ -138,6 +143,7 @@ class TwitapisearchatomAction extends ApiAction function handle($args) { parent::handle($args); + common_debug("In apisearchatom handle()"); $this->showAtom(); } @@ -342,10 +348,24 @@ class TwitapisearchatomAction extends ApiAction 'rel' => 'related', 'href' => $profile->avatarUrl())); - // TODO: Here is where we'd put in a link to an atom feed for threads + // @todo: Here is where we'd put in a link to an atom feed for threads + + $source = null; + + $ns = $notice->getSource(); + if ($ns) { + if (!empty($ns->name) && !empty($ns->url)) { + $source = '<a href="' + . htmlspecialchars($ns->url) + . '" rel="nofollow">' + . htmlspecialchars($ns->name) + . '</a>'; + } else { + $source = $ns->code; + } + } - $this->element("twitter:source", null, - htmlentities($this->sourceLink($notice->source))); + $this->element("twitter:source", null, $source); $this->elementStart('author'); diff --git a/actions/twitapisearchjson.php b/actions/apisearchjson.php index b5c006aa7..e44634684 100644 --- a/actions/twitapisearchjson.php +++ b/actions/apisearchjson.php @@ -22,7 +22,7 @@ * @category Search * @package StatusNet * @author Zach Copley <zach@status.net> - * @copyright 2008-2009 StatusNet, Inc. + * @copyright 2008-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -31,6 +31,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +require_once INSTALLDIR.'/lib/apiprivateauth.php'; require_once INSTALLDIR.'/lib/jsonsearchresultslist.php'; /** @@ -44,7 +45,7 @@ require_once INSTALLDIR.'/lib/jsonsearchresultslist.php'; * @see ApiAction */ -class TwitapisearchjsonAction extends ApiAction +class ApiSearchJSONAction extends ApiPrivateAuthAction { var $query; var $lang; @@ -64,6 +65,8 @@ class TwitapisearchjsonAction extends ApiAction function prepare($args) { + common_debug("apisearchjson prepare()"); + parent::prepare($args); $this->query = $this->trimmed('q'); diff --git a/actions/apistatusesdestroy.php b/actions/apistatusesdestroy.php index f7d52f020..0dfeb4812 100644 --- a/actions/apistatusesdestroy.php +++ b/actions/apistatusesdestroy.php @@ -29,6 +29,7 @@ * @author Robin Millette <robin@millette.info> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -57,7 +58,7 @@ require_once INSTALLDIR . '/lib/apiauth.php'; class ApiStatusesDestroyAction extends ApiAuthAction { - var $status = null; + var $status = null; /** * Take arguments for running @@ -99,39 +100,43 @@ class ApiStatusesDestroyAction extends ApiAuthAction parent::handle($args); if (!in_array($this->format, array('xml', 'json'))) { - $this->clientError(_('API method not found.'), $code = 404); - return; + $this->clientError( + _('API method not found.'), + 404 + ); + return; } - if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { - $this->clientError(_('This method requires a POST or DELETE.'), - 400, $this->format); - return; - } - - if (empty($this->notice)) { - $this->clientError(_('No status found with that ID.'), - 404, $this->format); - return; - } - - if ($this->user->id == $this->notice->profile_id) { - $replies = new Reply; - $replies->get('notice_id', $this->notice_id); - $replies->delete(); - $this->notice->delete(); - - if ($this->format == 'xml') { - $this->showSingleXmlStatus($this->notice); - } elseif ($this->format == 'json') { - $this->show_single_json_status($this->notice); - } - } else { - $this->clientError(_('You may not delete another user\'s status.'), - 403, $this->format); - } - - $this->showNotice(); + if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { + $this->clientError( + _('This method requires a POST or DELETE.'), + 400, + $this->format + ); + return; + } + + if (empty($this->notice)) { + $this->clientError( + _('No status found with that ID.'), + 404, $this->format + ); + return; + } + + if ($this->user->id == $this->notice->profile_id) { + $replies = new Reply; + $replies->get('notice_id', $this->notice_id); + $replies->delete(); + $this->notice->delete(); + $this->showNotice(); + } else { + $this->clientError( + _('You may not delete another user\'s status.'), + 403, + $this->format + ); + } } /** diff --git a/actions/apistatusesretweet.php b/actions/apistatusesretweet.php index 128c881e2..9aa337485 100644 --- a/actions/apistatusesretweet.php +++ b/actions/apistatusesretweet.php @@ -79,7 +79,7 @@ class ApiStatusesRetweetAction extends ApiAuthAction $this->user = $this->auth_user; - if ($this->user->id == $notice->profile_id) { + if ($this->user->id == $this->original->profile_id) { $this->clientError(_('Cannot repeat your own notice.'), 400, $this->format); return false; diff --git a/actions/apistatusesshow.php b/actions/apistatusesshow.php index 0315d2953..476820a43 100644 --- a/actions/apistatusesshow.php +++ b/actions/apistatusesshow.php @@ -29,6 +29,7 @@ * @author Robin Millette <robin@millette.info> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php index bf367e1e1..d65a068f5 100644 --- a/actions/apistatusesupdate.php +++ b/actions/apistatusesupdate.php @@ -29,10 +29,102 @@ * @author Robin Millette <robin@millette.info> * @author Zach Copley <zach@status.net> * @copyright 2009-2010 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ +/* External API usage documentation. Please update when you change how this method works. */ + +/*! @page statusesupdate statuses/update + + @section Description + Updates the authenticating user's status. Requires the status parameter specified below. + Request must be a POST. + + @par URL pattern + /api/statuses/update.:format + + @par Formats (:format) + xml, json + + @par HTTP Method(s) + POST + + @par Requires Authentication + Yes + + @param status (Required) The URL-encoded text of the status update. + @param source (Optional) The source of the status. + @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. + @param media (Optional) a media upload, such as an image or movie file. + + @sa @ref authentication + @sa @ref apiroot + + @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 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. + + @subsection exampleusage Example usage + + @verbatim + curl -u username:password http://example.com/api/statuses/update.xml -d status='Howdy!' -d lat='30.468' -d long='-94.743' + @endverbatim + + @subsection exampleresponse Example response + + @verbatim + <?xml version="1.0" encoding="UTF-8"?> + <status> + <text>Howdy!</text> + <truncated>false</truncated> + <created_at>Tue Mar 30 23:28:05 +0000 2010</created_at> + <in_reply_to_status_id/> + <source>api</source> + <id>26668724</id> + <in_reply_to_user_id/> + <in_reply_to_screen_name/> + <geo xmlns:georss="http://www.georss.org/georss"> + <georss:point>30.468 -94.743</georss:point> + </geo> + <favorited>false</favorited> + <user> + <id>25803</id> + <name>Jed Sanders</name> + <screen_name>jedsanders</screen_name> + <location>Hoop and Holler, Texas</location> + <description>I like to think of myself as America's Favorite.</description> + <profile_image_url>http://avatar.example.com/25803-48-20080924200604.png</profile_image_url> + <url>http://jedsanders.net</url> + <protected>false</protected> + <followers_count>5</followers_count> + <profile_background_color/> + <profile_text_color/> + <profile_link_color/> + <profile_sidebar_fill_color/> + <profile_sidebar_border_color/> + <friends_count>2</friends_count> + <created_at>Wed Sep 24 20:04:00 +0000 2008</created_at> + <favourites_count>0</favourites_count> + <utc_offset>0</utc_offset> + <time_zone>UTC</time_zone> + <profile_background_image_url/> + <profile_background_tile>false</profile_background_tile> + <statuses_count>70</statuses_count> + <following>true</following> + <notifications>true</notifications> + </user> + </status> + @endverbatim +*/ + if (!defined('STATUSNET')) { exit(1); } @@ -64,8 +156,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction var $lat = null; var $lon = null; - static $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api'); - /** * Take arguments for running * @@ -80,19 +170,9 @@ class ApiStatusesUpdateAction extends ApiAuthAction parent::prepare($args); $this->status = $this->trimmed('status'); - $this->source = $this->trimmed('source'); $this->lat = $this->trimmed('lat'); $this->lon = $this->trimmed('long'); - // try to set the source attr from OAuth app - if (empty($this->source)) { - $this->source = $this->oauth_source; - } - - if (empty($this->source) || in_array($this->source, self::$reserved_sources)) { - $this->source = 'api'; - } - $this->in_reply_to_status_id = intval($this->trimmed('in_reply_to_status_id')); @@ -199,7 +279,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction $reply_to = $this->in_reply_to_status_id; } else { $this->clientError( - _('Not found'), + _('Not found.'), $code = 404, $this->format ); @@ -244,11 +324,17 @@ class ApiStatusesUpdateAction extends ApiAuthAction $options = array_merge($options, $locOptions); } - $this->notice = - Notice::saveNew($this->auth_user->id, - $content, - $this->source, - $options); + try { + $this->notice = Notice::saveNew( + $this->auth_user->id, + $content, + $this->source, + $options + ); + } catch (Exception $e) { + $this->clientError($e->getMessage()); + return; + } if (isset($upload)) { $upload->attachToNotice($this->notice); diff --git a/actions/apistatusnetconfig.php b/actions/apistatusnetconfig.php index bff8313b5..76d37ea97 100644 --- a/actions/apistatusnetconfig.php +++ b/actions/apistatusnetconfig.php @@ -97,19 +97,25 @@ class ApiStatusnetConfigAction extends ApiAction // XXX: check that all sections and settings are legal XML elements - common_debug(var_export($this->keys, true)); - foreach ($this->keys as $section => $settings) { $this->elementStart($section); foreach ($settings as $setting) { $value = common_config($section, $setting); if (is_array($value)) { $value = implode(',', $value); - } else if ($value === false) { + } else if ($value === false || $value == '0') { $value = 'false'; - } else if ($value === true) { + } else if ($value === true || $value == '1') { $value = 'true'; } + + // return theme logo if there's no site specific one + if (empty($value)) { + if ($section == 'site' && $setting == 'logo') { + $value = Theme::path('logo.png'); + } + } + $this->element($setting, null, $value); } $this->elementEnd($section); diff --git a/actions/apisubscriptions.php b/actions/apisubscriptions.php index 0ba324057..63d65f289 100644 --- a/actions/apisubscriptions.php +++ b/actions/apisubscriptions.php @@ -206,7 +206,8 @@ class ApiSubscriptionsAction extends ApiBareAuthAction { switch ($this->format) { case 'xml': - $this->elementStart('users', array('type' => 'array')); + $this->elementStart('users', array('type' => 'array', + 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); foreach ($this->profiles as $profile) { $this->showProfile( $profile, diff --git a/actions/apitimelinefavorites.php b/actions/apitimelinefavorites.php index c89d02247..7228960c0 100644 --- a/actions/apitimelinefavorites.php +++ b/actions/apitimelinefavorites.php @@ -23,7 +23,9 @@ * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> * @author Evan Prodromou <evan@status.net> - * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @author Zach Copley <zach@status.net> + * @copyright 2009-2010 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -123,29 +125,33 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE); + $link = common_local_url( + 'showfavorites', + array('nickname' => $this->user->nickname) + ); + + $self = $this->getSelfUri(); + switch($this->format) { case 'xml': $this->showXmlTimeline($this->notices); break; case 'rss': - $link = common_local_url( - 'showfavorites', - array('nickname' => $this->user->nickname) - ); $this->showRssTimeline( $this->notices, $title, $link, $subtitle, null, - $logo + $logo, + $self ); break; case 'atom': header('Content-Type: application/atom+xml; charset=utf-8'); - $atom = new AtomNoticeFeed(); + $atom = new AtomNoticeFeed($this->auth_user); $atom->setId($id); $atom->setTitle($title); @@ -153,23 +159,8 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction $atom->setLogo($logo); $atom->setUpdated('now'); - $atom->addLink( - common_local_url( - 'showfavorites', - array('nickname' => $this->user->nickname) - ) - ); - - $id = $this->arg('id'); - $aargs = array('format' => 'atom'); - if (!empty($id)) { - $aargs['id'] = $id; - } - - $atom->addLink( - $this->getSelfUri('ApiTimelineFavorites', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); + $atom->addLink($link); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); @@ -195,17 +186,23 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction { $notices = array(); + common_debug("since id = " . $this->since_id . " max id = " . $this->max_id); + if (!empty($this->auth_user) && $this->auth_user->id == $this->user->id) { $notice = $this->user->favoriteNotices( + true, ($this->page-1) * $this->count, $this->count, - true + $this->since_id, + $this->max_id ); } else { $notice = $this->user->favoriteNotices( + false, ($this->page-1) * $this->count, $this->count, - false + $this->since_id, + $this->max_id ); } diff --git a/actions/apitimelinefriends.php b/actions/apitimelinefriends.php index 9ef3ace60..40ce35979 100644 --- a/actions/apitimelinefriends.php +++ b/actions/apitimelinefriends.php @@ -28,11 +28,107 @@ * @author Mike Cochrane <mikec@mikenz.geek.nz> * @author Robin Millette <robin@millette.info> * @author Zach Copley <zach@status.net> - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ +/* External API usage documentation. Please update when you change how this method works. */ + +/*! @page friendstimeline statuses/friends_timeline + + @section Description + Returns the 20 most recent statuses posted by the authenticating + user and that user's friends. This is the equivalent of "You and + friends" page in the web interface. + + @par URL patterns + @li /api/statuses/friends_timeline.:format + @li /api/statuses/friends_timeline/:id.:format + + @par Formats (:format) + xml, json, rss, atom + + @par ID (:id) + username, user id + + @par HTTP Method(s) + GET + + @par Requires Authentication + Sometimes (see: @ref authentication) + + @param user_id (Optional) Specifies a user by ID + @param screen_name (Optional) Specifies a user by screename (nickname) + @param since_id (Optional) Returns only statuses with an ID greater + than (that is, more recent than) the specified ID. + @param max_id (Optional) Returns only statuses with an ID less than + (that is, older than) or equal to the specified ID. + @param count (Optional) Specifies the number of statuses to retrieve. + @param page (Optional) Specifies the page of results to retrieve. + + @sa @ref authentication + @sa @ref apiroot + + @subsection usagenotes Usage notes + @li The URL pattern is relative to the @ref apiroot. + @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>). + + @subsection exampleusage Example usage + + @verbatim + curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2 + @endverbatim + + @subsection exampleresponse Example response + + @verbatim + <?xml version="1.0"?> + <statuses type="array"> + <status> + <text>back from the !yul !drupal meet with Evolving Web folk, @anarcat, @webchick and others, and an interesting refresher on SQL indexing</text> + <truncated>false</truncated> + <created_at>Wed Mar 31 01:33:02 +0000 2010</created_at> + <in_reply_to_status_id/> + <source><a href="http://code.google.com/p/microblog-purple/">mbpidgin</a></source> + <id>26674201</id> + <in_reply_to_user_id/> + <in_reply_to_screen_name/> + <geo/> + <favorited>false</favorited> + <user> + <id>246</id> + <name>Mark</name> + <screen_name>lambic</screen_name> + <location>Montreal, Canada</location> + <description>Geek</description> + <profile_image_url>http://avatar.identi.ca/246-48-20080702141545.png</profile_image_url> + <url>http://lambic.co.uk</url> + <protected>false</protected> + <followers_count>73</followers_count> + <profile_background_color>#F0F2F5</profile_background_color> + <profile_text_color/> + <profile_link_color>#002E6E</profile_link_color> + <profile_sidebar_fill_color>#CEE1E9</profile_sidebar_fill_color> + <profile_sidebar_border_color/> + <friends_count>58</friends_count> + <created_at>Wed Jul 02 14:12:15 +0000 2008</created_at> + <favourites_count>2</favourites_count> + <utc_offset>-14400</utc_offset> + <time_zone>US/Eastern</time_zone> + <profile_background_image_url/> + <profile_background_tile>false</profile_background_tile> + <statuses_count>933</statuses_count> + <following>false</following> + <notifications>false</notifications> + </user> + </status> + </statuses> + @endverbatim +*/ + if (!defined('STATUSNET')) { exit(1); } @@ -116,10 +212,19 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction $id = "tag:$taguribase:FriendsTimeline:" . $this->user->id; $subtitle = sprintf( + // TRANS: Message is used as a subtitle. %1$s is a user nickname, %2$s is a site name. _('Updates from %1$s and friends on %2$s!'), - $this->user->nickname, $sitename + $this->user->nickname, + $sitename + ); + + $link = common_local_url( + 'all', + array('nickname' => $this->user->nickname) ); + $self = $this->getSelfUri(); + $logo = (!empty($avatar)) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE); @@ -130,50 +235,29 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction break; case 'rss': - $link = common_local_url( - 'all', array( - 'nickname' => $this->user->nickname - ) - ); - $this->showRssTimeline( $this->notices, $title, $link, $subtitle, null, - $logo + $logo, + $self ); break; case 'atom': header('Content-Type: application/atom+xml; charset=utf-8'); - $atom = new AtomNoticeFeed(); + $atom = new AtomNoticeFeed($this->auth_user); $atom->setId($id); $atom->setTitle($title); $atom->setSubtitle($subtitle); $atom->setLogo($logo); $atom->setUpdated('now'); - - $atom->addLink( - common_local_url( - 'all', - array('nickname' => $this->user->nickname) - ) - ); - - $id = $this->arg('id'); - $aargs = array('format' => 'atom'); - if (!empty($id)) { - $aargs['id'] = $id; - } - - $atom->addLink( - $this->getSelfUri('ApiTimelineFriends', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); + $atom->addLink($link); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); diff --git a/actions/apitimelinegroup.php b/actions/apitimelinegroup.php index d0af49844..c4a6a18d2 100644 --- a/actions/apitimelinegroup.php +++ b/actions/apitimelinegroup.php @@ -26,6 +26,7 @@ * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -88,7 +89,7 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction parent::handle($args); if (empty($this->group)) { - $this->clientError(_('Group not found!'), 404, $this->format); + $this->clientError(_('Group not found.'), 404, $this->format); return false; } @@ -104,30 +105,24 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction function showTimeline() { - $sitename = common_config('site', 'name'); - $avatar = $this->group->homepage_logo; - $title = sprintf(_("%s timeline"), $this->group->nickname); - - $subtitle = sprintf( - _('Updates from %1$s on %2$s!'), - $this->group->nickname, - $sitename - ); + // We'll pull common formatting out of this for other formats + $atom = new AtomGroupNoticeFeed($this->group, $this->auth_user); - $logo = ($avatar) ? $avatar : User_group::defaultLogo(AVATAR_PROFILE_SIZE); + $self = $this->getSelfUri(); switch($this->format) { case 'xml': $this->showXmlTimeline($this->notices); break; case 'rss': - $this->showRssTimeline( + $this->showRssTimeline( $this->notices, - $title, + $atom->title, $this->group->homeUrl(), - $subtitle, + $atom->subtitle, null, - $logo + $atom->logo, + $self ); break; case 'atom': @@ -135,40 +130,12 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction header('Content-Type: application/atom+xml; charset=utf-8'); try { - - $atom = new AtomGroupNoticeFeed($this->group); - - // @todo set all this Atom junk up inside the feed class - - $atom->setId($id); - $atom->setTitle($title); - $atom->setSubtitle($subtitle); - $atom->setLogo($logo); - $atom->setUpdated('now'); - $atom->addAuthorRaw($this->group->asAtomAuthor()); $atom->setActivitySubject($this->group->asActivitySubject()); - - $atom->addLink($this->group->homeUrl()); - - $id = $this->arg('id'); - $aargs = array('format' => 'atom'); - if (!empty($id)) { - $aargs['id'] = $id; - } - - $atom->setId($this->getSelfUri('ApiTimelineGroup', $aargs)); - - $atom->addLink( - $this->getSelfUri('ApiTimelineGroup', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); - + $atom->setId($self); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); - - //$this->raw($atom->getString()); - print $atom->getString(); // temp hack until PuSH feeds are redone cleanly - + $this->raw($atom->getString()); } catch (Atom10FeedException $e) { $this->serverError( 'Could not generate feed for group - ' . $e->getMessage() diff --git a/actions/apitimelinehome.php b/actions/apitimelinehome.php index abd387786..27eb74169 100644 --- a/actions/apitimelinehome.php +++ b/actions/apitimelinehome.php @@ -29,6 +29,7 @@ * @author Robin Millette <robin@millette.info> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -72,7 +73,7 @@ class ApiTimelineHomeAction extends ApiBareAuthAction function prepare($args) { parent::prepare($args); - common_debug("api home_timeline"); + $this->user = $this->getTargetUser($this->arg('id')); if (empty($this->user)) { @@ -117,12 +118,20 @@ class ApiTimelineHomeAction extends ApiBareAuthAction $id = "tag:$taguribase:HomeTimeline:" . $this->user->id; $subtitle = sprintf( + // TRANS: Message is used as a subtitle. %1$s is a user nickname, %2$s is a site name. _('Updates from %1$s and friends on %2$s!'), $this->user->nickname, $sitename ); - $logo = (!empty($avatar)) - ? $avatar->displayUrl() + $link = common_local_url( + 'all', + array('nickname' => $this->user->nickname) + ); + + $self = $this->getSelfUri(); + + $logo = (!empty($avatar)) + ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE); switch($this->format) { @@ -130,24 +139,21 @@ class ApiTimelineHomeAction extends ApiBareAuthAction $this->showXmlTimeline($this->notices); break; case 'rss': - $link = common_local_url( - 'all', - array('nickname' => $this->user->nickname) - ); $this->showRssTimeline( $this->notices, $title, $link, $subtitle, null, - $logo + $logo, + $self ); break; case 'atom': header('Content-Type: application/atom+xml; charset=utf-8'); - $atom = new AtomNoticeFeed(); + $atom = new AtomNoticeFeed($this->auth_user); $atom->setId($id); $atom->setTitle($title); @@ -155,23 +161,8 @@ class ApiTimelineHomeAction extends ApiBareAuthAction $atom->setLogo($logo); $atom->setUpdated('now'); - $atom->addLink( - common_local_url( - 'all', - array('nickname' => $this->user->nickname) - ) - ); - - $id = $this->arg('id'); - $aargs = array('format' => 'atom'); - if (!empty($id)) { - $aargs['id'] = $id; - } - - $atom->addLink( - $this->getSelfUri('ApiTimelineHome', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); + $atom->addLink($link); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); $this->raw($atom->getString()); diff --git a/actions/apitimelinementions.php b/actions/apitimelinementions.php index 31627ab7b..ed1ad20e3 100644 --- a/actions/apitimelinementions.php +++ b/actions/apitimelinementions.php @@ -29,6 +29,7 @@ * @author Robin Millette <robin@millette.info> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -123,6 +124,9 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction 'replies', array('nickname' => $this->user->nickname) ); + + $self = $this->getSelfUri(); + $subtitle = sprintf( _('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $this->user->nickname, $profile->getBestName() @@ -134,11 +138,21 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction $this->showXmlTimeline($this->notices); break; case 'rss': - $this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $logo); + $this->showRssTimeline( + $this->notices, + $title, + $link, + $subtitle, + null, + $logo, + $self + ); break; case 'atom': - $atom = new AtomNoticeFeed(); + header('Content-Type: application/atom+xml; charset=utf-8'); + + $atom = new AtomNoticeFeed($this->auth_user); $atom->setId($id); $atom->setTitle($title); @@ -146,23 +160,8 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction $atom->setLogo($logo); $atom->setUpdated('now'); - $atom->addLink( - common_local_url( - 'replies', - array('nickname' => $this->user->nickname) - ) - ); - - $id = $this->arg('id'); - $aargs = array('format' => 'atom'); - if (!empty($id)) { - $aargs['id'] = $id; - } - - $atom->addLink( - $this->getSelfUri('ApiTimelineMentions', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); + $atom->addLink($link); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); $this->raw($atom->getString()); diff --git a/actions/apitimelinepublic.php b/actions/apitimelinepublic.php index 3e4dad690..f90164288 100644 --- a/actions/apitimelinepublic.php +++ b/actions/apitimelinepublic.php @@ -29,6 +29,7 @@ * @author Robin Millette <robin@millette.info> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -55,6 +56,95 @@ require_once INSTALLDIR . '/lib/apiprivateauth.php'; * @link http://status.net/ */ +/* External API usage documentation. Please update when you change how this method works. */ + +/*! @page publictimeline statuses/public_timeline + + @section Description + Returns the 20 most recent notices from users throughout the system who have + uploaded their own avatars. Depending on configuration, it may or may not + not include notices from automatic posting services. + + @par URL patterns + @li /api/statuses/public_timeline.:format + + @par Formats (:format) + xml, json, rss, atom + + @par HTTP Method(s) + GET + + @par Requires Authentication + No + + @param since_id (Optional) Returns only statuses with an ID greater + than (that is, more recent than) the specified ID. + @param max_id (Optional) Returns only statuses with an ID less than + (that is, older than) or equal to the specified ID. + @param count (Optional) Specifies the number of statuses to retrieve. + @param page (Optional) Specifies the page of results to retrieve. + + @sa @ref apiroot + + @subsection usagenotes Usage notes + @li The URL pattern is relative to the @ref apiroot. + @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>). + + @subsection exampleusage Example usage + + @verbatim + curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2 + @endverbatim + + @subsection exampleresponse Example response + + @verbatim + <?xml version="1.0" encoding="UTF-8"?> + <statuses type="array"> + <status> + <text>@skwashd oh, commbank reenabled me super quick both times. but disconcerting when you don't expect it though</text> + <truncated>false</truncated> + <created_at>Sat Apr 17 00:49:12 +0000 2010</created_at> + <in_reply_to_status_id>28838393</in_reply_to_status_id> + <source>xmpp</source> + <id>28838456</id> + <in_reply_to_user_id>39303</in_reply_to_user_id> + <in_reply_to_screen_name>skwashd</in_reply_to_screen_name> + <geo></geo> + <favorited>false</favorited> + <user> + <id>44517</id> + <name>joshua may</name> + <screen_name>notjosh</screen_name> + <location></location> + <description></description> + <profile_image_url>http://avatar.identi.ca/44517-48-20090321004106.jpeg</profile_image_url> + <url></url> + <protected>false</protected> + <followers_count>17</followers_count> + <profile_background_color></profile_background_color> + <profile_text_color></profile_text_color> + <profile_link_color></profile_link_color> + <profile_sidebar_fill_color></profile_sidebar_fill_color> + <profile_sidebar_border_color></profile_sidebar_border_color> + <friends_count>20</friends_count> + <created_at>Sat Mar 21 00:40:25 +0000 2009</created_at> + <favourites_count>0</favourites_count> + <utc_offset>0</utc_offset> + <time_zone>UTC</time_zone> + <profile_background_image_url></profile_background_image_url> + <profile_background_tile>false</profile_background_tile> + <statuses_count>100</statuses_count> + <following>false</following> + <notifications>false</notifications> + </user> + </status> + [....] + </statuses> +@endverbatim +*/ + class ApiTimelinePublicAction extends ApiPrivateAuthAction { @@ -107,7 +197,8 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction $title = sprintf(_("%s public timeline"), $sitename); $taguribase = TagURI::base(); $id = "tag:$taguribase:PublicTimeline"; - $link = common_root_url(); + $link = common_local_url('public'); + $self = $this->getSelfUri(); $subtitle = sprintf(_("%s updates from everyone!"), $sitename); switch($this->format) { @@ -115,27 +206,29 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction $this->showXmlTimeline($this->notices); break; case 'rss': - $this->showRssTimeline($this->notices, $title, $link, $subtitle, null, $sitelogo); + $this->showRssTimeline( + $this->notices, + $title, + $link, + $subtitle, + null, + $sitelogo, + $self + ); break; case 'atom': - $atom = new AtomNoticeFeed(); + header('Content-Type: application/atom+xml; charset=utf-8'); + + $atom = new AtomNoticeFeed($this->auth_user); $atom->setId($id); $atom->setTitle($title); $atom->setSubtitle($subtitle); $atom->setLogo($sitelogo); $atom->setUpdated('now'); - $atom->addLink(common_local_url('public')); - - $atom->addLink( - $this->getSelfUri( - 'ApiTimelinePublic', array('format' => 'atom') - ), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); - + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); $this->raw($atom->getString()); diff --git a/actions/apitimelineretweetedbyme.php b/actions/apitimelineretweetedbyme.php index 564e98619..af05623cd 100644 --- a/actions/apitimelineretweetedbyme.php +++ b/actions/apitimelineretweetedbyme.php @@ -69,7 +69,7 @@ class ApiTimelineRetweetedByMeAction extends ApiAuthAction { parent::prepare($args); - $this->serverError('Unimplemented', 503); + $this->serverError('Unimplemented.', 503); return false; } diff --git a/actions/apitimelineretweetsofme.php b/actions/apitimelineretweetsofme.php index c77912fd0..ea922fc42 100644 --- a/actions/apitimelineretweetsofme.php +++ b/actions/apitimelineretweetsofme.php @@ -117,7 +117,7 @@ class ApiTimelineRetweetsOfMeAction extends ApiAuthAction header('Content-Type: application/atom+xml; charset=utf-8'); - $atom = new AtomNoticeFeed(); + $atom = new AtomNoticeFeed($this->auth_user); $atom->setId($id); $atom->setTitle($title); diff --git a/actions/apitimelinetag.php b/actions/apitimelinetag.php index a29061fcc..c7ec172ae 100644 --- a/actions/apitimelinetag.php +++ b/actions/apitimelinetag.php @@ -25,7 +25,8 @@ * @author Evan Prodromou <evan@status.net> * @author Jeffery To <jeffery.to@gmail.com> * @author Zach Copley <zach@status.net> - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -67,6 +68,8 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction { parent::prepare($args); + common_debug("apitimelinetag prepare()"); + $this->tag = $this->arg('tag'); $this->notices = $this->getNotices(); @@ -108,29 +111,35 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction $taguribase = TagURI::base(); $id = "tag:$taguribase:TagTimeline:".$tag; + $link = common_local_url( + 'tag', + array('tag' => $this->tag) + ); + + $self = $this->getSelfUri(); + + common_debug("self link is: $self"); + switch($this->format) { case 'xml': $this->showXmlTimeline($this->notices); break; case 'rss': - $link = common_local_url( - 'tag', - array('tag' => $this->tag) - ); $this->showRssTimeline( $this->notices, $title, $link, $subtitle, null, - $sitelogo + $sitelogo, + $self ); break; case 'atom': header('Content-Type: application/atom+xml; charset=utf-8'); - $atom = new AtomNoticeFeed(); + $atom = new AtomNoticeFeed($this->auth_user); $atom->setId($id); $atom->setTitle($title); @@ -138,22 +147,8 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction $atom->setLogo($logo); $atom->setUpdated('now'); - $atom->addLink( - common_local_url( - 'tag', - array('tag' => $this->tag) - ) - ); - - $aargs = array('format' => 'atom'); - if (!empty($this->tag)) { - $aargs['tag'] = $this->tag; - } - - $atom->addLink( - $this->getSelfUri('ApiTimelineTag', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); + $atom->addLink($link); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); $this->raw($atom->getString()); diff --git a/actions/apitimelineuser.php b/actions/apitimelineuser.php index 94491946c..17a283663 100644 --- a/actions/apitimelineuser.php +++ b/actions/apitimelineuser.php @@ -29,6 +29,7 @@ * @author Robin Millette <robin@millette.info> * @author Zach Copley <zach@status.net> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -112,19 +113,17 @@ class ApiTimelineUserAction extends ApiBareAuthAction function showTimeline() { $profile = $this->user->getProfile(); - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - $sitename = common_config('site', 'name'); - $title = sprintf(_("%s timeline"), $this->user->nickname); - $link = common_local_url( + // We'll use the shared params from the Atom stub + // for other feed types. + $atom = new AtomUserNoticeFeed($this->user, $this->auth_user); + + $link = common_local_url( 'showstream', array('nickname' => $this->user->nickname) ); - $subtitle = sprintf( - _('Updates from %1$s on %2$s!'), - $this->user->nickname, $sitename - ); - $logo = ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE); + + $self = $this->getSelfUri(); // FriendFeed's SUP protocol // Also added RSS and Atom feeds @@ -138,55 +137,23 @@ class ApiTimelineUserAction extends ApiBareAuthAction break; case 'rss': $this->showRssTimeline( - $this->notices, $title, $link, - $subtitle, $suplink, $logo + $this->notices, + $atom->title, + $link, + $atom->subtitle, + $suplink, + $atom->logo, + $self ); break; case 'atom': header('Content-Type: application/atom+xml; charset=utf-8'); - // @todo set all this Atom junk up inside the feed class - - $atom = new AtomUserNoticeFeed($this->user); - - $atom->setTitle($title); - $atom->setSubtitle($subtitle); - $atom->setLogo($logo); - $atom->setUpdated('now'); - - $atom->addLink( - common_local_url( - 'showstream', - array('nickname' => $this->user->nickname) - ) - ); - - $id = $this->arg('id'); - $aargs = array('format' => 'atom'); - if (!empty($id)) { - $aargs['id'] = $id; - } - - $atom->setId($this->getSelfUri('ApiTimelineUser', $aargs)); - - $atom->addLink( - $this->getSelfUri('ApiTimelineUser', $aargs), - array('rel' => 'self', 'type' => 'application/atom+xml') - ); - - $atom->addLink( - $suplink, - array( - 'rel' => 'http://api.friendfeed.com/2008/03#sup', - 'type' => 'application/json' - ) - ); - + $atom->setId($self); + $atom->setSelfLink($self); $atom->addEntryFromNotices($this->notices); - - #$this->raw($atom->getString()); - print $atom->getString(); // temporary for output buffering + $this->raw($atom->getString()); break; case 'json': diff --git a/actions/twitapitrends.php b/actions/apitrends.php index 5a04569a2..5b74636c6 100644 --- a/actions/twitapitrends.php +++ b/actions/apitrends.php @@ -22,7 +22,7 @@ * @category Search * @package StatusNet * @author Zach Copley <zach@status.net> - * @copyright 2008-2009 StatusNet, Inc. + * @copyright 2008-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +require_once INSTALLDIR.'/lib/apiprivateauth.php'; + /** * Returns the top ten queries that are currently trending * @@ -43,7 +45,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @see ApiAction */ -class TwitapitrendsAction extends ApiAction +class ApiTrendsAction extends ApiPrivateAuthAction { var $callback; @@ -82,7 +84,7 @@ class TwitapitrendsAction extends ApiAction */ function showTrends() { - $this->serverError(_('API method under construction.'), $code = 501); + $this->serverError(_('API method under construction.'), 501); } }
\ No newline at end of file diff --git a/actions/apiusershow.php b/actions/apiusershow.php index 6c8fad49b..28993102c 100644 --- a/actions/apiusershow.php +++ b/actions/apiusershow.php @@ -113,7 +113,7 @@ class ApiUserShowAction extends ApiPrivateAuthAction if ($this->format == 'xml') { $this->initDocument('xml'); - $this->showTwitterXmlUser($twitter_user); + $this->showTwitterXmlUser($twitter_user, 'user', true); $this->endDocument('xml'); } elseif ($this->format == 'json') { $this->initDocument('json'); diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php index 6a7398746..52dc2e424 100644 --- a/actions/avatarsettings.php +++ b/actions/avatarsettings.php @@ -103,7 +103,7 @@ class AvatarsettingsAction extends AccountSettingsAction if (!$profile) { common_log_db_error($user, 'SELECT', __FILE__); - $this->serverError(_('User without matching profile')); + $this->serverError(_('User without matching profile.')); return; } @@ -182,7 +182,7 @@ class AvatarsettingsAction extends AccountSettingsAction if (!$profile) { common_log_db_error($user, 'SELECT', __FILE__); - $this->serverError(_('User without matching profile')); + $this->serverError(_('User without matching profile.')); return; } @@ -301,6 +301,10 @@ class AvatarsettingsAction extends AccountSettingsAction $this->showForm($e->getMessage()); return; } + if ($imagefile === null) { + $this->showForm(_('No file uploaded.')); + return; + } $cur = common_current_user(); diff --git a/actions/block.php b/actions/block.php index 5fae45dff..93f8ec937 100644 --- a/actions/block.php +++ b/actions/block.php @@ -66,7 +66,7 @@ class BlockAction extends ProfileFormAction assert(!empty($cur)); // checked by parent if ($cur->hasBlocked($this->profile)) { - $this->clientError(_("You already blocked that user.")); + $this->clientError(_('You already blocked that user.')); return false; } @@ -87,13 +87,15 @@ class BlockAction extends ProfileFormAction { if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($this->arg('no')) { - $this->returnToArgs(); + $this->returnToPrevious(); } elseif ($this->arg('yes')) { $this->handlePost(); - $this->returnToArgs(); + $this->returnToPrevious(); } else { $this->showPage(); } + } else { + $this->showPage(); } } @@ -118,6 +120,12 @@ class BlockAction extends ProfileFormAction */ function areYouSureForm() { + // @fixme if we ajaxify the confirmation form, skip the preview on ajax hits + $profile = new ArrayWrapper(array($this->profile)); + $preview = new ProfileList($profile, $this); + $preview->show(); + + $id = $this->profile->id; $this->elementStart('form', array('id' => 'block-' . $id, 'method' => 'post', @@ -140,8 +148,20 @@ class BlockAction extends ProfileFormAction $this->hidden($k, $v); } } - $this->submit('form_action-no', _('No'), 'submit form_action-primary', 'no', _("Do not block this user")); - $this->submit('form_action-yes', _('Yes'), 'submit form_action-secondary', 'yes', _('Block this user')); + $this->submit('form_action-no', + // TRANS: Button label on the user block form. + _m('BUTTON','No'), + 'submit form_action-primary', + 'no', + // TRANS: Submit button title for 'No' when blocking a user. + _('Do not block this user')); + $this->submit('form_action-yes', + // TRANS: Button label on the user block form. + _m('BUTTON','Yes'), + 'submit form_action-secondary', + 'yes', + // TRANS: Submit button title for 'Yes' when blocking a user. + _('Block this user')); $this->elementEnd('fieldset'); $this->elementEnd('form'); } @@ -168,4 +188,45 @@ class BlockAction extends ProfileFormAction return; } } + + function showScripts() + { + parent::showScripts(); + $this->autofocus('form_action-yes'); + } + + /** + * 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. + */ + function checkSessionToken() + { + if ($_SERVER['REQUEST_METHOD'] == 'POST' || + $this->arg('yes') || + $this->arg('no')) { + + return parent::checkSessionToken(); + } + } + + /** + * If we reached this form without returnto arguments, return to the + * current user's subscription list. + * + * @return string URL + */ + function defaultReturnTo() + { + $user = common_current_user(); + if ($user) { + return common_local_url('subscribers', + array('nickname' => $user->nickname)); + } else { + return common_local_url('public'); + } + } } diff --git a/actions/bookmarklet.php b/actions/bookmarklet.php index 0603a7456..041c2e894 100644 --- a/actions/bookmarklet.php +++ b/actions/bookmarklet.php @@ -47,7 +47,8 @@ class BookmarkletAction extends NewnoticeAction { function showTitle() { - $this->element('title', null, _('Post to ').common_config('site', 'name')); + // TRANS: Title for mini-posting window loaded from bookmarklet. + $this->element('title', null, sprintf(_('Post to %s'), common_config('site', 'name'))); } function showHeader() diff --git a/actions/confirmaddress.php b/actions/confirmaddress.php index cc8351d8d..8bf8c8c4d 100644 --- a/actions/confirmaddress.php +++ b/actions/confirmaddress.php @@ -87,10 +87,12 @@ class ConfirmaddressAction extends Action } $type = $confirm->address_type; if (!in_array($type, array('email', 'jabber', 'sms'))) { - $this->serverError(sprintf(_('Unrecognized address type %s'), $type)); + // TRANS: Server error for an unknow address type, 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. $this->clientError(_('That address has already been confirmed.')); return; } diff --git a/actions/deleteapplication.php b/actions/deleteapplication.php index 17526e111..806de0be6 100644 --- a/actions/deleteapplication.php +++ b/actions/deleteapplication.php @@ -150,13 +150,17 @@ class DeleteapplicationAction extends Action 'This will clear all data about the application from the '. 'database, including all existing user connections.')); $this->submit('form_action-no', - _('No'), + // TRANS: Button label on the delete application form. + _m('BUTTON','No'), 'submit form_action-primary', 'no', - _("Do not delete this application")); + // TRANS: Submit button title for 'No' when deleting an application. + _('Do not delete this application')); $this->submit('form_action-yes', - _('Yes'), + // TRANS: Button label on the delete application form. + _m('BUTTON','Yes'), 'submit form_action-secondary', + // TRANS: Submit button title for 'Yes' when deleting an application. 'yes', _('Delete this application')); $this->elementEnd('fieldset'); $this->elementEnd('form'); diff --git a/actions/deletenotice.php b/actions/deletenotice.php index 69cb1ebe8..f8010a814 100644 --- a/actions/deletenotice.php +++ b/actions/deletenotice.php @@ -142,8 +142,20 @@ class DeletenoticeAction extends Action $this->hidden('token', common_session_token()); $this->hidden('notice', $this->trimmed('notice')); $this->element('p', null, _('Are you sure you want to delete this notice?')); - $this->submit('form_action-no', _('No'), 'submit form_action-primary', 'no', _("Do not delete this notice")); - $this->submit('form_action-yes', _('Yes'), 'submit form_action-secondary', 'yes', _('Delete this notice')); + $this->submit('form_action-no', + // TRANS: Button label on the delete notice form. + _m('BUTTON','No'), + 'submit form_action-primary', + 'no', + // TRANS: Submit button title for 'No' when deleting a notice. + _("Do not delete this notice")); + $this->submit('form_action-yes', + // TRANS: Button label on the delete notice form. + _m('BUTTON','Yes'), + 'submit form_action-secondary', + 'yes', + // TRANS: Submit button title for 'Yes' when deleting a notice. + _('Delete this notice')); $this->elementEnd('fieldset'); $this->elementEnd('form'); } diff --git a/actions/deleteuser.php b/actions/deleteuser.php index c4f84fad2..02ded68b3 100644 --- a/actions/deleteuser.php +++ b/actions/deleteuser.php @@ -64,14 +64,14 @@ class DeleteuserAction extends ProfileFormAction assert(!empty($cur)); // checked by parent if (!$cur->hasRight(Right::DELETEUSER)) { - $this->clientError(_("You cannot delete users.")); + $this->clientError(_('You cannot delete users.')); return false; } $this->user = User::staticGet('id', $this->profile->id); if (empty($this->user)) { - $this->clientError(_("You can only delete local users.")); + $this->clientError(_('You can only delete local users.')); return false; } @@ -92,10 +92,10 @@ class DeleteuserAction extends ProfileFormAction { if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($this->arg('no')) { - $this->returnToArgs(); + $this->returnToPrevious(); } elseif ($this->arg('yes')) { $this->handlePost(); - $this->returnToArgs(); + $this->returnToPrevious(); } else { $this->showPage(); } @@ -147,8 +147,20 @@ class DeleteuserAction extends ProfileFormAction } Event::handle('EndDeleteUserForm', array($this, $this->user)); } - $this->submit('form_action-no', _('No'), 'submit form_action-primary', 'no', _("Do not block this user")); - $this->submit('form_action-yes', _('Yes'), 'submit form_action-secondary', 'yes', _('Delete this user')); + $this->submit('form_action-no', + // TRANS: Button label on the delete user form. + _m('BUTTON','No'), + 'submit form_action-primary', + 'no', + // TRANS: Submit button title for 'No' when deleting a user. + _('Do not block this user')); + $this->submit('form_action-yes', + // TRANS: Button label on the delete user form. + _m('BUTTON','Yes'), + 'submit form_action-secondary', + 'yes', + // TRANS: Submit button title for 'Yes' when deleting a user. + _('Delete this user')); $this->elementEnd('fieldset'); $this->elementEnd('form'); } @@ -162,7 +174,15 @@ class DeleteuserAction extends ProfileFormAction function handlePost() { if (Event::handle('StartDeleteUser', array($this, $this->user))) { - $this->user->delete(); + // Mark the account as deleted and shove low-level deletion tasks + // to background queues. Removing a lot of posts can take a while... + if (!$this->user->hasRole(Profile_role::DELETED)) { + $this->user->grantRole(Profile_role::DELETED); + } + + $qm = QueueManager::get(); + $qm->enqueue($this->user, 'deluser'); + Event::handle('EndDeleteUser', array($this, $this->user)); } } diff --git a/actions/designadminpanel.php b/actions/designadminpanel.php index 30e8bde1a..763737175 100644 --- a/actions/designadminpanel.php +++ b/actions/designadminpanel.php @@ -59,6 +59,7 @@ class DesignadminpanelAction extends AdminPanelAction function title() { + // TRANS: Message used as title for design settings for the site. return _('Design'); } @@ -125,9 +126,19 @@ class DesignadminpanelAction extends AdminPanelAction return; } - // check for an image upload + // check for file uploads $bgimage = $this->saveBackgroundImage(); + $customTheme = $this->saveCustomTheme(); + + $oldtheme = common_config('site', 'theme'); + if ($customTheme) { + // This feels pretty hacky :D + $this->args['theme'] = $customTheme; + $themeChanged = true; + } else { + $themeChanged = ($this->trimmed('theme') != $oldtheme); + } static $settings = array('theme', 'logo'); @@ -139,15 +150,13 @@ class DesignadminpanelAction extends AdminPanelAction $this->validate($values); - $oldtheme = common_config('site', 'theme'); - $config = new Config(); $config->query('BEGIN'); // Only update colors if the theme has not changed. - if ($oldtheme == $values['theme']) { + if (!$themeChanged) { $bgcolor = new WebColor($this->trimmed('design_background')); $ccolor = new WebColor($this->trimmed('design_content')); @@ -189,6 +198,13 @@ class DesignadminpanelAction extends AdminPanelAction Config::save('design', 'backgroundimage', $bgimage); } + if (common_config('custom_css', 'enabled')) { + $css = $this->arg('css'); + if ($css != common_config('custom_css', 'css')) { + Config::save('custom_css', 'css', $css); + } + } + $config->query('COMMIT'); } @@ -263,6 +279,33 @@ class DesignadminpanelAction extends AdminPanelAction } /** + * Save the custom theme if the user uploaded one. + * + * @return mixed custom theme name, if succesful, or null if no theme upload. + * @throws ClientException for invalid theme archives + * @throws ServerException if trouble saving the theme files + */ + + function saveCustomTheme() + { + if (common_config('theme_upload', 'enabled') && + $_FILES['design_upload_theme']['error'] == UPLOAD_ERR_OK) { + + $upload = ThemeUploader::fromUpload('design_upload_theme'); + $basedir = common_config('local', 'dir'); + if (empty($basedir)) { + $basedir = INSTALLDIR . '/local'; + } + $name = 'custom'; // @todo allow multiples, custom naming? + $outdir = $basedir . '/theme/' . $name; + $upload->extract($outdir); + return $name; + } else { + return null; + } + } + + /** * Attempt to validate setting values * * @return void @@ -272,11 +315,11 @@ class DesignadminpanelAction extends AdminPanelAction { if (!empty($values['logo']) && !Validate::uri($values['logo'], array('allowed_schemes' => array('http', 'https')))) { - $this->clientError(_("Invalid logo URL.")); + $this->clientError(_('Invalid logo URL.')); } if (!in_array($values['theme'], Theme::listAvailable())) { - $this->clientError(sprintf(_("Theme not available: %s"), $values['theme'])); + $this->clientError(sprintf(_("Theme not available: %s."), $values['theme'])); } } @@ -370,7 +413,15 @@ class DesignAdminPanelForm extends AdminForm function formData() { + $this->showLogo(); + $this->showTheme(); + $this->showBackground(); + $this->showColors(); + $this->showAdvanced(); + } + function showLogo() + { $this->out->elementStart('fieldset', array('id' => 'settings_design_logo')); $this->out->element('legend', null, _('Change logo')); @@ -383,6 +434,11 @@ class DesignAdminPanelForm extends AdminForm $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); + + } + + function showTheme() + { $this->out->elementStart('fieldset', array('id' => 'settings_design_theme')); $this->out->element('legend', null, _('Change theme')); @@ -406,10 +462,23 @@ class DesignAdminPanelForm extends AdminForm false, $this->value('theme')); $this->unli(); + if (common_config('theme_upload', 'enabled')) { + $this->li(); + $this->out->element('label', array('for' => 'design_upload_theme'), _('Custom theme')); + $this->out->element('input', array('id' => 'design_upload_theme', + 'name' => 'design_upload_theme', + 'type' => 'file')); + $this->out->element('p', 'form_guide', _('You can upload a custom StatusNet theme as a .ZIP archive.')); + $this->unli(); + } + $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); + } + function showBackground() + { $design = $this->out->design; $this->out->elementStart('fieldset', array('id' => @@ -454,6 +523,7 @@ class DesignAdminPanelForm extends AdminForm $this->out->element('label', array('for' => 'design_background-image_on', 'class' => 'radio'), + // TRANS: Used as radio button label to add a background image. _('On')); $attrs = array('name' => 'design_background-image_onoff', @@ -470,6 +540,7 @@ class DesignAdminPanelForm extends AdminForm $this->out->element('label', array('for' => 'design_background-image_off', 'class' => 'radio'), + // TRANS: Used as radio button label to not add a background image. _('Off')); $this->out->element('p', 'form_guide', _('Turn background image on or off.')); $this->unli(); @@ -483,6 +554,11 @@ class DesignAdminPanelForm extends AdminForm $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); + } + + function showColors() + { + $design = $this->out->design; $this->out->elementStart('fieldset', array('id' => 'settings_design_color')); $this->out->element('legend', null, _('Change colours')); @@ -490,6 +566,7 @@ class DesignAdminPanelForm extends AdminForm $this->out->elementStart('ul', 'form_data'); try { + // @fixme avoid loop unrolling in non-performance-critical contexts like this $bgcolor = new WebColor($design->backgroundcolor); @@ -557,6 +634,7 @@ class DesignAdminPanelForm extends AdminForm $this->unli(); } catch (WebColorException $e) { + // @fixme normalize them individually! common_log(LOG_ERR, 'Bad color values in site design: ' . $e->getMessage()); } @@ -566,6 +644,27 @@ class DesignAdminPanelForm extends AdminForm $this->out->elementEnd('ul'); } + function showAdvanced() + { + if (common_config('custom_css', 'enabled')) { + $this->out->elementStart('fieldset', array('id' => 'settings_design_advanced')); + $this->out->element('legend', null, _('Advanced')); + $this->out->elementStart('ul', 'form_data'); + + $this->li(); + $this->out->element('label', array('for' => 'css'), _('Custom CSS')); + $this->out->element('textarea', array('name' => 'css', + 'id' => 'css', + 'cols' => '50', + 'rows' => '10'), + strval(common_config('custom_css', 'css'))); + $this->unli(); + + $this->out->elementEnd('fieldset'); + $this->out->elementEnd('ul'); + } + } + /** * Action elements * diff --git a/actions/disfavor.php b/actions/disfavor.php index 6269f1bd2..3ccdd69af 100644 --- a/actions/disfavor.php +++ b/actions/disfavor.php @@ -71,7 +71,7 @@ class DisfavorAction extends Action $notice = Notice::staticGet($id); $token = $this->trimmed('token-'.$notice->id); if (!$token || $token != common_session_token()) { - $this->clientError(_("There was a problem with your session token. Try again, please.")); + $this->clientError(_('There was a problem with your session token. Try again, please.')); return; } $fave = new Fave(); diff --git a/actions/doc.php b/actions/doc.php index 459f5f096..f876fb8be 100644 --- a/actions/doc.php +++ b/actions/doc.php @@ -13,7 +13,7 @@ * @link http://status.net/ * * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2008, 2009, StatusNet, Inc. + * Copyright (C) 2008-2010, StatusNet, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -168,14 +168,28 @@ class DocAction extends Action function getFilename() { - if (file_exists(INSTALLDIR.'/local/doc-src/'.$this->title)) { - $localDef = INSTALLDIR.'/local/doc-src/'.$this->title; - } + $localDef = null; + $local = null; + + $site = StatusNet::currentSite(); - $local = glob(INSTALLDIR.'/local/doc-src/'.$this->title.'.*'); - if ($local === false) { - // Some systems return false, others array(), if dir didn't exist. - $local = array(); + if (!empty($site) && file_exists(INSTALLDIR.'/local/doc-src/'.$site.'/'.$this->title)) { + $localDef = INSTALLDIR.'/local/doc-src/'.$site.'/'.$this->title; + + $local = glob(INSTALLDIR.'/local/doc-src/'.$site.'/'.$this->title.'.*'); + if ($local === false) { + // Some systems return false, others array(), if dir didn't exist. + $local = array(); + } + } else { + if (file_exists(INSTALLDIR.'/local/doc-src/'.$this->title)) { + $localDef = INSTALLDIR.'/local/doc-src/'.$this->title; + } + + $local = glob(INSTALLDIR.'/local/doc-src/'.$this->title.'.*'); + if ($local === false) { + $local = array(); + } } if (count($local) || isset($localDef)) { diff --git a/actions/emailsettings.php b/actions/emailsettings.php index 08608348c..6138a88f9 100644 --- a/actions/emailsettings.php +++ b/actions/emailsettings.php @@ -57,6 +57,7 @@ class EmailsettingsAction extends AccountSettingsAction function title() { + // TRANS: Title for e-mail settings. return _('Email settings'); } @@ -68,6 +69,10 @@ class EmailsettingsAction extends AccountSettingsAction function getInstructions() { + // XXX: For consistency of parameters in messages, this should be a + // regular parameters, replaced with sprintf(). + // TRANS: E-mail settings page instructions. + // TRANS: %%site.name%% is the name of the site. return _('Manage how you get email from %%site.name%%.'); } @@ -97,102 +102,126 @@ class EmailsettingsAction extends AccountSettingsAction common_local_url('emailsettings'))); $this->elementStart('fieldset'); $this->elementStart('fieldset', array('id' => 'settings_email_address')); - $this->element('legend', null, _('Address')); + // TRANS: Form legend for e-mail settings form. + $this->element('legend', null, _('Email address')); $this->hidden('token', common_session_token()); if ($user->email) { $this->element('p', array('id' => 'form_confirmed'), $user->email); + // TRANS: Form note in e-mail settings form. $this->element('p', array('class' => 'form_note'), _('Current confirmed email address.')); $this->hidden('email', $user->email); - $this->submit('remove', _('Remove')); + // TRANS: Button label to remove a confirmed e-mail address. + $this->submit('remove', _m('BUTTON','Remove')); } else { $confirm = $this->getConfirmation(); if ($confirm) { $this->element('p', array('id' => 'form_unconfirmed'), $confirm->address); + // TRANS: Form note in e-mail settings form. $this->element('p', array('class' => 'form_note'), _('Awaiting confirmation on this address. '. 'Check your inbox (and spam box!) for a message '. 'with further instructions.')); $this->hidden('email', $confirm->address); - $this->submit('cancel', _('Cancel')); + // TRANS: Button label to cancel an e-mail address confirmation procedure. + $this->submit('cancel', _m('BUTTON','Cancel')); } else { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); + // TRANS: Field label for e-mail address input in e-mail settings form. $this->input('email', _('Email address'), ($this->arg('email')) ? $this->arg('email') : null, + // TRANS: Instructions for e-mail address input form. _('Email address, like "UserName@example.org"')); $this->elementEnd('li'); $this->elementEnd('ul'); - $this->submit('add', _('Add')); + // TRANS: Button label for adding an e-mail address in e-mail settings form. + $this->submit('add', _m('BUTTON','Add')); } } $this->elementEnd('fieldset'); if (common_config('emailpost', 'enabled') && $user->email) { $this->elementStart('fieldset', array('id' => 'settings_email_incoming')); + // TRANS: Form legend for incoming e-mail settings form. $this->element('legend', null, _('Incoming email')); if ($user->incomingemail) { $this->elementStart('p'); $this->element('span', 'address', $user->incomingemail); + // XXX: Looks a little awkward in the UI. + // Something like "xxxx@identi.ca Send email ..". Needs improvement. $this->element('span', 'input_instructions', + // TRANS: Form instructions for incoming e-mail form in e-mail settings. _('Send email to this address to post new notices.')); $this->elementEnd('p'); - $this->submit('removeincoming', _('Remove')); + // TRANS: Button label for removing a set sender e-mail address to post notices from. + $this->submit('removeincoming', _m('BUTTON','Remove')); } $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.')); $this->elementEnd('p'); - $this->submit('newincoming', _('New')); + // TRANS: Button label for adding an e-mail address to send notices from. + $this->submit('newincoming', _m('BUTTON','New')); $this->elementEnd('fieldset'); } $this->elementStart('fieldset', array('id' => 'settings_email_preferences')); - $this->element('legend', null, _('Preferences')); + // TRANS: Form legend for e-mail preferences form. + $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'); $this->elementEnd('ul'); - $this->submit('save', _('Save')); + // TRANS: Button label to save e-mail preferences. + $this->submit('save', _m('BUTTON','Save')); $this->elementEnd('fieldset'); $this->elementEnd('fieldset'); $this->elementEnd('form'); @@ -253,6 +282,7 @@ class EmailsettingsAction extends AccountSettingsAction } else if ($this->arg('newincoming')) { $this->newIncoming(); } else { + // TRANS: Message given submitting a form with an unknown action in e-mail settings. $this->showForm(_('Unexpected form submission.')); } } @@ -293,13 +323,15 @@ class EmailsettingsAction extends AccountSettingsAction 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'); - $this->showForm(_('Preferences saved.'), true); + // TRANS: Confirmation message for successful e-mail preferences save. + $this->showForm(_('Email preferences saved.'), true); } /** @@ -317,6 +349,7 @@ class EmailsettingsAction extends AccountSettingsAction // Some validation if (!$email) { + // TRANS: Message given saving e-mail address without having provided one. $this->showForm(_('No email address.')); return; } @@ -324,16 +357,20 @@ class EmailsettingsAction extends AccountSettingsAction $email = common_canonical_email($email); if (!$email) { + // TRANS: Message given saving e-mail address that cannot be normalised. $this->showForm(_('Cannot normalize that email address')); return; } if (!Validate::email($email, common_config('email', 'check_domain'))) { + // TRANS: Message given saving e-mail address that not valid. $this->showForm(_('Not a valid email address.')); return; } else if ($user->email == $email) { + // TRANS: Message given saving e-mail address that is already set. $this->showForm(_('That is already your email address.')); return; } else if ($this->emailExists($email)) { + // TRANS: Message given saving e-mail address that is already set for another user. $this->showForm(_('That email address already belongs '. 'to another user.')); return; @@ -350,12 +387,14 @@ class EmailsettingsAction extends AccountSettingsAction if ($result === false) { common_log_db_error($confirm, 'INSERT', __FILE__); + // TRANS: Server error thrown on database error adding e-mail confirmation code. $this->serverError(_('Couldn\'t insert confirmation code.')); return; } mail_confirm_address($user, $confirm->code, $user->nickname, $email); + // TRANS: Message given saving valid e-mail address that is to be confirmed. $msg = _('A confirmation code was sent to the email address you added. '. 'Check your inbox (and spam box!) for the code and instructions '. 'on how to use it.'); @@ -376,11 +415,13 @@ class EmailsettingsAction extends AccountSettingsAction $confirm = $this->getConfirmation(); if (!$confirm) { + // TRANS: Message given canceling e-mail address confirmation that is not pending. $this->showForm(_('No pending confirmation to cancel.')); return; } if ($confirm->address != $email) { - $this->showForm(_('That is the wrong IM address.')); + // TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address. + $this->showForm(_('That is the wrong email address.')); return; } @@ -388,11 +429,13 @@ class EmailsettingsAction extends AccountSettingsAction if (!$result) { common_log_db_error($confirm, 'DELETE', __FILE__); + // TRANS: Server error thrown on database error canceling e-mail address confirmation. $this->serverError(_('Couldn\'t delete email confirmation.')); return; } - $this->showForm(_('Confirmation cancelled.'), true); + // TRANS: Message given after successfully canceling e-mail address confirmation. + $this->showForm(_('Email confirmation cancelled.'), true); } /** @@ -410,6 +453,8 @@ class EmailsettingsAction extends AccountSettingsAction // Maybe an old tab open...? if ($user->email != $email) { + // TRANS: Message given trying to remove an e-mail address that is not + // TRANS: registered for the active user. $this->showForm(_('That is not your email address.')); return; } @@ -424,12 +469,14 @@ class EmailsettingsAction extends AccountSettingsAction if (!$result) { common_log_db_error($user, 'UPDATE', __FILE__); + // TRANS: Server error thrown on database error removing a registered e-mail address. $this->serverError(_('Couldn\'t update user.')); return; } $user->query('COMMIT'); - $this->showForm(_('The address was removed.'), true); + // TRANS: Message given after successfully removing a registered e-mail address. + $this->showForm(_('The email address was removed.'), true); } /** @@ -453,9 +500,11 @@ class EmailsettingsAction extends AccountSettingsAction if (!$user->updateKeys($orig)) { common_log_db_error($user, 'UPDATE', __FILE__); + // TRANS: Server error thrown on database error removing incoming e-mail address. $this->serverError(_("Couldn't update user record.")); } + // TRANS: Message given after successfully removing an incoming e-mail address. $this->showForm(_('Incoming email address removed.'), true); } @@ -475,9 +524,11 @@ class EmailsettingsAction extends AccountSettingsAction if (!$user->updateKeys($orig)) { common_log_db_error($user, 'UPDATE', __FILE__); + // TRANS: Server error thrown on database error adding incoming e-mail address. $this->serverError(_("Couldn't update user record.")); } + // TRANS: Message given after successfully adding an incoming e-mail address. $this->showForm(_('New incoming email address added.'), true); } diff --git a/actions/favor.php b/actions/favor.php index afca9768a..475912fd0 100644 --- a/actions/favor.php +++ b/actions/favor.php @@ -72,7 +72,7 @@ class FavorAction extends Action $notice = Notice::staticGet($id); $token = $this->trimmed('token-'.$notice->id); if (!$token || $token != common_session_token()) { - $this->clientError(_("There was a problem with your session token. Try again, please.")); + $this->clientError(_('There was a problem with your session token. Try again, please.')); return; } if ($user->hasFave($notice)) { diff --git a/actions/favoritesrss.php b/actions/favoritesrss.php index 62f06e841..51c92af93 100644 --- a/actions/favoritesrss.php +++ b/actions/favoritesrss.php @@ -89,7 +89,7 @@ class FavoritesrssAction extends Rss10Action function getNotices($limit=0) { $user = $this->user; - $notice = $user->favoriteNotices(0, $limit); + $notice = $user->favoriteNotices(false, 0, $limit); $notices = array(); while ($notice->fetch()) { $notices[] = clone($notice); diff --git a/actions/finishremotesubscribe.php b/actions/finishremotesubscribe.php index deee70f36..ac51ddec3 100644 --- a/actions/finishremotesubscribe.php +++ b/actions/finishremotesubscribe.php @@ -135,7 +135,7 @@ class FinishremotesubscribeAction extends Action $service->getServiceURI(OMB_ENDPOINT_UPDATEPROFILE); if (!$remote->update($orig_remote)) { - $this->serverError(_('Error updating remote profile')); + $this->serverError(_('Error updating remote profile.')); return; } diff --git a/actions/foaf.php b/actions/foaf.php index e9f67b7f2..09af7b502 100644 --- a/actions/foaf.php +++ b/actions/foaf.php @@ -95,7 +95,9 @@ class FoafAction extends Action // Would be nice to tell if they were a Person or not (e.g. a #person usertag?) $this->elementStart('Agent', array('rdf:about' => $this->user->uri)); - $this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email)); + if ($this->user->email) { + $this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email)); + } if ($this->profile->fullname) { $this->element('name', null, $this->profile->fullname); } @@ -126,7 +128,7 @@ class FoafAction extends Action $this->element('geo:lat', null, $location->lat); } if ($location->lon) { - $this->element('geo:long', null, $location->lat); + $this->element('geo:long', null, $location->lon); } if ($location->getURL()) { $this->element('page', array('rdf:resource'=>$location->getURL())); @@ -152,7 +154,9 @@ class FoafAction extends Action } $person = $this->showMicrobloggingAccount($this->profile, - common_root_url(), $this->user->uri, false); + common_root_url(), $this->user->uri, + /*$fetchSubscriptions*/true, + /*$isSubscriber*/false); // Get people who subscribe to user @@ -162,40 +166,29 @@ class FoafAction extends Action if ($sub->find()) { while ($sub->fetch()) { - if ($sub->token) { - $other = Remote_profile::staticGet('id', $sub->subscriber); - $profile = Profile::staticGet('id', $sub->subscriber); - } else { - $other = User::staticGet('id', $sub->subscriber); - $profile = Profile::staticGet('id', $sub->subscriber); - } - if (!$other) { + $profile = Profile::staticGet('id', $sub->subscriber); + if (empty($profile)) { common_debug('Got a bad subscription: '.print_r($sub,true)); continue; } - if (array_key_exists($other->uri, $person)) { - $person[$other->uri][0] = BOTH; + $user = $profile->getUser(); + $other_uri = $profile->getUri(); + if (array_key_exists($other_uri, $person)) { + $person[$other_uri][0] = BOTH; } else { - $person[$other->uri] = array(LISTENER, - $other->id, - $profile->nickname, - (empty($sub->token)) ? 'User' : 'Remote_profile'); + $person[$other_uri] = array(LISTENER, + $profile->id, + $profile->nickname, + $user ? 'local' : 'remote'); } - $other->free(); - $other = null; - unset($other); - $profile->free(); - $profile = null; unset($profile); } } - $sub->free(); - $sub = null; unset($sub); foreach ($person as $uri => $p) { - list($type, $id, $nickname, $cls) = $p; + list($type, $id, $nickname, $local) = $p; if ($type == BOTH) { $this->element('knows', array('rdf:resource' => $uri)); } @@ -206,8 +199,8 @@ class FoafAction extends Action foreach ($person as $uri => $p) { $foaf_url = null; - list($type, $id, $nickname, $cls) = $p; - if ($cls == 'User') { + list($type, $id, $nickname, $local) = $p; + if ($local == 'local') { $foaf_url = common_local_url('foaf', array('nickname' => $nickname)); } $profile = Profile::staticGet($id); @@ -216,9 +209,10 @@ class FoafAction extends Action $this->element('knows', array('rdf:resource' => $this->user->uri)); } $this->showMicrobloggingAccount($profile, - ($cls == 'User') ? common_root_url() : null, + ($local == 'local') ? common_root_url() : null, $uri, - true); + /*$fetchSubscriptions*/false, + /*$isSubscriber*/($type == LISTENER || $type == BOTH)); if ($foaf_url) { $this->element('rdfs:seeAlso', array('rdf:resource' => $foaf_url)); } @@ -243,7 +237,21 @@ class FoafAction extends Action $this->elementEnd('PersonalProfileDocument'); } - function showMicrobloggingAccount($profile, $service=null, $useruri=null, $isSubscriber=false) + /** + * Output FOAF <account> bit for the given profile. + * + * @param Profile $profile + * @param mixed $service Root URL of this StatusNet instance for a local + * user, otherwise null. + * @param mixed $useruri URI string for the referenced profile.. + * @param boolean $fetchSubscriptions Should we load and list all their subscriptions? + * @param boolean $isSubscriber if not fetching subs, we can still mark the user as following the current page. + * + * @return array if $fetchSubscribers is set, return a list of info on those + * subscriptions. + */ + + function showMicrobloggingAccount($profile, $service=null, $useruri=null, $fetchSubscriptions=false, $isSubscriber=false) { $attr = array(); if ($useruri) { @@ -251,7 +259,7 @@ class FoafAction extends Action } // Their account - $this->elementStart('holdsAccount'); + $this->elementStart('account'); $this->elementStart('OnlineAccount', $attr); if ($service) { $this->element('accountServiceHomepage', array('rdf:resource' => @@ -265,9 +273,7 @@ class FoafAction extends Action $person = array(); - if ($isSubscriber) { - $this->element('sioc:follows', array('rdf:resource'=>$this->user->uri . '#acct')); - } else { + if ($fetchSubscriptions) { // Get people user is subscribed to $sub = new Subscription(); $sub->subscriber = $profile->id; @@ -275,38 +281,30 @@ class FoafAction extends Action if ($sub->find()) { while ($sub->fetch()) { - if (!empty($sub->token)) { - $other = Remote_profile::staticGet('id', $sub->subscribed); - $profile = Profile::staticGet('id', $sub->subscribed); - } else { - $other = User::staticGet('id', $sub->subscribed); - $profile = Profile::staticGet('id', $sub->subscribed); - } - if (empty($other)) { + $profile = Profile::staticGet('id', $sub->subscribed); + if (empty($profile)) { common_debug('Got a bad subscription: '.print_r($sub,true)); continue; } - $this->element('sioc:follows', array('rdf:resource' => $other->uri.'#acct')); - $person[$other->uri] = array(LISTENEE, - $other->id, - $profile->nickname, - (empty($sub->token)) ? 'User' : 'Remote_profile'); - $other->free(); - $other = null; - unset($other); - $profile->free(); - $profile = null; + $user = $profile->getUser(); + $other_uri = $profile->getUri(); + $this->element('sioc:follows', array('rdf:resource' => $other_uri.'#acct')); + $person[$other_uri] = array(LISTENEE, + $profile->id, + $profile->nickname, + $user ? 'local' : 'remote'); unset($profile); } } - $sub->free(); - $sub = null; unset($sub); + } else if ($isSubscriber) { + // Just declare that they follow the user whose FOAF we're showing. + $this->element('sioc:follows', array('rdf:resource' => $this->user->uri . '#acct')); } $this->elementEnd('OnlineAccount'); - $this->elementEnd('holdsAccount'); + $this->elementEnd('account'); return $person; } diff --git a/actions/foafgroup.php b/actions/foafgroup.php index ebdf1cee2..4db40c28b 100644 --- a/actions/foafgroup.php +++ b/actions/foafgroup.php @@ -56,7 +56,7 @@ class FoafGroupAction extends Action return false; } - $local = Local_group::staticGet('nickname', $nickname); + $local = Local_group::staticGet('nickname', $this->nickname); if (!$local) { $this->clientError(_('No such group.'), 404); @@ -126,7 +126,8 @@ class FoafGroupAction extends Action while ($members->fetch()) { $member_uri = common_local_url('userbyid', array('id'=>$members->id)); $member_details[$member_uri] = array( - 'nickname' => $members->nickname + 'nickname' => $members->nickname, + 'is_admin' => false, ); $this->element('member', array('rdf:resource' => $member_uri)); } @@ -146,7 +147,7 @@ class FoafGroupAction extends Action { $this->elementStart('Agent', array('rdf:about' => $uri)); $this->element('nick', null, $details['nickname']); - $this->elementStart('holdsAccount'); + $this->elementStart('account'); $this->elementStart('sioc:User', array('rdf:about'=>$uri.'#acct')); $this->elementStart('sioc:has_function'); $this->elementStart('statusnet:GroupAdminRole'); @@ -154,7 +155,7 @@ class FoafGroupAction extends Action $this->elementEnd('statusnet:GroupAdminRole'); $this->elementEnd('sioc:has_function'); $this->elementEnd('sioc:User'); - $this->elementEnd('holdsAccount'); + $this->elementEnd('account'); $this->elementEnd('Agent'); } else @@ -177,4 +178,4 @@ class FoafGroupAction extends Action $this->elementEnd('Document'); } -}
\ No newline at end of file +} diff --git a/actions/geocode.php b/actions/geocode.php index e883c6ce4..d93493060 100644 --- a/actions/geocode.php +++ b/actions/geocode.php @@ -37,6 +37,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @category Action * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @link http://status.net/ */ diff --git a/actions/grantrole.php b/actions/grantrole.php new file mode 100644 index 000000000..b8b23d02e --- /dev/null +++ b/actions/grantrole.php @@ -0,0 +1,99 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Action class to sandbox an abusive 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 Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Sandbox a user. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class GrantRoleAction extends ProfileFormAction +{ + /** + * Check parameters + * + * @param array $args action arguments (URL, GET, POST) + * + * @return boolean success flag + */ + + function prepare($args) + { + if (!parent::prepare($args)) { + return false; + } + + $this->role = $this->arg('role'); + if (!Profile_role::isValid($this->role)) { + $this->clientError(_('Invalid role.')); + return false; + } + if (!Profile_role::isSettable($this->role)) { + $this->clientError(_('This role is reserved and cannot be set.')); + return false; + } + + $cur = common_current_user(); + + assert(!empty($cur)); // checked by parent + + if (!$cur->hasRight(Right::GRANTROLE)) { + $this->clientError(_('You cannot grant user roles on this site.')); + return false; + } + + assert(!empty($this->profile)); // checked by parent + + if ($this->profile->hasRole($this->role)) { + $this->clientError(_('User already has this role.')); + return false; + } + + return true; + } + + /** + * Sandbox a user. + * + * @return void + */ + + function handlePost() + { + $this->profile->grantRole($this->role); + } +} diff --git a/actions/groupblock.php b/actions/groupblock.php index ec673358e..39f783397 100644 --- a/actions/groupblock.php +++ b/actions/groupblock.php @@ -41,7 +41,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @link http://status.net/ */ -class GroupblockAction extends Action +class GroupblockAction extends RedirectingAction { var $profile = null; var $group = null; @@ -117,9 +117,7 @@ class GroupblockAction extends Action parent::handle($args); if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($this->arg('no')) { - common_redirect(common_local_url('groupmembers', - array('nickname' => $this->group->nickname)), - 303); + $this->returnToPrevious(); } elseif ($this->arg('yes')) { $this->blockProfile(); } elseif ($this->arg('blockto')) { @@ -175,8 +173,20 @@ class GroupblockAction extends Action $this->hidden($k, $v); } } - $this->submit('form_action-no', _('No'), 'submit form_action-primary', 'no', _("Do not block this user from this group")); - $this->submit('form_action-yes', _('Yes'), 'submit form_action-secondary', 'yes', _('Block this user from this group')); + $this->submit('form_action-no', + // TRANS: Button label on the form to block a user from a group. + _m('BUTTON','No'), + 'submit form_action-primary', + 'no', + // TRANS: Submit button title for 'No' when blocking a user from a group. + _('Do not block this user from this group')); + $this->submit('form_action-yes', + // TRANS: Button label on the form to block a user from a group. + _m('BUTTON','Yes'), + 'submit form_action-secondary', + 'yes', + // TRANS: Submit button title for 'Yes' when blocking a user from a group. + _('Block this user from this group')); $this->elementEnd('fieldset'); $this->elementEnd('form'); } @@ -196,23 +206,27 @@ class GroupblockAction extends Action $this->serverError(_("Database error blocking user from group.")); return false; } + + $this->returnToPrevious(); + } - // Now, gotta figure where we go back to - foreach ($this->args as $k => $v) { - if ($k == 'returnto-action') { - $action = $v; - } elseif (substr($k, 0, 9) == 'returnto-') { - $args[substr($k, 9)] = $v; - } - } + /** + * If we reached this form without returnto arguments, default to + * the top of the group's member list. + * + * @return string URL + */ + function defaultReturnTo() + { + return common_local_url('groupmembers', + array('nickname' => $this->group->nickname)); + } - if ($action) { - common_redirect(common_local_url($action, $args), 303); - } else { - common_redirect(common_local_url('groupmembers', - array('nickname' => $this->group->nickname)), - 303); - } + function showScripts() + { + parent::showScripts(); + $this->autofocus('form_action-yes'); } + } diff --git a/actions/groupmembers.php b/actions/groupmembers.php index a16debd7b..54f1d8dcd 100644 --- a/actions/groupmembers.php +++ b/actions/groupmembers.php @@ -205,13 +205,13 @@ class GroupMemberListItem extends ProfileListItem !$this->profile->isAdmin($this->group)) { $this->out->elementStart('li', 'entity_make_admin'); $maf = new MakeAdminForm($this->out, $this->profile, $this->group, - array('action' => 'groupmembers', - 'nickname' => $this->group->nickname)); + $this->returnToArgs()); $maf->show(); $this->out->elementEnd('li'); } } + function showGroupBlockForm() { $user = common_current_user(); @@ -219,12 +219,49 @@ class GroupMemberListItem extends ProfileListItem if (!empty($user) && $user->id != $this->profile->id && $user->isAdmin($this->group)) { $this->out->elementStart('li', 'entity_block'); $bf = new GroupBlockForm($this->out, $this->profile, $this->group, - array('action' => 'groupmembers', - 'nickname' => $this->group->nickname)); + $this->returnToArgs()); $bf->show(); $this->out->elementEnd('li'); } + } + + function linkAttributes() + { + $aAttrs = parent::linkAttributes(); + + if (common_config('nofollow', 'members')) { + $aAttrs['rel'] .= ' nofollow'; + } + + return $aAttrs; + } + + function homepageAttributes() + { + $aAttrs = parent::linkAttributes(); + + if (common_config('nofollow', 'members')) { + $aAttrs['rel'] = 'nofollow'; + } + + return $aAttrs; + } + /** + * Fetch necessary return-to arguments for the profile forms + * to return to this list when they're done. + * + * @return array + */ + protected function returnToArgs() + { + $args = array('action' => 'groupmembers', + 'nickname' => $this->group->nickname); + $page = $this->out->arg('page'); + if ($page) { + $args['param-page'] = $page; + } + return $args; } } diff --git a/actions/grouprss.php b/actions/grouprss.php index 490f6f945..98fdea38d 100644 --- a/actions/grouprss.php +++ b/actions/grouprss.php @@ -135,8 +135,10 @@ class groupRssAction extends Rss10Action $c = array('url' => common_local_url('grouprss', array('nickname' => $group->nickname)), + // TRANS: Message is used as link title. %s is a user nickname. 'title' => sprintf(_('%s timeline'), $group->nickname), 'link' => common_local_url('showgroup', array('nickname' => $group->nickname)), + // TRANS: Message is used as link description. %1$s is a username, %2$s is a site name. 'description' => sprintf(_('Updates from members of %1$s on %2$s!'), $group->nickname, common_config('site', 'name'))); return $c; diff --git a/actions/imsettings.php b/actions/imsettings.php index af4915843..6691c8dad 100644 --- a/actions/imsettings.php +++ b/actions/imsettings.php @@ -56,6 +56,7 @@ class ImsettingsAction extends ConnectSettingsAction function title() { + // TRANS: Title for instance messaging settings. return _('IM settings'); } @@ -67,6 +68,9 @@ class ImsettingsAction extends ConnectSettingsAction function getInstructions() { + // TRANS: Instant messaging settings page instructions. + // TRANS: [instant messages] is link text, "(%%doc.im%%)" is the link. + // TRANS: the order and formatting of link text and link should remain unchanged. return _('You can send and receive notices through '. 'Jabber/GTalk [instant messages](%%doc.im%%). '. 'Configure your address and settings below.'); @@ -86,6 +90,7 @@ class ImsettingsAction extends ConnectSettingsAction { if (!common_config('xmpp', 'enabled')) { $this->element('div', array('class' => 'error'), + // TRANS: Message given in the IM settings if XMPP is not enabled on the site. _('IM is not available.')); return; } @@ -97,32 +102,41 @@ class ImsettingsAction extends ConnectSettingsAction 'action' => common_local_url('imsettings'))); $this->elementStart('fieldset', array('id' => 'settings_im_address')); - $this->element('legend', null, _('Address')); + // TRANS: Form legend for IM settings form. + $this->element('legend', null, _('IM address')); $this->hidden('token', common_session_token()); if ($user->jabber) { $this->element('p', 'form_confirmed', $user->jabber); + // TRANS: Form note in IM settings form. $this->element('p', 'form_note', _('Current confirmed Jabber/GTalk address.')); $this->hidden('jabber', $user->jabber); - $this->submit('remove', _('Remove')); + // TRANS: Button label to remove a confirmed IM address. + $this->submit('remove', _m('BUTTON','Remove')); } else { $confirm = $this->getConfirmation(); if ($confirm) { $this->element('p', 'form_unconfirmed', $confirm->address); $this->element('p', 'form_note', + // TRANS: Form note in IM settings form. + // TRANS: %s is the IM address set for the site. sprintf(_('Awaiting confirmation on this address. '. 'Check your Jabber/GTalk account for a '. 'message with further instructions. '. '(Did you add %s to your buddy list?)'), jabber_daemon_address())); $this->hidden('jabber', $confirm->address); - $this->submit('cancel', _('Cancel')); + // TRANS: Button label to cancel an IM address confirmation procedure. + $this->submit('cancel', _m('BUTTON','Cancel')); } else { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); + // TRANS: Field label for IM address input in IM settings form. $this->input('jabber', _('IM address'), ($this->arg('jabber')) ? $this->arg('jabber') : null, + // TRANS: IM address input field instructions in IM settings form. + // TRANS: %s is the IM address set for the site. sprintf(_('Jabber or GTalk address, '. 'like "UserName@example.org". '. 'First, make sure to add %s to your '. @@ -130,37 +144,44 @@ class ImsettingsAction extends ConnectSettingsAction jabber_daemon_address())); $this->elementEnd('li'); $this->elementEnd('ul'); - $this->submit('add', _('Add')); + // TRANS: Button label for adding an IM address in IM settings form. + $this->submit('add', _m('BUTTON','Add')); } } $this->elementEnd('fieldset'); $this->elementStart('fieldset', array('id' => 'settings_im_preferences')); - $this->element('legend', null, _('Preferences')); + // TRANS: Form legend for IM preferences form. + $this->element('legend', null, _('IM preferences')); $this->elementStart('ul', 'form_data'); $this->elementStart('li'); $this->checkbox('jabbernotify', + // TRANS: Checkbox label in IM preferences form. _('Send me notices through Jabber/GTalk.'), $user->jabbernotify); $this->elementEnd('li'); $this->elementStart('li'); $this->checkbox('updatefrompresence', + // TRANS: Checkbox label in IM preferences form. _('Post a notice when my Jabber/GTalk status changes.'), $user->updatefrompresence); $this->elementEnd('li'); $this->elementStart('li'); $this->checkbox('jabberreplies', + // TRANS: Checkbox label in IM preferences form. _('Send me replies through Jabber/GTalk '. 'from people I\'m not subscribed to.'), $user->jabberreplies); $this->elementEnd('li'); $this->elementStart('li'); $this->checkbox('jabbermicroid', + // TRANS: Checkbox label in IM preferences form. _('Publish a MicroID for my Jabber/GTalk address.'), $user->jabbermicroid); $this->elementEnd('li'); $this->elementEnd('ul'); - $this->submit('save', _('Save')); + // TRANS: Button label to save IM preferences. + $this->submit('save', _m('BUTTON','Save')); $this->elementEnd('fieldset'); $this->elementEnd('form'); } @@ -217,6 +238,7 @@ class ImsettingsAction extends ConnectSettingsAction } else if ($this->arg('remove')) { $this->removeAddress(); } else { + // TRANS: Message given submitting a form with an unknown action in IM settings. $this->showForm(_('Unexpected form submission.')); } } @@ -232,7 +254,6 @@ class ImsettingsAction extends ConnectSettingsAction function savePreferences() { - $jabbernotify = $this->boolean('jabbernotify'); $updatefrompresence = $this->boolean('updatefrompresence'); $jabberreplies = $this->boolean('jabberreplies'); @@ -255,12 +276,14 @@ class ImsettingsAction extends ConnectSettingsAction if ($result === false) { common_log_db_error($user, 'UPDATE', __FILE__); + // TRANS: Server error thrown on database error updating IM preferences. $this->serverError(_('Couldn\'t update user.')); return; } $user->query('COMMIT'); + // TRANS: Confirmation message for successful IM preferences save. $this->showForm(_('Preferences saved.'), true); } @@ -282,6 +305,7 @@ class ImsettingsAction extends ConnectSettingsAction // Some validation if (!$jabber) { + // TRANS: Message given saving IM address without having provided one. $this->showForm(_('No Jabber ID.')); return; } @@ -289,16 +313,20 @@ class ImsettingsAction extends ConnectSettingsAction $jabber = jabber_normalize_jid($jabber); if (!$jabber) { + // TRANS: Message given saving IM address that cannot be normalised. $this->showForm(_('Cannot normalize that Jabber ID')); return; } - if (!jabber_valid_base_jid($jabber)) { + if (!jabber_valid_base_jid($jabber, common_config('email', 'domain_check'))) { + // TRANS: Message given saving IM address that not valid. $this->showForm(_('Not a valid Jabber ID')); return; } else if ($user->jabber == $jabber) { + // TRANS: Message given saving IM address that is already set. $this->showForm(_('That is already your Jabber ID.')); return; } else if ($this->jabberExists($jabber)) { + // TRANS: Message given saving IM address that is already set for another user. $this->showForm(_('Jabber ID already belongs to another user.')); return; } @@ -316,6 +344,7 @@ class ImsettingsAction extends ConnectSettingsAction if ($result === false) { common_log_db_error($confirm, 'INSERT', __FILE__); + // TRANS: Server error thrown on database error adding IM confirmation code. $this->serverError(_('Couldn\'t insert confirmation code.')); return; } @@ -324,6 +353,8 @@ class ImsettingsAction extends ConnectSettingsAction $user->nickname, $jabber); + // TRANS: Message given saving valid IM address that is to be confirmed. + // TRANS: %s is the IM address set for the site. $msg = sprintf(_('A confirmation code was sent '. 'to the IM address you added. '. 'You must approve %s for '. @@ -348,10 +379,12 @@ class ImsettingsAction extends ConnectSettingsAction $confirm = $this->getConfirmation(); if (!$confirm) { + // TRANS: Message given canceling IM address confirmation that is not pending. $this->showForm(_('No pending confirmation to cancel.')); return; } if ($confirm->address != $jabber) { + // TRANS: Message given canceling IM address confirmation for the wrong IM address. $this->showForm(_('That is the wrong IM address.')); return; } @@ -360,11 +393,13 @@ class ImsettingsAction extends ConnectSettingsAction if (!$result) { common_log_db_error($confirm, 'DELETE', __FILE__); - $this->serverError(_('Couldn\'t delete email confirmation.')); + // TRANS: Server error thrown on database error canceling IM address confirmation. + $this->serverError(_('Couldn\'t delete IM confirmation.')); return; } - $this->showForm(_('Confirmation cancelled.'), true); + // TRANS: Message given after successfully canceling IM address confirmation. + $this->showForm(_('IM confirmation cancelled.'), true); } /** @@ -384,6 +419,8 @@ class ImsettingsAction extends ConnectSettingsAction // Maybe an old tab open...? if ($user->jabber != $jabber) { + // TRANS: Message given trying to remove an IM address that is not + // TRANS: registered for the active user. $this->showForm(_('That is not your Jabber ID.')); return; } @@ -398,6 +435,7 @@ class ImsettingsAction extends ConnectSettingsAction if (!$result) { common_log_db_error($user, 'UPDATE', __FILE__); + // TRANS: Server error thrown on database error removing a registered IM address. $this->serverError(_('Couldn\'t update user.')); return; } @@ -405,7 +443,8 @@ class ImsettingsAction extends ConnectSettingsAction // XXX: unsubscribe to the old address - $this->showForm(_('The address was removed.'), true); + // TRANS: Message given after successfully removing a registered IM address. + $this->showForm(_('The IM address was removed.'), true); } /** diff --git a/actions/invite.php b/actions/invite.php index 54b2de62a..4bba8893d 100644 --- a/actions/invite.php +++ b/actions/invite.php @@ -38,7 +38,7 @@ class InviteAction extends CurrentUserDesignAction if (!common_config('invite', 'enabled')) { $this->clientError(_('Invites have been disabled.')); } else if (!common_logged_in()) { - $this->clientError(sprintf(_('You must be logged in to invite other users to use %s'), + $this->clientError(sprintf(_('You must be logged in to invite other users to use %s.'), common_config('site', 'name'))); return; } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { @@ -224,8 +224,10 @@ class InviteAction extends CurrentUserDesignAction $headers['From'] = mail_notify_from(); $headers['To'] = trim($email); + // TRANS: Subject for invitation email. Note that 'them' is correct as a gender-neutral singular 3rd-person pronoun in English. $headers['Subject'] = sprintf(_('%1$s has invited you to join them on %2$s'), $bestname, $sitename); + // TRANS: Body text for invitation email. Note that 'them' is correct as a gender-neutral singular 3rd-person pronoun in English. $body = sprintf(_("%1\$s has invited you to join them on %2\$s (%3\$s).\n\n". "%2\$s is a micro-blogging service that lets you keep up-to-date with people you know and people who interest you.\n\n". "You can also share news about yourself, your thoughts, or your life online with people who know about you. ". diff --git a/actions/login.php b/actions/login.php index 8ea3c800b..d3e4312f7 100644 --- a/actions/login.php +++ b/actions/login.php @@ -63,6 +63,28 @@ class LoginAction extends Action } /** + * Prepare page to run + * + * + * @param $args + * @return string title + */ + + function prepare($args) + { + parent::prepare($args); + + // @todo this check should really be in index.php for all sensitive actions + $ssl = common_config('site', 'ssl'); + if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) { + common_redirect(common_local_url('login')); + // exit + } + + return true; + } + + /** * Handle input, produce output * * Switches on request method; either shows the form or handles its input. @@ -267,9 +289,13 @@ class LoginAction extends Action 'user name and password ' . 'before changing your settings.'); } else { - return _('Login with your username and password. ' . - 'Don\'t have a username yet? ' . - '[Register](%%action.register%%) a new account.'); + $prompt = _('Login with your username and password.'); + if (!common_config('site', 'closed') && !common_config('site', 'inviteonly')) { + $prompt .= ' '; + $prompt .= _('Don\'t have a username yet? ' . + '[Register](%%action.register%%) a new account.'); + } + return $prompt; } } diff --git a/actions/makeadmin.php b/actions/makeadmin.php index f19348648..9ccb44230 100644 --- a/actions/makeadmin.php +++ b/actions/makeadmin.php @@ -41,7 +41,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @link http://status.net/ */ -class MakeadminAction extends Action +class MakeadminAction extends RedirectingAction { var $profile = null; var $group = null; @@ -148,20 +148,19 @@ class MakeadminAction extends Action $this->group->getBestName()); } - foreach ($this->args as $k => $v) { - if ($k == 'returnto-action') { - $action = $v; - } else if (substr($k, 0, 9) == 'returnto-') { - $args[substr($k, 9)] = $v; - } - } + $this->returnToArgs(); + } - if ($action) { - common_redirect(common_local_url($action, $args), 303); - } else { - common_redirect(common_local_url('groupmembers', - array('nickname' => $this->group->nickname)), - 303); - } + /** + * If we reached this form without returnto arguments, default to + * the top of the group's member list. + * + * @return string URL + */ + function defaultReturnTo() + { + return common_local_url('groupmembers', + array('nickname' => $this->group->nickname)); } + } diff --git a/actions/microsummary.php b/actions/microsummary.php index 5c761e8bb..d145dc3bc 100644 --- a/actions/microsummary.php +++ b/actions/microsummary.php @@ -66,7 +66,7 @@ class MicrosummaryAction extends Action $notice = $user->getCurrentNotice(); if (!$notice) { - $this->clientError(_('No current status'), 404); + $this->clientError(_('No current status.'), 404); } header('Content-Type: text/plain'); diff --git a/actions/newnotice.php b/actions/newnotice.php index ed0fa1b2b..748d104ff 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -184,13 +184,21 @@ class NewnoticeAction extends Action $options = array('reply_to' => ($replyto == 'false') ? null : $replyto); - if ($user->shareLocation() && $this->arg('notice_data-geo')) { - - $locOptions = Notice::locationOptions($this->trimmed('lat'), - $this->trimmed('lon'), - $this->trimmed('location_id'), - $this->trimmed('location_ns'), - $user->getProfile()); + if ($user->shareLocation()) { + // use browser data if checked; otherwise profile data + if ($this->arg('notice_data-geo')) { + $locOptions = Notice::locationOptions($this->trimmed('lat'), + $this->trimmed('lon'), + $this->trimmed('location_id'), + $this->trimmed('location_ns'), + $user->getProfile()); + } else { + $locOptions = Notice::locationOptions(null, + null, + null, + null, + $user->getProfile()); + } $options = array_merge($options, $locOptions); } @@ -201,8 +209,6 @@ class NewnoticeAction extends Action $upload->attachToNotice($notice); } - - if ($this->boolean('ajax')) { header('Content-Type: text/xml;charset=utf-8'); $this->xw->startDocument('1.0', 'UTF-8'); diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index b1467f0d0..8a206d710 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -99,7 +99,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction $application = $profile->getApplications($offset, $limit); - $cnt == 0; + $cnt = 0; if (!empty($application)) { $al = new ApplicationList($application, $user, $this, true); @@ -112,7 +112,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction $this->pagination($this->page > 1, $cnt > APPS_PER_PAGE, $this->page, 'connectionssettings', - array('nickname' => $this->user->nickname)); + array('nickname' => $user->nickname)); } /** @@ -183,7 +183,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction if (!$result) { common_log_db_error($orig, 'DELETE', __FILE__); - $this->clientError(_('Unable to revoke access for app: ' . $app->id)); + $this->clientError(sprintf(_('Unable to revoke access for app: %s.'), $app->id)); return false; } @@ -195,7 +195,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction function showEmptyListMessage() { - $message = sprintf(_('You have not authorized any applications to use your account.')); + $message = _('You have not authorized any applications to use your account.'); $this->elementStart('div', 'guide'); $this->raw(common_markup_to_html($message)); diff --git a/actions/oembed.php b/actions/oembed.php index e287b6ae2..e25e4cb25 100644 --- a/actions/oembed.php +++ b/actions/oembed.php @@ -23,6 +23,7 @@ * @package StatusNet * @author Evan Prodromou <evan@status.net> * @copyright 2008 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -60,7 +61,7 @@ class OembedAction extends Action $proxy_args = $r->map($path); if (!$proxy_args) { - $this->serverError(_("$path not found"), 404); + $this->serverError(_("$path not found."), 404); } $oembed=array(); $oembed['version']='1.0'; @@ -72,11 +73,11 @@ class OembedAction extends Action $id = $proxy_args['notice']; $notice = Notice::staticGet($id); if(empty($notice)){ - $this->serverError(_("notice $id not found"), 404); + $this->serverError(_("Notice $id not found."), 404); } $profile = $notice->getProfile(); if (empty($profile)) { - $this->serverError(_('Notice has no profile'), 500); + $this->serverError(_('Notice has no profile.'), 500); } if (!empty($profile->fullname)) { $authorname = $profile->fullname . ' (' . $profile->nickname . ')'; @@ -95,7 +96,7 @@ class OembedAction extends Action $id = $proxy_args['attachment']; $attachment = File::staticGet($id); if(empty($attachment)){ - $this->serverError(_("attachment $id not found"), 404); + $this->serverError(_("Attachment $id not found."), 404); } if(empty($attachment->filename) && $file_oembed = File_oembed::staticGet('file_id', $attachment->id)){ // Proxy the existing oembed information @@ -123,7 +124,7 @@ class OembedAction extends Action if($attachment->title) $oembed['title']=$attachment->title; break; default: - $this->serverError(_("$path not supported for oembed requests"), 501); + $this->serverError(_("$path not supported for oembed requests."), 501); } switch($args['format']){ case 'xml': @@ -154,10 +155,12 @@ class OembedAction extends Action $this->end_document('json'); break; default: - $this->serverError(_('content type ' . $apidata['content-type'] . ' not supported'), 501); + // TRANS: Error message displaying attachments. %s is a raw MIME type (eg 'image/png') + $this->serverError(sprintf(_('Content type %s not supported.'), $apidata['content-type']), 501); } }else{ - $this->serverError(_('Only ' . common_root_url() . ' urls over plain http please'), 404); + // TRANS: Error message displaying attachments. %s is the site's base URL. + $this->serverError(sprintf(_('Only %s URLs over plain HTTP please.'), common_root_url()), 404); } } diff --git a/actions/otp.php b/actions/otp.php index acf84aee8..1e06603d4 100644 --- a/actions/otp.php +++ b/actions/otp.php @@ -126,6 +126,8 @@ class OtpAction extends Action $this->lt->delete(); $this->lt = null; + common_real_login(true); + if ($this->rememberme) { common_rememberme($this->user); } diff --git a/actions/pathsadminpanel.php b/actions/pathsadminpanel.php index 9155a7e42..7ff3c2583 100644 --- a/actions/pathsadminpanel.php +++ b/actions/pathsadminpanel.php @@ -154,19 +154,19 @@ class PathsadminpanelAction extends AdminPanelAction // Validate theme dir if (!empty($values['theme']['dir']) && !is_readable($values['theme']['dir'])) { - $this->clientError(sprintf(_("Theme directory not readable: %s"), $values['theme']['dir'])); + $this->clientError(sprintf(_("Theme directory not readable: %s."), $values['theme']['dir'])); } // Validate avatar dir if (empty($values['avatar']['dir']) || !is_writable($values['avatar']['dir'])) { - $this->clientError(sprintf(_("Avatar directory not writable: %s"), $values['avatar']['dir'])); + $this->clientError(sprintf(_("Avatar directory not writable: %s."), $values['avatar']['dir'])); } // Validate background dir if (empty($values['background']['dir']) || !is_writable($values['background']['dir'])) { - $this->clientError(sprintf(_("Background directory not writable: %s"), $values['background']['dir'])); + $this->clientError(sprintf(_("Background directory not writable: %s."), $values['background']['dir'])); } // Validate locales dir @@ -174,13 +174,13 @@ class PathsadminpanelAction extends AdminPanelAction // XXX: What else do we need to validate for lacales path here? --Z if (!empty($values['site']['locale_path']) && !is_readable($values['site']['locale_path'])) { - $this->clientError(sprintf(_("Locales directory not readable: %s"), $values['site']['locale_path'])); + $this->clientError(sprintf(_("Locales directory not readable: %s."), $values['site']['locale_path'])); } // Validate SSL setup if (mb_strlen($values['site']['sslserver']) > 255) { - $this->clientError(_("Invalid SSL server. The maximum length is 255 characters.")); + $this->clientError(_('Invalid SSL server. The maximum length is 255 characters.')); } } diff --git a/actions/peopletag.php b/actions/peopletag.php index 4ba1dc0f1..7287cfbf9 100644 --- a/actions/peopletag.php +++ b/actions/peopletag.php @@ -32,8 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -require_once INSTALLDIR.'/lib/profilelist.php'; - /** * This class outputs a paginated list of profiles self-tagged with a given tag * @@ -67,7 +65,7 @@ class PeopletagAction extends Action $this->tag = $this->trimmed('tag'); if (!common_valid_profile_tag($this->tag)) { - $this->clientError(sprintf(_('Not a valid people tag: %s'), + $this->clientError(sprintf(_('Not a valid people tag: %s.'), $this->tag)); return; } @@ -124,8 +122,8 @@ class PeopletagAction extends Action $profile->query(sprintf($qry, $this->tag, $lim)); - $pl = new ProfileList($profile, $this); - $cnt = $pl->show(); + $ptl = new PeopleTagList($profile, $this); // pass the ammunition + $cnt = $ptl->show(); $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE, @@ -146,3 +144,37 @@ class PeopletagAction extends Action } } + +class PeopleTagList extends ProfileList +{ + function newListItem($profile) + { + return new PeopleTagListItem($profile, $this->action); + } +} + +class PeopleTagListItem extends ProfileListItem +{ + function linkAttributes() + { + $aAttrs = parent::linkAttributes(); + + if (common_config('nofollow', 'peopletag')) { + $aAttrs['rel'] .= ' nofollow'; + } + + return $aAttrs; + } + + function homepageAttributes() + { + $aAttrs = parent::linkAttributes(); + + if (common_config('nofollow', 'peopletag')) { + $aAttrs['rel'] = 'nofollow'; + } + + return $aAttrs; + } +} + diff --git a/actions/postnotice.php b/actions/postnotice.php index b2f6f1bb9..694c7808d 100644 --- a/actions/postnotice.php +++ b/actions/postnotice.php @@ -92,7 +92,7 @@ class PostnoticeAction extends Action { $content = common_shorten_links($_POST['omb_notice_content']); if (Notice::contentTooLong($content)) { - $this->clientError(_('Invalid notice content'), 400); + $this->clientError(_('Invalid notice content.'), 400); return false; } $license = $_POST['omb_notice_license']; diff --git a/actions/public.php b/actions/public.php index 0b3b5fde8..5fc547fea 100644 --- a/actions/public.php +++ b/actions/public.php @@ -80,7 +80,7 @@ class PublicAction extends Action $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; if ($this->page > MAX_PUBLIC_PAGE) { - $this->clientError(sprintf(_("Beyond the page limit (%s)"), MAX_PUBLIC_PAGE)); + $this->clientError(sprintf(_("Beyond the page limit (%s)."), MAX_PUBLIC_PAGE)); } common_set_returnto($this->selfUrl()); @@ -95,7 +95,7 @@ class PublicAction extends Action if($this->page > 1 && $this->notice->N == 0){ // TRANS: Server error when page not found (404) - $this->serverError(_('No such page'),$code=404); + $this->serverError(_('No such page.'),$code=404); } return true; diff --git a/actions/publictagcloud.php b/actions/publictagcloud.php index 9993b2d3f..70c356659 100644 --- a/actions/publictagcloud.php +++ b/actions/publictagcloud.php @@ -109,7 +109,7 @@ class PublictagcloudAction extends Action $cutoff = sprintf("notice_tag.created > '%s'", common_sql_date(time() - common_config('tag', 'cutoff'))); $tags->selectAdd($calc . ' as weight'); - $tags->addWhere($cutoff); + $tags->whereAdd($cutoff); $tags->groupBy('tag'); $tags->orderBy('weight DESC'); diff --git a/actions/publicxrds.php b/actions/publicxrds.php index 5fd4eead7..8f0337e4f 100644 --- a/actions/publicxrds.php +++ b/actions/publicxrds.php @@ -8,7 +8,9 @@ * @category Action * @package StatusNet * @author Evan Prodromou <evan@status.net> + * @author Craig Andrews <candrews@integralblue.com> * @author Robin Millette <millette@status.net> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @link http://status.net/ * @@ -44,6 +46,7 @@ require_once INSTALLDIR.'/lib/xrdsoutputter.php'; * @author Evan Prodromou <evan@status.net> * @author Robin Millette <millette@status.net> * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @link http://status.net/ * diff --git a/actions/recoverpassword.php b/actions/recoverpassword.php index dcff35f6e..f9956897f 100644 --- a/actions/recoverpassword.php +++ b/actions/recoverpassword.php @@ -21,7 +21,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } # You have 24 hours to claim your password -define(MAX_RECOVERY_TIME, 24 * 60 * 60); +define('MAX_RECOVERY_TIME', 24 * 60 * 60); class RecoverpasswordAction extends Action { @@ -262,10 +262,20 @@ class RecoverpasswordAction extends Action # See if it's an unconfirmed email address if (!$user) { - $confirm_email = Confirm_address::staticGet('address', common_canonical_email($nore)); - if ($confirm_email && $confirm_email->address_type == 'email') { + // Warning: it may actually be legit to have multiple folks + // who have claimed, but not yet confirmed, the same address. + // We'll only send to the first one that comes up. + $confirm_email = new Confirm_address(); + $confirm_email->address = common_canonical_email($nore); + $confirm_email->address_type = 'email'; + $confirm_email->find(); + if ($confirm_email->fetch()) { $user = User::staticGet($confirm_email->user_id); + } else { + $confirm_email = null; } + } else { + $confirm_email = null; } if (!$user) { @@ -276,9 +286,11 @@ class RecoverpasswordAction extends Action # Try to get an unconfirmed email address if they used a user name if (!$user->email && !$confirm_email) { - $confirm_email = Confirm_address::staticGet('user_id', $user->id); - if ($confirm_email && $confirm_email->address_type != 'email') { - # Skip non-email confirmations + $confirm_email = new Confirm_address(); + $confirm_email->user_id = $user->id; + $confirm_email->address_type = 'email'; + $confirm_email->find(); + if (!$confirm_email->fetch()) { $confirm_email = null; } } @@ -294,7 +306,7 @@ class RecoverpasswordAction extends Action $confirm->code = common_confirmation_code(128); $confirm->address_type = 'recover'; $confirm->user_id = $user->id; - $confirm->address = (isset($user->email)) ? $user->email : $confirm_email->address; + $confirm->address = (!empty($user->email)) ? $user->email : $confirm_email->address; if (!$confirm->insert()) { common_log_db_error($confirm, 'INSERT', __FILE__); @@ -319,7 +331,8 @@ class RecoverpasswordAction extends Action $body .= common_config('site', 'name'); $body .= "\n"; - mail_to_user($user, _('Password recovery requested'), $body, $confirm->address); + $headers = _mail_prepare_headers('recoverpassword', $user->nickname, $user->nickname); + mail_to_user($user, _('Password recovery requested'), $body, $headers, $confirm->address); $this->mode = 'sent'; $this->msg = _('Instructions for recovering your password ' . diff --git a/actions/register.php b/actions/register.php index ccab76cf0..7307bc689 100644 --- a/actions/register.php +++ b/actions/register.php @@ -74,6 +74,13 @@ class RegisterAction extends Action parent::prepare($args); $this->code = $this->trimmed('code'); + // @todo this check should really be in index.php for all sensitive actions + $ssl = common_config('site', 'ssl'); + if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) { + common_redirect(common_local_url('register')); + // exit + } + if (empty($this->code)) { common_ensure_session(); if (array_key_exists('invitecode', $_SESSION)) { @@ -341,7 +348,7 @@ class RegisterAction extends Action } else { $instr = common_markup_to_html(_('With this form you can create '. - ' a new account. ' . + 'a new account. ' . 'You can then post notices and '. 'link up to friends and colleagues. ')); @@ -491,11 +498,7 @@ class RegisterAction extends Action $this->elementStart('li'); $this->element('input', $attrs); $this->elementStart('label', array('class' => 'checkbox', 'for' => 'license')); - $this->text(_('My text and files are available under ')); - $this->element('a', array('href' => common_config('license', 'url')), - common_config('license', 'title'), _("Creative Commons Attribution 3.0")); - $this->text(_(' except this private data: password, '. - 'email address, IM address, and phone number.')); + $this->raw($this->licenseCheckbox()); $this->elementEnd('label'); $this->elementEnd('li'); } @@ -505,6 +508,48 @@ class RegisterAction extends Action $this->elementEnd('form'); } + function licenseCheckbox() + { + $out = ''; + switch (common_config('license', 'type')) { + case 'private': + // TRANS: Copyright checkbox label in registration dialog, for private sites. + $out .= htmlspecialchars(sprintf( + _('I understand that content and data of %1$s are private and confidential.'), + common_config('site', 'name'))); + // fall through + case 'allrightsreserved': + if ($out != '') { + $out .= ' '; + } + if (common_config('license', 'owner')) { + // TRANS: Copyright checkbox label in registration dialog, for all rights reserved with a specified copyright owner. + $out .= htmlspecialchars(sprintf( + _('My text and files are copyright by %1$s.'), + common_config('license', 'owner'))); + } else { + // TRANS: Copyright checkbox label in registration dialog, for all rights reserved with ownership left to contributors. + $out .= htmlspecialchars(_('My text and files remain under my own copyright.')); + } + // TRANS: Copyright checkbox label in registration dialog, for all rights reserved. + $out .= ' ' . _('All rights reserved.'); + break; + case 'cc': // fall through + default: + // TRANS: Copyright checkbox label in registration dialog, for Creative Commons-style licenses. + $message = _('My text and files are available under %s ' . + 'except this private data: password, ' . + 'email address, IM address, and phone number.'); + $link = '<a href="' . + htmlspecialchars(common_config('license', 'url')) . + '">' . + htmlspecialchars(common_config('license', 'title')) . + '</a>'; + $out .= sprintf(htmlspecialchars($message), $link); + } + return $out; + } + /** * Show some information about registering for the site * diff --git a/actions/remotesubscribe.php b/actions/remotesubscribe.php index c723d53a1..9fc235e74 100644 --- a/actions/remotesubscribe.php +++ b/actions/remotesubscribe.php @@ -188,7 +188,7 @@ class RemotesubscribeAction extends Action $profile = $user->getProfile(); if (!$profile) { common_log_db_error($user, 'SELECT', __FILE__); - $this->serverError(_('User without matching profile')); + $this->serverError(_('User without matching profile.')); return; } diff --git a/actions/repeat.php b/actions/repeat.php index e112496bc..893cae4ff 100644 --- a/actions/repeat.php +++ b/actions/repeat.php @@ -54,21 +54,21 @@ class RepeatAction extends Action $this->user = common_current_user(); if (empty($this->user)) { - $this->clientError(_("Only logged-in users can repeat notices.")); + $this->clientError(_('Only logged-in users can repeat notices.')); return false; } $id = $this->trimmed('notice'); if (empty($id)) { - $this->clientError(_("No notice specified.")); + $this->clientError(_('No notice specified.')); return false; } $this->notice = Notice::staticGet('id', $id); if (empty($this->notice)) { - $this->clientError(_("No notice specified.")); + $this->clientError(_('No notice specified.')); return false; } @@ -80,14 +80,14 @@ class RepeatAction extends Action $token = $this->trimmed('token-'.$id); if (empty($token) || $token != common_session_token()) { - $this->clientError(_("There was a problem with your session token. Try again, please.")); + $this->clientError(_('There was a problem with your session token. Try again, please.')); return false; } $profile = $this->user->getProfile(); if ($profile->hasRepeated($id)) { - $this->clientError(_("You already repeated that notice.")); + $this->clientError(_('You already repeated that notice.')); return false; } diff --git a/actions/replies.php b/actions/replies.php index 4ff1b7a8d..608f71d6e 100644 --- a/actions/replies.php +++ b/actions/replies.php @@ -90,7 +90,7 @@ class RepliesAction extends OwnerDesignAction if($this->page > 1 && $this->notice->N == 0){ // TRANS: Server error when page not found (404) - $this->serverError(_('No such page'),$code=404); + $this->serverError(_('No such page.'),$code=404); } return true; diff --git a/actions/revokerole.php b/actions/revokerole.php new file mode 100644 index 000000000..c67b70fda --- /dev/null +++ b/actions/revokerole.php @@ -0,0 +1,99 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Action class to sandbox an abusive 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 Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Sandbox a user. + * + * @category Action + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class RevokeRoleAction extends ProfileFormAction +{ + /** + * Check parameters + * + * @param array $args action arguments (URL, GET, POST) + * + * @return boolean success flag + */ + + function prepare($args) + { + if (!parent::prepare($args)) { + return false; + } + + $this->role = $this->arg('role'); + if (!Profile_role::isValid($this->role)) { + $this->clientError(_('Invalid role.')); + return false; + } + if (!Profile_role::isSettable($this->role)) { + $this->clientError(_('This role is reserved and cannot be set.')); + return false; + } + + $cur = common_current_user(); + + assert(!empty($cur)); // checked by parent + + if (!$cur->hasRight(Right::REVOKEROLE)) { + $this->clientError(_('You cannot revoke user roles on this site.')); + return false; + } + + assert(!empty($this->profile)); // checked by parent + + if (!$this->profile->hasRole($this->role)) { + $this->clientError(_("User doesn't have this role.")); + return false; + } + + return true; + } + + /** + * Sandbox a user. + * + * @return void + */ + + function handlePost() + { + $this->profile->revokeRole($this->role); + } +} diff --git a/actions/sandbox.php b/actions/sandbox.php index 5b034ff07..d1ef4c86b 100644 --- a/actions/sandbox.php +++ b/actions/sandbox.php @@ -62,14 +62,14 @@ class SandboxAction extends ProfileFormAction assert(!empty($cur)); // checked by parent if (!$cur->hasRight(Right::SANDBOXUSER)) { - $this->clientError(_("You cannot sandbox users on this site.")); + $this->clientError(_('You cannot sandbox users on this site.')); return false; } assert(!empty($this->profile)); // checked by parent if ($this->profile->isSandboxed()) { - $this->clientError(_("User is already sandboxed.")); + $this->clientError(_('User is already sandboxed.')); return false; } diff --git a/actions/showfavorites.php b/actions/showfavorites.php index 5b85de683..7f3c77ee2 100644 --- a/actions/showfavorites.php +++ b/actions/showfavorites.php @@ -121,11 +121,11 @@ class ShowfavoritesAction extends OwnerDesignAction // Show imported/gateway notices as well as local if // the user is looking at his own favorites - $this->notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE, - NOTICES_PER_PAGE + 1, true); + $this->notice = $this->user->favoriteNotices(true, ($this->page-1)*NOTICES_PER_PAGE, + NOTICES_PER_PAGE + 1); } else { - $this->notice = $this->user->favoriteNotices(($this->page-1)*NOTICES_PER_PAGE, - NOTICES_PER_PAGE + 1, false); + $this->notice = $this->user->favoriteNotices(false, ($this->page-1)*NOTICES_PER_PAGE, + NOTICES_PER_PAGE + 1); } if (empty($this->notice)) { @@ -135,7 +135,7 @@ class ShowfavoritesAction extends OwnerDesignAction if($this->page > 1 && $this->notice->N == 0){ // TRANS: Server error when page not found (404) - $this->serverError(_('No such page'),$code=404); + $this->serverError(_('No such page.'),$code=404); } return true; diff --git a/actions/showgroup.php b/actions/showgroup.php index 4e1fcb6c7..17c37e4d7 100644 --- a/actions/showgroup.php +++ b/actions/showgroup.php @@ -221,7 +221,8 @@ class ShowgroupAction extends GroupDesignAction function showGroupProfile() { - $this->elementStart('div', 'entity_profile vcard author'); + $this->elementStart('div', array('id' => 'i', + 'class' => 'entity_profile vcard author')); $this->element('h2', null, _('Group profile')); @@ -301,19 +302,20 @@ class ShowgroupAction extends GroupDesignAction $this->element('h2', null, _('Group actions')); $this->elementStart('ul'); $this->elementStart('li', 'entity_subscribe'); - $cur = common_current_user(); - if ($cur) { - if ($cur->isMember($this->group)) { - $lf = new LeaveForm($this, $this->group); - $lf->show(); - } else if (!Group_block::isBlocked($this->group, $cur->getProfile())) { - $jf = new JoinForm($this, $this->group); - $jf->show(); + if (Event::handle('StartGroupSubscribe', array($this, $this->group))) { + $cur = common_current_user(); + if ($cur) { + if ($cur->isMember($this->group)) { + $lf = new LeaveForm($this, $this->group); + $lf->show(); + } else if (!Group_block::isBlocked($this->group, $cur->getProfile())) { + $jf = new JoinForm($this, $this->group); + $jf->show(); + } } + Event::handle('EndGroupSubscribe', array($this, $this->group)); } - $this->elementEnd('li'); - $this->elementEnd('ul'); $this->elementEnd('div'); } @@ -386,18 +388,23 @@ class ShowgroupAction extends GroupDesignAction $this->elementStart('div', array('id' => 'entity_members', 'class' => 'section')); - $this->element('h2', null, _('Members')); + if (Event::handle('StartShowGroupMembersMiniList', array($this))) { - $pml = new ProfileMiniList($member, $this); - $cnt = $pml->show(); - if ($cnt == 0) { - $this->element('p', null, _('(None)')); - } + $this->element('h2', null, _('Members')); + + $gmml = new GroupMembersMiniList($member, $this); + $cnt = $gmml->show(); + if ($cnt == 0) { + $this->element('p', null, _('(None)')); + } - if ($cnt > MEMBERS_PER_SECTION) { - $this->element('a', array('href' => common_local_url('groupmembers', - array('nickname' => $this->group->nickname))), - _('All members')); + if ($cnt > MEMBERS_PER_SECTION) { + $this->element('a', array('href' => common_local_url('groupmembers', + array('nickname' => $this->group->nickname))), + _('All members')); + } + + Event::handle('EndShowGroupMembersMiniList', array($this)); } $this->elementEnd('div'); @@ -423,14 +430,6 @@ class ShowgroupAction extends GroupDesignAction function showStatistics() { - // XXX: WORM cache this - $members = $this->group->getMembers(); - $members_count = 0; - /** $member->count() doesn't work. */ - while ($members->fetch()) { - $members_count++; - } - $this->elementStart('div', array('id' => 'entity_statistics', 'class' => 'section')); @@ -444,7 +443,7 @@ class ShowgroupAction extends GroupDesignAction $this->elementStart('dl', 'entity_members'); $this->element('dt', null, _('Members')); - $this->element('dd', null, (is_int($members_count)) ? $members_count : '0'); + $this->element('dd', null, $this->group->getMemberCount()); $this->elementEnd('dl'); $this->elementEnd('div'); @@ -500,3 +499,26 @@ class GroupAdminSection extends ProfileSection return null; } } + +class GroupMembersMiniList extends ProfileMiniList +{ + function newListItem($profile) + { + return new GroupMembersMiniListItem($profile, $this->action); + } +} + +class GroupMembersMiniListItem extends ProfileMiniListItem +{ + function linkAttributes() + { + $aAttrs = parent::linkAttributes(); + + if (common_config('nofollow', 'members')) { + $aAttrs['rel'] .= ' nofollow'; + } + + return $aAttrs; + } +} + diff --git a/actions/shownotice.php b/actions/shownotice.php index d09100f67..9c5d83441 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -97,17 +97,12 @@ class ShownoticeAction extends OwnerDesignAction $this->profile = $this->notice->getProfile(); if (empty($this->profile)) { - $this->serverError(_('Notice has no profile'), 500); + $this->serverError(_('Notice has no profile.'), 500); return false; } $this->user = User::staticGet('id', $this->profile->id); - if ($this->notice->is_local == Notice::REMOTE_OMB) { - common_redirect($this->notice->uri); - return false; - } - $this->avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE); return true; @@ -172,7 +167,7 @@ class ShownoticeAction extends OwnerDesignAction function title() { if (!empty($this->profile->fullname)) { - $base = $this->profile->fullname . ' (' . $this->profile->nickname . ') '; + $base = $this->profile->fullname . ' (' . $this->profile->nickname . ')'; } else { $base = $this->profile->nickname; } @@ -198,13 +193,20 @@ class ShownoticeAction extends OwnerDesignAction if ($this->notice->is_local == Notice::REMOTE_OMB) { if (!empty($this->notice->url)) { - common_redirect($this->notice->url, 301); + $target = $this->notice->url; } else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) { - common_redirect($this->notice->uri, 301); + // Old OMB posts saved the remote URL only into the URI field. + $target = $this->notice->uri; + } else { + // Shouldn't happen. + $target = false; + } + if ($target && $target != $this->selfUrl()) { + common_redirect($target, 301); + return false; } - } else { - $this->showPage(); } + $this->showPage(); } /** diff --git a/actions/silence.php b/actions/silence.php index 206e5ba87..09cc480d9 100644 --- a/actions/silence.php +++ b/actions/silence.php @@ -62,14 +62,14 @@ class SilenceAction extends ProfileFormAction assert(!empty($cur)); // checked by parent if (!$cur->hasRight(Right::SILENCEUSER)) { - $this->clientError(_("You cannot silence users on this site.")); + $this->clientError(_('You cannot silence users on this site.')); return false; } assert(!empty($this->profile)); // checked by parent if ($this->profile->isSilenced()) { - $this->clientError(_("User is already silenced.")); + $this->clientError(_('User is already silenced.')); return false; } diff --git a/actions/siteadminpanel.php b/actions/siteadminpanel.php index 8c8f8b374..4238b3e85 100644 --- a/actions/siteadminpanel.php +++ b/actions/siteadminpanel.php @@ -66,7 +66,7 @@ class SiteadminpanelAction extends AdminPanelAction function getInstructions() { - return _('Basic settings for this StatusNet site.'); + return _('Basic settings for this StatusNet site'); } /** @@ -90,10 +90,11 @@ class SiteadminpanelAction extends AdminPanelAction function saveSettings() { - static $settings = array('site' => array('name', 'broughtby', 'broughtbyurl', - 'email', 'timezone', 'language', - 'site', 'textlimit', 'dupelimit'), - 'snapshot' => array('run', 'reporturl', 'frequency')); + static $settings = array( + 'site' => array('name', 'broughtby', 'broughtbyurl', + 'email', 'timezone', 'language', + 'site', 'textlimit', 'dupelimit'), + ); $values = array(); @@ -129,7 +130,7 @@ class SiteadminpanelAction extends AdminPanelAction // Validate site name if (empty($values['site']['name'])) { - $this->clientError(_("Site name must have non-zero length.")); + $this->clientError(_('Site name must have non-zero length.')); } // Validate email @@ -158,35 +159,16 @@ class SiteadminpanelAction extends AdminPanelAction $this->clientError(sprintf(_('Unknown language "%s".'), $values['site']['language'])); } - // Validate report URL - - if (!is_null($values['snapshot']['reporturl']) && - !Validate::uri($values['snapshot']['reporturl'], array('allowed_schemes' => array('http', 'https')))) { - $this->clientError(_("Invalid snapshot report URL.")); - } - - // Validate snapshot run value - - if (!in_array($values['snapshot']['run'], array('web', 'cron', 'never'))) { - $this->clientError(_("Invalid snapshot run value.")); - } - - // Validate snapshot run value - - if (!Validate::number($values['snapshot']['frequency'])) { - $this->clientError(_("Snapshot frequency must be a number.")); - } - // Validate text limit - if (!Validate::number($values['site']['textlimit'], array('min' => 140))) { - $this->clientError(_("Minimum text limit is 140 characters.")); + if (!Validate::number($values['site']['textlimit'], array('min' => 0))) { + $this->clientError(_("Minimum text limit is 0 (unlimited).")); } // Validate dupe limit if (!Validate::number($values['site']['dupelimit'], array('min' => 1))) { - $this->clientError(_("Dupe limit must 1 or more seconds.")); + $this->clientError(_("Dupe limit must be one or more seconds.")); } } @@ -277,40 +259,14 @@ class SiteAdminPanelForm extends AdminForm $this->unli(); $this->li(); - $this->out->dropdown('language', _('Language'), - get_nice_language_list(), _('Default site language'), + $this->out->dropdown('language', _('Default language'), + get_nice_language_list(), _('Site language when autodetection from browser settings is not available'), false, $this->value('language')); $this->unli(); $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); - $this->out->elementStart('fieldset', array('id' => 'settings_admin_snapshots')); - $this->out->element('legend', null, _('Snapshots')); - $this->out->elementStart('ul', 'form_data'); - $this->li(); - $snapshot = array('web' => _('Randomly during Web hit'), - 'cron' => _('In a scheduled job'), - 'never' => _('Never')); - $this->out->dropdown('run', _('Data snapshots'), - $snapshot, _('When to send statistical data to status.net servers'), - false, $this->value('run', 'snapshot')); - $this->unli(); - - $this->li(); - $this->input('frequency', _('Frequency'), - _('Snapshots will be sent once every N web hits'), - 'snapshot'); - $this->unli(); - - $this->li(); - $this->input('reporturl', _('Report URL'), - _('Snapshots will be sent to this URL'), - 'snapshot'); - $this->unli(); - $this->out->elementEnd('ul'); - $this->out->elementEnd('fieldset'); - $this->out->elementStart('fieldset', array('id' => 'settings_admin_limits')); $this->out->element('legend', null, _('Limits')); $this->out->elementStart('ul', 'form_data'); diff --git a/actions/sitenoticeadminpanel.php b/actions/sitenoticeadminpanel.php new file mode 100644 index 000000000..bdcaa2355 --- /dev/null +++ b/actions/sitenoticeadminpanel.php @@ -0,0 +1,201 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Site notice administration panel + * + * 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 Settings + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php'; + +/** + * Update the site-wide notice text + * + * @category Admin + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class SitenoticeadminpanelAction extends AdminPanelAction +{ + /** + * Returns the page title + * + * @return string page title + */ + + function title() + { + return _('Site Notice'); + } + + /** + * Instructions for using this form. + * + * @return string instructions + */ + + function getInstructions() + { + return _('Edit site-wide message'); + } + + /** + * Show the site notice admin panel form + * + * @return void + */ + + function showForm() + { + $form = new SiteNoticeAdminPanelForm($this); + $form->show(); + return; + } + + /** + * Save settings from the form + * + * @return void + */ + + function saveSettings() + { + $siteNotice = $this->trimmed('site-notice'); + + // assert(all values are valid); + // This throws an exception on validation errors + + $this->validate($siteNotice); + + $config = new Config(); + + $result = Config::save('site', 'notice', $siteNotice); + + if (!$result) { + $this->ServerError(_("Unable to save site notice.")); + } + } + + function validate(&$siteNotice) + { + // Validate notice text + + if (mb_strlen($siteNotice) > 255) { + $this->clientError( + _('Max length for the site-wide notice is 255 chars.') + ); + } + + // scrub HTML input + + $config = array( + 'safe' => 1, + 'deny_attribute' => 'id,style,on*' + ); + + $siteNotice = htmLawed($siteNotice, $config); + } +} + +class SiteNoticeAdminPanelForm extends AdminForm +{ + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'form_site_notice_admin_panel'; + } + + /** + * class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_settings'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('sitenoticeadminpanel'); + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + $this->out->elementStart('ul', 'form_data'); + + $this->out->elementStart('li'); + $this->out->textarea( + 'site-notice', + _('Site notice text'), + common_config('site', 'notice'), + _('Site-wide notice text (255 chars max; HTML okay)') + ); + $this->out->elementEnd('li'); + + $this->out->elementEnd('ul'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit( + 'submit', + _('Save'), + 'submit', + null, + _('Save site notice') + ); + } +} diff --git a/actions/smssettings.php b/actions/smssettings.php index 751495d57..6af1872a0 100644 --- a/actions/smssettings.php +++ b/actions/smssettings.php @@ -55,6 +55,7 @@ class SmssettingsAction extends ConnectSettingsAction function title() { + // TRANS: Title for SMS settings. return _('SMS settings'); } @@ -66,6 +67,10 @@ class SmssettingsAction extends ConnectSettingsAction function getInstructions() { + // XXX: For consistency of parameters in messages, this should be a + // regular parameters, replaced with sprintf(). + // TRANS: SMS settings page instructions. + // TRANS: %%site.name%% is the name of the site. return _('You can receive SMS messages through email from %%site.name%%.'); } @@ -88,6 +93,7 @@ class SmssettingsAction extends ConnectSettingsAction { if (!common_config('sms', 'enabled')) { $this->element('div', array('class' => 'error'), + // TRANS: Message given in the SMS settings if SMS is not enabled on the site. _('SMS is not available.')); return; } @@ -101,7 +107,8 @@ class SmssettingsAction extends ConnectSettingsAction common_local_url('smssettings'))); $this->elementStart('fieldset', array('id' => 'settings_sms_address')); - $this->element('legend', null, _('Address')); + // TRANS: Form legend for SMS settings form. + $this->element('legend', null, _('SMS address')); $this->hidden('token', common_session_token()); if ($user->sms) { @@ -109,10 +116,12 @@ class SmssettingsAction extends ConnectSettingsAction $this->element('p', 'form_confirmed', $user->sms . ' (' . $carrier->name . ')'); $this->element('p', 'form_guide', + // TRANS: Form guide in SMS settings form. _('Current confirmed SMS-enabled phone number.')); $this->hidden('sms', $user->sms); $this->hidden('carrier', $user->carrier); - $this->submit('remove', _('Remove')); + // TRANS: Button label to remove a confirmed SMS address. + $this->submit('remove', _m('BUTTON','Remove')); } else { $confirm = $this->getConfirmation(); if ($confirm) { @@ -120,57 +129,75 @@ class SmssettingsAction extends ConnectSettingsAction $this->element('p', 'form_unconfirmed', $confirm->address . ' (' . $carrier->name . ')'); $this->element('p', 'form_guide', + // TRANS: Form guide in IM settings form. _('Awaiting confirmation on this phone number.')); $this->hidden('sms', $confirm->address); $this->hidden('carrier', $confirm->address_extra); - $this->submit('cancel', _('Cancel')); + // TRANS: Button label to cancel a SMS address confirmation procedure. + $this->submit('cancel', _m('BUTTON','Cancel')); $this->elementStart('ul', 'form_data'); $this->elementStart('li'); + // TRANS: Field label for SMS address input in SMS settings form. $this->input('code', _('Confirmation code'), null, + // TRANS: Form field instructions in SMS settings form. _('Enter the code you received on your phone.')); $this->elementEnd('li'); $this->elementEnd('ul'); - $this->submit('confirm', _('Confirm')); + // TRANS: Button label to confirm SMS confirmation code in SMS settings. + $this->submit('confirm', _m('BUTTON','Confirm')); } else { $this->elementStart('ul', 'form_data'); $this->elementStart('li'); + // TRANS: Field label for SMS phone number input in SMS settings form. $this->input('sms', _('SMS phone number'), ($this->arg('sms')) ? $this->arg('sms') : null, + // TRANS: SMS phone number input field instructions in SMS settings form. _('Phone number, no punctuation or spaces, '. 'with area code')); $this->elementEnd('li'); $this->elementEnd('ul'); $this->carrierSelect(); - $this->submit('add', _('Add')); + // TRANS: Button label for adding a SMS phone number in SMS settings form. + $this->submit('add', _m('BUTTON','Add')); } } $this->elementEnd('fieldset'); if ($user->sms) { $this->elementStart('fieldset', array('id' => 'settings_sms_incoming_email')); + // XXX: Confused! This is about SMS. Should this message be updated? + // TRANS: Form legend for incoming SMS settings form. $this->element('legend', null, _('Incoming email')); if ($user->incomingemail) { $this->element('p', 'form_unconfirmed', $user->incomingemail); $this->element('p', 'form_note', + // XXX: Confused! This is about SMS. Should this message be updated? + // TRANS: Form instructions for incoming SMS e-mail address form in SMS settings. _('Send email to this address to post new notices.')); - $this->submit('removeincoming', _('Remove')); + // TRANS: Button label for removing a set sender SMS e-mail address to post notices from. + $this->submit('removeincoming', _m('BUTTON','Remove')); } $this->element('p', 'form_guide', + // XXX: Confused! This is about SMS. Should this message be updated? + // TRANS: Instructions for incoming SMS e-mail address input form. _('Make a new email address for posting to; '. 'cancels the old one.')); - $this->submit('newincoming', _('New')); + // TRANS: Button label for adding an SMS e-mail address to send notices from. + $this->submit('newincoming', _m('BUTTON','New')); $this->elementEnd('fieldset'); } $this->elementStart('fieldset', array('id' => 'settings_sms_preferences')); - $this->element('legend', null, _('Preferences')); + // TRANS: Form legend for SMS preferences form. + $this->element('legend', null, _('SMS preferences')); $this->elementStart('ul', 'form_data'); $this->elementStart('li'); $this->checkbox('smsnotify', + // TRANS: Checkbox label in SMS preferences form. _('Send me notices through SMS; '. 'I understand I may incur '. 'exorbitant charges from my carrier.'), @@ -178,7 +205,8 @@ class SmssettingsAction extends ConnectSettingsAction $this->elementEnd('li'); $this->elementEnd('ul'); - $this->submit('save', _('Save')); + // TRANS: Button label to save SMS preferences. + $this->submit('save', _m('BUTTON','Save')); $this->elementEnd('fieldset'); $this->elementEnd('form'); @@ -245,6 +273,7 @@ class SmssettingsAction extends ConnectSettingsAction } else if ($this->arg('confirm')) { $this->confirmCode(); } else { + // TRANS: Message given submitting a form with an unknown action in SMS settings. $this->showForm(_('Unexpected form submission.')); } } @@ -275,13 +304,15 @@ class SmssettingsAction extends ConnectSettingsAction if ($result === false) { common_log_db_error($user, 'UPDATE', __FILE__); + // TRANS: Server error thrown on database error updating SMS preferences. $this->serverError(_('Couldn\'t update user.')); return; } $user->query('COMMIT'); - $this->showForm(_('Preferences saved.'), true); + // TRANS: Confirmation message for successful SMS preferences save. + $this->showForm(_('SMS preferences saved.'), true); } /** @@ -303,11 +334,13 @@ class SmssettingsAction extends ConnectSettingsAction // Some validation if (!$sms) { + // TRANS: Message given saving SMS phone number without having provided one. $this->showForm(_('No phone number.')); return; } if (!$carrier_id) { + // TRANS: Message given saving SMS phone number without having selected a carrier. $this->showForm(_('No carrier selected.')); return; } @@ -315,9 +348,11 @@ class SmssettingsAction extends ConnectSettingsAction $sms = common_canonical_sms($sms); if ($user->sms == $sms) { + // TRANS: Message given saving SMS phone number that is already set. $this->showForm(_('That is already your phone number.')); return; } else if ($this->smsExists($sms)) { + // TRANS: Message given saving SMS phone number that is already set for another user. $this->showForm(_('That phone number already belongs to another user.')); return; } @@ -334,6 +369,7 @@ class SmssettingsAction extends ConnectSettingsAction if ($result === false) { common_log_db_error($confirm, 'INSERT', __FILE__); + // TRANS: Server error thrown on database error adding SMS confirmation code. $this->serverError(_('Couldn\'t insert confirmation code.')); return; } @@ -344,6 +380,7 @@ class SmssettingsAction extends ConnectSettingsAction $user->nickname, $carrier->toEmailAddress($sms)); + // TRANS: Message given saving valid SMS phone number that is to be confirmed. $msg = _('A confirmation code was sent to the phone number you added. '. 'Check your phone for the code and instructions '. 'on how to use it.'); @@ -367,10 +404,12 @@ class SmssettingsAction extends ConnectSettingsAction $confirm = $this->getConfirmation(); if (!$confirm) { + // TRANS: Message given canceling SMS phone number confirmation that is not pending. $this->showForm(_('No pending confirmation to cancel.')); return; } if ($confirm->address != $sms) { + // TRANS: Message given canceling SMS phone number confirmation for the wrong phone number. $this->showForm(_('That is the wrong confirmation number.')); return; } @@ -379,11 +418,13 @@ class SmssettingsAction extends ConnectSettingsAction if (!$result) { common_log_db_error($confirm, 'DELETE', __FILE__); + // TRANS: Server error thrown on database error canceling SMS phone number confirmation. $this->serverError(_('Couldn\'t delete email confirmation.')); return; } - $this->showForm(_('Confirmation cancelled.'), true); + // TRANS: Message given after successfully canceling SMS phone number confirmation. + $this->showForm(_('SMS confirmation cancelled.'), true); } /** @@ -402,6 +443,8 @@ class SmssettingsAction extends ConnectSettingsAction // Maybe an old tab open...? if ($user->sms != $sms) { + // TRANS: Message given trying to remove an SMS phone number that is not + // TRANS: registered for the active user. $this->showForm(_('That is not your phone number.')); return; } @@ -417,12 +460,14 @@ class SmssettingsAction extends ConnectSettingsAction $result = $user->updateKeys($original); if (!$result) { common_log_db_error($user, 'UPDATE', __FILE__); + // TRANS: Server error thrown on database error removing a registered SMS phone number. $this->serverError(_('Couldn\'t update user.')); return; } $user->query('COMMIT'); - $this->showForm(_('The address was removed.'), true); + // TRANS: Message given after successfully removing a registered SMS phone number. + $this->showForm(_('The SMS phone number was removed.'), true); } /** @@ -462,10 +507,12 @@ class SmssettingsAction extends ConnectSettingsAction $this->elementStart('ul', 'form_data'); $this->elementStart('li'); + // TRANS: Label for mobile carrier dropdown menu in SMS settings. $this->element('label', array('for' => 'carrier'), _('Mobile carrier')); $this->elementStart('select', array('name' => 'carrier', 'id' => 'carrier')); $this->element('option', array('value' => 0), + // TRANS: Default option for mobile carrier dropdown menu in SMS settings. _('Select a carrier')); while ($carrier->fetch()) { $this->element('option', array('value' => $carrier->id), @@ -473,6 +520,8 @@ class SmssettingsAction extends ConnectSettingsAction } $this->elementEnd('select'); $this->element('p', 'form_guide', + // TRANS: Form instructions for mobile carrier dropdown menu in SMS settings. + // TRANS: %s is an administrative contact's e-mail address. sprintf(_('Mobile carrier for your phone. '. 'If you know a carrier that accepts ' . 'SMS over email but isn\'t listed here, ' . @@ -495,6 +544,7 @@ class SmssettingsAction extends ConnectSettingsAction $code = $this->trimmed('code'); if (!$code) { + // TRANS: Message given saving SMS phone number confirmation code without having provided one. $this->showForm(_('No code entered')); return; } diff --git a/actions/snapshotadminpanel.php b/actions/snapshotadminpanel.php new file mode 100644 index 000000000..be0a793e5 --- /dev/null +++ b/actions/snapshotadminpanel.php @@ -0,0 +1,251 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Snapshots administration panel + * + * 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 Settings + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Manage snapshots + * + * @category Admin + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class SnapshotadminpanelAction extends AdminPanelAction +{ + /** + * Returns the page title + * + * @return string page title + */ + + function title() + { + return _('Snapshots'); + } + + /** + * Instructions for using this form. + * + * @return string instructions + */ + + function getInstructions() + { + return _('Manage snapshot configuration'); + } + + /** + * Show the snapshots admin panel form + * + * @return void + */ + + function showForm() + { + $form = new SnapshotAdminPanelForm($this); + $form->show(); + return; + } + + /** + * Save settings from the form + * + * @return void + */ + + function saveSettings() + { + static $settings = array( + 'snapshot' => array('run', 'reporturl', 'frequency') + ); + + $values = array(); + + foreach ($settings as $section => $parts) { + foreach ($parts as $setting) { + $values[$section][$setting] = $this->trimmed($setting); + } + } + + // This throws an exception on validation errors + + $this->validate($values); + + // assert(all values are valid); + + $config = new Config(); + + $config->query('BEGIN'); + + foreach ($settings as $section => $parts) { + foreach ($parts as $setting) { + Config::save($section, $setting, $values[$section][$setting]); + } + } + + $config->query('COMMIT'); + + return; + } + + function validate(&$values) + { + // Validate snapshot run value + + if (!in_array($values['snapshot']['run'], array('web', 'cron', 'never'))) { + $this->clientError(_('Invalid snapshot run value.')); + } + + // Validate snapshot frequency value + + if (!Validate::number($values['snapshot']['frequency'])) { + $this->clientError(_('Snapshot frequency must be a number.')); + } + + // Validate report URL + + if (!is_null($values['snapshot']['reporturl']) + && !Validate::uri( + $values['snapshot']['reporturl'], + array('allowed_schemes' => array('http', 'https') + ) + )) { + $this->clientError(_('Invalid snapshot report URL.')); + } + } +} + +class SnapshotAdminPanelForm extends AdminForm +{ + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'form_snapshot_admin_panel'; + } + + /** + * class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_settings'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('snapshotadminpanel'); + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + $this->out->elementStart( + 'fieldset', + array('id' => 'settings_admin_snapshots') + ); + $this->out->element('legend', null, _('Snapshots')); + $this->out->elementStart('ul', 'form_data'); + $this->li(); + $snapshot = array( + 'web' => _('Randomly during web hit'), + 'cron' => _('In a scheduled job'), + 'never' => _('Never') + ); + $this->out->dropdown( + 'run', + _('Data snapshots'), + $snapshot, + _('When to send statistical data to status.net servers'), + false, + $this->value('run', 'snapshot') + ); + $this->unli(); + + $this->li(); + $this->input( + 'frequency', + _('Frequency'), + _('Snapshots will be sent once every N web hits'), + 'snapshot' + ); + $this->unli(); + + $this->li(); + $this->input( + 'reporturl', + _('Report URL'), + _('Snapshots will be sent to this URL'), + 'snapshot' + ); + $this->unli(); + $this->out->elementEnd('ul'); + $this->out->elementEnd('fieldset'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit( + 'submit', + _('Save'), + 'submit', + null, + _('Save snapshot settings') + ); + } +} diff --git a/actions/subscribers.php b/actions/subscribers.php index cd3e2ee5b..2845a498e 100644 --- a/actions/subscribers.php +++ b/actions/subscribers.php @@ -143,9 +143,12 @@ class SubscribersListItem extends SubscriptionListItem function showActions() { $this->startActions(); - $this->showSubscribeButton(); - // Relevant code! - $this->showBlockForm(); + if (Event::handle('StartProfileListItemActionElements', array($this))) { + $this->showSubscribeButton(); + // Relevant code! + $this->showBlockForm(); + Event::handle('EndProfileListItemActionElements', array($this)); + } $this->endActions(); } @@ -154,10 +157,36 @@ class SubscribersListItem extends SubscriptionListItem $user = common_current_user(); if (!empty($user) && $this->owner->id == $user->id) { - $bf = new BlockForm($this->out, $this->profile, - array('action' => 'subscribers', - 'nickname' => $this->owner->nickname)); + $returnto = array('action' => 'subscribers', + 'nickname' => $this->owner->nickname); + $page = $this->out->arg('page'); + if ($page) { + $returnto['param-page'] = $page; + } + $bf = new BlockForm($this->out, $this->profile, $returnto); $bf->show(); } } + + function linkAttributes() + { + $aAttrs = parent::linkAttributes(); + + if (common_config('nofollow', 'subscribers')) { + $aAttrs['rel'] .= ' nofollow'; + } + + return $aAttrs; + } + + function homepageAttributes() + { + $aAttrs = parent::linkAttributes(); + + if (common_config('nofollow', 'subscribers')) { + $aAttrs['rel'] = 'nofollow'; + } + + return $aAttrs; + } } diff --git a/actions/subscriptions.php b/actions/subscriptions.php index ba6171ef4..7b10b3425 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -196,12 +196,30 @@ class SubscriptionsListItem extends SubscriptionListItem $this->out->hidden('token', common_session_token()); $this->out->hidden('profile', $this->profile->id); if (common_config('xmpp', 'enabled')) { - $this->out->checkbox('jabber', _('Jabber'), $sub->jabber); + $attrs = array('name' => 'jabber', + 'type' => 'checkbox', + 'class' => 'checkbox', + 'id' => 'jabber-'.$this->profile->id); + if ($sub->jabber) { + $attrs['checked'] = 'checked'; + } + + $this->out->element('input', $attrs); + $this->out->element('label', array('for' => 'jabber-'.$this->profile->id), _('Jabber')); } else { $this->out->hidden('jabber', $sub->jabber); } if (common_config('sms', 'enabled')) { - $this->out->checkbox('sms', _('SMS'), $sub->sms); + $attrs = array('name' => 'sms', + 'type' => 'checkbox', + 'class' => 'checkbox', + 'id' => 'sms-'.$this->profile->id); + if ($sub->sms) { + $attrs['checked'] = 'checked'; + } + + $this->out->element('input', $attrs); + $this->out->element('label', array('for' => 'sms-'.$this->profile->id), _('SMS')); } else { $this->out->hidden('sms', $sub->sms); } diff --git a/actions/tag.php b/actions/tag.php index ee9617b66..7c6f99d92 100644 --- a/actions/tag.php +++ b/actions/tag.php @@ -49,7 +49,7 @@ class TagAction extends Action if($this->page > 1 && $this->notice->N == 0){ // TRANS: Server error when page not found (404) - $this->serverError(_('No such page'),$code=404); + $this->serverError(_('No such page.'),$code=404); } return true; @@ -102,12 +102,17 @@ class TagAction extends Action function showContent() { - $nl = new NoticeList($this->notice, $this); + if(Event::handle('StartTagShowContent', array($this))) { + + $nl = new NoticeList($this->notice, $this); - $cnt = $nl->show(); + $cnt = $nl->show(); - $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, - $this->page, 'tag', array('tag' => $this->tag)); + $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, + $this->page, 'tag', array('tag' => $this->tag)); + + Event::handle('EndTagShowContent', array($this)); + } } function isReadOnly($args) diff --git a/actions/tagrss.php b/actions/tagrss.php index 75cbfa274..467a64abe 100644 --- a/actions/tagrss.php +++ b/actions/tagrss.php @@ -35,6 +35,7 @@ class TagrssAction extends Rss10Action $this->clientError(_('No such tag.')); return false; } else { + $this->notices = $this->getNotices($this->limit); return true; } } diff --git a/actions/unsandbox.php b/actions/unsandbox.php index 22f4d8e76..d50b5072e 100644 --- a/actions/unsandbox.php +++ b/actions/unsandbox.php @@ -62,14 +62,14 @@ class UnsandboxAction extends ProfileFormAction assert(!empty($cur)); // checked by parent if (!$cur->hasRight(Right::SANDBOXUSER)) { - $this->clientError(_("You cannot sandbox users on this site.")); + $this->clientError(_('You cannot sandbox users on this site.')); return false; } assert(!empty($this->profile)); // checked by parent if (!$this->profile->isSandboxed()) { - $this->clientError(_("User is not sandboxed.")); + $this->clientError(_('User is not sandboxed.')); return false; } diff --git a/actions/unsilence.php b/actions/unsilence.php index 9ff1b828b..7d282c366 100644 --- a/actions/unsilence.php +++ b/actions/unsilence.php @@ -62,14 +62,14 @@ class UnsilenceAction extends ProfileFormAction assert(!empty($cur)); // checked by parent if (!$cur->hasRight(Right::SILENCEUSER)) { - $this->clientError(_("You cannot silence users on this site.")); + $this->clientError(_('You cannot silence users on this site.')); return false; } assert(!empty($this->profile)); // checked by parent if (!$this->profile->isSilenced()) { - $this->clientError(_("User is not silenced.")); + $this->clientError(_('User is not silenced.')); return false; } diff --git a/actions/unsubscribe.php b/actions/unsubscribe.php index 6bb10d448..57ca15d68 100644 --- a/actions/unsubscribe.php +++ b/actions/unsubscribe.php @@ -74,7 +74,7 @@ class UnsubscribeAction extends Action $other_id = $this->arg('unsubscribeto'); if (!$other_id) { - $this->clientError(_('No profile id in request.')); + $this->clientError(_('No profile ID in request.')); return; } diff --git a/actions/userauthorization.php b/actions/userauthorization.php index 7f71c60db..e896ff96c 100644 --- a/actions/userauthorization.php +++ b/actions/userauthorization.php @@ -69,7 +69,7 @@ class UserauthorizationAction extends Action $profile = $user->getProfile(); if (!$profile) { common_log_db_error($user, 'SELECT', __FILE__); - $this->serverError(_('User without matching profile')); + $this->serverError(_('User without matching profile.')); return; } diff --git a/actions/usergroups.php b/actions/usergroups.php index 97faabae6..6606e76cd 100644 --- a/actions/usergroups.php +++ b/actions/usergroups.php @@ -59,8 +59,10 @@ class UsergroupsAction extends OwnerDesignAction function title() { if ($this->page == 1) { + // TRANS: Message is used as a page title. %s is a nick name. return sprintf(_('%s groups'), $this->user->nickname); } else { + // TRANS: Message is used as a page title. %1$s is a nick name, %2$d is a page number. return sprintf(_('%1$s groups, page %2$d'), $this->user->nickname, $this->page); @@ -130,22 +132,26 @@ class UsergroupsAction extends OwnerDesignAction _('Search for more groups')); $this->elementEnd('p'); - $offset = ($this->page-1) * GROUPS_PER_PAGE; - $limit = GROUPS_PER_PAGE + 1; + if (Event::handle('StartShowUserGroupsContent', array($this))) { + $offset = ($this->page-1) * GROUPS_PER_PAGE; + $limit = GROUPS_PER_PAGE + 1; - $groups = $this->user->getGroups($offset, $limit); + $groups = $this->user->getGroups($offset, $limit); - if ($groups) { - $gl = new GroupList($groups, $this->user, $this); - $cnt = $gl->show(); - if (0 == $cnt) { - $this->showEmptyListMessage(); + if ($groups) { + $gl = new GroupList($groups, $this->user, $this); + $cnt = $gl->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } } - } - $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE, - $this->page, 'usergroups', - array('nickname' => $this->user->nickname)); + $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE, + $this->page, 'usergroups', + array('nickname' => $this->user->nickname)); + + Event::handle('EndShowUserGroupsContent', array($this)); + } } function showEmptyListMessage() diff --git a/actions/userrss.php b/actions/userrss.php index 19e610551..b7078fcaf 100644 --- a/actions/userrss.php +++ b/actions/userrss.php @@ -29,6 +29,8 @@ class UserrssAction extends Rss10Action function prepare($args) { + common_debug("UserrssAction"); + parent::prepare($args); $nickname = $this->trimmed('nickname'); $this->user = User::staticGet('nickname', $nickname); @@ -38,20 +40,24 @@ class UserrssAction extends Rss10Action $this->clientError(_('No such user.')); return false; } else { - $this->notices = $this->getNotices($this->limit); + if (!empty($this->tag)) { + $this->notices = $this->getTaggedNotices(); + } else { + $this->notices = $this->getNotices(); + } return true; } } - function getTaggedNotices($tag = null, $limit=0) + function getTaggedNotices() { - $user = $this->user; - - if (is_null($user)) { - return null; - } - - $notice = $user->getTaggedNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit, 0, 0, null, $tag); + $notice = $this->user->getTaggedNotices( + $this->tag, + 0, + ($this->limit == 0) ? NOTICES_PER_PAGE : $this->limit, + 0, + 0 + ); $notices = array(); while ($notice->fetch()) { @@ -62,15 +68,12 @@ class UserrssAction extends Rss10Action } - function getNotices($limit=0) + function getNotices() { - $user = $this->user; - - if (is_null($user)) { - return null; - } - - $notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); + $notice = $this->user->getNotices( + 0, + ($this->limit == 0) ? NOTICES_PER_PAGE : $this->limit + ); $notices = array(); while ($notice->fetch()) { @@ -87,8 +90,10 @@ class UserrssAction extends Rss10Action $c = array('url' => common_local_url('userrss', array('nickname' => $user->nickname)), + // TRANS: Message is used as link title. %s is a user nickname. 'title' => sprintf(_('%s timeline'), $user->nickname), 'link' => $profile->profileurl, + // TRANS: Message is used as link description. %1$s is a username, %2$s is a site name. 'description' => sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, common_config('site', 'name'))); return $c; @@ -100,7 +105,7 @@ class UserrssAction extends Rss10Action $profile = $user->getProfile(); if (!$profile) { common_log_db_error($user, 'SELECT', __FILE__); - $this->serverError(_('User without matching profile')); + $this->serverError(_('User without matching profile.')); return null; } $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); diff --git a/actions/version.php b/actions/version.php index b6593e5ed..9e4e836d2 100644 --- a/actions/version.php +++ b/actions/version.php @@ -41,6 +41,8 @@ if (!defined('STATUSNET')) { * @category Info * @package StatusNet * @author Evan Prodromou <evan@status.net> + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 * @link http://status.net/ */ |