Revert "Made LCStoreDB try to use a separate DB connection"
[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( ApiQuery $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|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 String of otherParams (iiurlparam).
256 * @return array Array of parameters for transform.
257 */
258 protected function mergeThumbParams( $image, $thumbParams, $otherParams ) {
259 if ( !isset( $thumbParams['width'] ) && isset( $thumbParams['height'] ) ) {
260 // We want to limit only by height in this situation, so pass the
261 // image's full width as the limiting width. But some file types
262 // don't have a width of their own, so pick something arbitrary so
263 // thumbnailing the default icon works.
264 if ( $image->getWidth() <= 0 ) {
265 $thumbParams['width'] = max( $this->getConfig()->get( 'ThumbLimits' ) );
266 } else {
267 $thumbParams['width'] = $image->getWidth();
268 }
269 }
270
271 if ( !$otherParams ) {
272 return $thumbParams;
273 }
274 $p = $this->getModulePrefix();
275
276 $h = $image->getHandler();
277 if ( !$h ) {
278 $this->setWarning( 'Could not create thumbnail because ' .
279 $image->getName() . ' does not have an associated image handler' );
280
281 return $thumbParams;
282 }
283
284 $paramList = $h->parseParamString( $otherParams );
285 if ( !$paramList ) {
286 // Just set a warning (instead of dieUsage), as in many cases
287 // we could still render the image using width and height parameters,
288 // and this type of thing could happen between different versions of
289 // handlers.
290 $this->setWarning( "Could not parse {$p}urlparam for " . $image->getName()
291 . '. Using only width and height' );
292
293 return $thumbParams;
294 }
295
296 if ( isset( $paramList['width'] ) ) {
297 if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) {
298 $this->setWarning( "Ignoring width value set in {$p}urlparam ({$paramList['width']}) "
299 . "in favor of width value derived from {$p}urlwidth/{$p}urlheight "
300 . "({$thumbParams['width']})" );
301 }
302 }
303
304 foreach ( $paramList as $name => $value ) {
305 if ( !$h->validateParam( $name, $value ) ) {
306 $this->dieUsage( "Invalid value for {$p}urlparam ($name=$value)", "urlparam" );
307 }
308 }
309
310 return $thumbParams + $paramList;
311 }
312
313 /**
314 * Get result information for an image revision
315 *
316 * @param File $file
317 * @param array $prop Array of properties to get (in the keys)
318 * @param ApiResult $result
319 * @param array $thumbParams Containing 'width' and 'height' items, or null
320 * @param array|bool|string $opts Options for data fetching.
321 * This is an array consisting of the keys:
322 * 'version': The metadata version for the metadata option
323 * 'language': The language for extmetadata property
324 * 'multilang': Return all translations in extmetadata property
325 * 'revdelUser': User to use when checking whether to show revision-deleted fields.
326 * @return array Result array
327 */
328 static function getInfo( $file, $prop, $result, $thumbParams = null, $opts = false ) {
329 global $wgContLang;
330
331 $anyHidden = false;
332
333 if ( !$opts || is_string( $opts ) ) {
334 $opts = array(
335 'version' => $opts ?: 'latest',
336 'language' => $wgContLang,
337 'multilang' => false,
338 'extmetadatafilter' => array(),
339 'revdelUser' => null,
340 );
341 }
342 $version = $opts['version'];
343 $vals = array();
344 // Timestamp is shown even if the file is revdelete'd in interface
345 // so do same here.
346 if ( isset( $prop['timestamp'] ) ) {
347 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() );
348 }
349
350 // Handle external callers who don't pass revdelUser
351 if ( isset( $opts['revdelUser'] ) && $opts['revdelUser'] ) {
352 $revdelUser = $opts['revdelUser'];
353 $canShowField = function ( $field ) use ( $file, $revdelUser ) {
354 return $file->userCan( $field, $revdelUser );
355 };
356 } else {
357 $canShowField = function ( $field ) use ( $file ) {
358 return !$file->isDeleted( $field );
359 };
360 }
361
362 $user = isset( $prop['user'] );
363 $userid = isset( $prop['userid'] );
364
365 if ( $user || $userid ) {
366 if ( $file->isDeleted( File::DELETED_USER ) ) {
367 $vals['userhidden'] = '';
368 $anyHidden = true;
369 }
370 if ( $canShowField( File::DELETED_USER ) ) {
371 if ( $user ) {
372 $vals['user'] = $file->getUser();
373 }
374 if ( $userid ) {
375 $vals['userid'] = $file->getUser( 'id' );
376 }
377 if ( !$file->getUser( 'id' ) ) {
378 $vals['anon'] = '';
379 }
380 }
381 }
382
383 // This is shown even if the file is revdelete'd in interface
384 // so do same here.
385 if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) {
386 $vals['size'] = intval( $file->getSize() );
387 $vals['width'] = intval( $file->getWidth() );
388 $vals['height'] = intval( $file->getHeight() );
389
390 $pageCount = $file->pageCount();
391 if ( $pageCount !== false ) {
392 $vals['pagecount'] = $pageCount;
393 }
394 }
395
396 $pcomment = isset( $prop['parsedcomment'] );
397 $comment = isset( $prop['comment'] );
398
399 if ( $pcomment || $comment ) {
400 if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
401 $vals['commenthidden'] = '';
402 $anyHidden = true;
403 }
404 if ( $canShowField( File::DELETED_COMMENT ) ) {
405 if ( $pcomment ) {
406 $vals['parsedcomment'] = Linker::formatComment(
407 $file->getDescription( File::RAW ), $file->getTitle() );
408 }
409 if ( $comment ) {
410 $vals['comment'] = $file->getDescription( File::RAW );
411 }
412 }
413 }
414
415 $canonicaltitle = isset( $prop['canonicaltitle'] );
416 $url = isset( $prop['url'] );
417 $sha1 = isset( $prop['sha1'] );
418 $meta = isset( $prop['metadata'] );
419 $extmetadata = isset( $prop['extmetadata'] );
420 $commonmeta = isset( $prop['commonmetadata'] );
421 $mime = isset( $prop['mime'] );
422 $mediatype = isset( $prop['mediatype'] );
423 $archive = isset( $prop['archivename'] );
424 $bitdepth = isset( $prop['bitdepth'] );
425 $uploadwarning = isset( $prop['uploadwarning'] );
426
427 if ( $uploadwarning ) {
428 $vals['html'] = SpecialUpload::getExistsWarning( UploadBase::getExistsWarning( $file ) );
429 }
430
431 if ( $file->isDeleted( File::DELETED_FILE ) ) {
432 $vals['filehidden'] = '';
433 $anyHidden = true;
434 }
435
436 if ( $anyHidden && $file->isDeleted( File::DELETED_RESTRICTED ) ) {
437 $vals['suppressed'] = true;
438 }
439
440 if ( !$canShowField( File::DELETED_FILE ) ) {
441 //Early return, tidier than indenting all following things one level
442 return $vals;
443 }
444
445 if ( $canonicaltitle ) {
446 $vals['canonicaltitle'] = $file->getTitle()->getPrefixedText();
447 }
448
449 if ( $url ) {
450 if ( !is_null( $thumbParams ) ) {
451 $mto = $file->transform( $thumbParams );
452 self::$transformCount++;
453 if ( $mto && !$mto->isError() ) {
454 $vals['thumburl'] = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT );
455
456 // bug 23834 - If the URL's are the same, we haven't resized it, so shouldn't give the wanted
457 // thumbnail sizes for the thumbnail actual size
458 if ( $mto->getUrl() !== $file->getUrl() ) {
459 $vals['thumbwidth'] = intval( $mto->getWidth() );
460 $vals['thumbheight'] = intval( $mto->getHeight() );
461 } else {
462 $vals['thumbwidth'] = intval( $file->getWidth() );
463 $vals['thumbheight'] = intval( $file->getHeight() );
464 }
465
466 if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) {
467 list( , $mime ) = $file->getHandler()->getThumbType(
468 $mto->getExtension(), $file->getMimeType(), $thumbParams );
469 $vals['thumbmime'] = $mime;
470 }
471 } elseif ( $mto && $mto->isError() ) {
472 $vals['thumberror'] = $mto->toText();
473 }
474 }
475 $vals['url'] = wfExpandUrl( $file->getFullURL(), PROTO_CURRENT );
476 $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl(), PROTO_CURRENT );
477 }
478
479 if ( $sha1 ) {
480 $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 );
481 }
482
483 if ( $meta ) {
484 wfSuppressWarnings();
485 $metadata = unserialize( $file->getMetadata() );
486 wfRestoreWarnings();
487 if ( $metadata && $version !== 'latest' ) {
488 $metadata = $file->convertMetadataVersion( $metadata, $version );
489 }
490 $vals['metadata'] = $metadata ? self::processMetaData( $metadata, $result ) : null;
491 }
492 if ( $commonmeta ) {
493 $metaArray = $file->getCommonMetaArray();
494 $vals['commonmetadata'] = $metaArray ? self::processMetaData( $metaArray, $result ) : array();
495 }
496
497 if ( $extmetadata ) {
498 // Note, this should return an array where all the keys
499 // start with a letter, and all the values are strings.
500 // Thus there should be no issue with format=xml.
501 $format = new FormatMetadata;
502 $format->setSingleLanguage( !$opts['multilang'] );
503 $format->getContext()->setLanguage( $opts['language'] );
504 $extmetaArray = $format->fetchExtendedMetadata( $file );
505 if ( $opts['extmetadatafilter'] ) {
506 $extmetaArray = array_intersect_key(
507 $extmetaArray, array_flip( $opts['extmetadatafilter'] )
508 );
509 }
510 $vals['extmetadata'] = $extmetaArray;
511 }
512
513 if ( $mime ) {
514 $vals['mime'] = $file->getMimeType();
515 }
516
517 if ( $mediatype ) {
518 $vals['mediatype'] = $file->getMediaType();
519 }
520
521 if ( $archive && $file->isOld() ) {
522 $vals['archivename'] = $file->getArchiveName();
523 }
524
525 if ( $bitdepth ) {
526 $vals['bitdepth'] = $file->getBitDepth();
527 }
528
529 return $vals;
530 }
531
532 /**
533 * Get the count of image transformations performed
534 *
535 * If this is >= TRANSFORM_LIMIT, you should probably stop processing images.
536 *
537 * @return int Count
538 */
539 static function getTransformCount() {
540 return self::$transformCount;
541 }
542
543 /**
544 *
545 * @param array $metadata
546 * @param ApiResult $result
547 * @return array
548 */
549 public static function processMetaData( $metadata, $result ) {
550 $retval = array();
551 if ( is_array( $metadata ) ) {
552 foreach ( $metadata as $key => $value ) {
553 $r = array( 'name' => $key );
554 if ( is_array( $value ) ) {
555 $r['value'] = self::processMetaData( $value, $result );
556 } else {
557 $r['value'] = $value;
558 }
559 $retval[] = $r;
560 }
561 }
562 $result->setIndexedTagName( $retval, 'metadata' );
563
564 return $retval;
565 }
566
567 public function getCacheMode( $params ) {
568 if ( $this->userCanSeeRevDel() ) {
569 return 'private';
570 }
571
572 return 'public';
573 }
574
575 /**
576 * @param File $img
577 * @param null|string $start
578 * @return string
579 */
580 protected function getContinueStr( $img, $start = null ) {
581 if ( $start === null ) {
582 $start = $img->getTimestamp();
583 }
584
585 return $img->getOriginalTitle()->getDBkey() . '|' . $start;
586 }
587
588 public function getAllowedParams() {
589 global $wgContLang;
590
591 return array(
592 'prop' => array(
593 ApiBase::PARAM_ISMULTI => true,
594 ApiBase::PARAM_DFLT => 'timestamp|user',
595 ApiBase::PARAM_TYPE => self::getPropertyNames()
596 ),
597 'limit' => array(
598 ApiBase::PARAM_TYPE => 'limit',
599 ApiBase::PARAM_DFLT => 1,
600 ApiBase::PARAM_MIN => 1,
601 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
602 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
603 ),
604 'start' => array(
605 ApiBase::PARAM_TYPE => 'timestamp'
606 ),
607 'end' => array(
608 ApiBase::PARAM_TYPE => 'timestamp'
609 ),
610 'urlwidth' => array(
611 ApiBase::PARAM_TYPE => 'integer',
612 ApiBase::PARAM_DFLT => -1
613 ),
614 'urlheight' => array(
615 ApiBase::PARAM_TYPE => 'integer',
616 ApiBase::PARAM_DFLT => -1
617 ),
618 'metadataversion' => array(
619 ApiBase::PARAM_TYPE => 'string',
620 ApiBase::PARAM_DFLT => '1',
621 ),
622 'extmetadatalanguage' => array(
623 ApiBase::PARAM_TYPE => 'string',
624 ApiBase::PARAM_DFLT => $wgContLang->getCode(),
625 ),
626 'extmetadatamultilang' => array(
627 ApiBase::PARAM_TYPE => 'boolean',
628 ApiBase::PARAM_DFLT => false,
629 ),
630 'extmetadatafilter' => array(
631 ApiBase::PARAM_TYPE => 'string',
632 ApiBase::PARAM_ISMULTI => true,
633 ),
634 'urlparam' => array(
635 ApiBase::PARAM_DFLT => '',
636 ApiBase::PARAM_TYPE => 'string',
637 ),
638 'continue' => null,
639 'localonly' => false,
640 );
641 }
642
643 /**
644 * Returns all possible parameters to iiprop
645 *
646 * @param array $filter List of properties to filter out
647 *
648 * @return array
649 */
650 public static function getPropertyNames( $filter = array() ) {
651 return array_diff( array_keys( self::getProperties() ), $filter );
652 }
653
654 /**
655 * Returns array key value pairs of properties and their descriptions
656 *
657 * @param string $modulePrefix
658 * @return array
659 */
660 private static function getProperties( $modulePrefix = '' ) {
661 return array(
662 'timestamp' => ' timestamp - Adds timestamp for the uploaded version',
663 'user' => ' user - Adds the user who uploaded the image version',
664 'userid' => ' userid - Add the user ID that uploaded the image version',
665 'comment' => ' comment - Comment on the version',
666 'parsedcomment' => ' parsedcomment - Parse the comment on the version',
667 'canonicaltitle' => ' canonicaltitle - Adds the canonical title of the image file',
668 'url' => ' url - Gives URL to the image and the description page',
669 'size' => ' size - Adds the size of the image in bytes ' .
670 'and the height, width and page count (if applicable)',
671 'dimensions' => ' dimensions - Alias for size', // B/C with Allimages
672 'sha1' => ' sha1 - Adds SHA-1 hash for the image',
673 'mime' => ' mime - Adds MIME type of the image',
674 'thumbmime' => ' thumbmime - Adds MIME type of the image thumbnail' .
675 ' (requires url and param ' . $modulePrefix . 'urlwidth)',
676 'mediatype' => ' mediatype - Adds the media type of the image',
677 'metadata' => ' metadata - Lists Exif metadata for the version of the image',
678 'commonmetadata' => ' commonmetadata - Lists file format generic metadata ' .
679 'for the version of the image',
680 'extmetadata' => ' extmetadata - Lists formatted metadata combined ' .
681 'from multiple sources. Results are HTML formatted.',
682 'archivename' => ' archivename - Adds the file name of the archive ' .
683 'version for non-latest versions',
684 'bitdepth' => ' bitdepth - Adds the bit depth of the version',
685 'uploadwarning' => ' uploadwarning - Used by the Special:Upload page to ' .
686 'get information about an existing file. Not intended for use outside MediaWiki core',
687 );
688 }
689
690 /**
691 * Returns the descriptions for the properties provided by getPropertyNames()
692 *
693 * @param array $filter List of properties to filter out
694 * @param string $modulePrefix
695 * @return array
696 */
697 public static function getPropertyDescriptions( $filter = array(), $modulePrefix = '' ) {
698 return array_merge(
699 array( 'What image information to get:' ),
700 array_values( array_diff_key( self::getProperties( $modulePrefix ), array_flip( $filter ) ) )
701 );
702 }
703
704 /**
705 * Return the API documentation for the parameters.
706 * @return array Parameter documentation.
707 */
708 public function getParamDescription() {
709 $p = $this->getModulePrefix();
710
711 return array(
712 'prop' => self::getPropertyDescriptions( array(), $p ),
713 'urlwidth' => array(
714 "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
715 'For performance reasons if this option is used, ' .
716 'no more than ' . self::TRANSFORM_LIMIT . ' scaled images will be returned.'
717 ),
718 'urlheight' => "Similar to {$p}urlwidth.",
719 'urlparam' => array( "A handler specific parameter string. For example, pdf's ",
720 "might use 'page15-100px'. {$p}urlwidth must be used and be consistent with {$p}urlparam" ),
721 'limit' => 'How many image revisions to return per image',
722 'start' => 'Timestamp to start listing from',
723 'end' => 'Timestamp to stop listing at',
724 'metadataversion'
725 => array( "Version of metadata to use. if 'latest' is specified, use latest version.",
726 "Defaults to '1' for backwards compatibility" ),
727 'extmetadatalanguage' => array(
728 'What language to fetch extmetadata in. This affects both which',
729 'translation to fetch, if multiple are available, as well as how things',
730 'like numbers and various values are formatted.'
731 ),
732 'extmetadatamultilang'
733 =>'If translations for extmetadata property are available, fetch all of them.',
734 'extmetadatafilter'
735 => "If specified and non-empty, only these keys will be returned for {$p}prop=extmetadata",
736 'continue' => 'If the query response includes a continue value, ' .
737 'use it here to get another page of results',
738 'localonly' => 'Look only for files in the local repository',
739 );
740 }
741
742 public static function getResultPropertiesFiltered( $filter = array() ) {
743 $props = array(
744 'timestamp' => array(
745 'timestamp' => 'timestamp'
746 ),
747 'user' => array(
748 'userhidden' => 'boolean',
749 'user' => 'string',
750 'anon' => 'boolean'
751 ),
752 'userid' => array(
753 'userhidden' => 'boolean',
754 'userid' => 'integer',
755 'anon' => 'boolean'
756 ),
757 'size' => array(
758 'size' => 'integer',
759 'width' => 'integer',
760 'height' => 'integer',
761 'pagecount' => array(
762 ApiBase::PROP_TYPE => 'integer',
763 ApiBase::PROP_NULLABLE => true
764 )
765 ),
766 'dimensions' => array(
767 'size' => 'integer',
768 'width' => 'integer',
769 'height' => 'integer',
770 'pagecount' => array(
771 ApiBase::PROP_TYPE => 'integer',
772 ApiBase::PROP_NULLABLE => true
773 )
774 ),
775 'comment' => array(
776 'commenthidden' => 'boolean',
777 'comment' => array(
778 ApiBase::PROP_TYPE => 'string',
779 ApiBase::PROP_NULLABLE => true
780 )
781 ),
782 'parsedcomment' => array(
783 'commenthidden' => 'boolean',
784 'parsedcomment' => array(
785 ApiBase::PROP_TYPE => 'string',
786 ApiBase::PROP_NULLABLE => true
787 )
788 ),
789 'canonicaltitle' => array(
790 'canonicaltitle' => array(
791 ApiBase::PROP_TYPE => 'string',
792 ApiBase::PROP_NULLABLE => true
793 )
794 ),
795 'url' => array(
796 'filehidden' => 'boolean',
797 'thumburl' => array(
798 ApiBase::PROP_TYPE => 'string',
799 ApiBase::PROP_NULLABLE => true
800 ),
801 'thumbwidth' => array(
802 ApiBase::PROP_TYPE => 'integer',
803 ApiBase::PROP_NULLABLE => true
804 ),
805 'thumbheight' => array(
806 ApiBase::PROP_TYPE => 'integer',
807 ApiBase::PROP_NULLABLE => true
808 ),
809 'thumberror' => array(
810 ApiBase::PROP_TYPE => 'string',
811 ApiBase::PROP_NULLABLE => true
812 ),
813 'url' => array(
814 ApiBase::PROP_TYPE => 'string',
815 ApiBase::PROP_NULLABLE => true
816 ),
817 'descriptionurl' => array(
818 ApiBase::PROP_TYPE => 'string',
819 ApiBase::PROP_NULLABLE => true
820 )
821 ),
822 'sha1' => array(
823 'filehidden' => 'boolean',
824 'sha1' => array(
825 ApiBase::PROP_TYPE => 'string',
826 ApiBase::PROP_NULLABLE => true
827 )
828 ),
829 'mime' => array(
830 'filehidden' => 'boolean',
831 'mime' => array(
832 ApiBase::PROP_TYPE => 'string',
833 ApiBase::PROP_NULLABLE => true
834 )
835 ),
836 'thumbmime' => array(
837 'filehidden' => 'boolean',
838 'thumbmime' => array(
839 ApiBase::PROP_TYPE => 'string',
840 ApiBase::PROP_NULLABLE => true
841 )
842 ),
843 'mediatype' => array(
844 'filehidden' => 'boolean',
845 'mediatype' => array(
846 ApiBase::PROP_TYPE => 'string',
847 ApiBase::PROP_NULLABLE => true
848 )
849 ),
850 'archivename' => array(
851 'filehidden' => 'boolean',
852 'archivename' => array(
853 ApiBase::PROP_TYPE => 'string',
854 ApiBase::PROP_NULLABLE => true
855 )
856 ),
857 'bitdepth' => array(
858 'filehidden' => 'boolean',
859 'bitdepth' => array(
860 ApiBase::PROP_TYPE => 'integer',
861 ApiBase::PROP_NULLABLE => true
862 )
863 ),
864 );
865
866 return array_diff_key( $props, array_flip( $filter ) );
867 }
868
869 public function getResultProperties() {
870 return self::getResultPropertiesFiltered();
871 }
872
873 public function getDescription() {
874 return 'Returns image information and upload history.';
875 }
876
877 public function getPossibleErrors() {
878 $p = $this->getModulePrefix();
879
880 return array_merge( parent::getPossibleErrors(), array(
881 array( 'code' => "{$p}urlwidth", 'info' => "{$p}urlheight cannot be used without {$p}urlwidth" ),
882 array( 'code' => 'urlparam', 'info' => "Invalid value for {$p}urlparam" ),
883 array( 'code' => 'urlparam_no_width', 'info' => "{$p}urlparam requires {$p}urlwidth" ),
884 ) );
885 }
886
887 public function getExamples() {
888 return array(
889 'api.php?action=query&titles=File:Albert%20Einstein%20Head.jpg&prop=imageinfo',
890 'api.php?action=query&titles=File:Test.jpg&prop=imageinfo&iilimit=50&' .
891 'iiend=20071231235959&iiprop=timestamp|user|url',
892 );
893 }
894
895 public function getHelpUrls() {
896 return 'https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii';
897 }
898 }