X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FSpecialValidate.php;h=f58bf5e4a17dea4e377be7541a52743c5736742f;hb=39bb950f401807a71cb9e9b3d5f91d71876178bc;hp=944c18b01c36a9a8777b64da13059a7a9cbdf74e;hpb=fbfb479b19be8c92e069aec19115fe61986a3ec7;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/SpecialValidate.php b/includes/SpecialValidate.php index 944c18b01c..f58bf5e4a1 100644 --- a/includes/SpecialValidate.php +++ b/includes/SpecialValidate.php @@ -17,520 +17,840 @@ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # http://www.gnu.org/copyleft/gpl.html +/** + * + * @package MediaWiki + * @subpackage SpecialPage + */ + + class Validation { + var $topicList; + var $voteCache; + var $page_id; + var $rev_fields = "rev_id,rev_page,rev_timestamp,rev_user_text,rev_user,rev_comment" ; + + 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', $this->rev_fields, 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', $this->rev_fields, + 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 getRevisionLink( &$article, $revision, $text = "" ) { + global $wgUser; + $sk = $wgUser->getSkin(); + $t = $article->getTitle(); + if( $text == "" ) $text = wfMsg("val_view_version"); + return $sk->makeKnownLinkObj( $t, $this->getParsedWiki($text), 'oldid='.urlencode($revision) ); + } + + # Returns an array containing all topics you can vote on + function getTopicList() { + $db =& wfGetDB( DB_SLAVE ); + + $topics = array(); + + # NOTE : This query returns only the topics to vote on + $res = $db->select( 'validate', '*', array( 'val_page' => 0 ), 'SpecialValidate::getTopicList' ); + while( $topic = $db->fetchObject($res) ) { + $topics[$topic->val_type] = $topic; + } + $db->freeResult($res); + + ksort( $topics ); + return $topics; + } - function find_this_version( $article_title , &$article_time , &$id , &$tab ) { - $id = ""; - $tab = ""; - $sql = "SELECT cur_id,cur_timestamp FROM cur WHERE cur_namespace=0 AND cur_title='" . wfStrencode( $article_title ) . "'"; - $res = wfQuery( $sql, DB_READ ); - if( $s = wfFetchObject( $res ) ) { - if ( $article_time == "" ) { - # No timestamp = current version - $article_time = $s->cur_timestamp; - } elseif ( $article_time == $s->cur_timestamp ) { - # Means current version - $tab = "cur"; - $id = $s->cur_id; + # Merges one dataset into another + function mergeInto( &$source, &$dest ) { + $ret = false; + foreach( $source as $x => $y ) { + $doit = false; + if( !isset( $dest[$x] ) ) { + $doit = true; + } elseif( $dest[$x]->value == 0 ) { + $doit = true; } - } - - if ( $id == "" ) { - $sql = "SELECT old_id FROM old WHERE old_namespace=0 AND old_title='" . wfStrencode( $article_title ) . - "' AND old_timestamp='" . wfStrencode( $article_time ) . "'"; - $res = wfQuery( $sql, DB_READ ); - if( $s = wfFetchObject( $res ) ) { - $tab = "old"; - $id = $s->old_id; + if( $doit ) { + $dest[$x] = $y; + $ret = true; } } + if( $ret ) { + ksort ( $dest ); + } + return $ret; } - function get_prev_data( $user_id , $article_title , $article_timestamp = "" ) { - $ret = array (); - $sql = "SELECT * FROM validate WHERE val_user='" . wfStrencode( $user_id ) . - "' AND val_title='" . wfStrencode( $article_title ) . "'"; - if ( $article_timestamp != "" ) { - $sql .= " AND val_timestamp='" . wfStrencode( $article_timestamp ) . "'"; + # Merges all votes prior to the given revision into it + function mergeOldRevisions( &$article, $revision ) { + $tmp = $this->voteCache; + krsort( $tmp ); + $update = false; + $ts = $this->getRevisionTimestamp( $revision ); + $data = $this->voteCache[$ts]; + foreach( $tmp as $x => $y ) { + if( $x < $ts ) { + if( $this->mergeInto( $y, $data ) ) { + $update = true; + } + } } - $res = wfQuery( $sql, DB_READ ); - while( $s = wfFetchObject( $res ) ) { - $ret[$s->val_timestamp][$s->val_type] = $s; + if( $update ) { + $this->setRevision( $article, $revision, $data ); } - return $ret; } - - function validate_form( $article_title = "" ) { - global $wgOut, $wgLang, $wgUser, $wgArticle, $wgRequest; - - if ( $wgUser->getID() == 0 ) { - # Anon - $wgOut->addHTML( htmlspecialchars( wfMsg( 'val_no_anon_validation' ) ) . - $this->getPageStatistics ( $article_title ) ) ; - return; + + # Clears all votes prior to the given revision + function clearOldRevisions( &$article, $revision ) { + $tmp = $this->voteCache; + $ts = $this->getRevisionTimestamp( $revision ); + foreach( $tmp as $x => $y ) { + if( $x < $ts ) { + $this->deleteRevisionVote ( $article, $this->getRevisionId( $x ) ); + } } - - $validationtypes = $wgLang->getValidationTypes(); - if ( $article_title == "" ) { - $article_title = $wgRequest->getVal( 'article_title' ); - $heading = "

