Merge "BagOStuff doc tweaks"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 20 Apr 2015 17:38:22 +0000 (17:38 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 20 Apr 2015 17:38:23 +0000 (17:38 +0000)
17 files changed:
RELEASE-NOTES-1.25
RELEASE-NOTES-1.26
includes/EditPage.php
includes/api/ApiEditPage.php
includes/content/ContentHandler.php
includes/content/TextContentHandler.php
includes/logging/RightsLogFormatter.php
tests/TestsAutoLoader.php
tests/phpunit/includes/EditPageTest.php
tests/phpunit/includes/api/ApiEditPageTest.php
tests/phpunit/includes/content/ContentHandlerTest.php
tests/phpunit/includes/content/TextContentHandlerTest.php [new file with mode: 0644]
tests/phpunit/includes/content/WikitextContentHandlerTest.php
tests/phpunit/mocks/content/DummyContentForTesting.php [new file with mode: 0644]
tests/phpunit/mocks/content/DummyContentHandlerForTesting.php [new file with mode: 0644]
tests/phpunit/mocks/content/DummyNonTextContent.php [new file with mode: 0644]
tests/phpunit/mocks/content/DummyNonTextContentHandler.php [new file with mode: 0644]

index f0d1c07..13b01d9 100644 (file)
@@ -411,9 +411,6 @@ changes to languages because of Bugzilla reports.
   and getInternalLinkAttributes methods in Linker, and removed
   getExternalLinkAttributes method, which was deprecated in MediaWiki 1.18.
 * Removed Sites class, which was deprecated in 1.21 and replaced by SiteSQLStore.
-* The mw.api.getToken() method now uses action=query?meta=tokens. This will now
-  fail for custom tokens registered only via the deprecated ApiTokensGetTokenTypes
-  hook. The ApiQueryTokensRegisterTypes hook should be used for this to work.
 * Added wgRelevantArticleId to the client-side config, for use on special pages.
 * Deprecated the TitleIsCssOrJsPage hook. Superseded by the
   ContentHandlerDefaultModelFor hook since MediaWiki 1.21.
index bf9341d..eddfe30 100644 (file)
@@ -35,6 +35,13 @@ changes to languages because of Bugzilla reports.
 * ChangeTags::tagDescription() will return false if the interface message
   for the tag is disabled.
 * Added PageHistoryPager::doBatchLookups hook.
+* supportsDirectEditing and supportsDirectApiEditing methods added to
+ContentHandler, to provide a way for ApiEditPage and EditPage to check
+if direct editing of content is allowed. These methods return false,
+by default for the ContentHandler base class and true for TextContentHandler
+and it's derivative classes (everything in core). For Content types that
+do not support direct editing, an alternative mechanism should be provided
+for editing, such as action overrides or specific api modules.
 
 == Compatibility ==
 
index 8d27eac..1679c3b 100644 (file)
@@ -380,12 +380,14 @@ class EditPage {
 
        public $suppressIntro = false;
 
-       /** @var bool Set to true to allow editing of non-text content types. */
-       public $allowNonTextContent = false;
-
        /** @var bool */
        protected $edit;
 
+       /**
+        * @var bool Set in ApiEditPage, based on ContentHandler::allowsDirectApiEditing
+        */
+       private $enableApiEditOverride = false;
+
        /**
         * @param Article $article
         */
@@ -447,8 +449,18 @@ class EditPage {
         * @throws MWException If $modelId has no known handler
         */
        public function isSupportedContentModel( $modelId ) {
-               return $this->allowNonTextContent ||
-                       ContentHandler::getForModelID( $modelId ) instanceof TextContentHandler;
+               return $this->enableApiEditOverride === true ||
+                       ContentHandler::getForModelID( $modelId )->supportsDirectEditing();
+       }
+
+       /**
+        * Allow editing of content that supports API direct editing, but not general
+        * direct editing. Set to false by default.
+        *
+        * @param bool $enableOverride
+        */
+       public function setApiEditOverride( $enableOverride ) {
+               $this->enableApiEditOverride = $enableOverride;
        }
 
        function submit() {
index 56c67e0..601c64c 100644 (file)
@@ -96,8 +96,12 @@ class ApiEditPage extends ApiBase {
                        $contentHandler = ContentHandler::getForModelID( $params['contentmodel'] );
                }
 
-               // @todo Ask handler whether direct editing is supported at all! make
-               // allowFlatEdit() method or some such
+               if ( $contentHandler->supportsDirectApiEditing() === false ) {
+                       $this->dieUsage(
+                               'Direct editing via API is not supported for this content type.',
+                               'no-direct-editing'
+                       );
+               }
 
                if ( !isset( $params['contentformat'] ) || $params['contentformat'] == '' ) {
                        $params['contentformat'] = $contentHandler->getDefaultFormat();
@@ -362,9 +366,7 @@ class ApiEditPage extends ApiBase {
 
                $ep = new EditPage( $articleObject );
 
-               // allow editing of non-textual content.
-               $ep->allowNonTextContent = true;
-
+               $ep->setApiEditOverride( true );
                $ep->setContextTitle( $titleObj );
                $ep->importFormData( $req );
                $content = $ep->textbox1;
index 371b267..9c2435a 100644 (file)
@@ -1057,6 +1057,24 @@ abstract class ContentHandler {
                return false;
        }
 
+       /**
+        * Return true if this content model supports direct editing, such as via EditPage.
+        *
+        * @return bool Default is false, and true for TextContent and it's derivatives.
+        */
+       public function supportsDirectEditing() {
+               return false;
+       }
+
+       /**
+        * Whether or not this content model supports direct editing via ApiEditPage
+        *
+        * @return bool Default is false, and true for TextContent and derivatives.
+        */
+       public function supportsDirectApiEditing() {
+               return $this->supportsDirectEditing();
+       }
+
        /**
         * Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if
         * self::$enableDeprecationWarnings is set to true.
index ffe1acb..f5e8783 100644 (file)
@@ -134,4 +134,13 @@ class TextContentHandler extends ContentHandler {
                return new $class( '' );
        }
 
+       /**
+        * @see ContentHandler::supportsDirectEditing
+        *
+        * @return bool Default is true for TextContent and derivatives.
+        */
+       public function supportsDirectEditing() {
+               return true;
+       }
+
 }
index f69530c..352bda5 100644 (file)
@@ -116,8 +116,13 @@ class RightsLogFormatter extends LogFormatter {
                        }
                }
 
-               $params['4:array:oldgroups'] = $this->makeGroupArray( $params['4:array:oldgroups'] );
-               $params['5:array:newgroups'] = $this->makeGroupArray( $params['5:array:newgroups'] );
+               // Really old entries does not have log params
+               if ( isset( $params['4:array:oldgroups'] ) ) {
+                       $params['4:array:oldgroups'] = $this->makeGroupArray( $params['4:array:oldgroups'] );
+               }
+               if ( isset( $params['5:array:newgroups'] ) ) {
+                       $params['5:array:newgroups'] = $this->makeGroupArray( $params['5:array:newgroups'] );
+               }
 
                return $params;
        }
index def23da..1a2e0cb 100644 (file)
@@ -66,8 +66,10 @@ $wgAutoloadClasses += array(
        'TestRecentChangesHelper' => "$testDir/phpunit/includes/changes/TestRecentChangesHelper.php",
 
        # tests/phpunit/includes/content
-       'DummyContentHandlerForTesting' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
-       'DummyContentForTesting' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
+       'DummyContentHandlerForTesting' => "$testDir/phpunit/mocks/content/DummyContentHandlerForTesting.php",
+       'DummyContentForTesting' => "$testDir/phpunit/mocks/content/DummyContentForTesting.php",
+       'DummyNonTextContentHandler' => "$testDir/phpunit/mocks/content/DummyNonTextContentHandler.php",
+       'DummyNonTextContent' => "$testDir/phpunit/mocks/content/DummyNonTextContent.php",
        'ContentHandlerTest' => "$testDir/phpunit/includes/content/ContentHandlerTest.php",
        'JavaScriptContentTest' => "$testDir/phpunit/includes/content/JavaScriptContentTest.php",
        'TextContentTest' => "$testDir/phpunit/includes/content/TextContentTest.php",
index 15778e4..27959b1 100644 (file)
  */
 class EditPageTest extends MediaWikiLangTestCase {
 
+       protected function setUp() {
+               global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
+
+               parent::setUp();
+
+               $this->setMwGlobals( array(
+                       'wgExtraNamespaces' => $wgExtraNamespaces,
+                       'wgNamespaceContentModels' => $wgNamespaceContentModels,
+                       'wgContentHandlers' => $wgContentHandlers,
+                       'wgContLang' => $wgContLang,
+               ) );
+
+               $wgExtraNamespaces[12312] = 'Dummy';
+               $wgExtraNamespaces[12313] = 'Dummy_talk';
+
+               $wgNamespaceContentModels[12312] = "testing";
+               $wgContentHandlers["testing"] = 'DummyContentHandlerForTesting';
+
+               MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
+               $wgContLang->resetNamespaces(); # reset namespace cache
+       }
+
        /**
         * @dataProvider provideExtractSectionTitle
         * @covers EditPage::extractSectionTitle
@@ -499,4 +521,37 @@ hello
                $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Berta', $bertasEdit,
                        $expectedCode, $expectedText, $message );
        }
+
+       /**
+        * @depends testAutoMerge
+        */
+       public function testCheckDirectEditingDisallowed_forNonTextContent() {
+               $title = Title::newFromText( 'Dummy:NonTextPageForEditPage' );
+               $page = WikiPage::factory( $title );
+
+               $article = new Article( $title );
+               $article->getContext()->setTitle( $title );
+               $ep = new EditPage( $article );
+               $ep->setContextTitle( $title );
+
+               $user = $GLOBALS['wgUser'];
+
+               $edit = array(
+                       'wpTextbox1' => serialize( 'non-text content' ),
+                       'wpEditToken' => $user->getEditToken(),
+                       'wpEdittime' => '',
+                       'wpStarttime' => wfTimestampNow()
+               );
+
+               $req = new FauxRequest( $edit, true );
+               $ep->importFormData( $req );
+
+               $this->setExpectedException(
+                       'MWException',
+                       'This content model is not supported: testing'
+               );
+
+               $ep->internalAttemptSave( $result, false );
+       }
+
 }
index 3179a45..865f1c2 100644 (file)
@@ -27,9 +27,14 @@ class ApiEditPageTest extends ApiTestCase {
 
                $wgExtraNamespaces[12312] = 'Dummy';
                $wgExtraNamespaces[12313] = 'Dummy_talk';
+               $wgExtraNamespaces[12314] = 'DummyNonText';
+               $wgExtraNamespaces[12315] = 'DummyNonText_talk';
 
                $wgNamespaceContentModels[12312] = "testing";
+               $wgNamespaceContentModels[12314] = "testing-nontext";
+
                $wgContentHandlers["testing"] = 'DummyContentHandlerForTesting';
+               $wgContentHandlers["testing-nontext"] = 'DummyNonTextContentHandler';
 
                MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
                $wgContLang->resetNamespaces(); # reset namespace cache
@@ -96,33 +101,6 @@ class ApiEditPageTest extends ApiTestCase {
                );
        }
 
-       public function testNonTextEdit() {
-               $name = 'Dummy:ApiEditPageTest_testNonTextEdit';
-               $data = serialize( 'some bla bla text' );
-
-               // -- test new page --------------------------------------------
-               $apiResult = $this->doApiRequestWithToken( array(
-                       'action' => 'edit',
-                       'title' => $name,
-                       'text' => $data, ) );
-               $apiResult = $apiResult[0];
-
-               // Validate API result data
-               $this->assertArrayHasKey( 'edit', $apiResult );
-               $this->assertArrayHasKey( 'result', $apiResult['edit'] );
-               $this->assertEquals( 'Success', $apiResult['edit']['result'] );
-
-               $this->assertArrayHasKey( 'new', $apiResult['edit'] );
-               $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
-
-               $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
-
-               // validate resulting revision
-               $page = WikiPage::factory( Title::newFromText( $name ) );
-               $this->assertEquals( "testing", $page->getContentModel() );
-               $this->assertEquals( $data, $page->getContent()->serialize() );
-       }
-
        /**
         * @return array
         */
@@ -493,4 +471,45 @@ class ApiEditPageTest extends ApiTestCase {
 
                $page->clear();
        }
+
+       public function testCheckDirectApiEditingDisallowed_forNonTextContent() {
+               $this->setExpectedException(
+                       'UsageException',
+                       'Direct editing via API is not supported for this content type.'
+               );
+
+               $this->doApiRequestWithToken( array(
+                       'action' => 'edit',
+                       'title' => 'Dummy:ApiEditPageTest_nonTextPageEdit',
+                       'text' => '{"animals":["kittens!"]}'
+               ) );
+       }
+
+       public function testSupportsDirectApiEditing_withContentHandlerOverride() {
+               $name = 'DummyNonText:ApiEditPageTest_testNonTextEdit';
+               $data = serialize( 'some bla bla text' );
+
+               $result = $this->doApiRequestWithToken( array(
+                       'action' => 'edit',
+                       'title' => $name,
+                       'text' => $data,
+               ) );
+
+               $apiResult = $result[0];
+
+               // Validate API result data
+               $this->assertArrayHasKey( 'edit', $apiResult );
+               $this->assertArrayHasKey( 'result', $apiResult['edit'] );
+               $this->assertEquals( 'Success', $apiResult['edit']['result'] );
+
+               $this->assertArrayHasKey( 'new', $apiResult['edit'] );
+               $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] );
+
+               $this->assertArrayHasKey( 'pageid', $apiResult['edit'] );
+
+               // validate resulting revision
+               $page = WikiPage::factory( Title::newFromText( $name ) );
+               $this->assertEquals( "testing-nontext", $page->getContentModel() );
+               $this->assertEquals( $data, $page->getContent()->serialize() );
+       }
 }
index 988a59e..63743eb 100644 (file)
@@ -338,6 +338,11 @@ class ContentHandlerTest extends MediaWikiTestCase {
        }
        */
 
+       public function testSupportsDirectEditing() {
+               $handler = new DummyContentHandlerForTesting( CONTENT_MODEL_JSON );
+               $this->assertFalse( $handler->supportsDirectEditing(), 'direct editing is not supported' );
+       }
+
        /**
         * @covers ContentHandler::runLegacyHooks
         */
@@ -365,164 +370,3 @@ class ContentHandlerTest extends MediaWikiTestCase {
                return true;
        }
 }
