]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/media/Generic.php
MediaWiki 1.16.4
[autoinstalls/mediawiki.git] / includes / media / Generic.php
1 <?php
2 /**
3  * Media-handling base classes and generic functionality
4  * @file
5  * @ingroup Media
6  */
7
8 /**
9  * Base media handler class
10  *
11  * @ingroup Media
12  */
13 abstract class MediaHandler {
14         const TRANSFORM_LATER = 1;
15
16         /**
17          * Instance cache
18          */
19         static $handlers = array();
20
21         /**
22          * Get a MediaHandler for a given MIME type from the instance cache
23          */
24         static function getHandler( $type ) {
25                 global $wgMediaHandlers;
26                 if ( !isset( $wgMediaHandlers[$type] ) ) {
27                         wfDebug( __METHOD__ . ": no handler found for $type.\n");
28                         return false;
29                 }
30                 $class = $wgMediaHandlers[$type];
31                 if ( !isset( self::$handlers[$class] ) ) {
32                         self::$handlers[$class] = new $class;
33                         if ( !self::$handlers[$class]->isEnabled() ) {
34                                 self::$handlers[$class] = false;
35                         }
36                 }
37                 return self::$handlers[$class];
38         }
39
40         /**
41          * Get an associative array mapping magic word IDs to parameter names.
42          * Will be used by the parser to identify parameters.
43          */
44         abstract function getParamMap();
45
46         /*
47          * Validate a thumbnail parameter at parse time.
48          * Return true to accept the parameter, and false to reject it.
49          * If you return false, the parser will do something quiet and forgiving.
50          */
51         abstract function validateParam( $name, $value );
52
53         /**
54          * Merge a parameter array into a string appropriate for inclusion in filenames
55          */
56         abstract function makeParamString( $params );
57
58         /**
59          * Parse a param string made with makeParamString back into an array
60          */
61         abstract function parseParamString( $str );
62
63         /**
64          * Changes the parameter array as necessary, ready for transformation.
65          * Should be idempotent.
66          * Returns false if the parameters are unacceptable and the transform should fail
67          */
68         abstract function normaliseParams( $image, &$params );
69
70         /**
71          * Get an image size array like that returned by getimagesize(), or false if it
72          * can't be determined.
73          *
74          * @param $image File: the image object, or false if there isn't one
75          * @param $fileName String: the filename
76          * @return Array
77          */
78         abstract function getImageSize( $image, $path );
79
80         /**
81          * Get handler-specific metadata which will be saved in the img_metadata field.
82          *
83          * @param $image File: the image object, or false if there isn't one
84          * @param $path String: the filename
85          * @return String
86          */
87         function getMetadata( $image, $path ) { return ''; }
88
89         /**
90          * Get a string describing the type of metadata, for display purposes.
91          */
92         function getMetadataType( $image ) { return false; }
93
94         /**
95          * Check if the metadata string is valid for this handler.
96          * If it returns false, Image will reload the metadata from the file and update the database
97          */
98         function isMetadataValid( $image, $metadata ) { return true; }
99
100
101         /**
102          * Get a MediaTransformOutput object representing an alternate of the transformed
103          * output which will call an intermediary thumbnail assist script.
104          *
105          * Used when the repository has a thumbnailScriptUrl option configured.
106          *
107          * Return false to fall back to the regular getTransform().
108          */
109         function getScriptedTransform( $image, $script, $params ) {
110                 return false;
111         }
112
113         /**
114          * Get a MediaTransformOutput object representing the transformed output. Does not
115          * actually do the transform.
116          *
117          * @param $image File: the image object
118          * @param $dstPath String: filesystem destination path
119          * @param $dstUrl String: Destination URL to use in output HTML
120          * @param $params Array: Arbitrary set of parameters validated by $this->validateParam()
121          */
122         function getTransform( $image, $dstPath, $dstUrl, $params ) {
123                 return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
124         }
125
126         /**
127          * Get a MediaTransformOutput object representing the transformed output. Does the
128          * transform unless $flags contains self::TRANSFORM_LATER.
129          *
130          * @param $image File: the image object
131          * @param $dstPath String: filesystem destination path
132          * @param $dstUrl String: destination URL to use in output HTML
133          * @param $params Array: arbitrary set of parameters validated by $this->validateParam()
134          * @param $flags Integer: a bitfield, may contain self::TRANSFORM_LATER
135          */
136         abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
137
138         /**
139          * Get the thumbnail extension and MIME type for a given source MIME type
140          * @return array thumbnail extension and MIME type
141          */
142         function getThumbType( $ext, $mime ) {
143                 return array( $ext, $mime );
144         }
145
146         /**
147          * True if the handled types can be transformed
148          */
149         function canRender( $file ) { return true; }
150         /**
151          * True if handled types cannot be displayed directly in a browser
152          * but can be rendered
153          */
154         function mustRender( $file ) { return false; }
155         /**
156          * True if the type has multi-page capabilities
157          */
158         function isMultiPage( $file ) { return false; }
159         /**
160          * Page count for a multi-page document, false if unsupported or unknown
161          */
162         function pageCount( $file ) { return false; }
163         /**
164          * False if the handler is disabled for all files
165          */
166         function isEnabled() { return true; }
167
168         /**
169          * Get an associative array of page dimensions
170          * Currently "width" and "height" are understood, but this might be
171          * expanded in the future.
172          * Returns false if unknown or if the document is not multi-page.
173          */
174         function getPageDimensions( $image, $page ) {
175                 $gis = $this->getImageSize( $image, $image->getPath() );
176                 return array(
177                         'width' => $gis[0],
178                         'height' => $gis[1]
179                 );
180         }
181
182         /**
183          * Generic getter for text layer.
184          * Currently overloaded by PDF and DjVu handlers
185          */
186         function getPageText( $image, $page ) {
187                 return false;
188         }
189
190         /**
191          * Get an array structure that looks like this:
192          *
193          * array(
194          *    'visible' => array(
195          *       'Human-readable name' => 'Human readable value',
196          *       ...
197          *    ),
198          *    'collapsed' => array(
199          *       'Human-readable name' => 'Human readable value',
200          *       ...
201          *    )
202          * )
203          * The UI will format this into a table where the visible fields are always
204          * visible, and the collapsed fields are optionally visible.
205          *
206          * The function should return false if there is no metadata to display.
207          */
208
209         /**
210          * FIXME: I don't really like this interface, it's not very flexible
211          * I think the media handler should generate HTML instead. It can do
212          * all the formatting according to some standard. That makes it possible
213          * to do things like visual indication of grouped and chained streams
214          * in ogg container files.
215          */
216         function formatMetadata( $image ) {
217                 return false;
218         }
219
220         /**
221          * @todo Fixme: document this!
222          * 'value' thingy goes into a wikitext table; it used to be escaped but
223          * that was incompatible with previous practice of customized display
224          * with wikitext formatting via messages such as 'exif-model-value'.
225          * So the escaping is taken back out, but generally this seems a confusing
226          * interface.
227          */
228         protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
229                 $array[$visibility][] = array(
230                         'id' => "$type-$id",
231                         'name' => wfMsg( "$type-$id", $param ),
232                         'value' => $value
233                 );
234         }
235
236         function getShortDesc( $file ) {
237                 global $wgLang;
238                 $nbytes = '(' . wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
239                         $wgLang->formatNum( $file->getSize() ) ) . ')';
240                 return "$nbytes";
241         }
242
243         function getLongDesc( $file ) {
244                 global $wgUser;
245                 $sk = $wgUser->getSkin();
246                 return wfMsgExt( 'file-info', 'parseinline',
247                         $sk->formatSize( $file->getSize() ),
248                         $file->getMimeType() );
249         }
250         
251         static function getGeneralShortDesc( $file ) {
252                 global $wgLang;
253                 $nbytes = '(' . wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
254                         $wgLang->formatNum( $file->getSize() ) ) . ')';
255                 return "$nbytes";
256         }
257
258         static function getGeneralLongDesc( $file ) {
259                 global $wgUser;
260                 $sk = $wgUser->getSkin();
261                 return wfMsgExt( 'file-info', 'parseinline',
262                         $sk->formatSize( $file->getSize() ),
263                         $file->getMimeType() );
264         }
265
266         function getDimensionsString( $file ) {
267                 return '';
268         }
269
270         /**
271          * Modify the parser object post-transform
272          */
273         function parserTransformHook( $parser, $file ) {}
274
275         /**
276          * Check for zero-sized thumbnails. These can be generated when
277          * no disk space is available or some other error occurs
278          *
279          * @param $dstPath The location of the suspect file
280          * @param $retval Return value of some shell process, file will be deleted if this is non-zero
281          * @return true if removed, false otherwise
282          */
283         function removeBadFile( $dstPath, $retval = 0 ) {
284                 if( file_exists( $dstPath ) ) {
285                         $thumbstat = stat( $dstPath );
286                         if( $thumbstat['size'] == 0 || $retval != 0 ) {
287                                 wfDebugLog( 'thumbnail',
288                                         sprintf( 'Removing bad %d-byte thumbnail "%s"',
289                                                 $thumbstat['size'], $dstPath ) );
290                                 unlink( $dstPath );
291                                 return true;
292                         }
293                 }
294                 return false;
295         }
296 }
297
298 /**
299  * Media handler abstract base class for images
300  *
301  * @ingroup Media
302  */
303 abstract class ImageHandler extends MediaHandler {
304         function canRender( $file ) {
305                 if ( $file->getWidth() && $file->getHeight() ) {
306                         return true;
307                 } else {
308                         return false;
309                 }
310         }
311
312         function getParamMap() {
313                 return array( 'img_width' => 'width' );
314         }
315
316         function validateParam( $name, $value ) {
317                 if ( in_array( $name, array( 'width', 'height' ) ) ) {
318                         if ( $value <= 0 ) {
319                                 return false;
320                         } else {
321                                 return true;
322                         }
323                 } else {
324                         return false;
325                 }
326         }
327
328         function makeParamString( $params ) {
329                 if ( isset( $params['physicalWidth'] ) ) {
330                         $width = $params['physicalWidth'];
331                 } elseif ( isset( $params['width'] ) ) {
332                         $width = $params['width'];
333                 } else {
334                         throw new MWException( 'No width specified to '.__METHOD__ );
335                 }
336                 # Removed for ProofreadPage
337                 #$width = intval( $width );
338                 return "{$width}px";
339         }
340
341         function parseParamString( $str ) {
342                 $m = false;
343                 if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
344                         return array( 'width' => $m[1] );
345                 } else {
346                         return false;
347                 }
348         }
349
350         function getScriptParams( $params ) {
351                 return array( 'width' => $params['width'] );
352         }
353
354         function normaliseParams( $image, &$params ) {
355                 $mimeType = $image->getMimeType();
356
357                 if ( !isset( $params['width'] ) ) {
358                         return false;
359                 }
360                 if ( !isset( $params['page'] ) ) {
361                         $params['page'] = 1;
362                 }
363                 $srcWidth = $image->getWidth( $params['page'] );
364                 $srcHeight = $image->getHeight( $params['page'] );
365                 if ( isset( $params['height'] ) && $params['height'] != -1 ) {
366                         if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
367                                 $params['width'] = wfFitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
368                         }
369                 }
370                 $params['height'] = File::scaleHeight( $srcWidth, $srcHeight, $params['width'] );
371                 if ( !$this->validateThumbParams( $params['width'], $params['height'], $srcWidth, $srcHeight, $mimeType ) ) {
372                         return false;
373                 }
374                 return true;
375         }
376
377         /**
378          * Get a transform output object without actually doing the transform
379          */
380         function getTransform( $image, $dstPath, $dstUrl, $params ) {
381                 return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
382         }
383
384         /**
385          * Validate thumbnail parameters and fill in the correct height
386          *
387          * @param $width Integer: specified width (input/output)
388          * @param $height Integer: height (output only)
389          * @return false to indicate that an error should be returned to the user.
390          */
391         function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
392                 $width = intval( $width );
393
394                 # Sanity check $width
395                 if( $width <= 0) {
396                         wfDebug( __METHOD__.": Invalid destination width: $width\n" );
397                         return false;
398                 }
399                 if ( $srcWidth <= 0 ) {
400                         wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
401                         return false;
402                 }
403
404                 $height = File::scaleHeight( $srcWidth, $srcHeight, $width );
405                 return true;
406         }
407
408         function getScriptedTransform( $image, $script, $params ) {
409                 if ( !$this->normaliseParams( $image, $params ) ) {
410                         return false;
411                 }
412                 $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
413                 $page = isset( $params['page'] ) ? $params['page'] : false;
414
415                 if( $image->mustRender() || $params['width'] < $image->getWidth() ) {
416                         return new ThumbnailImage( $image, $url, $params['width'], $params['height'], $page );
417                 }
418         }
419
420         function getImageSize( $image, $path ) {
421                 wfSuppressWarnings();
422                 $gis = getimagesize( $path );
423                 wfRestoreWarnings();
424                 return $gis;
425         }
426
427         function getShortDesc( $file ) {
428                 global $wgLang;
429                 $nbytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
430                         $wgLang->formatNum( $file->getSize() ) );
431                 $widthheight = wfMsgHtml( 'widthheight', $wgLang->formatNum( $file->getWidth() ) ,$wgLang->formatNum( $file->getHeight() ) );
432
433                 return "$widthheight ($nbytes)";
434         }
435
436         function getLongDesc( $file ) {
437                 global $wgLang;
438                 return wfMsgExt('file-info-size', 'parseinline',
439                         $wgLang->formatNum( $file->getWidth() ),
440                         $wgLang->formatNum( $file->getHeight() ),
441                         $wgLang->formatSize( $file->getSize() ),
442                         $file->getMimeType() );
443         }
444
445         function getDimensionsString( $file ) {
446                 global $wgLang;
447                 $pages = $file->pageCount();
448                 $width = $wgLang->formatNum( $file->getWidth() );
449                 $height = $wgLang->formatNum( $file->getHeight() );
450                 $pagesFmt = $wgLang->formatNum( $pages );
451
452                 if ( $pages > 1 ) {
453                         return wfMsgExt( 'widthheightpage', 'parsemag', $width, $height, $pagesFmt );
454                 } else {
455                         return wfMsg( 'widthheight', $width, $height );
456                 }
457         }
458 }