Merged filerepo-work branch:
[lhc/web/wiklou.git] / includes / filerepo / File.php
1 <?php
2
3 /**
4 * Base file class. Do not instantiate.
5 *
6 * Implements some public methods and some protected utility functions which
7 * are required by multiple child classes. Contains stub functionality for
8 * unimplemented public methods.
9 *
10 * Stub functions which should be overridden are marked with STUB. Some more
11 * concrete functions are also typically overridden by child classes.
12 *
13 *
14 * NOTE FOR WINDOWS USERS:
15 * To enable EXIF functions, add the folloing lines to the
16 * "Windows extensions" section of php.ini:
17 *
18 * extension=extensions/php_mbstring.dll
19 * extension=extensions/php_exif.dll
20 *
21 * @addtogroup FileRepo
22 */
23 class File {
24 const DELETED_FILE = 1;
25 const DELETED_COMMENT = 2;
26 const DELETED_USER = 4;
27 const DELETED_RESTRICTED = 8;
28 const RENDER_NOW = 1;
29
30 const DELETE_SOURCE = 1;
31
32 /**
33 * Some member variables can be lazy-initialised using __get(). The
34 * initialisation function for these variables is always a function named
35 * like getVar(), where Var is the variable name with upper-case first
36 * letter.
37 *
38 * The following variables are initialised in this way in this base class:
39 * name, extension, handler, path, canRender, isSafeFile,
40 * transformScript, hashPath, pageCount, url
41 *
42 * Code within this class should generally use the accessor function
43 * directly, since __get() isn't re-entrant and therefore causes bugs that
44 * depend on initialisation order.
45 */
46
47 /**
48 * The following member variables are not lazy-initialised
49 */
50 var $repo, $title, $lastError;
51
52 function __construct( $title, $repo ) {
53 $this->title = $title;
54 $this->repo = $repo;
55 }
56
57 function __get( $name ) {
58 $function = array( $this, 'get' . ucfirst( $name ) );
59 if ( !is_callable( $function ) ) {
60 return null;
61 } else {
62 $this->$name = call_user_func( $function );
63 return $this->$name;
64 }
65 }
66
67 /**
68 * Normalize a file extension to the common form, and ensure it's clean.
69 * Extensions with non-alphanumeric characters will be discarded.
70 *
71 * @param $ext string (without the .)
72 * @return string
73 */
74 static function normalizeExtension( $ext ) {
75 $lower = strtolower( $ext );
76 $squish = array(
77 'htm' => 'html',
78 'jpeg' => 'jpg',
79 'mpeg' => 'mpg',
80 'tiff' => 'tif' );
81 if( isset( $squish[$lower] ) ) {
82 return $squish[$lower];
83 } elseif( preg_match( '/^[0-9a-z]+$/', $lower ) ) {
84 return $lower;
85 } else {
86 return '';
87 }
88 }
89
90 /**
91 * Upgrade the database row if there is one
92 * Called by ImagePage
93 * STUB
94 */
95 function upgradeRow() {}
96
97 /**
98 * Split an internet media type into its two components; if not
99 * a two-part name, set the minor type to 'unknown'.
100 *
101 * @param $mime "text/html" etc
102 * @return array ("text", "html") etc
103 */
104 static function splitMime( $mime ) {
105 if( strpos( $mime, '/' ) !== false ) {
106 return explode( '/', $mime, 2 );
107 } else {
108 return array( $mime, 'unknown' );
109 }
110 }
111
112 /**
113 * Return the name of this file
114 * @public
115 */
116 function getName() {
117 if ( !isset( $this->name ) ) {
118 $this->name = $this->title->getDBkey();
119 }
120 return $this->name;
121 }
122
123 /**
124 * Get the file extension, e.g. "svg"
125 */
126 function getExtension() {
127 if ( !isset( $this->extension ) ) {
128 $n = strrpos( $this->getName(), '.' );
129 $this->extension = self::normalizeExtension(
130 $n ? substr( $this->getName(), $n + 1 ) : '' );
131 }
132 return $this->extension;
133 }
134
135 /**
136 * Return the associated title object
137 * @public
138 */
139 function getTitle() { return $this->title; }
140
141 /**
142 * Return the URL of the file
143 * @public
144 */
145 function getUrl() {
146 if ( !isset( $this->url ) ) {
147 $this->url = $this->repo->getZoneUrl( 'public' ) . '/' . $this->getUrlRel();
148 }
149 return $this->url;
150 }
151
152 function getViewURL() {
153 if( $this->mustRender()) {
154 if( $this->canRender() ) {
155 return $this->createThumb( $this->getWidth() );
156 }
157 else {
158 wfDebug(__METHOD__.': supposed to render '.$this->getName().' ('.$this->getMimeType()."), but can't!\n");
159 return $this->getURL(); #hm... return NULL?
160 }
161 } else {
162 return $this->getURL();
163 }
164 }
165
166 /**
167 * Return the full filesystem path to the file. Note that this does
168 * not mean that a file actually exists under that location.
169 *
170 * This path depends on whether directory hashing is active or not,
171 * i.e. whether the files are all found in the same directory,
172 * or in hashed paths like /images/3/3c.
173 *
174 * May return false if the file is not locally accessible.
175 *
176 * @public
177 */
178 function getPath() {
179 if ( !isset( $this->path ) ) {
180 $this->path = $this->repo->getZonePath('public') . '/' . $this->getRel();
181 }
182 return $this->path;
183 }
184
185 /**
186 * Alias for getPath()
187 * @public
188 */
189 function getFullPath() {
190 return $this->getPath();
191 }
192
193 /**
194 * Return the width of the image. Returns false if the width is unknown
195 * or undefined.
196 *
197 * STUB
198 * Overridden by LocalFile, UnregisteredLocalFile
199 * @public
200 */
201 function getWidth( $page = 1 ) { return false; }
202
203 /**
204 * Return the height of the image. Returns false if the height is unknown
205 * or undefined
206 *
207 * STUB
208 * Overridden by LocalFile, UnregisteredLocalFile
209 * @public
210 */
211 function getHeight( $page = 1 ) { return false; }
212
213 /**
214 * Get handler-specific metadata
215 * Overridden by LocalFile, UnregisteredLocalFile
216 * STUB
217 */
218 function getMetadata() { return false; }
219
220 /**
221 * Return the size of the image file, in bytes
222 * Overridden by LocalFile, UnregisteredLocalFile
223 * STUB
224 * @public
225 */
226 function getSize() { return false; }
227
228 /**
229 * Returns the mime type of the file.
230 * Overridden by LocalFile, UnregisteredLocalFile
231 * STUB
232 */
233 function getMimeType() { return 'unknown/unknown'; }
234
235 /**
236 * Return the type of the media in the file.
237 * Use the value returned by this function with the MEDIATYPE_xxx constants.
238 * Overridden by LocalFile,
239 * STUB
240 */
241 function getMediaType() { return MEDIATYPE_UNKNOWN; }
242
243 /**
244 * Checks if the file can be presented to the browser as a bitmap.
245 *
246 * Currently, this checks if the file is an image format
247 * that can be converted to a format
248 * supported by all browsers (namely GIF, PNG and JPEG),
249 * or if it is an SVG image and SVG conversion is enabled.
250 */
251 function canRender() {
252 if ( !isset( $this->canRender ) ) {
253 $this->canRender = $this->getHandler() && $this->handler->canRender();
254 }
255 return $this->canRender;
256 }
257
258 /**
259 * Accessor for __get()
260 */
261 protected function getCanRender() {
262 return $this->canRender();
263 }
264
265 /**
266 * Return true if the file is of a type that can't be directly
267 * rendered by typical browsers and needs to be re-rasterized.
268 *
269 * This returns true for everything but the bitmap types
270 * supported by all browsers, i.e. JPEG; GIF and PNG. It will
271 * also return true for any non-image formats.
272 *
273 * @return bool
274 */
275 function mustRender() {
276 return $this->getHandler() && $this->handler->mustRender();
277 }
278
279 /**
280 * Determines if this media file may be shown inline on a page.
281 *
282 * This is currently synonymous to canRender(), but this could be
283 * extended to also allow inline display of other media,
284 * like flash animations or videos. If you do so, please keep in mind that
285 * that could be a security risk.
286 */
287 function allowInlineDisplay() {
288 return $this->canRender();
289 }
290
291 /**
292 * Determines if this media file is in a format that is unlikely to
293 * contain viruses or malicious content. It uses the global
294 * $wgTrustedMediaFormats list to determine if the file is safe.
295 *
296 * This is used to show a warning on the description page of non-safe files.
297 * It may also be used to disallow direct [[media:...]] links to such files.
298 *
299 * Note that this function will always return true if allowInlineDisplay()
300 * or isTrustedFile() is true for this file.
301 */
302 function isSafeFile() {
303 if ( !isset( $this->isSafeFile ) ) {
304 $this->isSafeFile = $this->_getIsSafeFile();
305 }
306 return $this->isSafeFile;
307 }
308
309 /** Accessor for __get() */
310 protected function getIsSafeFile() {
311 return $this->isSafeFile();
312 }
313
314 /** Uncached accessor */
315 protected function _getIsSafeFile() {
316 if ($this->allowInlineDisplay()) return true;
317 if ($this->isTrustedFile()) return true;
318
319 global $wgTrustedMediaFormats;
320
321 $type= $this->getMediaType();
322 $mime= $this->getMimeType();
323 #wfDebug("LocalFile::isSafeFile: type= $type, mime= $mime\n");
324
325 if (!$type || $type===MEDIATYPE_UNKNOWN) return false; #unknown type, not trusted
326 if ( in_array( $type, $wgTrustedMediaFormats) ) return true;
327
328 if ($mime==="unknown/unknown") return false; #unknown type, not trusted
329 if ( in_array( $mime, $wgTrustedMediaFormats) ) return true;
330
331 return false;
332 }
333
334 /** Returns true if the file is flagged as trusted. Files flagged that way
335 * can be linked to directly, even if that is not allowed for this type of
336 * file normally.
337 *
338 * This is a dummy function right now and always returns false. It could be
339 * implemented to extract a flag from the database. The trusted flag could be
340 * set on upload, if the user has sufficient privileges, to bypass script-
341 * and html-filters. It may even be coupled with cryptographics signatures
342 * or such.
343 */
344 function isTrustedFile() {
345 #this could be implemented to check a flag in the databas,
346 #look for signatures, etc
347 return false;
348 }
349
350 /**
351 * Returns true if file exists in the repository.
352 *
353 * Overridden by LocalFile to avoid unnecessary stat calls.
354 *
355 * @return boolean Whether file exists in the repository.
356 * @public
357 */
358 function exists() {
359 return $this->getPath() && file_exists( $this->path );
360 }
361
362 function getTransformScript() {
363 if ( !isset( $this->transformScript ) ) {
364 $this->transformScript = false;
365 if ( $this->repo ) {
366 $script = $this->repo->getThumbScriptUrl();
367 if ( $script ) {
368 $this->transformScript = "$script?f=" . urlencode( $this->getName() );
369 }
370 }
371 }
372 return $this->transformScript;
373 }
374
375 /**
376 * Get a ThumbnailImage which is the same size as the source
377 */
378 function getUnscaledThumb( $page = false ) {
379 $width = $this->getWidth( $page );
380 if ( !$width ) {
381 return $this->iconThumb();
382 }
383 if ( $page ) {
384 $params = array(
385 'page' => $page,
386 'width' => $this->getWidth( $page )
387 );
388 } else {
389 $params = array( 'width' => $this->getWidth() );
390 }
391 return $this->transform( $params );
392 }
393
394 /**
395 * Return the file name of a thumbnail with the specified parameters
396 *
397 * @param array $params Handler-specific parameters
398 * @private
399 */
400 function thumbName( $params ) {
401 if ( !$this->getHandler() ) {
402 return null;
403 }
404 $extension = $this->getExtension();
405 list( $thumbExt, $thumbMime ) = $this->handler->getThumbType( $extension, $this->getMimeType() );
406 $thumbName = $this->handler->makeParamString( $params ) . '-' . $this->getName();
407 if ( $thumbExt != $extension ) {
408 $thumbName .= ".$thumbExt";
409 }
410 return $thumbName;
411 }
412
413 /**
414 * Create a thumbnail of the image having the specified width/height.
415 * The thumbnail will not be created if the width is larger than the
416 * image's width. Let the browser do the scaling in this case.
417 * The thumbnail is stored on disk and is only computed if the thumbnail
418 * file does not exist OR if it is older than the image.
419 * Returns the URL.
420 *
421 * Keeps aspect ratio of original image. If both width and height are
422 * specified, the generated image will be no bigger than width x height,
423 * and will also have correct aspect ratio.
424 *
425 * @param integer $width maximum width of the generated thumbnail
426 * @param integer $height maximum height of the image (optional)
427 * @public
428 */
429 function createThumb( $width, $height = -1 ) {
430 $params = array( 'width' => $width );
431 if ( $height != -1 ) {
432 $params['height'] = $height;
433 }
434 $thumb = $this->transform( $params );
435 if( is_null( $thumb ) || $thumb->isError() ) return '';
436 return $thumb->getUrl();
437 }
438
439 /**
440 * As createThumb, but returns a ThumbnailImage object. This can
441 * provide access to the actual file, the real size of the thumb,
442 * and can produce a convenient <img> tag for you.
443 *
444 * For non-image formats, this may return a filetype-specific icon.
445 *
446 * @param integer $width maximum width of the generated thumbnail
447 * @param integer $height maximum height of the image (optional)
448 * @param boolean $render True to render the thumbnail if it doesn't exist,
449 * false to just return the URL
450 *
451 * @return ThumbnailImage or null on failure
452 * @public
453 *
454 * @deprecated use transform()
455 */
456 function getThumbnail( $width, $height=-1, $render = true ) {
457 $params = array( 'width' => $width );
458 if ( $height != -1 ) {
459 $params['height'] = $height;
460 }
461 $flags = $render ? self::RENDER_NOW : 0;
462 return $this->transform( $params, $flags );
463 }
464
465 /**
466 * Transform a media file
467 *
468 * @param array $params An associative array of handler-specific parameters. Typical
469 * keys are width, height and page.
470 * @param integer $flags A bitfield, may contain self::RENDER_NOW to force rendering
471 * @return MediaTransformOutput
472 */
473 function transform( $params, $flags = 0 ) {
474 global $wgUseSquid, $wgIgnoreImageErrors;
475
476 wfProfileIn( __METHOD__ );
477 do {
478 if ( !$this->getHandler() || !$this->handler->canRender() ) {
479 // not a bitmap or renderable image, don't try.
480 $thumb = $this->iconThumb();
481 break;
482 }
483
484 $script = $this->getTransformScript();
485 if ( $script && !($flags & self::RENDER_NOW) ) {
486 // Use a script to transform on client request
487 $thumb = $this->handler->getScriptedTransform( $this, $script, $params );
488 break;
489 }
490
491 $normalisedParams = $params;
492 $this->handler->normaliseParams( $this, $normalisedParams );
493 $thumbName = $this->thumbName( $normalisedParams );
494 $thumbPath = $this->getThumbPath( $thumbName );
495 $thumbUrl = $this->getThumbUrl( $thumbName );
496
497 if ( $this->repo->canTransformVia404() && !($flags & self::RENDER_NOW ) ) {
498 $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
499 break;
500 }
501
502 wfDebug( "Doing stat for $thumbPath\n" );
503 $this->migrateThumbFile( $thumbName );
504 if ( file_exists( $thumbPath ) ) {
505 $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
506 break;
507 }
508 $thumb = $this->handler->doTransform( $this, $thumbPath, $thumbUrl, $params );
509
510 // Ignore errors if requested
511 if ( !$thumb ) {
512 $thumb = null;
513 } elseif ( $thumb->isError() ) {
514 $this->lastError = $thumb->toText();
515 if ( $wgIgnoreImageErrors && !($flags & self::RENDER_NOW) ) {
516 $thumb = $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
517 }
518 }
519
520 if ( $wgUseSquid ) {
521 wfPurgeSquidServers( array( $thumbUrl ) );
522 }
523 } while (false);
524
525 wfProfileOut( __METHOD__ );
526 return $thumb;
527 }
528
529 /**
530 * Hook into transform() to allow migration of thumbnail files
531 * STUB
532 * Overridden by LocalFile
533 */
534 function migrateThumbFile() {}
535
536 /**
537 * Get a MediaHandler instance for this file
538 */
539 function getHandler() {
540 if ( !isset( $this->handler ) ) {
541 $this->handler = MediaHandler::getHandler( $this->getMimeType() );
542 }
543 return $this->handler;
544 }
545
546 /**
547 * Get a ThumbnailImage representing a file type icon
548 * @return ThumbnailImage
549 */
550 function iconThumb() {
551 global $wgStylePath, $wgStyleDirectory;
552
553 $try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' );
554 foreach( $try as $icon ) {
555 $path = '/common/images/icons/' . $icon;
556 $filepath = $wgStyleDirectory . $path;
557 if( file_exists( $filepath ) ) {
558 return new ThumbnailImage( $wgStylePath . $path, 120, 120 );
559 }
560 }
561 return null;
562 }
563
564 /**
565 * Get last thumbnailing error.
566 * Largely obsolete.
567 */
568 function getLastError() {
569 return $this->lastError;
570 }
571
572 /**
573 * Get all thumbnail names previously generated for this file
574 * STUB
575 * Overridden by LocalFile
576 */
577 function getThumbnails() { return array(); }
578
579 /**
580 * Purge shared caches such as thumbnails and DB data caching
581 * STUB
582 * Overridden by LocalFile
583 */
584 function purgeCache( $archiveFiles = array() ) {}
585
586 /**
587 * Purge the file description page, but don't go after
588 * pages using the file. Use when modifying file history
589 * but not the current data.
590 */
591 function purgeDescription() {
592 $title = $this->getTitle();
593 if ( $title ) {
594 $title->invalidateCache();
595 $title->purgeSquid();
596 }
597 }
598
599 /**
600 * Purge metadata and all affected pages when the file is created,
601 * deleted, or majorly updated. A set of additional URLs may be
602 * passed to purge, such as specific file files which have changed.
603 * @param $urlArray array
604 */
605 function purgeEverything( $urlArr=array() ) {
606 // Delete thumbnails and refresh file metadata cache
607 $this->purgeCache();
608 $this->purgeDescription();
609
610 // Purge cache of all pages using this file
611 $title = $this->getTitle();
612 if ( $title ) {
613 $update = new HTMLCacheUpdate( $title, 'imagelinks' );
614 $update->doUpdate();
615 }
616 }
617
618 /**
619 * Return the history of this file, line by line. Starts with current version,
620 * then old versions. Should return an object similar to an image/oldimage
621 * database row.
622 *
623 * @public
624 * STUB
625 * Overridden in LocalFile
626 */
627 function nextHistoryLine() {
628 return false;
629 }
630
631 /**
632 * Reset the history pointer to the first element of the history
633 * @public
634 * STUB
635 * Overridden in LocalFile.
636 */
637 function resetHistory() {}
638
639 /**
640 * Get the filename hash component of the directory including trailing slash,
641 * e.g. f/fa/
642 * If the repository is not hashed, returns an empty string.
643 */
644 function getHashPath() {
645 if ( !isset( $this->hashPath ) ) {
646 $this->hashPath = $this->repo->getHashPath( $this->getName() );
647 }
648 return $this->hashPath;
649 }
650
651 /**
652 * Get the path of the file relative to the public zone root
653 */
654 function getRel() {
655 return $this->getHashPath() . $this->getName();
656 }
657
658 /**
659 * Get urlencoded relative path of the file
660 */
661 function getUrlRel() {
662 return $this->getHashPath() . urlencode( $this->getName() );
663 }
664
665 /** Get the path of the archive directory, or a particular file if $suffix is specified */
666 function getArchivePath( $suffix = false ) {
667 $path = $this->repo->getZonePath('public') . '/archive/' . $this->getHashPath();
668 if ( $suffix !== false ) {
669 $path .= '/' . $suffix;
670 }
671 return $path;
672 }
673
674 /** Get the path of the thumbnail directory, or a particular file if $suffix is specified */
675 function getThumbPath( $suffix = false ) {
676 $path = $this->repo->getZonePath('public') . '/thumb/' . $this->getRel();
677 if ( $suffix !== false ) {
678 $path .= '/' . $suffix;
679 }
680 return $path;
681 }
682
683 /** Get the URL of the archive directory, or a particular file if $suffix is specified */
684 function getArchiveUrl( $suffix = false ) {
685 $path = $this->repo->getZoneUrl('public') . '/archive/' . $this->getHashPath();
686 if ( $suffix !== false ) {
687 $path .= '/' . urlencode( $suffix );
688 }
689 return $path;
690 }
691
692 /** Get the URL of the thumbnail directory, or a particular file if $suffix is specified */
693 function getThumbUrl( $suffix = false ) {
694 $path = $this->repo->getZoneUrl('public') . '/thumb/' . $this->getUrlRel();
695 if ( $suffix !== false ) {
696 $path .= '/' . urlencode( $suffix );
697 }
698 return $path;
699 }
700
701 /** Get the virtual URL for an archive file or directory */
702 function getArchiveVirtualUrl( $suffix = false ) {
703 $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath();
704 if ( $suffix !== false ) {
705 $path .= '/' . urlencode( $suffix );
706 }
707 return $path;
708 }
709
710 /** Get the virtual URL for a thumbnail file or directory */
711 function getThumbVirtualUrl( $suffix = false ) {
712 $path = $this->repo->getVirtualUrl() . '/public/thumb/' . $this->getHashPath();
713 if ( $suffix !== false ) {
714 $path .= '/' . urlencode( $suffix );
715 }
716 return $path;
717 }
718
719 /**
720 * @return bool
721 */
722 function isHashed() {
723 return $this->repo->isHashed();
724 }
725
726 function readOnlyError() {
727 throw new MWException( get_class($this) . ': write operations are not supported' );
728 }
729
730 /**
731 * Record a file upload in the upload log and the image table
732 * STUB
733 * Overridden by LocalFile
734 */
735 function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '', $watch = false ) {
736 $this->readOnlyError();
737 }
738
739 /**
740 * Move or copy a file to its public location. If a file exists at the
741 * destination, move it to an archive. Returns the archive name on success
742 * or an empty string if it was a new file, and a wikitext-formatted
743 * WikiError object on failure.
744 *
745 * The archive name should be passed through to recordUpload for database
746 * registration.
747 *
748 * @param string $sourcePath Local filesystem path to the source image
749 * @param integer $flags A bitwise combination of:
750 * File::DELETE_SOURCE Delete the source file, i.e. move
751 * rather than copy
752 * @return The archive name on success or an empty string if it was a new
753 * file, and a wikitext-formatted WikiError object on failure.
754 *
755 * STUB
756 * Overridden by LocalFile
757 */
758 function publish( $srcPath, $flags = 0 ) {
759 $this->readOnlyError();
760 }
761
762 /**
763 * Get an array of Title objects which are articles which use this file
764 * Also adds their IDs to the link cache
765 *
766 * This is mostly copied from Title::getLinksTo()
767 *
768 * @deprecated Use HTMLCacheUpdate, this function uses too much memory
769 */
770 function getLinksTo( $options = '' ) {
771 wfProfileIn( __METHOD__ );
772
773 // Note: use local DB not repo DB, we want to know local links
774 if ( $options ) {
775 $db = wfGetDB( DB_MASTER );
776 } else {
777 $db = wfGetDB( DB_SLAVE );
778 }
779 $linkCache =& LinkCache::singleton();
780
781 list( $page, $imagelinks ) = $db->tableNamesN( 'page', 'imagelinks' );
782 $encName = $db->addQuotes( $this->getName() );
783 $sql = "SELECT page_namespace,page_title,page_id FROM $page,$imagelinks WHERE page_id=il_from AND il_to=$encName $options";
784 $res = $db->query( $sql, __METHOD__ );
785
786 $retVal = array();
787 if ( $db->numRows( $res ) ) {
788 while ( $row = $db->fetchObject( $res ) ) {
789 if ( $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title ) ) {
790 $linkCache->addGoodLinkObj( $row->page_id, $titleObj );
791 $retVal[] = $titleObj;
792 }
793 }
794 }
795 $db->freeResult( $res );
796 wfProfileOut( __METHOD__ );
797 return $retVal;
798 }
799
800 function getExifData() {
801 if ( !$this->getHandler() || $this->handler->getMetadataType( $this ) != 'exif' ) {
802 return array();
803 }
804 $metadata = $this->getMetadata();
805 if ( !$metadata ) {
806 return array();
807 }
808 $exif = unserialize( $metadata );
809 if ( !$exif ) {
810 return array();
811 }
812 unset( $exif['MEDIAWIKI_EXIF_VERSION'] );
813 $format = new FormatExif( $exif );
814
815 return $format->getFormattedData();
816 }
817
818 /**
819 * Returns true if the file comes from the local file repository.
820 *
821 * @return bool
822 */
823 function isLocal() {
824 return $this->repo && $this->repo->getName() == 'local';
825 }
826
827 /**
828 * Returns true if the image is an old version
829 * STUB
830 */
831 function isOld() {
832 return false;
833 }
834
835 /**
836 * Is this file a "deleted" file in a private archive?
837 * STUB
838 */
839 function isDeleted( $field ) {
840 return false;
841 }
842
843 /**
844 * Was this file ever deleted from the wiki?
845 *
846 * @return bool
847 */
848 function wasDeleted() {
849 $title = $this->getTitle();
850 return $title && $title->isDeleted() > 0;
851 }
852
853 /**
854 * Delete all versions of the file.
855 *
856 * Moves the files into an archive directory (or deletes them)
857 * and removes the database rows.
858 *
859 * Cache purging is done; logging is caller's responsibility.
860 *
861 * @param $reason
862 * @return true on success, false on some kind of failure
863 * STUB
864 * Overridden by LocalFile
865 */
866 function delete( $reason, $suppress=false ) {
867 $this->readOnlyError();
868 }
869
870 /**
871 * Restore all or specified deleted revisions to the given file.
872 * Permissions and logging are left to the caller.
873 *
874 * May throw database exceptions on error.
875 *
876 * @param $versions set of record ids of deleted items to restore,
877 * or empty to restore all revisions.
878 * @return the number of file revisions restored if successful,
879 * or false on failure
880 * STUB
881 * Overridden by LocalFile
882 */
883 function restore( $versions=array(), $Unsuppress=false ) {
884 $this->readOnlyError();
885 }
886
887 /**
888 * Returns 'true' if this image is a multipage document, e.g. a DJVU
889 * document.
890 *
891 * @return Bool
892 */
893 function isMultipage() {
894 return $this->getHandler() && $this->handler->isMultiPage();
895 }
896
897 /**
898 * Returns the number of pages of a multipage document, or NULL for
899 * documents which aren't multipage documents
900 */
901 function pageCount() {
902 if ( !isset( $this->pageCount ) ) {
903 if ( $this->getHandler() && $this->handler->isMultiPage() ) {
904 $this->pageCount = $this->handler->pageCount( $this );
905 } else {
906 $this->pageCount = false;
907 }
908 }
909 return $this->pageCount;
910 }
911
912 /**
913 * Calculate the height of a thumbnail using the source and destination width
914 */
915 static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) {
916 // Exact integer multiply followed by division
917 if ( $srcWidth == 0 ) {
918 return 0;
919 } else {
920 return round( $srcHeight * $dstWidth / $srcWidth );
921 }
922 }
923
924 /**
925 * Get an image size array like that returned by getimagesize(), or false if it
926 * can't be determined.
927 *
928 * @param string $fileName The filename
929 * @return array
930 */
931 function getImageSize( $fileName ) {
932 if ( !$this->getHandler() ) {
933 return false;
934 }
935 return $this->handler->getImageSize( $this, $fileName );
936 }
937
938 /**
939 * Get the URL of the image description page. May return false if it is
940 * unknown or not applicable.
941 */
942 function getDescriptionUrl() {
943 return $this->repo->getDescriptionUrl( $this->getName() );
944 }
945
946 /**
947 * Get the HTML text of the description page, if available
948 */
949 function getDescriptionText() {
950 if ( !$this->repo->fetchDescription ) {
951 return false;
952 }
953 $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName() );
954 if ( $renderUrl ) {
955 wfDebug( "Fetching shared description from $renderUrl\n" );
956 return Http::get( $renderUrl );
957 } else {
958 return false;
959 }
960 }
961
962 /**
963 * Get the 14-character timestamp of the file upload, or false if
964 */
965 function getTimestmap() {
966 $path = $this->getPath();
967 if ( !file_exists( $path ) ) {
968 return false;
969 }
970 return wfTimestamp( filemtime( $path ) );
971 }
972
973 /**
974 * Determine if the current user is allowed to view a particular
975 * field of this file, if it's marked as deleted.
976 * STUB
977 * @param int $field
978 * @return bool
979 */
980 function userCan( $field ) {
981 return true;
982 }
983 }
984
985 ?>