jenkins-bot has submitted this change and it was merged. Change subject: Implements prop=listmembership ......................................................................
Implements prop=listmembership Usage examples: Watchlist (lsmid=0 or missing) * http://localhost:8080/w/api.php?action=query&prop=listmembership&titles=Main_Page&lsmid=0 Specific list, e.g. #7 * http://localhost:8080/w/api.php?action=query&prop=listmembership&titles=Main_Page&lsmid=7 Obviously because this is a prop, generators can be used: * http://localhost:8080/w/api.php?action=query&prop=listmembership&generator=allpages&lsmid=0 Bug: T95516 Change-Id: I90b6df0c7a9d69460921e9410284c312b3d09d7b --- M Gather.php M i18n/en.json M i18n/qqq.json A includes/api/ApiMixinListAccess.php A includes/api/ApiQueryListMembership.php M includes/api/ApiQueryListPages.php M tests/phpunit/api/GatherTests.php 7 files changed, 357 insertions(+), 66 deletions(-) Approvals: Yurik: Looks good to me, approved jenkins-bot: Verified diff --git a/Gather.php b/Gather.php index 28eb272..3eff5f6 100644 --- a/Gather.php +++ b/Gather.php @@ -67,8 +67,10 @@ 'Gather\SpecialGatherLists' => 'specials/SpecialGatherLists', 'Gather\SpecialGatherEditFeed' => 'specials/SpecialGatherEditFeed', + 'Gather\api\ApiMixinListAccess' => 'api/ApiMixinListAccess', 'Gather\api\ApiEditList' => 'api/ApiEditList', 'Gather\api\ApiQueryLists' => 'api/ApiQueryLists', + 'Gather\api\ApiQueryListMembership' => 'api/ApiQueryListMembership', 'Gather\api\ApiQueryListPages' => 'api/ApiQueryListPages', ); @@ -104,6 +106,7 @@ // Api $wgAPIModules['editlist'] = 'Gather\api\ApiEditList'; $wgAPIListModules['lists'] = 'Gather\api\ApiQueryLists'; +$wgAPIPropModules['listmembership'] = 'Gather\api\ApiQueryListMembership'; $wgAPIListModules['listpages'] = 'Gather\api\ApiQueryListPages'; // Configuration diff --git a/i18n/en.json b/i18n/en.json index d98088b..165a5d5 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -116,5 +116,10 @@ "gather-collection-more": "View more pages in this collection", "gather-add-to-another": "Show my other collections", "gather-watchstar-button-label": "Add to collection", - "gather-menu-guider": "Tap on the menu icon to take a look at your new collection." + "gather-menu-guider": "Tap on the menu icon to take a look at your new collection.", + "gather-api-help-param-listid": "List id; omit or set to 0 to use the watchlist.", + "gather-api-help-param-listowner": "User ID of the list owner. Only makes sense for watchlists.", + "gather-api-help-param-listtoken": "Watchlist token (see watchlist API); required when looking at someone else's watchlist.", + "apihelp-query+listmembership-description": "Returns which of a set of pages are in a given list.", + "apihelp-query+listmembership-example-1": "Check whether \"Page\" is on the watchlist." } diff --git a/i18n/qqq.json b/i18n/qqq.json index 4e888b0..4bf22cc 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -118,5 +118,10 @@ "gather-collection-more": "Label for link at bottom of Gather collection when there are more than 50 items.", "gather-add-to-another": "Label for button at bottom of collection overlay for when you have more collections that you could potentially add to.", "gather-watchstar-button-label": "A label for the button to add to a collection.", - "gather-menu-guider": "Message that shows up in an overlay pointing at the menu button." + "gather-menu-guider": "Message that shows up in an overlay pointing at the menu button.", + "gather-api-help-param-listid": "{{doc-apihelp-param|lsm|id|listmembership|query+listmembership}}", + "gather-api-help-param-listowner": "{{doc-apihelp-param|lsm|owner|listmembership|query+listmembership}}", + "gather-api-help-param-listtoken": "{{doc-apihelp-param|lsm|token|listmembership|query+listmembership}}", + "apihelp-query+listmembership-description": "{{doc-apihelp-description|lsm|listmembership|query+listmembership}}", + "apihelp-query+listmembership-example-1": "{{doc-apihelp-example|lsm|listmembership|query+listmembership}}" } diff --git a/includes/api/ApiMixinListAccess.php b/includes/api/ApiMixinListAccess.php new file mode 100644 index 0000000..ddea2dc --- /dev/null +++ b/includes/api/ApiMixinListAccess.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © 2015 Yuri Astrakhan "<Firstname><Lastname>@gmail.com", + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +namespace Gather\api; + +use ApiBase; +use DatabaseBase; + +/** + * Shared code for List's API + * + * @ingroup API + */ +class ApiMixinListAccess { + + /** + * Get parameters used to identify a list with ownership + * @return array + */ + public static function getListAccessParams() { + return array( + 'id' => array( + ApiBase::PARAM_HELP_MSG => 'gather-api-help-param-listid', + ApiBase::PARAM_DFLT => 0, + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_MIN => 0, + ), + 'owner' => array( + ApiBase::PARAM_HELP_MSG => 'gather-api-help-param-listowner', + ApiBase::PARAM_TYPE => 'user', + ), + 'token' => array( + ApiBase::PARAM_HELP_MSG => 'gather-api-help-param-listtoken', + ApiBase::PARAM_TYPE => 'string', + ), + ); + } + + /** + * Ensure that current params make sense, specify an existing list, and the requesting user has + * access to it. Die if that's not the case. + * @param DatabaseBase $db + * @param ApiBase $module + * @param array $params module parameters + * @param bool $isWatchlist Will be set to true if the requested list is a watchlist + * @param int $ownerId Will be set to the user ID of the list's owner + * @throws \UsageException In case access is not allowed + */ + public static function checkListAccess( + DatabaseBase $db, ApiBase $module, array $params, &$isWatchlist, &$ownerId + ) { + if ( is_null( $params['owner'] ) !== is_null( $params['token'] ) ) { + $p = $module->getModulePrefix(); + $module->dieUsage( "Both {$p}owner and {$p}token must be given or missing", + 'invalidparammix' ); + } + + if ( !$params['id'] ) { + // If collection id is not given (or equals to 0), this is a watchlist access; + // ApiBase::getWatchlistUser does all the necessary checks + $isWatchlist = true; + $ownerId = $module->getWatchlistUser( $params )->getId(); + return; + } + + // Id was given, this could be public or private list, legacy watchlist or regular + // Allow access to any public list/watchlist, and to private with proper owner/self + $listRow = $db->selectRow( 'gather_list', + array( 'gl_label', 'gl_user', 'gl_perm' ), + array( 'gl_id' => $params['id'] ), + __METHOD__ ); + if ( $listRow === false ) { + $module->dieUsage( 'List does not exist', 'badid' ); + } + + $listRow = ApiEditList::normalizeRow( $listRow ); + if ( $params['owner'] !== null ) { + // Caller supplied token: treat them as trusted, someone who could see even private + // At the same time, owner param must match list's owner + // TODO: if we allow non-matching owner, we could treat it as public-only, + // but that might be unexpected behavior + $user = $module->getWatchlistUser( $params ); + if ( $listRow->gl_user !== $user->getId() ) { + $module->dieUsage( 'The owner supplied does not match the list\'s owner', + 'permissiondenied' ); + } + $showPrivate = true; + } else { + $user = $module->getUser(); + $showPrivate = + $user->isLoggedIn() && $listRow->gl_user === $user->getId() && + $user->isAllowed( 'viewmywatchlist' ); + } + + // Check if this is a public list (if required) + if ( !$showPrivate && $listRow->gl_perm !== ApiEditList::PERM_PUBLIC ) { + $module->dieUsage( 'You have no rights to see this list', 'badid' ); + } + + // If true, this is actually a watchlist, and it is either public or belongs to current user + $isWatchlist = $listRow->gl_label === ''; + $ownerId = $listRow->gl_user; + } +} diff --git a/includes/api/ApiQueryListMembership.php b/includes/api/ApiQueryListMembership.php new file mode 100644 index 0000000..f26fcd8 --- /dev/null +++ b/includes/api/ApiQueryListMembership.php @@ -0,0 +1,137 @@ +<?php +/** + * + * Copyright © 2015 Yuri Astrakhan "<Firstname><Lastname>@gmail.com", + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +namespace Gather\api; + +use ApiQueryBase; +use ApiQuery; +use LinkBatch; +use ApiBase; + +/** + * A query prop module to show if pages belong to a specific list + * + * @ingroup API + */ +class ApiQueryListMembership extends ApiQueryBase { + + public function __construct( ApiQuery $query, $moduleName ) { + parent::__construct( $query, $moduleName, 'lsm' ); + } + + public function execute() { + $titles = $this->getPageSet()->getGoodAndMissingTitles(); + $titleLookup = $this->getPageSet()->getGoodAndMissingTitlesByNamespace(); + + if ( !count( $titles ) ) { + # Nothing to do + return; + } + + $params = $this->extractRequestParams(); + $db = $this->getDB(); + + // watchlist and gather_list_item tables are very similar, and have one identifying value - + // UserID for watchlists, and ListID for lists. CheckListAccess will tell us which table we + // should use. If userId is returned, use watchlist, otherwise the $params['id'] is valid, + // and should be used for the gather_list_item. + ApiMixinListAccess::checkListAccess( $db, $this, $params, $isWatchlist, $ownerId ); + + if ( $isWatchlist ) { + $prefix = 'wl'; + $db = $this->selectNamedDB( 'watchlist', DB_SLAVE, 'watchlist' ); + $this->addTables( 'watchlist' ); + $this->addWhereFld( 'wl_user', $ownerId ); + } else { + $prefix = 'gli'; + $this->addTables( 'gather_list_item' ); + $this->addWhereFld( 'gli_gl_id', $params['id'] ); + } + + $this->addFields( array( 'ns' => "{$prefix}_namespace", 'title' => "{$prefix}_title" ) ); + + $lb = new LinkBatch( $titles ); + $this->addWhere( $lb->constructSet( $prefix, $db ) ); + + if ( $params['continue'] !== null ) { + $cont = explode( '|', $params['continue'] ); + $this->dieContinueUsageIf( count( $cont ) != 2 ); + $contNs = intval( $cont[0] ); + $this->dieContinueUsageIf( strval( $contNs ) !== $cont[0] ); + $contTitle = $db->addQuotes( $cont[1] ); + $this->addWhere( "{$prefix}_namespace > $contNs OR " . + "({$prefix}_namespace = $contNs AND " . "{$prefix}_title >= $contTitle)" ); + } + + // Don't ORDER BY namespace if it's constant in the WHERE clause + if ( count( $titleLookup ) === 1 ) { + $this->addOption( 'ORDER BY', "{$prefix}_title" ); + } else { + $this->addOption( 'ORDER BY', array( "{$prefix}_namespace", "{$prefix}_title" ) ); + } + + // NOTE: We never set listmembership=false because we don't really know which ones are not + // in the database. If we ran out of memory halfway and need to continue, next time we will + // skip those already done, so even though DB contains rows, we skipped them and gotten + // the next batch. In other words, the pages that at the end of this module do not have + // listmembership=true might still be true, but they were reported in the previous API call. + + $result = $this->getResult(); + foreach ( $this->select( __METHOD__ ) as $row ) { + $ns = intval( $row->ns ); + if ( !isset( $titleLookup[$ns][$row->title] ) ) { + wfDebug( __METHOD__ . " Unexpected DB row {$row->ns}:{$row->title}\n" ); + continue; + } + $fit = $result->addValue( array( 'query', 'pages', $titleLookup[$ns][$row->title] ), + 'listmembership', true ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'continue', $row->ns . '|' . $row->title ); + break; + } + } + } + + public function getCacheMode( $params ) { + return 'anon-public-user-private'; + } + + public function getAllowedParams() { + return array_merge( ApiMixinListAccess::getListAccessParams(), array( + 'continue' => array( + ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', + ), + ) ); + } + + protected function getExamplesMessages() { + return array( + 'action=query&prop=listmembership&titles=Page&lsmid=0' + => 'apihelp-query+listmembership-example-1', + ); + } + + public function getHelpUrls() { + return '//www.mediawiki.org/wiki/Extension:Gather'; + } +} diff --git a/includes/api/ApiQueryListPages.php b/includes/api/ApiQueryListPages.php index 15ce75e..539afeb 100644 --- a/includes/api/ApiQueryListPages.php +++ b/includes/api/ApiQueryListPages.php @@ -62,60 +62,15 @@ * @throws \UsageException */ private function run( $resultPageSet = null ) { - $params = $this->extractRequestParams(); - $p = $this->getModulePrefix(); - - $useOwner = $params['owner'] !== null; - if ( $useOwner !== ( $params['token'] !== null ) ) { - $this->dieUsage( "Both {$p}owner and {$p}token must be given or missing", - 'invalidparammix' ); - } - $isGenerator = $resultPageSet !== null; - if ( !$params['id'] ) { - // If id is not given (or equals to 0), permissions the same as watchlistraw access - $user = $this->getWatchlistUser( $params ); - $titles = $this->queryLegacyWatchlist( $params, $isGenerator, $user->getId() ); + ApiMixinListAccess::checkListAccess( $this->getDB(), $this, $params, $isWatchlist, $ownerId ); + + if ( $isWatchlist ) { + $titles = $this->queryLegacyWatchlist( $params, $isGenerator, $ownerId ); } else { - // Id was given, this could be public or private list, legacy watchlist or regular - // Allow access to any public list/watchlist, and to private with proper owner/self - $db = $this->getDB(); - $listRow = ApiEditList::normalizeRow( $db->selectRow( 'gather_list', - array( 'gl_label', 'gl_user', 'gl_perm' ), - array( 'gl_id' => $params['id'] ), __METHOD__ ) ); - if ( $listRow === false ) { - $this->dieUsage( "List does not exist", 'badid' ); - } - if ( $useOwner ) { - // Caller supplied token: treat them as trusted, someone who could see even private - // At the same time, owner param must match list's owner - // TODO: if we allow non-matching owner, we could treat it as public-only, - // but that might be unexpected behavior - $user = $this->getWatchlistUser( $params ); - if ( $listRow->gl_user !== $user->getId() ) { - $this->dieUsage( 'The owner supplied does not match the list\'s owner', - 'permissiondenied' ); - } - $showPrivate = true; - } else { - $user = $this->getUser(); - $showPrivate = $user->isLoggedIn() && $listRow->gl_user === $user->getId() - && $user->isAllowed( 'viewmywatchlist' ); - } - - // Check if this is a public list (if required) - if ( !$showPrivate && $listRow->gl_perm !== ApiEditList::PERM_PUBLIC ) { - $this->dieUsage( "You have no rights to see this list", 'badid' ); - } - - if ( $listRow->gl_label === '' ) { - // This is actually a watchlist, and it is either public or belongs to current user - $titles = $this->queryLegacyWatchlist( $params, $isGenerator, $listRow->gl_user ); - } else { - $titles = $this->queryListItems( $params, $isGenerator ); - } + $titles = $this->queryListItems( $params, $isGenerator ); } if ( !$isGenerator ) { $this->getResult()->addIndexedTagName( $this->modulePath, 'wr' ); @@ -244,14 +199,9 @@ } public function getAllowedParams() { - return array( + return array_merge( ApiMixinListAccess::getListAccessParams(), array( 'continue' => array( ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', - ), - 'id' => array( - ApiBase::PARAM_DFLT => 0, - ApiBase::PARAM_TYPE => 'integer', - ApiBase::PARAM_MIN => 0, ), 'namespace' => array( ApiBase::PARAM_ISMULTI => true, @@ -264,12 +214,6 @@ ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2, ), - 'owner' => array( - ApiBase::PARAM_TYPE => 'user', - ), - 'token' => array( - ApiBase::PARAM_TYPE => 'string', - ), 'dir' => array( ApiBase::PARAM_DFLT => 'ascending', ApiBase::PARAM_TYPE => array( @@ -278,7 +222,7 @@ ), ApiBase::PARAM_HELP_MSG => 'api-help-param-direction', ), - ); + ) ); } protected function getExamplesMessages() { diff --git a/tests/phpunit/api/GatherTests.php b/tests/phpunit/api/GatherTests.php index 1d1db40..4d31495 100644 --- a/tests/phpunit/api/GatherTests.php +++ b/tests/phpunit/api/GatherTests.php @@ -175,7 +175,7 @@ foreach ( array( $usr, $usr2 ) as $user ) { $this->assertLists( 'ed-a0', $user, '{}', '[{"id":0, "watchlist":true, "label":"Watchlist"}]' ); - $this->assertPages( 'ed-a1', $user, null, array(), array() ); + $this->assertPages( 'ed-a1', $user, null, array() ); } $this->badUsePage( 'ed-a2', $usr, '"lspid": 9999999' ); @@ -239,10 +239,12 @@ $expListsW2->count = 1; $expPagesW = array( $pageW, $pageTW ); + $expPagesIn = array( 'Gather-ListW' ); $this->assertPages( 'ed-c2', $usr, null, $expPagesW ); $this->assertPages( 'ed-c3', $usr, 0, $expPagesW ); $this->assertLists( 'ed-c4', $usr, 0, $expListsW, $expListsW2 ); + $this->assertIsIn( 'ed-c5', $usr, 0, $expPagesIn ); // // Create Watchlist row @@ -256,10 +258,15 @@ $this->assertPages( 'ed-d2', $usr, 0, $expPagesW ); $this->assertPages( 'ed-d3', $usr, $id0, $expPagesW ); $this->assertLists( 'ed-d4', $usr, 0, $expListsW, $expListsW2 ); + $this->assertIsIn( 'ed-d4a', $usr, 0, $expPagesIn ); $this->assertLists( 'ed-d5', $usr, $id0, $expListsW, $expListsW2 ); + $this->assertIsIn( 'ed-d5a', $usr, $id0, $expPagesIn ); $this->assertLists( 'ed-d6', $usrA, $id0, null ); + $this->assertIsIn( 'ed-d6a', $usrA, $id0, null ); $this->assertLists( 'ed-d7', $usr2, $id0, null ); + $this->assertIsIn( 'ed-d7a', $usr2, $id0, null ); $this->assertLists( 'ed-d7a', $usrS, $id0, null ); + $this->assertIsIn( 'ed-d7a', $usrS, $id0, null ); $this->badUsePage( 'ed-d8', $usrA, '"lspid": ' . $id0 ); $this->badUsePage( 'ed-d9', $usr2, '"lspid": ' . $id0 ); $this->badUsePage( 'ed-d10', $usrS, '"lspid": ' . $id0 ); @@ -301,12 +308,15 @@ $expListsW2->count = 2; $expPagesW = array( $pageW, $pageWA, $pageTW, $pageTWA ); + $expPagesIn = array( 'Gather-ListW', 'Gather-ListWA' ); $this->assertPages( 'ed-e2', $usr, null, $expPagesW ); $this->assertPages( 'ed-e3', $usr, 0, $expPagesW ); $this->assertPages( 'ed-e4', $usr, $id0, $expPagesW ); $this->assertLists( 'ed-e5', $usr, 0, $expListsW, $expListsW2 ); + $this->assertIsIn( 'ed-e5a', $usr, 0, $expPagesIn ); $this->assertLists( 'ed-e6', $usr, $id0, $expListsW, $expListsW2 ); + $this->assertIsIn( 'ed-e6a', $usr, $id0, $expPagesIn ); // // Add Gather-ListWAB to the created watchlist with ID=0 and description change @@ -320,12 +330,15 @@ $expListsW2->count = 3; $expListsW2->description = 'y'; $expPagesW = array( $pageW, $pageWA, $pageWAB, $pageTW, $pageTWA, $pageTWAB ); + $expPagesIn = array( 'Gather-ListW', 'Gather-ListWA', 'Gather-ListWAB' ); $this->assertPages( 'ed-f2', $usr, null, $expPagesW ); $this->assertPages( 'ed-f3', $usr, 0, $expPagesW ); $this->assertPages( 'ed-f4', $usr, $id0, $expPagesW ); $this->assertLists( 'ed-f5', $usr, 0, $expListsW, $expListsW2 ); + $this->assertIsIn( 'ed-f5a', $usr, 0, $expPagesIn ); $this->assertLists( 'ed-f6', $usr, $id0, $expListsW, $expListsW2 ); + $this->assertIsIn( 'ed-f6a', $usr, $id0, $expPagesIn ); // // Create new list A @@ -341,12 +354,17 @@ 'count' => 0, ) ); $expPagesA = array(); + $expPagesIn = array(); $this->assertPages( 'ed-i2', $usr, $idA, $expPagesA ); $this->assertLists( 'ed-i3', $usr, $idA, $expListsA, $expListsA2 ); + $this->assertIsIn( 'ed-i3a', $usr, $idA, $expPagesIn ); $this->assertLists( 'ed-i4', $usrA, $idA, null ); + $this->assertIsIn( 'ed-i4a', $usrA, $idA, null ); $this->assertLists( 'ed-i5', $usr2, $idA, null ); + $this->assertIsIn( 'ed-i5a', $usr2, $idA, null ); $this->assertLists( 'ed-i6', $usrS, $idA, null ); + $this->assertIsIn( 'ed-i6a', $usrS, $idA, null ); $this->badUsePage( 'ed-ia1', $usrA, '"lspid": ' . $idA ); $this->badUsePage( 'ed-ia2', $usr2, '"lspid": ' . $idA ); @@ -414,9 +432,13 @@ $this->assertPages( 'ed-k2b', $usrA, $idA, $expPagesA ); $this->assertPages( 'ed-k2c', $usrS, $idA, $expPagesA ); $this->assertLists( 'ed-k3', $usr, $idA, $expListsA, $expListsA2 ); + $this->assertIsIn( 'ed-k3a', $usr, $idA, $expPagesIn ); $this->assertLists( 'ed-k4', $usrA, $idA, $expListsA, $expListsA2 ); + $this->assertIsIn( 'ed-k4a', $usrA, $idA, $expPagesIn ); $this->assertLists( 'ed-k5', $usr2, $idA, $expListsA, $expListsA2 ); + $this->assertIsIn( 'ed-k5a', $usr2, $idA, $expPagesIn ); $this->assertLists( 'ed-k6', $usrS, $idA, $expListsA, $expListsA2 ); + $this->assertIsIn( 'ed-k6a', $usrS, $idA, $expPagesIn ); $this->badUseEdit( 'ed-ka1', $usr, false, $idAs . ', "description": "x"' ); $this->badUseEdit( 'ed-ka2', $usrA, false, $idAs . ', "description": "x"' ); @@ -457,6 +479,7 @@ $this->badUsePage( 'ed-l3a', $usrS, '"lspid": ' . $idA ); $this->badUsePage( 'ed-l4', $usrA, '"lspid": ' . $idA ); $this->assertLists( 'ed-l5', $usr, $idA, null ); + $this->assertIsIn( 'ed-l5a', $usr, $idA, null ); $this->assertLists( 'ed-l6', $usrA, $idA, null ); $this->assertLists( 'ed-l7', $usr2, $idA, null ); $this->assertLists( 'ed-l7a', $usrS, $idA, null ); @@ -484,15 +507,20 @@ ) ); // Non-alphabetic order should be preserved $expPagesB = array( $pageB, $pageAB, $pageWAB ); + $expPagesIn = array( 'Gather-ListB', 'Gather-ListAB', 'Gather-ListWAB' ); $this->assertPages( 'ed-n2', $usr, $idB, $expPagesB ); $this->assertPages( 'ed-n3', $usr2, $idB, $expPagesB ); $this->assertPages( 'ed-n3a', $usrS, $idB, $expPagesB ); $this->assertPages( 'ed-n4', $usrA, $idB, $expPagesB ); $this->assertLists( 'ed-n5', $usr, $idB, $expListsB, $expListsB2 ); + $this->assertIsIn( 'ed-n5a', $usr, $idB, $expPagesIn ); $this->assertLists( 'ed-n6', $usrA, $idB, $expListsB, $expListsB2 ); + $this->assertIsIn( 'ed-n6a', $usrA, $idB, $expPagesIn ); $this->assertLists( 'ed-n7', $usr2, $idB, $expListsB, $expListsB2 ); + $this->assertIsIn( 'ed-n7a', $usr2, $idB, $expPagesIn ); $this->assertLists( 'ed-n8', $usrS, $idB, $expListsB, $expListsB2 ); + $this->assertIsIn( 'ed-n8a', $usrS, $idB, $expPagesIn ); $this->badUseEdit( 'ed-na1', $usr, $token, '"label": "B", "mode": "hidelist"' ); $this->badUseEdit( 'ed-na2', $usr, $token, '"label": "B", "mode": "showlist"' ); $this->badUseEdit( 'ed-na3', $usrA, $tokenA, '"label": "B", "mode": "hidelist"' ); @@ -525,9 +553,13 @@ $this->assertLists( 'ed-o4', $usr, $idB, $expListsB, $expListsB2 ); $this->assertLists( 'ed-o4a', $usr, '{}', array( $expListsW, $expListsB ), array( $expListsW2, $expListsB2 ) ); + $this->assertIsIn( 'ed-o4b', $usr, $idB, $expPagesIn ); $this->assertLists( 'ed-o5', $usrA, $idB, null ); + $this->assertIsIn( 'ed-o5a', $usrA, $idB, null ); $this->assertLists( 'ed-o6', $usr2, $idB, null ); + $this->assertIsIn( 'ed-o6a', $usr2, $idB, null ); $this->assertLists( 'ed-o7', $usrS, $idB, null ); + $this->assertIsIn( 'ed-o7a', $usrS, $idB, null ); $this->badUsePage( 'ed-oa1', $usrA, '"lspid": ' . $idB ); $this->badUsePage( 'ed-oa2', $usr2, '"lspid": ' . $idB ); @@ -557,6 +589,7 @@ $this->assertPages( 'ed-p2', $usr, $idB, $expPagesB ); $this->assertLists( 'ed-p3', $usr, $idB, $expListsB, $expListsB2 ); + $this->assertIsIn( 'ed-p3a', $usr, $idB, $expPagesIn ); } public function testMultipleLists() { @@ -1047,6 +1080,47 @@ } } + /** + * @param $message + * @param User $u + * @param $params + * @param null|array|object $expected if null, expect empty, if object, expect one page, + * if array, expect several pages + * @throws Exception + */ + private function assertIsIn( $message, $u, $params, $expected ) { + if ( is_integer( $params ) ) { + $params = '"lsmid":' . $params; + } + $params = array_merge( (array)$this->toArr( $message, $params, true ), array( + 'action' => 'query', + 'format' => 'json', + 'continue' => '', + 'generator' => 'allpages', + 'gapprefix' => 'Gather-List', + 'gaplimit' => 'max', + 'prop' => 'listmembership' + ) ); + + if ( $expected === null ) { + $this->badUse( $message, $u, 'query', false, $params ); + return; + } + + $res = $this->doApiRequest2( $message, $u, $params ); + + $found = array(); + if ( isset( $res[0]['query']['pages'] ) ) { + foreach ( $res[0]['query']['pages'] as $page ) { + if ( array_key_exists( 'listmembership', $page ) ) { + $this->assertEquals( true, $page['listmembership'], $message ); + $found[] = $page['title']; + } + } + } + $this->assertArrayEquals( $expected, $found, false, false, $message ); + } + private function assertPages( $message, $u, $id, $expected ) { $params = $id === null ? '{}' : '"lspid":' . $id; $res = $this->getPages( $message, $u, $params ); -- To view, visit https://gerrit.wikimedia.org/r/203003 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I90b6df0c7a9d69460921e9410284c312b3d09d7b Gerrit-PatchSet: 13 Gerrit-Project: mediawiki/extensions/Gather Gerrit-Branch: master Gerrit-Owner: Yurik <yu...@wikimedia.org> Gerrit-Reviewer: Aaron Schulz <asch...@wikimedia.org> Gerrit-Reviewer: Anomie <bjor...@wikimedia.org> Gerrit-Reviewer: Gergő Tisza <gti...@wikimedia.org> Gerrit-Reviewer: Jdlrobson <jrob...@wikimedia.org> Gerrit-Reviewer: Jhernandez <jhernan...@wikimedia.org> Gerrit-Reviewer: Jkatz <jk...@wikimedia.org> Gerrit-Reviewer: Robmoen <rm...@wikimedia.org> Gerrit-Reviewer: Yurik <yu...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits