/** * Show the new revision of the page. */ public function renderNewRevision() { $out = $this->getOutput(); $revHeader = $this->getRevisionHeader($this->mNewRev); # Add "current version as of X" title $out->addHTML("<hr class='diff-hr' id='mw-oldid' />\n\t\t<h2 class='diff-currentversion-title'>{$revHeader}</h2>\n"); # Page content may be handled by a hooked call instead... # @codingStandardsIgnoreStart Ignoring long lines. if (Hooks::run('ArticleContentOnDiff', [$this, $out])) { $this->loadNewText(); $out->setRevisionId($this->mNewid); $out->setRevisionTimestamp($this->mNewRev->getTimestamp()); $out->setArticleFlag(true); // NOTE: only needed for B/C: custom rendering of JS/CSS via hook if ($this->mNewPage->isCssJsSubpage() || $this->mNewPage->isCssOrJsPage()) { // This needs to be synchronised with Article::showCssOrJsPage(), which sucks // Give hooks a chance to customise the output // @todo standardize this crap into one function if (ContentHandler::runLegacyHooks('ShowRawCssJs', [$this->mNewContent, $this->mNewPage, $out], '1.24')) { // NOTE: deprecated hook, B/C only // use the content object's own rendering $cnt = $this->mNewRev->getContent(); $po = $cnt ? $cnt->getParserOutput($this->mNewRev->getTitle(), $this->mNewRev->getId()) : null; if ($po) { $out->addParserOutputContent($po); } } } elseif (!Hooks::run('ArticleContentViewCustom', [$this->mNewContent, $this->mNewPage, $out])) { // Handled by extension } elseif (!ContentHandler::runLegacyHooks('ArticleViewCustom', [$this->mNewContent, $this->mNewPage, $out], '1.21')) { // NOTE: deprecated hook, B/C only // Handled by extension } else { // Normal page if ($this->getTitle()->equals($this->mNewPage)) { // If the Title stored in the context is the same as the one // of the new revision, we can use its associated WikiPage // object. $wikiPage = $this->getWikiPage(); } else { // Otherwise we need to create our own WikiPage object $wikiPage = WikiPage::factory($this->mNewPage); } $parserOutput = $this->getParserOutput($wikiPage, $this->mNewRev); # WikiPage::getParserOutput() should not return false, but just in case if ($parserOutput) { // Allow extensions to change parser output here if (Hooks::run('DifferenceEngineRenderRevisionAddParserOutput', [$this, $out, $parserOutput, $wikiPage])) { $out->addParserOutput($parserOutput); } } } } # @codingStandardsIgnoreEnd // Allow extensions to optionally not show the final patrolled link if (Hooks::run('DifferenceEngineRenderRevisionShowFinalPatrolLink')) { # Add redundant patrol link on bottom... $out->addHTML($this->markPatrolledLink()); } }
/** * @param Title $title * @return null|string */ protected function getContent($title) { if (!$title->isCssJsSubpage() && !$title->isCssOrJsPage()) { return null; } $revision = Revision::newFromTitle($title, false, Revision::READ_NORMAL); if (!$revision) { return null; } $content = $revision->getContent(Revision::RAW); if (!$content) { wfDebugLog('resourceloader', __METHOD__ . ': failed to load content of JS/CSS page!'); return null; } $model = $content->getModel(); if ($model !== CONTENT_MODEL_CSS && $model !== CONTENT_MODEL_JAVASCRIPT) { wfDebugLog('resourceloader', __METHOD__ . ': bad content model $model for JS/CSS page!'); return null; } return $content->getNativeData(); //NOTE: this is safe, we know it's JS or CSS }
/** * @param Title $title * @return null|string */ protected function getContent($title) { if (!$title->isCssJsSubpage() && !$title->isCssOrJsPage()) { return null; } $revision = Revision::newFromTitle($title, false, Revision::READ_NORMAL); if (!$revision) { return null; } $content = $revision->getContent(Revision::RAW); if (!$content) { wfDebugLog('resourceloader', __METHOD__ . ': failed to load content of JS/CSS page!'); return null; } if ($content->isSupportedFormat(CONTENT_FORMAT_JAVASCRIPT)) { return $content->serialize(CONTENT_FORMAT_JAVASCRIPT); } elseif ($content->isSupportedFormat(CONTENT_FORMAT_CSS)) { return $content->serialize(CONTENT_FORMAT_CSS); } else { wfDebugLog('resourceloader', __METHOD__ . ": bad content model {$content->getModel()} for JS/CSS page!"); return null; } }
/** * Get the rendered text for previewing. * @return string */ function getPreviewText() { global $wgOut, $wgUser, $wgParser, $wgRawHtml; wfProfileIn(__METHOD__); if ($wgRawHtml && !$this->mTokenOk) { // Could be an offsite preview attempt. This is very unsafe if // HTML is enabled, as it could be an attack. $parsedNote = ''; if ($this->textbox1 !== '') { // Do not put big scary notice, if previewing the empty // string, which happens when you initially edit // a category page, due to automatic preview-on-open. $parsedNote = $wgOut->parse("<div class='previewnote'>" . wfMsg('session_fail_preview_html') . "</div>", true, true); } wfProfileOut(__METHOD__); return $parsedNote; } if ($this->mTriedSave && !$this->mTokenOk) { if ($this->mTokenOkExceptSuffix) { $note = wfMsg('token_suffix_mismatch'); } else { $note = wfMsg('session_fail_preview'); } } elseif ($this->incompleteForm) { $note = wfMsg('edit_form_incomplete'); } else { $note = wfMsg('previewnote'); } $parserOptions = ParserOptions::newFromUser($wgUser); $parserOptions->setEditSection(false); $parserOptions->setTidy(true); $parserOptions->setIsPreview(true); $parserOptions->setIsSectionPreview(!is_null($this->section) && $this->section !== ''); # don't parse non-wikitext pages, show message about preview # XXX: stupid php bug won't let us use $this->getContextTitle()->isCssJsSubpage() here -- This note has been there since r3530. Sure the bug was fixed time ago? if ($this->isCssJsSubpage || !$this->mTitle->isWikitextPage()) { if ($this->mTitle->isCssJsSubpage()) { $level = 'user'; } elseif ($this->mTitle->isCssOrJsPage()) { $level = 'site'; } else { $level = false; } # Used messages to make sure grep find them: # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview if ($level) { if (preg_match("/\\.css\$/", $this->mTitle->getText())) { $previewtext = "<div id='mw-{$level}csspreview'>\n" . wfMsg("{$level}csspreview") . "\n</div>"; $class = "mw-code mw-css"; } elseif (preg_match("/\\.js\$/", $this->mTitle->getText())) { $previewtext = "<div id='mw-{$level}jspreview'>\n" . wfMsg("{$level}jspreview") . "\n</div>"; $class = "mw-code mw-js"; } else { throw new MWException('A CSS/JS (sub)page but which is not css nor js!'); } } $parserOutput = $wgParser->parse($previewtext, $this->mTitle, $parserOptions); $previewHTML = $parserOutput->mText; $previewHTML .= "<pre class=\"{$class}\" dir=\"ltr\">\n" . htmlspecialchars($this->textbox1) . "\n</pre>\n"; } else { $rt = Title::newFromRedirectArray($this->textbox1); if ($rt) { $previewHTML = $this->mArticle->viewRedirect($rt, false); } else { $toparse = $this->textbox1; # If we're adding a comment, we need to show the # summary as the headline if ($this->section == "new" && $this->summary != "") { $toparse = wfMsgForContent('newsectionheaderdefaultlevel', $this->summary) . "\n\n" . $toparse; } wfRunHooks('EditPageGetPreviewText', array($this, &$toparse)); $parserOptions->enableLimitReport(); $toparse = $wgParser->preSaveTransform($toparse, $this->mTitle, $wgUser, $parserOptions); $parserOutput = $wgParser->parse($toparse, $this->mTitle, $parserOptions); $previewHTML = $parserOutput->getText(); $this->mParserOutput = $parserOutput; $wgOut->addParserOutputNoText($parserOutput); if (count($parserOutput->getWarnings())) { $note .= "\n\n" . implode("\n\n", $parserOutput->getWarnings()); } } } if ($this->isConflict) { $conflict = '<h2 id="mw-previewconflict">' . htmlspecialchars(wfMsg('previewconflict')) . "</h2>\n"; } else { $conflict = '<hr />'; } $previewhead = "<div class='previewnote'>\n" . '<h2 id="mw-previewheader">' . htmlspecialchars(wfMsg('preview')) . "</h2>" . $wgOut->parse($note, true, true) . $conflict . "</div>\n"; $pageLang = $this->mTitle->getPageLanguage(); $attribs = array('lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(), 'class' => 'mw-content-' . $pageLang->getDir()); $previewHTML = Html::rawElement('div', $attribs, $previewHTML); wfProfileOut(__METHOD__); return $previewhead . $previewHTML . $this->previewTextAfterContent; }
/** * Should the parser cache be used? * * @param $user User The relevant user * @return boolean */ public function isParserCacheUsed(User $user, $oldid) { global $wgEnableParserCache; return $wgEnableParserCache && $user->getStubThreshold() == 0 && $this->exists() && empty($oldid) && !$this->mTitle->isCssOrJsPage() && !$this->mTitle->isCssJsSubpage(); }
/** * Get the rendered text for previewing. * @throws MWException * @return string */ function getPreviewText() { global $wgOut, $wgUser, $wgRawHtml, $wgLang; global $wgAllowUserCss, $wgAllowUserJs; wfProfileIn(__METHOD__); if ($wgRawHtml && !$this->mTokenOk) { // Could be an offsite preview attempt. This is very unsafe if // HTML is enabled, as it could be an attack. $parsedNote = ''; if ($this->textbox1 !== '') { // Do not put big scary notice, if previewing the empty // string, which happens when you initially edit // a category page, due to automatic preview-on-open. $parsedNote = $wgOut->parse("<div class='previewnote'>" . wfMessage('session_fail_preview_html')->text() . "</div>", true, true); } wfProfileOut(__METHOD__); return $parsedNote; } $note = ''; try { $content = $this->toEditContent($this->textbox1); $previewHTML = ''; if (!wfRunHooks('AlternateEditPreview', array($this, &$content, &$previewHTML, &$this->mParserOutput))) { wfProfileOut(__METHOD__); return $previewHTML; } # provide a anchor link to the editform $continueEditing = '<span class="mw-continue-editing">' . '[[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . wfMessage('continue-editing')->text() . ']]</span>'; if ($this->mTriedSave && !$this->mTokenOk) { if ($this->mTokenOkExceptSuffix) { $note = wfMessage('token_suffix_mismatch')->plain(); } else { $note = wfMessage('session_fail_preview')->plain(); } } elseif ($this->incompleteForm) { $note = wfMessage('edit_form_incomplete')->plain(); } else { $note = wfMessage('previewnote')->plain() . ' ' . $continueEditing; } $parserOptions = $this->mArticle->makeParserOptions($this->mArticle->getContext()); $parserOptions->setEditSection(false); $parserOptions->setIsPreview(true); $parserOptions->setIsSectionPreview(!is_null($this->section) && $this->section !== ''); # don't parse non-wikitext pages, show message about preview if ($this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage()) { if ($this->mTitle->isCssJsSubpage()) { $level = 'user'; } elseif ($this->mTitle->isCssOrJsPage()) { $level = 'site'; } else { $level = false; } if ($content->getModel() == CONTENT_MODEL_CSS) { $format = 'css'; if ($level === 'user' && !$wgAllowUserCss) { $format = false; } } elseif ($content->getModel() == CONTENT_MODEL_JAVASCRIPT) { $format = 'js'; if ($level === 'user' && !$wgAllowUserJs) { $format = false; } } else { $format = false; } # Used messages to make sure grep find them: # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview if ($level && $format) { $note = "<div id='mw-{$level}{$format}preview'>" . wfMessage("{$level}{$format}preview")->text() . ' ' . $continueEditing . "</div>"; } } # If we're adding a comment, we need to show the # summary as the headline if ($this->section === "new" && $this->summary !== "") { $content = $content->addSectionHeader($this->summary); } $hook_args = array($this, &$content); ContentHandler::runLegacyHooks('EditPageGetPreviewText', $hook_args); wfRunHooks('EditPageGetPreviewContent', $hook_args); $parserOptions->enableLimitReport(); # For CSS/JS pages, we should have called the ShowRawCssJs hook here. # But it's now deprecated, so never mind $content = $content->preSaveTransform($this->mTitle, $wgUser, $parserOptions); $parserOutput = $content->getParserOutput($this->getArticle()->getTitle(), null, $parserOptions); $previewHTML = $parserOutput->getText(); $this->mParserOutput = $parserOutput; $wgOut->addParserOutputMetadata($parserOutput); if (count($parserOutput->getWarnings())) { $note .= "\n\n" . implode("\n\n", $parserOutput->getWarnings()); } } catch (MWContentSerializationException $ex) { $m = wfMessage('content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage()); $note .= "\n\n" . $m->parse(); $previewHTML = ''; } if ($this->isConflict) { $conflict = '<h2 id="mw-previewconflict">' . wfMessage('previewconflict')->escaped() . "</h2>\n"; } else { $conflict = '<hr />'; } $previewhead = "<div class='previewnote'>\n" . '<h2 id="mw-previewheader">' . wfMessage('preview')->escaped() . "</h2>" . $wgOut->parse($note, true, true) . $conflict . "</div>\n"; $pageViewLang = $this->mTitle->getPageViewLanguage(); $attribs = array('lang' => $pageViewLang->getHtmlCode(), 'dir' => $pageViewLang->getDir(), 'class' => 'mw-content-' . $pageViewLang->getDir()); $previewHTML = Html::rawElement('div', $attribs, $previewHTML); wfProfileOut(__METHOD__); return $previewhead . $previewHTML . $this->previewTextAfterContent; }
/** * Check if edited page is a code page * (page to edit CSS, JS or Lua code) * * @param Title $articleTitle page title * @return bool */ public static function isCodePage(Title $articleTitle) { $namespace = $articleTitle->getNamespace(); return $articleTitle->isCssOrJsPage() || $articleTitle->isCssJsSubpage() || $namespace === NS_MODULE || self::isInfoboxTemplate($articleTitle); }
/** * Get the rendered text for previewing. * @throws MWException * @return string */ function getPreviewText() { global $wgOut, $wgRawHtml, $wgLang; global $wgAllowUserCss, $wgAllowUserJs; $stats = $wgOut->getContext()->getStats(); if ($wgRawHtml && !$this->mTokenOk) { // Could be an offsite preview attempt. This is very unsafe if // HTML is enabled, as it could be an attack. $parsedNote = ''; if ($this->textbox1 !== '') { // Do not put big scary notice, if previewing the empty // string, which happens when you initially edit // a category page, due to automatic preview-on-open. $parsedNote = $wgOut->parse("<div class='previewnote'>" . $this->context->msg('session_fail_preview_html')->text() . "</div>", true, true); } $stats->increment('edit.failures.session_loss'); return $parsedNote; } $note = ''; try { $content = $this->toEditContent($this->textbox1); $previewHTML = ''; if (!Hooks::run('AlternateEditPreview', [$this, &$content, &$previewHTML, &$this->mParserOutput])) { return $previewHTML; } # provide a anchor link to the editform $continueEditing = '<span class="mw-continue-editing">' . '[[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . $this->context->msg('continue-editing')->text() . ']]</span>'; if ($this->mTriedSave && !$this->mTokenOk) { if ($this->mTokenOkExceptSuffix) { $note = $this->context->msg('token_suffix_mismatch')->plain(); $stats->increment('edit.failures.bad_token'); } else { $note = $this->context->msg('session_fail_preview')->plain(); $stats->increment('edit.failures.session_loss'); } } elseif ($this->incompleteForm) { $note = $this->context->msg('edit_form_incomplete')->plain(); if ($this->mTriedSave) { $stats->increment('edit.failures.incomplete_form'); } } else { $note = $this->context->msg('previewnote')->plain() . ' ' . $continueEditing; } # don't parse non-wikitext pages, show message about preview if ($this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage()) { if ($this->mTitle->isCssJsSubpage()) { $level = 'user'; } elseif ($this->mTitle->isCssOrJsPage()) { $level = 'site'; } else { $level = false; } if ($content->getModel() == CONTENT_MODEL_CSS) { $format = 'css'; if ($level === 'user' && !$wgAllowUserCss) { $format = false; } } elseif ($content->getModel() == CONTENT_MODEL_JAVASCRIPT) { $format = 'js'; if ($level === 'user' && !$wgAllowUserJs) { $format = false; } } else { $format = false; } # Used messages to make sure grep find them: # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview if ($level && $format) { $note = "<div id='mw-{$level}{$format}preview'>" . $this->context->msg("{$level}{$format}preview")->text() . ' ' . $continueEditing . "</div>"; } } # If we're adding a comment, we need to show the # summary as the headline if ($this->section === "new" && $this->summary !== "") { $content = $content->addSectionHeader($this->summary); } $hook_args = [$this, &$content]; ContentHandler::runLegacyHooks('EditPageGetPreviewText', $hook_args, '1.25'); Hooks::run('EditPageGetPreviewContent', $hook_args); $parserResult = $this->doPreviewParse($content); $parserOutput = $parserResult['parserOutput']; $previewHTML = $parserResult['html']; $this->mParserOutput = $parserOutput; $wgOut->addParserOutputMetadata($parserOutput); if (count($parserOutput->getWarnings())) { $note .= "\n\n" . implode("\n\n", $parserOutput->getWarnings()); } } catch (MWContentSerializationException $ex) { $m = $this->context->msg('content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage()); $note .= "\n\n" . $m->parse(); $previewHTML = ''; } if ($this->isConflict) { $conflict = '<h2 id="mw-previewconflict">' . $this->context->msg('previewconflict')->escaped() . "</h2>\n"; } else { $conflict = '<hr />'; } $previewhead = "<div class='previewnote'>\n" . '<h2 id="mw-previewheader">' . $this->context->msg('preview')->escaped() . "</h2>" . $wgOut->parse($note, true, true) . $conflict . "</div>\n"; $pageViewLang = $this->mTitle->getPageViewLanguage(); $attribs = ['lang' => $pageViewLang->getHtmlCode(), 'dir' => $pageViewLang->getDir(), 'class' => 'mw-content-' . $pageViewLang->getDir()]; $previewHTML = Html::rawElement('div', $attribs, $previewHTML); return $previewhead . $previewHTML . $this->previewTextAfterContent; }
/** * Show the new revision of the page. */ public function renderNewRevision() { wfProfileIn(__METHOD__); $out = $this->getOutput(); $revHeader = $this->getRevisionHeader($this->mNewRev); # Add "current version as of X" title $out->addHTML("<hr class='diff-hr' />\n\t\t<h2 class='diff-currentversion-title'>{$revHeader}</h2>\n"); # Page content may be handled by a hooked call instead... # @codingStandardsIgnoreStart Ignoring long lines. if (wfRunHooks('ArticleContentOnDiff', array($this, $out))) { $this->loadNewText(); $out->setRevisionId($this->mNewid); $out->setRevisionTimestamp($this->mNewRev->getTimestamp()); $out->setArticleFlag(true); // NOTE: only needed for B/C: custom rendering of JS/CSS via hook if ($this->mNewPage->isCssJsSubpage() || $this->mNewPage->isCssOrJsPage()) { // Stolen from Article::view --AG 2007-10-11 // Give hooks a chance to customise the output // @todo standardize this crap into one function if (ContentHandler::runLegacyHooks('ShowRawCssJs', array($this->mNewContent, $this->mNewPage, $out))) { // NOTE: deprecated hook, B/C only // use the content object's own rendering $cnt = $this->mNewRev->getContent(); $po = $cnt ? $cnt->getParserOutput($this->mNewRev->getTitle(), $this->mNewRev->getId()) : null; $txt = $po ? $po->getText() : ''; $out->addHTML($txt); } } elseif (!wfRunHooks('ArticleContentViewCustom', array($this->mNewContent, $this->mNewPage, $out))) { // Handled by extension } elseif (!ContentHandler::runLegacyHooks('ArticleViewCustom', array($this->mNewContent, $this->mNewPage, $out))) { // NOTE: deprecated hook, B/C only // Handled by extension } else { // Normal page if ($this->getTitle()->equals($this->mNewPage)) { // If the Title stored in the context is the same as the one // of the new revision, we can use its associated WikiPage // object. $wikiPage = $this->getWikiPage(); } else { // Otherwise we need to create our own WikiPage object $wikiPage = WikiPage::factory($this->mNewPage); } $parserOutput = $this->getParserOutput($wikiPage, $this->mNewRev); # Also try to load it as a redirect $rt = $this->mNewContent ? $this->mNewContent->getRedirectTarget() : null; if ($rt) { $article = Article::newFromTitle($this->mNewPage, $this->getContext()); $out->addHTML($article->viewRedirect($rt)); # WikiPage::getParserOutput() should not return false, but just in case if ($parserOutput) { # Show categories etc. $out->addParserOutputNoText($parserOutput); } } elseif ($parserOutput) { $out->addParserOutput($parserOutput); } } } # @codingStandardsIgnoreEnd # Add redundant patrol link on bottom... $out->addHTML($this->markPatrolledLink()); wfProfileOut(__METHOD__); }
/** * Clear the preloadTitleInfo() cache for all wiki modules on this wiki on * page change if it was a JS or CSS page * * @param Title $title * @param Revision|null $old Prior page revision * @param Revision|null $new New page revision * @param string $wikiId * @since 1.28 */ public static function invalidateModuleCache(Title $title, Revision $old = null, Revision $new = null, $wikiId) { static $formats = [CONTENT_FORMAT_CSS, CONTENT_FORMAT_JAVASCRIPT]; if ($old && in_array($old->getContentFormat(), $formats)) { $purge = true; } elseif ($new && in_array($new->getContentFormat(), $formats)) { $purge = true; } else { $purge = $title->isCssOrJsPage() || $title->isCssJsSubpage(); } if ($purge) { $cache = ObjectCache::getMainWANInstance(); $key = $cache->makeGlobalKey('resourceloader', 'titleinfo', $wikiId); $cache->touchCheckKey($key); } }