From 31ecc8619830746cadada92b5d7ab83c529bf3b3 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Sat, 27 Feb 2010 16:59:33 +0100 Subject: [PATCH 01/42] Fixes entity_tags alignment --- theme/base/css/display.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 52f97f6b12..f32c57ea45 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 { From babca69f67d6aa2f7d491f84a4982b772056ebcb Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 27 Feb 2010 11:04:24 -0500 Subject: [PATCH 02/42] add bugfix to version number. --- lib/common.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common.php b/lib/common.php index 2dbe3b3c53..546f6bbe4b 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'); From 55f27feb78a8ae5c6a829623f89bf3f89016aa08 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 27 Feb 2010 15:03:17 -0500 Subject: [PATCH 03/42] Plugin to restrict too many registrations from one IP We throttle registrations by IP. We record IP address of each registration, and if too many registrations have been done by the same IP address in the time interval, we reject the registration. --- .../RegisterThrottlePlugin.php | 249 ++++++++++++++++++ plugins/RegisterThrottle/Registration_ip.php | 124 +++++++++ 2 files changed, 373 insertions(+) create mode 100644 plugins/RegisterThrottle/RegisterThrottlePlugin.php create mode 100644 plugins/RegisterThrottle/Registration_ip.php diff --git a/plugins/RegisterThrottle/RegisterThrottlePlugin.php b/plugins/RegisterThrottle/RegisterThrottlePlugin.php new file mode 100644 index 0000000000..05709b7807 --- /dev/null +++ b/plugins/RegisterThrottle/RegisterThrottlePlugin.php @@ -0,0 +1,249 @@ +. + * + * @category Spam + * @package StatusNet + * @author Evan Prodromou + * @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 + * @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 0000000000..7e61d089e7 --- /dev/null +++ b/plugins/RegisterThrottle/Registration_ip.php @@ -0,0 +1,124 @@ + + * @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 . + */ + +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 + * @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); + } +} From 4d9daf21493e75354190667e5c1ab3140b46dee1 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 27 Feb 2010 16:06:46 -0500 Subject: [PATCH 04/42] Use notice for context when deciding who @nickname refers to In a federated system, "@nickname" is insufficient to uniquely identify a user. However, it's a very convenient idiom. We need to guess from context who 'nickname' refers to. Previously, we were using the sender's profile (or what we knew about them) as the only context. So, we assumed that they'd be mentioning to someone they followed, or someone who followed them, or someone on their own server. Now, we include the notice information for context. We check to see if the notice is a reply to another notice, and if the author of the original notice has the nickname 'nickname', then the mention is probably for them. Alternately, if the original notice mentions someone with nickname 'nickname', then this notice is probably referring to _them_. Doing this kind of context sleuthing means we have to render the content very late in the notice-saving process. --- classes/Notice.php | 12 +++++------ lib/util.php | 51 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 2d02a9a19f..6614f3d558 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 diff --git a/lib/util.php b/lib/util.php index 32061ec04c..d12a7920d2 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)) { From 04c4facba9230f40726c5891dcac21d928fbb2ab Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 27 Feb 2010 16:30:38 -0500 Subject: [PATCH 05/42] fix call of common_find_mentions() in Notice::saveReplies() --- classes/Notice.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/classes/Notice.php b/classes/Notice.php index 6614f3d558..3702dbcfa8 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -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(); From 6ea9eb33d2767df26fb757e3b507dd2e7e7c7286 Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Mon, 1 Mar 2010 15:09:44 +0100 Subject: [PATCH 06/42] Update pot file --- locale/statusnet.po | 389 +++++++++++++++++++++++--------------------- 1 file changed, 202 insertions(+), 187 deletions(-) diff --git a/locale/statusnet.po b/locale/statusnet.po index cf44e2d3c9..8e14344978 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 \n" "Language-Team: LANGUAGE \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 - direct message to user\n" "get - get last notice from user\n" "whois - get profile info on user\n" -"lose - force user to stop following you\n" "fav - add user's last notice as a 'fave'\n" "fav # - add notice with the given id as a 'fave'\n" "repeat # - 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 "" From cac6d4234a4ef8fea1d487f44062ae06dd9c0c52 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 12:55:51 -0500 Subject: [PATCH 07/42] Script to convert OMB subscriptions to OStatus subscriptions --- plugins/OStatus/scripts/updateostatus.php | 102 ++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 plugins/OStatus/scripts/updateostatus.php diff --git a/plugins/OStatus/scripts/updateostatus.php b/plugins/OStatus/scripts/updateostatus.php new file mode 100644 index 0000000000..8b10817040 --- /dev/null +++ b/plugins/OStatus/scripts/updateostatus.php @@ -0,0 +1,102 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..')); + +$shortoptions = 'i:n:a'; +$longoptions = array('id=', 'nickname=', 'all'); + +$helptext = <<find()) { + while ($user->fetch()) { + updateOStatus($user); + } + } + } else { + show_help(); + exit(1); + } +} catch (Exception $e) { + print $e->getMessage()."\n"; + exit(1); +} + +function updateOStatus($user) +{ + $up = $user->getProfile(); + + $sp = $user->getSubscriptions(); + + $rps = array(); + + while ($sp->fetch()) { + $remote = Remote_profile::staticGet('id', $sp->id); + + if (!empty($remote)) { + $rps[] = clone($sp); + } + } + + foreach ($rps as $rp) { + try { + $op = Ostatus_profile::ensureProfile($rp->profileurl); + + if (!empty($op)) { + Subscription::cancel($up, $rp); + Subscription::start($up, $op->localProfile()); + } + + } catch (Exception $e) { + common_log(LOG_WARNING, "Couldn't convert OMB subscription (" . $up->nickname . ", " . $rp->nickname . + ") to OStatus: " . $e->getMessage()); + continue; + } + } +} From caab6ddaa9ba9d8eb500d508e7580222886c0143 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 13:13:51 -0500 Subject: [PATCH 08/42] fix path for updateostatus.php --- plugins/OStatus/scripts/updateostatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/OStatus/scripts/updateostatus.php b/plugins/OStatus/scripts/updateostatus.php index 8b10817040..1414f81e93 100644 --- a/plugins/OStatus/scripts/updateostatus.php +++ b/plugins/OStatus/scripts/updateostatus.php @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..')); +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); $shortoptions = 'i:n:a'; $longoptions = array('id=', 'nickname=', 'all'); From 6f20b74dc7b3b69365bf8de7ba0652177e570437 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 13:41:06 -0500 Subject: [PATCH 09/42] drop tokens for OMB on unsubscribe --- classes/Subscription.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/classes/Subscription.php b/classes/Subscription.php index d6fb3fcbdd..878ab83e63 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -172,6 +172,26 @@ 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; + $token->secret = $sub->secret; + + if ($token->find(true)) { + + $result = $token->delete(); + if (!$result) { + common_log_db_error($sub, 'DELETE', __FILE__); + throw new Exception(_('Couldn\'t delete subscription OMB token.')); + } + } + } + $result = $sub->delete(); if (!$result) { From ef54702008143568afa77c1dda86da530a321d0f Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 14:17:25 -0500 Subject: [PATCH 10/42] Return empty array when no subscriptions to remote --- lib/oauthstore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oauthstore.php b/lib/oauthstore.php index eabe37f9fa..a6a6de7505 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 From 30c992004ff44516a1502f5e118b0395300bcbc0 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 14:30:28 -0500 Subject: [PATCH 11/42] Better logging on bad token in subscription --- classes/Subscription.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/classes/Subscription.php b/classes/Subscription.php index 878ab83e63..da695c36e5 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -185,10 +185,13 @@ class Subscription extends Memcached_DataObject if ($token->find(true)) { $result = $token->delete(); + if (!$result) { - common_log_db_error($sub, 'DELETE', __FILE__); + 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}"); } } From 486cbac522b0985ac0f65c3acc61d6e3d73fbc31 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 14:35:38 -0500 Subject: [PATCH 12/42] Remove check for secret in token deletion on Subscription::cancel() --- classes/Subscription.php | 1 - 1 file changed, 1 deletion(-) diff --git a/classes/Subscription.php b/classes/Subscription.php index da695c36e5..9cef2df1ad 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -180,7 +180,6 @@ class Subscription extends Memcached_DataObject $token = new Token(); $token->tok = $sub->token; - $token->secret = $sub->secret; if ($token->find(true)) { From 90689008ee8649d9592ad97579d7de44d1ce389d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 14:40:39 -0500 Subject: [PATCH 13/42] send smaller error pages for OMB API endpoints --- actions/postnotice.php | 3 +++ actions/updateprofile.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/actions/postnotice.php b/actions/postnotice.php index fb06703766..f092d54d1d 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) { diff --git a/actions/updateprofile.php b/actions/updateprofile.php index e416a6fa93..dfc31f5427 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'); From e529ceee21b4fb0c18f32f7a097bce14229af42f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 1 Mar 2010 12:20:04 -0800 Subject: [PATCH 14/42] Add index on post_id for file_to_post, needed for efficient lookups of files/urls attached to a given post. --- db/08to09.sql | 2 ++ db/statusnet.sql | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/db/08to09.sql b/db/08to09.sql index b10e47dbcb..582981614e 100644 --- a/db/08to09.sql +++ b/db/08to09.sql @@ -110,3 +110,5 @@ 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); diff --git a/db/statusnet.sql b/db/statusnet.sql index 4158f0167d..4aa37ce1c5 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -523,7 +523,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; From 9b4ee9070467eb5df02b350e2e3d01a2c2497836 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 15:31:20 -0500 Subject: [PATCH 15/42] return correct HTTP status code for OMB errors --- actions/postnotice.php | 8 ++++++++ actions/updateprofile.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/actions/postnotice.php b/actions/postnotice.php index f092d54d1d..ad3f00e349 100644 --- a/actions/postnotice.php +++ b/actions/postnotice.php @@ -74,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 dfc31f5427..44fafdd926 100644 --- a/actions/updateprofile.php +++ b/actions/updateprofile.php @@ -77,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; From baee6dac9e75601cf4a49885b69e8e28c623c86f Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 15:39:41 -0500 Subject: [PATCH 16/42] remove strict check on OMB exception strings --- actions/postnotice.php | 4 ++-- actions/updateprofile.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actions/postnotice.php b/actions/postnotice.php index ad3f00e349..b2f6f1bb95 100644 --- a/actions/postnotice.php +++ b/actions/postnotice.php @@ -76,8 +76,8 @@ class PostnoticeAction extends Action $srv->handlePostNotice(); } catch (OMB_RemoteServiceException $rse) { $msg = $rse->getMessage(); - if (preg_match('/^Revoked accesstoken/', $msg) || - preg_match('/^No subscriber/', $msg)) { + if (preg_match('/Revoked accesstoken/', $msg) || + preg_match('/No subscriber/', $msg)) { $this->clientError($msg, 403); } else { $this->clientError($msg); diff --git a/actions/updateprofile.php b/actions/updateprofile.php index 44fafdd926..bae6108cce 100644 --- a/actions/updateprofile.php +++ b/actions/updateprofile.php @@ -79,8 +79,8 @@ class UpdateprofileAction extends Action $srv->handleUpdateProfile(); } catch (OMB_RemoteServiceException $rse) { $msg = $rse->getMessage(); - if (preg_match('/^Revoked accesstoken/', $msg) || - preg_match('/^No subscriber/', $msg)) { + if (preg_match('/Revoked accesstoken/', $msg) || + preg_match('/No subscriber/', $msg)) { $this->clientError($msg, 403); } else { $this->clientError($msg); From 1ac3d15242aa2679a470948b9020714f7cce4acc Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 15:59:24 -0500 Subject: [PATCH 17/42] pass listener URI into consumer for OMB --- lib/omb.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/omb.php b/lib/omb.php index 17132a594f..14392d0799 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.* ' . '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.* ' . 'FROM subscription JOIN remote_profile ' . 'ON subscription.subscriber = remote_profile.id ' . 'WHERE subscription.subscribed = ' . $profile->id . ' '); @@ -141,7 +142,8 @@ 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); try { $service->setToken($rp->token, $rp->secret); $service->updateProfile($omb_profile); @@ -159,13 +161,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; } } From 946445eea945b28d50f7409815a4913f50061958 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 1 Mar 2010 13:09:20 -0800 Subject: [PATCH 18/42] Add index on group_index.notice_id, needed to pull list of target groups for inbox delivery. Index was present on live identi.ca database but missing from master definitions: group_inbox_notice_id_idx --- db/08to09.sql | 3 +++ db/statusnet.sql | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/db/08to09.sql b/db/08to09.sql index 582981614e..f305721541 100644 --- a/db/08to09.sql +++ b/db/08to09.sql @@ -112,3 +112,6 @@ 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 4aa37ce1c5..3f95948e1e 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; From 00f724d0e6380f8177bcee0ff2631783f514508a Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 1 Mar 2010 14:10:13 -0800 Subject: [PATCH 19/42] Localization tweak: include doc comments marked as 'TRANS' in .po file output; these should now get automatically pulled through to the TranslateWiki interface as translator help hints. --- plugins/OStatus/actions/ostatussub.php | 6 + plugins/OStatus/locale/OStatus.po | 280 +++++++++++++++++++++---- scripts/update_po_templates.php | 7 +- 3 files changed, 256 insertions(+), 37 deletions(-) diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index aae22f868a..f45e6a8d1a 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/locale/OStatus.po b/plugins/OStatus/locale/OStatus.po index dedc018e3f..ee19cf3dbd 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 \n" "Language-Team: LANGUAGE \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 "" -#: FeedSubPlugin.php:77 -msgid "Feed subscription options" +#: actions/groupsalmon.php:123 +msgid "Can't read profile to set up group membership." msgstr "" -#: feedmunger.php:215 +#: actions/groupsalmon.php:126 actions/groupsalmon.php:169 +msgid "Groups can't join groups." +msgstr "" + +#: 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/feedsubsettings.php:96 +#: 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/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/feedsubsettings.php:161 +#: 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/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 "" -#: actions/feedsubsettings.php:188 -msgid "Feed is not PuSH-enabled; cannot subscribe." +#. TRANS: OStatus remote group subscription dialog error. +#: actions/ostatussub.php:336 +msgid "Already a member!" msgstr "" -#: actions/feedsubsettings.php:208 -msgid "Feed subscription failed! Bad response from hub." +#. TRANS: OStatus remote group subscription dialog error. +#: actions/ostatussub.php:346 +msgid "Remote group join failed!" msgstr "" -#: actions/feedsubsettings.php:218 +#. TRANS: OStatus remote group subscription dialog error. +#: actions/ostatussub.php:350 +msgid "Remote group join aborted!" +msgstr "" + +#. 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/feedsubsettings.php:231 -msgid "Previewing feed:" +#: 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 "" + +#: 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/scripts/update_po_templates.php b/scripts/update_po_templates.php index 61a6ac7833..0bfa62a22e 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" \ From 79b2e671c21e07a0512ea0ed95ed2e00e150c842 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 17:55:46 -0500 Subject: [PATCH 20/42] show service debug info --- lib/omb.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/omb.php b/lib/omb.php index 14392d0799..0db178989c 100644 --- a/lib/omb.php +++ b/lib/omb.php @@ -144,6 +144,9 @@ function omb_broadcast_profile($profile) $service = new StatusNet_OMB_Service_Consumer( 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); From a5dc5f9c62aec5021b31e7f202edf2de3462b6ba Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 1 Mar 2010 14:58:06 -0800 Subject: [PATCH 21/42] Upgrade XML output scrubbing to better deal with newline and a few other chars --- lib/util.php | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/util.php b/lib/util.php index d12a7920d2..7a170a5f5f 100644 --- a/lib/util.php +++ b/lib/util.php @@ -809,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) From 3ff3abb247062ed61486930ed1203c286b1378b2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 12:55:51 -0500 Subject: [PATCH 22/42] Script to convert OMB subscriptions to OStatus subscriptions --- plugins/OStatus/scripts/updateostatus.php | 102 ++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 plugins/OStatus/scripts/updateostatus.php diff --git a/plugins/OStatus/scripts/updateostatus.php b/plugins/OStatus/scripts/updateostatus.php new file mode 100644 index 0000000000..8b10817040 --- /dev/null +++ b/plugins/OStatus/scripts/updateostatus.php @@ -0,0 +1,102 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..')); + +$shortoptions = 'i:n:a'; +$longoptions = array('id=', 'nickname=', 'all'); + +$helptext = <<find()) { + while ($user->fetch()) { + updateOStatus($user); + } + } + } else { + show_help(); + exit(1); + } +} catch (Exception $e) { + print $e->getMessage()."\n"; + exit(1); +} + +function updateOStatus($user) +{ + $up = $user->getProfile(); + + $sp = $user->getSubscriptions(); + + $rps = array(); + + while ($sp->fetch()) { + $remote = Remote_profile::staticGet('id', $sp->id); + + if (!empty($remote)) { + $rps[] = clone($sp); + } + } + + foreach ($rps as $rp) { + try { + $op = Ostatus_profile::ensureProfile($rp->profileurl); + + if (!empty($op)) { + Subscription::cancel($up, $rp); + Subscription::start($up, $op->localProfile()); + } + + } catch (Exception $e) { + common_log(LOG_WARNING, "Couldn't convert OMB subscription (" . $up->nickname . ", " . $rp->nickname . + ") to OStatus: " . $e->getMessage()); + continue; + } + } +} From d77248343f173d5a25e674abfc4bffe59d55300c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 13:13:51 -0500 Subject: [PATCH 23/42] fix path for updateostatus.php --- plugins/OStatus/scripts/updateostatus.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/OStatus/scripts/updateostatus.php b/plugins/OStatus/scripts/updateostatus.php index 8b10817040..1414f81e93 100644 --- a/plugins/OStatus/scripts/updateostatus.php +++ b/plugins/OStatus/scripts/updateostatus.php @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..')); +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); $shortoptions = 'i:n:a'; $longoptions = array('id=', 'nickname=', 'all'); From 320036dbfbd1d4b2b1e866aafb5da26330cc1e21 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 13:41:06 -0500 Subject: [PATCH 24/42] drop tokens for OMB on unsubscribe --- classes/Subscription.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/classes/Subscription.php b/classes/Subscription.php index d6fb3fcbdd..878ab83e63 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -172,6 +172,26 @@ 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; + $token->secret = $sub->secret; + + if ($token->find(true)) { + + $result = $token->delete(); + if (!$result) { + common_log_db_error($sub, 'DELETE', __FILE__); + throw new Exception(_('Couldn\'t delete subscription OMB token.')); + } + } + } + $result = $sub->delete(); if (!$result) { From c3ba26bb9955ae5d924426dc0e4ff6c48b4399ad Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 14:17:25 -0500 Subject: [PATCH 25/42] Return empty array when no subscriptions to remote --- lib/oauthstore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oauthstore.php b/lib/oauthstore.php index eabe37f9fa..a6a6de7505 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 From 48ce511f947e966b624dc3cf6e6b884361c3370d Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 14:30:28 -0500 Subject: [PATCH 26/42] Better logging on bad token in subscription --- classes/Subscription.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/classes/Subscription.php b/classes/Subscription.php index 878ab83e63..da695c36e5 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -185,10 +185,13 @@ class Subscription extends Memcached_DataObject if ($token->find(true)) { $result = $token->delete(); + if (!$result) { - common_log_db_error($sub, 'DELETE', __FILE__); + 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}"); } } From 17c2c793a5d9e1e7066715e57694678296f2221a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 14:35:38 -0500 Subject: [PATCH 27/42] Remove check for secret in token deletion on Subscription::cancel() --- classes/Subscription.php | 1 - 1 file changed, 1 deletion(-) diff --git a/classes/Subscription.php b/classes/Subscription.php index da695c36e5..9cef2df1ad 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -180,7 +180,6 @@ class Subscription extends Memcached_DataObject $token = new Token(); $token->tok = $sub->token; - $token->secret = $sub->secret; if ($token->find(true)) { From 1414b6b84954939b2f120443ab90a75d1ffb2a65 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 14:40:39 -0500 Subject: [PATCH 28/42] send smaller error pages for OMB API endpoints --- actions/postnotice.php | 3 +++ actions/updateprofile.php | 2 ++ 2 files changed, 5 insertions(+) diff --git a/actions/postnotice.php b/actions/postnotice.php index fb06703766..f092d54d1d 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) { diff --git a/actions/updateprofile.php b/actions/updateprofile.php index e416a6fa93..dfc31f5427 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'); From c7d8641e7dcb2af0ab9ad682f64c5ebc45fbbeae Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 15:31:20 -0500 Subject: [PATCH 29/42] return correct HTTP status code for OMB errors --- actions/postnotice.php | 8 ++++++++ actions/updateprofile.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/actions/postnotice.php b/actions/postnotice.php index f092d54d1d..ad3f00e349 100644 --- a/actions/postnotice.php +++ b/actions/postnotice.php @@ -74,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 dfc31f5427..44fafdd926 100644 --- a/actions/updateprofile.php +++ b/actions/updateprofile.php @@ -77,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; From 656a977016701e7a0a1f936909cd7d4a5975f5be Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 15:39:41 -0500 Subject: [PATCH 30/42] remove strict check on OMB exception strings --- actions/postnotice.php | 4 ++-- actions/updateprofile.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actions/postnotice.php b/actions/postnotice.php index ad3f00e349..b2f6f1bb95 100644 --- a/actions/postnotice.php +++ b/actions/postnotice.php @@ -76,8 +76,8 @@ class PostnoticeAction extends Action $srv->handlePostNotice(); } catch (OMB_RemoteServiceException $rse) { $msg = $rse->getMessage(); - if (preg_match('/^Revoked accesstoken/', $msg) || - preg_match('/^No subscriber/', $msg)) { + if (preg_match('/Revoked accesstoken/', $msg) || + preg_match('/No subscriber/', $msg)) { $this->clientError($msg, 403); } else { $this->clientError($msg); diff --git a/actions/updateprofile.php b/actions/updateprofile.php index 44fafdd926..bae6108cce 100644 --- a/actions/updateprofile.php +++ b/actions/updateprofile.php @@ -79,8 +79,8 @@ class UpdateprofileAction extends Action $srv->handleUpdateProfile(); } catch (OMB_RemoteServiceException $rse) { $msg = $rse->getMessage(); - if (preg_match('/^Revoked accesstoken/', $msg) || - preg_match('/^No subscriber/', $msg)) { + if (preg_match('/Revoked accesstoken/', $msg) || + preg_match('/No subscriber/', $msg)) { $this->clientError($msg, 403); } else { $this->clientError($msg); From 64918cfd352c50385ab4f0283fe09589018bcce5 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 15:59:24 -0500 Subject: [PATCH 31/42] pass listener URI into consumer for OMB --- lib/omb.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/omb.php b/lib/omb.php index 17132a594f..14392d0799 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.* ' . '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.* ' . 'FROM subscription JOIN remote_profile ' . 'ON subscription.subscriber = remote_profile.id ' . 'WHERE subscription.subscribed = ' . $profile->id . ' '); @@ -141,7 +142,8 @@ 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); try { $service->setToken($rp->token, $rp->secret); $service->updateProfile($omb_profile); @@ -159,13 +161,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; } } From 19ec0e3a62d4c60d7e5581b4e38ec705650b1d18 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 17:55:46 -0500 Subject: [PATCH 32/42] show service debug info --- lib/omb.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/omb.php b/lib/omb.php index 14392d0799..0db178989c 100644 --- a/lib/omb.php +++ b/lib/omb.php @@ -144,6 +144,9 @@ function omb_broadcast_profile($profile) $service = new StatusNet_OMB_Service_Consumer( 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); From f7c2c19ce89868fee5ce51d793d45c05ab209866 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 18:07:21 -0500 Subject: [PATCH 33/42] lost important fields when switching queries --- lib/omb.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/omb.php b/lib/omb.php index 0db178989c..8bbe5e8aac 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 remote_profile.* ' . + $rp->query('SELECT remote_profile.*, secret, token ' . 'FROM subscription JOIN remote_profile ' . 'ON subscription.subscriber = remote_profile.id ' . 'WHERE subscription.subscribed = ' . $notice->profile_id . ' '); @@ -126,7 +126,7 @@ function omb_broadcast_profile($profile) /* Get remote users subscribed to this profile. */ $rp = new Remote_profile(); - $rp->query('SELECT remote_profile.* ' . + $rp->query('SELECT remote_profile.*, secret, token ' . 'FROM subscription JOIN remote_profile ' . 'ON subscription.subscriber = remote_profile.id ' . 'WHERE subscription.subscribed = ' . $profile->id . ' '); From 0512151eabf64e88b4648869b3d029800d30668a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Mar 2010 18:19:20 -0500 Subject: [PATCH 34/42] more output in updateostatus.php --- plugins/OStatus/scripts/updateostatus.php | 29 +++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/plugins/OStatus/scripts/updateostatus.php b/plugins/OStatus/scripts/updateostatus.php index 1414f81e93..d553a7d625 100644 --- a/plugins/OStatus/scripts/updateostatus.php +++ b/plugins/OStatus/scripts/updateostatus.php @@ -24,7 +24,7 @@ $shortoptions = 'i:n:a'; $longoptions = array('id=', 'nickname=', 'all'); $helptext = <<nickname}..."; + } + $up = $user->getProfile(); $sp = $user->getSubscriptions(); @@ -84,16 +88,37 @@ function updateOStatus($user) } } + 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)) { + 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; From 45e8819c1b9cc618e9b2d6678b0ff14c653a09d3 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 1 Mar 2010 16:35:36 -0800 Subject: [PATCH 35/42] Fix a bunch of notice & warning-level messages that were breaking my inter-instance communications --- plugins/OStatus/classes/Magicsig.php | 4 +++- plugins/OStatus/classes/Ostatus_profile.php | 2 +- plugins/OStatus/lib/discovery.php | 2 +- plugins/OStatus/lib/xrd.php | 11 ++++++++--- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php index 96900d8761..5a46aeeb6e 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 7b1aec76ba..93e8934c9e 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -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 388df0a28f..f8449b309e 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 16d27f8eb7..1de065db9f 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; From 493b5479c15f0e135054fd6b961e50e690077eda Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 1 Mar 2010 16:36:33 -0800 Subject: [PATCH 36/42] OStatus: support @example.com/path/to/profile mentions as well as @profile@example.com (latter requires webfinger, former doesn't) Plus misc warnings/notices cleanup in the submission path. --- actions/newnotice.php | 3 ++ plugins/OStatus/OStatusPlugin.php | 55 ++++++++++++++++----- plugins/OStatus/classes/Ostatus_profile.php | 2 +- plugins/OStatus/lib/discovery.php | 2 +- plugins/OStatus/lib/xrd.php | 10 ++-- 5 files changed, 53 insertions(+), 19 deletions(-) diff --git a/actions/newnotice.php b/actions/newnotice.php index 78480ababb..ed0fa1b2b5 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/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 720dedd0a0..4ffbba45b9 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) { + $target = $wmatch[0]; + $oprofile = null; - $webfinger = $wmatch[0]; - - $this->log(LOG_INFO, "Checking Webfinger for address '$webfinger'"); - - $oprofile = Ostatus_profile::ensureWebfinger($webfinger); + 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/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 7b1aec76ba..668a31df4c 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 { diff --git a/plugins/OStatus/lib/discovery.php b/plugins/OStatus/lib/discovery.php index 388df0a28f..f8449b309e 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 16d27f8eb7..48c1c88e15 100644 --- a/plugins/OStatus/lib/xrd.php +++ b/plugins/OStatus/lib/xrd.php @@ -156,20 +156,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); From 680ace19665f064404ddb4723d9b41ab69c6e1bd Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 1 Mar 2010 18:46:34 -0800 Subject: [PATCH 37/42] High-priority OStatus fixes: * PuSHing out to multiple client services could fail; only first callback got reached * Correction for re-sub request to a known sub --- plugins/OStatus/actions/pushhub.php | 2 +- plugins/OStatus/classes/HubSub.php | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/OStatus/actions/pushhub.php b/plugins/OStatus/actions/pushhub.php index f33690bc49..842d65e7d2 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 e599d83a96..3120a70f9f 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'); } From 6b2d67216ef7d550921f813981f7744d1687be78 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 1 Mar 2010 21:34:50 -0800 Subject: [PATCH 38/42] Initial Twitter bridge admin panel --- lib/default.php | 5 +- plugins/TwitterBridge/README | 32 +- plugins/TwitterBridge/TwitterBridgePlugin.php | 129 ++++++-- plugins/TwitterBridge/twitteradminpanel.php | 280 ++++++++++++++++++ .../TwitterBridge/twitterauthorization.php | 2 +- 5 files changed, 403 insertions(+), 45 deletions(-) create mode 100644 plugins/TwitterBridge/twitteradminpanel.php diff --git a/lib/default.php b/lib/default.php index d849055c21..668206acf0 100644 --- a/lib/default.php +++ b/lib/default.php @@ -177,8 +177,9 @@ $default = array('source' => 'StatusNet', # source attribute for Twitter 'taguri' => null), # base for tag URIs 'twitter' => - array('enabled' => true, - 'consumer_key' => null, + array('enabled' => true, + 'signin' => true, + 'consumer_key' => null, 'consumer_secret' => null), 'cache' => array('base' => null), diff --git a/plugins/TwitterBridge/README b/plugins/TwitterBridge/README index d3bcda5984..91b34eb497 100644 --- a/plugins/TwitterBridge/README +++ b/plugins/TwitterBridge/README @@ -1,7 +1,7 @@ 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) @@ -9,18 +9,14 @@ instance with Twitter. Installing it will allow your users to: 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 (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: @@ -29,6 +25,16 @@ and your Callback URL to: 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 diff --git a/plugins/TwitterBridge/TwitterBridgePlugin.php b/plugins/TwitterBridge/TwitterBridgePlugin.php index c7f57ffc77..ac08cc593f 100644 --- a/plugins/TwitterBridge/TwitterBridgePlugin.php +++ b/plugins/TwitterBridge/TwitterBridgePlugin.php @@ -23,7 +23,7 @@ * @author Julien C * @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 * @author Julien C * @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,13 @@ 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')); + $m->connect('admin/twitter', array('action' => 'twitteradminpanel')); return true; } @@ -88,13 +111,14 @@ 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); + $action->menuItem( + common_local_url('twitterlogin'), + _m('Twitter'), + _m('Login or register using Twitter'), + 'twitterlogin' === $action_name + ); return true; } @@ -110,10 +134,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 +158,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 +200,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 +230,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 ' . - 'Twitter.')); + $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 ' . + 'Twitter.' + ) + ); return true; } } + diff --git a/plugins/TwitterBridge/twitteradminpanel.php b/plugins/TwitterBridge/twitteradminpanel.php new file mode 100644 index 0000000000..b22e6d99fe --- /dev/null +++ b/plugins/TwitterBridge/twitteradminpanel.php @@ -0,0 +1,280 @@ +. + * + * @category Settings + * @package StatusNet + * @author Zach Copley + * @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 + * @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 cabf69d7a8..c93f6666bc 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 * @author Julien C * @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 From 871b3aa6c00b38e1782949e201e5cbca7fb7a524 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 1 Mar 2010 21:52:31 -0800 Subject: [PATCH 39/42] Remove un-needed config variable for enabling/disabling Twitter integration --- lib/action.php | 2 -- lib/default.php | 3 +-- plugins/Facebook/FacebookPlugin.php | 2 -- plugins/MobileProfile/MobileProfilePlugin.php | 2 -- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/action.php b/lib/action.php index a7e0eb33ba..0918c68582 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/default.php b/lib/default.php index 668206acf0..7b50242ae2 100644 --- a/lib/default.php +++ b/lib/default.php @@ -177,8 +177,7 @@ $default = array('source' => 'StatusNet', # source attribute for Twitter 'taguri' => null), # base for tag URIs 'twitter' => - array('enabled' => true, - 'signin' => true, + array('signin' => true, 'consumer_key' => null, 'consumer_secret' => null), 'cache' => diff --git a/plugins/Facebook/FacebookPlugin.php b/plugins/Facebook/FacebookPlugin.php index 4266b886d9..8fb81aea02 100644 --- a/plugins/Facebook/FacebookPlugin.php +++ b/plugins/Facebook/FacebookPlugin.php @@ -359,8 +359,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)) { diff --git a/plugins/MobileProfile/MobileProfilePlugin.php b/plugins/MobileProfile/MobileProfilePlugin.php index cd2531fa72..f788639aed 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')); From 27a49361eb8fd4dc661283214003a71de34201e8 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 1 Mar 2010 22:41:07 -0800 Subject: [PATCH 40/42] - Make 'Sign in with Twitter' optional - Updates to the Twitter bridge plugin README --- plugins/TwitterBridge/README | 86 +++++++++++++------ plugins/TwitterBridge/TwitterBridgePlugin.php | 20 +++-- 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/plugins/TwitterBridge/README b/plugins/TwitterBridge/README index 91b34eb497..a386989b7a 100644 --- a/plugins/TwitterBridge/README +++ b/plugins/TwitterBridge/README @@ -5,25 +5,29 @@ instance with Twitter. Installing it will allow your users to: - 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 ------------ -OAuth (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. +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: @@ -36,18 +40,47 @@ To enable the plugin, add the following to your config.php: ); * 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 +Admin 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 +-------------------- + +As of 0.9.0 you 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 admin 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) @@ -57,13 +90,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; @@ -72,8 +105,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'; @@ -85,7 +119,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, this 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 system. 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 ac08cc593f..6ce69d5e2b 100644 --- a/plugins/TwitterBridge/TwitterBridgePlugin.php +++ b/plugins/TwitterBridge/TwitterBridgePlugin.php @@ -96,7 +96,11 @@ class TwitterBridgePlugin extends Plugin 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; @@ -113,12 +117,14 @@ class TwitterBridgePlugin extends Plugin { $action_name = $action->trimmed('action'); - $action->menuItem( - common_local_url('twitterlogin'), - _m('Twitter'), - _m('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; } From 1df8c2a44d9784e8f01794b20fe5a014d518474b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 1 Mar 2010 22:58:27 -0800 Subject: [PATCH 41/42] Some wording / spelling fixes --- plugins/TwitterBridge/README | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/plugins/TwitterBridge/README b/plugins/TwitterBridge/README index a386989b7a..72278b32e6 100644 --- a/plugins/TwitterBridge/README +++ b/plugins/TwitterBridge/README @@ -1,3 +1,6 @@ +Twitter Bridge Plugin +===================== + This Twitter "bridge" plugin allows you to integrate your StatusNet instance with Twitter. Installing it will allow your users to: @@ -44,8 +47,8 @@ To enable the plugin, add the following to your config.php: password under an older version of StatusNet, but all new Twitter bridge connections will use OAuth. -Admin panel ------------ +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, @@ -61,15 +64,15 @@ role (see: scripts/userrole.php). Sign in with Twitter -------------------- -As of 0.9.0 you StatusNet optionally allows users to register and +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 admin panel, or by adding the following -line to your config.php: +disabled via the Twitter bridge administration panel, or by adding the +following line to your config.php: $config['twitter']['signin'] = false; @@ -119,9 +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 +for larger sites and sites that wish to improve performance the script allows notices to be sent "offline" via a separate process. StatusNet will automatically use the TwitterQueueHandler if you have -enabled the queuing system. See the "Queues and daemons" section of the -main README file for more information about how to do that. +enabled the queuing subsystem. See the "Queues and daemons" section of +the main README file for more information about how to do that. From 40e1b249cf1535a6074c8b32e5820c8ad6427836 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 1 Mar 2010 23:31:56 -0800 Subject: [PATCH 42/42] Initial Facebook admin panel --- plugins/Facebook/FacebookPlugin.php | 85 +++++++-- plugins/Facebook/facebookadminpanel.php | 223 ++++++++++++++++++++++++ 2 files changed, 296 insertions(+), 12 deletions(-) create mode 100644 plugins/Facebook/facebookadminpanel.php diff --git a/plugins/Facebook/FacebookPlugin.php b/plugins/Facebook/FacebookPlugin.php index 8fb81aea02..014d0d1970 100644 --- a/plugins/Facebook/FacebookPlugin.php +++ b/plugins/Facebook/FacebookPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Zach Copley - * @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; @@ -122,6 +154,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 @@ -523,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 ' . - 'Facebook ' . - '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 ' . + 'Facebook ' . + 'and Facebook Connect.' + ) + ); return true; } diff --git a/plugins/Facebook/facebookadminpanel.php b/plugins/Facebook/facebookadminpanel.php new file mode 100644 index 0000000000..ae1c7302f9 --- /dev/null +++ b/plugins/Facebook/facebookadminpanel.php @@ -0,0 +1,223 @@ +. + * + * @category Settings + * @package StatusNet + * @author Zach Copley + * @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 + * @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')); + } +}