" . htmlspecialchars( $article_title ) . "

\n"; + } + + # Updates the votes for the given revision from the FORM data + function updateRevision( &$article, $revision ) { + global $wgRequest; + + if( isset( $this->voteCache[$this->getRevisionTimestamp( $revision )] ) ) { + $data = $this->voteCache[$this->getRevisionTimestamp( $revision )]; } else { - $heading = ""; - } - $article_time = ""; - $article_time = $wgRequest->getVal( 'timestamp' ); - $article = Title::newFromText( $article_title ); - if( is_null( $article ) ) { - $wgOut->errorpage( "badtitle", "badtitletext" ); - return; - } - - # Now we get all the "votes" for the different versions of this article for this user - $val = $this->get_prev_data( $wgUser->getID() , $article_title ); - - # No votes for this version, initial data - if( !isset( $val[$article_time] ) ) { - if( $article_time == "" ) { - $res = wfQuery( "select cur_timestamp FROM cur WHERE cur_title='" . - wfStrencode( $article_title ) . "' AND cur_namespace=0", DB_READ ); - if( $s = wfFetchObject( $res ) ) { - $article_time = $s->cur_timestamp; - } + $data = array(); + } + $nv = $wgRequest->getArray( "re_v_{$revision}", array() ); + $nc = $wgRequest->getArray( "re_c_{$revision}", array() ); + + foreach( $nv as $x => $y ) { + $data[$x]->value = $y; + $data[$x]->comment = $nc[$x]; + } + krsort( $data ); + + $this->setRevision( $article, $revision, $data ); + } + + # Sets a specific revision to both cache and database + function setRevision( &$article, $revision, &$data ) { + global $wgUser; + $this->deleteRevisionVote( $article, $revision ); + $this->voteCache[ $this->getRevisionTimestamp($revision) ] = $data; + foreach( $data as $x => $y ) { + if( $y->value > 0 ) { + $ip = $wgUser->isAnon() ? $wgUser->getName() : ''; + $dbw =& wfGetDB( DB_MASTER ); + $dbw->insert( 'validate', + array( + 'val_user' => $wgUser->getId(), + 'val_page' => $article->getId(), + 'val_revision' => $revision, + 'val_type' => $x, + 'val_value' => $y->value, + 'val_comment' => $y->comment, + 'val_ip' => $ip ), + 'SpecialValidate::setRevision' + ); + } + } + } + + # Returns a map identifying the current user + function identifyUser( $user = "" ) { + global $wgUser; + 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 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 = "" ) { + $db =& wfGetDB( DB_SLAVE ); + + # NOTE : This query gets the votes for a single user on a single page. + # Assuming most people will use the "merge" feature, + # this will be only a single entry. + $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(); } - $val[$article_time] = array(); + $revisions[$ts][$vote->val_type]->value = $vote->val_value; + $revisions[$ts][$vote->val_type]->comment = $vote->val_comment; } + $db->freeResult($res); - # Newest versions first - krsort( $val ); + return $revisions; + } + + # Reads a partial vote list for this user for all articles + function getAllVoteLists( $user , $offset , $limit ) { + $db =& wfGetDB( DB_SLAVE ); + $a = $this->identifyUser($user) ; + $b = array ( "ORDER BY" => "val_page,val_revision" , "OFFSET" => $offset , "LIMIT" => $limit ) ; + $res = $db->select( 'validate', '*', $a , 'getAllVotesList' , $b ); + + $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 ) { + $db =& wfGetDB( DB_MASTER ); - # User has clicked "Doit" before, so evaluate form - if( $wgRequest->wasPosted() ) { - $oldtime = StrVal( $wgRequest->getVal( 'oldtime' ) ); - if( !isset ( $val[$oldtime] ) ) { - $val[$oldtime] = array(); - } - - # Reading postdata - $postrad = array(); - $poscomment = array(); - for( $idx = 0 ; $idx < count( $validationtypes) ; $idx++ ) { - $postrad[$idx] = $wgRequest->getVal( "rad{$idx}" ); - $postcomment[$idx] = $wgRequest->getText( "comment{$idx}" ); - } - - # Merge others into this one - if( $wgRequest->getCheck( 'merge_other' ) ) { - foreach( $val as $time => $stuff ) { - if( $time != $article_time ) { - for( $idx = 0; $idx < count( $validationtypes ); $idx++ ) { - $rad = $postrad[$idx]; - if( isset ( $stuff[$idx] ) && $stuff[$idx]->val_value != -1 && $rad == -1 ) { - $postrad[$idx] = $stuff[$idx]->val_value; - $postcomment[$idx] = $stuff[$idx]->val_comment; - } - } - } - } - } + $next_idx = 1; + while( isset( $this->topicList[$next_idx] ) ) { + $next_idx++; + } - # Clear all others - if( $wgRequest->getCheck( 'clear_other' ) ) { - $sql = "DELETE FROM validate WHERE val_title='" . wfStrencode( $article_title ) . - "' AND val_timestamp<>'" . wfStrencode( $oldtime ) . "' AND val_user='"; - $sql .= wfStrencode( $wgUser->getID() ) . "'"; - wfQuery( $sql, DB_WRITE ); - $val2 = $val[$oldtime]; # Only version left - $val = array(); # So clear others - $val[$oldtime] = $val2; - } + $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 ); + } - # Delete old "votes" for this version - $sql = "DELETE FROM validate WHERE val_title='" . wfStrencode( $article_title ) . - "' AND val_timestamp='" . wfStrencode( $oldtime ) . "' AND val_user='"; - $sql .= wfStrencode( $wgUser->getID() ) . "'"; - wfQuery( $sql, DB_WRITE ); - - # Incorporate changes - for( $idx = 0; $idx < count( $validationtypes ); $idx++ ) { - $comment = $postcomment[$idx] ; - $rad = $postrad[$idx] ; - if ( !isset( $val[$oldtime][$idx] ) ) { - $val[$oldtime][$idx] = ""; - } - $val[$oldtime][$idx]->val_value = $rad; - $val[$oldtime][$idx]->val_comment = $comment; - if( $rad != -1 ) { - # Store it in the database - $sql = "INSERT INTO validate (val_user,val_title,val_timestamp,val_type,val_value,val_comment) " . - "VALUES ( '" . wfStrencode( $wgUser->getID() ) . "','" . - wfStrencode( $article_title ) . "','" . - wfStrencode( $oldtime ) . "','" . - wfStrencode( $idx ) . "','" . - wfStrencode( $rad ) . "','" . - wfStrencode( $comment ) . "')"; - wfQuery( $sql, DB_WRITE ); - } - } - $wgArticle->showArticle( "Juhuu", wfMsg( 'val_validated' ) ); - return; # Show article instead of validation page - } - - # Generating HTML - $html = ""; - - $skin = $wgUser->getSkin(); - $staturl = $skin->makeSpecialURL( "validate" , "mode=stat_page&article_title=" . urlencode( $article_title ) ); - $listurl = $skin->makeSpecialURL( "validate" , "mode=list_page" ); - $html .= "" . wfMsg('val_stat_link_text') . " \n"; - $html .= "" . wfMsg('val_article_lists') . "
\n"; - $html .= "" . wfMsg('val_form_note') . "
\n"; - - # Generating data tables - $tabsep = ""; - $topstyle = "style='border-top:2px solid black'"; - foreach( $val as $time => $stuff ) { - $tablestyle = "cellspacing='0' cellpadding='2'"; - if ( $article_time == $time ) { - $tablestyle .=" style='border: 2px solid red'"; - } - $html .= "

