/**
  * Tag output function must be called by caller
  * Parser cache control deferred to caller
  * @param $srev stable version
  * @param $tag review box/bar info
  * @param $prot protection notice
  * @return ParserOutput
  */
 protected function showStableVersion(FlaggedRevision $srev, &$tag, $prot)
 {
     $reqUser = $this->getUser();
     $this->load();
     $flags = $srev->getTags();
     $time = $this->getLang()->date($srev->getTimestamp(), true);
     # Set display revision ID
     $this->out->setRevisionId($srev->getRevId());
     # Get quality level
     $quality = FlaggedRevs::isQuality($flags);
     $synced = $this->article->stableVersionIsSynced();
     # Construct some tagging
     if (!$this->out->isPrintable() && !($this->article->lowProfileUI() && $synced)) {
         $revsSince = $this->article->getPendingRevCount();
         // Simple icon-based UI
         if ($this->useSimpleUI()) {
             $icon = '';
             # For protection based configs, show lock only if it's not redundant.
             if ($this->showRatingIcon()) {
                 $icon = FlaggedRevsXML::stableStatusIcon($quality);
             }
             if (!$reqUser->getId()) {
                 $msgHTML = '';
                 // Anons just see simple icons
             } else {
                 $msg = $quality ? 'revreview-quick-quality' : 'revreview-quick-basic';
                 # Uses messages 'revreview-quick-quality-same', 'revreview-quick-basic-same'
                 $msg = $synced ? "{$msg}-same" : $msg;
                 $msgHTML = wfMsgExt($msg, 'parseinline', $srev->getRevId(), $revsSince);
             }
             $msgHTML = $prot . $icon . $msgHTML;
             $tag = FlaggedRevsXML::prettyRatingBox($srev, $msgHTML, $revsSince, 'stable', $synced);
             // Standard UI
         } else {
             $icon = FlaggedRevsXML::stableStatusIcon($quality);
             $msg = $quality ? 'revreview-quality' : 'revreview-basic';
             if ($synced) {
                 # uses messages 'revreview-quality-same', 'revreview-basic-same'
                 $msg .= '-same';
             } elseif ($revsSince == 0) {
                 # uses messages 'revreview-quality-i', 'revreview-basic-i'
                 $msg .= '-i';
             }
             $tag = $prot . $icon;
             $tag .= wfMsgExt($msg, 'parseinline', $srev->getRevId(), $time, $revsSince);
             if (!empty($flags)) {
                 $tag .= FlaggedRevsXML::ratingToggle();
                 $tag .= "<div id='mw-fr-revisiondetails' style='display:block;'>" . FlaggedRevsXML::addTagRatings($flags) . '</div>';
             }
         }
     }
     # Get parsed stable version and output HTML
     $pOpts = $this->article->makeParserOptions($reqUser);
     $parserCache = FRParserCacheStable::singleton();
     $parserOut = $parserCache->get($this->article, $pOpts);
     # Do not use the parser cache if it lacks mImageTimeKeys and there is a
     # chance that a review form will be added to this page (which requires the versions).
     $canReview = $this->article->getTitle()->userCan('review');
     if ($parserOut && (!$canReview || FlaggedRevs::parserOutputIsVersioned($parserOut))) {
         # Cache hit. Note that redirects are not cached.
         $this->out->addParserOutput($parserOut);
     } else {
         $text = $srev->getRevText();
         # Get the new stable parser output...
         $parserOut = FlaggedRevs::parseStableText($this->article->getTitle(), $text, $srev->getRevId(), $pOpts);
         $redirHtml = $this->getRedirectHtml($text);
         if ($redirHtml == '') {
             // page is not a redirect...
             # Update the stable version cache
             $parserCache->save($parserOut, $this->article, $pOpts);
             # Add the stable output to the page view
             $this->out->addParserOutput($parserOut);
         } else {
             // page is a redirect...
             $this->out->addHtml($redirHtml);
             # Add output to set categories, displaytitle, etc.
             $this->out->addParserOutputNoText($parserOut);
         }
         # Update the stable version dependancies
         FlaggedRevs::updateStableOnlyDeps($this->article, $parserOut);
     }
     # Update page sync status for tracking purposes.
     # NOTE: avoids master hits and doesn't have to be perfect for what it does
     if ($this->article->syncedInTracking() != $synced) {
         if (wfGetLB()->safeGetLag(wfGetDB(DB_SLAVE)) <= 5) {
             // avoid write-delay cycles
             $this->article->updateSyncStatus($synced);
         }
     }
     return $parserOut;
 }
 /**
  * Adds or updates the flagged revision table for this page/id set
  * @param Revision $rev The revision to be accepted
  * @param FlaggedRevision $oldFrev Currently accepted version of $rev or null
  * @throws MWException
  * @return bool|array true on success, array of errors on failure
  */
 private function approveRevision(Revision $rev, FlaggedRevision $oldFrev = null)
 {
     wfProfileIn(__METHOD__);
     # Revision rating flags
     $flags = $this->dims;
     $quality = 0;
     // quality tier from flags
     if (FlaggedRevs::isQuality($flags)) {
         $quality = FlaggedRevs::isPristine($flags) ? 2 : 1;
     }
     # Our template/file version pointers
     list($tmpVersions, $fileVersions) = self::getIncludeVersions($this->templateParams, $this->imageParams);
     # If this is an image page, store corresponding file info
     $fileData = array('name' => null, 'timestamp' => null, 'sha1' => null);
     if ($this->page->getNamespace() == NS_FILE && $this->fileVersion) {
         # Stable upload version for file pages...
         $data = explode('#', $this->fileVersion, 2);
         if (count($data) == 2) {
             $fileData['name'] = $this->page->getDBkey();
             $fileData['timestamp'] = $data[0];
             $fileData['sha1'] = $data[1];
         }
     }
     # Get current stable version ID (for logging)
     $oldSv = FlaggedRevision::newFromStable($this->page, FR_MASTER);
     # Is this a duplicate review?
     if ($oldFrev && $oldFrev->getTags() == $flags && $oldFrev->getFileSha1() == $fileData['sha1'] && $oldFrev->getFileTimestamp() == $fileData['timestamp'] && $oldFrev->getTemplateVersions(FR_MASTER) == $tmpVersions && $oldFrev->getFileVersions(FR_MASTER) == $fileVersions) {
         wfProfileOut(__METHOD__);
         return true;
         // don't record if the same
     }
     # The new review entry...
     $flaggedRevision = new FlaggedRevision(array('rev' => $rev, 'user_id' => $this->user->getId(), 'timestamp' => wfTimestampNow(), 'quality' => $quality, 'tags' => FlaggedRevision::flattenRevisionTags($flags), 'img_name' => $fileData['name'], 'img_timestamp' => $fileData['timestamp'], 'img_sha1' => $fileData['sha1'], 'templateVersions' => $tmpVersions, 'fileVersions' => $fileVersions, 'flags' => ''));
     # Delete the old review entry if it exists...
     if ($oldFrev) {
         $oldFrev->delete();
     }
     # Insert the new review entry...
     if (!$flaggedRevision->insert()) {
         throw new MWException("Flagged revision with ID {$rev->getId()} exists with unexpected fr_page_id");
     }
     # Update the article review log...
     $oldSvId = $oldSv ? $oldSv->getRevId() : 0;
     FlaggedRevsLog::updateReviewLog($this->page, $this->dims, $this->oldFlags, $this->comment, $this->oldid, $oldSvId, true);
     # Get the new stable version as of now
     $sv = FlaggedRevision::determineStable($this->page, FR_MASTER);
     # Update recent changes...
     self::updateRecentChanges($rev, 'patrol', $sv);
     # Update page and tracking tables and clear cache
     $changed = FlaggedRevs::stableVersionUpdates($this->page, $sv, $oldSv);
     if ($changed) {
         FlaggedRevs::HTMLCacheUpdates($this->page);
         // purge pages that use this page
     }
     # Caller may want to get the change time
     $this->newLastChangeTime = $flaggedRevision->getTimestamp();
     wfProfileOut(__METHOD__);
     return true;
 }