From 4f6052d8d8bd3d64d395e7849a31183371040eb8 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 2 Feb 2010 05:53:05 -0800 Subject: [PATCH 01/18] Apply xopher's fix to add 'sitetype' parameter to setup_status_network.sh, exposed to the email --- scripts/setup_status_network.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/setup_status_network.sh b/scripts/setup_status_network.sh index 4ad808011c..bacf3c3e7f 100755 --- a/scripts/setup_status_network.sh +++ b/scripts/setup_status_network.sh @@ -13,6 +13,11 @@ export sitename="$2" export tags="$3" export email="$4" export fullname="$5" +export sitetype="$6" + +if [ "$sitetype" == '' ]; then + sitetype='single-user' +fi # Fixme: if this is changed later we need to update profile URLs # for the created user. @@ -71,6 +76,7 @@ then sed "s/\$nickname/$nickname/" | \ sed "s/\$sitename/$sitename/" | \ sed "s/\$userpass/$userpass/" | \ + sed "s/\$sitetype/$sitetype/" | \ php $PHPBASE/scripts/sendemail.php \ -s"$server" \ -n"$nickname" \ From 51c3606715573d8cea3c79ff7bc989a4ba86acc5 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 2 Feb 2010 09:30:15 -0800 Subject: [PATCH 02/18] Fix regression breaking file attachments. This is what I get for rushing fixes and not properly testing them. :P --- classes/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/File.php b/classes/File.php index 6dd9e0c06e..ee418a8024 100644 --- a/classes/File.php +++ b/classes/File.php @@ -181,7 +181,7 @@ class File extends Memcached_DataObject */ static function validFilename($filename) { - return preg_match('^/[A-Za-z0-9._-]+$/', $filename); + return preg_match('/^[A-Za-z0-9._-]+$/', $filename); } /** From 6215b9788c8d1f523d4d173ecf40f185bd024bca Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 3 Feb 2010 11:53:31 -0500 Subject: [PATCH 03/18] add nickname as unique key for user_group table --- classes/statusnet.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/classes/statusnet.ini b/classes/statusnet.ini index 6203650a69..e28424ce2a 100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -585,6 +585,7 @@ modified = 384 [user_group__keys] id = N +nickname = U [user_openid] canonical = 130 From 644a9163831938518ed3473c7a6d74251d254e8b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 3 Feb 2010 11:55:54 -0500 Subject: [PATCH 04/18] change 'sitetype' to 'siteplan' to use consistent language --- scripts/setup_status_network.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/setup_status_network.sh b/scripts/setup_status_network.sh index bacf3c3e7f..89d15415f9 100755 --- a/scripts/setup_status_network.sh +++ b/scripts/setup_status_network.sh @@ -13,10 +13,10 @@ export sitename="$2" export tags="$3" export email="$4" export fullname="$5" -export sitetype="$6" +export siteplan="$6" -if [ "$sitetype" == '' ]; then - sitetype='single-user' +if [ "$siteplan" == '' ]; then + siteplan='single-user' fi # Fixme: if this is changed later we need to update profile URLs @@ -76,7 +76,7 @@ then sed "s/\$nickname/$nickname/" | \ sed "s/\$sitename/$sitename/" | \ sed "s/\$userpass/$userpass/" | \ - sed "s/\$sitetype/$sitetype/" | \ + sed "s/\$siteplan/$siteplan/" | \ php $PHPBASE/scripts/sendemail.php \ -s"$server" \ -n"$nickname" \ From aee73d87ee33d1743a74fbe87f964bfc46ba1873 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 3 Feb 2010 12:13:07 -0500 Subject: [PATCH 05/18] showcache.php column flag was conflicting with default flag --- scripts/showcache.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/showcache.php b/scripts/showcache.php index f179795728..93b57a484b 100644 --- a/scripts/showcache.php +++ b/scripts/showcache.php @@ -20,14 +20,14 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); -$shortoptions = "t:c:v:k:"; +$shortoptions = "t:l:v:k:"; $helptext = << shows the cached object based on the args -t table Table to look up - -c column Column to look up, default "id" + -l column Column to look up, default "id" -v value Value to look up -k key Key to look up; other args are ignored @@ -44,7 +44,7 @@ if (!empty($karg)) { if (empty($table)) { die("No table or key specified\n"); } - $column = get_option_value('c'); + $column = get_option_value('l'); if (empty($column)) { $column = 'id'; } From 0e0beea5aa75e58de251752b2720d4ea6a8c89f7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 3 Feb 2010 12:13:20 -0500 Subject: [PATCH 06/18] clearcache.php column flag was conflicting with default flag --- scripts/clearcache.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/clearcache.php b/scripts/clearcache.php index 702c1e3d67..0fb75daa03 100644 --- a/scripts/clearcache.php +++ b/scripts/clearcache.php @@ -20,35 +20,35 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); -$shortoptions = "t:c:v:k:"; +$shortoptions = "t:l:v:k:"; -$helptext = << clears the cached object based on the args -t table Table to look up - -c column Column to look up, default "id" + -l column Column to look up, default "id" -v value Value to look up -k key Key to look up; other args are ignored -ENDOFHELP; +END_OF_CLEARCACHE_HELP; require_once INSTALLDIR.'/scripts/commandline.inc'; -$karg = get_option_value('k'); +$karg = get_option_value('k', 'key'); if (!empty($karg)) { $k = common_cache_key($karg); } else { - $table = get_option_value('t'); + $table = get_option_value('t', 'table'); if (empty($table)) { die("No table or key specified\n"); } - $column = get_option_value('c'); + $column = get_option_value('l', 'column'); if (empty($column)) { $column = 'id'; } - $value = get_option_value('v'); + $value = get_option_value('v', 'value'); $k = Memcached_DataObject::cacheKey($table, $column, $value); } From dbeb388adefec94c3bb34e99305a004d845b719c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 3 Feb 2010 12:31:25 -0500 Subject: [PATCH 07/18] clear cache for Profile::hasRepeated() at Notice::insert() time --- classes/Notice.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/classes/Notice.php b/classes/Notice.php index 42878d94f1..f9f3863579 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1468,4 +1468,25 @@ class Notice extends Memcached_DataObject $handler->handle($this); } } + + function insert() + { + $result = parent::insert(); + + if ($result) { + // Profile::hasRepeated() abuses pkeyGet(), so we + // have to clear manually + if (!empty($this->repeat_of)) { + $c = self::memcache(); + if (!empty($c)) { + $ck = self::multicacheKey('Notice', + array('profile_id' => $this->profile_id, + 'repeat_of' => $this->repeat_of)); + $c->delete($ck); + } + } + } + + return $result; + } } From 7a7e2162dd7eed59e60d9360d8692abc111d940c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 3 Feb 2010 14:58:29 -0500 Subject: [PATCH 08/18] Script to update profile URLs --- scripts/updateprofileurl.php | 99 ++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 scripts/updateprofileurl.php diff --git a/scripts/updateprofileurl.php b/scripts/updateprofileurl.php new file mode 100644 index 0000000000..2fc6828e60 --- /dev/null +++ b/scripts/updateprofileurl.php @@ -0,0 +1,99 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'i:n:a'; +$longoptions = array('id=', 'nickname=', 'all'); + +$helptext = <<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"; +} From d2dc3e41c571cb4e9edef18b3bb1918633a9ce39 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 4 Feb 2010 16:27:34 +0000 Subject: [PATCH 09/18] Fixes minor remote subscription profile layout --- actions/userauthorization.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/actions/userauthorization.php b/actions/userauthorization.php index 4321f1302e..7f71c60dbe 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 +} From c56250fb33e2e43a7c0f16bb78810e4ed70cf792 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 4 Feb 2010 16:51:51 +0000 Subject: [PATCH 10/18] Added accept and reject icons to remote subscription authorization --- theme/base/images/icons/icons-01.gif | Bin 3758 -> 3870 bytes .../images/icons/twotone/green/against.gif | Bin 0 -> 85 bytes .../images/icons/twotone/green/checkmark.gif | Bin 0 -> 76 bytes theme/default/css/display.css | 9 ++++++++- theme/identica/css/display.css | 9 ++++++++- 5 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 theme/base/images/icons/twotone/green/against.gif create mode 100644 theme/base/images/icons/twotone/green/checkmark.gif diff --git a/theme/base/images/icons/icons-01.gif b/theme/base/images/icons/icons-01.gif index 01a729c10b92b7669ed745051227990269f70fe2..6f284f023ee7c7c1fef480c26e7ccfd2b27ba32c 100644 GIT binary patch delta 171 zcmV;c096039iAQxM@dFFIbj+A(g^4Nu?+qK6aWAJA^8LW3IP8AEC2ui02%<&vmFEY z3IYC;77mhj>6VmPnU3MbOL5+!)erc0<`I&NwL7_=retDXM8JP8^nvltv zM;Vfnd6~3Xo3hhj~J*c|oTsoWog|*~XWGNtnh-oXUxsjoF*f Z8J)%1M8FxGVFa8l2N|0$NnZg006RIlO_cxu delta 59 zcmbOyw@#MF-P6s&GEs`bkK?)jMxK96!tqlC6o0ZXaxwg8&|v@qpwwnr<{w;)( Date: Thu, 4 Feb 2010 11:06:01 -0800 Subject: [PATCH 11/18] Add time-based cutoffs for public tag cloud, favorited lists to speed up those queries. Defaulting to only looking at last 90 days of activity, can be adjusted up or down. $config['tag']['cutoff'] = 86400 * 90; $config['popular']['cutoff'] = 86400 * 90; Per-user and per-group tag clouds do not use the cutoff (and it doesn't help with indexing on them). --- actions/favorited.php | 3 +++ actions/publictagcloud.php | 3 +++ lib/default.php | 6 ++++-- lib/grouptagcloudsection.php | 1 + lib/personaltagcloudsection.php | 1 + lib/popularnoticesection.php | 7 +++++-- 6 files changed, 17 insertions(+), 4 deletions(-) diff --git a/actions/favorited.php b/actions/favorited.php index 9ffa5b8445..d8980440d1 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 9e4478dbb1..9993b2d3fd 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/lib/default.php b/lib/default.php index 2bedc4bf08..485a08ba44 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 14ceda0850..f1106cc7bf 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/personaltagcloudsection.php b/lib/personaltagcloudsection.php index 091425f926..5ea3f188db 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 fbf9a60ab8..296ddbbb50 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,' . From 5bdc6fa5d456c3f520d8124950684220d8f440a3 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Thu, 4 Feb 2010 19:39:46 +0000 Subject: [PATCH 12/18] Moved hardcoded identica theme out of MobileProfile. In this case, it will use whichever theme is loaded as its base and then add its own mobile styles. Of course, if a theme comes with its own mobile styles, it will use that instead as an addition to its own base. --- plugins/MobileProfile/MobileProfilePlugin.php | 10 ++++++++++ plugins/MobileProfile/mp-screen.css | 5 +---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/plugins/MobileProfile/MobileProfilePlugin.php b/plugins/MobileProfile/MobileProfilePlugin.php index 5c913836dc..cd2531fa72 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 04fa5fb002..0fc801612b 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 + * @author Sarven Capadisli * @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%; From 239b88025ef1368bb871871ee903d6b078493f76 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 4 Feb 2010 13:08:34 -0800 Subject: [PATCH 13/18] Should fix spurious 'nickname taken' and 'email taken' errors on registration. Form's checks for existing nicks & emails would incorrectly return true on the second lookup due to bad interaction with negative caching. (was checking $obj !== false but we return null now on negative cache hits, with false for cache misses) --- actions/register.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/register.php b/actions/register.php index 6981373467..ccab76cf01 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 From 37f3a3d558ba55a085c9ee5427948b572c197bc3 Mon Sep 17 00:00:00 2001 From: Eric Helgeson Date: Sat, 16 Jan 2010 11:56:07 -0500 Subject: [PATCH 14/18] Missed change when refactoring groups. Thanks macno --- actions/apigroupjoin.php | 2 +- actions/apigroupleave.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/apigroupjoin.php b/actions/apigroupjoin.php index 3309d63e7b..374cf83df0 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 6f8d40527b..9848ece053 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); From 2eadeca74515802dccc63d7ec84af1bc7d1338d9 Mon Sep 17 00:00:00 2001 From: Eric Helgeson Date: Sat, 16 Jan 2010 11:56:07 -0500 Subject: [PATCH 15/18] Missed change when refactoring groups. Thanks macno --- actions/apigroupjoin.php | 2 +- actions/apigroupleave.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/apigroupjoin.php b/actions/apigroupjoin.php index 3309d63e7b..374cf83df0 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 6f8d40527b..9848ece053 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); From 9554b4ccbf0783516ea1735a7c999919be33c280 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 4 Feb 2010 14:50:20 -0800 Subject: [PATCH 16/18] Inbox::streamNotices() with deletion compensation: inbox paging should more or less work with deleted items now. No change in efficiency for the common case where nothing's deleted: does the same bulk fetch of just the notices we think we'll need as before, then if we turned up short keeps checking one by one until we've filled up to our $limit. This can leave us with overlap between pages, but we already have that when new messages come in between clicks; seems to be the lesser of evils versus not getting a 'before' button. More permanent fix for that will be to switch timeline paging in the UI to use notice IDs. --- classes/Inbox.php | 66 ++++++++++++++++++++++++++++++++++++++++++++--- classes/User.php | 14 +++------- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/classes/Inbox.php b/classes/Inbox.php index 26b27d2b58..0294107990 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,61 @@ 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) { + common_log(LOG_DEBUG, __METHOD__ . " ok got $wanted items"); + return $notices; + } + + common_log(LOG_DEBUG, __METHOD__ . " got $notices->N of $wanted items, getting more"); + // 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) { + common_log(LOG_DEBUG, __METHOD__ . " got another one"); + $items[] = $notice; + } else { + common_log(LOG_DEBUG, __METHOD__ . " skipping another deleted one"); + } + } + return new ArrayWrapper($items); + } } diff --git a/classes/User.php b/classes/User.php index 0ab816b57e..72c3f39e94 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() From bb16898b1c73073e0442de72f2af133a3bd39713 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 4 Feb 2010 14:50:20 -0800 Subject: [PATCH 17/18] Inbox::streamNotices() with deletion compensation: inbox paging should more or less work with deleted items now. No change in efficiency for the common case where nothing's deleted: does the same bulk fetch of just the notices we think we'll need as before, then if we turned up short keeps checking one by one until we've filled up to our $limit. This can leave us with overlap between pages, but we already have that when new messages come in between clicks; seems to be the lesser of evils versus not getting a 'before' button. More permanent fix for that will be to switch timeline paging in the UI to use notice IDs. --- classes/Inbox.php | 66 ++++++++++++++++++++++++++++++++++++++++++++--- classes/User.php | 14 +++------- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/classes/Inbox.php b/classes/Inbox.php index 26b27d2b58..0294107990 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,61 @@ 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) { + common_log(LOG_DEBUG, __METHOD__ . " ok got $wanted items"); + return $notices; + } + + common_log(LOG_DEBUG, __METHOD__ . " got $notices->N of $wanted items, getting more"); + // 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) { + common_log(LOG_DEBUG, __METHOD__ . " got another one"); + $items[] = $notice; + } else { + common_log(LOG_DEBUG, __METHOD__ . " skipping another deleted one"); + } + } + return new ArrayWrapper($items); + } } diff --git a/classes/User.php b/classes/User.php index 0ab816b57e..72c3f39e94 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() From 4502bea9a86fe5992eb9b359d90f0c1f004998c1 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 4 Feb 2010 15:16:27 -0800 Subject: [PATCH 18/18] drop debug messages from inbox deletion fix --- classes/Inbox.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/classes/Inbox.php b/classes/Inbox.php index 0294107990..be62611a16 100644 --- a/classes/Inbox.php +++ b/classes/Inbox.php @@ -212,11 +212,9 @@ class Inbox extends Memcached_DataObject $wanted = count($firstChunk); // raw entry count in the inbox up to our $limit if ($notices->N >= $wanted) { - common_log(LOG_DEBUG, __METHOD__ . " ok got $wanted items"); return $notices; } - common_log(LOG_DEBUG, __METHOD__ . " got $notices->N of $wanted items, getting more"); // There were deleted notices, we'll need to look for more. assert($notices instanceof ArrayWrapper); $items = $notices->_items; @@ -225,10 +223,8 @@ class Inbox extends Memcached_DataObject while (count($items) < $wanted && count($remainder) > 0) { $notice = Notice::staticGet(array_shift($remainder)); if ($notice) { - common_log(LOG_DEBUG, __METHOD__ . " got another one"); $items[] = $notice; } else { - common_log(LOG_DEBUG, __METHOD__ . " skipping another deleted one"); } } return new ArrayWrapper($items);