reworking EditPage to use the content object - work in horrible progress
authorDaniel Kinzler <daniel.kinzler@wikimedia.de>
Thu, 22 Mar 2012 17:43:17 +0000 (17:43 +0000)
committerDaniel Kinzler <daniel.kinzler@wikimedia.de>
Wed, 4 Apr 2012 17:57:30 +0000 (19:57 +0200)
includes/Content.php
includes/ContentHandler.php
includes/EditPage.php
includes/WikiPage.php
includes/diff/DifferenceEngine.php

index 1ef81b0..d7e43bb 100644 (file)
@@ -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;
 
index c9b27d3..567c8d6 100644 (file)
@@ -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;
     }
index 690b0e5..6f6a26f 100644 (file)
@@ -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 ) . '</div>', 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</form>\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( '</div>' );
 
                $wgOut->wrapWikiMsg( '<h2>$1</h2>', "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( '<h2>$1</h2>', "yourtext" );
                $this->showTextbox2();
index 6bd5e4c..f880d05 100644 (file)
@@ -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 {
index 439e320..ca5f9c9 100644 (file)
@@ -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;