" . wfMsg( 'val_version_of', gmdate( "F d, Y H:i:s", wfTimestamp2Unix( $time ) ) ); - $this->find_this_version ( $article_title , $time , $table_id , $table_name ); - if( $table_name == "cur" ) { - $html .= " (" . wfMsg( 'val_this_is_current_version' ) . ")"; + # This function deletes a topic and all votes for it. CAREFUL! + function deleteTopic( $id ) { + $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 getStatisticsLink( &$article ) { + global $wgUser; + $sk = $wgUser->getSkin(); + $nt = $article->getTitle(); + 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 getRevisionStatsLink( &$article, $revision ) { + global $wgUser; + $sk = $wgUser->getSkin(); + $nt = $article->getTitle(); + $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 getUserRatingsLink( $user, $text ) { + global $wgUser; + $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 getRevisionTimestamp( $rev_id ) { + $rev = $this->getRevisionFromId( $rev_id ); + return $rev->rev_timestamp; + } + + # Returns the revision number of a revision based on the timestamp + 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( $rev_id, &$article ) { + global $wgUser; + $sk = $wgUser->getSkin(); + + $metadata = ""; + $x = $this->getRevisionFromId($rev_id); + $metadata .= wfTimestamp( TS_DB, $x->rev_timestamp ); + $metadata .= " by "; + if( $x->rev_user == 0 ) { + $metadata .= $x->rev_user_text; + } else { + $u = new User; + $u->setId( $x->rev_user ); + $u->setName( $x->rev_user_text ); + $nt = $u->getUserPage(); + $metadata .= $sk->makeKnownLinkObj( $nt, htmlspecialchars( $nt->getText() ) ); + } + $metadata .= ': '. $sk->commentBlock( $x->rev_comment, $article->getTitle() ); + return $metadata; + } + + # Generates a link to the topic description + function getTopicLink($s) { + $t = Title::newFromText ( wfMsg ( 'val_topic_desc_page' ) ) ; + # FIXME: Why doesn't this use standard linking code? + $r = "escapeLocalURL () ; + $r .= "#" . urlencode ( $s ) ; + $r .= "\">{$s}" ; + return $r ; + } + + # Generates HTML from a wiki text, e.g., a wfMsg + function getParsedWiki ( $text ) { + global $wgOut, $wgTitle, $wgParser ; + $parserOutput = $wgParser->parse( $text , $wgTitle, $wgOut->mParserOptions,false); + return $parserOutput->getText() ; + } + + # Generates a form for a single revision + function getRevisionForm( &$article, $idx, &$data, $focus = false ) { + # Fill data with blank values + $ts = $idx; + $revision = $this->getRevisionId( $ts ); + foreach( $this->topicList as $x => $y ) { + if( !isset( $data[$x] ) ) { + $data[$x]->value = 0; + $data[$x]->comment = ""; } - $html .= "