-
-class DummyContentHandlerForTesting extends ContentHandler {
-
-       public function __construct( $dataModel ) {
-               parent::__construct( $dataModel, array( "testing" ) );
-       }
-
-       /**
-        * @see ContentHandler::serializeContent
-        *
-        * @param Content $content
-        * @param string $format
-        *
-        * @return string
-        */
-       public function serializeContent( Content $content, $format = null ) {
-               return $content->serialize();
-       }
-
-       /**
-        * @see ContentHandler::unserializeContent
-        *
-        * @param string $blob
-        * @param string $format Unused.
-        *
-        * @return Content
-        */
-       public function unserializeContent( $blob, $format = null ) {
-               $d = unserialize( $blob );
-
-               return new DummyContentForTesting( $d );
-       }
-
-       /**
-        * Creates an empty Content object of the type supported by this ContentHandler.
-        *
-        */
-       public function makeEmptyContent() {
-               return new DummyContentForTesting( '' );
-       }
-}
-
-class DummyContentForTesting extends AbstractContent {
-
-       public function __construct( $data ) {
-               parent::__construct( "testing" );
-
-               $this->data = $data;
-       }
-
-       public function serialize( $format = null ) {
-               return serialize( $this->data );
-       }
-
-       /**
-        * @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 function getTextForSearchIndex() {
-               return '';
-       }
-
-       /**
-        * @return string|bool The wikitext to include when another page includes this  content,
-        *  or false if the content is not includable in a wikitext page.
-        */
-       public function getWikitextForTransclusion() {
-               return false;
-       }
-
-       /**
-        * 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 function getTextForSummary( $maxlength = 250 ) {
-               return '';
-       }
-
-       /**
-        * Returns native represenation of the data. Interpretation depends on the data model used,
-        * as given by getDataModel().
-        *
-        * @return mixed The native representation of the content. Could be a string, a nested array
-        *  structure, an object, a binary blob... anything, really.
-        */
-       public function getNativeData() {
-               return $this->data;
-       }
-
-       /**
-        * returns the content's nominal size in bogo-bytes.
-        *
-        * @return int
-        */
-       public function getSize() {
-               return strlen( $this->data );
-       }
-
-       /**
-        * Return a copy of this Content object. The following must be true for the object returned
-        * if $copy = $original->copy()
-        *
-        * * get_class($original) === get_class($copy)
-        * * $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.
-        *
-        * @return Content A copy of this object
-        */
-       public function copy() {
-               return $this;
-       }
-
-       /**
-        * 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).
-        *
-        * @param bool $hasLinks If it is known whether this content contains links,
-        * provide this information here, to avoid redundant parsing to find out.
-        * @return bool
-        */
-       public function isCountable( $hasLinks = null ) {
-               return false;
-       }
-
-       /**
-        * @param Title $title
-        * @param int $revId Unused.
-        * @param null|ParserOptions $options
-        * @param bool $generateHtml Whether to generate Html (default: true). If false, the result
-        *  of calling getText() on the ParserOutput object returned by this method is undefined.
-        *
-        * @return ParserOutput
-        */
-       public function getParserOutput( Title $title, $revId = null,
-               ParserOptions $options = null, $generateHtml = true
-       ) {
-               return new ParserOutput( $this->getNativeData() );
-       }
-
-       /**
-        * @see AbstractContent::fillParserOutput()
-        *
-        * @param Title $title Context title for parsing
-        * @param int|null $revId Revision ID (for {{REVISIONID}})
-        * @param ParserOptions $options Parser options
-        * @param bool $generateHtml Whether or not to generate HTML
-        * @param ParserOutput &$output The output object to fill (reference).
-        */
-       protected function fillParserOutput( Title $title, $revId,
-                       ParserOptions $options, $generateHtml, ParserOutput &$output ) {
-               $output = new ParserOutput( $this->getNativeData() );
-       }
-}
diff --git a/tests/phpunit/includes/content/TextContentHandlerTest.php b/tests/phpunit/includes/content/TextContentHandlerTest.php
new file mode 100644 (file)
index 0000000..33861f1
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @group ContentHandler
+ */
+class TextContentHandlerTest extends MediaWikiLangTestCase {
+
+       public function testSupportsDirectEditing() {
+               $handler = new TextContentHandler();
+               $this->assertTrue( $handler->supportsDirectEditing(), 'direct editing is supported' );
+       }
+
+}
index 38fb573..361238b 100644 (file)
@@ -115,6 +115,11 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
                $this->assertEquals( $supported, $this->handler->isSupportedFormat( $format ) );
        }
 
