/**
  * This function does essentially the same as RevisionReview::AjaxReview,
  * except that it generates the template and image parameters itself.
  */
 public function execute()
 {
     global $wgUser;
     $params = $this->extractRequestParams();
     // Check basic permissions
     if (!$wgUser->isAllowed('review')) {
         $this->dieUsage("You don't have the right to review revisions.", 'permissiondenied');
     } elseif ($wgUser->isBlocked(false)) {
         $this->dieUsageMsg(array('blockedtext'));
     }
     $newRev = Revision::newFromId($params['oldid']);
     if (!$newRev || !$newRev->getTitle()) {
         $this->dieUsage("Cannot find a revision with the specified ID.", 'notarget');
     }
     $title = $newRev->getTitle();
     $fa = FlaggableWikiPage::getTitleInstance($title);
     if (!$fa->isReviewable()) {
         $this->dieUsage("Provided page is not reviewable.", 'notreviewable');
     }
     $status = false;
     if ($params['previd']) {
         // changes
         $oldRev = Revision::newFromId($params['previd']);
         if (!$oldRev || $oldRev->getPage() != $newRev->getPage()) {
             $this->dieUsage("Revisions do not belong to the same page.", 'notarget');
         }
         // Mark as reviewing...
         if ($params['reviewing']) {
             $status = FRUserActivity::setUserReviewingDiff($wgUser, $params['previd'], $params['oldid']);
             // Unmark as reviewing...
         } else {
             $status = FRUserActivity::clearUserReviewingDiff($wgUser, $params['previd'], $params['oldid']);
         }
     } else {
         // Mark as reviewing...
         if ($params['reviewing']) {
             $status = FRUserActivity::setUserReviewingPage($wgUser, $newRev->getPage());
             // Unmark as reviewing...
         } else {
             $status = FRUserActivity::clearUserReviewingPage($wgUser, $newRev->getPage());
         }
     }
     # Success in setting flag...
     if ($status === true) {
         $this->getResult()->addValue(null, $this->getModuleName(), array('result' => 'Success'));
         # Failure...
     } else {
         $this->getResult()->addValue(null, $this->getModuleName(), array('result' => 'Failure'));
     }
 }
    private function run($resultPageSet = null)
    {
        global $wgMemc;
        $params = $this->extractRequestParams();
        // Construct SQL Query
        $this->addTables(array('page', 'flaggedpages'));
        $this->addWhereFld('page_namespace', $params['namespace']);
        if ($params['filterredir'] == 'redirects') {
            $this->addWhereFld('page_is_redirect', 1);
        }
        if ($params['filterredir'] == 'nonredirects') {
            $this->addWhereFld('page_is_redirect', 0);
        }
        $dir = $params['dir'] == 'descending' ? 'older' : 'newer';
        $this->addWhereRange('page_title', $dir, $params['start'], $params['end']);
        $this->addJoinConds(array('flaggedpages' => array('LEFT JOIN', 'fp_page_id=page_id')));
        $this->addWhere('fp_page_id IS NULL OR
			fp_quality < ' . intval($params['filterlevel']));
        $this->addOption('USE INDEX', array('page' => 'name_title', 'flaggedpages' => 'PRIMARY'));
        if (is_null($resultPageSet)) {
            $this->addFields(array('page_id', 'page_namespace', 'page_title', 'page_len', 'page_latest'));
        } else {
            $this->addFields($resultPageSet->getPageTableFields());
        }
        $limit = $params['limit'];
        $this->addOption('LIMIT', $limit + 1);
        $res = $this->select(__METHOD__);
        $data = array();
        $count = 0;
        foreach ($res as $row) {
            if (++$count > $limit) {
                // We've reached the one extra which shows that there are
                // additional pages to be had. Stop here...
                $this->setContinueEnumParameter('start', $row->page_title);
                break;
            }
            if (is_null($resultPageSet)) {
                $title = Title::newFromRow($row);
                $data[] = array('pageid' => intval($row->page_id), 'ns' => intval($title->getNamespace()), 'title' => $title->getPrefixedText(), 'revid' => intval($row->page_latest), 'under_review' => FRUserActivity::pageIsUnderReview($row->page_id));
            } else {
                $resultPageSet->processDbRow($row);
            }
        }
        if (is_null($resultPageSet)) {
            $result = $this->getResult();
            $result->setIndexedTagName($data, 'p');
            $result->addValue('query', $this->getModuleName(), $data);
        }
    }
 private function run($resultPageSet = null)
 {
     global $wgUser, $wgMemc;
     $params = $this->extractRequestParams();
     // Construct SQL Query
     $this->addTables(array('page', 'flaggedpages', 'revision'));
     $this->addWhereFld('page_namespace', $params['namespace']);
     $useIndex = array('flaggedpages' => 'fp_pending_since');
     if ($params['filterredir'] == 'redirects') {
         $this->addWhereFld('page_is_redirect', 1);
     }
     if ($params['filterredir'] == 'nonredirects') {
         $this->addWhereFld('page_is_redirect', 0);
     }
     if ($params['maxsize'] !== null) {
         # Get absolute difference for comparison. ABS(x-y)
         # is broken due to mysql unsigned int design.
         $this->addWhere('GREATEST(page_len,rev_len)-LEAST(page_len,rev_len) <= ' . intval($params['maxsize']));
     }
     if ($params['filterwatched'] == 'watched') {
         if (!($uid = $wgUser->getId())) {
             $this->dieUsage('You must be logged-in to have a watchlist', 'notloggedin');
         }
         $this->addTables('watchlist');
         $this->addWhereFld('wl_user', $uid);
         $this->addWhere('page_namespace = wl_namespace');
         $this->addWhere('page_title = wl_title');
     }
     if ($params['category'] != '') {
         $this->addTables('categorylinks');
         $this->addWhere('cl_from = fp_page_id');
         $this->addWhereFld('cl_to', $params['category']);
         $useIndex['categorylinks'] = 'cl_from';
     }
     $this->addWhereRange('fp_pending_since', $params['dir'], $params['start'], $params['end']);
     $this->addWhere('page_id=fp_page_id');
     $this->addWhere('rev_id=fp_stable');
     if (!isset($params['start']) && !isset($params['end'])) {
         $this->addWhere('fp_pending_since IS NOT NULL');
     }
     $this->addOption('USE INDEX', $useIndex);
     if (is_null($resultPageSet)) {
         $this->addFields(array('page_id', 'page_namespace', 'page_title', 'page_latest', 'page_len', 'rev_len', 'fp_stable', 'fp_pending_since', 'fp_quality'));
     } else {
         $this->addFields($resultPageSet->getPageTableFields());
         $this->addFields('fp_pending_since');
     }
     $limit = $params['limit'];
     $this->addOption('LIMIT', $limit + 1);
     $res = $this->select(__METHOD__);
     $data = array();
     $count = 0;
     foreach ($res as $row) {
         if (++$count > $limit) {
             // We've reached the one extra which shows that there are
             // additional pages to be had. Stop here...
             $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->fp_pending_since));
             break;
         }
         if (is_null($resultPageSet)) {
             $title = Title::newFromRow($row);
             $underReview = FRUserActivity::diffIsUnderReview($row->fp_stable, $row->page_latest);
             $data[] = array('pageid' => intval($row->page_id), 'ns' => intval($row->page_namespace), 'title' => $title->getPrefixedText(), 'revid' => intval($row->page_latest), 'stable_revid' => intval($row->fp_stable), 'pending_since' => wfTimestamp(TS_ISO_8601, $row->fp_pending_since), 'flagged_level' => intval($row->fp_quality), 'flagged_level_text' => FlaggedRevs::getQualityLevelText($row->fp_quality), 'diff_size' => (int) $row->page_len - (int) $row->rev_len, 'under_review' => $underReview);
         } else {
             $resultPageSet->processDbRow($row);
         }
     }
     if (is_null($resultPageSet)) {
         $result = $this->getResult();
         $result->setIndexedTagName($data, 'p');
         $result->addValue('query', $this->getModuleName(), $data);
     }
 }
 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>";
 }
 public function formatRow($row)
 {
     $title = Title::newFromRow($row);
     $stxt = $underReview = $watching = '';
     $link = Linker::link($title, null, array(), 'redirect=no');
     $dirmark = $this->getLanguage()->getDirMark();
     $hist = Linker::linkKnown($title, $this->msg('hist')->escaped(), array(), array('action' => 'history'));
     if (!is_null($size = $row->page_len)) {
         $stxt = $size == 0 ? $this->msg('historyempty')->escaped() : $this->msg('historysize')->numParams($size)->escaped();
         $stxt = " <small>{$stxt}</small>";
     }
     # Get how long the first unreviewed edit has been waiting...
     $firstPendingTime = wfTimestamp(TS_UNIX, $row->creation);
     $hours = ($this->currentUnixTS - $firstPendingTime) / 3600;
     // After three days, just use days
     if ($hours > 3 * 24) {
         $days = round($hours / 24, 0);
         $age = ' ' . $this->msg('unreviewedpages-days')->numParams($days)->escaped();
         // If one or more hours, use hours
     } elseif ($hours >= 1) {
         $hours = round($hours, 0);
         $age = ' ' . $this->msg('unreviewedpages-hours')->numParams($hours)->escaped();
     } else {
         $age = ' ' . $this->msg('unreviewedpages-recent')->escaped();
         // hot off the press :)
     }
     if ($this->getUser()->isAllowed('unwatchedpages')) {
         $uw = FRUserActivity::numUsersWatchingPage($title);
         $watching = $uw ? $this->msg('unreviewedpages-watched')->numParams($uw)->escaped() : $this->msg('unreviewedpages-unwatched')->escaped();
         $watching = " {$watching}";
         // Oh-noes!
     } else {
         $uw = -1;
     }
     $css = self::getLineClass($hours, $uw);
     $css = $css ? " class='{$css}'" : "";
     # Show if a user is looking at this page
     list($u, $ts) = FRUserActivity::getUserReviewingPage($row->page_id);
     if ($u !== null) {
         $underReview = " <span class='fr-under-review'>" . $this->msg('unreviewedpages-viewing')->escaped() . '</span>';
     }
     return "<li{$css}>{$link} {$dirmark} {$stxt} ({$hist})" . "{$age}{$watching}{$underReview}</li>";
 }
 /**
  * Generates a brief review form for a page
  * @return array (html string, error string or true)
  */
 public function getHtml()
 {
     global $wgLang;
     $revId = $this->rev->getId();
     if ($this->rev->isDeleted(Revision::DELETED_TEXT)) {
         return array('', 'review_bad_oldid');
         # The revision must be valid and public
     }
     $article = $this->article;
     // convenience
     $srev = $article->getStableRev();
     # See if the version being displayed is flagged...
     if ($revId == $article->getStable()) {
         $frev = $srev;
         // avoid query
     } else {
         $frev = FlaggedRevision::newFromTitle($article->getTitle(), $revId);
     }
     $oldFlags = $frev ? $frev->getTags() : FlaggedRevs::quickTags(FR_CHECKED);
     // basic tags
     $reviewTime = $frev ? $frev->getTimestamp() : '';
     // last review of rev
     $priorRevId = $this->refRev ? $this->refRev->getId() : 0;
     # If we are reviewing updates to a page, start off with the stable revision's
     # flags. Otherwise, we just fill them in with the selected revision's flags.
     # @TODO: do we want to carry over info for other diffs?
     if ($srev && $srev->getRevId() == $priorRevId) {
         // diff-to-stable
         $flags = $srev->getTags();
         # Check if user is allowed to renew the stable version.
         # If not, then get the flags for the new revision itself.
         if (!FlaggedRevs::userCanSetFlags($this->user, $oldFlags)) {
             $flags = $oldFlags;
         }
         # Re-review button is need for template/file only review case
         $reviewIncludes = $srev->getRevId() == $revId && !$article->stableVersionIsSynced();
     } else {
         // views
         $flags = $oldFlags;
         $reviewIncludes = false;
         // re-review button not needed
     }
     # Disable form for unprivileged users
     $disabled = array();
     if (!$article->getTitle()->quickUserCan('review') || !FlaggedRevs::userCanSetFlags($this->user, $flags)) {
         $disabled = array('disabled' => 'disabled');
     }
     # Begin form...
     $reviewTitle = SpecialPage::getTitleFor('RevisionReview');
     $action = $reviewTitle->getLocalUrl('action=submit');
     $params = array('method' => 'post', 'action' => $action, 'id' => 'mw-fr-reviewform');
     $form = Xml::openElement('form', $params) . "\n";
     $form .= Xml::openElement('fieldset', array('class' => 'flaggedrevs_reviewform noprint')) . "\n";
     # Add appropriate legend text
     $legendMsg = $frev ? 'revreview-reflag' : 'revreview-flag';
     $form .= Xml::openElement('legend', array('id' => 'mw-fr-reviewformlegend'));
     $form .= "<strong>" . wfMessage($legendMsg)->escaped() . "</strong>";
     $form .= Xml::closeElement('legend') . "\n";
     # Show explanatory text
     $form .= $this->topNotice;
     # Check if anyone is reviewing this already and
     # show a conflict warning message as needed...
     if ($priorRevId) {
         list($u, $ts) = FRUserActivity::getUserReviewingDiff($priorRevId, $this->rev->getId());
     } else {
         list($u, $ts) = FRUserActivity::getUserReviewingPage($this->rev->getPage());
     }
     $form .= Xml::openElement('p');
     // Page under review (and not by this user)...
     if ($u !== null && $u != $this->user->getName()) {
         $form .= '<span class="fr-under-review">';
         $msg = $priorRevId ? 'revreview-poss-conflict-c' : 'revreview-poss-conflict-p';
         $form .= wfMessage($msg, $u, $wgLang->date($ts, true), $wgLang->time($ts, true))->parse();
         $form .= "</span>";
         // Page not under review or under review by this user...
     } elseif (!$frev) {
         // rev not already reviewed
         $form .= '<span id="mw-fr-reviewing-status" style="display:none;"></span>';
         // JS widget
     }
     $form .= Xml::closeElement('p') . "\n";
     # Start rating controls
     $css = $disabled ? 'fr-rating-controls-disabled' : 'fr-rating-controls';
     $form .= Xml::openElement('p', array('class' => $css, 'id' => 'fr-rating-controls')) . "\n";
     # Add main checkboxes/selects
     $form .= Xml::openElement('span', array('id' => 'mw-fr-ratingselects', 'class' => 'fr-rating-options')) . "\n";
     $form .= self::ratingInputs($this->user, $flags, (bool) $disabled, (bool) $frev) . "\n";
     $form .= Xml::closeElement('span') . "\n";
     # Don't put buttons & comment field on the same line as tag inputs.
     if (!$disabled && !FlaggedRevs::binaryFlagging()) {
         // $disabled => no comment/buttons
         $form .= "<br />";
     }
     # Start comment & buttons
     $form .= Xml::openElement('span', array('id' => 'mw-fr-confirmreview')) . "\n";
     # Hide comment input if needed
     if (!$disabled) {
         $form .= Xml::inputLabel(wfMessage('revreview-log')->text(), 'wpReason', 'mw-fr-commentbox', 40, '', array('maxlength' => 255, 'class' => 'fr-comment-box'));
     }
     # Add the submit buttons...
     $rejectId = $this->rejectRefRevId();
     // determine if there will be reject button
     $form .= self::submitButtons($rejectId, $frev, (bool) $disabled, $reviewIncludes);
     # Show stability log if there is anything interesting...
     if ($article->isPageLocked()) {
         $form .= ' ' . FlaggedRevsXML::logToggle('revreview-log-toggle-show');
     }
     # End comment & buttons
     $form .= Xml::closeElement('span') . "\n";
     # ..add the actual stability log body here
     if ($article->isPageLocked()) {
         $form .= FlaggedRevsXML::stabilityLogExcerpt($article);
     }
     # End rating controls
     $form .= Xml::closeElement('p') . "\n";
     # Show explanatory text
     $form .= $this->bottomNotice;
     # Get the file version used for File: pages as needed
     $fileKey = $this->getFileVersion();
     # Get template/file version info as needed
     list($templateIDs, $imageSHA1Keys) = $this->getIncludeVersions();
     # Convert these into flat string params
     list($templateParams, $imageParams, $fileVersion) = RevisionReviewForm::getIncludeParams($templateIDs, $imageSHA1Keys, $fileKey);
     # Hidden params
     $form .= Html::hidden('title', $reviewTitle->getPrefixedText()) . "\n";
     $form .= Html::hidden('target', $article->getTitle()->getPrefixedDBKey()) . "\n";
     $form .= Html::hidden('refid', $priorRevId, array('id' => 'mw-fr-input-refid')) . "\n";
     $form .= Html::hidden('oldid', $revId, array('id' => 'mw-fr-input-oldid')) . "\n";
     $form .= Html::hidden('wpEditToken', $this->user->getEditToken()) . "\n";
     $form .= Html::hidden('changetime', $reviewTime, array('id' => 'mw-fr-input-changetime')) . "\n";
     // id for JS
     $form .= Html::hidden('userreviewing', (int) ($u === $this->user->getName()), array('id' => 'mw-fr-user-reviewing')) . "\n";
     // id for JS
     # Add review parameters
     $form .= Html::hidden('templateParams', $templateParams) . "\n";
     $form .= Html::hidden('imageParams', $imageParams) . "\n";
     $form .= Html::hidden('fileVersion', $fileVersion) . "\n";
     # Special token to discourage fiddling...
     $key = $this->request->getSessionData('wsFlaggedRevsKey');
     $checkCode = RevisionReviewForm::validationKey($templateParams, $imageParams, $fileVersion, $revId, $key);
     $form .= Html::hidden('validatedParams', $checkCode) . "\n";
     $form .= Xml::closeElement('fieldset') . "\n";
     $form .= Xml::closeElement('form') . "\n";
     return array($form, true);
 }
 public function testUserReviewingDiff()
 {
     $oldid = 12910;
     $newid = 15910;
     FRUserActivity::clearAllReviewingDiff($oldid, $newid);
     // clear
     $this->assertEquals(true, FRUserActivity::setUserReviewingDiff($this->user, $oldid, $newid), "Set reviewing page succeeds");
     $this->assertEquals(true, FRUserActivity::clearUserReviewingDiff($this->user, $oldid, $newid), "Unset reviewing page");
     $this->assertEquals(false, FRUserActivity::clearUserReviewingDiff($this->user, $oldid, $newid), "Extra unset reviewing page");
     // set two instances...
     $this->assertEquals(true, FRUserActivity::setUserReviewingDiff($this->user, $oldid, $newid), "Set reviewing page (1)");
     $this->assertEquals(true, FRUserActivity::setUserReviewingDiff($this->user, $oldid, $newid), "Set reviewing page (2)");
     // clear both...
     $this->assertEquals(true, FRUserActivity::clearUserReviewingDiff($this->user, $oldid, $newid), "Unset reviewing page (1)");
     $this->assertEquals(true, FRUserActivity::clearUserReviewingDiff($this->user, $oldid, $newid), "Unset reviewing page (2)");
     // extra clears...
     $this->assertEquals(false, FRUserActivity::clearUserReviewingDiff($this->user, $oldid, $newid), "Extra unset reviewing page");
 }