Merge "Fixed dependencies for jquery.collapsibleTabs"
[lhc/web/wiklou.git] / includes / Article.php
index 76e566b..0eb0c68 100644 (file)
@@ -57,10 +57,17 @@ class Article extends Page {
        public $mParserOptions;
 
        /**
-        * Content of the revision we are working on
+        * Text of the revision we are working on
         * @var string $mContent
         */
-       var $mContent;                    // !<
+       var $mContent;                    // !< #BC cruft
+
+       /**
+        * Content of the revision we are working on
+        * @var Content
+        * @since 1.21
+        */
+       var $mContentObject;              // !<
 
        /**
         * Is the content ($mContent) already loaded?
@@ -231,9 +238,32 @@ class Article extends Page {
         * This function has side effects! Do not use this function if you
         * only want the real revision text if any.
         *
+        * @deprecated in 1.21; use WikiPage::getContent() instead
+        *
         * @return string Return the text of this revision
         */
        public function getContent() {
+               ContentHandler::deprecated( __METHOD__, '1.21' );
+               $content = $this->getContentObject();
+               return ContentHandler::getContentText( $content );
+       }
+
+       /**
+        * Returns a Content object representing the pages effective display content,
+        * not necessarily the revision's 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 Content Return the content of this revision
+        *
+        * @since 1.21
+        */
+       protected function getContentObject() {
                wfProfileIn( __METHOD__ );
 
                if ( $this->mPage->getID() === 0 ) {
@@ -244,18 +274,20 @@ class Article extends Page {
                                if ( $text === false ) {
                                        $text = '';
                                }
+
+                               $content = ContentHandler::makeContent( $text, $this->getTitle() );
                        } else {
                                $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
-                               $text = wfMessage( $message )->text();
+                               $content = new MessageContent( $message, null, 'parsemag' );
                        }
                        wfProfileOut( __METHOD__ );
 
-                       return $text;
+                       return $content;
                } else {
-                       $this->fetchContent();
+                       $this->fetchContentObject();
                        wfProfileOut( __METHOD__ );
 
-                       return $this->mContent;
+                       return $this->mContentObject;
                }
        }
 
@@ -336,22 +368,61 @@ class Article extends Page {
         * Get text of an article from database
         * Does *NOT* follow redirects.
         *
+        * @protected
+        * @note this is really internal functionality that should really NOT be used by other functions. For accessing
+        *       article content, use the WikiPage class, especially WikiBase::getContent(). However, a lot of legacy code
+        *       uses this method to retrieve page text from the database, so the function has to remain public for now.
+        *
         * @return mixed string containing article contents, or false if null
+        * @deprecated in 1.21, use WikiPage::getContent() instead
         */
-       function fetchContent() {
-               if ( $this->mContentLoaded ) {
+       function fetchContent() { #BC cruft!
+               ContentHandler::deprecated( __METHOD__, '1.21' );
+
+               if ( $this->mContentLoaded && $this->mContent ) {
                        return $this->mContent;
                }
 
                wfProfileIn( __METHOD__ );
 
+               $content = $this->fetchContentObject();
+
+               $this->mContent = ContentHandler::getContentText( $content ); #@todo: get rid of mContent everywhere!
+               ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
+
+               wfProfileOut( __METHOD__ );
+
+               return $this->mContent;
+       }
+
+
+       /**
+        * Get text content object
+        * Does *NOT* follow redirects.
+        * TODO: when is this null?
+        *
+        * @note code that wants to retrieve page content from the database should use WikiPage::getContent().
+        *
+        * @return Content|null
+        *
+        * @since 1.21
+        */
+       protected function fetchContentObject() {
+               if ( $this->mContentLoaded ) {
+                       return $this->mContentObject;
+               }
+
+               wfProfileIn( __METHOD__ );
+
                $this->mContentLoaded = true;
+               $this->mContent = null;
 
                $oldid = $this->getOldID();
 
                # Pre-fill content with error message so that if something
                # fails we'll have something telling us what we intended.
-               $this->mContent = wfMessage( 'missing-revision', $oldid )->plain();
+               //XXX: this isn't page content but a UI message. horrible.
+               $this->mContentObject = new MessageContent( 'missing-revision', array( $oldid ), array() ) ;
 
                if ( $oldid ) {
                        # $this->mRevision might already be fetched by getOldIDFromRequest()
@@ -371,6 +442,7 @@ class Article extends Page {
                        }
 
                        $this->mRevision = $this->mPage->getRevision();
+
                        if ( !$this->mRevision ) {
                                wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
                                wfProfileOut( __METHOD__ );
@@ -380,14 +452,14 @@ class Article extends Page {
 
                // @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->mContentObject = $this->mRevision->getContent( Revision::FOR_THIS_USER, $this->getContext()->getUser() ); // Loads if user is allowed
                $this->mRevIdFetched = $this->mRevision->getId();
 
-               wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
+               wfRunHooks( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) );
 
                wfProfileOut( __METHOD__ );
 
-               return $this->mContent;
+               return $this->mContentObject;
        }
 
        /**
@@ -420,7 +492,7 @@ class Article extends Page {
         * @return Revision|null
         */
        public function getRevisionFetched() {
-               $this->fetchContent();
+               $this->fetchContentObject();
 
                return $this->mRevision;
        }
@@ -580,7 +652,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 ) {
@@ -604,18 +676,25 @@ class Article extends Page {
                                                wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
                                                $this->showCssOrJsPage();
                                                $outputDone = true;
-                                       } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) {
+                                       } elseif( !wfRunHooks( 'ArticleContentViewCustom',
+                                                       array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
+
+                                               # Allow extensions do their own custom view for certain pages
+                                               $outputDone = true;
+                                       } elseif( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
+                                                       array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
+
                                                # 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)
                                                        $outputPage->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, false );
                                                        $outputPage->addParserOutputNoText( $this->mParserOutput );
                                                        $outputDone = true;
                                                }
@@ -625,8 +704,9 @@ class Article extends Page {
                                        # Run the parse, protected by a pool counter
                                        wfDebug( __METHOD__ . ": doing uncached parse\n" );
 
+                                       // @todo: shouldn't we be passing $this->getPage() to PoolWorkArticleView instead of plain $this?
                                        $poolArticleView = new PoolWorkArticleView( $this, $parserOptions,
-                                               $this->getRevIdFetched(), $useParserCache, $this->getContent() );
+                                               $this->getRevIdFetched(), $useParserCache, $this->getContentObject(), $this->getContext() );
 
                                        if ( !$poolArticleView->execute() ) {
                                                $error = $poolArticleView->getError();
@@ -708,6 +788,8 @@ class Article extends Page {
        /**
         * Show a diff page according to current request variables. For use within
         * Article::view() only, other callers should use the DifferenceEngine class.
+        *
+        * @todo: make protected
         */
        public function showDiffPage() {
                $request = $this->getContext()->getRequest();
@@ -719,7 +801,17 @@ class Article extends Page {
                $unhide = $request->getInt( 'unhide' ) == 1;
                $oldid = $this->getOldID();
 
-               $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
+               $rev = $this->getRevisionFetched();
+
+               if ( !$rev ) {
+                       $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
+                       $this->getContext()->getOutput()->addWikiMsg( 'difference-missing-revision', $oldid, 1 );
+                       return;
+               }
+
+               $contentHandler = $rev->getContentHandler();
+               $de = $contentHandler->createDifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
+
                // DifferenceEngine directly fetched the revision:
                $this->mRevIdFetched = $de->mNewid;
                $de->showDiffPage( $diffOnly );
@@ -736,23 +828,24 @@ class Article extends Page {
         *
         * This is hooked by SyntaxHighlight_GeSHi to do syntax highlighting of these
         * page views.
+        *
+        * @param bool $showCacheHint whether to show a message telling the user to clear the browser cache (default: true).
         */
-       protected function showCssOrJsPage() {
-               $dir = $this->getContext()->getLanguage()->getDir();
-               $lang = $this->getContext()->getLanguage()->getCode();
-
+       protected function showCssOrJsPage( $showCacheHint = true ) {
                $outputPage = $this->getContext()->getOutput();
-               $outputPage->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
-                       'clearyourcache' );
+
+               if ( $showCacheHint ) {
+                       $dir = $this->getContext()->getLanguage()->getDir();
+                       $lang = $this->getContext()->getLanguage()->getCode();
+
+                       $outputPage->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
+                               'clearyourcache' );
+               }
 
                // Give hooks a chance to customise the output
-               if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) {
-                       // Wrap the whole lot in a <pre> and don't parse
-                       $m = array();
-                       preg_match( '!\.(css|js)$!u', $this->getTitle()->getText(), $m );
-                       $outputPage->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
-                       $outputPage->addHTML( htmlspecialchars( $this->mContent ) );
-                       $outputPage->addHTML( "\n</pre>\n" );
+               if ( ContentHandler::runLegacyHooks( 'ShowRawCssJs', array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
+                       $po = $this->mContentObject->getParserOutput( $this->getTitle() );
+                       $outputPage->addHTML( $po->getText() );
                }
        }
 
@@ -1377,7 +1470,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
@@ -1617,6 +1716,8 @@ class Article extends Page {
         * @return ParserOutput or false if the given revsion ID is not found
         */
        public function getParserOutput( $oldid = null, User $user = null ) {
+               //XXX: bypasses mParserOptions and thus setParserOptions()
+
                if ( $user === null ) {
                        $parserOptions = $this->getParserOptions();
                } else {
@@ -1626,6 +1727,21 @@ class Article extends Page {
                return $this->mPage->getParserOutput( $parserOptions, $oldid );
        }
 
+       /**
+        * Override the ParserOptions used to render the primary article wikitext.
+        *
+        * @param ParserOptions $options
+        * @throws MWException if the parser options where already initialized.
+        */
+       public function setParserOptions( ParserOptions $options ) {
+               if ( $this->mParserOptions ) {
+                       throw new MWException( "can't change parser options after they have already been set" );
+               }
+
+               // clone, so if $options is modified later, it doesn't confuse the parser cache.
+               $this->mParserOptions = clone $options;
+       }
+
        /**
         * Get parser options suitable for rendering the primary article wikitext
         * @return ParserOptions
@@ -1845,7 +1961,13 @@ class Article extends Page {
         * @return bool
         */
        public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) {
-               return $this->mPage->updateRestrictions( $limit, $reason, $cascade, $expiry );
+               return $this->mPage->doUpdateRestrictions(
+                       $limit,
+                       $expiry,
+                       $cascade,
+                       $reason,
+                       $this->getContext()->getUser()
+               );
        }
 
        /**
@@ -1892,7 +2014,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 ) ****** //
@@ -1930,6 +2054,7 @@ class Article extends Page {
         * @param $newtext
         * @param $flags
         * @return string
+        * @deprecated since 1.21, use ContentHandler::getAutosummary() instead
         */
        public static function getAutosummary( $oldtext, $newtext, $flags ) {
                return WikiPage::getAutosummary( $oldtext, $newtext, $flags );