Use Title, not IContextSource; remove createArticle, etc.
[lhc/web/wiklou.git] / includes / EditPage.php
index 51fbaaf..5273c54 100644 (file)
@@ -36,11 +36,6 @@ class EditPage {
         */
        const AS_HOOK_ERROR                = 210;
 
-       /**
-        * Status: The filter function set in $wgFilterCallback returned true (= block it)
-        */
-       const AS_FILTERING                 = 211;
-
        /**
         * Status: A hook function returned an error
         */
@@ -149,6 +144,11 @@ class EditPage {
         */
        const AS_PARSE_ERROR                = 240;
 
+       /**
+        * HTML id and name for the beginning of the edit form.
+        */
+       const EDITFORM_ID                  = 'editform';
+
        /**
         * @var Article
         */
@@ -230,9 +230,9 @@ class EditPage {
                $this->mArticle = $article;
                $this->mTitle = $article->getTitle();
 
-               $this->content_model = $this->mTitle->getContentModelName();
+               $this->content_model = $this->mTitle->getContentModel();
 
-               $handler = ContentHandler::getForModelName( $this->content_model );
+               $handler = ContentHandler::getForModelID( $this->content_model );
                $this->content_format = $handler->getDefaultFormat(); #NOTE: should be overridden by format of actual revision
        }
 
@@ -580,9 +580,9 @@ class EditPage {
                                // Skip this if wpTextbox2 has input, it indicates that we came
                                // from a conflict page with raw page text, not a custom form
                                // modified by subclasses
-                               wfProfileIn( get_class($this)."::importContentFormData" );
-                               $textbox1 = $this->importContentFormData( $request ); #FIXME: what should this return??
-                               if ( isset($textbox1) )
+                               wfProfileIn( get_class( $this ) . "::importContentFormData" );
+                               $textbox1 = $this->importContentFormData( $request );
+                               if ( isset( $textbox1 ) )
                                        $this->textbox1 = $textbox1;
                                wfProfileOut( get_class( $this ) . "::importContentFormData" );
                        }
@@ -713,7 +713,7 @@ class EditPage {
                $this->nosummary = $request->getBool( 'nosummary' );
 
                $content_handler = ContentHandler::getForTitle( $this->mTitle );
-               $this->content_model = $request->getText( 'model', $content_handler->getModelName() ); #may be overridden by revision
+               $this->content_model = $request->getText( 'model', $content_handler->getModelID() ); #may be overridden by revision
                $this->content_format = $request->getText( 'format', $content_handler->getDefaultFormat() ); #may be overridden by revision
 
                #TODO: check if the desired model is allowed in this namespace, and if a transition from the page's current model to the new model is allowed
@@ -782,9 +782,11 @@ class EditPage {
         * @param $def_text string
         * @return mixed string on success, $def_text for invalid sections
         * @private
-        * @deprecated since 1.20
+        * @deprecated since 1.WD
         */
        function getContent( $def_text = false ) { #FIXME: deprecated, replace usage!
+               wfDeprecated( __METHOD__, '1.WD' );
+
                if ( $def_text !== null && $def_text !== false && $def_text !== '' ) {
                        $def_content = ContentHandler::makeContent( $def_text, $this->getTitle() );
                } else {
@@ -810,7 +812,7 @@ class EditPage {
                                # If this is a system message, get the default text.
                                $msg = $this->mTitle->getDefaultMessageText();
 
-                               $content = new WikitextContent($msg); //XXX: really hardcode wikitext here?
+                               $content = ContentHandler::makeContent( $msg, $this->mTitle );
                        }
                        if ( $content === false ) {
                                # If requested, preload some text.
@@ -861,7 +863,7 @@ class EditPage {
 
                                                        # If we just undid one rev, use an autosummary
                                                        $firstrev = $oldrev->getNext();
-                                                       if ( $firstrev->getId() == $undo ) {
+                                                       if ( $firstrev && $firstrev->getId() == $undo ) {
                                                                $undoSummary = wfMsgForContent( 'undo-summary', $undo, $undorev->getUserText() );
                                                                if ( $this->summary === '' ) {
                                                                        $this->summary = $undoSummary;
@@ -901,24 +903,24 @@ class EditPage {
         * section replaced in its context (using WikiPage::replaceSection())
         * to the original text of the edit.
         *
-        * When a missing revision is
-        * encountered, the result will be an empty Content object.
+        * This difers from Article::getContent() that when a missing revision is
+        * encountered the result will be an empty string and not the
+        * 'missing-article' message.
         *
         * @since 1.19
         * @return string
         */
-       private function getOriginalContent() { #FIXME: use Content! set content_model and content_format!
+       private function getOriginalContent() {
                if ( $this->section == 'new' ) {
                        return $this->getCurrentContent();
                }
                $revision = $this->mArticle->getRevisionFetched();
                if ( $revision === null ) {
-                       if ( !$this->content_model ) $this->content_model = $this->getTitle()->getContentModelName();
-                       $handler = ContentHandler::getForModelName( $this->content_model );
+                       if ( !$this->content_model ) $this->content_model = $this->getTitle()->getContentModel();
+                       $handler = ContentHandler::getForModelID( $this->content_model );
 
-                       return $handler->emptyContent();
+                       return $handler->makeEmptyContent();
                }
-
                $content = $revision->getContent();
                return $content;
        }
@@ -928,7 +930,7 @@ class EditPage {
         * WikiPage::getContent( Revision::RAW ) except that when the page doesn't exist an empty
         * content object is returned instead of null.
         *
-        * @since 1.20
+        * @since 1.WD
         * @return string
         */
        private function getCurrentContent() {
@@ -936,27 +938,28 @@ class EditPage {
                $content = $rev ? $rev->getContent( Revision::RAW ) : null;
 
                if ( $content  === false || $content === null ) {
-                       if ( !$this->content_model ) $this->content_model = $this->getTitle()->getContentModelName();
-                       $handler = ContentHandler::getForModelName( $this->content_model );
+                       if ( !$this->content_model ) $this->content_model = $this->getTitle()->getContentModel();
+                       $handler = ContentHandler::getForModelID( $this->content_model );
 
-                       return $handler->emptyContent();
+                       return $handler->makeEmptyContent();
                } else {
                        #FIXME: nasty side-effect!
-                       $this->content_model = $rev->getContentModelName();
+                       $this->content_model = $rev->getContentModel();
                        $this->content_format = $rev->getContentFormat();
 
                        return $content;
                }
        }
 
+
        /**
         * Use this method before edit() to preload some text into the edit box
         *
         * @param $text string
-        * @deprecated since 1.20
+        * @deprecated since 1.WD
         */
-       public function setPreloadedText( $text ) { #FIXME: deprecated, use setPreloadedContent()
-               wfDeprecated( __METHOD__, "1.20" );
+       public function setPreloadedText( $text ) {
+               wfDeprecated( __METHOD__, "1.WD" );
 
                $content = ContentHandler::makeContent( $text, $this->getTitle() );
 
@@ -967,8 +970,10 @@ class EditPage {
         * Use this method before edit() to preload some content into the edit box
         *
         * @param $content Content
+        *
+        * @since 1.WD
         */
-       public function setPreloadedContent( Content $content ) { #FIXME: use this!
+       public function setPreloadedContent( Content $content ) {
                $this->mPreloadedContent = $content;
        }
 
@@ -977,11 +982,13 @@ class EditPage {
         * an earlier setPreloadText() or by loading the given page.
         *
         * @param $preload String: representing the title to preload from.
+        *
         * @return String
-        * @deprecated since 1.20
+        *
+        * @deprecated since 1.WD, use getPreloadedContent() instead
         */
-       protected function getPreloadedText( $preload ) { #FIXME: B/C only, replace usage!
-               wfDeprecated( __METHOD__, "1.20" );
+       protected function getPreloadedText( $preload ) { #NOTE: B/C only, replace usage!
+               wfDeprecated( __METHOD__, "1.WD" );
 
                $content = $this->getPreloadedContent( $preload );
                $text = $content->serialize( $this->content_format ); #XXX: really use serialized form? use ContentHandler::getContentText() instead?!
@@ -989,7 +996,17 @@ class EditPage {
                return $text;
        }
 
-       protected function getPreloadedContent( $preload ) { #FIXME: use this!
+       /**
+        * 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 Content
+        *
+        * @since 1.WD
+        */
+       protected function getPreloadedContent( $preload ) { #@todo: use this!
                global $wgUser;
 
                if ( !empty( $this->mPreloadContent ) ) {
@@ -999,13 +1016,13 @@ class EditPage {
                $handler = ContentHandler::getForTitle( $this->getTitle() );
 
                if ( $preload === '' ) {
-                       return $handler->emptyContent();
+                       return $handler->makeEmptyContent();
                }
 
                $title = Title::newFromText( $preload );
                # Check for existence to avoid getting MediaWiki:Noarticletext
                if ( $title === null || !$title->exists() || !$title->userCan( 'read' ) ) {
-                       return $handler->emptyContent();
+                       return $handler->makeEmptyContent();
                }
 
                $page = WikiPage::factory( $title );
@@ -1013,7 +1030,7 @@ class EditPage {
                        $title = $page->getRedirectTarget();
                        # Same as before
                        if ( $title === null || !$title->exists() || !$title->userCan( 'read' ) ) {
-                               return $handler->emptyContent();
+                               return $handler->makeEmptyContent();
                        }
                        $page = WikiPage::factory( $title );
                }
@@ -1068,7 +1085,6 @@ class EditPage {
                                return true;
 
                        case self::AS_HOOK_ERROR:
-                       case self::AS_FILTERING:
                                return false;
 
                        case self::AS_PARSE_ERROR:
@@ -1145,8 +1161,7 @@ class EditPage {
         * AS_CONTENT_TOO_BIG and AS_BLOCKED_PAGE_FOR_USER. All that stuff needs to be cleaned up some time.
         */
        function internalAttemptSave( &$result, $bot = false ) {
-               global $wgFilterCallback, $wgUser, $wgRequest, $wgParser;
-               global $wgMaxArticleSize;
+               global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize;
 
                $status = Status::newGood();
 
@@ -1192,13 +1207,6 @@ class EditPage {
                        wfProfileOut( __METHOD__ );
                        return $status;
                }
-               if ( $wgFilterCallback && is_callable( $wgFilterCallback ) && $wgFilterCallback( $this->mTitle, $this->textbox1, $this->section, $this->hookError, $this->summary ) ) {
-                       # Error messages or other handling should be performed by the filter function
-                       $status->setResult( false, self::AS_FILTERING );
-                       wfProfileOut( __METHOD__ . '-checks' );
-                       wfProfileOut( __METHOD__ );
-                       return $status;
-               }
                if ( !wfRunHooks( 'EditFilter', array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) ) ) {
                        # Error messages etc. could be handled within the hook...
                        $status->fatal( 'hookaborted' );
@@ -1276,9 +1284,10 @@ class EditPage {
 
                wfProfileOut( __METHOD__ . '-checks' );
 
-               # If article is new, insert it.
-               $aid = $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE );
-               $new = ( $aid == 0 );
+               // Use SELECT FOR UPDATE here to avoid transaction collision in
+               // WikiPage::updateRevisionOn() and ending in the self::AS_END case.
+               $this->mArticle->loadPageData( 'forupdate' );
+               $new = !$this->mArticle->exists();
 
                try {
                        if ( $new ) {
@@ -1313,18 +1322,7 @@ class EditPage {
                                        return $status;
                                }
 
-                $content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format );
-
-                               # Handle the user preference to force summaries here. Check if it's not a redirect.
-                               if ( !$this->allowBlankSummary && !$content->isRedirect() ) {
-                                       if ( md5( $this->summary ) == $this->autoSumm ) {
-                                               $this->missingSummary = true;
-                                               $status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh
-                                               $status->value = self::AS_SUMMARY_NEEDED;
-                                               wfProfileOut( __METHOD__ );
-                                               return $status;
-                                       }
-                               }
+                               $content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format );
 
                                $result['sectionanchor'] = '';
                                if ( $this->section == 'new' ) {
@@ -1357,7 +1355,7 @@ class EditPage {
 
                                $status->value = self::AS_SUCCESS_NEW_ARTICLE;
 
-                       } else {
+                       } else { # not $new
 
                                # Article exists. Check for edit conflict.
 
@@ -1378,28 +1376,31 @@ class EditPage {
                                                } else {
                                                        // New comment; suppress conflict.
                                                        $this->isConflict = false;
-                                                       wfDebug( __METHOD__ .": conflict suppressed; new section\n" );
+                                                       wfDebug( __METHOD__ . ": conflict suppressed; new section\n" );
                                                }
                                        } elseif ( $this->section == '' && $this->userWasLastToEdit( $wgUser->getId(), $this->edittime ) ) {
                                                # Suppress edit conflict with self, except for section edits where merging is required.
                                                wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" );
                                                $this->isConflict = false;
-
-                                               wfDebug( __METHOD__ . ": conflict suppressed; new section\n" );
-
                                        }
                                }
 
+                               // If sectiontitle is set, use it, otherwise use the summary as the section title (for
+                               // backwards compatibility with old forms/bots).
+                               if ( $this->sectiontitle !== '' ) {
+                                       $sectionTitle = $this->sectiontitle;
+                               } else {
+                                       $sectionTitle = $this->summary;
+                               }
+
                                $textbox_content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format );
-                               $content = false;
+                               $content = null;
 
                                if ( $this->isConflict ) {
                                        wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '{$timestamp}')\n" );
-
                                        $content = $this->mArticle->replaceSectionContent( $this->section, $textbox_content, $sectionTitle, $this->edittime );
                                } else {
                                        wfDebug( __METHOD__ . ": getting section '$this->section'\n" );
-
                                        $content = $this->mArticle->replaceSectionContent( $this->section, $textbox_content, $sectionTitle );
                                }
 
@@ -1411,8 +1412,8 @@ class EditPage {
                                        # Attempt merge
                                        if ( $this->mergeChangesIntoContent( $textbox_content ) ) {
                                                // Successful merge! Maybe we should tell the user the good news?
-                                               $content = $textbox_content;
                                                $this->isConflict = false;
+                                               $content = $textbox_content;
                                                wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" );
                                        } else {
                                                $this->section = '';
@@ -1429,7 +1430,7 @@ class EditPage {
 
                                // Run post-section-merge edit filter
                                if ( !wfRunHooks( 'EditFilterMerged', array( $this, $content->serialize( $this->content_format ), &$this->hookError, $this->summary ) )
-                                       || !wfRunHooks( 'EditFilterMergedContent', array( $this, $content, &$this->hookError, $this->summary ) ) ) { #FIXME: document new hook
+                                               || !wfRunHooks( 'EditFilterMergedContent', array( $this, $content, &$this->hookError, $this->summary ) ) ) {
                                        # Error messages etc. could be handled within the hook...
                                        $status->fatal( 'hookaborted' );
                                        $status->value = self::AS_HOOK_ERROR;
@@ -1443,6 +1444,8 @@ class EditPage {
                                        return $status;
                                }
 
+                               $content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format );
+
                                # Handle the user preference to force summaries here, but not for null edits
                                if ( $this->section != 'new' && !$this->allowBlankSummary
                                        && !$content->equals( $this->getOriginalContent() )
@@ -1514,14 +1517,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 = $content->serialize( $this->content_format );
+                                       $this->textbox1 = $content->serialize( $this->content_format );
                                $this->section = '';
 
                                $status->value = self::AS_SUCCESS_UPDATE;
                        }
 
                        // Check for length errors again now that the section is merged in
-                       $this->kblength = (int)( strlen( $content->serialize( $this->content_format ) ) / 1024 );
+                               $this->kblength = (int)( strlen( $content->serialize( $this->content_format ) ) / 1024 );
                        if ( $this->kblength > $wgMaxArticleSize ) {
                                $this->tooBig = true;
                                $status->setResult( false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
@@ -1534,10 +1537,10 @@ class EditPage {
                                ( ( $this->minoredit && !$this->isNew ) ? EDIT_MINOR : 0 ) |
                                ( $bot ? EDIT_FORCE_BOT : 0 );
 
-                       $doEditStatus = $this->mArticle->doEditContent( $content, $this->summary, $flags, false, null, $this->content_format );
+                               $doEditStatus = $this->mArticle->doEditContent( $content, $this->summary, $flags, false, null, $this->content_format );
 
                        if ( $doEditStatus->isOK() ) {
-                               $result['redirect'] = $content->isRedirect();
+                                       $result['redirect'] = $content->isRedirect();
                                $this->commitWatch();
                                wfProfileOut( __METHOD__ );
                                return $status;
@@ -1608,10 +1611,10 @@ class EditPage {
         * @parma $editText string
         *
         * @return bool
-        * @deprecated since 1.20
+        * @deprecated since 1.WD, use mergeChangesIntoContent() instead
         */
        function mergeChangesInto( &$editText ){
-               wfDebug( __METHOD__, "1.20" );
+               wfDebug( __METHOD__, "1.WD" );
 
                $editContent = ContentHandler::makeContent( $editText, $this->getTitle(), $this->content_model, $this->content_format );
 
@@ -1632,7 +1635,7 @@ class EditPage {
         * @parma $editText string
         *
         * @return bool
-        * @since since 1.20
+        * @since since 1.WD
         */
        private function mergeChangesIntoContent( &$editContent ){
                wfProfileIn( __METHOD__ );
@@ -1655,7 +1658,7 @@ class EditPage {
                }
                $currentContent = $currentRevision->getContent();
 
-               $handler = ContentHandler::getForModelName( $baseContent->getModelName() );
+               $handler = ContentHandler::getForModelID( $baseContent->getModel() );
 
                $result = $handler->merge3( $baseContent, $editContent, $currentContent );
 
@@ -1908,7 +1911,7 @@ class EditPage {
                }
 
                #FIXME: add EditForm plugin interface and use it here! #FIXME: search for textarea1 and textares2, and allow EditForm to override all uses.
-               $wgOut->addHTML( Html::openElement( 'form', array( 'id' => 'editform', 'name' => 'editform',
+               $wgOut->addHTML( Html::openElement( 'form', array( 'id' => self::EDITFORM_ID, 'name' => self::EDITFORM_ID,
                        'method' => 'post', 'action' => $this->getActionURL( $this->getContextTitle() ),
                        'enctype' => 'multipart/form-data' ) ) );
 
@@ -2057,7 +2060,7 @@ class EditPage {
 
                # Optional notices on a per-namespace and per-page basis
                $editnotice_ns = 'editnotice-' . $this->mTitle->getNamespace();
-               $editnotice_ns_message = wfMessage( $editnotice_ns )->inContentLanguage();
+               $editnotice_ns_message = wfMessage( $editnotice_ns );
                if ( $editnotice_ns_message->exists() ) {
                        $wgOut->addWikiText( $editnotice_ns_message->plain() );
                }
@@ -2066,7 +2069,7 @@ class EditPage {
                        $editnotice_base = $editnotice_ns;
                        while ( count( $parts ) > 0 ) {
                                $editnotice_base .= '-' . array_shift( $parts );
-                               $editnotice_base_msg = wfMessage( $editnotice_base )->inContentLanguage();
+                               $editnotice_base_msg = wfMessage( $editnotice_base );
                                if ( $editnotice_base_msg->exists() ) {
                                        $wgOut->addWikiText( $editnotice_base_msg->plain() );
                                }
@@ -2074,7 +2077,7 @@ class EditPage {
                } else {
                        # Even if there are no subpages in namespace, we still don't want / in MW ns.
                        $editnoticeText = $editnotice_ns . '-' . str_replace( '/', '-', $this->mTitle->getDBkey() );
-                       $editnoticeMsg = wfMessage( $editnoticeText )->inContentLanguage();
+                       $editnoticeMsg = wfMessage( $editnoticeText );
                        if ( $editnoticeMsg->exists() ) {
                                $wgOut->addWikiText( $editnoticeMsg->plain() );
                        }
@@ -2477,28 +2480,28 @@ HTML
         * save and then make a comparison.
         */
        function showDiff() {
-               global $wgUser, $wgContLang, $wgOut;
-
-               $oldContent = $this->getOriginalContent();
-
-               $textboxContent = ContentHandler::makeContent( $this->textbox1, $this->getTitle(),
-                                                                                                               $this->content_model, $this->content_format ); #XXX: handle parse errors ?
+               global $wgUser, $wgContLang, $wgParser, $wgOut;
 
-               $newContent = $this->mArticle->replaceSectionContent(
-                                                                                       $this->section, $textboxContent,
-                                                                                       $this->summary, $this->edittime );
                $oldtitlemsg = 'currentrev';
                # if message does not exist, show diff against the preloaded default
                if( $this->mTitle->getNamespace() == NS_MEDIAWIKI && !$this->mTitle->exists() ) {
                        $oldtext = $this->mTitle->getDefaultMessageText();
                        if( $oldtext !== false ) {
                                $oldtitlemsg = 'defaultmessagetext';
+                               $oldContent = ContentHandler::makeContent( $oldtext, $this->mTitle );
+                       } else {
+                               $oldContent = null;
                        }
                } else {
-                       $oldtext = $this->mArticle->getRawText();
+                       $oldContent = $this->getOriginalContent();
                }
-               $newtext = $this->mArticle->replaceSection(
-                       $this->section, $this->textbox1, $this->summary, $this->edittime );
+
+               $textboxContent = ContentHandler::makeContent( $this->textbox1, $this->getTitle(),
+                                                                                                               $this->content_model, $this->content_format ); #XXX: handle parse errors ?
+
+               $newContent = $this->mArticle->replaceSectionContent(
+                                                                                                       $this->section, $textboxContent,
+                                                                                                       $this->summary, $this->edittime );
 
                # hanlde legacy text-based hook
                $newtext_orig = $newContent->serialize( $this->content_format );
@@ -2506,11 +2509,11 @@ HTML
                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() ); #XXX: handle parse errors ?
+                                               #if the hook changed the text, create a new Content object accordingly.
+                                               $newContent = ContentHandler::makeContent( $newtext, $this->getTitle(), $newContent->getModel() ); #XXX: handle parse errors ?
                }
 
-               wfRunHooks( 'EditPageGetDiffContent', array( $this, &$newContent ) ); #FIXME: document new hook
+               wfRunHooks( 'EditPageGetDiffContent', array( $this, &$newContent ) );
 
                $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
                $newContent = $newContent->preSaveTransform( $this->mTitle, $wgUser, $popts );
@@ -2519,7 +2522,7 @@ HTML
                        $oldtitle = wfMsgExt( $oldtitlemsg, array( 'parseinline' ) );
                        $newtitle = wfMsgExt( 'yourtext', array( 'parseinline' ) );
 
-                       $de = $oldContent->getContentHandler()->getDifferenceEngine( $this->mArticle->getContext() );
+                       $de = $oldContent->getContentHandler()->createDifferenceEngine( $this->mArticle->getContext() );
                        $de->setContent( $oldContent, $newContent );
 
                        $difftext = $de->getDiff( $oldtitle, $newtitle );
@@ -2614,8 +2617,8 @@ HTML
                        $content1 = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format ); #XXX: handle parse errors?
                        $content2 = ContentHandler::makeContent( $this->textbox2, $this->getTitle(), $this->content_model, $this->content_format ); #XXX: handle parse errors?
 
-                       $handler = ContentHandler::getForModelName( $this->content_model );
-                       $de = $handler->getDifferenceEngine( $this->mArticle->getContext() );
+                       $handler = ContentHandler::getForModelID( $this->content_model );
+                       $de = $handler->createDifferenceEngine( $this->mArticle->getContext() );
                        $de->setContent( $content2, $content1 );
                        $de->showDiff( wfMsgExt( 'yourtext', 'parseinline' ), wfMsg( 'storedversion' ) );
 
@@ -2717,7 +2720,7 @@ HTML
         * @return string
         */
        function getPreviewText() {
-               global $wgOut, $wgUser, $wgParser, $wgRawHtml;
+               global $wgOut, $wgUser, $wgParser, $wgRawHtml, $wgLang;
 
                wfProfileIn( __METHOD__ );
 
@@ -2736,171 +2739,101 @@ HTML
                        return $parsedNote;
                }
 
-        try {
-            $content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format );
-
-            if ( $this->mTriedSave && !$this->mTokenOk ) {
-                if ( $this->mTokenOkExceptSuffix ) {
-                    $note = wfMsg( 'token_suffix_mismatch' );
-                } else {
-                    $note = wfMsg( 'session_fail_preview' );
-                }
-            } elseif ( $this->incompleteForm ) {
-                $note = wfMsg( 'edit_form_incomplete' );
-            } elseif ( $this->isCssJsSubpage || $this->mTitle->isCssOrJsPage() ) {
-                # if this is a CSS or JS page used in the UI, show a special notice
-                # XXX: stupid php bug won't let us use $this->getContextTitle()->isCssJsSubpage() here -- This note has been there since r3530. Sure the bug was fixed time ago?
-
-                if( $this->mTitle->isCssJsSubpage() ) {
-                    $level = 'user';
-                } elseif( $this->mTitle->isCssOrJsPage() ) {
-                    $level = 'site';
-                } else {
-                    $level = false;
-                }
-
-                if ( $content->getModelName() == CONTENT_MODEL_CSS ) {
-                    $format = 'css';
-                } elseif ( $content->getModelName() == CONTENT_MODEL_JAVASCRIPT ) {
-                    $format = 'js';
-                } 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'>" . wfMsg( "{$level}{$format}preview" ) . "</div>";
-                } else {
-                    $note = wfMsg( 'previewnote' );
-                }
-            } else {
-                $note = wfMsg( 'previewnote' );
-            }
-
-            $parserOptions = ParserOptions::newFromUser( $wgUser );
-            $parserOptions->setEditSection( false );
-            $parserOptions->setTidy( true );
-            $parserOptions->setIsPreview( true );
-            $parserOptions->setIsSectionPreview( !is_null($this->section) && $this->section !== '' );
-
-            $rt = $content->getRedirectChain();
-
-            if ( $rt ) {
-                $previewHTML = $this->mArticle->viewRedirect( $rt, false );
-            } else {
-
-                # 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 );
-                }
-
-                $toparse_orig = $content->serialize( $this->content_format );
-                $toparse = $toparse_orig;
-                wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) );
-
-                if ( $toparse !== $toparse_orig ) {
-                    #hook changed the text, create new Content object
-                    $content = ContentHandler::makeContent( $toparse, $this->getTitle(), $this->content_model, $this->content_format );
-                }
-
-                wfRunHooks( 'EditPageGetPreviewContent', array( $this, &$content ) ); # FIXME: document new hook
-
-                $parserOptions->enableLimitReport();
-
-                #XXX: For CSS/JS pages, we should have called the ShowRawCssJs hook here. But it's now deprecated, so never mind
-                $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
-                $parserOutput = $content->getParserOutput( $this->mTitle, null, $parserOptions );
-
-                $previewHTML = $parserOutput->getText();
-                $this->mParserOutput = $parserOutput;
-                $wgOut->addParserOutputNoText( $parserOutput );
-
-                if ( count( $parserOutput->getWarnings() ) ) {
-                    $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
-                }
-            }
-        } catch (MWContentSerializationException $ex) {
-            $note .= "\n\n" . wfMsg('content-failed-to-parse', $this->content_model, $this->content_format, $ex->getMessage() );
-            $previewHTML = '';
-        }
-               if ( $this->mTriedSave && !$this->mTokenOk ) {
-                       if ( $this->mTokenOkExceptSuffix ) {
-                               $note = wfMsg( 'token_suffix_mismatch' );
-                       } else {
-                               $note = wfMsg( 'session_fail_preview' );
-                       }
-               } elseif ( $this->incompleteForm ) {
-                       $note = wfMsg( 'edit_form_incomplete' );
-               } else {
-                       $note = wfMsg( 'previewnote' );
-               }
+               $note = '';
 
-               $parserOptions = ParserOptions::newFromUser( $wgUser );
-               $parserOptions->setEditSection( false );
-               $parserOptions->setTidy( true );
-               $parserOptions->setIsPreview( true );
-               $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
-
-               # don't parse non-wikitext pages, show message about preview
-               # XXX: stupid php bug won't let us use $this->getContextTitle()->isCssJsSubpage() here -- This note has been there since r3530. Sure the bug was fixed time ago?
-
-               if ( $this->isCssJsSubpage || !$this->mTitle->isWikitextPage() ) {
-                       if ( $this->mTitle->isCssJsSubpage() ) {
-                               $level = 'user';
-                       } elseif ( $this->mTitle->isCssOrJsPage() ) {
-                               $level = 'site';
-                       } else {
-                               $level = false;
-                       }
+               try {
+                       $content = ContentHandler::makeContent( $this->textbox1, $this->getTitle(), $this->content_model, $this->content_format );
 
-                       # Used messages to make sure grep find them:
-                       # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
-                       if ( $level ) {
-                               if ( preg_match( "/\\.css$/", $this->mTitle->getText() ) ) {
-                                       $previewtext = "<div id='mw-{$level}csspreview'>\n" . wfMsg( "{$level}csspreview" ) . "\n</div>";
-                                       $class = "mw-code mw-css";
-                               } elseif ( preg_match( "/\\.js$/", $this->mTitle->getText() ) ) {
-                                       $previewtext = "<div id='mw-{$level}jspreview'>\n" . wfMsg( "{$level}jspreview" ) . "\n</div>";
-                                       $class = "mw-code mw-js";
+                       if ( $this->mTriedSave && !$this->mTokenOk ) {
+                               if ( $this->mTokenOkExceptSuffix ) {
+                                       $note = wfMsg( 'token_suffix_mismatch' );
                                } else {
-                                       throw new MWException( 'A CSS/JS (sub)page but which is not css nor js!' );
+                                       $note = wfMsg( 'session_fail_preview' );
+                               }
+                       } elseif ( $this->incompleteForm ) {
+                               $note = wfMsg( 'edit_form_incomplete' );
+                       } else {
+                               $note = wfMsg( 'previewnote' ) .
+                                       ' [[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . wfMsg( 'continue-editing' ) . ']]';
+                       }
+
+                       $parserOptions = ParserOptions::newFromUser( $wgUser );
+                       $parserOptions->setEditSection( false );
+                       $parserOptions->setTidy( true );
+                       $parserOptions->setIsPreview( true );
+                       $parserOptions->setIsSectionPreview( !is_null($this->section) && $this->section !== '' );
+
+                       if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
+                               # don't parse non-wikitext pages, show message about preview
+                               if( $this->mTitle->isCssJsSubpage() ) {
+                                       $level = 'user';
+                               } elseif( $this->mTitle->isCssOrJsPage() ) {
+                                       $level = 'site';
+                               } else {
+                                       $level = false;
                                }
-                       }
 
-                       $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions );
-                       $previewHTML = $parserOutput->mText;
-                       $previewHTML .= "<pre class=\"$class\" dir=\"ltr\">\n" . htmlspecialchars( $this->textbox1 ) . "\n</pre>\n";
-               } else {
-                       $toparse = $this->textbox1;
+                               if ( $content->getModel() == CONTENT_MODEL_CSS ) {
+                                       $format = 'css';
+                               } elseif ( $content->getModel() == CONTENT_MODEL_JAVASCRIPT ) {
+                                       $format = 'js';
+                               } else {
+                                       $format = false;
+                               }
 
-                       # If we're adding a comment, we need to show the
-                       # summary as the headline
-                       if ( $this->section == "new" && $this->summary != "" ) {
-                               $toparse = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->summary ) . "\n\n" . $toparse;
+                               # Used messages to make sure grep find them:
+                               # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
+                               if( $level && $format ) {
+                                       $note = "<div id='mw-{$level}{$format}preview'>" . wfMsg( "{$level}{$format}preview" ) . "</div>";
+                               } else {
+                                       $note = wfMsg( 'previewnote' );
+                               }
+                       } else {
+                               $note = wfMsg( 'previewnote' );
                        }
 
-                       wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) );
-
-                       $parserOptions->enableLimitReport();
+                       $rt = $content->getRedirectChain();
 
-                       $toparse = $wgParser->preSaveTransform( $toparse, $this->mTitle, $wgUser, $parserOptions );
-                       $parserOutput = $wgParser->parse( $toparse, $this->mTitle, $parserOptions );
-
-                       $rt = Title::newFromRedirectArray( $this->textbox1 );
                        if ( $rt ) {
                                $previewHTML = $this->mArticle->viewRedirect( $rt, false );
                        } else {
-                               $previewHTML = $parserOutput->getText();
-                       }
 
-                       $this->mParserOutput = $parserOutput;
-                       $wgOut->addParserOutputNoText( $parserOutput );
+                               # 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 );
+                               }
+
+                               $toparse_orig = $content->serialize( $this->content_format );
+                               $toparse = $toparse_orig;
+                               wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) );
+
+                               if ( $toparse !== $toparse_orig ) {
+                                       #hook changed the text, create new Content object
+                                       $content = ContentHandler::makeContent( $toparse, $this->getTitle(), $this->content_model, $this->content_format );
+                               }
+
+                               wfRunHooks( 'EditPageGetPreviewContent', array( $this, &$content ) );
+
+                               $parserOptions->enableLimitReport();
 
-                       if ( count( $parserOutput->getWarnings() ) ) {
-                               $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
+                               #XXX: For CSS/JS pages, we should have called the ShowRawCssJs hook here. But it's now deprecated, so never mind
+                               $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
+
+                               // TODO: might be a saner way to get a meaningfull context here?
+                               $parserOutput = $content->getParserOutput( $this->getArticle()->getTitle(), null, $parserOptions );
+
+                               $previewHTML = $parserOutput->getText();
+                               $this->mParserOutput = $parserOutput;
+                               $wgOut->addParserOutputNoText( $parserOutput );
+
+                               if ( count( $parserOutput->getWarnings() ) ) {
+                                       $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
+                               }
                        }
+               } catch (MWContentSerializationException $ex) {
+                       $note .= "\n\n" . wfMsg('content-failed-to-parse', $this->content_model, $this->content_format, $ex->getMessage() );
+                       $previewHTML = '';
                }
 
                if ( $this->isConflict ) {
@@ -3334,14 +3267,7 @@ HTML
                $wgOut->addHTML( '</div>' );
 
                $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
-
-               $handler = ContentHandler::getForTitle( $this->getTitle() );
-               $de = $handler->getDifferenceEngine( $this->mArticle->getContext() );
-
-               $content2 = ContentHandler::makeContent( $this->textbox2, $this->getTitle(), $this->content_model, $this->content_format ); #XXX: handle parse errors?
-               $de->setContent( $this->getCurrentContent(), $content2 );
-
-               $de->showDiff( wfMsg( "storedversion" ), wfMsgExt( 'yourtext', 'parseinline' ) );
+               $this->showDiff();
 
                $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourtext" );
                $this->showTextbox2();
@@ -3496,7 +3422,7 @@ HTML
                                // but should help keep the breakage down if the editor
                                // breaks one of the entities whilst editing.
                                if ( ( substr( $invalue, $i, 1 ) == ";" ) and ( strlen( $hexstring ) <= 6 ) ) {
-                                       $codepoint = hexdec($hexstring);
+                                       $codepoint = hexdec( $hexstring );
                                        $result .= codepointToUtf8( $codepoint );
                                } else {
                                        $result .= "&#x" . $hexstring . substr( $invalue, $i, 1 );
@@ -3508,5 +3434,4 @@ HTML
                // reverse the transform that we made for reversability reasons.
                return strtr( $result, array( "&#x0" => "&#x" ) );
        }
-
 }