/**
  * Get a selector of review levels. Used for filters.
  * @param int $selected, selected level
  * @param string $all, all selector msg?
  * @param int $max max level?
  * @return string
  */
 public static function getLevelMenu($selected = null, $all = 'revreview-filter-all', $max = 2)
 {
     $s = "<label for='wpLevel'>" . wfMsgHtml('revreview-levelfilter') . "</label>\n";
     $s .= Xml::openElement('select', array('name' => 'level', 'id' => 'wpLevel'));
     if ($all !== false) {
         $s .= Xml::option(wfMsg($all), -1, $selected === -1);
     }
     $s .= Xml::option(wfMsg('revreview-lev-basic'), 0, $selected === 0);
     if (FlaggedRevs::qualityVersions()) {
         $s .= Xml::option(wfMsg('revreview-lev-quality'), 1, $selected === 1);
     }
     if ($max >= 2 && FlaggedRevs::pristineVersions()) {
         $s .= Xml::option(wfMsg('revreview-lev-pristine'), 2, $selected === 2);
     }
     # Note: Pristine not tracked at sp:QualityOversight (counts as quality)
     $s .= Xml::closeElement('select') . "\n";
     return $s;
 }
 protected function showForm()
 {
     global $wgScript;
     # Add explanatory text
     $this->getOutput()->addWikiMsg('unreviewedpages-list', $this->getLanguage()->formatNum($this->pager->getNumRows()));
     # show/hide links
     $showhide = array($this->msg('show')->escaped(), $this->msg('hide')->escaped());
     $onoff = 1 - $this->hideRedirs;
     $link = Linker::link($this->getPageTitle(), $showhide[$onoff], array(), array('hideredirs' => $onoff, 'category' => $this->category, 'namespace' => $this->namespace));
     $showhideredirs = $this->msg('whatlinkshere-hideredirs')->rawParams($link)->escaped();
     # Add form...
     $form = Html::openElement('form', array('name' => 'unreviewedpages', 'action' => $wgScript, 'method' => 'get')) . "\n";
     $form .= "<fieldset><legend>" . $this->msg('unreviewedpages-legend')->escaped() . "</legend>\n";
     $form .= Html::hidden('title', $this->getPageTitle()->getPrefixedDBKey()) . "\n";
     # Add dropdowns as needed
     if (count(FlaggedRevs::getReviewNamespaces()) > 1) {
         $form .= FlaggedRevsXML::getNamespaceMenu($this->namespace) . '&#160;';
     }
     if (FlaggedRevs::qualityVersions()) {
         $form .= FlaggedRevsXML::getLevelMenu($this->level, false, 1) . '&#160;';
     }
     $form .= "<span style='white-space: nowrap;'>" . Xml::label($this->msg('unreviewedpages-category')->text(), 'category') . '&#160;' . Xml::input('category', 30, $this->category, array('id' => 'category')) . '</span><br />';
     $form .= $showhideredirs . '&#160;&#160;';
     $form .= Xml::submitButton($this->msg('allpagessubmit')->text());
     $form .= '</fieldset>';
     $form .= Html::closeElement('form') . "\n";
     # Query may get too slow to be live...
     if (!$this->live) {
         $dbr = wfGetDB(DB_SLAVE);
         $ts = $dbr->selectField('querycache_info', 'qci_timestamp', array('qci_type' => 'fr_unreviewedpages'), __METHOD__);
         if ($ts) {
             $ts = wfTimestamp(TS_MW, $ts);
             $td = $this->getLanguage()->timeanddate($ts);
             $d = $this->getLanguage()->date($ts);
             $t = $this->getLanguage()->time($ts);
             $form .= $this->msg('perfcachedts', $td, $d, $t)->parseAsBlock();
         } else {
             $form .= $this->msg('perfcached')->parseAsBlock();
         }
     }
     $this->getOutput()->addHTML($form);
 }
 protected function showRatingIcon()
 {
     if (FlaggedRevs::useOnlyIfProtected()) {
         // If there is only one quality level and we have tabs to know
         // which version we are looking at, then just use the lock icon...
         return FlaggedRevs::qualityVersions();
     }
     return true;
 }
 private function showForm()
 {
     global $wgScript;
     $this->getOutput()->addHTML(Xml::openElement('form', array('name' => 'qualityoversight', 'action' => $wgScript, 'method' => 'get')) . '<fieldset><legend>' . $this->msg('qualityoversight-legend')->escaped() . '</legend><p>' . Html::hidden('title', $this->getPageTitle()->getPrefixedDBKey()) . FlaggedRevsXML::getNamespaceMenu($this->namespace) . '&#160;' . (FlaggedRevs::qualityVersions() ? FlaggedRevsXML::getLevelMenu($this->level, 'revreview-filter-all', 1) . '&#160;' : "") . Xml::inputLabel($this->msg('specialloguserlabel')->text(), 'user', 'user', 20, $this->user) . '<br />' . FlaggedRevsXML::getStatusFilterMenu($this->status) . '&#160;' . FlaggedRevsXML::getAutoFilterMenu($this->automatic) . '&#160;' . Xml::submitButton($this->msg('go')->text()) . '</p></fieldset>' . Xml::closeElement('form'));
 }
 protected static function load()
 {
     global $wgFlaggedRevsTags, $wgFlaggedRevTags;
     if (self::$loaded) {
         return true;
     }
     if (!FlaggedRevsSetup::isReady()) {
         // sanity
         throw new MWException('FlaggedRevs config loaded too soon! Possibly before LocalSettings.php!');
     }
     self::$loaded = true;
     $flaggedRevsTags = null;
     if (isset($wgFlaggedRevTags)) {
         $flaggedRevsTags = $wgFlaggedRevTags;
         // b/c
         wfWarn('Please use $wgFlaggedRevsTags instead of $wgFlaggedRevTags in config.');
     } elseif (isset($wgFlaggedRevsTags)) {
         $flaggedRevsTags = $wgFlaggedRevsTags;
     }
     # Assume true, then set to false if needed
     if (!empty($flaggedRevsTags)) {
         self::$qualityVersions = true;
         self::$pristineVersions = true;
         self::$binaryFlagging = count($flaggedRevsTags) <= 1;
     }
     foreach ($flaggedRevsTags as $tag => $levels) {
         # Sanity checks
         $safeTag = htmlspecialchars($tag);
         if (!preg_match('/^[a-zA-Z]{1,20}$/', $tag) || $safeTag !== $tag) {
             throw new MWException('FlaggedRevs given invalid tag name!');
         }
         # Define "quality" and "pristine" reqs
         if (is_array($levels)) {
             $minQL = $levels['quality'];
             $minPL = $levels['pristine'];
             $ratingLevels = $levels['levels'];
             # B/C, $levels is just an integer (minQL)
         } else {
             global $wgFlaggedRevPristine, $wgFlaggedRevValues;
             $ratingLevels = isset($wgFlaggedRevValues) ? $wgFlaggedRevValues : 1;
             $minQL = $levels;
             // an integer
             $minPL = isset($wgFlaggedRevPristine) ? $wgFlaggedRevPristine : $ratingLevels + 1;
             wfWarn('Please update the format of $wgFlaggedRevsTags in config.');
         }
         # Set FlaggedRevs tags
         self::$dimensions[$tag] = array();
         for ($i = 0; $i <= $ratingLevels; $i++) {
             self::$dimensions[$tag][$i] = "{$tag}-{$i}";
         }
         if ($ratingLevels > 1) {
             self::$binaryFlagging = false;
             // more than one level
         }
         # Sanity checks
         if (!is_integer($minQL) || !is_integer($minPL)) {
             throw new MWException('FlaggedRevs given invalid tag value!');
         }
         if ($minQL > $ratingLevels) {
             self::$qualityVersions = false;
             self::$pristineVersions = false;
         }
         if ($minPL > $ratingLevels) {
             self::$pristineVersions = false;
         }
         self::$minQL[$tag] = max($minQL, 1);
         self::$minPL[$tag] = max($minPL, 1);
         self::$minSL[$tag] = 1;
     }
     global $wgFlaggedRevsTagsRestrictions, $wgFlagRestrictions;
     if (isset($wgFlagRestrictions)) {
         self::$tagRestrictions = $wgFlagRestrictions;
         // b/c
         wfWarn('Please use $wgFlaggedRevsTagsRestrictions instead of $wgFlagRestrictions in config.');
     } else {
         self::$tagRestrictions = $wgFlaggedRevsTagsRestrictions;
     }
     # Make sure that the restriction levels are unique
     global $wgFlaggedRevsRestrictionLevels;
     self::$restrictionLevels = array_unique($wgFlaggedRevsRestrictionLevels);
     self::$restrictionLevels = array_filter(self::$restrictionLevels, 'strlen');
     # Make sure no talk namespaces are in review namespace
     global $wgFlaggedRevsNamespaces;
     foreach ($wgFlaggedRevsNamespaces as $ns) {
         if (MWNamespace::isTalk($ns)) {
             throw new MWException('FlaggedRevs given talk namespace in $wgFlaggedRevsNamespaces!');
         } elseif ($ns == NS_MEDIAWIKI) {
             throw new MWException('FlaggedRevs given NS_MEDIAWIKI in $wgFlaggedRevsNamespaces!');
         }
     }
     self::$reviewNamespaces = $wgFlaggedRevsNamespaces;
     # Handle $wgFlaggedRevsAutoReview settings
     global $wgFlaggedRevsAutoReview, $wgFlaggedRevsAutoReviewNew;
     if (is_int($wgFlaggedRevsAutoReview)) {
         self::$autoReviewConfig = $wgFlaggedRevsAutoReview;
     } else {
         // b/c
         if ($wgFlaggedRevsAutoReview) {
             self::$autoReviewConfig = FR_AUTOREVIEW_CHANGES;
         }
         wfWarn('$wgFlaggedRevsAutoReview is now a bitfield instead of a boolean.');
     }
     if (isset($wgFlaggedRevsAutoReviewNew)) {
         // b/c
         self::$autoReviewConfig = $wgFlaggedRevsAutoReviewNew ? self::$autoReviewConfig |= FR_AUTOREVIEW_CREATION : self::$autoReviewConfig & ~FR_AUTOREVIEW_CREATION;
         wfWarn('$wgFlaggedRevsAutoReviewNew is deprecated; use $wgFlaggedRevsAutoReview.');
     }
     return true;
 }
 public function formatRow($row)
 {
     $title = Title::newFromRow($row);
     $link = Linker::link($title);
     # Link to page
     $dirmark = $this->getLanguage()->getDirMark();
     # Direction mark
     $stxt = '';
     # Size (bytes)
     if (!is_null($size = $row->page_len)) {
         if ($size == 0) {
             $stxt = ' <small>' . $this->msg('historyempty')->escaped() . '</small>';
         } else {
             $stxt = ' <small>' . $this->msg('historysize')->numParams($size)->escaped() . '</small>';
         }
     }
     # Link to list of reviewed versions for page
     $list = Linker::linkKnown(SpecialPage::getTitleFor('ReviewedVersions'), $this->msg('reviewedpages-all')->escaped(), array(), 'page=' . $title->getPrefixedUrl());
     # Link to highest tier rev
     $best = '';
     if (FlaggedRevs::qualityVersions()) {
         $best = Linker::linkKnown($title, $this->msg('reviewedpages-best')->escaped(), array(), 'stableid=best');
         $best = " [{$best}]";
     }
     return "<li>{$link} {$dirmark} {$stxt} ({$list}){$best}</li>";
 }
 /**
  * Get a FlaggedRevision of the stable version of a title.
  * Skips tracking tables to figure out new stable version.
  * @param Title $title, page title
  * @param int $flags (FR_MASTER, FR_FOR_UPDATE)
  * @param array $config, optional page config (use to skip queries)
  * @param string $precedence (latest,quality,pristine)
  * @return FlaggedRevision|null (null on failure)
  */
 public static function determineStable(Title $title, $flags = 0, $config = array(), $precedence = 'latest')
 {
     if (!FlaggedRevs::inReviewNamespace($title)) {
         return null;
         // short-circuit
     }
     $options = array();
     # User master/slave as appropriate...
     if ($flags & FR_FOR_UPDATE || $flags & FR_MASTER) {
         $db = wfGetDB(DB_MASTER);
         if ($flags & FR_FOR_UPDATE) {
             $options[] = 'FOR UPDATE';
         }
         $pageId = $title->getArticleID(Title::GAID_FOR_UPDATE);
     } else {
         $db = wfGetDB(DB_SLAVE);
         $pageId = $title->getArticleID();
     }
     if (!$pageId) {
         return null;
         // short-circuit query
     }
     # Get visiblity settings to see if page is reviewable...
     if (FlaggedRevs::useOnlyIfProtected()) {
         if (empty($config)) {
             $config = FRPageConfig::getStabilitySettings($title, $flags);
         }
         if (!$config['override']) {
             return null;
             // page is not reviewable; no stable version
         }
     }
     $baseConds = array('fr_page_id' => $pageId, 'rev_id = fr_rev_id', 'rev_page = fr_page_id', $db->bitAnd('rev_deleted', Revision::DELETED_TEXT) . ' = 0');
     $options['ORDER BY'] = 'fr_rev_timestamp DESC';
     $row = null;
     if ($precedence !== 'latest') {
         # Look for the latest pristine revision...
         if (FlaggedRevs::pristineVersions()) {
             $prow = $db->selectRow(array('flaggedrevs', 'revision'), self::selectFields(), array_merge($baseConds, array('fr_quality' => FR_PRISTINE)), __METHOD__, $options);
             # Looks like a plausible revision
             $row = $prow ? $prow : $row;
         }
         if ($row && $precedence === 'pristine') {
             // we have what we want already
             # Look for the latest quality revision...
         } elseif (FlaggedRevs::qualityVersions()) {
             // If we found a pristine rev above, this one must be newer...
             $newerClause = $row ? array('fr_rev_timestamp > ' . $db->addQuotes($row->fr_rev_timestamp)) : array();
             $qrow = $db->selectRow(array('flaggedrevs', 'revision'), self::selectFields(), array_merge($baseConds, array('fr_quality' => FR_QUALITY), $newerClause), __METHOD__, $options);
             $row = $qrow ? $qrow : $row;
         }
     }
     # Do we have one? If not, try the latest reviewed revision...
     if (!$row) {
         $row = $db->selectRow(array('flaggedrevs', 'revision'), self::selectFields(), $baseConds, __METHOD__, $options);
         if (!$row) {
             return null;
         }
     }
     $frev = new self($row);
     $frev->mTitle = $title;
     return $frev;
 }
 public function formatRow($row)
 {
     $css = $quality = $underReview = '';
     $title = Title::newFromRow($row);
     $stxt = ChangesList::showCharacterDifference($row->rev_len, $row->page_len);
     # Page links...
     $link = Linker::link($title);
     $hist = Linker::linkKnown($title, wfMsgHtml('hist'), array(), 'action=history');
     $review = Linker::linkKnown($title, wfMsg('pendingchanges-diff'), array(), array('diff' => 'cur', 'oldid' => $row->stable) + FlaggedRevs::diffOnlyCGI());
     # Show quality level if there are several
     if (FlaggedRevs::qualityVersions()) {
         $quality = $row->quality ? wfMsgHtml('revreview-lev-quality') : wfMsgHtml('revreview-lev-basic');
         $quality = " <b>[{$quality}]</b>";
     }
     # Is anybody watching?
     if (!$this->including() && $this->getUser()->isAllowed('unreviewedpages')) {
         $uw = FRUserActivity::numUsersWatchingPage($title);
         $watching = $uw ? wfMsgExt('pendingchanges-watched', 'parsemag', $this->getLang()->formatNum($uw)) : wfMsgHtml('pendingchanges-unwatched');
         $watching = " {$watching}";
     } else {
         $uw = -1;
         $watching = '';
         // leave out data
     }
     # Get how long the first unreviewed edit has been waiting...
     if ($row->pending_since) {
         $firstPendingTime = wfTimestamp(TS_UNIX, $row->pending_since);
         $hours = ($this->currentUnixTS - $firstPendingTime) / 3600;
         // After three days, just use days
         if ($hours > 3 * 24) {
             $days = round($hours / 24, 0);
             $age = wfMsgExt('pendingchanges-days', 'parsemag', $this->getLang()->formatNum($days));
             // If one or more hours, use hours
         } elseif ($hours >= 1) {
             $hours = round($hours, 0);
             $age = wfMsgExt('pendingchanges-hours', 'parsemag', $this->getLang()->formatNum($hours));
         } else {
             $age = wfMsg('pendingchanges-recent');
             // hot off the press :)
         }
         // Oh-noes!
         $css = self::getLineClass($hours, $uw);
         $css = $css ? " class='{$css}'" : "";
     } else {
         $age = "";
         // wtf?
     }
     # Show if a user is looking at this page
     list($u, $ts) = FRUserActivity::getUserReviewingDiff($row->stable, $row->page_latest);
     if ($u !== null) {
         $underReview = ' <span class="fr-under-review">' . wfMsgHtml('pendingchanges-viewing') . '</span>';
     }
     return "<li{$css}>{$link} ({$hist}) {$stxt} ({$review}) <i>{$age}</i>" . "{$quality}{$watching}{$underReview}</li>";
 }