/** * Get tag dropdown select * @param int $selected, selected level * @return string */ public static function getTagMenu( $selected = '' ) { $s = "<label for='wpRatingTag'>" . wfMsgHtml('readerfeedback-tagfilter') . "</label> "; $s .= Xml::openElement( 'select', array('name' => 'ratingtag', 'id' => 'wpRatingTag') ); foreach( ReaderFeedback::getFeedbackTags() as $tag => $weight ) { $s .= Xml::option( wfMsg( "readerfeedback-$tag" ), $tag, $selected===$tag ); } $s .= Xml::closeElement('select')."\n"; return $s; }
/** * Get a table of the vote totals for a page * @param Title $page * @param int $period, number of days back * @param array $add, optional vote to add on (used to visually avoid lag) * @param string $cache, optional param to not use cache * @return string HTML table */ public static function getVoteAggregates( $page, $period, $add = array(), $cache = 'useCache' ) { global $wgLang, $wgMemc; $votes = null; $now = time(); $key = wfMemcKey( 'feedback', 'ratingtally', $page->getArticleId(), $period ); // Check cache if( $cache == 'useCache' ) { $set = $wgMemc->get($key); // Cutoff is at the 24 hour mark due to the way the aggregate // schema groups ratings by date for graphs. $cache_cutoff = $now - ($now % 86400); if( is_array($set) && count($set) == 2 ) { list($val,$time) = $set; $touched = wfTimestamp( TS_UNIX, RatingHistory::getTouched($page) ); if( $time > $cache_cutoff && $time > $touched ) { $votes = $val; } } } // Do query, cache miss if( !isset($votes) ) { // Set cutoff time for period $dbr = wfGetDB( DB_SLAVE ); $cutoff_unixtime = $now - ($period * 24 * 3600); // Use integral number of days to be consistent with graphs $cutoff_unixtime = $cutoff_unixtime - ($cutoff_unixtime % 86400); $cutoff = $dbr->addQuotes( $dbr->timestamp( $cutoff_unixtime ) ); // Get the first revision possibly voted on in the range $firstRevTS = $dbr->selectField( 'revision', 'rev_timestamp', array( 'rev_page' => $page->getArticleId(), "rev_timestamp <= $cutoff" ), __METHOD__, array( 'ORDER BY' => 'rev_timestamp DESC' ) ); // Find average, median... $res = $dbr->select( array( 'revision', 'reader_feedback' ), array( 'rfb_ratings' ), array( 'rev_page' => $page->getArticleId(), "rev_id = rfb_rev_id", "rfb_timestamp >= $cutoff", // Trigger INDEX usage "rev_timestamp >= ".$dbr->addQuotes($firstRevTS) ), __METHOD__, array( 'USE INDEX' => array('revision' => 'page_timestamp') ) ); $votes = array(); foreach( ReaderFeedback::getFeedbackTags() as $tag => $w ) { $votes[$tag] = array( 0 => 0, 1 => 0, 2 => 0, 3 => 0, 4 => 0 ); } // Read votes and tally the numbers foreach ( $res as $row ) { $dims = ReaderFeedback::expandRatings( $row->rfb_ratings ); foreach( $dims as $tag => $val ) { if( isset($votes[$tag]) && isset($votes[$tag][$val]) ) { $votes[$tag][$val]++; } } } // Tack on $add for display (used to avoid cache/lag) foreach( $add as $tag => $val ) { if( isset($votes[$tag]) && isset($votes[$tag][$val]) ) { $votes[$tag][$val]++; } } $wgMemc->set( $key, array( $votes, $now ), 24*3600 ); } // Output multi-column list $html = "<table class='rfb-reader_feedback_table' cellspacing='0'><tr>"; foreach( ReaderFeedback::getFeedbackTags() as $tag => $w ) { // Get tag average... $dist = isset($votes[$tag]) ? $votes[$tag] : array(); $count = array_sum($dist); if( $count ) { $ave = ($dist[0] + 2*$dist[1] + 3*$dist[2] + 4*$dist[3] + 5*$dist[4])/$count; $ave = round($ave,1); } else { $ave = '-'; // DIV by zero } $html .= '<td align="center"><b>'.wfMsgHtml("readerfeedback-$tag").'</b>  '. '<sup>('.wfMsgHtml('ratinghistory-ave',$wgLang->formatNum($ave)).')</sup></td>'; } $html .= '</tr><tr>'; foreach( $votes as $dist ) { $html .= '<td><table>'; $html .= '<tr><th align="left">'.wfMsgHtml('ratinghistory-table-rating').'</th>'; for( $i = 1; $i <= 5; $i++ ) { $html .= "<td align='center' class='rfb-rating-option-".($i-1)."'>$i</td>"; } $html .= '</tr><tr>'; $html .= '<th align="left">'.wfMsgHtml("ratinghistory-table-votes").'</th>'; $html .= '<td align="center">'.$dist[0].'</td>'; $html .= '<td align="center">'.$dist[1].'</td>'; $html .= '<td align="center">'.$dist[2].'</td>'; $html .= '<td align="center">'.$dist[3].'</td>'; $html .= '<td align="center">'.$dist[4].'</td>'; $html .= "</tr></table></td>\n"; } $html .= '</tr></table>'; return $html; }
public static function addRatingLink( &$skintemplate, &$nav_urls, &$oldid, &$revid ) { # Add rating tab if( $skintemplate->getTitle() && ReaderFeedback::isPageRateable( $skintemplate->getTitle() ) ) { $nav_urls['ratinghist'] = array( 'text' => wfMsg( 'ratinghistory-link' ), 'href' => $skintemplate->makeSpecialUrl( 'RatingHistory', "target=" . wfUrlencode( "{$skintemplate->thispage}" ) ) ); } return true; }
private function submit() { global $wgUser; $dbw = wfGetDB( DB_MASTER ); # Get date timestamp... $now = wfTimestampNow(); $date = str_pad( substr( $now, 0, 8 ), 14, '0' ); if( count($this->dims) == 0 ) return self::REVIEW_ERROR; $ratings = $this->flattenRatings( $this->dims ); # Make sure revision is valid! $rev = Revision::newFromId( $this->oldid ); if( !$rev || !$rev->getTitle()->equals( $this->page ) ) { return self::REVIEW_ERROR; // opps! } $ip = wfGetIP(); if( !$wgUser->getId() && !$ip ) { return self::REVIEW_ERROR; // we need to keep track somehow } $article = new Article( $this->page ); # Check if the user is spamming reviews... if( $wgUser->pingLimiter( 'feedback' ) || $wgUser->pingLimiter() ) { return self::REVIEW_ERROR; } # Check if user already voted before... if( self::userAlreadyVoted( $this->page, $this->oldid ) ) { return self::REVIEW_DUP; } # Update review records to limit double voting! $insertRow = array( 'rfb_rev_id' => $this->oldid, 'rfb_user' => $wgUser->getId(), 'rfb_ip' => $ip, 'rfb_timestamp' => $dbw->timestamp( $now ), 'rfb_ratings' => $ratings ); # Make sure initial page data is there to begin with... $insertRows = array(); foreach( $this->dims as $tag => $val ) { if( $val < 0 ); // don't store "unsure" votes $insertRows[] = array( 'rfh_page_id' => $rev->getPage(), 'rfh_tag' => $tag, 'rfh_total' => 0, 'rfh_count' => 0, 'rfh_date' => $date ); } $dbw->insert( 'reader_feedback', $insertRow, __METHOD__, 'IGNORE' ); $dbw->insert( 'reader_feedback_history', $insertRows, __METHOD__, 'IGNORE' ); # Update aggregate data for this page over time... $touched = $dbw->timestamp( $now ); $insertRows = array(); foreach( $this->dims as $tag => $val ) { if( $val < 0 ) continue; // don't store "unsure" votes # Update daily averages $dbw->update( 'reader_feedback_history', array( 'rfh_total = rfh_total + '.intval($val), 'rfh_count = rfh_count + 1'), array( 'rfh_page_id' => $rev->getPage(), 'rfh_tag' => $tag, 'rfh_date' => $date ), __METHOD__ ); # Get effective tag values for this page.. list($aveVal,$n) = ReaderFeedback::getAverageRating( $article, $tag, true ); $insertRows[] = array( 'rfp_page_id' => $rev->getPage(), 'rfp_tag' => $tag, 'rfp_ave_val' => $aveVal, 'rfp_count' => $n, 'rfp_touched' => $touched ); } # Update recent averages $dbw->replace( 'reader_feedback_pages', array( 'PRIMARY' ), $insertRows, __METHOD__ ); # For logged in users, box should disappear if( $wgUser->getId() ) { $this->page->invalidateCache(); } # Prune expired page aggregate data if( 0 == mt_rand( 0, 99 ) ) { ReaderFeedback::purgeExpiredAverages(); } return self::REVIEW_OK; }
public function purgePage() { global $wgUploadDirectory; foreach( ReaderFeedback::getFeedbackTags() as $tag => $weight ) { $dir = "{$wgUploadDirectory}/graphs/".$this->page->getArticleId()."/{$tag}/"; if( is_dir( $dir ) ) { $handle = opendir( $dir ); if( $handle ) { while( false !== ( $file = readdir($handle) ) ) { @unlink("$dir/$file"); } closedir( $handle ); } } @rmdir( $dir ); } return true; }
function getQueryInfo() { $conds = $this->mConds; $conds[] = 'rfp_page_id = page_id'; $conds['rfp_tag'] = $this->tag; $conds['page_namespace'] = $this->namespace; // Has to be good/crappy enough switch( $this->tier ) { case 3: $conds[] = "rfp_ave_val > 3"; break; case 1: $conds[] = "rfp_ave_val < 2"; break; default: $conds[] = "rfp_ave_val >= 2 AND rfp_ave_val <= 3"; break; } // Reasonable samples only $conds[] = 'rfp_count >= '.$this->mDb->addQuotes( ReaderFeedback::getFeedbackSize() ); return array( 'tables' => array('reader_feedback_pages','page'), 'fields' => 'page_namespace,page_title,page_len,rfp_ave_val,rfp_count', 'conds' => $conds, 'options' => array( 'USE INDEX' => array('reader_feedback_pages' => 'rfp_tag_val_page') ) ); }