From c6eaef668133657fa1dc06b33643e2eed5a8a3aa Mon Sep 17 00:00:00 2001 From: Daniel Kinzler Date: Thu, 22 Mar 2012 17:43:17 +0000 Subject: [PATCH] reworking EditPage to use the content object - work in horrible progress --- includes/Content.php | 92 ++++++++++- includes/ContentHandler.php | 2 +- includes/EditPage.php | 255 ++++++++++++++++++++--------- includes/WikiPage.php | 86 +++++----- includes/diff/DifferenceEngine.php | 2 +- 5 files changed, 316 insertions(+), 121 deletions(-) diff --git a/includes/Content.php b/includes/Content.php index 1ef81b0424..d7e43bbdc8 100644 --- a/includes/Content.php +++ b/includes/Content.php @@ -24,10 +24,25 @@ abstract class Content { return $this->getContentHandler()->serialize( $this, $format ); } + /** + * @return String a string representing the content in a way useful for building a full text search index. + * If no useful representation exists, this method returns an empty string. + */ public abstract function getTextForSearchIndex( ); - public abstract function getWikitextForTransclusion( ); + /** + * @return String the wikitext to include when another page includes this content, or false if the content is not + * includable in a wikitext page. + */ + #TODO: allow native handling, bypassing wikitext representation, like for includable special pages. + public abstract function getWikitextForTransclusion( ); #FIXME: use in parser, etc! + /** + * Returns a textual representation of the content suitable for use in edit summaries and log messages. + * + * @param int $maxlength maximum length of the summary text + * @return String the summary text + */ public abstract function getTextForSummary( $maxlength = 250 ); /** @@ -67,16 +82,50 @@ abstract class Content { */ public abstract function isCountable( $hasLinks = null ) ; + /** + * @param null|Title $title + * @param null $revId + * @param null|ParserOptions $options + * @return ParserOutput + */ public abstract function getParserOutput( Title $title = null, $revId = null, ParserOptions $options = NULL ); - public function getRedirectChain() { #TODO: document! + /** + * Construct the redirect destination from this content and return an + * array of Titles, or null if this content doesn't represent a redirect. + * The last element in the array is the final destination after all redirects + * have been resolved (up to $wgMaxRedirects times). + * + * @return Array of Titles, with the destination last + */ + public function getRedirectChain() { return null; } + /** + * Construct the redirect destination from this content and return an + * array of Titles, or null if this content doesn't represent a redirect. + * This will only return the immediate redirect target, useful for + * the redirect table and other checks that don't need full recursion. + * + * @return Title: The corresponding Title + */ public function getRedirectTarget() { return null; } + /** + * Construct the redirect destination from this content and return the + * Title, or null if this content doesn't represent a redirect. + * This will recurse down $wgMaxRedirects times or until a non-redirect target is hit + * in order to provide (hopefully) the Title of the final destination instead of another redirect. + * + * @return Title + */ + public function getUltimateRedirectTarget() { + return null; + } + public function isRedirect() { return $this->getRedirectTarget() != null; } @@ -117,6 +166,17 @@ abstract class Content { return $this; } + /** + * Returns a Content object with preload transformations applied (or this object if no transformations apply). + * + * @param Title $title + * @param null|ParserOptions $popts + * @return Content + */ + public function preloadTransform( Title $title, ParserOptions $popts = null ) { + return $this; + } + #TODO: implement specialized ParserOutput for Wikidata model #TODO: provide "combined" ParserOutput for Multipart... somehow. @@ -124,7 +184,6 @@ abstract class Content { # TODO: EditPage::getPreloadedText( $preload ) // $wgParser->getPreloadText # TODO: tie into EditPage, make it use Content-objects throughout, make edit form aware of content model and format - # TODO: tie into WikiPage, make it use Content-objects throughout, especially in doEditUpdates(), doDelete(), updateRevisionOn(), etc # TODO: make model-aware diff view! # TODO: handle ImagePage and CategoryPage @@ -340,12 +399,32 @@ class WikitextContent extends TextContent { public function preSaveTransform( Title $title, User $user, ParserOptions $popts = null ) { global $wgParser; + if ( $popts == null ) $popts = $this->getDefaultParserOptions(); + $text = $this->getNativeData(); $pst = $wgParser->preSaveTransform( $text, $title, $user, $popts ); return new WikitextContent( $pst ); } + /** + * Returns a Content object with preload transformations applied (or this object if no transformations apply). + * + * @param Title $title + * @param null|ParserOptions $popts + * @return Content + */ + public function preloadTransform( Title $title, ParserOptions $popts = null ) { + global $wgParser; + + if ( $popts == null ) $popts = $this->getDefaultParserOptions(); + + $text = $this->getNativeData(); + $plt = $wgParser->getPreloadText( $text, $title, $popts ); + + return new WikitextContent( $plt ); + } + public function getRedirectChain() { $text = $this->getNativeData(); return Title::newFromRedirectArray( $text ); @@ -356,6 +435,11 @@ class WikitextContent extends TextContent { return Title::newFromRedirect( $text ); } + public function getUltimateRedirectTarget() { + $text = $this->getNativeData(); + return Title::newFromRedirectRecurse( $text ); + } + /** * Returns true if this content is not a redirect, and this content's text is countable according to * the criteria defiend by $wgArticleCountMethod. @@ -405,7 +489,7 @@ class WikitextContent extends TextContent { class MessageContent extends TextContent { public function __construct( $msg_key, $params = null, $options = null ) { - parent::__construct(null, CONTENT_MODEL_WIKITEXT); + parent::__construct(null, CONTENT_MODEL_WIKITEXT); #XXX: messages may be wikitext, html or plain text! and maybe even something else entirely. $this->mMessageKey = $msg_key; diff --git a/includes/ContentHandler.php b/includes/ContentHandler.php index c9b27d3786..567c8d6db4 100644 --- a/includes/ContentHandler.php +++ b/includes/ContentHandler.php @@ -213,7 +213,7 @@ abstract class ContentHandler { } **/ - public function getDiffEngine( Article $article ) { + public function getDiffEngine( Article $article ) { #FIXME: change interface of diff engine? or accept content objects here=? $de = new DifferenceEngine( $article->getContext() ); return $de; } diff --git a/includes/EditPage.php b/includes/EditPage.php index 690b0e538e..6f6a26faba 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -192,6 +192,7 @@ class EditPage { var $textbox1 = '', $textbox2 = '', $summary = '', $nosummary = false; var $edittime = '', $section = '', $sectiontitle = '', $starttime = ''; var $oldid = 0, $editintro = '', $scrolltop = null, $bot = true; + var $content_model = null, $content_format = null; # Placeholders for text injection by hooks (must be HTML) # extensions should take care to _append_ to the present value @@ -203,7 +204,7 @@ class EditPage { public $editFormTextBottom = ''; public $editFormTextAfterContent = ''; public $previewTextAfterContent = ''; - public $mPreloadText = ''; + public $mPreloadContent = null; /* $didSave should be set to true whenever an article was succesfully altered. */ public $didSave = false; @@ -217,6 +218,11 @@ class EditPage { public function __construct( Article $article ) { $this->mArticle = $article; $this->mTitle = $article->getTitle(); + + $this->content_model = $this->mTitle->getContentModelName(); + + $handler = ContentHandler::getForModelName( $this->content_model ); + $this->content_format = $handler->getDefaultFormat(); #NOTE: should be overridden by format of actual revision } /** @@ -428,10 +434,10 @@ class EditPage { return; } - $content = $this->getContent(); + $content = $this->getContentObject(); # Use the normal message if there's nothing to display - if ( $this->firsttime && $content === '' ) { + if ( $this->firsttime && $content->isEmpty() ) { $action = $this->mTitle->exists() ? 'edit' : ( $this->mTitle->isTalkPage() ? 'createtalk' : 'createpage' ); throw new PermissionsError( $action, $permErrors ); @@ -445,13 +451,18 @@ class EditPage { # If the user made changes, preserve them when showing the markup # (This happens when a user is blocked during edit, for instance) if ( !$this->firsttime ) { - $content = $this->textbox1; + $handler = ContentHandler::getForModelName( $this->content_model ); + + if ( empty( $this->textbox1 ) ) $content = $handler->emptyContent(); + else $content = $handler->unserialize( $this->textbox1 ); + $wgOut->addWikiMsg( 'viewyourtext' ); } else { $wgOut->addWikiMsg( 'viewsourcetext' ); } - $this->showTextbox( $content, 'wpTextbox1', array( 'readonly' ) ); + $text = $content->serialize( $this->content_format ); + $this->showTextbox( $text, 'wpTextbox1', array( 'readonly' ) ); $wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'templatesUsed' ), Linker::formatTemplates( $this->getTemplates() ) ) ); @@ -563,9 +574,9 @@ class EditPage { // from a conflict page with raw page text, not a custom form // modified by subclasses wfProfileIn( get_class($this)."::importContentFormData" ); - $textbox1 = $this->importContentFormData( $request ); + $textbox1 = $this->importContentFormData( $request ); #FIXME: what should this return?? if ( isset($textbox1) ) - $this->textbox1 = $textbox1; + $this->textbox1 = $textbox1; #XXX: unserialize to Content-object... when? wfProfileOut( get_class($this)."::importContentFormData" ); } @@ -657,7 +668,7 @@ class EditPage { } else { # Not a posted form? Start with nothing. wfDebug( __METHOD__ . ": Not a posted form.\n" ); - $this->textbox1 = ''; + $this->textbox1 = ''; #FIXME: track content object $this->summary = ''; $this->sectiontitle = ''; $this->edittime = ''; @@ -686,10 +697,14 @@ class EditPage { } } + $this->oldid = $request->getInt( 'oldid' ); + $this->bot = $request->getBool( 'bot', true ); $this->nosummary = $request->getBool( 'nosummary' ); - $this->oldid = $request->getInt( 'oldid' ); + $content_handler = ContentHandler::getForTitle( $this->mTitle ); + $this->content_model = $request->getText( 'model', $content_handler->getModelName() ); #may be overridden by revision + $this->content_format = $request->getText( 'format', $content_handler->getDefaultFormat() ); #may be overridden by revision $this->live = $request->getCheck( 'live' ); $this->editintro = $request->getText( 'editintro', @@ -722,7 +737,10 @@ class EditPage { function initialiseForm() { global $wgUser; $this->edittime = $this->mArticle->getTimestamp(); - $this->textbox1 = $this->getContent( false ); + + $content = $this->getContentObject( false ); #TODO: track content object?! + $this->textbox1 = $content->serialize( $this->content_format ); + // activate checkboxes if user wants them to be always active # Sort out the "watch" checkbox if ( $wgUser->getOption( 'watchdefault' ) ) { @@ -751,33 +769,52 @@ class EditPage { * @param $def_text string * @return mixed string on success, $def_text for invalid sections * @private + * @deprecated since 1.20 */ - function getContent( $def_text = '' ) { - global $wgOut, $wgRequest, $wgParser; + function getContent( $def_text = false ) { #FIXME: deprecated, replace usage! + if ( $def_text !== null && $def_text !== false && $def_text !== '' ) { + $def_content = ContentHandler::makeContent( $def_text, $this->getTitle() ); + } else { + $def_content = false; + } + + $content = $this->getContentObject( $def_content ); + + return $content->serialize( $this->content_format ); #XXX: really use serialized form? use ContentHandler::getContentText() instead? + } + + private function getContentObject( $def_content = null ) { #FIXME: use this! + global $wgOut, $wgRequest; wfProfileIn( __METHOD__ ); - $text = false; + $content = false; // For message page not locally set, use the i18n message. // For other non-existent articles, use preload text if any. if ( !$this->mTitle->exists() || $this->section == 'new' ) { if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI && $this->section != 'new' ) { # If this is a system message, get the default text. - $text = $this->mTitle->getDefaultMessageText(); + $msg = $this->mTitle->getDefaultMessageText(); + + $content = new WikitextContent($msg); //XXX: really hardcode wikitext here? } - if ( $text === false ) { + if ( $content === false ) { # If requested, preload some text. $preload = $wgRequest->getVal( 'preload', // Custom preload text for new sections $this->section === 'new' ? 'MediaWiki:addsection-preload' : '' ); - $text = $this->getPreloadedText( $preload ); + + $content = $this->getPreloadedContent( $preload ); } // For existing pages, get text based on "undo" or section parameters. } else { if ( $this->section != '' ) { // Get section edit text (returns $def_text for invalid sections) - $text = $wgParser->getSection( $this->getOriginalContent(), $this->section, $def_text ); + $orig = $this->getOriginalContent(); + $content = $orig ? $orig->getSection( $this->section ) : null; + + if ( !$content ) $content = $def_content; } else { $undoafter = $wgRequest->getInt( 'undoafter' ); $undo = $wgRequest->getInt( 'undo' ); @@ -793,15 +830,16 @@ class EditPage { # Sanity check, make sure it's the right page, # the revisions exist and they were not deleted. - # Otherwise, $text will be left as-is. + # Otherwise, $content will be left as-is. if ( !is_null( $undorev ) && !is_null( $oldrev ) && $undorev->getPage() == $oldrev->getPage() && $undorev->getPage() == $this->mTitle->getArticleId() && !$undorev->isDeleted( Revision::DELETED_TEXT ) && !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) { - $text = $this->mArticle->getUndoText( $undorev, $oldrev ); - if ( $text === false ) { + $content = $this->mArticle->getUndoContent( $undorev, $oldrev ); + + if ( $content === false ) { # Warn the user that something went wrong $undoMsg = 'failure'; } else { @@ -832,14 +870,14 @@ class EditPage { wfMsgNoTrans( 'undo-' . $undoMsg ) . '', true, /* interface */true ); } - if ( $text === false ) { - $text = $this->getOriginalContent(); + if ( $content === false ) { + $content = $this->getOriginalContent(); } } } wfProfileOut( __METHOD__ ); - return $text; + return $content; } /** @@ -856,33 +894,45 @@ class EditPage { * @since 1.19 * @return string */ - private function getOriginalContent() { + private function getOriginalContent() { #FIXME: use Content! set content_model and content_format! if ( $this->section == 'new' ) { - return $this->getCurrentText(); + return $this->getCurrentContent(); } $revision = $this->mArticle->getRevisionFetched(); if ( $revision === null ) { - return ''; + if ( !$this->content_model ) $this->content_model = $this->getTitle()->getContentModelName(); + $handler = ContentHandler::getForModelName( $this->content_model ); + + return $handler->emptyContent(); } $content = $this->mArticle->getContentObject(); - return ContentHandler::getContentText( $content ); # this editor is for editing the raw text. so use the raw text. + return $content; } /** - * Get the actual text of the page. This is basically similar to - * WikiPage::getRawText() except that when the page doesn't exist an empty - * string is returned instead of false. + * Get the current content of the page. This is basically similar to + * WikiPage::getContent( Revision::RAW ) except that when the page doesn't exist an empty + * content object is returned instead of null. * - * @since 1.19 + * @since 1.20 * @return string */ - private function getCurrentText() { - $text = $this->mArticle->getRawText(); - if ( $text === false ) { - return ''; + private function getCurrentContent() { + $rev = $this->mArticle->getRevision(); + $content = $rev->getContentObject( Revision::RAW ); + + if ( $content === false || $content === null ) { + if ( !$this->content_model ) $this->content_model = $this->getTitle()->getContentModelName(); + $handler = ContentHandler::getForModelName( $this->content_model ); + + return $handler->emptyContent(); } else { - return $text; + #FIXME: nasty side-effect! + $this->content_model = $rev->getContentModelName(); + $this->content_format = $rev->getContentFormat(); + + return $content; } } @@ -890,33 +940,59 @@ class EditPage { * Use this method before edit() to preload some text into the edit box * * @param $text string + * @deprecated since 1.20 */ - public function setPreloadedText( $text ) { - $this->mPreloadText = $text; + public function setPreloadedText( $text ) { #FIXME: deprecated, use setPreloadedContent() + wfDeprecated( __METHOD__, "1.20" ); + + $content = ContentHandler::makeContent( $text, $this->getTitle() ); + + $this->setPreloadedContent( $content ); } + /** + * Use this method before edit() to preload some content into the edit box + * + * @param $content Content + */ + public function setPreloadedContent( Content $content ) { #FIXME: use this! + $this->mPreloadedContent = $content; + } + /** * Get the contents to be preloaded into the box, either set by * an earlier setPreloadText() or by loading the given page. * * @param $preload String: representing the title to preload from. * @return String + * @deprecated since 1.20 */ - protected function getPreloadedText( $preload ) { #FIXME: change to getPreloadedContent() - global $wgUser, $wgParser; + protected function getPreloadedText( $preload ) { #FIXME: B/C only, replace usage! + wfDeprecated( __METHOD__, "1.20" ); + + $content = $this->getPreloadedContent( $preload ); + $text = $content->serialize( $this->content_format ); #XXX: really use serialized form? use ContentHandler::getContentText() instead?! - if ( !empty( $this->mPreloadText ) ) { - return $this->mPreloadText; + return $text; + } + + protected function getPreloadedContent( $preload ) { #FIXME: use this! + global $wgUser; + + if ( !empty( $this->mPreloadContent ) ) { + return $this->mPreloadContent; } + + $handler = ContentHandler::getForTitle( $this->getTitle() ); if ( $preload === '' ) { - return ''; + return $handler->emptyContent(); } $title = Title::newFromText( $preload ); # Check for existence to avoid getting MediaWiki:Noarticletext if ( $title === null || !$title->exists() || !$title->userCan( 'read' ) ) { - return ''; + return $handler->emptyContent(); } $page = WikiPage::factory( $title ); @@ -924,13 +1000,15 @@ class EditPage { $title = $page->getRedirectTarget(); # Same as before if ( $title === null || !$title->exists() || !$title->userCan( 'read' ) ) { - return ''; + return $handler->emptyContent(); } $page = WikiPage::factory( $title ); } $parserOptions = ParserOptions::newFromUser( $wgUser ); - return $wgParser->getPreloadText( $page->getRawText(), $title, $parserOptions ); #FIXME: create Content::getPreloadCopy + $content = $page->getContent( Revision::RAW ); + + return $content->preloadTransform( $title, $parserOptions ); } /** @@ -1227,12 +1305,14 @@ class EditPage { } } - $text = $this->textbox1; + $content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format ); + $result['sectionanchor'] = ''; if ( $this->section == 'new' ) { + .........FIXME............... if ( $this->sectiontitle !== '' ) { // Insert the section title above the content. - $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->sectiontitle ) . "\n\n" . $text; + $T_E_X_T = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->sectiontitle ) . "\n\n" . $T_E_X_T; // Jump to the new section $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle ); @@ -1246,7 +1326,7 @@ class EditPage { } } elseif ( $this->summary !== '' ) { // Insert the section title above the content. - $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->summary ) . "\n\n" . $text; + $T_E_X_T = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->summary ) . "\n\n" . $T_E_X_T; // Jump to the new section $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->summary ); @@ -1299,24 +1379,24 @@ class EditPage { if ( $this->isConflict ) { wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '{$timestamp}')\n" ); - $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle, $this->edittime ); #FIXME: use Content object throughout, make edit form aware of content model and serialization format + $T_E_X_T = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle, $this->edittime ); #FIXME: use Content object throughout, make edit form aware of content model and serialization format } else { wfDebug( __METHOD__ . ": getting section '$this->section'\n" ); - $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle ); #FIXME: use Content object throughout, make edit form aware of content model and serialization format + $T_E_X_T = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle ); #FIXME: use Content object throughout, make edit form aware of content model and serialization format } - if ( is_null( $text ) ) { + if ( is_null( $T_E_X_T ) ) { wfDebug( __METHOD__ . ": activating conflict; section replace failed.\n" ); $this->isConflict = true; - $text = $this->textbox1; // do not try to merge here! #FIXME: unserialize Content + $T_E_X_T = $this->textbox1; // do not try to merge here! #FIXME: unserialize Content } elseif ( $this->isConflict ) { # Attempt merge - if ( $this->mergeChangesInto( $text ) ) { #FIXME: passe/receive Content object + if ( $this->mergeChangesInto( $T_E_X_T ) ) { #FIXME: passe/receive Content object // Successful merge! Maybe we should tell the user the good news? $this->isConflict = false; wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" ); } else { $this->section = ''; - $this->textbox1 = $text; + $this->textbox1 = $T_E_X_T; wfDebug( __METHOD__ . ": Keeping edit conflict, failed merge.\n" ); } } @@ -1328,7 +1408,7 @@ class EditPage { } // Run post-section-merge edit filter - if ( !wfRunHooks( 'EditFilterMerged', array( $this, $text, &$this->hookError, $this->summary ) ) ) { + if ( !wfRunHooks( 'EditFilterMerged', array( $this, $T_E_X_T, &$this->hookError, $this->summary ) ) ) { # Error messages etc. could be handled within the hook... $status->fatal( 'hookaborted' ); $status->value = self::AS_HOOK_ERROR; @@ -1344,8 +1424,8 @@ class EditPage { # Handle the user preference to force summaries here, but not for null edits if ( $this->section != 'new' && !$this->allowBlankSummary - && $this->getOriginalContent() != $text - && !Title::newFromRedirect( $text ) ) # check if it's not a redirect + && $this->getOriginalContent()... != $T_E_X_T + && !Title::newFromRedirect( $T_E_X_T ) ) # check if it's not a redirect { if ( md5( $this->summary ) == $this->autoSumm ) { $this->missingSummary = true; @@ -1413,14 +1493,14 @@ class EditPage { // merged the section into full text. Clear the section field // so that later submission of conflict forms won't try to // replace that into a duplicated mess. - $this->textbox1 = $text; + $this->textbox1 = $T_E_X_T; $this->section = ''; $status->value = self::AS_SUCCESS_UPDATE; } // Check for length errors again now that the section is merged in - $this->kblength = (int)( strlen( $text ) / 1024 ); + $this->kblength = (int)( strlen( $T_E_X_T ) / 1024 ); if ( $this->kblength > $wgMaxArticleSize ) { $this->tooBig = true; $status->setResult( false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED ); @@ -1433,10 +1513,10 @@ class EditPage { ( ( $this->minoredit && !$this->isNew ) ? EDIT_MINOR : 0 ) | ( $bot ? EDIT_FORCE_BOT : 0 ); - $doEditStatus = $this->mArticle->doEdit( $text, $this->summary, $flags ); # FIXME: use WikiPage::doEditContent() + $doEditStatus = $this->mArticle->doEdit( $T_E_X_T, $this->summary, $flags ); # FIXME: use WikiPage::doEditContent() if ( $doEditStatus->isOK() ) { - $result['redirect'] = Title::newFromRedirect( $text ) !== null; + $result['redirect'] = Title::newFromRedirect( $T_E_X_T ) !== null; $this->commitWatch(); wfProfileOut( __METHOD__ ); return $status; @@ -1524,11 +1604,11 @@ class EditPage { $currentContent = $currentRevision->getContent(); $handler = ContentHandler::getForModelName( $baseContent->getModelName() ); - $editContent = $handler->unserialize( $editText ); #FIXME: supply serialization fomrat from edit form! + $editContent = $handler->unserialize( $editText, $this->content_format ); #FIXME: supply serialization fomrat from edit form! $result = $handler->merge3( $baseContent, $editContent, $currentContent ); if ( $result ) { - $editText = ContentHandler::getContentText($result); #FIXME: supply serialization fomrat from edit form! + $editText = ContentHandler::getContentText($result, $this->content_format ); #FIXME: supply serialization fomrat from edit form! wfProfileOut( __METHOD__ ); return true; } else { @@ -1822,7 +1902,9 @@ class EditPage { // resolved between page source edits and custom ui edits using the // custom edit ui. $this->textbox2 = $this->textbox1; - $this->textbox1 = $this->getCurrentText(); + + $content = $this->getCurrentContent(); + $this->textbox1 = $content->serialize( $this->content_format ); $this->showTextbox1(); } else { @@ -1857,7 +1939,7 @@ class EditPage { $wgOut->addHTML( $this->editFormTextBottom . "\n\n" ); - if ( !$wgUser->getOption( 'previewontop' ) ) { + if ( !$wgUser->getOption( 'previewontop' ) ) { $this->displayPreviewArea( $previewOutput, false ); } @@ -2230,10 +2312,10 @@ HTML $this->showTextbox( $this->textbox2, 'wpTextbox2', array( 'tabindex' => 6, 'readonly' ) ); } - protected function showTextbox( $content, $name, $customAttribs = array() ) { + protected function showTextbox( $text, $name, $customAttribs = array() ) { global $wgOut, $wgUser; - $wikitext = $this->safeUnicodeOutput( $content ); + $wikitext = $this->safeUnicodeOutput( $text ); if ( strval($wikitext) !== '' ) { // Ensure there's a newline at the end, otherwise adding lines // is awkward. @@ -2309,23 +2391,38 @@ HTML * save and then make a comparison. */ function showDiff() { - global $wgUser, $wgContLang, $wgParser, $wgOut; + global $wgUser, $wgContLang, $wgOut; + + $oldContent = $this->getOriginalContent(); + + $textboxContent = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), + $this->content_model, $this->content_format ); - $oldtext = $this->getOriginalContent(); - $newtext = $this->mArticle->replaceSection( - $this->section, $this->textbox1, $this->summary, $this->edittime ); #FIXME: use Content::replaceSection + $newContent = $this->mArticle->replaceSectionContent( + $this->section, $textboxContent, + $this->summary, $this->edittime ); + # hanlde legacy text-based hook + $newtext_orig = $newContent->serialize( $this->content_format ); + $newtext = $newtext_orig; #clone wfRunHooks( 'EditPageGetDiffText', array( $this, &$newtext ) ); + if ( $newtext != $newtext_orig ) { + #if the hook changed the text, create a new Content object accordingly. + $newContent = ContentHandler::makeContent( $newtext, $this->getTitle(), $newContent->getModelName() ); + } + + wfRunHooks( 'EditPageGetDiffContent', array( $this, &$newContent ) ); #FIXME: document new hook + $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang ); - $newtext = $wgParser->preSaveTransform( $newtext, $this->mTitle, $wgUser, $popts ); + $newContent = $newContent->preSaveTransform( $this->mTitle, $wgUser, $popts ); - if ( $oldtext !== false || $newtext != '' ) { + if ( ( $oldContent && !$oldContent->isEmpty() ) || ( $newContent && !$newContent->isEmpty() ) ) { $oldtitle = wfMsgExt( 'currentrev', array( 'parseinline' ) ); $newtitle = wfMsgExt( 'yourtext', array( 'parseinline' ) ); $de = new DifferenceEngine( $this->mArticle->getContext() ); - $de->setText( $oldtext, $newtext ); + $de->setText( $oldContent, $newContent ); #FIXME: content-based diff! $difftext = $de->getDiff( $oldtitle, $newtitle ); $de->showDiffStyle(); } else { @@ -3043,9 +3140,11 @@ HTML $wgOut->addHTML( '' ); $wgOut->wrapWikiMsg( '