\n" ; - $html .= "
\n" ; - $html .= "" ; - $html .= "\n" ; - $html .= wfMsg( 'val_table_header', $tabsep ); - for( $idx = 0; $idx < count( $validationtypes ); $idx++ ) { - $x = explode( "|" , $validationtypes[$idx] , 4 ); - if( isset ( $stuff[$idx] ) ) { - $choice = $stuff[$idx]->val_value; - } else { - $choice = -1; + } + ksort( $data ) ; + + # Generate form + $table_class = $focus ? 'revisionform_focus' : 'revisionform_default'; + $ret = "
\n"; + $head = "Revision #" . $revision; + $link = $this->getRevisionLink( $article, $revision ); + $metadata = $this->getMetadata( $revision, $article ); + $ret .= "\n"; + $line = 0; + foreach( $data as $x => $y ) { + $line = 1 - $line; + $trclass = $line == 1 ? "revision_tr_first" : "revision_tr_default"; + $idx = "_{$revision}[{$x}]"; + $ret .= "\n"; + $ret .= "\n"; + + $tlx = $this->topicList[$x]; + $vote = ""; + $max = $tlx->val_value; + for( $a = 0 ; $a <= $max ; $a++ ) { + if( $a == 0 ) { + $vote .= wfMsg ( "val_noop" ); } - if( isset( $stuff[$idx] ) ) { - $comment = $stuff[$idx]->val_comment; - } else { - $comment = ""; + $vote .= "value ) { + $vote .= " checked='checked'"; } - $html .= "{$tabsep}" - . ""; - $html .= "\n"; - } - $html .= ""; - $html .= "
" . $head . " ({$link}) {$metadata}
\n"; + $ret .= $this->getTopicLink ( $this->topicList[$x]->val_comment ) ; + $ret .= "
{$x[0]}{$x[1]}" ; - for( $cnt = 0 ; $cnt < $x[3] ; $cnt++) { - $html .= "{$tabsep}"; - $html .= "
"; - - # link to version - $title = Title::newFromDBkey( $article_title ); - if ( $table_name == "cur" ) { - $link_version = $title->getLocalURL( "" ); - } else { - $link_version = $title->getLocalURL( "oldid={$table_id}" ); - } - $link_version = "" . wfMsg ( 'val_view_version' ) . ""; - $html .= $link_version; - $html .= ""; - $html .= ""; - $html .= wfMsg( 'val_merge_old' ); - $html .= "
"; - $html .= wfMsg( 'val_clear_old', $skin->makeKnownLinkObj( $article ) ); - $html .= "
\n"; + } + $ret .= "{$vote}\n"; + + $ret .= ""; + $ret .= "\n"; } + $checked = $focus ? " checked='checked'" : ""; + $ret .= "\n"; + $ret .= "" . $this->getParsedWiki( wfMsg( 'val_merge_old' ) ) . " \n"; + $ret .= "" . $this->getParsedWiki( wfMsg( 'val_clear_old' ) ) . " \n"; + $ret .= "\n"; - $html .= "

" . wfMsg( 'preview' ) . "

"; - $wgOut->addHTML( $html ); - $wgOut->addWikiText( $wgArticle->getContent( true ) ); + if( $focus ) $ret .= "
\n" . $this->getParsedWiki ( wfMsg( "val_form_note" ) ) . ""; + $ret .= "\n"; + $ret .= "\n\n"; + return $ret; } + - function getData( $user = -1 , $title = "" , $type = -1 ) { - $ret = array(); - $sql = array(); - if( $user != -1 ) { - $sql[] = "val_user='" . wfStrencode( $user ) . "'"; + # Generates the page from the validation tab + function validatePageForm( &$article, $revision ) { + global $wgOut, $wgRequest, $wgUser; + + $ret = ""; + $this->page_id = $article->getID(); + $this->topicList = $this->getTopicList(); + $this->voteCache = $this->getVoteList( $article->getID() ); + + # Check for POST data + $re = $wgRequest->getArray( 're_submit' ); + if ( isset( $re ) ) { + $id = array_keys( $re ); + $id = $id[0] ; # $id is now the revision number the user clicked "OK" for + $clearOldRev = $wgRequest->getVal( "re_clear_{$id}", 0 ); + $mergeOldRev = $wgRequest->getVal( "re_merge_{$id}", 0 ); + $this->updateRevision( $article, $id ); + if( $mergeOldRev ) { + $this->mergeOldRevisions( $article, $id ); + } + if( $clearOldRev ) { + $this->clearOldRevisions( $article, $id ); + } + $ret .= '

