ApiQueryImageInfo.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  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. * A query action to get image information and upload history.
  28. *
  29. * @ingroup API
  30. */
  31. class ApiQueryImageInfo extends ApiQueryBase {
  32. const TRANSFORM_LIMIT = 50;
  33. private static $transformCount = 0;
  34. public function __construct( ApiQuery $query, $moduleName, $prefix = 'ii' ) {
  35. // We allow a subclass to override the prefix, to create a related API
  36. // module. Some other parts of MediaWiki construct this with a null
  37. // $prefix, which used to be ignored when this only took two arguments
  38. if ( is_null( $prefix ) ) {
  39. $prefix = 'ii';
  40. }
  41. parent::__construct( $query, $moduleName, $prefix );
  42. }
  43. public function execute() {
  44. $params = $this->extractRequestParams();
  45. $prop = array_flip( $params['prop'] );
  46. $scale = $this->getScale( $params );
  47. $opts = [
  48. 'version' => $params['metadataversion'],
  49. 'language' => $params['extmetadatalanguage'],
  50. 'multilang' => $params['extmetadatamultilang'],
  51. 'extmetadatafilter' => $params['extmetadatafilter'],
  52. 'revdelUser' => $this->getUser(),
  53. ];
  54. if ( isset( $params['badfilecontexttitle'] ) ) {
  55. $badFileContextTitle = Title::newFromText( $params['badfilecontexttitle'] );
  56. if ( !$badFileContextTitle ) {
  57. $this->dieUsage( 'Invalid title in badfilecontexttitle parameter', 'invalid-title' );
  58. }
  59. } else {
  60. $badFileContextTitle = false;
  61. }
  62. $pageIds = $this->getPageSet()->getGoodAndMissingTitlesByNamespace();
  63. if ( !empty( $pageIds[NS_FILE] ) ) {
  64. $titles = array_keys( $pageIds[NS_FILE] );
  65. asort( $titles ); // Ensure the order is always the same
  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. $user = $this->getUser();
  82. $findTitles = array_map( function ( $title ) use ( $user ) {
  83. return [
  84. 'title' => $title,
  85. 'private' => $user,
  86. ];
  87. }, $titles );
  88. if ( $params['localonly'] ) {
  89. $images = RepoGroup::singleton()->getLocalRepo()->findFiles( $findTitles );
  90. } else {
  91. $images = RepoGroup::singleton()->findFiles( $findTitles );
  92. }
  93. $result = $this->getResult();
  94. foreach ( $titles as $title ) {
  95. $info = [];
  96. $pageId = $pageIds[NS_FILE][$title];
  97. $start = $title === $fromTitle ? $fromTimestamp : $params['start'];
  98. if ( !isset( $images[$title] ) ) {
  99. if ( isset( $prop['uploadwarning'] ) || isset( $prop['badfile'] ) ) {
  100. // uploadwarning and badfile need info about non-existing files
  101. $images[$title] = wfLocalFile( $title );
  102. // Doesn't exist, so set an empty image repository
  103. $info['imagerepository'] = '';
  104. } else {
  105. $result->addValue(
  106. [ 'query', 'pages', intval( $pageId ) ],
  107. 'imagerepository', ''
  108. );
  109. // The above can't fail because it doesn't increase the result size
  110. continue;
  111. }
  112. }
  113. /** @var File $img */
  114. $img = $images[$title];
  115. if ( self::getTransformCount() >= self::TRANSFORM_LIMIT ) {
  116. if ( count( $pageIds[NS_FILE] ) == 1 ) {
  117. // See the 'the user is screwed' comment below
  118. $this->setContinueEnumParameter( 'start',
  119. $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
  120. );
  121. } else {
  122. $this->setContinueEnumParameter( 'continue',
  123. $this->getContinueStr( $img, $start ) );
  124. }
  125. break;
  126. }
  127. if ( !isset( $info['imagerepository'] ) ) {
  128. $info['imagerepository'] = $img->getRepoName();
  129. }
  130. if ( isset( $prop['badfile'] ) ) {
  131. $info['badfile'] = (bool)wfIsBadImage( $title, $badFileContextTitle );
  132. }
  133. $fit = $result->addValue( [ 'query', 'pages' ], intval( $pageId ), $info );
  134. if ( !$fit ) {
  135. if ( count( $pageIds[NS_FILE] ) == 1 ) {
  136. // The user is screwed. imageinfo can't be solely
  137. // responsible for exceeding the limit in this case,
  138. // so set a query-continue that just returns the same
  139. // thing again. When the violating queries have been
  140. // out-continued, the result will get through
  141. $this->setContinueEnumParameter( 'start',
  142. $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
  143. );
  144. } else {
  145. $this->setContinueEnumParameter( 'continue',
  146. $this->getContinueStr( $img, $start ) );
  147. }
  148. break;
  149. }
  150. // Check if we can make the requested thumbnail, and get transform parameters.
  151. $finalThumbParams = $this->mergeThumbParams( $img, $scale, $params['urlparam'] );
  152. // Get information about the current version first
  153. // Check that the current version is within the start-end boundaries
  154. $gotOne = false;
  155. if (
  156. ( is_null( $start ) || $img->getTimestamp() <= $start ) &&
  157. ( is_null( $params['end'] ) || $img->getTimestamp() >= $params['end'] )
  158. ) {
  159. $gotOne = true;
  160. $fit = $this->addPageSubItem( $pageId,
  161. static::getInfo( $img, $prop, $result,
  162. $finalThumbParams, $opts
  163. )
  164. );
  165. if ( !$fit ) {
  166. if ( count( $pageIds[NS_FILE] ) == 1 ) {
  167. // See the 'the user is screwed' comment above
  168. $this->setContinueEnumParameter( 'start',
  169. wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) );
  170. } else {
  171. $this->setContinueEnumParameter( 'continue',
  172. $this->getContinueStr( $img ) );
  173. }
  174. break;
  175. }
  176. }
  177. // Now get the old revisions
  178. // Get one more to facilitate query-continue functionality
  179. $count = ( $gotOne ? 1 : 0 );
  180. $oldies = $img->getHistory( $params['limit'] - $count + 1, $start, $params['end'] );
  181. /** @var File $oldie */
  182. foreach ( $oldies as $oldie ) {
  183. if ( ++$count > $params['limit'] ) {
  184. // We've reached the extra one which shows that there are
  185. // additional pages to be had. Stop here...
  186. // Only set a query-continue if there was only one title
  187. if ( count( $pageIds[NS_FILE] ) == 1 ) {
  188. $this->setContinueEnumParameter( 'start',
  189. wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) );
  190. }
  191. break;
  192. }
  193. $fit = self::getTransformCount() < self::TRANSFORM_LIMIT &&
  194. $this->addPageSubItem( $pageId,
  195. static::getInfo( $oldie, $prop, $result,
  196. $finalThumbParams, $opts
  197. )
  198. );
  199. if ( !$fit ) {
  200. if ( count( $pageIds[NS_FILE] ) == 1 ) {
  201. $this->setContinueEnumParameter( 'start',
  202. wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) );
  203. } else {
  204. $this->setContinueEnumParameter( 'continue',
  205. $this->getContinueStr( $oldie ) );
  206. }
  207. break;
  208. }
  209. }
  210. if ( !$fit ) {
  211. break;
  212. }
  213. }
  214. }
  215. }
  216. /**
  217. * From parameters, construct a 'scale' array
  218. * @param array $params Parameters passed to api.
  219. * @return array|null Key-val array of 'width' and 'height', or null
  220. */
  221. public function getScale( $params ) {
  222. if ( $params['urlwidth'] != -1 ) {
  223. $scale = [];
  224. $scale['width'] = $params['urlwidth'];
  225. $scale['height'] = $params['urlheight'];
  226. } elseif ( $params['urlheight'] != -1 ) {
  227. // Height is specified but width isn't
  228. // Don't set $scale['width']; this signals mergeThumbParams() to fill it with the image's width
  229. $scale = [];
  230. $scale['height'] = $params['urlheight'];
  231. } else {
  232. if ( $params['urlparam'] ) {
  233. // Audio files might not have a width/height.
  234. $scale = [];
  235. } else {
  236. $scale = null;
  237. }
  238. }
  239. return $scale;
  240. }
  241. /** Validate and merge scale parameters with handler thumb parameters, give error if invalid.
  242. *
  243. * We do this later than getScale, since we need the image
  244. * to know which handler, since handlers can make their own parameters.
  245. * @param File $image Image that params are for.
  246. * @param array $thumbParams Thumbnail parameters from getScale
  247. * @param string $otherParams String of otherParams (iiurlparam).
  248. * @return array Array of parameters for transform.
  249. */
  250. protected function mergeThumbParams( $image, $thumbParams, $otherParams ) {
  251. if ( $thumbParams === null ) {
  252. // No scaling requested
  253. return null;
  254. }
  255. if ( !isset( $thumbParams['width'] ) && isset( $thumbParams['height'] ) ) {
  256. // We want to limit only by height in this situation, so pass the
  257. // image's full width as the limiting width. But some file types
  258. // don't have a width of their own, so pick something arbitrary so
  259. // thumbnailing the default icon works.
  260. if ( $image->getWidth() <= 0 ) {
  261. $thumbParams['width'] = max( $this->getConfig()->get( 'ThumbLimits' ) );
  262. } else {
  263. $thumbParams['width'] = $image->getWidth();
  264. }
  265. }
  266. if ( !$otherParams ) {
  267. $this->checkParameterNormalise( $image, $thumbParams );
  268. return $thumbParams;
  269. }
  270. $p = $this->getModulePrefix();
  271. $h = $image->getHandler();
  272. if ( !$h ) {
  273. $this->addWarning( [ 'apiwarn-nothumb-noimagehandler', wfEscapeWikiText( $image->getName() ) ] );
  274. return $thumbParams;
  275. }
  276. $paramList = $h->parseParamString( $otherParams );
  277. if ( !$paramList ) {
  278. // Just set a warning (instead of dieUsage), as in many cases
  279. // we could still render the image using width and height parameters,
  280. // and this type of thing could happen between different versions of
  281. // handlers.
  282. $this->addWarning( [ 'apiwarn-badurlparam', $p, wfEscapeWikiText( $image->getName() ) ] );
  283. $this->checkParameterNormalise( $image, $thumbParams );
  284. return $thumbParams;
  285. }
  286. if ( isset( $paramList['width'] ) && isset( $thumbParams['width'] ) ) {
  287. if ( intval( $paramList['width'] ) != intval( $thumbParams['width'] ) ) {
  288. $this->addWarning(
  289. [ 'apiwarn-urlparamwidth', $p, $paramList['width'], $thumbParams['width'] ]
  290. );
  291. }
  292. }
  293. foreach ( $paramList as $name => $value ) {
  294. if ( !$h->validateParam( $name, $value ) ) {
  295. $this->dieWithError(
  296. [ 'apierror-invalidurlparam', $p, wfEscapeWikiText( $name ), wfEscapeWikiText( $value ) ]
  297. );
  298. }
  299. }
  300. $finalParams = $thumbParams + $paramList;
  301. $this->checkParameterNormalise( $image, $finalParams );
  302. return $finalParams;
  303. }
  304. /**
  305. * Verify that the final image parameters can be normalised.
  306. *
  307. * This doesn't use the normalised parameters, since $file->transform
  308. * expects the pre-normalised parameters, but doing the normalisation
  309. * allows us to catch certain error conditions early (such as missing
  310. * required parameter).
  311. *
  312. * @param File $image
  313. * @param array $finalParams List of parameters to transform image with
  314. */
  315. protected function checkParameterNormalise( $image, $finalParams ) {
  316. $h = $image->getHandler();
  317. if ( !$h ) {
  318. return;
  319. }
  320. // Note: normaliseParams modifies the array in place, but we aren't interested
  321. // in the actual normalised version, only if we can actually normalise them,
  322. // so we use the functions scope to throw away the normalisations.
  323. if ( !$h->normaliseParams( $image, $finalParams ) ) {
  324. $this->dieWithError( [ 'apierror-urlparamnormal', wfEscapeWikiText( $image->getName() ) ] );
  325. }
  326. }
  327. /**
  328. * Get result information for an image revision
  329. *
  330. * @param File $file
  331. * @param array $prop Array of properties to get (in the keys)
  332. * @param ApiResult $result
  333. * @param array $thumbParams Containing 'width' and 'height' items, or null
  334. * @param array|bool|string $opts Options for data fetching.
  335. * This is an array consisting of the keys:
  336. * 'version': The metadata version for the metadata option
  337. * 'language': The language for extmetadata property
  338. * 'multilang': Return all translations in extmetadata property
  339. * 'revdelUser': User to use when checking whether to show revision-deleted fields.
  340. * @return array Result array
  341. */
  342. public static function getInfo( $file, $prop, $result, $thumbParams = null, $opts = false ) {
  343. global $wgContLang;
  344. $anyHidden = false;
  345. if ( !$opts || is_string( $opts ) ) {
  346. $opts = [
  347. 'version' => $opts ?: 'latest',
  348. 'language' => $wgContLang,
  349. 'multilang' => false,
  350. 'extmetadatafilter' => [],
  351. 'revdelUser' => null,
  352. ];
  353. }
  354. $version = $opts['version'];
  355. $vals = [
  356. ApiResult::META_TYPE => 'assoc',
  357. ];
  358. // Timestamp is shown even if the file is revdelete'd in interface
  359. // so do same here.
  360. if ( isset( $prop['timestamp'] ) ) {
  361. $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $file->getTimestamp() );
  362. }
  363. // Handle external callers who don't pass revdelUser
  364. if ( isset( $opts['revdelUser'] ) && $opts['revdelUser'] ) {
  365. $revdelUser = $opts['revdelUser'];
  366. $canShowField = function ( $field ) use ( $file, $revdelUser ) {
  367. return $file->userCan( $field, $revdelUser );
  368. };
  369. } else {
  370. $canShowField = function ( $field ) use ( $file ) {
  371. return !$file->isDeleted( $field );
  372. };
  373. }
  374. $user = isset( $prop['user'] );
  375. $userid = isset( $prop['userid'] );
  376. if ( $user || $userid ) {
  377. if ( $file->isDeleted( File::DELETED_USER ) ) {
  378. $vals['userhidden'] = true;
  379. $anyHidden = true;
  380. }
  381. if ( $canShowField( File::DELETED_USER ) ) {
  382. if ( $user ) {
  383. $vals['user'] = $file->getUser();
  384. }
  385. if ( $userid ) {
  386. $vals['userid'] = $file->getUser( 'id' );
  387. }
  388. if ( !$file->getUser( 'id' ) ) {
  389. $vals['anon'] = true;
  390. }
  391. }
  392. }
  393. // This is shown even if the file is revdelete'd in interface
  394. // so do same here.
  395. if ( isset( $prop['size'] ) || isset( $prop['dimensions'] ) ) {
  396. $vals['size'] = intval( $file->getSize() );
  397. $vals['width'] = intval( $file->getWidth() );
  398. $vals['height'] = intval( $file->getHeight() );
  399. $pageCount = $file->pageCount();
  400. if ( $pageCount !== false ) {
  401. $vals['pagecount'] = $pageCount;
  402. }
  403. // length as in how many seconds long a video is.
  404. $length = $file->getLength();
  405. if ( $length ) {
  406. // Call it duration, because "length" can be ambiguous.
  407. $vals['duration'] = (float)$length;
  408. }
  409. }
  410. $pcomment = isset( $prop['parsedcomment'] );
  411. $comment = isset( $prop['comment'] );
  412. if ( $pcomment || $comment ) {
  413. if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
  414. $vals['commenthidden'] = true;
  415. $anyHidden = true;
  416. }
  417. if ( $canShowField( File::DELETED_COMMENT ) ) {
  418. if ( $pcomment ) {
  419. $vals['parsedcomment'] = Linker::formatComment(
  420. $file->getDescription( File::RAW ), $file->getTitle() );
  421. }
  422. if ( $comment ) {
  423. $vals['comment'] = $file->getDescription( File::RAW );
  424. }
  425. }
  426. }
  427. $canonicaltitle = isset( $prop['canonicaltitle'] );
  428. $url = isset( $prop['url'] );
  429. $sha1 = isset( $prop['sha1'] );
  430. $meta = isset( $prop['metadata'] );
  431. $extmetadata = isset( $prop['extmetadata'] );
  432. $commonmeta = isset( $prop['commonmetadata'] );
  433. $mime = isset( $prop['mime'] );
  434. $mediatype = isset( $prop['mediatype'] );
  435. $archive = isset( $prop['archivename'] );
  436. $bitdepth = isset( $prop['bitdepth'] );
  437. $uploadwarning = isset( $prop['uploadwarning'] );
  438. if ( $uploadwarning ) {
  439. $vals['html'] = SpecialUpload::getExistsWarning( UploadBase::getExistsWarning( $file ) );
  440. }
  441. if ( $file->isDeleted( File::DELETED_FILE ) ) {
  442. $vals['filehidden'] = true;
  443. $anyHidden = true;
  444. }
  445. if ( $anyHidden && $file->isDeleted( File::DELETED_RESTRICTED ) ) {
  446. $vals['suppressed'] = true;
  447. }
  448. if ( !$canShowField( File::DELETED_FILE ) ) {
  449. // Early return, tidier than indenting all following things one level
  450. return $vals;
  451. }
  452. if ( $canonicaltitle ) {
  453. $vals['canonicaltitle'] = $file->getTitle()->getPrefixedText();
  454. }
  455. if ( $url ) {
  456. if ( !is_null( $thumbParams ) ) {
  457. $mto = $file->transform( $thumbParams );
  458. self::$transformCount++;
  459. if ( $mto && !$mto->isError() ) {
  460. $vals['thumburl'] = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT );
  461. // T25834 - If the URLs are the same, we haven't resized it, so shouldn't give the wanted
  462. // thumbnail sizes for the thumbnail actual size
  463. if ( $mto->getUrl() !== $file->getUrl() ) {
  464. $vals['thumbwidth'] = intval( $mto->getWidth() );
  465. $vals['thumbheight'] = intval( $mto->getHeight() );
  466. } else {
  467. $vals['thumbwidth'] = intval( $file->getWidth() );
  468. $vals['thumbheight'] = intval( $file->getHeight() );
  469. }
  470. if ( isset( $prop['thumbmime'] ) && $file->getHandler() ) {
  471. list( , $mime ) = $file->getHandler()->getThumbType(
  472. $mto->getExtension(), $file->getMimeType(), $thumbParams );
  473. $vals['thumbmime'] = $mime;
  474. }
  475. } elseif ( $mto && $mto->isError() ) {
  476. $vals['thumberror'] = $mto->toText();
  477. }
  478. }
  479. $vals['url'] = wfExpandUrl( $file->getFullUrl(), PROTO_CURRENT );
  480. $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl(), PROTO_CURRENT );
  481. $shortDescriptionUrl = $file->getDescriptionShortUrl();
  482. if ( $shortDescriptionUrl !== null ) {
  483. $vals['descriptionshorturl'] = wfExpandUrl( $shortDescriptionUrl, PROTO_CURRENT );
  484. }
  485. }
  486. if ( $sha1 ) {
  487. $vals['sha1'] = Wikimedia\base_convert( $file->getSha1(), 36, 16, 40 );
  488. }
  489. if ( $meta ) {
  490. MediaWiki\suppressWarnings();
  491. $metadata = unserialize( $file->getMetadata() );
  492. MediaWiki\restoreWarnings();
  493. if ( $metadata && $version !== 'latest' ) {
  494. $metadata = $file->convertMetadataVersion( $metadata, $version );
  495. }
  496. $vals['metadata'] = $metadata ? static::processMetaData( $metadata, $result ) : null;
  497. }
  498. if ( $commonmeta ) {
  499. $metaArray = $file->getCommonMetaArray();
  500. $vals['commonmetadata'] = $metaArray ? static::processMetaData( $metaArray, $result ) : [];
  501. }
  502. if ( $extmetadata ) {
  503. // Note, this should return an array where all the keys
  504. // start with a letter, and all the values are strings.
  505. // Thus there should be no issue with format=xml.
  506. $format = new FormatMetadata;
  507. $format->setSingleLanguage( !$opts['multilang'] );
  508. $format->getContext()->setLanguage( $opts['language'] );
  509. $extmetaArray = $format->fetchExtendedMetadata( $file );
  510. if ( $opts['extmetadatafilter'] ) {
  511. $extmetaArray = array_intersect_key(
  512. $extmetaArray, array_flip( $opts['extmetadatafilter'] )
  513. );
  514. }
  515. $vals['extmetadata'] = $extmetaArray;
  516. }
  517. if ( $mime ) {
  518. $vals['mime'] = $file->getMimeType();
  519. }
  520. if ( $mediatype ) {
  521. $vals['mediatype'] = $file->getMediaType();
  522. }
  523. if ( $archive && $file->isOld() ) {
  524. $vals['archivename'] = $file->getArchiveName();
  525. }
  526. if ( $bitdepth ) {
  527. $vals['bitdepth'] = $file->getBitDepth();
  528. }
  529. return $vals;
  530. }
  531. /**
  532. * Get the count of image transformations performed
  533. *
  534. * If this is >= TRANSFORM_LIMIT, you should probably stop processing images.
  535. *
  536. * @return int Count
  537. */
  538. static function getTransformCount() {
  539. return self::$transformCount;
  540. }
  541. /**
  542. *
  543. * @param array $metadata
  544. * @param ApiResult $result
  545. * @return array
  546. */
  547. public static function processMetaData( $metadata, $result ) {
  548. $retval = [];
  549. if ( is_array( $metadata ) ) {
  550. foreach ( $metadata as $key => $value ) {
  551. $r = [
  552. 'name' => $key,
  553. ApiResult::META_BC_BOOLS => [ 'value' ],
  554. ];
  555. if ( is_array( $value ) ) {
  556. $r['value'] = static::processMetaData( $value, $result );
  557. } else {
  558. $r['value'] = $value;
  559. }
  560. $retval[] = $r;
  561. }
  562. }
  563. ApiResult::setIndexedTagName( $retval, 'metadata' );
  564. return $retval;
  565. }
  566. public function getCacheMode( $params ) {
  567. if ( $this->userCanSeeRevDel() ) {
  568. return 'private';
  569. }
  570. return 'public';
  571. }
  572. /**
  573. * @param File $img
  574. * @param null|string $start
  575. * @return string
  576. */
  577. protected function getContinueStr( $img, $start = null ) {
  578. if ( $start === null ) {
  579. $start = $img->getTimestamp();
  580. }
  581. return $img->getOriginalTitle()->getDBkey() . '|' . $start;
  582. }
  583. public function getAllowedParams() {
  584. global $wgContLang;
  585. return [
  586. 'prop' => [
  587. ApiBase::PARAM_ISMULTI => true,
  588. ApiBase::PARAM_DFLT => 'timestamp|user',
  589. ApiBase::PARAM_TYPE => static::getPropertyNames(),
  590. ApiBase::PARAM_HELP_MSG_PER_VALUE => static::getPropertyMessages(),
  591. ],
  592. 'limit' => [
  593. ApiBase::PARAM_TYPE => 'limit',
  594. ApiBase::PARAM_DFLT => 1,
  595. ApiBase::PARAM_MIN => 1,
  596. ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
  597. ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
  598. ],
  599. 'start' => [
  600. ApiBase::PARAM_TYPE => 'timestamp'
  601. ],
  602. 'end' => [
  603. ApiBase::PARAM_TYPE => 'timestamp'
  604. ],
  605. 'urlwidth' => [
  606. ApiBase::PARAM_TYPE => 'integer',
  607. ApiBase::PARAM_DFLT => -1,
  608. ApiBase::PARAM_HELP_MSG => [
  609. 'apihelp-query+imageinfo-param-urlwidth',
  610. self::TRANSFORM_LIMIT,
  611. ],
  612. ],
  613. 'urlheight' => [
  614. ApiBase::PARAM_TYPE => 'integer',
  615. ApiBase::PARAM_DFLT => -1
  616. ],
  617. 'metadataversion' => [
  618. ApiBase::PARAM_TYPE => 'string',
  619. ApiBase::PARAM_DFLT => '1',
  620. ],
  621. 'extmetadatalanguage' => [
  622. ApiBase::PARAM_TYPE => 'string',
  623. ApiBase::PARAM_DFLT => $wgContLang->getCode(),
  624. ],
  625. 'extmetadatamultilang' => [
  626. ApiBase::PARAM_TYPE => 'boolean',
  627. ApiBase::PARAM_DFLT => false,
  628. ],
  629. 'extmetadatafilter' => [
  630. ApiBase::PARAM_TYPE => 'string',
  631. ApiBase::PARAM_ISMULTI => true,
  632. ],
  633. 'urlparam' => [
  634. ApiBase::PARAM_DFLT => '',
  635. ApiBase::PARAM_TYPE => 'string',
  636. ],
  637. 'badfilecontexttitle' => [
  638. ApiBase::PARAM_TYPE => 'string',
  639. ],
  640. 'continue' => [
  641. ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
  642. ],
  643. 'localonly' => false,
  644. ];
  645. }
  646. /**
  647. * Returns all possible parameters to iiprop
  648. *
  649. * @param array $filter List of properties to filter out
  650. * @return array
  651. */
  652. public static function getPropertyNames( $filter = [] ) {
  653. return array_keys( static::getPropertyMessages( $filter ) );
  654. }
  655. /**
  656. * Returns messages for all possible parameters to iiprop
  657. *
  658. * @param array $filter List of properties to filter out
  659. * @return array
  660. */
  661. public static function getPropertyMessages( $filter = [] ) {
  662. return array_diff_key(
  663. [
  664. 'timestamp' => 'apihelp-query+imageinfo-paramvalue-prop-timestamp',
  665. 'user' => 'apihelp-query+imageinfo-paramvalue-prop-user',
  666. 'userid' => 'apihelp-query+imageinfo-paramvalue-prop-userid',
  667. 'comment' => 'apihelp-query+imageinfo-paramvalue-prop-comment',
  668. 'parsedcomment' => 'apihelp-query+imageinfo-paramvalue-prop-parsedcomment',
  669. 'canonicaltitle' => 'apihelp-query+imageinfo-paramvalue-prop-canonicaltitle',
  670. 'url' => 'apihelp-query+imageinfo-paramvalue-prop-url',
  671. 'size' => 'apihelp-query+imageinfo-paramvalue-prop-size',
  672. 'dimensions' => 'apihelp-query+imageinfo-paramvalue-prop-dimensions',
  673. 'sha1' => 'apihelp-query+imageinfo-paramvalue-prop-sha1',
  674. 'mime' => 'apihelp-query+imageinfo-paramvalue-prop-mime',
  675. 'thumbmime' => 'apihelp-query+imageinfo-paramvalue-prop-thumbmime',
  676. 'mediatype' => 'apihelp-query+imageinfo-paramvalue-prop-mediatype',
  677. 'metadata' => 'apihelp-query+imageinfo-paramvalue-prop-metadata',
  678. 'commonmetadata' => 'apihelp-query+imageinfo-paramvalue-prop-commonmetadata',
  679. 'extmetadata' => 'apihelp-query+imageinfo-paramvalue-prop-extmetadata',
  680. 'archivename' => 'apihelp-query+imageinfo-paramvalue-prop-archivename',
  681. 'bitdepth' => 'apihelp-query+imageinfo-paramvalue-prop-bitdepth',
  682. 'uploadwarning' => 'apihelp-query+imageinfo-paramvalue-prop-uploadwarning',
  683. 'badfile' => 'apihelp-query+imageinfo-paramvalue-prop-badfile',
  684. ],
  685. array_flip( $filter )
  686. );
  687. }
  688. /**
  689. * Returns array key value pairs of properties and their descriptions
  690. *
  691. * @deprecated since 1.25
  692. * @param string $modulePrefix
  693. * @return array
  694. */
  695. private static function getProperties( $modulePrefix = '' ) {
  696. return [
  697. 'timestamp' => ' timestamp - Adds timestamp for the uploaded version',
  698. 'user' => ' user - Adds the user who uploaded the image version',
  699. 'userid' => ' userid - Add the user ID that uploaded the image version',
  700. 'comment' => ' comment - Comment on the version',
  701. 'parsedcomment' => ' parsedcomment - Parse the comment on the version',
  702. 'canonicaltitle' => ' canonicaltitle - Adds the canonical title of the image file',
  703. 'url' => ' url - Gives URL to the image and the description page',
  704. 'size' => ' size - Adds the size of the image in bytes, ' .
  705. 'its height and its width. Page count and duration are added if applicable',
  706. 'dimensions' => ' dimensions - Alias for size', // B/C with Allimages
  707. 'sha1' => ' sha1 - Adds SHA-1 hash for the image',
  708. 'mime' => ' mime - Adds MIME type of the image',
  709. 'thumbmime' => ' thumbmime - Adds MIME type of the image thumbnail' .
  710. ' (requires url and param ' . $modulePrefix . 'urlwidth)',
  711. 'mediatype' => ' mediatype - Adds the media type of the image',
  712. 'metadata' => ' metadata - Lists Exif metadata for the version of the image',
  713. 'commonmetadata' => ' commonmetadata - Lists file format generic metadata ' .
  714. 'for the version of the image',
  715. 'extmetadata' => ' extmetadata - Lists formatted metadata combined ' .
  716. 'from multiple sources. Results are HTML formatted.',
  717. 'archivename' => ' archivename - Adds the file name of the archive ' .
  718. 'version for non-latest versions',
  719. 'bitdepth' => ' bitdepth - Adds the bit depth of the version',
  720. 'uploadwarning' => ' uploadwarning - Used by the Special:Upload page to ' .
  721. 'get information about an existing file. Not intended for use outside MediaWiki core',
  722. ];
  723. }
  724. /**
  725. * Returns the descriptions for the properties provided by getPropertyNames()
  726. *
  727. * @deprecated since 1.25
  728. * @param array $filter List of properties to filter out
  729. * @param string $modulePrefix
  730. * @return array
  731. */
  732. public static function getPropertyDescriptions( $filter = [], $modulePrefix = '' ) {
  733. return array_merge(
  734. [ 'What image information to get:' ],
  735. array_values( array_diff_key( static::getProperties( $modulePrefix ), array_flip( $filter ) ) )
  736. );
  737. }
  738. protected function getExamplesMessages() {
  739. return [
  740. 'action=query&titles=File:Albert%20Einstein%20Head.jpg&prop=imageinfo'
  741. => 'apihelp-query+imageinfo-example-simple',
  742. 'action=query&titles=File:Test.jpg&prop=imageinfo&iilimit=50&' .
  743. 'iiend=2007-12-31T23:59:59Z&iiprop=timestamp|user|url'
  744. => 'apihelp-query+imageinfo-example-dated',
  745. ];
  746. }
  747. public function getHelpUrls() {
  748. return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Imageinfo';
  749. }
  750. }