2 namespace TYPO3\CMS\Core\
Resource;
4 /***************************************************************
7 * (c) 2011-2013 Ingo Renner <ingo@typo3.org>
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
30 use TYPO3\CMS\Core\Utility\GeneralUtility
;
33 * File representation in the file abstraction layer.
35 * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
37 class File
extends AbstractFile
{
40 * File indexing status. True, if the file is indexed in the database;
41 * NULL is the default value, this means that the index status is unknown
45 protected $indexed = NULL;
48 * Tells whether to index a file or not.
49 * If yes, the file will be persisted into sys_file.
53 protected $indexable = TRUE;
58 protected $metaDataProperties = array();
61 * Set to TRUE while this file is being indexed - used to prevent some endless loops
65 protected $indexingInProgress = FALSE;
68 * Contains the names of all properties that have been update since the
69 * instantiation of this object
73 protected $updatedProperties = array();
76 * @var \TYPO3\CMS\Core\Resource\Service\IndexerService
78 protected $indexerService = NULL;
81 * Constructor for a file object. Should normally not be used directly, use
82 * the corresponding factory methods instead.
84 * @param array $fileData
85 * @param ResourceStorage $storage
87 public function __construct(array $fileData, ResourceStorage
$storage) {
88 $this->identifier
= $fileData['identifier'];
89 $this->name
= $fileData['name'];
90 $this->properties
= $fileData;
91 $this->storage
= $storage;
93 if (isset($fileData['uid']) && intval($fileData['uid']) > 0) {
94 $this->indexed
= TRUE;
95 $this->loadMetaData();
99 /*******************************
100 * VARIOUS FILE PROPERTY GETTERS
101 *******************************/
103 * Returns a property value
106 * @return mixed Property value
108 public function getProperty($key) {
109 if ($this->indexed
=== NULL) {
110 $this->loadIndexRecord();
112 if (parent
::hasProperty($key)) {
113 return parent
::getProperty($key);
115 return array_key_exists($key, $this->metaDataProperties
) ?
$this->metaDataProperties
[$key] : NULL;
120 * Returns the properties of this object.
124 public function getProperties() {
125 if ($this->indexed
=== NULL) {
126 $this->loadIndexRecord();
128 return array_merge(parent
::getProperties(), array_diff_key((array)$this->metaDataProperties
, parent
::getProperties()));
132 * Returns the MetaData
137 public function _getMetaData() {
138 return $this->metaDataProperties
;
145 * Get the contents of this file
147 * @return string File contents
149 public function getContents() {
150 return $this->getStorage()->getFileContents($this);
154 * Replace the current file contents with the given string
156 * @param string $contents The contents to write to the file.
157 * @return File The file object (allows chaining).
159 public function setContents($contents) {
160 $this->getStorage()->setFileContents($this, $contents);
164 /***********************
165 * INDEX RELATED METHODS
166 ***********************/
168 * Returns TRUE if this file is indexed
170 * @return boolean|NULL
172 public function isIndexed() {
173 if ($this->indexed
=== NULL && !$this->indexingInProgress
) {
174 $this->loadIndexRecord();
176 return $this->indexed
;
180 * @param bool $indexIfNotIndexed
182 * @throws \RuntimeException
185 protected function loadIndexRecord($indexIfNotIndexed = TRUE) {
186 if ($this->indexed
!== NULL ||
!$this->indexable ||
$this->indexingInProgress
) {
189 $this->indexingInProgress
= TRUE;
191 $indexRecord = $this->getFileIndexRepository()->findOneByCombinedIdentifier($this->getCombinedIdentifier());
192 if ($indexRecord === FALSE && $indexIfNotIndexed) {
193 $this->getIndexerService()->updateIndexEntry($this);
194 $this->updatedProperties
= array();
195 } elseif ($indexRecord !== FALSE) {
196 $this->mergeIndexRecord($indexRecord);
197 $this->indexed
= TRUE;
198 $this->loadMetaData();
200 throw new \
RuntimeException('Could not load index record for "' . $this->getIdentifier() . '"', 1321288316);
202 $this->indexingInProgress
= FALSE;
206 * Loads MetaData from Repository
208 protected function loadMetaData() {
209 $this->metaDataProperties
= $this->getMetaDataRepository()->findByFile($this);
213 * Merges the contents of this file's index record into the file properties.
215 * @param array $recordData The index record as fetched from the database
217 * @throws \InvalidArgumentException
220 protected function mergeIndexRecord(array $recordData) {
221 if ($this->properties
['uid'] != 0) {
222 throw new \
InvalidArgumentException('uid property is already set. Cannot merge index record.', 1321023156);
224 $this->properties
= array_merge($recordData, $this->properties
);
228 * Updates the properties of this file, e.g. after re-indexing or moving it.
229 * By default, only properties that exist as a key in the $properties array
230 * are overwritten. If you want to explicitly unset a property, set the
231 * corresponding key to NULL in the array.
233 * NOTE: This method should not be called from outside the File Abstraction Layer (FAL)!
235 * @param array $properties
239 public function updateProperties(array $properties) {
240 // Setting identifier and name to update values; we have to do this
241 // here because we might need a new identifier when loading
242 // (and thus possibly indexing) a file.
243 if (isset($properties['identifier'])) {
244 $this->identifier
= $properties['identifier'];
246 if (isset($properties['name'])) {
247 $this->name
= $properties['name'];
249 if ($this->indexed
=== NULL && !isset($properties['uid'])) {
250 $this->loadIndexRecord();
252 if ($this->properties
['uid'] != 0 && isset($properties['uid'])) {
253 unset($properties['uid']);
255 foreach ($properties as $key => $value) {
256 if ($this->properties
[$key] !== $value) {
257 if (!in_array($key, $this->updatedProperties
)) {
258 $this->updatedProperties
[] = $key;
260 // TODO check if we should completely remove properties that
262 $this->properties
[$key] = $value;
265 // Updating indexing status
266 if (isset($properties['uid']) && intval($properties['uid']) > 0) {
267 $this->indexed
= TRUE;
268 $this->loadMetaData();
270 if (array_key_exists('storage', $properties) && in_array('storage', $this->updatedProperties
)) {
271 $this->storage
= ResourceFactory
::getInstance()->getStorageObject($properties['storage']);
276 * Returns the names of all properties that have been updated in this record
280 public function getUpdatedProperties() {
281 return $this->updatedProperties
;
284 /****************************************
285 * STORAGE AND MANAGEMENT RELATED METHODS
286 ****************************************/
288 * Check if a file operation (= action) is allowed for this file
290 * @param string $action, can be read, write, delete
293 public function checkActionPermission($action) {
294 return $this->getStorage()->checkFileActionPermission($action, $this);
301 * Creates a MD5 hash checksum based on the combined identifier of the file,
302 * the files' mimetype and the systems' encryption key.
303 * used to generate a thumbnail, and this hash is checked if valid
305 * @todo maybe \TYPO3\CMS\Core\Utility\GeneralUtility::hmac() could be used?
306 * @return string the MD5 hash
308 public function calculateChecksum() {
309 return md5($this->getCombinedIdentifier() . '|' . $this->getMimeType() . '|' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']);
313 * Returns a modified version of the file.
315 * @param string $taskType The task type of this processing
316 * @param array $configuration the processing configuration, see manual for that
317 * @return ProcessedFile The processed file
319 public function process($taskType, array $configuration) {
320 return $this->getStorage()->processFile($this, $taskType, $configuration);
324 * Returns an array representation of the file.
325 * (This is used by the generic listing module vidi when displaying file records.)
327 * @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.
329 public function toArray() {
331 'id' => $this->getCombinedIdentifier(),
332 'name' => $this->getName(),
333 'extension' => $this->getExtension(),
334 'type' => $this->getType(),
335 'mimetype' => $this->getMimeType(),
336 'size' => $this->getSize(),
337 'url' => $this->getPublicUrl(),
338 'indexed' => $this->indexed
,
339 'uid' => $this->getUid(),
340 'permissions' => array(
341 'read' => $this->checkActionPermission('read'),
342 'write' => $this->checkActionPermission('write'),
343 'delete' => $this->checkActionPermission('delete')
345 'checksum' => $this->calculateChecksum()
347 foreach ($this->properties
as $key => $value) {
348 $array[$key] = $value;
350 $stat = $this->getStorage()->getFileInfo($this);
351 foreach ($stat as $key => $value) {
352 $array[$key] = $value;
360 public function isIndexable() {
361 return $this->indexable
;
365 * @param boolean $indexable
367 public function setIndexable($indexable) {
368 $this->indexable
= $indexable;
374 public function isMissing() {
375 return (bool) $this->getProperty('missing');
379 * @param boolean $missing
381 public function setMissing($missing) {
382 $this->updateProperties(array('missing' => $missing ?
1 : 0));
386 * Returns a publicly accessible URL for this file
387 * When file is marked as missing or deleted no url is returned
389 * WARNING: Access to the file may be restricted by further means, e.g. some
390 * web-based authentication. You have to take care of this yourself.
392 * @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)
396 public function getPublicUrl($relativeToCurrentScript = FALSE) {
397 if ($this->isMissing() ||
$this->deleted
) {
400 return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
405 * @return \TYPO3\CMS\Core\Resource\Index\MetaDataRepository
407 protected function getMetaDataRepository() {
408 return GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\MetaDataRepository');
412 * @return \TYPO3\CMS\Core\Resource\Index\FileIndexRepository
414 protected function getFileIndexRepository() {
415 return GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\FileIndexRepository');
419 * Internal function to retrieve the indexer service,
420 * if it does not exist, an instance will be created
422 * @return Index\Indexer
424 protected function getIndexerService() {
425 if ($this->indexerService
=== NULL) {
426 $this->indexerService
= \TYPO3\CMS\Core\Utility\GeneralUtility
::makeInstance('TYPO3\\CMS\\Core\\Resource\\Index\\Indexer', $this->storage
);
428 return $this->indexerService
;
432 * @param boolean $indexingState
433 * @internal Only for usage in Indexer
435 public function setIndexingInProgess($indexingState) {
436 $this->indexingInProgress
= (boolean
)$indexingState;
441 * @internal Only for use in Repositories and indexer
444 public function _getPropertyRaw($key) {
445 return parent
::getProperty($key);