forked from GNUsocial/gnu-social
		
	Merge branch 'master' of gitorious.org:statusnet/mainline
This commit is contained in:
		| @@ -120,18 +120,11 @@ class ApiStatusesDestroyAction extends ApiAuthAction | ||||
|              $replies->get('notice_id', $this->notice_id); | ||||
|              $replies->delete(); | ||||
|              $this->notice->delete(); | ||||
|  | ||||
|              if ($this->format == 'xml') { | ||||
|                  $this->showSingleXmlStatus($this->notice); | ||||
|              } elseif ($this->format == 'json') { | ||||
|                  $this->show_single_json_status($this->notice); | ||||
|              } | ||||
| 	     $this->showNotice(); | ||||
|          } else { | ||||
|              $this->clientError(_('You may not delete another user\'s status.'), | ||||
|                  403, $this->format); | ||||
|          } | ||||
|  | ||||
|         $this->showNotice(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -150,7 +150,7 @@ class ApiTimelineFavoritesAction extends ApiBareAuthAction | ||||
|  | ||||
|             header('Content-Type: application/atom+xml; charset=utf-8'); | ||||
|  | ||||
|             $atom = new AtomNoticeFeed(); | ||||
|             $atom = new AtomNoticeFeed($this->auth_user); | ||||
|  | ||||
|             $atom->setId($id); | ||||
|             $atom->setTitle($title); | ||||
|   | ||||
| @@ -152,7 +152,7 @@ class ApiTimelineFriendsAction extends ApiBareAuthAction | ||||
|  | ||||
|             header('Content-Type: application/atom+xml; charset=utf-8'); | ||||
|  | ||||
|             $atom = new AtomNoticeFeed(); | ||||
|             $atom = new AtomNoticeFeed($this->auth_user); | ||||
|  | ||||
|             $atom->setId($id); | ||||
|             $atom->setTitle($title); | ||||
|   | ||||
| @@ -105,7 +105,7 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction | ||||
|     function showTimeline() | ||||
|     { | ||||
|         // We'll pull common formatting out of this for other formats | ||||
|         $atom = new AtomGroupNoticeFeed($this->group); | ||||
|         $atom = new AtomGroupNoticeFeed($this->group, $this->auth_user); | ||||
|  | ||||
|         $self = $this->getSelfUri(); | ||||
|  | ||||
|   | ||||
| @@ -151,7 +151,7 @@ class ApiTimelineHomeAction extends ApiBareAuthAction | ||||
|  | ||||
|             header('Content-Type: application/atom+xml; charset=utf-8'); | ||||
|  | ||||
|             $atom = new AtomNoticeFeed(); | ||||
|             $atom = new AtomNoticeFeed($this->auth_user); | ||||
|  | ||||
|             $atom->setId($id); | ||||
|             $atom->setTitle($title); | ||||
|   | ||||
| @@ -151,7 +151,7 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction | ||||
|  | ||||
|             header('Content-Type: application/atom+xml; charset=utf-8'); | ||||
|  | ||||
|             $atom = new AtomNoticeFeed(); | ||||
|             $atom = new AtomNoticeFeed($this->auth_user); | ||||
|  | ||||
|             $atom->setId($id); | ||||
|             $atom->setTitle($title); | ||||
|   | ||||
| @@ -130,7 +130,7 @@ class ApiTimelinePublicAction extends ApiPrivateAuthAction | ||||
|  | ||||
|             header('Content-Type: application/atom+xml; charset=utf-8'); | ||||
|  | ||||
|             $atom = new AtomNoticeFeed(); | ||||
|             $atom = new AtomNoticeFeed($this->auth_user); | ||||
|  | ||||
|             $atom->setId($id); | ||||
|             $atom->setTitle($title); | ||||
|   | ||||
| @@ -117,7 +117,7 @@ class ApiTimelineRetweetsOfMeAction extends ApiAuthAction | ||||
|  | ||||
|             header('Content-Type: application/atom+xml; charset=utf-8'); | ||||
|  | ||||
|             $atom = new AtomNoticeFeed(); | ||||
|             $atom = new AtomNoticeFeed($this->auth_user); | ||||
|  | ||||
|             $atom->setId($id); | ||||
|             $atom->setTitle($title); | ||||
|   | ||||
| @@ -138,7 +138,7 @@ class ApiTimelineTagAction extends ApiPrivateAuthAction | ||||
|  | ||||
|             header('Content-Type: application/atom+xml; charset=utf-8'); | ||||
|  | ||||
|             $atom = new AtomNoticeFeed(); | ||||
|             $atom = new AtomNoticeFeed($this->auth_user); | ||||
|  | ||||
|             $atom->setId($id); | ||||
|             $atom->setTitle($title); | ||||
|   | ||||
| @@ -115,7 +115,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction | ||||
|  | ||||
|         // We'll use the shared params from the Atom stub | ||||
|         // for other feed types. | ||||
|         $atom = new AtomUserNoticeFeed($this->user); | ||||
|         $atom = new AtomUserNoticeFeed($this->user, $this->auth_user); | ||||
|  | ||||
|         $link = common_local_url( | ||||
|             'showstream', | ||||
|   | ||||
| @@ -95,7 +95,9 @@ class FoafAction extends Action | ||||
|         // Would be nice to tell if they were a Person or not (e.g. a #person usertag?) | ||||
|         $this->elementStart('Agent', array('rdf:about' => | ||||
|                                              $this->user->uri)); | ||||
|         if ($this->user->email) { | ||||
|             $this->element('mbox_sha1sum', null, sha1('mailto:' . $this->user->email)); | ||||
|         } | ||||
|         if ($this->profile->fullname) { | ||||
|             $this->element('name', null, $this->profile->fullname); | ||||
|         } | ||||
|   | ||||
| @@ -116,7 +116,11 @@ class File extends Memcached_DataObject | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     function processNew($given_url, $notice_id=null) { | ||||
|     /** | ||||
|      * @fixme refactor this mess, it's gotten pretty scary. | ||||
|      * @param bool $followRedirects | ||||
|      */ | ||||
|     function processNew($given_url, $notice_id=null, $followRedirects=true) { | ||||
|         if (empty($given_url)) return -1;   // error, no url to process | ||||
|         $given_url = File_redirection::_canonUrl($given_url); | ||||
|         if (empty($given_url)) return -1;   // error, no url to process | ||||
| @@ -124,6 +128,10 @@ class File extends Memcached_DataObject | ||||
|         if (empty($file)) { | ||||
|             $file_redir = File_redirection::staticGet('url', $given_url); | ||||
|             if (empty($file_redir)) { | ||||
|                 // @fixme for new URLs this also looks up non-redirect data | ||||
|                 // such as target content type, size, etc, which we need | ||||
|                 // for File::saveNew(); so we call it even if not following | ||||
|                 // new redirects. | ||||
|                 $redir_data = File_redirection::where($given_url); | ||||
|                 if (is_array($redir_data)) { | ||||
|                     $redir_url = $redir_data['url']; | ||||
| @@ -134,11 +142,19 @@ class File extends Memcached_DataObject | ||||
|                     throw new ServerException("Can't process url '$given_url'"); | ||||
|                 } | ||||
|                 // TODO: max field length | ||||
|                 if ($redir_url === $given_url || strlen($redir_url) > 255) { | ||||
|                 if ($redir_url === $given_url || strlen($redir_url) > 255 || !$followRedirects) { | ||||
|                     $x = File::saveNew($redir_data, $given_url); | ||||
|                     $file_id = $x->id; | ||||
|                 } else { | ||||
|                     $x = File::processNew($redir_url, $notice_id); | ||||
|                     // This seems kind of messed up... for now skipping this part | ||||
|                     // if we're already under a redirect, so we don't go into | ||||
|                     // horrible infinite loops if we've been given an unstable | ||||
|                     // redirect (where the final destination of the first request | ||||
|                     // doesn't match what we get when we ask for it again). | ||||
|                     // | ||||
|                     // Seen in the wild with clojure.org, which redirects through | ||||
|                     // wikispaces for auth and appends session data in the URL params. | ||||
|                     $x = File::processNew($redir_url, $notice_id, /*followRedirects*/false); | ||||
|                     $file_id = $x->id; | ||||
|                     File_redirection::saveNew($redir_data, $file_id, $given_url); | ||||
|                 } | ||||
|   | ||||
| @@ -97,6 +97,10 @@ class Notice extends Memcached_DataObject | ||||
|         // For auditing purposes, save a record that the notice | ||||
|         // was deleted. | ||||
|  | ||||
|         // @fixme we have some cases where things get re-run and so the | ||||
|         // insert fails. | ||||
|         $deleted = Deleted_notice::staticGet('id', $this->id); | ||||
|         if (!$deleted) { | ||||
|             $deleted = new Deleted_notice(); | ||||
|  | ||||
|             $deleted->id         = $this->id; | ||||
| @@ -106,6 +110,7 @@ class Notice extends Memcached_DataObject | ||||
|             $deleted->deleted    = common_sql_now(); | ||||
|  | ||||
|             $deleted->insert(); | ||||
|         } | ||||
|  | ||||
|         // Clear related records | ||||
|  | ||||
| @@ -1235,7 +1240,7 @@ class Notice extends Memcached_DataObject | ||||
|  | ||||
|         $noticeInfoAttr = array( | ||||
|             'local_id'   => $this->id,    // local notice ID (useful to clients for ordering) | ||||
|             'source'     => $this->source // the client name (source attribution) | ||||
|             'source'     => $this->source, // the client name (source attribution) | ||||
|         ); | ||||
|  | ||||
|         $ns = $this->getSource(); | ||||
| @@ -1246,7 +1251,11 @@ class Notice extends Memcached_DataObject | ||||
|         } | ||||
|  | ||||
|         if (!empty($cur)) { | ||||
|             $noticeInfoAttr['favorited'] = ($cur->hasFave($this)) ? 'true' : 'false'; | ||||
|             $noticeInfoAttr['favorite'] = ($cur->hasFave($this)) ? "true" : "false"; | ||||
|         } | ||||
|  | ||||
|         if (!empty($this->repeat_of)) { | ||||
|             $noticeInfoAttr['repeat_of'] = $this->repeat_of; | ||||
|         } | ||||
|  | ||||
|         $xs->element('statusnet:notice_info', $noticeInfoAttr, null); | ||||
|   | ||||
| @@ -9,6 +9,7 @@ VALUES | ||||
|     ('bti','bti','http://gregkh.github.com/bti/', now()), | ||||
|     ('choqok', 'Choqok', 'http://choqok.gnufolks.org/', now()), | ||||
|     ('cliqset', 'Cliqset', 'http://www.cliqset.com/', now()), | ||||
|     ('DarterosStatus', 'Darteros Status', 'http://www.darteros.com/doc/Darteros_Status', now()), | ||||
|     ('deskbar','Deskbar-Applet','http://www.gnome.org/projects/deskbar-applet/', now()), | ||||
|     ('Do','Gnome Do','http://do.davebsd.com/wiki/index.php?title=Microblog_Plugin', now()), | ||||
|     ('drupal','Drupal','http://drupal.org/', now()), | ||||
|   | ||||
| @@ -50,12 +50,13 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed | ||||
|      * Constructor | ||||
|      * | ||||
|      * @param Group   $group   the group for the feed | ||||
|      * @param User    $cur     the current authenticated user, if any | ||||
|      * @param boolean $indent  flag to turn indenting on or off | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     function __construct($group, $indent = true) { | ||||
|         parent::__construct($indent); | ||||
|     function __construct($group, $cur = null, $indent = true) { | ||||
|         parent::__construct($cur, $indent); | ||||
|         $this->group = $group; | ||||
|  | ||||
|         $title      = sprintf(_("%s timeline"), $group->nickname); | ||||
|   | ||||
| @@ -44,9 +44,22 @@ if (!defined('STATUSNET')) | ||||
|  */ | ||||
| class AtomNoticeFeed extends Atom10Feed | ||||
| { | ||||
|     function __construct($indent = true) { | ||||
|     var $cur; | ||||
|  | ||||
|     /** | ||||
|      * Constructor - adds a bunch of XML namespaces we need in our | ||||
|      * notice-specific Atom feeds, and allows setting the current | ||||
|      * authenticated user (useful for API methods). | ||||
|      * | ||||
|      * @param User    $cur     the current authenticated user (optional) | ||||
|      * @param boolean $indent  Whether to indent XML output | ||||
|      * | ||||
|      */ | ||||
|     function __construct($cur = null, $indent = true) { | ||||
|         parent::__construct($indent); | ||||
|  | ||||
|         $this->cur = $cur; | ||||
|  | ||||
|         // Feeds containing notice info use these namespaces | ||||
|  | ||||
|         $this->addNamespace( | ||||
| @@ -115,7 +128,7 @@ class AtomNoticeFeed extends Atom10Feed | ||||
|         $source = $this->showSource(); | ||||
|         $author = $this->showAuthor(); | ||||
|  | ||||
|         $cur = common_current_user(); | ||||
|         $cur = empty($this->cur) ? common_current_user() : $this->cur; | ||||
|  | ||||
|         $this->addEntryRaw($notice->asAtomEntry(false, $source, $author, $cur)); | ||||
|     } | ||||
|   | ||||
| @@ -50,13 +50,14 @@ class AtomUserNoticeFeed extends AtomNoticeFeed | ||||
|      * Constructor | ||||
|      * | ||||
|      * @param User    $user    the user for the feed | ||||
|      * @param User    $cur     the current authenticated user, if any | ||||
|      * @param boolean $indent  flag to turn indenting on or off | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|  | ||||
|     function __construct($user, $indent = true) { | ||||
|         parent::__construct($indent); | ||||
|     function __construct($user, $cur = null, $indent = true) { | ||||
|         parent::__construct($cur, $indent); | ||||
|         $this->user = $user; | ||||
|         if (!empty($user)) { | ||||
|             $profile = $user->getProfile(); | ||||
|   | ||||
| @@ -61,7 +61,7 @@ if (!function_exists('dpgettext')) { | ||||
|      * Not currently exposed in PHP's gettext module; implemented to be compat | ||||
|      * with gettext.h's macros. | ||||
|      * | ||||
|      * @param string $domain domain identifier, or null for default domain | ||||
|      * @param string $domain domain identifier | ||||
|      * @param string $context context identifier, should be some key like "menu|file" | ||||
|      * @param string $msgid English source text | ||||
|      * @return string original or translated message | ||||
| @@ -106,7 +106,7 @@ if (!function_exists('dnpgettext')) { | ||||
|      * Not currently exposed in PHP's gettext module; implemented to be compat | ||||
|      * with gettext.h's macros. | ||||
|      * | ||||
|      * @param string $domain domain identifier, or null for default domain | ||||
|      * @param string $domain domain identifier | ||||
|      * @param string $context context identifier, should be some key like "menu|file" | ||||
|      * @param string $msg singular English source text | ||||
|      * @param string $plural plural English source text | ||||
| @@ -180,7 +180,11 @@ function _m($msg/*, ...*/) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Looks for which plugin we've been called from to set the gettext domain. | ||||
|  * Looks for which plugin we've been called from to set the gettext domain; | ||||
|  * if not in a plugin subdirectory, we'll use the default 'statusnet'. | ||||
|  * | ||||
|  * Note: we can't return null for default domain since most of the PHP gettext | ||||
|  * wrapper functions turn null into "" before passing to the backend library. | ||||
|  * | ||||
|  * @param array $backtrace debug_backtrace() output | ||||
|  * @return string | ||||
| @@ -205,12 +209,20 @@ function _mdomain($backtrace) | ||||
|         if (DIRECTORY_SEPARATOR !== '/') { | ||||
|             $path = strtr($path, DIRECTORY_SEPARATOR, '/'); | ||||
|         } | ||||
|         $cut = strpos($path, '/plugins/') + 9; | ||||
|         $plug = strpos($path, '/plugins/'); | ||||
|         if ($plug === false) { | ||||
|             // We're not in a plugin; return default domain. | ||||
|             return 'statusnet'; | ||||
|         } else { | ||||
|             $cut = $plug + 9; | ||||
|             $cut2 = strpos($path, '/', $cut); | ||||
|         if ($cut && $cut2) { | ||||
|             if ($cut2) { | ||||
|                 $cached[$path] = substr($path, $cut, $cut2 - $cut); | ||||
|             } else { | ||||
|             return null; | ||||
|                 // We might be running directly from the plugins dir? | ||||
|                 // If so, there's no place to store locale info. | ||||
|                 return 'statusnet'; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return $cached[$path]; | ||||
|   | ||||
| @@ -122,7 +122,19 @@ class StompQueueManager extends QueueManager | ||||
|     public function enqueue($object, $queue) | ||||
|     { | ||||
|         $this->_connect(); | ||||
|         return $this->_doEnqueue($object, $queue, $this->defaultIdx); | ||||
|         if (common_config('queue', 'stomp_enqueue_on')) { | ||||
|             // We're trying to force all writes to a single server. | ||||
|             // WARNING: this might do odd things if that server connection dies. | ||||
|             $idx = array_search(common_config('queue', 'stomp_enqueue_on'), | ||||
|                                 $this->servers); | ||||
|             if ($idx === false) { | ||||
|                 common_log(LOG_ERR, 'queue stomp_enqueue_on setting does not match our server list.'); | ||||
|                 $idx = $this->defaultIdx; | ||||
|             } | ||||
|         } else { | ||||
|             $idx = $this->defaultIdx; | ||||
|         } | ||||
|         return $this->_doEnqueue($object, $queue, $idx); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -39,8 +39,8 @@ 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/ | ||||
| - 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/ | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|     } 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 | ||||
|     else if ($cookies = | ||||
|              $this->get_valid_fb_params($_COOKIE, null, $this->api_key)) { | ||||
|  | ||||
|       $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'); | ||||
|                     'v'         => '1.0', | ||||
|                     'req_perms' => $req_perms); | ||||
|  | ||||
|     if ($next) { | ||||
|       $params['next'] = $next; | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
|   | ||||
| @@ -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'); | ||||
|   | ||||
| @@ -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']; | ||||
|             } | ||||
|  | ||||
|             common_debug("FacebookPlugin - checking for status_update permission for user $user->id"); | ||||
|             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) | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             $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)."); | ||||
|             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) | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             // 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) { | ||||
|  | ||||
|             $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)!"; | ||||
|  | ||||
|             common_log(LOG_WARNING, $msg); | ||||
|  | ||||
|             if ($code == 100 || $code == 200 || $code == 250) { | ||||
|  | ||||
|                 // 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 | ||||
|  | ||||
|                 remove_facebook_app($flink); | ||||
|  | ||||
|         } else { | ||||
|  | ||||
|                 // Try sending again later. | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             return handleFacebookError($e, $notice, $flink); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return true; | ||||
|  | ||||
| } | ||||
|  | ||||
| function updateProfileBox($facebook, $flink, $notice) { | ||||
|     $fbaction = new FacebookAction($output = 'php://output', | ||||
|                                    $indent = null, $facebook, $flink); | ||||
| 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; | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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" | ||||
|     ); | ||||
|  | ||||
|     $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" | ||||
|     ); | ||||
| } | ||||
|  | ||||
| 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" | ||||
|     ); | ||||
|  | ||||
|     $fbattachment = format_attachments($notice->attachments()); | ||||
|  | ||||
|     $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" | ||||
|     ); | ||||
| } | ||||
|  | ||||
| function updateProfileBox($facebook, $flink, $notice, $user) { | ||||
|  | ||||
|     $facebook = getFacebook(); | ||||
|     $fbaction = new FacebookAction( | ||||
|         $output = 'php://output', | ||||
|         $indent = null, | ||||
|         $facebook, | ||||
|         $flink | ||||
|     ); | ||||
|  | ||||
|     $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" | ||||
|     ); | ||||
|  | ||||
|     $fbaction->updateProfileBox($notice); | ||||
|  | ||||
|     common_debug( | ||||
|         'FacebookPlugin - finished updating profile box for ' | ||||
|         . "$user->nickname ($user->id) Facebook UID: $fbuid" | ||||
|     ); | ||||
|  | ||||
| } | ||||
|  | ||||
| function format_attachments($attachments) | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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>'); | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
| @@ -577,6 +596,32 @@ class OpenIDPlugin extends Plugin | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|      * | ||||
|   | ||||
							
								
								
									
										6
									
								
								plugins/OpenID/extlib/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								plugins/OpenID/extlib/README
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										175
									
								
								plugins/OpenID/extlib/teams-extension.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								plugins/OpenID/extlib/teams-extension.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
|   } | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -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 (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! | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
|  | ||||
|         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); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
							
								
								
									
										280
									
								
								plugins/OpenID/openidadminpanel.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								plugins/OpenID/openidadminpanel.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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')); | ||||
|     } | ||||
| } | ||||
| @@ -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') { | ||||
|             $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,8 +97,16 @@ class OpenidloginAction extends Action | ||||
|     function showScripts() | ||||
|     { | ||||
|         parent::showScripts(); | ||||
|         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'); | ||||
|         $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, | ||||
|   | ||||
| @@ -90,12 +90,14 @@ class OpenidsettingsAction extends AccountSettingsAction | ||||
|     { | ||||
|         $user = common_current_user(); | ||||
|  | ||||
|         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', | ||||
| @@ -117,7 +119,7 @@ class OpenidsettingsAction extends AccountSettingsAction | ||||
|                                           'value' => _m('Add'))); | ||||
|             $this->elementEnd('fieldset'); | ||||
|             $this->elementEnd('form'); | ||||
|  | ||||
|         } | ||||
|         $oid = new User_openid(); | ||||
|  | ||||
|         $oid->user_id = $user->id; | ||||
| @@ -234,11 +236,15 @@ class OpenidsettingsAction extends AccountSettingsAction | ||||
|         } | ||||
|  | ||||
|         if ($this->arg('add')) { | ||||
|             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(); | ||||
|         } else if($this->arg('remove_trustroots')) { | ||||
|   | ||||
							
								
								
									
										6
									
								
								plugins/WikiHowProfile/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								plugins/WikiHowProfile/README
									
									
									
									
									
										Normal file
									
								
							| @@ -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. | ||||
							
								
								
									
										196
									
								
								plugins/WikiHowProfile/WikiHowProfilePlugin.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								plugins/WikiHowProfile/WikiHowProfilePlugin.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user