assert correct content model and format
authorDaniel Kinzler <daniel.kinzler@wikimedia.de>
Tue, 27 Mar 2012 12:15:30 +0000 (12:15 +0000)
committerDaniel Kinzler <daniel.kinzler@wikimedia.de>
Wed, 4 Apr 2012 17:59:31 +0000 (19:59 +0200)
includes/Content.php
includes/ContentHandler.php

index 52e3b1e..1ec4b0c 100644 (file)
@@ -16,10 +16,36 @@ abstract class Content {
         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 );
     }
index 3a0d50a..ce1c4fb 100644 (file)
@@ -118,8 +118,14 @@ abstract class ContentHandler {
         global $wgContentHandlers;
 
         if ( empty( $wgContentHandlers[$modelName] ) ) {
-            #FIXME: hook here!
-            throw new MWException( "No handler for model $modelName registered in \$wgContentHandlers" );
+            $handler = null;
+            wfRunHooks( "ContentHandlerForModelName", array( $modelName, &$handler ) );  #FIXME: document new hook
+
+            if ( $handler ) { # NOTE: may be a string or an object, either is fine!
+                $wgContentHandlers[$modelName] = $handler;
+            } else {
+                throw new MWException( "No handler for model $modelName registered in \$wgContentHandlers" );
+            }
         }
 
         if ( is_string( $wgContentHandlers[$modelName] ) ) {
@@ -142,6 +148,11 @@ abstract class ContentHandler {
         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 getSupportedFormats() {
         # for wikitext: "text/x-mediawiki-1", "text/x-mediawiki-2", etc
@@ -153,6 +164,18 @@ abstract class ContentHandler {
         return $this->mSupportedFormats[0];
     }
 
+    public function isSupportedFormat( $format ) {
+        if ( !$format ) return true; # this means "use the default"
+
+        return in_array( $format, $this->mSupportedFormats );
+    }
+
+    protected function checkFormat( $format ) {
+        if ( !$this->isSupportedFormat( $format ) ) {
+            throw new MWException( "Format $format is not supported for content model " . $this->getModelName() );
+        }
+    }
+
     /**
      * @abstract
      * @param Content $content
@@ -182,7 +205,8 @@ abstract class ContentHandler {
      * @todo Article is being refactored into an action class, keep track of that
      */
     public function createArticle( Title $title ) {
-        #XXX: assert that $title->getContentModelName() == $this->getModelname()?
+        $this->checkModelName( $title->getContentModelName() );
+
         $article = new Article($title);
         return $article;
     }
@@ -194,7 +218,8 @@ abstract class ContentHandler {
      * @return \EditPage 
      */
     public function createEditPage( Article $article ) {
-        #XXX: assert that $article->getContentObject()->getModelName() == $this->getModelname()?
+        $this->checkModelName( $article->getContentObject()->getModelName() );
+
         $editPage = new EditPage( $article );
         return $editPage;
     }
@@ -206,7 +231,8 @@ abstract class ContentHandler {
      * @return \ExternalEdit
      */
     public function createExternalEdit( IContextSource $context ) {
-        #XXX: assert that $article->getContentObject()->getModelName() == $this->getModelname()?
+        $this->checkModelName( $context->getTitle()->getModelName() );
+
         $externalEdit = new ExternalEdit( $context );
         return $externalEdit;
     }
@@ -223,6 +249,8 @@ abstract class ContentHandler {
     public function getDifferenceEngine( IContextSource $context, $old = 0, $new = 0, $rcid = 0, #FIMXE: use everywhere!
                                          $refreshCache = false, $unhide = false ) {
 
+        $this->checkModelName( $context->getTitle()->getModelName() );
+
         $de = new DifferenceEngine( $context, $old, $new, $rcid, $refreshCache, $unhide );
 
         return $de;
@@ -428,6 +456,14 @@ abstract class ContentHandler {
     #TODO: Article::showCssOrJsPage ---> specialized classes!
 
     #XXX: ImagePage and CategoryPage... wrappers that use ContentHandler? or ContentHandler creates wrappers?
+
+    #TODO: hook into dump generation to serialize and record model and format!
+    #TODO: cover action=raw
+    #TODO: make sure we cover lucene search / wikisearch.
+    #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: make sure we cover the external editor interface (does anyone actually use that?!)
 }
 
 
@@ -438,7 +474,7 @@ abstract class TextContentHandler extends ContentHandler {
     }
 
     public function serialize( Content $content, $format = null ) {
-        #FIXME: assert format
+        $this->checkFormat( $format );
         return $content->getNativeData();
     }
 
@@ -454,6 +490,9 @@ abstract class TextContentHandler extends ContentHandler {
      * @return Content|Bool
      */
     public function merge3( Content $oldContent, Content $myContent, Content $yourContent ) {
+        $this->checkModelName( $oldContent->getModelName() );
+        #TODO: check that all Content objects have the same content model! #XXX: what to do if they don't?
+
         $format = $this->getDefaultFormat();
 
         $old = $this->serialize( $oldContent, $format );
@@ -478,17 +517,20 @@ class WikitextContentHandler extends TextContentHandler {
     }
 
     public function unserialize( $text, $format = null ) {
-        #FIXME: assert format
-        return new WikitextContent($text);
+        $this->checkFormat( $format );
+
+        return new WikitextContent( $text );
     }
 
     public function emptyContent() {
-        return new WikitextContent("");
+        return new WikitextContent( "" );
     }
 
 
 }
 
+#TODO: make ScriptContentHandler base class with plugin interface for syntax highlighting!
+
 class JavaScriptContentHandler extends TextContentHandler {
 
     public function __construct( $modelName = CONTENT_MODEL_WIKITEXT ) {
@@ -496,11 +538,11 @@ class JavaScriptContentHandler extends TextContentHandler {
     }
 
     public function unserialize( $text, $format = null ) {
-        return new JavaScriptContent($text);
+        return new JavaScriptContent( $text );
     }
 
     public function emptyContent() {
-        return new JavaScriptContent("");
+        return new JavaScriptContent( "" );
     }
 }
 
@@ -511,11 +553,11 @@ class CssContentHandler extends TextContentHandler {
     }
 
     public function unserialize( $text, $format = null ) {
-        return new CssContent($text);
+        return new CssContent( $text );
     }
 
     public function emptyContent() {
-        return new CssContent("");
+        return new CssContent( "" );
     }
 
 }