]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'testing' of git@gitorious.org:statusnet/mainline into testing
authorEvan Prodromou <evan@status.net>
Sat, 6 Feb 2010 10:24:12 +0000 (11:24 +0100)
committerEvan Prodromou <evan@status.net>
Sat, 6 Feb 2010 10:24:12 +0000 (11:24 +0100)
30 files changed:
actions/apiaccountverifycredentials.php
actions/apigroupjoin.php
actions/apigroupleave.php
actions/favorited.php
actions/publictagcloud.php
actions/register.php
actions/userauthorization.php
classes/Inbox.php
classes/Memcached_DataObject.php
classes/User.php
classes/statusnet.ini
db/statusnet.sql
lib/cache.php
lib/default.php
lib/grouptagcloudsection.php
lib/oauthstore.php
lib/personaltagcloudsection.php
lib/popularnoticesection.php
plugins/MemcachePlugin.php
plugins/MobileProfile/MobileProfilePlugin.php
plugins/MobileProfile/mp-screen.css
plugins/TwitterBridge/daemons/twitterstatusfetcher.php
plugins/TwitterBridge/twitter.php
plugins/TwitterBridge/twitterauthorization.php
scripts/updateprofileurl.php [new file with mode: 0644]
theme/base/images/icons/icons-01.gif
theme/base/images/icons/twotone/green/against.gif [new file with mode: 0644]
theme/base/images/icons/twotone/green/checkmark.gif [new file with mode: 0644]
theme/default/css/display.css
theme/identica/css/display.css

index 1095d5162634f312772ce78e4e533dc2a2eaf00a..ea61a3205945c12f3c10afc62ecbef0e31c3e08b 100644 (file)
@@ -66,18 +66,21 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction
     {
         parent::handle($args);
 
-        switch ($this->format) {
-        case 'xml':
-        case 'json':
-            $args['id'] = $this->auth_user->id;
-            $action_obj = new ApiUserShowAction();
-            if ($action_obj->prepare($args)) {
-                $action_obj->handle($args);
-            }
-            break;
-        default:
-            header('Content-Type: text/html; charset=utf-8');
-            print 'Authorized';
+        if (!in_array($this->format, array('xml', 'json'))) {
+            $this->clientError(_('API method not found.'), $code = 404);
+            return;
+        }
+
+        $twitter_user = $this->twitterUserArray($this->auth_user->getProfile(), true);
+
+        if ($this->format == 'xml') {
+            $this->initDocument('xml');
+            $this->showTwitterXmlUser($twitter_user);
+            $this->endDocument('xml');
+        } elseif ($this->format == 'json') {
+            $this->initDocument('json');
+            $this->showJsonObjects($twitter_user);
+            $this->endDocument('json');
         }
 
     }
@@ -86,14 +89,14 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction
      * Is this action read only?
      *
      * @param array $args other arguments
-     * 
+     *
      * @return boolean true
      *
      **/
-    
+
     function isReadOnly($args)
     {
         return true;
     }
-    
+
 }
