MediaWiki  REL1_24
ApiQueryContributors.php
Go to the documentation of this file.
00001 <?php
00034 class ApiQueryContributors extends ApiQueryBase {
00039     const MAX_PAGES = 100;
00040 
00041     public function __construct( ApiQuery $query, $moduleName ) {
00042         // "pc" is short for "page contributors", "co" was already taken by the
00043         // GeoData extension's prop=coordinates.
00044         parent::__construct( $query, $moduleName, 'pc' );
00045     }
00046 
00047     public function execute() {
00048         $db = $this->getDB();
00049         $params = $this->extractRequestParams();
00050         $this->requireMaxOneParameter( $params, 'group', 'excludegroup', 'rights', 'excluderights' );
00051 
00052         // Only operate on existing pages
00053         $pages = array_keys( $this->getPageSet()->getGoodTitles() );
00054 
00055         // Filter out already-processed pages
00056         if ( $params['continue'] !== null ) {
00057             $cont = explode( '|', $params['continue'] );
00058             $this->dieContinueUsageIf( count( $cont ) != 2 );
00059             $cont_page = (int)$cont[0];
00060             $pages = array_filter( $pages, function ( $v ) use ( $cont_page ) {
00061                 return $v >= $cont_page;
00062             } );
00063         }
00064         if ( !count( $pages ) ) {
00065             // Nothing to do
00066             return;
00067         }
00068 
00069         // Apply MAX_PAGES, leaving any over the limit for a continue.
00070         sort( $pages );
00071         $continuePages = null;
00072         if ( count( $pages ) > self::MAX_PAGES ) {
00073             $continuePages = $pages[self::MAX_PAGES] . '|0';
00074             $pages = array_slice( $pages, 0, self::MAX_PAGES );
00075         }
00076 
00077         $result = $this->getResult();
00078 
00079         // First, count anons
00080         $this->addTables( 'revision' );
00081         $this->addFields( array(
00082             'page' => 'rev_page',
00083             'anons' => 'COUNT(DISTINCT rev_user_text)',
00084         ) );
00085         $this->addWhereFld( 'rev_page', $pages );
00086         $this->addWhere( 'rev_user = 0' );
00087         $this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
00088         $this->addOption( 'GROUP BY', 'rev_page' );
00089         $res = $this->select( __METHOD__ );
00090         foreach ( $res as $row ) {
00091             $fit = $result->addValue( array( 'query', 'pages', $row->page ),
00092                 'anoncontributors', $row->anons
00093             );
00094             if ( !$fit ) {
00095                 // This not fitting isn't reasonable, so it probably means that
00096                 // some other module used up all the space. Just set a dummy
00097                 // continue and hope it works next time.
00098                 $this->setContinueEnumParameter( 'continue',
00099                     $params['continue'] !== null ? $params['continue'] : '0|0'
00100                 );
00101 
00102                 return;
00103             }
00104         }
00105 
00106         // Next, add logged-in users
00107         $this->resetQueryParams();
00108         $this->addTables( 'revision' );
00109         $this->addFields( array(
00110             'page' => 'rev_page',
00111             'user' => 'rev_user',
00112             'username' => 'MAX(rev_user_text)', // Non-MySQL databases don't like partial group-by
00113         ) );
00114         $this->addWhereFld( 'rev_page', $pages );
00115         $this->addWhere( 'rev_user != 0' );
00116         $this->addWhere( $db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' );
00117         $this->addOption( 'GROUP BY', 'rev_page, rev_user' );
00118         $this->addOption( 'LIMIT', $params['limit'] + 1 );
00119 
00120         // Force a sort order to ensure that properties are grouped by page
00121         // But only if pp_page is not constant in the WHERE clause.
00122         if ( count( $pages ) > 1 ) {
00123             $this->addOption( 'ORDER BY', 'rev_page, rev_user' );
00124         } else {
00125             $this->addOption( 'ORDER BY', 'rev_user' );
00126         }
00127 
00128         $limitGroups = array();
00129         if ( $params['group'] ) {
00130             $excludeGroups = false;
00131             $limitGroups = $params['group'];
00132         } elseif ( $params['excludegroup'] ) {
00133             $excludeGroups = true;
00134             $limitGroups = $params['excludegroup'];
00135         } elseif ( $params['rights'] ) {
00136             $excludeGroups = false;
00137             foreach ( $params['rights'] as $r ) {
00138                 $limitGroups = array_merge( $limitGroups, User::getGroupsWithPermission( $r ) );
00139             }
00140 
00141             // If no group has the rights requested, no need to query
00142             if ( !$limitGroups ) {
00143                 if ( $continuePages !== null ) {
00144                     // But we still need to continue for the next page's worth
00145                     // of anoncontributors
00146                     $this->setContinueEnumParameter( 'continue', $continuePages );
00147                 }
00148 
00149                 return;
00150             }
00151         } elseif ( $params['excluderights'] ) {
00152             $excludeGroups = true;
00153             foreach ( $params['excluderights'] as $r ) {
00154                 $limitGroups = array_merge( $limitGroups, User::getGroupsWithPermission( $r ) );
00155             }
00156         }
00157 
00158         if ( $limitGroups ) {
00159             $limitGroups = array_unique( $limitGroups );
00160             $this->addTables( 'user_groups' );
00161             $this->addJoinConds( array( 'user_groups' => array(
00162                 $excludeGroups ? 'LEFT OUTER JOIN' : 'INNER JOIN',
00163                 array( 'ug_user=rev_user', 'ug_group' => $limitGroups )
00164             ) ) );
00165             $this->addWhereIf( 'ug_user IS NULL', $excludeGroups );
00166         }
00167 
00168         if ( $params['continue'] !== null ) {
00169             $cont = explode( '|', $params['continue'] );
00170             $this->dieContinueUsageIf( count( $cont ) != 2 );
00171             $cont_page = (int)$cont[0];
00172             $cont_user = (int)$cont[1];
00173             $this->addWhere(
00174                 "rev_page > $cont_page OR " .
00175                 "(rev_page = $cont_page AND " .
00176                 "rev_user >= $cont_user)"
00177             );
00178         }
00179 
00180         $res = $this->select( __METHOD__ );
00181         $count = 0;
00182         foreach ( $res as $row ) {
00183             if ( ++$count > $params['limit'] ) {
00184                 // We've reached the one extra which shows that
00185                 // there are additional pages to be had. Stop here...
00186                 $this->setContinueEnumParameter( 'continue', $row->page . '|' . $row->user );
00187 
00188                 return;
00189             }
00190 
00191             $fit = $this->addPageSubItem( $row->page,
00192                 array( 'userid' => $row->user, 'name' => $row->username ),
00193                 'user'
00194             );
00195             if ( !$fit ) {
00196                 $this->setContinueEnumParameter( 'continue', $row->page . '|' . $row->user );
00197 
00198                 return;
00199             }
00200         }
00201 
00202         if ( $continuePages !== null ) {
00203             $this->setContinueEnumParameter( 'continue', $continuePages );
00204         }
00205     }
00206 
00207     public function getCacheMode( $params ) {
00208         return 'public';
00209     }
00210 
00211     public function getAllowedParams() {
00212         $userGroups = User::getAllGroups();
00213         $userRights = User::getAllRights();
00214 
00215         return array(
00216             'group' => array(
00217                 ApiBase::PARAM_TYPE => $userGroups,
00218                 ApiBase::PARAM_ISMULTI => true,
00219             ),
00220             'excludegroup' => array(
00221                 ApiBase::PARAM_TYPE => $userGroups,
00222                 ApiBase::PARAM_ISMULTI => true,
00223             ),
00224             'rights' => array(
00225                 ApiBase::PARAM_TYPE => $userRights,
00226                 ApiBase::PARAM_ISMULTI => true,
00227             ),
00228             'excluderights' => array(
00229                 ApiBase::PARAM_TYPE => $userRights,
00230                 ApiBase::PARAM_ISMULTI => true,
00231             ),
00232             'limit' => array(
00233                 ApiBase::PARAM_DFLT => 10,
00234                 ApiBase::PARAM_TYPE => 'limit',
00235                 ApiBase::PARAM_MIN => 1,
00236                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
00237                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
00238             ),
00239             'continue' => null,
00240         );
00241     }
00242 
00243     public function getParamDescription() {
00244         return array(
00245             'group' => array(
00246                 'Limit users to given group name(s)',
00247                 'Does not include implicit or auto-promoted groups like *, user, or autoconfirmed'
00248             ),
00249             'excludegroup' => array(
00250                 'Exclude users in given group name(s)',
00251                 'Does not include implicit or auto-promoted groups like *, user, or autoconfirmed'
00252             ),
00253             'rights' => array(
00254                 'Limit users to those having given right(s)',
00255                 'Does not include rights granted by implicit or auto-promoted groups ' .
00256                     'like *, user, or autoconfirmed'
00257             ),
00258             'excluderights' => array(
00259                 'Limit users to those not having given right(s)',
00260                 'Does not include rights granted by implicit or auto-promoted groups ' .
00261                     'like *, user, or autoconfirmed'
00262             ),
00263             'limit' => 'How many contributors to return',
00264             'continue' => 'When more results are available, use this to continue',
00265         );
00266     }
00267 
00268     public function getDescription() {
00269         return 'Get the list of logged-in contributors and ' .
00270             'the count of anonymous contributors to a page.';
00271     }
00272 
00273     public function getExamples() {
00274         return array(
00275             'api.php?action=query&prop=contributors&titles=Main_Page',
00276         );
00277     }
00278 
00279     public function getHelpUrls() {
00280         return 'https://www.mediawiki.org/wiki/API:Properties#contributors_.2F_pc';
00281     }
00282 }