diff --git a/plugins/OpenID/finishaddopenid.php b/plugins/OpenID/finishaddopenid.php index 991e6584ee..18e150a83c 100644 --- a/plugins/OpenID/finishaddopenid.php +++ b/plugins/OpenID/finishaddopenid.php @@ -126,12 +126,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 32b092a0bd..60d46d4ce9 100644 --- a/plugins/OpenID/finishopenidlogin.php +++ b/plugins/OpenID/finishopenidlogin.php @@ -280,6 +280,8 @@ class FinishopenidloginAction extends Action return; } + Event::handle('StartOpenIDCreateNewUser', array($canonical, &$sreg)); + $location = ''; if (!empty($sreg['country'])) { if ($sreg['postcode']) { @@ -319,6 +321,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 +362,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 4ec336e1c3..cdeedbf4d0 100644 --- a/plugins/OpenID/openid.php +++ b/plugins/OpenID/openid.php @@ -212,11 +212,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/WikiHowProfile/README b/plugins/WikiHowProfile/README new file mode 100644 index 0000000000..ee6096c9fb --- /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 0000000000..b72bd55d6d --- /dev/null +++ b/plugins/WikiHowProfile/WikiHowProfilePlugin.php @@ -0,0 +1,196 @@ +. + * + * @category Plugins + * @package StatusNet + * @author Brion Vibber + * @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 + * @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); + } + +} +