index 3309d63e7b8b921bdd91cdb2d952917e8b291b88..374cf83df068c77506d025d7c66e3506fff69cf3 100644 (file)
@@ -145,7 +145,7 @@ class ApiGroupJoinAction extends ApiAuthAction
 
         switch($this->format) {
         case 'xml':
-            $this->show_single_xml_group($this->group);
+            $this->showSingleXmlGroup($this->group);
             break;
         case 'json':
             $this->showSingleJsonGroup($this->group);
index 6f8d40527b7ecdae845c504f3fddf6871f5f1c68..9848ece0530cb133937e993e49f98e6eccc20e9d 100644 (file)
@@ -131,7 +131,7 @@ class ApiGroupLeaveAction extends ApiAuthAction
 
         switch($this->format) {
         case 'xml':
-            $this->show_single_xml_group($this->group);
+            $this->showSingleXmlGroup($this->group);
             break;
         case 'json':
             $this->showSingleJsonGroup($this->group);
index 9ffa5b84454093fde32b98460751b60bbab32745..d8980440d17529a73db33990f72fed90f740b247 100644 (file)
@@ -186,10 +186,13 @@ class FavoritedAction extends Action
     function showContent()
     {
         $weightexpr = common_sql_weight('fave.modified', common_config('popular', 'dropoff'));
+        $cutoff = sprintf("fave.modified > '%s'",
+                          common_sql_date(time() - common_config('popular', 'cutoff')));
 
         $qry = 'SELECT notice.*, '.
           $weightexpr . ' as weight ' .
           'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
+          "WHERE $cutoff " .
           'GROUP BY id,profile_id,uri,content,rendered,url,created,notice.modified,reply_to,is_local,source,notice.conversation ' .
           'ORDER BY weight DESC';
 
index 9e4478dbb13165e1e908e9dbc02114f137e496ee..9993b2d3fde0b3473a84dbdb8e0c17151d0902cb 100644 (file)
@@ -106,7 +106,10 @@ class PublictagcloudAction extends Action
         #Add the aggregated columns...
         $tags->selectAdd('max(notice_id) as last_notice_id');
         $calc = common_sql_weight('created', common_config('tag', 'dropoff'));
+        $cutoff = sprintf("notice_tag.created > '%s'",
+                          common_sql_date(time() - common_config('tag', 'cutoff')));
         $tags->selectAdd($calc . ' as weight');
+        $tags->addWhere($cutoff);
         $tags->groupBy('tag');
         $tags->orderBy('weight DESC');
 
index 6981373467636ba77414e82e8c0c7afb7aaf46a6..ccab76cf0193af51e161624f054e8b05681292c6 100644 (file)
@@ -280,7 +280,7 @@ class RegisterAction extends Action
     function nicknameExists($nickname)
     {
         $user = User::staticGet('nickname', $nickname);
-        return ($user !== false);
+        return is_object($user);
     }
 
     /**
@@ -300,7 +300,7 @@ class RegisterAction extends Action
             return false;
         }
         $user = User::staticGet('email', $email);
-        return ($user !== false);
+        return is_object($user);
     }
 
     // overrrided to add entry-title class
index 4321f1302e0ca6735c8307128fe083a00e616946..7f71c60dbe64441adc9515b1ab106362089ce897 100644 (file)
@@ -127,10 +127,10 @@ class UserauthorizationAction extends Action
         $location = $params->getLocation();
         $avatar   = $params->getAvatarURL();
 
-        $this->elementStart('div', array('class' => 'profile'));
         $this->elementStart('div', 'entity_profile vcard');
-        $this->elementStart('a', array('href' => $profile,
-                                            'class' => 'url'));
+        $this->elementStart('dl', 'entity_depiction');
+        $this->element('dt', null, _('Photo'));
+        $this->elementStart('dd');
         if ($avatar) {
             $this->element('img', array('src' => $avatar,
                                         'class' => 'photo avatar',
@@ -138,11 +138,19 @@ class UserauthorizationAction extends Action
                                         'height' => AVATAR_PROFILE_SIZE,
                                         'alt' => $nickname));
         }
+        $this->elementEnd('dd');
+        $this->elementEnd('dl');
+
+        $this->elementStart('dl', 'entity_nickname');
+        $this->element('dt', null, _('Nickname'));
+        $this->elementStart('dd');
         $hasFN = ($fullname !== '') ? 'nickname' : 'fn nickname';
-        $this->elementStart('span', $hasFN);
+        $this->elementStart('a', array('href' => $profile,
+                                       'class' => 'url '.$hasFN));
         $this->raw($nickname);
-        $this->elementEnd('span');
         $this->elementEnd('a');
+        $this->elementEnd('dd');
+        $this->elementEnd('dl');
 
         if (!is_null($fullname)) {
             $this->elementStart('dl', 'entity_fn');
@@ -214,7 +222,6 @@ class UserauthorizationAction extends Action
         $this->elementEnd('li');
         $this->elementEnd('ul');
         $this->elementEnd('div');
-        $this->elementEnd('div');
     }
 
     function sendAuthorization()
@@ -350,4 +357,4 @@ class UserauthorizationAction extends Action
             }
         }
     }
-}
\ No newline at end of file
+}
index 26b27d2b58f71f47c2e6aa816a6cf668ab46cc3e..be62611a16504416675d474e6d1920d6d6080273 100644 (file)
@@ -32,6 +32,7 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
 class Inbox extends Memcached_DataObject
 {
     const BOXCAR = 128;
+    const MAX_NOTICES = 1024;
 
     ###START_AUTOCODE
     /* the code below is auto generated do not remove the above tag */
@@ -81,7 +82,7 @@ class Inbox extends Memcached_DataObject
         $ni->selectAdd();
         $ni->selectAdd('notice_id');
         $ni->orderBy('notice_id DESC');
-        $ni->limit(0, 1024);
+        $ni->limit(0, self::MAX_NOTICES);
 
         if ($ni->find()) {
             while($ni->fetch()) {
@@ -115,9 +116,11 @@ class Inbox extends Memcached_DataObject
 
         $result = $inbox->query(sprintf('UPDATE inbox '.
                                         'set notice_ids = concat(cast(0x%08x as binary(4)), '.
-                                        'substr(notice_ids, 1, 4092)) '.
+                                        'substr(notice_ids, 1, %d)) '.
                                         'WHERE user_id = %d',
-                                        $notice_id, $user_id));
+                                        $notice_id,
+                                        4 * (self::MAX_NOTICES - 1),
+                                        $user_id));
 
         if ($result) {
             self::blow('inbox:user_id:%d', $user_id);
@@ -173,4 +176,57 @@ class Inbox extends Memcached_DataObject
 
         return $ids;
     }
+
+    /**
+     * Wrapper for Inbox::stream() and Notice::getStreamByIds() returning
+     * additional items up to the limit if we were short due to deleted
+     * notices still being listed in the inbox.
+     *
+     * The fast path (when no items are deleted) should be just as fast; the
+     * offset parameter is applied *before* lookups for maximum efficiency.
+     *
+     * This means offset-based paging may show duplicates, but similar behavior
+     * already exists when new notices are posted between page views, so we
+     * think people will be ok with this until id-based paging is introduced
+     * to the user interface.
+     *
+     * @param int $user_id
+     * @param int $offset skip past the most recent N notices (after since_id checks)
+     * @param int $limit
+     * @param mixed $since_id return only notices after but not including this id
+     * @param mixed $max_id return only notices up to and including this id
+     * @param mixed $since obsolete/ignored
+     * @param mixed $own ignored?
+     * @return array of Notice objects
+     *
+     * @todo consider repacking the inbox when this happens?
+     */
+    function streamNotices($user_id, $offset, $limit, $since_id, $max_id, $since, $own=false)
+    {
+        $ids = self::stream($user_id, $offset, self::MAX_NOTICES, $since_id, $max_id, $since, $own);
+
+        // Do a bulk lookup for the first $limit items
+        // Fast path when nothing's deleted.
+        $firstChunk = array_slice($ids, 0, $limit);
+        $notices = Notice::getStreamByIds($firstChunk);
+
+        $wanted = count($firstChunk); // raw entry count in the inbox up to our $limit
+        if ($notices->N >= $wanted) {
+            return $notices;
+        }
+
+        // There were deleted notices, we'll need to look for more.
+        assert($notices instanceof ArrayWrapper);
+        $items = $notices->_items;
+        $remainder = array_slice($ids, $limit);
+
+        while (count($items) < $wanted && count($remainder) > 0) {
+            $notice = Notice::staticGet(array_shift($remainder));
+            if ($notice) {
+                $items[] = $notice;
+            } else {
+            }
+        }
+        return new ArrayWrapper($items);
+    }
 }
index ab65c30ce28579a8684b4e9fa8d9cc8124d2b833..dfd06b57e50366f4fb60b86578c2f3ddaecb510d 100644 (file)
@@ -363,7 +363,7 @@ class Memcached_DataObject extends DB_DataObject
             $cached[] = clone($inst);
         }
         $inst->free();
-        $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry);
+        $c->set($ckey, $cached, Cache::COMPRESSED, $expiry);
         return new ArrayWrapper($cached);
     }
 
