1 | <?php |
||||
2 | /** |
||||
3 | * Nextcloud - Gallery |
||||
4 | * |
||||
5 | * This file is licensed under the Affero General Public License version 3 or |
||||
6 | * later. See the COPYING file. |
||||
7 | * |
||||
8 | * @author Olivier Paroz <[email protected]> |
||||
9 | * |
||||
10 | * @copyright Olivier Paroz 2017 |
||||
11 | */ |
||||
12 | |||||
13 | namespace OCA\Gallery\Service; |
||||
14 | |||||
15 | use OCP\Files\Folder; |
||||
0 ignored issues
–
show
|
|||||
16 | use OCP\IPreview; |
||||
0 ignored issues
–
show
The type
OCP\IPreview was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||
17 | use OCP\ILogger; |
||||
0 ignored issues
–
show
The type
OCP\ILogger was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||
18 | |||||
19 | use OCA\Gallery\Config\ConfigParser; |
||||
20 | use OCA\Gallery\Config\ConfigException; |
||||
21 | use OCA\Gallery\Environment\Environment; |
||||
22 | |||||
23 | /** |
||||
24 | * Finds configurations files and returns a configuration array |
||||
25 | * |
||||
26 | * Checks the current and parent folders for configuration files and to see if we're allowed to |
||||
27 | * look for media file |
||||
28 | * Supports explicit inheritance |
||||
29 | * |
||||
30 | * @package OCA\Gallery\Service |
||||
31 | */ |
||||
32 | class ConfigService extends FilesService { |
||||
33 | |||||
34 | /** @var string */ |
||||
35 | private $configName = 'gallery.cnf'; |
||||
36 | /** @var array <string,bool> */ |
||||
37 | private $completionStatus = ['design' => false, 'information' => false, 'sorting' => false]; |
||||
38 | /** @var ConfigParser */ |
||||
39 | private $configParser; |
||||
40 | /** @var IPreview */ |
||||
41 | private $previewManager; |
||||
42 | /** |
||||
43 | * @todo This hard-coded array could be replaced by admin settings |
||||
44 | * |
||||
45 | * @var string[] |
||||
46 | */ |
||||
47 | private $baseMimeTypes = [ |
||||
48 | 'image/png', |
||||
49 | 'image/jpeg', |
||||
50 | 'image/gif', |
||||
51 | 'image/x-xbitmap', |
||||
52 | 'image/bmp', |
||||
53 | 'image/tiff', |
||||
54 | 'image/x-dcraw', |
||||
55 | 'image/heic', |
||||
56 | 'image/heif', |
||||
57 | 'application/x-photoshop', |
||||
58 | 'application/illustrator', |
||||
59 | 'application/postscript', |
||||
60 | ]; |
||||
61 | /** |
||||
62 | * These types are useful for files preview in the files app, but |
||||
63 | * not for the gallery side |
||||
64 | * |
||||
65 | * @var string[] |
||||
66 | */ |
||||
67 | private $slideshowMimeTypes = [ |
||||
68 | 'application/font-sfnt', |
||||
69 | 'application/x-font', |
||||
70 | ]; |
||||
71 | |||||
72 | /** |
||||
73 | * Constructor |
||||
74 | * |
||||
75 | * @param string $appName |
||||
76 | * @param Environment $environment |
||||
77 | * @param ConfigParser $configParser |
||||
78 | * @param IPreview $previewManager |
||||
79 | * @param ILogger $logger |
||||
80 | */ |
||||
81 | public function __construct( |
||||
82 | $appName, |
||||
83 | Environment $environment, |
||||
84 | ConfigParser $configParser, |
||||
85 | IPreview $previewManager, |
||||
86 | ILogger $logger |
||||
87 | ) { |
||||
88 | parent::__construct($appName, $environment, $logger); |
||||
89 | |||||
90 | $this->configParser = $configParser; |
||||
91 | $this->previewManager = $previewManager; |
||||
92 | } |
||||
93 | |||||
94 | /** |
||||
95 | * Returns a list of supported features |
||||
96 | * |
||||
97 | * @return string[] |
||||
98 | */ |
||||
99 | public function getFeaturesList() { |
||||
100 | $featuresList = []; |
||||
101 | /** @var Folder $rootFolder */ |
||||
102 | $rootFolder = $this->environment->getVirtualRootFolder(); |
||||
103 | if ($this->isAllowedAndAvailable($rootFolder) && $this->configExists($rootFolder)) { |
||||
104 | try { |
||||
105 | $featuresList = |
||||
106 | $this->configParser->getFeaturesList($rootFolder, $this->configName); |
||||
107 | } catch (ConfigException $exception) { |
||||
108 | $featuresList = $this->buildErrorMessage($exception, $rootFolder); |
||||
109 | } |
||||
110 | } |
||||
111 | |||||
112 | return $featuresList; |
||||
113 | } |
||||
114 | |||||
115 | /** |
||||
116 | * This builds and returns a list of all supported media types |
||||
117 | * |
||||
118 | * @todo Native SVG could be disabled via admin settings |
||||
119 | * |
||||
120 | * @param bool $extraMediaTypes |
||||
121 | * @param bool $nativeSvgSupport |
||||
122 | * |
||||
123 | * @return string[] all supported media types |
||||
124 | */ |
||||
125 | public function getSupportedMediaTypes($extraMediaTypes, $nativeSvgSupport) { |
||||
126 | $supportedMimes = []; |
||||
127 | $wantedMimes = $this->baseMimeTypes; |
||||
128 | if ($extraMediaTypes) { |
||||
129 | $wantedMimes = array_merge($wantedMimes, $this->slideshowMimeTypes); |
||||
130 | } |
||||
131 | foreach ($wantedMimes as $wantedMime) { |
||||
132 | // Let's see if a preview of files of that media type can be generated |
||||
133 | if ($this->isMimeSupported($wantedMime)) { |
||||
134 | // We store the media type |
||||
135 | $supportedMimes[] = $wantedMime; |
||||
136 | } |
||||
137 | } |
||||
138 | $supportedMimes = $this->addSvgSupport($supportedMimes, $nativeSvgSupport); |
||||
139 | |||||
140 | //$this->logger->debug("Supported Mimes: {mimes}", ['mimes' => $supportedMimes]); |
||||
141 | |||||
142 | return $supportedMimes; |
||||
143 | } |
||||
144 | |||||
145 | /** |
||||
146 | * Returns the configuration of the currently selected folder |
||||
147 | * |
||||
148 | * * information (description, copyright) |
||||
149 | * * sorting (date, name, inheritance) |
||||
150 | * * design (colour) |
||||
151 | * * if the album should be ignored |
||||
152 | * |
||||
153 | * @param Folder $folderNode the current folder |
||||
154 | * @param array $features the list of features retrieved fro the configuration file |
||||
155 | * |
||||
156 | * @return array|null |
||||
157 | * @throws ForbiddenServiceException |
||||
158 | */ |
||||
159 | public function getConfig($folderNode, $features) { |
||||
160 | $this->features = $features; |
||||
161 | list ($albumConfig, $ignored) = |
||||
162 | $this->collectConfig($folderNode, $this->ignoreAlbumStrings, $this->configName); |
||||
163 | if ($ignored) { |
||||
164 | throw new ForbiddenServiceException( |
||||
165 | 'The owner has placed a restriction or the storage location is unavailable' |
||||
166 | ); |
||||
167 | } |
||||
168 | |||||
169 | return $albumConfig; |
||||
170 | } |
||||
171 | |||||
172 | /** |
||||
173 | * Throws an exception if the media type of the file is not part of what the app allows |
||||
174 | * |
||||
175 | * @param $mimeType |
||||
176 | * |
||||
177 | * @throws ForbiddenServiceException |
||||
178 | */ |
||||
179 | public function validateMimeType($mimeType) { |
||||
180 | if (!in_array($mimeType, $this->getSupportedMediaTypes(true, true))) { |
||||
181 | throw new ForbiddenServiceException('Media type not allowed'); |
||||
182 | } |
||||
183 | } |
||||
184 | |||||
185 | /** |
||||
186 | * Determines if we have a configuration file to work with |
||||
187 | * |
||||
188 | * @param Folder $rootFolder the virtual root folder |
||||
189 | * |
||||
190 | * @return bool |
||||
191 | */ |
||||
192 | private function configExists($rootFolder) { |
||||
193 | return $rootFolder && $rootFolder->nodeExists($this->configName); |
||||
194 | } |
||||
195 | |||||
196 | /** |
||||
197 | * Adds the SVG media type if it's not already there |
||||
198 | * |
||||
199 | * If it's enabled, but doesn't work, an exception will be raised when trying to generate a |
||||
200 | * preview. If it's disabled, we support it via the browser's native support |
||||
201 | * |
||||
202 | * @param string[] $supportedMimes |
||||
203 | * @param bool $nativeSvgSupport |
||||
204 | * |
||||
205 | * @return string[] |
||||
206 | */ |
||||
207 | private function addSvgSupport($supportedMimes, $nativeSvgSupport) { |
||||
208 | if (!in_array('image/svg+xml', $supportedMimes) && $nativeSvgSupport) { |
||||
209 | $supportedMimes[] = 'image/svg+xml'; |
||||
210 | } |
||||
211 | |||||
212 | return $supportedMimes; |
||||
213 | } |
||||
214 | |||||
215 | /** |
||||
216 | * Returns true if the passed mime type is supported |
||||
217 | * |
||||
218 | * In case of a failure, we just return that the media type is not supported |
||||
219 | * |
||||
220 | * @param string $mimeType |
||||
221 | * |
||||
222 | * @return boolean |
||||
223 | */ |
||||
224 | private function isMimeSupported($mimeType = '*') { |
||||
225 | try { |
||||
226 | return $this->previewManager->isMimeSupported($mimeType); |
||||
227 | } catch (\Exception $exception) { |
||||
228 | unset($exception); |
||||
229 | |||||
230 | return false; |
||||
231 | } |
||||
232 | } |
||||
233 | |||||
234 | /** |
||||
235 | * Returns an album configuration array |
||||
236 | * |
||||
237 | * Goes through all the parent folders until either we're told the album is private or we've |
||||
238 | * reached the root folder |
||||
239 | * |
||||
240 | * @param Folder $folder the current folder |
||||
241 | * @param array $ignoreAlbumStrings names of the files which blacklist folders |
||||
242 | * @param string $configName name of the configuration file |
||||
243 | * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder |
||||
244 | * @param array $configSoFar the configuration collected so far |
||||
245 | * |
||||
246 | * @return array <null|array,bool> |
||||
247 | */ |
||||
248 | private function collectConfig( |
||||
249 | $folder, $ignoreAlbumStrings, $configName, $level = 0, $configSoFar = [] |
||||
250 | ) { |
||||
251 | foreach ($ignoreAlbumStrings as $ignoreAlbum) { |
||||
252 | if ($folder->nodeExists($ignoreAlbum)) { |
||||
253 | // Cancel as soon as we find out that the folder is private or external |
||||
254 | return [null, true]; |
||||
255 | } |
||||
256 | } |
||||
257 | $isRootFolder = $this->isRootFolder($folder, $level); |
||||
258 | if ($folder->nodeExists($configName)) { |
||||
259 | $configSoFar = $this->buildFolderConfig($folder, $configName, $configSoFar, $level); |
||||
260 | } |
||||
261 | if (!$isRootFolder) { |
||||
262 | return $this->getParentConfig($folder, $ignoreAlbumStrings, $configName, $level, $configSoFar); |
||||
0 ignored issues
–
show
$ignoreAlbumStrings of type array is incompatible with the type string expected by parameter $privacyChecker of OCA\Gallery\Service\Conf...vice::getParentConfig() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
263 | } |
||||
264 | $configSoFar = $this->validatesInfoConfig($configSoFar); |
||||
265 | |||||
266 | // We have reached the root folder |
||||
267 | return [$configSoFar, false]; |
||||
268 | } |
||||
269 | |||||
270 | /** |
||||
271 | * Returns a parsed configuration if one was found in the current folder or generates an error |
||||
272 | * message to send back |
||||
273 | * |
||||
274 | * @param Folder $folder the current folder |
||||
275 | * @param string $configName name of the configuration file |
||||
276 | * @param array $collectedConfig the configuration collected so far |
||||
277 | * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder |
||||
278 | * |
||||
279 | * @return array |
||||
280 | */ |
||||
281 | private function buildFolderConfig($folder, $configName, $collectedConfig, $level) { |
||||
282 | try { |
||||
283 | list($collectedConfig, $completionStatus) = $this->configParser->getFolderConfig( |
||||
284 | $folder, $configName, $collectedConfig, $this->completionStatus, $level |
||||
285 | ); |
||||
286 | $this->completionStatus = $completionStatus; |
||||
287 | } catch (ConfigException $exception) { |
||||
288 | $collectedConfig = $this->buildErrorMessage($exception, $folder); |
||||
289 | } |
||||
290 | |||||
291 | return $collectedConfig; |
||||
292 | } |
||||
293 | |||||
294 | /** |
||||
295 | * Builds the error message to send back when there is an error |
||||
296 | * |
||||
297 | * @fixme Missing translation |
||||
298 | * |
||||
299 | * @param ConfigException $exception |
||||
300 | * @param Folder $folder the current folder |
||||
301 | * |
||||
302 | * @return array<array<string,string>,bool> |
||||
303 | */ |
||||
304 | private function buildErrorMessage($exception, $folder) { |
||||
305 | $configPath = $this->environment->getPathFromVirtualRoot($folder); |
||||
306 | $errorMessage = $exception->getMessage() . ". Config location: /$configPath"; |
||||
307 | $this->logger->error($errorMessage); |
||||
308 | $config = ['error' => ['message' => $errorMessage]]; |
||||
309 | |||||
310 | $completionStatus = $this->completionStatus; |
||||
311 | foreach ($completionStatus as $key) { |
||||
312 | $completionStatus[$key] = true; |
||||
313 | } |
||||
314 | $this->completionStatus = $completionStatus; |
||||
315 | |||||
316 | return [$config]; |
||||
317 | } |
||||
318 | |||||
319 | /** |
||||
320 | * Removes links if they were collected outside of the virtual root |
||||
321 | * |
||||
322 | * This is for shared folders which have a virtual root |
||||
323 | * |
||||
324 | * @param array $albumConfig |
||||
325 | * |
||||
326 | * @return array |
||||
327 | */ |
||||
328 | private function validatesInfoConfig($albumConfig) { |
||||
329 | $this->virtualRootLevel; |
||||
330 | if (array_key_exists('information', $albumConfig)) { |
||||
331 | $info = $albumConfig['information']; |
||||
332 | if (array_key_exists('level', $info)) { |
||||
333 | $level = $info['level']; |
||||
334 | if ($level > $this->virtualRootLevel) { |
||||
335 | $albumConfig['information']['description_link'] = null; |
||||
336 | $albumConfig['information']['copyright_link'] = null; |
||||
337 | } |
||||
338 | } |
||||
339 | } |
||||
340 | |||||
341 | return $albumConfig; |
||||
342 | } |
||||
343 | |||||
344 | /** |
||||
345 | * Looks for an album configuration in the parent folder |
||||
346 | * |
||||
347 | * We will look up to the virtual root of a shared folder, for privacy reasons |
||||
348 | * |
||||
349 | * @param Folder $folder the current folder |
||||
350 | * @param string $privacyChecker names of the files which blacklist folders |
||||
351 | * @param string $configName name of the configuration file |
||||
352 | * @param int $level the starting level is 0 and we add 1 each time we visit a parent folder |
||||
353 | * @param array $collectedConfig the configuration collected so far |
||||
354 | * |
||||
355 | * @return array<null|array,bool> |
||||
356 | */ |
||||
357 | private function getParentConfig($folder, $privacyChecker, $configName, $level, $collectedConfig |
||||
358 | ) { |
||||
359 | $parentFolder = $folder->getParent(); |
||||
360 | $level++; |
||||
361 | |||||
362 | return $this->collectConfig( |
||||
363 | $parentFolder, $privacyChecker, $configName, $level, $collectedConfig |
||||
0 ignored issues
–
show
$privacyChecker of type string is incompatible with the type array expected by parameter $ignoreAlbumStrings of OCA\Gallery\Service\ConfigService::collectConfig() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
364 | ); |
||||
365 | } |
||||
366 | |||||
367 | } |
||||
368 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths