RatingExtension/Source Code

From PeacockWiki

(Difference between revisions)
Jump to: navigation, search
Revision as of 01:55, 30 July 2006 (edit)
Trevorp (Talk | contribs)

← Previous diff
Current revision (13:59, 7 August 2006) (edit)
Trevorp (Talk | contribs)

 
Line 1: Line 1:
<pre><?php <pre><?php
-#If html request requests file, serve file+################################################################################
 +# Setup #
 +################################################################################
 +# This section defines parameters of the plugin #
 +################################################################################
 + 
 +#-------------------------------------------------------------------------------
 +# Sets the minimum number of ratings that can be returned in a ratings tag #
 +# This helps keep performance for large wikis acceptable #
 +#-------------------------------------------------------------------------------
 +$renderLimit=100;
 + 
 +#-------------------------------------------------------------------------------
 +# Sets the minimum number of ratings required for the rating to be valid #
 +# If the number of ratings are less, rating is 0 and bolded #
 +#-------------------------------------------------------------------------------
 +$countLimit=3;
 + 
 +#-------------------------------------------------------------------------------
 +# Defines namespaces for which ratings are enabled #
 +#-------------------------------------------------------------------------------
 +$allowedNamespaces=array(
 + 0=>true, //Default
 + 1=>true, //Talk
 + 2=>true, //User
 + 3=>true, //User_talk
 + 4=>true, //Project
 + 5=>true, //Project_talk
 + 6=>true, //Image
 + 7=>true, //Image_talk
 +# 8=>true, //MediaWiki
 +# 9=>true, //MediaWiki_talk
 + 10=>true, //Template
 + 11=>true, //Template_talk
 + 12=>true, //Help
 + 13=>true, //Help_talk
 + 14=>true, //Category
 + 15=>true //Category_talk
 +);
 + 
 +################################################################################
 +# Core Code #
 +################################################################################
 +# This section handles the core functions of this extension #
 +# * Serving Files #
 +# * Recording Ratings #
 +# * Displaying Ratings #
 +################################################################################
 + 
 +#-------------------------------------------------------------------------------
 +# If html request requests file, serve file #
 +# Files are requested by adding the ?file= GET parameter #
 +#-------------------------------------------------------------------------------
if(isset($_GET['file'])) if(isset($_GET['file']))
doFile(); doFile();
-#If html request contains rate information, process it+#-------------------------------------------------------------------------------
 +# If html request contains rate information, process it #
 +# Rating information indicated by ?rate= GET parameter #
 +# #
 +# MediaWiki engine is started by imitating the code in index.php #
 +# This ensures database functions are working #
 +#-------------------------------------------------------------------------------