+       public function testSupportsDirectEditing() {
+               $handler = new WikiTextContentHandler();
+               $this->assertTrue( $handler->supportsDirectEditing(), 'direct editing is supported' );
+       }
+
        public static function dataMerge3() {
                return array(
                        array(
diff --git a/tests/phpunit/mocks/content/DummyContentForTesting.php b/tests/phpunit/mocks/content/DummyContentForTesting.php
new file mode 100644 (file)
index 0000000..0c69027
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+class DummyContentForTesting extends AbstractContent {
+
+       public function __construct( $data ) {
+               parent::__construct( "testing" );
+
+               $this->data = $data;
+       }
+
+       public function serialize( $format = null ) {
+               return serialize( $this->data );
+       }
+
+       /**
+        * @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 function getTextForSearchIndex() {
+               return '';
+       }
+
+       /**
+        * @return string|bool The wikitext to include when another page includes this  content,
+        *  or false if the content is not includable in a wikitext page.
+        */
+       public function getWikitextForTransclusion() {
+               return false;
+       }
+
+       /**
+        * 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 function getTextForSummary( $maxlength = 250 ) {
+               return '';
+       }
+
+       /**
+        * Returns native represenation of the data. Interpretation depends on the data model used,
+        * as given by getDataModel().
+        *
+        * @return mixed The native representation of the content. Could be a string, a nested array
+        *  structure, an object, a binary blob... anything, really.
+        */
+       public function getNativeData() {
+               return $this->data;
+       }
+
+       /**
+        * returns the content's nominal size in bogo-bytes.
+        *
+        * @return int
+        */
+       public function getSize() {
+               return strlen( $this->data );
+       }
+
+       /**
+        * Return a copy of this Content object. The following must be true for the object returned
+        * if $copy = $original->copy()
+        *
+        * * get_class($original) === get_class($copy)
+        * * $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.
+        *
+        * @return Content A copy of this object
+        */
+       public function copy() {
+               return $this;
+       }
+
+       /**
+        * 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).
+        *
+        * @param bool $hasLinks If it is known whether this content contains links,
+        * provide this information here, to avoid redundant parsing to find out.
+        * @return bool
+        */
+       public function isCountable( $hasLinks = null ) {
+               return false;
+       }
+
+       /**
+        * @param Title $title
+        * @param int $revId Unused.
+        * @param null|ParserOptions $options
+        * @param bool $generateHtml Whether to generate Html (default: true). If false, the result
+        *  of calling getText() on the ParserOutput object returned by this method is undefined.
+        *
+        * @return ParserOutput
+        */
+       public function getParserOutput( Title $title, $revId = null,
+               ParserOptions $options = null, $generateHtml = true
+       ) {
+               return new ParserOutput( $this->getNativeData() );
+       }
+
+       /**
+        * @see AbstractContent::fillParserOutput()
+        *
+        * @param Title $title Context title for parsing
+        * @param int|null $revId Revision ID (for {{REVISIONID}})
+        * @param ParserOptions $options Parser options
+        * @param bool $generateHtml Whether or not to generate HTML
+        * @param ParserOutput &$output The output object to fill (reference).
+        */
+       protected function fillParserOutput( Title $title, $revId,
+                       ParserOptions $options, $generateHtml, ParserOutput &$output ) {
+               $output = new ParserOutput( $this->getNativeData() );
+       }
+}
diff --git a/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php b/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php
new file mode 100644 (file)
index 0000000..fd253f2
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+
+class DummyContentHandlerForTesting extends ContentHandler {
+
+       public function __construct( $dataModel ) {
+               parent::__construct( $dataModel, array( "testing" ) );
+       }
+
+       /**
+        * @see ContentHandler::serializeContent
+        *
+        * @param Content $content
+        * @param string $format
+        *
+        * @return string
+        */
+       public function serializeContent( Content $content, $format = null ) {
+               return $content->serialize();
+       }
+
+       /**
+        * @see ContentHandler::unserializeContent
+        *
+        * @param string $blob
+        * @param string $format Unused.
+        *
+        * @return Content
+        */
+       public function unserializeContent( $blob, $format = null ) {
+               $d = unserialize( $blob );
+
+               return new DummyContentForTesting( $d );
+       }
+
+       /**
+        * Creates an empty Content object of the type supported by this ContentHandler.
+        *
+        */
+       public function makeEmptyContent() {
+               return new DummyContentForTesting( '' );
+       }
+}
diff --git a/tests/phpunit/mocks/content/DummyNonTextContent.php b/tests/phpunit/mocks/content/DummyNonTextContent.php
new file mode 100644 (file)
index 0000000..889efb7
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+
+class DummyNonTextContent extends AbstractContent {
+
+       public function __construct( $data ) {
+               parent::__construct( "testing-nontext" );
+
+               $this->data = $data;
+       }
+
+       public function serialize( $format = null ) {
+               return serialize( $this->data );
+       }
+
+       /**
+        * @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 function getTextForSearchIndex() {
+               return '';
+       }
+
+       /**
+        * @return string|bool The wikitext to include when another page includes this  content,
+        *  or false if the content is not includable in a wikitext page.
+        */
+       public function getWikitextForTransclusion() {
+               return false;
+       }
+
+       /**
+        * 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 function getTextForSummary( $maxlength = 250 ) {
+               return '';
+       }
+
+       /**
+        * Returns native represenation of the data. Interpretation depends on the data model used,
+        * as given by getDataModel().
+        *
+        * @return mixed The native representation of the content. Could be a string, a nested array
+        *  structure, an object, a binary blob... anything, really.
+        */
+       public function getNativeData() {
+               return $this->data;
+       }
+
+       /**
+        * returns the content's nominal size in bogo-bytes.
+        *
+        * @return int
+        */
+       public function getSize() {
+               return strlen( $this->data );
+       }
+
+       /**
+        * Return a copy of this Content object. The following must be true for the object returned
+        * if $copy = $original->copy()
+        *
+        * * get_class($original) === get_class($copy)
+        * * $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.
+        *
+        * @return Content A copy of this object
+        */
+       public function copy() {
+               return $this;
+       }
+
+       /**
+        * 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).
+        *
+        * @param bool $hasLinks If it is known whether this content contains links,
+        * provide this information here, to avoid redundant parsing to find out.
+        * @return bool
+        */
+       public function isCountable( $hasLinks = null ) {
+               return false;
+       }
+
+       /**
+        * @param Title $title
+        * @param int $revId Unused.
+        * @param null|ParserOptions $options
+        * @param bool $generateHtml Whether to generate Html (default: true). If false, the result
+        *  of calling getText() on the ParserOutput object returned by this method is undefined.
+        *
+        * @return ParserOutput
+        */
+       public function getParserOutput( Title $title, $revId = null,
+               ParserOptions $options = null, $generateHtml = true
+       ) {
+               return new ParserOutput( $this->getNativeData() );
+       }
+
+       /**
+        * @see AbstractContent::fillParserOutput()
+        *
+        * @param Title $title Context title for parsing
+        * @param int|null $revId Revision ID (for {{REVISIONID}})
+        * @param ParserOptions $options Parser options
+        * @param bool $generateHtml Whether or not to generate HTML
+        * @param ParserOutput &$output The output object to fill (reference).
+        */
+       protected function fillParserOutput( Title $title, $revId,
+                       ParserOptions $options, $generateHtml, ParserOutput &$output ) {
+               $output = new ParserOutput( $this->getNativeData() );
+       }
+}
diff --git a/tests/phpunit/mocks/content/DummyNonTextContentHandler.php b/tests/phpunit/mocks/content/DummyNonTextContentHandler.php
new file mode 100644 (file)
index 0000000..6995ae7
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+class DummyNonTextContentHandler extends DummyContentHandlerForTesting {
+
+       public function __construct( $dataModel ) {
+               parent::__construct( $dataModel, array( "testing-nontext" ) );
+       }
+
+       /**
+        * @see ContentHandler::serializeContent
+        *
+        * @param Content $content
+        * @param string $format
+        *
+        * @return string
+        */
+       public function serializeContent( Content $content, $format = null ) {
+               return $content->serialize();
+       }
+
+       /**
+        * @see ContentHandler::unserializeContent
+        *
+        * @param string $blob
+        * @param string $format Unused.
+        *
+        * @return Content
+        */
+       public function unserializeContent( $blob, $format = null ) {
+               $d = unserialize( $blob );
+
+               return new DummyNonTextContent( $d );
+       }
+
+       /**
+        * Creates an empty Content object of the type supported by this ContentHandler.
+        */
+       public function makeEmptyContent() {
+               return new DummyNonTextContent( '' );
+       }
+
+       public function supportsDirectApiEditing() {
+               return true;
+       }
+
+}