[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/filerepo/file/ -> File.php (source)

   1  <?php
   2  /**
   3   * @defgroup FileAbstraction File abstraction
   4   * @ingroup FileRepo
   5   *
   6   * Represents files in a repository.
   7   */
   8  
   9  /**
  10   * Base code for files.
  11   *
  12   * This program is free software; you can redistribute it and/or modify
  13   * it under the terms of the GNU General Public License as published by
  14   * the Free Software Foundation; either version 2 of the License, or
  15   * (at your option) any later version.
  16   *
  17   * This program is distributed in the hope that it will be useful,
  18   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20   * GNU General Public License for more details.
  21   *
  22   * You should have received a copy of the GNU General Public License along
  23   * with this program; if not, write to the Free Software Foundation, Inc.,
  24   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  25   * http://www.gnu.org/copyleft/gpl.html
  26   *
  27   * @file
  28   * @ingroup FileAbstraction
  29   */
  30  
  31  /**
  32   * Implements some public methods and some protected utility functions which
  33   * are required by multiple child classes. Contains stub functionality for
  34   * unimplemented public methods.
  35   *
  36   * Stub functions which should be overridden are marked with STUB. Some more
  37   * concrete functions are also typically overridden by child classes.
  38   *
  39   * Note that only the repo object knows what its file class is called. You should
  40   * never name a file class explictly outside of the repo class. Instead use the
  41   * repo's factory functions to generate file objects, for example:
  42   *
  43   * RepoGroup::singleton()->getLocalRepo()->newFile( $title );
  44   *
  45   * The convenience functions wfLocalFile() and wfFindFile() should be sufficient
  46   * in most cases.
  47   *
  48   * @ingroup FileAbstraction
  49   */
  50  abstract class File {
  51      // Bitfield values akin to the Revision deletion constants
  52      const DELETED_FILE = 1;
  53      const DELETED_COMMENT = 2;
  54      const DELETED_USER = 4;
  55      const DELETED_RESTRICTED = 8;
  56  
  57      /** Force rendering in the current process */
  58      const RENDER_NOW = 1;
  59      /**
  60       * Force rendering even if thumbnail already exist and using RENDER_NOW
  61       * I.e. you have to pass both flags: File::RENDER_NOW | File::RENDER_FORCE
  62       */
  63      const RENDER_FORCE = 2;
  64  
  65      const DELETE_SOURCE = 1;
  66  
  67      // Audience options for File::getDescription()
  68      const FOR_PUBLIC = 1;
  69      const FOR_THIS_USER = 2;
  70      const RAW = 3;
  71  
  72      // Options for File::thumbName()
  73      const THUMB_FULL_NAME = 1;
  74  
  75      /**
  76       * Some member variables can be lazy-initialised using __get(). The
  77       * initialisation function for these variables is always a function named
  78       * like getVar(), where Var is the variable name with upper-case first
  79       * letter.
  80       *
  81       * The following variables are initialised in this way in this base class:
  82       *    name, extension, handler, path, canRender, isSafeFile,
  83       *    transformScript, hashPath, pageCount, url
  84       *
  85       * Code within this class should generally use the accessor function
  86       * directly, since __get() isn't re-entrant and therefore causes bugs that
  87       * depend on initialisation order.
  88       */
  89  
  90      /**
  91       * The following member variables are not lazy-initialised
  92       */
  93  
  94      /** @var FileRepo|LocalRepo|ForeignAPIRepo|bool */
  95      public $repo;
  96  
  97      /** @var Title|string|bool */
  98      protected $title;
  99  
 100      /** @var string Text of last error */
 101      protected $lastError;
 102  
 103      /** @var string Main part of the title, with underscores (Title::getDBkey) */
 104      protected $redirected;
 105  
 106      /** @var Title */
 107      protected $redirectedTitle;
 108  
 109      /** @var FSFile|bool False if undefined */
 110      protected $fsFile;
 111  
 112      /** @var MediaHandler */
 113      protected $handler;
 114  
 115      /** @var string The URL corresponding to one of the four basic zones */
 116      protected $url;
 117  
 118      /** @var string File extension */
 119      protected $extension;
 120  
 121      /** @var string The name of a file from its title object */
 122      protected $name;
 123  
 124      /** @var string The storage path corresponding to one of the zones */
 125      protected $path;
 126  
 127      /** @var string Relative path including trailing slash */
 128      protected $hashPath;
 129  
 130      /** @var string Number of pages of a multipage document, or false for
 131       *    documents which aren't multipage documents
 132       */
 133      protected $pageCount;
 134  
 135      /** @var string URL of transformscript (for example thumb.php) */
 136      protected $transformScript;
 137  
 138      /** @var Title */
 139      protected $redirectTitle;
 140  
 141      /** @var bool Wether the output of transform() for this file is likely to be valid. */
 142      protected $canRender;
 143  
 144      /** @var bool Wether this media file is in a format that is unlikely to
 145       *    contain viruses or malicious content
 146       */
 147      protected $isSafeFile;
 148  
 149      /** @var string Required Repository class type */
 150      protected $repoClass = 'FileRepo';
 151  
 152      /** @var array Cache of tmp filepaths pointing to generated bucket thumbnails, keyed by width */
 153      protected $tmpBucketedThumbCache = array();
 154  
 155      /**
 156       * Call this constructor from child classes.
 157       *
 158       * Both $title and $repo are optional, though some functions
 159       * may return false or throw exceptions if they are not set.
 160       * Most subclasses will want to call assertRepoDefined() here.
 161       *
 162       * @param Title|string|bool $title
 163       * @param FileRepo|bool $repo
 164       */
 165  	function __construct( $title, $repo ) {
 166          if ( $title !== false ) { // subclasses may not use MW titles
 167              $title = self::normalizeTitle( $title, 'exception' );
 168          }
 169          $this->title = $title;
 170          $this->repo = $repo;
 171      }
 172  
 173      /**
 174       * Given a string or Title object return either a
 175       * valid Title object with namespace NS_FILE or null
 176       *
 177       * @param Title|string $title
 178       * @param string|bool $exception Use 'exception' to throw an error on bad titles
 179       * @throws MWException
 180       * @return Title|null
 181       */
 182  	static function normalizeTitle( $title, $exception = false ) {
 183          $ret = $title;
 184          if ( $ret instanceof Title ) {
 185              # Normalize NS_MEDIA -> NS_FILE
 186              if ( $ret->getNamespace() == NS_MEDIA ) {
 187                  $ret = Title::makeTitleSafe( NS_FILE, $ret->getDBkey() );
 188              # Sanity check the title namespace
 189              } elseif ( $ret->getNamespace() !== NS_FILE ) {
 190                  $ret = null;
 191              }
 192          } else {
 193              # Convert strings to Title objects
 194              $ret = Title::makeTitleSafe( NS_FILE, (string)$ret );
 195          }
 196          if ( !$ret && $exception !== false ) {
 197              throw new MWException( "`$title` is not a valid file title." );
 198          }
 199  
 200          return $ret;
 201      }
 202  
 203  	function __get( $name ) {
 204          $function = array( $this, 'get' . ucfirst( $name ) );
 205          if ( !is_callable( $function ) ) {
 206              return null;
 207          } else {
 208              $this->$name = call_user_func( $function );
 209  
 210              return $this->$name;
 211          }
 212      }
 213  
 214      /**
 215       * Normalize a file extension to the common form, and ensure it's clean.
 216       * Extensions with non-alphanumeric characters will be discarded.
 217       *
 218       * @param string $ext (without the .)
 219       * @return string
 220       */
 221  	static function normalizeExtension( $ext ) {
 222          $lower = strtolower( $ext );
 223          $squish = array(
 224              'htm' => 'html',
 225              'jpeg' => 'jpg',
 226              'mpeg' => 'mpg',
 227              'tiff' => 'tif',
 228              'ogv' => 'ogg' );
 229          if ( isset( $squish[$lower] ) ) {
 230              return $squish[$lower];
 231          } elseif ( preg_match( '/^[0-9a-z]+$/', $lower ) ) {
 232              return $lower;
 233          } else {
 234              return '';
 235          }
 236      }
 237  
 238      /**
 239       * Checks if file extensions are compatible
 240       *
 241       * @param File $old Old file
 242       * @param string $new New name
 243       *
 244       * @return bool|null
 245       */
 246  	static function checkExtensionCompatibility( File $old, $new ) {
 247          $oldMime = $old->getMimeType();
 248          $n = strrpos( $new, '.' );
 249          $newExt = self::normalizeExtension( $n ? substr( $new, $n + 1 ) : '' );
 250          $mimeMagic = MimeMagic::singleton();
 251  
 252          return $mimeMagic->isMatchingExtension( $newExt, $oldMime );
 253      }
 254  
 255      /**
 256       * Upgrade the database row if there is one
 257       * Called by ImagePage
 258       * STUB
 259       */
 260  	function upgradeRow() {
 261      }
 262  
 263      /**
 264       * Split an internet media type into its two components; if not
 265       * a two-part name, set the minor type to 'unknown'.
 266       *
 267       * @param string $mime "text/html" etc
 268       * @return array ("text", "html") etc
 269       */
 270  	public static function splitMime( $mime ) {
 271          if ( strpos( $mime, '/' ) !== false ) {
 272              return explode( '/', $mime, 2 );
 273          } else {
 274              return array( $mime, 'unknown' );
 275          }
 276      }
 277  
 278      /**
 279       * Callback for usort() to do file sorts by name
 280       *
 281       * @param File $a
 282       * @param File $b
 283       * @return int Result of name comparison
 284       */
 285  	public static function compare( File $a, File $b ) {
 286          return strcmp( $a->getName(), $b->getName() );
 287      }
 288  
 289      /**
 290       * Return the name of this file
 291       *
 292       * @return string
 293       */
 294  	public function getName() {
 295          if ( !isset( $this->name ) ) {
 296              $this->assertRepoDefined();
 297              $this->name = $this->repo->getNameFromTitle( $this->title );
 298          }
 299  
 300          return $this->name;
 301      }
 302  
 303      /**
 304       * Get the file extension, e.g. "svg"
 305       *
 306       * @return string
 307       */
 308  	function getExtension() {
 309          if ( !isset( $this->extension ) ) {
 310              $n = strrpos( $this->getName(), '.' );
 311              $this->extension = self::normalizeExtension(
 312                  $n ? substr( $this->getName(), $n + 1 ) : '' );
 313          }
 314  
 315          return $this->extension;
 316      }
 317  
 318      /**
 319       * Return the associated title object
 320       *
 321       * @return Title
 322       */
 323  	public function getTitle() {
 324          return $this->title;
 325      }
 326  
 327      /**
 328       * Return the title used to find this file
 329       *
 330       * @return Title
 331       */
 332  	public function getOriginalTitle() {
 333          if ( $this->redirected ) {
 334              return $this->getRedirectedTitle();
 335          }
 336  
 337          return $this->title;
 338      }
 339  
 340      /**
 341       * Return the URL of the file
 342       *
 343       * @return string
 344       */
 345  	public function getUrl() {
 346          if ( !isset( $this->url ) ) {
 347              $this->assertRepoDefined();
 348              $ext = $this->getExtension();
 349              $this->url = $this->repo->getZoneUrl( 'public', $ext ) . '/' . $this->getUrlRel();
 350          }
 351  
 352          return $this->url;
 353      }
 354  
 355      /**
 356       * Return a fully-qualified URL to the file.
 357       * Upload URL paths _may or may not_ be fully qualified, so
 358       * we check. Local paths are assumed to belong on $wgServer.
 359       *
 360       * @return string
 361       */
 362  	public function getFullUrl() {
 363          return wfExpandUrl( $this->getUrl(), PROTO_RELATIVE );
 364      }
 365  
 366      /**
 367       * @return string
 368       */
 369  	public function getCanonicalUrl() {
 370          return wfExpandUrl( $this->getUrl(), PROTO_CANONICAL );
 371      }
 372  
 373      /**
 374       * @return string
 375       */
 376  	function getViewURL() {
 377          if ( $this->mustRender() ) {
 378              if ( $this->canRender() ) {
 379                  return $this->createThumb( $this->getWidth() );
 380              } else {
 381                  wfDebug( __METHOD__ . ': supposed to render ' . $this->getName() .
 382                      ' (' . $this->getMimeType() . "), but can't!\n" );
 383  
 384                  return $this->getURL(); #hm... return NULL?
 385              }
 386          } else {
 387              return $this->getURL();
 388          }
 389      }
 390  
 391      /**
 392       * Return the storage path to the file. Note that this does
 393       * not mean that a file actually exists under that location.
 394       *
 395       * This path depends on whether directory hashing is active or not,
 396       * i.e. whether the files are all found in the same directory,
 397       * or in hashed paths like /images/3/3c.
 398       *
 399       * Most callers don't check the return value, but ForeignAPIFile::getPath
 400       * returns false.
 401       *
 402       * @return string|bool ForeignAPIFile::getPath can return false
 403       */
 404  	public function getPath() {
 405          if ( !isset( $this->path ) ) {
 406              $this->assertRepoDefined();
 407              $this->path = $this->repo->getZonePath( 'public' ) . '/' . $this->getRel();
 408          }
 409  
 410          return $this->path;
 411      }
 412  
 413      /**
 414       * Get an FS copy or original of this file and return the path.
 415       * Returns false on failure. Callers must not alter the file.
 416       * Temporary files are cleared automatically.
 417       *
 418       * @return string|bool False on failure
 419       */
 420  	public function getLocalRefPath() {
 421          $this->assertRepoDefined();
 422          if ( !isset( $this->fsFile ) ) {
 423              $this->fsFile = $this->repo->getLocalReference( $this->getPath() );
 424              if ( !$this->fsFile ) {
 425                  $this->fsFile = false; // null => false; cache negative hits
 426              }
 427          }
 428  
 429          return ( $this->fsFile )
 430              ? $this->fsFile->getPath()
 431              : false;
 432      }
 433  
 434      /**
 435       * Return the width of the image. Returns false if the width is unknown
 436       * or undefined.
 437       *
 438       * STUB
 439       * Overridden by LocalFile, UnregisteredLocalFile
 440       *
 441       * @param int $page
 442       * @return int|bool
 443       */
 444  	public function getWidth( $page = 1 ) {
 445          return false;
 446      }
 447  
 448      /**
 449       * Return the height of the image. Returns false if the height is unknown
 450       * or undefined
 451       *
 452       * STUB
 453       * Overridden by LocalFile, UnregisteredLocalFile
 454       *
 455       * @param int $page
 456       * @return bool|int False on failure
 457       */
 458  	public function getHeight( $page = 1 ) {
 459          return false;
 460      }
 461  
 462      /**
 463       * Return the smallest bucket from $wgThumbnailBuckets which is at least
 464       * $wgThumbnailMinimumBucketDistance larger than $desiredWidth. The returned bucket, if any,
 465       * will always be bigger than $desiredWidth.
 466       *
 467       * @param int $desiredWidth
 468       * @param int $page
 469       * @return bool|int
 470       */
 471  	public function getThumbnailBucket( $desiredWidth, $page = 1 ) {
 472          global $wgThumbnailBuckets, $wgThumbnailMinimumBucketDistance;
 473  
 474          $imageWidth = $this->getWidth( $page );
 475  
 476          if ( $imageWidth === false ) {
 477              return false;
 478          }
 479  
 480          if ( $desiredWidth > $imageWidth ) {
 481              return false;
 482          }
 483  
 484          if ( !$wgThumbnailBuckets ) {
 485              return false;
 486          }
 487  
 488          $sortedBuckets = $wgThumbnailBuckets;
 489  
 490          sort( $sortedBuckets );
 491  
 492          foreach ( $sortedBuckets as $bucket ) {
 493              if ( $bucket > $imageWidth ) {
 494                  return false;
 495              }
 496  
 497              if ( $bucket - $wgThumbnailMinimumBucketDistance > $desiredWidth ) {
 498                  return $bucket;
 499              }
 500          }
 501  
 502          // Image is bigger than any available bucket
 503          return false;
 504      }
 505  
 506      /**
 507       * Returns ID or name of user who uploaded the file
 508       * STUB
 509       *
 510       * @param string $type 'text' or 'id'
 511       * @return string|int
 512       */
 513  	public function getUser( $type = 'text' ) {
 514          return null;
 515      }
 516  
 517      /**
 518       * Get the duration of a media file in seconds
 519       *
 520       * @return int
 521       */
 522  	public function getLength() {
 523          $handler = $this->getHandler();
 524          if ( $handler ) {
 525              return $handler->getLength( $this );
 526          } else {
 527              return 0;
 528          }
 529      }
 530  
 531      /**
 532       * Return true if the file is vectorized
 533       *
 534       * @return bool
 535       */
 536  	public function isVectorized() {
 537          $handler = $this->getHandler();
 538          if ( $handler ) {
 539              return $handler->isVectorized( $this );
 540          } else {
 541              return false;
 542          }
 543      }
 544  
 545      /**
 546       * Gives a (possibly empty) list of languages to render
 547       * the file in.
 548       *
 549       * If the file doesn't have translations, or if the file
 550       * format does not support that sort of thing, returns
 551       * an empty array.
 552       *
 553       * @return array
 554       * @since 1.23
 555       */
 556  	public function getAvailableLanguages() {
 557          $handler = $this->getHandler();
 558          if ( $handler ) {
 559              return $handler->getAvailableLanguages( $this );
 560          } else {
 561              return array();
 562          }
 563      }
 564  
 565      /**
 566       * In files that support multiple language, what is the default language
 567       * to use if none specified.
 568       *
 569       * @return string Lang code, or null if filetype doesn't support multiple languages.
 570       * @since 1.23
 571       */
 572  	public function getDefaultRenderLanguage() {
 573          $handler = $this->getHandler();
 574          if ( $handler ) {
 575              return $handler->getDefaultRenderLanguage( $this );
 576          } else {
 577              return null;
 578          }
 579      }
 580  
 581      /**
 582       * Will the thumbnail be animated if one would expect it to be.
 583       *
 584       * Currently used to add a warning to the image description page
 585       *
 586       * @return bool False if the main image is both animated
 587       *   and the thumbnail is not. In all other cases must return
 588       *   true. If image is not renderable whatsoever, should
 589       *   return true.
 590       */
 591  	public function canAnimateThumbIfAppropriate() {
 592          $handler = $this->getHandler();
 593          if ( !$handler ) {
 594              // We cannot handle image whatsoever, thus
 595              // one would not expect it to be animated
 596              // so true.
 597              return true;
 598          } else {
 599              if ( $this->allowInlineDisplay()
 600                  && $handler->isAnimatedImage( $this )
 601                  && !$handler->canAnimateThumbnail( $this )
 602              ) {
 603                  // Image is animated, but thumbnail isn't.
 604                  // This is unexpected to the user.
 605                  return false;
 606              } else {
 607                  // Image is not animated, so one would
 608                  // not expect thumb to be
 609                  return true;
 610              }
 611          }
 612      }
 613  
 614      /**
 615       * Get handler-specific metadata
 616       * Overridden by LocalFile, UnregisteredLocalFile
 617       * STUB
 618       * @return bool|array
 619       */
 620  	public function getMetadata() {
 621          return false;
 622      }
 623  
 624      /**
 625       * Like getMetadata but returns a handler independent array of common values.
 626       * @see MediaHandler::getCommonMetaArray()
 627       * @return array|bool Array or false if not supported
 628       * @since 1.23
 629       */
 630  	public function getCommonMetaArray() {
 631          $handler = $this->getHandler();
 632  
 633          if ( !$handler ) {
 634              return false;
 635          }
 636  
 637          return $handler->getCommonMetaArray( $this );
 638      }
 639  
 640      /**
 641       * get versioned metadata
 642       *
 643       * @param array|string $metadata Array or string of (serialized) metadata
 644       * @param int $version Version number.
 645       * @return array Array containing metadata, or what was passed to it on fail
 646       *   (unserializing if not array)
 647       */
 648  	public function convertMetadataVersion( $metadata, $version ) {
 649          $handler = $this->getHandler();
 650          if ( !is_array( $metadata ) ) {
 651              // Just to make the return type consistent
 652              $metadata = unserialize( $metadata );
 653          }
 654          if ( $handler ) {
 655              return $handler->convertMetadataVersion( $metadata, $version );
 656          } else {
 657              return $metadata;
 658          }
 659      }
 660  
 661      /**
 662       * Return the bit depth of the file
 663       * Overridden by LocalFile
 664       * STUB
 665       * @return int
 666       */
 667  	public function getBitDepth() {
 668          return 0;
 669      }
 670  
 671      /**
 672       * Return the size of the image file, in bytes
 673       * Overridden by LocalFile, UnregisteredLocalFile
 674       * STUB
 675       * @return bool
 676       */
 677  	public function getSize() {
 678          return false;
 679      }
 680  
 681      /**
 682       * Returns the MIME type of the file.
 683       * Overridden by LocalFile, UnregisteredLocalFile
 684       * STUB
 685       *
 686       * @return string
 687       */
 688  	function getMimeType() {
 689          return 'unknown/unknown';
 690      }
 691  
 692      /**
 693       * Return the type of the media in the file.
 694       * Use the value returned by this function with the MEDIATYPE_xxx constants.
 695       * Overridden by LocalFile,
 696       * STUB
 697       * @return string
 698       */
 699  	function getMediaType() {
 700          return MEDIATYPE_UNKNOWN;
 701      }
 702  
 703      /**
 704       * Checks if the output of transform() for this file is likely
 705       * to be valid. If this is false, various user elements will
 706       * display a placeholder instead.
 707       *
 708       * Currently, this checks if the file is an image format
 709       * that can be converted to a format
 710       * supported by all browsers (namely GIF, PNG and JPEG),
 711       * or if it is an SVG image and SVG conversion is enabled.
 712       *
 713       * @return bool
 714       */
 715  	function canRender() {
 716          if ( !isset( $this->canRender ) ) {
 717              $this->canRender = $this->getHandler() && $this->handler->canRender( $this ) && $this->exists();
 718          }
 719  
 720          return $this->canRender;
 721      }
 722  
 723      /**
 724       * Accessor for __get()
 725       * @return bool
 726       */
 727  	protected function getCanRender() {
 728          return $this->canRender();
 729      }
 730  
 731      /**
 732       * Return true if the file is of a type that can't be directly
 733       * rendered by typical browsers and needs to be re-rasterized.
 734       *
 735       * This returns true for everything but the bitmap types
 736       * supported by all browsers, i.e. JPEG; GIF and PNG. It will
 737       * also return true for any non-image formats.
 738       *
 739       * @return bool
 740       */
 741  	function mustRender() {
 742          return $this->getHandler() && $this->handler->mustRender( $this );
 743      }
 744  
 745      /**
 746       * Alias for canRender()
 747       *
 748       * @return bool
 749       */
 750  	function allowInlineDisplay() {
 751          return $this->canRender();
 752      }
 753  
 754      /**
 755       * Determines if this media file is in a format that is unlikely to
 756       * contain viruses or malicious content. It uses the global
 757       * $wgTrustedMediaFormats list to determine if the file is safe.
 758       *
 759       * This is used to show a warning on the description page of non-safe files.
 760       * It may also be used to disallow direct [[media:...]] links to such files.
 761       *
 762       * Note that this function will always return true if allowInlineDisplay()
 763       * or isTrustedFile() is true for this file.
 764       *
 765       * @return bool
 766       */
 767  	function isSafeFile() {
 768          if ( !isset( $this->isSafeFile ) ) {
 769              $this->isSafeFile = $this->getIsSafeFileUncached();
 770          }
 771  
 772          return $this->isSafeFile;
 773      }
 774  
 775      /**
 776       * Accessor for __get()
 777       *
 778       * @return bool
 779       */
 780  	protected function getIsSafeFile() {
 781          return $this->isSafeFile();
 782      }
 783  
 784      /**
 785       * Uncached accessor
 786       *
 787       * @return bool
 788       */
 789  	protected function getIsSafeFileUncached() {
 790          global $wgTrustedMediaFormats;
 791  
 792          if ( $this->allowInlineDisplay() ) {
 793              return true;
 794          }
 795          if ( $this->isTrustedFile() ) {
 796              return true;
 797          }
 798  
 799          $type = $this->getMediaType();
 800          $mime = $this->getMimeType();
 801          #wfDebug( "LocalFile::isSafeFile: type= $type, mime= $mime\n" );
 802  
 803          if ( !$type || $type === MEDIATYPE_UNKNOWN ) {
 804              return false; #unknown type, not trusted
 805          }
 806          if ( in_array( $type, $wgTrustedMediaFormats ) ) {
 807              return true;
 808          }
 809  
 810          if ( $mime === "unknown/unknown" ) {
 811              return false; #unknown type, not trusted
 812          }
 813          if ( in_array( $mime, $wgTrustedMediaFormats ) ) {
 814              return true;
 815          }
 816  
 817          return false;
 818      }
 819  
 820      /**
 821       * Returns true if the file is flagged as trusted. Files flagged that way
 822       * can be linked to directly, even if that is not allowed for this type of
 823       * file normally.
 824       *
 825       * This is a dummy function right now and always returns false. It could be
 826       * implemented to extract a flag from the database. The trusted flag could be
 827       * set on upload, if the user has sufficient privileges, to bypass script-
 828       * and html-filters. It may even be coupled with cryptographics signatures
 829       * or such.
 830       *
 831       * @return bool
 832       */
 833  	function isTrustedFile() {
 834          #this could be implemented to check a flag in the database,
 835          #look for signatures, etc
 836          return false;
 837      }
 838  
 839      /**
 840       * Returns true if file exists in the repository.
 841       *
 842       * Overridden by LocalFile to avoid unnecessary stat calls.
 843       *
 844       * @return bool Whether file exists in the repository.
 845       */
 846  	public function exists() {
 847          return $this->getPath() && $this->repo->fileExists( $this->path );
 848      }
 849  
 850      /**
 851       * Returns true if file exists in the repository and can be included in a page.
 852       * It would be unsafe to include private images, making public thumbnails inadvertently
 853       *
 854       * @return bool Whether file exists in the repository and is includable.
 855       */
 856  	public function isVisible() {
 857          return $this->exists();
 858      }
 859  
 860      /**
 861       * @return string
 862       */
 863  	function getTransformScript() {
 864          if ( !isset( $this->transformScript ) ) {
 865              $this->transformScript = false;
 866              if ( $this->repo ) {
 867                  $script = $this->repo->getThumbScriptUrl();
 868                  if ( $script ) {
 869                      $this->transformScript = wfAppendQuery( $script, array( 'f' => $this->getName() ) );
 870                  }
 871              }
 872          }
 873  
 874          return $this->transformScript;
 875      }
 876  
 877      /**
 878       * Get a ThumbnailImage which is the same size as the source
 879       *
 880       * @param array $handlerParams
 881       *
 882       * @return string
 883       */
 884  	function getUnscaledThumb( $handlerParams = array() ) {
 885          $hp =& $handlerParams;
 886          $page = isset( $hp['page'] ) ? $hp['page'] : false;
 887          $width = $this->getWidth( $page );
 888          if ( !$width ) {
 889              return $this->iconThumb();
 890          }
 891          $hp['width'] = $width;
 892          // be sure to ignore any height specification as well (bug 62258)
 893          unset( $hp['height'] );
 894  
 895          return $this->transform( $hp );
 896      }
 897  
 898      /**
 899       * Return the file name of a thumbnail with the specified parameters.
 900       * Use File::THUMB_FULL_NAME to always get a name like "<params>-<source>".
 901       * Otherwise, the format may be "<params>-<source>" or "<params>-thumbnail.<ext>".
 902       *
 903       * @param array $params Handler-specific parameters
 904       * @param int $flags Bitfield that supports THUMB_* constants
 905       * @return string
 906       */
 907  	public function thumbName( $params, $flags = 0 ) {
 908          $name = ( $this->repo && !( $flags & self::THUMB_FULL_NAME ) )
 909              ? $this->repo->nameForThumb( $this->getName() )
 910              : $this->getName();
 911  
 912          return $this->generateThumbName( $name, $params );
 913      }
 914  
 915      /**
 916       * Generate a thumbnail file name from a name and specified parameters
 917       *
 918       * @param string $name
 919       * @param array $params Parameters which will be passed to MediaHandler::makeParamString
 920       * @return string
 921       */
 922  	public function generateThumbName( $name, $params ) {
 923          if ( !$this->getHandler() ) {
 924              return null;
 925          }
 926          $extension = $this->getExtension();
 927          list( $thumbExt, ) = $this->getHandler()->getThumbType(
 928              $extension, $this->getMimeType(), $params );
 929          $thumbName = $this->getHandler()->makeParamString( $params ) . '-' . $name;
 930          if ( $thumbExt != $extension ) {
 931              $thumbName .= ".$thumbExt";
 932          }
 933  
 934          return $thumbName;
 935      }
 936  
 937      /**
 938       * Create a thumbnail of the image having the specified width/height.
 939       * The thumbnail will not be created if the width is larger than the
 940       * image's width. Let the browser do the scaling in this case.
 941       * The thumbnail is stored on disk and is only computed if the thumbnail
 942       * file does not exist OR if it is older than the image.
 943       * Returns the URL.
 944       *
 945       * Keeps aspect ratio of original image. If both width and height are
 946       * specified, the generated image will be no bigger than width x height,
 947       * and will also have correct aspect ratio.
 948       *
 949       * @param int $width Maximum width of the generated thumbnail
 950       * @param int $height Maximum height of the image (optional)
 951       *
 952       * @return string
 953       */
 954  	public function createThumb( $width, $height = -1 ) {
 955          $params = array( 'width' => $width );
 956          if ( $height != -1 ) {
 957              $params['height'] = $height;
 958          }
 959          $thumb = $this->transform( $params );
 960          if ( !$thumb || $thumb->isError() ) {
 961              return '';
 962          }
 963  
 964          return $thumb->getUrl();
 965      }
 966  
 967      /**
 968       * Return either a MediaTransformError or placeholder thumbnail (if $wgIgnoreImageErrors)
 969       *
 970       * @param string $thumbPath Thumbnail storage path
 971       * @param string $thumbUrl Thumbnail URL
 972       * @param array $params
 973       * @param int $flags
 974       * @return MediaTransformOutput
 975       */
 976  	protected function transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags ) {
 977          global $wgIgnoreImageErrors;
 978  
 979          $handler = $this->getHandler();
 980          if ( $handler && $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
 981              return $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
 982          } else {
 983              return new MediaTransformError( 'thumbnail_error',
 984                  $params['width'], 0, wfMessage( 'thumbnail-dest-create' )->text() );
 985          }
 986      }
 987  
 988      /**
 989       * Transform a media file
 990       *
 991       * @param array $params An associative array of handler-specific parameters.
 992       *   Typical keys are width, height and page.
 993       * @param int $flags A bitfield, may contain self::RENDER_NOW to force rendering
 994       * @return MediaTransformOutput|bool False on failure
 995       */
 996  	function transform( $params, $flags = 0 ) {
 997          global $wgThumbnailEpoch;
 998  
 999          wfProfileIn( __METHOD__ );
1000          do {
1001              if ( !$this->canRender() ) {
1002                  $thumb = $this->iconThumb();
1003                  break; // not a bitmap or renderable image, don't try
1004              }
1005  
1006              // Get the descriptionUrl to embed it as comment into the thumbnail. Bug 19791.
1007              $descriptionUrl = $this->getDescriptionUrl();
1008              if ( $descriptionUrl ) {
1009                  $params['descriptionUrl'] = wfExpandUrl( $descriptionUrl, PROTO_CANONICAL );
1010              }
1011  
1012              $handler = $this->getHandler();
1013              $script = $this->getTransformScript();
1014              if ( $script && !( $flags & self::RENDER_NOW ) ) {
1015                  // Use a script to transform on client request, if possible
1016                  $thumb = $handler->getScriptedTransform( $this, $script, $params );
1017                  if ( $thumb ) {
1018                      break;
1019                  }
1020              }
1021  
1022              $normalisedParams = $params;
1023              $handler->normaliseParams( $this, $normalisedParams );
1024  
1025              $thumbName = $this->thumbName( $normalisedParams );
1026              $thumbUrl = $this->getThumbUrl( $thumbName );
1027              $thumbPath = $this->getThumbPath( $thumbName ); // final thumb path
1028  
1029              if ( $this->repo ) {
1030                  // Defer rendering if a 404 handler is set up...
1031                  if ( $this->repo->canTransformVia404() && !( $flags & self::RENDER_NOW ) ) {
1032                      wfDebug( __METHOD__ . " transformation deferred.\n" );
1033                      // XXX: Pass in the storage path even though we are not rendering anything
1034                      // and the path is supposed to be an FS path. This is due to getScalerType()
1035                      // getting called on the path and clobbering $thumb->getUrl() if it's false.
1036                      $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
1037                      break;
1038                  }
1039                  // Check if an up-to-date thumbnail already exists...
1040                  wfDebug( __METHOD__ . ": Doing stat for $thumbPath\n" );
1041                  if ( !( $flags & self::RENDER_FORCE ) && $this->repo->fileExists( $thumbPath ) ) {
1042                      $timestamp = $this->repo->getFileTimestamp( $thumbPath );
1043                      if ( $timestamp !== false && $timestamp >= $wgThumbnailEpoch ) {
1044                          // XXX: Pass in the storage path even though we are not rendering anything
1045                          // and the path is supposed to be an FS path. This is due to getScalerType()
1046                          // getting called on the path and clobbering $thumb->getUrl() if it's false.
1047                          $thumb = $handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
1048                          $thumb->setStoragePath( $thumbPath );
1049                          break;
1050                      }
1051                  } elseif ( $flags & self::RENDER_FORCE ) {
1052                      wfDebug( __METHOD__ . " forcing rendering per flag File::RENDER_FORCE\n" );
1053                  }
1054  
1055                  // If the backend is ready-only, don't keep generating thumbnails
1056                  // only to return transformation errors, just return the error now.
1057                  if ( $this->repo->getReadOnlyReason() !== false ) {
1058                      $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags );
1059                      break;
1060                  }
1061              }
1062  
1063              $tmpFile = $this->makeTransformTmpFile( $thumbPath );
1064  
1065              if ( !$tmpFile ) {
1066                  $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $params, $flags );
1067              } else {
1068                  $thumb = $this->generateAndSaveThumb( $tmpFile, $params, $flags );
1069              }
1070          } while ( false );
1071  
1072          wfProfileOut( __METHOD__ );
1073  
1074          return is_object( $thumb ) ? $thumb : false;
1075      }
1076  
1077      /**
1078       * Generates a thumbnail according to the given parameters and saves it to storage
1079       * @param TempFSFile $tmpFile Temporary file where the rendered thumbnail will be saved
1080       * @param array $transformParams
1081       * @param int $flags
1082       * @return bool|MediaTransformOutput
1083       */
1084  	public function generateAndSaveThumb( $tmpFile, $transformParams, $flags ) {
1085          global $wgUseSquid, $wgIgnoreImageErrors;
1086  
1087          $handler = $this->getHandler();
1088  
1089          $normalisedParams = $transformParams;
1090          $handler->normaliseParams( $this, $normalisedParams );
1091  
1092          $thumbName = $this->thumbName( $normalisedParams );
1093          $thumbUrl = $this->getThumbUrl( $thumbName );
1094          $thumbPath = $this->getThumbPath( $thumbName ); // final thumb path
1095  
1096          $tmpThumbPath = $tmpFile->getPath();
1097  
1098          if ( $handler->supportsBucketing() ) {
1099              $this->generateBucketsIfNeeded( $normalisedParams, $flags );
1100          }
1101  
1102          // Actually render the thumbnail...
1103          wfProfileIn( __METHOD__ . '-doTransform' );
1104          $thumb = $handler->doTransform( $this, $tmpThumbPath, $thumbUrl, $transformParams );
1105          wfProfileOut( __METHOD__ . '-doTransform' );
1106          $tmpFile->bind( $thumb ); // keep alive with $thumb
1107  
1108          if ( !$thumb ) { // bad params?
1109              $thumb = false;
1110          } elseif ( $thumb->isError() ) { // transform error
1111              $this->lastError = $thumb->toText();
1112              // Ignore errors if requested
1113              if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
1114                  $thumb = $handler->getTransform( $this, $tmpThumbPath, $thumbUrl, $transformParams );
1115              }
1116          } elseif ( $this->repo && $thumb->hasFile() && !$thumb->fileIsSource() ) {
1117              // Copy the thumbnail from the file system into storage...
1118              $disposition = $this->getThumbDisposition( $thumbName );
1119              $status = $this->repo->quickImport( $tmpThumbPath, $thumbPath, $disposition );
1120              if ( $status->isOK() ) {
1121                  $thumb->setStoragePath( $thumbPath );
1122              } else {
1123                  $thumb = $this->transformErrorOutput( $thumbPath, $thumbUrl, $transformParams, $flags );
1124              }
1125              // Give extensions a chance to do something with this thumbnail...
1126              wfRunHooks( 'FileTransformed', array( $this, $thumb, $tmpThumbPath, $thumbPath ) );
1127          }
1128  
1129          // Purge. Useful in the event of Core -> Squid connection failure or squid
1130          // purge collisions from elsewhere during failure. Don't keep triggering for
1131          // "thumbs" which have the main image URL though (bug 13776)
1132          if ( $wgUseSquid ) {
1133              if ( !$thumb || $thumb->isError() || $thumb->getUrl() != $this->getURL() ) {
1134                  SquidUpdate::purge( array( $thumbUrl ) );
1135              }
1136          }
1137  
1138          return $thumb;
1139      }
1140  
1141      /**
1142       * Generates chained bucketed thumbnails if needed
1143       * @param array $params
1144       * @param int $flags
1145       * @return bool Whether at least one bucket was generated
1146       */
1147  	protected function generateBucketsIfNeeded( $params, $flags = 0 ) {
1148          if ( !$this->repo
1149              || !isset( $params['physicalWidth'] )
1150              || !isset( $params['physicalHeight'] )
1151              || !( $bucket = $this->getThumbnailBucket( $params['physicalWidth'] ) )
1152              || $bucket == $params['physicalWidth'] ) {
1153              return false;
1154          }
1155  
1156          $bucketPath = $this->getBucketThumbPath( $bucket );
1157  
1158          if ( $this->repo->fileExists( $bucketPath ) ) {
1159              return false;
1160          }
1161  
1162          $params['physicalWidth'] = $bucket;
1163          $params['width'] = $bucket;
1164  
1165          $params = $this->getHandler()->sanitizeParamsForBucketing( $params );
1166  
1167          $bucketName = $this->getBucketThumbName( $bucket );
1168  
1169          $tmpFile = $this->makeTransformTmpFile( $bucketPath );
1170  
1171          if ( !$tmpFile ) {
1172              return false;
1173          }
1174  
1175          $thumb = $this->generateAndSaveThumb( $tmpFile, $params, $flags );
1176  
1177          if ( !$thumb || $thumb->isError() ) {
1178              return false;
1179          }
1180  
1181          $this->tmpBucketedThumbCache[$bucket] = $tmpFile->getPath();
1182          // For the caching to work, we need to make the tmp file survive as long as
1183          // this object exists
1184          $tmpFile->bind( $this );
1185  
1186          return true;
1187      }
1188  
1189      /**
1190       * Returns the most appropriate source image for the thumbnail, given a target thumbnail size
1191       * @param array $params
1192       * @return array Source path and width/height of the source
1193       */
1194  	public function getThumbnailSource( $params ) {
1195          if ( $this->repo
1196              && $this->getHandler()->supportsBucketing()
1197              && isset( $params['physicalWidth'] )
1198              && $bucket = $this->getThumbnailBucket( $params['physicalWidth'] )
1199          ) {
1200              if ( $this->getWidth() != 0 ) {
1201                  $bucketHeight = round( $this->getHeight() * ( $bucket / $this->getWidth() ) );
1202              } else {
1203                  $bucketHeight = 0;
1204              }
1205  
1206              // Try to avoid reading from storage if the file was generated by this script
1207              if ( isset( $this->tmpBucketedThumbCache[$bucket] ) ) {
1208                  $tmpPath = $this->tmpBucketedThumbCache[$bucket];
1209  
1210                  if ( file_exists( $tmpPath ) ) {
1211                      return array(
1212                          'path' => $tmpPath,
1213                          'width' => $bucket,
1214                          'height' => $bucketHeight
1215                      );
1216                  }
1217              }
1218  
1219              $bucketPath = $this->getBucketThumbPath( $bucket );
1220  
1221              if ( $this->repo->fileExists( $bucketPath ) ) {
1222                  $fsFile = $this->repo->getLocalReference( $bucketPath );
1223  
1224                  if ( $fsFile ) {
1225                      return array(
1226                          'path' => $fsFile->getPath(),
1227                          'width' => $bucket,
1228                          'height' => $bucketHeight
1229                      );
1230                  }
1231              }
1232          }
1233  
1234          // Thumbnailing a very large file could result in network saturation if
1235          // everyone does it at once.
1236          if ( $this->getSize() >= 1e7 ) { // 10MB
1237              $that = $this;
1238              $work = new PoolCounterWorkViaCallback( 'GetLocalFileCopy', sha1( $this->getName() ),
1239                  array(
1240                      'doWork' => function() use ( $that ) {
1241                          return $that->getLocalRefPath();
1242                      }
1243                  )
1244              );
1245              $srcPath = $work->execute();
1246          } else {
1247              $srcPath = $this->getLocalRefPath();
1248          }
1249  
1250          // Original file
1251          return array(
1252              'path' => $srcPath,
1253              'width' => $this->getWidth(),
1254              'height' => $this->getHeight()
1255          );
1256      }
1257  
1258      /**
1259       * Returns the repo path of the thumb for a given bucket
1260       * @param int $bucket
1261       * @return string
1262       */
1263  	protected function getBucketThumbPath( $bucket ) {
1264          $thumbName = $this->getBucketThumbName( $bucket );
1265          return $this->getThumbPath( $thumbName );
1266      }
1267  
1268      /**
1269       * Returns the name of the thumb for a given bucket
1270       * @param int $bucket
1271       * @return string
1272       */
1273  	protected function getBucketThumbName( $bucket ) {
1274          return $this->thumbName( array( 'physicalWidth' => $bucket ) );
1275      }
1276  
1277      /**
1278       * Creates a temp FS file with the same extension and the thumbnail
1279       * @param string $thumbPath Thumbnail path
1280       * @return TempFSFile
1281       */
1282  	protected function makeTransformTmpFile( $thumbPath ) {
1283          $thumbExt = FileBackend::extensionFromPath( $thumbPath );
1284          return TempFSFile::factory( 'transform_', $thumbExt );
1285      }
1286  
1287      /**
1288       * @param string $thumbName Thumbnail name
1289       * @param string $dispositionType Type of disposition (either "attachment" or "inline")
1290       * @return string Content-Disposition header value
1291       */
1292  	function getThumbDisposition( $thumbName, $dispositionType = 'inline' ) {
1293          $fileName = $this->name; // file name to suggest
1294          $thumbExt = FileBackend::extensionFromPath( $thumbName );
1295          if ( $thumbExt != '' && $thumbExt !== $this->getExtension() ) {
1296              $fileName .= ".$thumbExt";
1297          }
1298  
1299          return FileBackend::makeContentDisposition( $dispositionType, $fileName );
1300      }
1301  
1302      /**
1303       * Hook into transform() to allow migration of thumbnail files
1304       * STUB
1305       * Overridden by LocalFile
1306       * @param string $thumbName
1307       */
1308  	function migrateThumbFile( $thumbName ) {
1309      }
1310  
1311      /**
1312       * Get a MediaHandler instance for this file
1313       *
1314       * @return MediaHandler|bool Registered MediaHandler for file's MIME type
1315       *   or false if none found
1316       */
1317  	function getHandler() {
1318          if ( !isset( $this->handler ) ) {
1319              $this->handler = MediaHandler::getHandler( $this->getMimeType() );
1320          }
1321  
1322          return $this->handler;
1323      }
1324  
1325      /**
1326       * Get a ThumbnailImage representing a file type icon
1327       *
1328       * @return ThumbnailImage
1329       */
1330  	function iconThumb() {
1331          global $wgResourceBasePath, $IP;
1332          $assetsPath = "$wgResourceBasePath/resources/assets/file-type-icons/";
1333          $assetsDirectory = "$IP/resources/assets/file-type-icons/";
1334  
1335          $try = array( 'fileicon-' . $this->getExtension() . '.png', 'fileicon.png' );
1336          foreach ( $try as $icon ) {
1337              if ( file_exists( $assetsDirectory . $icon ) ) { // always FS
1338                  $params = array( 'width' => 120, 'height' => 120 );
1339  
1340                  return new ThumbnailImage( $this, $assetsPath . $icon, false, $params );
1341              }
1342          }
1343  
1344          return null;
1345      }
1346  
1347      /**
1348       * Get last thumbnailing error.
1349       * Largely obsolete.
1350       * @return string
1351       */
1352  	function getLastError() {
1353          return $this->lastError;
1354      }
1355  
1356      /**
1357       * Get all thumbnail names previously generated for this file
1358       * STUB
1359       * Overridden by LocalFile
1360       * @return array
1361       */
1362  	function getThumbnails() {
1363          return array();
1364      }
1365  
1366      /**
1367       * Purge shared caches such as thumbnails and DB data caching
1368       * STUB
1369       * Overridden by LocalFile
1370       * @param array $options Options, which include:
1371       *   'forThumbRefresh' : The purging is only to refresh thumbnails
1372       */
1373  	function purgeCache( $options = array() ) {
1374      }
1375  
1376      /**
1377       * Purge the file description page, but don't go after
1378       * pages using the file. Use when modifying file history
1379       * but not the current data.
1380       */
1381  	function purgeDescription() {
1382          $title = $this->getTitle();
1383          if ( $title ) {
1384              $title->invalidateCache();
1385              $title->purgeSquid();
1386          }
1387      }
1388  
1389      /**
1390       * Purge metadata and all affected pages when the file is created,
1391       * deleted, or majorly updated.
1392       */
1393  	function purgeEverything() {
1394          // Delete thumbnails and refresh file metadata cache
1395          $this->purgeCache();
1396          $this->purgeDescription();
1397  
1398          // Purge cache of all pages using this file
1399          $title = $this->getTitle();
1400          if ( $title ) {
1401              $update = new HTMLCacheUpdate( $title, 'imagelinks' );
1402              $update->doUpdate();
1403          }
1404      }
1405  
1406      /**
1407       * Return a fragment of the history of file.
1408       *
1409       * STUB
1410       * @param int $limit Limit of rows to return
1411       * @param string $start Only revisions older than $start will be returned
1412       * @param string $end Only revisions newer than $end will be returned
1413       * @param bool $inc Include the endpoints of the time range
1414       *
1415       * @return array
1416       */
1417  	function getHistory( $limit = null, $start = null, $end = null, $inc = true ) {
1418          return array();
1419      }
1420  
1421      /**
1422       * Return the history of this file, line by line. Starts with current version,
1423       * then old versions. Should return an object similar to an image/oldimage
1424       * database row.
1425       *
1426       * STUB
1427       * Overridden in LocalFile
1428       * @return bool
1429       */
1430  	public function nextHistoryLine() {
1431          return false;
1432      }
1433  
1434      /**
1435       * Reset the history pointer to the first element of the history.
1436       * Always call this function after using nextHistoryLine() to free db resources
1437       * STUB
1438       * Overridden in LocalFile.
1439       */
1440  	public function resetHistory() {
1441      }
1442  
1443      /**
1444       * Get the filename hash component of the directory including trailing slash,
1445       * e.g. f/fa/
1446       * If the repository is not hashed, returns an empty string.
1447       *
1448       * @return string
1449       */
1450  	function getHashPath() {
1451          if ( !isset( $this->hashPath ) ) {
1452              $this->assertRepoDefined();
1453              $this->hashPath = $this->repo->getHashPath( $this->getName() );
1454          }
1455  
1456          return $this->hashPath;
1457      }
1458  
1459      /**
1460       * Get the path of the file relative to the public zone root.
1461       * This function is overriden in OldLocalFile to be like getArchiveRel().
1462       *
1463       * @return string
1464       */
1465  	function getRel() {
1466          return $this->getHashPath() . $this->getName();
1467      }
1468  
1469      /**
1470       * Get the path of an archived file relative to the public zone root
1471       *
1472       * @param bool|string $suffix If not false, the name of an archived thumbnail file
1473       *
1474       * @return string
1475       */
1476  	function getArchiveRel( $suffix = false ) {
1477          $path = 'archive/' . $this->getHashPath();
1478          if ( $suffix === false ) {
1479              $path = substr( $path, 0, -1 );
1480          } else {
1481              $path .= $suffix;
1482          }
1483  
1484          return $path;
1485      }
1486  
1487      /**
1488       * Get the path, relative to the thumbnail zone root, of the
1489       * thumbnail directory or a particular file if $suffix is specified
1490       *
1491       * @param bool|string $suffix If not false, the name of a thumbnail file
1492       * @return string
1493       */
1494  	function getThumbRel( $suffix = false ) {
1495          $path = $this->getRel();
1496          if ( $suffix !== false ) {
1497              $path .= '/' . $suffix;
1498          }
1499  
1500          return $path;
1501      }
1502  
1503      /**
1504       * Get urlencoded path of the file relative to the public zone root.
1505       * This function is overriden in OldLocalFile to be like getArchiveUrl().
1506       *
1507       * @return string
1508       */
1509  	function getUrlRel() {
1510          return $this->getHashPath() . rawurlencode( $this->getName() );
1511      }
1512  
1513      /**
1514       * Get the path, relative to the thumbnail zone root, for an archived file's thumbs directory
1515       * or a specific thumb if the $suffix is given.
1516       *
1517       * @param string $archiveName The timestamped name of an archived image
1518       * @param bool|string $suffix If not false, the name of a thumbnail file
1519       * @return string
1520       */
1521  	function getArchiveThumbRel( $archiveName, $suffix = false ) {
1522          $path = 'archive/' . $this->getHashPath() . $archiveName . "/";
1523          if ( $suffix === false ) {
1524              $path = substr( $path, 0, -1 );
1525          } else {
1526              $path .= $suffix;
1527          }
1528  
1529          return $path;
1530      }
1531  
1532      /**
1533       * Get the path of the archived file.
1534       *
1535       * @param bool|string $suffix If not false, the name of an archived file.
1536       * @return string
1537       */
1538  	function getArchivePath( $suffix = false ) {
1539          $this->assertRepoDefined();
1540  
1541          return $this->repo->getZonePath( 'public' ) . '/' . $this->getArchiveRel( $suffix );
1542      }
1543  
1544      /**
1545       * Get the path of an archived file's thumbs, or a particular thumb if $suffix is specified
1546       *
1547       * @param string $archiveName The timestamped name of an archived image
1548       * @param bool|string $suffix If not false, the name of a thumbnail file
1549       * @return string
1550       */
1551  	function getArchiveThumbPath( $archiveName, $suffix = false ) {
1552          $this->assertRepoDefined();
1553  
1554          return $this->repo->getZonePath( 'thumb' ) . '/' .
1555          $this->getArchiveThumbRel( $archiveName, $suffix );
1556      }
1557  
1558      /**
1559       * Get the path of the thumbnail directory, or a particular file if $suffix is specified
1560       *
1561       * @param bool|string $suffix If not false, the name of a thumbnail file
1562       * @return string
1563       */
1564  	function getThumbPath( $suffix = false ) {
1565          $this->assertRepoDefined();
1566  
1567          return $this->repo->getZonePath( 'thumb' ) . '/' . $this->getThumbRel( $suffix );
1568      }
1569  
1570      /**
1571       * Get the path of the transcoded directory, or a particular file if $suffix is specified
1572       *
1573       * @param bool|string $suffix If not false, the name of a media file
1574       * @return string
1575       */
1576  	function getTranscodedPath( $suffix = false ) {
1577          $this->assertRepoDefined();
1578  
1579          return $this->repo->getZonePath( 'transcoded' ) . '/' . $this->getThumbRel( $suffix );
1580      }
1581  
1582      /**
1583       * Get the URL of the archive directory, or a particular file if $suffix is specified
1584       *
1585       * @param bool|string $suffix If not false, the name of an archived file
1586       * @return string
1587       */
1588  	function getArchiveUrl( $suffix = false ) {
1589          $this->assertRepoDefined();
1590          $ext = $this->getExtension();
1591          $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath();
1592          if ( $suffix === false ) {
1593              $path = substr( $path, 0, -1 );
1594          } else {
1595              $path .= rawurlencode( $suffix );
1596          }
1597  
1598          return $path;
1599      }
1600  
1601      /**
1602       * Get the URL of the archived file's thumbs, or a particular thumb if $suffix is specified
1603       *
1604       * @param string $archiveName The timestamped name of an archived image
1605       * @param bool|string $suffix If not false, the name of a thumbnail file
1606       * @return string
1607       */
1608  	function getArchiveThumbUrl( $archiveName, $suffix = false ) {
1609          $this->assertRepoDefined();
1610          $ext = $this->getExtension();
1611          $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/archive/' .
1612              $this->getHashPath() . rawurlencode( $archiveName ) . "/";
1613          if ( $suffix === false ) {
1614              $path = substr( $path, 0, -1 );
1615          } else {
1616              $path .= rawurlencode( $suffix );
1617          }
1618  
1619          return $path;
1620      }
1621  
1622      /**
1623       * Get the URL of the zone directory, or a particular file if $suffix is specified
1624       *
1625       * @param string $zone Name of requested zone
1626       * @param bool|string $suffix If not false, the name of a file in zone
1627       * @return string Path
1628       */
1629  	function getZoneUrl( $zone, $suffix = false ) {
1630          $this->assertRepoDefined();
1631          $ext = $this->getExtension();
1632          $path = $this->repo->getZoneUrl( $zone, $ext ) . '/' . $this->getUrlRel();
1633          if ( $suffix !== false ) {
1634              $path .= '/' . rawurlencode( $suffix );
1635          }
1636  
1637          return $path;
1638      }
1639  
1640      /**
1641       * Get the URL of the thumbnail directory, or a particular file if $suffix is specified
1642       *
1643       * @param bool|string $suffix If not false, the name of a thumbnail file
1644       * @return string Path
1645       */
1646  	function getThumbUrl( $suffix = false ) {
1647          return $this->getZoneUrl( 'thumb', $suffix );
1648      }
1649  
1650      /**
1651       * Get the URL of the transcoded directory, or a particular file if $suffix is specified
1652       *
1653       * @param bool|string $suffix If not false, the name of a media file
1654       * @return string Path
1655       */
1656  	function getTranscodedUrl( $suffix = false ) {
1657          return $this->getZoneUrl( 'transcoded', $suffix );
1658      }
1659  
1660      /**
1661       * Get the public zone virtual URL for a current version source file
1662       *
1663       * @param bool|string $suffix If not false, the name of a thumbnail file
1664       * @return string
1665       */
1666  	function getVirtualUrl( $suffix = false ) {
1667          $this->assertRepoDefined();
1668          $path = $this->repo->getVirtualUrl() . '/public/' . $this->getUrlRel();
1669          if ( $suffix !== false ) {
1670              $path .= '/' . rawurlencode( $suffix );
1671          }
1672  
1673          return $path;
1674      }
1675  
1676      /**
1677       * Get the public zone virtual URL for an archived version source file
1678       *
1679       * @param bool|string $suffix If not false, the name of a thumbnail file
1680       * @return string
1681       */
1682  	function getArchiveVirtualUrl( $suffix = false ) {
1683          $this->assertRepoDefined();
1684          $path = $this->repo->getVirtualUrl() . '/public/archive/' . $this->getHashPath();
1685          if ( $suffix === false ) {
1686              $path = substr( $path, 0, -1 );
1687          } else {
1688              $path .= rawurlencode( $suffix );
1689          }
1690  
1691          return $path;
1692      }
1693  
1694      /**
1695       * Get the virtual URL for a thumbnail file or directory
1696       *
1697       * @param bool|string $suffix If not false, the name of a thumbnail file
1698       * @return string
1699       */
1700  	function getThumbVirtualUrl( $suffix = false ) {
1701          $this->assertRepoDefined();
1702          $path = $this->repo->getVirtualUrl() . '/thumb/' . $this->getUrlRel();
1703          if ( $suffix !== false ) {
1704              $path .= '/' . rawurlencode( $suffix );
1705          }
1706  
1707          return $path;
1708      }
1709  
1710      /**
1711       * @return bool
1712       */
1713  	function isHashed() {
1714          $this->assertRepoDefined();
1715  
1716          return (bool)$this->repo->getHashLevels();
1717      }
1718  
1719      /**
1720       * @throws MWException
1721       */
1722  	function readOnlyError() {
1723          throw new MWException( get_class( $this ) . ': write operations are not supported' );
1724      }
1725  
1726      /**
1727       * Record a file upload in the upload log and the image table
1728       * STUB
1729       * Overridden by LocalFile
1730       * @param string $oldver
1731       * @param string $desc
1732       * @param string $license
1733       * @param string $copyStatus
1734       * @param string $source
1735       * @param bool $watch
1736       * @param string|bool $timestamp
1737       * @param null|User $user User object or null to use $wgUser
1738       * @return bool
1739       * @throws MWException
1740       */
1741  	function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '',
1742          $watch = false, $timestamp = false, User $user = null
1743      ) {
1744          $this->readOnlyError();
1745      }
1746  
1747      /**
1748       * Move or copy a file to its public location. If a file exists at the
1749       * destination, move it to an archive. Returns a FileRepoStatus object with
1750       * the archive name in the "value" member on success.
1751       *
1752       * The archive name should be passed through to recordUpload for database
1753       * registration.
1754       *
1755       * Options to $options include:
1756       *   - headers : name/value map of HTTP headers to use in response to GET/HEAD requests
1757       *
1758       * @param string $srcPath Local filesystem path to the source image
1759       * @param int $flags A bitwise combination of:
1760       *   File::DELETE_SOURCE    Delete the source file, i.e. move rather than copy
1761       * @param array $options Optional additional parameters
1762       * @return FileRepoStatus On success, the value member contains the
1763       *   archive name, or an empty string if it was a new file.
1764       *
1765       * STUB
1766       * Overridden by LocalFile
1767       */
1768  	function publish( $srcPath, $flags = 0, array $options = array() ) {
1769          $this->readOnlyError();
1770      }
1771  
1772      /**
1773       * @return bool
1774       */
1775  	function formatMetadata() {
1776          if ( !$this->getHandler() ) {
1777              return false;
1778          }
1779  
1780          return $this->getHandler()->formatMetadata( $this, $this->getMetadata() );
1781      }
1782  
1783      /**
1784       * Returns true if the file comes from the local file repository.
1785       *
1786       * @return bool
1787       */
1788  	function isLocal() {
1789          return $this->repo && $this->repo->isLocal();
1790      }
1791  
1792      /**
1793       * Returns the name of the repository.
1794       *
1795       * @return string
1796       */
1797  	function getRepoName() {
1798          return $this->repo ? $this->repo->getName() : 'unknown';
1799      }
1800  
1801      /**
1802       * Returns the repository
1803       *
1804       * @return FileRepo|LocalRepo|bool
1805       */
1806  	function getRepo() {
1807          return $this->repo;
1808      }
1809  
1810      /**
1811       * Returns true if the image is an old version
1812       * STUB
1813       *
1814       * @return bool
1815       */
1816  	function isOld() {
1817          return false;
1818      }
1819  
1820      /**
1821       * Is this file a "deleted" file in a private archive?
1822       * STUB
1823       *
1824       * @param int $field One of DELETED_* bitfield constants
1825       * @return bool
1826       */
1827  	function isDeleted( $field ) {
1828          return false;
1829      }
1830  
1831      /**
1832       * Return the deletion bitfield
1833       * STUB
1834       * @return int
1835       */
1836  	function getVisibility() {
1837          return 0;
1838      }
1839  
1840      /**
1841       * Was this file ever deleted from the wiki?
1842       *
1843       * @return bool
1844       */
1845  	function wasDeleted() {
1846          $title = $this->getTitle();
1847  
1848          return $title && $title->isDeletedQuick();
1849      }
1850  
1851      /**
1852       * Move file to the new title
1853       *
1854       * Move current, old version and all thumbnails
1855       * to the new filename. Old file is deleted.
1856       *
1857       * Cache purging is done; checks for validity
1858       * and logging are caller's responsibility
1859       *
1860       * @param Title $target New file name
1861       * @return FileRepoStatus
1862       */
1863  	function move( $target ) {
1864          $this->readOnlyError();
1865      }
1866  
1867      /**
1868       * Delete all versions of the file.
1869       *
1870       * Moves the files into an archive directory (or deletes them)
1871       * and removes the database rows.
1872       *
1873       * Cache purging is done; logging is caller's responsibility.
1874       *
1875       * @param string $reason
1876       * @param bool $suppress Hide content from sysops?
1877       * @param User|null $user
1878       * @return bool Boolean on success, false on some kind of failure
1879       * STUB
1880       * Overridden by LocalFile
1881       */
1882  	function delete( $reason, $suppress = false, $user = null ) {
1883          $this->readOnlyError();
1884      }
1885  
1886      /**
1887       * Restore all or specified deleted revisions to the given file.
1888       * Permissions and logging are left to the caller.
1889       *
1890       * May throw database exceptions on error.
1891       *
1892       * @param array $versions Set of record ids of deleted items to restore,
1893       *   or empty to restore all revisions.
1894       * @param bool $unsuppress Remove restrictions on content upon restoration?
1895       * @return int|bool The number of file revisions restored if successful,
1896       *   or false on failure
1897       * STUB
1898       * Overridden by LocalFile
1899       */
1900  	function restore( $versions = array(), $unsuppress = false ) {
1901          $this->readOnlyError();
1902      }
1903  
1904      /**
1905       * Returns 'true' if this file is a type which supports multiple pages,
1906       * e.g. DJVU or PDF. Note that this may be true even if the file in
1907       * question only has a single page.
1908       *
1909       * @return bool
1910       */
1911  	function isMultipage() {
1912          return $this->getHandler() && $this->handler->isMultiPage( $this );
1913      }
1914  
1915      /**
1916       * Returns the number of pages of a multipage document, or false for
1917       * documents which aren't multipage documents
1918       *
1919       * @return bool|int
1920       */
1921  	function pageCount() {
1922          if ( !isset( $this->pageCount ) ) {
1923              if ( $this->getHandler() && $this->handler->isMultiPage( $this ) ) {
1924                  $this->pageCount = $this->handler->pageCount( $this );
1925              } else {
1926                  $this->pageCount = false;
1927              }
1928          }
1929  
1930          return $this->pageCount;
1931      }
1932  
1933      /**
1934       * Calculate the height of a thumbnail using the source and destination width
1935       *
1936       * @param int $srcWidth
1937       * @param int $srcHeight
1938       * @param int $dstWidth
1939       *
1940       * @return int
1941       */
1942  	static function scaleHeight( $srcWidth, $srcHeight, $dstWidth ) {
1943          // Exact integer multiply followed by division
1944          if ( $srcWidth == 0 ) {
1945              return 0;
1946          } else {
1947              return round( $srcHeight * $dstWidth / $srcWidth );
1948          }
1949      }
1950  
1951      /**
1952       * Get an image size array like that returned by getImageSize(), or false if it
1953       * can't be determined. Loads the image size directly from the file ignoring caches.
1954       *
1955       * @note Use getWidth()/getHeight() instead of this method unless you have a
1956       *  a good reason. This method skips all caches.
1957       *
1958       * @param string $filePath The path to the file (e.g. From getLocalPathRef() )
1959       * @return array The width, followed by height, with optionally more things after
1960       */
1961  	function getImageSize( $filePath ) {
1962          if ( !$this->getHandler() ) {
1963              return false;
1964          }
1965  
1966          return $this->getHandler()->getImageSize( $this, $filePath );
1967      }
1968  
1969      /**
1970       * Get the URL of the image description page. May return false if it is
1971       * unknown or not applicable.
1972       *
1973       * @return string
1974       */
1975  	function getDescriptionUrl() {
1976          if ( $this->repo ) {
1977              return $this->repo->getDescriptionUrl( $this->getName() );
1978          } else {
1979              return false;
1980          }
1981      }
1982  
1983      /**
1984       * Get the HTML text of the description page, if available
1985       *
1986       * @param bool|Language $lang Optional language to fetch description in
1987       * @return string
1988       */
1989  	function getDescriptionText( $lang = false ) {
1990          global $wgMemc, $wgLang;
1991          if ( !$this->repo || !$this->repo->fetchDescription ) {
1992              return false;
1993          }
1994          if ( !$lang ) {
1995              $lang = $wgLang;
1996          }
1997          $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $lang->getCode() );
1998          if ( $renderUrl ) {
1999              if ( $this->repo->descriptionCacheExpiry > 0 ) {
2000                  wfDebug( "Attempting to get the description from cache..." );
2001                  $key = $this->repo->getLocalCacheKey(
2002                      'RemoteFileDescription',
2003                      'url',
2004                      $lang->getCode(),
2005                      $this->getName()
2006                  );
2007                  $obj = $wgMemc->get( $key );
2008                  if ( $obj ) {
2009                      wfDebug( "success!\n" );
2010  
2011                      return $obj;
2012                  }
2013                  wfDebug( "miss\n" );
2014              }
2015              wfDebug( "Fetching shared description from $renderUrl\n" );
2016              $res = Http::get( $renderUrl );
2017              if ( $res && $this->repo->descriptionCacheExpiry > 0 ) {
2018                  $wgMemc->set( $key, $res, $this->repo->descriptionCacheExpiry );
2019              }
2020  
2021              return $res;
2022          } else {
2023              return false;
2024          }
2025      }
2026  
2027      /**
2028       * Get description of file revision
2029       * STUB
2030       *
2031       * @param int $audience One of:
2032       *   File::FOR_PUBLIC       to be displayed to all users
2033       *   File::FOR_THIS_USER    to be displayed to the given user
2034       *   File::RAW              get the description regardless of permissions
2035       * @param User $user User object to check for, only if FOR_THIS_USER is
2036       *   passed to the $audience parameter
2037       * @return string
2038       */
2039  	function getDescription( $audience = self::FOR_PUBLIC, User $user = null ) {
2040          return null;
2041      }
2042  
2043      /**
2044       * Get the 14-character timestamp of the file upload
2045       *
2046       * @return string|bool TS_MW timestamp or false on failure
2047       */
2048  	function getTimestamp() {
2049          $this->assertRepoDefined();
2050  
2051          return $this->repo->getFileTimestamp( $this->getPath() );
2052      }
2053  
2054      /**
2055       * Get the SHA-1 base 36 hash of the file
2056       *
2057       * @return string
2058       */
2059  	function getSha1() {
2060          $this->assertRepoDefined();
2061  
2062          return $this->repo->getFileSha1( $this->getPath() );
2063      }
2064  
2065      /**
2066       * Get the deletion archive key, "<sha1>.<ext>"
2067       *
2068       * @return string
2069       */
2070  	function getStorageKey() {
2071          $hash = $this->getSha1();
2072          if ( !$hash ) {
2073              return false;
2074          }
2075          $ext = $this->getExtension();
2076          $dotExt = $ext === '' ? '' : ".$ext";
2077  
2078          return $hash . $dotExt;
2079      }
2080  
2081      /**
2082       * Determine if the current user is allowed to view a particular
2083       * field of this file, if it's marked as deleted.
2084       * STUB
2085       * @param int $field
2086       * @param User $user User object to check, or null to use $wgUser
2087       * @return bool
2088       */
2089  	function userCan( $field, User $user = null ) {
2090          return true;
2091      }
2092  
2093      /**
2094       * @return array HTTP header name/value map to use for HEAD/GET request responses
2095       */
2096  	function getStreamHeaders() {
2097          $handler = $this->getHandler();
2098          if ( $handler ) {
2099              return $handler->getStreamHeaders( $this->getMetadata() );
2100          } else {
2101              return array();
2102          }
2103      }
2104  
2105      /**
2106       * @return string
2107       */
2108  	function getLongDesc() {
2109          $handler = $this->getHandler();
2110          if ( $handler ) {
2111              return $handler->getLongDesc( $this );
2112          } else {
2113              return MediaHandler::getGeneralLongDesc( $this );
2114          }
2115      }
2116  
2117      /**
2118       * @return string
2119       */
2120  	function getShortDesc() {
2121          $handler = $this->getHandler();
2122          if ( $handler ) {
2123              return $handler->getShortDesc( $this );
2124          } else {
2125              return MediaHandler::getGeneralShortDesc( $this );
2126          }
2127      }
2128  
2129      /**
2130       * @return string
2131       */
2132  	function getDimensionsString() {
2133          $handler = $this->getHandler();
2134          if ( $handler ) {
2135              return $handler->getDimensionsString( $this );
2136          } else {
2137              return '';
2138          }
2139      }
2140  
2141      /**
2142       * @return string
2143       */
2144  	function getRedirected() {
2145          return $this->redirected;
2146      }
2147  
2148      /**
2149       * @return Title|null
2150       */
2151  	function getRedirectedTitle() {
2152          if ( $this->redirected ) {
2153              if ( !$this->redirectTitle ) {
2154                  $this->redirectTitle = Title::makeTitle( NS_FILE, $this->redirected );
2155              }
2156  
2157              return $this->redirectTitle;
2158          }
2159  
2160          return null;
2161      }
2162  
2163      /**
2164       * @param string $from
2165       * @return void
2166       */
2167  	function redirectedFrom( $from ) {
2168          $this->redirected = $from;
2169      }
2170  
2171      /**
2172       * @return bool
2173       */
2174  	function isMissing() {
2175          return false;
2176      }
2177  
2178      /**
2179       * Check if this file object is small and can be cached
2180       * @return bool
2181       */
2182  	public function isCacheable() {
2183          return true;
2184      }
2185  
2186      /**
2187       * Assert that $this->repo is set to a valid FileRepo instance
2188       * @throws MWException
2189       */
2190  	protected function assertRepoDefined() {
2191          if ( !( $this->repo instanceof $this->repoClass ) ) {
2192              throw new MWException( "A {$this->repoClass} object is not set for this File.\n" );
2193          }
2194      }
2195  
2196      /**
2197       * Assert that $this->title is set to a Title
2198       * @throws MWException
2199       */
2200  	protected function assertTitleDefined() {
2201          if ( !( $this->title instanceof Title ) ) {
2202              throw new MWException( "A Title object is not set for this File.\n" );
2203          }
2204      }
2205  
2206      /**
2207       * True if creating thumbnails from the file is large or otherwise resource-intensive.
2208       * @return bool
2209       */
2210  	public function isExpensiveToThumbnail() {
2211          $handler = $this->getHandler();
2212          return $handler ? $handler->isExpensiveToThumbnail( $this ) : false;
2213      }
2214  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1