c6339bef79dfc85e8a367fc40d6fff93f3be0100
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / File.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\GeneralUtility;
18
19 /**
20 * File representation in the file abstraction layer.
21 */
22 class 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'];
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 } else {
83 $metaData = $this->_getMetaData();
84 return isset($metaData[$key]) ? $metaData[$key] : null;
85 }
86 }
87
88 /**
89 * Checks if the file has a (metadata) property which
90 * can be retrieved by "getProperty"
91 *
92 * @param string $key
93 * @return bool
94 */
95 public function hasProperty($key)
96 {
97 if (!parent::hasProperty($key)) {
98 return array_key_exists($key, $this->_getMetaData());
99 }
100 return true;
101 }
102
103 /**
104 * Returns the properties of this object.
105 *
106 * @return array
107 */
108 public function getProperties()
109 {
110 return array_merge(parent::getProperties(), array_diff_key($this->_getMetaData(), parent::getProperties()));
111 }
112
113 /**
114 * Returns the MetaData
115 *
116 * @return array
117 * @internal
118 */
119 public function _getMetaData()
120 {
121 if (!$this->metaDataLoaded) {
122 $this->loadMetaData();
123 }
124 return $this->metaDataProperties;
125 }
126
127 /******************
128 * CONTENTS RELATED
129 ******************/
130 /**
131 * Get the contents of this file
132 *
133 * @return string File contents
134 */
135 public function getContents()
136 {
137 return $this->getStorage()->getFileContents($this);
138 }
139
140 /**
141 * Gets SHA1 hash.
142 *
143 * @return string
144 */
145 public function getSha1()
146 {
147 if (empty($this->properties['sha1'])) {
148 $this->properties['sha1'] = parent::getSha1();
149 }
150 return $this->properties['sha1'];
151 }
152
153 /**
154 * Replace the current file contents with the given string
155 *
156 * @param string $contents The contents to write to the file.
157 * @return File The file object (allows chaining).
158 */
159 public function setContents($contents)
160 {
161 $this->getStorage()->setFileContents($this, $contents);
162 return $this;
163 }
164
165 /***********************
166 * INDEX RELATED METHODS
167 ***********************/
168 /**
169 * Returns TRUE if this file is indexed
170 *
171 * @return bool|NULL
172 */
173 public function isIndexed()
174 {
175 return true;
176 }
177
178 /**
179 * Loads MetaData from Repository
180 */
181 protected function loadMetaData()
182 {
183 if (!$this->indexingInProgress) {
184 $this->indexingInProgress = true;
185 $this->metaDataProperties = $this->getMetaDataRepository()->findByFile($this);
186 $this->metaDataLoaded = true;
187 $this->indexingInProgress = false;
188 }
189 }
190
191 /**
192 * Updates the properties of this file, e.g. after re-indexing or moving it.
193 * By default, only properties that exist as a key in the $properties array
194 * are overwritten. If you want to explicitly unset a property, set the
195 * corresponding key to NULL in the array.
196 *
197 * NOTE: This method should not be called from outside the File Abstraction Layer (FAL)!
198 *
199 * @param array $properties
200 * @internal
201 */
202 public function updateProperties(array $properties)
203 {
204 // Setting identifier and name to update values; we have to do this
205 // here because we might need a new identifier when loading
206 // (and thus possibly indexing) a file.
207 if (isset($properties['identifier'])) {
208 $this->identifier = $properties['identifier'];
209 }
210 if (isset($properties['name'])) {
211 $this->name = $properties['name'];
212 }
213
214 if ($this->properties['uid'] != 0 && isset($properties['uid'])) {
215 unset($properties['uid']);
216 }
217 foreach ($properties as $key => $value) {
218 if ($this->properties[$key] !== $value) {
219 if (!in_array($key, $this->updatedProperties)) {
220 $this->updatedProperties[] = $key;
221 }
222 $this->properties[$key] = $value;
223 }
224 }
225 // If the mime_type property should be updated and it was changed also update the type.
226 if (array_key_exists('mime_type', $properties) && in_array('mime_type', $this->updatedProperties)) {
227 $this->updatedProperties[] = 'type';
228 unset($this->properties['type']);
229 $this->getType();
230 }
231 if (array_key_exists('storage', $properties) && in_array('storage', $this->updatedProperties)) {
232 $this->storage = ResourceFactory::getInstance()->getStorageObject($properties['storage']);
233 }
234 }
235
236 /**
237 * Updates MetaData properties
238 *
239 * @internal Do not use outside the FileAbstraction Layer classes
240 *
241 * @param array $properties
242 */
243 public function _updateMetaDataProperties(array $properties)
244 {
245 $this->metaDataProperties = array_merge($this->metaDataProperties, $properties);
246 }
247
248 /**
249 * Returns the names of all properties that have been updated in this record
250 *
251 * @return array
252 */
253 public function getUpdatedProperties()
254 {
255 return $this->updatedProperties;
256 }
257
258 /****************************************
259 * STORAGE AND MANAGEMENT RELATED METHODS
260 ****************************************/
261 /**
262 * Check if a file operation (= action) is allowed for this file
263 *
264 * @param string $action, can be read, write, delete
265 * @return bool
266 */
267 public function checkActionPermission($action)
268 {
269 return $this->getStorage()->checkFileActionPermission($action, $this);
270 }
271
272 /*****************
273 * SPECIAL METHODS
274 *****************/
275 /**
276 * Creates a MD5 hash checksum based on the combined identifier of the file,
277 * the files' mimetype and the systems' encryption key.
278 * used to generate a thumbnail, and this hash is checked if valid
279 *
280 * @return string the MD5 hash
281 */
282 public function calculateChecksum()
283 {
284 return md5(
285 $this->getCombinedIdentifier() . '|' .
286 $this->getMimeType() . '|' .
287 $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']
288 );
289 }
290
291 /**
292 * Returns a modified version of the file.
293 *
294 * @param string $taskType The task type of this processing
295 * @param array $configuration the processing configuration, see manual for that
296 * @return ProcessedFile The processed file
297 */
298 public function process($taskType, array $configuration)
299 {
300 return $this->getStorage()->processFile($this, $taskType, $configuration);
301 }
302
303 /**
304 * Returns an array representation of the file.
305 * (This is used by the generic listing module vidi when displaying file records.)
306 *
307 * @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.
308 */
309 public function toArray()
310 {
311 $array = [
312 'id' => $this->getCombinedIdentifier(),
313 'name' => $this->getName(),
314 'extension' => $this->getExtension(),
315 'type' => $this->getType(),
316 'mimetype' => $this->getMimeType(),
317 'size' => $this->getSize(),
318 'url' => $this->getPublicUrl(),
319 'indexed' => true,
320 'uid' => $this->getUid(),
321 'permissions' => [
322 'read' => $this->checkActionPermission('read'),
323 'write' => $this->checkActionPermission('write'),
324 'delete' => $this->checkActionPermission('delete')
325 ],
326 'checksum' => $this->calculateChecksum()
327 ];
328 foreach ($this->properties as $key => $value) {
329 $array[$key] = $value;
330 }
331 $stat = $this->getStorage()->getFileInfo($this);
332 foreach ($stat as $key => $value) {
333 $array[$key] = $value;
334 }
335 return $array;
336 }
337
338 /**
339 * @return bool
340 */
341 public function isMissing()
342 {
343 return (bool)$this->getProperty('missing');
344 }
345
346 /**
347 * @param bool $missing
348 */
349 public function setMissing($missing)
350 {
351 $this->updateProperties(['missing' => $missing ? 1 : 0]);
352 }
353
354 /**
355 * Returns a publicly accessible URL for this file
356 * When file is marked as missing or deleted no url is returned
357 *
358 * WARNING: Access to the file may be restricted by further means, e.g. some
359 * web-based authentication. You have to take care of this yourself.
360 *
361 * @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)
362 *
363 * @return string
364 */
365 public function getPublicUrl($relativeToCurrentScript = false)
366 {
367 if ($this->isMissing() || $this->deleted) {
368 return false;
369 } else {
370 return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
371 }
372 }
373
374 /**
375 * @return Index\MetaDataRepository
376 */
377 protected function getMetaDataRepository()
378 {
379 return GeneralUtility::makeInstance(Index\MetaDataRepository::class);
380 }
381
382 /**
383 * @return Index\FileIndexRepository
384 */
385 protected function getFileIndexRepository()
386 {
387 return GeneralUtility::makeInstance(Index\FileIndexRepository::class);
388 }
389
390 /**
391 * @param bool $indexingState
392 * @internal Only for usage in Indexer
393 */
394 public function setIndexingInProgess($indexingState)
395 {
396 $this->indexingInProgress = (bool)$indexingState;
397 }
398
399 /**
400 * @param $key
401 * @internal Only for use in Repositories and indexer
402 * @return mixed
403 */
404 public function _getPropertyRaw($key)
405 {
406 return parent::getProperty($key);
407 }
408 }