MediaHandler.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. <?php
  2. /**
  3. * Media-handling base classes and generic functionality.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup Media
  22. */
  23. use MediaWiki\MediaWikiServices;
  24. /**
  25. * Base media handler class
  26. *
  27. * @ingroup Media
  28. */
  29. abstract class MediaHandler {
  30. const TRANSFORM_LATER = 1;
  31. const METADATA_GOOD = true;
  32. const METADATA_BAD = false;
  33. const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
  34. /**
  35. * Max length of error logged by logErrorForExternalProcess()
  36. */
  37. const MAX_ERR_LOG_SIZE = 65535;
  38. /**
  39. * Get a MediaHandler for a given MIME type from the instance cache
  40. *
  41. * @param string $type
  42. * @return MediaHandler|bool
  43. */
  44. static function getHandler( $type ) {
  45. return MediaWikiServices::getInstance()
  46. ->getMediaHandlerFactory()->getHandler( $type );
  47. }
  48. /**
  49. * Get an associative array mapping magic word IDs to parameter names.
  50. * Will be used by the parser to identify parameters.
  51. */
  52. abstract public function getParamMap();
  53. /**
  54. * Validate a thumbnail parameter at parse time.
  55. * Return true to accept the parameter, and false to reject it.
  56. * If you return false, the parser will do something quiet and forgiving.
  57. *
  58. * @param string $name
  59. * @param mixed $value
  60. */
  61. abstract public function validateParam( $name, $value );
  62. /**
  63. * Merge a parameter array into a string appropriate for inclusion in filenames
  64. *
  65. * @param array $params Array of parameters that have been through normaliseParams.
  66. * @return string
  67. */
  68. abstract public function makeParamString( $params );
  69. /**
  70. * Parse a param string made with makeParamString back into an array
  71. *
  72. * @param string $str The parameter string without file name (e.g. 122px)
  73. * @return array|bool Array of parameters or false on failure.
  74. */
  75. abstract public function parseParamString( $str );
  76. /**
  77. * Changes the parameter array as necessary, ready for transformation.
  78. * Should be idempotent.
  79. * Returns false if the parameters are unacceptable and the transform should fail
  80. * @param File $image
  81. * @param array &$params
  82. */
  83. abstract public function normaliseParams( $image, &$params );
  84. /**
  85. * Get an image size array like that returned by getimagesize(), or false if it
  86. * can't be determined.
  87. *
  88. * This function is used for determining the width, height and bitdepth directly
  89. * from an image. The results are stored in the database in the img_width,
  90. * img_height, img_bits fields.
  91. *
  92. * @note If this is a multipage file, return the width and height of the
  93. * first page.
  94. *
  95. * @param File|FSFile $image The image object, or false if there isn't one.
  96. * Warning, FSFile::getPropsFromPath might pass an FSFile instead of File (!)
  97. * @param string $path The filename
  98. * @return array|false Follow the format of PHP getimagesize() internal function.
  99. * See https://www.php.net/getimagesize. MediaWiki will only ever use the
  100. * first two array keys (the width and height), and the 'bits' associative
  101. * key. All other array keys are ignored. Returning a 'bits' key is optional
  102. * as not all formats have a notion of "bitdepth". Returns false on failure.
  103. */
  104. abstract function getImageSize( $image, $path );
  105. /**
  106. * Get handler-specific metadata which will be saved in the img_metadata field.
  107. *
  108. * @param File|FSFile $image The image object, or false if there isn't one.
  109. * Warning, FSFile::getPropsFromPath might pass an FSFile instead of File (!)
  110. * @param string $path The filename
  111. * @return string A string of metadata in php serialized form (Run through serialize())
  112. */
  113. public function getMetadata( $image, $path ) {
  114. return '';
  115. }
  116. /**
  117. * Get metadata version.
  118. *
  119. * This is not used for validating metadata, this is used for the api when returning
  120. * metadata, since api content formats should stay the same over time, and so things
  121. * using ForeignApiRepo can keep backwards compatibility
  122. *
  123. * All core media handlers share a common version number, and extensions can
  124. * use the GetMetadataVersion hook to append to the array (they should append a unique
  125. * string so not to get confusing). If there was a media handler named 'foo' with metadata
  126. * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata
  127. * version is 2, the end version string would look like '2;foo=3'.
  128. *
  129. * @return string Version string
  130. */
  131. static function getMetadataVersion() {
  132. $version = [ '2' ]; // core metadata version
  133. Hooks::run( 'GetMetadataVersion', [ &$version ] );
  134. return implode( ';', $version );
  135. }
  136. /**
  137. * Convert metadata version.
  138. *
  139. * By default just returns $metadata, but can be used to allow
  140. * media handlers to convert between metadata versions.
  141. *
  142. * @param string|array $metadata Metadata array (serialized if string)
  143. * @param int $version Target version
  144. * @return array Serialized metadata in specified version, or $metadata on fail.
  145. */
  146. function convertMetadataVersion( $metadata, $version = 1 ) {
  147. if ( !is_array( $metadata ) ) {
  148. // unserialize to keep return parameter consistent.
  149. Wikimedia\suppressWarnings();
  150. $ret = unserialize( $metadata );
  151. Wikimedia\restoreWarnings();
  152. return $ret;
  153. }
  154. return $metadata;
  155. }
  156. /**
  157. * Get a string describing the type of metadata, for display purposes.
  158. *
  159. * @note This method is currently unused.
  160. * @param File $image
  161. * @return string
  162. */
  163. function getMetadataType( $image ) {
  164. return false;
  165. }
  166. /**
  167. * Check if the metadata string is valid for this handler.
  168. * If it returns MediaHandler::METADATA_BAD (or false), Image
  169. * will reload the metadata from the file and update the database.
  170. * MediaHandler::METADATA_GOOD for if the metadata is a-ok,
  171. * MediaHandler::METADATA_COMPATIBLE if metadata is old but backwards
  172. * compatible (which may or may not trigger a metadata reload).
  173. *
  174. * @note Returning self::METADATA_BAD will trigger a metadata reload from
  175. * file on page view. Always returning this from a broken file, or suddenly
  176. * triggering as bad metadata for a large number of files can cause
  177. * performance problems.
  178. * @param File $image
  179. * @param string $metadata The metadata in serialized form
  180. * @return bool|int
  181. */
  182. public function isMetadataValid( $image, $metadata ) {
  183. return self::METADATA_GOOD;
  184. }
  185. /**
  186. * Get an array of standard (FormatMetadata type) metadata values.
  187. *
  188. * The returned data is largely the same as that from getMetadata(),
  189. * but formatted in a standard, stable, handler-independent way.
  190. * The idea being that some values like ImageDescription or Artist
  191. * are universal and should be retrievable in a handler generic way.
  192. *
  193. * The specific properties are the type of properties that can be
  194. * handled by the FormatMetadata class. These values are exposed to the
  195. * user via the filemetadata parser function.
  196. *
  197. * Details of the response format of this function can be found at
  198. * https://www.mediawiki.org/wiki/Manual:File_metadata_handling
  199. * tl/dr: the response is an associative array of
  200. * properties keyed by name, but the value can be complex. You probably
  201. * want to call one of the FormatMetadata::flatten* functions on the
  202. * property values before using them, or call
  203. * FormatMetadata::getFormattedData() on the full response array, which
  204. * transforms all values into prettified, human-readable text.
  205. *
  206. * Subclasses overriding this function must return a value which is a
  207. * valid API response fragment (all associative array keys are valid
  208. * XML tagnames).
  209. *
  210. * Note, if the file simply has no metadata, but the handler supports
  211. * this interface, it should return an empty array, not false.
  212. *
  213. * @param File $file
  214. * @return array|bool False if interface not supported
  215. * @since 1.23
  216. */
  217. public function getCommonMetaArray( File $file ) {
  218. return false;
  219. }
  220. /**
  221. * Get a MediaTransformOutput object representing an alternate of the transformed
  222. * output which will call an intermediary thumbnail assist script.
  223. *
  224. * Used when the repository has a thumbnailScriptUrl option configured.
  225. *
  226. * Return false to fall back to the regular getTransform().
  227. * @param File $image
  228. * @param string $script
  229. * @param array $params
  230. * @return bool|ThumbnailImage
  231. */
  232. function getScriptedTransform( $image, $script, $params ) {
  233. return false;
  234. }
  235. /**
  236. * Get a MediaTransformOutput object representing the transformed output. Does not
  237. * actually do the transform.
  238. *
  239. * @param File $image
  240. * @param string $dstPath Filesystem destination path
  241. * @param string $dstUrl Destination URL to use in output HTML
  242. * @param array $params Arbitrary set of parameters validated by $this->validateParam()
  243. * @return MediaTransformOutput
  244. */
  245. final function getTransform( $image, $dstPath, $dstUrl, $params ) {
  246. return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
  247. }
  248. /**
  249. * Get a MediaTransformOutput object representing the transformed output. Does the
  250. * transform unless $flags contains self::TRANSFORM_LATER.
  251. *
  252. * @param File $image
  253. * @param string $dstPath Filesystem destination path
  254. * @param string $dstUrl Destination URL to use in output HTML
  255. * @param array $params Arbitrary set of parameters validated by $this->validateParam()
  256. * Note: These parameters have *not* gone through $this->normaliseParams()
  257. * @param int $flags A bitfield, may contain self::TRANSFORM_LATER
  258. * @return MediaTransformOutput
  259. */
  260. abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
  261. /**
  262. * Get the thumbnail extension and MIME type for a given source MIME type
  263. *
  264. * @param string $ext Extension of original file
  265. * @param string $mime MIME type of original file
  266. * @param array|null $params Handler specific rendering parameters
  267. * @return array Thumbnail extension and MIME type
  268. */
  269. public function getThumbType( $ext, $mime, $params = null ) {
  270. $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
  271. if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
  272. // The extension is not valid for this MIME type and we do
  273. // recognize the MIME type
  274. $extensions = $magic->getExtensionsForType( $mime );
  275. if ( $extensions ) {
  276. return [ strtok( $extensions, ' ' ), $mime ];
  277. }
  278. }
  279. // The extension is correct (true) or the MIME type is unknown to
  280. // MediaWiki (null)
  281. return [ $ext, $mime ];
  282. }
  283. /**
  284. * True if the handled types can be transformed
  285. *
  286. * @param File $file
  287. * @return bool
  288. */
  289. public function canRender( $file ) {
  290. return true;
  291. }
  292. /**
  293. * True if handled types cannot be displayed directly in a browser
  294. * but can be rendered
  295. *
  296. * @param File $file
  297. * @return bool
  298. */
  299. public function mustRender( $file ) {
  300. return false;
  301. }
  302. /**
  303. * True if the type has multi-page capabilities
  304. *
  305. * @param File $file
  306. * @return bool
  307. */
  308. public function isMultiPage( $file ) {
  309. return false;
  310. }
  311. /**
  312. * Page count for a multi-page document, false if unsupported or unknown
  313. *
  314. * @param File $file
  315. * @return bool
  316. */
  317. public function pageCount( File $file ) {
  318. return false;
  319. }
  320. /**
  321. * The material is vectorized and thus scaling is lossless
  322. *
  323. * @param File $file
  324. * @return bool
  325. */
  326. function isVectorized( $file ) {
  327. return false;
  328. }
  329. /**
  330. * The material is an image, and is animated.
  331. * In particular, video material need not return true.
  332. * @note Before 1.20, this was a method of ImageHandler only
  333. *
  334. * @param File $file
  335. * @return bool
  336. */
  337. function isAnimatedImage( $file ) {
  338. return false;
  339. }
  340. /**
  341. * If the material is animated, we can animate the thumbnail
  342. * @since 1.20
  343. *
  344. * @param File $file
  345. * @return bool If material is not animated, handler may return any value.
  346. */
  347. function canAnimateThumbnail( $file ) {
  348. return true;
  349. }
  350. /**
  351. * False if the handler is disabled for all files
  352. * @return bool
  353. */
  354. public function isEnabled() {
  355. return true;
  356. }
  357. /**
  358. * Get an associative array of page dimensions
  359. * Currently "width" and "height" are understood, but this might be
  360. * expanded in the future.
  361. * Returns false if unknown.
  362. *
  363. * It is expected that handlers for paged media (e.g. DjVuHandler)
  364. * will override this method so that it gives the correct results
  365. * for each specific page of the file, using the $page argument.
  366. *
  367. * @note For non-paged media, use getImageSize.
  368. *
  369. * @param File $image
  370. * @param int $page What page to get dimensions of
  371. * @return array|bool
  372. */
  373. public function getPageDimensions( File $image, $page ) {
  374. $gis = $this->getImageSize( $image, $image->getLocalRefPath() );
  375. if ( $gis ) {
  376. return [
  377. 'width' => $gis[0],
  378. 'height' => $gis[1]
  379. ];
  380. } else {
  381. return false;
  382. }
  383. }
  384. /**
  385. * Generic getter for text layer.
  386. * Currently overloaded by PDF and DjVu handlers
  387. * @param File $image
  388. * @param int $page Page number to get information for
  389. * @return bool|string Page text or false when no text found or if
  390. * unsupported.
  391. */
  392. function getPageText( File $image, $page ) {
  393. return false;
  394. }
  395. /**
  396. * Get the text of the entire document.
  397. * @param File $file
  398. * @return bool|string The text of the document or false if unsupported.
  399. */
  400. public function getEntireText( File $file ) {
  401. $numPages = $file->pageCount();
  402. if ( !$numPages ) {
  403. // Not a multipage document
  404. return $this->getPageText( $file, 1 );
  405. }
  406. $document = '';
  407. for ( $i = 1; $i <= $numPages; $i++ ) {
  408. $curPage = $this->getPageText( $file, $i );
  409. if ( is_string( $curPage ) ) {
  410. $document .= $curPage . "\n";
  411. }
  412. }
  413. if ( $document !== '' ) {
  414. return $document;
  415. }
  416. return false;
  417. }
  418. /**
  419. * Get an array structure that looks like this:
  420. *
  421. * [
  422. * 'visible' => [
  423. * 'Human-readable name' => 'Human readable value',
  424. * ...
  425. * ],
  426. * 'collapsed' => [
  427. * 'Human-readable name' => 'Human readable value',
  428. * ...
  429. * ]
  430. * ]
  431. * The UI will format this into a table where the visible fields are always
  432. * visible, and the collapsed fields are optionally visible.
  433. *
  434. * The function should return false if there is no metadata to display.
  435. */
  436. /**
  437. * @todo FIXME: This interface is not very flexible. The media handler
  438. * should generate HTML instead. It can do all the formatting according
  439. * to some standard. That makes it possible to do things like visual
  440. * indication of grouped and chained streams in ogg container files.
  441. * @param File $image
  442. * @param bool|IContextSource $context Context to use (optional)
  443. * @return array|bool
  444. */
  445. public function formatMetadata( $image, $context = false ) {
  446. return false;
  447. }
  448. /** sorts the visible/invisible field.
  449. * Split off from ImageHandler::formatMetadata, as used by more than
  450. * one type of handler.
  451. *
  452. * This is used by the media handlers that use the FormatMetadata class
  453. *
  454. * @param array $metadataArray
  455. * @param bool|IContextSource $context Context to use (optional)
  456. * @return array Array for use displaying metadata.
  457. */
  458. function formatMetadataHelper( $metadataArray, $context = false ) {
  459. $result = [
  460. 'visible' => [],
  461. 'collapsed' => []
  462. ];
  463. $formatted = FormatMetadata::getFormattedData( $metadataArray, $context );
  464. // Sort fields into visible and collapsed
  465. $visibleFields = $this->visibleMetadataFields();
  466. foreach ( $formatted as $name => $value ) {
  467. $tag = strtolower( $name );
  468. self::addMeta( $result,
  469. in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
  470. 'exif',
  471. $tag,
  472. $value
  473. );
  474. }
  475. return $result;
  476. }
  477. /**
  478. * Get a list of metadata items which should be displayed when
  479. * the metadata table is collapsed.
  480. *
  481. * @return array Array of strings
  482. */
  483. protected function visibleMetadataFields() {
  484. return FormatMetadata::getVisibleFields();
  485. }
  486. /**
  487. * This is used to generate an array element for each metadata value
  488. * That array is then used to generate the table of metadata values
  489. * on the image page
  490. *
  491. * @param array &$array An array containing elements for each type of visibility
  492. * and each of those elements being an array of metadata items. This function adds
  493. * a value to that array.
  494. * @param string $visibility ('visible' or 'collapsed') if this value is hidden
  495. * by default.
  496. * @param string $type Type of metadata tag (currently always 'exif')
  497. * @param string $id The name of the metadata tag (like 'artist' for example).
  498. * its name in the table displayed is the message "$type-$id" (Ex exif-artist ).
  499. * @param string $value Thingy goes into a wikitext table; it used to be escaped but
  500. * that was incompatible with previous practise of customized display
  501. * with wikitext formatting via messages such as 'exif-model-value'.
  502. * So the escaping is taken back out, but generally this seems a confusing
  503. * interface.
  504. * @param bool|string $param Value to pass to the message for the name of the field
  505. * as $1. Currently this parameter doesn't seem to ever be used.
  506. *
  507. * Note, everything here is passed through the parser later on (!)
  508. */
  509. protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
  510. $msg = wfMessage( "$type-$id", $param );
  511. if ( $msg->exists() ) {
  512. $name = $msg->text();
  513. } else {
  514. // This is for future compatibility when using instant commons.
  515. // So as to not display as ugly a name if a new metadata
  516. // property is defined that we don't know about
  517. // (not a major issue since such a property would be collapsed
  518. // by default).
  519. wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id . "\n" );
  520. $name = wfEscapeWikiText( $id );
  521. }
  522. $array[$visibility][] = [
  523. 'id' => "$type-$id",
  524. 'name' => $name,
  525. 'value' => $value
  526. ];
  527. }
  528. /**
  529. * Short description. Shown on Special:Search results.
  530. *
  531. * @param File $file
  532. * @return string
  533. */
  534. function getShortDesc( $file ) {
  535. return self::getGeneralShortDesc( $file );
  536. }
  537. /**
  538. * Long description. Shown under image on image description page surounded by ().
  539. *
  540. * @param File $file
  541. * @return string
  542. */
  543. public function getLongDesc( $file ) {
  544. return self::getGeneralLongDesc( $file );
  545. }
  546. /**
  547. * Used instead of getShortDesc if there is no handler registered for file.
  548. *
  549. * @param File $file
  550. * @return string
  551. */
  552. static function getGeneralShortDesc( $file ) {
  553. global $wgLang;
  554. return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
  555. }
  556. /**
  557. * Used instead of getLongDesc if there is no handler registered for file.
  558. *
  559. * @param File $file
  560. * @return string
  561. */
  562. static function getGeneralLongDesc( $file ) {
  563. return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
  564. ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse();
  565. }
  566. /**
  567. * Calculate the largest thumbnail width for a given original file size
  568. * such that the thumbnail's height is at most $maxHeight.
  569. * @param int $boxWidth Width of the thumbnail box.
  570. * @param int $boxHeight Height of the thumbnail box.
  571. * @param int $maxHeight Maximum height expected for the thumbnail.
  572. * @return int
  573. */
  574. public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
  575. $idealWidth = $boxWidth * $maxHeight / $boxHeight;
  576. $roundedUp = ceil( $idealWidth );
  577. if ( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
  578. return floor( $idealWidth );
  579. } else {
  580. return $roundedUp;
  581. }
  582. }
  583. /**
  584. * Shown in file history box on image description page.
  585. *
  586. * @param File $file
  587. * @return string Dimensions
  588. */
  589. function getDimensionsString( $file ) {
  590. return '';
  591. }
  592. /**
  593. * Modify the parser object post-transform.
  594. *
  595. * This is often used to do $parser->addOutputHook(),
  596. * in order to add some javascript to render a viewer.
  597. * See TimedMediaHandler or OggHandler for an example.
  598. *
  599. * @param Parser $parser
  600. * @param File $file
  601. */
  602. function parserTransformHook( $parser, $file ) {
  603. }
  604. /**
  605. * File validation hook called on upload.
  606. *
  607. * If the file at the given local path is not valid, or its MIME type does not
  608. * match the handler class, a Status object should be returned containing
  609. * relevant errors.
  610. *
  611. * @param string $fileName The local path to the file.
  612. * @return Status
  613. */
  614. public function verifyUpload( $fileName ) {
  615. return Status::newGood();
  616. }
  617. /**
  618. * Check for zero-sized thumbnails. These can be generated when
  619. * no disk space is available or some other error occurs
  620. *
  621. * @param string $dstPath The location of the suspect file
  622. * @param int $retval Return value of some shell process, file will be deleted if this is non-zero
  623. * @return bool True if removed, false otherwise
  624. */
  625. function removeBadFile( $dstPath, $retval = 0 ) {
  626. if ( file_exists( $dstPath ) ) {
  627. $thumbstat = stat( $dstPath );
  628. if ( $thumbstat['size'] == 0 || $retval != 0 ) {
  629. $result = unlink( $dstPath );
  630. if ( $result ) {
  631. wfDebugLog( 'thumbnail',
  632. sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
  633. $thumbstat['size'], $dstPath ) );
  634. } else {
  635. wfDebugLog( 'thumbnail',
  636. sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
  637. $thumbstat['size'], $dstPath ) );
  638. }
  639. return true;
  640. }
  641. }
  642. return false;
  643. }
  644. /**
  645. * Remove files from the purge list.
  646. *
  647. * This is used by some video handlers to prevent ?action=purge
  648. * from removing a transcoded video, which is expensive to
  649. * regenerate.
  650. *
  651. * @see LocalFile::purgeThumbnails
  652. *
  653. * @param array &$files
  654. * @param array $options Purge options. Currently will always be
  655. * an array with a single key 'forThumbRefresh' set to true.
  656. */
  657. public function filterThumbnailPurgeList( &$files, $options ) {
  658. // Do nothing
  659. }
  660. /**
  661. * True if the handler can rotate the media
  662. * @since 1.24 non-static. From 1.21-1.23 was static
  663. * @return bool
  664. */
  665. public function canRotate() {
  666. return false;
  667. }
  668. /**
  669. * On supporting image formats, try to read out the low-level orientation
  670. * of the file and return the angle that the file needs to be rotated to
  671. * be viewed.
  672. *
  673. * This information is only useful when manipulating the original file;
  674. * the width and height we normally work with is logical, and will match
  675. * any produced output views.
  676. *
  677. * For files we don't know, we return 0.
  678. *
  679. * @param File $file
  680. * @return int 0, 90, 180 or 270
  681. */
  682. public function getRotation( $file ) {
  683. return 0;
  684. }
  685. /**
  686. * Log an error that occurred in an external process
  687. *
  688. * Moved from BitmapHandler to MediaHandler with MediaWiki 1.23
  689. *
  690. * @since 1.23
  691. * @param int $retval
  692. * @param string $err Error reported by command. Anything longer than
  693. * MediaHandler::MAX_ERR_LOG_SIZE is stripped off.
  694. * @param string $cmd
  695. */
  696. protected function logErrorForExternalProcess( $retval, $err, $cmd ) {
  697. # Keep error output limited (T59985)
  698. $errMessage = trim( substr( $err, 0, self::MAX_ERR_LOG_SIZE ) );
  699. wfDebugLog( 'thumbnail',
  700. sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
  701. wfHostname(), $retval, $errMessage, $cmd ) );
  702. }
  703. /**
  704. * Get list of languages file can be viewed in.
  705. *
  706. * @param File $file
  707. * @return string[] Array of language codes, or empty array if unsupported.
  708. * @since 1.23
  709. */
  710. public function getAvailableLanguages( File $file ) {
  711. return [];
  712. }
  713. /**
  714. * When overridden in a descendant class, returns a language code most suiting
  715. *
  716. * @since 1.32
  717. *
  718. * @param string $userPreferredLanguage Language code requesed
  719. * @param string[] $availableLanguages Languages present in the file
  720. * @return string|null Language code picked or null if not supported/available
  721. */
  722. public function getMatchedLanguage( $userPreferredLanguage, array $availableLanguages ) {
  723. return null;
  724. }
  725. /**
  726. * On file types that support renderings in multiple languages,
  727. * which language is used by default if unspecified.
  728. *
  729. * If getAvailableLanguages returns a non-empty array, this must return
  730. * a valid language code. Otherwise can return null if files of this
  731. * type do not support alternative language renderings.
  732. *
  733. * @param File $file
  734. * @return string|null Language code or null if multi-language not supported for filetype.
  735. * @since 1.23
  736. */
  737. public function getDefaultRenderLanguage( File $file ) {
  738. return null;
  739. }
  740. /**
  741. * If its an audio file, return the length of the file. Otherwise 0.
  742. *
  743. * File::getLength() existed for a long time, but was calling a method
  744. * that only existed in some subclasses of this class (The TMH ones).
  745. *
  746. * @param File $file
  747. * @return float Length in seconds
  748. * @since 1.23
  749. */
  750. public function getLength( $file ) {
  751. return 0.0;
  752. }
  753. /**
  754. * True if creating thumbnails from the file is large or otherwise resource-intensive.
  755. * @param File $file
  756. * @return bool
  757. */
  758. public function isExpensiveToThumbnail( $file ) {
  759. return false;
  760. }
  761. /**
  762. * Returns whether or not this handler supports the chained generation of thumbnails according
  763. * to buckets
  764. * @return bool
  765. * @since 1.24
  766. */
  767. public function supportsBucketing() {
  768. return false;
  769. }
  770. /**
  771. * Returns a normalised params array for which parameters have been cleaned up for bucketing
  772. * purposes
  773. * @param array $params
  774. * @return array
  775. */
  776. public function sanitizeParamsForBucketing( $params ) {
  777. return $params;
  778. }
  779. /**
  780. * Gets configuration for the file warning message. Return value of
  781. * the following structure:
  782. * [
  783. * // Required, module with messages loaded for the client
  784. * 'module' => 'example.filewarning.messages',
  785. * // Required, array of names of messages
  786. * 'messages' => [
  787. * // Required, main warning message
  788. * 'main' => 'example-filewarning-main',
  789. * // Optional, header for warning dialog
  790. * 'header' => 'example-filewarning-header',
  791. * // Optional, footer for warning dialog
  792. * 'footer' => 'example-filewarning-footer',
  793. * // Optional, text for more-information link (see below)
  794. * 'info' => 'example-filewarning-info',
  795. * ],
  796. * // Optional, link for more information
  797. * 'link' => 'http://example.com',
  798. * ]
  799. *
  800. * Returns null if no warning is necessary.
  801. * @param File $file
  802. * @return array|null
  803. */
  804. public function getWarningConfig( $file ) {
  805. return null;
  806. }
  807. /**
  808. * Converts a dimensions array about a potentially multipage document from an
  809. * exhaustive list of ordered page numbers to a list of page ranges
  810. * @param array $pagesByDimensions
  811. * @return string
  812. * @since 1.30
  813. */
  814. public static function getPageRangesByDimensions( $pagesByDimensions ) {
  815. $pageRangesByDimensions = [];
  816. foreach ( $pagesByDimensions as $dimensions => $pageList ) {
  817. $ranges = [];
  818. $firstPage = $pageList[0];
  819. $lastPage = $firstPage - 1;
  820. foreach ( $pageList as $page ) {
  821. if ( $page > $lastPage + 1 ) {
  822. if ( $firstPage != $lastPage ) {
  823. $ranges[] = "$firstPage-$lastPage";
  824. } else {
  825. $ranges[] = "$firstPage";
  826. }
  827. $firstPage = $page;
  828. }
  829. $lastPage = $page;
  830. }
  831. if ( $firstPage != $lastPage ) {
  832. $ranges[] = "$firstPage-$lastPage";
  833. } else {
  834. $ranges[] = "$firstPage";
  835. }
  836. $pageRangesByDimensions[ $dimensions ] = $ranges;
  837. }
  838. $dimensionsString = [];
  839. foreach ( $pageRangesByDimensions as $dimensions => $pageRanges ) {
  840. $dimensionsString[] = "$dimensions:" . implode( ',', $pageRanges );
  841. }
  842. return implode( '/', $dimensionsString );
  843. }
  844. /**
  845. * Get useful response headers for GET/HEAD requests for a file with the given metadata
  846. * @param array $metadata Contains this handler's unserialized getMetadata() for a file
  847. * @return array
  848. * @since 1.30
  849. */
  850. public function getContentHeaders( $metadata ) {
  851. return [ 'X-Content-Dimensions' => '' ]; // T175689
  852. }
  853. }