Merge branch 'testing' of git@gitorious.org:statusnet/mainline into testing

This commit is contained in:
Evan Prodromou 2010-03-02 02:56:57 -05:00
commit f504a9237e
18 changed files with 856 additions and 109 deletions

View File

@ -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);

View File

@ -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'));

View File

@ -177,8 +177,8 @@ $default =
array('source' => 'StatusNet', # source attribute for Twitter
'taguri' => null), # base for tag URIs
'twitter' =>
array('enabled' => true,
'consumer_key' => null,
array('signin' => true,
'consumer_key' => null,
'consumer_secret' => null),
'cache' =>
array('base' => null),

View File

@ -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)

View File

@ -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;
@ -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
@ -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;
}

View File

@ -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'));
}
}

View File

@ -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'));

View File

@ -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);
}

View File

@ -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();

View File

@ -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');
}

View File

@ -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');

View File

@ -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;
}
}

View File

@ -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'];

View File

@ -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);

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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'));
}
}

View File

@ -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