]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Add support for an anonymous OAuth consumer. Note: this requires a
authorZach Copley <zach@status.net>
Wed, 20 Oct 2010 03:54:53 +0000 (20:54 -0700)
committerZach Copley <zach@status.net>
Wed, 20 Oct 2010 03:54:53 +0000 (20:54 -0700)
small DB tweak.  Oauth_application_user needs to have the primary
compound key: (profile_id, application_id, token).

http://status.net/open-source/issues/2761

This should also make it possible to have multiple access tokens
per application.

http://status.net/open-source/issues/2788

actions/apioauthaccesstoken.php
actions/apioauthauthorize.php
actions/apistatusesupdate.php
actions/oauthconnectionssettings.php
classes/Oauth_application_user.php
classes/Profile.php
classes/statusnet.ini
db/statusnet.sql
lib/apiauth.php
lib/apioauthstore.php
lib/applicationlist.php

index 6b36d1919e030c136cb7379a4c4d8ee9bc6d338f..21e0049cec9dea0e02d126946011ecdea2ede5a5 100644 (file)
@@ -81,7 +81,7 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
             $app = $datastore->getAppByRequestToken($this->reqToken);
             $atok = $server->fetch_access_token($req);
 
             $app = $datastore->getAppByRequestToken($this->reqToken);
             $atok = $server->fetch_access_token($req);
 
-        } catch (OAuthException $e) {
+        } catch (Exception $e) {
             common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
             common_debug(var_export($req, true));
             $code = $e->getCode();
             common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
             common_debug(var_export($req, true));
             $code = $e->getCode();
@@ -99,7 +99,7 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
                 $this->verifier
             );
 
                 $this->verifier
             );
 
-            common_log(LOG_WARNIGN, $msg);
+            common_log(LOG_WARNING, $msg);
             $this->clientError(_("Invalid request token or verifier.", 400, 'text'));
 
         } else {
             $this->clientError(_("Invalid request token or verifier.", 400, 'text'));
 
         } else {
index eb1000e25216b6bbe2b65d0db28ab9e568ae0fb0..01cbca18f7eccab1495e1dec43ec00715a6b7bd9 100644 (file)
@@ -177,21 +177,6 @@ class ApiOauthAuthorizeAction extends Action
                 $this->serverError($e->getMessage());
             }
 
                 $this->serverError($e->getMessage());
             }
 
-            // Check to see if there was a previous token associated
-            // with this user/app and kill it. If the user is doing this she
-            // probably doesn't want any old tokens anyway.
-
-            $appUser = Oauth_application_user::getByKeys($user, $this->app);
-
-            if (!empty($appUser)) {
-                $result = $appUser->delete();
-
-                if (!$result) {
-                    common_log_db_error($appUser, 'DELETE', __FILE__);
-                    $this->serverError(_('Database error deleting OAuth application user.'));
-                }
-            }
-
             // associated the authorized req token with the user and the app
 
             $appUser = new Oauth_application_user();
             // associated the authorized req token with the user and the app
 
             $appUser = new Oauth_application_user();