' . $this->getParsedWiki( wfMsg( 'val_revision_changes_ok' ) ) . "

"; + } else { + $ret .= $this->getParsedWiki( wfMsg ('val_votepage_intro') ); } - if( $type != -1 ) { - $sql[] = "val_type='" . wfStrencode( $type ) . "'"; + + # Make sure the requested revision exists + $rev = $this->getRevisionFromId($revision); + $ts = $rev->rev_timestamp; + if( !isset( $this->voteCache[$ts] ) ) { + $this->voteCache[$ts] = array(); + } + + # Sort revisions list, newest first + krsort( $this->voteCache ); + + # Output + $title = $article->getTitle(); + $title = $title->getPrefixedText(); + $wgOut->setPageTitle( wfMsg( 'val_rev_for', $title ) ); + foreach( $this->voteCache as $x => $y ) { + $ret .= $this->getRevisionForm( $article, $x, $y, $x == $ts ); + $ret .= "
\n"; } - if( $title != "" ) { - $sql[] = "val_title='" . wfStrencode( $title ) . "'"; + $ret .= $this->getStatisticsLink( $article ); + $ret .= "

" . $this->getUserRatingsLink( $wgUser->getID(), wfMsg( 'val_show_my_ratings' ) ) . "

"; + return $ret ; + } + + # This function performs the "management" mode on Special:Validate + function manageTopics() { + global $wgRequest; + $this->topicList = $this->getTopicList(); + + $iamsure = true ; # Sure by default # $wgRequest->getVal( "iamsure", "0" ) == 1; + + if( $iamsure && $wgRequest->getVal( "m_add", "--" ) != "--" ) { + $new_topic = $wgRequest->getVal( "m_topic" ); + $new_limit = $wgRequest->getVal( "m_limit" ); + if( $new_topic != "" && $new_limit > 1 ) { + $this->addTopic( $new_topic, $new_limit ); + } } - $sql = implode( " AND " , $sql ); - if( $sql != "" ) { - $sql = " WHERE " . $sql; + + $da = $wgRequest->getArray( "m_del" ); + if( $iamsure && isset( $da ) && count( $da ) > 0 ) { + $id = array_keys( $da ); + $id = array_shift( $id ); + $this->deleteTopic( $id ); } - $sql = "SELECT * FROM validate" . $sql; - $res = wfQuery( $sql, DB_READ ); - while( $s = wfFetchObject( $res ) ) { - $ret["{$s->val_title}"]["{$s->val_timestamp}"]["{$s->val_type}"][] = $s; + + # FIXME: Wikitext this + $r = "

" . $this->getParsedWiki( wfMsg( 'val_warning' ) ) . "

\n"; + $r .= "
\n"; + $r .= "\n"; + $r .= "" . wfMsg( 'val_list_header' ) . "\n"; + foreach( $this->topicList as $x => $y ) { + $r .= "\n"; + $r .= "\n"; + $r .= "\n"; + $r .= "\n"; + $r .= "\n"; + $r .= "\n"; } - return $ret; + $r .= "\n"; + $r .= "\n"; + $r .= '' . "\n"; + $r .= '' . "\n"; + $r .= '' . "\n"; + $r .= "
{$y->val_type}" . $this->getTopicLink ( $y->val_comment ) . "1 .. " . intval( $y->val_value ) . "
1 ..
\n"; +# $r .= '

'; +# $r .= '

\n"; + $r .= "
\n"; + return $r; } + + # 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 $wgOut, $wgUser; + $this->page_id = $article->getID(); + $this->topicList = $this->getTopicList(); - # Show statistics for the different versions of a single article - function getPageStatistics( $article_title = "" ) { - global $wgLang, $wgUser, $wgOut, $wgRequest; - $validationtypes = $wgLang->getValidationTypes(); - if( $article_title == "" ) { - $article_title = $wgRequest->getVal( 'article_title' ); + $sk = $wgUser->getSkin(); + $title = $article->getTitle(); + $wgOut->setPageTitle( str_replace( '$1', $title->getPrefixedText(), wfMsg( 'val_validation_of' ) ) ); + + $data = array(); + $users = array(); + $topics = array(); + + # 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; } - $d = $this->getData( -1 , $article_title , -1 ); - if( count ( $d ) ) { - $d = array_shift ( $d ) ; - } else { - $d = array(); - } - krsort( $d ); - - # Getting table data (cur_id, old_id etc.) for each version - $table_id = array(); - $table_name = array(); - foreach( $d as $version => $data ) { - $this->find_this_version( $article_title, $version, $table_id[$version], $table_name[$version] ); - } - - # Generating HTML - $title = Title::newFromDBkey( $article_title ); - $wgOut->setPageTitle( wfMsg( 'val_page_validation_statistics' , $title->getPrefixedText() ) ); - $html = ""; - $skin = $wgUser->getSkin(); - $listurl = $skin->makeSpecialURL( "validate" , "mode=list_page" ); - $html .= "" . wfMsg( 'val_article_lists' ) . "

