MediaWiki master
ApiQueryAllImages.php
Go to the documentation of this file.
1<?php
2
37
44
48 protected $mRepo;
49
50 private GroupPermissionsLookup $groupPermissionsLookup;
51
58 public function __construct(
59 ApiQuery $query,
60 $moduleName,
61 RepoGroup $repoGroup,
62 GroupPermissionsLookup $groupPermissionsLookup
63 ) {
64 parent::__construct( $query, $moduleName, 'ai' );
65 $this->mRepo = $repoGroup->getLocalRepo();
66 $this->groupPermissionsLookup = $groupPermissionsLookup;
67 }
68
76 protected function getDB() {
77 return $this->mRepo->getReplicaDB();
78 }
79
80 public function execute() {
81 $this->run();
82 }
83
84 public function getCacheMode( $params ) {
85 return 'public';
86 }
87
92 public function executeGenerator( $resultPageSet ) {
93 if ( $resultPageSet->isResolvingRedirects() ) {
94 $this->dieWithError( 'apierror-allimages-redirect', 'invalidparammix' );
95 }
96
97 $this->run( $resultPageSet );
98 }
99
104 private function run( $resultPageSet = null ) {
105 $repo = $this->mRepo;
106 if ( !$repo instanceof LocalRepo ) {
107 $this->dieWithError( 'apierror-unsupportedrepo' );
108 }
109
110 $prefix = $this->getModulePrefix();
111
112 $db = $this->getDB();
113
114 $params = $this->extractRequestParams();
115
116 // Table and return fields
117 $prop = array_fill_keys( $params['prop'], true );
118
119 $fileQuery = LocalFile::getQueryInfo();
120 $this->addTables( $fileQuery['tables'] );
121 $this->addFields( $fileQuery['fields'] );
122 $this->addJoinConds( $fileQuery['joins'] );
123
124 $ascendingOrder = true;
125 if ( $params['dir'] == 'descending' || $params['dir'] == 'older' ) {
126 $ascendingOrder = false;
127 }
128
129 if ( $params['sort'] == 'name' ) {
130 // Check mutually exclusive params
131 $disallowed = [ 'start', 'end', 'user' ];
132 foreach ( $disallowed as $pname ) {
133 if ( isset( $params[$pname] ) ) {
134 $this->dieWithError(
135 [
136 'apierror-invalidparammix-mustusewith',
137 "{$prefix}{$pname}",
138 "{$prefix}sort=timestamp"
139 ],
140 'invalidparammix'
141 );
142 }
143 }
144 if ( $params['filterbots'] != 'all' ) {
145 $this->dieWithError(
146 [
147 'apierror-invalidparammix-mustusewith',
148 "{$prefix}filterbots",
149 "{$prefix}sort=timestamp"
150 ],
151 'invalidparammix'
152 );
153 }
154
155 // Pagination
156 if ( $params['continue'] !== null ) {
157 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'string' ] );
158 $op = $ascendingOrder ? '>=' : '<=';
159 $this->addWhere( $db->expr( 'img_name', $op, $cont[0] ) );
160 }
161
162 // Image filters
163 $from = $params['from'] === null ? null : $this->titlePartToKey( $params['from'], NS_FILE );
164 $to = $params['to'] === null ? null : $this->titlePartToKey( $params['to'], NS_FILE );
165 $this->addWhereRange( 'img_name', $ascendingOrder ? 'newer' : 'older', $from, $to );
166
167 if ( isset( $params['prefix'] ) ) {
168 $this->addWhere(
169 $db->expr(
170 'img_name',
171 IExpression::LIKE,
172 new LikeValue( $this->titlePartToKey( $params['prefix'], NS_FILE ), $db->anyString() )
173 )
174 );
175 }
176 } else {
177 // Check mutually exclusive params
178 $disallowed = [ 'from', 'to', 'prefix' ];
179 foreach ( $disallowed as $pname ) {
180 if ( isset( $params[$pname] ) ) {
181 $this->dieWithError(
182 [
183 'apierror-invalidparammix-mustusewith',
184 "{$prefix}{$pname}",
185 "{$prefix}sort=name"
186 ],
187 'invalidparammix'
188 );
189 }
190 }
191 if ( $params['user'] !== null && $params['filterbots'] != 'all' ) {
192 // Since filterbots checks if each user has the bot right, it
193 // doesn't make sense to use it with user
194 $this->dieWithError(
195 [ 'apierror-invalidparammix-cannotusewith', "{$prefix}user", "{$prefix}filterbots" ]
196 );
197 }
198
199 // Pagination
201 'img_timestamp',
202 $ascendingOrder ? 'newer' : 'older',
203 $params['start'],
204 $params['end']
205 );
206 // Include in ORDER BY for uniqueness
207 $this->addWhereRange( 'img_name', $ascendingOrder ? 'newer' : 'older', null, null );
208
209 if ( $params['continue'] !== null ) {
210 $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'timestamp', 'string' ] );
211 $op = ( $ascendingOrder ? '>=' : '<=' );
212 $this->addWhere( $db->buildComparison( $op, [
213 'img_timestamp' => $db->timestamp( $cont[0] ),
214 'img_name' => $cont[1],
215 ] ) );
216 }
217
218 // Image filters
219 if ( $params['user'] !== null ) {
220 $this->addWhereFld( $fileQuery['fields']['img_user_text'], $params['user'] );
221 }
222 if ( $params['filterbots'] != 'all' ) {
223 $this->addTables( 'user_groups' );
224 $this->addJoinConds( [ 'user_groups' => [
225 'LEFT JOIN',
226 [
227 'ug_group' => $this->groupPermissionsLookup->getGroupsWithPermission( 'bot' ),
228 'ug_user = actor_user',
229 $db->expr( 'ug_expiry', '=', null )->or( 'ug_expiry', '>=', $db->timestamp() )
230 ]
231 ] ] );
232 $groupCond = $params['filterbots'] == 'nobots' ? 'NULL' : 'NOT NULL';
233 $this->addWhere( "ug_group IS $groupCond" );
234 }
235 }
236
237 // Filters not depending on sort
238 if ( isset( $params['minsize'] ) ) {
239 $this->addWhere( 'img_size>=' . (int)$params['minsize'] );
240 }
241
242 if ( isset( $params['maxsize'] ) ) {
243 $this->addWhere( 'img_size<=' . (int)$params['maxsize'] );
244 }
245
246 $sha1 = false;
247 if ( isset( $params['sha1'] ) ) {
248 $sha1 = strtolower( $params['sha1'] );
249 if ( !$this->validateSha1Hash( $sha1 ) ) {
250 $this->dieWithError( 'apierror-invalidsha1hash' );
251 }
252 $sha1 = Wikimedia\base_convert( $sha1, 16, 36, 31 );
253 } elseif ( isset( $params['sha1base36'] ) ) {
254 $sha1 = strtolower( $params['sha1base36'] );
255 if ( !$this->validateSha1Base36Hash( $sha1 ) ) {
256 $this->dieWithError( 'apierror-invalidsha1base36hash' );
257 }
258 }
259 if ( $sha1 ) {
260 $this->addWhereFld( 'img_sha1', $sha1 );
261 }
262
263 if ( $params['mime'] !== null ) {
264 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
265 $this->dieWithError( 'apierror-mimesearchdisabled' );
266 }
267
268 $mimeConds = [];
269 foreach ( $params['mime'] as $mime ) {
270 [ $major, $minor ] = File::splitMime( $mime );
271 $mimeConds[] =
272 $db->expr( 'img_major_mime', '=', $major )
273 ->and( 'img_minor_mime', '=', $minor );
274 }
275 if ( count( $mimeConds ) > 0 ) {
276 $this->addWhere( new OrExpressionGroup( ...$mimeConds ) );
277 } else {
278 // no MIME types, no files
279 $this->getResult()->addValue( 'query', $this->getModuleName(), [] );
280 return;
281 }
282 }
283
284 $limit = $params['limit'];
285 $this->addOption( 'LIMIT', $limit + 1 );
286
287 $res = $this->select( __METHOD__ );
288
289 $titles = [];
290 $count = 0;
291 $result = $this->getResult();
292 foreach ( $res as $row ) {
293 if ( ++$count > $limit ) {
294 // We've reached the one extra which shows that there are
295 // additional pages to be had. Stop here...
296 if ( $params['sort'] == 'name' ) {
297 $this->setContinueEnumParameter( 'continue', $row->img_name );
298 } else {
299 $this->setContinueEnumParameter( 'continue', "$row->img_timestamp|$row->img_name" );
300 }
301 break;
302 }
303
304 if ( $resultPageSet === null ) {
305 $file = $repo->newFileFromRow( $row );
306 $info = array_merge( [ 'name' => $row->img_name ],
307 ApiQueryImageInfo::getInfo( $file, $prop, $result ) );
308 self::addTitleInfo( $info, $file->getTitle() );
309
310 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $info );
311 if ( !$fit ) {
312 if ( $params['sort'] == 'name' ) {
313 $this->setContinueEnumParameter( 'continue', $row->img_name );
314 } else {
315 $this->setContinueEnumParameter( 'continue', "$row->img_timestamp|$row->img_name" );
316 }
317 break;
318 }
319 } else {
320 $titles[] = Title::makeTitle( NS_FILE, $row->img_name );
321 }
322 }
323
324 if ( $resultPageSet === null ) {
325 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'img' );
326 } else {
327 $resultPageSet->populateFromTitles( $titles );
328 }
329 }
330
331 public function getAllowedParams() {
332 $ret = [
333 'sort' => [
334 ParamValidator::PARAM_DEFAULT => 'name',
335 ParamValidator::PARAM_TYPE => [
336 'name',
337 'timestamp'
338 ]
339 ],
340 'dir' => [
341 ParamValidator::PARAM_DEFAULT => 'ascending',
342 ParamValidator::PARAM_TYPE => [
343 // sort=name
344 'ascending',
345 'descending',
346 // sort=timestamp
347 'newer',
348 'older'
349 ]
350 ],
351 'from' => null,
352 'to' => null,
353 'continue' => [
354 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
355 ],
356 'start' => [
357 ParamValidator::PARAM_TYPE => 'timestamp'
358 ],
359 'end' => [
360 ParamValidator::PARAM_TYPE => 'timestamp'
361 ],
362 'prop' => [
363 ParamValidator::PARAM_TYPE => ApiQueryImageInfo::getPropertyNames( $this->propertyFilter ),
364 ParamValidator::PARAM_DEFAULT => 'timestamp|url',
365 ParamValidator::PARAM_ISMULTI => true,
366 ApiBase::PARAM_HELP_MSG => 'apihelp-query+imageinfo-param-prop',
368 ApiQueryImageInfo::getPropertyMessages( $this->propertyFilter ),
369 ],
370 'prefix' => null,
371 'minsize' => [
372 ParamValidator::PARAM_TYPE => 'integer',
373 ],
374 'maxsize' => [
375 ParamValidator::PARAM_TYPE => 'integer',
376 ],
377 'sha1' => null,
378 'sha1base36' => null,
379 'user' => [
380 ParamValidator::PARAM_TYPE => 'user',
381 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'temp', 'id', 'interwiki' ],
382 ],
383 'filterbots' => [
384 ParamValidator::PARAM_DEFAULT => 'all',
385 ParamValidator::PARAM_TYPE => [
386 'all',
387 'bots',
388 'nobots'
389 ]
390 ],
391 'mime' => [
392 ParamValidator::PARAM_ISMULTI => true,
393 ],
394 'limit' => [
395 ParamValidator::PARAM_DEFAULT => 10,
396 ParamValidator::PARAM_TYPE => 'limit',
397 IntegerDef::PARAM_MIN => 1,
398 IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
399 IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
400 ],
401 ];
402
403 if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
404 $ret['mime'][ApiBase::PARAM_HELP_MSG] = 'api-help-param-disabled-in-miser-mode';
405 }
406
407 return $ret;
408 }
409
410 private $propertyFilter = [ 'archivename', 'thumbmime', 'uploadwarning' ];
411
412 protected function getExamplesMessages() {
413 return [
414 'action=query&list=allimages&aifrom=B'
415 => 'apihelp-query+allimages-example-b',
416 'action=query&list=allimages&aiprop=user|timestamp|url&' .
417 'aisort=timestamp&aidir=older'
418 => 'apihelp-query+allimages-example-recent',
419 'action=query&list=allimages&aimime=image/png|image/gif'
420 => 'apihelp-query+allimages-example-mimetypes',
421 'action=query&generator=allimages&gailimit=4&' .
422 'gaifrom=T&prop=imageinfo'
423 => 'apihelp-query+allimages-example-generator',
424 ];
425 }
426
427 public function getHelpUrls() {
428 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allimages';
429 }
430}
const NS_FILE
Definition Defines.php:70
array $params
The job parameters.
run()
Run the job.
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1533
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition ApiBase.php:541
parseContinueParamOrDie(string $continue, array $types)
Parse the 'continue' parameter in the usual format and validate the types of each part,...
Definition ApiBase.php:1725
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition ApiBase.php:211
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:236
getResult()
Get the result object.
Definition ApiBase.php:671
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:811
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:171
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:238
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:532
Query module to enumerate all images.
getExamplesMessages()
Returns usage examples for this module.
getCacheMode( $params)
Get the cache mode for the data generated by this module.
getDB()
Override parent method to make sure the repo's DB is used which may not necessarily be the same as th...
executeGenerator( $resultPageSet)
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
__construct(ApiQuery $query, $moduleName, RepoGroup $repoGroup, GroupPermissionsLookup $groupPermissionsLookup)
getHelpUrls()
Return links to more detailed help pages about the module.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
validateSha1Base36Hash( $hash)
addWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, and an ORDER BY clause to sort in the right direction.
validateSha1Hash( $hash)
addFields( $value)
Add a set of fields to select to the internal array.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
addWhere( $value)
Add a set of WHERE clauses to the internal array.
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
static getPropertyNames( $filter=[])
Returns all possible parameters to iiprop.
static getInfo( $file, $prop, $result, $thumbParams=null, $opts=false)
Get result information for an image revision.
static getPropertyMessages( $filter=[])
Returns messages for all possible parameters to iiprop.
This is the main query class.
Definition ApiQuery.php:43
static splitMime(?string $mime)
Split an internet media type into its two components; if not a two-part name, set the minor type to '...
Definition File.php:314
static getQueryInfo(array $options=[])
Return the tables, fields, and join conditions to be selected to create a new localfile object.
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition LocalRepo.php:49
A class containing constants representing the names of configuration variables.
Type definition for user types.
Definition UserDef.php:27
Represents a title within MediaWiki.
Definition Title.php:78
Prioritized list of file repositories.
Definition RepoGroup.php:30
getLocalRepo()
Get the local repository, i.e.
Service for formatting and validating API parameters.
Type definition for integer types.
Content of like value.
Definition LikeValue.php:14
Representing a group of expressions chained via OR.
A database connection without write operations.