index 0ab816b57ee301f730f4c31f18a53e9572c14fe5..72c3f39e947e507d191bae34a2a9ed1b674e02fa 100644 (file)
@@ -502,28 +502,22 @@ class User extends Memcached_DataObject
 
     function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
     {
-        $ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false);
-        return Notice::getStreamByIds($ids);
+        return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, $since, false);
     }
 
     function noticeInbox($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
     {
-        $ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true);
-        return Notice::getStreamByIds($ids);
+        return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, $since, true);
     }
 
     function friendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
     {
-        $ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, false);
-
-        return Notice::getStreamByIds($ids);
+        return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, $since, false);
     }
 
     function ownFriendsTimeline($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null)
     {
-        $ids = Inbox::stream($this->id, $offset, $limit, $since_id, $before_id, $since, true);
-
-        return Notice::getStreamByIds($ids);
+        return Inbox::streamNotices($this->id, $offset, $limit, $since_id, $before_id, $since, true);
     }
 
     function blowFavesCache()
index a535159e808b26919b426c3dd95682ae012156bd..5f8da7cf51bd5bcf80131eb8b74d362bd38fc2a3 100644 (file)
@@ -353,7 +353,7 @@ notice_id = K
 id = 129
 owner = 129
 consumer_key = 130
-name = 2
+name = 130
 description = 2
 icon = 130
 source_url = 2
