MediaWiki
REL1_24
|
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 }