/**
  * Get template and image versions from parsing a revision
  * @param Page $article
  * @param Revision $rev
  * @param User $user
  * @param string $regen use 'regen' to force regeneration
  * @return array( templateIds, fileSHA1Keys )
  * templateIds like ParserOutput->mTemplateIds
  * fileSHA1Keys like ParserOutput->mImageTimeKeys
  */
 public static function getRevIncludes(Page $article, Revision $rev, User $user, $regen = '')
 {
     global $wgParser, $wgMemc;
     wfProfileIn(__METHOD__);
     $versions = false;
     $key = self::getCacheKey($article->getTitle(), $rev->getId());
     if ($regen !== 'regen') {
         // check cache
         $versions = FlaggedRevs::getMemcValue($wgMemc->get($key), $article, 'allowStale');
     }
     if (!is_array($versions)) {
         // cache miss
         $pOut = false;
         if ($rev->isCurrent()) {
             $parserCache = ParserCache::singleton();
             # Try current version parser cache (as anon)...
             $pOut = $parserCache->get($article, $article->makeParserOptions($user));
             if ($pOut == false && $rev->getUser()) {
                 // try the user who saved the change
                 $author = User::newFromId($rev->getUser());
                 $pOut = $parserCache->get($article, $article->makeParserOptions($author));
             }
         }
         // ParserOutput::mImageTimeKeys wasn't always there
         if ($pOut == false || !FlaggedRevs::parserOutputIsVersioned($pOut)) {
             $title = $article->getTitle();
             $pOpts = ParserOptions::newFromUser($user);
             // Note: tidy off
             $pOut = $wgParser->parse($rev->getText(), $title, $pOpts, true, true, $rev->getId());
         }
         # Get the template/file versions used...
         $versions = array($pOut->getTemplateIds(), $pOut->getFileSearchOptions());
         # Save to cache (check cache expiry for dynamic elements)...
         $data = FlaggedRevs::makeMemcObj($versions);
         $wgMemc->set($key, $data, $pOut->getCacheExpiry());
     } else {
         $tVersions =& $versions[0];
         // templates
         # Do a link batch query for page_latest...
         $lb = new LinkBatch();
         foreach ($tVersions as $ns => $tmps) {
             foreach ($tmps as $dbKey => $revIdDraft) {
                 $lb->add($ns, $dbKey);
             }
         }
         $lb->execute();
         # Update array with the current page_latest values.
         # This kludge is there since $newTemplates (thus $revIdDraft) is cached.
         foreach ($tVersions as $ns => &$tmps) {
             foreach ($tmps as $dbKey => &$revIdDraft) {
                 $title = Title::makeTitle($ns, $dbKey);
                 $revIdDraft = (int) $title->getLatestRevID();
             }
         }
     }
     wfProfileOut(__METHOD__);
     return $versions;
 }
 /**
  * Get template and image versions from parsing a revision
  * @param Page $article
  * @param Revision $rev
  * @param User $user
  * @param string $regen use 'regen' to force regeneration
  * @return array( templateIds, fileSHA1Keys )
  * templateIds like ParserOutput->mTemplateIds
  * fileSHA1Keys like ParserOutput->mImageTimeKeys
  */
 public static function getRevIncludes(Page $article, Revision $rev, User $user, $regen = '')
 {
     global $wgMemc;
     wfProfileIn(__METHOD__);
     $key = self::getCacheKey($article->getTitle(), $rev->getId());
     if ($regen === 'regen') {
         $versions = false;
         // skip cache
     } elseif ($rev->isCurrent()) {
         // Check cache entry against page_touched
         $versions = FlaggedRevs::getMemcValue($wgMemc->get($key), $article);
     } else {
         // Old revs won't always be invalidated with template/file changes.
         // Also, we don't care if page_touched changed due to a direct edit.
         $versions = FlaggedRevs::getMemcValue($wgMemc->get($key), $article, 'allowStale');
         if (is_array($versions)) {
             // entry exists
             // Sanity check that the cache is reasonably up to date
             list($templates, $files) = $versions;
             if (self::templatesStale($templates) || self::filesStale($files)) {
                 $versions = false;
                 // no good
             }
         }
     }
     if (!is_array($versions)) {
         // cache miss
         $pOut = false;
         if ($rev->isCurrent()) {
             $parserCache = ParserCache::singleton();
             # Try current version parser cache for this user...
             $pOut = $parserCache->get($article, $article->makeParserOptions($user));
             if ($pOut == false) {
                 # Try current version parser cache for the revision author...
                 $optsUser = $rev->getUser() ? User::newFromId($rev->getUser()) : 'canonical';
                 $pOut = $parserCache->get($article, $article->makeParserOptions($optsUser));
             }
         }
         // ParserOutput::mImageTimeKeys wasn't always there
         if ($pOut == false || !FlaggedRevs::parserOutputIsVersioned($pOut)) {
             $content = $rev->getContent(Revision::RAW);
             if (!$content) {
                 // Just for extra sanity
                 $pOut = new ParserOutput();
             } else {
                 $pOut = $content->getParserOutput($article->getTitle(), $rev->getId(), ParserOptions::newFromUser($user));
             }
         }
         # Get the template/file versions used...
         $versions = array($pOut->getTemplateIds(), $pOut->getFileSearchOptions());
         # Save to cache (check cache expiry for dynamic elements)...
         $data = FlaggedRevs::makeMemcObj($versions);
         $wgMemc->set($key, $data, $pOut->getCacheExpiry());
     }
     wfProfileOut(__METHOD__);
     return $versions;
 }
 /**
  * Checks if the stable version is synced with the current revision
  * Note: slower than getPendingRevCount()
  * @return bool
  */
 public function stableVersionIsSynced()
 {
     global $wgMemc, $wgParserCacheExpireTime;
     $srev = $this->getStableRev();
     if (!$srev) {
         return true;
     }
     # Stable text revision must be the same as the current
     if ($this->revsArePending()) {
         return false;
         # Stable file revision must be the same as the current
     } elseif ($this->mTitle->getNamespace() == NS_FILE) {
         $file = $this->getFile();
         // current upload version
         if ($file && $file->getTimestamp() > $srev->getFileTimestamp()) {
             return false;
         }
     }
     # If using the current version of includes, there is nothing else to check.
     if (FlaggedRevs::inclusionSetting() == FR_INCLUDES_CURRENT) {
         return true;
         // short-circuit
     }
     # Try the cache...
     $key = wfMemcKey('flaggedrevs', 'includesSynced', $this->getId());
     $value = FlaggedRevs::getMemcValue($wgMemc->get($key), $this);
     if ($value === "true") {
         return true;
     } elseif ($value === "false") {
         return false;
     }
     # Since the stable and current revisions have the same text and only outputs,
     # the only other things to check for are template and file differences in the output.
     # (a) Check if the current output has a newer template/file used
     # (b) Check if the stable version has a file/template that was deleted
     $synced = !$srev->findPendingTemplateChanges() && !$srev->findPendingFileChanges('noForeign');
     # Save to cache. This will be updated whenever the page is touched.
     $data = FlaggedRevs::makeMemcObj($synced ? "true" : "false");
     $wgMemc->set($key, $data, $wgParserCacheExpireTime);
     return $synced;
 }
 public static function overrideRedirect(Title $title, WebRequest $request, &$ignoreRedirect, &$target, Article &$article)
 {
     global $wgMemc, $wgParserCacheExpireTime;
     $fa = FlaggableWikiPage::getTitleInstance($title);
     // on $wgTitle
     if (!$fa->isReviewable()) {
         return true;
         // nothing to do
     }
     # Viewing an old reviewed version...
     if ($request->getVal('stableid')) {
         $ignoreRedirect = true;
         // don't redirect (same as ?oldid=x)
         return true;
     }
     $srev = $fa->getStableRev();
     $view = FlaggablePageView::singleton();
     # Check if we are viewing an unsynced stable version...
     if ($srev && $view->showingStable() && $srev->getRevId() != $article->getLatest()) {
         # Check the stable redirect properties from the cache...
         $key = wfMemcKey('flaggedrevs', 'overrideRedirect', $article->getId());
         $tuple = FlaggedRevs::getMemcValue($wgMemc->get($key), $article);
         if (is_array($tuple)) {
             // cache hit
             list($ignoreRedirect, $target) = $tuple;
         } else {
             // cache miss; fetch the stable rev text...
             $text = $srev->getRevText();
             $redirect = $fa->getRedirectURL(Title::newFromRedirectRecurse($text));
             if ($redirect) {
                 $target = $redirect;
                 // use stable redirect
             } else {
                 $ignoreRedirect = true;
                 // make MW skip redirection
             }
             $data = FlaggedRevs::makeMemcObj(array($ignoreRedirect, $target));
             $wgMemc->set($key, $data, $wgParserCacheExpireTime);
             // cache results
         }
         $clearEnvironment = (bool) $target;
         # Check if the we are viewing a draft or synced stable version...
     } else {
         # In both cases, we can just let MW use followRedirect()
         # on the draft as normal, avoiding any page text hits.
         $clearEnvironment = $article->isRedirect();
     }
     # Environment (e.g. $wgTitle) will change in MediaWiki::initializeArticle
     if ($clearEnvironment) {
         $view->clear();
     }
     return true;
 }