index 4715f7002755695d6afecbbab08ef08c85cfe59b..91dcdd10fc7b1da3350769e70ae15cb130e0da1e 100644 (file)
@@ -150,7 +150,6 @@ require_once INSTALLDIR . '/lib/mediafile.php';
 
 class ApiStatusesUpdateAction extends ApiAuthAction
 {
 
 class ApiStatusesUpdateAction extends ApiAuthAction
 {
-    var $source                = null;
     var $status                = null;
     var $in_reply_to_status_id = null;
     var $lat                   = null;
     var $status                = null;
     var $in_reply_to_status_id = null;
     var $lat                   = null;
index 1fa70662fc88690c294abc3ad59fe319c769241b..72624de84dce61480f5223856a8238628d284dbc 100644 (file)
@@ -22,7 +22,7 @@
  * @category  Settings
  * @package   StatusNet
  * @author    Zach Copley <zach@status.net>
  * @category  Settings
  * @package   StatusNet
  * @author    Zach Copley <zach@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2008-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/
  */
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://status.net/
  */
@@ -50,13 +50,13 @@ require_once INSTALLDIR . '/lib/apioauthstore.php';
 class OauthconnectionssettingsAction extends ConnectSettingsAction
 {
 
 class OauthconnectionssettingsAction extends ConnectSettingsAction
 {
 
-    var $page = null;
-    var $id   = null;
+    var $page        = null;
+    var $oauth_token = null;
 
     function prepare($args)
     {
         parent::prepare($args);
 
     function prepare($args)
     {
         parent::prepare($args);
-        $this->id = (int)$this->arg('id');
+        $this->oauth_token = $this->arg('oauth_token');
         $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
         return true;
     }
         $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
         return true;
     }
@@ -80,7 +80,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
 
     function getInstructions()
     {
 
     function getInstructions()
     {
-        return _('You have allowed the following applications to access your account.');
+        return _('The following connections exist for your account.');
     }
 
     /**
     }
 
     /**
@@ -97,22 +97,26 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
         $offset = ($this->page - 1) * APPS_PER_PAGE;
         $limit  =  APPS_PER_PAGE + 1;
 
         $offset = ($this->page - 1) * APPS_PER_PAGE;
         $limit  =  APPS_PER_PAGE + 1;
 
-        $application = $profile->getApplications($offset, $limit);
+        $connection = $profile->getConnectedApps($offset, $limit);
 
         $cnt = 0;
 
 
         $cnt = 0;
 
-        if (!empty($application)) {
-            $al = new ApplicationList($application, $user, $this, true);
-            $cnt = $al->show();
+        if (!empty($connection)) {
+            $cal = new ConnectedAppsList($connection, $user, $this);
+            $cnt = $cal->show();
         }
 
         if ($cnt == 0) {
             $this->showEmptyListMessage();
         }
 
         }
 
         if ($cnt == 0) {
             $this->showEmptyListMessage();
         }
 
-        $this->pagination($this->page > 1, $cnt > APPS_PER_PAGE,
-                          $this->page, 'connectionssettings',
-                          array('nickname' => $user->nickname));
+        $this->pagination(
+            $this->page > 1,
+            $cnt > APPS_PER_PAGE,
+            $this->page,
+            'connectionssettings',
+            array('nickname' => $user->nickname)
+        );
     }
 
     /**
     }
 
     /**
@@ -138,11 +142,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
         }
 
         if ($this->arg('revoke')) {
         }
 
         if ($this->arg('revoke')) {
-            $this->revokeAccess($this->id);
-
-            // XXX: Show some indicator to the user of what's been done.
-
-            $this->showPage();
+            $this->revokeAccess($this->oauth_token);
         } else {
             $this->clientError(_('Unexpected form submission.'), 401);
             return false;
         } else {
             $this->clientError(_('Unexpected form submission.'), 401);
             return false;
@@ -150,32 +150,27 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
     }
 
     /**
     }
 
     /**
-     * Revoke access to an authorized OAuth application
+     * Revoke an access token
+     *
+     * XXX: Confirm revoke before doing it
      *
      * @param int $appId the ID of the application
      *
      */
 
      *
      * @param int $appId the ID of the application
      *
      */
 
-    function revokeAccess($appId)
+    function revokeAccess($token)
     {
         $cur = common_current_user();
 
     {
         $cur = common_current_user();
 
-        $app = Oauth_application::staticGet('id', $appId);
-
-        if (empty($app)) {
-            $this->clientError(_('No such application.'), 404);
-            return false;
-        }
-
-        // XXX: Transaction here?
-
-        $appUser = Oauth_application_user::getByKeys($cur, $app);
+        $appUser = Oauth_application_user::getByUserAndToken($cur, $token);
 
         if (empty($appUser)) {
             $this->clientError(_('You are not a user of that application.'), 401);
             return false;
         }
 
 
         if (empty($appUser)) {
             $this->clientError(_('You are not a user of that application.'), 401);
             return false;
         }
 
+        $app = Oauth_application::staticGet('id', $appUser->application_id);
+
         $datastore = new ApiStatusNetOAuthDataStore();
         $datastore->revoke_token($appUser->token, 1);
 
         $datastore = new ApiStatusNetOAuthDataStore();
         $datastore->revoke_token($appUser->token, 1);
 
@@ -187,10 +182,25 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
             return false;
         }
 
             return false;
         }
 
-        $msg = 'User %s (id: %d) revoked access to app %s (id: %d)';
-        common_log(LOG_INFO, sprintf($msg, $cur->nickname,
-                                     $cur->id, $app->name, $app->id));
-
+        $msg = 'API OAuth - user %s (id: %d) revoked access token %s for app id %d';
+        common_log(
+            LOG_INFO,
+            sprintf(
+                $msg,
+                $cur->nickname,
+                $cur->id,
+                $appUser->token,
+                $appUser->application_id
+            )
+        );
+
+        $msg = sprintf(
+            _('You have successfully revoked access for %s and the access token starting with %s'),
+             $app->name,
+             substr($appUser->token, 0, 7)
+        );
+
+        $this->showForm($msg, true);
     }
 
     function showEmptyListMessage()
     }
 
     function showEmptyListMessage()
@@ -204,15 +214,20 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction
 
     function showSections()
     {
 
     function showSections()
     {
-       $cur = common_current_user();
-
-       $this->element('h2', null, 'Developers');
-       $this->elementStart('p');
-       $this->raw(_('Developers can edit the registration settings for their applications '));
-       $this->element('a',
-           array('href' => common_local_url('oauthappssettings')),
-               'here.');
-       $this->elementEnd('p');
+        $cur = common_current_user();
+
+        $this->element('h2', null, 'Developers');
+        $this->elementStart('p');
+
+        $devMsg = sprintf(
+            _('Are you a developer? [Register an OAuth client application](%s) to use with this instance of StatusNet.'),
+            common_local_url('oauthappssettings')
+        );
+
+        $output = common_markup_to_html($devMsg);
+
+        $this->raw($output);
+        $this->elementEnd('p');
     }
 
 }
     }
 
 }
index 3d4238d640fc4db3e4055477e157acbef97ee45f..fcf6553ffed5ec0cec039893501cb49fd3fbfbb8 100644 (file)
@@ -13,7 +13,7 @@ class Oauth_application_user extends Memcached_DataObject
     public $profile_id;                      // int(4)  primary_key not_null
     public $application_id;                  // int(4)  primary_key not_null
     public $access_type;                     // tinyint(1)
     public $profile_id;                      // int(4)  primary_key not_null
     public $application_id;                  // int(4)  primary_key not_null
     public $access_type;                     // tinyint(1)
-    public $token;                           // varchar(255)
+    public $token;                           // varchar(255) primary_key not_null
     public $created;                         // datetime   not_null
     public $modified;                        // timestamp   not_null default_CURRENT_TIMESTAMP
 
     public $created;                         // datetime   not_null
     public $modified;                        // timestamp   not_null default_CURRENT_TIMESTAMP
 
@@ -24,20 +24,51 @@ class Oauth_application_user extends Memcached_DataObject
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
 
-    static function getByKeys($user, $app)
+    static function getByUserAndToken($user, $token)
     {
     {
-        if (empty($user) || empty($app)) {
+        if (empty($user) || empty($token)) {
             return null;
         }
 
         $oau = new Oauth_application_user();
 
             return null;
         }
 
         $oau = new Oauth_application_user();
 
-        $oau->profile_id     = $user->id;
-        $oau->application_id = $app->id;
+        $oau->profile_id = $user->id;
+        $oau->token      = $token;
         $oau->limit(1);
 
         $result = $oau->find(true);
 
         return empty($result) ? null : $oau;
     }
         $oau->limit(1);
 
         $result = $oau->find(true);
 
         return empty($result) ? null : $oau;
     }
+
+    function updateKeys(&$orig)
+    {
+        $this->_connect();
+        $parts = array();
+        foreach (array('profile_id', 'application_id', 'token', 'access_type') as $k) {
+            if (strcmp($this->$k, $orig->$k) != 0) {
+                $parts[] = $k . ' = ' . $this->_quote($this->$k);
+            }
+        }
+        if (count($parts) == 0) {
+            # No changes
+            return true;
+        }
+        $toupdate = implode(', ', $parts);
+
+        $table = $this->tableName();
+        if(common_config('db','quote_identifiers')) {
+            $table = '"' . $table . '"';
+        }
+        $qry = 'UPDATE ' . $table . ' SET ' . $toupdate .
+          ' WHERE profile_id = ' . $orig->profile_id
+          . ' AND application_id = ' . $orig->application_id
+          . " AND token = '$orig->token'";
+        $orig->decache();
+        $result = $this->query($qry);
+        if ($result) {
+            $this->encache();
+        }
+        return $result;
+    }
 }
 }
