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