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