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