diff options
30 files changed, 319 insertions, 112 deletions
diff --git a/actions/apiaccountverifycredentials.php b/actions/apiaccountverifycredentials.php index 1095d5162..ea61a3205 100644 --- a/actions/apiaccountverifycredentials.php +++ b/actions/apiaccountverifycredentials.php @@ -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; } - + } diff --git a/actions/apigroupjoin.php b/actions/apigroupjoin.php index 3309d63e7..374cf83df 100644 --- a/actions/apigroupjoin.php +++ b/actions/apigroupjoin.php @@ -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); diff --git a/actions/apigroupleave.php b/actions/apigroupleave.php index 6f8d40527..9848ece05 100644 --- a/actions/apigroupleave.php +++ b/actions/apigroupleave.php @@ -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); diff --git a/actions/favorited.php b/actions/favorited.php index 9ffa5b844..d8980440d 100644 --- a/actions/favorited.php +++ b/actions/favorited.php @@ -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'; diff --git a/actions/publictagcloud.php b/actions/publictagcloud.php index 9e4478dbb..9993b2d3f 100644 --- a/actions/publictagcloud.php +++ b/actions/publictagcloud.php @@ -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'); diff --git a/actions/register.php b/actions/register.php index 698137346..ccab76cf0 100644 --- a/actions/register.php +++ b/actions/register.php @@ -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 diff --git a/actions/userauthorization.php b/actions/userauthorization.php index 4321f1302..7f71c60db 100644 --- a/actions/userauthorization.php +++ b/actions/userauthorization.php @@ -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 +} diff --git a/classes/Inbox.php b/classes/Inbox.php index 26b27d2b5..be62611a1 100644 --- a/classes/Inbox.php +++ b/classes/Inbox.php @@ -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); + } } diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index ab65c30ce..dfd06b57e 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -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); } diff --git a/classes/User.php b/classes/User.php index 0ab816b57..72c3f39e9 100644 --- a/classes/User.php +++ b/classes/User.php @@ -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() diff --git a/classes/statusnet.ini b/classes/statusnet.ini index a535159e8..5f8da7cf5 100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -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 diff --git a/db/statusnet.sql b/db/statusnet.sql index 8946f4d7e..343464801 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -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', diff --git a/lib/cache.php b/lib/cache.php index 635c96ad4..df6fc3649 100644 --- a/lib/cache.php +++ b/lib/cache.php @@ -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 diff --git a/lib/default.php b/lib/default.php index 2bedc4bf0..485a08ba4 100644 --- a/lib/default.php +++ b/lib/default.php @@ -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, diff --git a/lib/grouptagcloudsection.php b/lib/grouptagcloudsection.php index 14ceda085..f1106cc7b 100644 --- a/lib/grouptagcloudsection.php +++ b/lib/grouptagcloudsection.php @@ -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(); diff --git a/lib/oauthstore.php b/lib/oauthstore.php index b30fb49d5..eabe37f9f 100644 --- a/lib/oauthstore.php +++ b/lib/oauthstore.php @@ -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())); - } /** diff --git a/lib/personaltagcloudsection.php b/lib/personaltagcloudsection.php index 091425f92..5ea3f188d 100644 --- a/lib/personaltagcloudsection.php +++ b/lib/personaltagcloudsection.php @@ -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 ' . diff --git a/lib/popularnoticesection.php b/lib/popularnoticesection.php index fbf9a60ab..296ddbbb5 100644 --- a/lib/popularnoticesection.php +++ b/lib/popularnoticesection.php @@ -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,' . diff --git a/plugins/MemcachePlugin.php b/plugins/MemcachePlugin.php index 2bc4b892b..c5e74fb41 100644 --- a/plugins/MemcachePlugin.php +++ b/plugins/MemcachePlugin.php @@ -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', diff --git a/plugins/MobileProfile/MobileProfilePlugin.php b/plugins/MobileProfile/MobileProfilePlugin.php index 5c913836d..cd2531fa7 100644 --- a/plugins/MobileProfile/MobileProfilePlugin.php +++ b/plugins/MobileProfile/MobileProfilePlugin.php @@ -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) { diff --git a/plugins/MobileProfile/mp-screen.css b/plugins/MobileProfile/mp-screen.css index 04fa5fb00..0fc801612 100644 --- a/plugins/MobileProfile/mp-screen.css +++ b/plugins/MobileProfile/mp-screen.css @@ -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%; diff --git a/plugins/TwitterBridge/daemons/twitterstatusfetcher.php b/plugins/TwitterBridge/daemons/twitterstatusfetcher.php index 36732ce46..bff657eb6 100755 --- a/plugins/TwitterBridge/daemons/twitterstatusfetcher.php +++ b/plugins/TwitterBridge/daemons/twitterstatusfetcher.php @@ -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__); diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index 33dfb788b..de30d9ebf 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -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) { diff --git a/plugins/TwitterBridge/twitterauthorization.php b/plugins/TwitterBridge/twitterauthorization.php index b2657ff61..dbef438a4 100644 --- a/plugins/TwitterBridge/twitterauthorization.php +++ b/plugins/TwitterBridge/twitterauthorization.php @@ -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 index 000000000..2fc6828e6 --- /dev/null +++ b/scripts/updateprofileurl.php @@ -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"; +} diff --git a/theme/base/images/icons/icons-01.gif b/theme/base/images/icons/icons-01.gif Binary files differindex 01a729c10..6f284f023 100644 --- a/theme/base/images/icons/icons-01.gif +++ b/theme/base/images/icons/icons-01.gif diff --git a/theme/base/images/icons/twotone/green/against.gif b/theme/base/images/icons/twotone/green/against.gif Binary files differnew file mode 100644 index 000000000..ca796c8a3 --- /dev/null +++ b/theme/base/images/icons/twotone/green/against.gif diff --git a/theme/base/images/icons/twotone/green/checkmark.gif b/theme/base/images/icons/twotone/green/checkmark.gif Binary files differnew file mode 100644 index 000000000..892429d48 --- /dev/null +++ b/theme/base/images/icons/twotone/green/checkmark.gif diff --git a/theme/default/css/display.css b/theme/default/css/display.css index 6954de7ba..82eb13531 100644 --- a/theme/default/css/display.css +++ b/theme/default/css/display.css @@ -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 { diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css index 9ac2730bd..44ae4953b 100644 --- a/theme/identica/css/display.css +++ b/theme/identica/css/display.css @@ -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 { |