MediaWiki  REL1_23
ApiQueryImageInfo.php
Go to the documentation of this file.
00001 <?php
00032 class ApiQueryImageInfo extends ApiQueryBase {
00033     const TRANSFORM_LIMIT = 50;
00034     private static $transformCount = 0;
00035 
00036     public function __construct( $query, $moduleName, $prefix = 'ii' ) {
00037         // We allow a subclass to override the prefix, to create a related API
00038         // module. Some other parts of MediaWiki construct this with a null
00039         // $prefix, which used to be ignored when this only took two arguments
00040         if ( is_null( $prefix ) ) {
00041             $prefix = 'ii';
00042         }
00043         parent::__construct( $query, $moduleName, $prefix );
00044     }
00045 
00046     public function execute() {
00047         $params = $this->extractRequestParams();
00048 
00049         $prop = array_flip( $params['prop'] );
00050 
00051         $scale = $this->getScale( $params );
00052 
00053         $opts = array(
00054             'version' => $params['metadataversion'],
00055             'language' => $params['extmetadatalanguage'],
00056             'multilang' => $params['extmetadatamultilang'],
00057             'extmetadatafilter' => $params['extmetadatafilter'],
00058             'revdelUser' => $this->getUser(),
00059         );
00060 
00061         $pageIds = $this->getPageSet()->getAllTitlesByNamespace();
00062         if ( !empty( $pageIds[NS_FILE] ) ) {
00063             $titles = array_keys( $pageIds[NS_FILE] );
00064             asort( $titles ); // Ensure the order is always the same
00065 
00066             $fromTitle = null;
00067             if ( !is_null( $params['continue'] ) ) {
00068                 $cont = explode( '|', $params['continue'] );
00069                 $this->dieContinueUsageIf( count( $cont ) != 2 );
00070                 $fromTitle = strval( $cont[0] );
00071                 $fromTimestamp = $cont[1];
00072                 // Filter out any titles before $fromTitle
00073                 foreach ( $titles as $key => $title ) {
00074                     if ( $title < $fromTitle ) {
00075                         unset( $titles[$key] );
00076                     } else {
00077                         break;
00078                     }
00079                 }
00080             }
00081 
00082             $user = $this->getUser();
00083             $findTitles = array_map( function ( $title ) use ( $user ) {
00084                 return array(
00085                     'title' => $title,
00086                     'private' => $user,
00087                 );
00088             }, $titles );
00089 
00090             if ( $params['localonly'] ) {
00091                 $images = RepoGroup::singleton()->getLocalRepo()->findFiles( $findTitles );
00092             } else {
00093                 $images = RepoGroup::singleton()->findFiles( $findTitles );
00094             }
00095 
00096             $result = $this->getResult();
00097             foreach ( $titles as $title ) {
00098                 $pageId = $pageIds[NS_FILE][$title];
00099                 $start = $title === $fromTitle ? $fromTimestamp : $params['start'];
00100 
00101                 if ( !isset( $images[$title] ) ) {
00102                     if ( isset( $prop['uploadwarning'] ) ) {
00103                         // Uploadwarning needs info about non-existing files
00104                         $images[$title] = wfLocalFile( $title );
00105                     } else {
00106                         $result->addValue(
00107                             array( 'query', 'pages', intval( $pageId ) ),
00108                             'imagerepository', ''
00109                         );
00110                         // The above can't fail because it doesn't increase the result size
00111                         continue;
00112                     }
00113                 }
00114 
00116                 $img = $images[$title];
00117 
00118                 if ( self::getTransformCount() >= self::TRANSFORM_LIMIT ) {
00119                     if ( count( $pageIds[NS_FILE] ) == 1 ) {
00120                         // See the 'the user is screwed' comment below
00121                         $this->setContinueEnumParameter( 'start',
00122                             $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
00123                         );
00124                     } else {
00125                         $this->setContinueEnumParameter( 'continue',
00126                             $this->getContinueStr( $img, $start ) );
00127                     }
00128                     break;
00129                 }
00130 
00131                 $fit = $result->addValue(
00132                     array( 'query', 'pages', intval( $pageId ) ),
00133                     'imagerepository', $img->getRepoName()
00134                 );
00135                 if ( !$fit ) {
00136                     if ( count( $pageIds[NS_FILE] ) == 1 ) {
00137                         // The user is screwed. imageinfo can't be solely
00138                         // responsible for exceeding the limit in this case,
00139                         // so set a query-continue that just returns the same
00140                         // thing again. When the violating queries have been
00141                         // out-continued, the result will get through
00142                         $this->setContinueEnumParameter( 'start',
00143                             $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
00144                         );
00145                     } else {
00146                         $this->setContinueEnumParameter( 'continue',
00147                             $this->getContinueStr( $img, $start ) );
00148                     }
00149                     break;
00150                 }
00151 
00152                 // Check if we can make the requested thumbnail, and get transform parameters.
00153                 $finalThumbParams = $this->mergeThumbParams( $img, $scale, $params['urlparam'] );
00154 
00155                 // Get information about the current version first
00156                 // Check that the current version is within the start-end boundaries
00157                 $gotOne = false;
00158                 if (
00159                     ( is_null( $start ) || $img->getTimestamp() <= $start ) &&
00160                     ( is_null( $params['end'] ) || $img->getTimestamp() >= $params['end'] )
00161                 ) {
00162                     $gotOne = true;
00163 
00164                     $fit = $this->addPageSubItem( $pageId,
00165                         self::getInfo( $img, $prop, $result,
00166                             $finalThumbParams, $opts
00167                         )
00168                     );
00169                     if ( !$fit ) {
00170                         if ( count( $pageIds[NS_FILE] ) == 1 ) {
00171                             // See the 'the user is screwed' comment above
00172                             $this->setContinueEnumParameter( 'start',
00173                                 wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) );
00174                         } else {
00175                             $this->setContinueEnumParameter( 'continue',
00176                                 $this->getContinueStr( $img ) );
00177                         }
00178                         break;
00179                     }
00180                 }
00181 
00182                 // Now get the old revisions
00183                 // Get one more to facilitate query-continue functionality
00184                 $count = ( $gotOne ? 1 : 0 );
00185                 $oldies = $img->getHistory( $params['limit'] - $count + 1, $start, $params['end'] );
00187                 foreach ( $oldies as $oldie ) {
00188                     if ( ++$count > $params['limit'] ) {
00189                         // We've reached the extra one which shows that there are
00190                         // additional pages to be had. Stop here...
00191                         // Only set a query-continue if there was only one title
00192                         if ( count( $pageIds[NS_FILE] ) == 1 ) {
00193                             $this->setContinueEnumParameter( 'start',
00194                                 wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) );
00195                         }
00196                         break;
00197                     }
00198                     $fit = self::getTransformCount() < self::TRANSFORM_LIMIT &&
00199                         $this->addPageSubItem( $pageId,
00200                             self::getInfo( $oldie, $prop, $result,
00201                                 $finalThumbParams, $opts
00202                             )
00203                         );
00204                     if ( !$fit ) {
00205                         if ( count( $pageIds[NS_FILE] ) == 1 ) {
00206                             $this->setContinueEnumParameter( 'start',
00207                                 wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) );
00208                         } else {
00209                             $this->setContinueEnumParameter( 'continue',
00210                                 $this->getContinueStr( $oldie ) );
00211                         }
00212                         break;
00213                     }
00214                 }
00215                 if ( !$fit ) {
00216                     break;
00217                 }
00218             }
00219         }
00220     }
00221 
00227     public function getScale( $params ) {
00228         $p = $this->getModulePrefix();
00229 
00230         if ( $params['urlwidth'] != -1 ) {
00231             $scale = array();
00232             $scale['width'] = $params['urlwidth'];
00233             $scale['height'] = $params['urlheight'];
00234         } elseif ( $params['urlheight'] != -1 ) {
00235             // Height is specified but width isn't
00236             // Don't set $scale['width']; this signals mergeThumbParams() to fill it with the image's width
00237             $scale = array();
00238             $scale['height'] = $params['urlheight'];
00239         } else {
00240             $scale = null;
00241             if ( $params['urlparam'] ) {
00242                 $this->dieUsage( "{$p}urlparam requires {$p}urlwidth", "urlparam_no_width" );
00243             }
00244         }
00245 
00246         return $scale;
00247     }
00248 
00258     protected function mergeThumbParams( $image, $thumbParams, $otherParams ) {
00259         global $wgThumbLimits;
00260 
00261         if ( !isset( $thumbParams['width'] ) && isset( $thumbParams['height'] ) ) {
00262             // We want to limit only by height in this situation, so pass the
00263             // image's full width as the limiting width. But some file types
00264             // don't have a width of their own, so pick something arbitrary so
00265             // thumbnailing the default icon works.
00266             if ( $image->getWidth() <= 0 ) {
00267                 $thumbParams['width'] = max( $wgThumbLimits );
00268             } else {
00269                 $thumbParams['width'] = $image->getWidth();
00270             }
00271         }
00272 
00273         if ( !$otherParams ) {
00274             return $thumbParams;
00275         }
00276         $p = $this->getModulePrefix();
00277 
00278         $h = $image->getHandler();
00279         if ( !$h ) {
00280             $this->setWarning( 'Could not create thumbnail because ' .
00281                 $image->getName() . ' does not have an associated image handler' );
00282 
00283             return $thumbParams;
00284         }
00285 
00286         $paramList = $h->parseParamString( $otherParams );
00287         if ( !$paramList ) {
00288             // Just set a warning (instead of dieUsage), as in many cases
00289             // we could still render the image using width and height parameters,
00290             // and this type of thing could happen between different versions of
00291             // handlers.
00292             $this->setWarning( "Could not parse {$p}urlparam for " . $image->getName()
00293                 . '. Using only width and height' );
00294 
00295             return $thumbParams;
00296         }
00297 
00298         if ( isset( $paramList['width'] ) ) {
00299             if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) {
00300                 $this->setWarning( "Ignoring width value set in {$p}urlparam ({$paramList['width']}) "
00301                     . "in favor of width value derived from {$p}urlwidth/{$p}urlheight "
00302                     . "({$thumbParams['width']})" );
00303             }
00304         }
00305 
00306         foreach ( $paramList as $name => $value ) {
00307             if ( !$h->validateParam( $name, $value ) ) {
00308                 $this->dieUsage( "Invalid value for {$p}urlparam ($name=$value)", "urlparam" );
00309             }
00310         }
00311 
00312         return $thumbParams + $paramList;
00313     }
00314 
00330     static function getInfo( $file, $prop, $result, $thumbParams = null, $opts = false ) {
00331         global $wgContLang;
00332 
00333         $anyHidden = false;
00334 
00335         if ( !$opts || is_string( $opts ) ) {
00336             $opts = array(
00337                 'version' => $opts ?: 'latest',
00338                 'language' => $wgContLang,
00339                 'multilang' => false,
00340                 'extmetadatafilter' => array(),
00341                 'revdelUser' => null,
00342             );
00343         }
00344         $version = $opts['version'];
00345         $vals = array();
00346         // Timestamp is shown even if the file is revdelete'd in interface
00347         // so do same here.
00348         if ( isset( $prop['timestamp'] ) ) {
00349             $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() );
00350         }
00351 
00352         // Handle external callers who don't pass revdelUser
00353         if ( isset( $opts['revdelUser'] ) && $opts['revdelUser'] ) {
00354             $revdelUser = $opts['revdelUser'];
00355             $canShowField = function ( $field ) use ( $file, $revdelUser ) {
00356                 return $file->userCan( $field, $revdelUser );
00357             };
00358         } else {
00359             $canShowField = function ( $field ) use ( $file ) {
00360                 return !$file->isDeleted( $field );
00361             };
00362         }
00363 
00364         $user = isset( $prop['user'] );
00365         $userid = isset( $prop['userid'] );
00366 
00367         if ( $user || $userid ) {
00368             if ( $file->isDeleted( File::DELETED_USER ) ) {
00369                 $vals['userhidden'] = '';
00370                 $anyHidden = true;
00371             }
00372             if ( $canShowField( File::DELETED_USER ) ) {
00373                 if ( $user ) {
00374                     $vals['user'] = $file->getUser();
00375                 }
00376                 if ( $userid ) {
00377                     $vals['userid'] = $file->getUser( 'id' );
00378                 }
00379                 if ( !$file->getUser( 'id' ) ) {
00380                     $vals['anon'] = '';
00381                 }
00382             }
00383         }
00384 
00385         // This is shown even if the file is revdelete'd in interface
00386         // so do same here.
00387         if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) {
00388             $vals['size'] = intval( $file->getSize() );
00389             $vals['width'] = intval( $file->getWidth() );
00390             $vals['height'] = intval( $file->getHeight() );
00391 
00392             $pageCount = $file->pageCount();
00393             if ( $pageCount !== false ) {
00394                 $vals['pagecount'] = $pageCount;
00395             }
00396         }
00397 
00398         $pcomment = isset( $prop['parsedcomment'] );
00399         $comment = isset( $prop['comment'] );
00400 
00401         if ( $pcomment || $comment ) {
00402             if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
00403                 $vals['commenthidden'] = '';
00404                 $anyHidden = true;
00405             }
00406             if ( $canShowField( File::DELETED_COMMENT ) ) {
00407                 if ( $pcomment ) {
00408                     $vals['parsedcomment'] = Linker::formatComment(
00409                         $file->getDescription( File::RAW ), $file->getTitle() );
00410                 }
00411                 if ( $comment ) {
00412                     $vals['comment'] = $file->getDescription( File::RAW );
00413                 }
00414             }
00415         }
00416 
00417         $canonicaltitle = isset( $prop['canonicaltitle'] );
00418         $url = isset( $prop['url'] );
00419         $sha1 = isset( $prop['sha1'] );
00420         $meta = isset( $prop['metadata'] );
00421         $extmetadata = isset( $prop['extmetadata'] );
00422         $commonmeta = isset( $prop['commonmetadata'] );
00423         $mime = isset( $prop['mime'] );
00424         $mediatype = isset( $prop['mediatype'] );
00425         $archive = isset( $prop['archivename'] );
00426         $bitdepth = isset( $prop['bitdepth'] );
00427         $uploadwarning = isset( $prop['uploadwarning'] );
00428 
00429         if ( $uploadwarning ) {
00430             $vals['html'] = SpecialUpload::getExistsWarning( UploadBase::getExistsWarning( $file ) );
00431         }
00432 
00433         if ( $file->isDeleted( File::DELETED_FILE ) ) {
00434             $vals['filehidden'] = '';
00435             $anyHidden = true;
00436         }
00437 
00438         if ( $anyHidden && $file->isDeleted( File::DELETED_RESTRICTED ) ) {
00439             $vals['suppressed'] = true;
00440         }
00441 
00442         if ( !$canShowField( File::DELETED_FILE ) ) {
00443             //Early return, tidier than indenting all following things one level
00444             return $vals;
00445         }
00446 
00447         if ( $canonicaltitle ) {
00448             $vals['canonicaltitle'] = $file->getTitle()->getPrefixedText();
00449         }
00450 
00451         if ( $url ) {
00452             if ( !is_null( $thumbParams ) ) {
00453                 $mto = $file->transform( $thumbParams );
00454                 self::$transformCount++;
00455                 if ( $mto && !$mto->isError() ) {
00456                     $vals['thumburl'] = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT );
00457 
00458                     // bug 23834 - If the URL's are the same, we haven't resized it, so shouldn't give the wanted
00459                     // thumbnail sizes for the thumbnail actual size
00460                     if ( $mto->getUrl() !== $file->getUrl() ) {
00461                         $vals['thumbwidth'] = intval( $mto->getWidth() );
00462                         $vals['thumbheight'] = intval( $mto->getHeight() );
00463                     } else {
00464                         $vals['thumbwidth'] = intval( $file->getWidth() );
00465                         $vals['thumbheight'] = intval( $file->getHeight() );
00466                     }
00467 
00468                     if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) {
00469                         list( , $mime ) = $file->getHandler()->getThumbType(
00470                             $mto->getExtension(), $file->getMimeType(), $thumbParams );
00471                         $vals['thumbmime'] = $mime;
00472                     }
00473                 } elseif ( $mto && $mto->isError() ) {
00474                     $vals['thumberror'] = $mto->toText();
00475                 }
00476             }
00477             $vals['url'] = wfExpandUrl( $file->getFullURL(), PROTO_CURRENT );
00478             $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl(), PROTO_CURRENT );
00479         }
00480 
00481         if ( $sha1 ) {
00482             $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 );
00483         }
00484 
00485         if ( $meta ) {
00486             wfSuppressWarnings();
00487             $metadata = unserialize( $file->getMetadata() );
00488             wfRestoreWarnings();
00489             if ( $metadata && $version !== 'latest' ) {
00490                 $metadata = $file->convertMetadataVersion( $metadata, $version );
00491             }
00492             $vals['metadata'] = $metadata ? self::processMetaData( $metadata, $result ) : null;
00493         }
00494         if ( $commonmeta ) {
00495             $metaArray = $file->getCommonMetaArray();
00496             $vals['commonmetadata'] = $metaArray ? self::processMetaData( $metaArray, $result ) : array();
00497         }
00498 
00499         if ( $extmetadata ) {
00500             // Note, this should return an array where all the keys
00501             // start with a letter, and all the values are strings.
00502             // Thus there should be no issue with format=xml.
00503             $format = new FormatMetadata;
00504             $format->setSingleLanguage( !$opts['multilang'] );
00505             $format->getContext()->setLanguage( $opts['language'] );
00506             $extmetaArray = $format->fetchExtendedMetadata( $file );
00507             if ( $opts['extmetadatafilter'] ) {
00508                 $extmetaArray = array_intersect_key(
00509                     $extmetaArray, array_flip( $opts['extmetadatafilter'] )
00510                 );
00511             }
00512             $vals['extmetadata'] = $extmetaArray;
00513         }
00514 
00515         if ( $mime ) {
00516             $vals['mime'] = $file->getMimeType();
00517         }
00518 
00519         if ( $mediatype ) {
00520             $vals['mediatype'] = $file->getMediaType();
00521         }
00522 
00523         if ( $archive && $file->isOld() ) {
00524             $vals['archivename'] = $file->getArchiveName();
00525         }
00526 
00527         if ( $bitdepth ) {
00528             $vals['bitdepth'] = $file->getBitDepth();
00529         }
00530 
00531         return $vals;
00532     }
00533 
00541     static function getTransformCount() {
00542         return self::$transformCount;
00543     }
00544 
00551     public static function processMetaData( $metadata, $result ) {
00552         $retval = array();
00553         if ( is_array( $metadata ) ) {
00554             foreach ( $metadata as $key => $value ) {
00555                 $r = array( 'name' => $key );
00556                 if ( is_array( $value ) ) {
00557                     $r['value'] = self::processMetaData( $value, $result );
00558                 } else {
00559                     $r['value'] = $value;
00560                 }
00561                 $retval[] = $r;
00562             }
00563         }
00564         $result->setIndexedTagName( $retval, 'metadata' );
00565 
00566         return $retval;
00567     }
00568 
00569     public function getCacheMode( $params ) {
00570         if ( $this->userCanSeeRevDel() ) {
00571             return 'private';
00572         }
00573 
00574         return 'public';
00575     }
00576 
00582     protected function getContinueStr( $img, $start = null ) {
00583         if ( $start === null ) {
00584             $start = $img->getTimestamp();
00585         }
00586 
00587         return $img->getOriginalTitle()->getDBkey() . '|' . $start;
00588     }
00589 
00590     public function getAllowedParams() {
00591         global $wgContLang;
00592 
00593         return array(
00594             'prop' => array(
00595                 ApiBase::PARAM_ISMULTI => true,
00596                 ApiBase::PARAM_DFLT => 'timestamp|user',
00597                 ApiBase::PARAM_TYPE => self::getPropertyNames()
00598             ),
00599             'limit' => array(
00600                 ApiBase::PARAM_TYPE => 'limit',
00601                 ApiBase::PARAM_DFLT => 1,
00602                 ApiBase::PARAM_MIN => 1,
00603                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
00604                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
00605             ),
00606             'start' => array(
00607                 ApiBase::PARAM_TYPE => 'timestamp'
00608             ),
00609             'end' => array(
00610                 ApiBase::PARAM_TYPE => 'timestamp'
00611             ),
00612             'urlwidth' => array(
00613                 ApiBase::PARAM_TYPE => 'integer',
00614                 ApiBase::PARAM_DFLT => -1
00615             ),
00616             'urlheight' => array(
00617                 ApiBase::PARAM_TYPE => 'integer',
00618                 ApiBase::PARAM_DFLT => -1
00619             ),
00620             'metadataversion' => array(
00621                 ApiBase::PARAM_TYPE => 'string',
00622                 ApiBase::PARAM_DFLT => '1',
00623             ),
00624             'extmetadatalanguage' => array(
00625                 ApiBase::PARAM_TYPE => 'string',
00626                 ApiBase::PARAM_DFLT => $wgContLang->getCode(),
00627             ),
00628             'extmetadatamultilang' => array(
00629                 ApiBase::PARAM_TYPE => 'boolean',
00630                 ApiBase::PARAM_DFLT => false,
00631             ),
00632             'extmetadatafilter' => array(
00633                 ApiBase::PARAM_TYPE => 'string',
00634                 ApiBase::PARAM_ISMULTI => true,
00635             ),
00636             'urlparam' => array(
00637                 ApiBase::PARAM_DFLT => '',
00638                 ApiBase::PARAM_TYPE => 'string',
00639             ),
00640             'continue' => null,
00641             'localonly' => false,
00642         );
00643     }
00644 
00652     public static function getPropertyNames( $filter = array() ) {
00653         return array_diff( array_keys( self::getProperties() ), $filter );
00654     }
00655 
00662     private static function getProperties( $modulePrefix = '' ) {
00663         return array(
00664             'timestamp' =>      ' timestamp     - Adds timestamp for the uploaded version',
00665             'user' =>           ' user          - Adds the user who uploaded the image version',
00666             'userid' =>         ' userid        - Add the user ID that uploaded the image version',
00667             'comment' =>        ' comment       - Comment on the version',
00668             'parsedcomment' =>  ' parsedcomment - Parse the comment on the version',
00669             'canonicaltitle' => ' canonicaltitle - Adds the canonical title of the image file',
00670             'url' =>            ' url           - Gives URL to the image and the description page',
00671             'size' =>           ' size          - Adds the size of the image in bytes ' .
00672                 'and the height, width and page count (if applicable)',
00673             'dimensions' =>     ' dimensions    - Alias for size', // B/C with Allimages
00674             'sha1' =>           ' sha1          - Adds SHA-1 hash for the image',
00675             'mime' =>           ' mime          - Adds MIME type of the image',
00676             'thumbmime' =>      ' thumbmime     - Adds MIME type of the image thumbnail' .
00677                 ' (requires url and param ' . $modulePrefix . 'urlwidth)',
00678             'mediatype' =>      ' mediatype     - Adds the media type of the image',
00679             'metadata' =>       ' metadata      - Lists Exif metadata for the version of the image',
00680             'commonmetadata' => ' commonmetadata - Lists file format generic metadata ' .
00681                 'for the version of the image',
00682             'extmetadata' =>    ' extmetadata   - Lists formatted metadata combined ' .
00683                 'from multiple sources. Results are HTML formatted.',
00684             'archivename' =>    ' archivename   - Adds the file name of the archive ' .
00685                 'version for non-latest versions',
00686             'bitdepth' =>       ' bitdepth      - Adds the bit depth of the version',
00687             'uploadwarning' =>  ' uploadwarning - Used by the Special:Upload page to ' .
00688                 'get information about an existing file. Not intended for use outside MediaWiki core',
00689         );
00690     }
00691 
00699     public static function getPropertyDescriptions( $filter = array(), $modulePrefix = '' ) {
00700         return array_merge(
00701             array( 'What image information to get:' ),
00702             array_values( array_diff_key( self::getProperties( $modulePrefix ), array_flip( $filter ) ) )
00703         );
00704     }
00705 
00710     public function getParamDescription() {
00711         $p = $this->getModulePrefix();
00712 
00713         return array(
00714             'prop' => self::getPropertyDescriptions( array(), $p ),
00715             'urlwidth' => array(
00716                 "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
00717                 'For performance reasons if this option is used, ' .
00718                     'no more than ' . self::TRANSFORM_LIMIT . ' scaled images will be returned.'
00719             ),
00720             'urlheight' => "Similar to {$p}urlwidth.",
00721             'urlparam' => array( "A handler specific parameter string. For example, pdf's ",
00722                 "might use 'page15-100px'. {$p}urlwidth must be used and be consistent with {$p}urlparam" ),
00723             'limit' => 'How many image revisions to return per image',
00724             'start' => 'Timestamp to start listing from',
00725             'end' => 'Timestamp to stop listing at',
00726             'metadataversion'
00727                 => array( "Version of metadata to use. if 'latest' is specified, use latest version.",
00728                 "Defaults to '1' for backwards compatibility" ),
00729             'extmetadatalanguage' => array(
00730                 'What language to fetch extmetadata in. This affects both which',
00731                 'translation to fetch, if multiple are available, as well as how things',
00732                 'like numbers and various values are formatted.'
00733             ),
00734             'extmetadatamultilang'
00735                 =>'If translations for extmetadata property are available, fetch all of them.',
00736             'extmetadatafilter'
00737                 => "If specified and non-empty, only these keys will be returned for {$p}prop=extmetadata",
00738             'continue' => 'If the query response includes a continue value, ' .
00739                 'use it here to get another page of results',
00740             'localonly' => 'Look only for files in the local repository',
00741         );
00742     }
00743 
00744     public static function getResultPropertiesFiltered( $filter = array() ) {
00745         $props = array(
00746             'timestamp' => array(
00747                 'timestamp' => 'timestamp'
00748             ),
00749             'user' => array(
00750                 'userhidden' => 'boolean',
00751                 'user' => 'string',
00752                 'anon' => 'boolean'
00753             ),
00754             'userid' => array(
00755                 'userhidden' => 'boolean',
00756                 'userid' => 'integer',
00757                 'anon' => 'boolean'
00758             ),
00759             'size' => array(
00760                 'size' => 'integer',
00761                 'width' => 'integer',
00762                 'height' => 'integer',
00763                 'pagecount' => array(
00764                     ApiBase::PROP_TYPE => 'integer',
00765                     ApiBase::PROP_NULLABLE => true
00766                 )
00767             ),
00768             'dimensions' => array(
00769                 'size' => 'integer',
00770                 'width' => 'integer',
00771                 'height' => 'integer',
00772                 'pagecount' => array(
00773                     ApiBase::PROP_TYPE => 'integer',
00774                     ApiBase::PROP_NULLABLE => true
00775                 )
00776             ),
00777             'comment' => array(
00778                 'commenthidden' => 'boolean',
00779                 'comment' => array(
00780                     ApiBase::PROP_TYPE => 'string',
00781                     ApiBase::PROP_NULLABLE => true
00782                 )
00783             ),
00784             'parsedcomment' => array(
00785                 'commenthidden' => 'boolean',
00786                 'parsedcomment' => array(
00787                     ApiBase::PROP_TYPE => 'string',
00788                     ApiBase::PROP_NULLABLE => true
00789                 )
00790             ),
00791             'canonicaltitle' => array(
00792                 'canonicaltitle' => array(
00793                     ApiBase::PROP_TYPE => 'string',
00794                     ApiBase::PROP_NULLABLE => true
00795                 )
00796             ),
00797             'url' => array(
00798                 'filehidden' => 'boolean',
00799                 'thumburl' => array(
00800                     ApiBase::PROP_TYPE => 'string',
00801                     ApiBase::PROP_NULLABLE => true
00802                 ),
00803                 'thumbwidth' => array(
00804                     ApiBase::PROP_TYPE => 'integer',
00805                     ApiBase::PROP_NULLABLE => true
00806                 ),
00807                 'thumbheight' => array(
00808                     ApiBase::PROP_TYPE => 'integer',
00809                     ApiBase::PROP_NULLABLE => true
00810                 ),
00811                 'thumberror' => array(
00812                     ApiBase::PROP_TYPE => 'string',
00813                     ApiBase::PROP_NULLABLE => true
00814                 ),
00815                 'url' => array(
00816                     ApiBase::PROP_TYPE => 'string',
00817                     ApiBase::PROP_NULLABLE => true
00818                 ),
00819                 'descriptionurl' => array(
00820                     ApiBase::PROP_TYPE => 'string',
00821                     ApiBase::PROP_NULLABLE => true
00822                 )
00823             ),
00824             'sha1' => array(
00825                 'filehidden' => 'boolean',
00826                 'sha1' => array(
00827                     ApiBase::PROP_TYPE => 'string',
00828                     ApiBase::PROP_NULLABLE => true
00829                 )
00830             ),
00831             'mime' => array(
00832                 'filehidden' => 'boolean',
00833                 'mime' => array(
00834                     ApiBase::PROP_TYPE => 'string',
00835                     ApiBase::PROP_NULLABLE => true
00836                 )
00837             ),
00838             'thumbmime' => array(
00839                 'filehidden' => 'boolean',
00840                 'thumbmime' => array(
00841                     ApiBase::PROP_TYPE => 'string',
00842                     ApiBase::PROP_NULLABLE => true
00843                 )
00844             ),
00845             'mediatype' => array(
00846                 'filehidden' => 'boolean',
00847                 'mediatype' => array(
00848                     ApiBase::PROP_TYPE => 'string',
00849                     ApiBase::PROP_NULLABLE => true
00850                 )
00851             ),
00852             'archivename' => array(
00853                 'filehidden' => 'boolean',
00854                 'archivename' => array(
00855                     ApiBase::PROP_TYPE => 'string',
00856                     ApiBase::PROP_NULLABLE => true
00857                 )
00858             ),
00859             'bitdepth' => array(
00860                 'filehidden' => 'boolean',
00861                 'bitdepth' => array(
00862                     ApiBase::PROP_TYPE => 'integer',
00863                     ApiBase::PROP_NULLABLE => true
00864                 )
00865             ),
00866         );
00867 
00868         return array_diff_key( $props, array_flip( $filter ) );
00869     }
00870 
00871     public function getResultProperties() {
00872         return self::getResultPropertiesFiltered();
00873     }
00874 
00875     public function getDescription() {
00876         return 'Returns image information and upload history.';
00877     }
00878 
00879     public function getPossibleErrors() {
00880         $p = $this->getModulePrefix();
00881 
00882         return array_merge( parent::getPossibleErrors(), array(
00883             array( 'code' => "{$p}urlwidth", 'info' => "{$p}urlheight cannot be used without {$p}urlwidth" ),
00884             array( 'code' => 'urlparam', 'info' => "Invalid value for {$p}urlparam" ),
00885             array( 'code' => 'urlparam_no_width', 'info' => "{$p}urlparam requires {$p}urlwidth" ),
00886         ) );
00887     }
00888 
00889     public function getExamples() {
00890         return array(
00891             'api.php?action=query&titles=File:Albert%20Einstein%20Head.jpg&prop=imageinfo',
00892             'api.php?action=query&titles=File:Test.jpg&prop=imageinfo&iilimit=50&' .
00893                 'iiend=20071231235959&iiprop=timestamp|user|url',
00894         );
00895     }
00896 
00897     public function getHelpUrls() {
00898         return 'https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii';
00899     }
00900 }