\n"; - - $html .= "\n"; - $html .= ""; - foreach( $validationtypes as $idx => $title ) { - $title = explode ( "|" , $title ); - $html .= ""; - } - $html .= ""; - $html .= "\n"; - foreach( $d as $version => $data ) { - # Preamble for this version - $title = Title::newFromDBkey( $article_title ); - $version_date = $wgLang->timeanddate( $version ); - $version_validate_link = $title->escapeLocalURL( "action=validate×tamp={$version}" ); - $version_validate_link = "" . wfMsg('val_validate_version') . ""; - if( $table_name[$version] == 'cur' ) { - $version_view_link = $title->escapeLocalURL( "" ); - } else { - $version_view_link = $title->escapeLocalURL( "oldid={$table_id[$version]}" ); - } - $version_view_link = "" . wfMsg('val_view_version') . ""; - $html .= ""; - $html .= ""; - - # Individual data - $vmax = array(); - $vcur = array(); - $users = array(); - foreach( $data as $type => $x2 ) { - if ( !isset ( $vcur[$type] ) ) $vcur[$type] = 0 ; - if ( !isset ( $vmax[$type] ) ) $vmax[$type] = 0 ; - if ( !isset ( $users[$type] ) ) $users[$type] = 0 ; - foreach( $x2 as $user => $x ) { - $vcur[$type] += $x->val_value; - $temp = explode( "|" , $validationtypes[$type] ); - $vmax[$type] += $temp[3] - 1; - $users[$type] += 1; - } + $db->freeResult($res); + + # Sorting lists of topics and users + ksort( $users ); + ksort( $topics ); + + $ts = $this->getRevisionTimestamp( $revision ); + $url = $this->getRevisionLink( $article, $revision, wfTimestamp( TS_DB, $ts ) ); + + # Table headers + $ret = "" ; + $ret .= "

" . str_replace( '$1', $url, wfMsg( 'val_revision_of' ) ) . "

\n"; + $ret .= "
" . wfMsg('val_version') . "{$title[0]}" . wfMsg('val_total') . "
{$version_date}
{$version_view_link}
{$version_validate_link}
\n"; + $ret .= "" ; + + foreach( $topics as $t => $dummy ) { + $ret .= ''; + } + $ret .= "\n"; + + # Table data + foreach( $users as $u => $dummy ) { # Every row a user + $ret .= ""; + $ret .= ""; } - $html .= ""; } - - if( $total_count > 0 ) { - $total = $total_percent / $total_count; - $total = number_format( $total , 2 ) . " %"; - } else { - $total = ""; - } - $html .= ""; - - $html .= ""; + $ret .= ""; } - $html .= "
" . $this->getParsedWiki ( wfMsg('val_details_th') ) . "' . $sk->commentBlock( $this->topicList[$t]->val_comment, $article->getTitle() ) . '
"; + if( !User::IsIP( $u ) ) { # Logged-in user rating + $ret .= $this->getUserRatingsLink( $u, User::whoIs( $u ) ); + } else { # Anon rating + $ret .= $this->getUserRatingsLink( $u, $u ); } - - $total_count = 0; - $total_percent = 0; - foreach( $validationtypes as $idx => $title ) { - if( isset ( $vcur[$idx] ) ) { - $average = 100 * $vcur[$idx] / $vmax[$idx] ; - $total_count += 1; - $total_percent += $average; - if( $users[$idx] > 1 ) { - $msgid = "val_percent"; - } else { - $msgid = "val_percent_single"; - } - $html .= "" . - wfMsg( $msgid, number_format( $average, 2 ) , - $vcur[$idx] , $vmax[$idx] , $users[$idx] ); + $ret .= ""; + foreach( $topics as $t => $dummy ) { # Every column a topic + if( !isset( $data[$u][$t] ) ) { + $ret .= ""; } else { - $html .= ""; - $html .= "(" . wfMsg ( "val_noop" ) . ")"; + $ret .= ""; + $ret .= $data[$u][$t]->val_value; + if( $data[$u][$t]->val_comment != "" ) { + $ret .= ' ' . $sk->commentBlock( $data[$u][$t]->val_comment, $article->getTitle() ); + } + $ret .= "{$total}
\n"; - return $html ; + $ret .= ""; + $ret .= "

" . $this->getStatisticsLink( $article ) . "

"; + $ret .= "

" . $this->getUserRatingsLink( $wgUser->getID(), wfMsg( 'val_show_my_ratings' ) ) . "