index 12ce5d9b6ce0025062401a844854f18810b3be6d..a32051d07dbc0c05b7bc5788124cc5dbb3b0a141 100644 (file)
@@ -401,10 +401,10 @@ class Profile extends Memcached_DataObject
         return $profile;
     }
 
         return $profile;
     }
 
-    function getApplications($offset = 0, $limit = null)
+    function getConnectedApps($offset = 0, $limit = null)
     {
         $qry =
     {
         $qry =
-          'SELECT a.* ' .
+          'SELECT u.* ' .
           'FROM oauth_application_user u, oauth_application a ' .
           'WHERE u.profile_id = %d ' .
           'AND a.id = u.application_id ' .
           'FROM oauth_application_user u, oauth_application a ' .
           'WHERE u.profile_id = %d ' .
           'AND a.id = u.application_id ' .
@@ -419,11 +419,11 @@ class Profile extends Memcached_DataObject
             }
         }
 
             }
         }
 
-        $application = new Oauth_application();
+        $apps = new Oauth_application_user();
 
 
-        $cnt = $application->query(sprintf($qry, $this->id));
+        $cnt = $apps->query(sprintf($qry, $this->id));
 
 
-        return $application;
+        return $apps;
     }
 
     function subscriptionCount()
     }
 
     function subscriptionCount()
