catch exceptions while generating auto delete reason
[lhc/web/wiklou.git] / includes / Article.php
index d33358a..5470dbc 100644 (file)
@@ -37,7 +37,8 @@ class Article extends Page {
         */
        public $mParserOptions;
 
-       var $mContent;                    // !<
+       var $mContent;                    // !< #BC cruft
+    var $mContentObject;              // !<
        var $mContentLoaded = false;      // !<
        var $mOldId;                      // !<
 
@@ -112,13 +113,14 @@ class Article extends Page {
                if ( !$page ) {
                        switch( $title->getNamespace() ) {
                                case NS_FILE:
-                                       $page = new ImagePage( $title );
+                                       $page = new ImagePage( $title ); #FIXME: teach ImagePage to use ContentHandler
                                        break;
                                case NS_CATEGORY:
-                                       $page = new CategoryPage( $title );
+                                       $page = new CategoryPage( $title ); #FIXME: teach ImagePage to use ContentHandler
                                        break;
                                default:
-                                       $page = new Article( $title );
+                    $handler = ContentHandler::getForTitle( $title );
+                                       $page = $handler->createArticle( $title );
                        }
                }
                $page->setContext( $context );
@@ -150,12 +152,23 @@ class Article extends Page {
 
        /**
         * Get the title object of the article
+        *
         * @return Title object of this page
         */
        public function getTitle() {
                return $this->mPage->getTitle();
        }
 
+       /**
+        * Get the WikiPage object of this instance
+        *
+        * @since 1.19
+        * @return WikiPage
+        */
+       public function getPage() {
+               return $this->mPage;
+       }
+
        /**
         * Clear the object
         */
@@ -172,14 +185,31 @@ class Article extends Page {
        /**
         * Note that getContent/loadContent do not follow redirects anymore.
         * If you need to fetch redirectable content easily, try
-        * the shortcut in Article::followRedirect()
+        * the shortcut in WikiPage::getRedirectTarget()
         *
         * This function has side effects! Do not use this function if you
         * only want the real revision text if any.
         *
         * @return Return the text of this revision
+     * @deprecated in 1.20; use getContentObject() instead
         */
        public function getContent() {
+        wfDeprecated( __METHOD__, '1.20' );
+        $content = $this->getContentObject();
+        return ContentHandler::getContentText( $content );
+    }
+
+    /**
+     * Note that getContent/loadContent do not follow redirects anymore.
+     * If you need to fetch redirectable content easily, try
+     * the shortcut in WikiPage::getRedirectTarget()
+     *
+     * This function has side effects! Do not use this function if you
+     * only want the real revision text if any.
+     *
+     * @return Return the content of this revision
+     */
+   public function getContentObject() {
                global $wgUser;
 
                wfProfileIn( __METHOD__ );
@@ -197,12 +227,12 @@ class Article extends Page {
                        }
                        wfProfileOut( __METHOD__ );
 
-                       return $text;
+                       return ContentHandler::makeContent( $text, $this->getTitle() );
                } else {
-                       $this->fetchContent();
+                       $this->fetchContentObject();
                        wfProfileOut( __METHOD__ );
 
-                       return $this->mContent;
+                       return $this->mContentObject;
                }
        }
 
@@ -251,6 +281,7 @@ class Article extends Page {
                        $nextid = $this->getTitle()->getNextRevisionID( $oldid );
                        if ( $nextid ) {
                                $oldid = $nextid;
+                               $this->mRevision = null;
                        } else {
                                $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
                        }
@@ -258,6 +289,7 @@ class Article extends Page {
                        $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
                        if ( $previd ) {
                                $oldid = $previd;
+                               $this->mRevision = null;
                        }
                }
 
@@ -279,60 +311,89 @@ class Article extends Page {
         * Does *NOT* follow redirects.
         *
         * @return mixed string containing article contents, or false if null
+     * @deprecated in 1.20, use getContentObject() instead
         */
-       function fetchContent() {
-               if ( $this->mContentLoaded ) {
+       protected function fetchContent() { #BC cruft!
+        wfDeprecated( __METHOD__, '1.20' );
+
+               if ( $this->mContentLoaded && $this->mContent ) {
                        return $this->mContent;
                }
 
                wfProfileIn( __METHOD__ );
 
-               $this->mContentLoaded = true;
+        $content = $this->fetchContentObject();
 
-               $oldid = $this->getOldID();
+        $this->mContent = ContentHandler::getContentText( $content ); #FIXME: get rid of mContent everywhere!
+               wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) ); #BC cruft!
 
-               # Pre-fill content with error message so that if something
-               # fails we'll have something telling us what we intended.
-               $t = $this->getTitle()->getPrefixedText();
-               $d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
-               $this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ;
+               wfProfileOut( __METHOD__ );
 
-               if ( $oldid ) {
-                       # $this->mRevision might already be fetched by getOldIDFromRequest()
-                       if ( !$this->mRevision ) {
-                               $this->mRevision = Revision::newFromId( $oldid );
-                               if ( !$this->mRevision ) {
-                                       wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
-                                       wfProfileOut( __METHOD__ );
-                                       return false;
-                               }
-                       }
-               } else {
-                       if ( !$this->mPage->getLatest() ) {
-                               wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
-                               wfProfileOut( __METHOD__ );
-                               return false;
-                       }
+               return $this->mContent;
+       }
 
-                       $this->mRevision = $this->mPage->getRevision();
-                       if ( !$this->mRevision ) {
-                               wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
-                               wfProfileOut( __METHOD__ );
-                               return false;
-                       }
-               }
 
-               // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
-               // We should instead work with the Revision object when we need it...
-               $this->mContent = $this->mRevision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed
-               $this->mRevIdFetched = $this->mRevision->getId();
+    /**
+     * Get text content object
+     * Does *NOT* follow redirects.
+     *
+     * @return Content object containing article contents, or null
+     */
+    protected function fetchContentObject() {
+        if ( $this->mContentLoaded ) {
+            return $this->mContentObject;
+        }
 
-               wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
+        wfProfileIn( __METHOD__ );
 
-               wfProfileOut( __METHOD__ );
+        $this->mContentLoaded = true;
+        $this->mContent = null;
 
-               return $this->mContent;
-       }
+        $oldid = $this->getOldID();
+
+        # Pre-fill content with error message so that if something
+        # fails we'll have something telling us what we intended.
+        $t = $this->getTitle()->getPrefixedText();
+        $d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
+        $this->mContentObject = new MessageContent( 'missing-article', array($t, $d), array() ) ;
+
+        if ( $oldid ) {
+            # $this->mRevision might already be fetched by getOldIDFromRequest()
+            if ( !$this->mRevision ) {
+                $this->mRevision = Revision::newFromId( $oldid );
+                if ( !$this->mRevision ) {
+                    wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
+                    wfProfileOut( __METHOD__ );
+                    return false;
+                }
+            }
+        } else {
+            if ( !$this->mPage->getLatest() ) {
+                wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
+                wfProfileOut( __METHOD__ );
+                return false;
+            }
+
+            $this->mRevision = $this->mPage->getRevision();
+
+            if ( !$this->mRevision ) {
+                wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
+                wfProfileOut( __METHOD__ );
+                return false;
+            }
+        }
+
+        // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
+        // We should instead work with the Revision object when we need it...
+        $this->mContentObject = $this->mRevision->getContent( Revision::FOR_THIS_USER ); // Loads if user is allowed
+        $this->mRevIdFetched = $this->mRevision->getId();
+
+        wfRunHooks( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) ); #FIXME: register new hook
+
+        wfProfileOut( __METHOD__ );
+
+        return $this->mContentObject;
+    }
 
        /**
         * No-op
@@ -364,7 +425,7 @@ class Article extends Page {
         * @return Revision|null
         */
        public function getRevisionFetched() {
-               $this->fetchContent();
+               $this->fetchContentObject();
 
                return $this->mRevision;
        }
@@ -523,7 +584,7 @@ class Article extends Page {
                                        break;
                                case 3:
                                        # This will set $this->mRevision if needed
-                                       $this->fetchContent();
+                                       $this->fetchContentObject();
 
                                        # Are we looking at an old revision
                                        if ( $oldid && $this->mRevision ) {
@@ -544,21 +605,25 @@ class Article extends Page {
 
                                        # Pages containing custom CSS or JavaScript get special treatment
                                        if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
+                        #FIXME: use ContentHandler for specialized actions insetad.
                                                wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
                                                $this->showCssOrJsPage();
                                                $outputDone = true;
-                                       } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) {
+                    } elseif( !wfRunHooks( 'ArticleContentViewCustom', array( $this->fetchContentObject(), $this->getTitle(), $wgOut ) ) ) { #FIXME: document new hook!
+                        # Allow extensions do their own custom view for certain pages
+                        $outputDone = true;
+                                       } elseif( Hooks::isRegistered( 'ArticleViewCustom' ) && !wfRunHooks( 'ArticleViewCustom', array( $this->fetchContent(), $this->getTitle(), $wgOut ) ) ) { #FIXME: fetchContent() is deprecated! #FIXME: deprecate hook!
                                                # Allow extensions do their own custom view for certain pages
                                                $outputDone = true;
                                        } else {
-                                               $text = $this->getContent();
-                                               $rt = Title::newFromRedirectArray( $text );
+                                               $content = $this->getContentObject();
+                                               $rt = $content->getRedirectChain();
                                                if ( $rt ) {
                                                        wfDebug( __METHOD__ . ": showing redirect=no page\n" );
                                                        # Viewing a redirect page (e.g. with parameter redirect=no)
                                                        $wgOut->addHTML( $this->viewRedirect( $rt ) );
                                                        # Parse just to get categories, displaytitle, etc.
-                                                       $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions );
+                                                       $this->mParserOutput = $content->getParserOutput( $this->getTitle(), $oldid, $parserOptions );
                                                        $wgOut->addParserOutputNoText( $this->mParserOutput );
                                                        $outputDone = true;
                                                }
@@ -569,7 +634,7 @@ class Article extends Page {
                                        wfDebug( __METHOD__ . ": doing uncached parse\n" );
 
                                        $poolArticleView = new PoolWorkArticleView( $this, $parserOptions,
-                                               $this->getRevIdFetched(), $useParserCache, $this->getContent() );
+                                               $this->getRevIdFetched(), $useParserCache, $this->getContentObject() );
 
                                        if ( !$poolArticleView->execute() ) {
                                                $error = $poolArticleView->getError();
@@ -663,7 +728,9 @@ class Article extends Page {
                $unhide = $wgRequest->getInt( 'unhide' ) == 1;
                $oldid = $this->getOldID();
 
-               $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
+        $contentHandler = ContentHandler::getForTitle( $this->getTitle() );
+               $de = $contentHandler->getDifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
+
                // DifferenceEngine directly fetched the revision:
                $this->mRevIdFetched = $de->mNewid;
                $de->showDiffPage( $diffOnly );
@@ -691,13 +758,9 @@ class Article extends Page {
                        'clearyourcache' );
 
                // Give hooks a chance to customise the output
-               if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) {
-                       // Wrap the whole lot in a <pre> and don't parse
-                       $m = array();
-                       preg_match( '!\.(css|js)$!u', $this->getTitle()->getText(), $m );
-                       $wgOut->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
-                       $wgOut->addHTML( htmlspecialchars( $this->mContent ) );
-                       $wgOut->addHTML( "\n</pre>\n" );
+               if ( !Hooks::isRegistered('ShowRawCssJs') || wfRunHooks( 'ShowRawCssJs', array( $this->fetchContent(), $this->getTitle(), $wgOut ) ) ) { #FIXME: fetchContent() is deprecated #FIXME: hook is deprecated
+            $po = $this->mContentObject->getParserOutput();
+                       $wgOut->addHTML( $po->getText() );
                }
        }
 
@@ -1321,7 +1384,13 @@ class Article extends Page {
                // Generate deletion reason
                $hasHistory = false;
                if ( !$reason ) {
-                       $reason = $this->generateReason( $hasHistory );
+            try {
+                $reason = $this->generateReason( $hasHistory );
+            } catch (MWException $e) {
+                # if a page is horribly broken, we still want to be able to delete it. so be lenient about errors here.
+                wfDebug("Error while building auto delete summary: $e");
+                $reason = '';
+            }
                }
 
                // If the page has a history, insert a warning
@@ -1837,7 +1906,9 @@ class Article extends Page {
         * @return mixed
         */
        public function generateReason( &$hasHistory ) {
-               return $this->mPage->getAutoDeleteReason( $hasHistory );
+        $title = $this->mPage->getTitle();
+        $handler = ContentHandler::getForTitle( $title );
+               return $handler->getAutoDeleteReason( $title, $hasHistory );
        }
 
        // ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** //
@@ -1875,6 +1946,7 @@ class Article extends Page {
         * @param $newtext
         * @param $flags
         * @return string
+     * @deprecated since 1.20, use ContentHandler::getAutosummary() instead
         */
        public static function getAutosummary( $oldtext, $newtext, $flags ) {
                return WikiPage::getAutosummary( $oldtext, $newtext, $flags );