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