Use Title, not IContextSource; remove createArticle, etc.
[lhc/web/wiklou.git] / includes / Content.php
index 28a1717..9463c91 100644 (file)
@@ -12,17 +12,21 @@ abstract class Content {
         * Name of the content model this Content object represents.
         * Use with CONTENT_MODEL_XXX constants
         *
-        * @var String $model_name
+        * @var String $model_id
         */
-       protected $model_name;
+       protected $model_id;
 
        /**
+        * @since WD.1
+        *
         * @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( );
 
        /**
+        * @since WD.1
+        *
         * @return String the wikitext to include when another page includes this  content, or false if the content is not
         *         includable in a wikitext page.
         *
@@ -34,6 +38,8 @@ abstract class Content {
        /**
         * Returns a textual representation of the content suitable for use in edit summaries and log messages.
         *
+        * @since WD.1
+        *
         * @param int $maxlength maximum length of the summary text
         * @return String the summary text
         */
@@ -43,6 +49,8 @@ abstract class Content {
         * Returns native represenation of the data. Interpretation depends on the data model used,
         * as given by getDataModel().
         *
+        * @since WD.1
+        *
         * @return mixed the native representation of the content. Could be a string, a nested array
         *         structure, an object, a binary blob... anything, really.
         *
@@ -58,31 +66,36 @@ abstract class Content {
        public abstract function getSize( );
 
        /**
-        * @param $model_name
+        * @param int $model_id
         */
-       public function __construct( $model_name = null ) {
-               $this->model_name = $model_name;
+       public function __construct( $model_id = null ) {
+               $this->model_id = $model_id;
        }
 
        /**
-        * Returns the name of the content model used by this content objects.
+        * Returns the id of the content model used by this content objects.
         * Corresponds to the CONTENT_MODEL_XXX constants.
         *
-        * @return String the model name
+        * @since WD.1
+        *
+        * @return int the model id
         */
-       public function getModelName() {
-               return $this->model_name;
+       public function getModel() {
+               return $this->model_id;
        }
 
        /**
-        * Throws an MWException if $model_name is not the name of the content model
+        * Throws an MWException if $model_id is not the id of the content model
         * supported by this Content object.
         *
-        * @param String $model_name the model to check
+        * @param int $model_id the model to check
         */
-       protected function checkModelName( $model_name ) {
-               if ( $model_name !== $this->model_name ) {
-                       throw new MWException( "Bad content model: expected " . $this->model_name . " but got found " . $model_name );
+       protected function checkModelID( $model_id ) {
+               if ( $model_id !== $this->model_id ) {
+                       $model_name = ContentHandler::getContentModelName( $model_id );
+                       $own_model_name = ContentHandler::getContentModelName( $this->model_id );
+
+                       throw new MWException( "Bad content model: expected {$this->model_id} ($own_model_name) but got found $model_id ($model_name)." );
                }
        }
 
@@ -92,6 +105,8 @@ abstract class Content {
         *
         * Shorthand for ContentHandler::getForContent( $this )
         *
+        * @since WD.1
+        *
         * @return ContentHandler
         */
        public function getContentHandler() {
@@ -104,6 +119,8 @@ abstract class Content {
         *
         * Shorthand for $this->getContentHandler()->getDefaultFormat()
         *
+        * @since WD.1
+        *
         * @return ContentHandler
         */
        public function getDefaultFormat() {
@@ -116,6 +133,8 @@ abstract class Content {
         *
         * Shorthand for $this->getContentHandler()->getSupportedFormats()
         *
+        * @since WD.1
+        *
         * @return array of supported serialization formats
         */
        public function getSupportedFormats() {
@@ -131,6 +150,8 @@ abstract class Content {
         *
         * Shorthand for $this->getContentHandler()->isSupportedFormat( $format )
         *
+        * @since WD.1
+        *
         * @param String $format the format to check
         * @return bool whether the format is supported
         */
@@ -150,7 +171,7 @@ abstract class Content {
         */
        protected function checkFormat( $format ) {
                if ( !$this->isSupportedFormat( $format ) ) {
-                       throw new MWException( "Format $format is not supported for content model " . $this->getModelName() );
+                       throw new MWException( "Format $format is not supported for content model " . $this->getModel() );
                }
        }
 
@@ -159,6 +180,8 @@ abstract class Content {
         *
         * Shorthand for $this->getContentHandler()->serializeContent( $this, $format )
         *
+        * @since WD.1
+        *
         * @param null|String $format the desired serialization format (or null for the default format).
         * @return String serialized form of this Content object
         */
@@ -169,18 +192,45 @@ abstract class Content {
        /**
         * Returns true if this Content object represents empty content.
         *
+        * @since WD.1
+        *
         * @return bool whether this Content object is empty
         */
        public function isEmpty() {
                return $this->getSize() == 0;
        }
 
+       /**
+        * Returns if the content is valid.
+        * It needs to be valid before it can be saved.
+        *
+        * @since WD.1
+        *
+        * @return boolean
+        */
+       public function isValid() {
+               // TODO
+               return true;
+       }
+
+       /**
+        * Diff the content object with what is currently stored in the database.
+        * If it is not currently stored, it will be diffed with an empty object.
+        *
+        * @since WD.diff
+        *
+        * @return ContentDiff
+        */
+       public function diffToDatabase() {
+               // TODO
+       }
+
        /**
         * Returns true if this Content objects is conceptually equivalent to the given Content object.
         *
         * Will returns false if $that is null.
         * Will return true if $that === $this.
-        * Will return false if $that->getModleName() != $this->getModelName().
+        * Will return false if $that->getModleName() != $this->getModel().
         * Will return false if $that->getNativeData() is not equal to $this->getNativeData(),
         * where the meaning of "equal" depends on the actual data model.
         *
@@ -189,6 +239,8 @@ abstract class Content {
         * * $a->equals( $b ) <=> $b->equals( $b )
         * * $a->equals( $b ) &&  $b->equals( $c ) ==> $a->equals( $c )
         *
+        * @since WD.1
+        *
         * @param Content $that the Content object to compare to
         * @return bool true if this Content object is euqual to $that, false otherwise.
         */
@@ -201,7 +253,7 @@ abstract class Content {
                        return true;
                }
 
-               if ( $that->getModelName() !== $this->getModelName() ) {
+               if ( $that->getModel() !== $this->getModel() ) {
                        return false;
                }
 
@@ -213,13 +265,15 @@ abstract class Content {
         * if $copy = $original->copy()
         *
         * * get_class($original) === get_class($copy)
-        * * $original->getModelName() === $copy->getModelName()
+        * * $original->getModel() === $copy->getModel()
         * * $original->equals( $copy )
         *
         * If and only if the Content object is imutable, the copy() method can and should
         * return $this. That is,  $copy === $original may be true, but only for imutable content
         * objects.
         *
+        * @since WD.1
+        *
         * @return Content. A copy of this object
         */
        public abstract function copy( );
@@ -228,6 +282,8 @@ abstract class Content {
         * Returns true if this content is countable as a "real" wiki page, provided
         * that it's also in a countable location (e.g. a current revision in the main namespace).
         *
+        * @since WD.1
+        *
         * @param $hasLinks Bool: if it is known whether this content contains links, provide this information here,
         *                        to avoid redundant parsing to find out.
         * @return boolean
@@ -235,16 +291,43 @@ abstract class Content {
        public abstract function isCountable( $hasLinks = null ) ;
 
        /**
-        * @param IContextSource $context
+        * @param Title $title
         * @param null $revId
         * @param null|ParserOptions $options
         * @param Boolean $generateHtml whether to generate Html (default: true). If false,
         *        the result of calling getText() on the ParserOutput object returned by
         *        this method is undefined.
         *
+        * @since WD.1
+        *
         * @return ParserOutput
         */
-       public abstract function getParserOutput( IContextSource $context, $revId = null, ParserOptions $options = NULL, $generateHtml = true );
+       public abstract function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true );
+
+       /**
+        * Returns a list of DataUpdate objects for recording information about this Content in some secondary
+        * data store. If the optional second argument, $old, is given, the updates may model only the changes that
+        * need to be made to replace information about the old content with infomration about the new content.
+        *
+        * This default implementation calls $this->getParserOutput( $title, null, null, false ), and then
+        * calls getSecondaryDataUpdates( $title, $recursive ) on the resulting ParserOutput object.
+        *
+        * Subclasses may implement this to determine the necessary updates more efficiently, or make use of information
+        * about the old content.
+        *
+        * @param Title $title the context for determining the necessary updates
+        * @param Content|null $old a Content object representing the previous content, i.e. the content being
+        *                     replaced by this Content object.
+        * @param bool $recursive whether to include recursive updates (default: false).
+        *
+        * @return Array. A list of DataUpdate objects for putting information about this content object somewhere.
+        *
+        * @since WD.1
+        */
+       public function getSecondaryDataUpdates( Title $title, Content $old = null, $recursive = false ) {
+               $po = $this->getParserOutput( $title, null, null, false );
+               return $po->getSecondaryDataUpdates( $title, $recursive );
+       }
 
        /**
         * Construct the redirect destination from this content and return an
@@ -252,6 +335,8 @@ abstract class Content {
         * The last element in the array is the final destination after all redirects
         * have been resolved (up to $wgMaxRedirects times).
         *
+        * @since WD.1
+        *
         * @return Array of Titles, with the destination last
         */
        public function getRedirectChain() {
@@ -264,6 +349,8 @@ abstract class Content {
         * This will only return the immediate redirect target, useful for
         * the redirect table and other checks that don't need full recursion.
         *
+        * @since WD.1
+        *
         * @return Title: The corresponding Title
         */
        public function getRedirectTarget() {
@@ -276,14 +363,21 @@ abstract class Content {
         * 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.
         *
+        * @since WD.1
+        *
         * @return Title
         */
        public function getUltimateRedirectTarget() {
                return null;
        }
 
+       /**
+        * @since WD.1
+        *
+        * @return bool
+        */
        public function isRedirect() {
-               return $this->getRedirectTarget() != null;
+               return $this->getRedirectTarget() !== null;
        }
 
        /**
@@ -291,6 +385,8 @@ abstract class Content {
         *
         * The default implementation returns null.
         *
+        * @since WD.1
+        *
         * @param String $sectionId the section's id, given as a numeric string. The id "0" retrieves the section before
         *          the first heading, "1" the text between the first heading (inluded) and the second heading (excluded), etc.
         * @return Content|Boolean|null the section, or false if no such section exist, or null if sections are not supported
@@ -302,6 +398,8 @@ abstract class Content {
        /**
         * Replaces a section of the content and returns a Content object with the section replaced.
         *
+        * @since WD.1
+        *
         * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...), or "new"
         * @param $with Content: new content of the section
         * @param $sectionTitle String: new section's subject, only if $section is 'new'
@@ -314,12 +412,14 @@ abstract class Content {
        /**
         * Returns a Content object with pre-save transformations applied (or this object if no transformations apply).
         *
+        * @since WD.1
+        *
         * @param Title $title
         * @param User $user
         * @param null|ParserOptions $popts
         * @return Content
         */
-       public function preSaveTransform( Title $title, User $user, ParserOptions $popts = null ) {
+       public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
                return $this;
        }
 
@@ -327,6 +427,8 @@ abstract class Content {
         * 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.
         *
+        * @since WD.1
+        *
         * @param $header String
         * @return Content
         */
@@ -337,11 +439,13 @@ abstract class Content {
        /**
         * Returns a Content object with preload transformations applied (or this object if no transformations apply).
         *
+        * @since WD.1
+        *
         * @param Title $title
         * @param null|ParserOptions $popts
         * @return Content
         */
-       public function preloadTransform( Title $title, ParserOptions $popts = null ) {
+       public function preloadTransform( Title $title, ParserOptions $popts ) {
                return $this;
        }
 
@@ -367,11 +471,13 @@ abstract class Content {
  * Content object implementation for representing flat text.
  *
  * TextContent instances are imutable
+ *
+ * @since WD.1
  */
 abstract class TextContent extends Content {
 
-       public function __construct( $text, $model_name = null ) {
-               parent::__construct( $model_name );
+       public function __construct( $text, $model_id = null ) {
+               parent::__construct( $model_id );
 
                $this->mText = $text;
        }
@@ -457,7 +563,7 @@ abstract class TextContent extends Content {
         *
         * @return ParserOutput representing the HTML form of the text
         */
-       public function getParserOutput( IContextSource $context, $revId = null, ParserOptions $options = null, $generateHtml = true ) {
+       public function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ) {
                # generic implementation, relying on $this->getHtml()
 
                if ( $generateHtml ) $html = $this->getHtml( $options );
@@ -472,28 +578,19 @@ abstract class TextContent extends Content {
 
 }
 
+/**
+ * @since WD.1
+ */
 class WikitextContent extends TextContent {
 
        public function __construct( $text ) {
                parent::__construct($text, CONTENT_MODEL_WIKITEXT);
-
-               $this->mDefaultParserOptions = null; #TODO: use per-class static member?!
        }
 
        protected function getHtml( ) {
                throw new MWException( "getHtml() not implemented for wikitext. Use getParserOutput()->getText()." );
        }
 
-       public function getDefaultParserOptions() {
-               global $wgUser, $wgContLang;
-
-               if ( !$this->mDefaultParserOptions ) { #TODO: use per-class static member?!
-                       $this->mDefaultParserOptions = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
-               }
-
-               return $this->mDefaultParserOptions;
-       }
-
        /**
         * Returns a ParserOutput object resulting from parsing the content's text using $wgParser.
         *
@@ -506,14 +603,14 @@ class WikitextContent extends TextContent {
         *
         * @return ParserOutput representing the HTML form of the text
         */
-       public function getParserOutput( IContextSource $context, $revId = null, ParserOptions $options = null, $generateHtml = true ) {
+       public function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ) {
                global $wgParser;
 
                if ( !$options ) {
-                       $options = $this->getDefaultParserOptions();
+                       $options = new ParserOptions();
                }
 
-               $po = $wgParser->parse( $this->mText, $context->getTitle(), $options, true, true, $revId );
+               $po = $wgParser->parse( $this->mText, $title, $options, true, true, $revId );
 
                return $po;
        }
@@ -544,11 +641,15 @@ class WikitextContent extends TextContent {
        public function replaceSection( $section, Content $with, $sectionTitle = '' ) {
                wfProfileIn( __METHOD__ );
 
-               $myModelName = $this->getModelName();
-               $sectionModelName = $with->getModelName();
+               $myModelId = $this->getModel();
+               $sectionModelId = $with->getModel();
+
+               if ( $sectionModelId != $myModelId  ) {
+                       $myModelName = ContentHandler::getContentModelName( $myModelId );
+                       $sectionModelName = ContentHandler::getContentModelName( $sectionModelId );
 
-               if ( $sectionModelName != $myModelName  ) {
-                       throw new MWException( "Incompatible content model for section: document uses $myModelName, section uses $sectionModelName." );
+                       throw new MWException( "Incompatible content model for section: document uses $myModelId ($myModelName), "
+                                                               . "section uses $sectionModelId ($sectionModelName)." );
                }
 
                $oldtext = $this->getNativeData();
@@ -594,13 +695,11 @@ class WikitextContent extends TextContent {
         *
         * @param Title $title
         * @param User $user
-        * @param null|ParserOptions $popts
+        * @param ParserOptions $popts
         * @return Content
         */
-       public function preSaveTransform( Title $title, User $user, ParserOptions $popts = null ) {
-               global $wgParser;
-
-               if ( $popts == null ) $popts = $this->getDefaultParserOptions();
+       public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
+               global $wgParser, $wgConteLang;
 
                $text = $this->getNativeData();
                $pst = $wgParser->preSaveTransform( $text, $title, $user, $popts );
@@ -612,13 +711,11 @@ class WikitextContent extends TextContent {
         * Returns a Content object with preload transformations applied (or this object if no transformations apply).
         *
         * @param Title $title
-        * @param null|ParserOptions $popts
+        * @param ParserOptions $popts
         * @return Content
         */
-       public function preloadTransform( Title $title, ParserOptions $popts = null ) {
-               global $wgParser;
-
-               if ( $popts == null ) $popts = $this->getDefaultParserOptions();
+       public function preloadTransform( Title $title, ParserOptions $popts ) {
+               global $wgParser, $wgConteLang;
 
                $text = $this->getNativeData();
                $plt = $wgParser->getPreloadText( $text, $title, $popts );
@@ -651,7 +748,7 @@ class WikitextContent extends TextContent {
         *
         * @return bool true if the content is countable
         */
-       public function isCountable( $hasLinks = null, IContextSource $context = null ) {
+       public function isCountable( $hasLinks = null, Title $title = null ) {
                global $wgArticleCountMethod, $wgRequest;
 
                if ( $this->isRedirect( ) ) {
@@ -667,12 +764,12 @@ class WikitextContent extends TextContent {
                                return strpos( $text,  ',' ) !== false;
                        case 'link':
                                if ( $hasLinks === null ) { # not known, find out
-                                       if ( !$context ) { # make dummy context
-                                               //XXX: caller of this method often knows the title, but not a context...
-                                               $context = new RequestContext( $wgRequest );
+                                       if ( !$title ) {
+                                               $context = RequestContext::getMain();
+                                               $title = $context->getTitle();
                                        }
 
-                                       $po = $this->getParserOutput( $context, null, null, false );
+                                       $po = $this->getParserOutput( $title, null, null, false );
                                        $links = $po->getLinks();
                                        $hasLinks = !empty( $links );
                                }
@@ -693,6 +790,9 @@ class WikitextContent extends TextContent {
 
 }
 
+/**
+ * @since WD.1
+ */
 class MessageContent extends TextContent {
        public function __construct( $msg_key, $params = null, $options = null ) {
                parent::__construct(null, CONTENT_MODEL_WIKITEXT); #XXX: messages may be wikitext, html or plain text! and maybe even something else entirely.
@@ -734,7 +834,9 @@ class MessageContent extends TextContent {
 
 }
 
-
+/**
+ * @since WD.1
+ */
 class JavaScriptContent extends TextContent {
        public function __construct( $text ) {
                parent::__construct($text, CONTENT_MODEL_JAVASCRIPT);
@@ -751,6 +853,9 @@ class JavaScriptContent extends TextContent {
 
 }
 
+/**
+ * @since WD.1
+ */
 class CssContent extends TextContent {
        public function __construct( $text ) {
                parent::__construct($text, CONTENT_MODEL_CSS);