@@ -75,7 +75,7 @@ discard block |
||
75 | 75 | try { |
76 | 76 | $client = $this->clientService->newClient(); |
77 | 77 | $response = $client->get( |
78 | - $lookupServerUrl . '/users?search=' . urlencode($search), |
|
78 | + $lookupServerUrl.'/users?search='.urlencode($search), |
|
79 | 79 | [ |
80 | 80 | 'timeout' => 10, |
81 | 81 | 'connect_timeout' => 3, |
@@ -88,7 +88,7 @@ discard block |
||
88 | 88 | try { |
89 | 89 | $remote = $this->cloudIdManager->resolveCloudId($lookup['federationId'])->getRemote(); |
90 | 90 | } catch (\Exception $e) { |
91 | - $this->logger->error('Can not parse federated cloud ID "' . $lookup['federationId'] . '"'); |
|
91 | + $this->logger->error('Can not parse federated cloud ID "'.$lookup['federationId'].'"'); |
|
92 | 92 | $this->logger->error($e->getMessage()); |
93 | 93 | continue; |
94 | 94 | } |
@@ -96,7 +96,7 @@ discard block |
||
96 | 96 | continue; |
97 | 97 | } |
98 | 98 | $name = isset($lookup['name']['value']) ? $lookup['name']['value'] : ''; |
99 | - $label = empty($name) ? $lookup['federationId'] : $name . ' (' . $lookup['federationId'] . ')'; |
|
99 | + $label = empty($name) ? $lookup['federationId'] : $name.' ('.$lookup['federationId'].')'; |
|
100 | 100 | $result[] = [ |
101 | 101 | 'label' => $label, |
102 | 102 | 'value' => [ |
@@ -39,87 +39,87 @@ |
||
39 | 39 | |
40 | 40 | class LookupPlugin implements ISearchPlugin { |
41 | 41 | |
42 | - /** @var IConfig */ |
|
43 | - private $config; |
|
44 | - /** @var IClientService */ |
|
45 | - private $clientService; |
|
46 | - /** @var string remote part of the current user's cloud id */ |
|
47 | - private $currentUserRemote; |
|
48 | - /** @var ICloudIdManager */ |
|
49 | - private $cloudIdManager; |
|
50 | - /** @var ILogger */ |
|
51 | - private $logger; |
|
42 | + /** @var IConfig */ |
|
43 | + private $config; |
|
44 | + /** @var IClientService */ |
|
45 | + private $clientService; |
|
46 | + /** @var string remote part of the current user's cloud id */ |
|
47 | + private $currentUserRemote; |
|
48 | + /** @var ICloudIdManager */ |
|
49 | + private $cloudIdManager; |
|
50 | + /** @var ILogger */ |
|
51 | + private $logger; |
|
52 | 52 | |
53 | - public function __construct(IConfig $config, |
|
54 | - IClientService $clientService, |
|
55 | - IUserSession $userSession, |
|
56 | - ICloudIdManager $cloudIdManager, |
|
57 | - ILogger $logger) { |
|
58 | - $this->config = $config; |
|
59 | - $this->clientService = $clientService; |
|
60 | - $this->cloudIdManager = $cloudIdManager; |
|
61 | - $currentUserCloudId = $userSession->getUser()->getCloudId(); |
|
62 | - $this->currentUserRemote = $cloudIdManager->resolveCloudId($currentUserCloudId)->getRemote(); |
|
63 | - $this->logger = $logger; |
|
64 | - } |
|
53 | + public function __construct(IConfig $config, |
|
54 | + IClientService $clientService, |
|
55 | + IUserSession $userSession, |
|
56 | + ICloudIdManager $cloudIdManager, |
|
57 | + ILogger $logger) { |
|
58 | + $this->config = $config; |
|
59 | + $this->clientService = $clientService; |
|
60 | + $this->cloudIdManager = $cloudIdManager; |
|
61 | + $currentUserCloudId = $userSession->getUser()->getCloudId(); |
|
62 | + $this->currentUserRemote = $cloudIdManager->resolveCloudId($currentUserCloudId)->getRemote(); |
|
63 | + $this->logger = $logger; |
|
64 | + } |
|
65 | 65 | |
66 | - public function search($search, $limit, $offset, ISearchResult $searchResult) { |
|
67 | - $isGlobalScaleEnabled = $this->config->getSystemValue('gs.enabled', false); |
|
68 | - $isLookupServerEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes') === 'yes'; |
|
69 | - $hasInternetConnection = $this->config->getSystemValueBool('has_internet_connection', true); |
|
66 | + public function search($search, $limit, $offset, ISearchResult $searchResult) { |
|
67 | + $isGlobalScaleEnabled = $this->config->getSystemValue('gs.enabled', false); |
|
68 | + $isLookupServerEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes') === 'yes'; |
|
69 | + $hasInternetConnection = $this->config->getSystemValueBool('has_internet_connection', true); |
|
70 | 70 | |
71 | - // if case of Global Scale we always search the lookup server |
|
72 | - if (!$isGlobalScaleEnabled && (!$isLookupServerEnabled || !$hasInternetConnection)) { |
|
73 | - return false; |
|
74 | - } |
|
71 | + // if case of Global Scale we always search the lookup server |
|
72 | + if (!$isGlobalScaleEnabled && (!$isLookupServerEnabled || !$hasInternetConnection)) { |
|
73 | + return false; |
|
74 | + } |
|
75 | 75 | |
76 | - $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com'); |
|
77 | - if (empty($lookupServerUrl)) { |
|
78 | - return false; |
|
79 | - } |
|
80 | - $lookupServerUrl = rtrim($lookupServerUrl, '/'); |
|
81 | - $result = []; |
|
76 | + $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com'); |
|
77 | + if (empty($lookupServerUrl)) { |
|
78 | + return false; |
|
79 | + } |
|
80 | + $lookupServerUrl = rtrim($lookupServerUrl, '/'); |
|
81 | + $result = []; |
|
82 | 82 | |
83 | - try { |
|
84 | - $client = $this->clientService->newClient(); |
|
85 | - $response = $client->get( |
|
86 | - $lookupServerUrl . '/users?search=' . urlencode($search), |
|
87 | - [ |
|
88 | - 'timeout' => 10, |
|
89 | - 'connect_timeout' => 3, |
|
90 | - ] |
|
91 | - ); |
|
83 | + try { |
|
84 | + $client = $this->clientService->newClient(); |
|
85 | + $response = $client->get( |
|
86 | + $lookupServerUrl . '/users?search=' . urlencode($search), |
|
87 | + [ |
|
88 | + 'timeout' => 10, |
|
89 | + 'connect_timeout' => 3, |
|
90 | + ] |
|
91 | + ); |
|
92 | 92 | |
93 | - $body = json_decode($response->getBody(), true); |
|
93 | + $body = json_decode($response->getBody(), true); |
|
94 | 94 | |
95 | - foreach ($body as $lookup) { |
|
96 | - try { |
|
97 | - $remote = $this->cloudIdManager->resolveCloudId($lookup['federationId'])->getRemote(); |
|
98 | - } catch (\Exception $e) { |
|
99 | - $this->logger->error('Can not parse federated cloud ID "' . $lookup['federationId'] . '"'); |
|
100 | - $this->logger->error($e->getMessage()); |
|
101 | - continue; |
|
102 | - } |
|
103 | - if ($this->currentUserRemote === $remote) { |
|
104 | - continue; |
|
105 | - } |
|
106 | - $name = isset($lookup['name']['value']) ? $lookup['name']['value'] : ''; |
|
107 | - $label = empty($name) ? $lookup['federationId'] : $name . ' (' . $lookup['federationId'] . ')'; |
|
108 | - $result[] = [ |
|
109 | - 'label' => $label, |
|
110 | - 'value' => [ |
|
111 | - 'shareType' => IShare::TYPE_REMOTE, |
|
112 | - 'shareWith' => $lookup['federationId'], |
|
113 | - ], |
|
114 | - 'extra' => $lookup, |
|
115 | - ]; |
|
116 | - } |
|
117 | - } catch (\Exception $e) { |
|
118 | - } |
|
95 | + foreach ($body as $lookup) { |
|
96 | + try { |
|
97 | + $remote = $this->cloudIdManager->resolveCloudId($lookup['federationId'])->getRemote(); |
|
98 | + } catch (\Exception $e) { |
|
99 | + $this->logger->error('Can not parse federated cloud ID "' . $lookup['federationId'] . '"'); |
|
100 | + $this->logger->error($e->getMessage()); |
|
101 | + continue; |
|
102 | + } |
|
103 | + if ($this->currentUserRemote === $remote) { |
|
104 | + continue; |
|
105 | + } |
|
106 | + $name = isset($lookup['name']['value']) ? $lookup['name']['value'] : ''; |
|
107 | + $label = empty($name) ? $lookup['federationId'] : $name . ' (' . $lookup['federationId'] . ')'; |
|
108 | + $result[] = [ |
|
109 | + 'label' => $label, |
|
110 | + 'value' => [ |
|
111 | + 'shareType' => IShare::TYPE_REMOTE, |
|
112 | + 'shareWith' => $lookup['federationId'], |
|
113 | + ], |
|
114 | + 'extra' => $lookup, |
|
115 | + ]; |
|
116 | + } |
|
117 | + } catch (\Exception $e) { |
|
118 | + } |
|
119 | 119 | |
120 | - $type = new SearchResultType('lookup'); |
|
121 | - $searchResult->addResultSet($type, $result, []); |
|
120 | + $type = new SearchResultType('lookup'); |
|
121 | + $searchResult->addResultSet($type, $result, []); |
|
122 | 122 | |
123 | - return false; |
|
124 | - } |
|
123 | + return false; |
|
124 | + } |
|
125 | 125 | } |
@@ -45,450 +45,450 @@ |
||
45 | 45 | |
46 | 46 | class Generator { |
47 | 47 | |
48 | - /** @var IPreview */ |
|
49 | - private $previewManager; |
|
50 | - /** @var IConfig */ |
|
51 | - private $config; |
|
52 | - /** @var IAppData */ |
|
53 | - private $appData; |
|
54 | - /** @var GeneratorHelper */ |
|
55 | - private $helper; |
|
56 | - /** @var EventDispatcherInterface */ |
|
57 | - private $eventDispatcher; |
|
58 | - |
|
59 | - /** |
|
60 | - * @param IConfig $config |
|
61 | - * @param IPreview $previewManager |
|
62 | - * @param IAppData $appData |
|
63 | - * @param GeneratorHelper $helper |
|
64 | - * @param EventDispatcherInterface $eventDispatcher |
|
65 | - */ |
|
66 | - public function __construct( |
|
67 | - IConfig $config, |
|
68 | - IPreview $previewManager, |
|
69 | - IAppData $appData, |
|
70 | - GeneratorHelper $helper, |
|
71 | - EventDispatcherInterface $eventDispatcher |
|
72 | - ) { |
|
73 | - $this->config = $config; |
|
74 | - $this->previewManager = $previewManager; |
|
75 | - $this->appData = $appData; |
|
76 | - $this->helper = $helper; |
|
77 | - $this->eventDispatcher = $eventDispatcher; |
|
78 | - } |
|
79 | - |
|
80 | - /** |
|
81 | - * Returns a preview of a file |
|
82 | - * |
|
83 | - * The cache is searched first and if nothing usable was found then a preview is |
|
84 | - * generated by one of the providers |
|
85 | - * |
|
86 | - * @param File $file |
|
87 | - * @param int $width |
|
88 | - * @param int $height |
|
89 | - * @param bool $crop |
|
90 | - * @param string $mode |
|
91 | - * @param string $mimeType |
|
92 | - * @return ISimpleFile |
|
93 | - * @throws NotFoundException |
|
94 | - * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) |
|
95 | - */ |
|
96 | - public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) { |
|
97 | - $specification = [ |
|
98 | - 'width' => $width, |
|
99 | - 'height' => $height, |
|
100 | - 'crop' => $crop, |
|
101 | - 'mode' => $mode, |
|
102 | - ]; |
|
103 | - $this->eventDispatcher->dispatch( |
|
104 | - IPreview::EVENT, |
|
105 | - new GenericEvent($file, $specification) |
|
106 | - ); |
|
107 | - |
|
108 | - // since we only ask for one preview, and the generate method return the last one it created, it returns the one we want |
|
109 | - return $this->generatePreviews($file, [$specification], $mimeType); |
|
110 | - } |
|
111 | - |
|
112 | - /** |
|
113 | - * Generates previews of a file |
|
114 | - * |
|
115 | - * @param File $file |
|
116 | - * @param array $specifications |
|
117 | - * @param string $mimeType |
|
118 | - * @return ISimpleFile the last preview that was generated |
|
119 | - * @throws NotFoundException |
|
120 | - * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) |
|
121 | - */ |
|
122 | - public function generatePreviews(File $file, array $specifications, $mimeType = null) { |
|
123 | - //Make sure that we can read the file |
|
124 | - if (!$file->isReadable()) { |
|
125 | - throw new NotFoundException('Cannot read file'); |
|
126 | - } |
|
127 | - |
|
128 | - if ($mimeType === null) { |
|
129 | - $mimeType = $file->getMimeType(); |
|
130 | - } |
|
131 | - |
|
132 | - $previewFolder = $this->getPreviewFolder($file); |
|
133 | - |
|
134 | - $previewVersion = ''; |
|
135 | - if ($file instanceof IVersionedPreviewFile) { |
|
136 | - $previewVersion = $file->getPreviewVersion() . '-'; |
|
137 | - } |
|
138 | - |
|
139 | - // Get the max preview and infer the max preview sizes from that |
|
140 | - $maxPreview = $this->getMaxPreview($previewFolder, $file, $mimeType, $previewVersion); |
|
141 | - $maxPreviewImage = null; // only load the image when we need it |
|
142 | - if ($maxPreview->getSize() === 0) { |
|
143 | - $maxPreview->delete(); |
|
144 | - throw new NotFoundException('Max preview size 0, invalid!'); |
|
145 | - } |
|
146 | - |
|
147 | - [$maxWidth, $maxHeight] = $this->getPreviewSize($maxPreview, $previewVersion); |
|
148 | - |
|
149 | - $preview = null; |
|
150 | - |
|
151 | - foreach ($specifications as $specification) { |
|
152 | - $width = $specification['width'] ?? -1; |
|
153 | - $height = $specification['height'] ?? -1; |
|
154 | - $crop = $specification['crop'] ?? false; |
|
155 | - $mode = $specification['mode'] ?? IPreview::MODE_FILL; |
|
156 | - |
|
157 | - // If both width and height are -1 we just want the max preview |
|
158 | - if ($width === -1 && $height === -1) { |
|
159 | - $width = $maxWidth; |
|
160 | - $height = $maxHeight; |
|
161 | - } |
|
162 | - |
|
163 | - // Calculate the preview size |
|
164 | - [$width, $height] = $this->calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight); |
|
165 | - |
|
166 | - // No need to generate a preview that is just the max preview |
|
167 | - if ($width === $maxWidth && $height === $maxHeight) { |
|
168 | - // ensure correct return value if this was the last one |
|
169 | - $preview = $maxPreview; |
|
170 | - continue; |
|
171 | - } |
|
172 | - |
|
173 | - // Try to get a cached preview. Else generate (and store) one |
|
174 | - try { |
|
175 | - try { |
|
176 | - $preview = $this->getCachedPreview($previewFolder, $width, $height, $crop, $maxPreview->getMimeType(), $previewVersion); |
|
177 | - } catch (NotFoundException $e) { |
|
178 | - if (!$this->previewManager->isMimeSupported($mimeType)) { |
|
179 | - throw new NotFoundException(); |
|
180 | - } |
|
181 | - |
|
182 | - if ($maxPreviewImage === null) { |
|
183 | - $maxPreviewImage = $this->helper->getImage($maxPreview); |
|
184 | - } |
|
185 | - |
|
186 | - $preview = $this->generatePreview($previewFolder, $maxPreviewImage, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion); |
|
187 | - } |
|
188 | - } catch (\InvalidArgumentException $e) { |
|
189 | - throw new NotFoundException("", 0, $e); |
|
190 | - } |
|
191 | - |
|
192 | - if ($preview->getSize() === 0) { |
|
193 | - $preview->delete(); |
|
194 | - throw new NotFoundException('Cached preview size 0, invalid!'); |
|
195 | - } |
|
196 | - } |
|
197 | - |
|
198 | - // Free memory being used by the embedded image resource. Without this the image is kept in memory indefinitely. |
|
199 | - // Garbage Collection does NOT free this memory. We have to do it ourselves. |
|
200 | - if ($maxPreviewImage instanceof \OC_Image) { |
|
201 | - $maxPreviewImage->destroy(); |
|
202 | - } |
|
203 | - |
|
204 | - return $preview; |
|
205 | - } |
|
206 | - |
|
207 | - /** |
|
208 | - * @param ISimpleFolder $previewFolder |
|
209 | - * @param File $file |
|
210 | - * @param string $mimeType |
|
211 | - * @param string $prefix |
|
212 | - * @return ISimpleFile |
|
213 | - * @throws NotFoundException |
|
214 | - */ |
|
215 | - private function getMaxPreview(ISimpleFolder $previewFolder, File $file, $mimeType, $prefix) { |
|
216 | - $nodes = $previewFolder->getDirectoryListing(); |
|
217 | - |
|
218 | - foreach ($nodes as $node) { |
|
219 | - $name = $node->getName(); |
|
220 | - if (($prefix === '' || strpos($name, $prefix) === 0) && strpos($name, 'max')) { |
|
221 | - return $node; |
|
222 | - } |
|
223 | - } |
|
224 | - |
|
225 | - $previewProviders = $this->previewManager->getProviders(); |
|
226 | - foreach ($previewProviders as $supportedMimeType => $providers) { |
|
227 | - if (!preg_match($supportedMimeType, $mimeType)) { |
|
228 | - continue; |
|
229 | - } |
|
230 | - |
|
231 | - foreach ($providers as $providerClosure) { |
|
232 | - $provider = $this->helper->getProvider($providerClosure); |
|
233 | - if (!($provider instanceof IProviderV2)) { |
|
234 | - continue; |
|
235 | - } |
|
236 | - |
|
237 | - if (!$provider->isAvailable($file)) { |
|
238 | - continue; |
|
239 | - } |
|
240 | - |
|
241 | - $maxWidth = $this->config->getSystemValueInt('preview_max_x', 4096); |
|
242 | - $maxHeight = $this->config->getSystemValueInt('preview_max_y', 4096); |
|
243 | - |
|
244 | - $preview = $this->helper->getThumbnail($provider, $file, $maxWidth, $maxHeight); |
|
245 | - |
|
246 | - if (!($preview instanceof IImage)) { |
|
247 | - continue; |
|
248 | - } |
|
249 | - |
|
250 | - // Try to get the extention. |
|
251 | - try { |
|
252 | - $ext = $this->getExtention($preview->dataMimeType()); |
|
253 | - } catch (\InvalidArgumentException $e) { |
|
254 | - // Just continue to the next iteration if this preview doesn't have a valid mimetype |
|
255 | - continue; |
|
256 | - } |
|
257 | - |
|
258 | - $path = $prefix . (string)$preview->width() . '-' . (string)$preview->height() . '-max.' . $ext; |
|
259 | - try { |
|
260 | - $file = $previewFolder->newFile($path); |
|
261 | - $file->putContent($preview->data()); |
|
262 | - } catch (NotPermittedException $e) { |
|
263 | - throw new NotFoundException(); |
|
264 | - } |
|
265 | - |
|
266 | - return $file; |
|
267 | - } |
|
268 | - } |
|
269 | - |
|
270 | - throw new NotFoundException(); |
|
271 | - } |
|
272 | - |
|
273 | - /** |
|
274 | - * @param ISimpleFile $file |
|
275 | - * @param string $prefix |
|
276 | - * @return int[] |
|
277 | - */ |
|
278 | - private function getPreviewSize(ISimpleFile $file, string $prefix = '') { |
|
279 | - $size = explode('-', substr($file->getName(), strlen($prefix))); |
|
280 | - return [(int)$size[0], (int)$size[1]]; |
|
281 | - } |
|
282 | - |
|
283 | - /** |
|
284 | - * @param int $width |
|
285 | - * @param int $height |
|
286 | - * @param bool $crop |
|
287 | - * @param string $mimeType |
|
288 | - * @param string $prefix |
|
289 | - * @return string |
|
290 | - */ |
|
291 | - private function generatePath($width, $height, $crop, $mimeType, $prefix) { |
|
292 | - $path = $prefix . (string)$width . '-' . (string)$height; |
|
293 | - if ($crop) { |
|
294 | - $path .= '-crop'; |
|
295 | - } |
|
296 | - |
|
297 | - $ext = $this->getExtention($mimeType); |
|
298 | - $path .= '.' . $ext; |
|
299 | - return $path; |
|
300 | - } |
|
301 | - |
|
302 | - |
|
303 | - /** |
|
304 | - * @param int $width |
|
305 | - * @param int $height |
|
306 | - * @param bool $crop |
|
307 | - * @param string $mode |
|
308 | - * @param int $maxWidth |
|
309 | - * @param int $maxHeight |
|
310 | - * @return int[] |
|
311 | - */ |
|
312 | - private function calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight) { |
|
313 | - |
|
314 | - /* |
|
48 | + /** @var IPreview */ |
|
49 | + private $previewManager; |
|
50 | + /** @var IConfig */ |
|
51 | + private $config; |
|
52 | + /** @var IAppData */ |
|
53 | + private $appData; |
|
54 | + /** @var GeneratorHelper */ |
|
55 | + private $helper; |
|
56 | + /** @var EventDispatcherInterface */ |
|
57 | + private $eventDispatcher; |
|
58 | + |
|
59 | + /** |
|
60 | + * @param IConfig $config |
|
61 | + * @param IPreview $previewManager |
|
62 | + * @param IAppData $appData |
|
63 | + * @param GeneratorHelper $helper |
|
64 | + * @param EventDispatcherInterface $eventDispatcher |
|
65 | + */ |
|
66 | + public function __construct( |
|
67 | + IConfig $config, |
|
68 | + IPreview $previewManager, |
|
69 | + IAppData $appData, |
|
70 | + GeneratorHelper $helper, |
|
71 | + EventDispatcherInterface $eventDispatcher |
|
72 | + ) { |
|
73 | + $this->config = $config; |
|
74 | + $this->previewManager = $previewManager; |
|
75 | + $this->appData = $appData; |
|
76 | + $this->helper = $helper; |
|
77 | + $this->eventDispatcher = $eventDispatcher; |
|
78 | + } |
|
79 | + |
|
80 | + /** |
|
81 | + * Returns a preview of a file |
|
82 | + * |
|
83 | + * The cache is searched first and if nothing usable was found then a preview is |
|
84 | + * generated by one of the providers |
|
85 | + * |
|
86 | + * @param File $file |
|
87 | + * @param int $width |
|
88 | + * @param int $height |
|
89 | + * @param bool $crop |
|
90 | + * @param string $mode |
|
91 | + * @param string $mimeType |
|
92 | + * @return ISimpleFile |
|
93 | + * @throws NotFoundException |
|
94 | + * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) |
|
95 | + */ |
|
96 | + public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) { |
|
97 | + $specification = [ |
|
98 | + 'width' => $width, |
|
99 | + 'height' => $height, |
|
100 | + 'crop' => $crop, |
|
101 | + 'mode' => $mode, |
|
102 | + ]; |
|
103 | + $this->eventDispatcher->dispatch( |
|
104 | + IPreview::EVENT, |
|
105 | + new GenericEvent($file, $specification) |
|
106 | + ); |
|
107 | + |
|
108 | + // since we only ask for one preview, and the generate method return the last one it created, it returns the one we want |
|
109 | + return $this->generatePreviews($file, [$specification], $mimeType); |
|
110 | + } |
|
111 | + |
|
112 | + /** |
|
113 | + * Generates previews of a file |
|
114 | + * |
|
115 | + * @param File $file |
|
116 | + * @param array $specifications |
|
117 | + * @param string $mimeType |
|
118 | + * @return ISimpleFile the last preview that was generated |
|
119 | + * @throws NotFoundException |
|
120 | + * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) |
|
121 | + */ |
|
122 | + public function generatePreviews(File $file, array $specifications, $mimeType = null) { |
|
123 | + //Make sure that we can read the file |
|
124 | + if (!$file->isReadable()) { |
|
125 | + throw new NotFoundException('Cannot read file'); |
|
126 | + } |
|
127 | + |
|
128 | + if ($mimeType === null) { |
|
129 | + $mimeType = $file->getMimeType(); |
|
130 | + } |
|
131 | + |
|
132 | + $previewFolder = $this->getPreviewFolder($file); |
|
133 | + |
|
134 | + $previewVersion = ''; |
|
135 | + if ($file instanceof IVersionedPreviewFile) { |
|
136 | + $previewVersion = $file->getPreviewVersion() . '-'; |
|
137 | + } |
|
138 | + |
|
139 | + // Get the max preview and infer the max preview sizes from that |
|
140 | + $maxPreview = $this->getMaxPreview($previewFolder, $file, $mimeType, $previewVersion); |
|
141 | + $maxPreviewImage = null; // only load the image when we need it |
|
142 | + if ($maxPreview->getSize() === 0) { |
|
143 | + $maxPreview->delete(); |
|
144 | + throw new NotFoundException('Max preview size 0, invalid!'); |
|
145 | + } |
|
146 | + |
|
147 | + [$maxWidth, $maxHeight] = $this->getPreviewSize($maxPreview, $previewVersion); |
|
148 | + |
|
149 | + $preview = null; |
|
150 | + |
|
151 | + foreach ($specifications as $specification) { |
|
152 | + $width = $specification['width'] ?? -1; |
|
153 | + $height = $specification['height'] ?? -1; |
|
154 | + $crop = $specification['crop'] ?? false; |
|
155 | + $mode = $specification['mode'] ?? IPreview::MODE_FILL; |
|
156 | + |
|
157 | + // If both width and height are -1 we just want the max preview |
|
158 | + if ($width === -1 && $height === -1) { |
|
159 | + $width = $maxWidth; |
|
160 | + $height = $maxHeight; |
|
161 | + } |
|
162 | + |
|
163 | + // Calculate the preview size |
|
164 | + [$width, $height] = $this->calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight); |
|
165 | + |
|
166 | + // No need to generate a preview that is just the max preview |
|
167 | + if ($width === $maxWidth && $height === $maxHeight) { |
|
168 | + // ensure correct return value if this was the last one |
|
169 | + $preview = $maxPreview; |
|
170 | + continue; |
|
171 | + } |
|
172 | + |
|
173 | + // Try to get a cached preview. Else generate (and store) one |
|
174 | + try { |
|
175 | + try { |
|
176 | + $preview = $this->getCachedPreview($previewFolder, $width, $height, $crop, $maxPreview->getMimeType(), $previewVersion); |
|
177 | + } catch (NotFoundException $e) { |
|
178 | + if (!$this->previewManager->isMimeSupported($mimeType)) { |
|
179 | + throw new NotFoundException(); |
|
180 | + } |
|
181 | + |
|
182 | + if ($maxPreviewImage === null) { |
|
183 | + $maxPreviewImage = $this->helper->getImage($maxPreview); |
|
184 | + } |
|
185 | + |
|
186 | + $preview = $this->generatePreview($previewFolder, $maxPreviewImage, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion); |
|
187 | + } |
|
188 | + } catch (\InvalidArgumentException $e) { |
|
189 | + throw new NotFoundException("", 0, $e); |
|
190 | + } |
|
191 | + |
|
192 | + if ($preview->getSize() === 0) { |
|
193 | + $preview->delete(); |
|
194 | + throw new NotFoundException('Cached preview size 0, invalid!'); |
|
195 | + } |
|
196 | + } |
|
197 | + |
|
198 | + // Free memory being used by the embedded image resource. Without this the image is kept in memory indefinitely. |
|
199 | + // Garbage Collection does NOT free this memory. We have to do it ourselves. |
|
200 | + if ($maxPreviewImage instanceof \OC_Image) { |
|
201 | + $maxPreviewImage->destroy(); |
|
202 | + } |
|
203 | + |
|
204 | + return $preview; |
|
205 | + } |
|
206 | + |
|
207 | + /** |
|
208 | + * @param ISimpleFolder $previewFolder |
|
209 | + * @param File $file |
|
210 | + * @param string $mimeType |
|
211 | + * @param string $prefix |
|
212 | + * @return ISimpleFile |
|
213 | + * @throws NotFoundException |
|
214 | + */ |
|
215 | + private function getMaxPreview(ISimpleFolder $previewFolder, File $file, $mimeType, $prefix) { |
|
216 | + $nodes = $previewFolder->getDirectoryListing(); |
|
217 | + |
|
218 | + foreach ($nodes as $node) { |
|
219 | + $name = $node->getName(); |
|
220 | + if (($prefix === '' || strpos($name, $prefix) === 0) && strpos($name, 'max')) { |
|
221 | + return $node; |
|
222 | + } |
|
223 | + } |
|
224 | + |
|
225 | + $previewProviders = $this->previewManager->getProviders(); |
|
226 | + foreach ($previewProviders as $supportedMimeType => $providers) { |
|
227 | + if (!preg_match($supportedMimeType, $mimeType)) { |
|
228 | + continue; |
|
229 | + } |
|
230 | + |
|
231 | + foreach ($providers as $providerClosure) { |
|
232 | + $provider = $this->helper->getProvider($providerClosure); |
|
233 | + if (!($provider instanceof IProviderV2)) { |
|
234 | + continue; |
|
235 | + } |
|
236 | + |
|
237 | + if (!$provider->isAvailable($file)) { |
|
238 | + continue; |
|
239 | + } |
|
240 | + |
|
241 | + $maxWidth = $this->config->getSystemValueInt('preview_max_x', 4096); |
|
242 | + $maxHeight = $this->config->getSystemValueInt('preview_max_y', 4096); |
|
243 | + |
|
244 | + $preview = $this->helper->getThumbnail($provider, $file, $maxWidth, $maxHeight); |
|
245 | + |
|
246 | + if (!($preview instanceof IImage)) { |
|
247 | + continue; |
|
248 | + } |
|
249 | + |
|
250 | + // Try to get the extention. |
|
251 | + try { |
|
252 | + $ext = $this->getExtention($preview->dataMimeType()); |
|
253 | + } catch (\InvalidArgumentException $e) { |
|
254 | + // Just continue to the next iteration if this preview doesn't have a valid mimetype |
|
255 | + continue; |
|
256 | + } |
|
257 | + |
|
258 | + $path = $prefix . (string)$preview->width() . '-' . (string)$preview->height() . '-max.' . $ext; |
|
259 | + try { |
|
260 | + $file = $previewFolder->newFile($path); |
|
261 | + $file->putContent($preview->data()); |
|
262 | + } catch (NotPermittedException $e) { |
|
263 | + throw new NotFoundException(); |
|
264 | + } |
|
265 | + |
|
266 | + return $file; |
|
267 | + } |
|
268 | + } |
|
269 | + |
|
270 | + throw new NotFoundException(); |
|
271 | + } |
|
272 | + |
|
273 | + /** |
|
274 | + * @param ISimpleFile $file |
|
275 | + * @param string $prefix |
|
276 | + * @return int[] |
|
277 | + */ |
|
278 | + private function getPreviewSize(ISimpleFile $file, string $prefix = '') { |
|
279 | + $size = explode('-', substr($file->getName(), strlen($prefix))); |
|
280 | + return [(int)$size[0], (int)$size[1]]; |
|
281 | + } |
|
282 | + |
|
283 | + /** |
|
284 | + * @param int $width |
|
285 | + * @param int $height |
|
286 | + * @param bool $crop |
|
287 | + * @param string $mimeType |
|
288 | + * @param string $prefix |
|
289 | + * @return string |
|
290 | + */ |
|
291 | + private function generatePath($width, $height, $crop, $mimeType, $prefix) { |
|
292 | + $path = $prefix . (string)$width . '-' . (string)$height; |
|
293 | + if ($crop) { |
|
294 | + $path .= '-crop'; |
|
295 | + } |
|
296 | + |
|
297 | + $ext = $this->getExtention($mimeType); |
|
298 | + $path .= '.' . $ext; |
|
299 | + return $path; |
|
300 | + } |
|
301 | + |
|
302 | + |
|
303 | + /** |
|
304 | + * @param int $width |
|
305 | + * @param int $height |
|
306 | + * @param bool $crop |
|
307 | + * @param string $mode |
|
308 | + * @param int $maxWidth |
|
309 | + * @param int $maxHeight |
|
310 | + * @return int[] |
|
311 | + */ |
|
312 | + private function calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight) { |
|
313 | + |
|
314 | + /* |
|
315 | 315 | * If we are not cropping we have to make sure the requested image |
316 | 316 | * respects the aspect ratio of the original. |
317 | 317 | */ |
318 | - if (!$crop) { |
|
319 | - $ratio = $maxHeight / $maxWidth; |
|
318 | + if (!$crop) { |
|
319 | + $ratio = $maxHeight / $maxWidth; |
|
320 | 320 | |
321 | - if ($width === -1) { |
|
322 | - $width = $height / $ratio; |
|
323 | - } |
|
324 | - if ($height === -1) { |
|
325 | - $height = $width * $ratio; |
|
326 | - } |
|
321 | + if ($width === -1) { |
|
322 | + $width = $height / $ratio; |
|
323 | + } |
|
324 | + if ($height === -1) { |
|
325 | + $height = $width * $ratio; |
|
326 | + } |
|
327 | 327 | |
328 | - $ratioH = $height / $maxHeight; |
|
329 | - $ratioW = $width / $maxWidth; |
|
328 | + $ratioH = $height / $maxHeight; |
|
329 | + $ratioW = $width / $maxWidth; |
|
330 | 330 | |
331 | - /* |
|
331 | + /* |
|
332 | 332 | * Fill means that the $height and $width are the max |
333 | 333 | * Cover means min. |
334 | 334 | */ |
335 | - if ($mode === IPreview::MODE_FILL) { |
|
336 | - if ($ratioH > $ratioW) { |
|
337 | - $height = $width * $ratio; |
|
338 | - } else { |
|
339 | - $width = $height / $ratio; |
|
340 | - } |
|
341 | - } elseif ($mode === IPreview::MODE_COVER) { |
|
342 | - if ($ratioH > $ratioW) { |
|
343 | - $width = $height / $ratio; |
|
344 | - } else { |
|
345 | - $height = $width * $ratio; |
|
346 | - } |
|
347 | - } |
|
348 | - } |
|
349 | - |
|
350 | - if ($height !== $maxHeight && $width !== $maxWidth) { |
|
351 | - /* |
|
335 | + if ($mode === IPreview::MODE_FILL) { |
|
336 | + if ($ratioH > $ratioW) { |
|
337 | + $height = $width * $ratio; |
|
338 | + } else { |
|
339 | + $width = $height / $ratio; |
|
340 | + } |
|
341 | + } elseif ($mode === IPreview::MODE_COVER) { |
|
342 | + if ($ratioH > $ratioW) { |
|
343 | + $width = $height / $ratio; |
|
344 | + } else { |
|
345 | + $height = $width * $ratio; |
|
346 | + } |
|
347 | + } |
|
348 | + } |
|
349 | + |
|
350 | + if ($height !== $maxHeight && $width !== $maxWidth) { |
|
351 | + /* |
|
352 | 352 | * Scale to the nearest power of four |
353 | 353 | */ |
354 | - $pow4height = 4 ** ceil(log($height) / log(4)); |
|
355 | - $pow4width = 4 ** ceil(log($width) / log(4)); |
|
356 | - |
|
357 | - // Minimum size is 64 |
|
358 | - $pow4height = max($pow4height, 64); |
|
359 | - $pow4width = max($pow4width, 64); |
|
360 | - |
|
361 | - $ratioH = $height / $pow4height; |
|
362 | - $ratioW = $width / $pow4width; |
|
363 | - |
|
364 | - if ($ratioH < $ratioW) { |
|
365 | - $width = $pow4width; |
|
366 | - $height /= $ratioW; |
|
367 | - } else { |
|
368 | - $height = $pow4height; |
|
369 | - $width /= $ratioH; |
|
370 | - } |
|
371 | - } |
|
372 | - |
|
373 | - /* |
|
354 | + $pow4height = 4 ** ceil(log($height) / log(4)); |
|
355 | + $pow4width = 4 ** ceil(log($width) / log(4)); |
|
356 | + |
|
357 | + // Minimum size is 64 |
|
358 | + $pow4height = max($pow4height, 64); |
|
359 | + $pow4width = max($pow4width, 64); |
|
360 | + |
|
361 | + $ratioH = $height / $pow4height; |
|
362 | + $ratioW = $width / $pow4width; |
|
363 | + |
|
364 | + if ($ratioH < $ratioW) { |
|
365 | + $width = $pow4width; |
|
366 | + $height /= $ratioW; |
|
367 | + } else { |
|
368 | + $height = $pow4height; |
|
369 | + $width /= $ratioH; |
|
370 | + } |
|
371 | + } |
|
372 | + |
|
373 | + /* |
|
374 | 374 | * Make sure the requested height and width fall within the max |
375 | 375 | * of the preview. |
376 | 376 | */ |
377 | - if ($height > $maxHeight) { |
|
378 | - $ratio = $height / $maxHeight; |
|
379 | - $height = $maxHeight; |
|
380 | - $width /= $ratio; |
|
381 | - } |
|
382 | - if ($width > $maxWidth) { |
|
383 | - $ratio = $width / $maxWidth; |
|
384 | - $width = $maxWidth; |
|
385 | - $height /= $ratio; |
|
386 | - } |
|
387 | - |
|
388 | - return [(int)round($width), (int)round($height)]; |
|
389 | - } |
|
390 | - |
|
391 | - /** |
|
392 | - * @param ISimpleFolder $previewFolder |
|
393 | - * @param ISimpleFile $maxPreview |
|
394 | - * @param int $width |
|
395 | - * @param int $height |
|
396 | - * @param bool $crop |
|
397 | - * @param int $maxWidth |
|
398 | - * @param int $maxHeight |
|
399 | - * @param string $prefix |
|
400 | - * @return ISimpleFile |
|
401 | - * @throws NotFoundException |
|
402 | - * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) |
|
403 | - */ |
|
404 | - private function generatePreview(ISimpleFolder $previewFolder, IImage $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $prefix) { |
|
405 | - $preview = $maxPreview; |
|
406 | - if (!$preview->valid()) { |
|
407 | - throw new \InvalidArgumentException('Failed to generate preview, failed to load image'); |
|
408 | - } |
|
409 | - |
|
410 | - if ($crop) { |
|
411 | - if ($height !== $preview->height() && $width !== $preview->width()) { |
|
412 | - //Resize |
|
413 | - $widthR = $preview->width() / $width; |
|
414 | - $heightR = $preview->height() / $height; |
|
415 | - |
|
416 | - if ($widthR > $heightR) { |
|
417 | - $scaleH = $height; |
|
418 | - $scaleW = $maxWidth / $heightR; |
|
419 | - } else { |
|
420 | - $scaleH = $maxHeight / $widthR; |
|
421 | - $scaleW = $width; |
|
422 | - } |
|
423 | - $preview = $preview->preciseResizeCopy((int)round($scaleW), (int)round($scaleH)); |
|
424 | - } |
|
425 | - $cropX = (int)floor(abs($width - $preview->width()) * 0.5); |
|
426 | - $cropY = (int)floor(abs($height - $preview->height()) * 0.5); |
|
427 | - $preview = $preview->cropCopy($cropX, $cropY, $width, $height); |
|
428 | - } else { |
|
429 | - $preview = $maxPreview->resizeCopy(max($width, $height)); |
|
430 | - } |
|
431 | - |
|
432 | - |
|
433 | - $path = $this->generatePath($width, $height, $crop, $preview->dataMimeType(), $prefix); |
|
434 | - try { |
|
435 | - $file = $previewFolder->newFile($path); |
|
436 | - $file->putContent($preview->data()); |
|
437 | - } catch (NotPermittedException $e) { |
|
438 | - throw new NotFoundException(); |
|
439 | - } |
|
440 | - |
|
441 | - return $file; |
|
442 | - } |
|
443 | - |
|
444 | - /** |
|
445 | - * @param ISimpleFolder $previewFolder |
|
446 | - * @param int $width |
|
447 | - * @param int $height |
|
448 | - * @param bool $crop |
|
449 | - * @param string $mimeType |
|
450 | - * @param string $prefix |
|
451 | - * @return ISimpleFile |
|
452 | - * |
|
453 | - * @throws NotFoundException |
|
454 | - */ |
|
455 | - private function getCachedPreview(ISimpleFolder $previewFolder, $width, $height, $crop, $mimeType, $prefix) { |
|
456 | - $path = $this->generatePath($width, $height, $crop, $mimeType, $prefix); |
|
457 | - |
|
458 | - return $previewFolder->getFile($path); |
|
459 | - } |
|
460 | - |
|
461 | - /** |
|
462 | - * Get the specific preview folder for this file |
|
463 | - * |
|
464 | - * @param File $file |
|
465 | - * @return ISimpleFolder |
|
466 | - */ |
|
467 | - private function getPreviewFolder(File $file) { |
|
468 | - try { |
|
469 | - $folder = $this->appData->getFolder($file->getId()); |
|
470 | - } catch (NotFoundException $e) { |
|
471 | - $folder = $this->appData->newFolder($file->getId()); |
|
472 | - } |
|
473 | - |
|
474 | - return $folder; |
|
475 | - } |
|
476 | - |
|
477 | - /** |
|
478 | - * @param string $mimeType |
|
479 | - * @return null|string |
|
480 | - * @throws \InvalidArgumentException |
|
481 | - */ |
|
482 | - private function getExtention($mimeType) { |
|
483 | - switch ($mimeType) { |
|
484 | - case 'image/png': |
|
485 | - return 'png'; |
|
486 | - case 'image/jpeg': |
|
487 | - return 'jpg'; |
|
488 | - case 'image/gif': |
|
489 | - return 'gif'; |
|
490 | - default: |
|
491 | - throw new \InvalidArgumentException('Not a valid mimetype: "' . $mimeType . '"'); |
|
492 | - } |
|
493 | - } |
|
377 | + if ($height > $maxHeight) { |
|
378 | + $ratio = $height / $maxHeight; |
|
379 | + $height = $maxHeight; |
|
380 | + $width /= $ratio; |
|
381 | + } |
|
382 | + if ($width > $maxWidth) { |
|
383 | + $ratio = $width / $maxWidth; |
|
384 | + $width = $maxWidth; |
|
385 | + $height /= $ratio; |
|
386 | + } |
|
387 | + |
|
388 | + return [(int)round($width), (int)round($height)]; |
|
389 | + } |
|
390 | + |
|
391 | + /** |
|
392 | + * @param ISimpleFolder $previewFolder |
|
393 | + * @param ISimpleFile $maxPreview |
|
394 | + * @param int $width |
|
395 | + * @param int $height |
|
396 | + * @param bool $crop |
|
397 | + * @param int $maxWidth |
|
398 | + * @param int $maxHeight |
|
399 | + * @param string $prefix |
|
400 | + * @return ISimpleFile |
|
401 | + * @throws NotFoundException |
|
402 | + * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) |
|
403 | + */ |
|
404 | + private function generatePreview(ISimpleFolder $previewFolder, IImage $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $prefix) { |
|
405 | + $preview = $maxPreview; |
|
406 | + if (!$preview->valid()) { |
|
407 | + throw new \InvalidArgumentException('Failed to generate preview, failed to load image'); |
|
408 | + } |
|
409 | + |
|
410 | + if ($crop) { |
|
411 | + if ($height !== $preview->height() && $width !== $preview->width()) { |
|
412 | + //Resize |
|
413 | + $widthR = $preview->width() / $width; |
|
414 | + $heightR = $preview->height() / $height; |
|
415 | + |
|
416 | + if ($widthR > $heightR) { |
|
417 | + $scaleH = $height; |
|
418 | + $scaleW = $maxWidth / $heightR; |
|
419 | + } else { |
|
420 | + $scaleH = $maxHeight / $widthR; |
|
421 | + $scaleW = $width; |
|
422 | + } |
|
423 | + $preview = $preview->preciseResizeCopy((int)round($scaleW), (int)round($scaleH)); |
|
424 | + } |
|
425 | + $cropX = (int)floor(abs($width - $preview->width()) * 0.5); |
|
426 | + $cropY = (int)floor(abs($height - $preview->height()) * 0.5); |
|
427 | + $preview = $preview->cropCopy($cropX, $cropY, $width, $height); |
|
428 | + } else { |
|
429 | + $preview = $maxPreview->resizeCopy(max($width, $height)); |
|
430 | + } |
|
431 | + |
|
432 | + |
|
433 | + $path = $this->generatePath($width, $height, $crop, $preview->dataMimeType(), $prefix); |
|
434 | + try { |
|
435 | + $file = $previewFolder->newFile($path); |
|
436 | + $file->putContent($preview->data()); |
|
437 | + } catch (NotPermittedException $e) { |
|
438 | + throw new NotFoundException(); |
|
439 | + } |
|
440 | + |
|
441 | + return $file; |
|
442 | + } |
|
443 | + |
|
444 | + /** |
|
445 | + * @param ISimpleFolder $previewFolder |
|
446 | + * @param int $width |
|
447 | + * @param int $height |
|
448 | + * @param bool $crop |
|
449 | + * @param string $mimeType |
|
450 | + * @param string $prefix |
|
451 | + * @return ISimpleFile |
|
452 | + * |
|
453 | + * @throws NotFoundException |
|
454 | + */ |
|
455 | + private function getCachedPreview(ISimpleFolder $previewFolder, $width, $height, $crop, $mimeType, $prefix) { |
|
456 | + $path = $this->generatePath($width, $height, $crop, $mimeType, $prefix); |
|
457 | + |
|
458 | + return $previewFolder->getFile($path); |
|
459 | + } |
|
460 | + |
|
461 | + /** |
|
462 | + * Get the specific preview folder for this file |
|
463 | + * |
|
464 | + * @param File $file |
|
465 | + * @return ISimpleFolder |
|
466 | + */ |
|
467 | + private function getPreviewFolder(File $file) { |
|
468 | + try { |
|
469 | + $folder = $this->appData->getFolder($file->getId()); |
|
470 | + } catch (NotFoundException $e) { |
|
471 | + $folder = $this->appData->newFolder($file->getId()); |
|
472 | + } |
|
473 | + |
|
474 | + return $folder; |
|
475 | + } |
|
476 | + |
|
477 | + /** |
|
478 | + * @param string $mimeType |
|
479 | + * @return null|string |
|
480 | + * @throws \InvalidArgumentException |
|
481 | + */ |
|
482 | + private function getExtention($mimeType) { |
|
483 | + switch ($mimeType) { |
|
484 | + case 'image/png': |
|
485 | + return 'png'; |
|
486 | + case 'image/jpeg': |
|
487 | + return 'jpg'; |
|
488 | + case 'image/gif': |
|
489 | + return 'gif'; |
|
490 | + default: |
|
491 | + throw new \InvalidArgumentException('Not a valid mimetype: "' . $mimeType . '"'); |
|
492 | + } |
|
493 | + } |
|
494 | 494 | } |
@@ -133,7 +133,7 @@ discard block |
||
133 | 133 | |
134 | 134 | $previewVersion = ''; |
135 | 135 | if ($file instanceof IVersionedPreviewFile) { |
136 | - $previewVersion = $file->getPreviewVersion() . '-'; |
|
136 | + $previewVersion = $file->getPreviewVersion().'-'; |
|
137 | 137 | } |
138 | 138 | |
139 | 139 | // Get the max preview and infer the max preview sizes from that |
@@ -255,7 +255,7 @@ discard block |
||
255 | 255 | continue; |
256 | 256 | } |
257 | 257 | |
258 | - $path = $prefix . (string)$preview->width() . '-' . (string)$preview->height() . '-max.' . $ext; |
|
258 | + $path = $prefix.(string) $preview->width().'-'.(string) $preview->height().'-max.'.$ext; |
|
259 | 259 | try { |
260 | 260 | $file = $previewFolder->newFile($path); |
261 | 261 | $file->putContent($preview->data()); |
@@ -277,7 +277,7 @@ discard block |
||
277 | 277 | */ |
278 | 278 | private function getPreviewSize(ISimpleFile $file, string $prefix = '') { |
279 | 279 | $size = explode('-', substr($file->getName(), strlen($prefix))); |
280 | - return [(int)$size[0], (int)$size[1]]; |
|
280 | + return [(int) $size[0], (int) $size[1]]; |
|
281 | 281 | } |
282 | 282 | |
283 | 283 | /** |
@@ -289,13 +289,13 @@ discard block |
||
289 | 289 | * @return string |
290 | 290 | */ |
291 | 291 | private function generatePath($width, $height, $crop, $mimeType, $prefix) { |
292 | - $path = $prefix . (string)$width . '-' . (string)$height; |
|
292 | + $path = $prefix.(string) $width.'-'.(string) $height; |
|
293 | 293 | if ($crop) { |
294 | 294 | $path .= '-crop'; |
295 | 295 | } |
296 | 296 | |
297 | 297 | $ext = $this->getExtention($mimeType); |
298 | - $path .= '.' . $ext; |
|
298 | + $path .= '.'.$ext; |
|
299 | 299 | return $path; |
300 | 300 | } |
301 | 301 | |
@@ -385,7 +385,7 @@ discard block |
||
385 | 385 | $height /= $ratio; |
386 | 386 | } |
387 | 387 | |
388 | - return [(int)round($width), (int)round($height)]; |
|
388 | + return [(int) round($width), (int) round($height)]; |
|
389 | 389 | } |
390 | 390 | |
391 | 391 | /** |
@@ -420,10 +420,10 @@ discard block |
||
420 | 420 | $scaleH = $maxHeight / $widthR; |
421 | 421 | $scaleW = $width; |
422 | 422 | } |
423 | - $preview = $preview->preciseResizeCopy((int)round($scaleW), (int)round($scaleH)); |
|
423 | + $preview = $preview->preciseResizeCopy((int) round($scaleW), (int) round($scaleH)); |
|
424 | 424 | } |
425 | - $cropX = (int)floor(abs($width - $preview->width()) * 0.5); |
|
426 | - $cropY = (int)floor(abs($height - $preview->height()) * 0.5); |
|
425 | + $cropX = (int) floor(abs($width - $preview->width()) * 0.5); |
|
426 | + $cropY = (int) floor(abs($height - $preview->height()) * 0.5); |
|
427 | 427 | $preview = $preview->cropCopy($cropX, $cropY, $width, $height); |
428 | 428 | } else { |
429 | 429 | $preview = $maxPreview->resizeCopy(max($width, $height)); |
@@ -488,7 +488,7 @@ discard block |
||
488 | 488 | case 'image/gif': |
489 | 489 | return 'gif'; |
490 | 490 | default: |
491 | - throw new \InvalidArgumentException('Not a valid mimetype: "' . $mimeType . '"'); |
|
491 | + throw new \InvalidArgumentException('Not a valid mimetype: "'.$mimeType.'"'); |
|
492 | 492 | } |
493 | 493 | } |
494 | 494 | } |
@@ -34,31 +34,31 @@ |
||
34 | 34 | |
35 | 35 | abstract class Image extends ProviderV2 { |
36 | 36 | |
37 | - /** |
|
38 | - * {@inheritDoc} |
|
39 | - */ |
|
40 | - public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { |
|
41 | - $maxSizeForImages = \OC::$server->getConfig()->getSystemValueInt('preview_max_filesize_image', 50); |
|
42 | - $size = $file->getSize(); |
|
37 | + /** |
|
38 | + * {@inheritDoc} |
|
39 | + */ |
|
40 | + public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { |
|
41 | + $maxSizeForImages = \OC::$server->getConfig()->getSystemValueInt('preview_max_filesize_image', 50); |
|
42 | + $size = $file->getSize(); |
|
43 | 43 | |
44 | - if ($maxSizeForImages !== -1 && $size > ($maxSizeForImages * 1024 * 1024)) { |
|
45 | - return null; |
|
46 | - } |
|
44 | + if ($maxSizeForImages !== -1 && $size > ($maxSizeForImages * 1024 * 1024)) { |
|
45 | + return null; |
|
46 | + } |
|
47 | 47 | |
48 | - $image = new \OC_Image(); |
|
48 | + $image = new \OC_Image(); |
|
49 | 49 | |
50 | - $fileName = $this->getLocalFile($file); |
|
50 | + $fileName = $this->getLocalFile($file); |
|
51 | 51 | |
52 | - $image->loadFromFile($fileName); |
|
53 | - $image->fixOrientation(); |
|
52 | + $image->loadFromFile($fileName); |
|
53 | + $image->fixOrientation(); |
|
54 | 54 | |
55 | - $this->cleanTmpFiles(); |
|
55 | + $this->cleanTmpFiles(); |
|
56 | 56 | |
57 | - if ($image->valid()) { |
|
58 | - $image->scaleDownToFit($maxX, $maxY); |
|
57 | + if ($image->valid()) { |
|
58 | + $image->scaleDownToFit($maxX, $maxY); |
|
59 | 59 | |
60 | - return $image; |
|
61 | - } |
|
62 | - return null; |
|
63 | - } |
|
60 | + return $image; |
|
61 | + } |
|
62 | + return null; |
|
63 | + } |
|
64 | 64 | } |
@@ -32,51 +32,51 @@ |
||
32 | 32 | |
33 | 33 | class CreateSessionTokenCommand extends ALoginCommand { |
34 | 34 | |
35 | - /** @var IConfig */ |
|
36 | - private $config; |
|
35 | + /** @var IConfig */ |
|
36 | + private $config; |
|
37 | 37 | |
38 | - /** @var Session */ |
|
39 | - private $userSession; |
|
38 | + /** @var Session */ |
|
39 | + private $userSession; |
|
40 | 40 | |
41 | - public function __construct(IConfig $config, |
|
42 | - Session $userSession) { |
|
43 | - $this->config = $config; |
|
44 | - $this->userSession = $userSession; |
|
45 | - } |
|
41 | + public function __construct(IConfig $config, |
|
42 | + Session $userSession) { |
|
43 | + $this->config = $config; |
|
44 | + $this->userSession = $userSession; |
|
45 | + } |
|
46 | 46 | |
47 | - public function process(LoginData $loginData): LoginResult { |
|
48 | - $tokenType = IToken::REMEMBER; |
|
49 | - if ($this->config->getSystemValueInt('remember_login_cookie_lifetime', 60 * 60 * 24 * 15) === 0) { |
|
50 | - $loginData->setRememberLogin(false); |
|
51 | - $tokenType = IToken::DO_NOT_REMEMBER; |
|
52 | - } |
|
47 | + public function process(LoginData $loginData): LoginResult { |
|
48 | + $tokenType = IToken::REMEMBER; |
|
49 | + if ($this->config->getSystemValueInt('remember_login_cookie_lifetime', 60 * 60 * 24 * 15) === 0) { |
|
50 | + $loginData->setRememberLogin(false); |
|
51 | + $tokenType = IToken::DO_NOT_REMEMBER; |
|
52 | + } |
|
53 | 53 | |
54 | - if ($loginData->getPassword() === '') { |
|
55 | - $this->userSession->createSessionToken( |
|
56 | - $loginData->getRequest(), |
|
57 | - $loginData->getUser()->getUID(), |
|
58 | - $loginData->getUsername(), |
|
59 | - null, |
|
60 | - $tokenType |
|
61 | - ); |
|
62 | - $this->userSession->updateTokens( |
|
63 | - $loginData->getUser()->getUID(), |
|
64 | - '' |
|
65 | - ); |
|
66 | - } else { |
|
67 | - $this->userSession->createSessionToken( |
|
68 | - $loginData->getRequest(), |
|
69 | - $loginData->getUser()->getUID(), |
|
70 | - $loginData->getUsername(), |
|
71 | - $loginData->getPassword(), |
|
72 | - $tokenType |
|
73 | - ); |
|
74 | - $this->userSession->updateTokens( |
|
75 | - $loginData->getUser()->getUID(), |
|
76 | - $loginData->getPassword() |
|
77 | - ); |
|
78 | - } |
|
54 | + if ($loginData->getPassword() === '') { |
|
55 | + $this->userSession->createSessionToken( |
|
56 | + $loginData->getRequest(), |
|
57 | + $loginData->getUser()->getUID(), |
|
58 | + $loginData->getUsername(), |
|
59 | + null, |
|
60 | + $tokenType |
|
61 | + ); |
|
62 | + $this->userSession->updateTokens( |
|
63 | + $loginData->getUser()->getUID(), |
|
64 | + '' |
|
65 | + ); |
|
66 | + } else { |
|
67 | + $this->userSession->createSessionToken( |
|
68 | + $loginData->getRequest(), |
|
69 | + $loginData->getUser()->getUID(), |
|
70 | + $loginData->getUsername(), |
|
71 | + $loginData->getPassword(), |
|
72 | + $tokenType |
|
73 | + ); |
|
74 | + $this->userSession->updateTokens( |
|
75 | + $loginData->getUser()->getUID(), |
|
76 | + $loginData->getPassword() |
|
77 | + ); |
|
78 | + } |
|
79 | 79 | |
80 | - return $this->processNextOrFinishSuccessfully($loginData); |
|
81 | - } |
|
80 | + return $this->processNextOrFinishSuccessfully($loginData); |
|
81 | + } |
|
82 | 82 | } |
@@ -57,383 +57,383 @@ |
||
57 | 57 | |
58 | 58 | class ShareesAPIController extends OCSController { |
59 | 59 | |
60 | - /** @var string */ |
|
61 | - protected $userId; |
|
62 | - |
|
63 | - /** @var IConfig */ |
|
64 | - protected $config; |
|
65 | - |
|
66 | - /** @var IURLGenerator */ |
|
67 | - protected $urlGenerator; |
|
68 | - |
|
69 | - /** @var IManager */ |
|
70 | - protected $shareManager; |
|
71 | - |
|
72 | - /** @var int */ |
|
73 | - protected $offset = 0; |
|
74 | - |
|
75 | - /** @var int */ |
|
76 | - protected $limit = 10; |
|
77 | - |
|
78 | - /** @var array */ |
|
79 | - protected $result = [ |
|
80 | - 'exact' => [ |
|
81 | - 'users' => [], |
|
82 | - 'groups' => [], |
|
83 | - 'remotes' => [], |
|
84 | - 'remote_groups' => [], |
|
85 | - 'emails' => [], |
|
86 | - 'circles' => [], |
|
87 | - 'rooms' => [], |
|
88 | - 'deck' => [], |
|
89 | - ], |
|
90 | - 'users' => [], |
|
91 | - 'groups' => [], |
|
92 | - 'remotes' => [], |
|
93 | - 'remote_groups' => [], |
|
94 | - 'emails' => [], |
|
95 | - 'lookup' => [], |
|
96 | - 'circles' => [], |
|
97 | - 'rooms' => [], |
|
98 | - 'deck' => [], |
|
99 | - 'lookupEnabled' => false, |
|
100 | - ]; |
|
101 | - |
|
102 | - protected $reachedEndFor = []; |
|
103 | - /** @var ISearch */ |
|
104 | - private $collaboratorSearch; |
|
105 | - |
|
106 | - /** |
|
107 | - * @param string $UserId |
|
108 | - * @param string $appName |
|
109 | - * @param IRequest $request |
|
110 | - * @param IConfig $config |
|
111 | - * @param IURLGenerator $urlGenerator |
|
112 | - * @param IManager $shareManager |
|
113 | - * @param ISearch $collaboratorSearch |
|
114 | - */ |
|
115 | - public function __construct( |
|
116 | - $UserId, |
|
117 | - string $appName, |
|
118 | - IRequest $request, |
|
119 | - IConfig $config, |
|
120 | - IURLGenerator $urlGenerator, |
|
121 | - IManager $shareManager, |
|
122 | - ISearch $collaboratorSearch |
|
123 | - ) { |
|
124 | - parent::__construct($appName, $request); |
|
125 | - $this->userId = $UserId; |
|
126 | - $this->config = $config; |
|
127 | - $this->urlGenerator = $urlGenerator; |
|
128 | - $this->shareManager = $shareManager; |
|
129 | - $this->collaboratorSearch = $collaboratorSearch; |
|
130 | - } |
|
131 | - |
|
132 | - /** |
|
133 | - * @NoAdminRequired |
|
134 | - * |
|
135 | - * @param string $search |
|
136 | - * @param string $itemType |
|
137 | - * @param int $page |
|
138 | - * @param int $perPage |
|
139 | - * @param int|int[] $shareType |
|
140 | - * @param bool $lookup |
|
141 | - * @return DataResponse |
|
142 | - * @throws OCSBadRequestException |
|
143 | - */ |
|
144 | - public function search(string $search = '', string $itemType = null, int $page = 1, int $perPage = 200, $shareType = null, bool $lookup = true): DataResponse { |
|
145 | - |
|
146 | - // only search for string larger than a given threshold |
|
147 | - $threshold = $this->config->getSystemValueInt('sharing.minSearchStringLength', 0); |
|
148 | - if (strlen($search) < $threshold) { |
|
149 | - return new DataResponse($this->result); |
|
150 | - } |
|
151 | - |
|
152 | - // never return more than the max. number of results configured in the config.php |
|
153 | - $maxResults = $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT); |
|
154 | - if ($maxResults > 0) { |
|
155 | - $perPage = min($perPage, $maxResults); |
|
156 | - } |
|
157 | - if ($perPage <= 0) { |
|
158 | - throw new OCSBadRequestException('Invalid perPage argument'); |
|
159 | - } |
|
160 | - if ($page <= 0) { |
|
161 | - throw new OCSBadRequestException('Invalid page'); |
|
162 | - } |
|
163 | - |
|
164 | - $shareTypes = [ |
|
165 | - IShare::TYPE_USER, |
|
166 | - ]; |
|
167 | - |
|
168 | - if ($itemType === null) { |
|
169 | - throw new OCSBadRequestException('Missing itemType'); |
|
170 | - } elseif ($itemType === 'file' || $itemType === 'folder') { |
|
171 | - if ($this->shareManager->allowGroupSharing()) { |
|
172 | - $shareTypes[] = IShare::TYPE_GROUP; |
|
173 | - } |
|
174 | - |
|
175 | - if ($this->isRemoteSharingAllowed($itemType)) { |
|
176 | - $shareTypes[] = IShare::TYPE_REMOTE; |
|
177 | - } |
|
178 | - |
|
179 | - if ($this->isRemoteGroupSharingAllowed($itemType)) { |
|
180 | - $shareTypes[] = IShare::TYPE_REMOTE_GROUP; |
|
181 | - } |
|
182 | - |
|
183 | - if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) { |
|
184 | - $shareTypes[] = IShare::TYPE_EMAIL; |
|
185 | - } |
|
186 | - |
|
187 | - if ($this->shareManager->shareProviderExists(IShare::TYPE_ROOM)) { |
|
188 | - $shareTypes[] = IShare::TYPE_ROOM; |
|
189 | - } |
|
190 | - |
|
191 | - if ($this->shareManager->shareProviderExists(IShare::TYPE_DECK)) { |
|
192 | - $shareTypes[] = IShare::TYPE_DECK; |
|
193 | - } |
|
194 | - } else { |
|
195 | - $shareTypes[] = IShare::TYPE_GROUP; |
|
196 | - $shareTypes[] = IShare::TYPE_EMAIL; |
|
197 | - } |
|
198 | - |
|
199 | - // FIXME: DI |
|
200 | - if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) { |
|
201 | - $shareTypes[] = IShare::TYPE_CIRCLE; |
|
202 | - } |
|
203 | - |
|
204 | - if ($this->shareManager->shareProviderExists(IShare::TYPE_DECK)) { |
|
205 | - $shareTypes[] = IShare::TYPE_DECK; |
|
206 | - } |
|
207 | - |
|
208 | - if ($shareType !== null && is_array($shareType)) { |
|
209 | - $shareTypes = array_intersect($shareTypes, $shareType); |
|
210 | - } elseif (is_numeric($shareType)) { |
|
211 | - $shareTypes = array_intersect($shareTypes, [(int) $shareType]); |
|
212 | - } |
|
213 | - sort($shareTypes); |
|
214 | - |
|
215 | - $this->limit = $perPage; |
|
216 | - $this->offset = $perPage * ($page - 1); |
|
217 | - |
|
218 | - // In global scale mode we always search the loogup server |
|
219 | - if ($this->config->getSystemValueBool('gs.enabled', false)) { |
|
220 | - $lookup = true; |
|
221 | - $this->result['lookupEnabled'] = true; |
|
222 | - } else { |
|
223 | - $this->result['lookupEnabled'] = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes') === 'yes'; |
|
224 | - } |
|
225 | - |
|
226 | - [$result, $hasMoreResults] = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $this->limit, $this->offset); |
|
227 | - |
|
228 | - // extra treatment for 'exact' subarray, with a single merge expected keys might be lost |
|
229 | - if (isset($result['exact'])) { |
|
230 | - $result['exact'] = array_merge($this->result['exact'], $result['exact']); |
|
231 | - } |
|
232 | - $this->result = array_merge($this->result, $result); |
|
233 | - $response = new DataResponse($this->result); |
|
234 | - |
|
235 | - if ($hasMoreResults) { |
|
236 | - $response->addHeader('Link', $this->getPaginationLink($page, [ |
|
237 | - 'search' => $search, |
|
238 | - 'itemType' => $itemType, |
|
239 | - 'shareType' => $shareTypes, |
|
240 | - 'perPage' => $perPage, |
|
241 | - ])); |
|
242 | - } |
|
243 | - |
|
244 | - return $response; |
|
245 | - } |
|
246 | - |
|
247 | - /** |
|
248 | - * @param string $user |
|
249 | - * @param int $shareType |
|
250 | - * |
|
251 | - * @return Generator<array<string>> |
|
252 | - */ |
|
253 | - private function getAllShareesByType(string $user, int $shareType): Generator { |
|
254 | - $offset = 0; |
|
255 | - $pageSize = 50; |
|
256 | - |
|
257 | - while (count($page = $this->shareManager->getSharesBy( |
|
258 | - $user, |
|
259 | - $shareType, |
|
260 | - null, |
|
261 | - false, |
|
262 | - $pageSize, |
|
263 | - $offset |
|
264 | - ))) { |
|
265 | - foreach ($page as $share) { |
|
266 | - yield [$share->getSharedWith(), $share->getSharedWithDisplayName() ?? $share->getSharedWith()]; |
|
267 | - } |
|
268 | - |
|
269 | - $offset += $pageSize; |
|
270 | - } |
|
271 | - } |
|
272 | - |
|
273 | - private function sortShareesByFrequency(array $sharees): array { |
|
274 | - usort($sharees, function (array $s1, array $s2) { |
|
275 | - return $s2['count'] - $s1['count']; |
|
276 | - }); |
|
277 | - return $sharees; |
|
278 | - } |
|
279 | - |
|
280 | - private $searchResultTypeMap = [ |
|
281 | - IShare::TYPE_USER => 'users', |
|
282 | - IShare::TYPE_GROUP => 'groups', |
|
283 | - IShare::TYPE_REMOTE => 'remotes', |
|
284 | - IShare::TYPE_REMOTE_GROUP => 'remote_groups', |
|
285 | - IShare::TYPE_EMAIL => 'emails', |
|
286 | - ]; |
|
287 | - |
|
288 | - private function getAllSharees(string $user, array $shareTypes): ISearchResult { |
|
289 | - $result = []; |
|
290 | - foreach ($shareTypes as $shareType) { |
|
291 | - $sharees = $this->getAllShareesByType($user, $shareType); |
|
292 | - $shareTypeResults = []; |
|
293 | - foreach ($sharees as [$sharee, $displayname]) { |
|
294 | - if (!isset($this->searchResultTypeMap[$shareType])) { |
|
295 | - continue; |
|
296 | - } |
|
297 | - |
|
298 | - if (!isset($shareTypeResults[$sharee])) { |
|
299 | - $shareTypeResults[$sharee] = [ |
|
300 | - 'count' => 1, |
|
301 | - 'label' => $displayname, |
|
302 | - 'value' => [ |
|
303 | - 'shareType' => $shareType, |
|
304 | - 'shareWith' => $sharee, |
|
305 | - ], |
|
306 | - ]; |
|
307 | - } else { |
|
308 | - $shareTypeResults[$sharee]['count']++; |
|
309 | - } |
|
310 | - } |
|
311 | - $result = array_merge($result, array_values($shareTypeResults)); |
|
312 | - } |
|
313 | - |
|
314 | - $top5 = array_slice( |
|
315 | - $this->sortShareesByFrequency($result), |
|
316 | - 0, |
|
317 | - 5 |
|
318 | - ); |
|
319 | - |
|
320 | - $searchResult = new SearchResult(); |
|
321 | - foreach ($this->searchResultTypeMap as $int => $str) { |
|
322 | - $searchResult->addResultSet(new SearchResultType($str), [], []); |
|
323 | - foreach ($top5 as $x) { |
|
324 | - if ($x['value']['shareType'] === $int) { |
|
325 | - $searchResult->addResultSet(new SearchResultType($str), [], [$x]); |
|
326 | - } |
|
327 | - } |
|
328 | - } |
|
329 | - return $searchResult; |
|
330 | - } |
|
331 | - |
|
332 | - /** |
|
333 | - * @NoAdminRequired |
|
334 | - * |
|
335 | - * @param string $itemType |
|
336 | - * @return DataResponse |
|
337 | - * @throws OCSBadRequestException |
|
338 | - */ |
|
339 | - public function findRecommended(string $itemType = null, $shareType = null): DataResponse { |
|
340 | - $shareTypes = [ |
|
341 | - IShare::TYPE_USER, |
|
342 | - ]; |
|
343 | - |
|
344 | - if ($itemType === null) { |
|
345 | - throw new OCSBadRequestException('Missing itemType'); |
|
346 | - } elseif ($itemType === 'file' || $itemType === 'folder') { |
|
347 | - if ($this->shareManager->allowGroupSharing()) { |
|
348 | - $shareTypes[] = IShare::TYPE_GROUP; |
|
349 | - } |
|
350 | - |
|
351 | - if ($this->isRemoteSharingAllowed($itemType)) { |
|
352 | - $shareTypes[] = IShare::TYPE_REMOTE; |
|
353 | - } |
|
354 | - |
|
355 | - if ($this->isRemoteGroupSharingAllowed($itemType)) { |
|
356 | - $shareTypes[] = IShare::TYPE_REMOTE_GROUP; |
|
357 | - } |
|
358 | - |
|
359 | - if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) { |
|
360 | - $shareTypes[] = IShare::TYPE_EMAIL; |
|
361 | - } |
|
362 | - |
|
363 | - if ($this->shareManager->shareProviderExists(IShare::TYPE_ROOM)) { |
|
364 | - $shareTypes[] = IShare::TYPE_ROOM; |
|
365 | - } |
|
366 | - } else { |
|
367 | - $shareTypes[] = IShare::TYPE_GROUP; |
|
368 | - $shareTypes[] = IShare::TYPE_EMAIL; |
|
369 | - } |
|
370 | - |
|
371 | - // FIXME: DI |
|
372 | - if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) { |
|
373 | - $shareTypes[] = IShare::TYPE_CIRCLE; |
|
374 | - } |
|
375 | - |
|
376 | - if (isset($_GET['shareType']) && is_array($_GET['shareType'])) { |
|
377 | - $shareTypes = array_intersect($shareTypes, $_GET['shareType']); |
|
378 | - sort($shareTypes); |
|
379 | - } elseif (is_numeric($shareType)) { |
|
380 | - $shareTypes = array_intersect($shareTypes, [(int) $shareType]); |
|
381 | - sort($shareTypes); |
|
382 | - } |
|
383 | - |
|
384 | - return new DataResponse( |
|
385 | - $this->getAllSharees($this->userId, $shareTypes)->asArray() |
|
386 | - ); |
|
387 | - } |
|
388 | - |
|
389 | - /** |
|
390 | - * Method to get out the static call for better testing |
|
391 | - * |
|
392 | - * @param string $itemType |
|
393 | - * @return bool |
|
394 | - */ |
|
395 | - protected function isRemoteSharingAllowed(string $itemType): bool { |
|
396 | - try { |
|
397 | - // FIXME: static foo makes unit testing unnecessarily difficult |
|
398 | - $backend = \OC\Share\Share::getBackend($itemType); |
|
399 | - return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE); |
|
400 | - } catch (\Exception $e) { |
|
401 | - return false; |
|
402 | - } |
|
403 | - } |
|
404 | - |
|
405 | - protected function isRemoteGroupSharingAllowed(string $itemType): bool { |
|
406 | - try { |
|
407 | - // FIXME: static foo makes unit testing unnecessarily difficult |
|
408 | - $backend = \OC\Share\Share::getBackend($itemType); |
|
409 | - return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE_GROUP); |
|
410 | - } catch (\Exception $e) { |
|
411 | - return false; |
|
412 | - } |
|
413 | - } |
|
414 | - |
|
415 | - |
|
416 | - /** |
|
417 | - * Generates a bunch of pagination links for the current page |
|
418 | - * |
|
419 | - * @param int $page Current page |
|
420 | - * @param array $params Parameters for the URL |
|
421 | - * @return string |
|
422 | - */ |
|
423 | - protected function getPaginationLink(int $page, array $params): string { |
|
424 | - if ($this->isV2()) { |
|
425 | - $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
426 | - } else { |
|
427 | - $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
428 | - } |
|
429 | - $params['page'] = $page + 1; |
|
430 | - return '<' . $url . http_build_query($params) . '>; rel="next"'; |
|
431 | - } |
|
432 | - |
|
433 | - /** |
|
434 | - * @return bool |
|
435 | - */ |
|
436 | - protected function isV2(): bool { |
|
437 | - return $this->request->getScriptName() === '/ocs/v2.php'; |
|
438 | - } |
|
60 | + /** @var string */ |
|
61 | + protected $userId; |
|
62 | + |
|
63 | + /** @var IConfig */ |
|
64 | + protected $config; |
|
65 | + |
|
66 | + /** @var IURLGenerator */ |
|
67 | + protected $urlGenerator; |
|
68 | + |
|
69 | + /** @var IManager */ |
|
70 | + protected $shareManager; |
|
71 | + |
|
72 | + /** @var int */ |
|
73 | + protected $offset = 0; |
|
74 | + |
|
75 | + /** @var int */ |
|
76 | + protected $limit = 10; |
|
77 | + |
|
78 | + /** @var array */ |
|
79 | + protected $result = [ |
|
80 | + 'exact' => [ |
|
81 | + 'users' => [], |
|
82 | + 'groups' => [], |
|
83 | + 'remotes' => [], |
|
84 | + 'remote_groups' => [], |
|
85 | + 'emails' => [], |
|
86 | + 'circles' => [], |
|
87 | + 'rooms' => [], |
|
88 | + 'deck' => [], |
|
89 | + ], |
|
90 | + 'users' => [], |
|
91 | + 'groups' => [], |
|
92 | + 'remotes' => [], |
|
93 | + 'remote_groups' => [], |
|
94 | + 'emails' => [], |
|
95 | + 'lookup' => [], |
|
96 | + 'circles' => [], |
|
97 | + 'rooms' => [], |
|
98 | + 'deck' => [], |
|
99 | + 'lookupEnabled' => false, |
|
100 | + ]; |
|
101 | + |
|
102 | + protected $reachedEndFor = []; |
|
103 | + /** @var ISearch */ |
|
104 | + private $collaboratorSearch; |
|
105 | + |
|
106 | + /** |
|
107 | + * @param string $UserId |
|
108 | + * @param string $appName |
|
109 | + * @param IRequest $request |
|
110 | + * @param IConfig $config |
|
111 | + * @param IURLGenerator $urlGenerator |
|
112 | + * @param IManager $shareManager |
|
113 | + * @param ISearch $collaboratorSearch |
|
114 | + */ |
|
115 | + public function __construct( |
|
116 | + $UserId, |
|
117 | + string $appName, |
|
118 | + IRequest $request, |
|
119 | + IConfig $config, |
|
120 | + IURLGenerator $urlGenerator, |
|
121 | + IManager $shareManager, |
|
122 | + ISearch $collaboratorSearch |
|
123 | + ) { |
|
124 | + parent::__construct($appName, $request); |
|
125 | + $this->userId = $UserId; |
|
126 | + $this->config = $config; |
|
127 | + $this->urlGenerator = $urlGenerator; |
|
128 | + $this->shareManager = $shareManager; |
|
129 | + $this->collaboratorSearch = $collaboratorSearch; |
|
130 | + } |
|
131 | + |
|
132 | + /** |
|
133 | + * @NoAdminRequired |
|
134 | + * |
|
135 | + * @param string $search |
|
136 | + * @param string $itemType |
|
137 | + * @param int $page |
|
138 | + * @param int $perPage |
|
139 | + * @param int|int[] $shareType |
|
140 | + * @param bool $lookup |
|
141 | + * @return DataResponse |
|
142 | + * @throws OCSBadRequestException |
|
143 | + */ |
|
144 | + public function search(string $search = '', string $itemType = null, int $page = 1, int $perPage = 200, $shareType = null, bool $lookup = true): DataResponse { |
|
145 | + |
|
146 | + // only search for string larger than a given threshold |
|
147 | + $threshold = $this->config->getSystemValueInt('sharing.minSearchStringLength', 0); |
|
148 | + if (strlen($search) < $threshold) { |
|
149 | + return new DataResponse($this->result); |
|
150 | + } |
|
151 | + |
|
152 | + // never return more than the max. number of results configured in the config.php |
|
153 | + $maxResults = $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT); |
|
154 | + if ($maxResults > 0) { |
|
155 | + $perPage = min($perPage, $maxResults); |
|
156 | + } |
|
157 | + if ($perPage <= 0) { |
|
158 | + throw new OCSBadRequestException('Invalid perPage argument'); |
|
159 | + } |
|
160 | + if ($page <= 0) { |
|
161 | + throw new OCSBadRequestException('Invalid page'); |
|
162 | + } |
|
163 | + |
|
164 | + $shareTypes = [ |
|
165 | + IShare::TYPE_USER, |
|
166 | + ]; |
|
167 | + |
|
168 | + if ($itemType === null) { |
|
169 | + throw new OCSBadRequestException('Missing itemType'); |
|
170 | + } elseif ($itemType === 'file' || $itemType === 'folder') { |
|
171 | + if ($this->shareManager->allowGroupSharing()) { |
|
172 | + $shareTypes[] = IShare::TYPE_GROUP; |
|
173 | + } |
|
174 | + |
|
175 | + if ($this->isRemoteSharingAllowed($itemType)) { |
|
176 | + $shareTypes[] = IShare::TYPE_REMOTE; |
|
177 | + } |
|
178 | + |
|
179 | + if ($this->isRemoteGroupSharingAllowed($itemType)) { |
|
180 | + $shareTypes[] = IShare::TYPE_REMOTE_GROUP; |
|
181 | + } |
|
182 | + |
|
183 | + if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) { |
|
184 | + $shareTypes[] = IShare::TYPE_EMAIL; |
|
185 | + } |
|
186 | + |
|
187 | + if ($this->shareManager->shareProviderExists(IShare::TYPE_ROOM)) { |
|
188 | + $shareTypes[] = IShare::TYPE_ROOM; |
|
189 | + } |
|
190 | + |
|
191 | + if ($this->shareManager->shareProviderExists(IShare::TYPE_DECK)) { |
|
192 | + $shareTypes[] = IShare::TYPE_DECK; |
|
193 | + } |
|
194 | + } else { |
|
195 | + $shareTypes[] = IShare::TYPE_GROUP; |
|
196 | + $shareTypes[] = IShare::TYPE_EMAIL; |
|
197 | + } |
|
198 | + |
|
199 | + // FIXME: DI |
|
200 | + if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) { |
|
201 | + $shareTypes[] = IShare::TYPE_CIRCLE; |
|
202 | + } |
|
203 | + |
|
204 | + if ($this->shareManager->shareProviderExists(IShare::TYPE_DECK)) { |
|
205 | + $shareTypes[] = IShare::TYPE_DECK; |
|
206 | + } |
|
207 | + |
|
208 | + if ($shareType !== null && is_array($shareType)) { |
|
209 | + $shareTypes = array_intersect($shareTypes, $shareType); |
|
210 | + } elseif (is_numeric($shareType)) { |
|
211 | + $shareTypes = array_intersect($shareTypes, [(int) $shareType]); |
|
212 | + } |
|
213 | + sort($shareTypes); |
|
214 | + |
|
215 | + $this->limit = $perPage; |
|
216 | + $this->offset = $perPage * ($page - 1); |
|
217 | + |
|
218 | + // In global scale mode we always search the loogup server |
|
219 | + if ($this->config->getSystemValueBool('gs.enabled', false)) { |
|
220 | + $lookup = true; |
|
221 | + $this->result['lookupEnabled'] = true; |
|
222 | + } else { |
|
223 | + $this->result['lookupEnabled'] = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes') === 'yes'; |
|
224 | + } |
|
225 | + |
|
226 | + [$result, $hasMoreResults] = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $this->limit, $this->offset); |
|
227 | + |
|
228 | + // extra treatment for 'exact' subarray, with a single merge expected keys might be lost |
|
229 | + if (isset($result['exact'])) { |
|
230 | + $result['exact'] = array_merge($this->result['exact'], $result['exact']); |
|
231 | + } |
|
232 | + $this->result = array_merge($this->result, $result); |
|
233 | + $response = new DataResponse($this->result); |
|
234 | + |
|
235 | + if ($hasMoreResults) { |
|
236 | + $response->addHeader('Link', $this->getPaginationLink($page, [ |
|
237 | + 'search' => $search, |
|
238 | + 'itemType' => $itemType, |
|
239 | + 'shareType' => $shareTypes, |
|
240 | + 'perPage' => $perPage, |
|
241 | + ])); |
|
242 | + } |
|
243 | + |
|
244 | + return $response; |
|
245 | + } |
|
246 | + |
|
247 | + /** |
|
248 | + * @param string $user |
|
249 | + * @param int $shareType |
|
250 | + * |
|
251 | + * @return Generator<array<string>> |
|
252 | + */ |
|
253 | + private function getAllShareesByType(string $user, int $shareType): Generator { |
|
254 | + $offset = 0; |
|
255 | + $pageSize = 50; |
|
256 | + |
|
257 | + while (count($page = $this->shareManager->getSharesBy( |
|
258 | + $user, |
|
259 | + $shareType, |
|
260 | + null, |
|
261 | + false, |
|
262 | + $pageSize, |
|
263 | + $offset |
|
264 | + ))) { |
|
265 | + foreach ($page as $share) { |
|
266 | + yield [$share->getSharedWith(), $share->getSharedWithDisplayName() ?? $share->getSharedWith()]; |
|
267 | + } |
|
268 | + |
|
269 | + $offset += $pageSize; |
|
270 | + } |
|
271 | + } |
|
272 | + |
|
273 | + private function sortShareesByFrequency(array $sharees): array { |
|
274 | + usort($sharees, function (array $s1, array $s2) { |
|
275 | + return $s2['count'] - $s1['count']; |
|
276 | + }); |
|
277 | + return $sharees; |
|
278 | + } |
|
279 | + |
|
280 | + private $searchResultTypeMap = [ |
|
281 | + IShare::TYPE_USER => 'users', |
|
282 | + IShare::TYPE_GROUP => 'groups', |
|
283 | + IShare::TYPE_REMOTE => 'remotes', |
|
284 | + IShare::TYPE_REMOTE_GROUP => 'remote_groups', |
|
285 | + IShare::TYPE_EMAIL => 'emails', |
|
286 | + ]; |
|
287 | + |
|
288 | + private function getAllSharees(string $user, array $shareTypes): ISearchResult { |
|
289 | + $result = []; |
|
290 | + foreach ($shareTypes as $shareType) { |
|
291 | + $sharees = $this->getAllShareesByType($user, $shareType); |
|
292 | + $shareTypeResults = []; |
|
293 | + foreach ($sharees as [$sharee, $displayname]) { |
|
294 | + if (!isset($this->searchResultTypeMap[$shareType])) { |
|
295 | + continue; |
|
296 | + } |
|
297 | + |
|
298 | + if (!isset($shareTypeResults[$sharee])) { |
|
299 | + $shareTypeResults[$sharee] = [ |
|
300 | + 'count' => 1, |
|
301 | + 'label' => $displayname, |
|
302 | + 'value' => [ |
|
303 | + 'shareType' => $shareType, |
|
304 | + 'shareWith' => $sharee, |
|
305 | + ], |
|
306 | + ]; |
|
307 | + } else { |
|
308 | + $shareTypeResults[$sharee]['count']++; |
|
309 | + } |
|
310 | + } |
|
311 | + $result = array_merge($result, array_values($shareTypeResults)); |
|
312 | + } |
|
313 | + |
|
314 | + $top5 = array_slice( |
|
315 | + $this->sortShareesByFrequency($result), |
|
316 | + 0, |
|
317 | + 5 |
|
318 | + ); |
|
319 | + |
|
320 | + $searchResult = new SearchResult(); |
|
321 | + foreach ($this->searchResultTypeMap as $int => $str) { |
|
322 | + $searchResult->addResultSet(new SearchResultType($str), [], []); |
|
323 | + foreach ($top5 as $x) { |
|
324 | + if ($x['value']['shareType'] === $int) { |
|
325 | + $searchResult->addResultSet(new SearchResultType($str), [], [$x]); |
|
326 | + } |
|
327 | + } |
|
328 | + } |
|
329 | + return $searchResult; |
|
330 | + } |
|
331 | + |
|
332 | + /** |
|
333 | + * @NoAdminRequired |
|
334 | + * |
|
335 | + * @param string $itemType |
|
336 | + * @return DataResponse |
|
337 | + * @throws OCSBadRequestException |
|
338 | + */ |
|
339 | + public function findRecommended(string $itemType = null, $shareType = null): DataResponse { |
|
340 | + $shareTypes = [ |
|
341 | + IShare::TYPE_USER, |
|
342 | + ]; |
|
343 | + |
|
344 | + if ($itemType === null) { |
|
345 | + throw new OCSBadRequestException('Missing itemType'); |
|
346 | + } elseif ($itemType === 'file' || $itemType === 'folder') { |
|
347 | + if ($this->shareManager->allowGroupSharing()) { |
|
348 | + $shareTypes[] = IShare::TYPE_GROUP; |
|
349 | + } |
|
350 | + |
|
351 | + if ($this->isRemoteSharingAllowed($itemType)) { |
|
352 | + $shareTypes[] = IShare::TYPE_REMOTE; |
|
353 | + } |
|
354 | + |
|
355 | + if ($this->isRemoteGroupSharingAllowed($itemType)) { |
|
356 | + $shareTypes[] = IShare::TYPE_REMOTE_GROUP; |
|
357 | + } |
|
358 | + |
|
359 | + if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) { |
|
360 | + $shareTypes[] = IShare::TYPE_EMAIL; |
|
361 | + } |
|
362 | + |
|
363 | + if ($this->shareManager->shareProviderExists(IShare::TYPE_ROOM)) { |
|
364 | + $shareTypes[] = IShare::TYPE_ROOM; |
|
365 | + } |
|
366 | + } else { |
|
367 | + $shareTypes[] = IShare::TYPE_GROUP; |
|
368 | + $shareTypes[] = IShare::TYPE_EMAIL; |
|
369 | + } |
|
370 | + |
|
371 | + // FIXME: DI |
|
372 | + if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) { |
|
373 | + $shareTypes[] = IShare::TYPE_CIRCLE; |
|
374 | + } |
|
375 | + |
|
376 | + if (isset($_GET['shareType']) && is_array($_GET['shareType'])) { |
|
377 | + $shareTypes = array_intersect($shareTypes, $_GET['shareType']); |
|
378 | + sort($shareTypes); |
|
379 | + } elseif (is_numeric($shareType)) { |
|
380 | + $shareTypes = array_intersect($shareTypes, [(int) $shareType]); |
|
381 | + sort($shareTypes); |
|
382 | + } |
|
383 | + |
|
384 | + return new DataResponse( |
|
385 | + $this->getAllSharees($this->userId, $shareTypes)->asArray() |
|
386 | + ); |
|
387 | + } |
|
388 | + |
|
389 | + /** |
|
390 | + * Method to get out the static call for better testing |
|
391 | + * |
|
392 | + * @param string $itemType |
|
393 | + * @return bool |
|
394 | + */ |
|
395 | + protected function isRemoteSharingAllowed(string $itemType): bool { |
|
396 | + try { |
|
397 | + // FIXME: static foo makes unit testing unnecessarily difficult |
|
398 | + $backend = \OC\Share\Share::getBackend($itemType); |
|
399 | + return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE); |
|
400 | + } catch (\Exception $e) { |
|
401 | + return false; |
|
402 | + } |
|
403 | + } |
|
404 | + |
|
405 | + protected function isRemoteGroupSharingAllowed(string $itemType): bool { |
|
406 | + try { |
|
407 | + // FIXME: static foo makes unit testing unnecessarily difficult |
|
408 | + $backend = \OC\Share\Share::getBackend($itemType); |
|
409 | + return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE_GROUP); |
|
410 | + } catch (\Exception $e) { |
|
411 | + return false; |
|
412 | + } |
|
413 | + } |
|
414 | + |
|
415 | + |
|
416 | + /** |
|
417 | + * Generates a bunch of pagination links for the current page |
|
418 | + * |
|
419 | + * @param int $page Current page |
|
420 | + * @param array $params Parameters for the URL |
|
421 | + * @return string |
|
422 | + */ |
|
423 | + protected function getPaginationLink(int $page, array $params): string { |
|
424 | + if ($this->isV2()) { |
|
425 | + $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
426 | + } else { |
|
427 | + $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
428 | + } |
|
429 | + $params['page'] = $page + 1; |
|
430 | + return '<' . $url . http_build_query($params) . '>; rel="next"'; |
|
431 | + } |
|
432 | + |
|
433 | + /** |
|
434 | + * @return bool |
|
435 | + */ |
|
436 | + protected function isV2(): bool { |
|
437 | + return $this->request->getScriptName() === '/ocs/v2.php'; |
|
438 | + } |
|
439 | 439 | } |
@@ -271,7 +271,7 @@ discard block |
||
271 | 271 | } |
272 | 272 | |
273 | 273 | private function sortShareesByFrequency(array $sharees): array { |
274 | - usort($sharees, function (array $s1, array $s2) { |
|
274 | + usort($sharees, function(array $s1, array $s2) { |
|
275 | 275 | return $s2['count'] - $s1['count']; |
276 | 276 | }); |
277 | 277 | return $sharees; |
@@ -422,12 +422,12 @@ discard block |
||
422 | 422 | */ |
423 | 423 | protected function getPaginationLink(int $page, array $params): string { |
424 | 424 | if ($this->isV2()) { |
425 | - $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
425 | + $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees').'?'; |
|
426 | 426 | } else { |
427 | - $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
427 | + $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees').'?'; |
|
428 | 428 | } |
429 | 429 | $params['page'] = $page + 1; |
430 | - return '<' . $url . http_build_query($params) . '>; rel="next"'; |
|
430 | + return '<'.$url.http_build_query($params).'>; rel="next"'; |
|
431 | 431 | } |
432 | 432 | |
433 | 433 | /** |