* (T198214) The 'disabletidy' parameter to action=parse has been
deprecated; untidy output will not be supported by future wikitext
parsers.
+* Added intestactionsdetail to action=query&prop=info to allow retrieving the
+ reasons an action is not allowed.
+* Deprecated action=query&prop=info inprop=readable in favor of
+ intestactions=read.
=== Action API internal changes in 1.32 ===
* Added 'ApiParseMakeOutputPage' hook.
* ApiUsageException::getCodeString() (deprecated in 1.29)
* ApiUsageException::getMessageArray() (deprecated in 1.29)
* Class UsageException, deprecated in 1.29, has been removed.
+* ApiErrorFormatter: Added getFormat() and newWithFormat(). In particular, you
+ can now easily test $formatter->getFormat() === 'bc', and then call
+ $formatter->newWithFormat( 'plaintext' ) to get a non-BC formatter.
=== Languages updated in 1.32 ===
MediaWiki supports over 350 languages. Many localisations are updated regularly.
* The $wgUseKeyHeader configuration option and the
OutputPage::getKeyHeader() method have been deprecated; the relevant
draft IETF spec expired without becoming a standard.
+* Deprecated API action=query&prop=info inprop=readable in favor of
+ intestactions=read.
=== Other changes in 1.32 ===
* (T198811) The following tables have had their UNIQUE indexes turned into
$this->format = $format;
}
+ /**
+ * Return a formatter like this one but with a different format
+ *
+ * @since 1.32
+ * @param string $format New format.
+ * @return ApiErrorFormatter
+ */
+ public function newWithFormat( $format ) {
+ return new self( $this->result, $this->lang, $format, $this->useDB );
+ }
+
+ /**
+ * Fetch the format for this formatter
+ * @since 1.32
+ * @return string
+ */
+ public function getFormat() {
+ return $this->format;
+ }
+
/**
* Fetch the Language for this formatter
* @since 1.29
parent::__construct( $result, Language::factory( 'en' ), 'none', false );
}
+ public function getFormat() {
+ return 'bc';
+ }
+
public function arrayFromStatus( StatusValue $status, $type = 'error', $format = null ) {
if ( $status->isGood() || !$status->getErrors() ) {
return [];
return null; // force a continuation
}
+ $detailLevel = $this->params['testactionsdetail'];
+ $rigor = $detailLevel === 'quick' ? 'quick' : 'secure';
+ $errorFormatter = $this->getErrorFormatter();
+ if ( $errorFormatter->getFormat() === 'bc' ) {
+ // Eew, no. Use a more modern format here.
+ $errorFormatter = $errorFormatter->newWithFormat( 'plaintext' );
+ }
+
$user = $this->getUser();
$pageInfo['actions'] = [];
foreach ( $this->params['testactions'] as $action ) {
$this->countTestedActions++;
- $pageInfo['actions'][$action] = $title->userCan( $action, $user );
+
+ if ( $detailLevel === 'boolean' ) {
+ $pageInfo['actions'][$action] = $title->userCan( $action, $user );
+ } else {
+ $pageInfo['actions'][$action] = $errorFormatter->arrayFromStatus( $this->errorArrayToStatus(
+ $title->getUserPermissionsErrors( $action, $user, $rigor ),
+ $user
+ ) );
+ }
}
}
// need to be added to getCacheMode()
],
ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
+ ApiBase::PARAM_DEPRECATED_VALUES => [
+ 'readable' => true, // Since 1.32
+ ],
],
'testactions' => [
ApiBase::PARAM_TYPE => 'string',
ApiBase::PARAM_ISMULTI => true,
],
+ 'testactionsdetail' => [
+ ApiBase::PARAM_TYPE => [ 'boolean', 'full', 'quick' ],
+ ApiBase::PARAM_DFLT => 'boolean',
+ ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
+ ],
'token' => [
ApiBase::PARAM_DEPRECATED => true,
ApiBase::PARAM_ISMULTI => true,
"apihelp-query+info-paramvalue-prop-notificationtimestamp": "The watchlist notification timestamp of each page.",
"apihelp-query+info-paramvalue-prop-subjectid": "The page ID of the parent page for each talk page.",
"apihelp-query+info-paramvalue-prop-url": "Gives a full URL, an edit URL, and the canonical URL for each page.",
- "apihelp-query+info-paramvalue-prop-readable": "Whether the user can read this page.",
+ "apihelp-query+info-paramvalue-prop-readable": "Whether the user can read this page. Use <kbd>intestactions=read</kbd> instead.",
"apihelp-query+info-paramvalue-prop-preload": "Gives the text returned by EditFormPreloadText.",
"apihelp-query+info-paramvalue-prop-displaytitle": "Gives the manner in which the page title is actually displayed.",
"apihelp-query+info-paramvalue-prop-varianttitles": "Gives the display title in all variants of the site content language.",
"apihelp-query+info-param-testactions": "Test whether the current user can perform certain actions on the page.",
+ "apihelp-query+info-param-testactionsdetail": "Detail level for <var>$1testactions</var>. Use the [[Special:ApiHelp/main|main module]]'s <var>errorformat</var> and <var>errorlang</var> parameters to control the format of the messages returned.",
+ "apihelp-query+info-paramvalue-testactionsdetail-boolean": "Return a boolean value for each action.",
+ "apihelp-query+info-paramvalue-testactionsdetail-full": "Return messages describing why the action is disallowed, or an empty array if it is allowed.",
+ "apihelp-query+info-paramvalue-testactionsdetail-quick": "Like <kbd>full</kbd> but skipping expensive checks.",
"apihelp-query+info-param-token": "Use [[Special:ApiHelp/query+tokens|action=query&meta=tokens]] instead.",
"apihelp-query+info-example-simple": "Get information about the page <kbd>Main Page</kbd>.",
"apihelp-query+info-example-protection": "Get general and protection information about the page <kbd>Main Page</kbd>.",
"apihelp-query+info-paramvalue-prop-displaytitle": "{{doc-apihelp-paramvalue|query+info|prop|displaytitle}}",
"apihelp-query+info-paramvalue-prop-varianttitles": "{{doc-apihelp-paramvalue|query+info|prop|varianttitles}}",
"apihelp-query+info-param-testactions": "{{doc-apihelp-param|query+info|testactions}}",
+ "apihelp-query+info-param-testactionsdetail": "{{doc-apihelp-param|query+info|testactionsdetail}}",
+ "apihelp-query+info-paramvalue-testactionsdetail-boolean": "{{doc-apihelp-paramvalue|query+info|testactionsdetail|boolean}}",
+ "apihelp-query+info-paramvalue-testactionsdetail-full": "{{doc-apihelp-paramvalue|query+info|testactionsdetail|full}}",
+ "apihelp-query+info-paramvalue-testactionsdetail-quick": "{{doc-apihelp-paramvalue|query+info|testactionsdetail|quick}}",
"apihelp-query+info-param-token": "{{doc-apihelp-param|query+info|token}}",
"apihelp-query+info-example-simple": "{{doc-apihelp-example|query+info}}",
"apihelp-query+info-example-protection": "{{doc-apihelp-example|query+info}}",
$result = new ApiResult( 8388608 );
$formatter = new ApiErrorFormatter( $result, Language::factory( 'de' ), 'wikitext', false );
$this->assertSame( 'de', $formatter->getLanguage()->getCode() );
+ $this->assertSame( 'wikitext', $formatter->getFormat() );
$formatter->addMessagesFromStatus( null, Status::newGood() );
$this->assertSame(
);
}
+ /**
+ * @covers ApiErrorFormatter
+ * @covers ApiErrorFormatter_BackCompat
+ */
+ public function testNewWithFormat() {
+ $result = new ApiResult( 8388608 );
+ $formatter = new ApiErrorFormatter( $result, Language::factory( 'de' ), 'wikitext', false );
+ $formatter2 = $formatter->newWithFormat( 'html' );
+
+ $this->assertSame( $formatter->getLanguage(), $formatter2->getLanguage() );
+ $this->assertSame( 'html', $formatter2->getFormat() );
+
+ $formatter3 = new ApiErrorFormatter_BackCompat( $result );
+ $formatter4 = $formatter3->newWithFormat( 'html' );
+ $this->assertNotInstanceOf( ApiErrorFormatter_BackCompat::class, $formatter4 );
+ $this->assertSame( $formatter3->getLanguage(), $formatter4->getLanguage() );
+ $this->assertSame( 'html', $formatter4->getFormat() );
+ }
+
/**
* @covers ApiErrorFormatter
* @dataProvider provideErrorFormatter
$formatter = new ApiErrorFormatter_BackCompat( $result );
$this->assertSame( 'en', $formatter->getLanguage()->getCode() );
+ $this->assertSame( 'bc', $formatter->getFormat() );
$this->assertSame( [], $formatter->arrayFromStatus( Status::newGood() ) );
--- /dev/null
+<?php
+
+/**
+ * @group API
+ * @group medium
+ * @group Database
+ *
+ * @coversDefaultClass ApiQueryInfo
+ */
+class ApiQueryInfoTest extends ApiTestCase {
+
+ /**
+ * @covers ::execute
+ * @covers ::extractPageInfo
+ */
+ public function testExecute() {
+ $page = $this->getExistingTestPage( 'Pluto' );
+ $title = $page->getTitle();
+
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'prop' => 'info',
+ 'titles' => $title->getText(),
+ ] );
+
+ $this->assertArrayHasKey( 'query', $data );
+ $this->assertArrayHasKey( 'pages', $data['query'] );
+ $this->assertArrayHasKey( $page->getId(), $data['query']['pages'] );
+
+ $info = $data['query']['pages'][$page->getId()];
+ $this->assertSame( $page->getId(), $info['pageid'] );
+ $this->assertSame( $title->getNamespace(), $info['ns'] );
+ $this->assertSame( $title->getText(), $info['title'] );
+ $this->assertSame( $title->getContentModel(), $info['contentmodel'] );
+ $this->assertSame( $title->getPageLanguage()->getCode(), $info['pagelanguage'] );
+ $this->assertSame( $title->getPageLanguage()->getHtmlCode(), $info['pagelanguagehtmlcode'] );
+ $this->assertSame( $title->getPageLanguage()->getDir(), $info['pagelanguagedir'] );
+ $this->assertSame( wfTimestamp( TS_ISO_8601, $title->getTouched() ), $info['touched'] );
+ $this->assertSame( $title->getLatestRevID(), $info['lastrevid'] );
+ $this->assertSame( $title->getLength(), $info['length'] );
+ $this->assertSame( $title->isNewPage(), $info['new'] );
+ $this->assertArrayNotHasKey( 'actions', $info );
+ }
+
+ /**
+ * @covers ::execute
+ * @covers ::extractPageInfo
+ */
+ public function testExecuteEditActions() {
+ $page = $this->getExistingTestPage( 'Pluto' );
+ $title = $page->getTitle();
+
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'prop' => 'info',
+ 'titles' => $title->getText(),
+ 'intestactions' => 'edit'
+ ] );
+
+ $this->assertArrayHasKey( 'query', $data );
+ $this->assertArrayHasKey( 'pages', $data['query'] );
+ $this->assertArrayHasKey( $page->getId(), $data['query']['pages'] );
+
+ $info = $data['query']['pages'][$page->getId()];
+ $this->assertArrayHasKey( 'actions', $info );
+ $this->assertArrayHasKey( 'edit', $info['actions'] );
+ $this->assertTrue( $info['actions']['edit'] );
+ }
+
+ /**
+ * @covers ::execute
+ * @covers ::extractPageInfo
+ */
+ public function testExecuteEditActionsFull() {
+ $page = $this->getExistingTestPage( 'Pluto' );
+ $title = $page->getTitle();
+
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'prop' => 'info',
+ 'titles' => $title->getText(),
+ 'intestactions' => 'edit',
+ 'intestactionsdetail' => 'full',
+ ] );
+
+ $this->assertArrayHasKey( 'query', $data );
+ $this->assertArrayHasKey( 'pages', $data['query'] );
+ $this->assertArrayHasKey( $page->getId(), $data['query']['pages'] );
+
+ $info = $data['query']['pages'][$page->getId()];
+ $this->assertArrayHasKey( 'actions', $info );
+ $this->assertArrayHasKey( 'edit', $info['actions'] );
+ $this->assertInternalType( 'array', $info['actions']['edit'] );
+ $this->assertSame( [], $info['actions']['edit'] );
+ }
+
+ /**
+ * @covers ::execute
+ * @covers ::extractPageInfo
+ */
+ public function testExecuteEditActionsFullBlock() {
+ $badActor = $this->getTestUser()->getUser();
+ $sysop = $this->getTestSysop()->getUser();
+
+ $block = new \Block( [
+ 'address' => $badActor->getName(),
+ 'user' => $badActor->getId(),
+ 'by' => $sysop->getId(),
+ 'expiry' => 'infinity',
+ 'sitewide' => 0,
+ 'enableAutoblock' => true,
+ ] );
+
+ $block->insert();
+
+ $page = $this->getExistingTestPage( 'Pluto' );
+ $title = $page->getTitle();
+
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'prop' => 'info',
+ 'titles' => $title->getText(),
+ 'intestactions' => 'edit',
+ 'intestactionsdetail' => 'full',
+ ], null, false, $badActor );
+
+ $block->delete();
+
+ $this->assertArrayHasKey( 'query', $data );
+ $this->assertArrayHasKey( 'pages', $data['query'] );
+ $this->assertArrayHasKey( $page->getId(), $data['query']['pages'] );
+
+ $info = $data['query']['pages'][$page->getId()];
+ $this->assertArrayHasKey( 'actions', $info );
+ $this->assertArrayHasKey( 'edit', $info['actions'] );
+ $this->assertInternalType( 'array', $info['actions']['edit'] );
+ $this->assertArrayHasKey( 0, $info['actions']['edit'] );
+ $this->assertArrayHasKey( 'code', $info['actions']['edit'][0] );
+ $this->assertSame( 'blocked', $info['actions']['edit'][0]['code'] );
+ $this->assertArrayHasKey( 'data', $info['actions']['edit'][0] );
+ $this->assertArrayHasKey( 'blockinfo', $info['actions']['edit'][0]['data'] );
+ $this->assertArrayHasKey( 'blockid', $info['actions']['edit'][0]['data']['blockinfo'] );
+ $this->assertSame( $block->getId(), $info['actions']['edit'][0]['data']['blockinfo']['blockid'] );
+ }
+
+}