summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorEvan Prodromou <evan@status.net>2010-06-01 13:51:41 -0700
committerEvan Prodromou <evan@status.net>2010-06-01 13:51:41 -0700
commita5de2152672f49ad95f114033ef6bf00ece9a0ab (patch)
treec31d031fb8ffb20b8497a80a7cf13958c50f5ad5 /plugins
parent6d8e01ad13dd9bc9e149f43a1eb88671d4737f4d (diff)
parent634752f0d262b4fb02456889250378fca084cd2e (diff)
Merge branch 'master' of gitorious.org:statusnet/mainline
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Facebook/README10
-rw-r--r--plugins/Facebook/facebook/facebook.php74
-rwxr-xr-xplugins/Facebook/facebook/facebookapi_php5_restlib.php56
-rw-r--r--plugins/Facebook/facebooksettings.php21
-rw-r--r--plugins/Facebook/facebookutil.php258
-rw-r--r--plugins/Mapstraction/MapstractionPlugin.php6
-rw-r--r--plugins/Mapstraction/usermap.js2
-rw-r--r--plugins/OpenID/OpenIDPlugin.php67
-rw-r--r--plugins/OpenID/extlib/README6
-rw-r--r--plugins/OpenID/extlib/teams-extension.php175
-rw-r--r--plugins/OpenID/finishaddopenid.php17
-rw-r--r--plugins/OpenID/finishopenidlogin.php16
-rw-r--r--plugins/OpenID/openid.php49
-rw-r--r--plugins/OpenID/openidadminpanel.php280
-rw-r--r--plugins/OpenID/openidlogin.php42
-rw-r--r--plugins/OpenID/openidsettings.php70
-rw-r--r--plugins/WikiHowProfile/README6
-rw-r--r--plugins/WikiHowProfile/WikiHowProfilePlugin.php196
18 files changed, 1122 insertions, 229 deletions
diff --git a/plugins/Facebook/README b/plugins/Facebook/README
index 14c1d3241..532f1d82e 100644
--- a/plugins/Facebook/README
+++ b/plugins/Facebook/README
@@ -38,11 +38,11 @@ editor or write them down.
In Facebook's application editor, specify the following URLs for your app:
-- Canvas Callback URL : http://example.net/mublog/facebook/app/
-- Post-Remove Callback URL: http://example.net/mublog/facebook/app/remove
-- Post-Add Redirect URL : http://apps.facebook.com/yourapp/
-- Canvas Page URL : http://apps.facebook.com/yourapp/
-- Connect URL : http://example.net/mublog/
+- Canvas Callback URL : http://example.net/mublog/facebook/app/
+- Post-Remove Callback URL : http://example.net/mublog/facebook/app/remove
+- Post-Authorize Redirect URL : http://apps.facebook.com/yourapp/
+- Canvas Page URL : http://apps.facebook.com/yourapp/
+- Connect URL : http://example.net/mublog/
*** ATTENTION ***
These URLs have changed slightly since StatusNet version 0.8.1,
diff --git a/plugins/Facebook/facebook/facebook.php b/plugins/Facebook/facebook/facebook.php
index 440706cbc..76696c1d5 100644
--- a/plugins/Facebook/facebook/facebook.php
+++ b/plugins/Facebook/facebook/facebook.php
@@ -45,7 +45,9 @@ class Facebook {
public $user;
public $profile_user;
public $canvas_user;
+ public $ext_perms = array();
protected $base_domain;
+
/*
* Create a Facebook client like this:
*
@@ -104,17 +106,17 @@ class Facebook {
*
* For nitty-gritty details of when each of these is used, check out
* http://wiki.developers.facebook.com/index.php/Verifying_The_Signature
- *
- * @param bool resolve_auth_token convert an auth token into a session
*/
- public function validate_fb_params($resolve_auth_token=true) {
+ public function validate_fb_params() {
$this->fb_params = $this->get_valid_fb_params($_POST, 48 * 3600, 'fb_sig');
// note that with preload FQL, it's possible to receive POST params in
// addition to GET, so use a different prefix to differentiate them
if (!$this->fb_params) {
$fb_params = $this->get_valid_fb_params($_GET, 48 * 3600, 'fb_sig');
- $fb_post_params = $this->get_valid_fb_params($_POST, 48 * 3600, 'fb_post_sig');
+ $fb_post_params = $this->get_valid_fb_params($_POST,
+ 48 * 3600, // 48 hours
+ 'fb_post_sig');
$this->fb_params = array_merge($fb_params, $fb_post_params);
}
@@ -128,6 +130,9 @@ class Facebook {
$this->fb_params['canvas_user'] : null;
$this->base_domain = isset($this->fb_params['base_domain']) ?
$this->fb_params['base_domain'] : null;
+ $this->ext_perms = isset($this->fb_params['ext_perms']) ?
+ explode(',', $this->fb_params['ext_perms'])
+ : array();
if (isset($this->fb_params['session_key'])) {
$session_key = $this->fb_params['session_key'];
@@ -141,13 +146,11 @@ class Facebook {
$this->set_user($user,
$session_key,
$expires);
- }
- // if no Facebook parameters were found in the GET or POST variables,
- // then fall back to cookies, which may have cached user information
- // Cookies are also used to receive session data via the Javascript API
- else if ($cookies =
- $this->get_valid_fb_params($_COOKIE, null, $this->api_key)) {
-
+ } else if ($cookies =
+ $this->get_valid_fb_params($_COOKIE, null, $this->api_key)) {
+ // if no Facebook parameters were found in the GET or POST variables,
+ // then fall back to cookies, which may have cached user information
+ // Cookies are also used to receive session data via the Javascript API
$base_domain_cookie = 'base_domain_' . $this->api_key;
if (isset($_COOKIE[$base_domain_cookie])) {
$this->base_domain = $_COOKIE[$base_domain_cookie];
@@ -160,25 +163,6 @@ class Facebook {
$cookies['session_key'],
$expires);
}
- // finally, if we received no parameters, but the 'auth_token' GET var
- // is present, then we are in the middle of auth handshake,
- // so go ahead and create the session
- else if ($resolve_auth_token && isset($_GET['auth_token']) &&
- $session = $this->do_get_session($_GET['auth_token'])) {
- if ($this->generate_session_secret &&
- !empty($session['secret'])) {
- $session_secret = $session['secret'];
- }
-
- if (isset($session['base_domain'])) {
- $this->base_domain = $session['base_domain'];
- }
-
- $this->set_user($session['uid'],
- $session['session_key'],
- $session['expires'],
- isset($session_secret) ? $session_secret : null);
- }
return !empty($this->fb_params);
}
@@ -309,11 +293,28 @@ class Facebook {
// require_add and require_install have been removed.
// see http://developer.facebook.com/news.php?blog=1&story=116 for more details
- public function require_login() {
- if ($user = $this->get_loggedin_user()) {
+ public function require_login($required_permissions = '') {
+ $user = $this->get_loggedin_user();
+ $has_permissions = true;
+
+ if ($required_permissions) {
+ $this->require_frame();
+ $permissions = array_map('trim', explode(',', $required_permissions));
+ foreach ($permissions as $permission) {
+ if (!in_array($permission, $this->ext_perms)) {
+ $has_permissions = false;
+ break;
+ }
+ }
+ }
+
+ if ($user && $has_permissions) {
return $user;
}
- $this->redirect($this->get_login_url(self::current_url(), $this->in_frame()));
+
+ $this->redirect(
+ $this->get_login_url(self::current_url(), $this->in_frame(),
+ $required_permissions));
}
public function require_frame() {
@@ -342,10 +343,11 @@ class Facebook {
return $page . '?' . http_build_query($params);
}
- public function get_login_url($next, $canvas) {
+ public function get_login_url($next, $canvas, $req_perms = '') {
$page = self::get_facebook_url().'/login.php';
- $params = array('api_key' => $this->api_key,
- 'v' => '1.0');
+ $params = array('api_key' => $this->api_key,
+ 'v' => '1.0',
+ 'req_perms' => $req_perms);
if ($next) {
$params['next'] = $next;
diff --git a/plugins/Facebook/facebook/facebookapi_php5_restlib.php b/plugins/Facebook/facebook/facebookapi_php5_restlib.php
index fa1088cd0..e249a326b 100755
--- a/plugins/Facebook/facebook/facebookapi_php5_restlib.php
+++ b/plugins/Facebook/facebook/facebookapi_php5_restlib.php
@@ -569,7 +569,7 @@ function toggleDisplay(id, type) {
return $this->call_method('facebook.events.invite',
array('eid' => $eid,
'uids' => $uids,
- 'personal_message', $personal_message));
+ 'personal_message' => $personal_message));
}
/**
@@ -1350,53 +1350,6 @@ function toggleDisplay(id, type) {
);
}
- /**
- * Dashboard API
- */
-
- /**
- * Set the news for the specified user.
- *
- * @param int $uid The user for whom you are setting news for
- * @param string $news Text of news to display
- *
- * @return bool Success
- */
- public function dashboard_setNews($uid, $news) {
- return $this->call_method('facebook.dashboard.setNews',
- array('uid' => $uid,
- 'news' => $news)
- );
- }
-
- /**
- * Get the current news of the specified user.
- *
- * @param int $uid The user to get the news of
- *
- * @return string The text of the current news for the user
- */
- public function dashboard_getNews($uid) {
- return json_decode(
- $this->call_method('facebook.dashboard.getNews',
- array('uid' => $uid)
- ), true);
- }
-
- /**
- * Set the news for the specified user.
- *
- * @param int $uid The user you are clearing the news of
- *
- * @return bool Success
- */
- public function dashboard_clearNews($uid) {
- return $this->call_method('facebook.dashboard.clearNews',
- array('uid' => $uid)
- );
- }
-
-
/**
* Creates a note with the specified title and content.
@@ -2005,7 +1958,7 @@ function toggleDisplay(id, type) {
* @return array A list of strings describing any compile errors for the
* submitted FBML
*/
- function profile_setFBML($markup,
+ public function profile_setFBML($markup,
$uid=null,
$profile='',
$profile_action='',
@@ -3267,9 +3220,8 @@ function toggleDisplay(id, type) {
} else {
$get['v'] = '1.0';
}
- if (isset($this->use_ssl_resources) &&
- $this->use_ssl_resources) {
- $post['return_ssl_resources'] = true;
+ if (isset($this->use_ssl_resources)) {
+ $post['return_ssl_resources'] = (bool) $this->use_ssl_resources;
}
return array($get, $post);
}
diff --git a/plugins/Facebook/facebooksettings.php b/plugins/Facebook/facebooksettings.php
index 766d0e199..f94a346b5 100644
--- a/plugins/Facebook/facebooksettings.php
+++ b/plugins/Facebook/facebooksettings.php
@@ -54,22 +54,11 @@ class FacebooksettingsAction extends FacebookAction
$noticesync = $this->boolean('noticesync');
$replysync = $this->boolean('replysync');
- $prefix = $this->trimmed('prefix');
$original = clone($this->flink);
$this->flink->set_flags($noticesync, false, $replysync, false);
$result = $this->flink->update($original);
- if ($prefix == '' || $prefix == '0') {
- // Facebook bug: saving empty strings to prefs now fails
- // http://bugs.developers.facebook.com/show_bug.cgi?id=7110
- $trimmed = $prefix . ' ';
- } else {
- $trimmed = substr($prefix, 0, 128);
- }
- $this->facebook->api_client->data_setUserPreference(FACEBOOK_NOTICE_PREFIX,
- $trimmed);
-
if ($result === false) {
$this->showForm(_m('There was a problem saving your sync preferences!'));
} else {
@@ -110,16 +99,6 @@ class FacebooksettingsAction extends FacebookAction
$this->elementStart('li');
- $prefix = trim($this->facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX));
-
- $this->input('prefix', _m('Prefix'),
- ($prefix) ? $prefix : null,
- _m('A string to prefix notices with.'));
-
- $this->elementEnd('li');
-
- $this->elementStart('li');
-
$this->submit('save', _m('Save'));
$this->elementEnd('li');
diff --git a/plugins/Facebook/facebookutil.php b/plugins/Facebook/facebookutil.php
index ab2d42726..1290fed55 100644
--- a/plugins/Facebook/facebookutil.php
+++ b/plugins/Facebook/facebookutil.php
@@ -81,101 +81,251 @@ function isFacebookBound($notice, $flink) {
function facebookBroadcastNotice($notice)
{
$facebook = getFacebook();
- $flink = Foreign_link::getByUserID($notice->profile_id, FACEBOOK_SERVICE);
+ $flink = Foreign_link::getByUserID(
+ $notice->profile_id,
+ FACEBOOK_SERVICE
+ );
if (isFacebookBound($notice, $flink)) {
// Okay, we're good to go, update the FB status
- $status = null;
$fbuid = $flink->foreign_id;
$user = $flink->getUser();
- $attachments = $notice->attachments();
try {
- // Get the status 'verb' (prefix) the user has set
+ // Check permissions
- // XXX: Does this call count against our per user FB request limit?
- // If so we should consider storing verb elsewhere or not storing
+ common_debug(
+ 'FacebookPlugin - checking for publish_stream permission for user '
+ . "$user->nickname ($user->id), Facebook UID: $fbuid"
+ );
- $prefix = trim($facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX,
- $fbuid));
+ // NOTE: $facebook->api_client->users_hasAppPermission('publish_stream', $fbuid)
+ // has been returning bogus results, so we're using FQL to check for
+ // publish_stream permission now
- $status = "$prefix $notice->content";
+ $fql = "SELECT publish_stream FROM permissions WHERE uid = $fbuid";
+ $result = $facebook->api_client->fql_query($fql);
- common_debug("FacebookPlugin - checking for publish_stream permission for user $user->id");
+ $canPublish = 0;
- $can_publish = $facebook->api_client->users_hasAppPermission('publish_stream',
- $fbuid);
+ if (!empty($result)) {
+ $canPublish = $result[0]['publish_stream'];
+ }
+
+ if ($canPublish == 1) {
+ common_debug(
+ "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid "
+ . 'has publish_stream permission.'
+ );
+ } else {
+ common_debug(
+ "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid "
+ . 'does NOT have publish_stream permission. Facebook '
+ . 'returned: ' . var_export($result, true)
+ );
+ }
- common_debug("FacebookPlugin - checking for status_update permission for user $user->id");
+ common_debug(
+ 'FacebookPlugin - checking for status_update permission for user '
+ . "$user->nickname ($user->id), Facebook UID: $fbuid. "
+ );
+
+ $canUpdate = $facebook->api_client->users_hasAppPermission(
+ 'status_update',
+ $fbuid
+ );
+
+ if ($canUpdate == 1) {
+ common_debug(
+ "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid "
+ . 'has status_update permission.'
+ );
+ } else {
+ common_debug(
+ "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid "
+ .'does NOT have status_update permission. Facebook '
+ . 'returned: ' . var_export($canPublish, true)
+ );
+ }
- $can_update = $facebook->api_client->users_hasAppPermission('status_update',
- $fbuid);
- if (!empty($attachments) && $can_publish == 1) {
- $fbattachment = format_attachments($attachments);
- $facebook->api_client->stream_publish($status, $fbattachment,
- null, null, $fbuid);
- common_log(LOG_INFO,
- "FacebookPlugin - Posted notice $notice->id w/attachment " .
- "to Facebook user's stream (fbuid = $fbuid).");
- } elseif ($can_update == 1 || $can_publish == 1) {
- $facebook->api_client->users_setStatus($status, $fbuid, false, true);
- common_log(LOG_INFO,
- "FacebookPlugin - Posted notice $notice->id to Facebook " .
- "as a status update (fbuid = $fbuid).");
+ // Post to Facebook
+
+ if ($notice->hasAttachments() && $canPublish == 1) {
+ publishStream($notice, $user, $fbuid);
+ } elseif ($canUpdate == 1 || $canPublish == 1) {
+ statusUpdate($notice, $user, $fbuid);
} else {
$msg = "FacebookPlugin - Not sending notice $notice->id to Facebook " .
- "because user $user->nickname hasn't given the " .
+ "because user $user->nickname has not given the " .
'Facebook app \'status_update\' or \'publish_stream\' permission.';
common_log(LOG_WARNING, $msg);
}
// Finally, attempt to update the user's profile box
- if ($can_publish == 1 || $can_update == 1) {
- updateProfileBox($facebook, $flink, $notice);
+ if ($canPublish == 1 || $canUpdate == 1) {
+ updateProfileBox($facebook, $flink, $notice, $user);
}
} catch (FacebookRestClientException $e) {
+ return handleFacebookError($e, $notice, $flink);
+ }
+ }
- $code = $e->getCode();
-
- $msg = "FacebookPlugin - Facebook returned error code $code: " .
- $e->getMessage() . ' - ' .
- "Unable to update Facebook status (notice $notice->id) " .
- "for $user->nickname (user id: $user->id)!";
+ return true;
+}
- common_log(LOG_WARNING, $msg);
+function handleFacebookError($e, $notice, $flink)
+{
+ $fbuid = $flink->foreign_id;
+ $user = $flink->getUser();
+ $code = $e->getCode();
+ $errmsg = $e->getMessage();
+
+ // XXX: Check for any others?
+ switch($code) {
+ case 100: // Invalid parameter
+ $msg = "FacebookPlugin - Facebook claims notice %d was posted with an invalid parameter (error code 100):"
+ . "\"%s\" (Notice details: nickname=%s, user ID=%d, Facebook ID=%d, notice content=\"%s\"). "
+ . "Removing notice from the Facebook queue for safety.";
+ common_log(
+ LOG_ERR, sprintf(
+ $msg,
+ $notice->id,
+ $errmsg,
+ $user->nickname,
+ $user->id,
+ $fbuid,
+ $notice->content
+ )
+ );
+ return true;
+ break;
+ case 200: // Permissions error
+ case 250: // Updating status requires the extended permission status_update
+ remove_facebook_app($flink);
+ return true; // dequeue
+ break;
+ case 341: // Feed action request limit reached
+ $msg = "FacebookPlugin - User %s (User ID=%d, Facebook ID=%d) has exceeded "
+ . "his/her limit for posting notices to Facebook today. Dequeuing "
+ . "notice %d.";
+ common_log(
+ LOG_INFO, sprintf(
+ $msg,
+ $user->nickname,
+ $user->id,
+ $fbuid,
+ $notice->id
+ )
+ );
+ // @fixme: We want to rety at a later time when the throttling has expired
+ // instead of just giving up.
+ return true;
+ break;
+ default:
+ $msg = "FacebookPlugin - Facebook returned an error we don't know how to deal with while trying to "
+ . "post notice %d. Error code: %d, error message: \"%s\". (Notice details: "
+ . "nickname=%s, user ID=%d, Facebook ID=%d, notice content=\"%s\"). Removing notice "
+ . "from the Facebook queue for safety.";
+ common_log(
+ LOG_ERR, sprintf(
+ $msg,
+ $notice->id,
+ $code,
+ $errmsg,
+ $user->nickname,
+ $user->id,
+ $fbuid,
+ $notice->content
+ )
+ );
+ return true; // dequeue
+ break;
+ }
+}
- if ($code == 100 || $code == 200 || $code == 250) {
+function statusUpdate($notice, $user, $fbuid)
+{
+ common_debug(
+ "FacebookPlugin - Attempting to post notice $notice->id "
+ . "as a status update for $user->nickname ($user->id), "
+ . "Facebook UID: $fbuid"
+ );
- // 100 The account is 'inactive' (probably - this is not well documented)
- // 200 The application does not have permission to operate on the passed in uid parameter.
- // 250 Updating status requires the extended permission status_update or publish_stream.
- // see: http://wiki.developers.facebook.com/index.php/Users.setStatus#Example_Return_XML
+ $facebook = getFacebook();
+ $result = $facebook->api_client->users_setStatus(
+ $notice->content,
+ $fbuid,
+ false,
+ true
+ );
+
+ common_debug('Facebook returned: ' . var_export($result, true));
+
+ common_log(
+ LOG_INFO,
+ "FacebookPlugin - Posted notice $notice->id as a status "
+ . "update for $user->nickname ($user->id), "
+ . "Facebook UID: $fbuid"
+ );
+}
- remove_facebook_app($flink);
+function publishStream($notice, $user, $fbuid)
+{
+ common_debug(
+ "FacebookPlugin - Attempting to post notice $notice->id "
+ . "as stream item with attachment for $user->nickname ($user->id), "
+ . "Facebook UID: $fbuid"
+ );
- } else {
+ $fbattachment = format_attachments($notice->attachments());
- // Try sending again later.
+ $facebook = getFacebook();
+ $facebook->api_client->stream_publish(
+ $notice->content,
+ $fbattachment,
+ null,
+ null,
+ $fbuid
+ );
+
+ common_log(
+ LOG_INFO,
+ "FacebookPlugin - Posted notice $notice->id as a stream "
+ . "item with attachment for $user->nickname ($user->id), "
+ . "Facebook UID: $fbuid"
+ );
+}
- return false;
- }
+function updateProfileBox($facebook, $flink, $notice, $user) {
- }
- }
+ $facebook = getFacebook();
+ $fbaction = new FacebookAction(
+ $output = 'php://output',
+ $indent = null,
+ $facebook,
+ $flink
+ );
- return true;
+ $fbuid = $flink->foreign_id;
-}
+ common_debug(
+ 'FacebookPlugin - Attempting to update profile box with '
+ . "content from notice $notice->id for $user->nickname ($user->id), "
+ . "Facebook UID: $fbuid"
+ );
-function updateProfileBox($facebook, $flink, $notice) {
- $fbaction = new FacebookAction($output = 'php://output',
- $indent = null, $facebook, $flink);
$fbaction->updateProfileBox($notice);
+
+ common_debug(
+ 'FacebookPlugin - finished updating profile box for '
+ . "$user->nickname ($user->id) Facebook UID: $fbuid"
+ );
+
}
function format_attachments($attachments)
diff --git a/plugins/Mapstraction/MapstractionPlugin.php b/plugins/Mapstraction/MapstractionPlugin.php
index 868933fd4..e7240a644 100644
--- a/plugins/Mapstraction/MapstractionPlugin.php
+++ b/plugins/Mapstraction/MapstractionPlugin.php
@@ -125,8 +125,8 @@ class MapstractionPlugin extends Plugin
$action->script('http://tile.cloudmade.com/wml/0.2/web-maps-lite.js');
break;
case 'google':
- $action->script(sprintf('http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=false&amp;key=%s',
- $this->apikey));
+ $action->script(sprintf('http://maps.google.com/maps?file=api&v=2&sensor=false&key=%s',
+ urlencode($this->apikey)));
break;
case 'microsoft':
$action->script('http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6');
@@ -137,7 +137,7 @@ class MapstractionPlugin extends Plugin
break;
case 'yahoo':
$action->script(sprintf('http://api.maps.yahoo.com/ajaxymap?v=3.8&appid=%s',
- $this->apikey));
+ urlencode($this->apikey)));
break;
case 'geocommons': // don't support this yet
default:
diff --git a/plugins/Mapstraction/usermap.js b/plugins/Mapstraction/usermap.js
index 4b7a6c26b..53cfe6bb0 100644
--- a/plugins/Mapstraction/usermap.js
+++ b/plugins/Mapstraction/usermap.js
@@ -104,7 +104,7 @@ function showMapstraction(element, notices) {
pt = new mxn.LatLonPoint(lat, lon);
mkr = new mxn.Marker(pt);
- mkr.setIcon(n['user']['profile_image_url']);
+ mkr.setIcon(n['user']['profile_image_url'], [24, 24]);
mkr.setInfoBubble('<a href="'+ n['user']['profile_url'] + '">' + n['user']['screen_name'] + '</a>' + ' ' + n['html'] +
'<br/><a href="'+ n['url'] + '">'+ n['created_at'] + '</a>');
diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php
index 270e2c624..9eac9f6fc 100644
--- a/plugins/OpenID/OpenIDPlugin.php
+++ b/plugins/OpenID/OpenIDPlugin.php
@@ -20,7 +20,7 @@
* @category Plugin
* @package StatusNet
* @author Evan Prodromou <evan@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/
*/
@@ -45,7 +45,19 @@ if (!defined('STATUSNET')) {
class OpenIDPlugin extends Plugin
{
- public $openidOnly = false;
+ // Plugin parameter: set true to disallow non-OpenID logins
+ // If set, overrides the setting in database or $config['site']['openidonly']
+ public $openidOnly = null;
+
+ function initialize()
+ {
+ parent::initialize();
+ if ($this->openidOnly !== null) {
+ global $config;
+ $config['site']['openidonly'] = (bool)$this->openidOnly;
+ }
+
+ }
/**
* Add OpenID-related paths to the router table
@@ -67,6 +79,7 @@ class OpenIDPlugin extends Plugin
$m->connect('index.php?action=finishaddopenid',
array('action' => 'finishaddopenid'));
$m->connect('main/openidserver', array('action' => 'openidserver'));
+ $m->connect('admin/openid', array('action' => 'openidadminpanel'));
return true;
}
@@ -84,7 +97,7 @@ class OpenIDPlugin extends Plugin
function onStartConnectPath(&$path, &$defaults, &$rules, &$result)
{
- if ($this->openidOnly) {
+ if (common_config('site', 'openidonly')) {
static $block = array('main/login',
'main/register',
'main/recoverpassword',
@@ -108,7 +121,7 @@ class OpenIDPlugin extends Plugin
function onArgsInitialize($args)
{
- if ($this->openidOnly) {
+ if (common_config('site', 'openidonly')) {
if (array_key_exists('action', $args)) {
$action = trim($args['action']);
if (in_array($action, array('login', 'register'))) {
@@ -199,7 +212,7 @@ class OpenIDPlugin extends Plugin
function onStartPrimaryNav($action)
{
- if ($this->openidOnly && !common_logged_in()) {
+ if (common_config('site', 'openidonly') && !common_logged_in()) {
// TRANS: Tooltip for main menu option "Login"
$tooltip = _m('TOOLTIP', 'Login to the site');
// TRANS: Main menu option when not logged in to log in
@@ -241,7 +254,7 @@ class OpenIDPlugin extends Plugin
function onStartLoginGroupNav(&$action)
{
- if ($this->openidOnly) {
+ if (common_config('site', 'openidonly')) {
$this->showOpenIDLoginTab($action);
// Even though we replace this code, we
// DON'T run the End* hook, to keep others from
@@ -297,7 +310,7 @@ class OpenIDPlugin extends Plugin
*/
function onStartAccountSettingsPasswordMenuItem($menu, &$unused) {
- if ($this->openidOnly) {
+ if (common_config('site', 'openidonly')) {
return false;
}
return true;
@@ -345,13 +358,19 @@ class OpenIDPlugin extends Plugin
case 'OpenidsettingsAction':
case 'OpenidserverAction':
case 'OpenidtrustAction':
- require_once INSTALLDIR.'/plugins/OpenID/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+ case 'OpenidadminpanelAction':
+ require_once dirname(__FILE__) . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;
case 'User_openid':
- require_once INSTALLDIR.'/plugins/OpenID/User_openid.php';
+ require_once dirname(__FILE__) . '/User_openid.php';
return false;
case 'User_openid_trustroot':
- require_once INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php';
+ require_once dirname(__FILE__) . '/User_openid_trustroot.php';
+ return false;
+ case 'Auth_OpenID_TeamsExtension':
+ case 'Auth_OpenID_TeamsRequest':
+ case 'Auth_OpenID_TeamsResponse':
+ require_once dirname(__FILE__) . '/extlib/teams-extension.php';
return false;
default:
return true;
@@ -442,7 +461,7 @@ class OpenIDPlugin extends Plugin
function onRedirectToLogin($action, $user)
{
- if ($this->openidOnly || (!empty($user) && User_openid::hasOpenID($user->id))) {
+ if (common_config('site', 'openid_only') || (!empty($user) && User_openid::hasOpenID($user->id))) {
common_redirect(common_local_url('openidlogin'), 303);
return false;
}
@@ -578,6 +597,32 @@ class OpenIDPlugin extends Plugin
}
/**
+ * Add an OpenID tab to the admin panel
+ *
+ * @param Widget $nav Admin panel nav
+ *
+ * @return boolean hook value
+ */
+
+ function onEndAdminPanelNav($nav)
+ {
+ if (AdminPanelAction::canAdmin('openid')) {
+
+ $action_name = $nav->action->trimmed('action');
+
+ $nav->out->menuItem(
+ common_local_url('openidadminpanel'),
+ _m('OpenID'),
+ _m('OpenID configuration'),
+ $action_name == 'openidadminpanel',
+ 'nav_openid_admin_panel'
+ );
+ }
+
+ return true;
+ }
+
+ /**
* Add our version information to output
*
* @param array &$versions Array of version-data arrays
diff --git a/plugins/OpenID/extlib/README b/plugins/OpenID/extlib/README
new file mode 100644
index 000000000..1fe80d79b
--- /dev/null
+++ b/plugins/OpenID/extlib/README
@@ -0,0 +1,6 @@
+team-extension.php
+ Support for Launchpad's OpenID Teams extension
+ Maintainer: Canonical
+ Source: https://code.edge.launchpad.net/wordpress-teams-integration
+ r27 2010-04-27
+ License: AGPLv3
diff --git a/plugins/OpenID/extlib/teams-extension.php b/plugins/OpenID/extlib/teams-extension.php
new file mode 100644
index 000000000..451f2fb19
--- /dev/null
+++ b/plugins/OpenID/extlib/teams-extension.php
@@ -0,0 +1,175 @@
+<?php
+/*
+ * Wordpress Teams plugin
+ * Copyright (C) 2009-2010 Canonical Ltd.
+ *
+ * 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/>.
+ */
+
+/**
+ * Provides an example OpenID extension to query user team/group membership
+ *
+ * This code is based on code supplied with the openid library for simple
+ * registration data.
+ */
+
+/**
+ * Require the Message implementation.
+ */
+require_once 'Auth/OpenID/Message.php';
+require_once 'Auth/OpenID/Extension.php';
+
+/**
+ * The team/group extension base class
+ */
+class Auth_OpenID_TeamsExtension extends Auth_OpenID_Extension {
+ var $ns_uri = 'http://ns.launchpad.net/2007/openid-teams';
+ var $ns_alias = 'lp';
+ var $request_field = 'query_membership';
+ var $response_field = 'is_member';
+
+ /**
+ * Get the string arguments that should be added to an OpenID
+ * message for this extension.
+ */
+ function getExtensionArgs() {
+ $args = array();
+
+ if ($this->_teams) {
+ $args[$this->request_field] = implode(',', $this->_teams);
+ }
+
+ return $args;
+ }
+
+ /**
+ * Add the arguments from this extension to the provided message.
+ *
+ * Returns the message with the extension arguments added.
+ */
+ function toMessage(&$message) {
+ if ($message->namespaces->addAlias($this->ns_uri, $this->ns_alias) === null) {
+ if ($message->namespaces->getAlias($this->ns_uri) != $this->ns_alias) {
+ return null;
+ }
+ }
+
+ $message->updateArgs($this->ns_uri, $this->getExtensionArgs());
+ return $message;
+ }
+
+ /**
+ * Extract the team/group namespace URI from the given OpenID message.
+ * Handles OpenID 1 and 2.
+ *
+ * $message: The OpenID message from which to parse team/group data.
+ * This may be a request or response message.
+ *
+ * Returns the sreg namespace URI for the supplied message.
+ *
+ * @access private
+ */
+ function _getExtensionNS(&$message) {
+ $alias = null;
+ $found_ns_uri = null;
+
+ // See if there exists an alias for the namespace
+ $alias = $message->namespaces->getAlias($this->ns_uri);
+
+ if ($alias !== null) {
+ $found_ns_uri = $this->ns_uri;
+ }
+
+ if ($alias === null) {
+ // There is no alias for this extension, so try to add one.
+ $found_ns_uri = Auth_OpenID_TYPE_1_0;
+
+ if ($message->namespaces->addAlias($this->ns_uri, $this->ns_alias) === null) {
+ // An alias for the string 'lp' already exists, but
+ // it's defined for something other than team/group membership
+ return null;
+ }
+ }
+
+ return $found_ns_uri;
+ }
+}
+
+/**
+ * The team/group extension request class
+ */
+class Auth_OpenID_TeamsRequest extends Auth_OpenID_TeamsExtension {
+ function __init($teams) {
+ if (!is_array($teams)) {
+ if (!empty($teams)) {
+ $teams = explode(',', $teams);
+ } else {
+ $teams = Array();
+ }
+ }
+
+ $this->_teams = $teams;
+ }
+
+ function Auth_OpenID_TeamsRequest($teams) {
+ $this->__init($teams);
+ }
+}
+
+/**
+ * The team/group extension response class
+ */
+class Auth_OpenID_TeamsResponse extends Auth_OpenID_TeamsExtension {
+ var $_teams = array();
+
+ function __init(&$resp, $signed_only=true) {
+ $this->ns_uri = $this->_getExtensionNS($resp->message);
+
+ if ($signed_only) {
+ $args = $resp->getSignedNS($this->ns_uri);
+ } else {
+ $args = $resp->message->getArgs($this->ns_uri);
+ }
+
+ if ($args === null) {
+ return null;
+ }
+
+ // An OpenID 2.0 response will handle the namespaces
+ if (in_array($this->response_field, array_keys($args)) && !empty($args[$this->response_field])) {
+ $this->_teams = explode(',', $args[$this->response_field]);
+ }
+
+ // Piggybacking on a 1.x request, however, won't so the field name will
+ // be different
+ elseif (in_array($this->ns_alias.'.'.$this->response_field, array_keys($args)) && !empty($args[$this->ns_alias.'.'.$this->response_field])) {
+ $this->_teams = explode(',', $args[$this->ns_alias.'.'.$this->response_field]);
+ }
+ }
+
+ function Auth_OpenID_TeamsResponse(&$resp, $signed_only=true) {
+ $this->__init($resp, $signed_only);
+ }
+
+ /**
+ * Get the array of teams the user is a member of
+ *
+ * @return array
+ */
+ function getTeams() {
+ return $this->_teams;
+ }
+}
+
+?>
diff --git a/plugins/OpenID/finishaddopenid.php b/plugins/OpenID/finishaddopenid.php
index 991e6584e..064e97642 100644
--- a/plugins/OpenID/finishaddopenid.php
+++ b/plugins/OpenID/finishaddopenid.php
@@ -103,6 +103,12 @@ class FinishaddopenidAction extends Action
$sreg = $sreg_resp->contents();
}
+ // Launchpad teams extension
+ if (!oid_check_teams($response)) {
+ $this->message(_m('OpenID authentication aborted: you are not allowed to login to this site.'));
+ return;
+ }
+
$cur = common_current_user();
$other = oid_get_user($canonical);
@@ -126,12 +132,15 @@ class FinishaddopenidAction extends Action
$this->message(_m('Error connecting user.'));
return;
}
- if ($sreg) {
- if (!oid_update_user($cur, $sreg)) {
- $this->message(_m('Error updating profile'));
- return;
+ if (Event::handle('StartOpenIDUpdateUser', array($cur, $canonical, &$sreg))) {
+ if ($sreg) {
+ if (!oid_update_user($cur, $sreg)) {
+ $this->message(_m('Error updating profile'));
+ return;
+ }
}
}
+ Event::handle('EndOpenIDUpdateUser', array($cur, $canonical, $sreg));
// success!
diff --git a/plugins/OpenID/finishopenidlogin.php b/plugins/OpenID/finishopenidlogin.php
index 32b092a0b..415fd8e66 100644
--- a/plugins/OpenID/finishopenidlogin.php
+++ b/plugins/OpenID/finishopenidlogin.php
@@ -177,6 +177,12 @@ class FinishopenidloginAction extends Action
$sreg = $sreg_resp->contents();
}
+ // Launchpad teams extension
+ if (!oid_check_teams($response)) {
+ $this->message(_m('OpenID authentication aborted: you are not allowed to login to this site.'));
+ return;
+ }
+
$user = oid_get_user($canonical);
if ($user) {
@@ -280,6 +286,8 @@ class FinishopenidloginAction extends Action
return;
}
+ Event::handle('StartOpenIDCreateNewUser', array($canonical, &$sreg));
+
$location = '';
if (!empty($sreg['country'])) {
if ($sreg['postcode']) {
@@ -319,6 +327,8 @@ class FinishopenidloginAction extends Action
$result = oid_link_user($user->id, $canonical, $display);
+ Event::handle('EndOpenIDCreateNewUser', array($user, $canonical, $sreg));
+
oid_set_last($display);
common_set_user($user);
common_real_login(true);
@@ -358,7 +368,11 @@ class FinishopenidloginAction extends Action
return;
}
- oid_update_user($user, $sreg);
+ if (Event::handle('StartOpenIDUpdateUser', array($user, $canonical, &$sreg))) {
+ oid_update_user($user, $sreg);
+ }
+ Event::handle('EndOpenIDUpdateUser', array($user, $canonical, $sreg));
+
oid_set_last($display);
common_set_user($user);
common_real_login(true);
diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php
index 4ec336e1c..8be02e031 100644
--- a/plugins/OpenID/openid.php
+++ b/plugins/OpenID/openid.php
@@ -144,8 +144,10 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
// Handle failure status return values.
if (!$auth_request) {
+ common_log(LOG_ERR, __METHOD__ . ": mystery fail contacting $openid_url");
return _m('Not a valid OpenID.');
} else if (Auth_OpenID::isFailure($auth_request)) {
+ common_log(LOG_ERR, __METHOD__ . ": OpenID fail to $openid_url: $auth_request->message");
return sprintf(_m('OpenID failure: %s'), $auth_request->message);
}
@@ -164,6 +166,15 @@ function oid_authenticate($openid_url, $returnto, $immediate=false)
$auth_request->addExtension($sreg_request);
}
+ $requiredTeam = common_config('openid', 'required_team');
+ if ($requiredTeam) {
+ // LaunchPad OpenID extension
+ $team_request = new Auth_OpenID_TeamsRequest(array($requiredTeam));
+ if ($team_request) {
+ $auth_request->addExtension($team_request);
+ }
+ }
+
$trust_root = common_root_url(true);
$process_url = common_local_url($returnto);
@@ -212,11 +223,14 @@ function _oid_print_instructions()
'OpenID provider.'));
}
-# update a user from sreg parameters
-
-function oid_update_user(&$user, &$sreg)
+/**
+ * Update a user from sreg parameters
+ * @param User $user
+ * @param array $sreg fields from OpenID sreg response
+ * @access private
+ */
+function oid_update_user($user, $sreg)
{
-
$profile = $user->getProfile();
$orig_profile = clone($profile);
@@ -286,6 +300,33 @@ function oid_assert_allowed($url)
return;
}
+/**
+ * Check the teams available in the given OpenID response
+ * Using Launchpad's OpenID teams extension
+ *
+ * @return boolean whether this user is acceptable
+ */
+function oid_check_teams($response)
+{
+ $requiredTeam = common_config('openid', 'required_team');
+ if ($requiredTeam) {
+ $team_resp = new Auth_OpenID_TeamsResponse($response);
+ if ($team_resp) {
+ $teams = $team_resp->getTeams();
+ } else {
+ $teams = array();
+ }
+
+ $match = in_array($requiredTeam, $teams);
+ $is = $match ? 'is' : 'is not';
+ common_log(LOG_DEBUG, "Remote user $is in required team $requiredTeam: [" . implode(', ', $teams) . "]");
+
+ return $match;
+ }
+
+ return true;
+}
+
class AutosubmitAction extends Action
{
var $form_html = null;
diff --git a/plugins/OpenID/openidadminpanel.php b/plugins/OpenID/openidadminpanel.php
new file mode 100644
index 000000000..ce4806cc8
--- /dev/null
+++ b/plugins/OpenID/openidadminpanel.php
@@ -0,0 +1,280 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * OpenID 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 OpenID 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 OpenidadminpanelAction extends AdminPanelAction
+{
+ /**
+ * Returns the page title
+ *
+ * @return string page title
+ */
+
+ function title()
+ {
+ return _m('OpenID');
+ }
+
+ /**
+ * Instructions for using this form.
+ *
+ * @return string instructions
+ */
+
+ function getInstructions()
+ {
+ return _m('OpenID settings');
+ }
+
+ /**
+ * Show the OpenID admin panel form
+ *
+ * @return void
+ */
+
+ function showForm()
+ {
+ $form = new OpenIDAdminPanelForm($this);
+ $form->show();
+ return;
+ }
+
+ /**
+ * Save settings from the form
+ *
+ * @return void
+ */
+
+ function saveSettings()
+ {
+ static $settings = array(
+ 'openid' => array('trusted_provider', 'required_team')
+ );
+
+ static $booleans = array(
+ 'openid' => array('append_username'),
+ 'site' => array('openidonly')
+ );
+
+ $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['openid']['trusted_provider']) > 255) {
+ $this->clientError(
+ _m("Invalid provider URL. Max length is 255 characters.")
+ );
+ }
+
+ if (mb_strlen($values['openid']['required_team']) > 255) {
+ $this->clientError(
+ _m("Invalid team name. Max length is 255 characters.")
+ );
+ }
+ }
+}
+
+class OpenIDAdminPanelForm extends AdminForm
+{
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ return 'openidadminpanel';
+ }
+
+ /**
+ * 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('openidadminpanel');
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ *
+ * @todo Some of the options could prevent users from logging in again.
+ * Make sure that the acting administrator has a valid OpenID matching,
+ * or more carefully warn folks.
+ */
+
+ function formData()
+ {
+ $this->out->elementStart(
+ 'fieldset',
+ array('id' => 'settings_openid')
+ );
+ $this->out->element('legend', null, _m('Trusted provider'));
+ $this->out->element('p', 'form_guide',
+ _m('By default, users are allowed to authenticate with any OpenID provider. ' .
+ 'If you are using your own OpenID service for shared sign-in, ' .
+ 'you can restrict access to only your own users here.'));
+ $this->out->elementStart('ul', 'form_data');
+
+ $this->li();
+ $this->input(
+ 'trusted_provider',
+ _m('Provider URL'),
+ _m('All OpenID logins will be sent to this URL; other providers may not be used.'),
+ 'openid'
+ );
+ $this->unli();
+
+ $this->li();
+ $this->out->checkbox(
+ 'append_username', _m('Append a username to base URL'),
+ (bool) $this->value('append_username', 'openid'),
+ _m('Login form will show the base URL and prompt for a username to add at the end. Use when OpenID provider URL should be the profile page for individual users.'),
+ 'true'
+ );
+ $this->unli();
+
+ $this->li();
+ $this->input(
+ 'required_team',
+ _m('Required team'),
+ _m('Only allow logins from users in the given team (Launchpad extension).'),
+ 'openid'
+ );
+ $this->unli();
+
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('fieldset');
+
+ $this->out->elementStart(
+ 'fieldset',
+ array('id' => 'settings_openid-options')
+ );
+ $this->out->element('legend', null, _m('Options'));
+
+ $this->out->elementStart('ul', 'form_data');
+
+ $this->li();
+
+ $this->out->checkbox(
+ 'openidonly', _m('Enable OpenID-only mode'),
+ (bool) $this->value('openidonly', 'site'),
+ _m('Require all users to login via OpenID. WARNING: disables password authentication for all users!'),
+ 'true'
+ );
+ $this->unli();
+
+ $this->out->elementEnd('ul');
+
+ $this->out->elementEnd('fieldset');
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ $this->out->submit('submit', _('Save'), 'submit', null, _m('Save OpenID settings'));
+ }
+}
diff --git a/plugins/OpenID/openidlogin.php b/plugins/OpenID/openidlogin.php
index 2a743672c..ffedc6481 100644
--- a/plugins/OpenID/openidlogin.php
+++ b/plugins/OpenID/openidlogin.php
@@ -29,7 +29,15 @@ class OpenidloginAction extends Action
if (common_is_real_login()) {
$this->clientError(_m('Already logged in.'));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
- $openid_url = $this->trimmed('openid_url');
+ $provider = common_config('openid', 'trusted_provider');
+ if ($provider) {
+ $openid_url = $provider;
+ if (common_config('openid', 'append_username')) {
+ $openid_url .= $this->trimmed('openid_username');
+ }
+ } else {
+ $openid_url = $this->trimmed('openid_url');
+ }
oid_assert_allowed($openid_url);
@@ -89,7 +97,15 @@ class OpenidloginAction extends Action
function showScripts()
{
parent::showScripts();
- $this->autofocus('openid_url');
+ if (common_config('openid', 'trusted_provider')) {
+ if (common_config('openid', 'append_username')) {
+ $this->autofocus('openid_username');
+ } else {
+ $this->autofocus('rememberme');
+ }
+ } else {
+ $this->autofocus('openid_url');
+ }
}
function title()
@@ -116,9 +132,25 @@ class OpenidloginAction extends Action
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
- $this->input('openid_url', _m('OpenID URL'),
- $this->openid_url,
- _m('Your OpenID URL'));
+ $provider = common_config('openid', 'trusted_provider');
+ $appendUsername = common_config('openid', 'append_username');
+ if ($provider) {
+ $this->element('label', array(), _m('OpenID provider'));
+ $this->element('span', array(), $provider);
+ if ($appendUsername) {
+ $this->element('input', array('id' => 'openid_username',
+ 'name' => 'openid_username',
+ 'style' => 'float: none'));
+ }
+ $this->element('p', 'form_guide',
+ ($appendUsername ? _m('Enter your username.') . ' ' : '') .
+ _m('You will be sent to the provider\'s site for authentication.'));
+ $this->hidden('openid_url', $provider);
+ } else {
+ $this->input('openid_url', _m('OpenID URL'),
+ $this->openid_url,
+ _m('Your OpenID URL'));
+ }
$this->elementEnd('li');
$this->elementStart('li', array('id' => 'settings_rememberme'));
$this->checkbox('rememberme', _m('Remember me'), false,
diff --git a/plugins/OpenID/openidsettings.php b/plugins/OpenID/openidsettings.php
index 16142cf48..505e7d0ee 100644
--- a/plugins/OpenID/openidsettings.php
+++ b/plugins/OpenID/openidsettings.php
@@ -90,34 +90,36 @@ class OpenidsettingsAction extends AccountSettingsAction
{
$user = common_current_user();
- $this->elementStart('form', array('method' => 'post',
- 'id' => 'form_settings_openid_add',
- 'class' => 'form_settings',
- 'action' =>
- common_local_url('openidsettings')));
- $this->elementStart('fieldset', array('id' => 'settings_openid_add'));
- $this->element('legend', null, _m('Add OpenID'));
- $this->hidden('token', common_session_token());
- $this->element('p', 'form_guide',
- _m('If you want to add an OpenID to your account, ' .
- 'enter it in the box below and click "Add".'));
- $this->elementStart('ul', 'form_data');
- $this->elementStart('li');
- $this->element('label', array('for' => 'openid_url'),
- _m('OpenID URL'));
- $this->element('input', array('name' => 'openid_url',
- 'type' => 'text',
- 'id' => 'openid_url'));
- $this->elementEnd('li');
- $this->elementEnd('ul');
- $this->element('input', array('type' => 'submit',
- 'id' => 'settings_openid_add_action-submit',
- 'name' => 'add',
- 'class' => 'submit',
- 'value' => _m('Add')));
- $this->elementEnd('fieldset');
- $this->elementEnd('form');
-
+ if (!common_config('openid', 'trusted_provider')) {
+ $this->elementStart('form', array('method' => 'post',
+ 'id' => 'form_settings_openid_add',
+ 'class' => 'form_settings',
+ 'action' =>
+ common_local_url('openidsettings')));
+ $this->elementStart('fieldset', array('id' => 'settings_openid_add'));
+
+ $this->element('legend', null, _m('Add OpenID'));
+ $this->hidden('token', common_session_token());
+ $this->element('p', 'form_guide',
+ _m('If you want to add an OpenID to your account, ' .
+ 'enter it in the box below and click "Add".'));
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+ $this->element('label', array('for' => 'openid_url'),
+ _m('OpenID URL'));
+ $this->element('input', array('name' => 'openid_url',
+ 'type' => 'text',
+ 'id' => 'openid_url'));
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+ $this->element('input', array('type' => 'submit',
+ 'id' => 'settings_openid_add_action-submit',
+ 'name' => 'add',
+ 'class' => 'submit',
+ 'value' => _m('Add')));
+ $this->elementEnd('fieldset');
+ $this->elementEnd('form');
+ }
$oid = new User_openid();
$oid->user_id = $user->id;
@@ -234,10 +236,14 @@ class OpenidsettingsAction extends AccountSettingsAction
}
if ($this->arg('add')) {
- $result = oid_authenticate($this->trimmed('openid_url'),
- 'finishaddopenid');
- if (is_string($result)) { // error message
- $this->showForm($result);
+ if (common_config('openid', 'trusted_provider')) {
+ $this->showForm(_m("Can't add new providers."));
+ } else {
+ $result = oid_authenticate($this->trimmed('openid_url'),
+ 'finishaddopenid');
+ if (is_string($result)) { // error message
+ $this->showForm($result);
+ }
}
} else if ($this->arg('remove')) {
$this->removeOpenid();
diff --git a/plugins/WikiHowProfile/README b/plugins/WikiHowProfile/README
new file mode 100644
index 000000000..ee6096c9f
--- /dev/null
+++ b/plugins/WikiHowProfile/README
@@ -0,0 +1,6 @@
+This is an additional plugin which piggybacks on OpenID authentication to pull
+profile information from WikiHow user pages when creating or updating accounts.
+
+WikiHow runs a customized MediaWiki setup, with locally-built extensions to add
+profile features such as an avatar. As this additional info isn't yet exposed
+through OpenID, we need to pull it separately.
diff --git a/plugins/WikiHowProfile/WikiHowProfilePlugin.php b/plugins/WikiHowProfile/WikiHowProfilePlugin.php
new file mode 100644
index 000000000..b72bd55d6
--- /dev/null
+++ b/plugins/WikiHowProfile/WikiHowProfilePlugin.php
@@ -0,0 +1,196 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Plugin to pull WikiHow-style user avatars at OpenID setup time.
+ * These are not currently exposed via OpenID.
+ *
+ * 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 Plugins
+ * @package StatusNet
+ * @author Brion Vibber <brion@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')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Sample plugin main class
+ *
+ * Each plugin requires a main class to interact with the StatusNet system.
+ *
+ * @category Plugins
+ * @package WikiHowProfilePlugin
+ * @author Brion Vibber <brion@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 WikiHowProfilePlugin extends Plugin
+{
+ function onPluginVersion(&$versions)
+ {
+ $versions[] = array('name' => 'WikiHow avatar fetcher',
+ 'version' => STATUSNET_VERSION,
+ 'author' => 'Brion Vibber',
+ 'homepage' => 'http://status.net/wiki/Plugin:Sample',
+ 'rawdescription' =>
+ _m('Fetches avatar and other profile info for WikiHow users when setting up an account via OpenID.'));
+ return true;
+ }
+
+ /**
+ * Hook for OpenID user creation; we'll pull the avatar.
+ *
+ * @param User $user
+ * @param string $canonical OpenID provider URL
+ * @param array $sreg query data from provider
+ */
+ function onEndOpenIDCreateNewUser($user, $canonical, $sreg)
+ {
+ $this->updateProfile($user, $canonical);
+ return true;
+ }
+
+ /**
+ * Hook for OpenID profile updating; we'll pull the avatar.
+ *
+ * @param User $user
+ * @param string $canonical OpenID provider URL (wiki profile page)
+ * @param array $sreg query data from provider
+ */
+ function onEndOpenIDUpdateUser($user, $canonical, $sreg)
+ {
+ $this->updateProfile($user, $canonical);
+ return true;
+ }
+
+ /**
+ * @param User $user
+ * @param string $canonical OpenID provider URL (wiki profile page)
+ */
+ private function updateProfile($user, $canonical)
+ {
+ $prefix = 'http://www.wikihow.com/User:';
+
+ if (substr($canonical, 0, strlen($prefix)) == $prefix) {
+ // Yes, it's a WikiHow user!
+ $profile = $this->fetchProfile($canonical);
+
+ if (!empty($profile['avatar'])) {
+ $this->saveAvatar($user, $profile['avatar']);
+ }
+ }
+ }
+
+ /**
+ * Given a user's WikiHow profile URL, find their avatar.
+ *
+ * @param string $profileUrl user page on the wiki
+ *
+ * @return array of data; possible members:
+ * 'avatar' => full URL to avatar image
+ *
+ * @throws Exception on various low-level failures
+ *
+ * @todo pull location, web site, and about sections -- they aren't currently marked up cleanly.
+ */
+ private function fetchProfile($profileUrl)
+ {
+ $client = HTTPClient::start();
+ $response = $client->get($profileUrl);
+ if (!$response->isOk()) {
+ throw new Exception("WikiHow profile page fetch failed.");
+ // HTTP error response already logged.
+ return false;
+ }
+
+ // Suppress warnings during HTML parsing; non-well-formed bits will
+ // spew horrible warning everywhere even though it works fine.
+ $old = error_reporting();
+ error_reporting($old & ~E_WARNING);
+
+ $dom = new DOMDocument();
+ $ok = $dom->loadHTML($response->getBody());
+
+ error_reporting($old);
+
+ if (!$ok) {
+ throw new Exception("HTML parse failure during check for WikiHow avatar.");
+ return false;
+ }
+
+ $data = array();
+
+ $avatar = $dom->getElementById('avatarULimg');
+ if ($avatar) {
+ $src = $avatar->getAttribute('src');
+
+ $base = new Net_URL2($profileUrl);
+ $absolute = $base->resolve($src);
+ $avatarUrl = strval($absolute);
+
+ common_log(LOG_DEBUG, "WikiHow avatar found for $profileUrl - $avatarUrl");
+ $data['avatar'] = $avatarUrl;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Actually save the avatar we found locally.
+ *
+ * @param User $user
+ * @param string $url to avatar URL
+ * @todo merge wrapper funcs for this into common place for 1.0 core
+ */
+ private function saveAvatar($user, $url)
+ {
+ if (!common_valid_http_url($url)) {
+ throw new ServerException(sprintf(_m("Invalid avatar URL %s"), $url));
+ }
+
+ // @fixme this should be better encapsulated
+ // ripped from OStatus via oauthstore.php (for old OMB client)
+ $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
+ if (!copy($url, $temp_filename)) {
+ throw new ServerException(sprintf(_m("Unable to fetch avatar from %s"), $url));
+ }
+
+ $profile = $user->getProfile();
+ $id = $profile->id;
+ // @fixme should we be using different ids?
+
+ $imagefile = new ImageFile($id, $temp_filename);
+ $filename = Avatar::filename($id,
+ image_type_to_extension($imagefile->type),
+ null,
+ common_timestamp());
+ rename($temp_filename, Avatar::path($filename));
+ $profile->setOriginal($filename);
+ }
+
+}
+