Yurik has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/203003

Change subject: Implements prop=listmembership
......................................................................

Implements prop=listmembership

Code-complete alpha code, might need more debugging, my vagrat broke

Bug: T95516
Change-Id: I90b6df0c7a9d69460921e9410284c312b3d09d7b
---
M Gather.php
A includes/api/ApiQueryListMembership.php
M includes/api/ApiQueryListPages.php
A includes/api/ApiShared.php
4 files changed, 260 insertions(+), 62 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Gather 
refs/changes/03/203003/1

diff --git a/Gather.php b/Gather.php
index b506820..37b7cbc 100644
--- a/Gather.php
+++ b/Gather.php
@@ -63,8 +63,10 @@
        'Gather\SpecialGatherLists' => 'specials/SpecialGatherLists',
        'Gather\SpecialGatherEditFeed' => 'specials/SpecialGatherEditFeed',
 
+       'Gather\api\ApiShared' => 'api/ApiShared',
        'Gather\api\ApiEditList' => 'api/ApiEditList',
        'Gather\api\ApiQueryLists' => 'api/ApiQueryLists',
+       'Gather\api\ApiQueryListMembership' => 'api/ApiQueryListMembership',
        'Gather\api\ApiQueryListPages' => 'api/ApiQueryListPages',
 
 );
@@ -95,6 +97,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/includes/api/ApiQueryListMembership.php 
b/includes/api/ApiQueryListMembership.php
new file mode 100644
index 0000000..780278f
--- /dev/null
+++ b/includes/api/ApiQueryListMembership.php
@@ -0,0 +1,136 @@
+<?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();
+
+               $userId = ApiShared::checkListAccess( $this->getDB(), $this, 
$params );
+
+               if ( $userId !== 0 ) {
+                       // This is a watchlist
+                       $pfx = 'wl';
+                       $this->selectNamedDB( 'watchlist', DB_SLAVE, 
'watchlist' );
+                       $db = $this->getDB(); // TODO: selectNamedDB should 
return this value
+                       $this->addTables( 'watchlist' );
+                       $this->addWhereFld( 'wl_user', $userId );
+               } else {
+                       // This is a gather list
+                       $pfx = 'gli';
+                       $this->addTables( 'gather_list_item' );
+                       $this->addWhereFld( 'gli_gl_id', $params['id'] );
+               }
+
+               $this->addFields( array( 'ns' => "{$pfx}_namespace", 'title' => 
"{$pfx}_title" ) );
+
+               $lb = new LinkBatch( $titles );
+               $this->addWhere( $lb->constructSet( $pfx, $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( "{$pfx}_namespace > $contNs OR " . 
"({$pfx}_namespace = $contNs AND " .
+                                                        "{$pfx}_title >= 
$contTitle)" );
+               }
+
+               // Don't ORDER BY namespace if it's constant in the WHERE clause
+               if ( count( $titleLookup ) === 1 ) {
+                       $this->addOption( 'ORDER BY', "{$pfx}_title" );
+               } else {
+                       $this->addOption( 'ORDER BY', array( 
"{$pfx}_namespace", "{$pfx}_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( ApiShared::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',
+               );
+       }
+
+       public function getHelpUrls() {
+               return '//www.mediawiki.org/wiki/Extension:Gather';
+       }
+}
diff --git a/includes/api/ApiQueryListPages.php 
b/includes/api/ApiQueryListPages.php
index 767c031..1c91f1a 100644
--- a/includes/api/ApiQueryListPages.php
+++ b/includes/api/ApiQueryListPages.php
@@ -61,58 +61,15 @@
        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() );
+               $userId = ApiShared::checkListAccess( $this->getDB(), $this, 
$params );
+
+               if ( $userId !== 0 ) {
+                       // This is a watchlist
+                       $titles = $this->queryLegacyWatchlist( $params, 
$isGenerator, $userId );
                } 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 ) {
@@ -242,14 +199,9 @@
        }
 
        public function getAllowedParams() {
-               return array(
+               return array_merge( ApiShared::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,
@@ -262,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(
@@ -276,7 +222,7 @@
                                ),
                                ApiBase::PARAM_HELP_MSG => 
'api-help-param-direction',
                        ),
-               );
+               ) );
        }
 
        protected function getExamplesMessages() {
diff --git a/includes/api/ApiShared.php b/includes/api/ApiShared.php
new file mode 100644
index 0000000..02b74bb
--- /dev/null
+++ b/includes/api/ApiShared.php
@@ -0,0 +1,113 @@
+<?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 ApiShared {
+
+       /**
+        * Get parameters used to identify a list with ownership
+        * @return array
+        */
+       public static function getListAccessParams() {
+               return array(
+                       'id' => array(
+                               ApiBase::PARAM_DFLT => 0,
+                               ApiBase::PARAM_TYPE => 'integer',
+                               ApiBase::PARAM_MIN => 0,
+                       ),
+                       'owner' => array(
+                               ApiBase::PARAM_TYPE => 'user',
+                       ),
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                       ),
+               );
+       }
+
+       /**
+        * Ensure that current params allow access to one list
+        * @param DatabaseBase $db
+        * @param ApiBase $context
+        * @param array $params module parameters
+        * @return int userId for watchlist, or 0 for a regular list
+        * @throws \UsageException In case access is not allowed
+        */
+       public static function checkListAccess( DatabaseBase $db, ApiBase 
$context, array $params ) {
+               $useOwner = $params['owner'] !== null;
+               if ( $useOwner !== ( $params['token'] !== null ) ) {
+                       $p = $context->getModulePrefix();
+                       $context->dieUsage( "Both {$p}owner and {$p}token must 
be given or missing",
+                               'invalidparammix' );
+               }
+
+               if ( !$params['id'] ) {
+                       // If id is not given (or equals to 0), permissions the 
same as watchlistraw access
+                       return $context->getWatchlistUser( $params )->getId();
+               }
+
+               // 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 ) {
+                       $context->dieUsage( "List does not exist", 'badid' );
+               }
+
+               $listRow = ApiEditList::normalizeRow( $listRow );
+               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 = $context->getWatchlistUser( $params );
+                       if ( $listRow->gl_user !== $user->getId() ) {
+                               $context->dieUsage( 'The owner supplied does 
not match the list\'s owner',
+                                       'permissiondenied' );
+                       }
+                       $showPrivate = true;
+               } else {
+                       $user = $context->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 ) {
+                       $context->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
+               return $listRow->gl_label === '' ? $listRow->gl_user : 0;
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/203003
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I90b6df0c7a9d69460921e9410284c312b3d09d7b
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Gather
Gerrit-Branch: master
Gerrit-Owner: Yurik <yu...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to