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