MediaWiki  master
ApiQueryAllImages.php
Go to the documentation of this file.
1 <?php
2 
28 
35  protected $mRepo;
36 
37  public function __construct( ApiQuery $query, $moduleName ) {
38  parent::__construct( $query, $moduleName, 'ai' );
39  $this->mRepo = RepoGroup::singleton()->getLocalRepo();
40  }
41 
49  protected function getDB() {
50  return $this->mRepo->getReplicaDB();
51  }
52 
53  public function execute() {
54  $this->run();
55  }
56 
57  public function getCacheMode( $params ) {
58  return 'public';
59  }
60 
65  public function executeGenerator( $resultPageSet ) {
66  if ( $resultPageSet->isResolvingRedirects() ) {
67  $this->dieWithError( 'apierror-allimages-redirect', 'invalidparammix' );
68  }
69 
70  $this->run( $resultPageSet );
71  }
72 
77  private function run( $resultPageSet = null ) {
78  $repo = $this->mRepo;
79  if ( !$repo instanceof LocalRepo ) {
80  $this->dieWithError( 'apierror-unsupportedrepo' );
81  }
82 
83  $prefix = $this->getModulePrefix();
84 
85  $db = $this->getDB();
86 
87  $params = $this->extractRequestParams();
88 
89  // Table and return fields
90  $prop = array_flip( $params['prop'] );
91 
92  $fileQuery = LocalFile::getQueryInfo();
93  $this->addTables( $fileQuery['tables'] );
94  $this->addFields( $fileQuery['fields'] );
95  $this->addJoinConds( $fileQuery['joins'] );
96 
97  $ascendingOrder = true;
98  if ( $params['dir'] == 'descending' || $params['dir'] == 'older' ) {
99  $ascendingOrder = false;
100  }
101 
102  if ( $params['sort'] == 'name' ) {
103  // Check mutually exclusive params
104  $disallowed = [ 'start', 'end', 'user' ];
105  foreach ( $disallowed as $pname ) {
106  if ( isset( $params[$pname] ) ) {
107  $this->dieWithError(
108  [
109  'apierror-invalidparammix-mustusewith',
110  "{$prefix}{$pname}",
111  "{$prefix}sort=timestamp"
112  ],
113  'invalidparammix'
114  );
115  }
116  }
117  if ( $params['filterbots'] != 'all' ) {
118  $this->dieWithError(
119  [
120  'apierror-invalidparammix-mustusewith',
121  "{$prefix}filterbots",
122  "{$prefix}sort=timestamp"
123  ],
124  'invalidparammix'
125  );
126  }
127 
128  // Pagination
129  if ( !is_null( $params['continue'] ) ) {
130  $cont = explode( '|', $params['continue'] );
131  $this->dieContinueUsageIf( count( $cont ) != 1 );
132  $op = $ascendingOrder ? '>' : '<';
133  $continueFrom = $db->addQuotes( $cont[0] );
134  $this->addWhere( "img_name $op= $continueFrom" );
135  }
136 
137  // Image filters
138  $from = $params['from'] === null ? null : $this->titlePartToKey( $params['from'], NS_FILE );
139  $to = $params['to'] === null ? null : $this->titlePartToKey( $params['to'], NS_FILE );
140  $this->addWhereRange( 'img_name', $ascendingOrder ? 'newer' : 'older', $from, $to );
141 
142  if ( isset( $params['prefix'] ) ) {
143  $this->addWhere( 'img_name' . $db->buildLike(
144  $this->titlePartToKey( $params['prefix'], NS_FILE ),
145  $db->anyString() ) );
146  }
147  } else {
148  // Check mutually exclusive params
149  $disallowed = [ 'from', 'to', 'prefix' ];
150  foreach ( $disallowed as $pname ) {
151  if ( isset( $params[$pname] ) ) {
152  $this->dieWithError(
153  [
154  'apierror-invalidparammix-mustusewith',
155  "{$prefix}{$pname}",
156  "{$prefix}sort=name"
157  ],
158  'invalidparammix'
159  );
160  }
161  }
162  if ( !is_null( $params['user'] ) && $params['filterbots'] != 'all' ) {
163  // Since filterbots checks if each user has the bot right, it
164  // doesn't make sense to use it with user
165  $this->dieWithError(
166  [ 'apierror-invalidparammix-cannotusewith', "{$prefix}user", "{$prefix}filterbots" ]
167  );
168  }
169 
170  // Pagination
171  $this->addTimestampWhereRange(
172  'img_timestamp',
173  $ascendingOrder ? 'newer' : 'older',
174  $params['start'],
175  $params['end']
176  );
177  // Include in ORDER BY for uniqueness
178  $this->addWhereRange( 'img_name', $ascendingOrder ? 'newer' : 'older', null, null );
179 
180  if ( !is_null( $params['continue'] ) ) {
181  $cont = explode( '|', $params['continue'] );
182  $this->dieContinueUsageIf( count( $cont ) != 2 );
183  $op = ( $ascendingOrder ? '>' : '<' );
184  $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
185  $continueName = $db->addQuotes( $cont[1] );
186  $this->addWhere( "img_timestamp $op $continueTimestamp OR " .
187  "(img_timestamp = $continueTimestamp AND " .
188  "img_name $op= $continueName)"
189  );
190  }
191 
192  // Image filters
193  if ( !is_null( $params['user'] ) ) {
194  $actorQuery = ActorMigration::newMigration()
195  ->getWhere( $db, 'img_user', User::newFromName( $params['user'], false ) );
196  $this->addTables( $actorQuery['tables'] );
197  $this->addJoinConds( $actorQuery['joins'] );
198  $this->addWhere( $actorQuery['conds'] );
199  }
200  if ( $params['filterbots'] != 'all' ) {
201  $actorQuery = ActorMigration::newMigration()->getJoin( 'img_user' );
202  $this->addTables( $actorQuery['tables'] );
203  $this->addTables( 'user_groups' );
204  $this->addJoinConds( $actorQuery['joins'] );
205  $this->addJoinConds( [ 'user_groups' => [
206  'LEFT JOIN',
207  [
208  'ug_group' => $this->getPermissionManager()->getGroupsWithPermission( 'bot' ),
209  'ug_user = ' . $actorQuery['fields']['img_user'],
210  'ug_expiry IS NULL OR ug_expiry >= ' . $db->addQuotes( $db->timestamp() )
211  ]
212  ] ] );
213  $groupCond = $params['filterbots'] == 'nobots' ? 'NULL' : 'NOT NULL';
214  $this->addWhere( "ug_group IS $groupCond" );
215  }
216  }
217 
218  // Filters not depending on sort
219  if ( isset( $params['minsize'] ) ) {
220  $this->addWhere( 'img_size>=' . (int)$params['minsize'] );
221  }
222 
223  if ( isset( $params['maxsize'] ) ) {
224  $this->addWhere( 'img_size<=' . (int)$params['maxsize'] );
225  }
226 
227  $sha1 = false;
228  if ( isset( $params['sha1'] ) ) {
229  $sha1 = strtolower( $params['sha1'] );
230  if ( !$this->validateSha1Hash( $sha1 ) ) {
231  $this->dieWithError( 'apierror-invalidsha1hash' );
232  }
233  $sha1 = Wikimedia\base_convert( $sha1, 16, 36, 31 );
234  } elseif ( isset( $params['sha1base36'] ) ) {
235  $sha1 = strtolower( $params['sha1base36'] );
236  if ( !$this->validateSha1Base36Hash( $sha1 ) ) {
237  $this->dieWithError( 'apierror-invalidsha1base36hash' );
238  }
239  }
240  if ( $sha1 ) {
241  $this->addWhereFld( 'img_sha1', $sha1 );
242  }
243 
244  if ( !is_null( $params['mime'] ) ) {
245  if ( $this->getConfig()->get( 'MiserMode' ) ) {
246  $this->dieWithError( 'apierror-mimesearchdisabled' );
247  }
248 
249  $mimeConds = [];
250  foreach ( $params['mime'] as $mime ) {
251  list( $major, $minor ) = File::splitMime( $mime );
252  $mimeConds[] = $db->makeList(
253  [
254  'img_major_mime' => $major,
255  'img_minor_mime' => $minor,
256  ],
257  LIST_AND
258  );
259  }
260  // safeguard against internal_api_error_DBQueryError
261  if ( count( $mimeConds ) > 0 ) {
262  $this->addWhere( $db->makeList( $mimeConds, LIST_OR ) );
263  } else {
264  // no MIME types, no files
265  $this->getResult()->addValue( 'query', $this->getModuleName(), [] );
266  return;
267  }
268  }
269 
270  $limit = $params['limit'];
271  $this->addOption( 'LIMIT', $limit + 1 );
272  $sortFlag = '';
273  if ( !$ascendingOrder ) {
274  $sortFlag = ' DESC';
275  }
276  if ( $params['sort'] == 'timestamp' ) {
277  $this->addOption( 'ORDER BY', 'img_timestamp' . $sortFlag );
278  } else {
279  $this->addOption( 'ORDER BY', 'img_name' . $sortFlag );
280  }
281 
282  $res = $this->select( __METHOD__ );
283 
284  $titles = [];
285  $count = 0;
286  $result = $this->getResult();
287  foreach ( $res as $row ) {
288  if ( ++$count > $limit ) {
289  // We've reached the one extra which shows that there are
290  // additional pages to be had. Stop here...
291  if ( $params['sort'] == 'name' ) {
292  $this->setContinueEnumParameter( 'continue', $row->img_name );
293  } else {
294  $this->setContinueEnumParameter( 'continue', "$row->img_timestamp|$row->img_name" );
295  }
296  break;
297  }
298 
299  if ( is_null( $resultPageSet ) ) {
300  $file = $repo->newFileFromRow( $row );
301  $info = array_merge( [ 'name' => $row->img_name ],
302  ApiQueryImageInfo::getInfo( $file, $prop, $result ) );
303  self::addTitleInfo( $info, $file->getTitle() );
304 
305  $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $info );
306  if ( !$fit ) {
307  if ( $params['sort'] == 'name' ) {
308  $this->setContinueEnumParameter( 'continue', $row->img_name );
309  } else {
310  $this->setContinueEnumParameter( 'continue', "$row->img_timestamp|$row->img_name" );
311  }
312  break;
313  }
314  } else {
315  $titles[] = Title::makeTitle( NS_FILE, $row->img_name );
316  }
317  }
318 
319  if ( is_null( $resultPageSet ) ) {
320  $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'img' );
321  } else {
322  $resultPageSet->populateFromTitles( $titles );
323  }
324  }
325 
326  public function getAllowedParams() {
327  $ret = [
328  'sort' => [
329  ApiBase::PARAM_DFLT => 'name',
331  'name',
332  'timestamp'
333  ]
334  ],
335  'dir' => [
336  ApiBase::PARAM_DFLT => 'ascending',
338  // sort=name
339  'ascending',
340  'descending',
341  // sort=timestamp
342  'newer',
343  'older'
344  ]
345  ],
346  'from' => null,
347  'to' => null,
348  'continue' => [
349  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
350  ],
351  'start' => [
352  ApiBase::PARAM_TYPE => 'timestamp'
353  ],
354  'end' => [
355  ApiBase::PARAM_TYPE => 'timestamp'
356  ],
357  'prop' => [
358  ApiBase::PARAM_TYPE => ApiQueryImageInfo::getPropertyNames( $this->propertyFilter ),
359  ApiBase::PARAM_DFLT => 'timestamp|url',
360  ApiBase::PARAM_ISMULTI => true,
361  ApiBase::PARAM_HELP_MSG => 'apihelp-query+imageinfo-param-prop',
363  ApiQueryImageInfo::getPropertyMessages( $this->propertyFilter ),
364  ],
365  'prefix' => null,
366  'minsize' => [
367  ApiBase::PARAM_TYPE => 'integer',
368  ],
369  'maxsize' => [
370  ApiBase::PARAM_TYPE => 'integer',
371  ],
372  'sha1' => null,
373  'sha1base36' => null,
374  'user' => [
375  ApiBase::PARAM_TYPE => 'user'
376  ],
377  'filterbots' => [
378  ApiBase::PARAM_DFLT => 'all',
380  'all',
381  'bots',
382  'nobots'
383  ]
384  ],
385  'mime' => [
386  ApiBase::PARAM_ISMULTI => true,
387  ],
388  'limit' => [
389  ApiBase::PARAM_DFLT => 10,
390  ApiBase::PARAM_TYPE => 'limit',
391  ApiBase::PARAM_MIN => 1,
394  ],
395  ];
396 
397  if ( $this->getConfig()->get( 'MiserMode' ) ) {
398  $ret['mime'][ApiBase::PARAM_HELP_MSG] = 'api-help-param-disabled-in-miser-mode';
399  }
400 
401  return $ret;
402  }
403 
404  private $propertyFilter = [ 'archivename', 'thumbmime', 'uploadwarning' ];
405 
406  protected function getExamplesMessages() {
407  return [
408  'action=query&list=allimages&aifrom=B'
409  => 'apihelp-query+allimages-example-b',
410  'action=query&list=allimages&aiprop=user|timestamp|url&' .
411  'aisort=timestamp&aidir=older'
412  => 'apihelp-query+allimages-example-recent',
413  'action=query&list=allimages&aimime=image/png|image/gif'
414  => 'apihelp-query+allimages-example-mimetypes',
415  'action=query&generator=allimages&gailimit=4&' .
416  'gaifrom=T&prop=imageinfo'
417  => 'apihelp-query+allimages-example-generator',
418  ];
419  }
420 
421  public function getHelpUrls() {
422  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allimages';
423  }
424 }
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below...
Definition: ApiBase.php:94
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:261
getResult()
Get the result object.
Definition: ApiBase.php:640
static getPropertyNames( $filter=[])
Returns all possible parameters to iiprop.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
validateSha1Base36Hash( $hash)
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:55
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:259
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE &#39;integer&#39; and &#39;limit&#39;.
Definition: ApiBase.php:97
__construct(ApiQuery $query, $moduleName)
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:2005
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user...
Definition: ApiBase.php:761
A repository that stores files in the local filesystem and registers them in the wiki&#39;s own database...
Definition: LocalRepo.php:37
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:164
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
getDB()
Override parent method to make sure the repo&#39;s DB is used which may not necessarily be the same as th...
static newMigration()
Static constructor.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
const LIST_AND
Definition: Defines.php:39
titlePartToKey( $titlePart, $namespace=NS_MAIN)
Convert an input title or title prefix into a dbkey.
static getInfo( $file, $prop, $result, $thumbParams=null, $opts=false)
Get result information for an image revision.
executeGenerator( $resultPageSet)
static singleton()
Definition: RepoGroup.php:60
dieContinueUsageIf( $condition)
Die with the &#39;badcontinue&#39; error.
Definition: ApiBase.php:2199
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
addFields( $value)
Add a set of fields to select to the internal array.
const NS_FILE
Definition: Defines.php:66
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right, for PARAM_TYPE &#39;limit&#39;.
Definition: ApiBase.php:103
This is the main query class.
Definition: ApiQuery.php:37
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter...
Definition: ApiBase.php:131
const LIST_OR
Definition: Defines.php:42
run( $resultPageSet=null)
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:586
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:528
addWhere( $value)
Add a set of WHERE clauses to the internal array.
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:58
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks...
Definition: ApiBase.php:710
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
static splitMime( $mime)
Split an internet media type into its two components; if not a two-part name, set the minor type to &#39;...
Definition: File.php:283
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
static getPropertyMessages( $filter=[])
Returns messages for all possible parameters to iiprop.
static getQueryInfo(array $options=[])
Return the tables, fields, and join conditions to be selected to create a new localfile object...
Definition: LocalFile.php:216
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE &#39;integer&#39; and &#39;limit&#39;.
Definition: ApiBase.php:106
validateSha1Hash( $hash)
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:515
Query module to enumerate all available pages.
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...