b03f600afffe005ae4502d09c0b268f8a867ba0e
[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 use TYPO3\CMS\Core\Utility\MathUtility;
19
20 /**
21 * File representation in the file abstraction layer.
22 */
23 class File extends AbstractFile
24 {
25 /**
26 * @var bool
27 */
28 protected $metaDataLoaded = false;
29
30 /**
31 * @var array
32 */
33 protected $metaDataProperties = [];
34
35 /**
36 * Set to TRUE while this file is being indexed - used to prevent some endless loops
37 *
38 * @var bool
39 */
40 protected $indexingInProgress = false;
41
42 /**
43 * Contains the names of all properties that have been update since the
44 * instantiation of this object
45 *
46 * @var array
47 */
48 protected $updatedProperties = [];
49
50 /**
51 * Constructor for a file object. Should normally not be used directly, use
52 * the corresponding factory methods instead.
53 *
54 * @param array $fileData
55 * @param ResourceStorage $storage
56 * @param array $metaData
57 */
58 public function __construct(array $fileData, ResourceStorage $storage, array $metaData = [])
59 {
60 $this->identifier = $fileData['identifier'];
61 $this->name = $fileData['name'];
62 $this->properties = $fileData;
63 $this->storage = $storage;
64 if (!empty($metaData)) {
65 $this->metaDataLoaded = true;
66 $this->metaDataProperties = $metaData;
67 }
68 }
69
70 /*******************************
71 * VARIOUS FILE PROPERTY GETTERS
72 *******************************/
73 /**
74 * Returns a property value
75 *
76 * @param string $key
77 * @return mixed Property value
78 */
79 public function getProperty($key)
80 {
81 if (parent::hasProperty($key)) {
82 return parent::getProperty($key);
83 }
84 $metaData = $this->_getMetaData();
85 return isset($metaData[$key]) ? $metaData[$key] : null;
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 if ($taskType === ProcessedFile::CONTEXT_IMAGEPREVIEW) {
301 $configuration = array_merge(['width' => 64, 'height' => 64], $configuration);
302 $configuration['width'] = MathUtility::forceIntegerInRange($configuration['width'], 1, 1000);
303 $configuration['height'] = MathUtility::forceIntegerInRange($configuration['height'], 1, 1000);
304 }
305 return $this->getStorage()->processFile($this, $taskType, $configuration);
306 }
307
308 /**
309 * Returns an array representation of the file.
310 * (This is used by the generic listing module vidi when displaying file records.)
311 *
312 * @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.
313 */
314 public function toArray()
315 {
316 $array = [
317 'id' => $this->getCombinedIdentifier(),
318 'name' => $this->getName(),
319 'extension' => $this->getExtension(),
320 'type' => $this->getType(),
321 'mimetype' => $this->getMimeType(),
322 'size' => $this->getSize(),
323 'url' => $this->getPublicUrl(),
324 'indexed' => true,
325 'uid' => $this->getUid(),
326 'permissions' => [
327 'read' => $this->checkActionPermission('read'),
328 'write' => $this->checkActionPermission('write'),
329 'delete' => $this->checkActionPermission('delete')
330 ],
331 'checksum' => $this->calculateChecksum()
332 ];
333 foreach ($this->properties as $key => $value) {
334 $array[$key] = $value;
335 }
336 $stat = $this->getStorage()->getFileInfo($this);
337 foreach ($stat as $key => $value) {
338 $array[$key] = $value;
339 }
340 return $array;
341 }
342
343 /**
344 * @return bool
345 */
346 public function isMissing()
347 {
348 return (bool)$this->getProperty('missing');
349 }
350
351 /**
352 * @param bool $missing
353 */
354 public function setMissing($missing)
355 {
356 $this->updateProperties(['missing' => $missing ? 1 : 0]);
357 }
358
359 /**
360 * Returns a publicly accessible URL for this file
361 * When file is marked as missing or deleted no url is returned
362 *
363 * WARNING: Access to the file may be restricted by further means, e.g. some
364 * web-based authentication. You have to take care of this yourself.
365 *
366 * @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)
367 *
368 * @return string
369 */
370 public function getPublicUrl($relativeToCurrentScript = false)
371 {
372 if ($this->isMissing() || $this->deleted) {
373 return false;
374 }
375 return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
376 }
377
378 /**
379 * @return Index\MetaDataRepository
380 */
381 protected function getMetaDataRepository()
382 {
383 return GeneralUtility::makeInstance(Index\MetaDataRepository::class);
384 }
385
386 /**
387 * @return Index\FileIndexRepository
388 */
389 protected function getFileIndexRepository()
390 {
391 return GeneralUtility::makeInstance(Index\FileIndexRepository::class);
392 }
393
394 /**
395 * @param bool $indexingState
396 * @internal Only for usage in Indexer
397 */
398 public function setIndexingInProgess($indexingState)
399 {
400 $this->indexingInProgress = (bool)$indexingState;
401 }
402
403 /**
404 * @param $key
405 * @internal Only for use in Repositories and indexer
406 * @return mixed
407 */
408 public function _getPropertyRaw($key)
409 {
410 return parent::getProperty($key);
411 }
412 }