diff options
Diffstat (limited to 'plugins')
54 files changed, 734 insertions, 242 deletions
diff --git a/plugins/Autocomplete/AutocompletePlugin.php b/plugins/Autocomplete/AutocompletePlugin.php index b2b18bf27..b2be365dd 100644 --- a/plugins/Autocomplete/AutocompletePlugin.php +++ b/plugins/Autocomplete/AutocompletePlugin.php @@ -22,7 +22,8 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2010 Free Software Foundation http://fsf.org + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/Autocomplete/autocomplete.php b/plugins/Autocomplete/autocomplete.php index 379390ffd..9a30ba01d 100644 --- a/plugins/Autocomplete/autocomplete.php +++ b/plugins/Autocomplete/autocomplete.php @@ -23,6 +23,7 @@ * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> * @copyright 2008-2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/BitlyUrl/BitlyUrlPlugin.php b/plugins/BitlyUrl/BitlyUrlPlugin.php index b649d3d0b..38c821636 100644 --- a/plugins/BitlyUrl/BitlyUrlPlugin.php +++ b/plugins/BitlyUrl/BitlyUrlPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/CasAuthentication/CasAuthenticationPlugin.php b/plugins/CasAuthentication/CasAuthenticationPlugin.php index 203e5fe42..1662db3eb 100644 --- a/plugins/CasAuthentication/CasAuthenticationPlugin.php +++ b/plugins/CasAuthentication/CasAuthenticationPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/ClientSideShorten/ClientSideShortenPlugin.php b/plugins/ClientSideShorten/ClientSideShortenPlugin.php index 454bedb08..3da08e05d 100644 --- a/plugins/ClientSideShorten/ClientSideShortenPlugin.php +++ b/plugins/ClientSideShorten/ClientSideShortenPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/ClientSideShorten/shorten.php b/plugins/ClientSideShorten/shorten.php index 07c19e2e7..f67cbf3b2 100644 --- a/plugins/ClientSideShorten/shorten.php +++ b/plugins/ClientSideShorten/shorten.php @@ -23,6 +23,7 @@ * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> * @copyright 2008-2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/EmailAuthentication/EmailAuthenticationPlugin.php b/plugins/EmailAuthentication/EmailAuthenticationPlugin.php index 406c00073..4c018537b 100644 --- a/plugins/EmailAuthentication/EmailAuthenticationPlugin.php +++ b/plugins/EmailAuthentication/EmailAuthenticationPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/Facebook/FacebookPlugin.php b/plugins/Facebook/FacebookPlugin.php index 5dba73a5d..19989a952 100644 --- a/plugins/Facebook/FacebookPlugin.php +++ b/plugins/Facebook/FacebookPlugin.php @@ -585,7 +585,7 @@ class FacebookPlugin extends Plugin function onStartEnqueueNotice($notice, &$transports) { - if (self::hasKeys()) { + if (self::hasKeys() && $notice->isLocal()) { array_push($transports, 'facebook'); } return true; 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 83664995a..1290fed55 100644 --- a/plugins/Facebook/facebookutil.php +++ b/plugins/Facebook/facebookutil.php @@ -81,97 +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 - - // XXX: Does this call count against our per user FB request limit? - // If so we should consider storing verb elsewhere or not storing - - $prefix = trim($facebook->api_client->data_getUserPreference(FACEBOOK_NOTICE_PREFIX, - $fbuid)); - - $status = "$prefix $notice->content"; - - $can_publish = $facebook->api_client->users_hasAppPermission('publish_stream', - $fbuid); - - $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, - "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, - "Posted notice $notice->id to Facebook " . - "as a status update (fbuid = $fbuid)."); + // Check permissions + + common_debug( + 'FacebookPlugin - checking for publish_stream permission for user ' + . "$user->nickname ($user->id), Facebook UID: $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 + + $fql = "SELECT publish_stream FROM permissions WHERE uid = $fbuid"; + $result = $facebook->api_client->fql_query($fql); + + $canPublish = 0; + + 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->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 { - $msg = "Not sending notice $notice->id to Facebook " . - "because user $user->nickname hasn't given the " . + common_debug( + "FacebookPlugin - $user->nickname ($user->id), Facebook UID: $fbuid " + .'does NOT have status_update permission. Facebook ' + . 'returned: ' . var_export($canPublish, true) + ); + } + + // 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 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 = "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/FirePHP/FirePHPPlugin.php b/plugins/FirePHP/FirePHPPlugin.php index 9143ff69c..d984ec1af 100644 --- a/plugins/FirePHP/FirePHPPlugin.php +++ b/plugins/FirePHP/FirePHPPlugin.php @@ -24,11 +24,13 @@ Author URI: http://candrews.integralblue.com/ * * 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 Plugin * @package MinifyPlugin * @maintainer Craig Andrews <candrews@integralblue.com> + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org + * @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') && !defined('LACONICA')) { exit(1); } diff --git a/plugins/Imap/ImapPlugin.php b/plugins/Imap/ImapPlugin.php index d1e920b00..66be799d3 100644 --- a/plugins/Imap/ImapPlugin.php +++ b/plugins/Imap/ImapPlugin.php @@ -21,8 +21,9 @@ * * @category Plugin * @package StatusNet - * @author Zach Copley <zach@status.net> + * @author Craig Andrews <candrews@integralblue.com * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -37,6 +38,7 @@ if (!defined('STATUSNET')) { * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/Imap/imapmanager.php b/plugins/Imap/imapmanager.php index 4c0edeaa1..cfc08c1ee 100644 --- a/plugins/Imap/imapmanager.php +++ b/plugins/Imap/imapmanager.php @@ -23,6 +23,8 @@ * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> * @copyright 2009-2010 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org + * @maintainer Craig Andrews <candrews@integralblue.com> * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/InfiniteScroll/InfiniteScrollPlugin.php b/plugins/InfiniteScroll/InfiniteScrollPlugin.php index a4d1a5d05..50c1b5a20 100644 --- a/plugins/InfiniteScroll/InfiniteScrollPlugin.php +++ b/plugins/InfiniteScroll/InfiniteScrollPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/LdapAuthentication/LdapAuthenticationPlugin.php b/plugins/LdapAuthentication/LdapAuthenticationPlugin.php index 0dfc4c63b..52d326287 100644 --- a/plugins/LdapAuthentication/LdapAuthenticationPlugin.php +++ b/plugins/LdapAuthentication/LdapAuthenticationPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/LdapAuthorization/LdapAuthorizationPlugin.php b/plugins/LdapAuthorization/LdapAuthorizationPlugin.php index 97103d158..3842385cf 100644 --- a/plugins/LdapAuthorization/LdapAuthorizationPlugin.php +++ b/plugins/LdapAuthorization/LdapAuthorizationPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/LdapCommon/LdapCommon.php b/plugins/LdapCommon/LdapCommon.php index ee436d824..1f1647a75 100644 --- a/plugins/LdapCommon/LdapCommon.php +++ b/plugins/LdapCommon/LdapCommon.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/LdapCommon/MemcacheSchemaCache.php b/plugins/LdapCommon/MemcacheSchemaCache.php index 6b91d17d6..4ee2e8e16 100644 --- a/plugins/LdapCommon/MemcacheSchemaCache.php +++ b/plugins/LdapCommon/MemcacheSchemaCache.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/LilUrl/LilUrlPlugin.php b/plugins/LilUrl/LilUrlPlugin.php index cdff9f4e6..06ea49ff5 100644 --- a/plugins/LilUrl/LilUrlPlugin.php +++ b/plugins/LilUrl/LilUrlPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ 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&v=2&sensor=false&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/allmap.php b/plugins/Mapstraction/allmap.php index e73aa76e8..5dab670e2 100644 --- a/plugins/Mapstraction/allmap.php +++ b/plugins/Mapstraction/allmap.php @@ -38,6 +38,7 @@ if (!defined('STATUSNET')) { * @package StatusNet * @author Evan Prodromou <evan@status.net> * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/Mapstraction/map.php b/plugins/Mapstraction/map.php index b809c1b8e..7dab8e10a 100644 --- a/plugins/Mapstraction/map.php +++ b/plugins/Mapstraction/map.php @@ -38,6 +38,7 @@ if (!defined('STATUSNET')) { * @package StatusNet * @author Evan Prodromou <evan@status.net> * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ 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/Mapstraction/usermap.php b/plugins/Mapstraction/usermap.php index ff47b6ada..094334f60 100644 --- a/plugins/Mapstraction/usermap.php +++ b/plugins/Mapstraction/usermap.php @@ -38,6 +38,7 @@ if (!defined('STATUSNET')) { * @package StatusNet * @author Evan Prodromou <evan@status.net> * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/MemcachedPlugin.php b/plugins/MemcachedPlugin.php index 707e6db9a..77b989b95 100644 --- a/plugins/MemcachedPlugin.php +++ b/plugins/MemcachedPlugin.php @@ -22,8 +22,10 @@ * * @category Cache * @package StatusNet - * @author Evan Prodromou <evan@status.net>, Craig Andrews <candrews@integralblue.com> + * @author Evan Prodromou <evan@status.net> + * @author Craig Andrews <candrews@integralblue.com> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -43,8 +45,10 @@ if (!defined('STATUSNET')) { * * @category Cache * @package StatusNet - * @author Evan Prodromou <evan@status.net>, Craig Andrews <candrews@integralblue.com> + * @author Evan Prodromou <evan@status.net> + * @author Craig Andrews <candrews@integralblue.com> * @copyright 2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/Meteor/MeteorPlugin.php b/plugins/Meteor/MeteorPlugin.php index 5600d5fcc..ec8c9e217 100644 --- a/plugins/Meteor/MeteorPlugin.php +++ b/plugins/Meteor/MeteorPlugin.php @@ -50,6 +50,7 @@ class MeteorPlugin extends RealtimePlugin public $controlport = null; public $controlserver = null; public $channelbase = null; + public $persistent = true; protected $_socket = null; function __construct($webserver=null, $webport=4670, $controlport=4671, $controlserver=null, $channelbase='') @@ -102,8 +103,14 @@ class MeteorPlugin extends RealtimePlugin function _connect() { $controlserver = (empty($this->controlserver)) ? $this->webserver : $this->controlserver; + + $errno = $errstr = null; + $timeout = 5; + $flags = STREAM_CLIENT_CONNECT; + if ($this->persistent) $flags |= STREAM_CLIENT_PERSISTENT; + // May throw an exception. - $this->_socket = stream_socket_client("tcp://{$controlserver}:{$this->controlport}"); + $this->_socket = stream_socket_client("tcp://{$controlserver}:{$this->controlport}", $errno, $errstr, $timeout, $flags); if (!$this->_socket) { throw new Exception("Couldn't connect to {$controlserver} on {$this->controlport}"); } @@ -124,8 +131,10 @@ class MeteorPlugin extends RealtimePlugin function _disconnect() { - $cnt = fwrite($this->_socket, "QUIT\n"); - @fclose($this->_socket); + if (!$this->persistent) { + $cnt = fwrite($this->_socket, "QUIT\n"); + @fclose($this->_socket); + } } // Meteord flips out with default '/' separator diff --git a/plugins/Minify/MinifyPlugin.php b/plugins/Minify/MinifyPlugin.php index 69def6064..13010e75a 100644 --- a/plugins/Minify/MinifyPlugin.php +++ b/plugins/Minify/MinifyPlugin.php @@ -29,6 +29,7 @@ Author URI: http://candrews.integralblue.com/ /** * @package MinifyPlugin * @maintainer Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org */ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 5b153216e..c61e2cc5f 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -87,6 +87,8 @@ class OStatusPlugin extends Plugin // Outgoing from our internal PuSH hub $qm->connect('hubconf', 'HubConfQueueHandler'); + $qm->connect('hubprep', 'HubPrepQueueHandler'); + $qm->connect('hubout', 'HubOutQueueHandler'); // Outgoing Salmon replies (when we don't need a return value) @@ -102,8 +104,10 @@ class OStatusPlugin extends Plugin */ function onStartEnqueueNotice($notice, &$transports) { - // put our transport first, in case there's any conflict (like OMB) - array_unshift($transports, 'ostatus'); + if ($notice->isLocal()) { + // put our transport first, in case there's any conflict (like OMB) + array_unshift($transports, 'ostatus'); + } return true; } diff --git a/plugins/OStatus/classes/HubSub.php b/plugins/OStatus/classes/HubSub.php index cdace3c1f..7db528a4e 100644 --- a/plugins/OStatus/classes/HubSub.php +++ b/plugins/OStatus/classes/HubSub.php @@ -260,6 +260,37 @@ class HubSub extends Memcached_DataObject $retries = intval(common_config('ostatus', 'hub_retries')); } + if (common_config('ostatus', 'local_push_bypass')) { + // If target is a local site, bypass the web server and drop the + // item directly into the target's input queue. + $url = parse_url($this->callback); + $wildcard = common_config('ostatus', 'local_wildcard'); + $site = Status_network::getFromHostname($url['host'], $wildcard); + + if ($site) { + if ($this->secret) { + $hmac = 'sha1=' . hash_hmac('sha1', $atom, $this->secret); + } else { + $hmac = ''; + } + + // Hack: at the moment we stick the subscription ID in the callback + // URL so we don't have to look inside the Atom to route the subscription. + // For now this means we need to extract that from the target URL + // so we can include it in the data. + $parts = explode('/', $url['path']); + $subId = intval(array_pop($parts)); + + $data = array('feedsub_id' => $subId, + 'post' => $atom, + 'hmac' => $hmac); + common_log(LOG_DEBUG, "Cross-site PuSH bypass enqueueing straight to $site->nickname feed $subId"); + $qm = QueueManager::get(); + $qm->enqueue($data, 'pushin', $site->nickname); + return; + } + } + // 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 @@ -274,6 +305,26 @@ class HubSub extends Memcached_DataObject } /** + * Queue up a large batch of pushes to multiple subscribers + * for this same topic update. + * + * If queues are disabled, this will run immediately. + * + * @param string $atom well-formed Atom feed + * @param array $pushCallbacks list of callback URLs + */ + function bulkDistribute($atom, $pushCallbacks) + { + $data = array('atom' => $atom, + 'topic' => $this->topic, + 'pushCallbacks' => $pushCallbacks); + common_log(LOG_INFO, "Queuing PuSH batch: $this->topic to " . + count($pushCallbacks) . " sites"); + $qm = QueueManager::get(); + $qm->enqueue($data, 'hubprep'); + } + + /** * Send a 'fat ping' to the subscriber's callback endpoint * containing the given Atom feed chunk. * diff --git a/plugins/OStatus/lib/discoveryhints.php b/plugins/OStatus/lib/discoveryhints.php index ca54a0f5f..34c9be277 100644 --- a/plugins/OStatus/lib/discoveryhints.php +++ b/plugins/OStatus/lib/discoveryhints.php @@ -84,7 +84,7 @@ class DiscoveryHints { $hints['fullname'] = implode(' ', $hcard['n']); } - if (array_key_exists('photo', $hcard)) { + if (array_key_exists('photo', $hcard) && count($hcard['photo'])) { $hints['avatar'] = $hcard['photo'][0]; } diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php index d1e58f1d6..8905d2e21 100644 --- a/plugins/OStatus/lib/ostatusqueuehandler.php +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -25,6 +25,18 @@ */ class OStatusQueueHandler extends QueueHandler { + // If we have more than this many subscribing sites on a single feed, + // break up the PuSH distribution into smaller batches which will be + // rolled into the queue progressively. This reduces disruption to + // other, shorter activities being enqueued while we work. + const MAX_UNBATCHED = 50; + + // Each batch (a 'hubprep' entry) will have this many items. + // Selected to provide a balance between queue packet size + // and number of batches that will end up getting processed. + // For 20,000 target sites, 1000 should work acceptably. + const BATCH_SIZE = 1000; + function transport() { return 'ostatus'; @@ -147,14 +159,31 @@ class OStatusQueueHandler extends QueueHandler /** * Queue up direct feed update pushes to subscribers on our internal hub. + * If there are a large number of subscriber sites, intermediate bulk + * distribution triggers may be queued. + * * @param string $atom update feed, containing only new/changed items * @param HubSub $sub open query of subscribers */ function pushFeedInternal($atom, $sub) { common_log(LOG_INFO, "Preparing $sub->N PuSH distribution(s) for $sub->topic"); + $n = 0; + $batch = array(); while ($sub->fetch()) { - $sub->distribute($atom); + $n++; + if ($n < self::MAX_UNBATCHED) { + $sub->distribute($atom); + } else { + $batch[] = $sub->callback; + if (count($batch) >= self::BATCH_SIZE) { + $sub->bulkDistribute($atom, $batch); + $batch = array(); + } + } + } + if (count($batch) >= 0) { + $sub->bulkDistribute($atom, $batch); } } diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php index fdcfacfa5..7d6a5dc00 100644 --- a/plugins/OpenID/OpenIDPlugin.php +++ b/plugins/OpenID/OpenIDPlugin.php @@ -20,7 +20,9 @@ * @category Plugin * @package StatusNet * @author Evan Prodromou <evan@status.net> + * @author Craig Andrews <candrews@integralblue.com> * @copyright 2009-2010 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -38,6 +40,8 @@ if (!defined('STATUSNET')) { * @category Plugin * @package StatusNet * @author Evan Prodromou <evan@status.net> + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ * @link http://openid.net/ diff --git a/plugins/OpenID/finishaddopenid.php b/plugins/OpenID/finishaddopenid.php index e07ab764e..47b3f7fb1 100644 --- a/plugins/OpenID/finishaddopenid.php +++ b/plugins/OpenID/finishaddopenid.php @@ -138,13 +138,16 @@ class FinishaddopenidAction extends Action $this->message(_m('Error connecting user.')); return; } - if ($sreg) { - if (!oid_update_user($cur, $sreg)) { - // TRANS: message in case the user or the user profile cannot be saved in StatusNet. - $this->message(_m('Error updating profile')); - return; + if (Event::handle('StartOpenIDUpdateUser', array($cur, $canonical, &$sreg))) { + if ($sreg) { + if (!oid_update_user($cur, $sreg)) { + // TRANS: message in case the user or the user profile cannot be saved in StatusNet. + $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 a9ab14199..0c03b5c4d 100644 --- a/plugins/OpenID/finishopenidlogin.php +++ b/plugins/OpenID/finishopenidlogin.php @@ -310,6 +310,8 @@ class FinishopenidloginAction extends Action return; } + Event::handle('StartOpenIDCreateNewUser', array($canonical, &$sreg)); + $location = ''; if (!empty($sreg['country'])) { if ($sreg['postcode']) { @@ -349,6 +351,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); @@ -391,7 +395,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 4dc924695..4ce350f77 100644 --- a/plugins/OpenID/openid.php +++ b/plugins/OpenID/openid.php @@ -145,9 +145,11 @@ 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"); // TRANS: OpenID plugin message. Given when an OpenID is not valid. 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"); // TRANS: OpenID plugin server error. Given when the OpenID authentication request fails. // TRANS: %s is the failure message. return sprintf(_m('OpenID failure: %s'), $auth_request->message); @@ -230,11 +232,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); diff --git a/plugins/OpenID/openidadminpanel.php b/plugins/OpenID/openidadminpanel.php index 063306366..ce4806cc8 100644 --- a/plugins/OpenID/openidadminpanel.php +++ b/plugins/OpenID/openidadminpanel.php @@ -91,6 +91,7 @@ class OpenidadminpanelAction extends AdminPanelAction ); static $booleans = array( + 'openid' => array('append_username'), 'site' => array('openidonly') ); @@ -223,6 +224,15 @@ class OpenIDAdminPanelForm extends AdminForm $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'), diff --git a/plugins/OpenID/openidlogin.php b/plugins/OpenID/openidlogin.php index 34e00ccce..20d6e070c 100644 --- a/plugins/OpenID/openidlogin.php +++ b/plugins/OpenID/openidlogin.php @@ -33,6 +33,9 @@ class OpenidloginAction extends Action $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'); } @@ -100,7 +103,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() @@ -130,10 +141,17 @@ class OpenidloginAction extends Action $this->elementStart('ul', 'form_data'); $this->elementStart('li'); $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 { diff --git a/plugins/OpenID/openidserver.php b/plugins/OpenID/openidserver.php index f7e3a45f2..0e16881c5 100644 --- a/plugins/OpenID/openidserver.php +++ b/plugins/OpenID/openidserver.php @@ -23,6 +23,7 @@ * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> * @copyright 2008-2009 StatusNet, Inc. + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -43,6 +44,7 @@ require_once(INSTALLDIR.'/plugins/OpenID/User_openid_trustroot.php'); * @category Settings * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/PtitUrl/PtitUrlPlugin.php b/plugins/PtitUrl/PtitUrlPlugin.php index cdf46846b..25a463c0b 100644 --- a/plugins/PtitUrl/PtitUrlPlugin.php +++ b/plugins/PtitUrl/PtitUrlPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php index 661c32141..c1951cdbf 100644 --- a/plugins/RSSCloud/RSSCloudPlugin.php +++ b/plugins/RSSCloud/RSSCloudPlugin.php @@ -192,25 +192,13 @@ class RSSCloudPlugin extends Plugin function onStartEnqueueNotice($notice, &$transports) { - array_push($transports, 'rsscloud'); + if ($notice->isLocal()) { + array_push($transports, 'rsscloud'); + } return true; } /** - * Determine whether the notice was locally created - * - * @param Notice $notice the notice in question - * - * @return boolean locality - */ - - function _isLocal($notice) - { - return ($notice->is_local == Notice::LOCAL_PUBLIC || - $notice->is_local == Notice::LOCAL_NONPUBLIC); - } - - /** * Create the rsscloud_subscription table if it's not * already in the DB * diff --git a/plugins/RequireValidatedEmail/README b/plugins/RequireValidatedEmail/README index 46ee24d5f..84b1485b2 100644 --- a/plugins/RequireValidatedEmail/README +++ b/plugins/RequireValidatedEmail/README @@ -12,6 +12,20 @@ registered prior to that timestamp. addPlugin('RequireValidatedEmail', array('grandfatherCutoff' => 'Dec 7, 2009'); +You can also exclude the validation checks from OpenID accounts +connected to a trusted provider, by providing a list of regular +expressions to match their provider URLs. + +For example, to trust WikiHow and Wikipedia users: + + addPlugin('RequireValidatedEmailPlugin', array( + 'trustedOpenIDs' => array( + '!^http://\w+\.wikihow\.com/!', + '!^http://\w+\.wikipedia\.org/!', + ), + )); + + Todo: * add a more visible indicator that validation is still outstanding diff --git a/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php b/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php index ccefa14f6..af75b96e0 100644 --- a/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php +++ b/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php @@ -21,8 +21,9 @@ * * @category Plugin * @package StatusNet - * @author Craig Andrews <candrews@integralblue.com>, Brion Vibber <brion@status.net> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @author Craig Andrews <candrews@integralblue.com> + * @author Brion Vibber <brion@status.net> + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ @@ -37,6 +38,20 @@ class RequireValidatedEmailPlugin extends Plugin // without the validation requirement. public $grandfatherCutoff=null; + // If OpenID plugin is installed, users with a verified OpenID + // association whose provider URL matches one of these regexes + // will be considered to be sufficiently valid for our needs. + // + // For example, to trust WikiHow and Wikipedia OpenID users: + // + // addPlugin('RequireValidatedEmailPlugin', array( + // 'trustedOpenIDs' => array( + // '!^http://\w+\.wikihow\.com/!', + // '!^http://\w+\.wikipedia\.org/!', + // ), + // )); + public $trustedOpenIDs=array(); + function __construct() { parent::__construct(); @@ -90,13 +105,17 @@ class RequireValidatedEmailPlugin extends Plugin */ protected function validated($user) { - if ($this->grandfathered($user)) { - return true; - } - // The email field is only stored after validation... // Until then you'll find them in confirm_address. - return !empty($user->email); + $knownGood = !empty($user->email) || + $this->grandfathered($user) || + $this->hasTrustedOpenID($user); + + // Give other plugins a chance to override, if they can validate + // that somebody's ok despite a non-validated email. + Event::handle('RequireValidatedEmailPlugin_Override', array($user, &$knownGood)); + + return $knownGood; } /** @@ -118,6 +137,28 @@ class RequireValidatedEmailPlugin extends Plugin return false; } + /** + * Override for RequireValidatedEmail plugin. If we have a user who's + * not validated an e-mail, but did come from a trusted provider, + * we'll consider them ok. + */ + function hasTrustedOpenID($user) + { + if ($this->trustedOpenIDs && class_exists('User_openid')) { + foreach ($this->trustedOpenIDs as $regex) { + $oid = new User_openid(); + $oid->user_id = $user->id; + $oid->find(); + while ($oid->fetch()) { + if (preg_match($regex, $oid->canonical)) { + return true; + } + } + } + } + return false; + } + function onPluginVersion(&$versions) { $versions[] = array('name' => 'Require Validated Email', diff --git a/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php b/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php index dac5a1588..8a05a7734 100644 --- a/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php +++ b/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/SimpleUrl/SimpleUrlPlugin.php b/plugins/SimpleUrl/SimpleUrlPlugin.php index 5d3f97d33..24250f4d0 100644 --- a/plugins/SimpleUrl/SimpleUrlPlugin.php +++ b/plugins/SimpleUrl/SimpleUrlPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/TabFocus/TabFocusPlugin.php b/plugins/TabFocus/TabFocusPlugin.php index bf89c478c..46e329d8a 100644 --- a/plugins/TabFocus/TabFocusPlugin.php +++ b/plugins/TabFocus/TabFocusPlugin.php @@ -23,7 +23,7 @@ * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> * @author Paul Irish <paul.irish@isobar.net> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/TightUrl/TightUrlPlugin.php b/plugins/TightUrl/TightUrlPlugin.php index f242db6c8..8fd645945 100644 --- a/plugins/TightUrl/TightUrlPlugin.php +++ b/plugins/TightUrl/TightUrlPlugin.php @@ -22,7 +22,7 @@ * @category Plugin * @package StatusNet * @author Craig Andrews <candrews@integralblue.com> - * @copyright 2009 Craig Andrews http://candrews.integralblue.com + * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ diff --git a/plugins/TwitterBridge/TwitterBridgePlugin.php b/plugins/TwitterBridge/TwitterBridgePlugin.php index 1a0a69682..65b3a6b38 100644 --- a/plugins/TwitterBridge/TwitterBridgePlugin.php +++ b/plugins/TwitterBridge/TwitterBridgePlugin.php @@ -221,7 +221,7 @@ class TwitterBridgePlugin extends Plugin */ function onStartEnqueueNotice($notice, &$transports) { - if (self::hasKeys()) { + if (self::hasKeys() && $notice->isLocal()) { // Avoid a possible loop if ($notice->source != 'twitter') { array_push($transports, 'twitter'); diff --git a/plugins/UserFlag/clearflag.php b/plugins/UserFlag/clearflag.php index bd6732e2d..f032527ed 100644 --- a/plugins/UserFlag/clearflag.php +++ b/plugins/UserFlag/clearflag.php @@ -81,7 +81,7 @@ class ClearflagAction extends ProfileFormAction if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->handlePost(); if (!$this->boolean('ajax')) { - $this->returnToArgs(); + $this->returnToPrevious(); } } } diff --git a/plugins/UserFlag/flagprofile.php b/plugins/UserFlag/flagprofile.php index 2d0f0abb9..018c1e8ac 100644 --- a/plugins/UserFlag/flagprofile.php +++ b/plugins/UserFlag/flagprofile.php @@ -87,7 +87,7 @@ class FlagprofileAction extends ProfileFormAction if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->handlePost(); if (!$this->boolean('ajax')) { - $this->returnToArgs(); + $this->returnToPrevious(); } } } 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); + } + +} + |