index 3fb8ee208ba17c850abd0df50544028b0e504712..7aa115fecd28e80f60ed9ad0400dca3b3f32c4d0 100644 (file)
@@ -393,13 +393,14 @@ name = U
 profile_id = 129
 application_id = 129
 access_type = 17
 profile_id = 129
 application_id = 129
 access_type = 17
-token = 2
+token = 130
 created = 142
 modified = 384
 
 [oauth_application_user__keys]
 profile_id = K
 application_id = K
 created = 142
 modified = 384
 
 [oauth_application_user__keys]
 profile_id = K
 application_id = K
+token = K
 
 [profile]
 id = 129
 
 [profile]
 id = 129
index 3f95948e1ed5ede5d9cde05511f78fb49f657021..4ae7e56841a720ffffd290b4a02c558e7a2cced4 100644 (file)
@@ -231,10 +231,10 @@ create table oauth_application_user (
     profile_id integer not null comment 'user of the application' references profile (id),
     application_id integer not null comment 'id of the application' references oauth_application (id),
     access_type tinyint default 0 comment 'access type, bit 1 = read, bit 2 = write',
     profile_id integer not null comment 'user of the application' references profile (id),
     application_id integer not null comment 'id of the application' references oauth_application (id),
     access_type tinyint default 0 comment 'access type, bit 1 = read, bit 2 = write',
-    token varchar(255) comment 'request or access token',
+    token varchar(255) not null comment 'request or access token',
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
-    constraint primary key (profile_id, application_id)
+    constraint primary key (profile_id, application_id, token)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 /* These are used by JanRain OpenID library */
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
 /* These are used by JanRain OpenID library */
index a1c698bba969dfd3cee7d6cc7ee7046c03b16bdb..0ebd7aa10560d964240f940b2f178e563eaf435f 100644 (file)
@@ -178,8 +178,10 @@ class ApiAuthAction extends ApiAction
             }
 
             // set the source attr
             }
 
             // set the source attr
+            if ($app->name != 'anonymous') {
+                $this->source = $app->name;
+            }
 
 
-            $this->source = $app->name;
 
             $appUser = Oauth_application_user::staticGet('token', $access_token);
 
 
             $appUser = Oauth_application_user::staticGet('token', $access_token);
 