$1

', "yourdiff" ); - $de = new DifferenceEngine( $this->mArticle->getContext() ); - $de->setText( $this->getCurrentText(), $this->textbox2 ); - $de->showDiff( wfMsg( "storedversion" ), wfMsgExt( 'yourtext', 'parseinline' ) ); + $de = new DifferenceEngine( $this->mArticle->getContext() ); #FIXME: get from content handler! + + $de->setText( $this->getCurrentText(), $this->textbox2 ); #FIXME: make Content based + + $de->showDiff( wfMsg( "storedversion" ), wfMsgExt( 'yourtext', 'parseinline' ) ); $wgOut->wrapWikiMsg( '

$1

', "yourtext" ); $this->showTextbox2(); diff --git a/includes/WikiPage.php b/includes/WikiPage.php index 6bd5e4cf4e..f880d05a90 100644 --- a/includes/WikiPage.php +++ b/includes/WikiPage.php @@ -632,7 +632,8 @@ class WikiPage extends Page { */ public function insertRedirect() { // recurse through to only get the final target - $retval = Title::newFromRedirectRecurse( $this->getRawText() ); #FIXME: move this to Content object + $content = $this->getContent(); + $retval = $content ? $content->getUltimateRedirectTarget() : null; if ( !$retval ) { return null; } @@ -983,9 +984,9 @@ class WikiPage extends Page { public function updateRevisionOn( $dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null ) { wfProfileIn( __METHOD__ ); - $text = $revision->getText(); - $len = strlen( $text ); - $rt = Title::newFromRedirectRecurse( $text ); + $content = $revision->getContent(); + $len = $content->getSize(); + $rt = $content->getUltimateRedirectTarget(); $conditions = array( 'page_id' => $this->getId() ); @@ -1129,45 +1130,53 @@ class WikiPage extends Page { * @param $sectionTitle String: new section's subject, only if $section is 'new' * @param $edittime String: revision timestamp or null to use the current revision * @return Content new complete article content, or null if error - * @deprecated since 1.20: use Content::replaceSection () instead. + * @deprected since 1.20, use replaceSectionContent() instead */ - public function replaceSection( $section, $text, $sectionTitle = '', $edittime = null ) { #FIXME: create a Content-based version (take and return Content object) - wfProfileIn( __METHOD__ ); + public function replaceSection( $section, $text, $sectionTitle = '', $edittime = null ) { #FIXME: use replaceSectionContent() instead! + wfDeprecated( __METHOD__, '1.20' ); $sectionContent = ContentHandler::makeContent( $text, $this->getTitle() ); #XXX: could make section title, but that's not required. - if ( strval( $section ) == '' ) { - // Whole-page edit; let the whole text through + $newContent = $this->replaceSectionContent( $section, $sectionContent, $sectionTitle, $edittime ); + + return ContentHandler::getContentText( $newContent ); #XXX: unclear what will happen for non-wikitext! + } + + public function replaceSectionContent( $section, $sectionContent, $sectionTitle = '', $edittime = null ) { + wfProfileIn( __METHOD__ ); + + if ( strval( $section ) == '' ) { + // Whole-page edit; let the whole text through $newContent = $sectionContent; - } else { - // Bug 30711: always use current version when adding a new section - if ( is_null( $edittime ) || $section == 'new' ) { - $oldContent = $this->getContent(); - if ( ! $oldContent ) { - wfDebug( __METHOD__ . ": no page text\n" ); - wfProfileOut( __METHOD__ ); - return null; - } - } else { - $dbw = wfGetDB( DB_MASTER ); - $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime ); + } else { + // Bug 30711: always use current version when adding a new section + if ( is_null( $edittime ) || $section == 'new' ) { + $oldContent = $this->getContent(); + if ( ! $oldContent ) { + wfDebug( __METHOD__ . ": no page text\n" ); + wfProfileOut( __METHOD__ ); + return null; + } + } else { + $dbw = wfGetDB( DB_MASTER ); + $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime ); - if ( !$rev ) { - wfDebug( "WikiPage::replaceSection asked for bogus section (page: " . - $this->getId() . "; section: $section; edittime: $edittime)\n" ); - wfProfileOut( __METHOD__ ); - return null; - } + if ( !$rev ) { + wfDebug( "WikiPage::replaceSection asked for bogus section (page: " . + $this->getId() . "; section: $section; edittime: $edittime)\n" ); + wfProfileOut( __METHOD__ ); + return null; + } $oldContent = $rev->getContent(); - } + } $newContent = $oldContent->replaceSection( $section, $sectionContent, $sectionTitle ); - } + } - wfProfileOut( __METHOD__ ); - return ContentHandler::getContentText( $newContent ); #XXX: unclear what will happen for non-wikitext! - } + wfProfileOut( __METHOD__ ); + return $newContent; + } /** * Check flags and add EDIT_NEW or EDIT_UPDATE to them as needed. @@ -1653,13 +1662,13 @@ class WikiPage extends Page { wfProfileIn( __METHOD__ ); $options += array( 'changed' => true, 'created' => false, 'oldcountable' => null ); - $text = $revision->getText(); + $content = $revision->getContent(); # Parse the text # Be careful not to double-PST: $text is usually already PST-ed once if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) { wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" ); - $editInfo = $this->prepareTextForEdit( $text, $revision->getId(), $user ); + $editInfo = $this->prepareContentForEdit( $content, $revision->getId(), $user ); } else { wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" ); $editInfo = $this->mPreparedEdit; @@ -1717,7 +1726,7 @@ class WikiPage extends Page { } DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 1, $good, $total ) ); - DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $text ) ); + DeferredUpdates::addUpdate( new SearchUpdate( $id, $title, $content->getTextForSearchIndex() ) ); # If this is another user's talk page, update newtalk. # Don't do this if $options['changed'] = false (null-edits) nor if @@ -1743,7 +1752,10 @@ class WikiPage extends Page { } if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { - MessageCache::singleton()->replace( $shortTitle, $text ); + $msgtext = ContentHandler::getContentText( $content ); #XXX: could skip pseudo-messages like js/css here, based on content model. + if ( $msgtext === false || $msgtext === null ) $msgtext = ''; + + MessageCache::singleton()->replace( $shortTitle, $msgtext ); } if( $options['created'] ) { @@ -2371,7 +2383,7 @@ class WikiPage extends Page { } # Actually store the edit - $status = $this->doEdit( $target->getText(), $summary, $flags, $target->getId(), $guser ); + $status = $this->doEditContent( $target->getContent(), $summary, $flags, $target->getId(), $guser ); if ( !empty( $status->value['revision'] ) ) { $revId = $status->value['revision']->getId(); } else { diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php index 439e3204ec..ca5f9c9a7c 100644 --- a/includes/diff/DifferenceEngine.php +++ b/includes/diff/DifferenceEngine.php @@ -926,7 +926,7 @@ class DifferenceEngine extends ContextSource { /** * Use specified text instead of loading from the database */ - function setText( $oldText, $newText ) { + function setText( $oldText, $newText ) { #FIXME: deprecate, use Content objects instead! $this->mOldtext = $oldText; $this->mNewtext = $newText; $this->mTextLoaded = 2; -- 2.20.1