From 2513086ec16150431cc9a62aaf6f09711f838c46 Mon Sep 17 00:00:00 2001 From: aude Date: Wed, 15 Apr 2015 10:26:22 +0200 Subject: [PATCH] Add supportsDirectEditing methods to ContentHandler This adds supportsDirectApiEditing and supportsDirectEditing methods to ContentHandler. Both return false by default for the ContentHandler base class, and true for TextContentHandler and it's derivatives. (everything in core) Extension content types that directly extend AbstractContent / ContentHandler, often / generally don't support direct editing. EntityContent in Wikibase and Flow boards are the two such content types currently in gerrit-hosted extensions. The use and direct settings of the allowNonTextContent member variable is replaced by enableApiEditOverride and a setter for that. The only place allowNonTextContent is used in all of Wikimedia-hosted git repos is core itself (EditPage and ApiEditPage), so should be safe to make this change. With this change, Wikibase can remove its ApiCheckCanExecute hook handler that disallows editing there, and MobileFrontend could check if direct editing is allowed before enabling it's editing features, instead of Wikibase having to add MobileFrontend hook handlers to disable the features. Bug: T96382 Change-Id: I276cd6ecedf38108f1f2be16b38e699e8c5d2d0c --- RELEASE-NOTES-1.26 | 7 + includes/EditPage.php | 22 ++- includes/api/ApiEditPage.php | 12 +- includes/content/ContentHandler.php | 18 ++ includes/content/TextContentHandler.php | 9 + tests/TestsAutoLoader.php | 6 +- tests/phpunit/includes/EditPageTest.php | 55 ++++++ .../phpunit/includes/api/ApiEditPageTest.php | 73 +++++--- .../includes/content/ContentHandlerTest.php | 166 +----------------- .../content/TextContentHandlerTest.php | 13 ++ .../content/WikitextContentHandlerTest.php | 5 + .../mocks/content/DummyContentForTesting.php | 121 +++++++++++++ .../content/DummyContentHandlerForTesting.php | 42 +++++ .../mocks/content/DummyNonTextContent.php | 121 +++++++++++++ .../content/DummyNonTextContentHandler.php | 46 +++++ 15 files changed, 516 insertions(+), 200 deletions(-) create mode 100644 tests/phpunit/includes/content/TextContentHandlerTest.php create mode 100644 tests/phpunit/mocks/content/DummyContentForTesting.php create mode 100644 tests/phpunit/mocks/content/DummyContentHandlerForTesting.php create mode 100644 tests/phpunit/mocks/content/DummyNonTextContent.php create mode 100644 tests/phpunit/mocks/content/DummyNonTextContentHandler.php diff --git a/RELEASE-NOTES-1.26 b/RELEASE-NOTES-1.26 index bf9341db08..eddfe30895 100644 --- a/RELEASE-NOTES-1.26 +++ b/RELEASE-NOTES-1.26 @@ -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 == diff --git a/includes/EditPage.php b/includes/EditPage.php index 8d27eac833..1679c3b5b7 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -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() { diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index 56c67e0e6e..601c64cd26 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -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; diff --git a/includes/content/ContentHandler.php b/includes/content/ContentHandler.php index 371b267e96..9c2435a306 100644 --- a/includes/content/ContentHandler.php +++ b/includes/content/ContentHandler.php @@ -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. diff --git a/includes/content/TextContentHandler.php b/includes/content/TextContentHandler.php index ffe1acbd20..f5e87830ca 100644 --- a/includes/content/TextContentHandler.php +++ b/includes/content/TextContentHandler.php @@ -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; + } + } diff --git a/tests/TestsAutoLoader.php b/tests/TestsAutoLoader.php index def23dad9c..1a2e0cb7f4 100644 --- a/tests/TestsAutoLoader.php +++ b/tests/TestsAutoLoader.php @@ -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", diff --git a/tests/phpunit/includes/EditPageTest.php b/tests/phpunit/includes/EditPageTest.php index 15778e40fa..27959b1d58 100644 --- a/tests/phpunit/includes/EditPageTest.php +++ b/tests/phpunit/includes/EditPageTest.php @@ -11,6 +11,28 @@ */ 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 ); + } + } diff --git a/tests/phpunit/includes/api/ApiEditPageTest.php b/tests/phpunit/includes/api/ApiEditPageTest.php index 3179a45228..865f1c22da 100644 --- a/tests/phpunit/includes/api/ApiEditPageTest.php +++ b/tests/phpunit/includes/api/ApiEditPageTest.php @@ -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() ); + } } diff --git a/tests/phpunit/includes/content/ContentHandlerTest.php b/tests/phpunit/includes/content/ContentHandlerTest.php index 988a59ee5a..63743eb7a5 100644 --- a/tests/phpunit/includes/content/ContentHandlerTest.php +++ b/tests/phpunit/includes/content/ContentHandlerTest.php @@ -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 index 0000000000..33861f11e3 --- /dev/null +++ b/tests/phpunit/includes/content/TextContentHandlerTest.php @@ -0,0 +1,13 @@ +assertTrue( $handler->supportsDirectEditing(), 'direct editing is supported' ); + } + +} diff --git a/tests/phpunit/includes/content/WikitextContentHandlerTest.php b/tests/phpunit/includes/content/WikitextContentHandlerTest.php index 38fb57330c..361238b709 100644 --- a/tests/phpunit/includes/content/WikitextContentHandlerTest.php +++ b/tests/phpunit/includes/content/WikitextContentHandlerTest.php @@ -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 index 0000000000..0c69027d41 --- /dev/null +++ b/tests/phpunit/mocks/content/DummyContentForTesting.php @@ -0,0 +1,121 @@ +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 index 0000000000..fd253f2123 --- /dev/null +++ b/tests/phpunit/mocks/content/DummyContentHandlerForTesting.php @@ -0,0 +1,42 @@ +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 index 0000000000..889efb716c --- /dev/null +++ b/tests/phpunit/mocks/content/DummyNonTextContent.php @@ -0,0 +1,121 @@ +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 index 0000000000..6995ae78ca --- /dev/null +++ b/tests/phpunit/mocks/content/DummyNonTextContentHandler.php @@ -0,0 +1,46 @@ +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; + } + +} -- 2.20.1