index 6e0039bdd96a40554e0babec3a2e4bed18724187..e30eea129fb24f0e52d72123c57dfee9945404e8 100644 (file)
@@ -23,16 +23,43 @@ require_once INSTALLDIR . '/lib/oauthstore.php';
 
 class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
 {
 
 class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
 {
-    function lookup_consumer($consumer_key)
+    function lookup_consumer($consumerKey)
     {
     {
-        $con = Consumer::staticGet('consumer_key', $consumer_key);
+        $con = Consumer::staticGet('consumer_key', $consumerKey);
 
         if (!$con) {
 
         if (!$con) {
-            return null;
+
+            // Create an anon consumer and anon application if one
+            // doesn't exist already
+            if ($consumerKey == 'anonymous') {
+                $con = new Consumer();
+                $con->consumer_key    = $consumerKey;
+                $con->consumer_secret = $consumerKey;
+                $result = $con->insert();
+                if (!$result) {
+                    $this->serverError(_("Could not create anonymous consumer."));
+                }
+                $app               = new OAuth_application();
+                $app->consumer_key = $con->consumer_key;
+                $app->name         = 'anonymous';
+
+                // XXX: allow the user to set the access type when
+                // authorizing? Currently we default to r+w for anonymous
+                // OAuth client applications
+                $app->access_type  = 3; // read + write
+                $id = $app->insert();
+                if (!$id) {
+                    $this->serverError(_("Could not create anonymous OAuth application."));
+                }
+            } else {
+                return null;
+            }
         }
 
         }
 
-        return new OAuthConsumer($con->consumer_key,
-                                 $con->consumer_secret);
+        return new OAuthConsumer(
+            $con->consumer_key,
+            $con->consumer_secret
+        );
     }
 
     function getAppByRequestToken($token_key)
     }
 
     function getAppByRequestToken($token_key)
@@ -94,7 +121,7 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
 
         if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized
 
 
         if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized
 
-            common_debug('request token found.', __FILE__);
+            common_debug('request token found.');
 
             // find the associated user of the app
 
 
             // find the associated user of the app
 
@@ -140,6 +167,7 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
                 // update the token from req to access for the user
 
                 $orig = clone($appUser);
                 // update the token from req to access for the user
 
                 $orig = clone($appUser);
+
                 $appUser->token = $at->tok;
 
                 // It's at this point that we change the access type
                 $appUser->token = $at->tok;
 
                 // It's at this point that we change the access type
@@ -150,11 +178,10 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
 
                 $appUser->access_type = $app->access_type;
 
 
                 $appUser->access_type = $app->access_type;
 
-                $result = $appUser->update($orig);
+                $result = $appUser->updateKeys($orig);
 
 
-                if (empty($result)) {
-                    common_debug('couldn\'t update OAuth app user.');
-                    return null;
+                if (!$result) {
+                    throw new Exception('Couldn\'t update OAuth app user.');
                 }
 
                 // Okay, good
                 }
 
                 // Okay, good
@@ -179,9 +206,9 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
      * @return void
      */
     public function revoke_token($token_key, $type = 0) {
      * @return void
      */
     public function revoke_token($token_key, $type = 0) {
-        $rt = new Token();
-        $rt->tok = $token_key;
-        $rt->type = $type;
+        $rt        = new Token();
+        $rt->tok   = $token_key;
+        $rt->type  = $type;
         $rt->state = 0;
 
         if (!$rt->find(true)) {
         $rt->state = 0;
 
         if (!$rt->find(true)) {
index 8b6e3a8addcc3ebf82ccb9c0e7e608795a1b3a75..6801fb6cf1e428eaf90b72ed17e16392b650ec02 100644 (file)
@@ -22,7 +22,7 @@
  * @category  Application
  * @package   StatusNet
  * @author    Zach Copley <zach@status.net>
  * @category  Application
  * @package   StatusNet
  * @author    Zach Copley <zach@status.net>
- * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2008-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/
  */
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://status.net/
  */
@@ -55,14 +55,13 @@ class ApplicationList extends Widget
     /** Action object using us. */
     var $action = null;
 
     /** Action object using us. */
     var $action = null;
 
-    function __construct($application, $owner=null, $action=null, $connections = false)
+    function __construct($application, $owner=null, $action=null)
     {
         parent::__construct($action);
 
         $this->application = $application;
         $this->owner       = $owner;
         $this->action      = $action;
     {
         parent::__construct($action);
 
         $this->application = $application;
         $this->owner       = $owner;
         $this->action      = $action;
-        $this->connections = $connections;
     }
 
     function show()
     }
 
     function show()
@@ -88,24 +87,34 @@ class ApplicationList extends Widget
     {
         $user = common_current_user();
 
     {
         $user = common_current_user();
 
-        $this->out->elementStart('li', array('class' => 'application',
-                                             'id' => 'oauthclient-' . $this->application->id));
+        $this->out->elementStart(
+            'li',
+            array(
+                'class' => 'application',
+                'id'    => 'oauthclient-' . $this->application->id
+            )
+        );
 
         $this->out->elementStart('span', 'vcard author');
 
         $this->out->elementStart('span', 'vcard author');
-        if (!$this->connections) {
-            $this->out->elementStart('a',
-                                     array('href' => common_local_url('showapplication',
-                                                                      array('id' => $this->application->id)),
-                                                                      'class' => 'url'));
-
-        } else {
-            $this->out->elementStart('a', array('href' =>  $this->application->source_url,
-                                                'class' => 'url'));
-        }
+
+        $this->out->elementStart(
+            'a',
+            array(
+                'href' => common_local_url(
+                    'showapplication',
+                    array('id' => $this->application->id)),
+                    'class' => 'url'
+            )
+        );
 
         if (!empty($this->application->icon)) {
 
         if (!empty($this->application->icon)) {
-            $this->out->element('img', array('src' => $this->application->icon,
-                                             'class' => 'photo avatar'));
+            $this->out->element(
+                'img',
+                array(
+                    'src' => $this->application->icon,
+                    'class' => 'photo avatar'
+                )
+            );
         }
 
         $this->out->element('span', 'fn', $this->application->name);
         }
 
         $this->out->element('span', 'fn', $this->application->name);
@@ -114,51 +123,58 @@ class ApplicationList extends Widget
 
         $this->out->raw(' by ');
 
 
         $this->out->raw(' by ');
 
-        $this->out->element('a', array('href' => $this->application->homepage,
-                                       'class' => 'url'),
-                                 $this->application->organization);
+        $this->out->element(
+            'a',
+            array(
+                'href' => $this->application->homepage,
+                'class' => 'url'
+            ),
+            $this->application->organization
+        );
 
         $this->out->element('p', 'note', $this->application->description);
         $this->out->elementEnd('li');
 
 
         $this->out->element('p', 'note', $this->application->description);
         $this->out->elementEnd('li');
 
-        if ($this->connections) {
-            $appUser = Oauth_application_user::getByKeys($this->owner, $this->application);
+    }
 
 
-            if (empty($appUser)) {
-                common_debug("empty appUser!");
-            }
+    /* Override this in subclasses. */
+    function showOwnerControls()
+    {
+        return;
+    }
 
 
-            $this->out->elementStart('li');
-
-            // TRANS: Application access type
-            $readWriteText = _('read-write');
-            // TRANS: Application access type
-            $readOnlyText = _('read-only');
-
-            $access = ($this->application->access_type & Oauth_application::$writeAccess)
-              ? $readWriteText : $readOnlyText;
-            $modifiedDate = common_date_string($appUser->modified);
-            // TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only")
-            $txt = sprintf(_('Approved %1$s - "%2$s" access.'),$modifiedDate,$access);
-
-            $this->out->raw($txt);
-            $this->out->elementEnd('li');
-
-            $this->out->elementStart('li', 'entity_revoke');
-            $this->out->elementStart('form', array('id' => 'form_revoke_app',
-                                                   'class' => 'form_revoke_app',
-                                                   'method' => 'POST',
-                                                   'action' =>
-                                                   common_local_url('oauthconnectionssettings')));
-            $this->out->elementStart('fieldset');
-            $this->out->hidden('id', $this->application->id);
-            $this->out->hidden('token', common_session_token());
-            // TRANS: Button label
-            $this->out->submit('revoke', _m('BUTTON','Revoke'));
-            $this->out->elementEnd('fieldset');
-            $this->out->elementEnd('form');
-            $this->out->elementEnd('li');
-        }
+}
+
+
+/**
+ * Widget to show a list of connected OAuth clients
+ *
+ * @category Application
+ * @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 ConnectedAppsList extends Widget
+{
+    /** Current connected application query */
+    var $connection = null;
+
+    /** Owner of this list */
+    var $owner = null;
+
+    /** Action object using us. */
+    var $action = null;
+
+    function __construct($connection, $owner=null, $action=null)
+    {
+        parent::__construct($action);
+
+        common_debug("ConnectedAppsList constructor");
+
+        $this->connection = $connection;
+        $this->owner       = $owner;
+        $this->action      = $action;
     }
 
     /* Override this in subclasses. */
     }
 
     /* Override this in subclasses. */
@@ -166,4 +182,124 @@ class ApplicationList extends Widget
     {
         return;
     }
     {
         return;
     }
+
+    function show()
+    {
+        $this->out->elementStart('ul', 'applications');
+
+        $cnt = 0;
+
+        while ($this->connection->fetch()) {
+            $cnt++;
+            if($cnt > APPS_PER_PAGE) {
+                break;
+            }
+            $this->showConnection();
+        }
+
+        $this->out->elementEnd('ul');
+
+        return $cnt;
+    }
+
+    function showConnection()
+    {
+        $app = Oauth_application::staticGet('id', $this->connection->application_id);
+
+        $this->out->elementStart(
+            'li',
+            array(
+                'class' => 'application',
+                'id'    => 'oauthclient-' . $app->id
+            )
+        );
+
+        $this->out->elementStart('span', 'vcard author');
+
+        $this->out->elementStart(
+            'a',
+            array(
+                'href' => $app->source_url,
+                'class' => 'url'
+            )
+        );
+
+        if (!empty($app->icon)) {
+            $this->out->element(
+                'img',
+                array(
+                    'src' => $app->icon,
+                    'class' => 'photo avatar'
+                )
+            );
+        }
+        if ($app->name != 'anonymous') {
+            $this->out->element('span', 'fn', $app->name);
+        }
+        $this->out->elementEnd('a');
+
+        if ($app->name == 'anonymous') {
+            $this->out->element('span', 'fn', "Unknown application");
+        }
+
+        $this->out->elementEnd('span');
+
+        if ($app->name != 'anonymous') {
+
+            $this->out->raw(_(' by '));
+
+            $this->out->element(
+                'a',
+                array(
+                    'href' => $app->homepage,
+                    'class' => 'url'
+                ),
+                $app->organization
+            );
+        }
+
+        // TRANS: Application access type
+        $readWriteText = _('read-write');
+        // TRANS: Application access type
+        $readOnlyText = _('read-only');
+
+        $access = ($this->connection->access_type & Oauth_application::$writeAccess)
+            ? $readWriteText : $readOnlyText;
+        $modifiedDate = common_date_string($this->connection->modified);
+        // TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only")
+        $txt = sprintf(_('Approved %1$s - "%2$s" access.'), $modifiedDate, $access);
+
+        $this->out->raw(" - $txt");
+        if (!empty($app->description)) {
+            $this->out->element(
+                'p', array('class' => 'application_description'),
+                $app->description
+            );
+        }
+        $this->out->element(
+            'p', array(
+            'class' => 'access_token'),
+            _('Access token starting with: ') . substr($this->connection->token, 0, 7)
+        );
+
+        $this->out->elementStart(
+            'form',
+            array(
+                'id' => 'form_revoke_app',
+                'class' => 'form_revoke_app',
+                'method' => 'POST',
+                'action' => common_local_url('oauthconnectionssettings')
+            )
+        );
+        $this->out->elementStart('fieldset');
+        $this->out->hidden('oauth_token', $this->connection->token);
+        $this->out->hidden('token', common_session_token());
+        // TRANS: Button label
+        $this->out->submit('revoke', _('Revoke'));
+        $this->out->elementEnd('fieldset');
+        $this->out->elementEnd('form');
+
+        $this->out->elementEnd('li');
+
+    }
 }
 }