Merge "API: Allow filtering keys in iiprop=extmetadata"
[lhc/web/wiklou.git] / includes / api / ApiQueryImageInfo.php
1 <?php
2 /**
3 *
4 *
5 * Created on July 6, 2007
6 *
7 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * http://www.gnu.org/copyleft/gpl.html
23 *
24 * @file
25 */
26
27 /**
28 * A query action to get image information and upload history.
29 *
30 * @ingroup API
31 */
32 class ApiQueryImageInfo extends ApiQueryBase {
33 const TRANSFORM_LIMIT = 50;
34 private static $transformCount = 0;
35
36 public function __construct( $query, $moduleName, $prefix = 'ii' ) {
37 // We allow a subclass to override the prefix, to create a related API module.
38 // Some other parts of MediaWiki construct this with a null $prefix, which used to be ignored when this only took two arguments
39 if ( is_null( $prefix ) ) {
40 $prefix = 'ii';
41 }
42 parent::__construct( $query, $moduleName, $prefix );
43 }
44
45 public function execute() {
46 $params = $this->extractRequestParams();
47
48 $prop = array_flip( $params['prop'] );
49
50 $scale = $this->getScale( $params );
51
52 $metadataOpts = array(
53 'version' => $params['metadataversion'],
54 'language' => $params['extmetadatalanguage'],
55 'multilang' => $params['extmetadatamultilang'],
56 'extmetadatafilter' => $params['extmetadatafilter'],
57 );
58
59 $pageIds = $this->getPageSet()->getAllTitlesByNamespace();
60 if ( !empty( $pageIds[NS_FILE] ) ) {
61 $titles = array_keys( $pageIds[NS_FILE] );
62 asort( $titles ); // Ensure the order is always the same
63
64 $fromTitle = null;
65 if ( !is_null( $params['continue'] ) ) {
66 $cont = explode( '|', $params['continue'] );
67 $this->dieContinueUsageIf( count( $cont ) != 2 );
68 $fromTitle = strval( $cont[0] );
69 $fromTimestamp = $cont[1];
70 // Filter out any titles before $fromTitle
71 foreach ( $titles as $key => $title ) {
72 if ( $title < $fromTitle ) {
73 unset( $titles[$key] );
74 } else {
75 break;
76 }
77 }
78 }
79
80 $result = $this->getResult();
81 //search only inside the local repo
82 if ( $params['localonly'] ) {
83 $images = RepoGroup::singleton()->getLocalRepo()->findFiles( $titles );
84 } else {
85 $images = RepoGroup::singleton()->findFiles( $titles );
86 }
87 foreach ( $titles as $title ) {
88 $pageId = $pageIds[NS_FILE][$title];
89 $start = $title === $fromTitle ? $fromTimestamp : $params['start'];
90
91 if ( !isset( $images[$title] ) ) {
92 if ( isset( $prop['uploadwarning'] ) ) {
93 // Uploadwarning needs info about non-existing files
94 $images[$title] = wfLocalFile( $title );
95 } else {
96 $result->addValue(
97 array( 'query', 'pages', intval( $pageId ) ),
98 'imagerepository', ''
99 );
100 // The above can't fail because it doesn't increase the result size
101 continue;
102 }
103 }
104
105 /** @var $img File */
106 $img = $images[$title];
107
108 if ( self::getTransformCount() >= self::TRANSFORM_LIMIT ) {
109 if ( count( $pageIds[NS_FILE] ) == 1 ) {
110 // See the 'the user is screwed' comment below
111 $this->setContinueEnumParameter( 'start',
112 $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
113 );
114 } else {
115 $this->setContinueEnumParameter( 'continue',
116 $this->getContinueStr( $img, $start ) );
117 }
118 break;
119 }
120
121 $fit = $result->addValue(
122 array( 'query', 'pages', intval( $pageId ) ),
123 'imagerepository', $img->getRepoName()
124 );
125 if ( !$fit ) {
126 if ( count( $pageIds[NS_FILE] ) == 1 ) {
127 // The user is screwed. imageinfo can't be solely
128 // responsible for exceeding the limit in this case,
129 // so set a query-continue that just returns the same
130 // thing again. When the violating queries have been
131 // out-continued, the result will get through
132 $this->setContinueEnumParameter( 'start',
133 $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
134 );
135 } else {
136 $this->setContinueEnumParameter( 'continue',
137 $this->getContinueStr( $img, $start ) );
138 }
139 break;
140 }
141
142 // Check if we can make the requested thumbnail, and get transform parameters.
143 $finalThumbParams = $this->mergeThumbParams( $img, $scale, $params['urlparam'] );
144
145 // Get information about the current version first
146 // Check that the current version is within the start-end boundaries
147 $gotOne = false;
148 if (
149 ( is_null( $start ) || $img->getTimestamp() <= $start ) &&
150 ( is_null( $params['end'] ) || $img->getTimestamp() >= $params['end'] )
151 ) {
152 $gotOne = true;
153
154 $fit = $this->addPageSubItem( $pageId,
155 self::getInfo( $img, $prop, $result,
156 $finalThumbParams, $metadataOpts
157 )
158 );
159 if ( !$fit ) {
160 if ( count( $pageIds[NS_FILE] ) == 1 ) {
161 // See the 'the user is screwed' comment above
162 $this->setContinueEnumParameter( 'start',
163 wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) );
164 } else {
165 $this->setContinueEnumParameter( 'continue',
166 $this->getContinueStr( $img ) );
167 }
168 break;
169 }
170 }
171
172 // Now get the old revisions
173 // Get one more to facilitate query-continue functionality
174 $count = ( $gotOne ? 1 : 0 );
175 $oldies = $img->getHistory( $params['limit'] - $count + 1, $start, $params['end'] );
176 /** @var $oldie File */
177 foreach ( $oldies as $oldie ) {
178 if ( ++$count > $params['limit'] ) {
179 // We've reached the extra one which shows that there are additional pages to be had. Stop here...
180 // Only set a query-continue if there was only one title
181 if ( count( $pageIds[NS_FILE] ) == 1 ) {
182 $this->setContinueEnumParameter( 'start',
183 wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) );
184 }
185 break;
186 }
187 $fit = self::getTransformCount() < self::TRANSFORM_LIMIT &&
188 $this->addPageSubItem( $pageId,
189 self::getInfo( $oldie, $prop, $result,
190 $finalThumbParams, $metadataOpts
191 )
192 );
193 if ( !$fit ) {
194 if ( count( $pageIds[NS_FILE] ) == 1 ) {
195 $this->setContinueEnumParameter( 'start',
196 wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) );
197 } else {
198 $this->setContinueEnumParameter( 'continue',
199 $this->getContinueStr( $oldie ) );
200 }
201 break;
202 }
203 }
204 if ( !$fit ) {
205 break;
206 }
207 }
208 }
209 }
210
211 /**
212 * From parameters, construct a 'scale' array
213 * @param array $params Parameters passed to api.
214 * @return Array or Null: key-val array of 'width' and 'height', or null
215 */
216 public function getScale( $params ) {
217 $p = $this->getModulePrefix();
218
219 if ( $params['urlwidth'] != -1 ) {
220 $scale = array();
221 $scale['width'] = $params['urlwidth'];
222 $scale['height'] = $params['urlheight'];
223 } elseif ( $params['urlheight'] != -1 ) {
224 // Height is specified but width isn't
225 // Don't set $scale['width']; this signals mergeThumbParams() to fill it with the image's width
226 $scale = array();
227 $scale['height'] = $params['urlheight'];
228 } else {
229 $scale = null;
230 if ( $params['urlparam'] ) {
231 $this->dieUsage( "{$p}urlparam requires {$p}urlwidth", "urlparam_no_width" );
232 }
233 }
234
235 return $scale;
236 }
237
238 /** Validate and merge scale parameters with handler thumb parameters, give error if invalid.
239 *
240 * We do this later than getScale, since we need the image
241 * to know which handler, since handlers can make their own parameters.
242 * @param File $image Image that params are for.
243 * @param array $thumbParams thumbnail parameters from getScale
244 * @param string $otherParams of otherParams (iiurlparam).
245 * @return Array of parameters for transform.
246 */
247 protected function mergeThumbParams( $image, $thumbParams, $otherParams ) {
248 global $wgThumbLimits;
249
250 if ( !isset( $thumbParams['width'] ) && isset( $thumbParams['height'] ) ) {
251 // We want to limit only by height in this situation, so pass the
252 // image's full width as the limiting width. But some file types
253 // don't have a width of their own, so pick something arbitrary so
254 // thumbnailing the default icon works.
255 if ( $image->getWidth() <= 0 ) {
256 $thumbParams['width'] = max( $wgThumbLimits );
257 } else {
258 $thumbParams['width'] = $image->getWidth();
259 }
260 }
261
262 if ( !$otherParams ) {
263 return $thumbParams;
264 }
265 $p = $this->getModulePrefix();
266
267 $h = $image->getHandler();
268 if ( !$h ) {
269 $this->setWarning( 'Could not create thumbnail because ' .
270 $image->getName() . ' does not have an associated image handler' );
271
272 return $thumbParams;
273 }
274
275 $paramList = $h->parseParamString( $otherParams );
276 if ( !$paramList ) {
277 // Just set a warning (instead of dieUsage), as in many cases
278 // we could still render the image using width and height parameters,
279 // and this type of thing could happen between different versions of
280 // handlers.
281 $this->setWarning( "Could not parse {$p}urlparam for " . $image->getName()
282 . '. Using only width and height' );
283
284 return $thumbParams;
285 }
286
287 if ( isset( $paramList['width'] ) ) {
288 if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) {
289 $this->setWarning( "Ignoring width value set in {$p}urlparam ({$paramList['width']}) "
290 . "in favor of width value derived from {$p}urlwidth/{$p}urlheight ({$thumbParams['width']})" );
291 }
292 }
293
294 foreach ( $paramList as $name => $value ) {
295 if ( !$h->validateParam( $name, $value ) ) {
296 $this->dieUsage( "Invalid value for {$p}urlparam ($name=$value)", "urlparam" );
297 }
298 }
299
300 return $thumbParams + $paramList;
301 }
302
303 /**
304 * Get result information for an image revision
305 *
306 * @param $file File object
307 * @param array $prop of properties to get (in the keys)
308 * @param $result ApiResult object
309 * @param array $thumbParams containing 'width' and 'height' items, or null
310 * @param string|array $metadataOpts Options for metadata fetching.
311 * This is an array consisting of the keys:
312 * 'version': The metadata version for the metadata option
313 * 'language': The language for extmetadata property
314 * 'multilang': Return all translations in extmetadata property
315 * @return Array: result array
316 */
317 static function getInfo( $file, $prop, $result, $thumbParams = null, $metadataOpts = false ) {
318 global $wgContLang;
319
320 if ( !$metadataOpts || is_string( $metadataOpts ) ) {
321 $metadataOpts = array(
322 'version' => $metadataOpts ?: 'latest',
323 'language' => $wgContLang,
324 'multilang' => false,
325 'extmetadatafilter' => array(),
326 );
327 }
328 $version = $metadataOpts['version'];
329 $vals = array();
330 // Timestamp is shown even if the file is revdelete'd in interface
331 // so do same here.
332 if ( isset( $prop['timestamp'] ) ) {
333 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() );
334 }
335
336 $user = isset( $prop['user'] );
337 $userid = isset( $prop['userid'] );
338
339 if ( $user || $userid ) {
340 if ( $file->isDeleted( File::DELETED_USER ) ) {
341 $vals['userhidden'] = '';
342 } else {
343 if ( $user ) {
344 $vals['user'] = $file->getUser();
345 }
346 if ( $userid ) {
347 $vals['userid'] = $file->getUser( 'id' );
348 }
349 if ( !$file->getUser( 'id' ) ) {
350 $vals['anon'] = '';
351 }
352 }
353 }
354
355 // This is shown even if the file is revdelete'd in interface
356 // so do same here.
357 if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) {
358 $vals['size'] = intval( $file->getSize() );
359 $vals['width'] = intval( $file->getWidth() );
360 $vals['height'] = intval( $file->getHeight() );
361
362 $pageCount = $file->pageCount();
363 if ( $pageCount !== false ) {
364 $vals['pagecount'] = $pageCount;
365 }
366 }
367
368 $pcomment = isset( $prop['parsedcomment'] );
369 $comment = isset( $prop['comment'] );
370
371 if ( $pcomment || $comment ) {
372 if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
373 $vals['commenthidden'] = '';
374 } else {
375 if ( $pcomment ) {
376 $vals['parsedcomment'] = Linker::formatComment(
377 $file->getDescription(), $file->getTitle() );
378 }
379 if ( $comment ) {
380 $vals['comment'] = $file->getDescription();
381 }
382 }
383 }
384
385 $url = isset( $prop['url'] );
386 $sha1 = isset( $prop['sha1'] );
387 $meta = isset( $prop['metadata'] );
388 $extmetadata = isset( $prop['extmetadata'] );
389 $mime = isset( $prop['mime'] );
390 $mediatype = isset( $prop['mediatype'] );
391 $archive = isset( $prop['archivename'] );
392 $bitdepth = isset( $prop['bitdepth'] );
393 $uploadwarning = isset( $prop['uploadwarning'] );
394
395 if ( ( $url || $sha1 || $meta || $mime || $mediatype || $archive || $bitdepth )
396 && $file->isDeleted( File::DELETED_FILE )
397 ) {
398 $vals['filehidden'] = '';
399
400 //Early return, tidier than indenting all following things one level
401 return $vals;
402 }
403
404 if ( $url ) {
405 if ( !is_null( $thumbParams ) ) {
406 $mto = $file->transform( $thumbParams );
407 self::$transformCount++;
408 if ( $mto && !$mto->isError() ) {
409 $vals['thumburl'] = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT );
410
411 // bug 23834 - If the URL's are the same, we haven't resized it, so shouldn't give the wanted
412 // thumbnail sizes for the thumbnail actual size
413 if ( $mto->getUrl() !== $file->getUrl() ) {
414 $vals['thumbwidth'] = intval( $mto->getWidth() );
415 $vals['thumbheight'] = intval( $mto->getHeight() );
416 } else {
417 $vals['thumbwidth'] = intval( $file->getWidth() );
418 $vals['thumbheight'] = intval( $file->getHeight() );
419 }
420
421 if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) {
422 list( , $mime ) = $file->getHandler()->getThumbType(
423 $mto->getExtension(), $file->getMimeType(), $thumbParams );
424 $vals['thumbmime'] = $mime;
425 }
426 } elseif ( $mto && $mto->isError() ) {
427 $vals['thumberror'] = $mto->toText();
428 }
429 }
430 $vals['url'] = wfExpandUrl( $file->getFullURL(), PROTO_CURRENT );
431 $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl(), PROTO_CURRENT );
432 }
433
434 if ( $sha1 ) {
435 $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 );
436 }
437
438 if ( $meta ) {
439 wfSuppressWarnings();
440 $metadata = unserialize( $file->getMetadata() );
441 wfRestoreWarnings();
442 if ( $metadata && $version !== 'latest' ) {
443 $metadata = $file->convertMetadataVersion( $metadata, $version );
444 }
445 $vals['metadata'] = $metadata ? self::processMetaData( $metadata, $result ) : null;
446 }
447
448 if ( $extmetadata ) {
449 // Note, this should return an array where all the keys
450 // start with a letter, and all the values are strings.
451 // Thus there should be no issue with format=xml.
452 $format = new FormatMetadata;
453 $format->setSingleLanguage( !$metadataOpts['multilang'] );
454 $format->getContext()->setLanguage( $metadataOpts['language'] );
455 $extmetaArray = $format->fetchExtendedMetadata( $file );
456 if ( $metadataOpts['extmetadatafilter'] ) {
457 $extmetaArray = array_intersect_key(
458 $extmetaArray, array_flip( $metadataOpts['extmetadatafilter'] )
459 );
460 }
461 $vals['extmetadata'] = $extmetaArray;
462 }
463
464 if ( $mime ) {
465 $vals['mime'] = $file->getMimeType();
466 }
467
468 if ( $mediatype ) {
469 $vals['mediatype'] = $file->getMediaType();
470 }
471
472 if ( $archive && $file->isOld() ) {
473 $vals['archivename'] = $file->getArchiveName();
474 }
475
476 if ( $bitdepth ) {
477 $vals['bitdepth'] = $file->getBitDepth();
478 }
479
480 if ( $uploadwarning ) {
481 $vals['html'] = SpecialUpload::getExistsWarning( UploadBase::getExistsWarning( $file ) );
482 }
483
484 return $vals;
485 }
486
487 /**
488 * Get the count of image transformations performed
489 *
490 * If this is >= TRANSFORM_LIMIT, you should probably stop processing images.
491 *
492 * @return integer count
493 */
494 static function getTransformCount() {
495 return self::$transformCount;
496 }
497
498 /**
499 *
500 * @param $metadata Array
501 * @param $result ApiResult
502 * @return Array
503 */
504 public static function processMetaData( $metadata, $result ) {
505 $retval = array();
506 if ( is_array( $metadata ) ) {
507 foreach ( $metadata as $key => $value ) {
508 $r = array( 'name' => $key );
509 if ( is_array( $value ) ) {
510 $r['value'] = self::processMetaData( $value, $result );
511 } else {
512 $r['value'] = $value;
513 }
514 $retval[] = $r;
515 }
516 }
517 $result->setIndexedTagName( $retval, 'metadata' );
518
519 return $retval;
520 }
521
522 public function getCacheMode( $params ) {
523 return 'public';
524 }
525
526 /**
527 * @param $img File
528 * @param null|string $start
529 * @return string
530 */
531 protected function getContinueStr( $img, $start = null ) {
532 if ( $start === null ) {
533 $start = $img->getTimestamp();
534 }
535
536 return $img->getOriginalTitle()->getDBkey() . '|' . $start;
537 }
538
539 public function getAllowedParams() {
540 global $wgContLang;
541
542 return array(
543 'prop' => array(
544 ApiBase::PARAM_ISMULTI => true,
545 ApiBase::PARAM_DFLT => 'timestamp|user',
546 ApiBase::PARAM_TYPE => self::getPropertyNames()
547 ),
548 'limit' => array(
549 ApiBase::PARAM_TYPE => 'limit',
550 ApiBase::PARAM_DFLT => 1,
551 ApiBase::PARAM_MIN => 1,
552 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
553 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
554 ),
555 'start' => array(
556 ApiBase::PARAM_TYPE => 'timestamp'
557 ),
558 'end' => array(
559 ApiBase::PARAM_TYPE => 'timestamp'
560 ),
561 'urlwidth' => array(
562 ApiBase::PARAM_TYPE => 'integer',
563 ApiBase::PARAM_DFLT => -1
564 ),
565 'urlheight' => array(
566 ApiBase::PARAM_TYPE => 'integer',
567 ApiBase::PARAM_DFLT => -1
568 ),
569 'metadataversion' => array(
570 ApiBase::PARAM_TYPE => 'string',
571 ApiBase::PARAM_DFLT => '1',
572 ),
573 'extmetadatalanguage' => array(
574 ApiBase::PARAM_TYPE => 'string',
575 ApiBase::PARAM_DFLT => $wgContLang->getCode(),
576 ),
577 'extmetadatamultilang' => array(
578 ApiBase::PARAM_TYPE => 'boolean',
579 ApiBase::PARAM_DFLT => false,
580 ),
581 'extmetadatafilter' => array(
582 ApiBase::PARAM_TYPE => 'string',
583 ApiBase::PARAM_ISMULTI => true,
584 ),
585 'urlparam' => array(
586 ApiBase::PARAM_DFLT => '',
587 ApiBase::PARAM_TYPE => 'string',
588 ),
589 'continue' => null,
590 'localonly' => false,
591 );
592 }
593
594 /**
595 * Returns all possible parameters to iiprop
596 *
597 * @param array $filter List of properties to filter out
598 *
599 * @return Array
600 */
601 public static function getPropertyNames( $filter = array() ) {
602 return array_diff( array_keys( self::getProperties() ), $filter );
603 }
604
605 /**
606 * Returns array key value pairs of properties and their descriptions
607 *
608 * @param string $modulePrefix
609 * @return array
610 */
611 private static function getProperties( $modulePrefix = '' ) {
612 return array(
613 'timestamp' => ' timestamp - Adds timestamp for the uploaded version',
614 'user' => ' user - Adds the user who uploaded the image version',
615 'userid' => ' userid - Add the user ID that uploaded the image version',
616 'comment' => ' comment - Comment on the version',
617 'parsedcomment' => ' parsedcomment - Parse the comment on the version',
618 'url' => ' url - Gives URL to the image and the description page',
619 'size' => ' size - Adds the size of the image in bytes and the height, width and page count (if applicable)',
620 'dimensions' => ' dimensions - Alias for size', // For backwards compatibility with Allimages
621 'sha1' => ' sha1 - Adds SHA-1 hash for the image',
622 'mime' => ' mime - Adds MIME type of the image',
623 'thumbmime' => ' thumbmime - Adds MIME type of the image thumbnail' .
624 ' (requires url and param ' . $modulePrefix . 'urlwidth)',
625 'mediatype' => ' mediatype - Adds the media type of the image',
626 'metadata' => ' metadata - Lists Exif metadata for the version of the image',
627 'extmetadata' => ' extmetadata - Lists formatted metadata combined from multiple sources. Results are HTML formatted.',
628 'archivename' => ' archivename - Adds the file name of the archive version for non-latest versions',
629 'bitdepth' => ' bitdepth - Adds the bit depth of the version',
630 'uploadwarning' => ' uploadwarning - Used by the Special:Upload page to get information about an existing file. Not intended for use outside MediaWiki core',
631 );
632 }
633
634 /**
635 * Returns the descriptions for the properties provided by getPropertyNames()
636 *
637 * @param array $filter List of properties to filter out
638 * @param string $modulePrefix
639 * @return array
640 */
641 public static function getPropertyDescriptions( $filter = array(), $modulePrefix = '' ) {
642 return array_merge(
643 array( 'What image information to get:' ),
644 array_values( array_diff_key( self::getProperties( $modulePrefix ), array_flip( $filter ) ) )
645 );
646 }
647
648 /**
649 * Return the API documentation for the parameters.
650 * @return Array parameter documentation.
651 */
652 public function getParamDescription() {
653 $p = $this->getModulePrefix();
654
655 return array(
656 'prop' => self::getPropertyDescriptions( array(), $p ),
657 'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
658 'For performance reasons if this option is used, ' .
659 'no more than ' . self::TRANSFORM_LIMIT . ' scaled images will be returned.' ),
660 'urlheight' => "Similar to {$p}urlwidth.",
661 'urlparam' => array( "A handler specific parameter string. For example, pdf's ",
662 "might use 'page15-100px'. {$p}urlwidth must be used and be consistent with {$p}urlparam" ),
663 'limit' => 'How many image revisions to return per image',
664 'start' => 'Timestamp to start listing from',
665 'end' => 'Timestamp to stop listing at',
666 'metadataversion' => array( "Version of metadata to use. if 'latest' is specified, use latest version.",
667 "Defaults to '1' for backwards compatibility" ),
668 'extmetadatalanguage' => array( 'What language to fetch extmetadata in. This affects both which',
669 'translation to fetch, if multiple are available, as well as how things',
670 'like numbers and various values are formatted.' ),
671 'extmetadatamultilang' => 'If translations for extmetadata property are available, fetch all of them.',
672 'extmetadatafilter' => "If specified and non-empty, only these keys will be returned for {$p}prop=extmetadata",
673 'continue' => 'If the query response includes a continue value, use it here to get another page of results',
674 'localonly' => 'Look only for files in the local repository',
675 );
676 }
677
678 public static function getResultPropertiesFiltered( $filter = array() ) {
679 $props = array(
680 'timestamp' => array(
681 'timestamp' => 'timestamp'
682 ),
683 'user' => array(
684 'userhidden' => 'boolean',
685 'user' => 'string',
686 'anon' => 'boolean'
687 ),
688 'userid' => array(
689 'userhidden' => 'boolean',
690 'userid' => 'integer',
691 'anon' => 'boolean'
692 ),
693 'size' => array(
694 'size' => 'integer',
695 'width' => 'integer',
696 'height' => 'integer',
697 'pagecount' => array(
698 ApiBase::PROP_TYPE => 'integer',
699 ApiBase::PROP_NULLABLE => true
700 )
701 ),
702 'dimensions' => array(
703 'size' => 'integer',
704 'width' => 'integer',
705 'height' => 'integer',
706 'pagecount' => array(
707 ApiBase::PROP_TYPE => 'integer',
708 ApiBase::PROP_NULLABLE => true
709 )
710 ),
711 'comment' => array(
712 'commenthidden' => 'boolean',
713 'comment' => array(
714 ApiBase::PROP_TYPE => 'string',
715 ApiBase::PROP_NULLABLE => true
716 )
717 ),
718 'parsedcomment' => array(
719 'commenthidden' => 'boolean',
720 'parsedcomment' => array(
721 ApiBase::PROP_TYPE => 'string',
722 ApiBase::PROP_NULLABLE => true
723 )
724 ),
725 'url' => array(
726 'filehidden' => 'boolean',
727 'thumburl' => array(
728 ApiBase::PROP_TYPE => 'string',
729 ApiBase::PROP_NULLABLE => true
730 ),
731 'thumbwidth' => array(
732 ApiBase::PROP_TYPE => 'integer',
733 ApiBase::PROP_NULLABLE => true
734 ),
735 'thumbheight' => array(
736 ApiBase::PROP_TYPE => 'integer',
737 ApiBase::PROP_NULLABLE => true
738 ),
739 'thumberror' => array(
740 ApiBase::PROP_TYPE => 'string',
741 ApiBase::PROP_NULLABLE => true
742 ),
743 'url' => array(
744 ApiBase::PROP_TYPE => 'string',
745 ApiBase::PROP_NULLABLE => true
746 ),
747 'descriptionurl' => array(
748 ApiBase::PROP_TYPE => 'string',
749 ApiBase::PROP_NULLABLE => true
750 )
751 ),
752 'sha1' => array(
753 'filehidden' => 'boolean',
754 'sha1' => array(
755 ApiBase::PROP_TYPE => 'string',
756 ApiBase::PROP_NULLABLE => true
757 )
758 ),
759 'mime' => array(
760 'filehidden' => 'boolean',
761 'mime' => array(
762 ApiBase::PROP_TYPE => 'string',
763 ApiBase::PROP_NULLABLE => true
764 )
765 ),
766 'thumbmime' => array(
767 'filehidden' => 'boolean',
768 'thumbmime' => array(
769 ApiBase::PROP_TYPE => 'string',
770 ApiBase::PROP_NULLABLE => true
771 )
772 ),
773 'mediatype' => array(
774 'filehidden' => 'boolean',
775 'mediatype' => array(
776 ApiBase::PROP_TYPE => 'string',
777 ApiBase::PROP_NULLABLE => true
778 )
779 ),
780 'archivename' => array(
781 'filehidden' => 'boolean',
782 'archivename' => array(
783 ApiBase::PROP_TYPE => 'string',
784 ApiBase::PROP_NULLABLE => true
785 )
786 ),
787 'bitdepth' => array(
788 'filehidden' => 'boolean',
789 'bitdepth' => array(
790 ApiBase::PROP_TYPE => 'integer',
791 ApiBase::PROP_NULLABLE => true
792 )
793 ),
794 );
795
796 return array_diff_key( $props, array_flip( $filter ) );
797 }
798
799 public function getResultProperties() {
800 return self::getResultPropertiesFiltered();
801 }
802
803 public function getDescription() {
804 return 'Returns image information and upload history';
805 }
806
807 public function getPossibleErrors() {
808 $p = $this->getModulePrefix();
809
810 return array_merge( parent::getPossibleErrors(), array(
811 array( 'code' => "{$p}urlwidth", 'info' => "{$p}urlheight cannot be used without {$p}urlwidth" ),
812 array( 'code' => 'urlparam', 'info' => "Invalid value for {$p}urlparam" ),
813 array( 'code' => 'urlparam_no_width', 'info' => "{$p}urlparam requires {$p}urlwidth" ),
814 ) );
815 }
816
817 public function getExamples() {
818 return array(
819 'api.php?action=query&titles=File:Albert%20Einstein%20Head.jpg&prop=imageinfo',
820 'api.php?action=query&titles=File:Test.jpg&prop=imageinfo&iilimit=50&iiend=20071231235959&iiprop=timestamp|user|url',
821 );
822 }
823
824 public function getHelpUrls() {
825 return 'https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii';
826 }
827 }