MediaWiki
REL1_19
|
00001 <?php 00032 class ApiQueryImageInfo extends ApiQueryBase { 00033 00034 public function __construct( $query, $moduleName, $prefix = 'ii' ) { 00035 // We allow a subclass to override the prefix, to create a related API module. 00036 // Some other parts of MediaWiki construct this with a null $prefix, which used to be ignored when this only took two arguments 00037 if ( is_null( $prefix ) ) { 00038 $prefix = 'ii'; 00039 } 00040 parent::__construct( $query, $moduleName, $prefix ); 00041 } 00042 00043 public function execute() { 00044 $params = $this->extractRequestParams(); 00045 00046 $prop = array_flip( $params['prop'] ); 00047 00048 $scale = $this->getScale( $params ); 00049 00050 $pageIds = $this->getPageSet()->getAllTitlesByNamespace(); 00051 if ( !empty( $pageIds[NS_FILE] ) ) { 00052 $titles = array_keys( $pageIds[NS_FILE] ); 00053 asort( $titles ); // Ensure the order is always the same 00054 00055 $skip = false; 00056 if ( !is_null( $params['continue'] ) ) { 00057 $skip = true; 00058 $cont = explode( '|', $params['continue'] ); 00059 if ( count( $cont ) != 2 ) { 00060 $this->dieUsage( 'Invalid continue param. You should pass the original ' . 00061 'value returned by the previous query', '_badcontinue' ); 00062 } 00063 $fromTitle = strval( $cont[0] ); 00064 $fromTimestamp = $cont[1]; 00065 // Filter out any titles before $fromTitle 00066 foreach ( $titles as $key => $title ) { 00067 if ( $title < $fromTitle ) { 00068 unset( $titles[$key] ); 00069 } else { 00070 break; 00071 } 00072 } 00073 } 00074 00075 $result = $this->getResult(); 00076 $images = RepoGroup::singleton()->findFiles( $titles ); 00077 foreach ( $images as $img ) { 00078 // Skip redirects 00079 if ( $img->getOriginalTitle()->isRedirect() ) { 00080 continue; 00081 } 00082 00083 $start = $skip ? $fromTimestamp : $params['start']; 00084 $pageId = $pageIds[NS_IMAGE][ $img->getOriginalTitle()->getDBkey() ]; 00085 00086 $fit = $result->addValue( 00087 array( 'query', 'pages', intval( $pageId ) ), 00088 'imagerepository', $img->getRepoName() 00089 ); 00090 if ( !$fit ) { 00091 if ( count( $pageIds[NS_IMAGE] ) == 1 ) { 00092 // The user is screwed. imageinfo can't be solely 00093 // responsible for exceeding the limit in this case, 00094 // so set a query-continue that just returns the same 00095 // thing again. When the violating queries have been 00096 // out-continued, the result will get through 00097 $this->setContinueEnumParameter( 'start', 00098 wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); 00099 } else { 00100 $this->setContinueEnumParameter( 'continue', 00101 $this->getContinueStr( $img ) ); 00102 } 00103 break; 00104 } 00105 00106 // Check if we can make the requested thumbnail, and get transform parameters. 00107 $finalThumbParams = $this->mergeThumbParams( $img, $scale, $params['urlparam'] ); 00108 00109 // Get information about the current version first 00110 // Check that the current version is within the start-end boundaries 00111 $gotOne = false; 00112 if ( 00113 ( is_null( $start ) || $img->getTimestamp() <= $start ) && 00114 ( is_null( $params['end'] ) || $img->getTimestamp() >= $params['end'] ) 00115 ) { 00116 $gotOne = true; 00117 00118 $fit = $this->addPageSubItem( $pageId, 00119 self::getInfo( $img, $prop, $result, 00120 $finalThumbParams, $params['metadataversion'] ) ); 00121 if ( !$fit ) { 00122 if ( count( $pageIds[NS_IMAGE] ) == 1 ) { 00123 // See the 'the user is screwed' comment above 00124 $this->setContinueEnumParameter( 'start', 00125 wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); 00126 } else { 00127 $this->setContinueEnumParameter( 'continue', 00128 $this->getContinueStr( $img ) ); 00129 } 00130 break; 00131 } 00132 } 00133 00134 // Now get the old revisions 00135 // Get one more to facilitate query-continue functionality 00136 $count = ( $gotOne ? 1 : 0 ); 00137 $oldies = $img->getHistory( $params['limit'] - $count + 1, $start, $params['end'] ); 00138 foreach ( $oldies as $oldie ) { 00139 if ( ++$count > $params['limit'] ) { 00140 // We've reached the extra one which shows that there are additional pages to be had. Stop here... 00141 // Only set a query-continue if there was only one title 00142 if ( count( $pageIds[NS_FILE] ) == 1 ) { 00143 $this->setContinueEnumParameter( 'start', 00144 wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); 00145 } 00146 break; 00147 } 00148 $fit = $this->addPageSubItem( $pageId, 00149 self::getInfo( $oldie, $prop, $result, 00150 $finalThumbParams, $params['metadataversion'] ) ); 00151 if ( !$fit ) { 00152 if ( count( $pageIds[NS_IMAGE] ) == 1 ) { 00153 $this->setContinueEnumParameter( 'start', 00154 wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); 00155 } else { 00156 $this->setContinueEnumParameter( 'continue', 00157 $this->getContinueStr( $oldie ) ); 00158 } 00159 break; 00160 } 00161 } 00162 if ( !$fit ) { 00163 break; 00164 } 00165 $skip = false; 00166 } 00167 00168 $data = $this->getResultData(); 00169 foreach ( $data['query']['pages'] as $pageid => $arr ) { 00170 if ( !isset( $arr['imagerepository'] ) ) { 00171 $result->addValue( 00172 array( 'query', 'pages', $pageid ), 00173 'imagerepository', '' 00174 ); 00175 } 00176 // The above can't fail because it doesn't increase the result size 00177 } 00178 } 00179 } 00180 00186 public function getScale( $params ) { 00187 $p = $this->getModulePrefix(); 00188 00189 // Height and width. 00190 if ( $params['urlheight'] != -1 && $params['urlwidth'] == -1 ) { 00191 $this->dieUsage( "{$p}urlheight cannot be used without {$p}urlwidth", "{$p}urlwidth" ); 00192 } 00193 00194 if ( $params['urlwidth'] != -1 ) { 00195 $scale = array(); 00196 $scale['width'] = $params['urlwidth']; 00197 $scale['height'] = $params['urlheight']; 00198 } else { 00199 $scale = null; 00200 if ( $params['urlparam'] ) { 00201 $this->dieUsage( "{$p}urlparam requires {$p}urlwidth", "urlparam_no_width" ); 00202 } 00203 return $scale; 00204 } 00205 00206 return $scale; 00207 } 00208 00218 protected function mergeThumbParams ( $image, $thumbParams, $otherParams ) { 00219 if ( !$otherParams ) { 00220 return $thumbParams; 00221 } 00222 $p = $this->getModulePrefix(); 00223 00224 $h = $image->getHandler(); 00225 if ( !$h ) { 00226 $this->setWarning( 'Could not create thumbnail because ' . 00227 $image->getName() . ' does not have an associated image handler' ); 00228 return $thumbParams; 00229 } 00230 00231 $paramList = $h->parseParamString( $otherParams ); 00232 if ( !$paramList ) { 00233 // Just set a warning (instead of dieUsage), as in many cases 00234 // we could still render the image using width and height parameters, 00235 // and this type of thing could happen between different versions of 00236 // handlers. 00237 $this->setWarning( "Could not parse {$p}urlparam for " . $image->getName() 00238 . '. Using only width and height' ); 00239 return $thumbParams; 00240 } 00241 00242 if ( isset( $paramList['width'] ) ) { 00243 if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) { 00244 $this->dieUsage( "{$p}urlparam had width of {$paramList['width']} but " 00245 . "{$p}urlwidth was {$thumbParams['width']}", "urlparam_urlwidth_mismatch" ); 00246 } 00247 } 00248 00249 foreach ( $paramList as $name => $value ) { 00250 if ( !$h->validateParam( $name, $value ) ) { 00251 $this->dieUsage( "Invalid value for {$p}urlparam ($name=$value)", "urlparam" ); 00252 } 00253 } 00254 00255 return $thumbParams + $paramList; 00256 } 00257 00268 static function getInfo( $file, $prop, $result, $thumbParams = null, $version = 'latest' ) { 00269 $vals = array(); 00270 // Timestamp is shown even if the file is revdelete'd in interface 00271 // so do same here. 00272 if ( isset( $prop['timestamp'] ) ) { 00273 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() ); 00274 } 00275 00276 $user = isset( $prop['user'] ); 00277 $userid = isset( $prop['userid'] ); 00278 00279 if ( $user || $userid ) { 00280 if ( $file->isDeleted( File::DELETED_USER ) ) { 00281 $vals['userhidden'] = ''; 00282 } else { 00283 if ( $user ) { 00284 $vals['user'] = $file->getUser(); 00285 } 00286 if ( $userid ) { 00287 $vals['userid'] = $file->getUser( 'id' ); 00288 } 00289 if ( !$file->getUser( 'id' ) ) { 00290 $vals['anon'] = ''; 00291 } 00292 } 00293 } 00294 00295 // This is shown even if the file is revdelete'd in interface 00296 // so do same here. 00297 if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) { 00298 $vals['size'] = intval( $file->getSize() ); 00299 $vals['width'] = intval( $file->getWidth() ); 00300 $vals['height'] = intval( $file->getHeight() ); 00301 00302 $pageCount = $file->pageCount(); 00303 if ( $pageCount !== false ) { 00304 $vals['pagecount'] = $pageCount; 00305 } 00306 } 00307 00308 $pcomment = isset( $prop['parsedcomment'] ); 00309 $comment = isset( $prop['comment'] ); 00310 00311 if ( $pcomment || $comment ) { 00312 if ( $file->isDeleted( File::DELETED_COMMENT ) ) { 00313 $vals['commenthidden'] = ''; 00314 } else { 00315 if ( $pcomment ) { 00316 $vals['parsedcomment'] = Linker::formatComment( 00317 $file->getDescription(), $file->getTitle() ); 00318 } 00319 if ( $comment ) { 00320 $vals['comment'] = $file->getDescription(); 00321 } 00322 } 00323 } 00324 00325 $url = isset( $prop['url'] ); 00326 $sha1 = isset( $prop['sha1'] ); 00327 $meta = isset( $prop['metadata'] ); 00328 $mime = isset( $prop['mime'] ); 00329 $mediatype = isset( $prop['mediatype'] ); 00330 $archive = isset( $prop['archivename'] ); 00331 $bitdepth = isset( $prop['bitdepth'] ); 00332 00333 if ( ( $url || $sha1 || $meta || $mime || $mediatype || $archive || $bitdepth ) 00334 && $file->isDeleted( File::DELETED_FILE ) ) { 00335 $vals['filehidden'] = ''; 00336 00337 //Early return, tidier than indenting all following things one level 00338 return $vals; 00339 } 00340 00341 if ( $url ) { 00342 if ( !is_null( $thumbParams ) ) { 00343 $mto = $file->transform( $thumbParams ); 00344 if ( $mto && !$mto->isError() ) { 00345 $vals['thumburl'] = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT ); 00346 00347 // bug 23834 - If the URL's are the same, we haven't resized it, so shouldn't give the wanted 00348 // thumbnail sizes for the thumbnail actual size 00349 if ( $mto->getUrl() !== $file->getUrl() ) { 00350 $vals['thumbwidth'] = intval( $mto->getWidth() ); 00351 $vals['thumbheight'] = intval( $mto->getHeight() ); 00352 } else { 00353 $vals['thumbwidth'] = intval( $file->getWidth() ); 00354 $vals['thumbheight'] = intval( $file->getHeight() ); 00355 } 00356 00357 if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) { 00358 list( $ext, $mime ) = $file->getHandler()->getThumbType( 00359 substr( $mto->getPath(), strrpos( $mto->getPath(), '.' ) + 1 ), 00360 $file->getMimeType(), $thumbParams ); 00361 $vals['thumbmime'] = $mime; 00362 } 00363 } elseif ( $mto && $mto->isError() ) { 00364 $vals['thumberror'] = $mto->toText(); 00365 } 00366 } 00367 $vals['url'] = wfExpandUrl( $file->getFullURL(), PROTO_CURRENT ); 00368 $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl(), PROTO_CURRENT ); 00369 } 00370 00371 if ( $sha1 ) { 00372 $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 ); 00373 } 00374 00375 if ( $meta ) { 00376 $metadata = unserialize( $file->getMetadata() ); 00377 if ( $version !== 'latest' ) { 00378 $metadata = $file->convertMetadataVersion( $metadata, $version ); 00379 } 00380 $vals['metadata'] = $metadata ? self::processMetaData( $metadata, $result ) : null; 00381 } 00382 00383 if ( $mime ) { 00384 $vals['mime'] = $file->getMimeType(); 00385 } 00386 00387 if ( $mediatype ) { 00388 $vals['mediatype'] = $file->getMediaType(); 00389 } 00390 00391 if ( $archive && $file->isOld() ) { 00392 $vals['archivename'] = $file->getArchiveName(); 00393 } 00394 00395 if ( $bitdepth ) { 00396 $vals['bitdepth'] = $file->getBitDepth(); 00397 } 00398 00399 return $vals; 00400 } 00401 00408 public static function processMetaData( $metadata, $result ) { 00409 $retval = array(); 00410 if ( is_array( $metadata ) ) { 00411 foreach ( $metadata as $key => $value ) { 00412 $r = array( 'name' => $key ); 00413 if ( is_array( $value ) ) { 00414 $r['value'] = self::processMetaData( $value, $result ); 00415 } else { 00416 $r['value'] = $value; 00417 } 00418 $retval[] = $r; 00419 } 00420 } 00421 $result->setIndexedTagName( $retval, 'metadata' ); 00422 return $retval; 00423 } 00424 00425 public function getCacheMode( $params ) { 00426 return 'public'; 00427 } 00428 00433 private function getContinueStr( $img ) { 00434 return $img->getOriginalTitle()->getText() . 00435 '|' . $img->getTimestamp(); 00436 } 00437 00438 public function getAllowedParams() { 00439 return array( 00440 'prop' => array( 00441 ApiBase::PARAM_ISMULTI => true, 00442 ApiBase::PARAM_DFLT => 'timestamp|user', 00443 ApiBase::PARAM_TYPE => self::getPropertyNames() 00444 ), 00445 'limit' => array( 00446 ApiBase::PARAM_TYPE => 'limit', 00447 ApiBase::PARAM_DFLT => 1, 00448 ApiBase::PARAM_MIN => 1, 00449 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, 00450 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 00451 ), 00452 'start' => array( 00453 ApiBase::PARAM_TYPE => 'timestamp' 00454 ), 00455 'end' => array( 00456 ApiBase::PARAM_TYPE => 'timestamp' 00457 ), 00458 'urlwidth' => array( 00459 ApiBase::PARAM_TYPE => 'integer', 00460 ApiBase::PARAM_DFLT => -1 00461 ), 00462 'urlheight' => array( 00463 ApiBase::PARAM_TYPE => 'integer', 00464 ApiBase::PARAM_DFLT => -1 00465 ), 00466 'metadataversion' => array( 00467 ApiBase::PARAM_TYPE => 'string', 00468 ApiBase::PARAM_DFLT => '1', 00469 ), 00470 'urlparam' => array( 00471 ApiBase::PARAM_DFLT => '', 00472 ApiBase::PARAM_TYPE => 'string', 00473 ), 00474 'continue' => null, 00475 ); 00476 } 00477 00485 public static function getPropertyNames( $filter = array() ) { 00486 return array_diff( array_keys( self::getProperties() ), $filter ); 00487 } 00488 00494 private static function getProperties() { 00495 return array( 00496 'timestamp' => ' timestamp - Adds timestamp for the uploaded version', 00497 'user' => ' user - Adds the user who uploaded the image version', 00498 'userid' => ' userid - Add the user ID that uploaded the image version', 00499 'comment' => ' comment - Comment on the version', 00500 'parsedcomment' => ' parsedcomment - Parse the comment on the version', 00501 'url' => ' url - Gives URL to the image and the description page', 00502 'size' => ' size - Adds the size of the image in bytes and the height, width and page count (if applicable)', 00503 'dimensions' => ' dimensions - Alias for size', // For backwards compatibility with Allimages 00504 'sha1' => ' sha1 - Adds SHA-1 hash for the image', 00505 'mime' => ' mime - Adds MIME type of the image', 00506 'thumbmime' => ' thumbmime - Adds MIME type of the image thumbnail (requires url)', 00507 'mediatype' => ' mediatype - Adds the media type of the image', 00508 'metadata' => ' metadata - Lists EXIF metadata for the version of the image', 00509 'archivename' => ' archivename - Adds the file name of the archive version for non-latest versions', 00510 'bitdepth' => ' bitdepth - Adds the bit depth of the version', 00511 ); 00512 } 00513 00521 public static function getPropertyDescriptions( $filter = array() ) { 00522 return array_merge( 00523 array( 'What image information to get:' ), 00524 array_values( array_diff_key( self::getProperties(), array_flip( $filter ) ) ) 00525 ); 00526 } 00527 00532 public function getParamDescription() { 00533 $p = $this->getModulePrefix(); 00534 return array( 00535 'prop' => self::getPropertyDescriptions(), 00536 'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.", 00537 'Only the current version of the image can be scaled' ), 00538 'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth", 00539 'urlparam' => array( "A handler specific parameter string. For example, pdf's ", 00540 "might use 'page15-100px'. {$p}urlwidth must be used and be consistent with {$p}urlparam" ), 00541 'limit' => 'How many image revisions to return', 00542 'start' => 'Timestamp to start listing from', 00543 'end' => 'Timestamp to stop listing at', 00544 'metadataversion' => array( "Version of metadata to use. if 'latest' is specified, use latest version.", 00545 "Defaults to '1' for backwards compatibility" ), 00546 'continue' => 'If the query response includes a continue value, use it here to get another page of results' 00547 ); 00548 } 00549 00550 public function getDescription() { 00551 return 'Returns image information and upload history'; 00552 } 00553 00554 public function getPossibleErrors() { 00555 $p = $this->getModulePrefix(); 00556 return array_merge( parent::getPossibleErrors(), array( 00557 array( 'code' => "{$p}urlwidth", 'info' => "{$p}urlheight cannot be used without {$p}urlwidth" ), 00558 array( 'code' => 'urlparam', 'info' => "Invalid value for {$p}urlparam" ), 00559 array( 'code' => 'urlparam_no_width', 'info' => "{$p}urlparam requires {$p}urlwidth" ), 00560 array( 'code' => 'urlparam_urlwidth_mismatch', 'info' => "The width set in {$p}urlparm doesnt't " . 00561 "match the one in {$p}urlwidth" ), 00562 ) ); 00563 } 00564 00565 public function getExamples() { 00566 return array( 00567 'api.php?action=query&titles=File:Albert%20Einstein%20Head.jpg&prop=imageinfo', 00568 'api.php?action=query&titles=File:Test.jpg&prop=imageinfo&iilimit=50&iiend=20071231235959&iiprop=timestamp|user|url', 00569 ); 00570 } 00571 00572 public function getHelpUrls() { 00573 return 'https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii'; 00574 } 00575 00576 public function getVersion() { 00577 return __CLASS__ . ': $Id$'; 00578 } 00579 }