/** * This is the default action of the index.php entry point: just view the * page of the given title. */ public function view() { global $wgParser, $wgUseFileCache, $wgUseETag, $wgDebugToolbar; wfProfileIn(__METHOD__); # Get variables from query string # As side effect this will load the revision and update the title # in a revision ID is passed in the request, so this should remain # the first call of this method even if $oldid is used way below. $oldid = $this->getOldID(); $user = $this->getContext()->getUser(); # Another whitelist check in case getOldID() is altering the title $permErrors = $this->getTitle()->getUserPermissionsErrors('read', $user); if (count($permErrors)) { wfDebug(__METHOD__ . ": denied on secondary read check\n"); wfProfileOut(__METHOD__); throw new PermissionsError('read', $permErrors); } $outputPage = $this->getContext()->getOutput(); # getOldID() may as well want us to redirect somewhere else if ($this->mRedirectUrl) { $outputPage->redirect($this->mRedirectUrl); wfDebug(__METHOD__ . ": redirecting due to oldid\n"); wfProfileOut(__METHOD__); return; } # If we got diff in the query, we want to see a diff page instead of the article. if ($this->getContext()->getRequest()->getCheck('diff')) { wfDebug(__METHOD__ . ": showing diff page\n"); $this->showDiffPage(); wfProfileOut(__METHOD__); return; } # Set page title (may be overridden by DISPLAYTITLE) $outputPage->setPageTitle($this->getTitle()->getPrefixedText()); $outputPage->setArticleFlag(true); # Allow frames by default $outputPage->allowClickjacking(); $parserCache = ParserCache::singleton(); $parserOptions = $this->getParserOptions(); # Render printable version, use printable version cache if ($outputPage->isPrintable()) { $parserOptions->setIsPrintable(true); $parserOptions->setEditSection(false); } elseif (!$this->isCurrent() || !$this->getTitle()->quickUserCan('edit', $user)) { $parserOptions->setEditSection(false); } # Try client and file cache if (!$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched()) { if ($wgUseETag) { $outputPage->setETag($parserCache->getETag($this, $parserOptions)); } # Is it client cached? if ($outputPage->checkLastModified($this->mPage->getTouched())) { wfDebug(__METHOD__ . ": done 304\n"); wfProfileOut(__METHOD__); return; # Try file cache } elseif ($wgUseFileCache && $this->tryFileCache()) { wfDebug(__METHOD__ . ": done file cache\n"); # tell wgOut that output is taken care of $outputPage->disable(); $this->mPage->doViewUpdates($user); wfProfileOut(__METHOD__); return; } } # Should the parser cache be used? $useParserCache = $this->mPage->isParserCacheUsed($parserOptions, $oldid); wfDebug('Article::view using parser cache: ' . ($useParserCache ? 'yes' : 'no') . "\n"); if ($user->getStubThreshold()) { wfIncrStats('pcache_miss_stub'); } $this->showRedirectedFromHeader(); $this->showNamespaceHeader(); # Iterate through the possible ways of constructing the output text. # Keep going until $outputDone is set, or we run out of things to do. $pass = 0; $outputDone = false; $this->mParserOutput = false; while (!$outputDone && ++$pass) { switch ($pass) { case 1: wfRunHooks('ArticleViewHeader', array(&$this, &$outputDone, &$useParserCache)); break; case 2: # Early abort if the page doesn't exist if (!$this->mPage->exists()) { wfDebug(__METHOD__ . ": showing missing article\n"); $this->showMissingArticle(); wfProfileOut(__METHOD__); return; } # Try the parser cache if ($useParserCache) { $this->mParserOutput = $parserCache->get($this, $parserOptions); if ($this->mParserOutput !== false) { if ($oldid) { wfDebug(__METHOD__ . ": showing parser cache contents for current rev permalink\n"); $this->setOldSubtitle($oldid); } else { wfDebug(__METHOD__ . ": showing parser cache contents\n"); } $outputPage->addParserOutput($this->mParserOutput); # Ensure that UI elements requiring revision ID have # the correct version information. $outputPage->setRevisionId($this->mPage->getLatest()); # Preload timestamp to avoid a DB hit $cachedTimestamp = $this->mParserOutput->getTimestamp(); if ($cachedTimestamp !== null) { $outputPage->setRevisionTimestamp($cachedTimestamp); $this->mPage->setTimestamp($cachedTimestamp); } $outputDone = true; } } break; case 3: # This will set $this->mRevision if needed $this->fetchContentObject(); # Are we looking at an old revision if ($oldid && $this->mRevision) { $this->setOldSubtitle($oldid); if (!$this->showDeletedRevisionHeader()) { wfDebug(__METHOD__ . ": cannot view deleted revision\n"); wfProfileOut(__METHOD__); return; } } # Ensure that UI elements requiring revision ID have # the correct version information. $outputPage->setRevisionId($this->getRevIdFetched()); # Preload timestamp to avoid a DB hit $outputPage->setRevisionTimestamp($this->getTimestamp()); # Pages containing custom CSS or JavaScript get special treatment if ($this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage()) { wfDebug(__METHOD__ . ": showing CSS/JS source\n"); $this->showCssOrJsPage(); $outputDone = true; } elseif (!wfRunHooks('ArticleContentViewCustom', array($this->fetchContentObject(), $this->getTitle(), $outputPage))) { # Allow extensions do their own custom view for certain pages $outputDone = true; } elseif (!ContentHandler::runLegacyHooks('ArticleViewCustom', array($this->fetchContentObject(), $this->getTitle(), $outputPage))) { # Allow extensions do their own custom view for certain pages $outputDone = true; } else { $content = $this->getContentObject(); $rt = $content->getRedirectChain(); if ($rt) { wfDebug(__METHOD__ . ": showing redirect=no page\n"); # Viewing a redirect page (e.g. with parameter redirect=no) $outputPage->addHTML($this->viewRedirect($rt)); # Parse just to get categories, displaytitle, etc. $this->mParserOutput = $content->getParserOutput($this->getTitle(), $oldid, $parserOptions, false); $outputPage->addParserOutputNoText($this->mParserOutput); $outputDone = true; } } break; case 4: # Run the parse, protected by a pool counter wfDebug(__METHOD__ . ": doing uncached parse\n"); // @todo: shouldn't we be passing $this->getPage() to PoolWorkArticleView instead of plain $this? $poolArticleView = new PoolWorkArticleView($this, $parserOptions, $this->getRevIdFetched(), $useParserCache, $this->getContentObject(), $this->getContext()); if (!$poolArticleView->execute()) { $error = $poolArticleView->getError(); if ($error) { $outputPage->clearHTML(); // for release() errors $outputPage->enableClientCache(false); $outputPage->setRobotPolicy('noindex,nofollow'); $errortext = $error->getWikiText(false, 'view-pool-error'); $outputPage->addWikiText('<div class="errorbox">' . $errortext . '</div>'); } # Connection or timeout error wfProfileOut(__METHOD__); return; } $this->mParserOutput = $poolArticleView->getParserOutput(); $outputPage->addParserOutput($this->mParserOutput); # Don't cache a dirty ParserOutput object if ($poolArticleView->getIsDirty()) { $outputPage->setSquidMaxage(0); $outputPage->addHTML("<!-- parser cache is expired, sending anyway due to pool overload-->\n"); } $outputDone = true; break; # Should be unreachable, but just in case... # Should be unreachable, but just in case... default: break 2; } } # Get the ParserOutput actually *displayed* here. # Note that $this->mParserOutput is the *current* version output. $pOutput = $outputDone instanceof ParserOutput ? $outputDone : $this->mParserOutput; # Adjust title for main page & pages with displaytitle if ($pOutput) { $this->adjustDisplayTitle($pOutput); } # For the main page, overwrite the <title> element with the con- # tents of 'pagetitle-view-mainpage' instead of the default (if # that's not empty). # This message always exists because it is in the i18n files if ($this->getTitle()->isMainPage()) { $msg = wfMessage('pagetitle-view-mainpage')->inContentLanguage(); if (!$msg->isDisabled()) { $outputPage->setHTMLTitle($msg->title($this->getTitle())->text()); } } # Check for any __NOINDEX__ tags on the page using $pOutput $policy = $this->getRobotPolicy('view', $pOutput); $outputPage->setIndexPolicy($policy['index']); $outputPage->setFollowPolicy($policy['follow']); $this->showViewFooter(); $this->mPage->doViewUpdates($user); wfProfileOut(__METHOD__); }
/** * Retrieve the ParserOutput from ParserCache. * false if not found or outdated. * * @param WikiPage|Article $article * @param ParserOptions $popts * @param bool $useOutdated (default false) * * @return ParserOutput|bool False on failure */ public function get($article, $popts, $useOutdated = false) { global $wgCacheEpoch; $canCache = $article->checkTouched(); if (!$canCache) { // It's a redirect now return false; } $touched = $article->getTouched(); $parserOutputKey = $this->getKey($article, $popts, $useOutdated); if ($parserOutputKey === false) { wfIncrStats('pcache.miss.absent'); return false; } $value = $this->mMemc->get($parserOutputKey); if (!$value) { wfDebug("ParserOutput cache miss.\n"); wfIncrStats("pcache.miss.absent"); return false; } wfDebug("ParserOutput cache found.\n"); // The edit section preference may not be the appropiate one in // the ParserOutput, as we are not storing it in the parsercache // key. Force it here. See bug 31445. $value->setEditSectionTokens($popts->getEditSection()); $wikiPage = method_exists($article, 'getPage') ? $article->getPage() : $article; if (!$useOutdated && $value->expired($touched)) { wfIncrStats("pcache.miss.expired"); $cacheTime = $value->getCacheTime(); wfDebug("ParserOutput key expired, touched {$touched}, " . "epoch {$wgCacheEpoch}, cached {$cacheTime}\n"); $value = false; } elseif ($value->isDifferentRevision($article->getLatest())) { wfIncrStats("pcache.miss.revid"); $revId = $article->getLatest(); $cachedRevId = $value->getCacheRevisionId(); wfDebug("ParserOutput key is for an old revision, latest {$revId}, cached {$cachedRevId}\n"); $value = false; } elseif (Hooks::run('RejectParserCacheValue', array($value, $wikiPage, $popts)) === false) { wfIncrStats('pcache.miss.rejected'); wfDebug("ParserOutput key valid, but rejected by RejectParserCacheValue hook handler.\n"); $value = false; } else { wfIncrStats("pcache.hit"); } return $value; }
/** * This is the default action of the index.php entry point: just view the * page of the given title. */ public function view() { global $wgUser, $wgOut, $wgRequest, $wgParser; global $wgUseFileCache, $wgUseETag; wfProfileIn(__METHOD__); # Get variables from query string $oldid = $this->getOldID(); # getOldID may want us to redirect somewhere else if ($this->mRedirectUrl) { $wgOut->redirect($this->mRedirectUrl); wfDebug(__METHOD__ . ": redirecting due to oldid\n"); wfProfileOut(__METHOD__); return; } $wgOut->setArticleFlag(true); # Set page title (may be overridden by DISPLAYTITLE) $wgOut->setPageTitle($this->getTitle()->getPrefixedText()); # If we got diff in the query, we want to see a diff page instead of the article. if ($wgRequest->getCheck('diff')) { wfDebug(__METHOD__ . ": showing diff page\n"); $this->showDiffPage(); wfProfileOut(__METHOD__); return; } # Allow frames by default $wgOut->allowClickjacking(); $parserCache = ParserCache::singleton(); $parserOptions = $this->mPage->getParserOptions(); # Render printable version, use printable version cache if ($wgOut->isPrintable()) { $parserOptions->setIsPrintable(true); $parserOptions->setEditSection(false); } elseif ($wgUseETag && !$this->getTitle()->quickUserCan('edit')) { $parserOptions->setEditSection(false); } # Try client and file cache if ($oldid === 0 && $this->mPage->checkTouched()) { if ($wgUseETag) { $wgOut->setETag($parserCache->getETag($this, $parserOptions)); } # Is it client cached? if ($wgOut->checkLastModified($this->mPage->getTouched())) { wfDebug(__METHOD__ . ": done 304\n"); wfProfileOut(__METHOD__); return; # Try file cache } elseif ($wgUseFileCache && $this->tryFileCache()) { wfDebug(__METHOD__ . ": done file cache\n"); # tell wgOut that output is taken care of $wgOut->disable(); $this->mPage->viewUpdates(); wfProfileOut(__METHOD__); return; } } if (!$wgUseETag && !$this->getTitle()->quickUserCan('edit')) { $parserOptions->setEditSection(false); } # Should the parser cache be used? $useParserCache = $this->useParserCache($oldid); wfDebug('Article::view using parser cache: ' . ($useParserCache ? 'yes' : 'no') . "\n"); if ($wgUser->getStubThreshold()) { wfIncrStats('pcache_miss_stub'); } $wasRedirected = $this->showRedirectedFromHeader(); $this->showNamespaceHeader(); # Iterate through the possible ways of constructing the output text. # Keep going until $outputDone is set, or we run out of things to do. $pass = 0; $outputDone = false; $this->mParserOutput = false; while (!$outputDone && ++$pass) { switch ($pass) { case 1: wfRunHooks('ArticleViewHeader', array(&$this, &$outputDone, &$useParserCache)); break; case 2: # Try the parser cache if ($useParserCache) { $this->mParserOutput = $parserCache->get($this, $parserOptions); if ($this->mParserOutput !== false) { wfDebug(__METHOD__ . ": showing parser cache contents\n"); $wgOut->addParserOutput($this->mParserOutput); # Ensure that UI elements requiring revision ID have # the correct version information. $wgOut->setRevisionId($this->mPage->getLatest()); $outputDone = true; # Preload timestamp to avoid a DB hit if (isset($this->mParserOutput->mTimestamp)) { $this->mPage->setTimestamp($this->mParserOutput->mTimestamp); } } } break; case 3: $text = $this->getContent(); if ($text === false || $this->mPage->getID() == 0) { wfDebug(__METHOD__ . ": showing missing article\n"); $this->showMissingArticle(); wfProfileOut(__METHOD__); return; } # Another whitelist check in case oldid is altering the title if (!$this->getTitle()->userCanRead()) { wfDebug(__METHOD__ . ": denied on secondary read check\n"); $wgOut->loginToUse(); $wgOut->output(); $wgOut->disable(); wfProfileOut(__METHOD__); return; } # Are we looking at an old revision if ($oldid && !is_null($this->mRevision)) { $this->setOldSubtitle($oldid); if (!$this->showDeletedRevisionHeader()) { wfDebug(__METHOD__ . ": cannot view deleted revision\n"); wfProfileOut(__METHOD__); return; } # If this "old" version is the current, then try the parser cache... if ($oldid === $this->mPage->getLatest() && $this->useParserCache(false)) { $this->mParserOutput = $parserCache->get($this, $parserOptions); if ($this->mParserOutput) { wfDebug(__METHOD__ . ": showing parser cache for current rev permalink\n"); $wgOut->addParserOutput($this->mParserOutput); $wgOut->setRevisionId($this->mPage->getLatest()); $outputDone = true; break; } } } # Ensure that UI elements requiring revision ID have # the correct version information. $wgOut->setRevisionId($this->getRevIdFetched()); # Pages containing custom CSS or JavaScript get special treatment if ($this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage()) { wfDebug(__METHOD__ . ": showing CSS/JS source\n"); $this->showCssOrJsPage(); $outputDone = true; } else { $rt = Title::newFromRedirectArray($text); if ($rt) { wfDebug(__METHOD__ . ": showing redirect=no page\n"); # Viewing a redirect page (e.g. with parameter redirect=no) # Don't append the subtitle if this was an old revision $wgOut->addHTML($this->viewRedirect($rt, !$wasRedirected && $this->isCurrent())); # Parse just to get categories, displaytitle, etc. $this->mParserOutput = $wgParser->parse($text, $this->getTitle(), $parserOptions); $wgOut->addParserOutputNoText($this->mParserOutput); $outputDone = true; } } break; case 4: # Run the parse, protected by a pool counter wfDebug(__METHOD__ . ": doing uncached parse\n"); $key = $parserCache->getKey($this, $parserOptions); $poolArticleView = new PoolWorkArticleView($this, $key, $useParserCache, $parserOptions); if (!$poolArticleView->execute()) { # Connection or timeout error wfProfileOut(__METHOD__); return; } else { $outputDone = true; } break; # Should be unreachable, but just in case... # Should be unreachable, but just in case... default: break 2; } } # Adjust the title if it was set by displaytitle, -{T|}- or language conversion if ($this->mParserOutput) { $titleText = $this->mParserOutput->getTitleText(); if (strval($titleText) !== '') { $wgOut->setPageTitle($titleText); } } # For the main page, overwrite the <title> element with the con- # tents of 'pagetitle-view-mainpage' instead of the default (if # that's not empty). # This message always exists because it is in the i18n files if ($this->getTitle()->equals(Title::newMainPage())) { $msg = wfMessage('pagetitle-view-mainpage')->inContentLanguage(); if (!$msg->isDisabled()) { $wgOut->setHTMLTitle($msg->title($this->getTitle())->text()); } } # Now that we've filled $this->mParserOutput, we know whether # there are any __NOINDEX__ tags on the page $policy = $this->getRobotPolicy('view'); $wgOut->setIndexPolicy($policy['index']); $wgOut->setFollowPolicy($policy['follow']); $this->showViewFooter(); $this->mPage->viewUpdates(); wfProfileOut(__METHOD__); }