"; + + return $ret; } + + function showList( &$article ) { + global $wgOut, $wgUser , $wgRequest; + $this->page_id = $article->getID(); + $this->topicList = $this->getTopicList(); - function countUserValidations( $userid ) { - $sql = "SELECT count(DISTINCT val_title) AS num FROM validate WHERE val_user=" . IntVal( $userid ); - $res = wfQuery( $sql, DB_READ ); - if ( $s = wfFetchObject( $res ) ) { - $num = $s->num; - } else { - $num = 0; + $title = $article->getTitle(); + $wgOut->setPageTitle( wfMsg( 'val_validation_of', $title->getPrefixedText() ) ); + + $offset = $wgRequest->getVal ( "offset" , 0 ) ; + $limit = $wgRequest->getVal ( "limit" , 25 ) ; + + # Collecting statistic data + # Unfortunately, it has to read all the data, though it will only display a part + $db =& wfGetDB( DB_SLAVE ); + $res = $db->select( 'validate', 'val_revision,val_type,val_value', array( "val_page" => $this->page_id ), 'SpecialValidate::showList' );#, $b ); + + $statistics = array(); + while( $vote = $db->fetchObject($res) ) { + $ts = $this->getRevisionTimestamp($vote->val_revision); + if ( !isset ( $statistics[$ts] ) ) $statistics[$ts] = array () ; + if ( !isset ( $statistics[$ts][$vote->val_type]->count ) ) $statistics[$ts][$vote->val_type]->count = 0 ; + if ( !isset ( $statistics[$ts][$vote->val_type]->sum ) ) $statistics[$ts][$vote->val_type]->sum = 0 ; + $statistics[$ts][$vote->val_type]->count++; + $statistics[$ts][$vote->val_type]->sum += $vote->val_value; } - return $num; - } + $db->freeResult($res); - function getArticleList() { - global $wgLang, $wgOut; - $validationtypes = $wgLang->getValidationTypes(); - $wgOut->setPageTitle( wfMsg( 'val_article_lists' ) ); - $html = ""; + krsort( $statistics ); - # Choices - $choice = array (); - $maxw = 0; - foreach( $validationtypes as $idx => $data ) { - $x = explode( "|" , $data , 4 ); - if( $x[3] > $maxw ) { - $maxw = $x[3]; - } - } - foreach( $validationtypes as $idx => $data ) { - $choice[$idx] = array(); - for( $a = 0 ; $a < $maxw ; $a++ ) { - $var = "cb_{$idx}_{$a}"; - if( isset ( $_POST[$var] ) ) $choice[$idx][$a] = $_POST[$var] ; # Selected - else if ( !isset ( $_POST["doit"] ) ) $choice[$idx][$a] = 1 ; # First time - else $choice[$idx][$a] = 0 ; # De-selected - } + $ret = "\n"; + $ret .= "\n"; + foreach( $this->topicList as $topic ) { + $ret .= ""; } + $ret .= "\n"; - # The form - $html .= "\n"; - $html .= "
" . $this->getParsedWiki( wfMsg( "val_revision" ) ) . "" . $this->getTopicLink($topic->val_comment) . "
" ; - foreach( $validationtypes as $idx => $data ) { - $x = explode ( "|" , $data , 4 ); - - $html .= ""; - $html .= ""; - $html .= ""; - - for( $a = 0; $a < $maxw; $a++ ) { - if( $a < $x[3] ) { - $td = " $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 .= ""; + foreach( $this->topicList as $topicType => $topic ) { + if( isset( $data[$topicType] ) ) { + $stats = $data[$topicType]; + $average = $stats->count == 0 ? 0 : $stats->sum / $stats->count; + $ret .= sprintf( "", $average, $stats->count ); } else { - $td = ''; + $ret .= ""; } - $html .= ""; } - - $html .= ""; - $html .= "\n"; + $ret .= "\n"; } - $html .= "\n"; - $html .= ""; - $html .= "
{$x[0]}{$x[1]}
{$revision_link} {$details_link}%1.1f (%d){$td}{$x[2]}
\n"; - $html .= "\n"; + $ret .= "\n"; - # The query - $articles = array(); - $sql = "SELECT DISTINCT val_title,val_timestamp,val_type,avg(val_value) AS avg FROM validate GROUP BY val_title,val_timestamp,val_type"; - $res = wfQuery( $sql, DB_READ ); - while( $s = wfFetchObject( $res ) ) { - $articles[$s->val_title][$s->val_timestamp][$s->val_type] = $s; - } + # Navbar + $s = $this->navBar ( $offset , $limit , count ( $statistics ) , "list" ) ; + $ret = $s . $ret . $s . "

" ; - # The list - $html .= "

