Newer
Older
<?php
namespace SGalinski\SgRest\Service;
/***************************************************************
* Copyright notice
*
* (c) sgalinski Internet Services (http://www.sgalinski.de)
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use SGalinski\SgRest\Utility\PathUtility;
use TYPO3\CMS\Core\Resource\AbstractFile;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
use TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter;
/**
* The DataResolverService converts the data representations of the REST requests. For instance from objects to array
* or the way around.
*/
class DataResolveService implements SingletonInterface {
/**
* @inject
* @var \TYPO3\CMS\Extbase\Object\ObjectManager
*/
protected $objectManager;
/**
* @inject
* @var \SGalinski\SgRest\Service\RegistrationService
*/
protected $registrationService;
/**
* @var bool
*/
protected $resolveResourceLinks = FALSE;
/**
* @var int
*/
protected $resolveNestingLevel = 1;
/**
* @var string
*/
protected $apiKey = '';
/**
* @var string
*/
protected $dateFormat = DateTimeConverter::DEFAULT_DATE_FORMAT;
/**
* @var array
*/
protected $objectCache = [];
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
* Setter for the nesting level property.
*
* @param int $resolveNestingLevel
* @return void
*/
public function setResolveNestingLevel($resolveNestingLevel) {
$this->resolveNestingLevel = $resolveNestingLevel;
}
/**
* Setter for the resolve resource links flag.
*
* @param boolean $resolveResourceLinks
* @return void
*/
public function setResolveResourceLinks($resolveResourceLinks) {
$this->resolveResourceLinks = $resolveResourceLinks;
}
/**
* Setter for the apiKey.
*
* @param string $apiKey
* @return void
*/
public function setApiKey($apiKey) {
$this->apiKey = $apiKey;
}
/**
* Setter for the date format.
*
* @param string $format
* @return void
*/
public function setDateFormat($format) {
$this->dateFormat = $format;
}
/**
* Method converts a given object to an array.
*
* @param AbstractEntity $object
* @param bool $returnReference
* @param int $nestingLevel
* @return array
*/
public function getArrayFromObject(AbstractEntity $object, $returnReference = FALSE, $nestingLevel = -1) {
$entityName = $this->getEntityName($object);
$identifier = (method_exists($object, 'getUid') ? $object->getUid() : 0);
$objectCacheKey = $entityName . '-' . $identifier . '-' . ($returnReference ? 1 : 0);
if (isset($this->objectCache[$objectCacheKey])) {
return $this->objectCache[$objectCacheKey];
}
$properties = $object->_getCleanProperties();
if ($object instanceof FileReference && $identifier) {
return [
'href' => PathUtility::createFileUrl($object),
'uid' => $identifier,
'metadata' => $this->getFileMetaData($object)
];
}
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
if ($returnReference && $identifier) {
return [
'href' => PathUtility::createRestEntityUrl($this->apiKey, $entityName, $identifier),
'uid' => $identifier
];
}
// Initialize nesting level
if ($this->resolveResourceLinks && $nestingLevel === -1) {
$nestingLevel = $this->resolveNestingLevel;
}
$allowedProperties = [];
if ($this->apiKey !== '' && $this->registrationService->isApiKeyAvailable($this->apiKey)) {
$accessGroups = $this->registrationService->getAccessGroups();
$allowedProperties = $accessGroups[$this->apiKey]['entities'][$entityName]['read'];
}
$properties = array_intersect_key($properties, array_flip($allowedProperties));
foreach ($properties as $propertyName => $propertyValues) {
if (!is_object($propertyValues)) {
continue;
}
if ($propertyValues instanceof \DateTime) {
/** @var \DateTime $date */
$date = $propertyValues;
$properties[$propertyName] = $date->format($this->dateFormat);
} else {
$properties[$propertyName] = $this->convertSubPropertyToArray($object, $propertyName, $nestingLevel);
}
}
$this->objectCache[$objectCacheKey] = $properties;
return $properties;
}
/**
* Method returns an array with relations to the sub property as rest urls.
*
* @param AbstractEntity $parentObject
* @param string $subProperty
* @param int $nestingLevel
* @throws \Exception
* @return array
*/
protected function convertSubPropertyToArray(AbstractEntity $parentObject, $subProperty, $nestingLevel) {
$result = [];
$getMethod = 'get' . ucfirst($subProperty);
if (!method_exists($parentObject, $getMethod)) {
$message = 'The sub property ' . $subProperty . ' of the object you requested could not be found.';
throw new \Exception($message, 404);
}
$subObject = $parentObject->{$getMethod}();
$returnReference = ($this->resolveResourceLinks && $nestingLevel >= 1);
if ($subObject instanceof LazyLoadingProxy) {
$subObject->_loadRealInstance();
}
if ($subObject instanceof LazyObjectStorage) {
$subObject->toArray();
}
if ($subObject instanceof ObjectStorage || $subObject instanceof \Traversable) {
// toArray() is necessary, because an ObjectStorage is losing it's index, if the child object references to
// the same storage. So it can happen, that not all objects are iterated.
foreach ($subObject->toArray() as $item) {
$result[] = $this->getArrayFromObject($item, !$returnReference, $nestingLevel);
}
} else {
$entityName = (($subObject instanceof AbstractEntity) ? $this->getEntityName($subObject) : '');
if (!$this->registrationService->isEntityAvailable($entityName, $this->apiKey)) {
$message = 'You are not allowed to access the sub property: ' . $subProperty;
throw new \Exception($message, 403);
}
if ($subObject instanceof AbstractEntity && $returnReference) {
$nestingLevel = ($nestingLevel > 0 ? $nestingLevel - 1 : 0);
$result = $this->getArrayFromObject($subObject, FALSE, $nestingLevel);
}
$identifier = (method_exists($subObject, 'getUid') ? $subObject->getUid() : 0);
if ($subObject instanceof FileReference && $identifier) {
$result['href'] = PathUtility::createFileUrl($subObject);
} else {
$result['href'] = PathUtility::createRestEntityUrl($this->apiKey, $entityName, $identifier);
}
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
$result['uid'] = $identifier;
}
return $result;
}
/**
* Returns lowercase representation of the entity name.
*
* @param AbstractEntity $object
* @return string
*/
public function getEntityName(AbstractEntity $object) {
return $this->getEntityNameByClassName(get_class($object));
}
/**
* Returns lowercase representation of the entity name.
*
* @param string $className
* @return string
*/
public function getEntityNameByClassName($className) {
$classPath = GeneralUtility::trimExplode('\\', $className);
return lcfirst(array_pop($classPath));
}
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/**
* Returns an array with meta data of the file.
* - fileSize
* - mime type
* - name
*
* And for images also:
* - height
* - width
* - aspect ratio
*
* @param FileReference $fileReference
* @return array
*/
protected function getFileMetaData(FileReference $fileReference) {
$originalResource = $fileReference->getOriginalResource();
if (!$originalResource) {
return [];
}
/** @var File $originalFile */
$originalFile = $originalResource->getOriginalFile();
if (!$originalFile) {
return [];
}
$metadata = [
'fileSize' => $originalFile->getSize(),
'mimeType' => $originalFile->getMimeType(),
'name' => $originalFile->getName(),
];
if ($originalFile->getType() === AbstractFile::FILETYPE_IMAGE) {
$height = $originalFile->getProperty('height');
$width = $originalFile->getProperty('width');
$metadata['height'] = $height;
$metadata['width'] = $width;
$metadata['aspectRatio'] = $height / $width;
}
return $metadata;
}