index 8946f4d7e2e47c3382fd5be211c0ecb2b3a54772..343464801662d0fd7ff37828632952214f873fb5 100644 (file)
@@ -214,7 +214,7 @@ create table oauth_application (
     id integer auto_increment primary key comment 'unique identifier',
     owner integer not null comment 'owner of the application' references profile (id),
     consumer_key varchar(255) not null comment 'application consumer key' references consumer (consumer_key),
-    name varchar(255) unique key comment 'name of the application',
+    name varchar(255) not null unique key comment 'name of the application',
     description varchar(255) comment 'description of the application',
     icon varchar(255) not null comment 'application icon',
     source_url varchar(255) comment 'application homepage - used for source link',
index 635c96ad4c7899640ac0f8c3ed7e149daad9ce5a..df6fc364931fb2f7b0734523e76724b8c8b675d5 100644 (file)
@@ -47,6 +47,8 @@ class Cache
     var $_items   = array();
     static $_inst = null;
 
+    const COMPRESSED = 1;
+
     /**
      * Singleton constructor
      *
@@ -133,7 +135,7 @@ class Cache
      *
      * @param string  $key    The key to use for lookups
      * @param string  $value  The value to store
-     * @param integer $flag   Flags to use, mostly ignored
+     * @param integer $flag   Flags to use, may include Cache::COMPRESSED
      * @param integer $expiry Expiry value, mostly ignored
      *
      * @return boolean success flag
index 2bedc4bf08489076675b77b83085b71b04a25d33..485a08ba44e427a4ffc4bb341ed955b0dc204775 100644 (file)
@@ -144,9 +144,11 @@ $default =
         'invite' =>
         array('enabled' => true),
         'tag' =>
-        array('dropoff' => 864000.0),
+        array('dropoff' => 864000.0,   # controls weighting based on age
+              'cutoff' => 86400 * 90), # only look at notices posted in last 90 days
         'popular' =>
-        array('dropoff' => 864000.0),
+        array('dropoff' => 864000.0,   # controls weighting based on age
+              'cutoff' => 86400 * 90), # only look at notices favorited in last 90 days
         'daemon' =>
         array('piddir' => '/var/run',
               'user' => false,
index 14ceda0850b09b6918c4da00517370f6b31bad42..f1106cc7bf9caeb3151595e5e2d5b022ea8427c5 100644 (file)
@@ -59,6 +59,7 @@ class GroupTagCloudSection extends TagCloudSection
     function getTags()
     {
         $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff'));
+        // @fixme should we use the cutoff too? Doesn't help with indexing per-group.
 
         $names = $this->group->getAliases();
 
index b30fb49d57e2f16b0c737df00d0354492dbd4aa9..eabe37f9fa4a944d76f99c91c9a57de69c6c0f23 100644 (file)
@@ -65,7 +65,7 @@ class StatusNetOAuthDataStore extends OAuthDataStore
     {
         $n = new Nonce();
         $n->consumer_key = $consumer->key;
-        $n->ts = $timestamp;
+        $n->ts = common_sql_date($timestamp);
         $n->nonce = $nonce;
         if ($n->find(true)) {
             return true;
@@ -362,7 +362,6 @@ class StatusNetOAuthDataStore extends OAuthDataStore
                                   array('is_local' => Notice::REMOTE_OMB,
                                         'uri' => $omb_notice->getIdentifierURI()));
 
-
     }
 
     /**
index 091425f926d296f8959322e63c2a3ac770bae41d..5ea3f188dbf61b2385ad85fde65d15831a381154 100644 (file)
@@ -59,6 +59,7 @@ class PersonalTagCloudSection extends TagCloudSection
     function getTags()
     {
         $weightexpr = common_sql_weight('notice_tag.created', common_config('tag', 'dropoff'));
+        // @fixme should we use the cutoff too? Doesn't help with indexing per-user.
 
         $qry = 'SELECT notice_tag.tag, '.
           $weightexpr . ' as weight ' .
index fbf9a60ab8112395156ffa26b9a5021293f816b6..296ddbbb5034b273e5486d25484cdd191142f0d4 100644 (file)
@@ -59,12 +59,15 @@ class PopularNoticeSection extends NoticeSection
             }
         }
         $weightexpr = common_sql_weight('fave.modified', common_config('popular', 'dropoff'));
+        $cutoff = sprintf("fave.modified > '%s'",
+                          common_sql_date(time() - common_config('popular', 'cutoff')));
         $qry = "SELECT notice.*, $weightexpr as weight ";
         if(isset($tag)) {
             $qry .= 'FROM notice_tag, notice JOIN fave ON notice.id = fave.notice_id ' .
-                    "WHERE notice.id = notice_tag.notice_id and '$tag' = notice_tag.tag";
+                    "WHERE $cutoff and notice.id = notice_tag.notice_id and '$tag' = notice_tag.tag";
         } else {
-            $qry .= 'FROM notice JOIN fave ON notice.id = fave.notice_id';
+            $qry .= 'FROM notice JOIN fave ON notice.id = fave.notice_id ' .
+                    "WHERE $cutoff";
         }
         $qry .= ' GROUP BY notice.id,notice.profile_id,notice.content,notice.uri,' .
                 'notice.rendered,notice.url,notice.created,notice.modified,' .
index 2bc4b892bd6a287c9750c73060f7522f68db5bf5..c5e74fb4162a38efb35a2b37354caf63d32b4287 100644 (file)
@@ -102,7 +102,7 @@ class MemcachePlugin extends Plugin
      *
      * @param string  &$key     in; Key to use for lookups
      * @param mixed   &$value   in; Value to associate
-     * @param integer &$flag    in; Flag (passed through to Memcache)
+     * @param integer &$flag    in; Flag empty or Cache::COMPRESSED
      * @param integer &$expiry  in; Expiry (passed through to Memcache)
      * @param boolean &$success out; Whether the set was successful
      *
@@ -115,7 +115,7 @@ class MemcachePlugin extends Plugin
         if ($expiry === null) {
             $expiry = $this->defaultExpiry;
         }
-        $success = $this->_conn->set($key, $value, $flag, $expiry);
+        $success = $this->_conn->set($key, $value, $this->flag(intval($flag)), $expiry);
         Event::handle('EndCacheSet', array($key, $value, $flag,
                                            $expiry));
         return false;
@@ -197,6 +197,20 @@ class MemcachePlugin extends Plugin
         }
     }
 
+    /**
+     * Translate general flags to Memcached-specific flags
+     * @param int $flag
+     * @return int
+     */
+    protected function flag($flag)
+    {
+        $out = 0;
+        if ($flag & Cache::COMPRESSED == Cache::COMPRESSED) {
+            $out |= MEMCACHE_COMPRESSED;
+        }
+        return $out;
+    }
+
     function onPluginVersion(&$versions)
     {
         $versions[] = array('name' => 'Memcache',
index 5c913836dccd7db6f6375e88bf24626ee596c8d2..cd2531fa727a8f3b7d4d2fd41ed645a8f3d4dc7d 100644 (file)
@@ -240,6 +240,8 @@ class MobileProfilePlugin extends WAP20Plugin
             return true;
         }
 
+        $action->cssLink('css/display.css');
+
         if (file_exists(Theme::file('css/mp-screen.css'))) {
             $action->cssLink('css/mp-screen.css', null, 'screen');
         } else {
@@ -256,6 +258,14 @@ class MobileProfilePlugin extends WAP20Plugin
     }
 
 
+    function onStartShowUAStyles($action) {
+        if (!$this->serveMobile) {
+            return true;
+        }
+
+        return false;
+    }
+
     function onStartShowHeader($action)
     {
         if (!$this->serveMobile) {
index 04fa5fb0021054e871bfd81673b2e15dd0a580eb..0fc801612be9d4848bc4318057c5ea29741d6fef 100644 (file)
@@ -1,15 +1,12 @@
 /** theme: mobile profile screen
  *
  * @package   StatusNet
- * @author Sarven Capadisli <csarven@status.net>
+ * @author    Sarven Capadisli <csarven@status.net>
  * @copyright 2009 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/
  */
 
-@import url(../../theme/base/css/display.css);
-@import url(../../theme/identica/css/display.css);
-
 #wrap {
 min-width:0;
 max-width:100%;
index 36732ce46a528f4defba455cf11aa6cd753ed544..bff657eb68c023cab98d805b5b30eb3fe87b4ba7 100755 (executable)
@@ -2,7 +2,7 @@
 <?php
 /**
  * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
+ * Copyright (C) 2008-2010, StatusNet, Inc.
  *
  * 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
@@ -262,7 +262,7 @@ class TwitterStatusFetcher extends ParallelizingDaemon
             $notice->is_local   = Notice::GATEWAY;
 
             if (Event::handle('StartNoticeSave', array(&$notice))) {
-                $id = $notice->insert();
+                $notice->insert();
                 Event::handle('EndNoticeSave', array($notice));
             }
 
@@ -270,17 +270,41 @@ class TwitterStatusFetcher extends ParallelizingDaemon
 
         Inbox::insertNotice($flink->user_id, $notice->id);
 
-        $notice->blowCaches();
+        $notice->blowOnInsert();
 
         return $notice;
     }
 
+    /**
+     * Look up a Profile by profileurl field.  Profile::staticGet() was
+     * not working consistently.
+     *
+     * @param string $url the profile url
+     *
+     * @return mixed the first profile with that url, or null
+     */
+
+    function getProfileByUrl($nickname, $profileurl)
+    {
+        $profile = new Profile();
+        $profile->nickname = $nickname;
+        $profile->profileurl = $profileurl;
+        $profile->limit(1);
+
+        if ($profile->find()) {
+            $profile->fetch();
+            return $profile;
+        }
+
+        return null;
+    }
+
     function ensureProfile($user)
     {
         // check to see if there's already a profile for this user
 
         $profileurl = 'http://twitter.com/' . $user->screen_name;
-        $profile = Profile::staticGet('profileurl', $profileurl);
+        $profile = $this->getProfileByUrl($user->screen_name, $profileurl);
 
         if (!empty($profile)) {
             common_debug($this->name() .
@@ -292,6 +316,7 @@ class TwitterStatusFetcher extends ParallelizingDaemon
             return $profile->id;
 
         } else {
+
             common_debug($this->name() . ' - Adding profile and remote profile ' .
                          "for Twitter user: $profileurl.");
 
@@ -306,7 +331,11 @@ class TwitterStatusFetcher extends ParallelizingDaemon
             $profile->profileurl = $profileurl;
             $profile->created = common_sql_now();
 
-            $id = $profile->insert();
+            try {
+                $id = $profile->insert();
+            } catch(Exception $e) {
+                common_log(LOG_WARNING, $this->name . ' Couldn\'t insert profile - ' . $e->getMessage());
+            }
 
             if (empty($id)) {
                 common_log_db_error($profile, 'INSERT', __FILE__);
@@ -326,7 +355,11 @@ class TwitterStatusFetcher extends ParallelizingDaemon
                 $remote_pro->uri = $profileurl;
                 $remote_pro->created = common_sql_now();
 
-                $rid = $remote_pro->insert();
+                try {
+                    $rid = $remote_pro->insert();
+                } catch (Exception $e) {
+                    common_log(LOG_WARNING, $this->name() . ' Couldn\'t save remote profile - ' . $e->getMessage());
+                }
 
                 if (empty($rid)) {
                     common_log_db_error($profile, 'INSERT', __FILE__);
@@ -446,7 +479,7 @@ class TwitterStatusFetcher extends ParallelizingDaemon
             if ($this->fetchAvatar($url, $filename)) {
                 $this->newAvatar($id, $size, $mediatype, $filename);
             } else {
-                common_log(LOG_WARNING, $this->id() .
+                common_log(LOG_WARNING, $id() .
                            " - Problem fetching Avatar: $url");
             }
         }
@@ -507,7 +540,11 @@ class TwitterStatusFetcher extends ParallelizingDaemon
 
         $avatar->created = common_sql_now();
 
-        $id = $avatar->insert();
+        try {
+            $id = $avatar->insert();
+        } catch (Exception $e) {
+            common_log(LOG_WARNING, $this->name() . ' Couldn\'t insert avatar - ' . $e->getMessage());
+        }
 
         if (empty($id)) {
             common_log_db_error($avatar, 'INSERT', __FILE__);
index 33dfb788bf5d3d27d11b447bbc8644cdf51660a7..de30d9ebf1d8999fcc7a23967e3e091ca3aaded6 100644 (file)
@@ -26,38 +26,6 @@ define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1
 require_once INSTALLDIR . '/plugins/TwitterBridge/twitterbasicauthclient.php';
 require_once INSTALLDIR . '/plugins/TwitterBridge/twitteroauthclient.php';
 
-function updateTwitter_user($twitter_id, $screen_name)
-{
-    $uri = 'http://twitter.com/' . $screen_name;
-    $fuser = new Foreign_user();
-
-    $fuser->query('BEGIN');
-
-    // Dropping down to SQL because regular DB_DataObject udpate stuff doesn't seem
-    // to work so good with tables that have multiple column primary keys
-
-    // Any time we update the uri for a forein user we have to make sure there
-    // are no dupe entries first -- unique constraint on the uri column
-
-    $qry = 'UPDATE foreign_user set uri = \'\' WHERE uri = ';
-    $qry .= '\'' . $uri . '\'' . ' AND service = ' . TWITTER_SERVICE;
-
-    $fuser->query($qry);
-
-    // Update the user
-
-    $qry = 'UPDATE foreign_user SET nickname = ';
-    $qry .= '\'' . $screen_name . '\'' . ', uri = \'' . $uri . '\' ';
-    $qry .= 'WHERE id = ' . $twitter_id . ' AND service = ' . TWITTER_SERVICE;
-
-    $fuser->query('COMMIT');
-
-    $fuser->free();
-    unset($fuser);
-
-    return true;
-}
-
 function add_twitter_user($twitter_id, $screen_name)
 {
 
@@ -105,7 +73,6 @@ function add_twitter_user($twitter_id, $screen_name)
 // Creates or Updates a Twitter user
 function save_twitter_user($twitter_id, $screen_name)
 {
-
     // Check to see whether the Twitter user is already in the system,
     // and update its screen name and uri if so.
 
@@ -115,25 +82,20 @@ function save_twitter_user($twitter_id, $screen_name)
 
         $result = true;
 
-        // Only update if Twitter screen name has changed
+        // Delete old record if Twitter user changed screen name
 
         if ($fuser->nickname != $screen_name) {
-            $result = updateTwitter_user($twitter_id, $screen_name);
-
-            common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' .
-                "$fuser->id to $screen_name, was $fuser->nickname");
+            $oldname = $fuser->nickname;
+            $fuser->delete();
+            common_log(LOG_INFO, sprintf('Twitter bridge - Updated nickname (and URI) ' .
+                                         'for Twitter user %1$d - %2$s, was %3$s.',
+                                         $fuser->id,
+                                         $screen_name,
+                                         $oldname));
         }
 
-        return $result;
-
-    } else {
         return add_twitter_user($twitter_id, $screen_name);
     }
-
-    $fuser->free();
-    unset($fuser);
-
-    return true;
 }
 
 function is_twitter_bound($notice, $flink) {
index b2657ff61fb0bbd220e71105139b8cc1c51fd96d..dbef438a4bd10dc4edfabd4695142e374d89eeba 100644 (file)
@@ -219,7 +219,7 @@ class TwitterauthorizationAction extends Action
 
             $user = common_current_user();
             $this->saveForeignLink($user->id, $twitter_user->id, $atok);
-            save_twitter_user($twitter_user->id, $twitter_user->name);
+            save_twitter_user($twitter_user->id, $twitter_user->screen_name);
 
         } else {
 
diff --git a/scripts/updateprofileurl.php b/scripts/updateprofileurl.php
new file mode 100644 (file)
index 0000000..2fc6828
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * 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/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'i:n:a';
+$longoptions = array('id=', 'nickname=', 'all');
+
+$helptext = <<<END_OF_UPDATEPROFILEURL_HELP
+updateprofileurl.php [options]
+update the URLs of all avatars in the system
+
+  -i --id       ID of user to update
+  -n --nickname nickname of the user to update
+  -a --all      update all
+
+END_OF_UPDATEPROFILEURL_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+try {
+    $user = null;
+
+    if (have_option('i', 'id')) {
+        $id = get_option_value('i', 'id');
+        $user = User::staticGet('id', $id);
+        if (empty($user)) {
+            throw new Exception("Can't find user with id '$id'.");
+        }
+        updateProfileURL($user);
+    } else if (have_option('n', 'nickname')) {
+        $nickname = get_option_value('n', 'nickname');
+        $user = User::staticGet('nickname', $nickname);
+        if (empty($user)) {
+            throw new Exception("Can't find user with nickname '$nickname'");
+        }
+        updateProfileURL($user);
+    } else if (have_option('a', 'all')) {
+        $user = new User();
+        if ($user->find()) {
+            while ($user->fetch()) {
+                updateProfileURL($user);
+            }
+        }
+    } else {
+        show_help();
+        exit(1);
+    }
+} catch (Exception $e) {
+    print $e->getMessage()."\n";
+    exit(1);
+}
+
+function updateProfileURL($user)
+{
+    $profile = $user->getProfile();
+
+    if (empty($profile)) {
+        throw new Exception("Can't find profile for user $user->nickname ($user->id)");
+    }
+
+    $orig = clone($profile);
+
+    $profile->profileurl = common_profile_url($user->nickname);
+
+    if (!have_option('q', 'quiet')) {
+        print "Updating profile url for $user->nickname ($user->id) ".
+          "from $orig->profileurl to $profile->profileurl...";
+    }
+
+    $result = $profile->update($orig);
+
+    if (!$result) {
+        print "FAIL.\n";
+        common_log_db_error($profile, 'UPDATE', __FILE__);
+        throw new Exception("Can't update profile for user $user->nickname ($user->id)");
+    }
+
+    common_broadcast_profile($profile);
+
+    print "OK.\n";
+}
index 01a729c10b92b7669ed745051227990269f70fe2..6f284f023ee7c7c1fef480c26e7ccfd2b27ba32c 100644 (file)
Binary files a/theme/base/images/icons/icons-01.gif and b/theme/base/images/icons/icons-01.gif differ
diff --git a/theme/base/images/icons/twotone/green/against.gif b/theme/base/images/icons/twotone/green/against.gif
new file mode 100644 (file)
index 0000000..ca796c8
Binary files /dev/null and b/theme/base/images/icons/twotone/green/against.gif differ
diff --git a/theme/base/images/icons/twotone/green/checkmark.gif b/theme/base/images/icons/twotone/green/checkmark.gif
new file mode 100644 (file)
index 0000000..892429d
Binary files /dev/null and b/theme/base/images/icons/twotone/green/checkmark.gif differ
index 6954de7bad6a0e8cdda7cb29c3f7a7b5c971801d..82eb135316c30638c54bb091faf708853574f4a4 100644 (file)
@@ -192,7 +192,8 @@ button.minimize,
 .form_reset_key input.submit,
 .entity_clear input.submit,
 .entity_flag input.submit,
-.entity_flag p {
+.entity_flag p,
+.entity_subscribe input.submit {
 background-image:url(../../base/images/icons/icons-01.gif);
 background-repeat:no-repeat;
 background-color:transparent;
@@ -348,6 +349,12 @@ background-position: 5px -2039px;
 .entity_flag p {
 background-position: 5px -2105px;
 }
+.entity_subscribe input.accept {
+background-position: 5px -2171px;
+}
+.entity_subscribe input.reject {
+background-position: 5px -2237px;
+}
 
 /* NOTICES */
 .notice .attachment {
index 9ac2730bdae5c6649d887a302561fd57b84bde2f..44ae4953b72d5b555c7d9bbbc094eba9f9c4742d 100644 (file)
@@ -192,7 +192,8 @@ button.minimize,
 .form_reset_key input.submit,
 .entity_clear input.submit,
 .entity_flag input.submit,
-.entity_flag p {
+.entity_flag p,
+.entity_subscribe input.submit {
 background-image:url(../../base/images/icons/icons-01.gif);
 background-repeat:no-repeat;
 background-color:transparent;
@@ -347,6 +348,12 @@ background-position: 5px -2039px;
 .entity_flag p {
 background-position: 5px -2105px;
 }
+.entity_subscribe input.accept {
+background-position: 5px -2171px;
+}
+.entity_subscribe input.reject {
+background-position: 5px -2237px;
+}
 
 /* NOTICES */
 .notice .attachment {