summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actions/newnotice.php3
-rw-r--r--actions/postnotice.php11
-rw-r--r--actions/updateprofile.php10
-rw-r--r--classes/Notice.php17
-rw-r--r--classes/Subscription.php22
-rw-r--r--db/08to09.sql5
-rw-r--r--db/statusnet.sql6
-rw-r--r--lib/action.php2
-rw-r--r--lib/common.php2
-rw-r--r--lib/default.php4
-rw-r--r--lib/oauthstore.php2
-rw-r--r--lib/omb.php16
-rw-r--r--lib/util.php75
-rw-r--r--locale/statusnet.po389
-rw-r--r--plugins/Facebook/FacebookPlugin.php87
-rw-r--r--plugins/Facebook/facebookadminpanel.php223
-rw-r--r--plugins/MobileProfile/MobileProfilePlugin.php2
-rw-r--r--plugins/OStatus/OStatusPlugin.php57
-rw-r--r--plugins/OStatus/actions/ostatussub.php6
-rw-r--r--plugins/OStatus/actions/pushhub.php2
-rw-r--r--plugins/OStatus/classes/HubSub.php8
-rw-r--r--plugins/OStatus/classes/Magicsig.php4
-rw-r--r--plugins/OStatus/classes/Ostatus_profile.php4
-rw-r--r--plugins/OStatus/lib/discovery.php2
-rw-r--r--plugins/OStatus/lib/xrd.php21
-rw-r--r--plugins/OStatus/locale/OStatus.po280
-rw-r--r--plugins/OStatus/scripts/updateostatus.php127
-rw-r--r--plugins/RegisterThrottle/RegisterThrottlePlugin.php249
-rw-r--r--plugins/RegisterThrottle/Registration_ip.php124
-rw-r--r--plugins/TwitterBridge/README105
-rw-r--r--plugins/TwitterBridge/TwitterBridgePlugin.php137
-rw-r--r--plugins/TwitterBridge/twitteradminpanel.php280
-rw-r--r--plugins/TwitterBridge/twitterauthorization.php2
-rwxr-xr-xscripts/update_po_templates.php7
-rw-r--r--theme/base/css/display.css4
35 files changed, 1937 insertions, 358 deletions
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 78480abab..ed0fa1b2b 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -294,6 +294,9 @@ class NewnoticeAction extends Action
if ($profile) {
$content = '@' . $profile->nickname . ' ';
}
+ } else {
+ // @fixme most of these bits above aren't being passed on above
+ $inreplyto = null;
}
$notice_form = new NoticeForm($this, '', $content, null, $inreplyto);
diff --git a/actions/postnotice.php b/actions/postnotice.php
index fb0670376..b2f6f1bb9 100644
--- a/actions/postnotice.php
+++ b/actions/postnotice.php
@@ -54,7 +54,10 @@ class PostnoticeAction extends Action
*/
function prepare($argarray)
{
+ StatusNet::setApi(true); // Send smaller error pages
+
parent::prepare($argarray);
+
try {
$this->checkNotice();
} catch (Exception $e) {
@@ -71,6 +74,14 @@ class PostnoticeAction extends Action
$srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
omb_oauth_server());
$srv->handlePostNotice();
+ } catch (OMB_RemoteServiceException $rse) {
+ $msg = $rse->getMessage();
+ if (preg_match('/Revoked accesstoken/', $msg) ||
+ preg_match('/No subscriber/', $msg)) {
+ $this->clientError($msg, 403);
+ } else {
+ $this->clientError($msg);
+ }
} catch (Exception $e) {
$this->serverError($e->getMessage());
return;
diff --git a/actions/updateprofile.php b/actions/updateprofile.php
index e416a6fa9..bae6108cc 100644
--- a/actions/updateprofile.php
+++ b/actions/updateprofile.php
@@ -55,6 +55,8 @@ class UpdateprofileAction extends Action
*/
function prepare($argarray)
{
+ StatusNet::setApi(true); // Send smaller error pages
+
parent::prepare($argarray);
$license = $_POST['omb_listenee_license'];
$site_license = common_config('license', 'url');
@@ -75,6 +77,14 @@ class UpdateprofileAction extends Action
$srv = new OMB_Service_Provider(null, omb_oauth_datastore(),
omb_oauth_server());
$srv->handleUpdateProfile();
+ } catch (OMB_RemoteServiceException $rse) {
+ $msg = $rse->getMessage();
+ if (preg_match('/Revoked accesstoken/', $msg) ||
+ preg_match('/No subscriber/', $msg)) {
+ $this->clientError($msg, 403);
+ } else {
+ $this->clientError($msg);
+ }
} catch (Exception $e) {
$this->serverError($e->getMessage());
return;
diff --git a/classes/Notice.php b/classes/Notice.php
index 2d02a9a19..3702dbcfa 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -282,12 +282,6 @@ class Notice extends Memcached_DataObject
$notice->content = $final;
- if (!empty($rendered)) {
- $notice->rendered = $rendered;
- } else {
- $notice->rendered = common_render_content($final, $notice);
- }
-
$notice->source = $source;
$notice->uri = $uri;
$notice->url = $url;
@@ -315,6 +309,12 @@ class Notice extends Memcached_DataObject
$notice->location_ns = $location_ns;
}
+ if (!empty($rendered)) {
+ $notice->rendered = $rendered;
+ } else {
+ $notice->rendered = common_render_content($final, $notice);
+ }
+
if (Event::handle('StartNoticeSave', array(&$notice))) {
// XXX: some of these functions write to the DB
@@ -973,7 +973,10 @@ class Notice extends Memcached_DataObject
$sender = Profile::staticGet($this->profile_id);
- $mentions = common_find_mentions($this->profile_id, $this->content);
+ // @todo ideally this parser information would only
+ // be calculated once.
+
+ $mentions = common_find_mentions($this->content, $this);
$replied = array();
diff --git a/classes/Subscription.php b/classes/Subscription.php
index d6fb3fcbd..9cef2df1a 100644
--- a/classes/Subscription.php
+++ b/classes/Subscription.php
@@ -172,6 +172,28 @@ class Subscription extends Memcached_DataObject
assert(!empty($sub));
+ // @todo: move this block to EndSubscribe handler for
+ // OMB plugin when it exists.
+
+ if (!empty($sub->token)) {
+
+ $token = new Token();
+
+ $token->tok = $sub->token;
+
+ if ($token->find(true)) {
+
+ $result = $token->delete();
+
+ if (!$result) {
+ common_log_db_error($token, 'DELETE', __FILE__);
+ throw new Exception(_('Couldn\'t delete subscription OMB token.'));
+ }
+ } else {
+ common_log(LOG_ERR, "Couldn't find credentials with token {$token->tok}");
+ }
+ }
+
$result = $sub->delete();
if (!$result) {
diff --git a/db/08to09.sql b/db/08to09.sql
index b10e47dbc..f30572154 100644
--- a/db/08to09.sql
+++ b/db/08to09.sql
@@ -110,3 +110,8 @@ insert into queue_item_new (frame,transport,created,claimed)
alter table queue_item rename to queue_item_old;
alter table queue_item_new rename to queue_item;
+alter table file_to_post
+ add index post_id_idx (post_id);
+
+alter table group_inbox
+ add index group_inbox_notice_id_idx (notice_id);
diff --git a/db/statusnet.sql b/db/statusnet.sql
index 4158f0167..3f95948e1 100644
--- a/db/statusnet.sql
+++ b/db/statusnet.sql
@@ -458,7 +458,8 @@ create table group_inbox (
created datetime not null comment 'date the notice was created',
constraint primary key (group_id, notice_id),
- index group_inbox_created_idx (created)
+ index group_inbox_created_idx (created),
+ index group_inbox_notice_id_idx (notice_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
@@ -523,7 +524,8 @@ create table file_to_post (
post_id integer comment 'id of the notice it belongs to' references notice (id),
modified timestamp comment 'date this record was modified',
- constraint primary key (file_id, post_id)
+ constraint primary key (file_id, post_id),
+ index post_id_idx (post_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
diff --git a/lib/action.php b/lib/action.php
index a7e0eb33b..0918c6858 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -425,8 +425,6 @@ class Action extends HTMLOutputter // lawsuit
$connect = 'imsettings';
} else if (common_config('sms', 'enabled')) {
$connect = 'smssettings';
- } else if (common_config('twitter', 'enabled')) {
- $connect = 'twittersettings';
}
$this->elementStart('dl', array('id' => 'site_nav_global_primary'));
diff --git a/lib/common.php b/lib/common.php
index 2dbe3b3c5..546f6bbe4 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -22,7 +22,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
//exit with 200 response, if this is checking fancy from the installer
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
-define('STATUSNET_VERSION', '0.9.0beta6');
+define('STATUSNET_VERSION', '0.9.0beta6+bugfix1');
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
define('STATUSNET_CODENAME', 'Stand');
diff --git a/lib/default.php b/lib/default.php
index d849055c2..7b50242ae 100644
--- a/lib/default.php
+++ b/lib/default.php
@@ -177,8 +177,8 @@ $default =
array('source' => 'StatusNet', # source attribute for Twitter
'taguri' => null), # base for tag URIs
'twitter' =>
- array('enabled' => true,
- 'consumer_key' => null,
+ array('signin' => true,
+ 'consumer_key' => null,
'consumer_secret' => null),
'cache' =>
array('base' => null),
diff --git a/lib/oauthstore.php b/lib/oauthstore.php
index eabe37f9f..a6a6de750 100644
--- a/lib/oauthstore.php
+++ b/lib/oauthstore.php
@@ -390,7 +390,7 @@ class StatusNetOAuthDataStore extends OAuthDataStore
$sub->subscribed = $user->id;
if (!$sub->find(true)) {
- return 0;
+ return array();
}
/* Since we do not use OMB_Service_Provider’s action methods, there
diff --git a/lib/omb.php b/lib/omb.php
index 17132a594..8bbe5e8aa 100644
--- a/lib/omb.php
+++ b/lib/omb.php
@@ -77,7 +77,7 @@ function omb_broadcast_notice($notice)
/* Get remote users subscribed to this profile. */
$rp = new Remote_profile();
- $rp->query('SELECT postnoticeurl, token, secret ' .
+ $rp->query('SELECT remote_profile.*, secret, token ' .
'FROM subscription JOIN remote_profile ' .
'ON subscription.subscriber = remote_profile.id ' .
'WHERE subscription.subscribed = ' . $notice->profile_id . ' ');
@@ -93,7 +93,8 @@ function omb_broadcast_notice($notice)
/* Post notice. */
$service = new StatusNet_OMB_Service_Consumer(
- array(OMB_ENDPOINT_POSTNOTICE => $rp->postnoticeurl));
+ array(OMB_ENDPOINT_POSTNOTICE => $rp->postnoticeurl),
+ $rp->uri);
try {
$service->setToken($rp->token, $rp->secret);
$service->postNotice($omb_notice);
@@ -125,7 +126,7 @@ function omb_broadcast_profile($profile)
/* Get remote users subscribed to this profile. */
$rp = new Remote_profile();
- $rp->query('SELECT updateprofileurl, token, secret ' .
+ $rp->query('SELECT remote_profile.*, secret, token ' .
'FROM subscription JOIN remote_profile ' .
'ON subscription.subscriber = remote_profile.id ' .
'WHERE subscription.subscribed = ' . $profile->id . ' ');
@@ -141,7 +142,11 @@ function omb_broadcast_profile($profile)
/* Update profile. */
$service = new StatusNet_OMB_Service_Consumer(
- array(OMB_ENDPOINT_UPDATEPROFILE => $rp->updateprofileurl));
+ array(OMB_ENDPOINT_UPDATEPROFILE => $rp->updateprofileurl),
+ $rp->uri);
+
+ common_debug('service = ' . print_r($service, true));
+
try {
$service->setToken($rp->token, $rp->secret);
$service->updateProfile($omb_profile);
@@ -159,13 +164,14 @@ function omb_broadcast_profile($profile)
}
class StatusNet_OMB_Service_Consumer extends OMB_Service_Consumer {
- public function __construct($urls)
+ public function __construct($urls, $listener_uri=null)
{
$this->services = $urls;
$this->datastore = omb_oauth_datastore();
$this->oauth_consumer = omb_oauth_consumer();
$this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
$this->fetcher->timeout = intval(common_config('omb', 'timeout'));
+ $this->listener_uri = $listener_uri;
}
}
diff --git a/lib/util.php b/lib/util.php
index 32061ec04..7a170a5f5 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -426,14 +426,14 @@ function common_render_content($text, $notice)
{
$r = common_render_text($text);
$id = $notice->profile_id;
- $r = common_linkify_mentions($id, $r);
+ $r = common_linkify_mentions($r, $notice);
$r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r);
return $r;
}
-function common_linkify_mentions($profile_id, $text)
+function common_linkify_mentions($text, $notice)
{
- $mentions = common_find_mentions($profile_id, $text);
+ $mentions = common_find_mentions($text, $notice);
// We need to go through in reverse order by position,
// so our positions stay valid despite our fudging with the
@@ -487,11 +487,11 @@ function common_linkify_mention($mention)
return $output;
}
-function common_find_mentions($profile_id, $text)
+function common_find_mentions($text, $notice)
{
$mentions = array();
- $sender = Profile::staticGet('id', $profile_id);
+ $sender = Profile::staticGet('id', $notice->profile_id);
if (empty($sender)) {
return $mentions;
@@ -499,6 +499,30 @@ function common_find_mentions($profile_id, $text)
if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) {
+ // Get the context of the original notice, if any
+
+ $originalAuthor = null;
+ $originalNotice = null;
+ $originalMentions = array();
+
+ // Is it a reply?
+
+ if (!empty($notice) && !empty($notice->reply_to)) {
+ $originalNotice = Notice::staticGet('id', $notice->reply_to);
+ if (!empty($originalNotice)) {
+ $originalAuthor = Profile::staticGet('id', $originalNotice->profile_id);
+
+ $ids = $originalNotice->getReplies();
+
+ foreach ($ids as $id) {
+ $repliedTo = Profile::staticGet('id', $id);
+ if (!empty($repliedTo)) {
+ $originalMentions[$repliedTo->nickname] = $repliedTo;
+ }
+ }
+ }
+ }
+
preg_match_all('/^T ([A-Z0-9]{1,64}) /',
$text,
$tmatches,
@@ -514,7 +538,22 @@ function common_find_mentions($profile_id, $text)
foreach ($matches as $match) {
$nickname = common_canonical_nickname($match[0]);
- $mentioned = common_relative_profile($sender, $nickname);
+
+ // Try to get a profile for this nickname.
+ // Start with conversation context, then go to
+ // sender context.
+
+ if (!empty($originalAuthor) && $originalAuthor->nickname == $nickname) {
+
+ $mentioned = $originalAuthor;
+
+ } else if (!empty($originalMentions) &&
+ array_key_exists($nickname, $originalMentions)) {
+
+ $mention = $originalMentions[$nickname];
+ } else {
+ $mentioned = common_relative_profile($sender, $nickname);
+ }
if (!empty($mentioned)) {
@@ -770,8 +809,28 @@ function common_shorten_links($text)
function common_xml_safe_str($str)
{
- // Neutralize control codes and surrogates
- return preg_replace('/[\p{Cc}\p{Cs}]/u', '*', $str);
+ // Replace common eol and extra whitespace input chars
+ $unWelcome = array(
+ "\t", // tab
+ "\n", // newline
+ "\r", // cr
+ "\0", // null byte eos
+ "\x0B" // vertical tab
+ );
+
+ $replacement = array(
+ ' ', // single space
+ ' ',
+ '', // nothing
+ '',
+ ' '
+ );
+
+ $str = str_replace($unWelcome, $replacement, $str);
+
+ // Neutralize any additional control codes and UTF-16 surrogates
+ // (Twitter uses '*')
+ return preg_replace('/[\p{Cc}\p{Cs}]/u', '*', $str);
}
function common_tag_link($tag)
diff --git a/locale/statusnet.po b/locale/statusnet.po
index cf44e2d3c..8e1434497 100644
--- a/locale/statusnet.po
+++ b/locale/statusnet.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-02-24 23:49+0000\n"
+"POT-Creation-Date: 2010-03-01 14:08+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-#: actions/accessadminpanel.php:54 lib/adminpanelaction.php:326
+#: actions/accessadminpanel.php:54 lib/adminpanelaction.php:337
msgid "Access"
msgstr ""
@@ -88,14 +88,15 @@ msgstr ""
#: actions/apitimelinehome.php:79 actions/apitimelinementions.php:79
#: actions/apitimelineuser.php:81 actions/avatarbynickname.php:75
#: actions/favoritesrss.php:74 actions/foaf.php:40 actions/foaf.php:58
-#: actions/microsummary.php:62 actions/newmessage.php:116 actions/otp.php:76
-#: actions/remotesubscribe.php:145 actions/remotesubscribe.php:154
-#: actions/replies.php:73 actions/repliesrss.php:38 actions/rsd.php:116
-#: actions/showfavorites.php:105 actions/userbyid.php:74
-#: actions/usergroups.php:91 actions/userrss.php:38 actions/xrds.php:71
-#: lib/command.php:163 lib/command.php:302 lib/command.php:355
-#: lib/command.php:401 lib/command.php:462 lib/command.php:518
-#: lib/galleryaction.php:59 lib/mailbox.php:82 lib/profileaction.php:77
+#: actions/hcard.php:67 actions/microsummary.php:62 actions/newmessage.php:116
+#: actions/otp.php:76 actions/remotesubscribe.php:145
+#: actions/remotesubscribe.php:154 actions/replies.php:73
+#: actions/repliesrss.php:38 actions/rsd.php:116 actions/showfavorites.php:105
+#: actions/userbyid.php:74 actions/usergroups.php:91 actions/userrss.php:38
+#: actions/xrds.php:71 lib/command.php:163 lib/command.php:302
+#: lib/command.php:355 lib/command.php:401 lib/command.php:462
+#: lib/command.php:518 lib/galleryaction.php:59 lib/mailbox.php:82
+#: lib/profileaction.php:77
msgid "No such user."
msgstr ""
@@ -171,20 +172,20 @@ msgstr ""
#: actions/apiaccountverifycredentials.php:70 actions/apidirectmessage.php:156
#: actions/apifavoritecreate.php:99 actions/apifavoritedestroy.php:100
#: actions/apifriendshipscreate.php:100 actions/apifriendshipsdestroy.php:100
-#: actions/apifriendshipsshow.php:128 actions/apigroupcreate.php:136
+#: actions/apifriendshipsshow.php:128 actions/apigroupcreate.php:138
#: actions/apigroupismember.php:114 actions/apigroupjoin.php:155
#: actions/apigroupleave.php:141 actions/apigrouplist.php:132
#: actions/apigrouplistall.php:120 actions/apigroupmembership.php:106
-#: actions/apigroupshow.php:115 actions/apihelptest.php:88
+#: actions/apigroupshow.php:105 actions/apihelptest.php:88
#: actions/apistatusesdestroy.php:102 actions/apistatusesretweets.php:112
-#: actions/apistatusesshow.php:108 actions/apistatusnetconfig.php:137
+#: actions/apistatusesshow.php:108 actions/apistatusnetconfig.php:131
#: actions/apistatusnetversion.php:93 actions/apisubscriptions.php:111
#: actions/apitimelinefavorites.php:183 actions/apitimelinefriends.php:187
-#: actions/apitimelinegroup.php:195 actions/apitimelinehome.php:184
+#: actions/apitimelinegroup.php:185 actions/apitimelinehome.php:184
#: actions/apitimelinementions.php:175 actions/apitimelinepublic.php:152
#: actions/apitimelineretweetedtome.php:121
#: actions/apitimelineretweetsofme.php:152 actions/apitimelinetag.php:166
-#: actions/apitimelineuser.php:207 actions/apiusershow.php:101
+#: actions/apitimelineuser.php:196 actions/apiusershow.php:101
msgid "API method not found."
msgstr ""
@@ -216,8 +217,9 @@ msgstr ""
#: actions/apiaccountupdateprofilebackgroundimage.php:194
#: actions/apiaccountupdateprofilecolors.php:185
#: actions/apiaccountupdateprofileimage.php:130 actions/apiusershow.php:108
-#: actions/avatarbynickname.php:80 actions/foaf.php:65 actions/replies.php:80
-#: actions/usergroups.php:98 lib/galleryaction.php:66 lib/profileaction.php:84
+#: actions/avatarbynickname.php:80 actions/foaf.php:65 actions/hcard.php:74
+#: actions/replies.php:80 actions/usergroups.php:98 lib/galleryaction.php:66
+#: lib/profileaction.php:84
msgid "User has no profile."
msgstr ""
@@ -241,7 +243,7 @@ msgstr ""
#: actions/apiaccountupdateprofilebackgroundimage.php:146
#: actions/apiaccountupdateprofilecolors.php:164
#: actions/apiaccountupdateprofilecolors.php:174
-#: actions/groupdesignsettings.php:287 actions/groupdesignsettings.php:297
+#: actions/groupdesignsettings.php:290 actions/groupdesignsettings.php:300
#: actions/userdesignsettings.php:210 actions/userdesignsettings.php:220
#: actions/userdesignsettings.php:263 actions/userdesignsettings.php:273
msgid "Unable to save your design settings."
@@ -351,87 +353,87 @@ msgstr ""
msgid "Could not find target user."
msgstr ""
-#: actions/apigroupcreate.php:164 actions/editgroup.php:182
+#: actions/apigroupcreate.php:166 actions/editgroup.php:186
#: actions/newgroup.php:126 actions/profilesettings.php:215
#: actions/register.php:205
msgid "Nickname must have only lowercase letters and numbers and no spaces."
msgstr ""
-#: actions/apigroupcreate.php:173 actions/editgroup.php:186
+#: actions/apigroupcreate.php:175 actions/editgroup.php:190
#: actions/newgroup.php:130 actions/profilesettings.php:238
#: actions/register.php:208
msgid "Nickname already in use. Try another one."
msgstr ""
-#: actions/apigroupcreate.php:180 actions/editgroup.php:189
+#: actions/apigroupcreate.php:182 actions/editgroup.php:193
#: actions/newgroup.php:133 actions/profilesettings.php:218
#: actions/register.php:210
msgid "Not a valid nickname."
msgstr ""
-#: actions/apigroupcreate.php:196 actions/editapplication.php:215
-#: actions/editgroup.php:195 actions/newapplication.php:203
+#: actions/apigroupcreate.php:198 actions/editapplication.php:215
+#: actions/editgroup.php:199 actions/newapplication.php:203
#: actions/newgroup.php:139 actions/profilesettings.php:222
#: actions/register.php:217
msgid "Homepage is not a valid URL."
msgstr ""
-#: actions/apigroupcreate.php:205 actions/editgroup.php:198
+#: actions/apigroupcreate.php:207 actions/editgroup.php:202
#: actions/newgroup.php:142 actions/profilesettings.php:225
#: actions/register.php:220
msgid "Full name is too long (max 255 chars)."
msgstr ""
-#: actions/apigroupcreate.php:213 actions/editapplication.php:190
+#: actions/apigroupcreate.php:215 actions/editapplication.php:190
#: actions/newapplication.php:172
#, php-format
msgid "Description is too long (max %d chars)."
msgstr ""
-#: actions/apigroupcreate.php:224 actions/editgroup.php:204
+#: actions/apigroupcreate.php:226 actions/editgroup.php:208
#: actions/newgroup.php:148 actions/profilesettings.php:232
#: actions/register.php:227
msgid "Location is too long (max 255 chars)."
msgstr ""
-#: actions/apigroupcreate.php:243 actions/editgroup.php:215
+#: actions/apigroupcreate.php:245 actions/editgroup.php:219
#: actions/newgroup.php:159
#, php-format
msgid "Too many aliases! Maximum %d."
msgstr ""
-#: actions/apigroupcreate.php:264 actions/editgroup.php:224
+#: actions/apigroupcreate.php:266 actions/editgroup.php:228
#: actions/newgroup.php:168
#, php-format
msgid "Invalid alias: \"%s\""
msgstr ""
-#: actions/apigroupcreate.php:273 actions/editgroup.php:228
+#: actions/apigroupcreate.php:275 actions/editgroup.php:232
#: actions/newgroup.php:172
#, php-format
msgid "Alias \"%s\" already in use. Try another one."
msgstr ""
-#: actions/apigroupcreate.php:286 actions/editgroup.php:234
+#: actions/apigroupcreate.php:288 actions/editgroup.php:238
#: actions/newgroup.php:178
msgid "Alias can't be the same as nickname."
msgstr ""
#: actions/apigroupismember.php:95 actions/apigroupjoin.php:104
#: actions/apigroupleave.php:104 actions/apigroupmembership.php:91
-#: actions/apigroupshow.php:82 actions/apitimelinegroup.php:91
+#: actions/apigroupshow.php:90 actions/apitimelinegroup.php:91
msgid "Group not found!"
msgstr ""
-#: actions/apigroupjoin.php:110 actions/joingroup.php:90
+#: actions/apigroupjoin.php:110 actions/joingroup.php:100
msgid "You are already a member of that group."
msgstr ""
-#: actions/apigroupjoin.php:119 actions/joingroup.php:95 lib/command.php:221
+#: actions/apigroupjoin.php:119 actions/joingroup.php:105 lib/command.php:221
msgid "You have been blocked from that group by the admin."
msgstr ""
-#: actions/apigroupjoin.php:138 actions/joingroup.php:124
+#: actions/apigroupjoin.php:138 actions/joingroup.php:134
#, php-format
msgid "Could not join user %1$s to group %2$s."
msgstr ""
@@ -440,7 +442,7 @@ msgstr ""
msgid "You are not a member of this group."
msgstr ""
-#: actions/apigroupleave.php:124 actions/leavegroup.php:119
+#: actions/apigroupleave.php:124 actions/leavegroup.php:129
#, php-format
msgid "Could not remove user %1$s from group %2$s."
msgstr ""
@@ -471,7 +473,7 @@ msgstr ""
#: actions/apioauthauthorize.php:123 actions/avatarsettings.php:268
#: actions/deletenotice.php:157 actions/disfavor.php:74
#: actions/emailsettings.php:238 actions/favor.php:75 actions/geocode.php:54
-#: actions/groupblock.php:66 actions/grouplogo.php:309
+#: actions/groupblock.php:66 actions/grouplogo.php:312
#: actions/groupunblock.php:66 actions/imsettings.php:206
#: actions/invite.php:56 actions/login.php:115 actions/makeadmin.php:66
#: actions/newmessage.php:135 actions/newnotice.php:103 actions/nudge.php:80
@@ -491,11 +493,11 @@ msgid "Invalid nickname / password!"
msgstr ""
#: actions/apioauthauthorize.php:159
-msgid "Database error deleting OAuth application user."
+msgid "DB error deleting OAuth app user."
msgstr ""
#: actions/apioauthauthorize.php:185
-msgid "Database error inserting OAuth application user."
+msgid "DB error inserting OAuth app user."
msgstr ""
#: actions/apioauthauthorize.php:214
@@ -512,7 +514,7 @@ msgstr ""
#: actions/apioauthauthorize.php:232 actions/avatarsettings.php:281
#: actions/designadminpanel.php:103 actions/editapplication.php:139
-#: actions/emailsettings.php:256 actions/grouplogo.php:319
+#: actions/emailsettings.php:256 actions/grouplogo.php:322
#: actions/imsettings.php:220 actions/newapplication.php:121
#: actions/oauthconnectionssettings.php:147 actions/recoverpassword.php:44
#: actions/smssettings.php:248 lib/designsettings.php:304
@@ -541,7 +543,7 @@ msgstr ""
#: actions/apioauthauthorize.php:313 actions/login.php:230
#: actions/profilesettings.php:106 actions/register.php:424
-#: actions/showgroup.php:236 actions/tagother.php:94
+#: actions/showgroup.php:244 actions/tagother.php:94
#: actions/userauthorization.php:145 lib/groupeditform.php:152
#: lib/userprofile.php:131
msgid "Nickname"
@@ -623,12 +625,12 @@ msgid "%1$s updates favorited by %2$s / %2$s."
msgstr ""
#: actions/apitimelinegroup.php:109 actions/apitimelineuser.php:118
-#: actions/grouprss.php:131 actions/userrss.php:90
+#: actions/grouprss.php:138 actions/userrss.php:90
#, php-format
msgid "%s timeline"
msgstr ""
-#: actions/apitimelinegroup.php:114 actions/apitimelineuser.php:126
+#: actions/apitimelinegroup.php:112 actions/apitimelineuser.php:124
#: actions/userrss.php:92
#, php-format
msgid "Updates from %1$s on %2$s!"
@@ -685,8 +687,7 @@ msgstr ""
#: actions/avatarbynickname.php:59 actions/blockedfromgroup.php:73
#: actions/editgroup.php:84 actions/groupdesignsettings.php:84
#: actions/grouplogo.php:86 actions/groupmembers.php:76
-#: actions/grouprss.php:91 actions/joingroup.php:76 actions/leavegroup.php:76
-#: actions/showgroup.php:121
+#: actions/grouprss.php:91 actions/showgroup.php:121
msgid "No nickname."
msgstr ""
@@ -698,7 +699,7 @@ msgstr ""
msgid "Invalid size."
msgstr ""
-#: actions/avatarsettings.php:67 actions/showgroup.php:221
+#: actions/avatarsettings.php:67 actions/showgroup.php:229
#: lib/accountsettingsaction.php:112
msgid "Avatar"
msgstr ""
@@ -715,30 +716,30 @@ msgid "User without matching profile"
msgstr ""
#: actions/avatarsettings.php:119 actions/avatarsettings.php:197
-#: actions/grouplogo.php:251
+#: actions/grouplogo.php:254
msgid "Avatar settings"
msgstr ""
#: actions/avatarsettings.php:127 actions/avatarsettings.php:205
-#: actions/grouplogo.php:199 actions/grouplogo.php:259
+#: actions/grouplogo.php:202 actions/grouplogo.php:262
msgid "Original"
msgstr ""
#: actions/avatarsettings.php:142 actions/avatarsettings.php:217
-#: actions/grouplogo.php:210 actions/grouplogo.php:271
+#: actions/grouplogo.php:213 actions/grouplogo.php:274
msgid "Preview"
msgstr ""
#: actions/avatarsettings.php:149 actions/showapplication.php:252
-#: lib/deleteuserform.php:66 lib/noticelist.php:637
+#: lib/deleteuserform.php:66 lib/noticelist.php:655
msgid "Delete"
msgstr ""
-#: actions/avatarsettings.php:166 actions/grouplogo.php:233
+#: actions/avatarsettings.php:166 actions/grouplogo.php:236
msgid "Upload"
msgstr ""
-#: actions/avatarsettings.php:231 actions/grouplogo.php:286
+#: actions/avatarsettings.php:231 actions/grouplogo.php:289
msgid "Crop"
msgstr ""
@@ -746,7 +747,7 @@ msgstr ""
msgid "Pick a square area of the image to be your avatar"
msgstr ""
-#: actions/avatarsettings.php:343 actions/grouplogo.php:377
+#: actions/avatarsettings.php:343 actions/grouplogo.php:380
msgid "Lost our file data."
msgstr ""
@@ -778,22 +779,22 @@ msgid ""
msgstr ""
#: actions/block.php:143 actions/deleteapplication.php:153
-#: actions/deletenotice.php:145 actions/deleteuser.php:147
+#: actions/deletenotice.php:145 actions/deleteuser.php:150
#: actions/groupblock.php:178
msgid "No"
msgstr ""
-#: actions/block.php:143 actions/deleteuser.php:147
+#: actions/block.php:143 actions/deleteuser.php:150
msgid "Do not block this user"
msgstr ""
#: actions/block.php:144 actions/deleteapplication.php:158
-#: actions/deletenotice.php:146 actions/deleteuser.php:148
+#: actions/deletenotice.php:146 actions/deleteuser.php:151
#: actions/groupblock.php:179 lib/repeatform.php:132
msgid "Yes"
msgstr ""
-#: actions/block.php:144 actions/groupmembers.php:348 lib/blockform.php:80
+#: actions/block.php:144 actions/groupmembers.php:355 lib/blockform.php:80
msgid "Block this user"
msgstr ""
@@ -801,39 +802,43 @@ msgstr ""
msgid "Failed to save block information."
msgstr ""
-#: actions/blockedfromgroup.php:80 actions/editgroup.php:96
-#: actions/foafgroup.php:44 actions/foafgroup.php:62 actions/groupblock.php:86
-#: actions/groupbyid.php:83 actions/groupdesignsettings.php:97
-#: actions/grouplogo.php:99 actions/groupmembers.php:83
-#: actions/grouprss.php:98 actions/groupunblock.php:86
-#: actions/joingroup.php:83 actions/leavegroup.php:83 actions/makeadmin.php:86
-#: actions/showgroup.php:137 lib/command.php:212 lib/command.php:260
+#: actions/blockedfromgroup.php:80 actions/blockedfromgroup.php:87
+#: actions/editgroup.php:100 actions/foafgroup.php:44 actions/foafgroup.php:62
+#: actions/foafgroup.php:69 actions/groupblock.php:86 actions/groupbyid.php:83
+#: actions/groupdesignsettings.php:100 actions/grouplogo.php:102
+#: actions/groupmembers.php:83 actions/groupmembers.php:90
+#: actions/grouprss.php:98 actions/grouprss.php:105
+#: actions/groupunblock.php:86 actions/joingroup.php:82
+#: actions/joingroup.php:93 actions/leavegroup.php:82
+#: actions/leavegroup.php:93 actions/makeadmin.php:86
+#: actions/showgroup.php:138 actions/showgroup.php:146 lib/command.php:212
+#: lib/command.php:260
msgid "No such group."
msgstr ""
-#: actions/blockedfromgroup.php:90
+#: actions/blockedfromgroup.php:97
#, php-format
msgid "%s blocked profiles"
msgstr ""
-#: actions/blockedfromgroup.php:93
+#: actions/blockedfromgroup.php:100
#, php-format
msgid "%1$s blocked profiles, page %2$d"
msgstr ""
-#: actions/blockedfromgroup.php:108
+#: actions/blockedfromgroup.php:115
msgid "A list of the users blocked from joining this group."
msgstr ""
-#: actions/blockedfromgroup.php:281
+#: actions/blockedfromgroup.php:288
msgid "Unblock user from group"
msgstr ""
-#: actions/blockedfromgroup.php:313 lib/unblockform.php:69
+#: actions/blockedfromgroup.php:320 lib/unblockform.php:69
msgid "Unblock"
msgstr ""
-#: actions/blockedfromgroup.php:313 lib/unblockform.php:80
+#: actions/blockedfromgroup.php:320 lib/unblockform.php:80
msgid "Unblock this user"
msgstr ""
@@ -876,7 +881,7 @@ msgid "Couldn't delete email confirmation."
msgstr ""
#: actions/confirmaddress.php:144
-msgid "Confirm address"
+msgid "Confirm Address"
msgstr ""
#: actions/confirmaddress.php:159
@@ -963,7 +968,7 @@ msgstr ""
msgid "Do not delete this notice"
msgstr ""
-#: actions/deletenotice.php:146 lib/noticelist.php:637
+#: actions/deletenotice.php:146 lib/noticelist.php:655
msgid "Delete this notice"
msgstr ""
@@ -979,18 +984,18 @@ msgstr ""
msgid "Delete user"
msgstr ""
-#: actions/deleteuser.php:135
+#: actions/deleteuser.php:136
msgid ""
"Are you sure you want to delete this user? This will clear all data about "
"the user from the database, without a backup."
msgstr ""
-#: actions/deleteuser.php:148 lib/deleteuserform.php:77
+#: actions/deleteuser.php:151 lib/deleteuserform.php:77
msgid "Delete this user"
msgstr ""
#: actions/designadminpanel.php:62 lib/accountsettingsaction.php:124
-#: lib/adminpanelaction.php:316 lib/groupnav.php:119
+#: lib/adminpanelaction.php:327 lib/groupnav.php:119
msgid "Design"
msgstr ""
@@ -1182,29 +1187,29 @@ msgstr ""
msgid "You must be logged in to create a group."
msgstr ""
-#: actions/editgroup.php:103 actions/editgroup.php:168
-#: actions/groupdesignsettings.php:104 actions/grouplogo.php:106
+#: actions/editgroup.php:107 actions/editgroup.php:172
+#: actions/groupdesignsettings.php:107 actions/grouplogo.php:109
msgid "You must be an admin to edit the group."
msgstr ""
-#: actions/editgroup.php:154
+#: actions/editgroup.php:158
msgid "Use this form to edit the group."
msgstr ""
-#: actions/editgroup.php:201 actions/newgroup.php:145
+#: actions/editgroup.php:205 actions/newgroup.php:145
#, php-format
msgid "description is too long (max %d chars)."
msgstr ""
-#: actions/editgroup.php:253
+#: actions/editgroup.php:258
msgid "Could not update group."
msgstr ""
-#: actions/editgroup.php:259 classes/User_group.php:433
+#: actions/editgroup.php:264 classes/User_group.php:478
msgid "Could not create aliases."
msgstr ""
-#: actions/editgroup.php:269
+#: actions/editgroup.php:280
msgid "Options saved."
msgstr ""
@@ -1533,7 +1538,7 @@ msgstr ""
msgid "User is not a member of group."
msgstr ""
-#: actions/groupblock.php:136 actions/groupmembers.php:316
+#: actions/groupblock.php:136 actions/groupmembers.php:323
msgid "Block user from group"
msgstr ""
@@ -1565,86 +1570,86 @@ msgstr ""
msgid "You must be logged in to edit a group."
msgstr ""
-#: actions/groupdesignsettings.php:141
+#: actions/groupdesignsettings.php:144
msgid "Group design"
msgstr ""
-#: actions/groupdesignsettings.php:152
+#: actions/groupdesignsettings.php:155
msgid ""
"Customize the way your group looks with a background image and a colour "
"palette of your choice."
msgstr ""
-#: actions/groupdesignsettings.php:263 actions/userdesignsettings.php:186
+#: actions/groupdesignsettings.php:266 actions/userdesignsettings.php:186
#: lib/designsettings.php:391 lib/designsettings.php:413
msgid "Couldn't update your design."
msgstr ""
-#: actions/groupdesignsettings.php:308 actions/userdesignsettings.php:231
+#: actions/groupdesignsettings.php:311 actions/userdesignsettings.php:231
msgid "Design preferences saved."
msgstr ""
-#: actions/grouplogo.php:139 actions/grouplogo.php:192
+#: actions/grouplogo.php:142 actions/grouplogo.php:195
msgid "Group logo"
msgstr ""
-#: actions/grouplogo.php:150
+#: actions/grouplogo.php:153
#, php-format
msgid ""
"You can upload a logo image for your group. The maximum file size is %s."
msgstr ""
-#: actions/grouplogo.php:178
+#: actions/grouplogo.php:181
msgid "User without matching profile."
msgstr ""
-#: actions/grouplogo.php:362
+#: actions/grouplogo.php:365
msgid "Pick a square area of the image to be the logo."
msgstr ""
-#: actions/grouplogo.php:396
+#: actions/grouplogo.php:399
msgid "Logo updated."
msgstr ""
-#: actions/grouplogo.php:398
+#: actions/grouplogo.php:401
msgid "Failed updating logo."
msgstr ""
-#: actions/groupmembers.php:93 lib/groupnav.php:92
+#: actions/groupmembers.php:100 lib/groupnav.php:92
#, php-format
msgid "%s group members"
msgstr ""
-#: actions/groupmembers.php:96
+#: actions/groupmembers.php:103
#, php-format
msgid "%1$s group members, page %2$d"
msgstr ""
-#: actions/groupmembers.php:111
+#: actions/groupmembers.php:118
msgid "A list of the users in this group."
msgstr ""
-#: actions/groupmembers.php:175 lib/action.php:448 lib/groupnav.php:107
+#: actions/groupmembers.php:182 lib/action.php:448 lib/groupnav.php:107
msgid "Admin"
msgstr ""
-#: actions/groupmembers.php:348 lib/blockform.php:69
+#: actions/groupmembers.php:355 lib/blockform.php:69
msgid "Block"
msgstr ""
-#: actions/groupmembers.php:443
+#: actions/groupmembers.php:450
msgid "Make user an admin of the group"
msgstr ""
-#: actions/groupmembers.php:475
+#: actions/groupmembers.php:482
msgid "Make Admin"
msgstr ""
-#: actions/groupmembers.php:475
+#: actions/groupmembers.php:482
msgid "Make this user an admin"
msgstr ""
-#: actions/grouprss.php:133
+#: actions/grouprss.php:140
#, php-format
msgid "Updates from members of %1$s on %2$s!"
msgstr ""
@@ -1924,7 +1929,11 @@ msgstr ""
msgid "You must be logged in to join a group."
msgstr ""
-#: actions/joingroup.php:131
+#: actions/joingroup.php:88 actions/leavegroup.php:88
+msgid "No nickname or ID."
+msgstr ""
+
+#: actions/joingroup.php:141
#, php-format
msgid "%1$s joined group %2$s"
msgstr ""
@@ -1933,11 +1942,11 @@ msgstr ""
msgid "You must be logged in to leave a group."
msgstr ""
-#: actions/leavegroup.php:90 lib/command.php:265
+#: actions/leavegroup.php:100 lib/command.php:265
msgid "You are not a member of that group."
msgstr ""
-#: actions/leavegroup.php:127
+#: actions/leavegroup.php:137
#, php-format
msgid "%1$s left group %2$s"
msgstr ""
@@ -2194,8 +2203,8 @@ msgstr ""
msgid "Only "
msgstr ""
-#: actions/oembed.php:181 actions/oembed.php:200 lib/api.php:1040
-#: lib/api.php:1068 lib/api.php:1177
+#: actions/oembed.php:181 actions/oembed.php:200 lib/apiaction.php:1039
+#: lib/apiaction.php:1067 lib/apiaction.php:1176
msgid "Not a supported data format."
msgstr ""
@@ -2208,7 +2217,7 @@ msgid "Notice Search"
msgstr ""
#: actions/othersettings.php:60
-msgid "Other settings"
+msgid "Other Settings"
msgstr ""
#: actions/othersettings.php:71
@@ -2334,7 +2343,7 @@ msgstr ""
msgid "Password saved."
msgstr ""
-#: actions/pathsadminpanel.php:59 lib/adminpanelaction.php:331
+#: actions/pathsadminpanel.php:59 lib/adminpanelaction.php:342
msgid "Paths"
msgstr ""
@@ -2367,7 +2376,7 @@ msgid "Invalid SSL server. The maximum length is 255 characters."
msgstr ""
#: actions/pathsadminpanel.php:234 actions/siteadminpanel.php:58
-#: lib/adminpanelaction.php:311
+#: lib/adminpanelaction.php:322
msgid "Site"
msgstr ""
@@ -2535,7 +2544,7 @@ msgid "1-64 lowercase letters or numbers, no punctuation or spaces"
msgstr ""
#: actions/profilesettings.php:111 actions/register.php:448
-#: actions/showgroup.php:247 actions/tagother.php:104
+#: actions/showgroup.php:255 actions/tagother.php:104
#: lib/groupeditform.php:157 lib/userprofile.php:149
msgid "Full name"
msgstr ""
@@ -2563,7 +2572,7 @@ msgid "Bio"
msgstr ""
#: actions/profilesettings.php:132 actions/register.php:471
-#: actions/showgroup.php:256 actions/tagother.php:112
+#: actions/showgroup.php:264 actions/tagother.php:112
#: actions/userauthorization.php:166 lib/groupeditform.php:177
#: lib/userprofile.php:164
msgid "Location"
@@ -3032,7 +3041,7 @@ msgstr ""
msgid "You already repeated that notice."
msgstr ""
-#: actions/repeat.php:114 lib/noticelist.php:656
+#: actions/repeat.php:114 lib/noticelist.php:674
msgid "Repeated"
msgstr ""
@@ -3105,7 +3114,7 @@ msgid "User is already sandboxed."
msgstr ""
#: actions/sessionsadminpanel.php:54 actions/sessionsadminpanel.php:170
-#: lib/adminpanelaction.php:336
+#: lib/adminpanelaction.php:347
msgid "Sessions"
msgstr ""
@@ -3160,7 +3169,7 @@ msgstr ""
msgid "Description"
msgstr ""
-#: actions/showapplication.php:192 actions/showgroup.php:429
+#: actions/showapplication.php:192 actions/showgroup.php:437
#: lib/profileaction.php:174
msgid "Statistics"
msgstr ""
@@ -3271,67 +3280,67 @@ msgstr ""
msgid "%1$s group, page %2$d"
msgstr ""
-#: actions/showgroup.php:218
+#: actions/showgroup.php:226
msgid "Group profile"
msgstr ""
-#: actions/showgroup.php:263 actions/tagother.php:118
+#: actions/showgroup.php:271 actions/tagother.php:118
#: actions/userauthorization.php:175 lib/userprofile.php:177
msgid "URL"
msgstr ""
-#: actions/showgroup.php:274 actions/tagother.php:128
+#: actions/showgroup.php:282 actions/tagother.php:128
#: actions/userauthorization.php:187 lib/userprofile.php:194
msgid "Note"
msgstr ""
-#: actions/showgroup.php:284 lib/groupeditform.php:184
+#: actions/showgroup.php:292 lib/groupeditform.php:184
msgid "Aliases"
msgstr ""
-#: actions/showgroup.php:293
+#: actions/showgroup.php:301
msgid "Group actions"
msgstr ""
-#: actions/showgroup.php:328
+#: actions/showgroup.php:336
#, php-format
msgid "Notice feed for %s group (RSS 1.0)"
msgstr ""
-#: actions/showgroup.php:334
+#: actions/showgroup.php:342
#, php-format
msgid "Notice feed for %s group (RSS 2.0)"
msgstr ""
-#: actions/showgroup.php:340
+#: actions/showgroup.php:348
#, php-format
msgid "Notice feed for %s group (Atom)"
msgstr ""
-#: actions/showgroup.php:345
+#: actions/showgroup.php:353
#, php-format
msgid "FOAF for %s group"
msgstr ""
-#: actions/showgroup.php:381 actions/showgroup.php:438 lib/groupnav.php:91
+#: actions/showgroup.php:389 actions/showgroup.php:446 lib/groupnav.php:91
msgid "Members"
msgstr ""
-#: actions/showgroup.php:386 lib/profileaction.php:117
+#: actions/showgroup.php:394 lib/profileaction.php:117
#: lib/profileaction.php:148 lib/profileaction.php:236 lib/section.php:95
#: lib/subscriptionlist.php:126 lib/tagcloudsection.php:71
msgid "(None)"
msgstr ""
-#: actions/showgroup.php:392
+#: actions/showgroup.php:400
msgid "All members"
msgstr ""
-#: actions/showgroup.php:432
+#: actions/showgroup.php:440
msgid "Created"
msgstr ""
-#: actions/showgroup.php:448
+#: actions/showgroup.php:456
#, php-format
msgid ""
"**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en."
@@ -3341,7 +3350,7 @@ msgid ""
"of this group and many more! ([Read more](%%%%doc.help%%%%))"
msgstr ""
-#: actions/showgroup.php:454
+#: actions/showgroup.php:462
#, php-format
msgid ""
"**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en."
@@ -3350,7 +3359,7 @@ msgid ""
"their life and interests. "
msgstr ""
-#: actions/showgroup.php:482
+#: actions/showgroup.php:490
msgid "Admins"
msgstr ""
@@ -3861,7 +3870,7 @@ msgstr ""
msgid "No such tag."
msgstr ""
-#: actions/twitapitrends.php:87
+#: actions/twitapitrends.php:85
msgid "API method under construction."
msgstr ""
@@ -3891,7 +3900,7 @@ msgid ""
"Listenee stream license ‘%1$s’ is not compatible with site license ‘%2$s’."
msgstr ""
-#: actions/useradminpanel.php:58 lib/adminpanelaction.php:321
+#: actions/useradminpanel.php:58 lib/adminpanelaction.php:332
#: lib/personalgroupnav.php:115
msgid "User"
msgstr ""
@@ -4164,6 +4173,10 @@ msgstr ""
msgid "Group leave failed."
msgstr ""
+#: classes/Local_group.php:41
+msgid "Could not update local group."
+msgstr ""
+
#: classes/Login_token.php:76
#, php-format
msgid "Could not create login token for %s"
@@ -4181,43 +4194,43 @@ msgstr ""
msgid "Could not update message with new URI."
msgstr ""
-#: classes/Notice.php:157
+#: classes/Notice.php:172
#, php-format
msgid "DB error inserting hashtag: %s"
msgstr ""
-#: classes/Notice.php:222
+#: classes/Notice.php:239
msgid "Problem saving notice. Too long."
msgstr ""
-#: classes/Notice.php:226
+#: classes/Notice.php:243
msgid "Problem saving notice. Unknown user."
msgstr ""
-#: classes/Notice.php:231
+#: classes/Notice.php:248
msgid ""
"Too many notices too fast; take a breather and post again in a few minutes."
msgstr ""
-#: classes/Notice.php:237
+#: classes/Notice.php:254
msgid ""
"Too many duplicate messages too quickly; take a breather and post again in a "
"few minutes."
msgstr ""
-#: classes/Notice.php:243
+#: classes/Notice.php:260
msgid "You are banned from posting notices on this site."
msgstr ""
-#: classes/Notice.php:309 classes/Notice.php:335
+#: classes/Notice.php:326 classes/Notice.php:352
msgid "Problem saving notice."
msgstr ""
-#: classes/Notice.php:882
+#: classes/Notice.php:911
msgid "Problem saving group inbox."
msgstr ""
-#: classes/Notice.php:1407
+#: classes/Notice.php:1442
#, php-format
msgid "RT @%1$s %2$s"
msgstr ""
@@ -4242,7 +4255,7 @@ msgstr ""
msgid "Couldn't delete self-subscription."
msgstr ""
-#: classes/Subscription.php:179 lib/subs.php:69
+#: classes/Subscription.php:179
msgid "Couldn't delete subscription."
msgstr ""
@@ -4251,14 +4264,22 @@ msgstr ""
msgid "Welcome to %1$s, @%2$s!"
msgstr ""
-#: classes/User_group.php:423
+#: classes/User_group.php:462
msgid "Could not create group."
msgstr ""
-#: classes/User_group.php:452
+#: classes/User_group.php:471
+msgid "Could not set group uri."
+msgstr ""
+
+#: classes/User_group.php:492
msgid "Could not set group membership."
msgstr ""
+#: classes/User_group.php:506
+msgid "Could not save local group info."
+msgstr ""
+
#: lib/accountsettingsaction.php:108
msgid "Change your profile settings"
msgstr ""
@@ -4471,15 +4492,15 @@ msgstr ""
msgid "Before"
msgstr ""
-#: lib/activity.php:382
+#: lib/activity.php:449
msgid "Can't handle remote content yet."
msgstr ""
-#: lib/activity.php:410
+#: lib/activity.php:477
msgid "Can't handle embedded XML content yet."
msgstr ""
-#: lib/activity.php:414
+#: lib/activity.php:481
msgid "Can't handle embedded Base64 content yet."
msgstr ""
@@ -4503,35 +4524,35 @@ msgstr ""
msgid "Unable to delete design setting."
msgstr ""
-#: lib/adminpanelaction.php:312
+#: lib/adminpanelaction.php:323
msgid "Basic site configuration"
msgstr ""
-#: lib/adminpanelaction.php:317
+#: lib/adminpanelaction.php:328
msgid "Design configuration"
msgstr ""
-#: lib/adminpanelaction.php:322
+#: lib/adminpanelaction.php:333
msgid "User configuration"
msgstr ""
-#: lib/adminpanelaction.php:327
+#: lib/adminpanelaction.php:338
msgid "Access configuration"
msgstr ""
-#: lib/adminpanelaction.php:332
+#: lib/adminpanelaction.php:343
msgid "Paths configuration"
msgstr ""
-#: lib/adminpanelaction.php:337
+#: lib/adminpanelaction.php:348
msgid "Sessions configuration"
msgstr ""
-#: lib/apiauth.php:95
+#: lib/apiauth.php:94
msgid "API resource requires read-write access, but you only have read access."
msgstr ""
-#: lib/apiauth.php:273
+#: lib/apiauth.php:272
#, php-format
msgid "Failed API auth attempt, nickname = %1$s, proxy = %2$s, ip = %3$s"
msgstr ""
@@ -4621,11 +4642,11 @@ msgstr ""
msgid "Tags for this attachment"
msgstr ""
-#: lib/authenticationplugin.php:218 lib/authenticationplugin.php:223
+#: lib/authenticationplugin.php:182 lib/authenticationplugin.php:187
msgid "Password changing failed"
msgstr ""
-#: lib/authenticationplugin.php:233
+#: lib/authenticationplugin.php:197
msgid "Password changing is not allowed"
msgstr ""
@@ -4782,7 +4803,7 @@ msgstr ""
msgid "Subscribed to %s"
msgstr ""
-#: lib/command.php:582 lib/command.php:685
+#: lib/command.php:582
msgid "Specify the name of the user to unsubscribe from"
msgstr ""
@@ -4820,42 +4841,37 @@ msgstr ""
msgid "This link is useable only once, and is good for only 2 minutes: %s"
msgstr ""
-#: lib/command.php:692
-#, php-format
-msgid "Unsubscribed %s"
-msgstr ""
-
-#: lib/command.php:709
+#: lib/command.php:681
msgid "You are not subscribed to anyone."
msgstr ""
-#: lib/command.php:711
+#: lib/command.php:683
msgid "You are subscribed to this person:"
msgid_plural "You are subscribed to these people:"
msgstr[0] ""
msgstr[1] ""
-#: lib/command.php:731
+#: lib/command.php:703
msgid "No one is subscribed to you."
msgstr ""
-#: lib/command.php:733
+#: lib/command.php:705
msgid "This person is subscribed to you:"
msgid_plural "These people are subscribed to you:"
msgstr[0] ""
msgstr[1] ""
-#: lib/command.php:753
+#: lib/command.php:725
msgid "You are not a member of any groups."
msgstr ""
-#: lib/command.php:755
+#: lib/command.php:727
msgid "You are a member of this group:"
msgid_plural "You are a member of these groups:"
msgstr[0] ""
msgstr[1] ""
-#: lib/command.php:769
+#: lib/command.php:741
msgid ""
"Commands:\n"
"on - turn on notifications\n"
@@ -4869,7 +4885,6 @@ msgid ""
"d <nickname> <text> - direct message to user\n"
"get <nickname> - get last notice from user\n"
"whois <nickname> - get profile info on user\n"
-"lose <nickname> - force user to stop following you\n"
"fav <nickname> - add user's last notice as a 'fave'\n"
"fav #<notice_id> - add notice with the given id as a 'fave'\n"
"repeat #<notice_id> - repeat a notice with a given id\n"
@@ -5460,23 +5475,23 @@ msgstr ""
msgid "at"
msgstr ""
-#: lib/noticelist.php:558
+#: lib/noticelist.php:566
msgid "in context"
msgstr ""
-#: lib/noticelist.php:583
+#: lib/noticelist.php:601
msgid "Repeated by"
msgstr ""
-#: lib/noticelist.php:610
+#: lib/noticelist.php:628
msgid "Reply to this notice"
msgstr ""
-#: lib/noticelist.php:611
+#: lib/noticelist.php:629
msgid "Reply"
msgstr ""
-#: lib/noticelist.php:655
+#: lib/noticelist.php:673
msgid "Notice repeated"
msgstr ""
@@ -5613,7 +5628,7 @@ msgstr ""
msgid "Repeat this notice"
msgstr ""
-#: lib/router.php:665
+#: lib/router.php:668
msgid "No single user defined for single-user mode."
msgstr ""
@@ -5754,47 +5769,47 @@ msgstr ""
msgid "Moderate"
msgstr ""
-#: lib/util.php:952
+#: lib/util.php:1000
msgid "a few seconds ago"
msgstr ""
-#: lib/util.php:954
+#: lib/util.php:1002
msgid "about a minute ago"
msgstr ""
-#: lib/util.php:956
+#: lib/util.php:1004
#, php-format
msgid "about %d minutes ago"
msgstr ""
-#: lib/util.php:958
+#: lib/util.php:1006
msgid "about an hour ago"
msgstr ""
-#: lib/util.php:960
+#: lib/util.php:1008
#, php-format
msgid "about %d hours ago"
msgstr ""
-#: lib/util.php:962
+#: lib/util.php:1010
msgid "about a day ago"
msgstr ""
-#: lib/util.php:964
+#: lib/util.php:1012
#, php-format
msgid "about %d days ago"
msgstr ""
-#: lib/util.php:966
+#: lib/util.php:1014
msgid "about a month ago"
msgstr ""
-#: lib/util.php:968
+#: lib/util.php:1016
#, php-format
msgid "about %d months ago"
msgstr ""
-#: lib/util.php:970
+#: lib/util.php:1018
msgid "about a year ago"
msgstr ""
diff --git a/plugins/Facebook/FacebookPlugin.php b/plugins/Facebook/FacebookPlugin.php
index 4266b886d..014d0d197 100644
--- a/plugins/Facebook/FacebookPlugin.php
+++ b/plugins/Facebook/FacebookPlugin.php
@@ -22,7 +22,7 @@
* @category Plugin
* @package StatusNet
* @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/
*/
@@ -32,12 +32,12 @@ if (!defined('STATUSNET')) {
}
define("FACEBOOK_CONNECT_SERVICE", 3);
-define('FACEBOOKPLUGIN_VERSION', '0.9');
require_once INSTALLDIR . '/plugins/Facebook/facebookutil.php';
/**
- * Facebook plugin to add a StatusNet Facebook application
+ * Facebook plugin to add a StatusNet Facebook canvas application
+ * and allow registration and authentication via Facebook Connect
*
* @category Plugin
* @package StatusNet
@@ -49,6 +49,36 @@ require_once INSTALLDIR . '/plugins/Facebook/facebookutil.php';
class FacebookPlugin extends Plugin
{
+ const VERSION = STATUSNET_VERSION;
+
+ /**
+ * Initializer for the plugin.
+ */
+
+ function initialize()
+ {
+ // Allow the key and secret to be passed in
+ // Control panel will override
+
+ if (isset($this->apikey)) {
+ $key = common_config('facebook', 'apikey');
+ if (empty($key)) {
+ Config::save('facebook', 'apikey', $this->apikey);
+ }
+ }
+
+ if (isset($this->secret)) {
+ $secret = common_config('facebook', 'secret');
+ if (empty($secret)) {
+ Config::save(
+ 'facebook',
+ 'secret',
+ $this->secret
+ );
+ }
+ }
+ }
+
/**
* Add Facebook app actions to the router table
*
@@ -70,6 +100,7 @@ class FacebookPlugin extends Plugin
array('action' => 'facebooksettings'));
$m->connect('facebook/app/invite.php', array('action' => 'facebookinvite'));
$m->connect('facebook/app/remove', array('action' => 'facebookremove'));
+ $m->connect('admin/facebook', array('action' => 'facebookadminpanel'));
// Facebook Connect stuff
@@ -98,6 +129,7 @@ class FacebookPlugin extends Plugin
case 'FacebookinviteAction':
case 'FacebookremoveAction':
case 'FacebooksettingsAction':
+ case 'FacebookadminpanelAction':
include_once INSTALLDIR . '/plugins/Facebook/' .
strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;
@@ -123,6 +155,32 @@ class FacebookPlugin extends Plugin
}
/**
+ * Add a Facebook tab to the admin panels
+ *
+ * @param Widget $nav Admin panel nav
+ *
+ * @return boolean hook value
+ */
+
+ function onEndAdminPanelNav($nav)
+ {
+ if (AdminPanelAction::canAdmin('facebook')) {
+
+ $action_name = $nav->action->trimmed('action');
+
+ $nav->out->menuItem(
+ common_local_url('facebookadminpanel'),
+ _m('Facebook'),
+ _m('Facebook integration configuration'),
+ $action_name == 'facebookadminpanel',
+ 'nav_facebook_admin_panel'
+ );
+ }
+
+ return true;
+ }
+
+ /**
* Override normal HTML output to force the content type to
* text/html and add in xmlns:fb
*
@@ -359,8 +417,6 @@ class FacebookPlugin extends Plugin
$connect = 'imsettings';
} else if (common_config('sms', 'enabled')) {
$connect = 'smssettings';
- } else if (common_config('twitter', 'enabled')) {
- $connect = 'twittersettings';
}
if (!empty($user)) {
@@ -525,15 +581,18 @@ class FacebookPlugin extends Plugin
function onPluginVersion(&$versions)
{
- $versions[] = array('name' => 'Facebook',
- 'version' => FACEBOOKPLUGIN_VERSION,
- 'author' => 'Zach Copley',
- 'homepage' => 'http://status.net/wiki/Plugin:Facebook',
- 'rawdescription' =>
- _m('The Facebook plugin allows you to integrate ' .
- 'your StatusNet instance with ' .
- '<a href="http://facebook.com/">Facebook</a> ' .
- 'and Facebook Connect.'));
+ $versions[] = array(
+ 'name' => 'Facebook',
+ 'version' => self::VERSION,
+ 'author' => 'Zach Copley',
+ 'homepage' => 'http://status.net/wiki/Plugin:Facebook',
+ 'rawdescription' => _m(
+ 'The Facebook plugin allows you to integrate ' .
+ 'your StatusNet instance with ' .
+ '<a href="http://facebook.com/">Facebook</a> ' .
+ 'and Facebook Connect.'
+ )
+ );
return true;
}
diff --git a/plugins/Facebook/facebookadminpanel.php b/plugins/Facebook/facebookadminpanel.php
new file mode 100644
index 000000000..ae1c7302f
--- /dev/null
+++ b/plugins/Facebook/facebookadminpanel.php
@@ -0,0 +1,223 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Facebook integration 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);
+}
+
+/**
+ * Administer global Facebook integration settings
+ *
+ * @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 FacebookadminpanelAction extends AdminPanelAction
+{
+ /**
+ * Returns the page title
+ *
+ * @return string page title
+ */
+
+ function title()
+ {
+ return _m('Facebook');
+ }
+
+ /**
+ * Instructions for using this form.
+ *
+ * @return string instructions
+ */
+
+ function getInstructions()
+ {
+ return _m('Facebook integration settings');
+ }
+
+ /**
+ * Show the Facebook admin panel form
+ *
+ * @return void
+ */
+
+ function showForm()
+ {
+ $form = new FacebookAdminPanelForm($this);
+ $form->show();
+ return;
+ }
+
+ /**
+ * Save settings from the form
+ *
+ * @return void
+ */
+
+ function saveSettings()
+ {
+ static $settings = array(
+ 'facebook' => array('apikey', 'secret'),
+ );
+
+ $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 consumer key and secret (can't be too long)
+
+ if (mb_strlen($values['facebook']['apikey']) > 255) {
+ $this->clientError(
+ _m("Invalid Facebook API key. Max length is 255 characters.")
+ );
+ }
+
+ if (mb_strlen($values['facebook']['secret']) > 255) {
+ $this->clientError(
+ _m("Invalid Facebook API secret. Max length is 255 characters.")
+ );
+ }
+ }
+}
+
+class FacebookAdminPanelForm extends AdminForm
+{
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ return 'facebookadminpanel';
+ }
+
+ /**
+ * 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('facebookadminpanel');
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->out->elementStart(
+ 'fieldset',
+ array('id' => 'settings_facebook-application')
+ );
+ $this->out->element('legend', null, _m('Facebook application settings'));
+ $this->out->elementStart('ul', 'form_data');
+
+ $this->li();
+ $this->input(
+ 'apikey',
+ _m('API key'),
+ _m('API key provided by Facebook'),
+ 'facebook'
+ );
+ $this->unli();
+
+ $this->li();
+ $this->input(
+ 'secret',
+ _m('Secret'),
+ _m('API secret provided by Facebook'),
+ 'facebook'
+ );
+ $this->unli();
+
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('fieldset');
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->out->submit('submit', _('Save'), 'submit', null, _('Save Facebook settings'));
+ }
+}
diff --git a/plugins/MobileProfile/MobileProfilePlugin.php b/plugins/MobileProfile/MobileProfilePlugin.php
index cd2531fa7..f788639ae 100644
--- a/plugins/MobileProfile/MobileProfilePlugin.php
+++ b/plugins/MobileProfile/MobileProfilePlugin.php
@@ -312,8 +312,6 @@ class MobileProfilePlugin extends WAP20Plugin
$connect = 'imsettings';
} else if (common_config('sms', 'enabled')) {
$connect = 'smssettings';
- } else if (common_config('twitter', 'enabled')) {
- $connect = 'twittersettings';
}
$action->elementStart('ul', array('id' => 'site_nav_global_primary'));
diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php
index 720dedd0a..4ffbba45b 100644
--- a/plugins/OStatus/OStatusPlugin.php
+++ b/plugins/OStatus/OStatusPlugin.php
@@ -222,31 +222,62 @@ class OStatusPlugin extends Plugin
}
/**
- *
+ * Find any explicit remote mentions. Accepted forms:
+ * Webfinger: @user@example.com
+ * Profile link: @example.com/mublog/user
+ * @param Profile $sender (os user?)
+ * @param string $text input markup text
+ * @param array &$mention in/out param: set of found mentions
+ * @return boolean hook return value
*/
function onEndFindMentions($sender, $text, &$mentions)
{
- preg_match_all('/(?:^|\s+)@((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)/',
+ preg_match_all('!(?:^|\s+)
+ @( # Webfinger:
+ (?:\w+\.)*\w+ # user
+ @ # @
+ (?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+ # domain
+ | # Profile:
+ (?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+ # domain
+ (?:/\w+)+ # /path1(/path2...)
+ )!x',
$text,
$wmatches,
PREG_OFFSET_CAPTURE);
foreach ($wmatches[1] as $wmatch) {
-
- $webfinger = $wmatch[0];
-
- $this->log(LOG_INFO, "Checking Webfinger for address '$webfinger'");
-
- $oprofile = Ostatus_profile::ensureWebfinger($webfinger);
+ $target = $wmatch[0];
+ $oprofile = null;
+
+ if (strpos($target, '/') === false) {
+ $this->log(LOG_INFO, "Checking Webfinger for address '$target'");
+ try {
+ $oprofile = Ostatus_profile::ensureWebfinger($target);
+ } catch (Exception $e) {
+ $this->log(LOG_ERR, "Webfinger check failed: " . $e->getMessage());
+ }
+ } else {
+ $schemes = array('https', 'http');
+ foreach ($schemes as $scheme) {
+ $url = "$scheme://$target";
+ $this->log(LOG_INFO, "Checking profile address '$url'");
+ try {
+ $oprofile = Ostatus_profile::ensureProfile($url);
+ if ($oprofile) {
+ continue;
+ }
+ } catch (Exception $e) {
+ $this->log(LOG_ERR, "Profile check failed: " . $e->getMessage());
+ }
+ }
+ }
if (empty($oprofile)) {
-
- $this->log(LOG_INFO, "No Ostatus_profile found for address '$webfinger'");
-
+ $this->log(LOG_INFO, "No Ostatus_profile found for address '$target'");
} else {
- $this->log(LOG_INFO, "Ostatus_profile found for address '$webfinger'");
+ $this->log(LOG_INFO, "Ostatus_profile found for address '$target'");
if ($oprofile->isGroup()) {
continue;
@@ -261,7 +292,7 @@ class OStatusPlugin extends Plugin
}
}
$mentions[] = array('mentioned' => array($profile),
- 'text' => $wmatch[0],
+ 'text' => $target,
'position' => $pos,
'url' => $profile->profileurl);
}
diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php
index aae22f868..f45e6a8d1 100644
--- a/plugins/OStatus/actions/ostatussub.php
+++ b/plugins/OStatus/actions/ostatussub.php
@@ -332,6 +332,7 @@ class OStatusSubAction extends Action
if ($this->oprofile->isGroup()) {
$group = $this->oprofile->localGroup();
if ($user->isMember($group)) {
+ // TRANS: OStatus remote group subscription dialog error.
$this->showForm(_m('Already a member!'));
return;
}
@@ -341,18 +342,22 @@ class OStatusSubAction extends Action
Event::handle('EndJoinGroup', array($group, $user));
$this->successGroup();
} else {
+ // TRANS: OStatus remote group subscription dialog error.
$this->showForm(_m('Remote group join failed!'));
}
} else {
+ // TRANS: OStatus remote group subscription dialog error.
$this->showForm(_m('Remote group join aborted!'));
}
} else {
$local = $this->oprofile->localProfile();
if ($user->isSubscribed($local)) {
+ // TRANS: OStatus remote subscription dialog error.
$this->showForm(_m('Already subscribed!'));
} elseif ($this->oprofile->subscribeLocalToRemote($user)) {
$this->successUser();
} else {
+ // TRANS: OStatus remote subscription dialog error.
$this->showForm(_m('Remote subscription failed!'));
}
}
@@ -450,6 +455,7 @@ class OStatusSubAction extends Action
function title()
{
+ // TRANS: Page title for OStatus remote subscription form
return _m('Authorize subscription');
}
diff --git a/plugins/OStatus/actions/pushhub.php b/plugins/OStatus/actions/pushhub.php
index f33690bc4..842d65e7d 100644
--- a/plugins/OStatus/actions/pushhub.php
+++ b/plugins/OStatus/actions/pushhub.php
@@ -104,7 +104,7 @@ class PushHubAction extends Action
throw new ClientException("Invalid hub.secret $secret; must be under 200 bytes.");
}
- $sub = HubSub::staticGet($sub->topic, $sub->callback);
+ $sub = HubSub::staticGet($topic, $callback);
if (!$sub) {
// Creating a new one!
$sub = new HubSub();
diff --git a/plugins/OStatus/classes/HubSub.php b/plugins/OStatus/classes/HubSub.php
index e599d83a9..3120a70f9 100644
--- a/plugins/OStatus/classes/HubSub.php
+++ b/plugins/OStatus/classes/HubSub.php
@@ -260,9 +260,15 @@ class HubSub extends Memcached_DataObject
$retries = intval(common_config('ostatus', 'hub_retries'));
}
- $data = array('sub' => clone($this),
+ // We dare not clone() as when the clone is discarded it'll
+ // destroy the result data for the parent query.
+ // @fixme use clone() again when it's safe to copy an
+ // individual item from a multi-item query again.
+ $sub = HubSub::staticGet($this->topic, $this->callback);
+ $data = array('sub' => $sub,
'atom' => $atom,
'retries' => $retries);
+ common_log(LOG_INFO, "Queuing PuSH: $this->topic to $this->callback");
$qm = QueueManager::get();
$qm->enqueue($data, 'hubout');
}
diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php
index 96900d876..5a46aeeb6 100644
--- a/plugins/OStatus/classes/Magicsig.php
+++ b/plugins/OStatus/classes/Magicsig.php
@@ -146,8 +146,10 @@ class Magicsig extends Memcached_DataObject
$mod = base64_url_decode($matches[1]);
$exp = base64_url_decode($matches[2]);
- if ($matches[4]) {
+ if (!empty($matches[4])) {
$private_exp = base64_url_decode($matches[4]);
+ } else {
+ $private_exp = false;
}
$params['public_key'] = new Crypt_RSA_KEY($mod, $exp, 'public');
diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php
index 7b1aec76b..a33e95d93 100644
--- a/plugins/OStatus/classes/Ostatus_profile.php
+++ b/plugins/OStatus/classes/Ostatus_profile.php
@@ -698,7 +698,7 @@ class Ostatus_profile extends Memcached_DataObject
{
// Get the canonical feed URI and check it
$discover = new FeedDiscovery();
- if ($hints['feedurl']) {
+ if (isset($hints['feedurl'])) {
$feeduri = $hints['feedurl'];
$feeduri = $discover->discoverFromFeedURL($feeduri);
} else {
@@ -1145,7 +1145,7 @@ class Ostatus_profile extends Memcached_DataObject
if (!empty($poco)) {
$url = $poco->getPrimaryURL();
- if ($url->type == 'homepage') {
+ if ($url && $url->type == 'homepage') {
$homepage = $url->value;
}
}
diff --git a/plugins/OStatus/lib/discovery.php b/plugins/OStatus/lib/discovery.php
index 388df0a28..f8449b309 100644
--- a/plugins/OStatus/lib/discovery.php
+++ b/plugins/OStatus/lib/discovery.php
@@ -94,7 +94,7 @@ class Discovery
$links = call_user_func(array($class, 'discover'), $uri);
if ($link = Discovery::getService($links, Discovery::LRDD_REL)) {
// Load the LRDD XRD
- if ($link['template']) {
+ if (!empty($link['template'])) {
$xrd_uri = Discovery::applyTemplate($link['template'], $uri);
} else {
$xrd_uri = $link['href'];
diff --git a/plugins/OStatus/lib/xrd.php b/plugins/OStatus/lib/xrd.php
index 16d27f8eb..85df26c54 100644
--- a/plugins/OStatus/lib/xrd.php
+++ b/plugins/OStatus/lib/xrd.php
@@ -53,17 +53,22 @@ class XRD
$xrd = new XRD();
$dom = new DOMDocument();
- $dom->loadXML($xml);
+ if (!$dom->loadXML($xml)) {
+ throw new Exception("Invalid XML");
+ }
$xrd_element = $dom->getElementsByTagName('XRD')->item(0);
// Check for host-meta host
- $host = $xrd_element->getElementsByTagName('Host')->item(0)->nodeValue;
+ $host = $xrd_element->getElementsByTagName('Host')->item(0);
if ($host) {
- $xrd->host = $host;
+ $xrd->host = $host->nodeValue;
}
// Loop through other elements
foreach ($xrd_element->childNodes as $node) {
+ if (!($node instanceof DOMElement)) {
+ continue;
+ }
switch ($node->tagName) {
case 'Expires':
$xrd->expires = $node->nodeValue;
@@ -156,20 +161,20 @@ class XRD
function saveLink($doc, $link)
{
$link_element = $doc->createElement('Link');
- if ($link['rel']) {
+ if (!empty($link['rel'])) {
$link_element->setAttribute('rel', $link['rel']);
}
- if ($link['type']) {
+ if (!empty($link['type'])) {
$link_element->setAttribute('type', $link['type']);
}
- if ($link['href']) {
+ if (!empty($link['href'])) {
$link_element->setAttribute('href', $link['href']);
}
- if ($link['template']) {
+ if (!empty($link['template'])) {
$link_element->setAttribute('template', $link['template']);
}
- if (is_array($link['title'])) {
+ if (!empty($link['title']) && is_array($link['title'])) {
foreach($link['title'] as $title) {
$title = $doc->createElement('Title', $title);
$link_element->appendChild($title);
diff --git a/plugins/OStatus/locale/OStatus.po b/plugins/OStatus/locale/OStatus.po
index dedc018e3..ee19cf3db 100644
--- a/plugins/OStatus/locale/OStatus.po
+++ b/plugins/OStatus/locale/OStatus.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-12-07 20:38-0800\n"
+"POT-Creation-Date: 2010-03-01 14:08-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,89 +16,297 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: tests/gettext-speedtest.php:57 FeedSubPlugin.php:76
-msgid "Feeds"
+#: actions/groupsalmon.php:51
+msgid "Can't accept remote posts for a remote group."
+msgstr ""
+
+#: actions/groupsalmon.php:123
+msgid "Can't read profile to set up group membership."
msgstr ""
-#: FeedSubPlugin.php:77
-msgid "Feed subscription options"
+#: actions/groupsalmon.php:126 actions/groupsalmon.php:169
+msgid "Groups can't join groups."
msgstr ""
-#: feedmunger.php:215
+#: actions/groupsalmon.php:153
#, php-format
-msgid "New post: \"%1$s\" %2$s"
+msgid "Could not join remote user %1$s to group %2$s."
msgstr ""
-#: actions/feedsubsettings.php:41
-msgid "Feed subscriptions"
+#: actions/groupsalmon.php:166
+msgid "Can't read profile to cancel group membership."
msgstr ""
-#: actions/feedsubsettings.php:52
-msgid ""
-"You can subscribe to feeds from other sites; updates will appear in your "
-"personal timeline."
+#: actions/groupsalmon.php:182
+#, php-format
+msgid "Could not remove remote user %1$s from group %2$s."
+msgstr ""
+
+#: actions/ostatusinit.php:40
+msgid "You can use the local subscription!"
+msgstr ""
+
+#: actions/ostatusinit.php:61
+msgid "There was a problem with your session token. Try again, please."
+msgstr ""
+
+#: actions/ostatusinit.php:79 actions/ostatussub.php:439
+msgid "Subscribe to user"
+msgstr ""
+
+#: actions/ostatusinit.php:97
+#, php-format
+msgid "Subscribe to %s"
msgstr ""
-#: actions/feedsubsettings.php:96
+#: actions/ostatusinit.php:102
+msgid "User nickname"
+msgstr ""
+
+#: actions/ostatusinit.php:103
+msgid "Nickname of the user you want to follow"
+msgstr ""
+
+#: actions/ostatusinit.php:106
+msgid "Profile Account"
+msgstr ""
+
+#: actions/ostatusinit.php:107
+msgid "Your account id (i.e. user@identi.ca)"
+msgstr ""
+
+#: actions/ostatusinit.php:110 actions/ostatussub.php:115
+#: OStatusPlugin.php:205
msgid "Subscribe"
msgstr ""
-#: actions/feedsubsettings.php:98
+#: actions/ostatusinit.php:128
+msgid "Must provide a remote profile."
+msgstr ""
+
+#: actions/ostatusinit.php:138
+msgid "Couldn't look up OStatus account profile."
+msgstr ""
+
+#: actions/ostatusinit.php:153
+msgid "Couldn't confirm remote profile address."
+msgstr ""
+
+#: actions/ostatusinit.php:171
+msgid "OStatus Connect"
+msgstr ""
+
+#: actions/ostatussub.php:68
+msgid "Address or profile URL"
+msgstr ""
+
+#: actions/ostatussub.php:70
+msgid "Enter the profile URL of a PubSubHubbub-enabled feed"
+msgstr ""
+
+#: actions/ostatussub.php:74
msgid "Continue"
msgstr ""
-#: actions/feedsubsettings.php:151
-msgid "Empty feed URL!"
+#: actions/ostatussub.php:112 OStatusPlugin.php:503
+msgid "Join"
+msgstr ""
+
+#: actions/ostatussub.php:113
+msgid "Join this group"
+msgstr ""
+
+#: actions/ostatussub.php:116
+msgid "Subscribe to this user"
+msgstr ""
+
+#: actions/ostatussub.php:137
+msgid "You are already subscribed to this user."
+msgstr ""
+
+#: actions/ostatussub.php:165
+msgid "You are already a member of this group."
msgstr ""
-#: actions/feedsubsettings.php:161
+#: actions/ostatussub.php:286
+msgid "Empty remote profile URL!"
+msgstr ""
+
+#: actions/ostatussub.php:297
+msgid "Invalid address format."
+msgstr ""
+
+#: actions/ostatussub.php:302
msgid "Invalid URL or could not reach server."
msgstr ""
-#: actions/feedsubsettings.php:164
+#: actions/ostatussub.php:304
msgid "Cannot read feed; server returned error."
msgstr ""
-#: actions/feedsubsettings.php:167
+#: actions/ostatussub.php:306
msgid "Cannot read feed; server returned an empty page."
msgstr ""
-#: actions/feedsubsettings.php:170
+#: actions/ostatussub.php:308
msgid "Bad HTML, could not find feed link."
msgstr ""
-#: actions/feedsubsettings.php:173
+#: actions/ostatussub.php:310
msgid "Could not find a feed linked from this URL."
msgstr ""
-#: actions/feedsubsettings.php:176
+#: actions/ostatussub.php:312
msgid "Not a recognized feed type."
msgstr ""
-#: actions/feedsubsettings.php:180
-msgid "Bad feed URL."
+#: actions/ostatussub.php:315
+#, php-format
+msgid "Bad feed URL: %s %s"
+msgstr ""
+
+#. TRANS: OStatus remote group subscription dialog error.
+#: actions/ostatussub.php:336
+msgid "Already a member!"
msgstr ""
-#: actions/feedsubsettings.php:188
-msgid "Feed is not PuSH-enabled; cannot subscribe."
+#. TRANS: OStatus remote group subscription dialog error.
+#: actions/ostatussub.php:346
+msgid "Remote group join failed!"
msgstr ""
-#: actions/feedsubsettings.php:208
-msgid "Feed subscription failed! Bad response from hub."
+#. TRANS: OStatus remote group subscription dialog error.
+#: actions/ostatussub.php:350
+msgid "Remote group join aborted!"
msgstr ""
-#: actions/feedsubsettings.php:218
+#. TRANS: OStatus remote subscription dialog error.
+#: actions/ostatussub.php:356
msgid "Already subscribed!"
msgstr ""
-#: actions/feedsubsettings.php:220
-msgid "Feed subscribed!"
+#. TRANS: OStatus remote subscription dialog error.
+#: actions/ostatussub.php:361
+msgid "Remote subscription failed!"
msgstr ""
-#: actions/feedsubsettings.php:222
-msgid "Feed subscription failed!"
+#. TRANS: Page title for OStatus remote subscription form
+#: actions/ostatussub.php:459
+msgid "Authorize subscription"
+msgstr ""
+
+#: actions/ostatussub.php:470
+msgid ""
+"You can subscribe to users from other supported sites. Paste their address "
+"or profile URI below:"
+msgstr ""
+
+#: classes/Ostatus_profile.php:789
+#, php-format
+msgid "Tried to update avatar for unsaved remote profile %s"
msgstr ""
-#: actions/feedsubsettings.php:231
-msgid "Previewing feed:"
+#: classes/Ostatus_profile.php:797
+#, php-format
+msgid "Unable to fetch avatar from %s"
+msgstr ""
+
+#: lib/salmonaction.php:41
+msgid "This method requires a POST."
+msgstr ""
+
+#: lib/salmonaction.php:45
+msgid "Salmon requires application/magic-envelope+xml"
+msgstr ""
+
+#: lib/salmonaction.php:55
+msgid "Salmon signature verification failed."
+msgstr ""
+
+#: lib/salmonaction.php:66
+msgid "Salmon post must be an Atom entry."
+msgstr ""
+
+#: lib/salmonaction.php:114
+msgid "Unrecognized activity type."
+msgstr ""
+
+#: lib/salmonaction.php:122
+msgid "This target doesn't understand posts."
+msgstr ""
+
+#: lib/salmonaction.php:127
+msgid "This target doesn't understand follows."
+msgstr ""
+
+#: lib/salmonaction.php:132
+msgid "This target doesn't understand unfollows."
+msgstr ""
+
+#: lib/salmonaction.php:137
+msgid "This target doesn't understand favorites."
+msgstr ""
+
+#: lib/salmonaction.php:142
+msgid "This target doesn't understand unfavorites."
+msgstr ""
+
+#: lib/salmonaction.php:147
+msgid "This target doesn't understand share events."
+msgstr ""
+
+#: lib/salmonaction.php:152
+msgid "This target doesn't understand joins."
+msgstr ""
+
+#: lib/salmonaction.php:157
+msgid "This target doesn't understand leave events."
+msgstr ""
+
+#: OStatusPlugin.php:319
+#, php-format
+msgid "Sent from %s via OStatus"
+msgstr ""
+
+#: OStatusPlugin.php:371
+msgid "Could not set up remote subscription."
+msgstr ""
+
+#: OStatusPlugin.php:487
+msgid "Could not set up remote group membership."
+msgstr ""
+
+#: OStatusPlugin.php:504
+#, php-format
+msgid "%s has joined group %s."
+msgstr ""
+
+#: OStatusPlugin.php:512
+msgid "Failed joining remote group."
+msgstr ""
+
+#: OStatusPlugin.php:553
+msgid "Leave"
+msgstr ""
+
+#: OStatusPlugin.php:554
+#, php-format
+msgid "%s has left group %s."
+msgstr ""
+
+#: OStatusPlugin.php:685
+msgid "Subscribe to remote user"
+msgstr ""
+
+#: OStatusPlugin.php:726
+msgid "Profile update"
+msgstr ""
+
+#: OStatusPlugin.php:727
+#, php-format
+msgid "%s has updated their profile page."
+msgstr ""
+
+#: tests/gettext-speedtest.php:57
+msgid "Feeds"
msgstr ""
diff --git a/plugins/OStatus/scripts/updateostatus.php b/plugins/OStatus/scripts/updateostatus.php
new file mode 100644
index 000000000..d553a7d62
--- /dev/null
+++ b/plugins/OStatus/scripts/updateostatus.php
@@ -0,0 +1,127 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
+
+$shortoptions = 'i:n:a';
+$longoptions = array('id=', 'nickname=', 'all');
+
+$helptext = <<<END_OF_UPDATEOSTATUS_HELP
+updateostatus.php [options]
+update the OMB subscriptions of a user to use OStatus if possible
+
+ -i --id ID of user to update
+ -n --nickname nickname of the user to update
+ -a --all update all
+
+END_OF_UPDATEOSTATUS_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+try {
+ $user = null;
+
+ if (have_option('i', 'id')) {
+ $id = get_option_value('i', 'id');
+ $user = User::staticGet('id', $id);
+ if (empty($user)) {
+ throw new Exception("Can't find user with id '$id'.");
+ }
+ updateProfileURL($user);
+ } else if (have_option('n', 'nickname')) {
+ $nickname = get_option_value('n', 'nickname');
+ $user = User::staticGet('nickname', $nickname);
+ if (empty($user)) {
+ throw new Exception("Can't find user with nickname '$nickname'");
+ }
+ updateProfileURL($user);
+ } else if (have_option('a', 'all')) {
+ $user = new User();
+ if ($user->find()) {
+ while ($user->fetch()) {
+ updateOStatus($user);
+ }
+ }
+ } else {
+ show_help();
+ exit(1);
+ }
+} catch (Exception $e) {
+ print $e->getMessage()."\n";
+ exit(1);
+}
+
+function updateOStatus($user)
+{
+ if (!have_option('q', 'quiet')) {
+ echo "{$user->nickname}...";
+ }
+
+ $up = $user->getProfile();
+
+ $sp = $user->getSubscriptions();
+
+ $rps = array();
+
+ while ($sp->fetch()) {
+ $remote = Remote_profile::staticGet('id', $sp->id);
+
+ if (!empty($remote)) {
+ $rps[] = clone($sp);
+ }
+ }
+
+ if (!have_option('q', 'quiet')) {
+ echo count($rps) . "\n";
+ }
+
+ foreach ($rps as $rp) {
+ try {
+ if (!have_option('q', 'quiet')) {
+ echo "Checking {$rp->nickname}...";
+ }
+
+ $op = Ostatus_profile::ensureProfile($rp->profileurl);
+
+ if (empty($op)) {
+ echo "can't convert.\n";
+ continue;
+ } else {
+ if (!have_option('q', 'quiet')) {
+ echo "Converting...";
+ }
+ Subscription::cancel($up, $rp);
+ Subscription::start($up, $op->localProfile());
+ if (!have_option('q', 'quiet')) {
+ echo "done.\n";
+ }
+ }
+
+ } catch (Exception $e) {
+ if (!have_option('q', 'quiet')) {
+ echo "fail.\n";
+ }
+ continue;
+ common_log(LOG_WARNING, "Couldn't convert OMB subscription (" . $up->nickname . ", " . $rp->nickname .
+ ") to OStatus: " . $e->getMessage());
+ continue;
+ }
+ }
+}
diff --git a/plugins/RegisterThrottle/RegisterThrottlePlugin.php b/plugins/RegisterThrottle/RegisterThrottlePlugin.php
new file mode 100644
index 000000000..05709b780
--- /dev/null
+++ b/plugins/RegisterThrottle/RegisterThrottlePlugin.php
@@ -0,0 +1,249 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Throttle registration by IP address
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Spam
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+/**
+ * Throttle registration by IP address
+ *
+ * We a) record IP address of registrants and b) throttle registrations.
+ *
+ * @category Spam
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class RegisterThrottlePlugin extends Plugin
+{
+ /**
+ * Array of time spans in seconds to limits.
+ *
+ * Default is 3 registrations per hour, 5 per day, 10 per week.
+ */
+
+ public $regLimits = array(604800 => 10, // per week
+ 86400 => 5, // per day
+ 3600 => 3); // per hour
+
+ /**
+ * Database schema setup
+ *
+ * We store user registrations in a table registration_ip.
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+
+ function onCheckSchema()
+ {
+ $schema = Schema::get();
+
+ // For storing user-submitted flags on profiles
+
+ $schema->ensureTable('registration_ip',
+ array(new ColumnDef('user_id', 'integer', null,
+ false, 'PRI'),
+ new ColumnDef('ipaddress', 'varchar', 15, false, 'MUL'),
+ new ColumnDef('created', 'timestamp', null, false, 'MUL')));
+
+ return true;
+ }
+
+ /**
+ * Load related modules when needed
+ *
+ * @param string $cls Name of the class to be loaded
+ *
+ * @return boolean hook value; true means continue processing, false means stop.
+ */
+
+ function onAutoload($cls)
+ {
+ $dir = dirname(__FILE__);
+
+ switch ($cls)
+ {
+ case 'Registration_ip':
+ include_once $dir . '/'.$cls.'.php';
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Called when someone tries to register.
+ *
+ * We check the IP here to determine if it goes over any of our
+ * configured limits.
+ *
+ * @param Action $action Action that is being executed
+ *
+ * @return boolean hook value
+ *
+ */
+
+ function onStartRegistrationTry($action)
+ {
+ $ipaddress = $this->_getIpAddress();
+
+ if (empty($ipaddress)) {
+ throw new ServerException(_m('Cannot find IP address.'));
+ }
+
+ foreach ($this->regLimits as $seconds => $limit) {
+
+ $this->debug("Checking $seconds ($limit)");
+
+ $reg = $this->_getNthReg($ipaddress, $limit);
+
+ if (!empty($reg)) {
+ $this->debug("Got a {$limit}th registration.");
+ $regtime = strtotime($reg->created);
+ $now = time();
+ $this->debug("Comparing {$regtime} to {$now}");
+ if ($now - $regtime < $seconds) {
+ throw new Exception(_("Too many registrations. Take a break and try again later."));
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Called after someone registers.
+ *
+ * We record the successful registration and IP address.
+ *
+ * @param Action $action Action that is being executed
+ *
+ * @return boolean hook value
+ *
+ */
+
+ function onEndRegistrationTry($action)
+ {
+ $ipaddress = $this->_getIpAddress();
+
+ if (empty($ipaddress)) {
+ throw new ServerException(_m('Cannot find IP address.'));
+ }
+
+ $user = common_current_user();
+
+ if (empty($user)) {
+ throw new ServerException(_m('Cannot find user after successful registration.'));
+ }
+
+ $reg = new Registration_ip();
+
+ $reg->user_id = $user->id;
+ $reg->ipaddress = $ipaddress;
+
+ $result = $reg->insert();
+
+ if (!$result) {
+ common_log_db_error($reg, 'INSERT', __FILE__);
+ // @todo throw an exception?
+ }
+
+ return true;
+ }
+
+ /**
+ * Check the version of the plugin.
+ *
+ * @param array &$versions Version array.
+ *
+ * @return boolean hook value
+ */
+
+ function onPluginVersion(&$versions)
+ {
+ $versions[] = array('name' => 'RegisterThrottle',
+ 'version' => STATUSNET_VERSION,
+ 'author' => 'Evan Prodromou',
+ 'homepage' => 'http://status.net/wiki/Plugin:RegisterThrottle',
+ 'description' =>
+ _m('Throttles excessive registration from a single IP.'));
+ return true;
+ }
+
+ /**
+ * Gets the current IP address.
+ *
+ * @return string IP address or null if not found.
+ */
+
+ private function _getIpAddress()
+ {
+ $keys = array('HTTP_X_FORWARDED_FOR',
+ 'CLIENT-IP',
+ 'REMOTE_ADDR');
+
+ foreach ($keys as $k) {
+ if (!empty($_SERVER[$k])) {
+ return $_SERVER[$k];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the Nth registration with the given IP address.
+ *
+ * @param string $ipaddress Address to key on
+ * @param integer $n Nth address
+ *
+ * @return Registration_ip nth registration or null if not found.
+ */
+
+ private function _getNthReg($ipaddress, $n)
+ {
+ $reg = new Registration_ip();
+
+ $reg->ipaddress = $ipaddress;
+
+ $reg->orderBy('created DESC');
+ $reg->limit($n - 1, 1);
+
+ if ($reg->find(true)) {
+ return $reg;
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/plugins/RegisterThrottle/Registration_ip.php b/plugins/RegisterThrottle/Registration_ip.php
new file mode 100644
index 000000000..7e61d089e
--- /dev/null
+++ b/plugins/RegisterThrottle/Registration_ip.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Data class for storing IP addresses of new registrants.
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for storing IP addresses of new registrants.
+ *
+ * @category Spam
+ * @package StatusNet
+ * @author Evan Prodromou <evan@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link http://status.net/
+ */
+
+class Registration_ip extends Memcached_DataObject
+{
+ public $__table = 'registration_ip'; // table name
+ public $user_id; // int(4) primary_key not_null
+ public $ipaddress; // varchar(15)
+ public $created; // timestamp
+
+ /**
+ * Get an instance by key
+ *
+ * @param string $k Key to use to lookup (usually 'user_id' for this class)
+ * @param mixed $v Value to lookup
+ *
+ * @return User_greeting_count object found, or null for no hits
+ *
+ */
+
+ function staticGet($k, $v=null)
+ {
+ return Memcached_DataObject::staticGet('Registration_ip', $k, $v);
+ }
+
+ /**
+ * return table definition for DB_DataObject
+ *
+ * @return array array of column definitions
+ */
+
+ function table()
+ {
+ return array('user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
+ 'ipaddress' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
+ 'created' => DB_DATAOBJECT_MYSQLTIMESTAMP + DB_DATAOBJECT_NOTNULL);
+ }
+
+ /**
+ * return key definitions for DB_DataObject
+ *
+ * DB_DataObject needs to know about keys that the table has; this function
+ * defines them.
+ *
+ * @return array key definitions
+ */
+
+ function keys()
+ {
+ return array('user_id' => 'K');
+ }
+
+ /**
+ * return key definitions for Memcached_DataObject
+ *
+ * Our caching system uses the same key definitions, but uses a different
+ * method to get them.
+ *
+ * @return array key definitions
+ */
+
+ function keyTypes()
+ {
+ return $this->keys();
+ }
+
+ /**
+ * Magic formula for non-autoincrementing integer primary keys
+ *
+ * If a table has a single integer column as its primary key, DB_DataObject
+ * assumes that the column is auto-incrementing and makes a sequence table
+ * to do this incrementation. Since we don't need this for our class, we
+ * overload this method and return the magic formula that DB_DataObject needs.
+ *
+ * @return array magic three-false array that stops auto-incrementing.
+ */
+
+ function sequenceKey()
+ {
+ return array(false, false, false);
+ }
+}
diff --git a/plugins/TwitterBridge/README b/plugins/TwitterBridge/README
index d3bcda598..72278b32e 100644
--- a/plugins/TwitterBridge/README
+++ b/plugins/TwitterBridge/README
@@ -1,47 +1,89 @@
+Twitter Bridge Plugin
+=====================
+
This Twitter "bridge" plugin allows you to integrate your StatusNet
instance with Twitter. Installing it will allow your users to:
- - automatically post notices to thier Twitter accounts
+ - automatically post notices to their Twitter accounts
- automatically subscribe to other Twitter users who are also using
your StatusNet install, if possible (requires running a daemon)
- import their Twitter friends' tweets (requires running a daemon)
+ - allow users to authenticate using Twitter ('Sign in with Twitter')
Installation
------------
-To enable the plugin, add the following to your config.php:
-
- addPlugin("TwitterBridge");
-
-OAuth is used to to access protected resources on Twitter (as opposed to
-HTTP Basic Auth)*. To use Twitter bridging you will need to register
-your instance of StatusNet as an application on Twitter
-(http://twitter.com/apps), and update the following variables in your
-config.php with the consumer key and secret Twitter generates for you:
-
- $config['twitter']['consumer_key'] = 'YOURKEY';
- $config['twitter']['consumer_secret'] = 'YOURSECRET';
+OAuth 1.0a (http://oauth.net) is used to to access protected resources
+on Twitter (as opposed to HTTP Basic Auth)*. To use Twitter bridging
+you will need to register your instance of StatusNet as an application
+on Twitter (http://twitter.com/apps). During the application
+registration process your application will be assigned a "consumer" key
+and secret, which the plugin will use to make OAuth requests to Twitter.
+You can either pass the consumer key and secret in when you enable the
+plugin, or set it using the Twitter administration panel.
When registering your application with Twitter set the type to "Browser"
and your Callback URL to:
http://example.org/mublog/twitter/authorization
-The default access type should be, "Read & Write".
+(Change "example.org" to your site domain and "mublog" to your site
+path.)
+
+The default access type should be "Read & Write".
+
+To enable the plugin, add the following to your config.php:
+
+ addPlugin(
+ 'TwitterBridge',
+ array(
+ 'consumer_key' => 'YOUR_CONSUMER_KEY',
+ 'consumer_secret' => 'YOUR_CONSUMER_SECRET'
+ )
+ );
* Note: The plugin will still push notices to Twitter for users who
- have previously setup the Twitter bridge using their Twitter name and
- password under an older versions of StatusNet, but all new Twitter
+ have previously set up the Twitter bridge using their Twitter name and
+ password under an older version of StatusNet, but all new Twitter
bridge connections will use OAuth.
-Deamons
+Administration panel
+--------------------
+
+As of StatusNet 0.9.0 there is a new administration panel that allows
+you to configure Twitter bridge settings within StatusNet itself,
+instead of having to specify them manually in your config.php. To enable
+the administration panel, you will need to add it to the list of active
+administration panels. You can do this via your config.php. E.g.:
+
+ $config['admin']['panels'][] = 'twitter';
+
+And to access it, you'll need to use a user with the "administrator"
+role (see: scripts/userrole.php).
+
+Sign in with Twitter
+--------------------
+
+With 0.9.0, StatusNet optionally allows users to register and
+authenticate using their Twitter credentials via the "Sign in with
+Twitter" pattern described here:
+
+ http://apiwiki.twitter.com/Sign-in-with-Twitter
+
+The option is _on_ by default when you install the plugin, but it can
+disabled via the Twitter bridge administration panel, or by adding the
+following line to your config.php:
+
+ $config['twitter']['signin'] = false;
+
+Daemons
-------
-For friend syncing and importing notices running two additional daemon
-scripts is necessary (synctwitterfriends.php and
-twitterstatusfetcher.php).
+For friend syncing and importing Twitter tweets, running two
+additional daemon scripts is necessary: synctwitterfriends.php and
+twitterstatusfetcher.php.
-In the daemons subidrectory of the plugin are three scripts:
+In the daemons subdirectory of the plugin are three scripts:
* Twitter Friends Syncing (daemons/synctwitterfriends.php)
@@ -51,13 +93,13 @@ subscribe to "friends" (people they "follow") on Twitter who also have
accounts on your StatusNet system, and who have previously set up a link
for automatically posting notices to Twitter.
-The plugin will try to start this daemon when you run
-scripts/startdaemons.sh.
+The plugin will start this daemon when you run scripts/startdaemons.sh.
* Importing statuses from Twitter (daemons/twitterstatusfetcher.php)
-To allow your users to import their friends' Twitter statuses, you will
-need to enable the bidirectional Twitter bridge in your config.php:
+You can allow uses to enable importing of your friends' Twitter
+timelines either in the Twitter bridge administration panel or in your
+config.php using the following configuration line:
$config['twitterimport']['enabled'] = true;
@@ -66,8 +108,9 @@ other daemons when you run scripts/startdaemons.sh.
Additionally, you will want to set the integration source variable,
which will keep notices posted to Twitter via StatusNet from looping
-back. The integration source should be set to the name of your
-application, exactly as you specified it on the settings page for your
+back. You can do this in the Twitter bridge administration panel, or
+via config.php. The integration source should be set to the name of your
+application _exactly_ as you specified it on the settings page for your
StatusNet application on Twitter, e.g.:
$config['integration']['source'] = 'YourApp';
@@ -79,7 +122,9 @@ set up Twitter bridging.
It's not strictly necessary to run this queue handler, and sites that
haven't enabled queuing are still able to push notices to Twitter, but
-for larger sites and sites that wish to improve performance, this
-script allows notices to be sent "offline" via a separate process.
+for larger sites and sites that wish to improve performance the script
+allows notices to be sent "offline" via a separate process.
-The plugin will start this script when you run scripts/startdaemons.sh.
+StatusNet will automatically use the TwitterQueueHandler if you have
+enabled the queuing subsystem. See the "Queues and daemons" section of
+the main README file for more information about how to do that.
diff --git a/plugins/TwitterBridge/TwitterBridgePlugin.php b/plugins/TwitterBridge/TwitterBridgePlugin.php
index c7f57ffc7..6ce69d5e2 100644
--- a/plugins/TwitterBridge/TwitterBridgePlugin.php
+++ b/plugins/TwitterBridge/TwitterBridgePlugin.php
@@ -23,7 +23,7 @@
* @author Julien C <chaumond@gmail.com>
* @copyright 2009-2010 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://laconi.ca/
+ * @link http://status.net/
*/
if (!defined('STATUSNET')) {
@@ -32,8 +32,6 @@ if (!defined('STATUSNET')) {
require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
-define('TWITTERBRIDGEPLUGIN_VERSION', '0.9');
-
/**
* Plugin for sending and importing Twitter statuses
*
@@ -44,19 +42,41 @@ define('TWITTERBRIDGEPLUGIN_VERSION', '0.9');
* @author Zach Copley <zach@status.net>
* @author Julien C <chaumond@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://laconi.ca/
+ * @link http://status.net/
* @link http://twitter.com/
*/
class TwitterBridgePlugin extends Plugin
{
+
+ const VERSION = STATUSNET_VERSION;
+
/**
* Initializer for the plugin.
*/
- function __construct()
+ function initialize()
{
- parent::__construct();
+ // Allow the key and secret to be passed in
+ // Control panel will override
+
+ if (isset($this->consumer_key)) {
+ $key = common_config('twitter', 'consumer_key');
+ if (empty($key)) {
+ Config::save('twitter', 'consumer_key', $this->consumer_key);
+ }
+ }
+
+ if (isset($this->consumer_secret)) {
+ $secret = common_config('twitter', 'consumer_secret');
+ if (empty($secret)) {
+ Config::save(
+ 'twitter',
+ 'consumer_secret',
+ $this->consumer_secret
+ );
+ }
+ }
}
/**
@@ -71,10 +91,17 @@ class TwitterBridgePlugin extends Plugin
function onRouterInitialized($m)
{
- $m->connect('twitter/authorization',
- array('action' => 'twitterauthorization'));
+ $m->connect(
+ 'twitter/authorization',
+ array('action' => 'twitterauthorization')
+ );
$m->connect('settings/twitter', array('action' => 'twittersettings'));
- $m->connect('main/twitterlogin', array('action' => 'twitterlogin'));
+
+ if (common_config('twitter', 'signin')) {
+ $m->connect('main/twitterlogin', array('action' => 'twitterlogin'));
+ }
+
+ $m->connect('admin/twitter', array('action' => 'twitteradminpanel'));
return true;
}
@@ -88,13 +115,16 @@ class TwitterBridgePlugin extends Plugin
*/
function onEndLoginGroupNav(&$action)
{
-
$action_name = $action->trimmed('action');
- $action->menuItem(common_local_url('twitterlogin'),
- _('Twitter'),
- _('Login or register using Twitter'),
- 'twitterlogin' === $action_name);
+ if (common_config('twitter', 'signin')) {
+ $action->menuItem(
+ common_local_url('twitterlogin'),
+ _m('Twitter'),
+ _m('Login or register using Twitter'),
+ 'twitterlogin' === $action_name
+ );
+ }
return true;
}
@@ -110,10 +140,12 @@ class TwitterBridgePlugin extends Plugin
{
$action_name = $action->trimmed('action');
- $action->menuItem(common_local_url('twittersettings'),
- _m('Twitter'),
- _m('Twitter integration options'),
- $action_name === 'twittersettings');
+ $action->menuItem(
+ common_local_url('twittersettings'),
+ _m('Twitter'),
+ _m('Twitter integration options'),
+ $action_name === 'twittersettings'
+ );
return true;
}
@@ -132,6 +164,7 @@ class TwitterBridgePlugin extends Plugin
case 'TwittersettingsAction':
case 'TwitterauthorizationAction':
case 'TwitterloginAction':
+ case 'TwitteradminpanelAction':
include_once INSTALLDIR . '/plugins/TwitterBridge/' .
strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;
@@ -173,12 +206,18 @@ class TwitterBridgePlugin extends Plugin
*/
function onGetValidDaemons($daemons)
{
- array_push($daemons, INSTALLDIR .
- '/plugins/TwitterBridge/daemons/synctwitterfriends.php');
+ array_push(
+ $daemons,
+ INSTALLDIR
+ . '/plugins/TwitterBridge/daemons/synctwitterfriends.php'
+ );
if (common_config('twitterimport', 'enabled')) {
- array_push($daemons, INSTALLDIR
- . '/plugins/TwitterBridge/daemons/twitterstatusfetcher.php');
+ array_push(
+ $daemons,
+ INSTALLDIR
+ . '/plugins/TwitterBridge/daemons/twitterstatusfetcher.php'
+ );
}
return true;
@@ -197,17 +236,55 @@ class TwitterBridgePlugin extends Plugin
return true;
}
+ /**
+ * Add a Twitter tab to the admin panel
+ *
+ * @param Widget $nav Admin panel nav
+ *
+ * @return boolean hook value
+ */
+
+ function onEndAdminPanelNav($nav)
+ {
+ if (AdminPanelAction::canAdmin('twitter')) {
+
+ $action_name = $nav->action->trimmed('action');
+
+ $nav->out->menuItem(
+ common_local_url('twitteradminpanel'),
+ _m('Twitter'),
+ _m('Twitter bridge configuration'),
+ $action_name == 'twitteradminpanel',
+ 'nav_twitter_admin_panel'
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Plugin version data
+ *
+ * @param array &$versions array of version blocks
+ *
+ * @return boolean hook value
+ */
+
function onPluginVersion(&$versions)
{
- $versions[] = array('name' => 'TwitterBridge',
- 'version' => TWITTERBRIDGEPLUGIN_VERSION,
- 'author' => 'Zach Copley',
- 'homepage' => 'http://status.net/wiki/Plugin:TwitterBridge',
- 'rawdescription' =>
- _m('The Twitter "bridge" plugin allows you to integrate ' .
- 'your StatusNet instance with ' .
- '<a href="http://twitter.com/">Twitter</a>.'));
+ $versions[] = array(
+ 'name' => 'TwitterBridge',
+ 'version' => self::VERSION,
+ 'author' => 'Zach Copley, Julien C',
+ 'homepage' => 'http://status.net/wiki/Plugin:TwitterBridge',
+ 'rawdescription' => _m(
+ 'The Twitter "bridge" plugin allows you to integrate ' .
+ 'your StatusNet instance with ' .
+ '<a href="http://twitter.com/">Twitter</a>.'
+ )
+ );
return true;
}
}
+
diff --git a/plugins/TwitterBridge/twitteradminpanel.php b/plugins/TwitterBridge/twitteradminpanel.php
new file mode 100644
index 000000000..b22e6d99f
--- /dev/null
+++ b/plugins/TwitterBridge/twitteradminpanel.php
@@ -0,0 +1,280 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Twitter bridge 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);
+}
+
+/**
+ * Administer global Twitter bridge settings
+ *
+ * @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 TwitteradminpanelAction extends AdminPanelAction
+{
+ /**
+ * Returns the page title
+ *
+ * @return string page title
+ */
+
+ function title()
+ {
+ return _m('Twitter');
+ }
+
+ /**
+ * Instructions for using this form.
+ *
+ * @return string instructions
+ */
+
+ function getInstructions()
+ {
+ return _m('Twitter bridge settings');
+ }
+
+ /**
+ * Show the Twitter admin panel form
+ *
+ * @return void
+ */
+
+ function showForm()
+ {
+ $form = new TwitterAdminPanelForm($this);
+ $form->show();
+ return;
+ }
+
+ /**
+ * Save settings from the form
+ *
+ * @return void
+ */
+
+ function saveSettings()
+ {
+ static $settings = array(
+ 'twitter' => array('consumer_key', 'consumer_secret'),
+ 'integration' => array('source')
+ );
+
+ static $booleans = array(
+ 'twitter' => array('signin'),
+ 'twitterimport' => array('enabled')
+ );
+
+ $values = array();
+
+ foreach ($settings as $section => $parts) {
+ foreach ($parts as $setting) {
+ $values[$section][$setting]
+ = $this->trimmed($setting);
+ }
+ }
+
+ foreach ($booleans as $section => $parts) {
+ foreach ($parts as $setting) {
+ $values[$section][$setting]
+ = ($this->boolean($setting)) ? 1 : 0;
+ }
+ }
+
+ // 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]);
+ }
+ }
+
+ foreach ($booleans as $section => $parts) {
+ foreach ($parts as $setting) {
+ Config::save($section, $setting, $values[$section][$setting]);
+ }
+ }
+
+ $config->query('COMMIT');
+
+ return;
+ }
+
+ function validate(&$values)
+ {
+ // Validate consumer key and secret (can't be too long)
+
+ if (mb_strlen($values['twitter']['consumer_key']) > 255) {
+ $this->clientError(
+ _m("Invalid consumer key. Max length is 255 characters.")
+ );
+ }
+
+ if (mb_strlen($values['twitter']['consumer_secret']) > 255) {
+ $this->clientError(
+ _m("Invalid consumer secret. Max length is 255 characters.")
+ );
+ }
+ }
+}
+
+class TwitterAdminPanelForm extends AdminForm
+{
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ return 'twitteradminpanel';
+ }
+
+ /**
+ * 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('twitteradminpanel');
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->out->elementStart(
+ 'fieldset',
+ array('id' => 'settings_twitter-application')
+ );
+ $this->out->element('legend', null, _m('Twitter application settings'));
+ $this->out->elementStart('ul', 'form_data');
+
+ $this->li();
+ $this->input(
+ 'consumer_key',
+ _m('Consumer key'),
+ _m('Consumer key assigned by Twitter'),
+ 'twitter'
+ );
+ $this->unli();
+
+ $this->li();
+ $this->input(
+ 'consumer_secret',
+ _m('Consumer secret'),
+ _m('Consumer secret assigned by Twitter'),
+ 'twitter'
+ );
+ $this->unli();
+
+ $this->li();
+ $this->input(
+ 'source',
+ _m('Integration source'),
+ _m('Name of your Twitter application'),
+ 'integration'
+ );
+ $this->unli();
+
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('fieldset');
+
+ $this->out->elementStart(
+ 'fieldset',
+ array('id' => 'settings_twitter-options')
+ );
+ $this->out->element('legend', null, _m('Options'));
+
+ $this->out->elementStart('ul', 'form_data');
+
+ $this->li();
+
+ $this->out->checkbox(
+ 'signin', _m('Enable "Sign-in with Twitter"'),
+ (bool) $this->value('signin', 'twitter'),
+ _m('Allow users to login with their Twitter credentials')
+ );
+ $this->unli();
+
+ $this->li();
+ $this->out->checkbox(
+ 'enabled', _m('Enable Twitter import'),
+ (bool) $this->value('enabled', 'twitterimport'),
+ _m('Allow users to import their Twitter friends\' timelines')
+ );
+ $this->unli();
+
+ $this->out->elementEnd('ul');
+
+ $this->out->elementEnd('fieldset');
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->out->submit('submit', _('Save'), 'submit', null, _('Save Twitter settings'));
+ }
+}
diff --git a/plugins/TwitterBridge/twitterauthorization.php b/plugins/TwitterBridge/twitterauthorization.php
index cabf69d7a..c93f6666b 100644
--- a/plugins/TwitterBridge/twitterauthorization.php
+++ b/plugins/TwitterBridge/twitterauthorization.php
@@ -47,7 +47,7 @@ require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
* @author Zach Copley <zach@status.net>
* @author Julien C <chaumond@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://laconi.ca/
+ * @link http://status.net/
*
*/
class TwitterauthorizationAction extends Action
diff --git a/scripts/update_po_templates.php b/scripts/update_po_templates.php
index 61a6ac783..0bfa62a22 100755
--- a/scripts/update_po_templates.php
+++ b/scripts/update_po_templates.php
@@ -36,7 +36,11 @@ xgettext \
--default-domain=$domain \
--output=locale/$domain.po \
--language=PHP \
- --keyword="_m:1" \
+ --add-comments=TRANS \
+ --keyword="_m:1,1t" \
+ --keyword="_m:1c,2,2t" \
+ --keyword="_m:1,2,3t" \
+ --keyword="_m:1c,2,3,4t" \
--keyword="pgettext:1c,2" \
--keyword="npgettext:1c,2,3" \
actions/*.php \
@@ -62,6 +66,7 @@ xgettext \
--default-domain=$domain \
--output=locale/$domain.po \
--language=PHP \
+ --add-comments=TRANS \
--keyword='' \
--keyword="_m:1,1t" \
--keyword="_m:1c,2,2t" \
diff --git a/theme/base/css/display.css b/theme/base/css/display.css
index 52f97f6b1..f32c57ea4 100644
--- a/theme/base/css/display.css
+++ b/theme/base/css/display.css
@@ -799,8 +799,8 @@ list-style-type:none;
display:inline;
}
.entity_tags li {
-float:left;
-margin-right:11px;
+display:inline;
+margin-right:7px;
}
.aside .section {