[BUGFIX] ProcessedFile is persisted in sys_file as well
[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 * All rights reserved
9 *
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.
15 *
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.
20 *
21 *
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.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * File representation in the file abstraction layer.
31 *
32 * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
33 */
34 class File extends AbstractFile {
35
36 /**
37 * File indexing status. True, if the file is indexed in the database;
38 * NULL is the default value, this means that the index status is unknown
39 *
40 * @var boolean|NULL
41 */
42 protected $indexed = NULL;
43
44 /**
45 * Tells whether to index a file or not.
46 * If yes, the file will be persisted into sys_file.
47 *
48 * @var boolean
49 */
50 protected $indexable = TRUE;
51
52 /**
53 * Set to TRUE while this file is being indexed - used to prevent some endless loops
54 *
55 * @var boolean
56 */
57 protected $indexingInProgress = FALSE;
58
59 /**
60 * Contains the names of all properties that have been update since the
61 * instantiation of this object
62 *
63 * @var array
64 */
65 protected $updatedProperties = array();
66
67 /*********************************************
68 * GENERIC FILE TYPES
69 * these are generic filetypes or -groups,
70 * don't mix it up with mime types
71 *********************************************/
72 /**
73 * Constructor for a file object. Should normally not be used directly, use
74 * the corresponding factory methods instead.
75 *
76 * @param array $fileData
77 * @param ResourceStorage $storage
78 */
79 public function __construct(array $fileData, $storage = NULL) {
80 if (isset($fileData['uid']) && intval($fileData['uid']) > 0) {
81 $this->indexed = TRUE;
82 }
83 $this->identifier = $fileData['identifier'];
84 $this->name = $fileData['name'];
85 $this->properties = $fileData;
86 if (is_object($storage)) {
87 $this->storage = $storage;
88 } elseif (isset($fileData['storage']) && is_object($fileData['storage'])) {
89 $this->storage = $fileData['storage'];
90 }
91 }
92
93 /*******************************
94 * VARIOUS FILE PROPERTY GETTERS
95 *******************************/
96 /**
97 * Returns a property value
98 *
99 * @param string $key
100 * @return mixed Property value
101 */
102 public function getProperty($key) {
103 if ($this->indexed === NULL) {
104 $this->loadIndexRecord();
105 }
106 return parent::getProperty($key);
107 }
108
109 /**
110 * Returns the properties of this object.
111 *
112 * @return array
113 */
114 public function getProperties() {
115 if ($this->indexed === NULL) {
116 $this->loadIndexRecord();
117 }
118 return parent::getProperties();
119 }
120
121 /******************
122 * CONTENTS RELATED
123 ******************/
124 /**
125 * Get the contents of this file
126 *
127 * @return string File contents
128 */
129 public function getContents() {
130 return $this->getStorage()->getFileContents($this);
131 }
132
133 /**
134 * Replace the current file contents with the given string
135 *
136 * @param string $contents The contents to write to the file.
137 * @return File The file object (allows chaining).
138 */
139 public function setContents($contents) {
140 $this->getStorage()->setFileContents($this, $contents);
141 return $this;
142 }
143
144 /***********************
145 * INDEX RELATED METHODS
146 ***********************/
147 /**
148 * Returns TRUE if this file is indexed
149 *
150 * @return boolean|NULL
151 */
152 public function isIndexed() {
153 if ($this->indexed === NULL && !$this->indexingInProgress) {
154 $this->loadIndexRecord();
155 }
156 return $this->indexed;
157 }
158
159 /**
160 * @param bool $indexIfNotIndexed
161 *
162 * @throws \RuntimeException
163 * @return void
164 */
165 protected function loadIndexRecord($indexIfNotIndexed = TRUE) {
166 if ($this->indexed !== NULL || !$this->indexable) {
167 return;
168 }
169 /** @var $repo FileRepository */
170 $repo = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileRepository');
171 $indexRecord = $repo->getFileIndexRecord($this);
172 if ($indexRecord === FALSE && $indexIfNotIndexed) {
173 $this->indexingInProgress = TRUE;
174 $indexRecord = $repo->addToIndex($this);
175 $this->mergeIndexRecord($indexRecord);
176 $this->indexed = TRUE;
177 $this->indexingInProgress = FALSE;
178 } elseif ($indexRecord !== FALSE) {
179 $this->mergeIndexRecord($indexRecord);
180 $this->indexed = TRUE;
181 } else {
182 throw new \RuntimeException('Could not load index record for "' . $this->getIdentifier() . '"', 1321288316);
183 }
184 }
185
186 /**
187 * Merges the contents of this file's index record into the file properties.
188 *
189 * @param array $recordData The index record as fetched from the database
190 *
191 * @throws \InvalidArgumentException
192 * @return void
193 */
194 protected function mergeIndexRecord(array $recordData) {
195 if ($this->properties['uid'] != 0) {
196 throw new \InvalidArgumentException('uid property is already set. Cannot merge index record.', 1321023156);
197 }
198 $this->properties = array_merge($recordData, $this->properties);
199 }
200
201 /**
202 * Updates the properties of this file, e.g. after re-indexing or moving it.
203 * By default, only properties that exist as a key in the $properties array
204 * are overwritten. If you want to explicitly unset a property, set the
205 * corresponding key to NULL in the array.
206 *
207 * NOTE: This method should not be called from outside the File Abstraction Layer (FAL)!
208 *
209 * @param array $properties
210 * @return void
211 * @internal
212 */
213 public function updateProperties(array $properties) {
214 // Setting identifier and name to update values; we have to do this
215 // here because we might need a new identifier when loading
216 // (and thus possibly indexing) a file.
217 if (isset($properties['identifier'])) {
218 $this->identifier = $properties['identifier'];
219 }
220 if (isset($properties['name'])) {
221 $this->name = $properties['name'];
222 }
223 if ($this->indexed === NULL && !isset($properties['uid'])) {
224 $this->loadIndexRecord();
225 }
226 if ($this->properties['uid'] != 0 && isset($properties['uid'])) {
227 unset($properties['uid']);
228 }
229 foreach ($properties as $key => $value) {
230 if ($this->properties[$key] !== $value) {
231 if (!in_array($key, $this->updatedProperties)) {
232 $this->updatedProperties[] = $key;
233 }
234 // TODO check if we should completely remove properties that
235 // are set to NULL
236 $this->properties[$key] = $value;
237 }
238 }
239 // Updating indexing status
240 if (isset($properties['uid']) && intval($properties['uid']) > 0) {
241 $this->indexed = TRUE;
242 }
243 if (isset($properties['storage'])) {
244 $this->loadStorage();
245 }
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 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 boolean
265 */
266 public function checkActionPermission($action) {
267 return $this->getStorage()->checkFileActionPermission($action, $this);
268 }
269
270 /*****************
271 * SPECIAL METHODS
272 *****************/
273 /**
274 * Creates a MD5 hash checksum based on the combined identifier of the file,
275 * the files' mimetype and the systems' encryption key.
276 * used to generate a thumbnail, and this hash is checked if valid
277 *
278 * @todo maybe \TYPO3\CMS\Core\Utility\GeneralUtility::hmac() could be used?
279 * @return string the MD5 hash
280 */
281 public function calculateChecksum() {
282 return md5($this->getCombinedIdentifier() . '|' . $this->getMimeType() . '|' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']);
283 }
284
285 /**
286 * Returns a modified version of the file.
287 *
288 * @param string $taskType The task type of this processing
289 * @param array $configuration the processing configuration, see manual for that
290 * @return ProcessedFile The processed file
291 */
292 public function process($taskType, array $configuration) {
293 return $this->getStorage()->processFile($this, $taskType, $configuration);
294 }
295
296 /**
297 * Returns an array representation of the file.
298 * (This is used by the generic listing module vidi when displaying file records.)
299 *
300 * @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.
301 */
302 public function toArray() {
303 $array = array(
304 'id' => $this->getCombinedIdentifier(),
305 'name' => $this->getName(),
306 'extension' => $this->getExtension(),
307 'type' => $this->getType(),
308 'mimetype' => $this->getMimeType(),
309 'size' => $this->getSize(),
310 'url' => $this->getPublicUrl(),
311 'indexed' => $this->indexed,
312 'uid' => $this->getUid(),
313 'permissions' => array(
314 'read' => $this->checkActionPermission('read'),
315 'write' => $this->checkActionPermission('write'),
316 'delete' => $this->checkActionPermission('delete')
317 ),
318 'checksum' => $this->calculateChecksum()
319 );
320 foreach ($this->properties as $key => $value) {
321 $array[$key] = $value;
322 }
323 $stat = $this->storage->getFileInfo($this);
324 foreach ($stat as $key => $value) {
325 $array[$key] = $value;
326 }
327 return $array;
328 }
329
330 /**
331 * @return boolean
332 */
333 public function isIndexable() {
334 return $this->indexable;
335 }
336
337 /**
338 * @param boolean $indexable
339 */
340 public function setIndexable($indexable) {
341 $this->indexable = $indexable;
342 }
343
344 }
345
346
347 ?>