if(isset($_GET['rate'])) if(isset($_GET['rate']))
{ {
Line 15: Line 73:
$action = $wgRequest->getVal( 'action', 'view' ); $action = $wgRequest->getVal( 'action', 'view' );
$title = $wgRequest->getVal( 'title' ); $title = $wgRequest->getVal( 'title' );
- $wgTitle = $mediaWiki->checkInitialQueries( $title,$action,$wgOut, $wgRequest, $wgContLang );+ $wgTitle = $mediaWiki->checkInitialQueries($title, $action, $wgOut,
 + $wgRequest, $wgContLang);
#Record Rating #Record Rating
#---------------- #----------------
- $sql = "CREATE TABLE IF NOT EXISTS `ratings` (\n". 
- " `page_oldid` int(8) unsigned NOT NULL auto_increment,\n". 
- " `user_id` int(11) NOT NULL,\n". 
- " `page_rating` int(8) unsigned NOT NULL,\n". 
- " PRIMARY KEY (`page_oldid`, `user_id`)\n". 
- ") ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;"; 
- $sql = "REPLACE INTO `ratings` (`page_oldid`, `user_id`, `page_rating`) VALUES (".$_GET['oldid'].", '".($wgUser->getID()?$wgUser->getID():$wgUser->getName())."', ".$_GET['rate'].")"; 
$dbr =& wfGetDB( DB_WRITE ); $dbr =& wfGetDB( DB_WRITE );
 + $sql = "REPLACE INTO `ratings` (`page_oldid`, `user_id`, `page_rating`) ".
 + "VALUES (".intval($_GET['oldid']).", '".
 + ($wgUser->getID()?$wgUser->getID():$wgUser->getName()).
 + "', ".intval($_GET['rate']).")";
$res=wfQuery($sql, DB_WRITE, ""); $res=wfQuery($sql, DB_WRITE, "");
 +
 + calculateRating($_GET['oldid']);
#Return to refering page and exit #Return to refering page and exit
#---------------- #----------------
$wgTitle->invalidateCache(); $wgTitle->invalidateCache();
 + $dbr->immediateCommit();
header( 'Location: '.$_SERVER["HTTP_REFERER"] ) ; header( 'Location: '.$_SERVER["HTTP_REFERER"] ) ;
die(); die();
} }
-#get oldid of current page+#-------------------------------------------------------------------------------
 +# Initialize extension #
 +# Sets up credit information, and hooks #
 +#-------------------------------------------------------------------------------
 +if(isset($wgScriptPath))
 +{
 + $wgExtensionCredits["parserhook"][]=array(
 + 'name' => 'Rating Extension',
 + 'version' => '0.3.1',
 + 'url' => 'http://wiki.peacocktech.com/wiki/RatingExtension',
 + 'author' => '[http://about.peacocktech.com/trevorp/ Trevor Peacock]',
 + 'description' => 'Adds rating mechanism' );
 + $wgHooks['OutputPageBeforeHTML'][] = 'InsertRating';
 + $wgHooks['SkinTemplateSetupPageCss'][] = 'RatingCss';
 + $wgExtensionFunctions[] = "RatingExtension";
 +}
 + 
 +function RatingExtension()
 +{
 + global $wgParser;
 + $wgParser->setHook("rating", "renderRating");
 + $wgParser->setHook("ratingcount", "renderRatingCount");
 + $wgParser->setHook("ratings", "renderRatings");
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Render the <rating> tag #
 +# Shows the rating of the page specified by the text between the tags #
 +#-------------------------------------------------------------------------------
 +function renderRating($input, $argv, &$parser)
 +{
 + $rating=getRatingCache(getOldIDFromTitle(trim($input)));
 + return number_format($rating['rating'], 2);
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Render the <rating> tag #
 +# Shows the number of ratings of the page specified by the text between #
 +# the tags #
 +#-------------------------------------------------------------------------------
 +function renderRatingCount($input, $argv, &$parser)
 +{
 + $rating=getRatingCache(getOldIDFromTitle(trim($input)));
 + return number_format($rating['count'], 2);
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Displays a list of ratings based on the parameters given between the tags #
 +#-------------------------------------------------------------------------------
 +function renderRatings($input, $argv, &$parser)
 +{
 + $parameters=splitParameters($input);
 + $sortdata=getSortData(isset($parameters['namespace'])?
 + $parameters['namespace']:array());
 + $ratings=getRatingList(isset($parameters['namespace'])?
 + $parameters['namespace']:array(), $sortdata['column'], $sortdata['order'],
 + isset($parameters['limit'])?$parameters['limit'][0]:10);
 + $output='';
 + $displayCount=(isset($parameters['displaycount'])?
 + $parameters['displaycount'][0]:'false')!='false';
 + $displayRating=(isset($parameters['displayrating'])?
 + $parameters['displayrating'][0]:'true')!='false';
 + $pattern=isset($parameters['pattern'])?$parameters['pattern'][0]:
 + '[[$1|$2]]'.($displayRating?' ($3)':'').($displayCount?' ($4 ratings)':'');
 + $limit=isset($parameters['limit'])?$parameters['limit'][0]:10;
 + foreach($ratings as $rating)
 + {
 + $output.=str_replace(array('$1', '$2', '$3', '$4', '$5'),
 + array(($rating['namespace']==6?':':'').$rating['title'],
 + strtr($rating['title'], '_', ' '),
 + number_format($rating['rating'], 2),
 + $rating['count'], $rating['oldid']), $pattern)."\n\n";
 + if(--$limit==0) break;
 + }
 + return renderWikiText(trim($output), $parser);
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Insert rating to top of page #
 +#-------------------------------------------------------------------------------
 +function InsertRating($parserOutput, $text) {
 + global $wgArticle, $allowedNamespaces;
 + if(!$allowedNamespaces[$wgArticle->getTitle()->getNamespace()])
 + return;
 + $oldid=getOldID($wgArticle);
 + if($oldid)
 + $text='<div id="ratingsection">'.
 + getRatingHTML(getRatingCache($oldid), $oldid).'</div>'.$text;
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Add CSS into skin for rating #
 +#-------------------------------------------------------------------------------
 +function RatingCss(&$css) {
 + global $wgScriptPath;
 + $css = "/*<![CDATA[*/".
 + " @import \"$wgScriptPath/?file=rating.css\"; ".
 + "/*]]>*/";
 + return true;
 +}
 + 
 +################################################################################
 +# Supporting Functions #
 +################################################################################
 +# These functions support the core functions of the extension #
 +################################################################################
 + 
 +#-------------------------------------------------------------------------------
 +# Processes sortby parameters to determine how to sort ratings #
 +#-------------------------------------------------------------------------------
 +function getSortData($sort)
 +{
 + $column='rating';
 + $order=-1;
 + foreach($sort as $item)
 + {
 + switch($item)
 + {
 + case 'rating':
 + $column='rating'; break;
 + case 'name':
 + $column='title'; break;
 + case 'count':
 + $column='count'; break;
 + case 'ascending':
 + $order=SORT_ASC; break;
 + case 'descending':
 + $order=SORT_DESC; break;
 + }
 + }
 + if($order==-1)
 + {
 + switch($column)
 + {
 + case 'rating':
 + $order=SORT_DESC; break;
 + case 'title':
 + $order=SORT_ASC; break;
 + case 'count':
 + $order=SORT_DESC; break;
 + default:
 + $order=SORT_DESC;
 + }
 + }
 + return array('column'=>$column, 'order'=>$order);
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Processes the given text using the mediawiki parser engine #
 +#-------------------------------------------------------------------------------
 +function renderWikiText($input, &$parser)
 +{
 + return $parser->parse($input, $parser->mTitle, $parser->mOptions,
 + false, false)->getText();
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Returns the page rating given the pages string title #
 +#-------------------------------------------------------------------------------
 +function getOldIDFromTitle($title)
 +{
 + $title=Title::newFromText($title);
 + if($title==null)
 + {
 + echo "NoArticle";
 + return 0;
 + }
 + $oldid=getOldID(new Article($title));
 + if(!$oldid)
 + {
 + return "NoOldID";
 + return 0;
 + }
 + return $oldid;
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Completes ratings_cache table for any revisions without ratings #
 +#-------------------------------------------------------------------------------
 +function doFillCache()
 +{
 + global $allowedNamespaces;
 + $namespacestring='';
 + foreach(array_keys($allowedNamespaces) as $space)
 + {
 + if(is_numeric($space))
 + {
 + $namespacestring.=', '.$space;
 + }
 + }
 + $namespacestring=substr($namespacestring, 2);
 + $sql="SELECT `page_latest` AS `oldid` FROM `page` WHERE".
 + ' `page_namespace` IN ('.$namespacestring.') AND'.' `page_latest` NOT IN '.
 + '(SELECT `page_oldid` FROM `ratings_cache`);';
 + $articles=runQuery2($sql);
 + foreach($articles as $article)
 + getRating($article->oldid);
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Returns a list of pages in the specified namespaces #
 +# Optionally it may return sorting rating information for results #
 +#-------------------------------------------------------------------------------
 +function getPageList($namespace=array(), $ratings=false, $sort='title',
 + $order=SORT_ASC, $limit=100)
 +{
 + global $renderLimit, $allowedNamespaces;
 + $limit=($limit>$renderLimit?$renderLimit:$limit);
 + $namespacestring='';
 + $filterByNamespace=false;
 + foreach($namespace as $space)
 + {
 + if(is_numeric($space) && isset($allowedNamespaces[$space]))
 + {
 + $namespacestring.=', '.$space;
 + $filterByNamespace=true;
 + }
 + }
 + if($filterByNamespace)
 + $namespacestring=substr($namespacestring, 2);
 + $sql="SELECT `page_title` AS `title`, `page_namespace` AS `namespace`,
 + `page_latest` AS `oldid` FROM `page`".($filterByNamespace?
 + ' WHERE `page_namespace` IN ('.$namespacestring.')':"").';';
 + if($ratings)
 + $sql="SELECT `page_title` AS `title`, `page_namespace` AS `namespace`, ".
 + "`page_latest` AS `oldid`, `page_oldid`, `page_rating` AS `rating`, ".
 + "`page_rating_count` AS `count` FROM `page`, `ratings_cache` ".
 + "WHERE `page_latest`=`page_oldid`".($filterByNamespace?
 + ' AND `page_namespace` IN ('.$namespacestring.')':"").
 + ' ORDER BY '.$sort.($order==SORT_ASC?'':'').
 + ($order==SORT_DESC?' DESC':'').' LIMIT '.$limit.';';
 + return runQuery2($sql);
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Returns a list of pages and their ratings for all pages in the specified #
 +# namespaces #
 +#-------------------------------------------------------------------------------
 +function getRatingList($namespace=array(), $sort='title', $order=SORT_ASC,
 + $limit=100)
 +{
 + global $wgNamespaceNamesEn, $renderLimit;
 + doFillCache();
 + $limit=($limit>$renderLimit?$renderLimit:$limit);
 + $articles=getPageList($namespace, true, $sort, $order, $limit);
 + $ratings=array();
 + foreach($articles as $article)
 + {
 + $rating=formatRating(array('count'=>$article->count,
 + 'rating'=>$article->rating));
 + $ratings[]=array('title' => $wgNamespaceNamesEn[$article->namespace].
 + ($article->namespace==0?'':':').$article->title,
 + 'rating' => $rating['rating'], 'count' => $rating['count'],
 + 'oldid'=>$article->oldid, 'title_name'=>$article->title,
 + 'namespace_name'=>$wgNamespaceNamesEn[$article->namespace],
 + 'namespace'=>$article->namespace);
 + }
 + return $ratings;
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Splits parameters from the wikitext. #
 +# Each parameter should be on its own line in the format parameter = value #
 +#-------------------------------------------------------------------------------
 +function splitParameters($input)
 +{
 + $parameters=array();
 + foreach(split("\n", $input) as $parameter)
 + {
 + $parameter=split('=', $parameter, 2);
 + if(count($parameter)==2)
 + {
 + foreach($parameter as $key => $val)
 + $parameter[$key]=trim($val);
 + if(isset($parameters[$parameter[0]]))
 + $parameters[$parameter[0]][]=$parameter[1];
 + else
 + $parameters[$parameter[0]]=array($parameter[1]);
 + }
 + }
 + return $parameters;
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Use the mediawiki engine to run the given sql code and return an object #
 +# containing the first result of the query #
 +#-------------------------------------------------------------------------------
 +function runQuery($sql)
 +{
 + $dbr =& wfGetDB( DB_SLAVE );
 + $res=wfQuery($sql, DB_SLAVE, "");
 + if(wfNumRows($res)>0)
 + return $dbr->fetchObject( $res );
 + else
 + return null;
 +}
 + 
 +function runQuery2($sql)
 +{
 + $dbr =& wfGetDB( DB_SLAVE );
 + $res=wfQuery($sql, DB_SLAVE, "");
 + $array=array();
 + while($item=$dbr->fetchObject( $res ))
 + $array[]=$item;
 + return $array;
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Return the oldid of the current page #
 +# If oldid=0 (most current revision) take the latest oldid from the database #
 +# for the current article #
 +#-------------------------------------------------------------------------------
function getOldID($article) function getOldID($article)
{ {
Line 44: Line 414:
return $oldid; return $oldid;
$dbr =& wfGetDB( DB_SLAVE ); $dbr =& wfGetDB( DB_SLAVE );
- $sql = "SELECT MAX(`rc_this_oldid`) AS `rc_this_oldid` FROM recentchanges WHERE rc_cur_id=".$article->getID().";";+ $sql="SELECT `page_latest` AS `oldid` FROM `page` ".
- $res=wfQuery($sql, DB_WRITE, "");+ "WHERE `page_id`=".$article->getID().";";
 + $res=wfQuery($sql, DB_SLAVE, "");
$row=$dbr->fetchObject( $res ); $row=$dbr->fetchObject( $res );
- if($row->rc_this_oldid)+ if($row->oldid)
- return $row->rc_this_oldid;+ return $row->oldid;
return null; return null;
} }
-#run sql code+#-------------------------------------------------------------------------------
-function runQuery($sql)+# Performs a SQL query to fetch a rating for page oldid #
-{+#-------------------------------------------------------------------------------
- $dbr =& wfGetDB( DB_SLAVE );+
- $res=wfQuery($sql, DB_SLAVE, "");+
- return $dbr->fetchObject( $res );+
-}+
- +
-#perform sql query to fetch rating for a given oldid+
function getRatingData($oldid) function getRatingData($oldid)
{ {
- $sql="SELECT COUNT(*) AS `count`, AVG(`page_rating`) AS `rating` FROM ratings WHERE `page_oldid`=".$oldid." GROUP BY `page_oldid`;";+ $sql="SELECT COUNT(*) AS `count`, AVG(`page_rating`) AS `rating` ".
 + "FROM ratings WHERE `page_oldid`=".intval($oldid)." GROUP BY `page_oldid`;";
return runQuery($sql); return runQuery($sql);
} }
-#fetch/calculate rating from database+#-------------------------------------------------------------------------------
-function getRating($oldid)+# Returns the ID for the revision before $revision #
 +#-------------------------------------------------------------------------------
 +function getPreviousRevisionID( $revision ) {
 + $dbr =& wfGetDB( DB_SLAVE );
 + return $dbr->selectField( 'revision', 'rev_id',
 + 'rev_page=(SELECT `rev_page` from `revision` WHERE `rev_id`='.
 + intval( $revision ).')'.' AND rev_id<' . intval( $revision ) .
 + ' ORDER BY rev_id DESC' );
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Fetch and calculate a rating for page oldid #
 +# If there are not enough ratings for the current revivion, cycle #
 +# older revisions to gather a minimum number of ratings #
 +# Updates cache table with calcaulated values #
 +#-------------------------------------------------------------------------------
 +function calculateRating($oldid)
{ {
- global $wgTitle;+ global $wgTitle, $countLimit;
- + $origid=$oldid;
- $countlimit=3;+
- +
$ratingdata=getRatingData($oldid); $ratingdata=getRatingData($oldid);
$finalrating='?'; $finalrating='?';
$currentcount=number_format($ratingdata->count, 0); $currentcount=number_format($ratingdata->count, 0);
- if($rating->count<$countlimit)+ #If there are not enough ratings for the current revision
 + if($ratingdata->count<$countLimit)
{ {
$count=$ratingdata->count; $count=$ratingdata->count;
$rating=$count*$ratingdata->rating; $rating=$count*$ratingdata->rating;
-# echo "not enough data ($rating/$count)\n";+ #cycle older revisions looking for more ratings
- while($oldid=$wgTitle->getPreviousRevisionID($oldid))+ while($oldid=getPreviousRevisionID($oldid))
{ {
-# echo "trying previous ratings\n"; 
$ratingdata=getRatingData($oldid); $ratingdata=getRatingData($oldid);
- if($count+$ratingdata->count<$countlimit)+ #If still not enough ratings
 + if($count+$ratingdata->count<$countLimit)
{ {
$count+=$ratingdata->count; $count+=$ratingdata->count;
$rating+=$ratingdata->count*$ratingdata->rating; $rating+=$ratingdata->count*$ratingdata->rating;
-# echo "still not enough data ($rating/$count)\n"; 
} }
- else+ else #found enough ratings
{ {
- $rating+=($countlimit-$count)*$ratingdata->rating;+ $rating+=($countLimit-$count)*$ratingdata->rating;
- $count=$countlimit;+ $count=$countLimit;
-# echo "enough data ($rating/$count)\n";+
$finalrating=$rating/$count; $finalrating=$rating/$count;
$oldid=false; $oldid=false;
Line 103: Line 482:
} }
else else
- $finalrating=$rating/$count;+ $finalrating=$ratingdata->rating;
 + 
 + $dbr =& wfGetDB( DB_WRITE );
 + $sql = "REPLACE INTO `ratings_cache` (`page_oldid`, `page_rating`, ".
 + "`page_rating_count`) VALUES (".intval($origid).", ".
 + (is_numeric($finalrating)?$finalrating:0).", ".$currentcount.")";
 + $res=wfQuery($sql, DB_WRITE, "");
- $finalrating=($finalrating-1)*1.25;+ return array('rating'=>$finalrating, 'count'=>$currentcount);
- $ratingarray=array('display'=>(is_numeric($finalrating)?number_format($finalrating, 2):$finalrating)." ($currentcount ratings)",+}
 + 
 +#-------------------------------------------------------------------------------
 +# Formats rating array for insertion to page rating section #
 +#-------------------------------------------------------------------------------
 +function formatRating($rating)
 +{
 + $finalrating=$rating['rating'];
 + $currentcount=$rating['count'];
 +
 + #format rating data
 + if(is_numeric($finalrating) && $finalrating>0)
 + $finalrating=($finalrating-1)*1.25;
 + $ratingarray=array('display'=>
 + (is_numeric($finalrating)?number_format($finalrating, 2):$finalrating).
 + " ($currentcount ratings)",
'count'=>$currentcount, 'count'=>$currentcount,
- 'rating'=>$finalrating);+ 'rating'=>(is_numeric($finalrating)?$finalrating:0));
return $ratingarray; return $ratingarray;
} }
-#Generate html code to insert into page+#-------------------------------------------------------------------------------
 +# Calculates rating from database for specified revision #
 +# Updates rating cache table #
 +#-------------------------------------------------------------------------------
 +function getRating($oldid)
 +{
 + return formatRating(calculateRating($oldid));
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Fetches the ratings from cache table #
 +# Calculates rating if it is not found in cache #
 +#-------------------------------------------------------------------------------
 +function getRatingCache($oldid)
 +{
 + $sql='SELECT * FROM `ratings_cache` WHERE `page_oldid`='.intval($oldid).';';
 + $rating=runQuery($sql);
 + $rating=array('rating'=>$rating->page_rating,
 + 'count'=>$rating->age_rating_count);
 + if($rating->page_oldid==null)
 + $rating=calculateRating($oldid);
 + return formatRating($rating);
 +}
 + 
 +#-------------------------------------------------------------------------------
 +# Given the rating array and the page oldid, generate HTML code to be #
 +# displayed #
 +#-------------------------------------------------------------------------------
function getRatingHTML($rating, $oldid) function getRatingHTML($rating, $oldid)
{ {
- global $wgTitle, $wgScriptPath;+ global $wgTitle, $wgScriptPath, $countLimit;
- $countlimit=3;+
$html=''; $html='';
 + #generate stars
for($x=0;$x<=4;$x++) for($x=0;$x<=4;$x++)
{ {
- $html.='<a href="'.$wgTitle->getFullURL('oldid='.$oldid.'&rate='.($x+1)).'">'.+ $html.='<a href="'.
- '<img src="'.$wgScriptPath.'/extensions/rating/star';+ $wgTitle->getFullURL('oldid='.$oldid.'&rate='.($x+1)).'" rel="nofollow">'.
- if($rating['rating']>=$x+1)+ '<img src="?file=Star';
- $html.='4'.($rating['count']<$countlimit?'b':'');+ if($rating['rating']>=$x+1) #larger than current star : filled
- elseif($rating['rating']>$x+0.75)+ $html.='4'.($rating['count']<$countLimit?'b':'');
- $html.='3'.($rating['count']<$countlimit?'b':'');+ elseif($rating['rating']>=$x+0.75) #3/4 current star : 3/4 filled
- elseif($rating['rating']>$x+0.5)+ $html.='3'.($rating['count']<$countLimit?'b':'');
- $html.='2'.($rating['count']<$countlimit?'b':'');+ elseif($rating['rating']>=$x+0.5) #1/2 current star : 1/2 filled
- elseif($rating['rating']>$x+0.25)+ $html.='2'.($rating['count']<$countLimit?'b':'');
- $html.='1'.($rating['count']<$countlimit?'b':'');+ elseif($rating['rating']>=$x+0.25) #1/4 current star : 1/4 filled
- else+ $html.='1'.($rating['count']<$countLimit?'b':'');
 + else #less than current star : empty
$html.='0'; $html.='0';
$html.='.png" align=bottom/></a>'."\n"; $html.='.png" align=bottom/></a>'."\n";
} }
 + #add text rating
$html.=' '.$rating['display'].''; $html.=' '.$rating['display'].'';
- $html=($rating['count']<$countlimit?'<b>'.$html.'</b>':$html);+ $html=($rating['count']<$countLimit?'<b>'.$html.'</b>':$html);
return $html; return $html;
-} 
- 
-#Insert rating to top of page 
-function InsertRating($parserOutput, $text) { 
- global $wgArticle; 
- $oldid=getOldID($wgArticle); 
- if($oldid) 
- $text='<div id="ratingsection">'.getRatingHTML(getRating($oldid), $oldid).'</div>'.$text; 
-} 
- 
- 
-#set up extension info 
-if(isset($wgScriptPath)) 
-{ 
-$wgExtensionCredits["parserhook"][]=array( 
- 'name' => 'Rating Extension', 
- 'version' => '0.1', 
- 'url' => 'http://wiki.peacocktech.com/wiki/RatingExtension', 
- 'author' => '[http://about.peacocktech.com/trevorp/ Trevor Peacock]', 
- 'description' => 'Adds rating mechanism' ); 
- $wgHooks['OutputPageBeforeHTML'][] = 'InsertRating'; 
- $wgHooks['SkinTemplateSetupPageCss'][] = 'RatingCss'; 
-} 
- 
-#Add CSS into skin for rating 
-function RatingCss(&$css) { 
- global $wgScriptPath; 
- $css = "/*<![CDATA[*/". 
- " @import \"$wgScriptPath/?file=rating.css\"; ". 
- "/*]]>*/"; 
- return true; 
} }
-#Return file if requested+#-------------------------------------------------------------------------------
 +# Return a file and exit. #
 +# File determined by ?file= GET parameter #
 +#-------------------------------------------------------------------------------
function doFile() function doFile()
{ {
switch ($_GET['file']) switch ($_GET['file'])
{ {
 + #Star .png files
 + case "Star0.png":
 + case "Star1.png":
 + case "Star1b.png":
 + case "Star2.png":
 + case "Star2b.png":
 + case "Star3.png":
 + case "Star3b.png":
 + case "Star4.png":
 + case "Star4b.png":
 + header("Content-type: image/png");
 + echo readFile('extensions/rating/'.$_GET['file']);
 + die();
 + #extension css styling
case "rating.css": case "rating.css":
header("Content-type: text/css"); header("Content-type: text/css");
Line 186: Line 601:
#ratingsection b { #ratingsection b {
-// border: 1px solid black; 
color: red; color: red;
padding: 4px; padding: 4px;

Current revision

<?php

################################################################################
# Setup                                                                        #
################################################################################
# This section defines parameters of the plugin                                #
################################################################################

#-------------------------------------------------------------------------------
# Sets the minimum number of ratings that can be returned in a ratings tag     #
# This helps keep performance for large wikis acceptable                       #
#-------------------------------------------------------------------------------
$renderLimit=100;

#-------------------------------------------------------------------------------
# Sets the minimum number of ratings required for the rating to be valid       #
# If the number of ratings are less, rating is 0 and bolded                    #
#-------------------------------------------------------------------------------
$countLimit=3;

#-------------------------------------------------------------------------------
# Defines namespaces for which ratings are enabled                             #
#-------------------------------------------------------------------------------
$allowedNamespaces=array(
  0=>true, //Default
  1=>true, //Talk
  2=>true, //User
  3=>true, //User_talk
  4=>true, //Project
  5=>true, //Project_talk
  6=>true, //Image
  7=>true, //Image_talk
#  8=>true, //MediaWiki
#  9=>true, //MediaWiki_talk
  10=>true, //Template
  11=>true, //Template_talk
  12=>true, //Help
  13=>true, //Help_talk
  14=>true, //Category
  15=>true //Category_talk 
);

################################################################################
# Core Code                                                                    #
################################################################################
# This section handles the core functions of this extension                    #
#   * Serving Files                                                            #
#   * Recording Ratings                                                        #
#   * Displaying Ratings                                                       #
################################################################################

#-------------------------------------------------------------------------------
# If html request requests file, serve file                                    #
# Files are requested by adding the ?file= GET parameter                       #
#-------------------------------------------------------------------------------
if(isset($_GET['file']))
  doFile();

#-------------------------------------------------------------------------------
# If html request contains rate information, process it                        #
# Rating information indicated by ?rate= GET parameter                         #
#                                                                              #
# MediaWiki engine is started by imitating the code in index.php               #
# This ensures database functions are working                                  #
#-------------------------------------------------------------------------------
if(isset($_GET['rate']))
{
  #Init
  #----------------
  require_once( 'includes/Setup.php' );
  require_once( "includes/Wiki.php" );
  $mediaWiki = new MediaWiki();
  $action = $wgRequest->getVal( 'action', 'view' );
  $title = $wgRequest->getVal( 'title' );
  $wgTitle = $mediaWiki->checkInitialQueries($title, $action, $wgOut,
    $wgRequest, $wgContLang);


  #Record Rating
  #----------------
  $dbr =& wfGetDB( DB_WRITE );
  $sql = "REPLACE INTO `ratings` (`page_oldid`, `user_id`, `page_rating`) ".
    "VALUES (".intval($_GET['oldid']).", '".
    ($wgUser->getID()?$wgUser->getID():$wgUser->getName()).
    "', ".intval($_GET['rate']).")";
  $res=wfQuery($sql, DB_WRITE, "");

  calculateRating($_GET['oldid']);
  
  #Return to refering page and exit
  #----------------
  $wgTitle->invalidateCache();
  $dbr->immediateCommit();
  header( 'Location: '.$_SERVER["HTTP_REFERER"] ) ;
  die();
}

#-------------------------------------------------------------------------------
# Initialize extension                                                         #
# Sets up credit information, and hooks                                        #
#-------------------------------------------------------------------------------
if(isset($wgScriptPath))
{
  $wgExtensionCredits["parserhook"][]=array(
    'name' => 'Rating Extension',
    'version' => '0.3.1',
    'url' => 'http://wiki.peacocktech.com/wiki/RatingExtension',
    'author' => '[http://about.peacocktech.com/trevorp/ Trevor Peacock]',
    'description' => 'Adds rating mechanism' );
  $wgHooks['OutputPageBeforeHTML'][] = 'InsertRating';
  $wgHooks['SkinTemplateSetupPageCss'][] = 'RatingCss';
  $wgExtensionFunctions[] = "RatingExtension";
}

function RatingExtension()
{
  global $wgParser;
  $wgParser->setHook("rating", "renderRating");
  $wgParser->setHook("ratingcount", "renderRatingCount");
  $wgParser->setHook("ratings", "renderRatings");
}

#-------------------------------------------------------------------------------
# Render the <rating> tag                                                      #
# Shows the rating of the page specified by the text between the tags          #
#-------------------------------------------------------------------------------
function renderRating($input, $argv, &$parser)
{
  $rating=getRatingCache(getOldIDFromTitle(trim($input)));
  return number_format($rating['rating'], 2);
}

#-------------------------------------------------------------------------------
# Render the <rating> tag                                                      #
# Shows the number of ratings of the page specified by the text between        #
#  the tags                                                                    #
#-------------------------------------------------------------------------------
function renderRatingCount($input, $argv, &$parser)
{
  $rating=getRatingCache(getOldIDFromTitle(trim($input)));
  return number_format($rating['count'], 2);
}

#-------------------------------------------------------------------------------
# Displays a list of ratings based on the parameters given between the tags    #
#-------------------------------------------------------------------------------
function renderRatings($input, $argv, &$parser)
{
  $parameters=splitParameters($input);
  $sortdata=getSortData(isset($parameters['namespace'])?
    $parameters['namespace']:array());
  $ratings=getRatingList(isset($parameters['namespace'])?
    $parameters['namespace']:array(), $sortdata['column'], $sortdata['order'],
    isset($parameters['limit'])?$parameters['limit'][0]:10);
  $output='';
  $displayCount=(isset($parameters['displaycount'])?
    $parameters['displaycount'][0]:'false')!='false';
  $displayRating=(isset($parameters['displayrating'])?
    $parameters['displayrating'][0]:'true')!='false';
  $pattern=isset($parameters['pattern'])?$parameters['pattern'][0]:
    '[[$1|$2]]'.($displayRating?' ($3)':'').($displayCount?' ($4 ratings)':'');
  $limit=isset($parameters['limit'])?$parameters['limit'][0]:10;
  foreach($ratings as $rating)
  {
    $output.=str_replace(array('$1', '$2', '$3', '$4', '$5'), 
      array(($rating['namespace']==6?':':'').$rating['title'],
        strtr($rating['title'], '_', ' '), 
        number_format($rating['rating'], 2), 
        $rating['count'], $rating['oldid']), $pattern)."\n\n";
    if(--$limit==0) break;
  }
  return renderWikiText(trim($output), $parser);
}

#-------------------------------------------------------------------------------
# Insert rating to top of page                                                 #
#-------------------------------------------------------------------------------
function InsertRating($parserOutput, $text) {
  global $wgArticle, $allowedNamespaces;
  if(!$allowedNamespaces[$wgArticle->getTitle()->getNamespace()])
    return;
  $oldid=getOldID($wgArticle);
  if($oldid)
    $text='<div id="ratingsection">'.
      getRatingHTML(getRatingCache($oldid), $oldid).'</div>'.$text;
}

#-------------------------------------------------------------------------------
# Add CSS into skin for rating                                                 #
#-------------------------------------------------------------------------------
function RatingCss(&$css) {
  global $wgScriptPath;
  $css = "/*<![CDATA[*/".
  " @import \"$wgScriptPath/?file=rating.css\"; ".
  "/*]]>*/";
  return true;
}

################################################################################
# Supporting Functions                                                         #
################################################################################
# These functions support the core functions of the extension                  #
################################################################################

#-------------------------------------------------------------------------------
# Processes sortby parameters to determine how to sort ratings                 #
#-------------------------------------------------------------------------------
function getSortData($sort)
{
  $column='rating';
  $order=-1;
  foreach($sort as $item)
  {
    switch($item)
    {
      case 'rating':
        $column='rating'; break;
      case 'name':
        $column='title'; break;
      case 'count':
        $column='count'; break;
      case 'ascending':
        $order=SORT_ASC; break;
      case 'descending':
        $order=SORT_DESC; break;
    }
  }
  if($order==-1)
  {
    switch($column)
    {
      case 'rating':
        $order=SORT_DESC; break;
      case 'title':
        $order=SORT_ASC; break;
      case 'count':
        $order=SORT_DESC; break;
      default:
        $order=SORT_DESC;
    }
  }
  return array('column'=>$column, 'order'=>$order);
}

#-------------------------------------------------------------------------------
# Processes the given text using the mediawiki parser engine                   #
#-------------------------------------------------------------------------------
function renderWikiText($input, &$parser)
{
  return $parser->parse($input, $parser->mTitle, $parser->mOptions, 
    false, false)->getText();
}

#-------------------------------------------------------------------------------
# Returns the page rating given the pages string title                         #
#-------------------------------------------------------------------------------
function getOldIDFromTitle($title)
{
  $title=Title::newFromText($title);
  if($title==null)
  {
    echo "NoArticle";
    return 0;
  }
  $oldid=getOldID(new Article($title));
  if(!$oldid)
  {
    return "NoOldID";
    return 0;
  }
  return $oldid;
}

#-------------------------------------------------------------------------------
# Completes ratings_cache table for any revisions without ratings              #
#-------------------------------------------------------------------------------
function doFillCache()
{
  global $allowedNamespaces;
  $namespacestring='';
  foreach(array_keys($allowedNamespaces) as $space)
  {
    if(is_numeric($space))
    {
      $namespacestring.=', '.$space;
    }
  }
  $namespacestring=substr($namespacestring, 2);
  $sql="SELECT `page_latest` AS `oldid` FROM `page` WHERE".
    ' `page_namespace` IN ('.$namespacestring.') AND'.' `page_latest` NOT IN '.
    '(SELECT `page_oldid` FROM `ratings_cache`);';
  $articles=runQuery2($sql);
  foreach($articles as $article)
    getRating($article->oldid);
}

#-------------------------------------------------------------------------------
# Returns a list of pages in the specified namespaces                          #
# Optionally it may return sorting rating information for results              #
#-------------------------------------------------------------------------------
function getPageList($namespace=array(), $ratings=false, $sort='title',
    $order=SORT_ASC, $limit=100)
{
  global $renderLimit, $allowedNamespaces;
  $limit=($limit>$renderLimit?$renderLimit:$limit);
  $namespacestring='';
  $filterByNamespace=false;
  foreach($namespace as $space)
  {
    if(is_numeric($space) && isset($allowedNamespaces[$space]))
    {
      $namespacestring.=', '.$space;
      $filterByNamespace=true;
    }
  }
  if($filterByNamespace)
    $namespacestring=substr($namespacestring, 2);
  $sql="SELECT `page_title` AS `title`, `page_namespace` AS `namespace`, 
    `page_latest` AS `oldid` FROM `page`".($filterByNamespace?
    ' WHERE `page_namespace` IN ('.$namespacestring.')':"").';';
  if($ratings)
    $sql="SELECT `page_title` AS `title`, `page_namespace` AS `namespace`, ".
      "`page_latest` AS `oldid`, `page_oldid`, `page_rating` AS `rating`, ".
      "`page_rating_count` AS `count` FROM `page`, `ratings_cache` ".
      "WHERE `page_latest`=`page_oldid`".($filterByNamespace?
      ' AND `page_namespace` IN ('.$namespacestring.')':"").
      ' ORDER BY '.$sort.($order==SORT_ASC?'':'').
      ($order==SORT_DESC?' DESC':'').' LIMIT '.$limit.';';
  return runQuery2($sql);
}

#-------------------------------------------------------------------------------
# Returns a list of pages and their ratings for all pages in the specified     #
#  namespaces                                                                  #
#-------------------------------------------------------------------------------
function getRatingList($namespace=array(), $sort='title', $order=SORT_ASC,
    $limit=100)
{
  global $wgNamespaceNamesEn, $renderLimit;
  doFillCache();
  $limit=($limit>$renderLimit?$renderLimit:$limit);
  $articles=getPageList($namespace, true, $sort, $order, $limit);
  $ratings=array();
  foreach($articles as $article)
  {
    $rating=formatRating(array('count'=>$article->count,
      'rating'=>$article->rating));
    $ratings[]=array('title' => $wgNamespaceNamesEn[$article->namespace].
      ($article->namespace==0?'':':').$article->title, 
        'rating' => $rating['rating'], 'count' => $rating['count'],
        'oldid'=>$article->oldid, 'title_name'=>$article->title,
        'namespace_name'=>$wgNamespaceNamesEn[$article->namespace],
	'namespace'=>$article->namespace);
  }
  return $ratings;
}

#-------------------------------------------------------------------------------
# Splits parameters from the wikitext.                                         #
# Each parameter should be on its own line in the format  parameter = value    #
#-------------------------------------------------------------------------------
function splitParameters($input)
{
  $parameters=array();
  foreach(split("\n", $input) as $parameter)
  {
    $parameter=split('=', $parameter, 2);
    if(count($parameter)==2)
    {
      foreach($parameter as $key => $val)
        $parameter[$key]=trim($val);
      if(isset($parameters[$parameter[0]]))
        $parameters[$parameter[0]][]=$parameter[1];
      else
        $parameters[$parameter[0]]=array($parameter[1]);
    }
  }
  return $parameters;
}

#-------------------------------------------------------------------------------
# Use the mediawiki engine to run the given sql code and return an object      #
# containing the first result of the query                                     #
#-------------------------------------------------------------------------------
function runQuery($sql)
{
  $dbr =& wfGetDB( DB_SLAVE );
  $res=wfQuery($sql, DB_SLAVE, "");
  if(wfNumRows($res)>0)
    return $dbr->fetchObject( $res );
  else
    return null;
}

function runQuery2($sql)
{
  $dbr =& wfGetDB( DB_SLAVE );
  $res=wfQuery($sql, DB_SLAVE, "");
  $array=array();
  while($item=$dbr->fetchObject( $res ))
    $array[]=$item;
  return $array;
}

#-------------------------------------------------------------------------------
# Return the oldid of the current page                                         #
# If oldid=0 (most current revision) take the latest oldid from the database   #
# for the current article                                                      #
#-------------------------------------------------------------------------------
function getOldID($article)
{
  $oldid=$article->getOldIDFromRequest();
  if($oldid!=0)
    return $oldid;
  $dbr =& wfGetDB( DB_SLAVE );
  $sql="SELECT `page_latest` AS `oldid` FROM `page` ".
    "WHERE `page_id`=".$article->getID().";";
  $res=wfQuery($sql, DB_SLAVE, "");
  $row=$dbr->fetchObject( $res );
  if($row->oldid)
    return $row->oldid;
  return null;
}

#-------------------------------------------------------------------------------
# Performs a SQL query to fetch a rating for page oldid                        #
#-------------------------------------------------------------------------------
function getRatingData($oldid)
{
  $sql="SELECT COUNT(*) AS `count`, AVG(`page_rating`) AS `rating` ".
    "FROM ratings WHERE `page_oldid`=".intval($oldid)." GROUP BY `page_oldid`;";
  return runQuery($sql);
}

#-------------------------------------------------------------------------------
# Returns the ID for the revision before $revision                             #
#-------------------------------------------------------------------------------
function getPreviousRevisionID( $revision ) {
	$dbr =& wfGetDB( DB_SLAVE );
	return $dbr->selectField( 'revision', 'rev_id',
		'rev_page=(SELECT `rev_page` from `revision` WHERE `rev_id`='.
		intval( $revision ).')'.' AND rev_id<' . intval( $revision ) . 
		' ORDER BY rev_id DESC' );
}

#-------------------------------------------------------------------------------
# Fetch and calculate a rating for page oldid                                  #
# If there are not enough ratings for the current revivion, cycle              #
# older revisions to gather a minimum number of ratings                        #
# Updates cache table with calcaulated values                                  #
#-------------------------------------------------------------------------------
function calculateRating($oldid)
{
  global $wgTitle, $countLimit;
  $origid=$oldid;
  $ratingdata=getRatingData($oldid);
  $finalrating='?';
  $currentcount=number_format($ratingdata->count, 0);
  #If there are not enough ratings for the current revision
  if($ratingdata->count<$countLimit)
  {
    $count=$ratingdata->count;
    $rating=$count*$ratingdata->rating;
    #cycle older revisions looking for more ratings
    while($oldid=getPreviousRevisionID($oldid)) 
    {
      $ratingdata=getRatingData($oldid);
      #If still not enough ratings
      if($count+$ratingdata->count<$countLimit)
      {
        $count+=$ratingdata->count;
        $rating+=$ratingdata->count*$ratingdata->rating;
      }
      else #found enough ratings
      {
        $rating+=($countLimit-$count)*$ratingdata->rating;
        $count=$countLimit;
        $finalrating=$rating/$count;
        $oldid=false;
      }
    }
  }
  else
    $finalrating=$ratingdata->rating;

  $dbr =& wfGetDB( DB_WRITE );
  $sql = "REPLACE INTO `ratings_cache` (`page_oldid`, `page_rating`, ".
    "`page_rating_count`) VALUES (".intval($origid).", ".
    (is_numeric($finalrating)?$finalrating:0).", ".$currentcount.")";
  $res=wfQuery($sql, DB_WRITE, "");
  
  return array('rating'=>$finalrating, 'count'=>$currentcount);
}

#-------------------------------------------------------------------------------
# Formats rating array for insertion to page rating section                    #
#-------------------------------------------------------------------------------
function formatRating($rating)
{
  $finalrating=$rating['rating'];
  $currentcount=$rating['count'];
  
  #format rating data
  if(is_numeric($finalrating) && $finalrating>0)
    $finalrating=($finalrating-1)*1.25;
  $ratingarray=array('display'=>
      (is_numeric($finalrating)?number_format($finalrating, 2):$finalrating).
      " ($currentcount ratings)",
    'count'=>$currentcount,
    'rating'=>(is_numeric($finalrating)?$finalrating:0));
  return $ratingarray;
}

#-------------------------------------------------------------------------------
# Calculates rating from database for specified revision                       #
# Updates rating cache table                                                   #
#-------------------------------------------------------------------------------
function getRating($oldid)
{
  return formatRating(calculateRating($oldid));
}

#-------------------------------------------------------------------------------
# Fetches the ratings from cache table                                         #
# Calculates rating if it is not found in cache                                #
#-------------------------------------------------------------------------------
function getRatingCache($oldid)
{
  $sql='SELECT * FROM `ratings_cache` WHERE `page_oldid`='.intval($oldid).';';
  $rating=runQuery($sql);
  $rating=array('rating'=>$rating->page_rating,
    'count'=>$rating->age_rating_count);
  if($rating->page_oldid==null)
    $rating=calculateRating($oldid);
  return formatRating($rating);
}

#-------------------------------------------------------------------------------
# Given the rating array and the page oldid, generate HTML code to be          #
# displayed                                                                    #
#-------------------------------------------------------------------------------
function getRatingHTML($rating, $oldid)
{
  global $wgTitle, $wgScriptPath, $countLimit;
  $html='';
  #generate stars
  for($x=0;$x<=4;$x++)
  {
    $html.='<a href="'.
      $wgTitle->getFullURL('oldid='.$oldid.'&rate='.($x+1)).'" rel="nofollow">'.
      '<img src="?file=Star';
    if($rating['rating']>=$x+1) #larger than current star : filled
         $html.='4'.($rating['count']<$countLimit?'b':'');
    elseif($rating['rating']>=$x+0.75) #3/4 current star : 3/4 filled
      $html.='3'.($rating['count']<$countLimit?'b':'');
    elseif($rating['rating']>=$x+0.5) #1/2 current star : 1/2 filled
      $html.='2'.($rating['count']<$countLimit?'b':'');
    elseif($rating['rating']>=$x+0.25) #1/4 current star : 1/4 filled
      $html.='1'.($rating['count']<$countLimit?'b':'');
    else #less than current star : empty
      $html.='0';
    $html.='.png" align=bottom/></a>'."\n";
  }
  #add text rating 
  $html.=' '.$rating['display'].'';
  $html=($rating['count']<$countLimit?'<b>'.$html.'</b>':$html);
  return $html;
}


#-------------------------------------------------------------------------------
# Return a file and exit.                                                      #
# File determined by ?file= GET parameter                                      #
#-------------------------------------------------------------------------------
function doFile()
{
  switch ($_GET['file'])
  {
    #Star .png files
    case "Star0.png":
    case "Star1.png":
    case "Star1b.png":
    case "Star2.png":
    case "Star2b.png":
    case "Star3.png":
    case "Star3b.png":
    case "Star4.png":
    case "Star4b.png":
      header("Content-type: image/png");
      echo readFile('extensions/rating/'.$_GET['file']);
      die();
    #extension css styling
    case "rating.css":
      header("Content-type: text/css");
        ?>
#ratingsection {
  float: right;
  margin-top: -3.7em;
  padding: 3px;
}

#ratingsection b {
  color: red;
  padding: 4px;
}
<?php
      die();
  }
}

?>
Personal tools