Пример #1
0
 protected function storeInSerializationCache($title, $oldid, $html, $etag)
 {
     global $wgMemc;
     // Convert the VE HTML to wikitext
     $text = $this->postHTML($title, $html, array('oldid' => $oldid), $etag);
     if ($text === false) {
         return false;
     }
     // Store the corresponding wikitext, referenceable by a new key
     $hash = md5($text);
     $key = wfMemcKey('visualeditor', 'serialization', $hash);
     $wgMemc->set($key, $text, $this->veConfig->get('VisualEditorSerializationCacheTimeout'));
     // Also parse and prepare the edit in case it might be saved later
     $page = WikiPage::factory($title);
     $content = ContentHandler::makeContent($text, $title, CONTENT_MODEL_WIKITEXT);
     $res = ApiStashEdit::parseAndStash($page, $content, $this->getUser());
     if ($res === ApiStashEdit::ERROR_NONE) {
         wfDebugLog('StashEdit', "Cached parser output for VE content key '{$key}'.");
     }
     return $hash;
 }
Пример #2
0
 /**
  * Prepare content which is about to be saved.
  * Returns a stdClass with source, pst and output members
  *
  * @param Content $content
  * @param Revision|int|null $revision Revision object. For backwards compatibility, a
  *        revision ID is also accepted, but this is deprecated.
  * @param User|null $user
  * @param string|null $serialFormat
  * @param bool $useCache Check shared prepared edit cache
  *
  * @return object
  *
  * @since 1.21
  */
 public function prepareContentForEdit(Content $content, $revision = null, User $user = null, $serialFormat = null, $useCache = true)
 {
     global $wgContLang, $wgUser, $wgAjaxEditStash;
     if (is_object($revision)) {
         $revid = $revision->getId();
     } else {
         $revid = $revision;
         // This code path is deprecated, and nothing is known to
         // use it, so performance here shouldn't be a worry.
         if ($revid !== null) {
             $revision = Revision::newFromId($revid, Revision::READ_LATEST);
         } else {
             $revision = null;
         }
     }
     $user = is_null($user) ? $wgUser : $user;
     // XXX: check $user->getId() here???
     // Use a sane default for $serialFormat, see bug 57026
     if ($serialFormat === null) {
         $serialFormat = $content->getContentHandler()->getDefaultFormat();
     }
     if ($this->mPreparedEdit && isset($this->mPreparedEdit->newContent) && $this->mPreparedEdit->newContent->equals($content) && $this->mPreparedEdit->revid == $revid && $this->mPreparedEdit->format == $serialFormat) {
         // Already prepared
         return $this->mPreparedEdit;
     }
     // The edit may have already been prepared via api.php?action=stashedit
     $cachedEdit = $useCache && $wgAjaxEditStash ? ApiStashEdit::checkCache($this->getTitle(), $content, $user) : false;
     $popts = ParserOptions::newFromUserAndLang($user, $wgContLang);
     Hooks::run('ArticlePrepareTextForEdit', [$this, $popts]);
     $edit = (object) [];
     if ($cachedEdit) {
         $edit->timestamp = $cachedEdit->timestamp;
     } else {
         $edit->timestamp = wfTimestampNow();
     }
     // @note: $cachedEdit is safely not used if the rev ID was referenced in the text
     $edit->revid = $revid;
     if ($cachedEdit) {
         $edit->pstContent = $cachedEdit->pstContent;
     } else {
         $edit->pstContent = $content ? $content->preSaveTransform($this->mTitle, $user, $popts) : null;
     }
     $edit->format = $serialFormat;
     $edit->popts = $this->makeParserOptions('canonical');
     if ($cachedEdit) {
         $edit->output = $cachedEdit->output;
     } else {
         if ($revision) {
             // We get here if vary-revision is set. This means that this page references
             // itself (such as via self-transclusion). In this case, we need to make sure
             // that any such self-references refer to the newly-saved revision, and not
             // to the previous one, which could otherwise happen due to replica DB lag.
             $oldCallback = $edit->popts->getCurrentRevisionCallback();
             $edit->popts->setCurrentRevisionCallback(function (Title $title, $parser = false) use($revision, &$oldCallback) {
                 if ($title->equals($revision->getTitle())) {
                     return $revision;
                 } else {
                     return call_user_func($oldCallback, $title, $parser);
                 }
             });
         } else {
             // Try to avoid a second parse if {{REVISIONID}} is used
             $edit->popts->setSpeculativeRevIdCallback(function () {
                 return 1 + (int) wfGetDB(DB_MASTER)->selectField('revision', 'MAX(rev_id)', [], __METHOD__);
             });
         }
         $edit->output = $edit->pstContent ? $edit->pstContent->getParserOutput($this->mTitle, $revid, $edit->popts) : null;
     }
     $edit->newContent = $content;
     $edit->oldContent = $this->getContent(Revision::RAW);
     // NOTE: B/C for hooks! don't use these fields!
     $edit->newText = $edit->newContent ? ContentHandler::getContentText($edit->newContent) : '';
     $edit->oldText = $edit->oldContent ? ContentHandler::getContentText($edit->oldContent) : '';
     $edit->pst = $edit->pstContent ? $edit->pstContent->serialize($serialFormat) : '';
     if ($edit->output) {
         $edit->output->setCacheTime(wfTimestampNow());
     }
     // Process cache the result
     $this->mPreparedEdit = $edit;
     return $edit;
 }
Пример #3
0
 /**
  * Get the rendered text for previewing.
  * @throws MWException
  * @return string
  */
 function getPreviewText()
 {
     global $wgOut, $wgUser, $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'>" . wfMessage('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', array($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() . ' ' . wfMessage('continue-editing')->text() . ']]</span>';
         if ($this->mTriedSave && !$this->mTokenOk) {
             if ($this->mTokenOkExceptSuffix) {
                 $note = wfMessage('token_suffix_mismatch')->plain();
                 $stats->increment('edit.failures.bad_token');
             } else {
                 $note = wfMessage('session_fail_preview')->plain();
                 $stats->increment('edit.failures.session_loss');
             }
         } elseif ($this->incompleteForm) {
             $note = wfMessage('edit_form_incomplete')->plain();
             if ($this->mTriedSave) {
                 $stats->increment('edit.failures.incomplete_form');
             }
         } else {
             $note = wfMessage('previewnote')->plain() . ' ' . $continueEditing;
         }
         $parserOptions = $this->page->makeParserOptions($this->mArticle->getContext());
         $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);
         Hooks::run('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
         $pstContent = $content->preSaveTransform($this->mTitle, $wgUser, $parserOptions);
         $scopedCallback = $parserOptions->setupFakeRevision($this->mTitle, $pstContent, $wgUser);
         $parserOutput = $pstContent->getParserOutput($this->mTitle, null, $parserOptions);
         # Try to stash the edit for the final submission step
         # @todo: different date format preferences cause cache misses
         ApiStashEdit::stashEditFromPreview($this->getArticle(), $content, $pstContent, $parserOutput, $parserOptions, $parserOptions, wfTimestampNow());
         $parserOutput->setEditSectionTokens(false);
         // no section edit links
         $previewHTML = $parserOutput->getText();
         $this->mParserOutput = $parserOutput;
         $wgOut->addParserOutputMetadata($parserOutput);
         if (count($parserOutput->getWarnings())) {
             $note .= "\n\n" . implode("\n\n", $parserOutput->getWarnings());
         }
         ScopedCallback::consume($scopedCallback);
     } 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);
     return $previewhead . $previewHTML . $this->previewTextAfterContent;
 }