1<?php 2namespace TYPO3\CMS\Core\Resource; 3 4/* 5 * This file is part of the TYPO3 CMS project. 6 * 7 * It is free software; you can redistribute it and/or modify it under 8 * the terms of the GNU General Public License, either version 2 9 * of the License, or any later version. 10 * 11 * For the full copyright and license information, please read the 12 * LICENSE.txt file that was distributed with this source code. 13 * 14 * The TYPO3 project - inspiring people to share! 15 */ 16 17use TYPO3\CMS\Core\Utility\GeneralUtility; 18 19/** 20 * File representation in the file abstraction layer. 21 */ 22class File extends AbstractFile 23{ 24 /** 25 * @var bool 26 */ 27 protected $metaDataLoaded = false; 28 29 /** 30 * @var array 31 */ 32 protected $metaDataProperties = []; 33 34 /** 35 * Set to TRUE while this file is being indexed - used to prevent some endless loops 36 * 37 * @var bool 38 */ 39 protected $indexingInProgress = false; 40 41 /** 42 * Contains the names of all properties that have been update since the 43 * instantiation of this object 44 * 45 * @var array 46 */ 47 protected $updatedProperties = []; 48 49 /** 50 * Constructor for a file object. Should normally not be used directly, use 51 * the corresponding factory methods instead. 52 * 53 * @param array $fileData 54 * @param ResourceStorage $storage 55 * @param array $metaData 56 */ 57 public function __construct(array $fileData, ResourceStorage $storage, array $metaData = []) 58 { 59 $this->identifier = $fileData['identifier'] ?? null; 60 $this->name = $fileData['name'] ?? ''; 61 $this->properties = $fileData; 62 $this->storage = $storage; 63 if (!empty($metaData)) { 64 $this->metaDataLoaded = true; 65 $this->metaDataProperties = $metaData; 66 } 67 } 68 69 /******************************* 70 * VARIOUS FILE PROPERTY GETTERS 71 *******************************/ 72 /** 73 * Returns a property value 74 * 75 * @param string $key 76 * @return mixed Property value 77 */ 78 public function getProperty($key) 79 { 80 if (parent::hasProperty($key)) { 81 return parent::getProperty($key); 82 } 83 $metaData = $this->_getMetaData(); 84 return $metaData[$key] ?? null; 85 } 86 87 /** 88 * Checks if the file has a (metadata) property which 89 * can be retrieved by "getProperty" 90 * 91 * @param string $key 92 * @return bool 93 */ 94 public function hasProperty($key) 95 { 96 if (!parent::hasProperty($key)) { 97 return array_key_exists($key, $this->_getMetaData()); 98 } 99 return true; 100 } 101 102 /** 103 * Returns the properties of this object. 104 * 105 * @return array 106 */ 107 public function getProperties() 108 { 109 return array_merge(parent::getProperties(), array_diff_key($this->_getMetaData(), parent::getProperties())); 110 } 111 112 /** 113 * Returns the MetaData 114 * 115 * @return array 116 * @internal 117 */ 118 public function _getMetaData() 119 { 120 if (!$this->metaDataLoaded) { 121 $this->loadMetaData(); 122 } 123 return $this->metaDataProperties; 124 } 125 126 /****************** 127 * CONTENTS RELATED 128 ******************/ 129 /** 130 * Get the contents of this file 131 * 132 * @return string File contents 133 */ 134 public function getContents() 135 { 136 return $this->getStorage()->getFileContents($this); 137 } 138 139 /** 140 * Gets SHA1 hash. 141 * 142 * @return string 143 */ 144 public function getSha1() 145 { 146 if (empty($this->properties['sha1'])) { 147 $this->properties['sha1'] = parent::getSha1(); 148 } 149 return $this->properties['sha1']; 150 } 151 152 /** 153 * Replace the current file contents with the given string 154 * 155 * @param string $contents The contents to write to the file. 156 * @return File The file object (allows chaining). 157 */ 158 public function setContents($contents) 159 { 160 $this->getStorage()->setFileContents($this, $contents); 161 return $this; 162 } 163 164 /*********************** 165 * INDEX RELATED METHODS 166 ***********************/ 167 /** 168 * Returns TRUE if this file is indexed 169 * 170 * @return bool|null 171 */ 172 public function isIndexed() 173 { 174 return true; 175 } 176 177 /** 178 * Loads MetaData from Repository 179 */ 180 protected function loadMetaData() 181 { 182 if (!$this->indexingInProgress) { 183 $this->indexingInProgress = true; 184 $this->metaDataProperties = $this->getMetaDataRepository()->findByFile($this); 185 $this->metaDataLoaded = true; 186 $this->indexingInProgress = false; 187 } 188 } 189 190 /** 191 * Updates the properties of this file, e.g. after re-indexing or moving it. 192 * By default, only properties that exist as a key in the $properties array 193 * are overwritten. If you want to explicitly unset a property, set the 194 * corresponding key to NULL in the array. 195 * 196 * NOTE: This method should not be called from outside the File Abstraction Layer (FAL)! 197 * 198 * @param array $properties 199 * @internal 200 */ 201 public function updateProperties(array $properties) 202 { 203 // Setting identifier and name to update values; we have to do this 204 // here because we might need a new identifier when loading 205 // (and thus possibly indexing) a file. 206 if (isset($properties['identifier'])) { 207 $this->identifier = $properties['identifier']; 208 } 209 if (isset($properties['name'])) { 210 $this->name = $properties['name']; 211 } 212 213 if (isset($properties['uid']) && $this->properties['uid'] != 0) { 214 unset($properties['uid']); 215 } 216 foreach ($properties as $key => $value) { 217 if ($this->properties[$key] !== $value) { 218 if (!in_array($key, $this->updatedProperties)) { 219 $this->updatedProperties[] = $key; 220 } 221 $this->properties[$key] = $value; 222 } 223 } 224 // If the mime_type property should be updated and it was changed also update the type. 225 if (array_key_exists('mime_type', $properties) && in_array('mime_type', $this->updatedProperties)) { 226 $this->updatedProperties[] = 'type'; 227 unset($this->properties['type']); 228 $this->getType(); 229 } 230 if (array_key_exists('storage', $properties) && in_array('storage', $this->updatedProperties)) { 231 $this->storage = ResourceFactory::getInstance()->getStorageObject($properties['storage']); 232 } 233 } 234 235 /** 236 * Updates MetaData properties 237 * 238 * @internal Do not use outside the FileAbstraction Layer classes 239 * 240 * @param array $properties 241 */ 242 public function _updateMetaDataProperties(array $properties) 243 { 244 $this->metaDataProperties = array_merge($this->metaDataProperties, $properties); 245 } 246 247 /** 248 * Returns the names of all properties that have been updated in this record 249 * 250 * @return array 251 */ 252 public function getUpdatedProperties() 253 { 254 return $this->updatedProperties; 255 } 256 257 /**************************************** 258 * STORAGE AND MANAGEMENT RELATED METHODS 259 ****************************************/ 260 /** 261 * Check if a file operation (= action) is allowed for this file 262 * 263 * @param string $action, can be read, write, delete 264 * @return bool 265 */ 266 public function checkActionPermission($action) 267 { 268 return $this->getStorage()->checkFileActionPermission($action, $this); 269 } 270 271 /***************** 272 * SPECIAL METHODS 273 *****************/ 274 /** 275 * Creates a MD5 hash checksum based on the combined identifier of the file, 276 * the files' mimetype and the systems' encryption key. 277 * used to generate a thumbnail, and this hash is checked if valid 278 * 279 * @return string the MD5 hash 280 */ 281 public function calculateChecksum() 282 { 283 return md5( 284 $this->getCombinedIdentifier() . '|' . 285 $this->getMimeType() . '|' . 286 $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] 287 ); 288 } 289 290 /** 291 * Returns a modified version of the file. 292 * 293 * @param string $taskType The task type of this processing 294 * @param array $configuration the processing configuration, see manual for that 295 * @return ProcessedFile The processed file 296 */ 297 public function process($taskType, array $configuration) 298 { 299 return $this->getStorage()->processFile($this, $taskType, $configuration); 300 } 301 302 /** 303 * Returns an array representation of the file. 304 * (This is used by the generic listing module vidi when displaying file records.) 305 * 306 * @return array Array of main data of the file. Don't rely on all data to be present here, it's just a selection of the most relevant information. 307 */ 308 public function toArray() 309 { 310 $array = [ 311 'id' => $this->getCombinedIdentifier(), 312 'name' => $this->getName(), 313 'extension' => $this->getExtension(), 314 'type' => $this->getType(), 315 'mimetype' => $this->getMimeType(), 316 'size' => $this->getSize(), 317 'url' => $this->getPublicUrl(), 318 'indexed' => true, 319 'uid' => $this->getUid(), 320 'permissions' => [ 321 'read' => $this->checkActionPermission('read'), 322 'write' => $this->checkActionPermission('write'), 323 'delete' => $this->checkActionPermission('delete') 324 ], 325 'checksum' => $this->calculateChecksum() 326 ]; 327 foreach ($this->properties as $key => $value) { 328 $array[$key] = $value; 329 } 330 $stat = $this->getStorage()->getFileInfo($this); 331 foreach ($stat as $key => $value) { 332 $array[$key] = $value; 333 } 334 return $array; 335 } 336 337 /** 338 * @return bool 339 */ 340 public function isMissing() 341 { 342 return (bool)$this->getProperty('missing'); 343 } 344 345 /** 346 * @param bool $missing 347 */ 348 public function setMissing($missing) 349 { 350 $this->updateProperties(['missing' => $missing ? 1 : 0]); 351 } 352 353 /** 354 * Returns a publicly accessible URL for this file 355 * When file is marked as missing or deleted no url is returned 356 * 357 * WARNING: Access to the file may be restricted by further means, e.g. some 358 * web-based authentication. You have to take care of this yourself. 359 * 360 * @param bool $relativeToCurrentScript Determines whether the URL returned should be relative to the current script, in case it is relative at all (only for the LocalDriver) 361 * 362 * @return string|null NULL if file is missing or deleted, the generated url otherwise 363 */ 364 public function getPublicUrl($relativeToCurrentScript = false) 365 { 366 if ($this->isMissing() || $this->deleted) { 367 return null; 368 } 369 return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript); 370 } 371 372 /** 373 * @return Index\MetaDataRepository 374 */ 375 protected function getMetaDataRepository() 376 { 377 return GeneralUtility::makeInstance(Index\MetaDataRepository::class); 378 } 379 380 /** 381 * @return Index\FileIndexRepository 382 */ 383 protected function getFileIndexRepository() 384 { 385 return GeneralUtility::makeInstance(Index\FileIndexRepository::class); 386 } 387 388 /** 389 * @param bool $indexingState 390 * @internal Only for usage in Indexer 391 */ 392 public function setIndexingInProgess($indexingState) 393 { 394 $this->indexingInProgress = (bool)$indexingState; 395 } 396 397 /** 398 * @param $key 399 * @internal Only for use in Repositories and indexer 400 * @return mixed 401 */ 402 public function _getPropertyRaw($key) 403 { 404 return parent::getProperty($key); 405 } 406} 407