MediaWiki
REL1_20
|
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 //search only inside the local repo 00077 if( $params['localonly'] ) { 00078 $images = RepoGroup::singleton()->getLocalRepo()->findFiles( $titles ); 00079 } else { 00080 $images = RepoGroup::singleton()->findFiles( $titles ); 00081 } 00082 foreach ( $images as $img ) { 00083 // Skip redirects 00084 if ( $img->getOriginalTitle()->isRedirect() ) { 00085 continue; 00086 } 00087 00088 $start = $skip ? $fromTimestamp : $params['start']; 00089 $pageId = $pageIds[NS_FILE][ $img->getOriginalTitle()->getDBkey() ]; 00090 00091 $fit = $result->addValue( 00092 array( 'query', 'pages', intval( $pageId ) ), 00093 'imagerepository', $img->getRepoName() 00094 ); 00095 if ( !$fit ) { 00096 if ( count( $pageIds[NS_FILE] ) == 1 ) { 00097 // The user is screwed. imageinfo can't be solely 00098 // responsible for exceeding the limit in this case, 00099 // so set a query-continue that just returns the same 00100 // thing again. When the violating queries have been 00101 // out-continued, the result will get through 00102 $this->setContinueEnumParameter( 'start', 00103 wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); 00104 } else { 00105 $this->setContinueEnumParameter( 'continue', 00106 $this->getContinueStr( $img ) ); 00107 } 00108 break; 00109 } 00110 00111 // Check if we can make the requested thumbnail, and get transform parameters. 00112 $finalThumbParams = $this->mergeThumbParams( $img, $scale, $params['urlparam'] ); 00113 00114 // Get information about the current version first 00115 // Check that the current version is within the start-end boundaries 00116 $gotOne = false; 00117 if ( 00118 ( is_null( $start ) || $img->getTimestamp() <= $start ) && 00119 ( is_null( $params['end'] ) || $img->getTimestamp() >= $params['end'] ) 00120 ) { 00121 $gotOne = true; 00122 00123 $fit = $this->addPageSubItem( $pageId, 00124 self::getInfo( $img, $prop, $result, 00125 $finalThumbParams, $params['metadataversion'] ) ); 00126 if ( !$fit ) { 00127 if ( count( $pageIds[NS_FILE] ) == 1 ) { 00128 // See the 'the user is screwed' comment above 00129 $this->setContinueEnumParameter( 'start', 00130 wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); 00131 } else { 00132 $this->setContinueEnumParameter( 'continue', 00133 $this->getContinueStr( $img ) ); 00134 } 00135 break; 00136 } 00137 } 00138 00139 // Now get the old revisions 00140 // Get one more to facilitate query-continue functionality 00141 $count = ( $gotOne ? 1 : 0 ); 00142 $oldies = $img->getHistory( $params['limit'] - $count + 1, $start, $params['end'] ); 00143 foreach ( $oldies as $oldie ) { 00144 if ( ++$count > $params['limit'] ) { 00145 // We've reached the extra one which shows that there are additional pages to be had. Stop here... 00146 // Only set a query-continue if there was only one title 00147 if ( count( $pageIds[NS_FILE] ) == 1 ) { 00148 $this->setContinueEnumParameter( 'start', 00149 wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); 00150 } 00151 break; 00152 } 00153 $fit = $this->addPageSubItem( $pageId, 00154 self::getInfo( $oldie, $prop, $result, 00155 $finalThumbParams, $params['metadataversion'] ) ); 00156 if ( !$fit ) { 00157 if ( count( $pageIds[NS_FILE] ) == 1 ) { 00158 $this->setContinueEnumParameter( 'start', 00159 wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); 00160 } else { 00161 $this->setContinueEnumParameter( 'continue', 00162 $this->getContinueStr( $oldie ) ); 00163 } 00164 break; 00165 } 00166 } 00167 if ( !$fit ) { 00168 break; 00169 } 00170 $skip = false; 00171 } 00172 00173 $data = $this->getResultData(); 00174 foreach ( $data['query']['pages'] as $pageid => $arr ) { 00175 if ( !isset( $arr['imagerepository'] ) ) { 00176 $result->addValue( 00177 array( 'query', 'pages', $pageid ), 00178 'imagerepository', '' 00179 ); 00180 } 00181 // The above can't fail because it doesn't increase the result size 00182 } 00183 } 00184 } 00185 00191 public function getScale( $params ) { 00192 $p = $this->getModulePrefix(); 00193 00194 // Height and width. 00195 if ( $params['urlheight'] != -1 && $params['urlwidth'] == -1 ) { 00196 $this->dieUsage( "{$p}urlheight cannot be used without {$p}urlwidth", "{$p}urlwidth" ); 00197 } 00198 00199 if ( $params['urlwidth'] != -1 ) { 00200 $scale = array(); 00201 $scale['width'] = $params['urlwidth']; 00202 $scale['height'] = $params['urlheight']; 00203 } else { 00204 $scale = null; 00205 if ( $params['urlparam'] ) { 00206 $this->dieUsage( "{$p}urlparam requires {$p}urlwidth", "urlparam_no_width" ); 00207 } 00208 return $scale; 00209 } 00210 00211 return $scale; 00212 } 00213 00223 protected function mergeThumbParams ( $image, $thumbParams, $otherParams ) { 00224 if ( !$otherParams ) { 00225 return $thumbParams; 00226 } 00227 $p = $this->getModulePrefix(); 00228 00229 $h = $image->getHandler(); 00230 if ( !$h ) { 00231 $this->setWarning( 'Could not create thumbnail because ' . 00232 $image->getName() . ' does not have an associated image handler' ); 00233 return $thumbParams; 00234 } 00235 00236 $paramList = $h->parseParamString( $otherParams ); 00237 if ( !$paramList ) { 00238 // Just set a warning (instead of dieUsage), as in many cases 00239 // we could still render the image using width and height parameters, 00240 // and this type of thing could happen between different versions of 00241 // handlers. 00242 $this->setWarning( "Could not parse {$p}urlparam for " . $image->getName() 00243 . '. Using only width and height' ); 00244 return $thumbParams; 00245 } 00246 00247 if ( isset( $paramList['width'] ) ) { 00248 if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) { 00249 $this->dieUsage( "{$p}urlparam had width of {$paramList['width']} but " 00250 . "{$p}urlwidth was {$thumbParams['width']}", "urlparam_urlwidth_mismatch" ); 00251 } 00252 } 00253 00254 foreach ( $paramList as $name => $value ) { 00255 if ( !$h->validateParam( $name, $value ) ) { 00256 $this->dieUsage( "Invalid value for {$p}urlparam ($name=$value)", "urlparam" ); 00257 } 00258 } 00259 00260 return $thumbParams + $paramList; 00261 } 00262 00273 static function getInfo( $file, $prop, $result, $thumbParams = null, $version = 'latest' ) { 00274 $vals = array(); 00275 // Timestamp is shown even if the file is revdelete'd in interface 00276 // so do same here. 00277 if ( isset( $prop['timestamp'] ) ) { 00278 $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() ); 00279 } 00280 00281 $user = isset( $prop['user'] ); 00282 $userid = isset( $prop['userid'] ); 00283 00284 if ( $user || $userid ) { 00285 if ( $file->isDeleted( File::DELETED_USER ) ) { 00286 $vals['userhidden'] = ''; 00287 } else { 00288 if ( $user ) { 00289 $vals['user'] = $file->getUser(); 00290 } 00291 if ( $userid ) { 00292 $vals['userid'] = $file->getUser( 'id' ); 00293 } 00294 if ( !$file->getUser( 'id' ) ) { 00295 $vals['anon'] = ''; 00296 } 00297 } 00298 } 00299 00300 // This is shown even if the file is revdelete'd in interface 00301 // so do same here. 00302 if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) { 00303 $vals['size'] = intval( $file->getSize() ); 00304 $vals['width'] = intval( $file->getWidth() ); 00305 $vals['height'] = intval( $file->getHeight() ); 00306 00307 $pageCount = $file->pageCount(); 00308 if ( $pageCount !== false ) { 00309 $vals['pagecount'] = $pageCount; 00310 } 00311 } 00312 00313 $pcomment = isset( $prop['parsedcomment'] ); 00314 $comment = isset( $prop['comment'] ); 00315 00316 if ( $pcomment || $comment ) { 00317 if ( $file->isDeleted( File::DELETED_COMMENT ) ) { 00318 $vals['commenthidden'] = ''; 00319 } else { 00320 if ( $pcomment ) { 00321 $vals['parsedcomment'] = Linker::formatComment( 00322 $file->getDescription(), $file->getTitle() ); 00323 } 00324 if ( $comment ) { 00325 $vals['comment'] = $file->getDescription(); 00326 } 00327 } 00328 } 00329 00330 $url = isset( $prop['url'] ); 00331 $sha1 = isset( $prop['sha1'] ); 00332 $meta = isset( $prop['metadata'] ); 00333 $mime = isset( $prop['mime'] ); 00334 $mediatype = isset( $prop['mediatype'] ); 00335 $archive = isset( $prop['archivename'] ); 00336 $bitdepth = isset( $prop['bitdepth'] ); 00337 00338 if ( ( $url || $sha1 || $meta || $mime || $mediatype || $archive || $bitdepth ) 00339 && $file->isDeleted( File::DELETED_FILE ) ) { 00340 $vals['filehidden'] = ''; 00341 00342 //Early return, tidier than indenting all following things one level 00343 return $vals; 00344 } 00345 00346 if ( $url ) { 00347 if ( !is_null( $thumbParams ) ) { 00348 $mto = $file->transform( $thumbParams ); 00349 if ( $mto && !$mto->isError() ) { 00350 $vals['thumburl'] = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT ); 00351 00352 // bug 23834 - If the URL's are the same, we haven't resized it, so shouldn't give the wanted 00353 // thumbnail sizes for the thumbnail actual size 00354 if ( $mto->getUrl() !== $file->getUrl() ) { 00355 $vals['thumbwidth'] = intval( $mto->getWidth() ); 00356 $vals['thumbheight'] = intval( $mto->getHeight() ); 00357 } else { 00358 $vals['thumbwidth'] = intval( $file->getWidth() ); 00359 $vals['thumbheight'] = intval( $file->getHeight() ); 00360 } 00361 00362 if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) { 00363 list( $ext, $mime ) = $file->getHandler()->getThumbType( 00364 $mto->getExtension(), $file->getMimeType(), $thumbParams ); 00365 $vals['thumbmime'] = $mime; 00366 } 00367 } elseif ( $mto && $mto->isError() ) { 00368 $vals['thumberror'] = $mto->toText(); 00369 } 00370 } 00371 $vals['url'] = wfExpandUrl( $file->getFullURL(), PROTO_CURRENT ); 00372 $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl(), PROTO_CURRENT ); 00373 } 00374 00375 if ( $sha1 ) { 00376 $vals['sha1'] = wfBaseConvert( $file->getSha1(), 36, 16, 40 ); 00377 } 00378 00379 if ( $meta ) { 00380 $metadata = unserialize( $file->getMetadata() ); 00381 if ( $version !== 'latest' ) { 00382 $metadata = $file->convertMetadataVersion( $metadata, $version ); 00383 } 00384 $vals['metadata'] = $metadata ? self::processMetaData( $metadata, $result ) : null; 00385 } 00386 00387 if ( $mime ) { 00388 $vals['mime'] = $file->getMimeType(); 00389 } 00390 00391 if ( $mediatype ) { 00392 $vals['mediatype'] = $file->getMediaType(); 00393 } 00394 00395 if ( $archive && $file->isOld() ) { 00396 $vals['archivename'] = $file->getArchiveName(); 00397 } 00398 00399 if ( $bitdepth ) { 00400 $vals['bitdepth'] = $file->getBitDepth(); 00401 } 00402 00403 return $vals; 00404 } 00405 00412 public static function processMetaData( $metadata, $result ) { 00413 $retval = array(); 00414 if ( is_array( $metadata ) ) { 00415 foreach ( $metadata as $key => $value ) { 00416 $r = array( 'name' => $key ); 00417 if ( is_array( $value ) ) { 00418 $r['value'] = self::processMetaData( $value, $result ); 00419 } else { 00420 $r['value'] = $value; 00421 } 00422 $retval[] = $r; 00423 } 00424 } 00425 $result->setIndexedTagName( $retval, 'metadata' ); 00426 return $retval; 00427 } 00428 00429 public function getCacheMode( $params ) { 00430 return 'public'; 00431 } 00432 00437 protected function getContinueStr( $img ) { 00438 return $img->getOriginalTitle()->getText() . 00439 '|' . $img->getTimestamp(); 00440 } 00441 00442 public function getAllowedParams() { 00443 return array( 00444 'prop' => array( 00445 ApiBase::PARAM_ISMULTI => true, 00446 ApiBase::PARAM_DFLT => 'timestamp|user', 00447 ApiBase::PARAM_TYPE => self::getPropertyNames() 00448 ), 00449 'limit' => array( 00450 ApiBase::PARAM_TYPE => 'limit', 00451 ApiBase::PARAM_DFLT => 1, 00452 ApiBase::PARAM_MIN => 1, 00453 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, 00454 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 00455 ), 00456 'start' => array( 00457 ApiBase::PARAM_TYPE => 'timestamp' 00458 ), 00459 'end' => array( 00460 ApiBase::PARAM_TYPE => 'timestamp' 00461 ), 00462 'urlwidth' => array( 00463 ApiBase::PARAM_TYPE => 'integer', 00464 ApiBase::PARAM_DFLT => -1 00465 ), 00466 'urlheight' => array( 00467 ApiBase::PARAM_TYPE => 'integer', 00468 ApiBase::PARAM_DFLT => -1 00469 ), 00470 'metadataversion' => array( 00471 ApiBase::PARAM_TYPE => 'string', 00472 ApiBase::PARAM_DFLT => '1', 00473 ), 00474 'urlparam' => array( 00475 ApiBase::PARAM_DFLT => '', 00476 ApiBase::PARAM_TYPE => 'string', 00477 ), 00478 'continue' => null, 00479 'localonly' => false, 00480 ); 00481 } 00482 00490 public static function getPropertyNames( $filter = array() ) { 00491 return array_diff( array_keys( self::getProperties() ), $filter ); 00492 } 00493 00499 private static function getProperties( $modulePrefix = '' ) { 00500 return array( 00501 'timestamp' => ' timestamp - Adds timestamp for the uploaded version', 00502 'user' => ' user - Adds the user who uploaded the image version', 00503 'userid' => ' userid - Add the user ID that uploaded the image version', 00504 'comment' => ' comment - Comment on the version', 00505 'parsedcomment' => ' parsedcomment - Parse the comment on the version', 00506 'url' => ' url - Gives URL to the image and the description page', 00507 'size' => ' size - Adds the size of the image in bytes and the height, width and page count (if applicable)', 00508 'dimensions' => ' dimensions - Alias for size', // For backwards compatibility with Allimages 00509 'sha1' => ' sha1 - Adds SHA-1 hash for the image', 00510 'mime' => ' mime - Adds MIME type of the image', 00511 'thumbmime' => ' thumbmime - Adds MIME type of the image thumbnail' . 00512 ' (requires url and param ' . $modulePrefix . 'urlwidth)', 00513 'mediatype' => ' mediatype - Adds the media type of the image', 00514 'metadata' => ' metadata - Lists EXIF metadata for the version of the image', 00515 'archivename' => ' archivename - Adds the file name of the archive version for non-latest versions', 00516 'bitdepth' => ' bitdepth - Adds the bit depth of the version', 00517 ); 00518 } 00519 00527 public static function getPropertyDescriptions( $filter = array(), $modulePrefix = '' ) { 00528 return array_merge( 00529 array( 'What image information to get:' ), 00530 array_values( array_diff_key( self::getProperties( $modulePrefix ), array_flip( $filter ) ) ) 00531 ); 00532 } 00533 00538 public function getParamDescription() { 00539 $p = $this->getModulePrefix(); 00540 return array( 00541 'prop' => self::getPropertyDescriptions( array(), $p ), 00542 'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.", 00543 'Only the current version of the image can be scaled' ), 00544 'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth", 00545 'urlparam' => array( "A handler specific parameter string. For example, pdf's ", 00546 "might use 'page15-100px'. {$p}urlwidth must be used and be consistent with {$p}urlparam" ), 00547 'limit' => 'How many image revisions to return', 00548 'start' => 'Timestamp to start listing from', 00549 'end' => 'Timestamp to stop listing at', 00550 'metadataversion' => array( "Version of metadata to use. if 'latest' is specified, use latest version.", 00551 "Defaults to '1' for backwards compatibility" ), 00552 'continue' => 'If the query response includes a continue value, use it here to get another page of results', 00553 'localonly' => 'Look only for files in the local repository', 00554 ); 00555 } 00556 00557 public static function getResultPropertiesFiltered( $filter = array() ) { 00558 $props = array( 00559 'timestamp' => array( 00560 'timestamp' => 'timestamp' 00561 ), 00562 'user' => array( 00563 'userhidden' => 'boolean', 00564 'user' => 'string', 00565 'anon' => 'boolean' 00566 ), 00567 'userid' => array( 00568 'userhidden' => 'boolean', 00569 'userid' => 'integer', 00570 'anon' => 'boolean' 00571 ), 00572 'size' => array( 00573 'size' => 'integer', 00574 'width' => 'integer', 00575 'height' => 'integer', 00576 'pagecount' => array( 00577 ApiBase::PROP_TYPE => 'integer', 00578 ApiBase::PROP_NULLABLE => true 00579 ) 00580 ), 00581 'comment' => array( 00582 'commenthidden' => 'boolean', 00583 'comment' => array( 00584 ApiBase::PROP_TYPE => 'string', 00585 ApiBase::PROP_NULLABLE => true 00586 ) 00587 ), 00588 'parsedcomment' => array( 00589 'commenthidden' => 'boolean', 00590 'parsedcomment' => array( 00591 ApiBase::PROP_TYPE => 'string', 00592 ApiBase::PROP_NULLABLE => true 00593 ) 00594 ), 00595 'url' => array( 00596 'filehidden' => 'boolean', 00597 'thumburl' => array( 00598 ApiBase::PROP_TYPE => 'string', 00599 ApiBase::PROP_NULLABLE => true 00600 ), 00601 'thumbwidth' => array( 00602 ApiBase::PROP_TYPE => 'integer', 00603 ApiBase::PROP_NULLABLE => true 00604 ), 00605 'thumbheight' => array( 00606 ApiBase::PROP_TYPE => 'integer', 00607 ApiBase::PROP_NULLABLE => true 00608 ), 00609 'thumberror' => array( 00610 ApiBase::PROP_TYPE => 'string', 00611 ApiBase::PROP_NULLABLE => true 00612 ), 00613 'url' => array( 00614 ApiBase::PROP_TYPE => 'string', 00615 ApiBase::PROP_NULLABLE => true 00616 ), 00617 'descriptionurl' => array( 00618 ApiBase::PROP_TYPE => 'string', 00619 ApiBase::PROP_NULLABLE => true 00620 ) 00621 ), 00622 'sha1' => array( 00623 'filehidden' => 'boolean', 00624 'sha1' => array( 00625 ApiBase::PROP_TYPE => 'string', 00626 ApiBase::PROP_NULLABLE => true 00627 ) 00628 ), 00629 'mime' => array( 00630 'filehidden' => 'boolean', 00631 'mime' => array( 00632 ApiBase::PROP_TYPE => 'string', 00633 ApiBase::PROP_NULLABLE => true 00634 ) 00635 ), 00636 'mediatype' => array( 00637 'filehidden' => 'boolean', 00638 'mediatype' => array( 00639 ApiBase::PROP_TYPE => 'string', 00640 ApiBase::PROP_NULLABLE => true 00641 ) 00642 ), 00643 'archivename' => array( 00644 'filehidden' => 'boolean', 00645 'archivename' => array( 00646 ApiBase::PROP_TYPE => 'string', 00647 ApiBase::PROP_NULLABLE => true 00648 ) 00649 ), 00650 'bitdepth' => array( 00651 'filehidden' => 'boolean', 00652 'bitdepth' => array( 00653 ApiBase::PROP_TYPE => 'integer', 00654 ApiBase::PROP_NULLABLE => true 00655 ) 00656 ), 00657 ); 00658 return array_diff_key( $props, array_flip( $filter ) ); 00659 } 00660 00661 public function getResultProperties() { 00662 return self::getResultPropertiesFiltered(); 00663 } 00664 00665 public function getDescription() { 00666 return 'Returns image information and upload history'; 00667 } 00668 00669 public function getPossibleErrors() { 00670 $p = $this->getModulePrefix(); 00671 return array_merge( parent::getPossibleErrors(), array( 00672 array( 'code' => "{$p}urlwidth", 'info' => "{$p}urlheight cannot be used without {$p}urlwidth" ), 00673 array( 'code' => 'urlparam', 'info' => "Invalid value for {$p}urlparam" ), 00674 array( 'code' => 'urlparam_no_width', 'info' => "{$p}urlparam requires {$p}urlwidth" ), 00675 array( 'code' => 'urlparam_urlwidth_mismatch', 'info' => "The width set in {$p}urlparm doesnt't " . 00676 "match the one in {$p}urlwidth" ), 00677 ) ); 00678 } 00679 00680 public function getExamples() { 00681 return array( 00682 'api.php?action=query&titles=File:Albert%20Einstein%20Head.jpg&prop=imageinfo', 00683 'api.php?action=query&titles=File:Test.jpg&prop=imageinfo&iilimit=50&iiend=20071231235959&iiprop=timestamp|user|url', 00684 ); 00685 } 00686 00687 public function getHelpUrls() { 00688 return 'https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii'; 00689 } 00690 00691 public function getVersion() { 00692 return __CLASS__ . ': $Id$'; 00693 } 00694 }