[BUGFIX] Fallback of FAL properties does not work
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / AbstractFile.php
1 <?php
2 namespace TYPO3\CMS\Core\Resource;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2011 Ingmar Schlecht <ingmar@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * Abstract file representation in the file abstraction layer.
31 *
32 * @author Ingmar Schlecht <ingmar@typo3.org>
33 */
34 abstract class AbstractFile implements \TYPO3\CMS\Core\Resource\FileInterface {
35
36 /**
37 * Various file properties
38 *
39 * Note that all properties, which only the persisted (indexed) files have are stored in this
40 * overall properties array only. The only properties which really exist as object properties of
41 * the file object are the storage, the identifier, the fileName and the indexing status.
42 *
43 * @var array
44 */
45 protected $properties;
46
47 /**
48 * The storage this file is located in
49 *
50 * @var \TYPO3\CMS\Core\Resource\ResourceStorage
51 */
52 protected $storage = NULL;
53
54 /**
55 * The identifier of this file to identify it on the storage.
56 * On some drivers, this is the path to the file, but drivers could also just
57 * provide any other unique identifier for this file on the specific storage.
58 *
59 * @var string
60 */
61 protected $identifier;
62
63 /**
64 * The file name of this file
65 *
66 * @var string
67 */
68 protected $name;
69
70 /**
71 * If set to true, this file is regarded as being deleted.
72 *
73 * @var boolean
74 */
75 protected $deleted = FALSE;
76
77 /**
78 * any other file
79 */
80 const FILETYPE_UNKNOWN = 0;
81 /**
82 * Any kind of text
83 */
84 const FILETYPE_TEXT = 1;
85 /**
86 * Any kind of image
87 */
88 const FILETYPE_IMAGE = 2;
89 /**
90 * Any kind of audio file
91 */
92 const FILETYPE_AUDIO = 3;
93 /**
94 * Any kind of video
95 */
96 const FILETYPE_VIDEO = 4;
97 /**
98 * Any kind of software, often known as "application"
99 */
100 const FILETYPE_SOFTWARE = 5;
101 /******************
102 * VARIOUS FILE PROPERTY GETTERS
103 ******************/
104 /**
105 * Returns true if the given property key exists for this file.
106 *
107 * @param string $key
108 * @return boolean
109 */
110 public function hasProperty($key) {
111 return array_key_exists($key, $this->properties);
112 }
113
114 /**
115 * Returns a property value
116 *
117 * @param string $key
118 * @return mixed Property value
119 */
120 public function getProperty($key) {
121 return $this->properties[$key];
122 }
123
124 /**
125 * Returns the properties of this object.
126 *
127 * @return array
128 */
129 public function getProperties() {
130 return $this->properties;
131 }
132
133 /**
134 * Returns the identifier of this file
135 *
136 * @return string
137 */
138 public function getIdentifier() {
139 return $this->identifier;
140 }
141
142 /**
143 * Returns the name of this file
144 *
145 * @return string
146 */
147 public function getName() {
148 // Do not check if file has been deleted because we might need the
149 // name for undeleting it.
150 return $this->name;
151 }
152
153 /**
154 * Returns the basename (the name without extension) of this file.
155 *
156 * @return string
157 */
158 public function getNameWithoutExtension() {
159 return pathinfo($this->getName(), PATHINFO_FILENAME);
160 }
161
162 /**
163 * Returns the size of this file
164 *
165 * @return integer
166 */
167 public function getSize() {
168 if ($this->deleted) {
169 throw new \RuntimeException('File has been deleted.', 1329821480);
170 }
171 return $this->properties['size'];
172 }
173
174 /**
175 * Returns the uid of this file
176 *
177 * @return integer
178 */
179 public function getUid() {
180 return $this->getProperty('uid');
181 }
182
183 /**
184 * Returns the Sha1 of this file
185 *
186 * @return string
187 */
188 public function getSha1() {
189 if ($this->deleted) {
190 throw new \RuntimeException('File has been deleted.', 1329821481);
191 }
192 return $this->getStorage()->hashFile($this, 'sha1');
193 }
194
195 /**
196 * Returns the creation time of the file as Unix timestamp
197 *
198 * @return integer
199 */
200 public function getCreationTime() {
201 if ($this->deleted) {
202 throw new \RuntimeException('File has been deleted.', 1329821487);
203 }
204 return $this->getProperty('creation_date');
205 }
206
207 /**
208 * Returns the date (as UNIX timestamp) the file was last modified.
209 *
210 * @return integer
211 */
212 public function getModificationTime() {
213 if ($this->deleted) {
214 throw new \RuntimeException('File has been deleted.', 1329821488);
215 }
216 return $this->getProperty('modification_date');
217 }
218
219 /**
220 * Get the extension of this file in a lower-case variant
221 *
222 * @return string The file extension
223 */
224 public function getExtension() {
225 $pathinfo = pathinfo($this->getName());
226
227 $extension = strtolower($pathinfo['extension']);
228
229 return $extension;
230 }
231
232 /**
233 * Get the MIME type of this file
234 *
235 * @return array file information
236 */
237 public function getMimeType() {
238 // TODO this will be slow - use the cached version if possible
239 $stat = $this->getStorage()->getFileInfo($this);
240 return $stat['mimetype'];
241 }
242
243 /**
244 * Returns the fileType of this file
245 * basically there are only five main "file types"
246 * "audio"
247 * "image"
248 * "software"
249 * "text"
250 * "video"
251 * "other"
252 * see the constants in this class
253 *
254 * @return integer $fileType
255 */
256 public function getType() {
257 // this basically extracts the mimetype and guess the filetype based
258 // on the first part of the mimetype works for 99% of all cases, and
259 // we don't need to make an SQL statement like EXT:media does currently
260 if (!$this->properties['type']) {
261 $mimeType = $this->getMimeType();
262 list($fileType) = explode('/', $mimeType);
263 switch (strtolower($fileType)) {
264 case 'text':
265 $this->properties['type'] = self::FILETYPE_TEXT;
266 break;
267 case 'image':
268 $this->properties['type'] = self::FILETYPE_IMAGE;
269 break;
270 case 'audio':
271 $this->properties['type'] = self::FILETYPE_AUDIO;
272 break;
273 case 'video':
274 $this->properties['type'] = self::FILETYPE_VIDEO;
275 break;
276 case 'application':
277
278 case 'software':
279 $this->properties['type'] = self::FILETYPE_SOFTWARE;
280 break;
281 default:
282 $this->properties['type'] = self::FILETYPE_UNKNOWN;
283 }
284 }
285 return $this->properties['type'];
286 }
287
288 /******************
289 * CONTENTS RELATED
290 ******************/
291 /**
292 * Get the contents of this file
293 *
294 * @return string File contents
295 */
296 public function getContents() {
297 if ($this->deleted) {
298 throw new \RuntimeException('File has been deleted.', 1329821479);
299 }
300 return $this->getStorage()->getFileContents($this);
301 }
302
303 /**
304 * Replace the current file contents with the given string
305 *
306 * @param string $contents The contents to write to the file.
307 * @return \TYPO3\CMS\Core\Resource\File The file object (allows chaining).
308 */
309 public function setContents($contents) {
310 if ($this->deleted) {
311 throw new \RuntimeException('File has been deleted.', 1329821478);
312 }
313 $this->getStorage()->setFileContents($this, $contents);
314 return $this;
315 }
316
317 /****************************************
318 * STORAGE AND MANAGEMENT RELATED METHDOS
319 ****************************************/
320 /**
321 * Get the storage this file is located in
322 *
323 * @return \TYPO3\CMS\Core\Resource\ResourceStorage
324 */
325 public function getStorage() {
326 if ($this->storage === NULL) {
327 $this->loadStorage();
328 }
329 return $this->storage;
330 }
331
332 /**
333 * Loads the storage object of this file object.
334 *
335 * @return void
336 */
337 protected function loadStorage() {
338 $storageUid = $this->getProperty('storage');
339 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($storageUid)) {
340 /** @var $fileFactory \TYPO3\CMS\Core\Resource\ResourceFactory */
341 $fileFactory = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ResourceFactory');
342 $this->storage = $fileFactory->getStorageObject($storageUid);
343 }
344 }
345
346 /**
347 * Checks if this file exists. This should normally always return TRUE;
348 * it might only return FALSE when this object has been created from an
349 * index record without checking for.
350 *
351 * @return boolean TRUE if this file physically exists
352 */
353 public function exists() {
354 if ($this->deleted) {
355 return FALSE;
356 }
357 return $this->storage->hasFile($this->getIdentifier());
358 }
359
360 /**
361 * Sets the storage this file is located in. This is only meant for
362 * t3lib/file/-internal usage; don't use it to move files.
363 *
364 * @internal Should only be used by other parts of the File API (e.g. drivers after moving a file)
365 * @param integer|\TYPO3\CMS\Core\Resource\ResourceStorage $storage
366 * @return \TYPO3\CMS\Core\Resource\File
367 */
368 public function setStorage($storage) {
369 // Do not check for deleted file here as we might need this method for the recycler later on
370 if (is_object($storage) && $storage instanceof \TYPO3\CMS\Core\Resource\ResourceStorage) {
371 $this->storage = $storage;
372 $this->properties['storage'] = $storage->getUid();
373 } else {
374 $this->properties['storage'] = $storage;
375 $this->storage = NULL;
376 }
377 return $this;
378 }
379
380 /**
381 * Set the identifier of this file
382 *
383 * @internal Should only be used by other parts of the File API (e.g. drivers after moving a file)
384 * @param string $identifier
385 * @return string
386 */
387 public function setIdentifier($identifier) {
388 $this->identifier = $identifier;
389 }
390
391 /**
392 * Returns a combined identifier of this file, i.e. the storage UID and the
393 * folder identifier separated by a colon ":".
394 *
395 * @return string Combined storage and file identifier, e.g. StorageUID:path/and/fileName.png
396 */
397 public function getCombinedIdentifier() {
398 if (is_array($this->properties) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->properties['storage'])) {
399 $combinedIdentifier = $this->properties['storage'] . ':' . $this->getIdentifier();
400 } else {
401 $combinedIdentifier = $this->getStorage()->getUid() . ':' . $this->getIdentifier();
402 }
403 return $combinedIdentifier;
404 }
405
406 /**
407 * Deletes this file from its storage. This also means that this object becomes useless.
408 *
409 * @return bool TRUE if deletion succeeded
410 */
411 public function delete() {
412 // The storage will mark this file as deleted
413 return $this->getStorage()->deleteFile($this);
414 }
415
416 /**
417 * Marks this file as deleted. This should only be used inside the
418 * File Abstraction Layer, as it is a low-level API method.
419 *
420 * @return void
421 */
422 public function setDeleted() {
423 $this->deleted = TRUE;
424 }
425
426 /**
427 * Returns TRUE if this file has been deleted
428 *
429 * @return boolean
430 */
431 public function isDeleted() {
432 return $this->deleted;
433 }
434
435 /**
436 * Renames this file.
437 *
438 * @param string $newName The new file name
439 * @return \TYPO3\CMS\Core\Resource\File
440 */
441 public function rename($newName) {
442 if ($this->deleted) {
443 throw new \RuntimeException('File has been deleted.', 1329821482);
444 }
445 return $this->getStorage()->renameFile($this, $newName);
446 }
447
448 /**
449 * Copies this file into a target folder
450 *
451 * @param \TYPO3\CMS\Core\Resource\Folder $targetFolder Folder to copy file into.
452 * @param string $targetFileName an optional destination fileName
453 * @param string $conflictMode overrideExistingFile", "renameNewFile", "cancel
454 * @return \TYPO3\CMS\Core\Resource\File The new (copied) file.
455 */
456 public function copyTo(\TYPO3\CMS\Core\Resource\Folder $targetFolder, $targetFileName = NULL, $conflictMode = 'renameNewFile') {
457 if ($this->deleted) {
458 throw new \RuntimeException('File has been deleted.', 1329821483);
459 }
460 return $targetFolder->getStorage()->copyFile($this, $targetFolder, $targetFileName, $conflictMode);
461 }
462
463 /**
464 * Moves the file into the target folder
465 *
466 * @param \TYPO3\CMS\Core\Resource\Folder $targetFolder Folder to move file into.
467 * @param string $targetFileName an optional destination fileName
468 * @param string $conflictMode overrideExistingFile", "renameNewFile", "cancel
469 * @return \TYPO3\CMS\Core\Resource\File This file object, with updated properties.
470 */
471 public function moveTo(\TYPO3\CMS\Core\Resource\Folder $targetFolder, $targetFileName = NULL, $conflictMode = 'renameNewFile') {
472 if ($this->deleted) {
473 throw new \RuntimeException('File has been deleted.', 1329821484);
474 }
475 return $targetFolder->getStorage()->moveFile($this, $targetFolder, $targetFileName, $conflictMode);
476 }
477
478 /*****************
479 * SPECIAL METHODS
480 *****************/
481 /**
482 * Returns a publicly accessible URL for this file
483 *
484 * WARNING: Access to the file may be restricted by further means, e.g. some
485 * web-based authentication. You have to take care of this yourself.
486 *
487 * @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)
488 * @return string
489 */
490 public function getPublicUrl($relativeToCurrentScript = FALSE) {
491 if ($this->deleted) {
492 throw new \RuntimeException('File has been deleted.', 1329821485);
493 }
494 return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
495 }
496
497 /**
498 * Returns a path to a local version of this file to process it locally (e.g. with some system tool).
499 * If the file is normally located on a remote storages, this creates a local copy.
500 * If the file is already on the local system, this only makes a new copy if $writable is set to TRUE.
501 *
502 * @param boolean $writable Set this to FALSE if you only want to do read operations on the file.
503 * @return string
504 */
505 public function getForLocalProcessing($writable = TRUE) {
506 if ($this->deleted) {
507 throw new \RuntimeException('File has been deleted.', 1329821486);
508 }
509 return $this->getStorage()->getFileForLocalProcessing($this, $writable);
510 }
511
512 /***********************
513 * INDEX RELATED METHODS
514 ***********************/
515 /**
516 * Updates properties of this object.
517 * This method is used to reconstitute settings from the
518 * database into this object after being intantiated.
519 *
520 * @param array $properties
521 */
522 abstract public function updateProperties(array $properties);
523
524 }
525
526
527 ?>