]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/api/ApiQueryAllImages.php
MediaWiki 1.30.2-scripts2
[autoinstalls/mediawiki.git] / includes / api / ApiQueryAllImages.php
1 <?php
2
3 /**
4  * API for MediaWiki 1.12+
5  *
6  * Created on Mar 16, 2008
7  *
8  * Copyright © 2008 Vasiliev Victor vasilvv@gmail.com,
9  * based on ApiQueryAllPages.php
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  * http://www.gnu.org/copyleft/gpl.html
25  *
26  * @file
27  */
28
29 use Wikimedia\Rdbms\IDatabase;
30
31 /**
32  * Query module to enumerate all available pages.
33  *
34  * @ingroup API
35  */
36 class ApiQueryAllImages extends ApiQueryGeneratorBase {
37         protected $mRepo;
38
39         public function __construct( ApiQuery $query, $moduleName ) {
40                 parent::__construct( $query, $moduleName, 'ai' );
41                 $this->mRepo = RepoGroup::singleton()->getLocalRepo();
42         }
43
44         /**
45          * Override parent method to make sure the repo's DB is used
46          * which may not necessarily be the same as the local DB.
47          *
48          * TODO: allow querying non-local repos.
49          * @return IDatabase
50          */
51         protected function getDB() {
52                 return $this->mRepo->getReplicaDB();
53         }
54
55         public function execute() {
56                 $this->run();
57         }
58
59         public function getCacheMode( $params ) {
60                 return 'public';
61         }
62
63         /**
64          * @param ApiPageSet $resultPageSet
65          * @return void
66          */
67         public function executeGenerator( $resultPageSet ) {
68                 if ( $resultPageSet->isResolvingRedirects() ) {
69                         $this->dieWithError( 'apierror-allimages-redirect', 'invalidparammix' );
70                 }
71
72                 $this->run( $resultPageSet );
73         }
74
75         /**
76          * @param ApiPageSet $resultPageSet
77          * @return void
78          */
79         private function run( $resultPageSet = null ) {
80                 $repo = $this->mRepo;
81                 if ( !$repo instanceof LocalRepo ) {
82                         $this->dieWithError( 'apierror-unsupportedrepo' );
83                 }
84
85                 $prefix = $this->getModulePrefix();
86
87                 $db = $this->getDB();
88
89                 $params = $this->extractRequestParams();
90                 $userId = !is_null( $params['user'] ) ? User::idFromName( $params['user'] ) : null;
91
92                 // Table and return fields
93                 $this->addTables( 'image' );
94
95                 $prop = array_flip( $params['prop'] );
96                 $this->addFields( LocalFile::selectFields() );
97
98                 $ascendingOrder = true;
99                 if ( $params['dir'] == 'descending' || $params['dir'] == 'older' ) {
100                         $ascendingOrder = false;
101                 }
102
103                 if ( $params['sort'] == 'name' ) {
104                         // Check mutually exclusive params
105                         $disallowed = [ 'start', 'end', 'user' ];
106                         foreach ( $disallowed as $pname ) {
107                                 if ( isset( $params[$pname] ) ) {
108                                         $this->dieWithError(
109                                                 [
110                                                         'apierror-invalidparammix-mustusewith',
111                                                         "{$prefix}{$pname}",
112                                                         "{$prefix}sort=timestamp"
113                                                 ],
114                                                 'invalidparammix'
115                                         );
116                                 }
117                         }
118                         if ( $params['filterbots'] != 'all' ) {
119                                 $this->dieWithError(
120                                         [
121                                                 'apierror-invalidparammix-mustusewith',
122                                                 "{$prefix}filterbots",
123                                                 "{$prefix}sort=timestamp"
124                                         ],
125                                         'invalidparammix'
126                                 );
127                         }
128
129                         // Pagination
130                         if ( !is_null( $params['continue'] ) ) {
131                                 $cont = explode( '|', $params['continue'] );
132                                 $this->dieContinueUsageIf( count( $cont ) != 1 );
133                                 $op = ( $ascendingOrder ? '>' : '<' );
134                                 $continueFrom = $db->addQuotes( $cont[0] );
135                                 $this->addWhere( "img_name $op= $continueFrom" );
136                         }
137
138                         // Image filters
139                         $from = ( $params['from'] === null ? null : $this->titlePartToKey( $params['from'], NS_FILE ) );
140                         $to = ( $params['to'] === null ? null : $this->titlePartToKey( $params['to'], NS_FILE ) );
141                         $this->addWhereRange( 'img_name', ( $ascendingOrder ? 'newer' : 'older' ), $from, $to );
142
143                         if ( isset( $params['prefix'] ) ) {
144                                 $this->addWhere( 'img_name' . $db->buildLike(
145                                         $this->titlePartToKey( $params['prefix'], NS_FILE ),
146                                         $db->anyString() ) );
147                         }
148                 } else {
149                         // Check mutually exclusive params
150                         $disallowed = [ 'from', 'to', 'prefix' ];
151                         foreach ( $disallowed as $pname ) {
152                                 if ( isset( $params[$pname] ) ) {
153                                         $this->dieWithError(
154                                                 [
155                                                         'apierror-invalidparammix-mustusewith',
156                                                         "{$prefix}{$pname}",
157                                                         "{$prefix}sort=name"
158                                                 ],
159                                                 'invalidparammix'
160                                         );
161                                 }
162                         }
163                         if ( !is_null( $params['user'] ) && $params['filterbots'] != 'all' ) {
164                                 // Since filterbots checks if each user has the bot right, it
165                                 // doesn't make sense to use it with user
166                                 $this->dieWithError(
167                                         [ 'apierror-invalidparammix-cannotusewith', "{$prefix}user", "{$prefix}filterbots" ]
168                                 );
169                         }
170
171                         // Pagination
172                         $this->addTimestampWhereRange(
173                                 'img_timestamp',
174                                 $ascendingOrder ? 'newer' : 'older',
175                                 $params['start'],
176                                 $params['end']
177                         );
178                         // Include in ORDER BY for uniqueness
179                         $this->addWhereRange( 'img_name', $ascendingOrder ? 'newer' : 'older', null, null );
180
181                         if ( !is_null( $params['continue'] ) ) {
182                                 $cont = explode( '|', $params['continue'] );
183                                 $this->dieContinueUsageIf( count( $cont ) != 2 );
184                                 $op = ( $ascendingOrder ? '>' : '<' );
185                                 $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
186                                 $continueName = $db->addQuotes( $cont[1] );
187                                 $this->addWhere( "img_timestamp $op $continueTimestamp OR " .
188                                         "(img_timestamp = $continueTimestamp AND " .
189                                         "img_name $op= $continueName)"
190                                 );
191                         }
192
193                         // Image filters
194                         if ( !is_null( $params['user'] ) ) {
195                                 if ( $userId ) {
196                                         $this->addWhereFld( 'img_user', $userId );
197                                 } else {
198                                         $this->addWhereFld( 'img_user_text', $params['user'] );
199                                 }
200                         }
201                         if ( $params['filterbots'] != 'all' ) {
202                                 $this->addTables( 'user_groups' );
203                                 $this->addJoinConds( [ 'user_groups' => [
204                                         'LEFT JOIN',
205                                         [
206                                                 'ug_group' => User::getGroupsWithPermission( 'bot' ),
207                                                 'ug_user = img_user',
208                                                 'ug_expiry IS NULL OR ug_expiry >= ' . $db->addQuotes( $db->timestamp() )
209                                         ]
210                                 ] ] );
211                                 $groupCond = ( $params['filterbots'] == 'nobots' ? 'NULL' : 'NOT NULL' );
212                                 $this->addWhere( "ug_group IS $groupCond" );
213                         }
214                 }
215
216                 // Filters not depending on sort
217                 if ( isset( $params['minsize'] ) ) {
218                         $this->addWhere( 'img_size>=' . intval( $params['minsize'] ) );
219                 }
220
221                 if ( isset( $params['maxsize'] ) ) {
222                         $this->addWhere( 'img_size<=' . intval( $params['maxsize'] ) );
223                 }
224
225                 $sha1 = false;
226                 if ( isset( $params['sha1'] ) ) {
227                         $sha1 = strtolower( $params['sha1'] );
228                         if ( !$this->validateSha1Hash( $sha1 ) ) {
229                                 $this->dieWithError( 'apierror-invalidsha1hash' );
230                         }
231                         $sha1 = Wikimedia\base_convert( $sha1, 16, 36, 31 );
232                 } elseif ( isset( $params['sha1base36'] ) ) {
233                         $sha1 = strtolower( $params['sha1base36'] );
234                         if ( !$this->validateSha1Base36Hash( $sha1 ) ) {
235                                 $this->dieWithError( 'apierror-invalidsha1base36hash' );
236                         }
237                 }
238                 if ( $sha1 ) {
239                         $this->addWhereFld( 'img_sha1', $sha1 );
240                 }
241
242                 if ( !is_null( $params['mime'] ) ) {
243                         if ( $this->getConfig()->get( 'MiserMode' ) ) {
244                                 $this->dieWithError( 'apierror-mimesearchdisabled' );
245                         }
246
247                         $mimeConds = [];
248                         foreach ( $params['mime'] as $mime ) {
249                                 list( $major, $minor ) = File::splitMime( $mime );
250                                 $mimeConds[] = $db->makeList(
251                                         [
252                                                 'img_major_mime' => $major,
253                                                 'img_minor_mime' => $minor,
254                                         ],
255                                         LIST_AND
256                                 );
257                         }
258                         // safeguard against internal_api_error_DBQueryError
259                         if ( count( $mimeConds ) > 0 ) {
260                                 $this->addWhere( $db->makeList( $mimeConds, LIST_OR ) );
261                         } else {
262                                 // no MIME types, no files
263                                 $this->getResult()->addValue( 'query', $this->getModuleName(), [] );
264                                 return;
265                         }
266                 }
267
268                 $limit = $params['limit'];
269                 $this->addOption( 'LIMIT', $limit + 1 );
270                 $sortFlag = '';
271                 if ( !$ascendingOrder ) {
272                         $sortFlag = ' DESC';
273                 }
274                 if ( $params['sort'] == 'timestamp' ) {
275                         $this->addOption( 'ORDER BY', 'img_timestamp' . $sortFlag );
276                         if ( !is_null( $params['user'] ) ) {
277                                 if ( $userId ) {
278                                         $this->addOption( 'USE INDEX', [ 'image' => 'img_user_timestamp' ] );
279                                 } else {
280                                         $this->addOption( 'USE INDEX', [ 'image' => 'img_usertext_timestamp' ] );
281                                 }
282                         } else {
283                                 $this->addOption( 'USE INDEX', [ 'image' => 'img_timestamp' ] );
284                         }
285                 } else {
286                         $this->addOption( 'ORDER BY', 'img_name' . $sortFlag );
287                 }
288
289                 $res = $this->select( __METHOD__ );
290
291                 $titles = [];
292                 $count = 0;
293                 $result = $this->getResult();
294                 foreach ( $res as $row ) {
295                         if ( ++$count > $limit ) {
296                                 // We've reached the one extra which shows that there are
297                                 // additional pages to be had. Stop here...
298                                 if ( $params['sort'] == 'name' ) {
299                                         $this->setContinueEnumParameter( 'continue', $row->img_name );
300                                 } else {
301                                         $this->setContinueEnumParameter( 'continue', "$row->img_timestamp|$row->img_name" );
302                                 }
303                                 break;
304                         }
305
306                         if ( is_null( $resultPageSet ) ) {
307                                 $file = $repo->newFileFromRow( $row );
308                                 $info = array_merge( [ 'name' => $row->img_name ],
309                                         ApiQueryImageInfo::getInfo( $file, $prop, $result ) );
310                                 self::addTitleInfo( $info, $file->getTitle() );
311
312                                 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $info );
313                                 if ( !$fit ) {
314                                         if ( $params['sort'] == 'name' ) {
315                                                 $this->setContinueEnumParameter( 'continue', $row->img_name );
316                                         } else {
317                                                 $this->setContinueEnumParameter( 'continue', "$row->img_timestamp|$row->img_name" );
318                                         }
319                                         break;
320                                 }
321                         } else {
322                                 $titles[] = Title::makeTitle( NS_FILE, $row->img_name );
323                         }
324                 }
325
326                 if ( is_null( $resultPageSet ) ) {
327                         $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'img' );
328                 } else {
329                         $resultPageSet->populateFromTitles( $titles );
330                 }
331         }
332
333         public function getAllowedParams() {
334                 $ret = [
335                         'sort' => [
336                                 ApiBase::PARAM_DFLT => 'name',
337                                 ApiBase::PARAM_TYPE => [
338                                         'name',
339                                         'timestamp'
340                                 ]
341                         ],
342                         'dir' => [
343                                 ApiBase::PARAM_DFLT => 'ascending',
344                                 ApiBase::PARAM_TYPE => [
345                                         // sort=name
346                                         'ascending',
347                                         'descending',
348                                         // sort=timestamp
349                                         'newer',
350                                         'older'
351                                 ]
352                         ],
353                         'from' => null,
354                         'to' => null,
355                         'continue' => [
356                                 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
357                         ],
358                         'start' => [
359                                 ApiBase::PARAM_TYPE => 'timestamp'
360                         ],
361                         'end' => [
362                                 ApiBase::PARAM_TYPE => 'timestamp'
363                         ],
364                         'prop' => [
365                                 ApiBase::PARAM_TYPE => ApiQueryImageInfo::getPropertyNames( $this->propertyFilter ),
366                                 ApiBase::PARAM_DFLT => 'timestamp|url',
367                                 ApiBase::PARAM_ISMULTI => true,
368                                 ApiBase::PARAM_HELP_MSG => 'apihelp-query+imageinfo-param-prop',
369                                 ApiBase::PARAM_HELP_MSG_PER_VALUE =>
370                                         ApiQueryImageInfo::getPropertyMessages( $this->propertyFilter ),
371                         ],
372                         'prefix' => null,
373                         'minsize' => [
374                                 ApiBase::PARAM_TYPE => 'integer',
375                         ],
376                         'maxsize' => [
377                                 ApiBase::PARAM_TYPE => 'integer',
378                         ],
379                         'sha1' => null,
380                         'sha1base36' => null,
381                         'user' => [
382                                 ApiBase::PARAM_TYPE => 'user'
383                         ],
384                         'filterbots' => [
385                                 ApiBase::PARAM_DFLT => 'all',
386                                 ApiBase::PARAM_TYPE => [
387                                         'all',
388                                         'bots',
389                                         'nobots'
390                                 ]
391                         ],
392                         'mime' => [
393                                 ApiBase::PARAM_ISMULTI => true,
394                         ],
395                         'limit' => [
396                                 ApiBase::PARAM_DFLT => 10,
397                                 ApiBase::PARAM_TYPE => 'limit',
398                                 ApiBase::PARAM_MIN => 1,
399                                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
400                                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
401                         ],
402                 ];
403
404                 if ( $this->getConfig()->get( 'MiserMode' ) ) {
405                         $ret['mime'][ApiBase::PARAM_HELP_MSG] = 'api-help-param-disabled-in-miser-mode';
406                 }
407
408                 return $ret;
409         }
410
411         private $propertyFilter = [ 'archivename', 'thumbmime', 'uploadwarning' ];
412
413         protected function getExamplesMessages() {
414                 return [
415                         'action=query&list=allimages&aifrom=B'
416                                 => 'apihelp-query+allimages-example-B',
417                         'action=query&list=allimages&aiprop=user|timestamp|url&' .
418                                 'aisort=timestamp&aidir=older'
419                                 => 'apihelp-query+allimages-example-recent',
420                         'action=query&list=allimages&aimime=image/png|image/gif'
421                                 => 'apihelp-query+allimages-example-mimetypes',
422                         'action=query&generator=allimages&gailimit=4&' .
423                                 'gaifrom=T&prop=imageinfo'
424                                 => 'apihelp-query+allimages-example-generator',
425                 ];
426         }
427
428         public function getHelpUrls() {
429                 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allimages';
430         }
431 }