minor cleanup, added todos and attempted (but aborted) refactoring to make stuff...
[lhc/web/wiklou.git] / includes / Content.php
index 1ef81b0..aed9bba 100644 (file)
@@ -7,27 +7,28 @@
  *
  */
 abstract class Content {
-    
-    public function __construct( $modelName = null ) {
-        $this->mModelName = $modelName;
-    }
-
-    public function getModelName() {
-        return $this->mModelName;
-    }
-
-    public function getContentHandler() {
-        return ContentHandler::getForContent( $this );
-    }
 
-    public function serialize( $format = null ) {
-        return $this->getContentHandler()->serialize( $this, $format );
-    }
+       // TODO: create actual fields and document them
 
+    /**
+     * @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 );
 
     /**
@@ -46,6 +47,54 @@ abstract class Content {
      */
     public abstract function getSize( );
 
+       /**
+        * TODO: do we really need to pass a $modelName here?
+        * Seems odd and makes lots of stuff hard (ie having a newEmpty static method in TextContent)
+        *
+        * @param $modelName
+        */
+       public function __construct( $modelName = null ) {
+               $this->mModelName = $modelName;
+       }
+
+       public function getModelName() {
+               return $this->mModelName;
+       }
+
+       protected function checkModelName( $modelName ) {
+               if ( $modelName !== $this->mModelName ) {
+                       throw new MWException( "Bad content model: expected " . $this->mModelName . " but got found " . $modelName );
+               }
+       }
+
+       public function getContentHandler() {
+               return ContentHandler::getForContent( $this );
+       }
+
+       public function getDefaultFormat() {
+               return $this->getContentHandler()->getDefaultFormat();
+       }
+
+       public function getSupportedFormats() {
+               return $this->getContentHandler()->getSupportedFormats();
+       }
+
+       public function isSupportedFormat( $format ) {
+               if ( !$format ) return true; # this means "use the default"
+
+               return $this->getContentHandler()->isSupportedFormat( $format );
+       }
+
+       protected function checkFormat( $format ) {
+               if ( !$this->isSupportedFormat( $format ) ) {
+                       throw new MWException( "Format $format is not supported for content model " . $this->getModelName() );
+               }
+       }
+
+       public function serialize( $format = null ) {
+               return $this->getContentHandler()->serialize( $this, $format );
+       }
+
     public function isEmpty() {
         return $this->getSize() == 0;
     }
@@ -67,16 +116,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,31 +200,55 @@ abstract class Content {
         return $this;
     }
 
-    #TODO: implement specialized ParserOutput for Wikidata model
-    #TODO: provide "combined" ParserOutput for Multipart... somehow.
+    /**
+     * Returns a new WikitextContent object with the given section heading prepended, if supported.
+     * The default implementation just returns this Content object unmodified, ignoring the section header.
+     *
+     * @param $header String
+     * @return Content
+     */
+    public function addSectionHeader( $header ) {
+        return $this;
+    }
 
-    # XXX: isCacheable( ) # can/should we do this here?
+    /**
+     * 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: 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: minimize special cases for CSS/JS; how to handle extra message for JS/CSS previews??
     # TODO: handle ImagePage and CategoryPage
+    # TODO: hook into dump generation to serialize and record model and format!
+
+    # TODO: make sure we cover lucene search / wikisearch.
+    # TODO: make sure ReplaceTemplates still works
+    # TODO: nice&sane integration of GeSHi syntax highlighting
+    #   [11:59] <vvv> Hooks are ugly; make CodeHighlighter interface and a config to set the class which handles syntax highlighting
+    #   [12:00] <vvv> And default it to a DummyHighlighter
 
-    # TODO: Title::newFromRedirectRecurse( $this->getRawText() );
+    # TODO: make sure we cover the external editor interface (does anyone actually use that?!)
 
     # TODO: tie into API to provide contentModel for Revisions
     # TODO: tie into API to provide serialized version and contentFormat for Revisions
     # TODO: tie into API edit interface
+    # TODO: make EditForm plugin for EditPage
 
+    # XXX: isCacheable( ) # can/should we do this here?
 }
 
 /**
  * Content object implementation for representing flat text. The
  */
 abstract class TextContent extends Content {
+
     public function __construct( $text, $modelName = null ) {
-        parent::__construct($modelName);
+        parent::__construct( $modelName );
 
         $this->mText = $text;
     }
@@ -233,6 +340,7 @@ abstract class TextContent extends Content {
 }
 
 class WikitextContent extends TextContent {
+
     public function __construct( $text ) {
         parent::__construct($text, CONTENT_MODEL_WIKITEXT);
 
@@ -294,8 +402,6 @@ class WikitextContent extends TextContent {
      * @return string Complete article text, or null if error
      */
     public function replaceSection( $section, Content $with, $sectionTitle = '' ) {
-        global $wgParser;
-
         wfProfileIn( __METHOD__ );
 
         $myModelName = $this->getModelName();
@@ -329,6 +435,18 @@ class WikitextContent extends TextContent {
         return $newContent;
     }
 
+    /**
+     * Returns a new WikitextContent object with the given section heading prepended.
+     *
+     * @param $header String
+     * @return Content
+     */
+    public function addSectionHeader( $header ) {
+        $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->sectiontitle ) . "\n\n" . $this->getNativeData();
+
+        return new WikitextContent( $text );
+    }
+
     /**
      * Returns a Content object with pre-save transformations applied (or this object if no transformations apply).
      *
@@ -340,12 +458,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 +494,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 +548,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;