From 7448c1e5c9694e6c66b558a362a4451c9437c224 Mon Sep 17 00:00:00 2001 From: River Tarnell Date: Fri, 1 Jul 2005 06:50:03 +0000 Subject: [PATCH] page contributions based on timestamp, not on limit,offset. significantly reduces database load (O(offset) -> O(1)) for users with many edits. --- includes/SpecialContributions.php | 257 +++++++++++++++++++++--------- 1 file changed, 179 insertions(+), 78 deletions(-) diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php index 9272ede239..ffd5c6da1d 100644 --- a/includes/SpecialContributions.php +++ b/includes/SpecialContributions.php @@ -4,6 +4,110 @@ * @subpackage SpecialPage */ +class contribs_finder { + var $username, $offset, $limit, $namespace, $invert; + var $dbr; + + function contribs_finder($username) { + $this->username = $username; + $this->dbr =& wfGetDB(DB_SLAVE); + } + + function set_limit($limit) { + $this->limit = $limit; + } + + function set_offset($offset) { + $this->offset = $offset; + } + + function set_namespace($namespace, $invert = false) { + $this->namespace = $namespace; + $this->invert = $invert; + } + + function set_hide_minor($hm) { + $this->hide_minor = $hm; + } + + function get_edit_limits() { + $sql = "SELECT MIN(rev_timestamp) as earliest, MAX(rev_timestamp) as latest " . + "FROM revision " . + "WHERE rev_user_text = " . $this->dbr->addQuotes($this->username); + $res = $this->dbr->query($sql, "contribs_finder::get_edit_limits"); + $row = $this->dbr->fetchObject($res); + if (!$row) + return array(null, null); + $this->dbr->freeResult($res); + return array($row->earliest, $row->latest); + } + + function get_previous_offset_for_paging() { + $sql = "SELECT rev_timestamp FROM revision " . + "WHERE rev_timestamp > " . $this->offset . " AND " . + "rev_user_text = " . $this->dbr->addQuotes($this->username) . + " ORDER BY rev_timestamp ASC LIMIT " . $this->limit; + $res = $this->dbr->query($sql); + $rows = array(); + while ($obj = $this->dbr->fetchObject($res)) + $rows[] = $obj; + $this->dbr->freeResult($res); + return $rows[count($rows) - 1]->rev_timestamp; + } + + /* private */ function make_sql() { + $userCond = $condition = $index = $minorQuery = $nsQuery + = $offsetQuery = $limitQuery = $nsinvert = ""; + + if ($this->username == 'newbies') { + $max = $this->dbr->selectField('user', 'max(user_id)', false, "make_sql"); + $userCond = '>' . ($max - $max / 100); + $id = 0; + } + + if ($this->hide_minor) + $minorQuery = 'AND rev_minor_edit=0'; + + if ($this->invert) + $nsinvert = "!"; + + if (!is_null($this->namespace)) + $nsQuery .= "AND page_namespace {$nsinvert}= {$this->namespace}"; + + extract($this->dbr->tableNames('page', 'revision')); + if ($userCond == "") { + $condition = "rev_user_text=" . $this->dbr->addQuotes($this->username); + $index = 'usertext_timestamp'; + } else { + $condition = "rev_user {$userCond}"; + $index = 'user_timestamp'; + } + + $limitQuery = "LIMIT {$this->limit}"; + if ($this->offset) + $offsetQuery = "AND rev_timestamp < {$this->offset}"; + + $use_index = $this->dbr->useIndexClause($index); + $sql = "SELECT + page_namespace,page_title,page_is_new,page_latest, + rev_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user_text, + rev_deleted + FROM $page,$revision $use_index + WHERE page_id=rev_page AND $condition $minorQuery $nsQuery $offsetQuery + ORDER BY rev_timestamp DESC $limitQuery"; + return $sql; + } + + function find() { + $contribs = array(); + $res = $this->dbr->query($this->make_sql(), "contribs_finder::find"); + while ($c = $this->dbr->fetchObject($res)) + $contribs[] = $c; + $this->dbr->freeResult($res); + return $contribs; + } +}; + /** * Special page "user contributions". * Shows a list of the contributions of a user. @@ -12,35 +116,52 @@ * @param string $par (optional) user name of the user for which to show the contributions */ function wfSpecialContributions( $par = null ) { - global $wgUser, $wgOut, $wgLang, $wgContLang, $wgRequest; + global $wgUser, $wgOut, $wgLang, $wgContLang, $wgRequest, $wgTitle; $fname = 'wfSpecialContributions'; - // GET values $target = isset($par) ? $par : $wgRequest->getVal( 'target' ); - $namespace = $wgRequest->getIntOrNull( 'namespace' ); - $invert = $wgRequest->getBool( 'invert' ); - $hideminor = ($wgRequest->getBool( 'hideminor' ) ? true : false); - - if ( '' == $target ) { - $wgOut->errorpage( 'notargettitle', 'notargettext' ); + if (!strlen($target)) { + $wgOut->errorpage('notargettitle', 'notargettext'); return; } - # FIXME: Change from numeric offsets to date offsets - list( $limit, $offset ) = wfCheckLimits( 50, '' ); - $querylimit = $limit + 1; - $sk = $wgUser->getSkin(); - $dbr =& wfGetDB( DB_SLAVE ); - $userCond = ""; - $nt = Title::newFromURL( $target ); - if ( !$nt ) { + if (!$nt) { $wgOut->errorpage( 'notargettitle', 'notargettext' ); return; } - $nt =& Title::makeTitle( NS_USER, $nt->getDBkey() ); + $nt =& Title::makeTitle(NS_USER, $nt->getDBkey()); + + $namespace = $wgRequest->getIntOrNull('namespace'); + $invert = $wgRequest->getBool('invert'); + $hideminor = $wgRequest->getBool('hideminor'); + $limit = min($wgRequest->getInt('limit', 50), 500); + $offset = $wgRequest->getText('offset'); + /* Offset must be an integral. */ + if (!strlen($offset) || !preg_match("/^[0-9]+$/", $offset)) + $offset = 0; + + $title = Title::newFromText($wgContLang->specialpage("Contributions")); + $urlbits = "hideminor=$hideminor&namespace=$namespace&invert=$invert&target=" . wfUrlEncode($target); + $myurl = $title->escapeLocalURL($urlbits); - $id = User::idFromName( $nt->getText() ); + $finder = new contribs_finder($nt->getText()); + + $finder->set_namespace($namespace, $invert); + $finder->set_hide_minor($hideminor); + $finder->set_limit($limit); + $finder->set_offset($offset); + + if ($wgRequest->getText('go') == "prev") { + $prevts = $finder->get_previous_offset_for_paging(); + $prevurl = $title->getLocalURL($urlbits . "&offset=$prevts&limit=$limit"); + $wgOut->redirect($prevurl); + return; + } + + $sk = $wgUser->getSkin(); + + $id = User::idFromName($nt->getText()); if ( 0 == $id ) { $ul = $nt->getText(); @@ -53,13 +174,8 @@ function wfSpecialContributions( $par = null ) { $ul .= ' (' . $sk->makeLinkObj( $talk, $wgLang->getNsText( NS_TALK ) ) . ')'; } - - if ( $target == 'newbies' ) { - # View the contributions of all recently created accounts - $max = $dbr->selectField( 'user', 'max(user_id)', false, $fname ); - $userCond = '>' . ($max - $max / 100); - $ul = wfMsg ( 'newbies' ); - $id = 0; + if ($target == 'newbies') { + $ul = wfMsg ('newbies'); } $wgOut->setSubtitle( wfMsgHtml( 'contribsub', $ul ) ); @@ -73,65 +189,50 @@ function wfSpecialContributions( $par = null ) { 'limit' => $limit, 'hideminor' => $hideminor ? 0 : 1, 'namespace' => $namespace ) ) ); - - $minorQuery = $hideminor ? "AND rev_minor_edit=0" : ""; - if( !is_null($namespace) ) { - $minorQuery .= ' AND page_namespace ' . ($invert ? '!' : '') . "= {$namespace}"; - } - - extract( $dbr->tableNames( 'page', 'revision' ) ); - if ( $userCond == "" ) { - $condition = "rev_user_text=" . $dbr->addQuotes( $nt->getText() ); - $index = 'usertext_timestamp'; - } else { - $condition = "rev_user {$userCond}"; - $index = 'user_timestamp'; + + $contribs = $finder->find(); + + if (count($contribs) == 0) { + $wgOut->addWikiText( wfMsg( "nocontribs" ) ); + return; } + list($early, $late) = $finder->get_edit_limits(); + $lastts = count($contribs) ? $contribs[count($contribs) - 1]->rev_timestamp : 0; + $atstart = (!count($contribs) || $late == $contribs[0]->rev_timestamp); + $atend = (!count($contribs) || $early == $lastts); +wfdebug("early=$early late=$late lastts=$lastts\n"); + $wgOut->addHTML(ucNamespaceForm($target, $hideminor, $namespace, $invert)); - $use_index = $dbr->useIndexClause( $index ); - $sql = "SELECT - page_namespace,page_title,page_is_new,page_latest, - rev_id,rev_timestamp,rev_comment,rev_minor_edit,rev_user_text, - rev_deleted - FROM $page,$revision $use_index - WHERE page_id=rev_page AND $condition $minorQuery " . - "ORDER BY rev_timestamp DESC " . $dbr->limitResult( $querylimit, $offset ); - $res = $dbr->query( $sql, $fname ); - $numRows = $dbr->numRows( $res ); + $prevtext = wfMsg("prevn", $limit); + if ($atstart) + $prevlink = $prevtext; + else + $prevlink = "$prevtext"; - $wgOut->addHTML( ucNamespaceForm( $target, $hideminor, $namespace, $invert ) ); + $nexttext = wfMsg("nextn", $limit); + if ($atend) + $nextlink = $nexttext; + else + $nextlink = "$nexttext"; - $top = wfShowingResults( $offset, $limit ); - $wgOut->addHTML( "

{$top}\n" ); + $urls = array(); + foreach (array(20, 50, 100, 250, 500) as $num) + $urls[] = "".$wgLang->formatNum($num).""; + $bits = implode($urls, ' | '); - $sl = wfViewPrevNext( $offset, $limit, - $wgContLang->specialpage( "Contributions" ), - "hideminor=$hideminor&namespace=$namespace&invert=$invert&target=" . wfUrlEncode( $target ), - ($numRows) <= $limit); + $prevnextbits = wfMsgHtml("viewprevnext", $prevlink, $nextlink, $bits); $shm = wfMsgHtml( "showhideminor", $mlink ); - $wgOut->addHTML( "
{$sl} ($shm)

\n"); + $wgOut->addHTML( "
{$prevnextbits} ($shm)

\n"); + $wgOut->addHTML( "