\n"; - return $html; + return $ret; } - function getVersionLink( &$title , $timestamp ) { - $dbkey = $title->getDBkey(); - $this->find_this_version( $dbkey, $timestamp, $table_id, $table_name ); - if( $table_name == 'cur' ) { - $link = $title->getLocalURL( "" ); + function navBar ( $offset , $limit , $lastcount , $mode = "userstats" ) { + global $wgRequest , $wgUser , $wgTitle ; + $sk = $wgUser->getSkin(); + $r = array () ; + $user = $wgRequest->getVal( "user" ); + + if ( $mode == "userstats" ) { + $nt = Title::newFromText( 'Special:Validate' ); } else { - $link = $title->getLocalURL( "action=validate×tamp={$table_id}" ); + $nt = $wgTitle ; + } + + $base = "action=validate&mode={$mode}&" ; + if ( $user != "" ) $base .= "user={$user}&" ; + $base .= "limit={$limit}&offset=" ; + + if ( $offset > 0 ) { + $o = $offset - $limit ; + $t = $offset-$limit+1 ; + $r[] = $sk->makeKnownLinkObj( $nt, "{$t} <<" , $base.$o ); } - $linktitle = wfMsg( 'val_version_of', $wgLang->timeanddate( $timestamp ) ); - $link = "" . $linktitle . ""; - if( $table_name == 'cur' ) { - $link .= " (" . wfMsg ( 'val_this_is_current_version' ) . ")"; + + $s1 = $offset + 1 ; + $s2 = $s1 + $lastcount - 1 ; + $r[] = $s1 . " - " . $s2 ; + + if ( $lastcount == $limit ) { + $o = $offset + $limit ; + $t = $offset+$limit+1 ; + $r[] = $sk->makeKnownLinkObj( $nt, ">> {$t}" , $base.$o ); } - - $vlink = wfMsg( 'val_tab' ); - $vlink = "[escapeLocalURL( "action=validate×tamp={$timestamp}" ) . "\">{$vlink}] " . $link; - return $vlink ; + + $r = implode ( " | " , $r ) ; + return $r ; + } + + function showUserStats( $user ) { + global $wgOut, $wgUser, $wgRequest; + $this->topicList = $this->getTopicList(); + $sk = $wgUser->getSkin(); + + $offset = $wgRequest->getVal( "offset" , 0 ); + $limit = $wgRequest->getVal( "limit" , 25 ); + $data = $this->getAllVoteLists( $user , $offset , $limit ) ; + + if( $user == $wgUser->getID() ) { + $wgOut->setPageTitle ( wfMsg ( 'val_my_stats_title' ) ); + } elseif( !User::IsIP( $user ) ) { + $wgOut->setPageTitle( wfMsg( 'val_user_stats_title', User::whoIs( $user ) ) ); + } else { + $wgOut->setPageTitle( wfMsg( 'val_user_stats_title', $user ) ); + } + + $ret = "" ; + $ret = "\n"; + + $linecount = 0 ; + $lastpage = -1 ; + $lastrevision = -1 ; + $initial = false ; + foreach ( $data AS $articleid => $revisions ) { + $title = Title::newFromID( $articleid ); + if ( $lastpage != $articleid ) { + $ret .= ""; + $lastpage = $articleid ; + $lastrevision = -1 ; + } + krsort( $revisions ); + foreach( $revisions as $revid => $revision ) { + $url = $title->getLocalURL( "oldid={$revid}" ); + if ( $lastrevision != $revid ) { + $initial = true ; + $lastrevision = $revid ; + } + $ret .= "" ; + if ( $initial ) { + $ret .= ""; + } + ksort( $revision ); + #$initial = true; + foreach( $revision as $topic => $rating ) { + if( !$initial ) { + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $linecount++ ; + } + } + $ret .= ""; + } + $ret .= "
"; + $ret .= $sk->makeKnownLinkObj( $title, $title->getEscapedText() ); + $ret .= "
"; + $ret .= $sk->makeKnownLinkObj( $title, wfMsg('val_revision_number', $revid ), "oldid={$revid}" ); + $ret .= "
"; + } + $initial = false; + $ret .= "" . $this->getTopicLink ( $this->topicList[$topic]->val_comment ) . "" . $this->getRatingText( $rating->val_value, $this->topicList[$topic]->val_value ) . "" . $sk->commentBlock( $rating->val_comment ) . "
"; + + + $s = $this->navBar ( $offset , $limit , $linecount ) ; + if ( $s != "" ) $ret = $s . "
" . $ret . "
" . $s ; + + return $ret; } } -function wfSpecialValidate( $page = "" ) { - global $wgOut, $wgRequest, $wgUseValidation; - +/** + * constructor + */ +function wfSpecialValidate( $page = '' ) { + global $wgOut, $wgRequest, $wgUseValidation, $wgUser, $wgContLang; + if( !$wgUseValidation ) { $wgOut->errorpage( "nosuchspecialpage", "nospecialpagetext" ); return; } - $mode = $wgRequest->getVal( 'mode', 'form' ); - $v = new Validation; - $html = "" ; -/* if( $mode == "form" ) { - $html = $v->validate_form () ; - } else */ - if( $mode == "stat_page" ) { - $html = $v->getPageStatistics(); - } else if( $mode == "list_page" ) { - $html = $v->getArticleList(); + + # Can do? + if( ! $wgUser->isSysop() ) {#isAllowed('change_validation') ) { + $wgOut->sysopRequired(); + return; } + + $mode = $wgRequest->getVal( "mode" ); + $skin = $wgUser->getSkin(); + + + if( $mode == "manage" ) { + $v = new Validation(); + $html = $v->manageTopics(); + } elseif( $mode == "userstats" ) { + $v = new Validation(); + $user = $wgRequest->getVal( "user" ); + $html = $v->showUserStats( $user ); + } else { + $html = "$mode"; + $html .= "\n"; + } + $wgOut->addHTML( $html ); }