Anomie has uploaded a new change for review.

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


Change subject: API: Add prop=contributors
......................................................................

API: Add prop=contributors

Certain applications, such as the generation of PDFs, could use a list
of all non-anonymous contributors to the page (as well as a count of
anonymous contributors) without crawling the output of prop=revisions.
This patch adds a prop module to retrieve this information.

Note this probably requires the schema change in bug 10788 to finally be
completed, as otherwise the query will filesort.

Including the IP addresses of anonymoys contributors is not realisticly
possible without further schema changes, so that is not done here.
Additionally, revisions with DELETED_USER will be skipped entirely.

Change-Id: Iaff50dfb09016154901a5197aa14eb9f8febcbc5
---
M includes/AutoLoader.php
M includes/api/ApiQuery.php
A includes/api/ApiQueryContributors.php
3 files changed, 242 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/33/95633/1

diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index c94c46b..5f92caf 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -332,6 +332,7 @@
        'ApiQueryCategoryInfo' => 'includes/api/ApiQueryCategoryInfo.php',
        'ApiQueryCategoryMembers' => 'includes/api/ApiQueryCategoryMembers.php',
        'ApiQueryContributions' => 'includes/api/ApiQueryUserContributions.php',
+       'ApiQueryContributors' => 'includes/api/ApiQueryContributors.php',
        'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
        'ApiQueryDisabled' => 'includes/api/ApiQueryDisabled.php',
        'ApiQueryDuplicateFiles' => 'includes/api/ApiQueryDuplicateFiles.php',
diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php
index ea57196..f43f5c8 100644
--- a/includes/api/ApiQuery.php
+++ b/includes/api/ApiQuery.php
@@ -44,6 +44,7 @@
        private static $QueryPropModules = array(
                'categories' => 'ApiQueryCategories',
                'categoryinfo' => 'ApiQueryCategoryInfo',
+               'contributors' => 'ApiQueryContributors',
                'duplicatefiles' => 'ApiQueryDuplicateFiles',
                'extlinks' => 'ApiQueryExternalLinks',
                'images' => 'ApiQueryImages',
diff --git a/includes/api/ApiQueryContributors.php 
b/includes/api/ApiQueryContributors.php
new file mode 100644
index 0000000..28a940a
--- /dev/null
+++ b/includes/api/ApiQueryContributors.php
@@ -0,0 +1,240 @@
+<?php
+/**
+ * Query the list of contributors to a page
+ *
+ * Created on Nov 14, 2013
+ *
+ * Copyright © 2013 Brad Jorsch
+ *
+ * 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
+ * @since 1.23
+ */
+
+/**
+ * A query module to show contributors to a page
+ *
+ * @ingroup API
+ * @since 1.23
+ */
+class ApiQueryContributors extends ApiQueryBase {
+
+       public function __construct( $query, $moduleName ) {
+               parent::__construct( $query, $moduleName, 'co' );
+       }
+
+       public function execute() {
+               $params = $this->extractRequestParams();
+               $this->requireMaxOneParameter( $params, 'group', 
'excludegroup', 'rights', 'excluderights' );
+
+               # Only operate on existing pages
+               $pages = $this->getPageSet()->getGoodTitles();
+               if ( !count( $pages ) ) {
+                       # Nothing to do
+                       return;
+               }
+
+               $result = $this->getResult();
+
+               # First, count anons
+               $this->addTables( 'revision' );
+               $this->addFields( array(
+                       'page' => 'rev_page',
+                       'anons' => 'COUNT(DISTINCT rev_user_text)',
+               ) );
+               $this->addWhereFld( 'rev_page', array_keys( $pages ) );
+               $this->addWhere( 'rev_user = 0' );
+               $this->addWhere( '(rev_deleted & ' . Revision::DELETED_USER . 
') = 0' );
+               $this->addOption( 'GROUP BY', 'rev_page' );
+               $res = $this->select( __METHOD__ );
+               foreach ( $res as $row ) {
+                       $fit = $result->addValue( array( 'query', 'pages', 
$row->page),
+                               'anoncontributors', $row->anons
+                       );
+                       if ( !$fit ) {
+                               # This not fitting isn't reasonable, so it 
probably means that
+                               # some other module used up all the space. Just 
set a dummy
+                               # continue and hope it works next time.
+                               $this->setContinueEnumParameter( 'continue', 
'0|0' );
+                               return;
+                       }
+               }
+
+               # Next, add logged-in users
+               $this->resetQueryParams();
+               $this->addTables( 'revision' );
+               $this->addFields( array(
+                       'page' => 'rev_page',
+                       'user' => 'rev_user',
+                       'username' => 'MAX(rev_user_text)', // Non-MySQL 
databases don't like partial group-by
+               ) );
+               $this->addWhereFld( 'rev_page', array_keys( $pages ) );
+               $this->addWhere( 'rev_user != 0' );
+               $this->addWhere( '(rev_deleted & ' . Revision::DELETED_USER . 
') = 0' );
+
+               $limitGroups = array();
+               if ( $params['group'] ) {
+                       $excludeGroups = false;
+                       $limitGroups = $params['group'];
+               } elseif ( $params['excludegroup'] ) {
+                       $excludeGroups = true;
+                       $limitGroups = $params['excludegroup'];
+               } elseif ( $params['rights'] ) {
+                       $excludeGroups = false;
+                       foreach ( $params['rights'] as $r ) {
+                               $limitGroups = array_merge( $limitGroups, 
User::getGroupsWithPermission( $r ) );
+                       }
+                       $limitGroups = array_unique( $limitGroups );
+
+                       // If no group has the rights requested, no need to 
query
+                       if ( !$limitGroups ) {
+                               return;
+                       }
+               } elseif ( $params['excluderights'] ) {
+                       $excludeGroups = true;
+                       foreach ( $params['excluderights'] as $r ) {
+                               $limitGroups = array_merge( $limitGroups, 
User::getGroupsWithPermission( $r ) );
+                       }
+                       $limitGroups = array_unique( $limitGroups );
+               }
+
+               if ( $limitGroups ) {
+                       $this->addTables( 'user_groups' );
+                       $this->addJoinConds( array( 'user_groups' => array(
+                               $excludeGroups ? 'LEFT OUTER JOIN' : 'INNER 
JOIN',
+                               array( 'ug_user=rev_user', 'ug_group' => 
$limitGroups )
+                       ) ) );
+                       $this->addWhereIf( 'ug_user IS NULL', $excludeGroups );
+               }
+
+               if ( $params['continue'] !== null ) {
+                       $cont = explode( '|', $params['continue'] );
+                       $this->dieContinueUsageIf( count( $cont ) != 2 );
+                       $cont_page = intval( $cont[0] );
+                       $cont_user = intval( $cont[1] );
+                       $this->addWhere(
+                               "rev_page > $cont_page OR " .
+                               "(rev_page = $cont_page AND " .
+                               "rev_user >= $cont_user)"
+                       );
+               }
+
+               # Force a sort order to ensure that properties are grouped by 
page
+               # But only if pp_page is not constant in the WHERE clause.
+               if ( count( $pages ) > 1 ) {
+                       $this->addOption( 'ORDER BY', 'rev_page, rev_user' );
+               } else {
+                       $this->addOption( 'ORDER BY', 'rev_user' );
+               }
+
+               $this->addOption( 'GROUP BY', 'rev_page, rev_user' );
+               $this->addOption( 'LIMIT', $params['limit'] + 1 );
+
+               $res = $this->select( __METHOD__ );
+               $count = 0;
+               foreach ( $res as $row ) {
+                       if ( ++$count > $params['limit'] ) {
+                               // We've reached the one extra which shows that
+                               // there are additional pages to be had. Stop 
here...
+                               $this->setContinueEnumParameter( 'continue', 
$row->page . '|' . $row->user );
+                               return;
+                       }
+
+                       $fit = $this->addPageSubItem( $row->page,
+                               array( 'userid' => $row->user, 'name' => 
$row->username ),
+                               'user'
+                       );
+                       if ( !$fit ) {
+                               $this->setContinueEnumParameter( 'continue', 
$row->page . '|' . $row->user );
+                               return;
+                       }
+               }
+       }
+
+       public function getCacheMode( $params ) {
+               return 'public';
+       }
+
+       public function getAllowedParams() {
+               $userGroups = User::getAllGroups();
+               $userRights = User::getAllRights();
+
+               return array(
+                       'group' => array(
+                               ApiBase::PARAM_TYPE => $userGroups,
+                               ApiBase::PARAM_ISMULTI => true,
+                       ),
+                       'excludegroup' => array(
+                               ApiBase::PARAM_TYPE => $userGroups,
+                               ApiBase::PARAM_ISMULTI => true,
+                       ),
+                       'rights' => array(
+                               ApiBase::PARAM_TYPE => $userRights,
+                               ApiBase::PARAM_ISMULTI => true,
+                       ),
+                       'excluderights' => array(
+                               ApiBase::PARAM_TYPE => $userRights,
+                               ApiBase::PARAM_ISMULTI => true,
+                       ),
+                       'limit' => array(
+                               ApiBase::PARAM_DFLT => 10,
+                               ApiBase::PARAM_TYPE => 'limit',
+                               ApiBase::PARAM_MIN => 1,
+                               ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
+                               ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
+                       ),
+                       'continue' => null,
+               );
+       }
+
+       public function getParamDescription() {
+               return array(
+                       'group' => 'Limit users to given group name(s)',
+                       'excludegroup' => 'Exclude users in given group 
name(s)',
+                       'rights' => 'Limit users to those having given 
right(s)' .
+                               '(does not include rights granted by implicit 
or auto-promoted groups like *, user, or autoconfirmed)',
+                       'excluderights' => 'Limit users to those not having 
given right(s)' .
+                               '(does not include rights granted by implicit 
or auto-promoted groups like *, user, or autoconfirmed)',
+                       'excluderights' => 'Limit users to given right(s) (does 
not include rights granted by implicit or auto-promoted groups like *, user, or 
autoconfirmed)',
+                       'limit' => 'How many contributors to return',
+                       'continue' => 'When more results are available, use 
this to continue',
+               );
+       }
+
+       public function getPossibleErrors() {
+               return array_merge( parent::getPossibleErrors(),
+                       $this->getRequireMaxOneParameterErrorMessages(
+                               array( 'group', 'excludegroup', 'rights', 
'excluderights' )
+                       )
+               );
+       }
+
+
+       public function getDescription() {
+               return 'Get the list of logged-in contributors and the count of 
anonymous contributors to a page';
+       }
+
+       public function getExamples() {
+               return array(
+                       
'api.php?action=query&prop=contributors&titles=Main_Page',
+               );
+       }
+
+       public function getHelpUrls() {
+               return 
'https://www.mediawiki.org/wiki/API:Properties#contributors_.2F_co';
+       }
+}

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Iaff50dfb09016154901a5197aa14eb9f8febcbc5
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Anomie <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to