Patch by David Iberri
authorMagnus Manske <magnusmanske@users.mediawiki.org>
Thu, 7 Jul 2005 17:09:10 +0000 (17:09 +0000)
committerMagnus Manske <magnusmanske@users.mediawiki.org>
Thu, 7 Jul 2005 17:09:10 +0000 (17:09 +0000)
includes/SpecialValidate.php

index 343ae21..5766a31 100644 (file)
 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 # http://www.gnu.org/copyleft/gpl.html
 
-/**
- *
- * @package MediaWiki
- * @subpackage SpecialPage
- */
-
 /**
  *
  * @package MediaWiki
 class Validation {
        var $topicList;
        var $voteCache;
-       var $rev2date;
-       var $date2ref;
-
-       # Reads all revision information of the specified article
-       function prepareRevisions( $id ) {
-               global $wgDBprefix;
-               $this->rev2date = array();
-               $this->date2rev = array();
-               $sql = "SELECT * FROM {$wgDBprefix}revision WHERE rev_page='{$id}'";
-               $res = wfQuery( $sql, DB_READ );
-               while( $x = wfFetchObject( $res ) ) {
-                       $this->rev2date[$x->rev_id] = $x;
-                       $this->date2rev[$x->rev_timestamp] = $x;
-               }
+       var $page_id;
+
+       function getRevisionFromId( $rev_id ) {
+               if( isset( $this->id2rev[$rev_id] ) ) return $this->id2rev[$rev_id];
+
+               $db =& wfGetDB( DB_SLAVE );
+               $fname = 'SpecialValidate::getRevisionFromId';
+               $res = $db->select( 'revision', '*', array( 'rev_id' => $rev_id ), $fname, array( 'LIMIT' => 1 ) );
+               $rev = $db->fetchObject($res);
+               $db->freeResult($res);
+
+               $this->id2rev[$rev->rev_id] = $rev;
+               $this->ts2rev[$rev->rev_timestamp] = $rev;
+
+               return $rev;
+       }
+
+       function getRevisionFromTimestamp( $timestamp ) {
+               if( isset( $this->ts2rev[$timestamp] ) ) return $this->ts2rev[$timestamp];
+
+               $db =& wfGetDB( DB_SLAVE );
+               $fname = 'SpecialValidate::getRevisionFromTimestamp';
+               $res = $db->select( 'revision', '*',
+                       array( 'rev_page' => $this->page_id, 'rev_timestamp' => $timestamp ),
+                       $fname, array( 'LIMIT' => 1 )
+               );
+               $rev = $db->fetchObject($res);
+               $db->freeResult($res);
+
+               $this->id2rev[$rev->rev_id] = $rev;
+               $this->ts2rev[$rev->rev_timestamp] = $rev;
+
+               return $rev;
        }
 
        # Returns a HTML link to the specified article revision
-       function getVersionLink( &$article, $revision, $text = "" ) {
+       function getRevisionLink( &$article, $revision, $text = "" ) {
+               global $wgUser;
+               $sk = $wgUser->getSkin();
                $t = $article->getTitle();
                if( $text == "" ) $text = wfMsg("val_view_version");
-               $ret = "<a href=\"" . $t->escapeLocalURL( "oldid={$revision}" ) . "\">" . $this->getParsedWiki($text) . "</a>";
-               return $ret;
+               return $sk->makeKnownLinkObj( $t, $this->getParsedWiki($text), 'oldid='.urlencode($revision) );
        }
 
        # Returns an array containing all topics you can vote on
        function getTopicList() {
-               global $wgDBprefix;
-               $ret = array();
-               $sql = "SELECT * FROM {$wgDBprefix}validate WHERE val_page=0";
-               $res = wfQuery( $sql, DB_READ );
-               while( $x = wfFetchObject( $res ) ) {
-                       $ret[$x->val_type] = $x;
+               $db =& wfGetDB( DB_SLAVE );
+
+               $topics = array();
+               $res = $db->select( 'validate', '*', array( 'val_page' => 0 ), 'SpecialValidate::getTopicList' );
+               while( $topic = $db->fetchObject($res) ) {
+                       $topics[$topic->val_type] = $topic;
                }
-               ksort( $ret );
-               return $ret;
+               $db->freeResult($res);
+
+               ksort( $topics );
+               return $topics;
        }
        
        # Merges one dataset into another
@@ -95,7 +111,7 @@ class Validation {
                $tmp = $this->voteCache;
                krsort( $tmp );
                $update = false;
-               $ts = $this->getTimestamp( $revision );
+               $ts = $this->getRevisionTimestamp( $revision );
                $data = $this->voteCache[$ts];
                foreach( $tmp as $x => $y ) {
                        if( $x < $ts ) {
@@ -112,20 +128,20 @@ class Validation {
        # Clears all votes prior to the given revision
        function clearOldRevisions( &$article, $revision ) {
                $tmp = $this->voteCache;
-               $ts = $this->getTimestamp( $revision );
+               $ts = $this->getRevisionTimestamp( $revision );
                foreach( $tmp as $x => $y ) {
                        if( $x < $ts ) {
-                               $this->deleteRevision ( $article, $this->getRevisionNumber( $x ) );
+                               $this->deleteRevisionVote ( $article, $this->getRevisionId( $x ) );
                        }
                }
        }
        
        # Updates the votes for the given revision from the FORM data
        function updateRevision( &$article, $revision ) {
-               global $wgUser, $wgRequest;
+               global $wgRequest;
                
-               if( isset( $this->voteCache[$this->getTimestamp( $revision )] ) ) {
-                       $data = $this->voteCache[$this->getTimestamp( $revision )];
+               if( isset( $this->voteCache[$this->getRevisionTimestamp( $revision )] ) ) {
+                       $data = $this->voteCache[$this->getRevisionTimestamp( $revision )];
                } else {
                        $data = array();
                }
@@ -144,8 +160,8 @@ class Validation {
        # Sets a specific revision to both cache and database
        function setRevision( &$article, $revision, &$data ) {
                global $wgUser;
-               $this->deleteRevision( $article, $revision );
-               $this->voteCache[$this->getTimestamp( $revision )] = $data;
+               $this->deleteRevisionVote( $article, $revision );
+               $this->voteCache[ $this->getRevisionTimestamp($revision) ] = $data;
                foreach( $data as $x => $y ) {
                        if( $y->value > 0 ) {
                                $ip = $wgUser->isAnon() ? $wgUser->getName() : '';
@@ -159,153 +175,164 @@ class Validation {
                                                'val_value'    => $y->value,
                                                'val_comment'  => $y->comment,
                                                'val_ip'       => $ip ),
-                                       'Validation::setRevision' );
+                                       'SpecialValidate::setRevision'
+                               );
                        }
                }
        }
        
-       # This function returns a MySQL statement to identify the current user
-       function identifyMe( $user = "" ) {
+       # Returns a map identifying the current user
+       function identifyUser( $user = "" ) {
                global $wgUser;
-               if( $user == "" ) $user = $wgUser->GetID();
-               if( User::isIP( $user ) ) {
-                       return "(val_user='0' AND val_ip='{$user}')";
-               } else {
-                       return "(val_user='{$user}')";
-               }
+               if( $user == "" ) $user = $wgUser->getID();
+               return User::isIP($user)
+                       ? array( 'val_user' => 0, 'val_ip' => $user )
+                       : array( 'val_user' => $user );
        }
        
        # Deletes a specific vote set in both cache and database
-       function deleteRevision( &$article, $revision ) {
-               global $wgUser, $wgDBprefix;
-               $ts = $this->getTimestamp( $revision );
-               if( !isset ( $this->voteCache[$ts] ) ) {
-                       return; # Nothing to do
-               }
-               $sql = "DELETE FROM {$wgDBprefix}validate WHERE" . $this->identifyMe() . " AND ";
-               $sql .= " val_page='" . $article->getID() . "' AND val_revision='{$revision}'";
-               $res = wfQuery( $sql, DB_WRITE );
+       function deleteRevisionVote( &$article, $revision ) {
+               $ts = $this->getRevisionTimestamp( $revision );
+               if( !isset ( $this->voteCache[$ts] ) ) return;
+
+               $db =& wfGetDB( DB_MASTER );
+               $db->delete(
+                       'validate',
+                       array_merge(
+                               $this->identifyUser(),
+                               array(
+                                       'val_page' => $article->getID(),
+                                       'val_revision' => $revision
+                               )
+                       ),
+                       'SpecialValidate::deleteRevisionVote'
+               );
+
                unset( $this->voteCache[$ts] );
        }
        
        # Reads the entire vote list for this user for the given article
        function getVoteList( $id, $user = "" ) {
-               global $wgUser, $wgDBprefix;
-               if( $user == "" ) {
-                       $user = $wgUser->GetID();
-               }
-               $r = array() ; # Revisions
-               $sql = "SELECT * FROM {$wgDBprefix}validate WHERE val_page=" . $id . " AND " . $this->identifyMe( $user );
-               $res = wfQuery( $sql, DB_READ );
-               while( $x = wfFetchObject( $res ) ) {
-                       #$y = $x->val_revision;
-                       $y = $this->rev2date[$x->val_revision];
-                       $y = $y->rev_timestamp;
-                       if( !isset( $r[$y] ) ) {
-                               $r[$y] = array();
+               $db =& wfGetDB( DB_SLAVE );
+               $res = $db->select( 'validate', '*', array_merge( array( 'val_page' => $id ), $this->identifyUser($user) ) );
+
+               $revisions = array();
+               while( $vote = $db->fetchObject($res) ) {
+                       $ts = $this->getRevisionTimestamp( $vote->val_revision );
+                       if( ! isset( $revisions[$ts] ) ) {
+                               $revisions[$ts] = array();
                        }
-                       $r[$y][$x->val_type]->value = $x->val_value;
-                       $r[$y][$x->val_type]->comment = $x->val_comment;
-               }               
-               return $r;
+                       $revisions[$ts][$vote->val_type]->value = $vote->val_value;
+                       $revisions[$ts][$vote->val_type]->comment = $vote->val_comment;
+               }
+               $db->freeResult($res);
+
+               return $revisions;
        }
        
        # Reads the entire vote list for this user for all articles
+       # XXX Should be paged
        function getAllVoteLists( $user ) {
-               global $wgDBprefix;
-               $r = array() ; # Revisions
-               $sql = "SELECT * FROM {$wgDBprefix}validate WHERE " . $this->identifyMe( $user );
-               $res = wfQuery( $sql, DB_READ );
-               while( $x = wfFetchObject( $res ) ) {
-                       $a = $x->val_page;
-                       $y = $x->val_revision;
-                       if( !isset( $r[$a] ) ) {
-                               $r[$a] = array();
-                       }
-                       if( !isset( $r[$a][$y] ) ) {
-                               $r[$a][$y] = array();
-                       }
-                       $r[$a][$y][$x->val_type] = $x;
-               }               
-               return $r;
+               $db =& wfGetDB( DB_SLAVE );
+               $res = $db->select( 'validate', '*', $this->identifyUser($user) );
+
+               $votes = array();
+               while( $vote = $db->fetchObject($res) ) {
+                       $votes[$vote->val_page][$vote->val_revision][$vote->val_type] = $vote;
+               }
+               $db->freeResult($res);
+
+               return $votes;
        }
        
        # This functions adds a topic to the database
        function addTopic( $topic, $limit ) {
-               global $wgDBprefix;
-               $a = 1;
-               while( isset( $this->topicList[$a] ) ) {
-                       $a++;
-               }
-               $sql = "INSERT INTO {$wgDBprefix}validate (val_user,val_page,val_revision,val_type,val_value,val_comment,val_ip) VALUES (";
-               $sql .= "'0','0','0','{$a}','{$limit}','";
-               $sql .= Database::strencode( $topic ) . "','')";
-               $res = wfQuery( $sql, DB_WRITE );
-               $x->val_user = $x->val_page = $x->val_revision = 0;
-               $x->val_type = $a;
-               $x->val_value = $limit;
-               $x->val_comment = $topic;
-               $x->val_ip = "";
-               $this->topicList[$a] = $x;
+               $db =& wfGetDB( DB_MASTER );
+
+               $next_idx = 1;
+               while( isset( $this->topicList[$next_idx] ) ) {
+                      $next_idx++;
+               }
+
+               $db->insert(
+                       'validate',
+                       array(
+                               'val_user' => 0,
+                               'val_page' => 0,
+                               'val_revision' => 0,
+                               'val_type' => $next_idx,
+                               'val_value' => $limit,
+                               'val_comment' => $topic,
+                               'val_ip' => ''
+                       ),
+                       'SpecialValidate::addTopic'
+               );
+
+               $t->val_user = $t->val_page = $t->val_revision = 0;
+               $t->val_type = $next_idx;
+               $t->val_value = $limit;
+               $t->val_comment = $topic;
+               $t->val_ip = "";
+               $this->topicList[$next_idx] = $t;
+
                ksort( $this->topicList );
        }
 
-       # This functions adds a topic to the database
        function deleteTopic( $id ) {
-               global $wgDBprefix;
-               $dbw =& wfGetDB( DB_MASTER );
-               $dbw->delete( 'validate',
-                       array( 'val_type' => $id ),
-                       'Validation::deleteTopic' );
+               $db =& wfGetDB( DB_MASTER );
+               $db->delete( 'validate', array( 'val_type' => $id ), 'SpecialValidate::deleteTopic' );
                unset( $this->topicList[$id] );
        }
        
        # This function returns a link text to the page validation statistics
-       function link2statistics( &$article ) {
+       function getStatisticsLink( &$article ) {
+               global $wgUser;
+               $sk = $wgUser->getSkin();
                $nt = $article->getTitle();
-               $url = $nt->escapeLocalURL( "action=validate&mode=list" );
-               return wfMsg( 'val_rev_stats_link', $nt->getPrefixedText(), $url );
+               return $sk->makeKnownLinkObj( $nt, wfMsg( 'val_rev_stats', $nt->getPrefixedText() ), 'action=validate&mode=list' );
        }
 
        # This function returns a link text to the page validation statistics of a single revision
-       function link2revisionstatistics( &$article, $revision ) {
+       function getRevisionStatsLink( &$article, $revision ) {
+               global $wgUser;
+               $sk = $wgUser->getSkin();
                $nt = $article->getTitle();
-               $url = $nt->escapeLocalURL( "action=validate&mode=details&revision={$revision}" );
-               return "(<a href=\"{$url}\">" . $this->getParsedWiki( wfMsg('val_revision_stats_link') ) . '</a>)' ;
+               $text = $this->getParsedWiki( wfMsg('val_revision_stats_link') );
+               $query = "action=validate&mode=details&revision={$revision}";
+               return '(' . $sk->makeKnownLinkObj( $nt, $text, $query ) . ')';
        }
 
        # This function returns a link text to the user rating statistics page
-       function link2userratings( $user, $text ) {
+       function getUserRatingsLink( $user, $text ) {
                global $wgUser;
-               if( $user == 0 ) {
-                       $user = $wgUser->GetName();
-               }
-               $nt = Title::newFromText( "Special:Validate" );
-               $url = $nt->escapeLocalURL( "mode=userstats&user=" . urlencode( $user ) );
-               return "<a href=\"{$url}\">{$text}</a>";
+               $sk = $wgUser->getSkin();
+               if( $user == 0 ) $user = $wgUser->getName();
+               $nt = Title::newFromText( 'Special:Validate' );
+               return $sk->makeKnownLinkObj( $nt, $text, 'mode=userstats&user='.urlencode($user) );
        }
 
        # Returns the timestamp of a revision based on the revision number
-       function getTimestamp( $revision ) {
-               $ts = $this->rev2date[$revision];
-               $ts = $ts->rev_timestamp;
-               return $ts;
+       function getRevisionTimestamp( $rev_id ) {
+               $rev = $this->getRevisionFromId( $rev_id );
+               return $rev->rev_timestamp;
        }
 
        # Returns the revision number of a revision based on the timestamp
-       function getRevisionNumber( $ts ) {
-               $revision = $this->date2rev[$ts];
-               $revision = $revision->rev_id;
-               return $revision;
+       function getRevisionId( $ts ) {
+               $rev = $this->getRevisionFromTimestamp( $ts );
+               return $rev->rev_id;
        }
 
 
        # HTML generation functions from this point on
        
        # Returns the metadata string for a revision
-       function getMetadata( $idx ) {
+       function getMetadata( $rev_id, &$article ) {
+               global $wgUser;
+               $sk = $wgUser->getSkin();
+
                $metadata = "";
-               $x = $this->rev2date[$idx];
+               $x = $this->getRevisionFromId($rev_id);
                $metadata .= wfTimestamp( TS_DB, $x->rev_timestamp );
                $metadata .= " by ";
                if( $x->rev_user == 0 ) {
@@ -315,19 +342,16 @@ class Validation {
                        $u->setId( $x->rev_user );
                        $u->setName( $x->rev_user_text );
                        $nt = $u->getUserPage();
-                       # FIXME: Why doesn't this use standard linking code?
-                       $url = "<a href='" . $nt->escapeLocalUrl() . "'>" . htmlspecialchars( $nt->getText() ) . "</a>";
-                       $metadata .= $url;
+                       $metadata .= $sk->makeKnownLinkObj( $nt, htmlspecialchars( $nt->getText() ) );
                }
-               # FIXME: Why doesn't this use standard comment formatting?
-               $metadata .= " : <small>\"" . $this->getParsedWiki( $x->rev_comment ) . "\"</small>";
+               $metadata .= ': '. $sk->commentBlock( $x->rev_comment, $article->getTitle() );
                return $metadata;
        }
        
        # Generates a link to the topic description
-       function linkTopic ( $s ) {
-               # FIXME: Why doesn't this use standard linking code?
+       function getTopicLink($s) {
                $t = Title::newFromText ( wfMsg ( 'val_topic_desc_page' ) ) ;
+               # FIXME: Why doesn't this use standard linking code?
                $r = "<a href=\"" ;
                $r .= $t->escapeLocalURL () ;
                $r .= "#" . urlencode ( $s ) ;
@@ -337,7 +361,7 @@ class Validation {
                
        # Generates HTML from a wiki text, e.g., a wfMsg
        function getParsedWiki ( $text ) {
-               global $wgOut , $wgTitle, $wgParser ;
+               global $wgOut, $wgTitle, $wgParser ;
                $parserOutput = $wgParser->parse( $text , $wgTitle, $wgOut->mParserOptions,false);
                return $parserOutput->getText() ;
        }
@@ -346,7 +370,7 @@ class Validation {
        function getRevisionForm( &$article, $idx, &$data, $focus = false ) {
                # Fill data with blank values
                $ts = $idx;
-               $revision = $this->getRevisionNumber( $ts );
+               $revision = $this->getRevisionId( $ts );
                foreach( $this->topicList as $x => $y ) {
                        if( !isset( $data[$x] ) ) {
                                $data[$x]->value = 0;
@@ -356,23 +380,20 @@ class Validation {
                ksort( $data ) ;                
        
                # Generate form
-               $ret = "<form method='post'>";
-               $ret .= "<table cellspacing='0' cellpadding='2' class='";
-               if( $focus ) $ret .= "revisionform_focus" ;
-               else $ret .= "revisionform_default" ;
-               $ret .= "'>\n";
+               $table_class = $focus ? 'revisionform_focus' : 'revisionform_default';
+               $ret = "<form method='post'><table class='{$table_class}'>\n";
                $head = "Revision #" . $revision;
-               $link = " " . $this->getVersionLink( $article, $revision );
-               $metadata = $this->getMetadata( $revision );
-               $ret .= "<tr><th align='left' colspan='3'>" . $head . " ({$link}) {$metadata}</th></tr>\n";
+               $link = $this->getRevisionLink( $article, $revision );
+               $metadata = $this->getMetadata( $revision, $article );
+               $ret .= "<tr><th colspan='3'>" . $head . " ({$link}) {$metadata}</th></tr>\n";
                $line = 0;
                foreach( $data as $x => $y ) {
                        $line = 1 - $line;
                        $trclass = $line == 1 ? "revision_tr_first" : "revision_tr_default";
                        $idx = "_{$revision}[{$x}]";
                        $ret .= "<tr class='{$trclass}'>\n";
-                       $ret .= "<th nowrap>";
-                       $ret .= $this->linkTopic ( $this->topicList[$x]->val_comment ) ;
+                       $ret .= "<th>\n";
+                       $ret .= $this->getTopicLink ( $this->topicList[$x]->val_comment ) ;
                        $ret .= "</th>\n";
                        
                        $tlx = $this->topicList[$x];
@@ -398,22 +419,20 @@ class Validation {
                                        $vote .= " &nbsp; ";
                                }
                        }                       
-                       $ret .= "<td nowrap valign='center'>{$vote}</td>\n";
+                       $ret .= "<td>{$vote}</td>\n";
                        
-                       $ret .= "<td width='100%' align='center'><input size='50' style='width:98%' maxlength='250' type='text' name='re_c{$idx}' value='{$y->comment}'/>";
+                       $ret .= "<td><input size='50' style='width:98%' maxlength='250' type='text' name='re_c{$idx}' value='{$y->comment}'/>";
                        $ret .= "</td></tr>\n";
                }
                $checked = $focus ? " checked='checked'" : "";
-               $ret .= "<tr><td colspan='3' valign='center'>\n";
+               $ret .= "<tr><td colspan='3'>\n";
                $ret .= "<input type='checkbox' name='re_merge_{$revision}' value='1'{$checked} />" . $this->getParsedWiki( wfMsg( 'val_merge_old' ) ) . " \n";
                $ret .= "<input type='checkbox' name='re_clear_{$revision}' value='1'{$checked} />" . $this->getParsedWiki( wfMsg( 'val_clear_old' ) ) . " \n";
                $ret .= "<input type='submit' name='re_submit[{$revision}]' value=\"" . wfMsgHtml( "ok" ) . "\" />\n";
                
-               if( $focus ) {
-                       $ret .= "<br/>\n<small>" . $this->getParsedWiki ( wfMsg( "val_form_note" ) ) . "</small>";
-               }
+               if( $focus ) $ret .= "<br/>\n<small>" . $this->getParsedWiki ( wfMsg( "val_form_note" ) ) . "</small>";
                $ret .= "</td></tr>\n";
-               $ret .= "</table>\n</form>\n\n";
+               $ret .= "</table></form>\n\n";
                return $ret;
        }
        
@@ -423,7 +442,7 @@ class Validation {
                global $wgOut, $wgRequest, $wgUser;
                
                $ret = "";
-               $this->prepareRevisions( $article->getID() );
+               $this->page_id = $article->getID();
                $this->topicList = $this->getTopicList();
                $this->voteCache = $this->getVoteList( $article->getID() );
                
@@ -441,12 +460,14 @@ class Validation {
                        if( $clearOldRev ) {
                                $this->clearOldRevisions( $article, $id );
                        }
-                       $ret .= "<p class='revision_saved'>" . $this->getParsedWiki( wfMsg( 'val_revision_changes_ok' ) ) . "</p>";
+                       $ret .= '<p class="revision_saved">' . $this->getParsedWiki( wfMsg( 'val_revision_changes_ok' ) ) . "</p>";
+               } else {
+                       $ret .= $this->getParsedWiki( wfMsg ('val_votepage_intro') );
                }
-               else $ret .= wfMsgHtml ( 'val_votepage_intro' ) ;
                
                # Make sure the requested revision exists
-               $ts = $this->rev2date[$revision]->rev_timestamp;
+               $rev = $this->getRevisionFromId($revision);
+               $ts = $rev->rev_timestamp;
                if( !isset( $this->voteCache[$ts] ) ) {
                        $this->voteCache[$ts] = array();
                }
@@ -462,8 +483,8 @@ class Validation {
                        $ret .= $this->getRevisionForm( $article, $x, $y, $x == $ts );
                        $ret .= "<br/>\n";
                }
-               $ret .= $this->link2statistics( $article );
-               $ret .= "<p>" . $this->link2userratings( $wgUser->getID(), wfMsg( 'val_show_my_ratings' ) ) . "</p>";
+               $ret .= $this->getStatisticsLink( $article );
+               $ret .= "<p>" . $this->getUserRatingsLink( $wgUser->getID(), wfMsg( 'val_show_my_ratings' ) ) . "</p>";
                return $ret ;   
        }
        
@@ -491,96 +512,93 @@ class Validation {
                
                # FIXME: Wikitext this
                $r = "<p>" . $this->getParsedWiki( wfMsg( 'val_warning' ) ) . "</p>\n";
-               
                $r .= "<form method='post'>\n";
-               $r .= "<table border='1' cellspacing='0' cellpadding='2'>\n";
+               $r .= "<table>\n";
                $r .= "<tr>" . wfMsg( 'val_list_header' ) . "</tr>\n";
                foreach( $this->topicList as $x => $y ) {
                        $r .= "<tr>\n";
-                       $r .= "<th>" . $this->getParsedWiki( $y->val_type ) . "</th>\n";
-                       $r .= "<td>" . $this->linkTopic ( $y->val_comment ) . "</td>\n";
+                       $r .= "<th>{$y->val_type}</th>\n";
+                       $r .= "<td>" . $this->getTopicLink ( $y->val_comment ) . "</td>\n";
                        $r .= "<td>1 .. <b>" . intval( $y->val_value ) . "</b></td>\n";
                        $r .= "<td><input type='submit' name='m_del[" . intval( $x ) . "]' value='" . htmlspecialchars( wfMsg( 'val_del' ) ) . "'/></td>\n";
                        $r .= "</tr>\n";
                }
                $r .= "<tr>\n";
-               $r .= "<td/>\n";
-               $r .= "<td><input type='text' name='m_topic' value=''/></td>\n";
-               $r .= "<td><input type='text' name='m_limit' value=''/></td>\n";
-               $r .= "<td><input type='submit' name='m_add' value='" . htmlspecialchars( wfMsg( 'val_add' ) ) . "'/></td>\n";
-               $r .= "</tr>\n";
-               $r .= "</table>\n";
-               $r .= "<input type='checkbox' name='iamsure' value='1'/>" . $this->getParsedWiki( wfMsg( 'val_iamsure' ) ) . "\n";
+               $r .= "<td></td>\n";
+               $r .= '<td><input type="text" name="m_topic" value=""/></td>' . "\n";
+               $r .= '<td>1 .. <input type="text" name="m_limit" value="" size="4"/></td>' . "\n";
+               $r .= '<td><input type="submit" name="m_add" value="' . htmlspecialchars( wfMsg( 'val_add' ) ) . '"/></td>' . "\n";
+               $r .= "</tr></table>\n";
+               $r .= '<p><input type="checkbox" name="iamsure" id="iamsure" value="1"/>';
+               $r .= '<label for="iamsure">' . $this->getParsedWiki( wfMsg( 'val_iamsure' ) ) . "</label></p>\n";
                $r .= "</form>\n";
                return $r;
        }
        
-       # Generates a user ID for both logged-in users and anons; $x is an object from an SQL query
-       function make_user_id( &$x ) {
-               if( $x->val_user == 0 ) {
-                       return $x->val_ip;
-               } else {
-                       return $x->val_user;
-               }
+       # Generates an ID for both logged-in users and anons; $res is an object from an SQL query
+       function make_user_id( &$res ) {
+               return $res->val_user == 0 ? $res->val_ip : $res->val_user;
        }
 
        function showDetails( &$article, $revision ) {
-               global $wgDBprefix, $wgOut, $wgUser;
-               $this->prepareRevisions( $article->getID() );
+               global $wgOut, $wgUser;
+               $this->page_id = $article->getID();
                $this->topicList = $this->getTopicList();
 
+               $sk = $wgUser->getSkin();
                $title = $article->getTitle();
                $wgOut->setPageTitle( str_replace( '$1', $title->getPrefixedText(), wfMsg( 'val_validation_of' ) ) );
                
-               # Collecting statistic data
-               $id = $article->getID();
-               $sql = "SELECT * FROM {$wgDBprefix}validate WHERE val_page='{$id}' AND val_revision='{$revision}'";
-               $res = wfQuery( $sql, DB_READ );
                $data = array();
                $users = array();
                $topics = array();
-               while( $x = wfFetchObject( $res ) ) {
+
+               # Collecting statistic data
+               $db =& wfGetDB( DB_SLAVE );
+               $res = $db->select( 'validate', '*', array( 'val_page' => $this->page_id, val_revision => $revision ), 'SpecialValidate::showDetails' );
+               while( $x = $db->fetchObject($res) ) {
                        $data[$this->make_user_id($x)][$x->val_type] = $x;
                        $users[$this->make_user_id($x)] = true;
                        $topics[$x->val_type] = true;
                }
+               $db->freeResult($res);
                
                # Sorting lists of topics and users
                ksort( $users );
                ksort( $topics );
                
-               $ts = $this->getTimestamp( $revision );
-               $url = $this->getVersionLink( $article, $revision, wfTimestamp( TS_DB, $ts ) );
+               $ts = $this->getRevisionTimestamp( $revision );
+               $url = $this->getRevisionLink( $article, $revision, wfTimestamp( TS_DB, $ts ) );
 
                # Table headers
                $ret = "" ;                     
                $ret .= "<p><b>" . str_replace( '$1', $url, wfMsg( 'val_revision_of' ) ) . "</b></p>\n";
-               $ret .= "<table border='1' cellspacing='0' cellpadding='2'>\n";
+               $ret .= "<table>\n";
                $ret .= "<tr><th>" . $this->getParsedWiki ( wfMsg('val_details_th') ) . "</th>" ;
                
                foreach( $topics as $t => $dummy ) {
-                       $ret .= "<th>" . $this->linkTopic ( $this->topicList[$t]->val_comment ) . "</th>";
+                       $ret .= '<th>' . $sk->commentBlock( $this->topicList[$t]->val_comment, $article->getTitle() ) . '</th>';
                }
                $ret .= "</tr>\n";
 
                # Table data
                foreach( $users as $u => $dummy ) { # Every row a user
                        $ret .= "<tr>";
-                       $ret .= "<th align='left'>";
+                       $ret .= "<th>";
                        if( !User::IsIP( $u ) ) { # Logged-in user rating
-                               $ret .= $this->link2userratings( $u, User::whoIs( $u ) );
+                               $ret .= $this->getUserRatingsLink( $u, User::whoIs( $u ) );
                        } else { # Anon rating
-                               $ret .= $this->link2userratings( $u, $u );
+                               $ret .= $this->getUserRatingsLink( $u, $u );
                        }
                        $ret .= "</th>";
                        foreach( $topics as $t => $dummy ) { # Every column a topic
                                if( !isset( $data[$u][$t] ) ) {
                                        $ret .= "<td/>";
                                } else {
-                                       $ret .= "<td valign='center'>";
+                                       $ret .= "<td>";
                                        $ret .= $data[$u][$t]->val_value;
                                        if( $data[$u][$t]->val_comment != "" ) {
-                                               $ret .= " <small>(" . $this->getParsedWiki( $data[$u][$t]->val_comment ) . ")</small>";
+                                               $ret .= ' ' . $sk->commentBlock( $data[$u][$t]->val_comment, $article->getTitle() );
                                        }
                                        $ret .= "</td>";
                                }
@@ -588,61 +606,52 @@ class Validation {
                        $ret .= "</tr>";
                }
                $ret .= "</table>";
-               $ret .= "<p>" . $this->link2statistics( $article ) . "</p>";
-               $ret .= "<p>" . $this->link2userratings( $wgUser->GetID(), wfMsg( 'val_show_my_ratings' ) ) . "</p>";
+               $ret .= "<p>" . $this->getStatisticsLink( $article ) . "</p>";
+               $ret .= "<p>" . $this->getUserRatingsLink( $wgUser->getID(), wfMsg( 'val_show_my_ratings' ) ) . "</p>";
                
                return $ret;
        }
        
+       # XXX This should be paged
        function showList( &$article ) {
-               global $wgDBprefix, $wgOut, $wgUser;
-               $this->prepareRevisions( $article->getID() );
+               global $wgOut, $wgUser;
+               $this->page_id = $article->getID();
                $this->topicList = $this->getTopicList();
 
                $title = $article->getTitle();
                $wgOut->setPageTitle( str_replace( '$1', $title->getPrefixedText(), wfMsg( 'val_validation_of' ) ) );
                
                # Collecting statistic data
-               $id = $article->getID();
-               $sql = "SELECT * FROM {$wgDBprefix}validate WHERE val_page='{$id}'";
-               $res = wfQuery( $sql, DB_READ );
-               $data = array();
-               while( $x = wfFetchObject( $res ) ) {
-                       $idx = $this->getTimestamp( $x->val_revision );
-                       if( !isset( $data[$idx] ) ) {
-                               $data[$idx] = array();
-                       }
-                       if( !isset( $data[$idx][$x->val_type] ) ) {
-                               $data[$idx][$x->val_type]->count = 0;
-                               $data[$idx][$x->val_type]->sum = 0;
-                       }
-                       $data[$idx][$x->val_type]->count++;
-                       $data[$idx][$x->val_type]->sum += $x->val_value;
+               $db =& wfGetDB( DB_SLAVE );
+               $res = $db->select( 'validate', '*', array( val_page => $this->page_id ), 'SpecialValidate::showList' );
+
+               $statistics = array();
+               while( $vote = $db->fetchObject($res) ) {
+                       $ts = $this->getRevisionTimestamp($vote->val_revision);
+                       $statistics[$ts][$vote->val_type]->count++;
+                       $statistics[$ts][$vote->val_type]->sum += $vote->val_value;
                }
+               $db->freeResult($res);
+
+               krsort( $statistics );
                
-               krsort( $data );
-               
-               $ret = "";
-               $ret .= "<table border='1' cellspacing='0' cellpadding='2'>\n";
-               $ret .= "<tr><th>" . $this->getParsedWiki( wfMsg( "val_revision" ) ) . "</th>";
-               foreach( $this->topicList as $x => $y ) {
-                       $ret .= "<th>" . $this->linkTopic ( $y->val_comment ) . "</th>";
+               $ret = "<table><tr>\n";
+               $ret .= "<th>" . $this->getParsedWiki( wfMsg( "val_revision" ) ) . "</th>\n";
+               foreach( $this->topicList as $topic ) {
+                       $ret .= "<th>" . $this->getTopicLink($topic->val_comment) . "</th>";
                }
                $ret .= "</tr>\n";
-               foreach( $data as $ts => $y ) {
-                       $revision = $this->getRevisionNumber( $ts );
-                       $url = $this->getVersionLink( $article, $revision, wfTimestamp( TS_DB, $ts ) );
-                       $detailsurl = $this->link2revisionstatistics( $article, $revision );
-                       $ret .= "<tr><td>{$url} {$detailsurl}</td>";
-                       foreach( $this->topicList as $topicID => $dummy ) {
-                               if( isset( $y[$topicID] ) ) {
-                                       $z = $y[$topicID];
-                                       if( $z->count == 0 ) {
-                                               $a = 0;
-                                       } else {
-                                               $a = $z->sum / $z->count;
-                                       }
-                                       $ret .= sprintf( "<td><b>%1.1f</b> (%d)</td>", $a, $z->count );
+
+               foreach( $statistics as $ts => $data ) {
+                       $rev_id = $this->getRevisionId( $ts );
+                       $revision_link = $this->getRevisionLink( $article, $rev_id, wfTimestamp( TS_DB, $ts ) );
+                       $details_link = $this->getRevisionStatsLink( $article, $rev_id );
+                       $ret .= "<tr><td>{$revision_link} {$details_link}</td>";
+                       foreach( $this->topicList as $topicType => $topic ) {
+                               if( isset( $data[$topicType] ) ) {
+                                       $stats = $data[$topicType];
+                                       $average = $stats->count == 0 ? 0 : $stats->sum / $stats->count;
+                                       $ret .= sprintf( "<td><b>%1.1f</b> (%d)</td>", $average, $stats->count );
                                } else {
                                        $ret .= "<td></td>";
                                }
@@ -650,7 +659,7 @@ class Validation {
                        $ret .= "</tr>\n";
                }
                $ret .= "</table>\n";
-               $ret .= "<p>" . $this->link2userratings( $wgUser->getID(), wfMsg( 'val_show_my_ratings' ) ) . "</p>";
+               $ret .= "<p>" . $this->getUserRatingsLink( $wgUser->getID(), wfMsg( 'val_show_my_ratings' ) ) . "</p>";
                return $ret;
        }
        
@@ -667,10 +676,12 @@ class Validation {
                return $ret;
        }
 
+       # XXX This should be paged
        function showUserStats( $user ) {
-               global $wgDBprefix, $wgOut, $wgUser;
+               global $wgOut, $wgUser;
                $this->topicList = $this->getTopicList();
                $data = $this->getAllVoteLists( $user );
+               $sk = $wgUser->getSkin();
                
                if( $user == $wgUser->getID() ) {
                        $wgOut->setPageTitle ( wfMsg ( 'val_my_stats_title' ) );
@@ -680,15 +691,18 @@ class Validation {
                        $wgOut->setPageTitle( wfMsg( 'val_user_stats_title', $user ) );
                }
                
-               $ret = "<table border='1' cellspacing='0' cellpadding='2'>\n";
-               
+               $ret = "<table>\n";
                foreach( $data as $articleid => $revisions ) {
                        $title = Title::newFromID( $articleid );
-                       $ret .= "<tr><th align='left' colspan='4'><a href=\"" . $title->escapeLocalURL() . "\">" . $title->getEscapedText() . "</a></th></tr>";
+                       $ret .= "<tr><th colspan='4'>";
+                       $ret .= $sk->makeKnownLinkObj( $title, $title->getEscapedText() );
+                       $ret .= "</th></tr>";
                        krsort( $revisions );
                        foreach( $revisions as $revid => $revision ) {
                                $url = $title->getLocalURL( "oldid={$revid}" );
-                               $ret .= "<tr><th align='left'><a href=\"{$url}\">" . wfMsg( 'val_revision_number', $revid ) . "</a></th>";
+                               $ret .= "<tr><th>";
+                               $ret .= $sk->makeKnownLinkObj( $title, wfMsg('val_revision_number', $revid ), "oldid={$revid}" );
+                               $ret .= "</th>";
                                ksort( $revision );
                                $initial = true;
                                foreach( $revision as $topic => $rating ) {
@@ -696,9 +710,9 @@ class Validation {
                                                $ret .= "<tr><td/>";
                                        }
                                        $initial = false;
-                                       $ret .= "<td>" . $this->linkTopic ( $this->topicList[$topic]->val_comment ) . "</td>";
+                                       $ret .= "<td>" . $this->getTopicLink ( $this->topicList[$topic]->val_comment ) . "</td>";
                                        $ret .= "<td>" . $this->getRatingText( $rating->val_value, $this->topicList[$topic]->val_value ) . "</td>";
-                                       $ret .= "<td>" . $this->getParsedWiki( $rating->val_comment ) . "</td>";
+                                       $ret .= "<td>" . $sk->commentBlock( $rating->val_comment ) . "</td>";
                                        $ret .= "</tr>";
                                }
                        }