[BUGFIX] Get folder object if a path is given
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / ResourceFactory.php
1 <?php
2 namespace TYPO3\CMS\Core\Resource;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2011 Andreas Wolf <andreas.wolf@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 use TYPO3\CMS\Core\Utility\PathUtility;
31
32 // TODO implement constructor-level caching
33 /**
34 * Factory class for FAL objects.
35 *
36 * NOTE: This class is part of the lowlevel FAL API and should not be used from outside the FAL package.
37 *
38 * @author Andreas Wolf <andreas.wolf@typo3.org>
39 */
40 class ResourceFactory implements \TYPO3\CMS\Core\SingletonInterface {
41
42 /**
43 * Gets a singleton instance of this class.
44 *
45 * @return ResourceFactory
46 */
47 static public function getInstance() {
48 return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ResourceFactory');
49 }
50
51 /**
52 * @var ResourceStorage[]
53 */
54 protected $storageInstances = array();
55
56 /**
57 * @var Collection\AbstractFileCollection[]
58 */
59 protected $collectionInstances = array();
60
61 /**
62 * @var File[]
63 */
64 protected $fileInstances = array();
65
66 /**
67 * @var FileReference[]
68 */
69 protected $fileReferenceInstances = array();
70
71 /**
72 * Creates a driver object for a specified storage object.
73 *
74 * @param string $driverIdentificationString The driver class (or identifier) to use.
75 * @param array $driverConfiguration The configuration of the storage
76 * @return Driver\AbstractDriver
77 * @throws \InvalidArgumentException
78 */
79 public function getDriverObject($driverIdentificationString, array $driverConfiguration) {
80 /** @var $driverRegistry Driver\DriverRegistry */
81 $driverRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Driver\\DriverRegistry');
82 $driverClass = $driverRegistry->getDriverClass($driverIdentificationString);
83 $driverObject = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($driverClass, $driverConfiguration);
84 return $driverObject;
85 }
86
87 /**
88 * Creates an instance of the storage from given UID. The $recordData can
89 * be supplied to increase performance.
90 *
91 * @param integer $uid The uid of the storage to instantiate.
92 * @param array $recordData The record row from database.
93 *
94 * @throws \InvalidArgumentException
95 * @return ResourceStorage
96 */
97 public function getStorageObject($uid, array $recordData = array()) {
98 if (!is_numeric($uid)) {
99 throw new \InvalidArgumentException('uid of Storage has to be numeric.', 1314085991);
100 }
101 if (!$this->storageInstances[$uid]) {
102 $storageConfiguration = NULL;
103 $storageObject = NULL;
104 // If the built-in storage with UID=0 is requested:
105 if (intval($uid) === 0) {
106 $recordData = array(
107 'uid' => 0,
108 'pid' => 0,
109 'name' => 'Default Storage',
110 'description' => 'Internal storage, mounting the main TYPO3_site directory.',
111 'driver' => 'Local',
112 'processingfolder' => 'typo3temp/_processed_/',
113 // legacy code
114 'configuration' => '',
115 'is_online' => TRUE,
116 'is_browsable' => TRUE,
117 'is_public' => TRUE,
118 'is_writable' => TRUE
119 );
120 $storageConfiguration = array(
121 'basePath' => '/',
122 'pathType' => 'relative'
123 );
124 } elseif (count($recordData) === 0 || $recordData['uid'] !== $uid) {
125 /** @var $storageRepository StorageRepository */
126 $storageRepository = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\StorageRepository');
127 /** @var $storage ResourceStorage */
128 $storageObject = $storageRepository->findByUid($uid);
129 }
130 if (!$storageObject instanceof ResourceStorage) {
131 $storageObject = $this->createStorageObject($recordData, $storageConfiguration);
132 }
133 $this->storageInstances[$uid] = $storageObject;
134 }
135 return $this->storageInstances[$uid];
136 }
137
138 /**
139 * Converts a flexform data string to a flat array with key value pairs
140 *
141 * @param string $flexFormData
142 * @return array Array with key => value pairs of the field data in the FlexForm
143 */
144 public function convertFlexFormDataToConfigurationArray($flexFormData) {
145 $configuration = array();
146 if ($flexFormData) {
147 $flexFormContents = \TYPO3\CMS\Core\Utility\GeneralUtility::xml2array($flexFormData);
148 if (!empty($flexFormContents['data']['sDEF']['lDEF']) && is_array($flexFormContents['data']['sDEF']['lDEF'])) {
149 foreach ($flexFormContents['data']['sDEF']['lDEF'] as $key => $value) {
150 if (isset($value['vDEF'])) {
151 $configuration[$key] = $value['vDEF'];
152 }
153 }
154 }
155 }
156 return $configuration;
157 }
158
159 /**
160 * Creates an instance of the collection from given UID. The $recordData can be supplied to increase performance.
161 *
162 * @param integer $uid The uid of the collection to instantiate.
163 * @param array $recordData The record row from database.
164 *
165 * @throws \InvalidArgumentException
166 * @return Collection\AbstractFileCollection
167 */
168 public function getCollectionObject($uid, array $recordData = array()) {
169 if (!is_numeric($uid)) {
170 throw new \InvalidArgumentException('uid of collection has to be numeric.', 1314085999);
171 }
172 if (!$this->collectionInstances[$uid]) {
173 // Get mount data if not already supplied as argument to this function
174 if (count($recordData) === 0 || $recordData['uid'] !== $uid) {
175 /** @var $GLOBALS['TYPO3_DB'] \TYPO3\CMS\Core\Database\DatabaseConnection */
176 $recordData = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'sys_file_collection', 'uid=' . intval($uid) . ' AND deleted=0');
177 if (!is_array($recordData)) {
178 throw new \InvalidArgumentException('No collection found for given UID.', 1314085992);
179 }
180 }
181 $collectionObject = $this->createCollectionObject($recordData);
182 $this->collectionInstances[$uid] = $collectionObject;
183 }
184 return $this->collectionInstances[$uid];
185 }
186
187 /**
188 * Creates a collection object.
189 *
190 * @param array $collectionData The database row of the sys_file_collection record.
191 * @return Collection\AbstractFileCollection
192 */
193 public function createCollectionObject(array $collectionData) {
194 switch ($collectionData['type']) {
195 case 'static':
196 $collection = Collection\StaticFileCollection::create($collectionData);
197 break;
198 case 'folder':
199 $collection = Collection\FolderBasedFileCollection::create($collectionData);
200 break;
201 default:
202 $collection = NULL;
203 }
204 return $collection;
205 }
206
207 /**
208 * Creates a storage object from a storage database row.
209 *
210 * @param array $storageRecord
211 * @param array $storageConfiguration Storage configuration (if given, this won't be extracted from the FlexForm value but the supplied array used instead)
212 * @return ResourceStorage
213 */
214 public function createStorageObject(array $storageRecord, array $storageConfiguration = NULL) {
215 $className = 'TYPO3\\CMS\\Core\\Resource\\ResourceStorage';
216 if (!$storageConfiguration) {
217 $storageConfiguration = $this->convertFlexFormDataToConfigurationArray($storageRecord['configuration']);
218 }
219 $driverType = $storageRecord['driver'];
220 $driverObject = $this->getDriverObject($driverType, $storageConfiguration);
221 /** @var $storage ResourceStorage */
222 $storage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($className, $driverObject, $storageRecord);
223 // TODO handle publisher
224 return $storage;
225 }
226
227 /**
228 * Creates a folder to directly access (a part of) a storage.
229 *
230 * @param ResourceStorage $storage The storage the folder belongs to
231 * @param string $identifier The path to the folder. Might also be a simple unique string, depending on the storage driver.
232 * @param string $name The name of the folder (e.g. the folder name)
233 * @return Folder
234 */
235 public function createFolderObject(ResourceStorage $storage, $identifier, $name) {
236 return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\Folder', $storage, $identifier, $name);
237 }
238
239 protected function createPublisherFromConfiguration(array $configuration) {
240 $publishingTarget = $this->getStorageObject($configuration['publisherConfiguration']['publishingTarget']);
241 $publisher = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance($configuration['publisher'], $publishingTarget, $configuration['publisherConfiguration']);
242 return $publisher;
243 }
244
245 /**
246 * Creates an instance of the file given UID. The $fileData can be supplied
247 * to increase performance.
248 *
249 * @param integer $uid The uid of the file to instantiate.
250 * @param array $fileData The record row from database.
251 *
252 * @throws \InvalidArgumentException
253 * @throws \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
254 * @return File
255 */
256 public function getFileObject($uid, array $fileData = array()) {
257 if (!is_numeric($uid)) {
258 throw new \InvalidArgumentException('uid of file has to be numeric.', 1300096564);
259 }
260 if (!$this->fileInstances[$uid]) {
261 // Fetches data in case $fileData is empty
262 if (empty($fileData)) {
263 /** @var $GLOBALS['TYPO3_DB'] \TYPO3\CMS\Core\Database\DatabaseConnection */
264 $fileData = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'sys_file', 'uid=' . intval($uid) . ' AND deleted=0');
265 if (!is_array($fileData)) {
266 throw new \TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException('No file found for given UID.', 1317178604);
267 }
268 }
269 $this->fileInstances[$uid] = $this->createFileObject($fileData);
270 }
271 return $this->fileInstances[$uid];
272 }
273
274 /**
275 * Gets an file object from an identifier [storage]:[fileId]
276 *
277 * @param string $identifier
278 * @return File
279 */
280 public function getFileObjectFromCombinedIdentifier($identifier) {
281 $parts = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(':', $identifier);
282 if (count($parts) === 2) {
283 $storageUid = $parts[0];
284 $fileIdentifier = $parts[1];
285 } else {
286 // We only got a path: Go into backwards compatibility mode and
287 // use virtual Storage (uid=0)
288 $storageUid = 0;
289 $fileIdentifier = $parts[0];
290 }
291 return $this->getStorageObject($storageUid)->getFile($fileIdentifier);
292 }
293
294 /**
295 * Bulk function, can be used for anything to get a file or folder
296 *
297 * 1. It's a UID
298 * 2. It's a combined identifier
299 * 3. It's just a path/filename (coming from the oldstyle/backwards compatibility)
300 *
301 * Files, previously laid on fileadmin/ or something, will be "mapped" to the storage the file is
302 * in now. Files like typo3temp/ or typo3conf/ will be moved to the first writable storage
303 * in its processing folder
304 *
305 * $input could be
306 * - "2:myfolder/myfile.jpg" (combined identifier)
307 * - "23" (file UID)
308 * - "uploads/myfile.png" (backwards-compatibility, storage "0")
309 * - "file:23"
310 *
311 * @param string $input
312 * @return FileInterface|Folder
313 */
314 public function retrieveFileOrFolderObject($input) {
315 // Easy function to deal with that, could be dropped in the future
316 // if we know where to use this function
317 if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($input, 'file:')) {
318 $input = substr($input, 5);
319 return $this->retrieveFileOrFolderObject($input);
320 } elseif (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($input)) {
321 return $this->getFileObject($input);
322 } elseif (strpos($input, ':') > 0) {
323 list($prefix, $folderIdentifier) = explode(':', $input);
324 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($prefix)) {
325 // path or folder in a valid storageUID
326 return $this->getObjectFromCombinedIdentifier($input);
327 } elseif ($prefix == 'EXT') {
328 $input = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($input);
329 $input = PathUtility::getRelativePath(PATH_site, PathUtility::dirname($input)) . PathUtility::basename($input);
330 return $this->getFileObjectFromCombinedIdentifier($input);
331 }
332 // this is a backwards-compatible way to access "0-storage" files or folders
333 } elseif (@is_file(PATH_site . $input)) {
334 // only the local file
335 return $this->getFileObjectFromCombinedIdentifier($input);
336 } else {
337 // only the local path
338 return $this->getFolderObjectFromCombinedIdentifier($input);
339 }
340 }
341
342 /**
343 * Gets a folder object from an identifier [storage]:[fileId]
344 *
345 * @TODO check naming, inserted by SteffenR while working on filelist
346 * @param string $identifier
347 * @return Folder
348 */
349 public function getFolderObjectFromCombinedIdentifier($identifier) {
350 $parts = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(':', $identifier);
351 if (count($parts) === 2) {
352 $storageUid = $parts[0];
353 $folderIdentifier = $parts[1];
354 } else {
355 // We only got a path: Go into backwards compatibility mode and
356 // use virtual Storage (uid=0)
357 $storageUid = 0;
358 $folderIdentifier = $parts[0];
359 // make sure to not use an absolute path, and remove PATH_site if it is prepended
360 if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($folderIdentifier, PATH_site)) {
361 $folderIdentifier = substr($parts[0], strlen(PATH_site));
362 }
363 }
364 return $this->getStorageObject($storageUid)->getFolder($folderIdentifier);
365 }
366
367 /**
368 * Gets a storage object from a combined identifier
369 *
370 * @param string $identifier An identifier of the form [storage uid]:[object identifier]
371 * @return ResourceStorage
372 */
373 public function getStorageObjectFromCombinedIdentifier($identifier) {
374 $parts = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(':', $identifier);
375 if (count($parts) === 2) {
376 $storageUid = $parts[0];
377 }
378
379 return $this->getStorageObject($storageUid);
380 }
381
382 /**
383 * Gets a file or folder object.
384 *
385 * @param string $identifier
386 *
387 * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
388 * @return FileInterface|Folder
389 */
390 public function getObjectFromCombinedIdentifier($identifier) {
391 list($storageId, $objectIdentifier) = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(':', $identifier);
392 $storage = $this->getStorageObject($storageId);
393 if ($storage->hasFile($objectIdentifier)) {
394 return $storage->getFile($objectIdentifier);
395 } elseif ($storage->hasFolder($objectIdentifier)) {
396 return $storage->getFolder($objectIdentifier);
397 } else {
398 throw new \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException('Object with identifier "' . $identifier . '" does not exist in storage', 1329647780);
399 }
400 }
401
402 /**
403 * Creates a file object from an array of file data. Requires a database
404 * row to be fetched.
405 *
406 * @param array $fileData
407 * @return File
408 */
409 public function createFileObject(array $fileData) {
410 /** @var File $fileObject */
411 $fileObject = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\File', $fileData);
412 if (is_numeric($fileData['storage'])) {
413 $storageObject = $this->getStorageObject($fileData['storage']);
414 $fileObject->setStorage($storageObject);
415 }
416 return $fileObject;
417 }
418
419 /**
420 * Creates an instance of a FileReference object. The $fileReferenceData can
421 * be supplied to increase performance.
422 *
423 * @param integer $uid The uid of the file usage (sys_file_reference) to instantiate.
424 * @param array $fileReferenceData The record row from database.
425 *
426 * @throws \InvalidArgumentException
427 * @return FileReference
428 */
429 public function getFileReferenceObject($uid, array $fileReferenceData = array()) {
430 if (!is_numeric($uid)) {
431 throw new \InvalidArgumentException('uid of fileusage (sys_file_reference) has to be numeric.', 1300086584);
432 }
433 if (!$this->fileReferenceInstances[$uid]) {
434 // Fetches data in case $fileData is empty
435 if (empty($fileReferenceData)) {
436 // fetch the reference record of the current workspace
437 if (TYPO3_MODE === 'BE') {
438 $fileReferenceData = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordWSOL('sys_file_reference', $uid);
439 } elseif (is_object($GLOBALS['TSFE'])) {
440 $fileReferenceData = $GLOBALS['TSFE']->sys_page->checkRecord('sys_file_reference', $uid);
441 } else {
442 /** @var $GLOBALS['TYPO3_DB'] \TYPO3\CMS\Core\Database\DatabaseConnection */
443 $fileReferenceData = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 'sys_file_reference', 'uid=' . intval($uid) . ' AND deleted=0');
444 }
445 if (!is_array($fileReferenceData)) {
446 throw new \InvalidArgumentException('No fileusage (sys_file_reference) found for given UID.', 1317178794);
447 }
448 }
449 $this->fileReferenceInstances[$uid] = $this->createFileReferenceObject($fileReferenceData);
450 }
451 return $this->fileReferenceInstances[$uid];
452 }
453
454 /**
455 * Creates a file usage object from an array of fileReference data
456 * from sys_file_reference table.
457 * Requires a database row to be already fetched and present.
458 *
459 * @param array $fileReferenceData
460 * @return FileReference
461 */
462 public function createFileReferenceObject(array $fileReferenceData) {
463 /** @var FileReference $fileReferenceObject */
464 $fileReferenceObject = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\FileReference', $fileReferenceData);
465 return $fileReferenceObject;
466 }
467 }
468
469
470 ?>