diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/Facebook/FacebookPlugin.php | 87 | ||||
-rw-r--r-- | plugins/Facebook/facebookadminpanel.php | 223 | ||||
-rw-r--r-- | plugins/MobileProfile/MobileProfilePlugin.php | 2 | ||||
-rw-r--r-- | plugins/OStatus/OStatusPlugin.php | 57 | ||||
-rw-r--r-- | plugins/OStatus/actions/ostatussub.php | 6 | ||||
-rw-r--r-- | plugins/OStatus/actions/pushhub.php | 2 | ||||
-rw-r--r-- | plugins/OStatus/classes/HubSub.php | 8 | ||||
-rw-r--r-- | plugins/OStatus/classes/Magicsig.php | 4 | ||||
-rw-r--r-- | plugins/OStatus/classes/Ostatus_profile.php | 4 | ||||
-rw-r--r-- | plugins/OStatus/lib/discovery.php | 2 | ||||
-rw-r--r-- | plugins/OStatus/lib/xrd.php | 21 | ||||
-rw-r--r-- | plugins/OStatus/locale/OStatus.po | 280 | ||||
-rw-r--r-- | plugins/OStatus/scripts/updateostatus.php | 127 | ||||
-rw-r--r-- | plugins/RegisterThrottle/RegisterThrottlePlugin.php | 249 | ||||
-rw-r--r-- | plugins/RegisterThrottle/Registration_ip.php | 124 | ||||
-rw-r--r-- | plugins/TwitterBridge/README | 105 | ||||
-rw-r--r-- | plugins/TwitterBridge/TwitterBridgePlugin.php | 137 | ||||
-rw-r--r-- | plugins/TwitterBridge/twitteradminpanel.php | 280 | ||||
-rw-r--r-- | plugins/TwitterBridge/twitterauthorization.php | 2 |
19 files changed, 1580 insertions, 140 deletions
diff --git a/plugins/Facebook/FacebookPlugin.php b/plugins/Facebook/FacebookPlugin.php index 4266b886d..014d0d197 100644 --- a/plugins/Facebook/FacebookPlugin.php +++ b/plugins/Facebook/FacebookPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Zach Copley <zach@status.net> - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-2010 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -32,12 +32,12 @@ if (!defined('STATUSNET')) { } define("FACEBOOK_CONNECT_SERVICE", 3); -define('FACEBOOKPLUGIN_VERSION', '0.9'); require_once INSTALLDIR . '/plugins/Facebook/facebookutil.php'; /** - * Facebook plugin to add a StatusNet Facebook application + * Facebook plugin to add a StatusNet Facebook canvas application + * and allow registration and authentication via Facebook Connect * * @category Plugin * @package StatusNet @@ -49,6 +49,36 @@ require_once INSTALLDIR . '/plugins/Facebook/facebookutil.php'; class FacebookPlugin extends Plugin { + const VERSION = STATUSNET_VERSION; + + /** + * Initializer for the plugin. + */ + + function initialize() + { + // Allow the key and secret to be passed in + // Control panel will override + + if (isset($this->apikey)) { + $key = common_config('facebook', 'apikey'); + if (empty($key)) { + Config::save('facebook', 'apikey', $this->apikey); + } + } + + if (isset($this->secret)) { + $secret = common_config('facebook', 'secret'); + if (empty($secret)) { + Config::save( + 'facebook', + 'secret', + $this->secret + ); + } + } + } + /** * Add Facebook app actions to the router table * @@ -70,6 +100,7 @@ class FacebookPlugin extends Plugin array('action' => 'facebooksettings')); $m->connect('facebook/app/invite.php', array('action' => 'facebookinvite')); $m->connect('facebook/app/remove', array('action' => 'facebookremove')); + $m->connect('admin/facebook', array('action' => 'facebookadminpanel')); // Facebook Connect stuff @@ -98,6 +129,7 @@ class FacebookPlugin extends Plugin case 'FacebookinviteAction': case 'FacebookremoveAction': case 'FacebooksettingsAction': + case 'FacebookadminpanelAction': include_once INSTALLDIR . '/plugins/Facebook/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; return false; @@ -123,6 +155,32 @@ class FacebookPlugin extends Plugin } /** + * Add a Facebook tab to the admin panels + * + * @param Widget $nav Admin panel nav + * + * @return boolean hook value + */ + + function onEndAdminPanelNav($nav) + { + if (AdminPanelAction::canAdmin('facebook')) { + + $action_name = $nav->action->trimmed('action'); + + $nav->out->menuItem( + common_local_url('facebookadminpanel'), + _m('Facebook'), + _m('Facebook integration configuration'), + $action_name == 'facebookadminpanel', + 'nav_facebook_admin_panel' + ); + } + + return true; + } + + /** * Override normal HTML output to force the content type to * text/html and add in xmlns:fb * @@ -359,8 +417,6 @@ class FacebookPlugin extends Plugin $connect = 'imsettings'; } else if (common_config('sms', 'enabled')) { $connect = 'smssettings'; - } else if (common_config('twitter', 'enabled')) { - $connect = 'twittersettings'; } if (!empty($user)) { @@ -525,15 +581,18 @@ class FacebookPlugin extends Plugin function onPluginVersion(&$versions) { - $versions[] = array('name' => 'Facebook', - 'version' => FACEBOOKPLUGIN_VERSION, - 'author' => 'Zach Copley', - 'homepage' => 'http://status.net/wiki/Plugin:Facebook', - 'rawdescription' => - _m('The Facebook plugin allows you to integrate ' . - 'your StatusNet instance with ' . - '<a href="http://facebook.com/">Facebook</a> ' . - 'and Facebook Connect.')); + $versions[] = array( + 'name' => 'Facebook', + 'version' => self::VERSION, + 'author' => 'Zach Copley', + 'homepage' => 'http://status.net/wiki/Plugin:Facebook', + 'rawdescription' => _m( + 'The Facebook plugin allows you to integrate ' . + 'your StatusNet instance with ' . + '<a href="http://facebook.com/">Facebook</a> ' . + 'and Facebook Connect.' + ) + ); return true; } diff --git a/plugins/Facebook/facebookadminpanel.php b/plugins/Facebook/facebookadminpanel.php new file mode 100644 index 000000000..ae1c7302f --- /dev/null +++ b/plugins/Facebook/facebookadminpanel.php @@ -0,0 +1,223 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Facebook integration administration panel + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Settings + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Administer global Facebook integration settings + * + * @category Admin + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class FacebookadminpanelAction extends AdminPanelAction +{ + /** + * Returns the page title + * + * @return string page title + */ + + function title() + { + return _m('Facebook'); + } + + /** + * Instructions for using this form. + * + * @return string instructions + */ + + function getInstructions() + { + return _m('Facebook integration settings'); + } + + /** + * Show the Facebook admin panel form + * + * @return void + */ + + function showForm() + { + $form = new FacebookAdminPanelForm($this); + $form->show(); + return; + } + + /** + * Save settings from the form + * + * @return void + */ + + function saveSettings() + { + static $settings = array( + 'facebook' => array('apikey', 'secret'), + ); + + $values = array(); + + foreach ($settings as $section => $parts) { + foreach ($parts as $setting) { + $values[$section][$setting] + = $this->trimmed($setting); + } + } + + // This throws an exception on validation errors + + $this->validate($values); + + // assert(all values are valid); + + $config = new Config(); + + $config->query('BEGIN'); + + foreach ($settings as $section => $parts) { + foreach ($parts as $setting) { + Config::save($section, $setting, $values[$section][$setting]); + } + } + + $config->query('COMMIT'); + + return; + } + + function validate(&$values) + { + // Validate consumer key and secret (can't be too long) + + if (mb_strlen($values['facebook']['apikey']) > 255) { + $this->clientError( + _m("Invalid Facebook API key. Max length is 255 characters.") + ); + } + + if (mb_strlen($values['facebook']['secret']) > 255) { + $this->clientError( + _m("Invalid Facebook API secret. Max length is 255 characters.") + ); + } + } +} + +class FacebookAdminPanelForm extends AdminForm +{ + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'facebookadminpanel'; + } + + /** + * class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_settings'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('facebookadminpanel'); + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + $this->out->elementStart( + 'fieldset', + array('id' => 'settings_facebook-application') + ); + $this->out->element('legend', null, _m('Facebook application settings')); + $this->out->elementStart('ul', 'form_data'); + + $this->li(); + $this->input( + 'apikey', + _m('API key'), + _m('API key provided by Facebook'), + 'facebook' + ); + $this->unli(); + + $this->li(); + $this->input( + 'secret', + _m('Secret'), + _m('API secret provided by Facebook'), + 'facebook' + ); + $this->unli(); + + $this->out->elementEnd('ul'); + $this->out->elementEnd('fieldset'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Save'), 'submit', null, _('Save Facebook settings')); + } +} diff --git a/plugins/MobileProfile/MobileProfilePlugin.php b/plugins/MobileProfile/MobileProfilePlugin.php index cd2531fa7..f788639ae 100644 --- a/plugins/MobileProfile/MobileProfilePlugin.php +++ b/plugins/MobileProfile/MobileProfilePlugin.php @@ -312,8 +312,6 @@ class MobileProfilePlugin extends WAP20Plugin $connect = 'imsettings'; } else if (common_config('sms', 'enabled')) { $connect = 'smssettings'; - } else if (common_config('twitter', 'enabled')) { - $connect = 'twittersettings'; } $action->elementStart('ul', array('id' => 'site_nav_global_primary')); diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 720dedd0a..4ffbba45b 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -222,31 +222,62 @@ class OStatusPlugin extends Plugin } /** - * + * Find any explicit remote mentions. Accepted forms: + * Webfinger: @user@example.com + * Profile link: @example.com/mublog/user + * @param Profile $sender (os user?) + * @param string $text input markup text + * @param array &$mention in/out param: set of found mentions + * @return boolean hook return value */ function onEndFindMentions($sender, $text, &$mentions) { - preg_match_all('/(?:^|\s+)@((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)/', + preg_match_all('!(?:^|\s+) + @( # Webfinger: + (?:\w+\.)*\w+ # user + @ # @ + (?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+ # domain + | # Profile: + (?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+ # domain + (?:/\w+)+ # /path1(/path2...) + )!x', $text, $wmatches, PREG_OFFSET_CAPTURE); foreach ($wmatches[1] as $wmatch) { - - $webfinger = $wmatch[0]; - - $this->log(LOG_INFO, "Checking Webfinger for address '$webfinger'"); - - $oprofile = Ostatus_profile::ensureWebfinger($webfinger); + $target = $wmatch[0]; + $oprofile = null; + + if (strpos($target, '/') === false) { + $this->log(LOG_INFO, "Checking Webfinger for address '$target'"); + try { + $oprofile = Ostatus_profile::ensureWebfinger($target); + } catch (Exception $e) { + $this->log(LOG_ERR, "Webfinger check failed: " . $e->getMessage()); + } + } else { + $schemes = array('https', 'http'); + foreach ($schemes as $scheme) { + $url = "$scheme://$target"; + $this->log(LOG_INFO, "Checking profile address '$url'"); + try { + $oprofile = Ostatus_profile::ensureProfile($url); + if ($oprofile) { + continue; + } + } catch (Exception $e) { + $this->log(LOG_ERR, "Profile check failed: " . $e->getMessage()); + } + } + } if (empty($oprofile)) { - - $this->log(LOG_INFO, "No Ostatus_profile found for address '$webfinger'"); - + $this->log(LOG_INFO, "No Ostatus_profile found for address '$target'"); } else { - $this->log(LOG_INFO, "Ostatus_profile found for address '$webfinger'"); + $this->log(LOG_INFO, "Ostatus_profile found for address '$target'"); if ($oprofile->isGroup()) { continue; @@ -261,7 +292,7 @@ class OStatusPlugin extends Plugin } } $mentions[] = array('mentioned' => array($profile), - 'text' => $wmatch[0], + 'text' => $target, 'position' => $pos, 'url' => $profile->profileurl); } diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index aae22f868..f45e6a8d1 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -332,6 +332,7 @@ class OStatusSubAction extends Action if ($this->oprofile->isGroup()) { $group = $this->oprofile->localGroup(); if ($user->isMember($group)) { + // TRANS: OStatus remote group subscription dialog error. $this->showForm(_m('Already a member!')); return; } @@ -341,18 +342,22 @@ class OStatusSubAction extends Action Event::handle('EndJoinGroup', array($group, $user)); $this->successGroup(); } else { + // TRANS: OStatus remote group subscription dialog error. $this->showForm(_m('Remote group join failed!')); } } else { + // TRANS: OStatus remote group subscription dialog error. $this->showForm(_m('Remote group join aborted!')); } } else { $local = $this->oprofile->localProfile(); if ($user->isSubscribed($local)) { + // TRANS: OStatus remote subscription dialog error. $this->showForm(_m('Already subscribed!')); } elseif ($this->oprofile->subscribeLocalToRemote($user)) { $this->successUser(); } else { + // TRANS: OStatus remote subscription dialog error. $this->showForm(_m('Remote subscription failed!')); } } @@ -450,6 +455,7 @@ class OStatusSubAction extends Action function title() { + // TRANS: Page title for OStatus remote subscription form return _m('Authorize subscription'); } diff --git a/plugins/OStatus/actions/pushhub.php b/plugins/OStatus/actions/pushhub.php index f33690bc4..842d65e7d 100644 --- a/plugins/OStatus/actions/pushhub.php +++ b/plugins/OStatus/actions/pushhub.php @@ -104,7 +104,7 @@ class PushHubAction extends Action throw new ClientException("Invalid hub.secret $secret; must be under 200 bytes."); } - $sub = HubSub::staticGet($sub->topic, $sub->callback); + $sub = HubSub::staticGet($topic, $callback); if (!$sub) { // Creating a new one! $sub = new HubSub(); diff --git a/plugins/OStatus/classes/HubSub.php b/plugins/OStatus/classes/HubSub.php index e599d83a9..3120a70f9 100644 --- a/plugins/OStatus/classes/HubSub.php +++ b/plugins/OStatus/classes/HubSub.php @@ -260,9 +260,15 @@ class HubSub extends Memcached_DataObject $retries = intval(common_config('ostatus', 'hub_retries')); } - $data = array('sub' => clone($this), + // We dare not clone() as when the clone is discarded it'll + // destroy the result data for the parent query. + // @fixme use clone() again when it's safe to copy an + // individual item from a multi-item query again. + $sub = HubSub::staticGet($this->topic, $this->callback); + $data = array('sub' => $sub, 'atom' => $atom, 'retries' => $retries); + common_log(LOG_INFO, "Queuing PuSH: $this->topic to $this->callback"); $qm = QueueManager::get(); $qm->enqueue($data, 'hubout'); } diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php index 96900d876..5a46aeeb6 100644 --- a/plugins/OStatus/classes/Magicsig.php +++ b/plugins/OStatus/classes/Magicsig.php @@ -146,8 +146,10 @@ class Magicsig extends Memcached_DataObject $mod = base64_url_decode($matches[1]); $exp = base64_url_decode($matches[2]); - if ($matches[4]) { + if (!empty($matches[4])) { $private_exp = base64_url_decode($matches[4]); + } else { + $private_exp = false; } $params['public_key'] = new Crypt_RSA_KEY($mod, $exp, 'public'); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 7b1aec76b..a33e95d93 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -698,7 +698,7 @@ class Ostatus_profile extends Memcached_DataObject { // Get the canonical feed URI and check it $discover = new FeedDiscovery(); - if ($hints['feedurl']) { + if (isset($hints['feedurl'])) { $feeduri = $hints['feedurl']; $feeduri = $discover->discoverFromFeedURL($feeduri); } else { @@ -1145,7 +1145,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($poco)) { $url = $poco->getPrimaryURL(); - if ($url->type == 'homepage') { + if ($url && $url->type == 'homepage') { $homepage = $url->value; } } diff --git a/plugins/OStatus/lib/discovery.php b/plugins/OStatus/lib/discovery.php index 388df0a28..f8449b309 100644 --- a/plugins/OStatus/lib/discovery.php +++ b/plugins/OStatus/lib/discovery.php @@ -94,7 +94,7 @@ class Discovery $links = call_user_func(array($class, 'discover'), $uri); if ($link = Discovery::getService($links, Discovery::LRDD_REL)) { // Load the LRDD XRD - if ($link['template']) { + if (!empty($link['template'])) { $xrd_uri = Discovery::applyTemplate($link['template'], $uri); } else { $xrd_uri = $link['href']; diff --git a/plugins/OStatus/lib/xrd.php b/plugins/OStatus/lib/xrd.php index 16d27f8eb..85df26c54 100644 --- a/plugins/OStatus/lib/xrd.php +++ b/plugins/OStatus/lib/xrd.php @@ -53,17 +53,22 @@ class XRD $xrd = new XRD(); $dom = new DOMDocument(); - $dom->loadXML($xml); + if (!$dom->loadXML($xml)) { + throw new Exception("Invalid XML"); + } $xrd_element = $dom->getElementsByTagName('XRD')->item(0); // Check for host-meta host - $host = $xrd_element->getElementsByTagName('Host')->item(0)->nodeValue; + $host = $xrd_element->getElementsByTagName('Host')->item(0); if ($host) { - $xrd->host = $host; + $xrd->host = $host->nodeValue; } // Loop through other elements foreach ($xrd_element->childNodes as $node) { + if (!($node instanceof DOMElement)) { + continue; + } switch ($node->tagName) { case 'Expires': $xrd->expires = $node->nodeValue; @@ -156,20 +161,20 @@ class XRD function saveLink($doc, $link) { $link_element = $doc->createElement('Link'); - if ($link['rel']) { + if (!empty($link['rel'])) { $link_element->setAttribute('rel', $link['rel']); } - if ($link['type']) { + if (!empty($link['type'])) { $link_element->setAttribute('type', $link['type']); } - if ($link['href']) { + if (!empty($link['href'])) { $link_element->setAttribute('href', $link['href']); } - if ($link['template']) { + if (!empty($link['template'])) { $link_element->setAttribute('template', $link['template']); } - if (is_array($link['title'])) { + if (!empty($link['title']) && is_array($link['title'])) { foreach($link['title'] as $title) { $title = $doc->createElement('Title', $title); $link_element->appendChild($title); diff --git a/plugins/OStatus/locale/OStatus.po b/plugins/OStatus/locale/OStatus.po index dedc018e3..ee19cf3db 100644 --- a/plugins/OStatus/locale/OStatus.po +++ b/plugins/OStatus/locale/OStatus.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-12-07 20:38-0800\n" +"POT-Creation-Date: 2010-03-01 14:08-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -16,89 +16,297 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: tests/gettext-speedtest.php:57 FeedSubPlugin.php:76 -msgid "Feeds" +#: actions/groupsalmon.php:51 +msgid "Can't accept remote posts for a remote group." +msgstr "" + +#: actions/groupsalmon.php:123 +msgid "Can't read profile to set up group membership." msgstr "" -#: FeedSubPlugin.php:77 -msgid "Feed subscription options" +#: actions/groupsalmon.php:126 actions/groupsalmon.php:169 +msgid "Groups can't join groups." msgstr "" -#: feedmunger.php:215 +#: actions/groupsalmon.php:153 #, php-format -msgid "New post: \"%1$s\" %2$s" +msgid "Could not join remote user %1$s to group %2$s." msgstr "" -#: actions/feedsubsettings.php:41 -msgid "Feed subscriptions" +#: actions/groupsalmon.php:166 +msgid "Can't read profile to cancel group membership." msgstr "" -#: actions/feedsubsettings.php:52 -msgid "" -"You can subscribe to feeds from other sites; updates will appear in your " -"personal timeline." +#: actions/groupsalmon.php:182 +#, php-format +msgid "Could not remove remote user %1$s from group %2$s." +msgstr "" + +#: actions/ostatusinit.php:40 +msgid "You can use the local subscription!" +msgstr "" + +#: actions/ostatusinit.php:61 +msgid "There was a problem with your session token. Try again, please." +msgstr "" + +#: actions/ostatusinit.php:79 actions/ostatussub.php:439 +msgid "Subscribe to user" +msgstr "" + +#: actions/ostatusinit.php:97 +#, php-format +msgid "Subscribe to %s" msgstr "" -#: actions/feedsubsettings.php:96 +#: actions/ostatusinit.php:102 +msgid "User nickname" +msgstr "" + +#: actions/ostatusinit.php:103 +msgid "Nickname of the user you want to follow" +msgstr "" + +#: actions/ostatusinit.php:106 +msgid "Profile Account" +msgstr "" + +#: actions/ostatusinit.php:107 +msgid "Your account id (i.e. user@identi.ca)" +msgstr "" + +#: actions/ostatusinit.php:110 actions/ostatussub.php:115 +#: OStatusPlugin.php:205 msgid "Subscribe" msgstr "" -#: actions/feedsubsettings.php:98 +#: actions/ostatusinit.php:128 +msgid "Must provide a remote profile." +msgstr "" + +#: actions/ostatusinit.php:138 +msgid "Couldn't look up OStatus account profile." +msgstr "" + +#: actions/ostatusinit.php:153 +msgid "Couldn't confirm remote profile address." +msgstr "" + +#: actions/ostatusinit.php:171 +msgid "OStatus Connect" +msgstr "" + +#: actions/ostatussub.php:68 +msgid "Address or profile URL" +msgstr "" + +#: actions/ostatussub.php:70 +msgid "Enter the profile URL of a PubSubHubbub-enabled feed" +msgstr "" + +#: actions/ostatussub.php:74 msgid "Continue" msgstr "" -#: actions/feedsubsettings.php:151 -msgid "Empty feed URL!" +#: actions/ostatussub.php:112 OStatusPlugin.php:503 +msgid "Join" +msgstr "" + +#: actions/ostatussub.php:113 +msgid "Join this group" +msgstr "" + +#: actions/ostatussub.php:116 +msgid "Subscribe to this user" +msgstr "" + +#: actions/ostatussub.php:137 +msgid "You are already subscribed to this user." +msgstr "" + +#: actions/ostatussub.php:165 +msgid "You are already a member of this group." msgstr "" -#: actions/feedsubsettings.php:161 +#: actions/ostatussub.php:286 +msgid "Empty remote profile URL!" +msgstr "" + +#: actions/ostatussub.php:297 +msgid "Invalid address format." +msgstr "" + +#: actions/ostatussub.php:302 msgid "Invalid URL or could not reach server." msgstr "" -#: actions/feedsubsettings.php:164 +#: actions/ostatussub.php:304 msgid "Cannot read feed; server returned error." msgstr "" -#: actions/feedsubsettings.php:167 +#: actions/ostatussub.php:306 msgid "Cannot read feed; server returned an empty page." msgstr "" -#: actions/feedsubsettings.php:170 +#: actions/ostatussub.php:308 msgid "Bad HTML, could not find feed link." msgstr "" -#: actions/feedsubsettings.php:173 +#: actions/ostatussub.php:310 msgid "Could not find a feed linked from this URL." msgstr "" -#: actions/feedsubsettings.php:176 +#: actions/ostatussub.php:312 msgid "Not a recognized feed type." msgstr "" -#: actions/feedsubsettings.php:180 -msgid "Bad feed URL." +#: actions/ostatussub.php:315 +#, php-format +msgid "Bad feed URL: %s %s" +msgstr "" + +#. TRANS: OStatus remote group subscription dialog error. +#: actions/ostatussub.php:336 +msgid "Already a member!" msgstr "" -#: actions/feedsubsettings.php:188 -msgid "Feed is not PuSH-enabled; cannot subscribe." +#. TRANS: OStatus remote group subscription dialog error. +#: actions/ostatussub.php:346 +msgid "Remote group join failed!" msgstr "" -#: actions/feedsubsettings.php:208 -msgid "Feed subscription failed! Bad response from hub." +#. TRANS: OStatus remote group subscription dialog error. +#: actions/ostatussub.php:350 +msgid "Remote group join aborted!" msgstr "" -#: actions/feedsubsettings.php:218 +#. TRANS: OStatus remote subscription dialog error. +#: actions/ostatussub.php:356 msgid "Already subscribed!" msgstr "" -#: actions/feedsubsettings.php:220 -msgid "Feed subscribed!" +#. TRANS: OStatus remote subscription dialog error. +#: actions/ostatussub.php:361 +msgid "Remote subscription failed!" msgstr "" -#: actions/feedsubsettings.php:222 -msgid "Feed subscription failed!" +#. TRANS: Page title for OStatus remote subscription form +#: actions/ostatussub.php:459 +msgid "Authorize subscription" +msgstr "" + +#: actions/ostatussub.php:470 +msgid "" +"You can subscribe to users from other supported sites. Paste their address " +"or profile URI below:" +msgstr "" + +#: classes/Ostatus_profile.php:789 +#, php-format +msgid "Tried to update avatar for unsaved remote profile %s" msgstr "" -#: actions/feedsubsettings.php:231 -msgid "Previewing feed:" +#: classes/Ostatus_profile.php:797 +#, php-format +msgid "Unable to fetch avatar from %s" +msgstr "" + +#: lib/salmonaction.php:41 +msgid "This method requires a POST." +msgstr "" + +#: lib/salmonaction.php:45 +msgid "Salmon requires application/magic-envelope+xml" +msgstr "" + +#: lib/salmonaction.php:55 +msgid "Salmon signature verification failed." +msgstr "" + +#: lib/salmonaction.php:66 +msgid "Salmon post must be an Atom entry." +msgstr "" + +#: lib/salmonaction.php:114 +msgid "Unrecognized activity type." +msgstr "" + +#: lib/salmonaction.php:122 +msgid "This target doesn't understand posts." +msgstr "" + +#: lib/salmonaction.php:127 +msgid "This target doesn't understand follows." +msgstr "" + +#: lib/salmonaction.php:132 +msgid "This target doesn't understand unfollows." +msgstr "" + +#: lib/salmonaction.php:137 +msgid "This target doesn't understand favorites." +msgstr "" + +#: lib/salmonaction.php:142 +msgid "This target doesn't understand unfavorites." +msgstr "" + +#: lib/salmonaction.php:147 +msgid "This target doesn't understand share events." +msgstr "" + +#: lib/salmonaction.php:152 +msgid "This target doesn't understand joins." +msgstr "" + +#: lib/salmonaction.php:157 +msgid "This target doesn't understand leave events." +msgstr "" + +#: OStatusPlugin.php:319 +#, php-format +msgid "Sent from %s via OStatus" +msgstr "" + +#: OStatusPlugin.php:371 +msgid "Could not set up remote subscription." +msgstr "" + +#: OStatusPlugin.php:487 +msgid "Could not set up remote group membership." +msgstr "" + +#: OStatusPlugin.php:504 +#, php-format +msgid "%s has joined group %s." +msgstr "" + +#: OStatusPlugin.php:512 +msgid "Failed joining remote group." +msgstr "" + +#: OStatusPlugin.php:553 +msgid "Leave" +msgstr "" + +#: OStatusPlugin.php:554 +#, php-format +msgid "%s has left group %s." +msgstr "" + +#: OStatusPlugin.php:685 +msgid "Subscribe to remote user" +msgstr "" + +#: OStatusPlugin.php:726 +msgid "Profile update" +msgstr "" + +#: OStatusPlugin.php:727 +#, php-format +msgid "%s has updated their profile page." +msgstr "" + +#: tests/gettext-speedtest.php:57 +msgid "Feeds" msgstr "" diff --git a/plugins/OStatus/scripts/updateostatus.php b/plugins/OStatus/scripts/updateostatus.php new file mode 100644 index 000000000..d553a7d62 --- /dev/null +++ b/plugins/OStatus/scripts/updateostatus.php @@ -0,0 +1,127 @@ +#!/usr/bin/env php +<?php +/* + * StatusNet - a distributed open-source microblogging tool + * Copyright (C) 2008, 2009, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); + +$shortoptions = 'i:n:a'; +$longoptions = array('id=', 'nickname=', 'all'); + +$helptext = <<<END_OF_UPDATEOSTATUS_HELP +updateostatus.php [options] +update the OMB subscriptions of a user to use OStatus if possible + + -i --id ID of user to update + -n --nickname nickname of the user to update + -a --all update all + +END_OF_UPDATEOSTATUS_HELP; + +require_once INSTALLDIR.'/scripts/commandline.inc'; + +try { + $user = null; + + if (have_option('i', 'id')) { + $id = get_option_value('i', 'id'); + $user = User::staticGet('id', $id); + if (empty($user)) { + throw new Exception("Can't find user with id '$id'."); + } + updateProfileURL($user); + } else if (have_option('n', 'nickname')) { + $nickname = get_option_value('n', 'nickname'); + $user = User::staticGet('nickname', $nickname); + if (empty($user)) { + throw new Exception("Can't find user with nickname '$nickname'"); + } + updateProfileURL($user); + } else if (have_option('a', 'all')) { + $user = new User(); + if ($user->find()) { + while ($user->fetch()) { + updateOStatus($user); + } + } + } else { + show_help(); + exit(1); + } +} catch (Exception $e) { + print $e->getMessage()."\n"; + exit(1); +} + +function updateOStatus($user) +{ + if (!have_option('q', 'quiet')) { + echo "{$user->nickname}..."; + } + + $up = $user->getProfile(); + + $sp = $user->getSubscriptions(); + + $rps = array(); + + while ($sp->fetch()) { + $remote = Remote_profile::staticGet('id', $sp->id); + + if (!empty($remote)) { + $rps[] = clone($sp); + } + } + + if (!have_option('q', 'quiet')) { + echo count($rps) . "\n"; + } + + foreach ($rps as $rp) { + try { + if (!have_option('q', 'quiet')) { + echo "Checking {$rp->nickname}..."; + } + + $op = Ostatus_profile::ensureProfile($rp->profileurl); + + if (empty($op)) { + echo "can't convert.\n"; + continue; + } else { + if (!have_option('q', 'quiet')) { + echo "Converting..."; + } + Subscription::cancel($up, $rp); + Subscription::start($up, $op->localProfile()); + if (!have_option('q', 'quiet')) { + echo "done.\n"; + } + } + + } catch (Exception $e) { + if (!have_option('q', 'quiet')) { + echo "fail.\n"; + } + continue; + common_log(LOG_WARNING, "Couldn't convert OMB subscription (" . $up->nickname . ", " . $rp->nickname . + ") to OStatus: " . $e->getMessage()); + continue; + } + } +} diff --git a/plugins/RegisterThrottle/RegisterThrottlePlugin.php b/plugins/RegisterThrottle/RegisterThrottlePlugin.php new file mode 100644 index 000000000..05709b780 --- /dev/null +++ b/plugins/RegisterThrottle/RegisterThrottlePlugin.php @@ -0,0 +1,249 @@ +<?php +/** + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * Throttle registration by IP address + * + * PHP version 5 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Spam + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Throttle registration by IP address + * + * We a) record IP address of registrants and b) throttle registrations. + * + * @category Spam + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class RegisterThrottlePlugin extends Plugin +{ + /** + * Array of time spans in seconds to limits. + * + * Default is 3 registrations per hour, 5 per day, 10 per week. + */ + + public $regLimits = array(604800 => 10, // per week + 86400 => 5, // per day + 3600 => 3); // per hour + + /** + * Database schema setup + * + * We store user registrations in a table registration_ip. + * + * @return boolean hook value; true means continue processing, false means stop. + */ + + function onCheckSchema() + { + $schema = Schema::get(); + + // For storing user-submitted flags on profiles + + $schema->ensureTable('registration_ip', + array(new ColumnDef('user_id', 'integer', null, + false, 'PRI'), + new ColumnDef('ipaddress', 'varchar', 15, false, 'MUL'), + new ColumnDef('created', 'timestamp', null, false, 'MUL'))); + + return true; + } + + /** + * Load related modules when needed + * + * @param string $cls Name of the class to be loaded + * + * @return boolean hook value; true means continue processing, false means stop. + */ + + function onAutoload($cls) + { + $dir = dirname(__FILE__); + + switch ($cls) + { + case 'Registration_ip': + include_once $dir . '/'.$cls.'.php'; + return false; + default: + return true; + } + } + + /** + * Called when someone tries to register. + * + * We check the IP here to determine if it goes over any of our + * configured limits. + * + * @param Action $action Action that is being executed + * + * @return boolean hook value + * + */ + + function onStartRegistrationTry($action) + { + $ipaddress = $this->_getIpAddress(); + + if (empty($ipaddress)) { + throw new ServerException(_m('Cannot find IP address.')); + } + + foreach ($this->regLimits as $seconds => $limit) { + + $this->debug("Checking $seconds ($limit)"); + + $reg = $this->_getNthReg($ipaddress, $limit); + + if (!empty($reg)) { + $this->debug("Got a {$limit}th registration."); + $regtime = strtotime($reg->created); + $now = time(); + $this->debug("Comparing {$regtime} to {$now}"); + if ($now - $regtime < $seconds) { + throw new Exception(_("Too many registrations. Take a break and try again later.")); + } + } + } + + return true; + } + + /** + * Called after someone registers. + * + * We record the successful registration and IP address. + * + * @param Action $action Action that is being executed + * + * @return boolean hook value + * + */ + + function onEndRegistrationTry($action) + { + $ipaddress = $this->_getIpAddress(); + + if (empty($ipaddress)) { + throw new ServerException(_m('Cannot find IP address.')); + } + + $user = common_current_user(); + + if (empty($user)) { + throw new ServerException(_m('Cannot find user after successful registration.')); + } + + $reg = new Registration_ip(); + + $reg->user_id = $user->id; + $reg->ipaddress = $ipaddress; + + $result = $reg->insert(); + + if (!$result) { + common_log_db_error($reg, 'INSERT', __FILE__); + // @todo throw an exception? + } + + return true; + } + + /** + * Check the version of the plugin. + * + * @param array &$versions Version array. + * + * @return boolean hook value + */ + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'RegisterThrottle', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:RegisterThrottle', + 'description' => + _m('Throttles excessive registration from a single IP.')); + return true; + } + + /** + * Gets the current IP address. + * + * @return string IP address or null if not found. + */ + + private function _getIpAddress() + { + $keys = array('HTTP_X_FORWARDED_FOR', + 'CLIENT-IP', + 'REMOTE_ADDR'); + + foreach ($keys as $k) { + if (!empty($_SERVER[$k])) { + return $_SERVER[$k]; + } + } + + return null; + } + + /** + * Gets the Nth registration with the given IP address. + * + * @param string $ipaddress Address to key on + * @param integer $n Nth address + * + * @return Registration_ip nth registration or null if not found. + */ + + private function _getNthReg($ipaddress, $n) + { + $reg = new Registration_ip(); + + $reg->ipaddress = $ipaddress; + + $reg->orderBy('created DESC'); + $reg->limit($n - 1, 1); + + if ($reg->find(true)) { + return $reg; + } else { + return null; + } + } +} diff --git a/plugins/RegisterThrottle/Registration_ip.php b/plugins/RegisterThrottle/Registration_ip.php new file mode 100644 index 000000000..7e61d089e --- /dev/null +++ b/plugins/RegisterThrottle/Registration_ip.php @@ -0,0 +1,124 @@ +<?php +/** + * Data class for storing IP addresses of new registrants. + * + * PHP version 5 + * + * @category Data + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; + +/** + * Data class for storing IP addresses of new registrants. + * + * @category Spam + * @package StatusNet + * @author Evan Prodromou <evan@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class Registration_ip extends Memcached_DataObject +{ + public $__table = 'registration_ip'; // table name + public $user_id; // int(4) primary_key not_null + public $ipaddress; // varchar(15) + public $created; // timestamp + + /** + * Get an instance by key + * + * @param string $k Key to use to lookup (usually 'user_id' for this class) + * @param mixed $v Value to lookup + * + * @return User_greeting_count object found, or null for no hits + * + */ + + function staticGet($k, $v=null) + { + return Memcached_DataObject::staticGet('Registration_ip', $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * @return array array of column definitions + */ + + function table() + { + return array('user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'ipaddress' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'created' => DB_DATAOBJECT_MYSQLTIMESTAMP + DB_DATAOBJECT_NOTNULL); + } + + /** + * return key definitions for DB_DataObject + * + * DB_DataObject needs to know about keys that the table has; this function + * defines them. + * + * @return array key definitions + */ + + function keys() + { + return array('user_id' => 'K'); + } + + /** + * return key definitions for Memcached_DataObject + * + * Our caching system uses the same key definitions, but uses a different + * method to get them. + * + * @return array key definitions + */ + + function keyTypes() + { + return $this->keys(); + } + + /** + * Magic formula for non-autoincrementing integer primary keys + * + * If a table has a single integer column as its primary key, DB_DataObject + * assumes that the column is auto-incrementing and makes a sequence table + * to do this incrementation. Since we don't need this for our class, we + * overload this method and return the magic formula that DB_DataObject needs. + * + * @return array magic three-false array that stops auto-incrementing. + */ + + function sequenceKey() + { + return array(false, false, false); + } +} diff --git a/plugins/TwitterBridge/README b/plugins/TwitterBridge/README index d3bcda598..72278b32e 100644 --- a/plugins/TwitterBridge/README +++ b/plugins/TwitterBridge/README @@ -1,47 +1,89 @@ +Twitter Bridge Plugin +===================== + This Twitter "bridge" plugin allows you to integrate your StatusNet instance with Twitter. Installing it will allow your users to: - - automatically post notices to thier Twitter accounts + - automatically post notices to their Twitter accounts - automatically subscribe to other Twitter users who are also using your StatusNet install, if possible (requires running a daemon) - import their Twitter friends' tweets (requires running a daemon) + - allow users to authenticate using Twitter ('Sign in with Twitter') Installation ------------ -To enable the plugin, add the following to your config.php: - - addPlugin("TwitterBridge"); - -OAuth is used to to access protected resources on Twitter (as opposed to -HTTP Basic Auth)*. To use Twitter bridging you will need to register -your instance of StatusNet as an application on Twitter -(http://twitter.com/apps), and update the following variables in your -config.php with the consumer key and secret Twitter generates for you: - - $config['twitter']['consumer_key'] = 'YOURKEY'; - $config['twitter']['consumer_secret'] = 'YOURSECRET'; +OAuth 1.0a (http://oauth.net) is used to to access protected resources +on Twitter (as opposed to HTTP Basic Auth)*. To use Twitter bridging +you will need to register your instance of StatusNet as an application +on Twitter (http://twitter.com/apps). During the application +registration process your application will be assigned a "consumer" key +and secret, which the plugin will use to make OAuth requests to Twitter. +You can either pass the consumer key and secret in when you enable the +plugin, or set it using the Twitter administration panel. When registering your application with Twitter set the type to "Browser" and your Callback URL to: http://example.org/mublog/twitter/authorization -The default access type should be, "Read & Write". +(Change "example.org" to your site domain and "mublog" to your site +path.) + +The default access type should be "Read & Write". + +To enable the plugin, add the following to your config.php: + + addPlugin( + 'TwitterBridge', + array( + 'consumer_key' => 'YOUR_CONSUMER_KEY', + 'consumer_secret' => 'YOUR_CONSUMER_SECRET' + ) + ); * Note: The plugin will still push notices to Twitter for users who - have previously setup the Twitter bridge using their Twitter name and - password under an older versions of StatusNet, but all new Twitter + have previously set up the Twitter bridge using their Twitter name and + password under an older version of StatusNet, but all new Twitter bridge connections will use OAuth. -Deamons +Administration panel +-------------------- + +As of StatusNet 0.9.0 there is a new administration panel that allows +you to configure Twitter bridge settings within StatusNet itself, +instead of having to specify them manually in your config.php. To enable +the administration panel, you will need to add it to the list of active +administration panels. You can do this via your config.php. E.g.: + + $config['admin']['panels'][] = 'twitter'; + +And to access it, you'll need to use a user with the "administrator" +role (see: scripts/userrole.php). + +Sign in with Twitter +-------------------- + +With 0.9.0, StatusNet optionally allows users to register and +authenticate using their Twitter credentials via the "Sign in with +Twitter" pattern described here: + + http://apiwiki.twitter.com/Sign-in-with-Twitter + +The option is _on_ by default when you install the plugin, but it can +disabled via the Twitter bridge administration panel, or by adding the +following line to your config.php: + + $config['twitter']['signin'] = false; + +Daemons ------- -For friend syncing and importing notices running two additional daemon -scripts is necessary (synctwitterfriends.php and -twitterstatusfetcher.php). +For friend syncing and importing Twitter tweets, running two +additional daemon scripts is necessary: synctwitterfriends.php and +twitterstatusfetcher.php. -In the daemons subidrectory of the plugin are three scripts: +In the daemons subdirectory of the plugin are three scripts: * Twitter Friends Syncing (daemons/synctwitterfriends.php) @@ -51,13 +93,13 @@ subscribe to "friends" (people they "follow") on Twitter who also have accounts on your StatusNet system, and who have previously set up a link for automatically posting notices to Twitter. -The plugin will try to start this daemon when you run -scripts/startdaemons.sh. +The plugin will start this daemon when you run scripts/startdaemons.sh. * Importing statuses from Twitter (daemons/twitterstatusfetcher.php) -To allow your users to import their friends' Twitter statuses, you will -need to enable the bidirectional Twitter bridge in your config.php: +You can allow uses to enable importing of your friends' Twitter +timelines either in the Twitter bridge administration panel or in your +config.php using the following configuration line: $config['twitterimport']['enabled'] = true; @@ -66,8 +108,9 @@ other daemons when you run scripts/startdaemons.sh. Additionally, you will want to set the integration source variable, which will keep notices posted to Twitter via StatusNet from looping -back. The integration source should be set to the name of your -application, exactly as you specified it on the settings page for your +back. You can do this in the Twitter bridge administration panel, or +via config.php. The integration source should be set to the name of your +application _exactly_ as you specified it on the settings page for your StatusNet application on Twitter, e.g.: $config['integration']['source'] = 'YourApp'; @@ -79,7 +122,9 @@ set up Twitter bridging. It's not strictly necessary to run this queue handler, and sites that haven't enabled queuing are still able to push notices to Twitter, but -for larger sites and sites that wish to improve performance, this -script allows notices to be sent "offline" via a separate process. +for larger sites and sites that wish to improve performance the script +allows notices to be sent "offline" via a separate process. -The plugin will start this script when you run scripts/startdaemons.sh. +StatusNet will automatically use the TwitterQueueHandler if you have +enabled the queuing subsystem. See the "Queues and daemons" section of +the main README file for more information about how to do that. diff --git a/plugins/TwitterBridge/TwitterBridgePlugin.php b/plugins/TwitterBridge/TwitterBridgePlugin.php index c7f57ffc7..6ce69d5e2 100644 --- a/plugins/TwitterBridge/TwitterBridgePlugin.php +++ b/plugins/TwitterBridge/TwitterBridgePlugin.php @@ -23,7 +23,7 @@ * @author Julien C <chaumond@gmail.com> * @copyright 2009-2010 Control Yourself, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ + * @link http://status.net/ */ if (!defined('STATUSNET')) { @@ -32,8 +32,6 @@ if (!defined('STATUSNET')) { require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php'; -define('TWITTERBRIDGEPLUGIN_VERSION', '0.9'); - /** * Plugin for sending and importing Twitter statuses * @@ -44,19 +42,41 @@ define('TWITTERBRIDGEPLUGIN_VERSION', '0.9'); * @author Zach Copley <zach@status.net> * @author Julien C <chaumond@gmail.com> * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ + * @link http://status.net/ * @link http://twitter.com/ */ class TwitterBridgePlugin extends Plugin { + + const VERSION = STATUSNET_VERSION; + /** * Initializer for the plugin. */ - function __construct() + function initialize() { - parent::__construct(); + // Allow the key and secret to be passed in + // Control panel will override + + if (isset($this->consumer_key)) { + $key = common_config('twitter', 'consumer_key'); + if (empty($key)) { + Config::save('twitter', 'consumer_key', $this->consumer_key); + } + } + + if (isset($this->consumer_secret)) { + $secret = common_config('twitter', 'consumer_secret'); + if (empty($secret)) { + Config::save( + 'twitter', + 'consumer_secret', + $this->consumer_secret + ); + } + } } /** @@ -71,10 +91,17 @@ class TwitterBridgePlugin extends Plugin function onRouterInitialized($m) { - $m->connect('twitter/authorization', - array('action' => 'twitterauthorization')); + $m->connect( + 'twitter/authorization', + array('action' => 'twitterauthorization') + ); $m->connect('settings/twitter', array('action' => 'twittersettings')); - $m->connect('main/twitterlogin', array('action' => 'twitterlogin')); + + if (common_config('twitter', 'signin')) { + $m->connect('main/twitterlogin', array('action' => 'twitterlogin')); + } + + $m->connect('admin/twitter', array('action' => 'twitteradminpanel')); return true; } @@ -88,13 +115,16 @@ class TwitterBridgePlugin extends Plugin */ function onEndLoginGroupNav(&$action) { - $action_name = $action->trimmed('action'); - $action->menuItem(common_local_url('twitterlogin'), - _('Twitter'), - _('Login or register using Twitter'), - 'twitterlogin' === $action_name); + if (common_config('twitter', 'signin')) { + $action->menuItem( + common_local_url('twitterlogin'), + _m('Twitter'), + _m('Login or register using Twitter'), + 'twitterlogin' === $action_name + ); + } return true; } @@ -110,10 +140,12 @@ class TwitterBridgePlugin extends Plugin { $action_name = $action->trimmed('action'); - $action->menuItem(common_local_url('twittersettings'), - _m('Twitter'), - _m('Twitter integration options'), - $action_name === 'twittersettings'); + $action->menuItem( + common_local_url('twittersettings'), + _m('Twitter'), + _m('Twitter integration options'), + $action_name === 'twittersettings' + ); return true; } @@ -132,6 +164,7 @@ class TwitterBridgePlugin extends Plugin case 'TwittersettingsAction': case 'TwitterauthorizationAction': case 'TwitterloginAction': + case 'TwitteradminpanelAction': include_once INSTALLDIR . '/plugins/TwitterBridge/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; return false; @@ -173,12 +206,18 @@ class TwitterBridgePlugin extends Plugin */ function onGetValidDaemons($daemons) { - array_push($daemons, INSTALLDIR . - '/plugins/TwitterBridge/daemons/synctwitterfriends.php'); + array_push( + $daemons, + INSTALLDIR + . '/plugins/TwitterBridge/daemons/synctwitterfriends.php' + ); if (common_config('twitterimport', 'enabled')) { - array_push($daemons, INSTALLDIR - . '/plugins/TwitterBridge/daemons/twitterstatusfetcher.php'); + array_push( + $daemons, + INSTALLDIR + . '/plugins/TwitterBridge/daemons/twitterstatusfetcher.php' + ); } return true; @@ -197,17 +236,55 @@ class TwitterBridgePlugin extends Plugin return true; } + /** + * Add a Twitter tab to the admin panel + * + * @param Widget $nav Admin panel nav + * + * @return boolean hook value + */ + + function onEndAdminPanelNav($nav) + { + if (AdminPanelAction::canAdmin('twitter')) { + + $action_name = $nav->action->trimmed('action'); + + $nav->out->menuItem( + common_local_url('twitteradminpanel'), + _m('Twitter'), + _m('Twitter bridge configuration'), + $action_name == 'twitteradminpanel', + 'nav_twitter_admin_panel' + ); + } + + return true; + } + + /** + * Plugin version data + * + * @param array &$versions array of version blocks + * + * @return boolean hook value + */ + function onPluginVersion(&$versions) { - $versions[] = array('name' => 'TwitterBridge', - 'version' => TWITTERBRIDGEPLUGIN_VERSION, - 'author' => 'Zach Copley', - 'homepage' => 'http://status.net/wiki/Plugin:TwitterBridge', - 'rawdescription' => - _m('The Twitter "bridge" plugin allows you to integrate ' . - 'your StatusNet instance with ' . - '<a href="http://twitter.com/">Twitter</a>.')); + $versions[] = array( + 'name' => 'TwitterBridge', + 'version' => self::VERSION, + 'author' => 'Zach Copley, Julien C', + 'homepage' => 'http://status.net/wiki/Plugin:TwitterBridge', + 'rawdescription' => _m( + 'The Twitter "bridge" plugin allows you to integrate ' . + 'your StatusNet instance with ' . + '<a href="http://twitter.com/">Twitter</a>.' + ) + ); return true; } } + diff --git a/plugins/TwitterBridge/twitteradminpanel.php b/plugins/TwitterBridge/twitteradminpanel.php new file mode 100644 index 000000000..b22e6d99f --- /dev/null +++ b/plugins/TwitterBridge/twitteradminpanel.php @@ -0,0 +1,280 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * Twitter bridge administration panel + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Settings + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Administer global Twitter bridge settings + * + * @category Admin + * @package StatusNet + * @author Zach Copley <zach@status.net> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class TwitteradminpanelAction extends AdminPanelAction +{ + /** + * Returns the page title + * + * @return string page title + */ + + function title() + { + return _m('Twitter'); + } + + /** + * Instructions for using this form. + * + * @return string instructions + */ + + function getInstructions() + { + return _m('Twitter bridge settings'); + } + + /** + * Show the Twitter admin panel form + * + * @return void + */ + + function showForm() + { + $form = new TwitterAdminPanelForm($this); + $form->show(); + return; + } + + /** + * Save settings from the form + * + * @return void + */ + + function saveSettings() + { + static $settings = array( + 'twitter' => array('consumer_key', 'consumer_secret'), + 'integration' => array('source') + ); + + static $booleans = array( + 'twitter' => array('signin'), + 'twitterimport' => array('enabled') + ); + + $values = array(); + + foreach ($settings as $section => $parts) { + foreach ($parts as $setting) { + $values[$section][$setting] + = $this->trimmed($setting); + } + } + + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { + $values[$section][$setting] + = ($this->boolean($setting)) ? 1 : 0; + } + } + + // This throws an exception on validation errors + + $this->validate($values); + + // assert(all values are valid); + + $config = new Config(); + + $config->query('BEGIN'); + + foreach ($settings as $section => $parts) { + foreach ($parts as $setting) { + Config::save($section, $setting, $values[$section][$setting]); + } + } + + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { + Config::save($section, $setting, $values[$section][$setting]); + } + } + + $config->query('COMMIT'); + + return; + } + + function validate(&$values) + { + // Validate consumer key and secret (can't be too long) + + if (mb_strlen($values['twitter']['consumer_key']) > 255) { + $this->clientError( + _m("Invalid consumer key. Max length is 255 characters.") + ); + } + + if (mb_strlen($values['twitter']['consumer_secret']) > 255) { + $this->clientError( + _m("Invalid consumer secret. Max length is 255 characters.") + ); + } + } +} + +class TwitterAdminPanelForm extends AdminForm +{ + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'twitteradminpanel'; + } + + /** + * class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_settings'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('twitteradminpanel'); + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + $this->out->elementStart( + 'fieldset', + array('id' => 'settings_twitter-application') + ); + $this->out->element('legend', null, _m('Twitter application settings')); + $this->out->elementStart('ul', 'form_data'); + + $this->li(); + $this->input( + 'consumer_key', + _m('Consumer key'), + _m('Consumer key assigned by Twitter'), + 'twitter' + ); + $this->unli(); + + $this->li(); + $this->input( + 'consumer_secret', + _m('Consumer secret'), + _m('Consumer secret assigned by Twitter'), + 'twitter' + ); + $this->unli(); + + $this->li(); + $this->input( + 'source', + _m('Integration source'), + _m('Name of your Twitter application'), + 'integration' + ); + $this->unli(); + + $this->out->elementEnd('ul'); + $this->out->elementEnd('fieldset'); + + $this->out->elementStart( + 'fieldset', + array('id' => 'settings_twitter-options') + ); + $this->out->element('legend', null, _m('Options')); + + $this->out->elementStart('ul', 'form_data'); + + $this->li(); + + $this->out->checkbox( + 'signin', _m('Enable "Sign-in with Twitter"'), + (bool) $this->value('signin', 'twitter'), + _m('Allow users to login with their Twitter credentials') + ); + $this->unli(); + + $this->li(); + $this->out->checkbox( + 'enabled', _m('Enable Twitter import'), + (bool) $this->value('enabled', 'twitterimport'), + _m('Allow users to import their Twitter friends\' timelines') + ); + $this->unli(); + + $this->out->elementEnd('ul'); + + $this->out->elementEnd('fieldset'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Save'), 'submit', null, _('Save Twitter settings')); + } +} diff --git a/plugins/TwitterBridge/twitterauthorization.php b/plugins/TwitterBridge/twitterauthorization.php index cabf69d7a..c93f6666b 100644 --- a/plugins/TwitterBridge/twitterauthorization.php +++ b/plugins/TwitterBridge/twitterauthorization.php @@ -47,7 +47,7 @@ require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php'; * @author Zach Copley <zach@status.net> * @author Julien C <chaumond@gmail.com> * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://laconi.ca/ + * @link http://status.net/ * */ class TwitterauthorizationAction extends Action |