/** * Get a selector of reviewable namespaces * @param int $selected, namespace selected * @param $all Mixed: Value of an item denoting all namespaces, or null to omit * @return string */ public static function getNamespaceMenu($selected = null, $all = null) { global $wgContLang; $namespaces = FlaggedRevs::getReviewNamespaces(); $s = "<label for='namespace'>" . wfMsgHtml('namespace') . "</label>"; if ($selected !== '') { if (is_null($selected)) { # No namespace selected; let exact match work without hitting Main $selected = ''; } else { # Let input be numeric strings without breaking the empty match. $selected = intval($selected); } } $s .= "\n<select id='namespace' name='namespace' class='namespaceselector'>\n"; $arr = $wgContLang->getFormattedNamespaces(); if (!is_null($all)) { $arr = array($all => wfMsg('namespacesall')) + $arr; // should be first } foreach ($arr as $index => $name) { # Content pages only (except 'all') if ($index !== $all && !in_array($index, $namespaces)) { continue; } $name = $index !== 0 ? $name : wfMsg('blanknamespace'); if ($index === $selected) { $s .= "\t" . Xml::element("option", array("value" => $index, "selected" => "selected"), $name) . "\n"; } else { $s .= "\t" . Xml::element("option", array("value" => $index), $name) . "\n"; } } $s .= "</select>\n"; return $s; }
protected function autoreview_current(User $user) { $this->output("Auto-reviewing all current page versions...\n"); if (!$user->getID()) { $this->output("Invalid user specified.\n"); return; } elseif (!$user->isAllowed('review')) { $this->output("User specified (id: {$user->getID()}) does not have \"review\" rights.\n"); return; } $db = wfGetDB(DB_MASTER); $this->output("Reviewer username: "******"\n"); $start = $db->selectField('page', 'MIN(page_id)', false, __METHOD__); $end = $db->selectField('page', 'MAX(page_id)', false, __METHOD__); if (is_null($start) || is_null($end)) { $this->output("...page table seems to be empty.\n"); return; } # Do remaining chunk $end += $this->mBatchSize - 1; $blockStart = $start; $blockEnd = $start + $this->mBatchSize - 1; $count = 0; $changed = 0; $flags = FlaggedRevs::quickTags(FR_CHECKED); // Assume basic level while ($blockEnd <= $end) { $this->output("...doing page_id from {$blockStart} to {$blockEnd}\n"); $res = $db->select(array('page', 'revision'), '*', array("page_id BETWEEN {$blockStart} AND {$blockEnd}", 'page_namespace' => FlaggedRevs::getReviewNamespaces(), 'rev_id = page_latest'), __METHOD__); # Go through and autoreview the current version of every page... foreach ($res as $row) { $title = Title::newFromRow($row); $rev = Revision::newFromRow($row); # Is it already reviewed? $frev = FlaggedRevision::newFromTitle($title, $row->page_latest, FR_MASTER); # Rev should exist, but to be safe... if (!$frev && $rev) { $article = new Article($title); $db->begin(); FlaggedRevs::autoReviewEdit($article, $user, $rev, $flags, true); FlaggedRevs::HTMLCacheUpdates($article->getTitle()); $db->commit(); $changed++; } $count++; } $db->freeResult($res); $blockStart += $this->mBatchSize - 1; $blockEnd += $this->mBatchSize - 1; // XXX: Don't let deferred jobs array get absurdly large (bug 24375) DeferredUpdates::doUpdates('commit'); wfWaitForSlaves(5); } $this->output("Auto-reviewing of all pages complete ..." . "{$count} rows [{$changed} changed]\n"); }
public function getAllowedParams() { $namespaces = FlaggedRevs::getReviewNamespaces(); return array('start' => array(ApiBase::PARAM_TYPE => 'integer'), 'end' => array(ApiBase::PARAM_TYPE => 'integer'), 'dir' => array(ApiBase::PARAM_DFLT => 'newer', ApiBase::PARAM_TYPE => array('newer', 'older')), 'namespace' => array(ApiBase::PARAM_DFLT => !$namespaces ? NS_MAIN : $namespaces[0], ApiBase::PARAM_TYPE => 'namespace', ApiBase::PARAM_ISMULTI => true), 'filterredir' => array(ApiBase::PARAM_DFLT => 'all', ApiBase::PARAM_TYPE => array('redirects', 'nonredirects', 'all')), 'filterlevel' => array(ApiBase::PARAM_DFLT => null, ApiBase::PARAM_TYPE => 'integer', ApiBase::PARAM_MIN => 0, ApiBase::PARAM_MAX => 2), 'limit' => array(ApiBase::PARAM_DFLT => 10, ApiBase::PARAM_TYPE => 'limit', ApiBase::PARAM_MIN => 1, ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2)); }
public function getAllowedParams() { $namespaces = FlaggedRevs::getReviewNamespaces(); // Replace '' with more readable 'none' in autoreview restiction levels $autoreviewLevels = FlaggedRevs::getRestrictionLevels(); $autoreviewLevels[] = 'none'; return array('start' => array(ApiBase::PARAM_TYPE => 'integer'), 'end' => array(ApiBase::PARAM_TYPE => 'integer'), 'dir' => array(ApiBase::PARAM_DFLT => 'newer', ApiBase::PARAM_TYPE => array('newer', 'older')), 'namespace' => array(ApiBase::PARAM_DFLT => null, ApiBase::PARAM_TYPE => 'namespace', ApiBase::PARAM_ISMULTI => true), 'default' => array(ApiBase::PARAM_DFLT => null, ApiBase::PARAM_TYPE => array('latest', 'stable')), 'autoreview' => array(ApiBase::PARAM_DFLT => null, ApiBase::PARAM_TYPE => $autoreviewLevels), 'limit' => array(ApiBase::PARAM_DFLT => 10, ApiBase::PARAM_TYPE => 'limit', ApiBase::PARAM_MIN => 1, ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2)); }
/** * @param int $namespace (null for "all") * @param string $autoreview ('' for "all", 'none' for no restriction) */ function __construct($form, $conds = array(), $namespace, $autoreview, $indef) { $this->mForm = $form; $this->mConds = $conds; $this->indef = $indef; // Must be content pages... $validNS = FlaggedRevs::getReviewNamespaces(); if (is_integer($namespace)) { if (!in_array($namespace, $validNS)) { $namespace = $validNS; // fallback to "all" } } else { $namespace = $validNS; // "all" } $this->namespace = $namespace; if ($autoreview === 'none') { $autoreview = ''; // 'none' => '' } elseif ($autoreview === '') { $autoreview = null; // '' => null } $this->autoreview = $autoreview; parent::__construct(); }
function __construct($form, $conds = array(), $type = 0, $namespace = 0, $hideRedirs = 1) { $this->mForm = $form; $this->mConds = $conds; $this->type = $type; # Must be a content page... if (!is_null($namespace)) { $namespace = intval($namespace); } $vnamespaces = FlaggedRevs::getReviewNamespaces(); if (is_null($namespace) || !in_array($namespace, $vnamespaces)) { $namespace = !$vnamespaces ? -1 : $vnamespaces[0]; } $this->namespace = $namespace; $this->hideRedirs = $hideRedirs; parent::__construct(); }
function __construct($form, $live, $namespace, $redirs = false, $category = null, $level = 0) { $this->mForm = $form; $this->live = (bool) $live; # Must be a content page... if (!is_null($namespace)) { $namespace = (int) $namespace; } $vnamespaces = FlaggedRevs::getReviewNamespaces(); # Must be a single NS for perfomance reasons if (is_null($namespace) || !in_array($namespace, $vnamespaces)) { $namespace = !$vnamespaces ? -1 : $vnamespaces[0]; } $this->namespace = $namespace; $this->category = $category ? str_replace(' ', '_', $category) : null; $this->level = intval($level); $this->showredirs = (bool) $redirs; parent::__construct(); // Don't get too expensive $this->mLimitsShown = array(20, 50); $this->setLimit($this->mLimit); // apply max limit }
function __construct($form, $namespace, $level = -1, $category = '', $size = null, $watched = false, $stable = false) { $this->mForm = $form; # Must be a content page... $vnamespaces = FlaggedRevs::getReviewNamespaces(); if (is_null($namespace)) { $namespace = $vnamespaces; } else { $namespace = intval($namespace); } # Sanity check if (!in_array($namespace, $vnamespaces)) { $namespace = $vnamespaces; } $this->namespace = $namespace; # Sanity check level: 0 = checked; 1 = quality; 2 = pristine $this->level = $level >= 0 && $level <= 2 ? $level : -1; $this->category = $category ? str_replace(' ', '_', $category) : null; $this->size = $size !== null ? intval($size) : null; $this->watched = (bool) $watched; $this->stable = $stable && !FlaggedRevs::isStableShownByDefault() && !FlaggedRevs::useOnlyIfProtected(); parent::__construct(); # Don't get too expensive $this->mLimitsShown = array(20, 50, 100); $this->setLimit($this->mLimit); // apply max limit }
function __construct($form, $level = -1, $category = '', $tag = '') { $this->mForm = $form; # Must be a content page... $this->namespace = FlaggedRevs::getReviewNamespaces(); # Sanity check level: 0 = checked; 1 = quality; 2 = pristine $this->level = $level >= 0 && $level <= 2 ? $level : -1; $this->tag = $tag; $this->category = $category ? str_replace(' ', '_', $category) : null; parent::__construct(); // Don't get to expensive $this->mLimitsShown = array(20, 50, 100); $this->setLimit($this->mLimit); // apply max limit }
public static function stableDumpQuery(array &$tables, array &$opts, array &$join) { $namespaces = FlaggedRevs::getReviewNamespaces(); if ($namespaces) { $tables[] = 'flaggedpages'; $opts['ORDER BY'] = 'fp_page_id ASC'; $opts['USE INDEX'] = array('flaggedpages' => 'PRIMARY'); $join['page'] = array('INNER JOIN', array('page_id = fp_page_id', 'page_namespace' => $namespaces)); $join['revision'] = array('INNER JOIN', 'rev_page = fp_page_id AND rev_id = fp_stable'); } return false; // final }
/** * Get edit review time statistics (as recent as possible) * @param $dbcache Database cache object * @param $users string "anons" or "users" * @return Array associative */ private static function getEditReviewTimes($dbCache, $users = 'anons') { $result = array('average' => 0, 'median' => 0, 'percTable' => array(), 'sampleSize' => 0, 'sampleStartTS' => null, 'sampleEndTS' => null); if (FlaggedRevs::useOnlyIfProtected()) { return $result; // disabled } $aveRT = $medianRT = 0; $rPerTable = array(); // review wait percentiles # Only go so far back...otherwise we will get garbage values due to # the fact that FlaggedRevs wasn't enabled until after a while. $dbr = wfGetDB(DB_SLAVE); $installedUnix = (int) $dbr->selectField('logging', 'UNIX_TIMESTAMP( MIN(log_timestamp) )', array('log_type' => 'review')); if (!$installedUnix) { $installedUnix = wfTimestamp(TS_UNIX); // now } $encInstalled = $dbr->addQuotes($dbr->timestamp($installedUnix)); # Skip the most recent recent revs as they are likely to just # be WHERE condition misses. This also gives us more data to use. # Lastly, we want to avoid bias that would make the time too low # since new revisions could not have "took a long time to sight". $worstLagTS = $dbr->timestamp(); // now $last = '0'; while (true) { // should almost always be ~1 pass # Get the page with the worst pending lag... $row = $dbr->selectRow(array('flaggedpage_pending', 'flaggedrevs'), array('fpp_page_id', 'fpp_rev_id', 'fpp_pending_since', 'fr_timestamp'), array('fpp_quality' => 0, 'fpp_pending_since > ' . $encInstalled, 'fr_page_id = fpp_page_id AND fr_rev_id = fpp_rev_id', 'fpp_pending_since > ' . $dbr->addQuotes($last)), __METHOD__, array('ORDER BY' => 'fpp_pending_since ASC', 'USE INDEX' => array('flaggedpage_pending' => 'fpp_quality_pending'))); if (!$row) { break; } # Find the newest revision at the time the page was reviewed, # this is the one that *should* have been reviewed. $idealRev = (int) $dbr->selectField('revision', 'rev_id', array('rev_page' => $row->fpp_page_id, 'rev_timestamp < ' . $dbr->addQuotes($row->fr_timestamp)), __METHOD__, array('ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 1)); if ($row->fpp_rev_id >= $idealRev) { $worstLagTS = $row->fpp_pending_since; break; // sane $worstLagTS found # Fudge factor to prevent deliberate reviewing of non-current revisions # from squeezing the range. Shouldn't effect anything otherwise. } else { $last = $row->fpp_pending_since; // next iteration } } # User condition (anons/users) if ($users === 'anons') { $userCondition = 'rev_user = 0'; } elseif ($users === 'users') { $userCondition = 'rev_user = 1'; } else { throw new MWException('Invalid $users param given.'); } # Avoid having to censor data # Note: if no edits pending, $worstLagTS is the cur time just before we checked # for the worst lag. Thus, new edits *right* after the check are properly excluded. $maxTSUnix = wfTimestamp(TS_UNIX, $worstLagTS) - 1; // all edits later reviewed $encMaxTS = $dbr->addQuotes($dbr->timestamp($maxTSUnix)); # Use a one week time range $days = 7; $minTSUnix = $maxTSUnix - $days * 86400; $encMinTS = $dbr->addQuotes($dbr->timestamp($minTSUnix)); # Approximate the number rows to scan $rows = $dbr->estimateRowCount('revision', '1', array($userCondition, "rev_timestamp BETWEEN {$encMinTS} AND {$encMaxTS}")); # If the range doesn't have many rows (like on small wikis), use 30 days if ($rows < 500) { $days = 30; $minTSUnix = $maxTSUnix - $days * 86400; $encMinTS = $dbr->addQuotes($dbr->timestamp($minTSUnix)); # Approximate rows to scan $rows = $dbr->estimateRowCount('revision', '1', array($userCondition, "rev_timestamp BETWEEN {$encMinTS} AND {$encMaxTS}")); # If the range doesn't have many rows (like on really tiny wikis), use 90 days if ($rows < 500) { $days = 90; $minTSUnix = $maxTSUnix - $days * 86400; } } $sampleSize = 1500; // sample size # Sanity check the starting timestamp $minTSUnix = max($minTSUnix, $installedUnix); $encMinTS = $dbr->addQuotes($dbr->timestamp($minTSUnix)); # Get timestamp boundaries $timeCondition = "rev_timestamp BETWEEN {$encMinTS} AND {$encMaxTS}"; # Get mod for edit spread $ecKey = wfMemcKey('flaggedrevs', 'anonEditCount', $days); $edits = (int) $dbCache->get($ecKey); if (!$edits) { $edits = (int) $dbr->selectField(array('page', 'revision'), 'COUNT(*)', array($userCondition, $timeCondition, 'page_id = rev_page', 'page_namespace' => FlaggedRevs::getReviewNamespaces())); $dbCache->set($ecKey, $edits, 14 * 24 * 3600); // cache for 2 weeks } $mod = max(floor($edits / $sampleSize), 1); # $mod >= 1 # For edits that started off pending, how long do they take to get reviewed? # Edits started off pending if made when a flagged rev of the page already existed. # Get the *first* reviewed rev *after* each edit and get the time difference. $res = $dbr->select(array('revision', 'p' => 'flaggedrevs', 'n' => 'flaggedrevs'), array('MIN(rev_timestamp) AS rt', 'MIN(n.fr_timestamp) AS nft', 'MAX(p.fr_rev_id)'), array($userCondition, $timeCondition, "(rev_id % {$mod}) = 0"), __METHOD__, array('GROUP BY' => array('rev_timestamp', 'rev_id'), 'USE INDEX' => array('revision' => 'user_timestamp'), 'STRAIGHT_JOIN'), array('p' => array('INNER JOIN', array('p.fr_page_id = rev_page', 'p.fr_rev_id < rev_id', 'p.fr_timestamp < rev_timestamp')), 'n' => array('INNER JOIN', array('n.fr_page_id = rev_page', 'n.fr_rev_id >= rev_id', 'n.fr_timestamp >= rev_timestamp')))); $secondsR = 0; // total wait seconds for edits later reviewed $secondsP = 0; // total wait seconds for edits still pending $aveRT = $medianRT = 0; $times = array(); if ($dbr->numRows($res)) { # Get the elapsed times revs were pending (flagged time - edit time) foreach ($res as $row) { $time = wfTimestamp(TS_UNIX, $row->nft) - wfTimestamp(TS_UNIX, $row->rt); $time = max($time, 0); // sanity $secondsR += $time; $times[] = $time; } $sampleSize = count($times); $aveRT = ($secondsR + $secondsP) / $sampleSize; // sample mean sort($times); // order smallest -> largest // Sample median $rank = round(count($times) / 2 + 0.5) - 1; $medianRT = $times[$rank]; // Make percentile tabulation data $doPercentiles = array(35, 45, 55, 65, 75, 85, 90, 95); foreach ($doPercentiles as $percentile) { $rank = round($percentile * count($times) / 100 + 0.5) - 1; $rPerTable[$percentile] = $times[$rank]; } $result['average'] = $aveRT; $result['median'] = $medianRT; $result['percTable'] = $rPerTable; $result['sampleSize'] = count($times); $result['sampleStartTS'] = $minTSUnix; $result['sampleEndTS'] = $maxTSUnix; } return $result; }
public function execute($par) { global $wgContLang, $wgFlaggedRevsStats, $wgFlaggedRevsProtection; $out = $this->getOutput(); $lang = $this->getLanguage(); $this->setHeaders(); $this->db = wfGetDB(DB_SLAVE); $this->maybeUpdate(); $ec = $this->getEditorCount(); $rc = $this->getReviewerCount(); $mt = $this->getMeanReviewWaitAnon(); $mdt = $this->getMedianReviewWaitAnon(); $pt = $this->getMeanPendingWait(); $pData = $this->getReviewPercentilesAnon(); $timestamp = $this->getLastUpdate(); $out->addWikiMsg('validationstatistics-users', $lang->formatnum($ec), $lang->formatnum($rc)); # Most of the output depends on background queries if (!$this->readyForQuery()) { return false; } # Is there a review time table available? if (count($pData)) { $headerRows = $dataRows = ''; foreach ($pData as $percentile => $perValue) { $headerRows .= "<th>P<sub>" . intval($percentile) . "</sub></th>"; $dataRows .= '<td>' . $lang->formatTimePeriod($perValue, 'avoidminutes') . '</td>'; } $css = 'wikitable flaggedrevs_stats_table'; $reviewChart = "<table class='{$css}' style='white-space: nowrap;'>\n"; $reviewChart .= "<tr align='center'>{$headerRows}</tr>\n"; $reviewChart .= "<tr align='center'>{$dataRows}</tr>\n"; $reviewChart .= "</table>\n"; } else { $reviewChart = ''; } if ($timestamp != '-') { # Show "last updated"... $out->addWikiMsg('validationstatistics-lastupdate', $lang->date($timestamp, true), $lang->time($timestamp, true)); } $out->addHtml('<hr/>'); # Show pending time stats... $out->addWikiMsg('validationstatistics-pndtime', $lang->formatTimePeriod($pt, 'avoidminutes')); # Show review time stats... if (!FlaggedRevs::useOnlyIfProtected()) { $out->addWikiMsg('validationstatistics-revtime', $lang->formatTimePeriod($mt, 'avoidminutes'), $lang->formatTimePeriod($mdt, 'avoidminutes'), $reviewChart); } # Show per-namespace stats table... $out->addWikiMsg('validationstatistics-table'); $out->addHTML(Xml::openElement('table', array('class' => 'wikitable flaggedrevs_stats_table'))); $out->addHTML("<tr>\n"); // Headings (for a positive grep result): // validationstatistics-ns, validationstatistics-total, validationstatistics-stable, // validationstatistics-latest, validationstatistics-synced, validationstatistics-old, // validationstatistics-unreviewed $msgs = array('ns', 'total', 'stable', 'latest', 'synced', 'old'); // our headings if (!$wgFlaggedRevsProtection) { $msgs[] = 'unreviewed'; } foreach ($msgs as $msg) { $out->addHTML('<th>' . $this->msg("validationstatistics-{$msg}")->parse() . '</th>'); } $out->addHTML("</tr>\n"); $namespaces = FlaggedRevs::getReviewNamespaces(); foreach ($namespaces as $namespace) { $total = $this->getTotalPages($namespace); $reviewed = $this->getReviewedPages($namespace); $synced = $this->getSyncedPages($namespace); if ($total === '-' || $reviewed === '-' || $synced === '-') { continue; // NS added to config recently? } $NsText = $wgContLang->getFormattedNsText($namespace); $NsText = $NsText ? $NsText : $this->msg('blanknamespace')->escaped(); $percRev = intval($total) == 0 ? '-' : $this->msg('parentheses', $this->msg('percent')->numParams(sprintf('%4.2f', 100 * intval($reviewed) / intval($total)))->escaped())->text(); $percLatest = intval($total) == 0 ? '-' : $this->msg('parentheses', $this->msg('percent')->numParams(sprintf('%4.2f', 100 * intval($synced) / intval($total)))->escaped())->text(); $percSynced = intval($reviewed) == 0 ? '-' : $this->msg('percent')->numParams(sprintf('%4.2f', 100 * intval($synced) / intval($reviewed)))->escaped(); $outdated = intval($reviewed) - intval($synced); $outdated = $lang->formatnum(max(0, $outdated)); // lag between queries $unreviewed = intval($total) - intval($reviewed); $unreviewed = $lang->formatnum(max(0, $unreviewed)); // lag between queries $out->addHTML("<tr align='center'>\n\t\t\t\t\t<td>" . htmlspecialchars($NsText) . "</td>\n\t\t\t\t\t<td>" . htmlspecialchars($lang->formatnum($total)) . "</td>\n\t\t\t\t\t<td>" . htmlspecialchars($lang->formatnum($reviewed) . $wgContLang->getDirMark()) . " <i>{$percRev}</i>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>" . htmlspecialchars($lang->formatnum($synced) . $wgContLang->getDirMark()) . " <i>{$percLatest}</i>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>" . $percSynced . "</td>\n\t\t\t\t\t<td>" . Linker::linkKnown(SpecialPage::getTitleFor('PendingChanges'), htmlspecialchars($outdated), array(), array('namespace' => $namespace)) . "</td>"); if (!$wgFlaggedRevsProtection) { $out->addHTML("\n\t\t\t\t\t<td>" . Linker::linkKnown(SpecialPage::getTitleFor('UnreviewedPages'), htmlspecialchars($unreviewed), array(), array('namespace' => $namespace)) . "</td>"); } $out->addHTML("\n\t\t\t\t</tr>"); } $out->addHTML(Xml::closeElement('table')); # Is there a top X user list? If so, then show it... $data = $this->getTopReviewers(); if (is_array($data) && count($data)) { $out->addWikiMsg('validationstatistics-utable', $lang->formatNum($wgFlaggedRevsStats['topReviewersCount']), $lang->formatNum($wgFlaggedRevsStats['topReviewersHours'])); $css = 'wikitable flaggedrevs_stats_table'; $reviewChart = "<table class='{$css}' style='white-space: nowrap;'>\n"; $reviewChart .= '<tr><th>' . $this->msg('validationstatistics-user')->escaped() . '</th><th>' . $this->msg('validationstatistics-reviews')->escaped() . '</th></tr>'; foreach ($data as $userId => $reviews) { $reviewChart .= '<tr><td>' . htmlspecialchars(User::whois($userId)) . '</td><td>' . $lang->formatNum($reviews) . '</td></tr>'; } $reviewChart .= "</table>\n"; $out->addHTML($reviewChart); } return true; }
protected static function maybeAddBacklogNotice(OutputPage &$out) { global $wgUser; if (!$wgUser->isAllowed('review')) { return true; // not relevant to user } $namespaces = FlaggedRevs::getReviewNamespaces(); $watchlist = SpecialPage::getTitleFor('Watchlist'); # Add notice to watchlist about pending changes... if ($out->getTitle()->equals($watchlist) && $namespaces) { $dbr = wfGetDB(DB_SLAVE, 'watchlist'); // consistency with watchlist $watchedOutdated = (bool) $dbr->selectField(array('watchlist', 'page', 'flaggedpages'), '1', array('wl_user' => $wgUser->getId(), 'wl_namespace' => $namespaces, 'wl_namespace = page_namespace', 'wl_title = page_title', 'fp_page_id = page_id', 'fp_pending_since IS NOT NULL'), __METHOD__); # Give a notice if pages on the users's wachlist have pending edits if ($watchedOutdated) { $css = 'plainlinks fr-watchlist-pending-notice'; $out->prependHTML("<div id='mw-fr-watchlist-pending-notice' class='{$css}'>" . wfMsgExt('flaggedrevs-watched-pending', 'parseinline') . "</div>"); } } return true; }