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