@@ -42,366 +42,366 @@ |
||
42 | 42 | |
43 | 43 | /** @template-implements IEventListener<BeforeNodeCopiedEvent|BeforeNodeDeletedEvent|BeforeNodeRenamedEvent|BeforeNodeTouchedEvent|BeforeNodeWrittenEvent|NodeCopiedEvent|NodeCreatedEvent|NodeDeletedEvent|NodeRenamedEvent|NodeTouchedEvent|NodeWrittenEvent> */ |
44 | 44 | class FileEventsListener implements IEventListener { |
45 | - /** |
|
46 | - * @var array<int, array> |
|
47 | - */ |
|
48 | - private array $writeHookInfo = []; |
|
49 | - /** |
|
50 | - * @var array<int, Node> |
|
51 | - */ |
|
52 | - private array $nodesTouched = []; |
|
53 | - /** |
|
54 | - * @var array<string, Node> |
|
55 | - */ |
|
56 | - private array $versionsDeleted = []; |
|
57 | - |
|
58 | - public function __construct( |
|
59 | - private IRootFolder $rootFolder, |
|
60 | - private IVersionManager $versionManager, |
|
61 | - private IMimeTypeLoader $mimeTypeLoader, |
|
62 | - private IUserSession $userSession, |
|
63 | - private LoggerInterface $logger, |
|
64 | - ) { |
|
65 | - } |
|
66 | - |
|
67 | - public function handle(Event $event): void { |
|
68 | - if ($event instanceof NodeCreatedEvent) { |
|
69 | - $this->created($event->getNode()); |
|
70 | - } |
|
71 | - |
|
72 | - if ($event instanceof BeforeNodeTouchedEvent) { |
|
73 | - $this->pre_touch_hook($event->getNode()); |
|
74 | - } |
|
75 | - |
|
76 | - if ($event instanceof NodeTouchedEvent) { |
|
77 | - $this->touch_hook($event->getNode()); |
|
78 | - } |
|
79 | - |
|
80 | - if ($event instanceof BeforeNodeWrittenEvent) { |
|
81 | - $this->write_hook($event->getNode()); |
|
82 | - } |
|
83 | - |
|
84 | - if ($event instanceof NodeWrittenEvent) { |
|
85 | - $this->post_write_hook($event->getNode()); |
|
86 | - } |
|
87 | - |
|
88 | - if ($event instanceof BeforeNodeDeletedEvent) { |
|
89 | - $this->pre_remove_hook($event->getNode()); |
|
90 | - } |
|
91 | - |
|
92 | - if ($event instanceof NodeDeletedEvent) { |
|
93 | - $this->remove_hook($event->getNode()); |
|
94 | - } |
|
95 | - |
|
96 | - if ($event instanceof NodeRenamedEvent) { |
|
97 | - $this->rename_hook($event->getSource(), $event->getTarget()); |
|
98 | - } |
|
99 | - |
|
100 | - if ($event instanceof NodeCopiedEvent) { |
|
101 | - $this->copy_hook($event->getSource(), $event->getTarget()); |
|
102 | - } |
|
103 | - |
|
104 | - if ($event instanceof BeforeNodeRenamedEvent) { |
|
105 | - $this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget()); |
|
106 | - } |
|
107 | - |
|
108 | - if ($event instanceof BeforeNodeCopiedEvent) { |
|
109 | - $this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget()); |
|
110 | - } |
|
111 | - } |
|
112 | - |
|
113 | - public function pre_touch_hook(Node $node): void { |
|
114 | - // Do not handle folders. |
|
115 | - if ($node instanceof Folder) { |
|
116 | - return; |
|
117 | - } |
|
118 | - |
|
119 | - // $node is a non-existing on file creation. |
|
120 | - if ($node instanceof NonExistingFile) { |
|
121 | - return; |
|
122 | - } |
|
123 | - |
|
124 | - $this->nodesTouched[$node->getId()] = $node; |
|
125 | - } |
|
126 | - |
|
127 | - public function touch_hook(Node $node): void { |
|
128 | - $previousNode = $this->nodesTouched[$node->getId()] ?? null; |
|
129 | - |
|
130 | - if ($previousNode === null) { |
|
131 | - return; |
|
132 | - } |
|
133 | - |
|
134 | - unset($this->nodesTouched[$node->getId()]); |
|
135 | - |
|
136 | - try { |
|
137 | - if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { |
|
138 | - // We update the timestamp of the version entity associated with the previousNode. |
|
139 | - $this->versionManager->updateVersionEntity($node, $previousNode->getMTime(), ['timestamp' => $node->getMTime()]); |
|
140 | - } |
|
141 | - } catch (DbalException $ex) { |
|
142 | - // Ignore UniqueConstraintViolationException, as we are probably in the middle of a rollback |
|
143 | - // Where the previous node would temporary have the mtime of the old version, so the rollback touches it to fix it. |
|
144 | - if (!($ex->getPrevious() instanceof UniqueConstraintViolationException)) { |
|
145 | - throw $ex; |
|
146 | - } |
|
147 | - } catch (DoesNotExistException $ex) { |
|
148 | - // Ignore DoesNotExistException, as we are probably in the middle of a rollback |
|
149 | - // Where the previous node would temporary have a wrong mtime, so the rollback touches it to fix it. |
|
150 | - } |
|
151 | - } |
|
152 | - |
|
153 | - public function created(Node $node): void { |
|
154 | - // Do not handle folders. |
|
155 | - if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { |
|
156 | - $this->versionManager->createVersionEntity($node); |
|
157 | - } |
|
158 | - } |
|
159 | - |
|
160 | - /** |
|
161 | - * listen to write event. |
|
162 | - */ |
|
163 | - public function write_hook(Node $node): void { |
|
164 | - // Do not handle folders. |
|
165 | - if ($node instanceof Folder) { |
|
166 | - return; |
|
167 | - } |
|
168 | - |
|
169 | - // $node is a non-existing on file creation. |
|
170 | - if ($node instanceof NonExistingFile) { |
|
171 | - return; |
|
172 | - } |
|
173 | - |
|
174 | - $path = $this->getPathForNode($node); |
|
175 | - $result = Storage::store($path); |
|
176 | - |
|
177 | - // Store the result of the version creation so it can be used in post_write_hook. |
|
178 | - $this->writeHookInfo[$node->getId()] = [ |
|
179 | - 'previousNode' => $node, |
|
180 | - 'versionCreated' => $result !== false |
|
181 | - ]; |
|
182 | - } |
|
183 | - |
|
184 | - /** |
|
185 | - * listen to post_write event. |
|
186 | - */ |
|
187 | - public function post_write_hook(Node $node): void { |
|
188 | - // Do not handle folders. |
|
189 | - if ($node instanceof Folder) { |
|
190 | - return; |
|
191 | - } |
|
192 | - |
|
193 | - $writeHookInfo = $this->writeHookInfo[$node->getId()] ?? null; |
|
194 | - |
|
195 | - if ($writeHookInfo === null) { |
|
196 | - return; |
|
197 | - } |
|
198 | - |
|
199 | - if ( |
|
200 | - $writeHookInfo['versionCreated'] |
|
201 | - && $node->getMTime() !== $writeHookInfo['previousNode']->getMTime() |
|
202 | - ) { |
|
203 | - // If a new version was created, insert a version in the DB for the current content. |
|
204 | - // If both versions have the same mtime, it means the latest version file simply got overrode, |
|
205 | - // so no need to create a new version. |
|
206 | - $this->created($node); |
|
207 | - } else { |
|
208 | - try { |
|
209 | - // If no new version was stored in the FS, no new version should be added in the DB. |
|
210 | - // So we simply update the associated version. |
|
211 | - if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { |
|
212 | - $this->versionManager->updateVersionEntity( |
|
213 | - $node, |
|
214 | - $writeHookInfo['previousNode']->getMtime(), |
|
215 | - [ |
|
216 | - 'timestamp' => $node->getMTime(), |
|
217 | - 'size' => $node->getSize(), |
|
218 | - 'mimetype' => $this->mimeTypeLoader->getId($node->getMimetype()), |
|
219 | - ], |
|
220 | - ); |
|
221 | - } |
|
222 | - } catch (DoesNotExistException $e) { |
|
223 | - // This happens if the versions app was not enabled while the file was created or updated the last time. |
|
224 | - // meaning there is no such revision and we need to create this file. |
|
225 | - if ($writeHookInfo['versionCreated']) { |
|
226 | - $this->created($node); |
|
227 | - } else { |
|
228 | - // Normally this should not happen so we re-throw the exception to not hide any potential issues. |
|
229 | - throw $e; |
|
230 | - } |
|
231 | - } catch (Exception $e) { |
|
232 | - $this->logger->error('Failed to update existing version for ' . $node->getPath(), [ |
|
233 | - 'exception' => $e, |
|
234 | - 'versionCreated' => $writeHookInfo['versionCreated'], |
|
235 | - 'previousNode' => [ |
|
236 | - 'size' => $writeHookInfo['previousNode']->getSize(), |
|
237 | - 'mtime' => $writeHookInfo['previousNode']->getMTime(), |
|
238 | - ], |
|
239 | - 'node' => [ |
|
240 | - 'size' => $node->getSize(), |
|
241 | - 'mtime' => $node->getMTime(), |
|
242 | - ] |
|
243 | - ]); |
|
244 | - throw $e; |
|
245 | - } |
|
246 | - } |
|
247 | - |
|
248 | - unset($this->writeHookInfo[$node->getId()]); |
|
249 | - } |
|
250 | - |
|
251 | - /** |
|
252 | - * Erase versions of deleted file |
|
253 | - * |
|
254 | - * This function is connected to the delete signal of OC_Filesystem |
|
255 | - * cleanup the versions directory if the actual file gets deleted |
|
256 | - */ |
|
257 | - public function remove_hook(Node $node): void { |
|
258 | - // Need to normalize the path as there is an issue with path concatenation in View.php::getAbsolutePath. |
|
259 | - $path = Filesystem::normalizePath($node->getPath()); |
|
260 | - if (!array_key_exists($path, $this->versionsDeleted)) { |
|
261 | - return; |
|
262 | - } |
|
263 | - $node = $this->versionsDeleted[$path]; |
|
264 | - $relativePath = $this->getPathForNode($node); |
|
265 | - unset($this->versionsDeleted[$path]); |
|
266 | - Storage::delete($relativePath); |
|
267 | - // If no new version was stored in the FS, no new version should be added in the DB. |
|
268 | - // So we simply update the associated version. |
|
269 | - if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { |
|
270 | - $this->versionManager->deleteVersionsEntity($node); |
|
271 | - } |
|
272 | - } |
|
273 | - |
|
274 | - /** |
|
275 | - * mark file as "deleted" so that we can clean up the versions if the file is gone |
|
276 | - */ |
|
277 | - public function pre_remove_hook(Node $node): void { |
|
278 | - $path = $this->getPathForNode($node); |
|
279 | - Storage::markDeletedFile($path); |
|
280 | - $this->versionsDeleted[$node->getPath()] = $node; |
|
281 | - } |
|
282 | - |
|
283 | - /** |
|
284 | - * rename/move versions of renamed/moved files |
|
285 | - * |
|
286 | - * This function is connected to the rename signal of OC_Filesystem and adjust the name and location |
|
287 | - * of the stored versions along the actual file |
|
288 | - */ |
|
289 | - public function rename_hook(Node $source, Node $target): void { |
|
290 | - $sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage()); |
|
291 | - $targetBackend = $this->versionManager->getBackendForStorage($target->getStorage()); |
|
292 | - // If different backends, do nothing. |
|
293 | - if ($sourceBackend !== $targetBackend) { |
|
294 | - return; |
|
295 | - } |
|
296 | - |
|
297 | - $oldPath = $this->getPathForNode($source); |
|
298 | - $newPath = $this->getPathForNode($target); |
|
299 | - Storage::renameOrCopy($oldPath, $newPath, 'rename'); |
|
300 | - } |
|
301 | - |
|
302 | - /** |
|
303 | - * copy versions of copied files |
|
304 | - * |
|
305 | - * This function is connected to the copy signal of OC_Filesystem and copies the |
|
306 | - * the stored versions to the new location |
|
307 | - */ |
|
308 | - public function copy_hook(Node $source, Node $target): void { |
|
309 | - $sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage()); |
|
310 | - $targetBackend = $this->versionManager->getBackendForStorage($target->getStorage()); |
|
311 | - // If different backends, do nothing. |
|
312 | - if ($sourceBackend !== $targetBackend) { |
|
313 | - return; |
|
314 | - } |
|
315 | - |
|
316 | - $oldPath = $this->getPathForNode($source); |
|
317 | - $newPath = $this->getPathForNode($target); |
|
318 | - Storage::renameOrCopy($oldPath, $newPath, 'copy'); |
|
319 | - } |
|
320 | - |
|
321 | - /** |
|
322 | - * Remember owner and the owner path of the source file. |
|
323 | - * If the file already exists, then it was a upload of a existing file |
|
324 | - * over the web interface and we call Storage::store() directly |
|
325 | - * |
|
326 | - * |
|
327 | - */ |
|
328 | - public function pre_renameOrCopy_hook(Node $source, Node $target): void { |
|
329 | - $sourceBackend = $this->versionManager->getBackendForStorage($source->getStorage()); |
|
330 | - $targetBackend = $this->versionManager->getBackendForStorage($target->getParent()->getStorage()); |
|
331 | - // If different backends, do nothing. |
|
332 | - if ($sourceBackend !== $targetBackend) { |
|
333 | - return; |
|
334 | - } |
|
335 | - |
|
336 | - // if we rename a movable mount point, then the versions don't have to be renamed |
|
337 | - $oldPath = $this->getPathForNode($source); |
|
338 | - $newPath = $this->getPathForNode($target); |
|
339 | - if ($oldPath === null || $newPath === null) { |
|
340 | - return; |
|
341 | - } |
|
342 | - |
|
343 | - $user = $this->userSession->getUser()?->getUID(); |
|
344 | - if ($user === null) { |
|
345 | - return; |
|
346 | - } |
|
347 | - |
|
348 | - $absOldPath = Filesystem::normalizePath('/' . $user . '/files' . $oldPath); |
|
349 | - $manager = Filesystem::getMountManager(); |
|
350 | - $mount = $manager->find($absOldPath); |
|
351 | - $internalPath = $mount->getInternalPath($absOldPath); |
|
352 | - if ($internalPath === '' and $mount instanceof MoveableMount) { |
|
353 | - return; |
|
354 | - } |
|
355 | - |
|
356 | - $view = new View($user . '/files'); |
|
357 | - if ($view->file_exists($newPath)) { |
|
358 | - Storage::store($newPath); |
|
359 | - } else { |
|
360 | - Storage::setSourcePathAndUser($oldPath); |
|
361 | - } |
|
362 | - } |
|
363 | - |
|
364 | - /** |
|
365 | - * Retrieve the path relative to the current user root folder. |
|
366 | - * If no user is connected, try to use the node's owner. |
|
367 | - */ |
|
368 | - private function getPathForNode(Node $node): ?string { |
|
369 | - $user = $this->userSession->getUser()?->getUID(); |
|
370 | - if ($user) { |
|
371 | - $path = $this->rootFolder |
|
372 | - ->getUserFolder($user) |
|
373 | - ->getRelativePath($node->getPath()); |
|
374 | - |
|
375 | - if ($path !== null) { |
|
376 | - return $path; |
|
377 | - } |
|
378 | - } |
|
379 | - |
|
380 | - try { |
|
381 | - $owner = $node->getOwner()?->getUid(); |
|
382 | - } catch (NotFoundException) { |
|
383 | - $owner = null; |
|
384 | - } |
|
385 | - |
|
386 | - // If no owner, extract it from the path. |
|
387 | - // e.g. /user/files/foobar.txt |
|
388 | - if (!$owner) { |
|
389 | - $parts = explode('/', $node->getPath(), 4); |
|
390 | - if (count($parts) === 4) { |
|
391 | - $owner = $parts[1]; |
|
392 | - } |
|
393 | - } |
|
394 | - |
|
395 | - if ($owner) { |
|
396 | - $path = $this->rootFolder |
|
397 | - ->getUserFolder($owner) |
|
398 | - ->getRelativePath($node->getPath()); |
|
399 | - |
|
400 | - if ($path !== null) { |
|
401 | - return $path; |
|
402 | - } |
|
403 | - } |
|
404 | - |
|
405 | - return null; |
|
406 | - } |
|
45 | + /** |
|
46 | + * @var array<int, array> |
|
47 | + */ |
|
48 | + private array $writeHookInfo = []; |
|
49 | + /** |
|
50 | + * @var array<int, Node> |
|
51 | + */ |
|
52 | + private array $nodesTouched = []; |
|
53 | + /** |
|
54 | + * @var array<string, Node> |
|
55 | + */ |
|
56 | + private array $versionsDeleted = []; |
|
57 | + |
|
58 | + public function __construct( |
|
59 | + private IRootFolder $rootFolder, |
|
60 | + private IVersionManager $versionManager, |
|
61 | + private IMimeTypeLoader $mimeTypeLoader, |
|
62 | + private IUserSession $userSession, |
|
63 | + private LoggerInterface $logger, |
|
64 | + ) { |
|
65 | + } |
|
66 | + |
|
67 | + public function handle(Event $event): void { |
|
68 | + if ($event instanceof NodeCreatedEvent) { |
|
69 | + $this->created($event->getNode()); |
|
70 | + } |
|
71 | + |
|
72 | + if ($event instanceof BeforeNodeTouchedEvent) { |
|
73 | + $this->pre_touch_hook($event->getNode()); |
|
74 | + } |
|
75 | + |
|
76 | + if ($event instanceof NodeTouchedEvent) { |
|
77 | + $this->touch_hook($event->getNode()); |
|
78 | + } |
|
79 | + |
|
80 | + if ($event instanceof BeforeNodeWrittenEvent) { |
|
81 | + $this->write_hook($event->getNode()); |
|
82 | + } |
|
83 | + |
|
84 | + if ($event instanceof NodeWrittenEvent) { |
|
85 | + $this->post_write_hook($event->getNode()); |
|
86 | + } |
|
87 | + |
|
88 | + if ($event instanceof BeforeNodeDeletedEvent) { |
|
89 | + $this->pre_remove_hook($event->getNode()); |
|
90 | + } |
|
91 | + |
|
92 | + if ($event instanceof NodeDeletedEvent) { |
|
93 | + $this->remove_hook($event->getNode()); |
|
94 | + } |
|
95 | + |
|
96 | + if ($event instanceof NodeRenamedEvent) { |
|
97 | + $this->rename_hook($event->getSource(), $event->getTarget()); |
|
98 | + } |
|
99 | + |
|
100 | + if ($event instanceof NodeCopiedEvent) { |
|
101 | + $this->copy_hook($event->getSource(), $event->getTarget()); |
|
102 | + } |
|
103 | + |
|
104 | + if ($event instanceof BeforeNodeRenamedEvent) { |
|
105 | + $this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget()); |
|
106 | + } |
|
107 | + |
|
108 | + if ($event instanceof BeforeNodeCopiedEvent) { |
|
109 | + $this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget()); |
|
110 | + } |
|
111 | + } |
|
112 | + |
|
113 | + public function pre_touch_hook(Node $node): void { |
|
114 | + // Do not handle folders. |
|
115 | + if ($node instanceof Folder) { |
|
116 | + return; |
|
117 | + } |
|
118 | + |
|
119 | + // $node is a non-existing on file creation. |
|
120 | + if ($node instanceof NonExistingFile) { |
|
121 | + return; |
|
122 | + } |
|
123 | + |
|
124 | + $this->nodesTouched[$node->getId()] = $node; |
|
125 | + } |
|
126 | + |
|
127 | + public function touch_hook(Node $node): void { |
|
128 | + $previousNode = $this->nodesTouched[$node->getId()] ?? null; |
|
129 | + |
|
130 | + if ($previousNode === null) { |
|
131 | + return; |
|
132 | + } |
|
133 | + |
|
134 | + unset($this->nodesTouched[$node->getId()]); |
|
135 | + |
|
136 | + try { |
|
137 | + if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { |
|
138 | + // We update the timestamp of the version entity associated with the previousNode. |
|
139 | + $this->versionManager->updateVersionEntity($node, $previousNode->getMTime(), ['timestamp' => $node->getMTime()]); |
|
140 | + } |
|
141 | + } catch (DbalException $ex) { |
|
142 | + // Ignore UniqueConstraintViolationException, as we are probably in the middle of a rollback |
|
143 | + // Where the previous node would temporary have the mtime of the old version, so the rollback touches it to fix it. |
|
144 | + if (!($ex->getPrevious() instanceof UniqueConstraintViolationException)) { |
|
145 | + throw $ex; |
|
146 | + } |
|
147 | + } catch (DoesNotExistException $ex) { |
|
148 | + // Ignore DoesNotExistException, as we are probably in the middle of a rollback |
|
149 | + // Where the previous node would temporary have a wrong mtime, so the rollback touches it to fix it. |
|
150 | + } |
|
151 | + } |
|
152 | + |
|
153 | + public function created(Node $node): void { |
|
154 | + // Do not handle folders. |
|
155 | + if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { |
|
156 | + $this->versionManager->createVersionEntity($node); |
|
157 | + } |
|
158 | + } |
|
159 | + |
|
160 | + /** |
|
161 | + * listen to write event. |
|
162 | + */ |
|
163 | + public function write_hook(Node $node): void { |
|
164 | + // Do not handle folders. |
|
165 | + if ($node instanceof Folder) { |
|
166 | + return; |
|
167 | + } |
|
168 | + |
|
169 | + // $node is a non-existing on file creation. |
|
170 | + if ($node instanceof NonExistingFile) { |
|
171 | + return; |
|
172 | + } |
|
173 | + |
|
174 | + $path = $this->getPathForNode($node); |
|
175 | + $result = Storage::store($path); |
|
176 | + |
|
177 | + // Store the result of the version creation so it can be used in post_write_hook. |
|
178 | + $this->writeHookInfo[$node->getId()] = [ |
|
179 | + 'previousNode' => $node, |
|
180 | + 'versionCreated' => $result !== false |
|
181 | + ]; |
|
182 | + } |
|
183 | + |
|
184 | + /** |
|
185 | + * listen to post_write event. |
|
186 | + */ |
|
187 | + public function post_write_hook(Node $node): void { |
|
188 | + // Do not handle folders. |
|
189 | + if ($node instanceof Folder) { |
|
190 | + return; |
|
191 | + } |
|
192 | + |
|
193 | + $writeHookInfo = $this->writeHookInfo[$node->getId()] ?? null; |
|
194 | + |
|
195 | + if ($writeHookInfo === null) { |
|
196 | + return; |
|
197 | + } |
|
198 | + |
|
199 | + if ( |
|
200 | + $writeHookInfo['versionCreated'] |
|
201 | + && $node->getMTime() !== $writeHookInfo['previousNode']->getMTime() |
|
202 | + ) { |
|
203 | + // If a new version was created, insert a version in the DB for the current content. |
|
204 | + // If both versions have the same mtime, it means the latest version file simply got overrode, |
|
205 | + // so no need to create a new version. |
|
206 | + $this->created($node); |
|
207 | + } else { |
|
208 | + try { |
|
209 | + // If no new version was stored in the FS, no new version should be added in the DB. |
|
210 | + // So we simply update the associated version. |
|
211 | + if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { |
|
212 | + $this->versionManager->updateVersionEntity( |
|
213 | + $node, |
|
214 | + $writeHookInfo['previousNode']->getMtime(), |
|
215 | + [ |
|
216 | + 'timestamp' => $node->getMTime(), |
|
217 | + 'size' => $node->getSize(), |
|
218 | + 'mimetype' => $this->mimeTypeLoader->getId($node->getMimetype()), |
|
219 | + ], |
|
220 | + ); |
|
221 | + } |
|
222 | + } catch (DoesNotExistException $e) { |
|
223 | + // This happens if the versions app was not enabled while the file was created or updated the last time. |
|
224 | + // meaning there is no such revision and we need to create this file. |
|
225 | + if ($writeHookInfo['versionCreated']) { |
|
226 | + $this->created($node); |
|
227 | + } else { |
|
228 | + // Normally this should not happen so we re-throw the exception to not hide any potential issues. |
|
229 | + throw $e; |
|
230 | + } |
|
231 | + } catch (Exception $e) { |
|
232 | + $this->logger->error('Failed to update existing version for ' . $node->getPath(), [ |
|
233 | + 'exception' => $e, |
|
234 | + 'versionCreated' => $writeHookInfo['versionCreated'], |
|
235 | + 'previousNode' => [ |
|
236 | + 'size' => $writeHookInfo['previousNode']->getSize(), |
|
237 | + 'mtime' => $writeHookInfo['previousNode']->getMTime(), |
|
238 | + ], |
|
239 | + 'node' => [ |
|
240 | + 'size' => $node->getSize(), |
|
241 | + 'mtime' => $node->getMTime(), |
|
242 | + ] |
|
243 | + ]); |
|
244 | + throw $e; |
|
245 | + } |
|
246 | + } |
|
247 | + |
|
248 | + unset($this->writeHookInfo[$node->getId()]); |
|
249 | + } |
|
250 | + |
|
251 | + /** |
|
252 | + * Erase versions of deleted file |
|
253 | + * |
|
254 | + * This function is connected to the delete signal of OC_Filesystem |
|
255 | + * cleanup the versions directory if the actual file gets deleted |
|
256 | + */ |
|
257 | + public function remove_hook(Node $node): void { |
|
258 | + // Need to normalize the path as there is an issue with path concatenation in View.php::getAbsolutePath. |
|
259 | + $path = Filesystem::normalizePath($node->getPath()); |
|
260 | + if (!array_key_exists($path, $this->versionsDeleted)) { |
|
261 | + return; |
|
262 | + } |
|
263 | + $node = $this->versionsDeleted[$path]; |
|
264 | + $relativePath = $this->getPathForNode($node); |
|
265 | + unset($this->versionsDeleted[$path]); |
|
266 | + Storage::delete($relativePath); |
|
267 | + // If no new version was stored in the FS, no new version should be added in the DB. |
|
268 | + // So we simply update the associated version. |
|
269 | + if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) { |
|
270 | + $this->versionManager->deleteVersionsEntity($node); |
|
271 | + } |
|
272 | + } |
|
273 | + |
|
274 | + /** |
|
275 | + * mark file as "deleted" so that we can clean up the versions if the file is gone |
|
276 | + */ |
|
277 | + public function pre_remove_hook(Node $node): void { |
|
278 | + $path = $this->getPathForNode($node); |
|
279 | + Storage::markDeletedFile($path); |
|
280 | + $this->versionsDeleted[$node->getPath()] = $node; |
|
281 | + } |
|
282 | + |
|
283 | + /** |
|
284 | + * rename/move versions of renamed/moved files |
|
285 | + * |
|
286 | + * This function is connected to the rename signal of OC_Filesystem and adjust the name and location |
|
287 | + * of the stored versions along the actual file |
|
288 | + */ |
|
289 | + public function rename_hook(Node $source, Node $target): void { |
|
290 | + $sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage()); |
|
291 | + $targetBackend = $this->versionManager->getBackendForStorage($target->getStorage()); |
|
292 | + // If different backends, do nothing. |
|
293 | + if ($sourceBackend !== $targetBackend) { |
|
294 | + return; |
|
295 | + } |
|
296 | + |
|
297 | + $oldPath = $this->getPathForNode($source); |
|
298 | + $newPath = $this->getPathForNode($target); |
|
299 | + Storage::renameOrCopy($oldPath, $newPath, 'rename'); |
|
300 | + } |
|
301 | + |
|
302 | + /** |
|
303 | + * copy versions of copied files |
|
304 | + * |
|
305 | + * This function is connected to the copy signal of OC_Filesystem and copies the |
|
306 | + * the stored versions to the new location |
|
307 | + */ |
|
308 | + public function copy_hook(Node $source, Node $target): void { |
|
309 | + $sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage()); |
|
310 | + $targetBackend = $this->versionManager->getBackendForStorage($target->getStorage()); |
|
311 | + // If different backends, do nothing. |
|
312 | + if ($sourceBackend !== $targetBackend) { |
|
313 | + return; |
|
314 | + } |
|
315 | + |
|
316 | + $oldPath = $this->getPathForNode($source); |
|
317 | + $newPath = $this->getPathForNode($target); |
|
318 | + Storage::renameOrCopy($oldPath, $newPath, 'copy'); |
|
319 | + } |
|
320 | + |
|
321 | + /** |
|
322 | + * Remember owner and the owner path of the source file. |
|
323 | + * If the file already exists, then it was a upload of a existing file |
|
324 | + * over the web interface and we call Storage::store() directly |
|
325 | + * |
|
326 | + * |
|
327 | + */ |
|
328 | + public function pre_renameOrCopy_hook(Node $source, Node $target): void { |
|
329 | + $sourceBackend = $this->versionManager->getBackendForStorage($source->getStorage()); |
|
330 | + $targetBackend = $this->versionManager->getBackendForStorage($target->getParent()->getStorage()); |
|
331 | + // If different backends, do nothing. |
|
332 | + if ($sourceBackend !== $targetBackend) { |
|
333 | + return; |
|
334 | + } |
|
335 | + |
|
336 | + // if we rename a movable mount point, then the versions don't have to be renamed |
|
337 | + $oldPath = $this->getPathForNode($source); |
|
338 | + $newPath = $this->getPathForNode($target); |
|
339 | + if ($oldPath === null || $newPath === null) { |
|
340 | + return; |
|
341 | + } |
|
342 | + |
|
343 | + $user = $this->userSession->getUser()?->getUID(); |
|
344 | + if ($user === null) { |
|
345 | + return; |
|
346 | + } |
|
347 | + |
|
348 | + $absOldPath = Filesystem::normalizePath('/' . $user . '/files' . $oldPath); |
|
349 | + $manager = Filesystem::getMountManager(); |
|
350 | + $mount = $manager->find($absOldPath); |
|
351 | + $internalPath = $mount->getInternalPath($absOldPath); |
|
352 | + if ($internalPath === '' and $mount instanceof MoveableMount) { |
|
353 | + return; |
|
354 | + } |
|
355 | + |
|
356 | + $view = new View($user . '/files'); |
|
357 | + if ($view->file_exists($newPath)) { |
|
358 | + Storage::store($newPath); |
|
359 | + } else { |
|
360 | + Storage::setSourcePathAndUser($oldPath); |
|
361 | + } |
|
362 | + } |
|
363 | + |
|
364 | + /** |
|
365 | + * Retrieve the path relative to the current user root folder. |
|
366 | + * If no user is connected, try to use the node's owner. |
|
367 | + */ |
|
368 | + private function getPathForNode(Node $node): ?string { |
|
369 | + $user = $this->userSession->getUser()?->getUID(); |
|
370 | + if ($user) { |
|
371 | + $path = $this->rootFolder |
|
372 | + ->getUserFolder($user) |
|
373 | + ->getRelativePath($node->getPath()); |
|
374 | + |
|
375 | + if ($path !== null) { |
|
376 | + return $path; |
|
377 | + } |
|
378 | + } |
|
379 | + |
|
380 | + try { |
|
381 | + $owner = $node->getOwner()?->getUid(); |
|
382 | + } catch (NotFoundException) { |
|
383 | + $owner = null; |
|
384 | + } |
|
385 | + |
|
386 | + // If no owner, extract it from the path. |
|
387 | + // e.g. /user/files/foobar.txt |
|
388 | + if (!$owner) { |
|
389 | + $parts = explode('/', $node->getPath(), 4); |
|
390 | + if (count($parts) === 4) { |
|
391 | + $owner = $parts[1]; |
|
392 | + } |
|
393 | + } |
|
394 | + |
|
395 | + if ($owner) { |
|
396 | + $path = $this->rootFolder |
|
397 | + ->getUserFolder($owner) |
|
398 | + ->getRelativePath($node->getPath()); |
|
399 | + |
|
400 | + if ($path !== null) { |
|
401 | + return $path; |
|
402 | + } |
|
403 | + } |
|
404 | + |
|
405 | + return null; |
|
406 | + } |
|
407 | 407 | } |
@@ -36,960 +36,960 @@ |
||
36 | 36 | * @group DB |
37 | 37 | */ |
38 | 38 | class VersioningTest extends \Test\TestCase { |
39 | - public const TEST_VERSIONS_USER = 'test-versions-user'; |
|
40 | - public const TEST_VERSIONS_USER2 = 'test-versions-user2'; |
|
41 | - public const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions'; |
|
42 | - |
|
43 | - /** |
|
44 | - * @var View |
|
45 | - */ |
|
46 | - private $rootView; |
|
47 | - /** |
|
48 | - * @var VersionsMapper |
|
49 | - */ |
|
50 | - private $versionsMapper; |
|
51 | - /** |
|
52 | - * @var IMimeTypeLoader |
|
53 | - */ |
|
54 | - private $mimeTypeLoader; |
|
55 | - private $user1; |
|
56 | - private $user2; |
|
57 | - |
|
58 | - public static function setUpBeforeClass(): void { |
|
59 | - parent::setUpBeforeClass(); |
|
60 | - |
|
61 | - $application = new Application(); |
|
62 | - |
|
63 | - // create test user |
|
64 | - self::loginHelper(self::TEST_VERSIONS_USER2, true); |
|
65 | - self::loginHelper(self::TEST_VERSIONS_USER, true); |
|
66 | - } |
|
67 | - |
|
68 | - public static function tearDownAfterClass(): void { |
|
69 | - // cleanup test user |
|
70 | - $user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER); |
|
71 | - if ($user !== null) { |
|
72 | - $user->delete(); |
|
73 | - } |
|
74 | - $user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER2); |
|
75 | - if ($user !== null) { |
|
76 | - $user->delete(); |
|
77 | - } |
|
78 | - |
|
79 | - parent::tearDownAfterClass(); |
|
80 | - } |
|
81 | - |
|
82 | - protected function setUp(): void { |
|
83 | - parent::setUp(); |
|
84 | - |
|
85 | - $config = Server::get(IConfig::class); |
|
86 | - $mockConfig = $this->createMock(IConfig::class); |
|
87 | - $mockConfig->expects($this->any()) |
|
88 | - ->method('getSystemValue') |
|
89 | - ->willReturnCallback(function ($key, $default) use ($config) { |
|
90 | - if ($key === 'filesystem_check_changes') { |
|
91 | - return Watcher::CHECK_ONCE; |
|
92 | - } else { |
|
93 | - return $config->getSystemValue($key, $default); |
|
94 | - } |
|
95 | - }); |
|
96 | - $this->overwriteService(AllConfig::class, $mockConfig); |
|
97 | - |
|
98 | - // clear hooks |
|
99 | - \OC_Hook::clear(); |
|
100 | - \OC::registerShareHooks(Server::get(SystemConfig::class)); |
|
101 | - \OC::$server->boot(); |
|
102 | - |
|
103 | - self::loginHelper(self::TEST_VERSIONS_USER); |
|
104 | - $this->rootView = new View(); |
|
105 | - if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) { |
|
106 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); |
|
107 | - } |
|
108 | - |
|
109 | - $this->versionsMapper = Server::get(VersionsMapper::class); |
|
110 | - $this->mimeTypeLoader = Server::get(IMimeTypeLoader::class); |
|
111 | - |
|
112 | - $this->user1 = $this->createMock(IUser::class); |
|
113 | - $this->user1->method('getUID') |
|
114 | - ->willReturn(self::TEST_VERSIONS_USER); |
|
115 | - $this->user2 = $this->createMock(IUser::class); |
|
116 | - $this->user2->method('getUID') |
|
117 | - ->willReturn(self::TEST_VERSIONS_USER2); |
|
118 | - } |
|
119 | - |
|
120 | - protected function tearDown(): void { |
|
121 | - $this->restoreService(AllConfig::class); |
|
122 | - |
|
123 | - if ($this->rootView) { |
|
124 | - $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/'); |
|
125 | - $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/'); |
|
126 | - $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/'); |
|
127 | - $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/'); |
|
128 | - } |
|
129 | - |
|
130 | - \OC_Hook::clear(); |
|
131 | - |
|
132 | - parent::tearDown(); |
|
133 | - } |
|
134 | - |
|
135 | - /** |
|
136 | - * @medium |
|
137 | - * test expire logic |
|
138 | - * @dataProvider versionsProvider |
|
139 | - */ |
|
140 | - public function testGetExpireList($versions, $sizeOfAllDeletedFiles): void { |
|
141 | - |
|
142 | - // last interval end at 2592000 |
|
143 | - $startTime = 5000000; |
|
144 | - |
|
145 | - $testClass = new VersionStorageToTest(); |
|
146 | - [$deleted, $size] = $testClass->callProtectedGetExpireList($startTime, $versions); |
|
147 | - |
|
148 | - // we should have deleted 16 files each of the size 1 |
|
149 | - $this->assertEquals($sizeOfAllDeletedFiles, $size); |
|
150 | - |
|
151 | - // the deleted array should only contain versions which should be deleted |
|
152 | - foreach ($deleted as $key => $path) { |
|
153 | - unset($versions[$key]); |
|
154 | - $this->assertEquals('delete', substr($path, 0, strlen('delete'))); |
|
155 | - } |
|
156 | - |
|
157 | - // the versions array should only contain versions which should be kept |
|
158 | - foreach ($versions as $version) { |
|
159 | - $this->assertEquals('keep', $version['path']); |
|
160 | - } |
|
161 | - } |
|
162 | - |
|
163 | - public function versionsProvider() { |
|
164 | - return [ |
|
165 | - // first set of versions uniformly distributed versions |
|
166 | - [ |
|
167 | - [ |
|
168 | - // first slice (10sec) keep one version every 2 seconds |
|
169 | - ['version' => 4999999, 'path' => 'keep', 'size' => 1], |
|
170 | - ['version' => 4999998, 'path' => 'delete', 'size' => 1], |
|
171 | - ['version' => 4999997, 'path' => 'keep', 'size' => 1], |
|
172 | - ['version' => 4999995, 'path' => 'keep', 'size' => 1], |
|
173 | - ['version' => 4999994, 'path' => 'delete', 'size' => 1], |
|
174 | - //next slice (60sec) starts at 4999990 keep one version every 10 secons |
|
175 | - ['version' => 4999988, 'path' => 'keep', 'size' => 1], |
|
176 | - ['version' => 4999978, 'path' => 'keep', 'size' => 1], |
|
177 | - ['version' => 4999975, 'path' => 'delete', 'size' => 1], |
|
178 | - ['version' => 4999972, 'path' => 'delete', 'size' => 1], |
|
179 | - ['version' => 4999967, 'path' => 'keep', 'size' => 1], |
|
180 | - ['version' => 4999958, 'path' => 'delete', 'size' => 1], |
|
181 | - ['version' => 4999957, 'path' => 'keep', 'size' => 1], |
|
182 | - //next slice (3600sec) start at 4999940 keep one version every 60 seconds |
|
183 | - ['version' => 4999900, 'path' => 'keep', 'size' => 1], |
|
184 | - ['version' => 4999841, 'path' => 'delete', 'size' => 1], |
|
185 | - ['version' => 4999840, 'path' => 'keep', 'size' => 1], |
|
186 | - ['version' => 4999780, 'path' => 'keep', 'size' => 1], |
|
187 | - ['version' => 4996401, 'path' => 'keep', 'size' => 1], |
|
188 | - // next slice (86400sec) start at 4996400 keep one version every 3600 seconds |
|
189 | - ['version' => 4996350, 'path' => 'delete', 'size' => 1], |
|
190 | - ['version' => 4992800, 'path' => 'keep', 'size' => 1], |
|
191 | - ['version' => 4989800, 'path' => 'delete', 'size' => 1], |
|
192 | - ['version' => 4989700, 'path' => 'delete', 'size' => 1], |
|
193 | - ['version' => 4989200, 'path' => 'keep', 'size' => 1], |
|
194 | - // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds |
|
195 | - ['version' => 4913600, 'path' => 'keep', 'size' => 1], |
|
196 | - ['version' => 4852800, 'path' => 'delete', 'size' => 1], |
|
197 | - ['version' => 4827201, 'path' => 'delete', 'size' => 1], |
|
198 | - ['version' => 4827200, 'path' => 'keep', 'size' => 1], |
|
199 | - ['version' => 4777201, 'path' => 'delete', 'size' => 1], |
|
200 | - ['version' => 4777501, 'path' => 'delete', 'size' => 1], |
|
201 | - ['version' => 4740000, 'path' => 'keep', 'size' => 1], |
|
202 | - // final slice starts at 2408000 keep one version every 604800 secons |
|
203 | - ['version' => 2408000, 'path' => 'keep', 'size' => 1], |
|
204 | - ['version' => 1803201, 'path' => 'delete', 'size' => 1], |
|
205 | - ['version' => 1803200, 'path' => 'keep', 'size' => 1], |
|
206 | - ['version' => 1800199, 'path' => 'delete', 'size' => 1], |
|
207 | - ['version' => 1800100, 'path' => 'delete', 'size' => 1], |
|
208 | - ['version' => 1198300, 'path' => 'keep', 'size' => 1], |
|
209 | - ], |
|
210 | - 16 // size of all deleted files (every file has the size 1) |
|
211 | - ], |
|
212 | - // second set of versions, here we have only really old versions |
|
213 | - [ |
|
214 | - [ |
|
215 | - // first slice (10sec) keep one version every 2 seconds |
|
216 | - // next slice (60sec) starts at 4999990 keep one version every 10 secons |
|
217 | - // next slice (3600sec) start at 4999940 keep one version every 60 seconds |
|
218 | - // next slice (86400sec) start at 4996400 keep one version every 3600 seconds |
|
219 | - ['version' => 4996400, 'path' => 'keep', 'size' => 1], |
|
220 | - ['version' => 4996350, 'path' => 'delete', 'size' => 1], |
|
221 | - ['version' => 4996350, 'path' => 'delete', 'size' => 1], |
|
222 | - ['version' => 4992800, 'path' => 'keep', 'size' => 1], |
|
223 | - ['version' => 4989800, 'path' => 'delete', 'size' => 1], |
|
224 | - ['version' => 4989700, 'path' => 'delete', 'size' => 1], |
|
225 | - ['version' => 4989200, 'path' => 'keep', 'size' => 1], |
|
226 | - // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds |
|
227 | - ['version' => 4913600, 'path' => 'keep', 'size' => 1], |
|
228 | - ['version' => 4852800, 'path' => 'delete', 'size' => 1], |
|
229 | - ['version' => 4827201, 'path' => 'delete', 'size' => 1], |
|
230 | - ['version' => 4827200, 'path' => 'keep', 'size' => 1], |
|
231 | - ['version' => 4777201, 'path' => 'delete', 'size' => 1], |
|
232 | - ['version' => 4777501, 'path' => 'delete', 'size' => 1], |
|
233 | - ['version' => 4740000, 'path' => 'keep', 'size' => 1], |
|
234 | - // final slice starts at 2408000 keep one version every 604800 secons |
|
235 | - ['version' => 2408000, 'path' => 'keep', 'size' => 1], |
|
236 | - ['version' => 1803201, 'path' => 'delete', 'size' => 1], |
|
237 | - ['version' => 1803200, 'path' => 'keep', 'size' => 1], |
|
238 | - ['version' => 1800199, 'path' => 'delete', 'size' => 1], |
|
239 | - ['version' => 1800100, 'path' => 'delete', 'size' => 1], |
|
240 | - ['version' => 1198300, 'path' => 'keep', 'size' => 1], |
|
241 | - ], |
|
242 | - 11 // size of all deleted files (every file has the size 1) |
|
243 | - ], |
|
244 | - // third set of versions, with some gaps between |
|
245 | - [ |
|
246 | - [ |
|
247 | - // first slice (10sec) keep one version every 2 seconds |
|
248 | - ['version' => 4999999, 'path' => 'keep', 'size' => 1], |
|
249 | - ['version' => 4999998, 'path' => 'delete', 'size' => 1], |
|
250 | - ['version' => 4999997, 'path' => 'keep', 'size' => 1], |
|
251 | - ['version' => 4999995, 'path' => 'keep', 'size' => 1], |
|
252 | - ['version' => 4999994, 'path' => 'delete', 'size' => 1], |
|
253 | - //next slice (60sec) starts at 4999990 keep one version every 10 secons |
|
254 | - ['version' => 4999988, 'path' => 'keep', 'size' => 1], |
|
255 | - ['version' => 4999978, 'path' => 'keep', 'size' => 1], |
|
256 | - //next slice (3600sec) start at 4999940 keep one version every 60 seconds |
|
257 | - // next slice (86400sec) start at 4996400 keep one version every 3600 seconds |
|
258 | - ['version' => 4989200, 'path' => 'keep', 'size' => 1], |
|
259 | - // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds |
|
260 | - ['version' => 4913600, 'path' => 'keep', 'size' => 1], |
|
261 | - ['version' => 4852800, 'path' => 'delete', 'size' => 1], |
|
262 | - ['version' => 4827201, 'path' => 'delete', 'size' => 1], |
|
263 | - ['version' => 4827200, 'path' => 'keep', 'size' => 1], |
|
264 | - ['version' => 4777201, 'path' => 'delete', 'size' => 1], |
|
265 | - ['version' => 4777501, 'path' => 'delete', 'size' => 1], |
|
266 | - ['version' => 4740000, 'path' => 'keep', 'size' => 1], |
|
267 | - // final slice starts at 2408000 keep one version every 604800 secons |
|
268 | - ['version' => 2408000, 'path' => 'keep', 'size' => 1], |
|
269 | - ['version' => 1803201, 'path' => 'delete', 'size' => 1], |
|
270 | - ['version' => 1803200, 'path' => 'keep', 'size' => 1], |
|
271 | - ['version' => 1800199, 'path' => 'delete', 'size' => 1], |
|
272 | - ['version' => 1800100, 'path' => 'delete', 'size' => 1], |
|
273 | - ['version' => 1198300, 'path' => 'keep', 'size' => 1], |
|
274 | - ], |
|
275 | - 9 // size of all deleted files (every file has the size 1) |
|
276 | - ], |
|
277 | - // fourth set of versions: empty (see issue #19066) |
|
278 | - [ |
|
279 | - [], |
|
280 | - 0 |
|
281 | - ] |
|
282 | - |
|
283 | - ]; |
|
284 | - } |
|
285 | - |
|
286 | - public function testRename(): void { |
|
287 | - Filesystem::file_put_contents('test.txt', 'test file'); |
|
288 | - |
|
289 | - $t1 = time(); |
|
290 | - // second version is two weeks older, this way we make sure that no |
|
291 | - // version will be expired |
|
292 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
293 | - |
|
294 | - // create some versions |
|
295 | - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
296 | - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
297 | - $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
298 | - $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
299 | - |
|
300 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
301 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
302 | - |
|
303 | - // execute rename hook of versions app |
|
304 | - Filesystem::rename('test.txt', 'test2.txt'); |
|
305 | - |
|
306 | - $this->runCommands(); |
|
307 | - |
|
308 | - $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist'); |
|
309 | - $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist'); |
|
310 | - |
|
311 | - $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists'); |
|
312 | - $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists'); |
|
313 | - } |
|
314 | - |
|
315 | - public function testRenameInSharedFolder(): void { |
|
316 | - Filesystem::mkdir('folder1'); |
|
317 | - Filesystem::mkdir('folder1/folder2'); |
|
318 | - Filesystem::file_put_contents('folder1/test.txt', 'test file'); |
|
319 | - |
|
320 | - $t1 = time(); |
|
321 | - // second version is two weeks older, this way we make sure that no |
|
322 | - // version will be expired |
|
323 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
324 | - |
|
325 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); |
|
326 | - // create some versions |
|
327 | - $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; |
|
328 | - $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; |
|
329 | - $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1; |
|
330 | - $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2; |
|
331 | - |
|
332 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
333 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
334 | - |
|
335 | - $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); |
|
336 | - $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
337 | - $share->setNode($node) |
|
338 | - ->setShareType(IShare::TYPE_USER) |
|
339 | - ->setSharedBy(self::TEST_VERSIONS_USER) |
|
340 | - ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
341 | - ->setPermissions(Constants::PERMISSION_ALL); |
|
342 | - $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
343 | - Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
344 | - |
|
345 | - self::loginHelper(self::TEST_VERSIONS_USER2); |
|
346 | - |
|
347 | - $this->assertTrue(Filesystem::file_exists('folder1/test.txt')); |
|
348 | - |
|
349 | - // execute rename hook of versions app |
|
350 | - Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt'); |
|
351 | - |
|
352 | - $this->runCommands(); |
|
353 | - |
|
354 | - self::loginHelper(self::TEST_VERSIONS_USER); |
|
355 | - |
|
356 | - $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist'); |
|
357 | - $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist'); |
|
358 | - |
|
359 | - $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists'); |
|
360 | - $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists'); |
|
361 | - |
|
362 | - Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
363 | - } |
|
364 | - |
|
365 | - public function testMoveFolder(): void { |
|
366 | - Filesystem::mkdir('folder1'); |
|
367 | - Filesystem::mkdir('folder2'); |
|
368 | - Filesystem::file_put_contents('folder1/test.txt', 'test file'); |
|
369 | - |
|
370 | - $t1 = time(); |
|
371 | - // second version is two weeks older, this way we make sure that no |
|
372 | - // version will be expired |
|
373 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
374 | - |
|
375 | - // create some versions |
|
376 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); |
|
377 | - $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; |
|
378 | - $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; |
|
379 | - $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1; |
|
380 | - $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2; |
|
39 | + public const TEST_VERSIONS_USER = 'test-versions-user'; |
|
40 | + public const TEST_VERSIONS_USER2 = 'test-versions-user2'; |
|
41 | + public const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions'; |
|
42 | + |
|
43 | + /** |
|
44 | + * @var View |
|
45 | + */ |
|
46 | + private $rootView; |
|
47 | + /** |
|
48 | + * @var VersionsMapper |
|
49 | + */ |
|
50 | + private $versionsMapper; |
|
51 | + /** |
|
52 | + * @var IMimeTypeLoader |
|
53 | + */ |
|
54 | + private $mimeTypeLoader; |
|
55 | + private $user1; |
|
56 | + private $user2; |
|
57 | + |
|
58 | + public static function setUpBeforeClass(): void { |
|
59 | + parent::setUpBeforeClass(); |
|
60 | + |
|
61 | + $application = new Application(); |
|
62 | + |
|
63 | + // create test user |
|
64 | + self::loginHelper(self::TEST_VERSIONS_USER2, true); |
|
65 | + self::loginHelper(self::TEST_VERSIONS_USER, true); |
|
66 | + } |
|
67 | + |
|
68 | + public static function tearDownAfterClass(): void { |
|
69 | + // cleanup test user |
|
70 | + $user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER); |
|
71 | + if ($user !== null) { |
|
72 | + $user->delete(); |
|
73 | + } |
|
74 | + $user = Server::get(IUserManager::class)->get(self::TEST_VERSIONS_USER2); |
|
75 | + if ($user !== null) { |
|
76 | + $user->delete(); |
|
77 | + } |
|
78 | + |
|
79 | + parent::tearDownAfterClass(); |
|
80 | + } |
|
81 | + |
|
82 | + protected function setUp(): void { |
|
83 | + parent::setUp(); |
|
84 | + |
|
85 | + $config = Server::get(IConfig::class); |
|
86 | + $mockConfig = $this->createMock(IConfig::class); |
|
87 | + $mockConfig->expects($this->any()) |
|
88 | + ->method('getSystemValue') |
|
89 | + ->willReturnCallback(function ($key, $default) use ($config) { |
|
90 | + if ($key === 'filesystem_check_changes') { |
|
91 | + return Watcher::CHECK_ONCE; |
|
92 | + } else { |
|
93 | + return $config->getSystemValue($key, $default); |
|
94 | + } |
|
95 | + }); |
|
96 | + $this->overwriteService(AllConfig::class, $mockConfig); |
|
97 | + |
|
98 | + // clear hooks |
|
99 | + \OC_Hook::clear(); |
|
100 | + \OC::registerShareHooks(Server::get(SystemConfig::class)); |
|
101 | + \OC::$server->boot(); |
|
102 | + |
|
103 | + self::loginHelper(self::TEST_VERSIONS_USER); |
|
104 | + $this->rootView = new View(); |
|
105 | + if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) { |
|
106 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); |
|
107 | + } |
|
108 | + |
|
109 | + $this->versionsMapper = Server::get(VersionsMapper::class); |
|
110 | + $this->mimeTypeLoader = Server::get(IMimeTypeLoader::class); |
|
111 | + |
|
112 | + $this->user1 = $this->createMock(IUser::class); |
|
113 | + $this->user1->method('getUID') |
|
114 | + ->willReturn(self::TEST_VERSIONS_USER); |
|
115 | + $this->user2 = $this->createMock(IUser::class); |
|
116 | + $this->user2->method('getUID') |
|
117 | + ->willReturn(self::TEST_VERSIONS_USER2); |
|
118 | + } |
|
119 | + |
|
120 | + protected function tearDown(): void { |
|
121 | + $this->restoreService(AllConfig::class); |
|
122 | + |
|
123 | + if ($this->rootView) { |
|
124 | + $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/'); |
|
125 | + $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/'); |
|
126 | + $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/'); |
|
127 | + $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/'); |
|
128 | + } |
|
129 | + |
|
130 | + \OC_Hook::clear(); |
|
131 | + |
|
132 | + parent::tearDown(); |
|
133 | + } |
|
134 | + |
|
135 | + /** |
|
136 | + * @medium |
|
137 | + * test expire logic |
|
138 | + * @dataProvider versionsProvider |
|
139 | + */ |
|
140 | + public function testGetExpireList($versions, $sizeOfAllDeletedFiles): void { |
|
141 | + |
|
142 | + // last interval end at 2592000 |
|
143 | + $startTime = 5000000; |
|
144 | + |
|
145 | + $testClass = new VersionStorageToTest(); |
|
146 | + [$deleted, $size] = $testClass->callProtectedGetExpireList($startTime, $versions); |
|
147 | + |
|
148 | + // we should have deleted 16 files each of the size 1 |
|
149 | + $this->assertEquals($sizeOfAllDeletedFiles, $size); |
|
150 | + |
|
151 | + // the deleted array should only contain versions which should be deleted |
|
152 | + foreach ($deleted as $key => $path) { |
|
153 | + unset($versions[$key]); |
|
154 | + $this->assertEquals('delete', substr($path, 0, strlen('delete'))); |
|
155 | + } |
|
156 | + |
|
157 | + // the versions array should only contain versions which should be kept |
|
158 | + foreach ($versions as $version) { |
|
159 | + $this->assertEquals('keep', $version['path']); |
|
160 | + } |
|
161 | + } |
|
162 | + |
|
163 | + public function versionsProvider() { |
|
164 | + return [ |
|
165 | + // first set of versions uniformly distributed versions |
|
166 | + [ |
|
167 | + [ |
|
168 | + // first slice (10sec) keep one version every 2 seconds |
|
169 | + ['version' => 4999999, 'path' => 'keep', 'size' => 1], |
|
170 | + ['version' => 4999998, 'path' => 'delete', 'size' => 1], |
|
171 | + ['version' => 4999997, 'path' => 'keep', 'size' => 1], |
|
172 | + ['version' => 4999995, 'path' => 'keep', 'size' => 1], |
|
173 | + ['version' => 4999994, 'path' => 'delete', 'size' => 1], |
|
174 | + //next slice (60sec) starts at 4999990 keep one version every 10 secons |
|
175 | + ['version' => 4999988, 'path' => 'keep', 'size' => 1], |
|
176 | + ['version' => 4999978, 'path' => 'keep', 'size' => 1], |
|
177 | + ['version' => 4999975, 'path' => 'delete', 'size' => 1], |
|
178 | + ['version' => 4999972, 'path' => 'delete', 'size' => 1], |
|
179 | + ['version' => 4999967, 'path' => 'keep', 'size' => 1], |
|
180 | + ['version' => 4999958, 'path' => 'delete', 'size' => 1], |
|
181 | + ['version' => 4999957, 'path' => 'keep', 'size' => 1], |
|
182 | + //next slice (3600sec) start at 4999940 keep one version every 60 seconds |
|
183 | + ['version' => 4999900, 'path' => 'keep', 'size' => 1], |
|
184 | + ['version' => 4999841, 'path' => 'delete', 'size' => 1], |
|
185 | + ['version' => 4999840, 'path' => 'keep', 'size' => 1], |
|
186 | + ['version' => 4999780, 'path' => 'keep', 'size' => 1], |
|
187 | + ['version' => 4996401, 'path' => 'keep', 'size' => 1], |
|
188 | + // next slice (86400sec) start at 4996400 keep one version every 3600 seconds |
|
189 | + ['version' => 4996350, 'path' => 'delete', 'size' => 1], |
|
190 | + ['version' => 4992800, 'path' => 'keep', 'size' => 1], |
|
191 | + ['version' => 4989800, 'path' => 'delete', 'size' => 1], |
|
192 | + ['version' => 4989700, 'path' => 'delete', 'size' => 1], |
|
193 | + ['version' => 4989200, 'path' => 'keep', 'size' => 1], |
|
194 | + // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds |
|
195 | + ['version' => 4913600, 'path' => 'keep', 'size' => 1], |
|
196 | + ['version' => 4852800, 'path' => 'delete', 'size' => 1], |
|
197 | + ['version' => 4827201, 'path' => 'delete', 'size' => 1], |
|
198 | + ['version' => 4827200, 'path' => 'keep', 'size' => 1], |
|
199 | + ['version' => 4777201, 'path' => 'delete', 'size' => 1], |
|
200 | + ['version' => 4777501, 'path' => 'delete', 'size' => 1], |
|
201 | + ['version' => 4740000, 'path' => 'keep', 'size' => 1], |
|
202 | + // final slice starts at 2408000 keep one version every 604800 secons |
|
203 | + ['version' => 2408000, 'path' => 'keep', 'size' => 1], |
|
204 | + ['version' => 1803201, 'path' => 'delete', 'size' => 1], |
|
205 | + ['version' => 1803200, 'path' => 'keep', 'size' => 1], |
|
206 | + ['version' => 1800199, 'path' => 'delete', 'size' => 1], |
|
207 | + ['version' => 1800100, 'path' => 'delete', 'size' => 1], |
|
208 | + ['version' => 1198300, 'path' => 'keep', 'size' => 1], |
|
209 | + ], |
|
210 | + 16 // size of all deleted files (every file has the size 1) |
|
211 | + ], |
|
212 | + // second set of versions, here we have only really old versions |
|
213 | + [ |
|
214 | + [ |
|
215 | + // first slice (10sec) keep one version every 2 seconds |
|
216 | + // next slice (60sec) starts at 4999990 keep one version every 10 secons |
|
217 | + // next slice (3600sec) start at 4999940 keep one version every 60 seconds |
|
218 | + // next slice (86400sec) start at 4996400 keep one version every 3600 seconds |
|
219 | + ['version' => 4996400, 'path' => 'keep', 'size' => 1], |
|
220 | + ['version' => 4996350, 'path' => 'delete', 'size' => 1], |
|
221 | + ['version' => 4996350, 'path' => 'delete', 'size' => 1], |
|
222 | + ['version' => 4992800, 'path' => 'keep', 'size' => 1], |
|
223 | + ['version' => 4989800, 'path' => 'delete', 'size' => 1], |
|
224 | + ['version' => 4989700, 'path' => 'delete', 'size' => 1], |
|
225 | + ['version' => 4989200, 'path' => 'keep', 'size' => 1], |
|
226 | + // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds |
|
227 | + ['version' => 4913600, 'path' => 'keep', 'size' => 1], |
|
228 | + ['version' => 4852800, 'path' => 'delete', 'size' => 1], |
|
229 | + ['version' => 4827201, 'path' => 'delete', 'size' => 1], |
|
230 | + ['version' => 4827200, 'path' => 'keep', 'size' => 1], |
|
231 | + ['version' => 4777201, 'path' => 'delete', 'size' => 1], |
|
232 | + ['version' => 4777501, 'path' => 'delete', 'size' => 1], |
|
233 | + ['version' => 4740000, 'path' => 'keep', 'size' => 1], |
|
234 | + // final slice starts at 2408000 keep one version every 604800 secons |
|
235 | + ['version' => 2408000, 'path' => 'keep', 'size' => 1], |
|
236 | + ['version' => 1803201, 'path' => 'delete', 'size' => 1], |
|
237 | + ['version' => 1803200, 'path' => 'keep', 'size' => 1], |
|
238 | + ['version' => 1800199, 'path' => 'delete', 'size' => 1], |
|
239 | + ['version' => 1800100, 'path' => 'delete', 'size' => 1], |
|
240 | + ['version' => 1198300, 'path' => 'keep', 'size' => 1], |
|
241 | + ], |
|
242 | + 11 // size of all deleted files (every file has the size 1) |
|
243 | + ], |
|
244 | + // third set of versions, with some gaps between |
|
245 | + [ |
|
246 | + [ |
|
247 | + // first slice (10sec) keep one version every 2 seconds |
|
248 | + ['version' => 4999999, 'path' => 'keep', 'size' => 1], |
|
249 | + ['version' => 4999998, 'path' => 'delete', 'size' => 1], |
|
250 | + ['version' => 4999997, 'path' => 'keep', 'size' => 1], |
|
251 | + ['version' => 4999995, 'path' => 'keep', 'size' => 1], |
|
252 | + ['version' => 4999994, 'path' => 'delete', 'size' => 1], |
|
253 | + //next slice (60sec) starts at 4999990 keep one version every 10 secons |
|
254 | + ['version' => 4999988, 'path' => 'keep', 'size' => 1], |
|
255 | + ['version' => 4999978, 'path' => 'keep', 'size' => 1], |
|
256 | + //next slice (3600sec) start at 4999940 keep one version every 60 seconds |
|
257 | + // next slice (86400sec) start at 4996400 keep one version every 3600 seconds |
|
258 | + ['version' => 4989200, 'path' => 'keep', 'size' => 1], |
|
259 | + // next slice (2592000sec) start at 4913600 keep one version every 86400 seconds |
|
260 | + ['version' => 4913600, 'path' => 'keep', 'size' => 1], |
|
261 | + ['version' => 4852800, 'path' => 'delete', 'size' => 1], |
|
262 | + ['version' => 4827201, 'path' => 'delete', 'size' => 1], |
|
263 | + ['version' => 4827200, 'path' => 'keep', 'size' => 1], |
|
264 | + ['version' => 4777201, 'path' => 'delete', 'size' => 1], |
|
265 | + ['version' => 4777501, 'path' => 'delete', 'size' => 1], |
|
266 | + ['version' => 4740000, 'path' => 'keep', 'size' => 1], |
|
267 | + // final slice starts at 2408000 keep one version every 604800 secons |
|
268 | + ['version' => 2408000, 'path' => 'keep', 'size' => 1], |
|
269 | + ['version' => 1803201, 'path' => 'delete', 'size' => 1], |
|
270 | + ['version' => 1803200, 'path' => 'keep', 'size' => 1], |
|
271 | + ['version' => 1800199, 'path' => 'delete', 'size' => 1], |
|
272 | + ['version' => 1800100, 'path' => 'delete', 'size' => 1], |
|
273 | + ['version' => 1198300, 'path' => 'keep', 'size' => 1], |
|
274 | + ], |
|
275 | + 9 // size of all deleted files (every file has the size 1) |
|
276 | + ], |
|
277 | + // fourth set of versions: empty (see issue #19066) |
|
278 | + [ |
|
279 | + [], |
|
280 | + 0 |
|
281 | + ] |
|
282 | + |
|
283 | + ]; |
|
284 | + } |
|
285 | + |
|
286 | + public function testRename(): void { |
|
287 | + Filesystem::file_put_contents('test.txt', 'test file'); |
|
288 | + |
|
289 | + $t1 = time(); |
|
290 | + // second version is two weeks older, this way we make sure that no |
|
291 | + // version will be expired |
|
292 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
293 | + |
|
294 | + // create some versions |
|
295 | + $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
296 | + $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
297 | + $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
298 | + $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
299 | + |
|
300 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
301 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
302 | + |
|
303 | + // execute rename hook of versions app |
|
304 | + Filesystem::rename('test.txt', 'test2.txt'); |
|
305 | + |
|
306 | + $this->runCommands(); |
|
307 | + |
|
308 | + $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist'); |
|
309 | + $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist'); |
|
310 | + |
|
311 | + $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists'); |
|
312 | + $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists'); |
|
313 | + } |
|
314 | + |
|
315 | + public function testRenameInSharedFolder(): void { |
|
316 | + Filesystem::mkdir('folder1'); |
|
317 | + Filesystem::mkdir('folder1/folder2'); |
|
318 | + Filesystem::file_put_contents('folder1/test.txt', 'test file'); |
|
319 | + |
|
320 | + $t1 = time(); |
|
321 | + // second version is two weeks older, this way we make sure that no |
|
322 | + // version will be expired |
|
323 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
324 | + |
|
325 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); |
|
326 | + // create some versions |
|
327 | + $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; |
|
328 | + $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; |
|
329 | + $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1; |
|
330 | + $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2; |
|
331 | + |
|
332 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
333 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
334 | + |
|
335 | + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); |
|
336 | + $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
337 | + $share->setNode($node) |
|
338 | + ->setShareType(IShare::TYPE_USER) |
|
339 | + ->setSharedBy(self::TEST_VERSIONS_USER) |
|
340 | + ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
341 | + ->setPermissions(Constants::PERMISSION_ALL); |
|
342 | + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
343 | + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
344 | + |
|
345 | + self::loginHelper(self::TEST_VERSIONS_USER2); |
|
346 | + |
|
347 | + $this->assertTrue(Filesystem::file_exists('folder1/test.txt')); |
|
348 | + |
|
349 | + // execute rename hook of versions app |
|
350 | + Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt'); |
|
351 | + |
|
352 | + $this->runCommands(); |
|
353 | + |
|
354 | + self::loginHelper(self::TEST_VERSIONS_USER); |
|
355 | + |
|
356 | + $this->assertFalse($this->rootView->file_exists($v1), 'version 1 of old file does not exist'); |
|
357 | + $this->assertFalse($this->rootView->file_exists($v2), 'version 2 of old file does not exist'); |
|
358 | + |
|
359 | + $this->assertTrue($this->rootView->file_exists($v1Renamed), 'version 1 of renamed file exists'); |
|
360 | + $this->assertTrue($this->rootView->file_exists($v2Renamed), 'version 2 of renamed file exists'); |
|
361 | + |
|
362 | + Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
363 | + } |
|
364 | + |
|
365 | + public function testMoveFolder(): void { |
|
366 | + Filesystem::mkdir('folder1'); |
|
367 | + Filesystem::mkdir('folder2'); |
|
368 | + Filesystem::file_put_contents('folder1/test.txt', 'test file'); |
|
369 | + |
|
370 | + $t1 = time(); |
|
371 | + // second version is two weeks older, this way we make sure that no |
|
372 | + // version will be expired |
|
373 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
374 | + |
|
375 | + // create some versions |
|
376 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); |
|
377 | + $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; |
|
378 | + $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; |
|
379 | + $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1; |
|
380 | + $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2; |
|
381 | 381 | |
382 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
383 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
382 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
383 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
384 | 384 | |
385 | - // execute rename hook of versions app |
|
386 | - Filesystem::rename('folder1', 'folder2/folder1'); |
|
385 | + // execute rename hook of versions app |
|
386 | + Filesystem::rename('folder1', 'folder2/folder1'); |
|
387 | 387 | |
388 | - $this->runCommands(); |
|
388 | + $this->runCommands(); |
|
389 | 389 | |
390 | - $this->assertFalse($this->rootView->file_exists($v1)); |
|
391 | - $this->assertFalse($this->rootView->file_exists($v2)); |
|
390 | + $this->assertFalse($this->rootView->file_exists($v1)); |
|
391 | + $this->assertFalse($this->rootView->file_exists($v2)); |
|
392 | 392 | |
393 | - $this->assertTrue($this->rootView->file_exists($v1Renamed)); |
|
394 | - $this->assertTrue($this->rootView->file_exists($v2Renamed)); |
|
395 | - } |
|
393 | + $this->assertTrue($this->rootView->file_exists($v1Renamed)); |
|
394 | + $this->assertTrue($this->rootView->file_exists($v2Renamed)); |
|
395 | + } |
|
396 | 396 | |
397 | 397 | |
398 | - public function testMoveFileIntoSharedFolderAsRecipient(): void { |
|
399 | - Filesystem::mkdir('folder1'); |
|
400 | - $fileInfo = Filesystem::getFileInfo('folder1'); |
|
398 | + public function testMoveFileIntoSharedFolderAsRecipient(): void { |
|
399 | + Filesystem::mkdir('folder1'); |
|
400 | + $fileInfo = Filesystem::getFileInfo('folder1'); |
|
401 | 401 | |
402 | - $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); |
|
403 | - $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
404 | - $share->setNode($node) |
|
405 | - ->setShareType(IShare::TYPE_USER) |
|
406 | - ->setSharedBy(self::TEST_VERSIONS_USER) |
|
407 | - ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
408 | - ->setPermissions(Constants::PERMISSION_ALL); |
|
409 | - $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
410 | - Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
402 | + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); |
|
403 | + $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
404 | + $share->setNode($node) |
|
405 | + ->setShareType(IShare::TYPE_USER) |
|
406 | + ->setSharedBy(self::TEST_VERSIONS_USER) |
|
407 | + ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
408 | + ->setPermissions(Constants::PERMISSION_ALL); |
|
409 | + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
410 | + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
411 | 411 | |
412 | - self::loginHelper(self::TEST_VERSIONS_USER2); |
|
413 | - $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; |
|
414 | - Filesystem::file_put_contents('test.txt', 'test file'); |
|
412 | + self::loginHelper(self::TEST_VERSIONS_USER2); |
|
413 | + $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; |
|
414 | + Filesystem::file_put_contents('test.txt', 'test file'); |
|
415 | 415 | |
416 | - $t1 = time(); |
|
417 | - // second version is two weeks older, this way we make sure that no |
|
418 | - // version will be expired |
|
419 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
416 | + $t1 = time(); |
|
417 | + // second version is two weeks older, this way we make sure that no |
|
418 | + // version will be expired |
|
419 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
420 | 420 | |
421 | - $this->rootView->mkdir($versionsFolder2); |
|
422 | - // create some versions |
|
423 | - $v1 = $versionsFolder2 . '/test.txt.v' . $t1; |
|
424 | - $v2 = $versionsFolder2 . '/test.txt.v' . $t2; |
|
421 | + $this->rootView->mkdir($versionsFolder2); |
|
422 | + // create some versions |
|
423 | + $v1 = $versionsFolder2 . '/test.txt.v' . $t1; |
|
424 | + $v2 = $versionsFolder2 . '/test.txt.v' . $t2; |
|
425 | 425 | |
426 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
427 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
426 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
427 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
428 | 428 | |
429 | - // move file into the shared folder as recipient |
|
430 | - Filesystem::rename('/test.txt', '/folder1/test.txt'); |
|
429 | + // move file into the shared folder as recipient |
|
430 | + Filesystem::rename('/test.txt', '/folder1/test.txt'); |
|
431 | 431 | |
432 | - $this->assertFalse($this->rootView->file_exists($v1)); |
|
433 | - $this->assertFalse($this->rootView->file_exists($v2)); |
|
432 | + $this->assertFalse($this->rootView->file_exists($v1)); |
|
433 | + $this->assertFalse($this->rootView->file_exists($v2)); |
|
434 | 434 | |
435 | - self::loginHelper(self::TEST_VERSIONS_USER); |
|
435 | + self::loginHelper(self::TEST_VERSIONS_USER); |
|
436 | 436 | |
437 | - $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; |
|
437 | + $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; |
|
438 | 438 | |
439 | - $v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1; |
|
440 | - $v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2; |
|
439 | + $v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1; |
|
440 | + $v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2; |
|
441 | 441 | |
442 | - $this->assertTrue($this->rootView->file_exists($v1Renamed)); |
|
443 | - $this->assertTrue($this->rootView->file_exists($v2Renamed)); |
|
442 | + $this->assertTrue($this->rootView->file_exists($v1Renamed)); |
|
443 | + $this->assertTrue($this->rootView->file_exists($v2Renamed)); |
|
444 | 444 | |
445 | - Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
446 | - } |
|
445 | + Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
446 | + } |
|
447 | 447 | |
448 | - public function testMoveFolderIntoSharedFolderAsRecipient(): void { |
|
449 | - Filesystem::mkdir('folder1'); |
|
448 | + public function testMoveFolderIntoSharedFolderAsRecipient(): void { |
|
449 | + Filesystem::mkdir('folder1'); |
|
450 | 450 | |
451 | - $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); |
|
452 | - $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
453 | - $share->setNode($node) |
|
454 | - ->setShareType(IShare::TYPE_USER) |
|
455 | - ->setSharedBy(self::TEST_VERSIONS_USER) |
|
456 | - ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
457 | - ->setPermissions(Constants::PERMISSION_ALL); |
|
458 | - $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
459 | - Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
451 | + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1'); |
|
452 | + $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
453 | + $share->setNode($node) |
|
454 | + ->setShareType(IShare::TYPE_USER) |
|
455 | + ->setSharedBy(self::TEST_VERSIONS_USER) |
|
456 | + ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
457 | + ->setPermissions(Constants::PERMISSION_ALL); |
|
458 | + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
459 | + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
460 | 460 | |
461 | - self::loginHelper(self::TEST_VERSIONS_USER2); |
|
462 | - $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; |
|
463 | - Filesystem::mkdir('folder2'); |
|
464 | - Filesystem::file_put_contents('folder2/test.txt', 'test file'); |
|
461 | + self::loginHelper(self::TEST_VERSIONS_USER2); |
|
462 | + $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; |
|
463 | + Filesystem::mkdir('folder2'); |
|
464 | + Filesystem::file_put_contents('folder2/test.txt', 'test file'); |
|
465 | 465 | |
466 | - $t1 = time(); |
|
467 | - // second version is two weeks older, this way we make sure that no |
|
468 | - // version will be expired |
|
469 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
466 | + $t1 = time(); |
|
467 | + // second version is two weeks older, this way we make sure that no |
|
468 | + // version will be expired |
|
469 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
470 | 470 | |
471 | - $this->rootView->mkdir($versionsFolder2); |
|
472 | - $this->rootView->mkdir($versionsFolder2 . '/folder2'); |
|
473 | - // create some versions |
|
474 | - $v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1; |
|
475 | - $v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2; |
|
471 | + $this->rootView->mkdir($versionsFolder2); |
|
472 | + $this->rootView->mkdir($versionsFolder2 . '/folder2'); |
|
473 | + // create some versions |
|
474 | + $v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1; |
|
475 | + $v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2; |
|
476 | 476 | |
477 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
478 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
477 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
478 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
479 | 479 | |
480 | - // move file into the shared folder as recipient |
|
481 | - Filesystem::rename('/folder2', '/folder1/folder2'); |
|
480 | + // move file into the shared folder as recipient |
|
481 | + Filesystem::rename('/folder2', '/folder1/folder2'); |
|
482 | 482 | |
483 | - $this->assertFalse($this->rootView->file_exists($v1)); |
|
484 | - $this->assertFalse($this->rootView->file_exists($v2)); |
|
483 | + $this->assertFalse($this->rootView->file_exists($v1)); |
|
484 | + $this->assertFalse($this->rootView->file_exists($v2)); |
|
485 | 485 | |
486 | - self::loginHelper(self::TEST_VERSIONS_USER); |
|
486 | + self::loginHelper(self::TEST_VERSIONS_USER); |
|
487 | 487 | |
488 | - $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; |
|
488 | + $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; |
|
489 | 489 | |
490 | - $v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1; |
|
491 | - $v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2; |
|
490 | + $v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1; |
|
491 | + $v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2; |
|
492 | 492 | |
493 | - $this->assertTrue($this->rootView->file_exists($v1Renamed)); |
|
494 | - $this->assertTrue($this->rootView->file_exists($v2Renamed)); |
|
493 | + $this->assertTrue($this->rootView->file_exists($v1Renamed)); |
|
494 | + $this->assertTrue($this->rootView->file_exists($v2Renamed)); |
|
495 | 495 | |
496 | - Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
497 | - } |
|
496 | + Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
497 | + } |
|
498 | 498 | |
499 | - public function testRenameSharedFile(): void { |
|
500 | - Filesystem::file_put_contents('test.txt', 'test file'); |
|
499 | + public function testRenameSharedFile(): void { |
|
500 | + Filesystem::file_put_contents('test.txt', 'test file'); |
|
501 | 501 | |
502 | - $t1 = time(); |
|
503 | - // second version is two weeks older, this way we make sure that no |
|
504 | - // version will be expired |
|
505 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
502 | + $t1 = time(); |
|
503 | + // second version is two weeks older, this way we make sure that no |
|
504 | + // version will be expired |
|
505 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
506 | 506 | |
507 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); |
|
508 | - // create some versions |
|
509 | - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
510 | - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
511 | - // the renamed versions should not exist! Because we only moved the mount point! |
|
512 | - $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
513 | - $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
507 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); |
|
508 | + // create some versions |
|
509 | + $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
510 | + $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
511 | + // the renamed versions should not exist! Because we only moved the mount point! |
|
512 | + $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
513 | + $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
514 | 514 | |
515 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
516 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
515 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
516 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
517 | 517 | |
518 | - $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('test.txt'); |
|
519 | - $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
520 | - $share->setNode($node) |
|
521 | - ->setShareType(IShare::TYPE_USER) |
|
522 | - ->setSharedBy(self::TEST_VERSIONS_USER) |
|
523 | - ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
524 | - ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE); |
|
525 | - $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
526 | - Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
518 | + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('test.txt'); |
|
519 | + $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
520 | + $share->setNode($node) |
|
521 | + ->setShareType(IShare::TYPE_USER) |
|
522 | + ->setSharedBy(self::TEST_VERSIONS_USER) |
|
523 | + ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
524 | + ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE); |
|
525 | + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
526 | + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
527 | 527 | |
528 | - self::loginHelper(self::TEST_VERSIONS_USER2); |
|
528 | + self::loginHelper(self::TEST_VERSIONS_USER2); |
|
529 | 529 | |
530 | - $this->assertTrue(Filesystem::file_exists('test.txt')); |
|
530 | + $this->assertTrue(Filesystem::file_exists('test.txt')); |
|
531 | 531 | |
532 | - // execute rename hook of versions app |
|
533 | - Filesystem::rename('test.txt', 'test2.txt'); |
|
532 | + // execute rename hook of versions app |
|
533 | + Filesystem::rename('test.txt', 'test2.txt'); |
|
534 | 534 | |
535 | - self::loginHelper(self::TEST_VERSIONS_USER); |
|
535 | + self::loginHelper(self::TEST_VERSIONS_USER); |
|
536 | 536 | |
537 | - $this->runCommands(); |
|
537 | + $this->runCommands(); |
|
538 | 538 | |
539 | - $this->assertTrue($this->rootView->file_exists($v1)); |
|
540 | - $this->assertTrue($this->rootView->file_exists($v2)); |
|
539 | + $this->assertTrue($this->rootView->file_exists($v1)); |
|
540 | + $this->assertTrue($this->rootView->file_exists($v2)); |
|
541 | 541 | |
542 | - $this->assertFalse($this->rootView->file_exists($v1Renamed)); |
|
543 | - $this->assertFalse($this->rootView->file_exists($v2Renamed)); |
|
542 | + $this->assertFalse($this->rootView->file_exists($v1Renamed)); |
|
543 | + $this->assertFalse($this->rootView->file_exists($v2Renamed)); |
|
544 | 544 | |
545 | - Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
546 | - } |
|
545 | + Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
546 | + } |
|
547 | 547 | |
548 | - public function testCopy(): void { |
|
549 | - Filesystem::file_put_contents('test.txt', 'test file'); |
|
548 | + public function testCopy(): void { |
|
549 | + Filesystem::file_put_contents('test.txt', 'test file'); |
|
550 | 550 | |
551 | - $t1 = time(); |
|
552 | - // second version is two weeks older, this way we make sure that no |
|
553 | - // version will be expired |
|
554 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
551 | + $t1 = time(); |
|
552 | + // second version is two weeks older, this way we make sure that no |
|
553 | + // version will be expired |
|
554 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
555 | 555 | |
556 | - // create some versions |
|
557 | - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
558 | - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
559 | - $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
560 | - $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
556 | + // create some versions |
|
557 | + $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
558 | + $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
559 | + $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
560 | + $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
561 | 561 | |
562 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
563 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
562 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
563 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
564 | 564 | |
565 | - // execute copy hook of versions app |
|
566 | - Filesystem::copy('test.txt', 'test2.txt'); |
|
565 | + // execute copy hook of versions app |
|
566 | + Filesystem::copy('test.txt', 'test2.txt'); |
|
567 | 567 | |
568 | - $this->runCommands(); |
|
568 | + $this->runCommands(); |
|
569 | 569 | |
570 | - $this->assertTrue($this->rootView->file_exists($v1), 'version 1 of original file exists'); |
|
571 | - $this->assertTrue($this->rootView->file_exists($v2), 'version 2 of original file exists'); |
|
570 | + $this->assertTrue($this->rootView->file_exists($v1), 'version 1 of original file exists'); |
|
571 | + $this->assertTrue($this->rootView->file_exists($v2), 'version 2 of original file exists'); |
|
572 | 572 | |
573 | - $this->assertTrue($this->rootView->file_exists($v1Copied), 'version 1 of copied file exists'); |
|
574 | - $this->assertTrue($this->rootView->file_exists($v2Copied), 'version 2 of copied file exists'); |
|
575 | - } |
|
573 | + $this->assertTrue($this->rootView->file_exists($v1Copied), 'version 1 of copied file exists'); |
|
574 | + $this->assertTrue($this->rootView->file_exists($v2Copied), 'version 2 of copied file exists'); |
|
575 | + } |
|
576 | 576 | |
577 | - /** |
|
578 | - * test if we find all versions and if the versions array contain |
|
579 | - * the correct 'path' and 'name' |
|
580 | - */ |
|
581 | - public function testGetVersions(): void { |
|
582 | - $t1 = time(); |
|
583 | - // second version is two weeks older, this way we make sure that no |
|
584 | - // version will be expired |
|
585 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
577 | + /** |
|
578 | + * test if we find all versions and if the versions array contain |
|
579 | + * the correct 'path' and 'name' |
|
580 | + */ |
|
581 | + public function testGetVersions(): void { |
|
582 | + $t1 = time(); |
|
583 | + // second version is two weeks older, this way we make sure that no |
|
584 | + // version will be expired |
|
585 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
586 | 586 | |
587 | - // create some versions |
|
588 | - $v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1; |
|
589 | - $v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2; |
|
587 | + // create some versions |
|
588 | + $v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1; |
|
589 | + $v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2; |
|
590 | 590 | |
591 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/'); |
|
591 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/'); |
|
592 | 592 | |
593 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
594 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
593 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
594 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
595 | 595 | |
596 | - // execute copy hook of versions app |
|
597 | - $versions = Storage::getVersions(self::TEST_VERSIONS_USER, '/subfolder/test.txt'); |
|
596 | + // execute copy hook of versions app |
|
597 | + $versions = Storage::getVersions(self::TEST_VERSIONS_USER, '/subfolder/test.txt'); |
|
598 | 598 | |
599 | - $this->assertCount(2, $versions); |
|
599 | + $this->assertCount(2, $versions); |
|
600 | 600 | |
601 | - foreach ($versions as $version) { |
|
602 | - $this->assertSame('/subfolder/test.txt', $version['path']); |
|
603 | - $this->assertSame('test.txt', $version['name']); |
|
604 | - } |
|
601 | + foreach ($versions as $version) { |
|
602 | + $this->assertSame('/subfolder/test.txt', $version['path']); |
|
603 | + $this->assertSame('test.txt', $version['name']); |
|
604 | + } |
|
605 | 605 | |
606 | - //cleanup |
|
607 | - $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder'); |
|
608 | - } |
|
606 | + //cleanup |
|
607 | + $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder'); |
|
608 | + } |
|
609 | 609 | |
610 | - /** |
|
611 | - * test if we find all versions and if the versions array contain |
|
612 | - * the correct 'path' and 'name' |
|
613 | - */ |
|
614 | - public function testGetVersionsEmptyFile(): void { |
|
615 | - // execute copy hook of versions app |
|
616 | - $versions = Storage::getVersions(self::TEST_VERSIONS_USER, ''); |
|
617 | - $this->assertCount(0, $versions); |
|
610 | + /** |
|
611 | + * test if we find all versions and if the versions array contain |
|
612 | + * the correct 'path' and 'name' |
|
613 | + */ |
|
614 | + public function testGetVersionsEmptyFile(): void { |
|
615 | + // execute copy hook of versions app |
|
616 | + $versions = Storage::getVersions(self::TEST_VERSIONS_USER, ''); |
|
617 | + $this->assertCount(0, $versions); |
|
618 | 618 | |
619 | - $versions = Storage::getVersions(self::TEST_VERSIONS_USER, null); |
|
620 | - $this->assertCount(0, $versions); |
|
621 | - } |
|
619 | + $versions = Storage::getVersions(self::TEST_VERSIONS_USER, null); |
|
620 | + $this->assertCount(0, $versions); |
|
621 | + } |
|
622 | 622 | |
623 | - public function testExpireNonexistingFile(): void { |
|
624 | - $this->logout(); |
|
625 | - // needed to have a FS setup (the background job does this) |
|
626 | - \OC_Util::setupFS(self::TEST_VERSIONS_USER); |
|
623 | + public function testExpireNonexistingFile(): void { |
|
624 | + $this->logout(); |
|
625 | + // needed to have a FS setup (the background job does this) |
|
626 | + \OC_Util::setupFS(self::TEST_VERSIONS_USER); |
|
627 | 627 | |
628 | - $this->assertFalse(Storage::expire('/void/unexist.txt', self::TEST_VERSIONS_USER)); |
|
629 | - } |
|
628 | + $this->assertFalse(Storage::expire('/void/unexist.txt', self::TEST_VERSIONS_USER)); |
|
629 | + } |
|
630 | 630 | |
631 | 631 | |
632 | - public function testExpireNonexistingUser(): void { |
|
633 | - $this->expectException(NoUserException::class); |
|
632 | + public function testExpireNonexistingUser(): void { |
|
633 | + $this->expectException(NoUserException::class); |
|
634 | 634 | |
635 | - $this->logout(); |
|
636 | - // needed to have a FS setup (the background job does this) |
|
637 | - \OC_Util::setupFS(self::TEST_VERSIONS_USER); |
|
638 | - Filesystem::file_put_contents('test.txt', 'test file'); |
|
635 | + $this->logout(); |
|
636 | + // needed to have a FS setup (the background job does this) |
|
637 | + \OC_Util::setupFS(self::TEST_VERSIONS_USER); |
|
638 | + Filesystem::file_put_contents('test.txt', 'test file'); |
|
639 | 639 | |
640 | - $this->assertFalse(Storage::expire('test.txt', 'unexist')); |
|
641 | - } |
|
640 | + $this->assertFalse(Storage::expire('test.txt', 'unexist')); |
|
641 | + } |
|
642 | 642 | |
643 | - public function testRestoreSameStorage(): void { |
|
644 | - Filesystem::mkdir('sub'); |
|
645 | - $this->doTestRestore(); |
|
646 | - } |
|
643 | + public function testRestoreSameStorage(): void { |
|
644 | + Filesystem::mkdir('sub'); |
|
645 | + $this->doTestRestore(); |
|
646 | + } |
|
647 | 647 | |
648 | - public function testRestoreCrossStorage(): void { |
|
649 | - $storage2 = new Temporary([]); |
|
650 | - Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER . '/files/sub'); |
|
648 | + public function testRestoreCrossStorage(): void { |
|
649 | + $storage2 = new Temporary([]); |
|
650 | + Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER . '/files/sub'); |
|
651 | 651 | |
652 | - $this->doTestRestore(); |
|
653 | - } |
|
652 | + $this->doTestRestore(); |
|
653 | + } |
|
654 | 654 | |
655 | - public function testRestoreNoPermission(): void { |
|
656 | - $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
655 | + public function testRestoreNoPermission(): void { |
|
656 | + $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
657 | 657 | |
658 | - $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER); |
|
659 | - $node = $userHome->newFolder('folder'); |
|
660 | - $file = $node->newFile('test.txt'); |
|
658 | + $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER); |
|
659 | + $node = $userHome->newFolder('folder'); |
|
660 | + $file = $node->newFile('test.txt'); |
|
661 | 661 | |
662 | - $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
663 | - $share->setNode($node) |
|
664 | - ->setShareType(IShare::TYPE_USER) |
|
665 | - ->setSharedBy(self::TEST_VERSIONS_USER) |
|
666 | - ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
667 | - ->setPermissions(Constants::PERMISSION_READ); |
|
668 | - $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
669 | - Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
670 | - |
|
671 | - $versions = $this->createAndCheckVersions( |
|
672 | - Filesystem::getView(), |
|
673 | - 'folder/test.txt' |
|
674 | - ); |
|
675 | - |
|
676 | - $file->putContent('test file'); |
|
677 | - |
|
678 | - $this->loginAsUser(self::TEST_VERSIONS_USER2); |
|
662 | + $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
663 | + $share->setNode($node) |
|
664 | + ->setShareType(IShare::TYPE_USER) |
|
665 | + ->setSharedBy(self::TEST_VERSIONS_USER) |
|
666 | + ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
667 | + ->setPermissions(Constants::PERMISSION_READ); |
|
668 | + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
669 | + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
670 | + |
|
671 | + $versions = $this->createAndCheckVersions( |
|
672 | + Filesystem::getView(), |
|
673 | + 'folder/test.txt' |
|
674 | + ); |
|
675 | + |
|
676 | + $file->putContent('test file'); |
|
677 | + |
|
678 | + $this->loginAsUser(self::TEST_VERSIONS_USER2); |
|
679 | 679 | |
680 | - $firstVersion = current($versions); |
|
680 | + $firstVersion = current($versions); |
|
681 | 681 | |
682 | - $this->assertFalse(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user2), 'Revert did not happen'); |
|
682 | + $this->assertFalse(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user2), 'Revert did not happen'); |
|
683 | 683 | |
684 | - $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
684 | + $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
685 | 685 | |
686 | - Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
687 | - $this->assertEquals('test file', $file->getContent(), 'File content has not changed'); |
|
688 | - } |
|
686 | + Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
687 | + $this->assertEquals('test file', $file->getContent(), 'File content has not changed'); |
|
688 | + } |
|
689 | 689 | |
690 | - public function testRestoreMovedShare(): void { |
|
691 | - $this->markTestSkipped('Unreliable test'); |
|
692 | - $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
693 | - |
|
694 | - $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER); |
|
695 | - $node = $userHome->newFolder('folder'); |
|
696 | - $file = $node->newFile('test.txt'); |
|
697 | - |
|
698 | - $userHome2 = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER2); |
|
699 | - $userHome2->newFolder('subfolder'); |
|
700 | - |
|
701 | - $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
702 | - $share->setNode($node) |
|
703 | - ->setShareType(IShare::TYPE_USER) |
|
704 | - ->setSharedBy(self::TEST_VERSIONS_USER) |
|
705 | - ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
706 | - ->setPermissions(Constants::PERMISSION_ALL); |
|
707 | - $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
708 | - $shareManager = Server::get(\OCP\Share\IManager::class); |
|
709 | - $shareManager->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
710 | - |
|
711 | - $share->setTarget('subfolder/folder'); |
|
712 | - $shareManager->moveShare($share, self::TEST_VERSIONS_USER2); |
|
713 | - |
|
714 | - $versions = $this->createAndCheckVersions( |
|
715 | - Filesystem::getView(), |
|
716 | - 'folder/test.txt' |
|
717 | - ); |
|
718 | - |
|
719 | - $file->putContent('test file'); |
|
720 | - |
|
721 | - $this->loginAsUser(self::TEST_VERSIONS_USER2); |
|
722 | - |
|
723 | - $firstVersion = current($versions); |
|
724 | - |
|
725 | - $this->assertTrue(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user1)); |
|
726 | - |
|
727 | - $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
728 | - |
|
729 | - Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
730 | - $this->assertEquals('version 2', $file->getContent(), 'File content has not changed'); |
|
731 | - } |
|
732 | - |
|
733 | - /** |
|
734 | - * @param string $hookName name of hook called |
|
735 | - * @param string $params variable to receive parameters provided by hook |
|
736 | - */ |
|
737 | - private function connectMockHooks($hookName, &$params) { |
|
738 | - if ($hookName === null) { |
|
739 | - return; |
|
740 | - } |
|
741 | - |
|
742 | - $eventHandler = $this->getMockBuilder(\stdclass::class) |
|
743 | - ->setMethods(['callback']) |
|
744 | - ->getMock(); |
|
745 | - |
|
746 | - $eventHandler->expects($this->any()) |
|
747 | - ->method('callback') |
|
748 | - ->willReturnCallback( |
|
749 | - function ($p) use (&$params): void { |
|
750 | - $params = $p; |
|
751 | - } |
|
752 | - ); |
|
753 | - |
|
754 | - Util::connectHook( |
|
755 | - '\OCP\Versions', |
|
756 | - $hookName, |
|
757 | - $eventHandler, |
|
758 | - 'callback' |
|
759 | - ); |
|
760 | - } |
|
761 | - |
|
762 | - private function doTestRestore() { |
|
763 | - $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt'; |
|
764 | - $this->rootView->file_put_contents($filePath, 'test file'); |
|
765 | - |
|
766 | - $fileInfo = $this->rootView->getFileInfo($filePath); |
|
767 | - $t0 = $this->rootView->filemtime($filePath); |
|
768 | - |
|
769 | - // not exactly the same timestamp as the file |
|
770 | - $t1 = time() - 60; |
|
771 | - // second version is two weeks older |
|
772 | - $t2 = $t1 - 60 * 60 * 24 * 14; |
|
773 | - |
|
774 | - // create some versions |
|
775 | - $v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1; |
|
776 | - $v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2; |
|
777 | - |
|
778 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub'); |
|
779 | - |
|
780 | - $this->rootView->file_put_contents($v1, 'version1'); |
|
781 | - $fileInfoV1 = $this->rootView->getFileInfo($v1); |
|
782 | - $versionEntity = new VersionEntity(); |
|
783 | - $versionEntity->setFileId($fileInfo->getId()); |
|
784 | - $versionEntity->setTimestamp($t1); |
|
785 | - $versionEntity->setSize($fileInfoV1->getSize()); |
|
786 | - $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV1->getMimetype())); |
|
787 | - $versionEntity->setMetadata([]); |
|
788 | - $this->versionsMapper->insert($versionEntity); |
|
789 | - |
|
790 | - $this->rootView->file_put_contents($v2, 'version2'); |
|
791 | - $fileInfoV2 = $this->rootView->getFileInfo($v2); |
|
792 | - $versionEntity = new VersionEntity(); |
|
793 | - $versionEntity->setFileId($fileInfo->getId()); |
|
794 | - $versionEntity->setTimestamp($t2); |
|
795 | - $versionEntity->setSize($fileInfoV2->getSize()); |
|
796 | - $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV2->getMimetype())); |
|
797 | - $versionEntity->setMetadata([]); |
|
798 | - $this->versionsMapper->insert($versionEntity); |
|
799 | - |
|
800 | - $oldVersions = Storage::getVersions( |
|
801 | - self::TEST_VERSIONS_USER, '/sub/test.txt' |
|
802 | - ); |
|
803 | - |
|
804 | - $this->assertCount(2, $oldVersions); |
|
805 | - |
|
806 | - $this->assertEquals('test file', $this->rootView->file_get_contents($filePath)); |
|
807 | - $info1 = $this->rootView->getFileInfo($filePath); |
|
808 | - |
|
809 | - $eventDispatcher = Server::get(IEventDispatcher::class); |
|
810 | - $eventFired = false; |
|
811 | - $eventDispatcher->addListener(VersionRestoredEvent::class, function ($event) use (&$eventFired, $t2): void { |
|
812 | - $eventFired = true; |
|
813 | - $this->assertEquals('/sub/test.txt', $event->getVersion()->getVersionPath()); |
|
814 | - $this->assertTrue($event->getVersion()->getRevisionId() > 0); |
|
815 | - }); |
|
816 | - |
|
817 | - $versionManager = Server::get(IVersionManager::class); |
|
818 | - $versions = $versionManager->getVersionsForFile($this->user1, $info1); |
|
819 | - $version = array_filter($versions, function ($version) use ($t2) { |
|
820 | - return $version->getRevisionId() === $t2; |
|
821 | - }); |
|
822 | - $this->assertTrue($versionManager->rollback(current($version))); |
|
823 | - |
|
824 | - $this->assertTrue($eventFired, 'VersionRestoredEvent was not fired'); |
|
825 | - |
|
826 | - $this->assertEquals('version2', $this->rootView->file_get_contents($filePath)); |
|
827 | - $info2 = $this->rootView->getFileInfo($filePath); |
|
828 | - |
|
829 | - $this->assertNotEquals( |
|
830 | - $info2['etag'], |
|
831 | - $info1['etag'], |
|
832 | - 'Etag must change after rolling back version' |
|
833 | - ); |
|
834 | - $this->assertEquals( |
|
835 | - $info2['fileid'], |
|
836 | - $info1['fileid'], |
|
837 | - 'File id must not change after rolling back version' |
|
838 | - ); |
|
839 | - $this->assertEquals( |
|
840 | - $info2['mtime'], |
|
841 | - $t2, |
|
842 | - 'Restored file has mtime from version' |
|
843 | - ); |
|
844 | - |
|
845 | - $newVersions = Storage::getVersions( |
|
846 | - self::TEST_VERSIONS_USER, '/sub/test.txt' |
|
847 | - ); |
|
848 | - |
|
849 | - $this->assertTrue( |
|
850 | - $this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0), |
|
851 | - 'A version file was created for the file before restoration' |
|
852 | - ); |
|
853 | - $this->assertTrue( |
|
854 | - $this->rootView->file_exists($v1), |
|
855 | - 'Untouched version file is still there' |
|
856 | - ); |
|
857 | - $this->assertFalse( |
|
858 | - $this->rootView->file_exists($v2), |
|
859 | - 'Restored version file gone from files_version folder' |
|
860 | - ); |
|
861 | - |
|
862 | - $this->assertCount(2, $newVersions, 'Additional version created'); |
|
863 | - |
|
864 | - $this->assertTrue( |
|
865 | - isset($newVersions[$t0 . '#' . 'test.txt']), |
|
866 | - 'A version was created for the file before restoration' |
|
867 | - ); |
|
868 | - $this->assertTrue( |
|
869 | - isset($newVersions[$t1 . '#' . 'test.txt']), |
|
870 | - 'Untouched version is still there' |
|
871 | - ); |
|
872 | - $this->assertFalse( |
|
873 | - isset($newVersions[$t2 . '#' . 'test.txt']), |
|
874 | - 'Restored version is not in the list any more' |
|
875 | - ); |
|
876 | - } |
|
877 | - |
|
878 | - /** |
|
879 | - * Test whether versions are created when overwriting as owner |
|
880 | - */ |
|
881 | - public function testStoreVersionAsOwner(): void { |
|
882 | - $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
883 | - |
|
884 | - $this->createAndCheckVersions( |
|
885 | - Filesystem::getView(), |
|
886 | - 'test.txt' |
|
887 | - ); |
|
888 | - } |
|
889 | - |
|
890 | - /** |
|
891 | - * Test whether versions are created when overwriting as share recipient |
|
892 | - */ |
|
893 | - public function testStoreVersionAsRecipient(): void { |
|
894 | - $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
895 | - |
|
896 | - Filesystem::mkdir('folder'); |
|
897 | - Filesystem::file_put_contents('folder/test.txt', 'test file'); |
|
898 | - |
|
899 | - $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder'); |
|
900 | - $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
901 | - $share->setNode($node) |
|
902 | - ->setShareType(IShare::TYPE_USER) |
|
903 | - ->setSharedBy(self::TEST_VERSIONS_USER) |
|
904 | - ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
905 | - ->setPermissions(Constants::PERMISSION_ALL); |
|
906 | - $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
907 | - Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
908 | - |
|
909 | - $this->loginAsUser(self::TEST_VERSIONS_USER2); |
|
910 | - |
|
911 | - $this->createAndCheckVersions( |
|
912 | - Filesystem::getView(), |
|
913 | - 'folder/test.txt' |
|
914 | - ); |
|
915 | - |
|
916 | - Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
917 | - } |
|
918 | - |
|
919 | - /** |
|
920 | - * Test whether versions are created when overwriting anonymously. |
|
921 | - * |
|
922 | - * When uploading through a public link or publicwebdav, no user |
|
923 | - * is logged in. File modification must still be able to find |
|
924 | - * the owner and create versions. |
|
925 | - */ |
|
926 | - public function testStoreVersionAsAnonymous(): void { |
|
927 | - $this->logout(); |
|
928 | - |
|
929 | - // note: public link upload does this, |
|
930 | - // needed to make the hooks fire |
|
931 | - \OC_Util::setupFS(self::TEST_VERSIONS_USER); |
|
932 | - |
|
933 | - $userView = new View('/' . self::TEST_VERSIONS_USER . '/files'); |
|
934 | - $this->createAndCheckVersions( |
|
935 | - $userView, |
|
936 | - 'test.txt' |
|
937 | - ); |
|
938 | - } |
|
939 | - |
|
940 | - /** |
|
941 | - * @param View $view |
|
942 | - * @param string $path |
|
943 | - */ |
|
944 | - private function createAndCheckVersions(View $view, $path) { |
|
945 | - $view->file_put_contents($path, 'test file'); |
|
946 | - $view->file_put_contents($path, 'version 1'); |
|
947 | - $view->file_put_contents($path, 'version 2'); |
|
948 | - |
|
949 | - $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
950 | - |
|
951 | - // need to scan for the versions |
|
952 | - [$rootStorage,] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions'); |
|
953 | - $rootStorage->getScanner()->scan('files_versions'); |
|
954 | - |
|
955 | - $versions = Storage::getVersions( |
|
956 | - self::TEST_VERSIONS_USER, '/' . $path |
|
957 | - ); |
|
958 | - |
|
959 | - // note: we cannot predict how many versions are created due to |
|
960 | - // test run timing |
|
961 | - $this->assertGreaterThan(0, count($versions)); |
|
962 | - |
|
963 | - return $versions; |
|
964 | - } |
|
965 | - |
|
966 | - /** |
|
967 | - * @param string $user |
|
968 | - * @param bool $create |
|
969 | - */ |
|
970 | - public static function loginHelper($user, $create = false) { |
|
971 | - if ($create) { |
|
972 | - $backend = new \Test\Util\User\Dummy(); |
|
973 | - $backend->createUser($user, $user); |
|
974 | - Server::get(IUserManager::class)->registerBackend($backend); |
|
975 | - } |
|
976 | - |
|
977 | - \OC_Util::tearDownFS(); |
|
978 | - \OC_User::setUserId(''); |
|
979 | - Filesystem::tearDown(); |
|
980 | - \OC_User::setUserId($user); |
|
981 | - \OC_Util::setupFS($user); |
|
982 | - \OC::$server->getUserFolder($user); |
|
983 | - } |
|
690 | + public function testRestoreMovedShare(): void { |
|
691 | + $this->markTestSkipped('Unreliable test'); |
|
692 | + $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
693 | + |
|
694 | + $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER); |
|
695 | + $node = $userHome->newFolder('folder'); |
|
696 | + $file = $node->newFile('test.txt'); |
|
697 | + |
|
698 | + $userHome2 = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER2); |
|
699 | + $userHome2->newFolder('subfolder'); |
|
700 | + |
|
701 | + $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
702 | + $share->setNode($node) |
|
703 | + ->setShareType(IShare::TYPE_USER) |
|
704 | + ->setSharedBy(self::TEST_VERSIONS_USER) |
|
705 | + ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
706 | + ->setPermissions(Constants::PERMISSION_ALL); |
|
707 | + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
708 | + $shareManager = Server::get(\OCP\Share\IManager::class); |
|
709 | + $shareManager->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
710 | + |
|
711 | + $share->setTarget('subfolder/folder'); |
|
712 | + $shareManager->moveShare($share, self::TEST_VERSIONS_USER2); |
|
713 | + |
|
714 | + $versions = $this->createAndCheckVersions( |
|
715 | + Filesystem::getView(), |
|
716 | + 'folder/test.txt' |
|
717 | + ); |
|
718 | + |
|
719 | + $file->putContent('test file'); |
|
720 | + |
|
721 | + $this->loginAsUser(self::TEST_VERSIONS_USER2); |
|
722 | + |
|
723 | + $firstVersion = current($versions); |
|
724 | + |
|
725 | + $this->assertTrue(Storage::rollback('folder/test.txt', $firstVersion['version'], $this->user1)); |
|
726 | + |
|
727 | + $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
728 | + |
|
729 | + Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
730 | + $this->assertEquals('version 2', $file->getContent(), 'File content has not changed'); |
|
731 | + } |
|
732 | + |
|
733 | + /** |
|
734 | + * @param string $hookName name of hook called |
|
735 | + * @param string $params variable to receive parameters provided by hook |
|
736 | + */ |
|
737 | + private function connectMockHooks($hookName, &$params) { |
|
738 | + if ($hookName === null) { |
|
739 | + return; |
|
740 | + } |
|
741 | + |
|
742 | + $eventHandler = $this->getMockBuilder(\stdclass::class) |
|
743 | + ->setMethods(['callback']) |
|
744 | + ->getMock(); |
|
745 | + |
|
746 | + $eventHandler->expects($this->any()) |
|
747 | + ->method('callback') |
|
748 | + ->willReturnCallback( |
|
749 | + function ($p) use (&$params): void { |
|
750 | + $params = $p; |
|
751 | + } |
|
752 | + ); |
|
753 | + |
|
754 | + Util::connectHook( |
|
755 | + '\OCP\Versions', |
|
756 | + $hookName, |
|
757 | + $eventHandler, |
|
758 | + 'callback' |
|
759 | + ); |
|
760 | + } |
|
761 | + |
|
762 | + private function doTestRestore() { |
|
763 | + $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt'; |
|
764 | + $this->rootView->file_put_contents($filePath, 'test file'); |
|
765 | + |
|
766 | + $fileInfo = $this->rootView->getFileInfo($filePath); |
|
767 | + $t0 = $this->rootView->filemtime($filePath); |
|
768 | + |
|
769 | + // not exactly the same timestamp as the file |
|
770 | + $t1 = time() - 60; |
|
771 | + // second version is two weeks older |
|
772 | + $t2 = $t1 - 60 * 60 * 24 * 14; |
|
773 | + |
|
774 | + // create some versions |
|
775 | + $v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1; |
|
776 | + $v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2; |
|
777 | + |
|
778 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub'); |
|
779 | + |
|
780 | + $this->rootView->file_put_contents($v1, 'version1'); |
|
781 | + $fileInfoV1 = $this->rootView->getFileInfo($v1); |
|
782 | + $versionEntity = new VersionEntity(); |
|
783 | + $versionEntity->setFileId($fileInfo->getId()); |
|
784 | + $versionEntity->setTimestamp($t1); |
|
785 | + $versionEntity->setSize($fileInfoV1->getSize()); |
|
786 | + $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV1->getMimetype())); |
|
787 | + $versionEntity->setMetadata([]); |
|
788 | + $this->versionsMapper->insert($versionEntity); |
|
789 | + |
|
790 | + $this->rootView->file_put_contents($v2, 'version2'); |
|
791 | + $fileInfoV2 = $this->rootView->getFileInfo($v2); |
|
792 | + $versionEntity = new VersionEntity(); |
|
793 | + $versionEntity->setFileId($fileInfo->getId()); |
|
794 | + $versionEntity->setTimestamp($t2); |
|
795 | + $versionEntity->setSize($fileInfoV2->getSize()); |
|
796 | + $versionEntity->setMimetype($this->mimeTypeLoader->getId($fileInfoV2->getMimetype())); |
|
797 | + $versionEntity->setMetadata([]); |
|
798 | + $this->versionsMapper->insert($versionEntity); |
|
799 | + |
|
800 | + $oldVersions = Storage::getVersions( |
|
801 | + self::TEST_VERSIONS_USER, '/sub/test.txt' |
|
802 | + ); |
|
803 | + |
|
804 | + $this->assertCount(2, $oldVersions); |
|
805 | + |
|
806 | + $this->assertEquals('test file', $this->rootView->file_get_contents($filePath)); |
|
807 | + $info1 = $this->rootView->getFileInfo($filePath); |
|
808 | + |
|
809 | + $eventDispatcher = Server::get(IEventDispatcher::class); |
|
810 | + $eventFired = false; |
|
811 | + $eventDispatcher->addListener(VersionRestoredEvent::class, function ($event) use (&$eventFired, $t2): void { |
|
812 | + $eventFired = true; |
|
813 | + $this->assertEquals('/sub/test.txt', $event->getVersion()->getVersionPath()); |
|
814 | + $this->assertTrue($event->getVersion()->getRevisionId() > 0); |
|
815 | + }); |
|
816 | + |
|
817 | + $versionManager = Server::get(IVersionManager::class); |
|
818 | + $versions = $versionManager->getVersionsForFile($this->user1, $info1); |
|
819 | + $version = array_filter($versions, function ($version) use ($t2) { |
|
820 | + return $version->getRevisionId() === $t2; |
|
821 | + }); |
|
822 | + $this->assertTrue($versionManager->rollback(current($version))); |
|
823 | + |
|
824 | + $this->assertTrue($eventFired, 'VersionRestoredEvent was not fired'); |
|
825 | + |
|
826 | + $this->assertEquals('version2', $this->rootView->file_get_contents($filePath)); |
|
827 | + $info2 = $this->rootView->getFileInfo($filePath); |
|
828 | + |
|
829 | + $this->assertNotEquals( |
|
830 | + $info2['etag'], |
|
831 | + $info1['etag'], |
|
832 | + 'Etag must change after rolling back version' |
|
833 | + ); |
|
834 | + $this->assertEquals( |
|
835 | + $info2['fileid'], |
|
836 | + $info1['fileid'], |
|
837 | + 'File id must not change after rolling back version' |
|
838 | + ); |
|
839 | + $this->assertEquals( |
|
840 | + $info2['mtime'], |
|
841 | + $t2, |
|
842 | + 'Restored file has mtime from version' |
|
843 | + ); |
|
844 | + |
|
845 | + $newVersions = Storage::getVersions( |
|
846 | + self::TEST_VERSIONS_USER, '/sub/test.txt' |
|
847 | + ); |
|
848 | + |
|
849 | + $this->assertTrue( |
|
850 | + $this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0), |
|
851 | + 'A version file was created for the file before restoration' |
|
852 | + ); |
|
853 | + $this->assertTrue( |
|
854 | + $this->rootView->file_exists($v1), |
|
855 | + 'Untouched version file is still there' |
|
856 | + ); |
|
857 | + $this->assertFalse( |
|
858 | + $this->rootView->file_exists($v2), |
|
859 | + 'Restored version file gone from files_version folder' |
|
860 | + ); |
|
861 | + |
|
862 | + $this->assertCount(2, $newVersions, 'Additional version created'); |
|
863 | + |
|
864 | + $this->assertTrue( |
|
865 | + isset($newVersions[$t0 . '#' . 'test.txt']), |
|
866 | + 'A version was created for the file before restoration' |
|
867 | + ); |
|
868 | + $this->assertTrue( |
|
869 | + isset($newVersions[$t1 . '#' . 'test.txt']), |
|
870 | + 'Untouched version is still there' |
|
871 | + ); |
|
872 | + $this->assertFalse( |
|
873 | + isset($newVersions[$t2 . '#' . 'test.txt']), |
|
874 | + 'Restored version is not in the list any more' |
|
875 | + ); |
|
876 | + } |
|
877 | + |
|
878 | + /** |
|
879 | + * Test whether versions are created when overwriting as owner |
|
880 | + */ |
|
881 | + public function testStoreVersionAsOwner(): void { |
|
882 | + $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
883 | + |
|
884 | + $this->createAndCheckVersions( |
|
885 | + Filesystem::getView(), |
|
886 | + 'test.txt' |
|
887 | + ); |
|
888 | + } |
|
889 | + |
|
890 | + /** |
|
891 | + * Test whether versions are created when overwriting as share recipient |
|
892 | + */ |
|
893 | + public function testStoreVersionAsRecipient(): void { |
|
894 | + $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
895 | + |
|
896 | + Filesystem::mkdir('folder'); |
|
897 | + Filesystem::file_put_contents('folder/test.txt', 'test file'); |
|
898 | + |
|
899 | + $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder'); |
|
900 | + $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
901 | + $share->setNode($node) |
|
902 | + ->setShareType(IShare::TYPE_USER) |
|
903 | + ->setSharedBy(self::TEST_VERSIONS_USER) |
|
904 | + ->setSharedWith(self::TEST_VERSIONS_USER2) |
|
905 | + ->setPermissions(Constants::PERMISSION_ALL); |
|
906 | + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
907 | + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
|
908 | + |
|
909 | + $this->loginAsUser(self::TEST_VERSIONS_USER2); |
|
910 | + |
|
911 | + $this->createAndCheckVersions( |
|
912 | + Filesystem::getView(), |
|
913 | + 'folder/test.txt' |
|
914 | + ); |
|
915 | + |
|
916 | + Server::get(\OCP\Share\IManager::class)->deleteShare($share); |
|
917 | + } |
|
918 | + |
|
919 | + /** |
|
920 | + * Test whether versions are created when overwriting anonymously. |
|
921 | + * |
|
922 | + * When uploading through a public link or publicwebdav, no user |
|
923 | + * is logged in. File modification must still be able to find |
|
924 | + * the owner and create versions. |
|
925 | + */ |
|
926 | + public function testStoreVersionAsAnonymous(): void { |
|
927 | + $this->logout(); |
|
928 | + |
|
929 | + // note: public link upload does this, |
|
930 | + // needed to make the hooks fire |
|
931 | + \OC_Util::setupFS(self::TEST_VERSIONS_USER); |
|
932 | + |
|
933 | + $userView = new View('/' . self::TEST_VERSIONS_USER . '/files'); |
|
934 | + $this->createAndCheckVersions( |
|
935 | + $userView, |
|
936 | + 'test.txt' |
|
937 | + ); |
|
938 | + } |
|
939 | + |
|
940 | + /** |
|
941 | + * @param View $view |
|
942 | + * @param string $path |
|
943 | + */ |
|
944 | + private function createAndCheckVersions(View $view, $path) { |
|
945 | + $view->file_put_contents($path, 'test file'); |
|
946 | + $view->file_put_contents($path, 'version 1'); |
|
947 | + $view->file_put_contents($path, 'version 2'); |
|
948 | + |
|
949 | + $this->loginAsUser(self::TEST_VERSIONS_USER); |
|
950 | + |
|
951 | + // need to scan for the versions |
|
952 | + [$rootStorage,] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions'); |
|
953 | + $rootStorage->getScanner()->scan('files_versions'); |
|
954 | + |
|
955 | + $versions = Storage::getVersions( |
|
956 | + self::TEST_VERSIONS_USER, '/' . $path |
|
957 | + ); |
|
958 | + |
|
959 | + // note: we cannot predict how many versions are created due to |
|
960 | + // test run timing |
|
961 | + $this->assertGreaterThan(0, count($versions)); |
|
962 | + |
|
963 | + return $versions; |
|
964 | + } |
|
965 | + |
|
966 | + /** |
|
967 | + * @param string $user |
|
968 | + * @param bool $create |
|
969 | + */ |
|
970 | + public static function loginHelper($user, $create = false) { |
|
971 | + if ($create) { |
|
972 | + $backend = new \Test\Util\User\Dummy(); |
|
973 | + $backend->createUser($user, $user); |
|
974 | + Server::get(IUserManager::class)->registerBackend($backend); |
|
975 | + } |
|
976 | + |
|
977 | + \OC_Util::tearDownFS(); |
|
978 | + \OC_User::setUserId(''); |
|
979 | + Filesystem::tearDown(); |
|
980 | + \OC_User::setUserId($user); |
|
981 | + \OC_Util::setupFS($user); |
|
982 | + \OC::$server->getUserFolder($user); |
|
983 | + } |
|
984 | 984 | } |
985 | 985 | |
986 | 986 | // extend the original class to make it possible to test protected methods |
987 | 987 | class VersionStorageToTest extends Storage { |
988 | 988 | |
989 | - /** |
|
990 | - * @param integer $time |
|
991 | - */ |
|
992 | - public function callProtectedGetExpireList($time, $versions) { |
|
993 | - return self::getExpireList($time, $versions); |
|
994 | - } |
|
989 | + /** |
|
990 | + * @param integer $time |
|
991 | + */ |
|
992 | + public function callProtectedGetExpireList($time, $versions) { |
|
993 | + return self::getExpireList($time, $versions); |
|
994 | + } |
|
995 | 995 | } |
@@ -86,7 +86,7 @@ discard block |
||
86 | 86 | $mockConfig = $this->createMock(IConfig::class); |
87 | 87 | $mockConfig->expects($this->any()) |
88 | 88 | ->method('getSystemValue') |
89 | - ->willReturnCallback(function ($key, $default) use ($config) { |
|
89 | + ->willReturnCallback(function($key, $default) use ($config) { |
|
90 | 90 | if ($key === 'filesystem_check_changes') { |
91 | 91 | return Watcher::CHECK_ONCE; |
92 | 92 | } else { |
@@ -121,10 +121,10 @@ discard block |
||
121 | 121 | $this->restoreService(AllConfig::class); |
122 | 122 | |
123 | 123 | if ($this->rootView) { |
124 | - $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files/'); |
|
125 | - $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files/'); |
|
126 | - $this->rootView->deleteAll(self::TEST_VERSIONS_USER . '/files_versions/'); |
|
127 | - $this->rootView->deleteAll(self::TEST_VERSIONS_USER2 . '/files_versions/'); |
|
124 | + $this->rootView->deleteAll(self::TEST_VERSIONS_USER.'/files/'); |
|
125 | + $this->rootView->deleteAll(self::TEST_VERSIONS_USER2.'/files/'); |
|
126 | + $this->rootView->deleteAll(self::TEST_VERSIONS_USER.'/files_versions/'); |
|
127 | + $this->rootView->deleteAll(self::TEST_VERSIONS_USER2.'/files_versions/'); |
|
128 | 128 | } |
129 | 129 | |
130 | 130 | \OC_Hook::clear(); |
@@ -292,10 +292,10 @@ discard block |
||
292 | 292 | $t2 = $t1 - 60 * 60 * 24 * 14; |
293 | 293 | |
294 | 294 | // create some versions |
295 | - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
296 | - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
297 | - $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
298 | - $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
295 | + $v1 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t1; |
|
296 | + $v2 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t2; |
|
297 | + $v1Renamed = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t1; |
|
298 | + $v2Renamed = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t2; |
|
299 | 299 | |
300 | 300 | $this->rootView->file_put_contents($v1, 'version1'); |
301 | 301 | $this->rootView->file_put_contents($v2, 'version2'); |
@@ -322,12 +322,12 @@ discard block |
||
322 | 322 | // version will be expired |
323 | 323 | $t2 = $t1 - 60 * 60 * 24 * 14; |
324 | 324 | |
325 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); |
|
325 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT.'/folder1'); |
|
326 | 326 | // create some versions |
327 | - $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; |
|
328 | - $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; |
|
329 | - $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t1; |
|
330 | - $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder1/folder2/test.txt.v' . $t2; |
|
327 | + $v1 = self::USERS_VERSIONS_ROOT.'/folder1/test.txt.v'.$t1; |
|
328 | + $v2 = self::USERS_VERSIONS_ROOT.'/folder1/test.txt.v'.$t2; |
|
329 | + $v1Renamed = self::USERS_VERSIONS_ROOT.'/folder1/folder2/test.txt.v'.$t1; |
|
330 | + $v2Renamed = self::USERS_VERSIONS_ROOT.'/folder1/folder2/test.txt.v'.$t2; |
|
331 | 331 | |
332 | 332 | $this->rootView->file_put_contents($v1, 'version1'); |
333 | 333 | $this->rootView->file_put_contents($v2, 'version2'); |
@@ -373,11 +373,11 @@ discard block |
||
373 | 373 | $t2 = $t1 - 60 * 60 * 24 * 14; |
374 | 374 | |
375 | 375 | // create some versions |
376 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/folder1'); |
|
377 | - $v1 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t1; |
|
378 | - $v2 = self::USERS_VERSIONS_ROOT . '/folder1/test.txt.v' . $t2; |
|
379 | - $v1Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t1; |
|
380 | - $v2Renamed = self::USERS_VERSIONS_ROOT . '/folder2/folder1/test.txt.v' . $t2; |
|
376 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT.'/folder1'); |
|
377 | + $v1 = self::USERS_VERSIONS_ROOT.'/folder1/test.txt.v'.$t1; |
|
378 | + $v2 = self::USERS_VERSIONS_ROOT.'/folder1/test.txt.v'.$t2; |
|
379 | + $v1Renamed = self::USERS_VERSIONS_ROOT.'/folder2/folder1/test.txt.v'.$t1; |
|
380 | + $v2Renamed = self::USERS_VERSIONS_ROOT.'/folder2/folder1/test.txt.v'.$t2; |
|
381 | 381 | |
382 | 382 | $this->rootView->file_put_contents($v1, 'version1'); |
383 | 383 | $this->rootView->file_put_contents($v2, 'version2'); |
@@ -410,7 +410,7 @@ discard block |
||
410 | 410 | Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
411 | 411 | |
412 | 412 | self::loginHelper(self::TEST_VERSIONS_USER2); |
413 | - $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; |
|
413 | + $versionsFolder2 = '/'.self::TEST_VERSIONS_USER2.'/files_versions'; |
|
414 | 414 | Filesystem::file_put_contents('test.txt', 'test file'); |
415 | 415 | |
416 | 416 | $t1 = time(); |
@@ -420,8 +420,8 @@ discard block |
||
420 | 420 | |
421 | 421 | $this->rootView->mkdir($versionsFolder2); |
422 | 422 | // create some versions |
423 | - $v1 = $versionsFolder2 . '/test.txt.v' . $t1; |
|
424 | - $v2 = $versionsFolder2 . '/test.txt.v' . $t2; |
|
423 | + $v1 = $versionsFolder2.'/test.txt.v'.$t1; |
|
424 | + $v2 = $versionsFolder2.'/test.txt.v'.$t2; |
|
425 | 425 | |
426 | 426 | $this->rootView->file_put_contents($v1, 'version1'); |
427 | 427 | $this->rootView->file_put_contents($v2, 'version2'); |
@@ -434,10 +434,10 @@ discard block |
||
434 | 434 | |
435 | 435 | self::loginHelper(self::TEST_VERSIONS_USER); |
436 | 436 | |
437 | - $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; |
|
437 | + $versionsFolder1 = '/'.self::TEST_VERSIONS_USER.'/files_versions'; |
|
438 | 438 | |
439 | - $v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1; |
|
440 | - $v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2; |
|
439 | + $v1Renamed = $versionsFolder1.'/folder1/test.txt.v'.$t1; |
|
440 | + $v2Renamed = $versionsFolder1.'/folder1/test.txt.v'.$t2; |
|
441 | 441 | |
442 | 442 | $this->assertTrue($this->rootView->file_exists($v1Renamed)); |
443 | 443 | $this->assertTrue($this->rootView->file_exists($v2Renamed)); |
@@ -459,7 +459,7 @@ discard block |
||
459 | 459 | Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_VERSIONS_USER2); |
460 | 460 | |
461 | 461 | self::loginHelper(self::TEST_VERSIONS_USER2); |
462 | - $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions'; |
|
462 | + $versionsFolder2 = '/'.self::TEST_VERSIONS_USER2.'/files_versions'; |
|
463 | 463 | Filesystem::mkdir('folder2'); |
464 | 464 | Filesystem::file_put_contents('folder2/test.txt', 'test file'); |
465 | 465 | |
@@ -469,10 +469,10 @@ discard block |
||
469 | 469 | $t2 = $t1 - 60 * 60 * 24 * 14; |
470 | 470 | |
471 | 471 | $this->rootView->mkdir($versionsFolder2); |
472 | - $this->rootView->mkdir($versionsFolder2 . '/folder2'); |
|
472 | + $this->rootView->mkdir($versionsFolder2.'/folder2'); |
|
473 | 473 | // create some versions |
474 | - $v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1; |
|
475 | - $v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2; |
|
474 | + $v1 = $versionsFolder2.'/folder2/test.txt.v'.$t1; |
|
475 | + $v2 = $versionsFolder2.'/folder2/test.txt.v'.$t2; |
|
476 | 476 | |
477 | 477 | $this->rootView->file_put_contents($v1, 'version1'); |
478 | 478 | $this->rootView->file_put_contents($v2, 'version2'); |
@@ -485,10 +485,10 @@ discard block |
||
485 | 485 | |
486 | 486 | self::loginHelper(self::TEST_VERSIONS_USER); |
487 | 487 | |
488 | - $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions'; |
|
488 | + $versionsFolder1 = '/'.self::TEST_VERSIONS_USER.'/files_versions'; |
|
489 | 489 | |
490 | - $v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1; |
|
491 | - $v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2; |
|
490 | + $v1Renamed = $versionsFolder1.'/folder1/folder2/test.txt.v'.$t1; |
|
491 | + $v2Renamed = $versionsFolder1.'/folder1/folder2/test.txt.v'.$t2; |
|
492 | 492 | |
493 | 493 | $this->assertTrue($this->rootView->file_exists($v1Renamed)); |
494 | 494 | $this->assertTrue($this->rootView->file_exists($v2Renamed)); |
@@ -506,11 +506,11 @@ discard block |
||
506 | 506 | |
507 | 507 | $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); |
508 | 508 | // create some versions |
509 | - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
510 | - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
509 | + $v1 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t1; |
|
510 | + $v2 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t2; |
|
511 | 511 | // the renamed versions should not exist! Because we only moved the mount point! |
512 | - $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
513 | - $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
512 | + $v1Renamed = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t1; |
|
513 | + $v2Renamed = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t2; |
|
514 | 514 | |
515 | 515 | $this->rootView->file_put_contents($v1, 'version1'); |
516 | 516 | $this->rootView->file_put_contents($v2, 'version2'); |
@@ -554,10 +554,10 @@ discard block |
||
554 | 554 | $t2 = $t1 - 60 * 60 * 24 * 14; |
555 | 555 | |
556 | 556 | // create some versions |
557 | - $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; |
|
558 | - $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; |
|
559 | - $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; |
|
560 | - $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; |
|
557 | + $v1 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t1; |
|
558 | + $v2 = self::USERS_VERSIONS_ROOT.'/test.txt.v'.$t2; |
|
559 | + $v1Copied = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t1; |
|
560 | + $v2Copied = self::USERS_VERSIONS_ROOT.'/test2.txt.v'.$t2; |
|
561 | 561 | |
562 | 562 | $this->rootView->file_put_contents($v1, 'version1'); |
563 | 563 | $this->rootView->file_put_contents($v2, 'version2'); |
@@ -585,10 +585,10 @@ discard block |
||
585 | 585 | $t2 = $t1 - 60 * 60 * 24 * 14; |
586 | 586 | |
587 | 587 | // create some versions |
588 | - $v1 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t1; |
|
589 | - $v2 = self::USERS_VERSIONS_ROOT . '/subfolder/test.txt.v' . $t2; |
|
588 | + $v1 = self::USERS_VERSIONS_ROOT.'/subfolder/test.txt.v'.$t1; |
|
589 | + $v2 = self::USERS_VERSIONS_ROOT.'/subfolder/test.txt.v'.$t2; |
|
590 | 590 | |
591 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/subfolder/'); |
|
591 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT.'/subfolder/'); |
|
592 | 592 | |
593 | 593 | $this->rootView->file_put_contents($v1, 'version1'); |
594 | 594 | $this->rootView->file_put_contents($v2, 'version2'); |
@@ -604,7 +604,7 @@ discard block |
||
604 | 604 | } |
605 | 605 | |
606 | 606 | //cleanup |
607 | - $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT . '/subfolder'); |
|
607 | + $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT.'/subfolder'); |
|
608 | 608 | } |
609 | 609 | |
610 | 610 | /** |
@@ -647,7 +647,7 @@ discard block |
||
647 | 647 | |
648 | 648 | public function testRestoreCrossStorage(): void { |
649 | 649 | $storage2 = new Temporary([]); |
650 | - Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER . '/files/sub'); |
|
650 | + Filesystem::mount($storage2, [], self::TEST_VERSIONS_USER.'/files/sub'); |
|
651 | 651 | |
652 | 652 | $this->doTestRestore(); |
653 | 653 | } |
@@ -746,7 +746,7 @@ discard block |
||
746 | 746 | $eventHandler->expects($this->any()) |
747 | 747 | ->method('callback') |
748 | 748 | ->willReturnCallback( |
749 | - function ($p) use (&$params): void { |
|
749 | + function($p) use (&$params): void { |
|
750 | 750 | $params = $p; |
751 | 751 | } |
752 | 752 | ); |
@@ -760,7 +760,7 @@ discard block |
||
760 | 760 | } |
761 | 761 | |
762 | 762 | private function doTestRestore() { |
763 | - $filePath = self::TEST_VERSIONS_USER . '/files/sub/test.txt'; |
|
763 | + $filePath = self::TEST_VERSIONS_USER.'/files/sub/test.txt'; |
|
764 | 764 | $this->rootView->file_put_contents($filePath, 'test file'); |
765 | 765 | |
766 | 766 | $fileInfo = $this->rootView->getFileInfo($filePath); |
@@ -772,10 +772,10 @@ discard block |
||
772 | 772 | $t2 = $t1 - 60 * 60 * 24 * 14; |
773 | 773 | |
774 | 774 | // create some versions |
775 | - $v1 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t1; |
|
776 | - $v2 = self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t2; |
|
775 | + $v1 = self::USERS_VERSIONS_ROOT.'/sub/test.txt.v'.$t1; |
|
776 | + $v2 = self::USERS_VERSIONS_ROOT.'/sub/test.txt.v'.$t2; |
|
777 | 777 | |
778 | - $this->rootView->mkdir(self::USERS_VERSIONS_ROOT . '/sub'); |
|
778 | + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT.'/sub'); |
|
779 | 779 | |
780 | 780 | $this->rootView->file_put_contents($v1, 'version1'); |
781 | 781 | $fileInfoV1 = $this->rootView->getFileInfo($v1); |
@@ -808,7 +808,7 @@ discard block |
||
808 | 808 | |
809 | 809 | $eventDispatcher = Server::get(IEventDispatcher::class); |
810 | 810 | $eventFired = false; |
811 | - $eventDispatcher->addListener(VersionRestoredEvent::class, function ($event) use (&$eventFired, $t2): void { |
|
811 | + $eventDispatcher->addListener(VersionRestoredEvent::class, function($event) use (&$eventFired, $t2): void { |
|
812 | 812 | $eventFired = true; |
813 | 813 | $this->assertEquals('/sub/test.txt', $event->getVersion()->getVersionPath()); |
814 | 814 | $this->assertTrue($event->getVersion()->getRevisionId() > 0); |
@@ -816,7 +816,7 @@ discard block |
||
816 | 816 | |
817 | 817 | $versionManager = Server::get(IVersionManager::class); |
818 | 818 | $versions = $versionManager->getVersionsForFile($this->user1, $info1); |
819 | - $version = array_filter($versions, function ($version) use ($t2) { |
|
819 | + $version = array_filter($versions, function($version) use ($t2) { |
|
820 | 820 | return $version->getRevisionId() === $t2; |
821 | 821 | }); |
822 | 822 | $this->assertTrue($versionManager->rollback(current($version))); |
@@ -847,7 +847,7 @@ discard block |
||
847 | 847 | ); |
848 | 848 | |
849 | 849 | $this->assertTrue( |
850 | - $this->rootView->file_exists(self::USERS_VERSIONS_ROOT . '/sub/test.txt.v' . $t0), |
|
850 | + $this->rootView->file_exists(self::USERS_VERSIONS_ROOT.'/sub/test.txt.v'.$t0), |
|
851 | 851 | 'A version file was created for the file before restoration' |
852 | 852 | ); |
853 | 853 | $this->assertTrue( |
@@ -862,15 +862,15 @@ discard block |
||
862 | 862 | $this->assertCount(2, $newVersions, 'Additional version created'); |
863 | 863 | |
864 | 864 | $this->assertTrue( |
865 | - isset($newVersions[$t0 . '#' . 'test.txt']), |
|
865 | + isset($newVersions[$t0.'#'.'test.txt']), |
|
866 | 866 | 'A version was created for the file before restoration' |
867 | 867 | ); |
868 | 868 | $this->assertTrue( |
869 | - isset($newVersions[$t1 . '#' . 'test.txt']), |
|
869 | + isset($newVersions[$t1.'#'.'test.txt']), |
|
870 | 870 | 'Untouched version is still there' |
871 | 871 | ); |
872 | 872 | $this->assertFalse( |
873 | - isset($newVersions[$t2 . '#' . 'test.txt']), |
|
873 | + isset($newVersions[$t2.'#'.'test.txt']), |
|
874 | 874 | 'Restored version is not in the list any more' |
875 | 875 | ); |
876 | 876 | } |
@@ -930,7 +930,7 @@ discard block |
||
930 | 930 | // needed to make the hooks fire |
931 | 931 | \OC_Util::setupFS(self::TEST_VERSIONS_USER); |
932 | 932 | |
933 | - $userView = new View('/' . self::TEST_VERSIONS_USER . '/files'); |
|
933 | + $userView = new View('/'.self::TEST_VERSIONS_USER.'/files'); |
|
934 | 934 | $this->createAndCheckVersions( |
935 | 935 | $userView, |
936 | 936 | 'test.txt' |
@@ -949,11 +949,11 @@ discard block |
||
949 | 949 | $this->loginAsUser(self::TEST_VERSIONS_USER); |
950 | 950 | |
951 | 951 | // need to scan for the versions |
952 | - [$rootStorage,] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER . '/files_versions'); |
|
952 | + [$rootStorage, ] = $this->rootView->resolvePath(self::TEST_VERSIONS_USER.'/files_versions'); |
|
953 | 953 | $rootStorage->getScanner()->scan('files_versions'); |
954 | 954 | |
955 | 955 | $versions = Storage::getVersions( |
956 | - self::TEST_VERSIONS_USER, '/' . $path |
|
956 | + self::TEST_VERSIONS_USER, '/'.$path |
|
957 | 957 | ); |
958 | 958 | |
959 | 959 | // note: we cannot predict how many versions are created due to |
@@ -13,71 +13,71 @@ |
||
13 | 13 | use OCP\Settings\IDelegatedSettings; |
14 | 14 | |
15 | 15 | class Mail implements IDelegatedSettings { |
16 | - /** |
|
17 | - * @param IConfig $config |
|
18 | - * @param IL10N $l |
|
19 | - */ |
|
20 | - public function __construct( |
|
21 | - private IConfig $config, |
|
22 | - private IL10N $l, |
|
23 | - ) { |
|
24 | - } |
|
16 | + /** |
|
17 | + * @param IConfig $config |
|
18 | + * @param IL10N $l |
|
19 | + */ |
|
20 | + public function __construct( |
|
21 | + private IConfig $config, |
|
22 | + private IL10N $l, |
|
23 | + ) { |
|
24 | + } |
|
25 | 25 | |
26 | - /** |
|
27 | - * @return TemplateResponse |
|
28 | - */ |
|
29 | - public function getForm() { |
|
30 | - $finder = Server::get(IBinaryFinder::class); |
|
26 | + /** |
|
27 | + * @return TemplateResponse |
|
28 | + */ |
|
29 | + public function getForm() { |
|
30 | + $finder = Server::get(IBinaryFinder::class); |
|
31 | 31 | |
32 | - $parameters = [ |
|
33 | ||
34 | - 'sendmail_is_available' => $finder->findBinaryPath('sendmail') !== false, |
|
35 | - 'mail_domain' => $this->config->getSystemValue('mail_domain', ''), |
|
36 | - 'mail_from_address' => $this->config->getSystemValue('mail_from_address', ''), |
|
37 | - 'mail_smtpmode' => $this->config->getSystemValue('mail_smtpmode', ''), |
|
38 | - 'mail_smtpsecure' => $this->config->getSystemValue('mail_smtpsecure', ''), |
|
39 | - 'mail_smtphost' => $this->config->getSystemValue('mail_smtphost', ''), |
|
40 | - 'mail_smtpport' => $this->config->getSystemValue('mail_smtpport', ''), |
|
41 | - 'mail_smtpauth' => $this->config->getSystemValue('mail_smtpauth', false), |
|
42 | - 'mail_smtpname' => $this->config->getSystemValue('mail_smtpname', ''), |
|
43 | - 'mail_smtppassword' => $this->config->getSystemValue('mail_smtppassword', ''), |
|
44 | - 'mail_sendmailmode' => $this->config->getSystemValue('mail_sendmailmode', 'smtp'), |
|
45 | - ]; |
|
32 | + $parameters = [ |
|
33 | ||
34 | + 'sendmail_is_available' => $finder->findBinaryPath('sendmail') !== false, |
|
35 | + 'mail_domain' => $this->config->getSystemValue('mail_domain', ''), |
|
36 | + 'mail_from_address' => $this->config->getSystemValue('mail_from_address', ''), |
|
37 | + 'mail_smtpmode' => $this->config->getSystemValue('mail_smtpmode', ''), |
|
38 | + 'mail_smtpsecure' => $this->config->getSystemValue('mail_smtpsecure', ''), |
|
39 | + 'mail_smtphost' => $this->config->getSystemValue('mail_smtphost', ''), |
|
40 | + 'mail_smtpport' => $this->config->getSystemValue('mail_smtpport', ''), |
|
41 | + 'mail_smtpauth' => $this->config->getSystemValue('mail_smtpauth', false), |
|
42 | + 'mail_smtpname' => $this->config->getSystemValue('mail_smtpname', ''), |
|
43 | + 'mail_smtppassword' => $this->config->getSystemValue('mail_smtppassword', ''), |
|
44 | + 'mail_sendmailmode' => $this->config->getSystemValue('mail_sendmailmode', 'smtp'), |
|
45 | + ]; |
|
46 | 46 | |
47 | - if ($parameters['mail_smtppassword'] !== '') { |
|
48 | - $parameters['mail_smtppassword'] = '********'; |
|
49 | - } |
|
47 | + if ($parameters['mail_smtppassword'] !== '') { |
|
48 | + $parameters['mail_smtppassword'] = '********'; |
|
49 | + } |
|
50 | 50 | |
51 | - if ($parameters['mail_smtpmode'] === '' || $parameters['mail_smtpmode'] === 'php') { |
|
52 | - $parameters['mail_smtpmode'] = 'smtp'; |
|
53 | - } |
|
51 | + if ($parameters['mail_smtpmode'] === '' || $parameters['mail_smtpmode'] === 'php') { |
|
52 | + $parameters['mail_smtpmode'] = 'smtp'; |
|
53 | + } |
|
54 | 54 | |
55 | - return new TemplateResponse('settings', 'settings/admin/additional-mail', $parameters, ''); |
|
56 | - } |
|
55 | + return new TemplateResponse('settings', 'settings/admin/additional-mail', $parameters, ''); |
|
56 | + } |
|
57 | 57 | |
58 | - /** |
|
59 | - * @return string the section ID, e.g. 'sharing' |
|
60 | - */ |
|
61 | - public function getSection() { |
|
62 | - return 'server'; |
|
63 | - } |
|
58 | + /** |
|
59 | + * @return string the section ID, e.g. 'sharing' |
|
60 | + */ |
|
61 | + public function getSection() { |
|
62 | + return 'server'; |
|
63 | + } |
|
64 | 64 | |
65 | - /** |
|
66 | - * @return int whether the form should be rather on the top or bottom of |
|
67 | - * the admin section. The forms are arranged in ascending order of the |
|
68 | - * priority values. It is required to return a value between 0 and 100. |
|
69 | - * |
|
70 | - * E.g.: 70 |
|
71 | - */ |
|
72 | - public function getPriority() { |
|
73 | - return 10; |
|
74 | - } |
|
65 | + /** |
|
66 | + * @return int whether the form should be rather on the top or bottom of |
|
67 | + * the admin section. The forms are arranged in ascending order of the |
|
68 | + * priority values. It is required to return a value between 0 and 100. |
|
69 | + * |
|
70 | + * E.g.: 70 |
|
71 | + */ |
|
72 | + public function getPriority() { |
|
73 | + return 10; |
|
74 | + } |
|
75 | 75 | |
76 | - public function getName(): ?string { |
|
77 | - return $this->l->t('Email server'); |
|
78 | - } |
|
76 | + public function getName(): ?string { |
|
77 | + return $this->l->t('Email server'); |
|
78 | + } |
|
79 | 79 | |
80 | - public function getAuthorizedAppConfig(): array { |
|
81 | - return []; |
|
82 | - } |
|
80 | + public function getAuthorizedAppConfig(): array { |
|
81 | + return []; |
|
82 | + } |
|
83 | 83 | } |
@@ -33,287 +33,287 @@ |
||
33 | 33 | |
34 | 34 | class PersonalInfo implements ISettings { |
35 | 35 | |
36 | - /** @var ProfileManager */ |
|
37 | - private $profileManager; |
|
38 | - |
|
39 | - public function __construct( |
|
40 | - private IConfig $config, |
|
41 | - private IUserManager $userManager, |
|
42 | - private IGroupManager $groupManager, |
|
43 | - private IAccountManager $accountManager, |
|
44 | - ProfileManager $profileManager, |
|
45 | - private IAppManager $appManager, |
|
46 | - private IFactory $l10nFactory, |
|
47 | - private IL10N $l, |
|
48 | - private IInitialState $initialStateService, |
|
49 | - private IManager $manager, |
|
50 | - ) { |
|
51 | - $this->profileManager = $profileManager; |
|
52 | - } |
|
53 | - |
|
54 | - public function getForm(): TemplateResponse { |
|
55 | - $federationEnabled = $this->appManager->isEnabledForUser('federation'); |
|
56 | - $federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing'); |
|
57 | - $lookupServerUploadEnabled = false; |
|
58 | - if ($federatedFileSharingEnabled) { |
|
59 | - /** @var FederatedShareProvider $shareProvider */ |
|
60 | - $shareProvider = Server::get(FederatedShareProvider::class); |
|
61 | - $lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled(); |
|
62 | - } |
|
63 | - |
|
64 | - $uid = \OC_User::getUser(); |
|
65 | - $user = $this->userManager->get($uid); |
|
66 | - $account = $this->accountManager->getAccount($user); |
|
67 | - |
|
68 | - // make sure FS is setup before querying storage related stuff... |
|
69 | - \OC_Util::setupFS($user->getUID()); |
|
70 | - |
|
71 | - $storageInfo = \OC_Helper::getStorageInfo('/'); |
|
72 | - if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) { |
|
73 | - $totalSpace = $this->l->t('Unlimited'); |
|
74 | - } else { |
|
75 | - $totalSpace = Util::humanFileSize($storageInfo['total']); |
|
76 | - } |
|
77 | - |
|
78 | - $messageParameters = $this->getMessageParameters($account); |
|
79 | - |
|
80 | - $parameters = [ |
|
81 | - 'lookupServerUploadEnabled' => $lookupServerUploadEnabled, |
|
82 | - 'isFairUseOfFreePushService' => $this->isFairUseOfFreePushService(), |
|
83 | - 'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(), |
|
84 | - ] + $messageParameters; |
|
85 | - |
|
86 | - $personalInfoParameters = [ |
|
87 | - 'userId' => $uid, |
|
88 | - 'avatar' => $this->getProperty($account, IAccountManager::PROPERTY_AVATAR), |
|
89 | - 'groups' => $this->getGroups($user), |
|
90 | - 'quota' => $storageInfo['quota'], |
|
91 | - 'totalSpace' => $totalSpace, |
|
92 | - 'usage' => Util::humanFileSize($storageInfo['used']), |
|
93 | - 'usageRelative' => round($storageInfo['relative']), |
|
94 | - 'displayName' => $this->getProperty($account, IAccountManager::PROPERTY_DISPLAYNAME), |
|
95 | - 'emailMap' => $this->getEmailMap($account), |
|
96 | - 'phone' => $this->getProperty($account, IAccountManager::PROPERTY_PHONE), |
|
97 | - 'defaultPhoneRegion' => $this->config->getSystemValueString('default_phone_region'), |
|
98 | - 'location' => $this->getProperty($account, IAccountManager::PROPERTY_ADDRESS), |
|
99 | - 'website' => $this->getProperty($account, IAccountManager::PROPERTY_WEBSITE), |
|
100 | - 'twitter' => $this->getProperty($account, IAccountManager::PROPERTY_TWITTER), |
|
101 | - 'fediverse' => $this->getProperty($account, IAccountManager::PROPERTY_FEDIVERSE), |
|
102 | - 'languageMap' => $this->getLanguageMap($user), |
|
103 | - 'localeMap' => $this->getLocaleMap($user), |
|
104 | - 'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(), |
|
105 | - 'profileEnabled' => $this->profileManager->isProfileEnabled($user), |
|
106 | - 'organisation' => $this->getProperty($account, IAccountManager::PROPERTY_ORGANISATION), |
|
107 | - 'role' => $this->getProperty($account, IAccountManager::PROPERTY_ROLE), |
|
108 | - 'headline' => $this->getProperty($account, IAccountManager::PROPERTY_HEADLINE), |
|
109 | - 'biography' => $this->getProperty($account, IAccountManager::PROPERTY_BIOGRAPHY), |
|
110 | - 'birthdate' => $this->getProperty($account, IAccountManager::PROPERTY_BIRTHDATE), |
|
111 | - 'firstDayOfWeek' => $this->config->getUserValue($uid, 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK), |
|
112 | - 'pronouns' => $this->getProperty($account, IAccountManager::PROPERTY_PRONOUNS), |
|
113 | - ]; |
|
114 | - |
|
115 | - $accountParameters = [ |
|
116 | - 'avatarChangeSupported' => $user->canChangeAvatar(), |
|
117 | - 'displayNameChangeSupported' => $user->canChangeDisplayName(), |
|
118 | - 'emailChangeSupported' => $user->canChangeEmail(), |
|
119 | - 'federationEnabled' => $federationEnabled, |
|
120 | - 'lookupServerUploadEnabled' => $lookupServerUploadEnabled, |
|
121 | - ]; |
|
122 | - |
|
123 | - $profileParameters = [ |
|
124 | - 'profileConfig' => $this->profileManager->getProfileConfigWithMetadata($user, $user), |
|
125 | - ]; |
|
126 | - |
|
127 | - $this->initialStateService->provideInitialState('profileEnabledGlobally', $this->profileManager->isProfileEnabled()); |
|
128 | - $this->initialStateService->provideInitialState('personalInfoParameters', $personalInfoParameters); |
|
129 | - $this->initialStateService->provideInitialState('accountParameters', $accountParameters); |
|
130 | - $this->initialStateService->provideInitialState('profileParameters', $profileParameters); |
|
131 | - |
|
132 | - return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, ''); |
|
133 | - } |
|
134 | - |
|
135 | - /** |
|
136 | - * Check if is fair use of free push service |
|
137 | - * @return boolean |
|
138 | - */ |
|
139 | - private function isFairUseOfFreePushService(): bool { |
|
140 | - return $this->manager->isFairUseOfFreePushService(); |
|
141 | - } |
|
142 | - |
|
143 | - /** |
|
144 | - * returns the property data in an |
|
145 | - * associative array |
|
146 | - */ |
|
147 | - private function getProperty(IAccount $account, string $property): array { |
|
148 | - $property = [ |
|
149 | - 'name' => $account->getProperty($property)->getName(), |
|
150 | - 'value' => $account->getProperty($property)->getValue(), |
|
151 | - 'scope' => $account->getProperty($property)->getScope(), |
|
152 | - 'verified' => $account->getProperty($property)->getVerified(), |
|
153 | - ]; |
|
154 | - |
|
155 | - return $property; |
|
156 | - } |
|
157 | - |
|
158 | - /** |
|
159 | - * returns the section ID string, e.g. 'sharing' |
|
160 | - * @since 9.1 |
|
161 | - */ |
|
162 | - public function getSection(): string { |
|
163 | - return 'personal-info'; |
|
164 | - } |
|
165 | - |
|
166 | - /** |
|
167 | - * @return int whether the form should be rather on the top or bottom of |
|
168 | - * the admin section. The forms are arranged in ascending order of the |
|
169 | - * priority values. It is required to return a value between 0 and 100. |
|
170 | - * |
|
171 | - * E.g.: 70 |
|
172 | - * @since 9.1 |
|
173 | - */ |
|
174 | - public function getPriority(): int { |
|
175 | - return 10; |
|
176 | - } |
|
177 | - |
|
178 | - /** |
|
179 | - * returns a sorted list of the user's group GIDs |
|
180 | - */ |
|
181 | - private function getGroups(IUser $user): array { |
|
182 | - $groups = array_map( |
|
183 | - static function (IGroup $group) { |
|
184 | - return $group->getDisplayName(); |
|
185 | - }, |
|
186 | - $this->groupManager->getUserGroups($user) |
|
187 | - ); |
|
188 | - sort($groups); |
|
189 | - |
|
190 | - return $groups; |
|
191 | - } |
|
192 | - |
|
193 | - /** |
|
194 | - * returns the primary email and additional emails in an |
|
195 | - * associative array |
|
196 | - */ |
|
197 | - private function getEmailMap(IAccount $account): array { |
|
198 | - $systemEmail = [ |
|
199 | - 'name' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getName(), |
|
200 | - 'value' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(), |
|
201 | - 'scope' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(), |
|
202 | - 'verified' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getVerified(), |
|
203 | - ]; |
|
204 | - |
|
205 | - $additionalEmails = array_map( |
|
206 | - function (IAccountProperty $property) { |
|
207 | - return [ |
|
208 | - 'name' => $property->getName(), |
|
209 | - 'value' => $property->getValue(), |
|
210 | - 'scope' => $property->getScope(), |
|
211 | - 'verified' => $property->getVerified(), |
|
212 | - 'locallyVerified' => $property->getLocallyVerified(), |
|
213 | - ]; |
|
214 | - }, |
|
215 | - $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties(), |
|
216 | - ); |
|
217 | - |
|
218 | - $emailMap = [ |
|
219 | - 'primaryEmail' => $systemEmail, |
|
220 | - 'additionalEmails' => $additionalEmails, |
|
221 | - 'notificationEmail' => (string)$account->getUser()->getPrimaryEMailAddress(), |
|
222 | - ]; |
|
223 | - |
|
224 | - return $emailMap; |
|
225 | - } |
|
226 | - |
|
227 | - /** |
|
228 | - * returns the user's active language, common languages, and other languages in an |
|
229 | - * associative array |
|
230 | - */ |
|
231 | - private function getLanguageMap(IUser $user): array { |
|
232 | - $forceLanguage = $this->config->getSystemValue('force_language', false); |
|
233 | - if ($forceLanguage !== false) { |
|
234 | - return []; |
|
235 | - } |
|
236 | - |
|
237 | - $uid = $user->getUID(); |
|
238 | - |
|
239 | - $userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage()); |
|
240 | - $languages = $this->l10nFactory->getLanguages(); |
|
241 | - |
|
242 | - // associate the user language with the proper array |
|
243 | - $userLangIndex = array_search($userConfLang, array_column($languages['commonLanguages'], 'code')); |
|
244 | - $userLang = $languages['commonLanguages'][$userLangIndex]; |
|
245 | - // search in the other languages |
|
246 | - if ($userLangIndex === false) { |
|
247 | - $userLangIndex = array_search($userConfLang, array_column($languages['otherLanguages'], 'code')); |
|
248 | - $userLang = $languages['otherLanguages'][$userLangIndex]; |
|
249 | - } |
|
250 | - // if user language is not available but set somehow: show the actual code as name |
|
251 | - if (!is_array($userLang)) { |
|
252 | - $userLang = [ |
|
253 | - 'code' => $userConfLang, |
|
254 | - 'name' => $userConfLang, |
|
255 | - ]; |
|
256 | - } |
|
257 | - |
|
258 | - return array_merge( |
|
259 | - ['activeLanguage' => $userLang], |
|
260 | - $languages |
|
261 | - ); |
|
262 | - } |
|
263 | - |
|
264 | - private function getLocaleMap(IUser $user): array { |
|
265 | - $forceLanguage = $this->config->getSystemValue('force_locale', false); |
|
266 | - if ($forceLanguage !== false) { |
|
267 | - return []; |
|
268 | - } |
|
269 | - |
|
270 | - $uid = $user->getUID(); |
|
271 | - $userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage()); |
|
272 | - $userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale($userLang)); |
|
273 | - $localeCodes = $this->l10nFactory->findAvailableLocales(); |
|
274 | - $userLocale = array_filter($localeCodes, fn ($value) => $userLocaleString === $value['code']); |
|
275 | - |
|
276 | - if (!empty($userLocale)) { |
|
277 | - $userLocale = reset($userLocale); |
|
278 | - } |
|
279 | - |
|
280 | - $localesForLanguage = array_values(array_filter($localeCodes, fn ($localeCode) => str_starts_with($localeCode['code'], $userLang))); |
|
281 | - $otherLocales = array_values(array_filter($localeCodes, fn ($localeCode) => !str_starts_with($localeCode['code'], $userLang))); |
|
282 | - |
|
283 | - if (!$userLocale) { |
|
284 | - $userLocale = [ |
|
285 | - 'code' => 'en', |
|
286 | - 'name' => 'English' |
|
287 | - ]; |
|
288 | - } |
|
289 | - |
|
290 | - return [ |
|
291 | - 'activeLocaleLang' => $userLocaleString, |
|
292 | - 'activeLocale' => $userLocale, |
|
293 | - 'localesForLanguage' => $localesForLanguage, |
|
294 | - 'otherLocales' => $otherLocales, |
|
295 | - ]; |
|
296 | - } |
|
297 | - |
|
298 | - /** |
|
299 | - * returns the message parameters |
|
300 | - */ |
|
301 | - private function getMessageParameters(IAccount $account): array { |
|
302 | - $needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER]; |
|
303 | - $messageParameters = []; |
|
304 | - foreach ($needVerifyMessage as $property) { |
|
305 | - switch ($account->getProperty($property)->getVerified()) { |
|
306 | - case IAccountManager::VERIFIED: |
|
307 | - $message = $this->l->t('Verifying'); |
|
308 | - break; |
|
309 | - case IAccountManager::VERIFICATION_IN_PROGRESS: |
|
310 | - $message = $this->l->t('Verifying …'); |
|
311 | - break; |
|
312 | - default: |
|
313 | - $message = $this->l->t('Verify'); |
|
314 | - } |
|
315 | - $messageParameters[$property . 'Message'] = $message; |
|
316 | - } |
|
317 | - return $messageParameters; |
|
318 | - } |
|
36 | + /** @var ProfileManager */ |
|
37 | + private $profileManager; |
|
38 | + |
|
39 | + public function __construct( |
|
40 | + private IConfig $config, |
|
41 | + private IUserManager $userManager, |
|
42 | + private IGroupManager $groupManager, |
|
43 | + private IAccountManager $accountManager, |
|
44 | + ProfileManager $profileManager, |
|
45 | + private IAppManager $appManager, |
|
46 | + private IFactory $l10nFactory, |
|
47 | + private IL10N $l, |
|
48 | + private IInitialState $initialStateService, |
|
49 | + private IManager $manager, |
|
50 | + ) { |
|
51 | + $this->profileManager = $profileManager; |
|
52 | + } |
|
53 | + |
|
54 | + public function getForm(): TemplateResponse { |
|
55 | + $federationEnabled = $this->appManager->isEnabledForUser('federation'); |
|
56 | + $federatedFileSharingEnabled = $this->appManager->isEnabledForUser('federatedfilesharing'); |
|
57 | + $lookupServerUploadEnabled = false; |
|
58 | + if ($federatedFileSharingEnabled) { |
|
59 | + /** @var FederatedShareProvider $shareProvider */ |
|
60 | + $shareProvider = Server::get(FederatedShareProvider::class); |
|
61 | + $lookupServerUploadEnabled = $shareProvider->isLookupServerUploadEnabled(); |
|
62 | + } |
|
63 | + |
|
64 | + $uid = \OC_User::getUser(); |
|
65 | + $user = $this->userManager->get($uid); |
|
66 | + $account = $this->accountManager->getAccount($user); |
|
67 | + |
|
68 | + // make sure FS is setup before querying storage related stuff... |
|
69 | + \OC_Util::setupFS($user->getUID()); |
|
70 | + |
|
71 | + $storageInfo = \OC_Helper::getStorageInfo('/'); |
|
72 | + if ($storageInfo['quota'] === FileInfo::SPACE_UNLIMITED) { |
|
73 | + $totalSpace = $this->l->t('Unlimited'); |
|
74 | + } else { |
|
75 | + $totalSpace = Util::humanFileSize($storageInfo['total']); |
|
76 | + } |
|
77 | + |
|
78 | + $messageParameters = $this->getMessageParameters($account); |
|
79 | + |
|
80 | + $parameters = [ |
|
81 | + 'lookupServerUploadEnabled' => $lookupServerUploadEnabled, |
|
82 | + 'isFairUseOfFreePushService' => $this->isFairUseOfFreePushService(), |
|
83 | + 'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(), |
|
84 | + ] + $messageParameters; |
|
85 | + |
|
86 | + $personalInfoParameters = [ |
|
87 | + 'userId' => $uid, |
|
88 | + 'avatar' => $this->getProperty($account, IAccountManager::PROPERTY_AVATAR), |
|
89 | + 'groups' => $this->getGroups($user), |
|
90 | + 'quota' => $storageInfo['quota'], |
|
91 | + 'totalSpace' => $totalSpace, |
|
92 | + 'usage' => Util::humanFileSize($storageInfo['used']), |
|
93 | + 'usageRelative' => round($storageInfo['relative']), |
|
94 | + 'displayName' => $this->getProperty($account, IAccountManager::PROPERTY_DISPLAYNAME), |
|
95 | + 'emailMap' => $this->getEmailMap($account), |
|
96 | + 'phone' => $this->getProperty($account, IAccountManager::PROPERTY_PHONE), |
|
97 | + 'defaultPhoneRegion' => $this->config->getSystemValueString('default_phone_region'), |
|
98 | + 'location' => $this->getProperty($account, IAccountManager::PROPERTY_ADDRESS), |
|
99 | + 'website' => $this->getProperty($account, IAccountManager::PROPERTY_WEBSITE), |
|
100 | + 'twitter' => $this->getProperty($account, IAccountManager::PROPERTY_TWITTER), |
|
101 | + 'fediverse' => $this->getProperty($account, IAccountManager::PROPERTY_FEDIVERSE), |
|
102 | + 'languageMap' => $this->getLanguageMap($user), |
|
103 | + 'localeMap' => $this->getLocaleMap($user), |
|
104 | + 'profileEnabledGlobally' => $this->profileManager->isProfileEnabled(), |
|
105 | + 'profileEnabled' => $this->profileManager->isProfileEnabled($user), |
|
106 | + 'organisation' => $this->getProperty($account, IAccountManager::PROPERTY_ORGANISATION), |
|
107 | + 'role' => $this->getProperty($account, IAccountManager::PROPERTY_ROLE), |
|
108 | + 'headline' => $this->getProperty($account, IAccountManager::PROPERTY_HEADLINE), |
|
109 | + 'biography' => $this->getProperty($account, IAccountManager::PROPERTY_BIOGRAPHY), |
|
110 | + 'birthdate' => $this->getProperty($account, IAccountManager::PROPERTY_BIRTHDATE), |
|
111 | + 'firstDayOfWeek' => $this->config->getUserValue($uid, 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK), |
|
112 | + 'pronouns' => $this->getProperty($account, IAccountManager::PROPERTY_PRONOUNS), |
|
113 | + ]; |
|
114 | + |
|
115 | + $accountParameters = [ |
|
116 | + 'avatarChangeSupported' => $user->canChangeAvatar(), |
|
117 | + 'displayNameChangeSupported' => $user->canChangeDisplayName(), |
|
118 | + 'emailChangeSupported' => $user->canChangeEmail(), |
|
119 | + 'federationEnabled' => $federationEnabled, |
|
120 | + 'lookupServerUploadEnabled' => $lookupServerUploadEnabled, |
|
121 | + ]; |
|
122 | + |
|
123 | + $profileParameters = [ |
|
124 | + 'profileConfig' => $this->profileManager->getProfileConfigWithMetadata($user, $user), |
|
125 | + ]; |
|
126 | + |
|
127 | + $this->initialStateService->provideInitialState('profileEnabledGlobally', $this->profileManager->isProfileEnabled()); |
|
128 | + $this->initialStateService->provideInitialState('personalInfoParameters', $personalInfoParameters); |
|
129 | + $this->initialStateService->provideInitialState('accountParameters', $accountParameters); |
|
130 | + $this->initialStateService->provideInitialState('profileParameters', $profileParameters); |
|
131 | + |
|
132 | + return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, ''); |
|
133 | + } |
|
134 | + |
|
135 | + /** |
|
136 | + * Check if is fair use of free push service |
|
137 | + * @return boolean |
|
138 | + */ |
|
139 | + private function isFairUseOfFreePushService(): bool { |
|
140 | + return $this->manager->isFairUseOfFreePushService(); |
|
141 | + } |
|
142 | + |
|
143 | + /** |
|
144 | + * returns the property data in an |
|
145 | + * associative array |
|
146 | + */ |
|
147 | + private function getProperty(IAccount $account, string $property): array { |
|
148 | + $property = [ |
|
149 | + 'name' => $account->getProperty($property)->getName(), |
|
150 | + 'value' => $account->getProperty($property)->getValue(), |
|
151 | + 'scope' => $account->getProperty($property)->getScope(), |
|
152 | + 'verified' => $account->getProperty($property)->getVerified(), |
|
153 | + ]; |
|
154 | + |
|
155 | + return $property; |
|
156 | + } |
|
157 | + |
|
158 | + /** |
|
159 | + * returns the section ID string, e.g. 'sharing' |
|
160 | + * @since 9.1 |
|
161 | + */ |
|
162 | + public function getSection(): string { |
|
163 | + return 'personal-info'; |
|
164 | + } |
|
165 | + |
|
166 | + /** |
|
167 | + * @return int whether the form should be rather on the top or bottom of |
|
168 | + * the admin section. The forms are arranged in ascending order of the |
|
169 | + * priority values. It is required to return a value between 0 and 100. |
|
170 | + * |
|
171 | + * E.g.: 70 |
|
172 | + * @since 9.1 |
|
173 | + */ |
|
174 | + public function getPriority(): int { |
|
175 | + return 10; |
|
176 | + } |
|
177 | + |
|
178 | + /** |
|
179 | + * returns a sorted list of the user's group GIDs |
|
180 | + */ |
|
181 | + private function getGroups(IUser $user): array { |
|
182 | + $groups = array_map( |
|
183 | + static function (IGroup $group) { |
|
184 | + return $group->getDisplayName(); |
|
185 | + }, |
|
186 | + $this->groupManager->getUserGroups($user) |
|
187 | + ); |
|
188 | + sort($groups); |
|
189 | + |
|
190 | + return $groups; |
|
191 | + } |
|
192 | + |
|
193 | + /** |
|
194 | + * returns the primary email and additional emails in an |
|
195 | + * associative array |
|
196 | + */ |
|
197 | + private function getEmailMap(IAccount $account): array { |
|
198 | + $systemEmail = [ |
|
199 | + 'name' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getName(), |
|
200 | + 'value' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(), |
|
201 | + 'scope' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(), |
|
202 | + 'verified' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getVerified(), |
|
203 | + ]; |
|
204 | + |
|
205 | + $additionalEmails = array_map( |
|
206 | + function (IAccountProperty $property) { |
|
207 | + return [ |
|
208 | + 'name' => $property->getName(), |
|
209 | + 'value' => $property->getValue(), |
|
210 | + 'scope' => $property->getScope(), |
|
211 | + 'verified' => $property->getVerified(), |
|
212 | + 'locallyVerified' => $property->getLocallyVerified(), |
|
213 | + ]; |
|
214 | + }, |
|
215 | + $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties(), |
|
216 | + ); |
|
217 | + |
|
218 | + $emailMap = [ |
|
219 | + 'primaryEmail' => $systemEmail, |
|
220 | + 'additionalEmails' => $additionalEmails, |
|
221 | + 'notificationEmail' => (string)$account->getUser()->getPrimaryEMailAddress(), |
|
222 | + ]; |
|
223 | + |
|
224 | + return $emailMap; |
|
225 | + } |
|
226 | + |
|
227 | + /** |
|
228 | + * returns the user's active language, common languages, and other languages in an |
|
229 | + * associative array |
|
230 | + */ |
|
231 | + private function getLanguageMap(IUser $user): array { |
|
232 | + $forceLanguage = $this->config->getSystemValue('force_language', false); |
|
233 | + if ($forceLanguage !== false) { |
|
234 | + return []; |
|
235 | + } |
|
236 | + |
|
237 | + $uid = $user->getUID(); |
|
238 | + |
|
239 | + $userConfLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage()); |
|
240 | + $languages = $this->l10nFactory->getLanguages(); |
|
241 | + |
|
242 | + // associate the user language with the proper array |
|
243 | + $userLangIndex = array_search($userConfLang, array_column($languages['commonLanguages'], 'code')); |
|
244 | + $userLang = $languages['commonLanguages'][$userLangIndex]; |
|
245 | + // search in the other languages |
|
246 | + if ($userLangIndex === false) { |
|
247 | + $userLangIndex = array_search($userConfLang, array_column($languages['otherLanguages'], 'code')); |
|
248 | + $userLang = $languages['otherLanguages'][$userLangIndex]; |
|
249 | + } |
|
250 | + // if user language is not available but set somehow: show the actual code as name |
|
251 | + if (!is_array($userLang)) { |
|
252 | + $userLang = [ |
|
253 | + 'code' => $userConfLang, |
|
254 | + 'name' => $userConfLang, |
|
255 | + ]; |
|
256 | + } |
|
257 | + |
|
258 | + return array_merge( |
|
259 | + ['activeLanguage' => $userLang], |
|
260 | + $languages |
|
261 | + ); |
|
262 | + } |
|
263 | + |
|
264 | + private function getLocaleMap(IUser $user): array { |
|
265 | + $forceLanguage = $this->config->getSystemValue('force_locale', false); |
|
266 | + if ($forceLanguage !== false) { |
|
267 | + return []; |
|
268 | + } |
|
269 | + |
|
270 | + $uid = $user->getUID(); |
|
271 | + $userLang = $this->config->getUserValue($uid, 'core', 'lang', $this->l10nFactory->findLanguage()); |
|
272 | + $userLocaleString = $this->config->getUserValue($uid, 'core', 'locale', $this->l10nFactory->findLocale($userLang)); |
|
273 | + $localeCodes = $this->l10nFactory->findAvailableLocales(); |
|
274 | + $userLocale = array_filter($localeCodes, fn ($value) => $userLocaleString === $value['code']); |
|
275 | + |
|
276 | + if (!empty($userLocale)) { |
|
277 | + $userLocale = reset($userLocale); |
|
278 | + } |
|
279 | + |
|
280 | + $localesForLanguage = array_values(array_filter($localeCodes, fn ($localeCode) => str_starts_with($localeCode['code'], $userLang))); |
|
281 | + $otherLocales = array_values(array_filter($localeCodes, fn ($localeCode) => !str_starts_with($localeCode['code'], $userLang))); |
|
282 | + |
|
283 | + if (!$userLocale) { |
|
284 | + $userLocale = [ |
|
285 | + 'code' => 'en', |
|
286 | + 'name' => 'English' |
|
287 | + ]; |
|
288 | + } |
|
289 | + |
|
290 | + return [ |
|
291 | + 'activeLocaleLang' => $userLocaleString, |
|
292 | + 'activeLocale' => $userLocale, |
|
293 | + 'localesForLanguage' => $localesForLanguage, |
|
294 | + 'otherLocales' => $otherLocales, |
|
295 | + ]; |
|
296 | + } |
|
297 | + |
|
298 | + /** |
|
299 | + * returns the message parameters |
|
300 | + */ |
|
301 | + private function getMessageParameters(IAccount $account): array { |
|
302 | + $needVerifyMessage = [IAccountManager::PROPERTY_EMAIL, IAccountManager::PROPERTY_WEBSITE, IAccountManager::PROPERTY_TWITTER]; |
|
303 | + $messageParameters = []; |
|
304 | + foreach ($needVerifyMessage as $property) { |
|
305 | + switch ($account->getProperty($property)->getVerified()) { |
|
306 | + case IAccountManager::VERIFIED: |
|
307 | + $message = $this->l->t('Verifying'); |
|
308 | + break; |
|
309 | + case IAccountManager::VERIFICATION_IN_PROGRESS: |
|
310 | + $message = $this->l->t('Verifying …'); |
|
311 | + break; |
|
312 | + default: |
|
313 | + $message = $this->l->t('Verify'); |
|
314 | + } |
|
315 | + $messageParameters[$property . 'Message'] = $message; |
|
316 | + } |
|
317 | + return $messageParameters; |
|
318 | + } |
|
319 | 319 | } |
@@ -13,42 +13,42 @@ |
||
13 | 13 | use OCP\Files\StorageNotAvailableException; |
14 | 14 | |
15 | 15 | class Scanner extends \OC\Files\Cache\Scanner { |
16 | - /** @var Storage */ |
|
17 | - protected $storage; |
|
16 | + /** @var Storage */ |
|
17 | + protected $storage; |
|
18 | 18 | |
19 | - public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { |
|
20 | - // Disable locking for federated shares |
|
21 | - parent::scan($path, $recursive, $reuse, false); |
|
22 | - } |
|
19 | + public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { |
|
20 | + // Disable locking for federated shares |
|
21 | + parent::scan($path, $recursive, $reuse, false); |
|
22 | + } |
|
23 | 23 | |
24 | - /** |
|
25 | - * Scan a single file and store it in the cache. |
|
26 | - * If an exception happened while accessing the external storage, |
|
27 | - * the storage will be checked for availability and removed |
|
28 | - * if it is not available any more. |
|
29 | - * |
|
30 | - * @param string $file file to scan |
|
31 | - * @param int $reuseExisting |
|
32 | - * @param int $parentId |
|
33 | - * @param CacheEntry|array|null|false $cacheData existing data in the cache for the file to be scanned |
|
34 | - * @param bool $lock set to false to disable getting an additional read lock during scanning |
|
35 | - * @param array|null $data the metadata for the file, as returned by the storage |
|
36 | - * @return array|null an array of metadata of the scanned file |
|
37 | - */ |
|
38 | - public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true, $data = null) { |
|
39 | - try { |
|
40 | - return parent::scanFile($file, $reuseExisting, $parentId, $cacheData, $lock, $data); |
|
41 | - } catch (ForbiddenException $e) { |
|
42 | - $this->storage->checkStorageAvailability(); |
|
43 | - } catch (NotFoundException $e) { |
|
44 | - // if the storage isn't found, the call to |
|
45 | - // checkStorageAvailable() will verify it and remove it |
|
46 | - // if appropriate |
|
47 | - $this->storage->checkStorageAvailability(); |
|
48 | - } catch (StorageInvalidException $e) { |
|
49 | - $this->storage->checkStorageAvailability(); |
|
50 | - } catch (StorageNotAvailableException $e) { |
|
51 | - $this->storage->checkStorageAvailability(); |
|
52 | - } |
|
53 | - } |
|
24 | + /** |
|
25 | + * Scan a single file and store it in the cache. |
|
26 | + * If an exception happened while accessing the external storage, |
|
27 | + * the storage will be checked for availability and removed |
|
28 | + * if it is not available any more. |
|
29 | + * |
|
30 | + * @param string $file file to scan |
|
31 | + * @param int $reuseExisting |
|
32 | + * @param int $parentId |
|
33 | + * @param CacheEntry|array|null|false $cacheData existing data in the cache for the file to be scanned |
|
34 | + * @param bool $lock set to false to disable getting an additional read lock during scanning |
|
35 | + * @param array|null $data the metadata for the file, as returned by the storage |
|
36 | + * @return array|null an array of metadata of the scanned file |
|
37 | + */ |
|
38 | + public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true, $data = null) { |
|
39 | + try { |
|
40 | + return parent::scanFile($file, $reuseExisting, $parentId, $cacheData, $lock, $data); |
|
41 | + } catch (ForbiddenException $e) { |
|
42 | + $this->storage->checkStorageAvailability(); |
|
43 | + } catch (NotFoundException $e) { |
|
44 | + // if the storage isn't found, the call to |
|
45 | + // checkStorageAvailable() will verify it and remove it |
|
46 | + // if appropriate |
|
47 | + $this->storage->checkStorageAvailability(); |
|
48 | + } catch (StorageInvalidException $e) { |
|
49 | + $this->storage->checkStorageAvailability(); |
|
50 | + } catch (StorageNotAvailableException $e) { |
|
51 | + $this->storage->checkStorageAvailability(); |
|
52 | + } |
|
53 | + } |
|
54 | 54 | } |
@@ -21,98 +21,98 @@ |
||
21 | 21 | |
22 | 22 | class CleanUp extends Command { |
23 | 23 | |
24 | - public function __construct( |
|
25 | - protected IRootFolder $rootFolder, |
|
26 | - protected IUserManager $userManager, |
|
27 | - protected IDBConnection $dbConnection, |
|
28 | - ) { |
|
29 | - parent::__construct(); |
|
30 | - } |
|
24 | + public function __construct( |
|
25 | + protected IRootFolder $rootFolder, |
|
26 | + protected IUserManager $userManager, |
|
27 | + protected IDBConnection $dbConnection, |
|
28 | + ) { |
|
29 | + parent::__construct(); |
|
30 | + } |
|
31 | 31 | |
32 | - protected function configure() { |
|
33 | - $this |
|
34 | - ->setName('trashbin:cleanup') |
|
35 | - ->setDescription('Remove deleted files') |
|
36 | - ->addArgument( |
|
37 | - 'user_id', |
|
38 | - InputArgument::OPTIONAL | InputArgument::IS_ARRAY, |
|
39 | - 'remove deleted files of the given user(s)' |
|
40 | - ) |
|
41 | - ->addOption( |
|
42 | - 'all-users', |
|
43 | - null, |
|
44 | - InputOption::VALUE_NONE, |
|
45 | - 'run action on all users' |
|
46 | - ); |
|
47 | - } |
|
32 | + protected function configure() { |
|
33 | + $this |
|
34 | + ->setName('trashbin:cleanup') |
|
35 | + ->setDescription('Remove deleted files') |
|
36 | + ->addArgument( |
|
37 | + 'user_id', |
|
38 | + InputArgument::OPTIONAL | InputArgument::IS_ARRAY, |
|
39 | + 'remove deleted files of the given user(s)' |
|
40 | + ) |
|
41 | + ->addOption( |
|
42 | + 'all-users', |
|
43 | + null, |
|
44 | + InputOption::VALUE_NONE, |
|
45 | + 'run action on all users' |
|
46 | + ); |
|
47 | + } |
|
48 | 48 | |
49 | - protected function execute(InputInterface $input, OutputInterface $output): int { |
|
50 | - $users = $input->getArgument('user_id'); |
|
51 | - $verbose = $input->getOption('verbose'); |
|
52 | - if ((!empty($users)) and ($input->getOption('all-users'))) { |
|
53 | - throw new InvalidOptionException('Either specify a user_id or --all-users'); |
|
54 | - } elseif (!empty($users)) { |
|
55 | - foreach ($users as $user) { |
|
56 | - if ($this->userManager->userExists($user)) { |
|
57 | - $output->writeln("Remove deleted files of <info>$user</info>"); |
|
58 | - $this->removeDeletedFiles($user, $output, $verbose); |
|
59 | - } else { |
|
60 | - $output->writeln("<error>Unknown user $user</error>"); |
|
61 | - return 1; |
|
62 | - } |
|
63 | - } |
|
64 | - } elseif ($input->getOption('all-users')) { |
|
65 | - $output->writeln('Remove deleted files for all users'); |
|
66 | - foreach ($this->userManager->getBackends() as $backend) { |
|
67 | - $name = get_class($backend); |
|
68 | - if ($backend instanceof IUserBackend) { |
|
69 | - $name = $backend->getBackendName(); |
|
70 | - } |
|
71 | - $output->writeln("Remove deleted files for users on backend <info>$name</info>"); |
|
72 | - $limit = 500; |
|
73 | - $offset = 0; |
|
74 | - do { |
|
75 | - $users = $backend->getUsers('', $limit, $offset); |
|
76 | - foreach ($users as $user) { |
|
77 | - $output->writeln(" <info>$user</info>"); |
|
78 | - $this->removeDeletedFiles($user, $output, $verbose); |
|
79 | - } |
|
80 | - $offset += $limit; |
|
81 | - } while (count($users) >= $limit); |
|
82 | - } |
|
83 | - } else { |
|
84 | - throw new InvalidOptionException('Either specify a user_id or --all-users'); |
|
85 | - } |
|
86 | - return 0; |
|
87 | - } |
|
49 | + protected function execute(InputInterface $input, OutputInterface $output): int { |
|
50 | + $users = $input->getArgument('user_id'); |
|
51 | + $verbose = $input->getOption('verbose'); |
|
52 | + if ((!empty($users)) and ($input->getOption('all-users'))) { |
|
53 | + throw new InvalidOptionException('Either specify a user_id or --all-users'); |
|
54 | + } elseif (!empty($users)) { |
|
55 | + foreach ($users as $user) { |
|
56 | + if ($this->userManager->userExists($user)) { |
|
57 | + $output->writeln("Remove deleted files of <info>$user</info>"); |
|
58 | + $this->removeDeletedFiles($user, $output, $verbose); |
|
59 | + } else { |
|
60 | + $output->writeln("<error>Unknown user $user</error>"); |
|
61 | + return 1; |
|
62 | + } |
|
63 | + } |
|
64 | + } elseif ($input->getOption('all-users')) { |
|
65 | + $output->writeln('Remove deleted files for all users'); |
|
66 | + foreach ($this->userManager->getBackends() as $backend) { |
|
67 | + $name = get_class($backend); |
|
68 | + if ($backend instanceof IUserBackend) { |
|
69 | + $name = $backend->getBackendName(); |
|
70 | + } |
|
71 | + $output->writeln("Remove deleted files for users on backend <info>$name</info>"); |
|
72 | + $limit = 500; |
|
73 | + $offset = 0; |
|
74 | + do { |
|
75 | + $users = $backend->getUsers('', $limit, $offset); |
|
76 | + foreach ($users as $user) { |
|
77 | + $output->writeln(" <info>$user</info>"); |
|
78 | + $this->removeDeletedFiles($user, $output, $verbose); |
|
79 | + } |
|
80 | + $offset += $limit; |
|
81 | + } while (count($users) >= $limit); |
|
82 | + } |
|
83 | + } else { |
|
84 | + throw new InvalidOptionException('Either specify a user_id or --all-users'); |
|
85 | + } |
|
86 | + return 0; |
|
87 | + } |
|
88 | 88 | |
89 | - /** |
|
90 | - * remove deleted files for the given user |
|
91 | - */ |
|
92 | - protected function removeDeletedFiles(string $uid, OutputInterface $output, bool $verbose): void { |
|
93 | - \OC_Util::tearDownFS(); |
|
94 | - \OC_Util::setupFS($uid); |
|
95 | - $path = '/' . $uid . '/files_trashbin'; |
|
96 | - if ($this->rootFolder->nodeExists($path)) { |
|
97 | - $node = $this->rootFolder->get($path); |
|
89 | + /** |
|
90 | + * remove deleted files for the given user |
|
91 | + */ |
|
92 | + protected function removeDeletedFiles(string $uid, OutputInterface $output, bool $verbose): void { |
|
93 | + \OC_Util::tearDownFS(); |
|
94 | + \OC_Util::setupFS($uid); |
|
95 | + $path = '/' . $uid . '/files_trashbin'; |
|
96 | + if ($this->rootFolder->nodeExists($path)) { |
|
97 | + $node = $this->rootFolder->get($path); |
|
98 | 98 | |
99 | - if ($verbose) { |
|
100 | - $output->writeln('Deleting <info>' . Util::humanFileSize($node->getSize()) . "</info> in trash for <info>$uid</info>."); |
|
101 | - } |
|
102 | - $node->delete(); |
|
103 | - if ($this->rootFolder->nodeExists($path)) { |
|
104 | - $output->writeln('<error>Trash folder sill exists after attempting to delete it</error>'); |
|
105 | - return; |
|
106 | - } |
|
107 | - $query = $this->dbConnection->getQueryBuilder(); |
|
108 | - $query->delete('files_trash') |
|
109 | - ->where($query->expr()->eq('user', $query->createParameter('uid'))) |
|
110 | - ->setParameter('uid', $uid); |
|
111 | - $query->executeStatement(); |
|
112 | - } else { |
|
113 | - if ($verbose) { |
|
114 | - $output->writeln("No trash found for <info>$uid</info>"); |
|
115 | - } |
|
116 | - } |
|
117 | - } |
|
99 | + if ($verbose) { |
|
100 | + $output->writeln('Deleting <info>' . Util::humanFileSize($node->getSize()) . "</info> in trash for <info>$uid</info>."); |
|
101 | + } |
|
102 | + $node->delete(); |
|
103 | + if ($this->rootFolder->nodeExists($path)) { |
|
104 | + $output->writeln('<error>Trash folder sill exists after attempting to delete it</error>'); |
|
105 | + return; |
|
106 | + } |
|
107 | + $query = $this->dbConnection->getQueryBuilder(); |
|
108 | + $query->delete('files_trash') |
|
109 | + ->where($query->expr()->eq('user', $query->createParameter('uid'))) |
|
110 | + ->setParameter('uid', $uid); |
|
111 | + $query->executeStatement(); |
|
112 | + } else { |
|
113 | + if ($verbose) { |
|
114 | + $output->writeln("No trash found for <info>$uid</info>"); |
|
115 | + } |
|
116 | + } |
|
117 | + } |
|
118 | 118 | } |
@@ -92,12 +92,12 @@ |
||
92 | 92 | protected function removeDeletedFiles(string $uid, OutputInterface $output, bool $verbose): void { |
93 | 93 | \OC_Util::tearDownFS(); |
94 | 94 | \OC_Util::setupFS($uid); |
95 | - $path = '/' . $uid . '/files_trashbin'; |
|
95 | + $path = '/'.$uid.'/files_trashbin'; |
|
96 | 96 | if ($this->rootFolder->nodeExists($path)) { |
97 | 97 | $node = $this->rootFolder->get($path); |
98 | 98 | |
99 | 99 | if ($verbose) { |
100 | - $output->writeln('Deleting <info>' . Util::humanFileSize($node->getSize()) . "</info> in trash for <info>$uid</info>."); |
|
100 | + $output->writeln('Deleting <info>'.Util::humanFileSize($node->getSize())."</info> in trash for <info>$uid</info>."); |
|
101 | 101 | } |
102 | 102 | $node->delete(); |
103 | 103 | if ($this->rootFolder->nodeExists($path)) { |
@@ -20,105 +20,105 @@ |
||
20 | 20 | use Symfony\Component\Console\Output\OutputInterface; |
21 | 21 | |
22 | 22 | class Size extends Base { |
23 | - public function __construct( |
|
24 | - private IConfig $config, |
|
25 | - private IUserManager $userManager, |
|
26 | - private IBus $commandBus, |
|
27 | - ) { |
|
28 | - parent::__construct(); |
|
29 | - } |
|
23 | + public function __construct( |
|
24 | + private IConfig $config, |
|
25 | + private IUserManager $userManager, |
|
26 | + private IBus $commandBus, |
|
27 | + ) { |
|
28 | + parent::__construct(); |
|
29 | + } |
|
30 | 30 | |
31 | - protected function configure() { |
|
32 | - parent::configure(); |
|
33 | - $this |
|
34 | - ->setName('trashbin:size') |
|
35 | - ->setDescription('Configure the target trashbin size') |
|
36 | - ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'configure the target size for the provided user, if no user is given the default size is configured') |
|
37 | - ->addArgument( |
|
38 | - 'size', |
|
39 | - InputArgument::OPTIONAL, |
|
40 | - 'the target size for the trashbin, if not provided the current trashbin size will be returned' |
|
41 | - ); |
|
42 | - } |
|
31 | + protected function configure() { |
|
32 | + parent::configure(); |
|
33 | + $this |
|
34 | + ->setName('trashbin:size') |
|
35 | + ->setDescription('Configure the target trashbin size') |
|
36 | + ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'configure the target size for the provided user, if no user is given the default size is configured') |
|
37 | + ->addArgument( |
|
38 | + 'size', |
|
39 | + InputArgument::OPTIONAL, |
|
40 | + 'the target size for the trashbin, if not provided the current trashbin size will be returned' |
|
41 | + ); |
|
42 | + } |
|
43 | 43 | |
44 | - protected function execute(InputInterface $input, OutputInterface $output): int { |
|
45 | - $user = $input->getOption('user'); |
|
46 | - $size = $input->getArgument('size'); |
|
44 | + protected function execute(InputInterface $input, OutputInterface $output): int { |
|
45 | + $user = $input->getOption('user'); |
|
46 | + $size = $input->getArgument('size'); |
|
47 | 47 | |
48 | - if ($size) { |
|
49 | - $parsedSize = Util::computerFileSize($size); |
|
50 | - if ($parsedSize === false) { |
|
51 | - $output->writeln('<error>Failed to parse input size</error>'); |
|
52 | - return -1; |
|
53 | - } |
|
54 | - if ($user) { |
|
55 | - $this->config->setUserValue($user, 'files_trashbin', 'trashbin_size', (string)$parsedSize); |
|
56 | - $this->commandBus->push(new Expire($user)); |
|
57 | - } else { |
|
58 | - $this->config->setAppValue('files_trashbin', 'trashbin_size', (string)$parsedSize); |
|
59 | - $output->writeln('<info>Warning: changing the default trashbin size will automatically trigger cleanup of existing trashbins,</info>'); |
|
60 | - $output->writeln('<info>a users trashbin can exceed the configured size until they move a new file to the trashbin.</info>'); |
|
61 | - } |
|
62 | - } else { |
|
63 | - $this->printTrashbinSize($input, $output, $user); |
|
64 | - } |
|
48 | + if ($size) { |
|
49 | + $parsedSize = Util::computerFileSize($size); |
|
50 | + if ($parsedSize === false) { |
|
51 | + $output->writeln('<error>Failed to parse input size</error>'); |
|
52 | + return -1; |
|
53 | + } |
|
54 | + if ($user) { |
|
55 | + $this->config->setUserValue($user, 'files_trashbin', 'trashbin_size', (string)$parsedSize); |
|
56 | + $this->commandBus->push(new Expire($user)); |
|
57 | + } else { |
|
58 | + $this->config->setAppValue('files_trashbin', 'trashbin_size', (string)$parsedSize); |
|
59 | + $output->writeln('<info>Warning: changing the default trashbin size will automatically trigger cleanup of existing trashbins,</info>'); |
|
60 | + $output->writeln('<info>a users trashbin can exceed the configured size until they move a new file to the trashbin.</info>'); |
|
61 | + } |
|
62 | + } else { |
|
63 | + $this->printTrashbinSize($input, $output, $user); |
|
64 | + } |
|
65 | 65 | |
66 | - return 0; |
|
67 | - } |
|
66 | + return 0; |
|
67 | + } |
|
68 | 68 | |
69 | - private function printTrashbinSize(InputInterface $input, OutputInterface $output, ?string $user) { |
|
70 | - $globalSize = (int)$this->config->getAppValue('files_trashbin', 'trashbin_size', '-1'); |
|
71 | - if ($globalSize < 0) { |
|
72 | - $globalHumanSize = 'default (50% of available space)'; |
|
73 | - } else { |
|
74 | - $globalHumanSize = Util::humanFileSize($globalSize); |
|
75 | - } |
|
69 | + private function printTrashbinSize(InputInterface $input, OutputInterface $output, ?string $user) { |
|
70 | + $globalSize = (int)$this->config->getAppValue('files_trashbin', 'trashbin_size', '-1'); |
|
71 | + if ($globalSize < 0) { |
|
72 | + $globalHumanSize = 'default (50% of available space)'; |
|
73 | + } else { |
|
74 | + $globalHumanSize = Util::humanFileSize($globalSize); |
|
75 | + } |
|
76 | 76 | |
77 | - if ($user) { |
|
78 | - $userSize = (int)$this->config->getUserValue($user, 'files_trashbin', 'trashbin_size', '-1'); |
|
77 | + if ($user) { |
|
78 | + $userSize = (int)$this->config->getUserValue($user, 'files_trashbin', 'trashbin_size', '-1'); |
|
79 | 79 | |
80 | - if ($userSize < 0) { |
|
81 | - $userHumanSize = ($globalSize < 0) ? $globalHumanSize : "default($globalHumanSize)"; |
|
82 | - } else { |
|
83 | - $userHumanSize = Util::humanFileSize($userSize); |
|
84 | - } |
|
80 | + if ($userSize < 0) { |
|
81 | + $userHumanSize = ($globalSize < 0) ? $globalHumanSize : "default($globalHumanSize)"; |
|
82 | + } else { |
|
83 | + $userHumanSize = Util::humanFileSize($userSize); |
|
84 | + } |
|
85 | 85 | |
86 | - if ($input->getOption('output') == self::OUTPUT_FORMAT_PLAIN) { |
|
87 | - $output->writeln($userHumanSize); |
|
88 | - } else { |
|
89 | - $userValue = ($userSize < 0) ? 'default' : $userSize; |
|
90 | - $globalValue = ($globalSize < 0) ? 'default' : $globalSize; |
|
91 | - $this->writeArrayInOutputFormat($input, $output, [ |
|
92 | - 'user_size' => $userValue, |
|
93 | - 'global_size' => $globalValue, |
|
94 | - 'effective_size' => ($userSize < 0) ? $globalValue : $userValue, |
|
95 | - ]); |
|
96 | - } |
|
97 | - } else { |
|
98 | - $users = []; |
|
99 | - $this->userManager->callForSeenUsers(function (IUser $user) use (&$users): void { |
|
100 | - $users[] = $user->getUID(); |
|
101 | - }); |
|
102 | - $userValues = $this->config->getUserValueForUsers('files_trashbin', 'trashbin_size', $users); |
|
86 | + if ($input->getOption('output') == self::OUTPUT_FORMAT_PLAIN) { |
|
87 | + $output->writeln($userHumanSize); |
|
88 | + } else { |
|
89 | + $userValue = ($userSize < 0) ? 'default' : $userSize; |
|
90 | + $globalValue = ($globalSize < 0) ? 'default' : $globalSize; |
|
91 | + $this->writeArrayInOutputFormat($input, $output, [ |
|
92 | + 'user_size' => $userValue, |
|
93 | + 'global_size' => $globalValue, |
|
94 | + 'effective_size' => ($userSize < 0) ? $globalValue : $userValue, |
|
95 | + ]); |
|
96 | + } |
|
97 | + } else { |
|
98 | + $users = []; |
|
99 | + $this->userManager->callForSeenUsers(function (IUser $user) use (&$users): void { |
|
100 | + $users[] = $user->getUID(); |
|
101 | + }); |
|
102 | + $userValues = $this->config->getUserValueForUsers('files_trashbin', 'trashbin_size', $users); |
|
103 | 103 | |
104 | - if ($input->getOption('output') == self::OUTPUT_FORMAT_PLAIN) { |
|
105 | - $output->writeln("Default size: $globalHumanSize"); |
|
106 | - $output->writeln(''); |
|
107 | - if (count($userValues)) { |
|
108 | - $output->writeln('Per-user sizes:'); |
|
109 | - $this->writeArrayInOutputFormat($input, $output, array_map(function ($size) { |
|
110 | - return Util::humanFileSize($size); |
|
111 | - }, $userValues)); |
|
112 | - } else { |
|
113 | - $output->writeln('No per-user sizes configured'); |
|
114 | - } |
|
115 | - } else { |
|
116 | - $globalValue = ($globalSize < 0) ? 'default' : $globalSize; |
|
117 | - $this->writeArrayInOutputFormat($input, $output, [ |
|
118 | - 'global_size' => $globalValue, |
|
119 | - 'user_sizes' => $userValues, |
|
120 | - ]); |
|
121 | - } |
|
122 | - } |
|
123 | - } |
|
104 | + if ($input->getOption('output') == self::OUTPUT_FORMAT_PLAIN) { |
|
105 | + $output->writeln("Default size: $globalHumanSize"); |
|
106 | + $output->writeln(''); |
|
107 | + if (count($userValues)) { |
|
108 | + $output->writeln('Per-user sizes:'); |
|
109 | + $this->writeArrayInOutputFormat($input, $output, array_map(function ($size) { |
|
110 | + return Util::humanFileSize($size); |
|
111 | + }, $userValues)); |
|
112 | + } else { |
|
113 | + $output->writeln('No per-user sizes configured'); |
|
114 | + } |
|
115 | + } else { |
|
116 | + $globalValue = ($globalSize < 0) ? 'default' : $globalSize; |
|
117 | + $this->writeArrayInOutputFormat($input, $output, [ |
|
118 | + 'global_size' => $globalValue, |
|
119 | + 'user_sizes' => $userValues, |
|
120 | + ]); |
|
121 | + } |
|
122 | + } |
|
123 | + } |
|
124 | 124 | } |
@@ -52,10 +52,10 @@ discard block |
||
52 | 52 | return -1; |
53 | 53 | } |
54 | 54 | if ($user) { |
55 | - $this->config->setUserValue($user, 'files_trashbin', 'trashbin_size', (string)$parsedSize); |
|
55 | + $this->config->setUserValue($user, 'files_trashbin', 'trashbin_size', (string) $parsedSize); |
|
56 | 56 | $this->commandBus->push(new Expire($user)); |
57 | 57 | } else { |
58 | - $this->config->setAppValue('files_trashbin', 'trashbin_size', (string)$parsedSize); |
|
58 | + $this->config->setAppValue('files_trashbin', 'trashbin_size', (string) $parsedSize); |
|
59 | 59 | $output->writeln('<info>Warning: changing the default trashbin size will automatically trigger cleanup of existing trashbins,</info>'); |
60 | 60 | $output->writeln('<info>a users trashbin can exceed the configured size until they move a new file to the trashbin.</info>'); |
61 | 61 | } |
@@ -67,7 +67,7 @@ discard block |
||
67 | 67 | } |
68 | 68 | |
69 | 69 | private function printTrashbinSize(InputInterface $input, OutputInterface $output, ?string $user) { |
70 | - $globalSize = (int)$this->config->getAppValue('files_trashbin', 'trashbin_size', '-1'); |
|
70 | + $globalSize = (int) $this->config->getAppValue('files_trashbin', 'trashbin_size', '-1'); |
|
71 | 71 | if ($globalSize < 0) { |
72 | 72 | $globalHumanSize = 'default (50% of available space)'; |
73 | 73 | } else { |
@@ -75,7 +75,7 @@ discard block |
||
75 | 75 | } |
76 | 76 | |
77 | 77 | if ($user) { |
78 | - $userSize = (int)$this->config->getUserValue($user, 'files_trashbin', 'trashbin_size', '-1'); |
|
78 | + $userSize = (int) $this->config->getUserValue($user, 'files_trashbin', 'trashbin_size', '-1'); |
|
79 | 79 | |
80 | 80 | if ($userSize < 0) { |
81 | 81 | $userHumanSize = ($globalSize < 0) ? $globalHumanSize : "default($globalHumanSize)"; |
@@ -96,7 +96,7 @@ discard block |
||
96 | 96 | } |
97 | 97 | } else { |
98 | 98 | $users = []; |
99 | - $this->userManager->callForSeenUsers(function (IUser $user) use (&$users): void { |
|
99 | + $this->userManager->callForSeenUsers(function(IUser $user) use (&$users): void { |
|
100 | 100 | $users[] = $user->getUID(); |
101 | 101 | }); |
102 | 102 | $userValues = $this->config->getUserValueForUsers('files_trashbin', 'trashbin_size', $users); |
@@ -106,7 +106,7 @@ discard block |
||
106 | 106 | $output->writeln(''); |
107 | 107 | if (count($userValues)) { |
108 | 108 | $output->writeln('Per-user sizes:'); |
109 | - $this->writeArrayInOutputFormat($input, $output, array_map(function ($size) { |
|
109 | + $this->writeArrayInOutputFormat($input, $output, array_map(function($size) { |
|
110 | 110 | return Util::humanFileSize($size); |
111 | 111 | }, $userValues)); |
112 | 112 | } else { |
@@ -34,681 +34,681 @@ |
||
34 | 34 | * @group DB |
35 | 35 | */ |
36 | 36 | class TrashbinTest extends \Test\TestCase { |
37 | - public const TEST_TRASHBIN_USER1 = 'test-trashbin-user1'; |
|
38 | - public const TEST_TRASHBIN_USER2 = 'test-trashbin-user2'; |
|
39 | - |
|
40 | - private $trashRoot1; |
|
41 | - private $trashRoot2; |
|
42 | - |
|
43 | - private static $rememberRetentionObligation; |
|
44 | - |
|
45 | - /** |
|
46 | - * @var bool |
|
47 | - */ |
|
48 | - private static $trashBinStatus; |
|
49 | - |
|
50 | - /** |
|
51 | - * @var View |
|
52 | - */ |
|
53 | - private $rootView; |
|
54 | - |
|
55 | - public static function setUpBeforeClass(): void { |
|
56 | - parent::setUpBeforeClass(); |
|
57 | - |
|
58 | - $appManager = Server::get(IAppManager::class); |
|
59 | - self::$trashBinStatus = $appManager->isEnabledForUser('files_trashbin'); |
|
60 | - |
|
61 | - // reset backend |
|
62 | - Server::get(IUserManager::class)->clearBackends(); |
|
63 | - Server::get(IUserManager::class)->registerBackend(new Database()); |
|
64 | - |
|
65 | - // clear share hooks |
|
66 | - \OC_Hook::clear('OCP\\Share'); |
|
67 | - \OC::registerShareHooks(Server::get(SystemConfig::class)); |
|
68 | - |
|
69 | - // init files sharing |
|
70 | - new Application(); |
|
71 | - |
|
72 | - //disable encryption |
|
73 | - Server::get(IAppManager::class)->disableApp('encryption'); |
|
74 | - |
|
75 | - $config = Server::get(IConfig::class); |
|
76 | - //configure trashbin |
|
77 | - self::$rememberRetentionObligation = $config->getSystemValue('trashbin_retention_obligation', Expiration::DEFAULT_RETENTION_OBLIGATION); |
|
78 | - /** @var Expiration $expiration */ |
|
79 | - $expiration = Server::get(Expiration::class); |
|
80 | - $expiration->setRetentionObligation('auto, 2'); |
|
81 | - |
|
82 | - // register trashbin hooks |
|
83 | - $trashbinApp = new TrashbinApplication(); |
|
84 | - $trashbinApp->boot(new BootContext(new DIContainer('', [], \OC::$server))); |
|
85 | - |
|
86 | - // create test user |
|
87 | - self::loginHelper(self::TEST_TRASHBIN_USER2, true); |
|
88 | - self::loginHelper(self::TEST_TRASHBIN_USER1, true); |
|
89 | - } |
|
90 | - |
|
91 | - |
|
92 | - public static function tearDownAfterClass(): void { |
|
93 | - // cleanup test user |
|
94 | - $user = Server::get(IUserManager::class)->get(self::TEST_TRASHBIN_USER1); |
|
95 | - if ($user !== null) { |
|
96 | - $user->delete(); |
|
97 | - } |
|
98 | - |
|
99 | - /** @var Expiration $expiration */ |
|
100 | - $expiration = Server::get(Expiration::class); |
|
101 | - $expiration->setRetentionObligation(self::$rememberRetentionObligation); |
|
102 | - |
|
103 | - \OC_Hook::clear(); |
|
104 | - |
|
105 | - Filesystem::getLoader()->removeStorageWrapper('oc_trashbin'); |
|
106 | - |
|
107 | - if (self::$trashBinStatus) { |
|
108 | - Server::get(IAppManager::class)->enableApp('files_trashbin'); |
|
109 | - } |
|
110 | - |
|
111 | - parent::tearDownAfterClass(); |
|
112 | - } |
|
113 | - |
|
114 | - protected function setUp(): void { |
|
115 | - parent::setUp(); |
|
116 | - |
|
117 | - Server::get(IAppManager::class)->enableApp('files_trashbin'); |
|
118 | - $config = Server::get(IConfig::class); |
|
119 | - $mockConfig = $this->createMock(IConfig::class); |
|
120 | - $mockConfig |
|
121 | - ->method('getSystemValue') |
|
122 | - ->willReturnCallback(static function ($key, $default) use ($config) { |
|
123 | - if ($key === 'filesystem_check_changes') { |
|
124 | - return Watcher::CHECK_ONCE; |
|
125 | - } else { |
|
126 | - return $config->getSystemValue($key, $default); |
|
127 | - } |
|
128 | - }); |
|
129 | - $mockConfig |
|
130 | - ->method('getUserValue') |
|
131 | - ->willReturnCallback(static function ($userId, $appName, $key, $default = '') use ($config) { |
|
132 | - return $config->getUserValue($userId, $appName, $key, $default); |
|
133 | - }); |
|
134 | - $mockConfig |
|
135 | - ->method('getAppValue') |
|
136 | - ->willReturnCallback(static function ($appName, $key, $default = '') use ($config) { |
|
137 | - return $config->getAppValue($appName, $key, $default); |
|
138 | - }); |
|
139 | - $this->overwriteService(AllConfig::class, $mockConfig); |
|
140 | - |
|
141 | - $this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin'; |
|
142 | - $this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin'; |
|
143 | - $this->rootView = new View(); |
|
144 | - self::loginHelper(self::TEST_TRASHBIN_USER1); |
|
145 | - } |
|
146 | - |
|
147 | - protected function tearDown(): void { |
|
148 | - $this->restoreService(AllConfig::class); |
|
149 | - // disable trashbin to be able to properly clean up |
|
150 | - Server::get(IAppManager::class)->disableApp('files_trashbin'); |
|
151 | - |
|
152 | - $this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER1 . '/files'); |
|
153 | - $this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER2 . '/files'); |
|
154 | - $this->rootView->deleteAll($this->trashRoot1); |
|
155 | - $this->rootView->deleteAll($this->trashRoot2); |
|
156 | - |
|
157 | - // clear trash table |
|
158 | - $connection = Server::get(IDBConnection::class); |
|
159 | - $connection->executeUpdate('DELETE FROM `*PREFIX*files_trash`'); |
|
160 | - |
|
161 | - parent::tearDown(); |
|
162 | - } |
|
163 | - |
|
164 | - /** |
|
165 | - * test expiration of files older then the max storage time defined for the trash |
|
166 | - */ |
|
167 | - public function testExpireOldFiles(): void { |
|
168 | - |
|
169 | - /** @var ITimeFactory $time */ |
|
170 | - $time = Server::get(ITimeFactory::class); |
|
171 | - $currentTime = $time->getTime(); |
|
172 | - $expireAt = $currentTime - 2 * 24 * 60 * 60; |
|
173 | - $expiredDate = $currentTime - 3 * 24 * 60 * 60; |
|
174 | - |
|
175 | - // create some files |
|
176 | - Filesystem::file_put_contents('file1.txt', 'file1'); |
|
177 | - Filesystem::file_put_contents('file2.txt', 'file2'); |
|
178 | - Filesystem::file_put_contents('file3.txt', 'file3'); |
|
179 | - |
|
180 | - // delete them so that they end up in the trash bin |
|
181 | - Filesystem::unlink('file1.txt'); |
|
182 | - Filesystem::unlink('file2.txt'); |
|
183 | - Filesystem::unlink('file3.txt'); |
|
184 | - |
|
185 | - //make sure that files are in the trash bin |
|
186 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name'); |
|
187 | - $this->assertSame(3, count($filesInTrash)); |
|
188 | - |
|
189 | - // every second file will get a date in the past so that it will get expired |
|
190 | - $manipulatedList = $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate); |
|
191 | - |
|
192 | - $testClass = new TrashbinForTesting(); |
|
193 | - [$sizeOfDeletedFiles, $count] = $testClass->dummyDeleteExpiredFiles($manipulatedList, $expireAt); |
|
194 | - |
|
195 | - $this->assertSame(10, $sizeOfDeletedFiles); |
|
196 | - $this->assertSame(2, $count); |
|
197 | - |
|
198 | - // only file2.txt should be left |
|
199 | - $remainingFiles = array_slice($manipulatedList, $count); |
|
200 | - $this->assertSame(1, count($remainingFiles)); |
|
201 | - $remainingFile = reset($remainingFiles); |
|
202 | - // TODO: failing test |
|
203 | - #$this->assertSame('file2.txt', $remainingFile['name']); |
|
204 | - |
|
205 | - // check that file1.txt and file3.txt was really deleted |
|
206 | - $newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); |
|
207 | - $this->assertSame(1, count($newTrashContent)); |
|
208 | - $element = reset($newTrashContent); |
|
209 | - // TODO: failing test |
|
210 | - #$this->assertSame('file2.txt', $element['name']); |
|
211 | - } |
|
212 | - |
|
213 | - /** |
|
214 | - * test expiration of files older then the max storage time defined for the trash |
|
215 | - * in this test we delete a shared file and check if both trash bins, the one from |
|
216 | - * the owner of the file and the one from the user who deleted the file get expired |
|
217 | - * correctly |
|
218 | - */ |
|
219 | - public function testExpireOldFilesShared(): void { |
|
220 | - $currentTime = time(); |
|
221 | - $folder = 'trashTest-' . $currentTime . '/'; |
|
222 | - $expiredDate = $currentTime - 3 * 24 * 60 * 60; |
|
223 | - |
|
224 | - // create some files |
|
225 | - Filesystem::mkdir($folder); |
|
226 | - Filesystem::file_put_contents($folder . 'user1-1.txt', 'file1'); |
|
227 | - Filesystem::file_put_contents($folder . 'user1-2.txt', 'file2'); |
|
228 | - Filesystem::file_put_contents($folder . 'user1-3.txt', 'file3'); |
|
229 | - Filesystem::file_put_contents($folder . 'user1-4.txt', 'file4'); |
|
230 | - |
|
231 | - //share user1-4.txt with user2 |
|
232 | - $node = \OC::$server->getUserFolder(self::TEST_TRASHBIN_USER1)->get($folder); |
|
233 | - $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
234 | - $share->setShareType(IShare::TYPE_USER) |
|
235 | - ->setNode($node) |
|
236 | - ->setSharedBy(self::TEST_TRASHBIN_USER1) |
|
237 | - ->setSharedWith(self::TEST_TRASHBIN_USER2) |
|
238 | - ->setPermissions(Constants::PERMISSION_ALL); |
|
239 | - $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
240 | - Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_TRASHBIN_USER2); |
|
241 | - |
|
242 | - // delete them so that they end up in the trash bin |
|
243 | - Filesystem::unlink($folder . 'user1-1.txt'); |
|
244 | - Filesystem::unlink($folder . 'user1-2.txt'); |
|
245 | - Filesystem::unlink($folder . 'user1-3.txt'); |
|
246 | - |
|
247 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name'); |
|
248 | - $this->assertSame(3, count($filesInTrash)); |
|
249 | - |
|
250 | - // every second file will get a date in the past so that it will get expired |
|
251 | - $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate); |
|
252 | - |
|
253 | - // login as user2 |
|
254 | - self::loginHelper(self::TEST_TRASHBIN_USER2); |
|
255 | - |
|
256 | - $this->assertTrue(Filesystem::file_exists($folder . 'user1-4.txt')); |
|
257 | - |
|
258 | - // create some files |
|
259 | - Filesystem::file_put_contents('user2-1.txt', 'file1'); |
|
260 | - Filesystem::file_put_contents('user2-2.txt', 'file2'); |
|
261 | - |
|
262 | - // delete them so that they end up in the trash bin |
|
263 | - Filesystem::unlink('user2-1.txt'); |
|
264 | - Filesystem::unlink('user2-2.txt'); |
|
265 | - |
|
266 | - $filesInTrashUser2 = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2, 'name'); |
|
267 | - $this->assertSame(2, count($filesInTrashUser2)); |
|
268 | - |
|
269 | - // every second file will get a date in the past so that it will get expired |
|
270 | - $this->manipulateDeleteTime($filesInTrashUser2, $this->trashRoot2, $expiredDate); |
|
271 | - |
|
272 | - Filesystem::unlink($folder . 'user1-4.txt'); |
|
273 | - |
|
274 | - $this->runCommands(); |
|
275 | - |
|
276 | - $filesInTrashUser2AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2); |
|
277 | - |
|
278 | - // user2-1.txt should have been expired |
|
279 | - $this->verifyArray($filesInTrashUser2AfterDelete, ['user2-2.txt', 'user1-4.txt']); |
|
280 | - |
|
281 | - self::loginHelper(self::TEST_TRASHBIN_USER1); |
|
282 | - |
|
283 | - // user1-1.txt and user1-3.txt should have been expired |
|
284 | - $filesInTrashUser1AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); |
|
285 | - |
|
286 | - $this->verifyArray($filesInTrashUser1AfterDelete, ['user1-2.txt', 'user1-4.txt']); |
|
287 | - } |
|
288 | - |
|
289 | - /** |
|
290 | - * verify that the array contains the expected results |
|
291 | - * |
|
292 | - * @param FileInfo[] $result |
|
293 | - * @param string[] $expected |
|
294 | - */ |
|
295 | - private function verifyArray($result, $expected) { |
|
296 | - $this->assertSame(count($expected), count($result)); |
|
297 | - foreach ($expected as $expectedFile) { |
|
298 | - $found = false; |
|
299 | - foreach ($result as $fileInTrash) { |
|
300 | - if ($expectedFile === $fileInTrash['name']) { |
|
301 | - $found = true; |
|
302 | - break; |
|
303 | - } |
|
304 | - } |
|
305 | - if (!$found) { |
|
306 | - // if we didn't found the expected file, something went wrong |
|
307 | - $this->assertTrue(false, "can't find expected file '" . $expectedFile . "' in trash bin"); |
|
308 | - } |
|
309 | - } |
|
310 | - } |
|
311 | - |
|
312 | - /** |
|
313 | - * @param FileInfo[] $files |
|
314 | - * @param string $trashRoot |
|
315 | - * @param integer $expireDate |
|
316 | - */ |
|
317 | - private function manipulateDeleteTime($files, $trashRoot, $expireDate) { |
|
318 | - $counter = 0; |
|
319 | - foreach ($files as &$file) { |
|
320 | - // modify every second file |
|
321 | - $counter = ($counter + 1) % 2; |
|
322 | - if ($counter === 1) { |
|
323 | - $source = $trashRoot . '/files/' . $file['name'] . '.d' . $file['mtime']; |
|
324 | - $target = Filesystem::normalizePath($trashRoot . '/files/' . $file['name'] . '.d' . $expireDate); |
|
325 | - $this->rootView->rename($source, $target); |
|
326 | - $file['mtime'] = $expireDate; |
|
327 | - } |
|
328 | - } |
|
329 | - return \OCA\Files\Helper::sortFiles($files, 'mtime'); |
|
330 | - } |
|
331 | - |
|
332 | - |
|
333 | - /** |
|
334 | - * test expiration of old files in the trash bin until the max size |
|
335 | - * of the trash bin is met again |
|
336 | - */ |
|
337 | - public function testExpireOldFilesUtilLimitsAreMet(): void { |
|
338 | - |
|
339 | - // create some files |
|
340 | - Filesystem::file_put_contents('file1.txt', 'file1'); |
|
341 | - Filesystem::file_put_contents('file2.txt', 'file2'); |
|
342 | - Filesystem::file_put_contents('file3.txt', 'file3'); |
|
343 | - |
|
344 | - // delete them so that they end up in the trash bin |
|
345 | - Filesystem::unlink('file3.txt'); |
|
346 | - sleep(1); // make sure that every file has a unique mtime |
|
347 | - Filesystem::unlink('file2.txt'); |
|
348 | - sleep(1); // make sure that every file has a unique mtime |
|
349 | - Filesystem::unlink('file1.txt'); |
|
350 | - |
|
351 | - //make sure that files are in the trash bin |
|
352 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
353 | - $this->assertSame(3, count($filesInTrash)); |
|
354 | - |
|
355 | - $testClass = new TrashbinForTesting(); |
|
356 | - $sizeOfDeletedFiles = $testClass->dummyDeleteFiles($filesInTrash, -8); |
|
357 | - |
|
358 | - // the two oldest files (file3.txt and file2.txt) should be deleted |
|
359 | - $this->assertSame(10, $sizeOfDeletedFiles); |
|
360 | - |
|
361 | - $newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); |
|
362 | - $this->assertSame(1, count($newTrashContent)); |
|
363 | - $element = reset($newTrashContent); |
|
364 | - $this->assertSame('file1.txt', $element['name']); |
|
365 | - } |
|
366 | - |
|
367 | - /** |
|
368 | - * Test restoring a file |
|
369 | - */ |
|
370 | - public function testRestoreFileInRoot(): void { |
|
371 | - $userFolder = \OC::$server->getUserFolder(); |
|
372 | - $file = $userFolder->newFile('file1.txt'); |
|
373 | - $file->putContent('foo'); |
|
374 | - |
|
375 | - $this->assertTrue($userFolder->nodeExists('file1.txt')); |
|
376 | - |
|
377 | - $file->delete(); |
|
378 | - |
|
379 | - $this->assertFalse($userFolder->nodeExists('file1.txt')); |
|
380 | - |
|
381 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
382 | - $this->assertCount(1, $filesInTrash); |
|
383 | - |
|
384 | - /** @var FileInfo */ |
|
385 | - $trashedFile = $filesInTrash[0]; |
|
386 | - |
|
387 | - $this->assertTrue( |
|
388 | - Trashbin::restore( |
|
389 | - 'file1.txt.d' . $trashedFile->getMtime(), |
|
390 | - $trashedFile->getName(), |
|
391 | - $trashedFile->getMtime() |
|
392 | - ) |
|
393 | - ); |
|
394 | - |
|
395 | - $file = $userFolder->get('file1.txt'); |
|
396 | - $this->assertEquals('foo', $file->getContent()); |
|
397 | - } |
|
398 | - |
|
399 | - /** |
|
400 | - * Test restoring a file in subfolder |
|
401 | - */ |
|
402 | - public function testRestoreFileInSubfolder(): void { |
|
403 | - $userFolder = \OC::$server->getUserFolder(); |
|
404 | - $folder = $userFolder->newFolder('folder'); |
|
405 | - $file = $folder->newFile('file1.txt'); |
|
406 | - $file->putContent('foo'); |
|
407 | - |
|
408 | - $this->assertTrue($userFolder->nodeExists('folder/file1.txt')); |
|
409 | - |
|
410 | - $file->delete(); |
|
411 | - |
|
412 | - $this->assertFalse($userFolder->nodeExists('folder/file1.txt')); |
|
413 | - |
|
414 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
415 | - $this->assertCount(1, $filesInTrash); |
|
416 | - |
|
417 | - /** @var FileInfo */ |
|
418 | - $trashedFile = $filesInTrash[0]; |
|
419 | - |
|
420 | - $this->assertTrue( |
|
421 | - Trashbin::restore( |
|
422 | - 'file1.txt.d' . $trashedFile->getMtime(), |
|
423 | - $trashedFile->getName(), |
|
424 | - $trashedFile->getMtime() |
|
425 | - ) |
|
426 | - ); |
|
427 | - |
|
428 | - $file = $userFolder->get('folder/file1.txt'); |
|
429 | - $this->assertEquals('foo', $file->getContent()); |
|
430 | - } |
|
431 | - |
|
432 | - /** |
|
433 | - * Test restoring a folder |
|
434 | - */ |
|
435 | - public function testRestoreFolder(): void { |
|
436 | - $userFolder = \OC::$server->getUserFolder(); |
|
437 | - $folder = $userFolder->newFolder('folder'); |
|
438 | - $file = $folder->newFile('file1.txt'); |
|
439 | - $file->putContent('foo'); |
|
440 | - |
|
441 | - $this->assertTrue($userFolder->nodeExists('folder')); |
|
442 | - |
|
443 | - $folder->delete(); |
|
444 | - |
|
445 | - $this->assertFalse($userFolder->nodeExists('folder')); |
|
446 | - |
|
447 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
448 | - $this->assertCount(1, $filesInTrash); |
|
37 | + public const TEST_TRASHBIN_USER1 = 'test-trashbin-user1'; |
|
38 | + public const TEST_TRASHBIN_USER2 = 'test-trashbin-user2'; |
|
39 | + |
|
40 | + private $trashRoot1; |
|
41 | + private $trashRoot2; |
|
42 | + |
|
43 | + private static $rememberRetentionObligation; |
|
44 | + |
|
45 | + /** |
|
46 | + * @var bool |
|
47 | + */ |
|
48 | + private static $trashBinStatus; |
|
49 | + |
|
50 | + /** |
|
51 | + * @var View |
|
52 | + */ |
|
53 | + private $rootView; |
|
54 | + |
|
55 | + public static function setUpBeforeClass(): void { |
|
56 | + parent::setUpBeforeClass(); |
|
57 | + |
|
58 | + $appManager = Server::get(IAppManager::class); |
|
59 | + self::$trashBinStatus = $appManager->isEnabledForUser('files_trashbin'); |
|
60 | + |
|
61 | + // reset backend |
|
62 | + Server::get(IUserManager::class)->clearBackends(); |
|
63 | + Server::get(IUserManager::class)->registerBackend(new Database()); |
|
64 | + |
|
65 | + // clear share hooks |
|
66 | + \OC_Hook::clear('OCP\\Share'); |
|
67 | + \OC::registerShareHooks(Server::get(SystemConfig::class)); |
|
68 | + |
|
69 | + // init files sharing |
|
70 | + new Application(); |
|
71 | + |
|
72 | + //disable encryption |
|
73 | + Server::get(IAppManager::class)->disableApp('encryption'); |
|
74 | + |
|
75 | + $config = Server::get(IConfig::class); |
|
76 | + //configure trashbin |
|
77 | + self::$rememberRetentionObligation = $config->getSystemValue('trashbin_retention_obligation', Expiration::DEFAULT_RETENTION_OBLIGATION); |
|
78 | + /** @var Expiration $expiration */ |
|
79 | + $expiration = Server::get(Expiration::class); |
|
80 | + $expiration->setRetentionObligation('auto, 2'); |
|
81 | + |
|
82 | + // register trashbin hooks |
|
83 | + $trashbinApp = new TrashbinApplication(); |
|
84 | + $trashbinApp->boot(new BootContext(new DIContainer('', [], \OC::$server))); |
|
85 | + |
|
86 | + // create test user |
|
87 | + self::loginHelper(self::TEST_TRASHBIN_USER2, true); |
|
88 | + self::loginHelper(self::TEST_TRASHBIN_USER1, true); |
|
89 | + } |
|
90 | + |
|
91 | + |
|
92 | + public static function tearDownAfterClass(): void { |
|
93 | + // cleanup test user |
|
94 | + $user = Server::get(IUserManager::class)->get(self::TEST_TRASHBIN_USER1); |
|
95 | + if ($user !== null) { |
|
96 | + $user->delete(); |
|
97 | + } |
|
98 | + |
|
99 | + /** @var Expiration $expiration */ |
|
100 | + $expiration = Server::get(Expiration::class); |
|
101 | + $expiration->setRetentionObligation(self::$rememberRetentionObligation); |
|
102 | + |
|
103 | + \OC_Hook::clear(); |
|
104 | + |
|
105 | + Filesystem::getLoader()->removeStorageWrapper('oc_trashbin'); |
|
106 | + |
|
107 | + if (self::$trashBinStatus) { |
|
108 | + Server::get(IAppManager::class)->enableApp('files_trashbin'); |
|
109 | + } |
|
110 | + |
|
111 | + parent::tearDownAfterClass(); |
|
112 | + } |
|
113 | + |
|
114 | + protected function setUp(): void { |
|
115 | + parent::setUp(); |
|
116 | + |
|
117 | + Server::get(IAppManager::class)->enableApp('files_trashbin'); |
|
118 | + $config = Server::get(IConfig::class); |
|
119 | + $mockConfig = $this->createMock(IConfig::class); |
|
120 | + $mockConfig |
|
121 | + ->method('getSystemValue') |
|
122 | + ->willReturnCallback(static function ($key, $default) use ($config) { |
|
123 | + if ($key === 'filesystem_check_changes') { |
|
124 | + return Watcher::CHECK_ONCE; |
|
125 | + } else { |
|
126 | + return $config->getSystemValue($key, $default); |
|
127 | + } |
|
128 | + }); |
|
129 | + $mockConfig |
|
130 | + ->method('getUserValue') |
|
131 | + ->willReturnCallback(static function ($userId, $appName, $key, $default = '') use ($config) { |
|
132 | + return $config->getUserValue($userId, $appName, $key, $default); |
|
133 | + }); |
|
134 | + $mockConfig |
|
135 | + ->method('getAppValue') |
|
136 | + ->willReturnCallback(static function ($appName, $key, $default = '') use ($config) { |
|
137 | + return $config->getAppValue($appName, $key, $default); |
|
138 | + }); |
|
139 | + $this->overwriteService(AllConfig::class, $mockConfig); |
|
140 | + |
|
141 | + $this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin'; |
|
142 | + $this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin'; |
|
143 | + $this->rootView = new View(); |
|
144 | + self::loginHelper(self::TEST_TRASHBIN_USER1); |
|
145 | + } |
|
146 | + |
|
147 | + protected function tearDown(): void { |
|
148 | + $this->restoreService(AllConfig::class); |
|
149 | + // disable trashbin to be able to properly clean up |
|
150 | + Server::get(IAppManager::class)->disableApp('files_trashbin'); |
|
151 | + |
|
152 | + $this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER1 . '/files'); |
|
153 | + $this->rootView->deleteAll('/' . self::TEST_TRASHBIN_USER2 . '/files'); |
|
154 | + $this->rootView->deleteAll($this->trashRoot1); |
|
155 | + $this->rootView->deleteAll($this->trashRoot2); |
|
156 | + |
|
157 | + // clear trash table |
|
158 | + $connection = Server::get(IDBConnection::class); |
|
159 | + $connection->executeUpdate('DELETE FROM `*PREFIX*files_trash`'); |
|
160 | + |
|
161 | + parent::tearDown(); |
|
162 | + } |
|
163 | + |
|
164 | + /** |
|
165 | + * test expiration of files older then the max storage time defined for the trash |
|
166 | + */ |
|
167 | + public function testExpireOldFiles(): void { |
|
168 | + |
|
169 | + /** @var ITimeFactory $time */ |
|
170 | + $time = Server::get(ITimeFactory::class); |
|
171 | + $currentTime = $time->getTime(); |
|
172 | + $expireAt = $currentTime - 2 * 24 * 60 * 60; |
|
173 | + $expiredDate = $currentTime - 3 * 24 * 60 * 60; |
|
174 | + |
|
175 | + // create some files |
|
176 | + Filesystem::file_put_contents('file1.txt', 'file1'); |
|
177 | + Filesystem::file_put_contents('file2.txt', 'file2'); |
|
178 | + Filesystem::file_put_contents('file3.txt', 'file3'); |
|
179 | + |
|
180 | + // delete them so that they end up in the trash bin |
|
181 | + Filesystem::unlink('file1.txt'); |
|
182 | + Filesystem::unlink('file2.txt'); |
|
183 | + Filesystem::unlink('file3.txt'); |
|
184 | + |
|
185 | + //make sure that files are in the trash bin |
|
186 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name'); |
|
187 | + $this->assertSame(3, count($filesInTrash)); |
|
188 | + |
|
189 | + // every second file will get a date in the past so that it will get expired |
|
190 | + $manipulatedList = $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate); |
|
191 | + |
|
192 | + $testClass = new TrashbinForTesting(); |
|
193 | + [$sizeOfDeletedFiles, $count] = $testClass->dummyDeleteExpiredFiles($manipulatedList, $expireAt); |
|
194 | + |
|
195 | + $this->assertSame(10, $sizeOfDeletedFiles); |
|
196 | + $this->assertSame(2, $count); |
|
197 | + |
|
198 | + // only file2.txt should be left |
|
199 | + $remainingFiles = array_slice($manipulatedList, $count); |
|
200 | + $this->assertSame(1, count($remainingFiles)); |
|
201 | + $remainingFile = reset($remainingFiles); |
|
202 | + // TODO: failing test |
|
203 | + #$this->assertSame('file2.txt', $remainingFile['name']); |
|
204 | + |
|
205 | + // check that file1.txt and file3.txt was really deleted |
|
206 | + $newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); |
|
207 | + $this->assertSame(1, count($newTrashContent)); |
|
208 | + $element = reset($newTrashContent); |
|
209 | + // TODO: failing test |
|
210 | + #$this->assertSame('file2.txt', $element['name']); |
|
211 | + } |
|
212 | + |
|
213 | + /** |
|
214 | + * test expiration of files older then the max storage time defined for the trash |
|
215 | + * in this test we delete a shared file and check if both trash bins, the one from |
|
216 | + * the owner of the file and the one from the user who deleted the file get expired |
|
217 | + * correctly |
|
218 | + */ |
|
219 | + public function testExpireOldFilesShared(): void { |
|
220 | + $currentTime = time(); |
|
221 | + $folder = 'trashTest-' . $currentTime . '/'; |
|
222 | + $expiredDate = $currentTime - 3 * 24 * 60 * 60; |
|
223 | + |
|
224 | + // create some files |
|
225 | + Filesystem::mkdir($folder); |
|
226 | + Filesystem::file_put_contents($folder . 'user1-1.txt', 'file1'); |
|
227 | + Filesystem::file_put_contents($folder . 'user1-2.txt', 'file2'); |
|
228 | + Filesystem::file_put_contents($folder . 'user1-3.txt', 'file3'); |
|
229 | + Filesystem::file_put_contents($folder . 'user1-4.txt', 'file4'); |
|
230 | + |
|
231 | + //share user1-4.txt with user2 |
|
232 | + $node = \OC::$server->getUserFolder(self::TEST_TRASHBIN_USER1)->get($folder); |
|
233 | + $share = Server::get(\OCP\Share\IManager::class)->newShare(); |
|
234 | + $share->setShareType(IShare::TYPE_USER) |
|
235 | + ->setNode($node) |
|
236 | + ->setSharedBy(self::TEST_TRASHBIN_USER1) |
|
237 | + ->setSharedWith(self::TEST_TRASHBIN_USER2) |
|
238 | + ->setPermissions(Constants::PERMISSION_ALL); |
|
239 | + $share = Server::get(\OCP\Share\IManager::class)->createShare($share); |
|
240 | + Server::get(\OCP\Share\IManager::class)->acceptShare($share, self::TEST_TRASHBIN_USER2); |
|
241 | + |
|
242 | + // delete them so that they end up in the trash bin |
|
243 | + Filesystem::unlink($folder . 'user1-1.txt'); |
|
244 | + Filesystem::unlink($folder . 'user1-2.txt'); |
|
245 | + Filesystem::unlink($folder . 'user1-3.txt'); |
|
246 | + |
|
247 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name'); |
|
248 | + $this->assertSame(3, count($filesInTrash)); |
|
249 | + |
|
250 | + // every second file will get a date in the past so that it will get expired |
|
251 | + $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate); |
|
252 | + |
|
253 | + // login as user2 |
|
254 | + self::loginHelper(self::TEST_TRASHBIN_USER2); |
|
255 | + |
|
256 | + $this->assertTrue(Filesystem::file_exists($folder . 'user1-4.txt')); |
|
257 | + |
|
258 | + // create some files |
|
259 | + Filesystem::file_put_contents('user2-1.txt', 'file1'); |
|
260 | + Filesystem::file_put_contents('user2-2.txt', 'file2'); |
|
261 | + |
|
262 | + // delete them so that they end up in the trash bin |
|
263 | + Filesystem::unlink('user2-1.txt'); |
|
264 | + Filesystem::unlink('user2-2.txt'); |
|
265 | + |
|
266 | + $filesInTrashUser2 = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2, 'name'); |
|
267 | + $this->assertSame(2, count($filesInTrashUser2)); |
|
268 | + |
|
269 | + // every second file will get a date in the past so that it will get expired |
|
270 | + $this->manipulateDeleteTime($filesInTrashUser2, $this->trashRoot2, $expiredDate); |
|
271 | + |
|
272 | + Filesystem::unlink($folder . 'user1-4.txt'); |
|
273 | + |
|
274 | + $this->runCommands(); |
|
275 | + |
|
276 | + $filesInTrashUser2AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2); |
|
277 | + |
|
278 | + // user2-1.txt should have been expired |
|
279 | + $this->verifyArray($filesInTrashUser2AfterDelete, ['user2-2.txt', 'user1-4.txt']); |
|
280 | + |
|
281 | + self::loginHelper(self::TEST_TRASHBIN_USER1); |
|
282 | + |
|
283 | + // user1-1.txt and user1-3.txt should have been expired |
|
284 | + $filesInTrashUser1AfterDelete = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); |
|
285 | + |
|
286 | + $this->verifyArray($filesInTrashUser1AfterDelete, ['user1-2.txt', 'user1-4.txt']); |
|
287 | + } |
|
288 | + |
|
289 | + /** |
|
290 | + * verify that the array contains the expected results |
|
291 | + * |
|
292 | + * @param FileInfo[] $result |
|
293 | + * @param string[] $expected |
|
294 | + */ |
|
295 | + private function verifyArray($result, $expected) { |
|
296 | + $this->assertSame(count($expected), count($result)); |
|
297 | + foreach ($expected as $expectedFile) { |
|
298 | + $found = false; |
|
299 | + foreach ($result as $fileInTrash) { |
|
300 | + if ($expectedFile === $fileInTrash['name']) { |
|
301 | + $found = true; |
|
302 | + break; |
|
303 | + } |
|
304 | + } |
|
305 | + if (!$found) { |
|
306 | + // if we didn't found the expected file, something went wrong |
|
307 | + $this->assertTrue(false, "can't find expected file '" . $expectedFile . "' in trash bin"); |
|
308 | + } |
|
309 | + } |
|
310 | + } |
|
311 | + |
|
312 | + /** |
|
313 | + * @param FileInfo[] $files |
|
314 | + * @param string $trashRoot |
|
315 | + * @param integer $expireDate |
|
316 | + */ |
|
317 | + private function manipulateDeleteTime($files, $trashRoot, $expireDate) { |
|
318 | + $counter = 0; |
|
319 | + foreach ($files as &$file) { |
|
320 | + // modify every second file |
|
321 | + $counter = ($counter + 1) % 2; |
|
322 | + if ($counter === 1) { |
|
323 | + $source = $trashRoot . '/files/' . $file['name'] . '.d' . $file['mtime']; |
|
324 | + $target = Filesystem::normalizePath($trashRoot . '/files/' . $file['name'] . '.d' . $expireDate); |
|
325 | + $this->rootView->rename($source, $target); |
|
326 | + $file['mtime'] = $expireDate; |
|
327 | + } |
|
328 | + } |
|
329 | + return \OCA\Files\Helper::sortFiles($files, 'mtime'); |
|
330 | + } |
|
331 | + |
|
332 | + |
|
333 | + /** |
|
334 | + * test expiration of old files in the trash bin until the max size |
|
335 | + * of the trash bin is met again |
|
336 | + */ |
|
337 | + public function testExpireOldFilesUtilLimitsAreMet(): void { |
|
338 | + |
|
339 | + // create some files |
|
340 | + Filesystem::file_put_contents('file1.txt', 'file1'); |
|
341 | + Filesystem::file_put_contents('file2.txt', 'file2'); |
|
342 | + Filesystem::file_put_contents('file3.txt', 'file3'); |
|
343 | + |
|
344 | + // delete them so that they end up in the trash bin |
|
345 | + Filesystem::unlink('file3.txt'); |
|
346 | + sleep(1); // make sure that every file has a unique mtime |
|
347 | + Filesystem::unlink('file2.txt'); |
|
348 | + sleep(1); // make sure that every file has a unique mtime |
|
349 | + Filesystem::unlink('file1.txt'); |
|
350 | + |
|
351 | + //make sure that files are in the trash bin |
|
352 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
353 | + $this->assertSame(3, count($filesInTrash)); |
|
354 | + |
|
355 | + $testClass = new TrashbinForTesting(); |
|
356 | + $sizeOfDeletedFiles = $testClass->dummyDeleteFiles($filesInTrash, -8); |
|
357 | + |
|
358 | + // the two oldest files (file3.txt and file2.txt) should be deleted |
|
359 | + $this->assertSame(10, $sizeOfDeletedFiles); |
|
360 | + |
|
361 | + $newTrashContent = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); |
|
362 | + $this->assertSame(1, count($newTrashContent)); |
|
363 | + $element = reset($newTrashContent); |
|
364 | + $this->assertSame('file1.txt', $element['name']); |
|
365 | + } |
|
366 | + |
|
367 | + /** |
|
368 | + * Test restoring a file |
|
369 | + */ |
|
370 | + public function testRestoreFileInRoot(): void { |
|
371 | + $userFolder = \OC::$server->getUserFolder(); |
|
372 | + $file = $userFolder->newFile('file1.txt'); |
|
373 | + $file->putContent('foo'); |
|
374 | + |
|
375 | + $this->assertTrue($userFolder->nodeExists('file1.txt')); |
|
376 | + |
|
377 | + $file->delete(); |
|
378 | + |
|
379 | + $this->assertFalse($userFolder->nodeExists('file1.txt')); |
|
380 | + |
|
381 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
382 | + $this->assertCount(1, $filesInTrash); |
|
383 | + |
|
384 | + /** @var FileInfo */ |
|
385 | + $trashedFile = $filesInTrash[0]; |
|
386 | + |
|
387 | + $this->assertTrue( |
|
388 | + Trashbin::restore( |
|
389 | + 'file1.txt.d' . $trashedFile->getMtime(), |
|
390 | + $trashedFile->getName(), |
|
391 | + $trashedFile->getMtime() |
|
392 | + ) |
|
393 | + ); |
|
394 | + |
|
395 | + $file = $userFolder->get('file1.txt'); |
|
396 | + $this->assertEquals('foo', $file->getContent()); |
|
397 | + } |
|
398 | + |
|
399 | + /** |
|
400 | + * Test restoring a file in subfolder |
|
401 | + */ |
|
402 | + public function testRestoreFileInSubfolder(): void { |
|
403 | + $userFolder = \OC::$server->getUserFolder(); |
|
404 | + $folder = $userFolder->newFolder('folder'); |
|
405 | + $file = $folder->newFile('file1.txt'); |
|
406 | + $file->putContent('foo'); |
|
407 | + |
|
408 | + $this->assertTrue($userFolder->nodeExists('folder/file1.txt')); |
|
409 | + |
|
410 | + $file->delete(); |
|
411 | + |
|
412 | + $this->assertFalse($userFolder->nodeExists('folder/file1.txt')); |
|
413 | + |
|
414 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
415 | + $this->assertCount(1, $filesInTrash); |
|
416 | + |
|
417 | + /** @var FileInfo */ |
|
418 | + $trashedFile = $filesInTrash[0]; |
|
419 | + |
|
420 | + $this->assertTrue( |
|
421 | + Trashbin::restore( |
|
422 | + 'file1.txt.d' . $trashedFile->getMtime(), |
|
423 | + $trashedFile->getName(), |
|
424 | + $trashedFile->getMtime() |
|
425 | + ) |
|
426 | + ); |
|
427 | + |
|
428 | + $file = $userFolder->get('folder/file1.txt'); |
|
429 | + $this->assertEquals('foo', $file->getContent()); |
|
430 | + } |
|
431 | + |
|
432 | + /** |
|
433 | + * Test restoring a folder |
|
434 | + */ |
|
435 | + public function testRestoreFolder(): void { |
|
436 | + $userFolder = \OC::$server->getUserFolder(); |
|
437 | + $folder = $userFolder->newFolder('folder'); |
|
438 | + $file = $folder->newFile('file1.txt'); |
|
439 | + $file->putContent('foo'); |
|
440 | + |
|
441 | + $this->assertTrue($userFolder->nodeExists('folder')); |
|
442 | + |
|
443 | + $folder->delete(); |
|
444 | + |
|
445 | + $this->assertFalse($userFolder->nodeExists('folder')); |
|
446 | + |
|
447 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
448 | + $this->assertCount(1, $filesInTrash); |
|
449 | 449 | |
450 | - /** @var FileInfo */ |
|
451 | - $trashedFolder = $filesInTrash[0]; |
|
450 | + /** @var FileInfo */ |
|
451 | + $trashedFolder = $filesInTrash[0]; |
|
452 | 452 | |
453 | - $this->assertTrue( |
|
454 | - Trashbin::restore( |
|
455 | - 'folder.d' . $trashedFolder->getMtime(), |
|
456 | - $trashedFolder->getName(), |
|
457 | - $trashedFolder->getMtime() |
|
458 | - ) |
|
459 | - ); |
|
453 | + $this->assertTrue( |
|
454 | + Trashbin::restore( |
|
455 | + 'folder.d' . $trashedFolder->getMtime(), |
|
456 | + $trashedFolder->getName(), |
|
457 | + $trashedFolder->getMtime() |
|
458 | + ) |
|
459 | + ); |
|
460 | 460 | |
461 | - $file = $userFolder->get('folder/file1.txt'); |
|
462 | - $this->assertEquals('foo', $file->getContent()); |
|
463 | - } |
|
464 | - |
|
465 | - /** |
|
466 | - * Test restoring a file from inside a trashed folder |
|
467 | - */ |
|
468 | - public function testRestoreFileFromTrashedSubfolder(): void { |
|
469 | - $userFolder = \OC::$server->getUserFolder(); |
|
470 | - $folder = $userFolder->newFolder('folder'); |
|
471 | - $file = $folder->newFile('file1.txt'); |
|
472 | - $file->putContent('foo'); |
|
461 | + $file = $userFolder->get('folder/file1.txt'); |
|
462 | + $this->assertEquals('foo', $file->getContent()); |
|
463 | + } |
|
464 | + |
|
465 | + /** |
|
466 | + * Test restoring a file from inside a trashed folder |
|
467 | + */ |
|
468 | + public function testRestoreFileFromTrashedSubfolder(): void { |
|
469 | + $userFolder = \OC::$server->getUserFolder(); |
|
470 | + $folder = $userFolder->newFolder('folder'); |
|
471 | + $file = $folder->newFile('file1.txt'); |
|
472 | + $file->putContent('foo'); |
|
473 | 473 | |
474 | - $this->assertTrue($userFolder->nodeExists('folder')); |
|
474 | + $this->assertTrue($userFolder->nodeExists('folder')); |
|
475 | 475 | |
476 | - $folder->delete(); |
|
477 | - |
|
478 | - $this->assertFalse($userFolder->nodeExists('folder')); |
|
479 | - |
|
480 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
481 | - $this->assertCount(1, $filesInTrash); |
|
482 | - |
|
483 | - /** @var FileInfo */ |
|
484 | - $trashedFile = $filesInTrash[0]; |
|
485 | - |
|
486 | - $this->assertTrue( |
|
487 | - Trashbin::restore( |
|
488 | - 'folder.d' . $trashedFile->getMtime() . '/file1.txt', |
|
489 | - 'file1.txt', |
|
490 | - $trashedFile->getMtime() |
|
491 | - ) |
|
492 | - ); |
|
476 | + $folder->delete(); |
|
477 | + |
|
478 | + $this->assertFalse($userFolder->nodeExists('folder')); |
|
479 | + |
|
480 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
481 | + $this->assertCount(1, $filesInTrash); |
|
482 | + |
|
483 | + /** @var FileInfo */ |
|
484 | + $trashedFile = $filesInTrash[0]; |
|
485 | + |
|
486 | + $this->assertTrue( |
|
487 | + Trashbin::restore( |
|
488 | + 'folder.d' . $trashedFile->getMtime() . '/file1.txt', |
|
489 | + 'file1.txt', |
|
490 | + $trashedFile->getMtime() |
|
491 | + ) |
|
492 | + ); |
|
493 | 493 | |
494 | - $file = $userFolder->get('file1.txt'); |
|
495 | - $this->assertEquals('foo', $file->getContent()); |
|
496 | - } |
|
494 | + $file = $userFolder->get('file1.txt'); |
|
495 | + $this->assertEquals('foo', $file->getContent()); |
|
496 | + } |
|
497 | 497 | |
498 | - /** |
|
499 | - * Test restoring a file whenever the source folder was removed. |
|
500 | - * The file should then land in the root. |
|
501 | - */ |
|
502 | - public function testRestoreFileWithMissingSourceFolder(): void { |
|
503 | - $userFolder = \OC::$server->getUserFolder(); |
|
504 | - $folder = $userFolder->newFolder('folder'); |
|
505 | - $file = $folder->newFile('file1.txt'); |
|
506 | - $file->putContent('foo'); |
|
498 | + /** |
|
499 | + * Test restoring a file whenever the source folder was removed. |
|
500 | + * The file should then land in the root. |
|
501 | + */ |
|
502 | + public function testRestoreFileWithMissingSourceFolder(): void { |
|
503 | + $userFolder = \OC::$server->getUserFolder(); |
|
504 | + $folder = $userFolder->newFolder('folder'); |
|
505 | + $file = $folder->newFile('file1.txt'); |
|
506 | + $file->putContent('foo'); |
|
507 | 507 | |
508 | - $this->assertTrue($userFolder->nodeExists('folder/file1.txt')); |
|
508 | + $this->assertTrue($userFolder->nodeExists('folder/file1.txt')); |
|
509 | 509 | |
510 | - $file->delete(); |
|
510 | + $file->delete(); |
|
511 | 511 | |
512 | - $this->assertFalse($userFolder->nodeExists('folder/file1.txt')); |
|
513 | - |
|
514 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
515 | - $this->assertCount(1, $filesInTrash); |
|
516 | - |
|
517 | - /** @var FileInfo */ |
|
518 | - $trashedFile = $filesInTrash[0]; |
|
519 | - |
|
520 | - // delete source folder |
|
521 | - $folder->delete(); |
|
522 | - |
|
523 | - $this->assertTrue( |
|
524 | - Trashbin::restore( |
|
525 | - 'file1.txt.d' . $trashedFile->getMtime(), |
|
526 | - $trashedFile->getName(), |
|
527 | - $trashedFile->getMtime() |
|
528 | - ) |
|
529 | - ); |
|
530 | - |
|
531 | - $file = $userFolder->get('file1.txt'); |
|
532 | - $this->assertEquals('foo', $file->getContent()); |
|
533 | - } |
|
534 | - |
|
535 | - /** |
|
536 | - * Test restoring a file in the root folder whenever there is another file |
|
537 | - * with the same name in the root folder |
|
538 | - */ |
|
539 | - public function testRestoreFileDoesNotOverwriteExistingInRoot(): void { |
|
540 | - $userFolder = \OC::$server->getUserFolder(); |
|
541 | - $file = $userFolder->newFile('file1.txt'); |
|
542 | - $file->putContent('foo'); |
|
543 | - |
|
544 | - $this->assertTrue($userFolder->nodeExists('file1.txt')); |
|
545 | - |
|
546 | - $file->delete(); |
|
547 | - |
|
548 | - $this->assertFalse($userFolder->nodeExists('file1.txt')); |
|
549 | - |
|
550 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
551 | - $this->assertCount(1, $filesInTrash); |
|
552 | - |
|
553 | - /** @var FileInfo */ |
|
554 | - $trashedFile = $filesInTrash[0]; |
|
555 | - |
|
556 | - // create another file |
|
557 | - $file = $userFolder->newFile('file1.txt'); |
|
558 | - $file->putContent('bar'); |
|
559 | - |
|
560 | - $this->assertTrue( |
|
561 | - Trashbin::restore( |
|
562 | - 'file1.txt.d' . $trashedFile->getMtime(), |
|
563 | - $trashedFile->getName(), |
|
564 | - $trashedFile->getMtime() |
|
565 | - ) |
|
566 | - ); |
|
567 | - |
|
568 | - $anotherFile = $userFolder->get('file1.txt'); |
|
569 | - $this->assertEquals('bar', $anotherFile->getContent()); |
|
570 | - |
|
571 | - $restoredFile = $userFolder->get('file1 (restored).txt'); |
|
572 | - $this->assertEquals('foo', $restoredFile->getContent()); |
|
573 | - } |
|
574 | - |
|
575 | - /** |
|
576 | - * Test restoring a file whenever there is another file |
|
577 | - * with the same name in the source folder |
|
578 | - */ |
|
579 | - public function testRestoreFileDoesNotOverwriteExistingInSubfolder(): void { |
|
580 | - $userFolder = \OC::$server->getUserFolder(); |
|
581 | - $folder = $userFolder->newFolder('folder'); |
|
582 | - $file = $folder->newFile('file1.txt'); |
|
583 | - $file->putContent('foo'); |
|
584 | - |
|
585 | - $this->assertTrue($userFolder->nodeExists('folder/file1.txt')); |
|
586 | - |
|
587 | - $file->delete(); |
|
588 | - |
|
589 | - $this->assertFalse($userFolder->nodeExists('folder/file1.txt')); |
|
590 | - |
|
591 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
592 | - $this->assertCount(1, $filesInTrash); |
|
593 | - |
|
594 | - /** @var FileInfo */ |
|
595 | - $trashedFile = $filesInTrash[0]; |
|
596 | - |
|
597 | - // create another file |
|
598 | - $file = $folder->newFile('file1.txt'); |
|
599 | - $file->putContent('bar'); |
|
600 | - |
|
601 | - $this->assertTrue( |
|
602 | - Trashbin::restore( |
|
603 | - 'file1.txt.d' . $trashedFile->getMtime(), |
|
604 | - $trashedFile->getName(), |
|
605 | - $trashedFile->getMtime() |
|
606 | - ) |
|
607 | - ); |
|
608 | - |
|
609 | - $anotherFile = $userFolder->get('folder/file1.txt'); |
|
610 | - $this->assertEquals('bar', $anotherFile->getContent()); |
|
611 | - |
|
612 | - $restoredFile = $userFolder->get('folder/file1 (restored).txt'); |
|
613 | - $this->assertEquals('foo', $restoredFile->getContent()); |
|
614 | - } |
|
615 | - |
|
616 | - /** |
|
617 | - * Test restoring a non-existing file from trashbin, returns false |
|
618 | - */ |
|
619 | - public function testRestoreUnexistingFile(): void { |
|
620 | - $this->assertFalse( |
|
621 | - Trashbin::restore( |
|
622 | - 'unexist.txt.d123456', |
|
623 | - 'unexist.txt', |
|
624 | - '123456' |
|
625 | - ) |
|
626 | - ); |
|
627 | - } |
|
628 | - |
|
629 | - /** |
|
630 | - * Test restoring a file into a read-only folder, will restore |
|
631 | - * the file to root instead |
|
632 | - */ |
|
633 | - public function testRestoreFileIntoReadOnlySourceFolder(): void { |
|
634 | - $userFolder = \OC::$server->getUserFolder(); |
|
635 | - $folder = $userFolder->newFolder('folder'); |
|
636 | - $file = $folder->newFile('file1.txt'); |
|
637 | - $file->putContent('foo'); |
|
638 | - |
|
639 | - $this->assertTrue($userFolder->nodeExists('folder/file1.txt')); |
|
640 | - |
|
641 | - $file->delete(); |
|
642 | - |
|
643 | - $this->assertFalse($userFolder->nodeExists('folder/file1.txt')); |
|
644 | - |
|
645 | - $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
646 | - $this->assertCount(1, $filesInTrash); |
|
647 | - |
|
648 | - /** @var FileInfo */ |
|
649 | - $trashedFile = $filesInTrash[0]; |
|
650 | - |
|
651 | - // delete source folder |
|
652 | - [$storage, $internalPath] = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder'); |
|
653 | - if ($storage instanceof Local) { |
|
654 | - $folderAbsPath = $storage->getSourcePath($internalPath); |
|
655 | - // make folder read-only |
|
656 | - chmod($folderAbsPath, 0555); |
|
657 | - |
|
658 | - $this->assertTrue( |
|
659 | - Trashbin::restore( |
|
660 | - 'file1.txt.d' . $trashedFile->getMtime(), |
|
661 | - $trashedFile->getName(), |
|
662 | - $trashedFile->getMtime() |
|
663 | - ) |
|
664 | - ); |
|
665 | - |
|
666 | - $file = $userFolder->get('file1.txt'); |
|
667 | - $this->assertEquals('foo', $file->getContent()); |
|
668 | - |
|
669 | - chmod($folderAbsPath, 0755); |
|
670 | - } |
|
671 | - } |
|
672 | - |
|
673 | - /** |
|
674 | - * @param string $user |
|
675 | - * @param bool $create |
|
676 | - */ |
|
677 | - public static function loginHelper($user, $create = false) { |
|
678 | - if ($create) { |
|
679 | - try { |
|
680 | - Server::get(IUserManager::class)->createUser($user, $user); |
|
681 | - } catch (\Exception $e) { // catch username is already being used from previous aborted runs |
|
682 | - } |
|
683 | - } |
|
684 | - |
|
685 | - \OC_Util::tearDownFS(); |
|
686 | - \OC_User::setUserId(''); |
|
687 | - Filesystem::tearDown(); |
|
688 | - \OC_User::setUserId($user); |
|
689 | - \OC_Util::setupFS($user); |
|
690 | - \OC::$server->getUserFolder($user); |
|
691 | - } |
|
512 | + $this->assertFalse($userFolder->nodeExists('folder/file1.txt')); |
|
513 | + |
|
514 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
515 | + $this->assertCount(1, $filesInTrash); |
|
516 | + |
|
517 | + /** @var FileInfo */ |
|
518 | + $trashedFile = $filesInTrash[0]; |
|
519 | + |
|
520 | + // delete source folder |
|
521 | + $folder->delete(); |
|
522 | + |
|
523 | + $this->assertTrue( |
|
524 | + Trashbin::restore( |
|
525 | + 'file1.txt.d' . $trashedFile->getMtime(), |
|
526 | + $trashedFile->getName(), |
|
527 | + $trashedFile->getMtime() |
|
528 | + ) |
|
529 | + ); |
|
530 | + |
|
531 | + $file = $userFolder->get('file1.txt'); |
|
532 | + $this->assertEquals('foo', $file->getContent()); |
|
533 | + } |
|
534 | + |
|
535 | + /** |
|
536 | + * Test restoring a file in the root folder whenever there is another file |
|
537 | + * with the same name in the root folder |
|
538 | + */ |
|
539 | + public function testRestoreFileDoesNotOverwriteExistingInRoot(): void { |
|
540 | + $userFolder = \OC::$server->getUserFolder(); |
|
541 | + $file = $userFolder->newFile('file1.txt'); |
|
542 | + $file->putContent('foo'); |
|
543 | + |
|
544 | + $this->assertTrue($userFolder->nodeExists('file1.txt')); |
|
545 | + |
|
546 | + $file->delete(); |
|
547 | + |
|
548 | + $this->assertFalse($userFolder->nodeExists('file1.txt')); |
|
549 | + |
|
550 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
551 | + $this->assertCount(1, $filesInTrash); |
|
552 | + |
|
553 | + /** @var FileInfo */ |
|
554 | + $trashedFile = $filesInTrash[0]; |
|
555 | + |
|
556 | + // create another file |
|
557 | + $file = $userFolder->newFile('file1.txt'); |
|
558 | + $file->putContent('bar'); |
|
559 | + |
|
560 | + $this->assertTrue( |
|
561 | + Trashbin::restore( |
|
562 | + 'file1.txt.d' . $trashedFile->getMtime(), |
|
563 | + $trashedFile->getName(), |
|
564 | + $trashedFile->getMtime() |
|
565 | + ) |
|
566 | + ); |
|
567 | + |
|
568 | + $anotherFile = $userFolder->get('file1.txt'); |
|
569 | + $this->assertEquals('bar', $anotherFile->getContent()); |
|
570 | + |
|
571 | + $restoredFile = $userFolder->get('file1 (restored).txt'); |
|
572 | + $this->assertEquals('foo', $restoredFile->getContent()); |
|
573 | + } |
|
574 | + |
|
575 | + /** |
|
576 | + * Test restoring a file whenever there is another file |
|
577 | + * with the same name in the source folder |
|
578 | + */ |
|
579 | + public function testRestoreFileDoesNotOverwriteExistingInSubfolder(): void { |
|
580 | + $userFolder = \OC::$server->getUserFolder(); |
|
581 | + $folder = $userFolder->newFolder('folder'); |
|
582 | + $file = $folder->newFile('file1.txt'); |
|
583 | + $file->putContent('foo'); |
|
584 | + |
|
585 | + $this->assertTrue($userFolder->nodeExists('folder/file1.txt')); |
|
586 | + |
|
587 | + $file->delete(); |
|
588 | + |
|
589 | + $this->assertFalse($userFolder->nodeExists('folder/file1.txt')); |
|
590 | + |
|
591 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
592 | + $this->assertCount(1, $filesInTrash); |
|
593 | + |
|
594 | + /** @var FileInfo */ |
|
595 | + $trashedFile = $filesInTrash[0]; |
|
596 | + |
|
597 | + // create another file |
|
598 | + $file = $folder->newFile('file1.txt'); |
|
599 | + $file->putContent('bar'); |
|
600 | + |
|
601 | + $this->assertTrue( |
|
602 | + Trashbin::restore( |
|
603 | + 'file1.txt.d' . $trashedFile->getMtime(), |
|
604 | + $trashedFile->getName(), |
|
605 | + $trashedFile->getMtime() |
|
606 | + ) |
|
607 | + ); |
|
608 | + |
|
609 | + $anotherFile = $userFolder->get('folder/file1.txt'); |
|
610 | + $this->assertEquals('bar', $anotherFile->getContent()); |
|
611 | + |
|
612 | + $restoredFile = $userFolder->get('folder/file1 (restored).txt'); |
|
613 | + $this->assertEquals('foo', $restoredFile->getContent()); |
|
614 | + } |
|
615 | + |
|
616 | + /** |
|
617 | + * Test restoring a non-existing file from trashbin, returns false |
|
618 | + */ |
|
619 | + public function testRestoreUnexistingFile(): void { |
|
620 | + $this->assertFalse( |
|
621 | + Trashbin::restore( |
|
622 | + 'unexist.txt.d123456', |
|
623 | + 'unexist.txt', |
|
624 | + '123456' |
|
625 | + ) |
|
626 | + ); |
|
627 | + } |
|
628 | + |
|
629 | + /** |
|
630 | + * Test restoring a file into a read-only folder, will restore |
|
631 | + * the file to root instead |
|
632 | + */ |
|
633 | + public function testRestoreFileIntoReadOnlySourceFolder(): void { |
|
634 | + $userFolder = \OC::$server->getUserFolder(); |
|
635 | + $folder = $userFolder->newFolder('folder'); |
|
636 | + $file = $folder->newFile('file1.txt'); |
|
637 | + $file->putContent('foo'); |
|
638 | + |
|
639 | + $this->assertTrue($userFolder->nodeExists('folder/file1.txt')); |
|
640 | + |
|
641 | + $file->delete(); |
|
642 | + |
|
643 | + $this->assertFalse($userFolder->nodeExists('folder/file1.txt')); |
|
644 | + |
|
645 | + $filesInTrash = Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); |
|
646 | + $this->assertCount(1, $filesInTrash); |
|
647 | + |
|
648 | + /** @var FileInfo */ |
|
649 | + $trashedFile = $filesInTrash[0]; |
|
650 | + |
|
651 | + // delete source folder |
|
652 | + [$storage, $internalPath] = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder'); |
|
653 | + if ($storage instanceof Local) { |
|
654 | + $folderAbsPath = $storage->getSourcePath($internalPath); |
|
655 | + // make folder read-only |
|
656 | + chmod($folderAbsPath, 0555); |
|
657 | + |
|
658 | + $this->assertTrue( |
|
659 | + Trashbin::restore( |
|
660 | + 'file1.txt.d' . $trashedFile->getMtime(), |
|
661 | + $trashedFile->getName(), |
|
662 | + $trashedFile->getMtime() |
|
663 | + ) |
|
664 | + ); |
|
665 | + |
|
666 | + $file = $userFolder->get('file1.txt'); |
|
667 | + $this->assertEquals('foo', $file->getContent()); |
|
668 | + |
|
669 | + chmod($folderAbsPath, 0755); |
|
670 | + } |
|
671 | + } |
|
672 | + |
|
673 | + /** |
|
674 | + * @param string $user |
|
675 | + * @param bool $create |
|
676 | + */ |
|
677 | + public static function loginHelper($user, $create = false) { |
|
678 | + if ($create) { |
|
679 | + try { |
|
680 | + Server::get(IUserManager::class)->createUser($user, $user); |
|
681 | + } catch (\Exception $e) { // catch username is already being used from previous aborted runs |
|
682 | + } |
|
683 | + } |
|
684 | + |
|
685 | + \OC_Util::tearDownFS(); |
|
686 | + \OC_User::setUserId(''); |
|
687 | + Filesystem::tearDown(); |
|
688 | + \OC_User::setUserId($user); |
|
689 | + \OC_Util::setupFS($user); |
|
690 | + \OC::$server->getUserFolder($user); |
|
691 | + } |
|
692 | 692 | } |
693 | 693 | |
694 | 694 | |
695 | 695 | // just a dummy class to make protected methods available for testing |
696 | 696 | class TrashbinForTesting extends Trashbin { |
697 | 697 | |
698 | - /** |
|
699 | - * @param FileInfo[] $files |
|
700 | - * @param integer $limit |
|
701 | - */ |
|
702 | - public function dummyDeleteExpiredFiles($files) { |
|
703 | - // dummy value for $retention_obligation because it is not needed here |
|
704 | - return parent::deleteExpiredFiles($files, TrashbinTest::TEST_TRASHBIN_USER1); |
|
705 | - } |
|
706 | - |
|
707 | - /** |
|
708 | - * @param FileInfo[] $files |
|
709 | - * @param integer $availableSpace |
|
710 | - */ |
|
711 | - public function dummyDeleteFiles($files, $availableSpace) { |
|
712 | - return parent::deleteFiles($files, TrashbinTest::TEST_TRASHBIN_USER1, $availableSpace); |
|
713 | - } |
|
698 | + /** |
|
699 | + * @param FileInfo[] $files |
|
700 | + * @param integer $limit |
|
701 | + */ |
|
702 | + public function dummyDeleteExpiredFiles($files) { |
|
703 | + // dummy value for $retention_obligation because it is not needed here |
|
704 | + return parent::deleteExpiredFiles($files, TrashbinTest::TEST_TRASHBIN_USER1); |
|
705 | + } |
|
706 | + |
|
707 | + /** |
|
708 | + * @param FileInfo[] $files |
|
709 | + * @param integer $availableSpace |
|
710 | + */ |
|
711 | + public function dummyDeleteFiles($files, $availableSpace) { |
|
712 | + return parent::deleteFiles($files, TrashbinTest::TEST_TRASHBIN_USER1, $availableSpace); |
|
713 | + } |
|
714 | 714 | } |
@@ -20,64 +20,64 @@ |
||
20 | 20 | * @psalm-import-type CalendarInfo from CalDavBackend |
21 | 21 | */ |
22 | 22 | class CalendarShareUpdatedEvent extends Event { |
23 | - /** |
|
24 | - * CalendarShareUpdatedEvent constructor. |
|
25 | - * |
|
26 | - * @param int $calendarId |
|
27 | - * @psalm-param CalendarInfo $calendarData |
|
28 | - * @param array $calendarData |
|
29 | - * @param list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}> $oldShares |
|
30 | - * @param list<array{href: string, commonName: string, readOnly: bool}> $added |
|
31 | - * @param list<string> $removed |
|
32 | - * @since 20.0.0 |
|
33 | - */ |
|
34 | - public function __construct( |
|
35 | - private int $calendarId, |
|
36 | - private array $calendarData, |
|
37 | - private array $oldShares, |
|
38 | - private array $added, |
|
39 | - private array $removed, |
|
40 | - ) { |
|
41 | - parent::__construct(); |
|
42 | - } |
|
23 | + /** |
|
24 | + * CalendarShareUpdatedEvent constructor. |
|
25 | + * |
|
26 | + * @param int $calendarId |
|
27 | + * @psalm-param CalendarInfo $calendarData |
|
28 | + * @param array $calendarData |
|
29 | + * @param list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}> $oldShares |
|
30 | + * @param list<array{href: string, commonName: string, readOnly: bool}> $added |
|
31 | + * @param list<string> $removed |
|
32 | + * @since 20.0.0 |
|
33 | + */ |
|
34 | + public function __construct( |
|
35 | + private int $calendarId, |
|
36 | + private array $calendarData, |
|
37 | + private array $oldShares, |
|
38 | + private array $added, |
|
39 | + private array $removed, |
|
40 | + ) { |
|
41 | + parent::__construct(); |
|
42 | + } |
|
43 | 43 | |
44 | - /** |
|
45 | - * @since 20.0.0 |
|
46 | - */ |
|
47 | - public function getCalendarId(): int { |
|
48 | - return $this->calendarId; |
|
49 | - } |
|
44 | + /** |
|
45 | + * @since 20.0.0 |
|
46 | + */ |
|
47 | + public function getCalendarId(): int { |
|
48 | + return $this->calendarId; |
|
49 | + } |
|
50 | 50 | |
51 | - /** |
|
52 | - * @psalm-return CalendarInfo |
|
53 | - * @return array |
|
54 | - * @since 20.0.0 |
|
55 | - */ |
|
56 | - public function getCalendarData(): array { |
|
57 | - return $this->calendarData; |
|
58 | - } |
|
51 | + /** |
|
52 | + * @psalm-return CalendarInfo |
|
53 | + * @return array |
|
54 | + * @since 20.0.0 |
|
55 | + */ |
|
56 | + public function getCalendarData(): array { |
|
57 | + return $this->calendarData; |
|
58 | + } |
|
59 | 59 | |
60 | - /** |
|
61 | - * @return list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}> |
|
62 | - * @since 20.0.0 |
|
63 | - */ |
|
64 | - public function getOldShares(): array { |
|
65 | - return $this->oldShares; |
|
66 | - } |
|
60 | + /** |
|
61 | + * @return list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}> |
|
62 | + * @since 20.0.0 |
|
63 | + */ |
|
64 | + public function getOldShares(): array { |
|
65 | + return $this->oldShares; |
|
66 | + } |
|
67 | 67 | |
68 | - /** |
|
69 | - * @return list<array{href: string, commonName: string, readOnly: bool}> |
|
70 | - * @since 20.0.0 |
|
71 | - */ |
|
72 | - public function getAdded(): array { |
|
73 | - return $this->added; |
|
74 | - } |
|
68 | + /** |
|
69 | + * @return list<array{href: string, commonName: string, readOnly: bool}> |
|
70 | + * @since 20.0.0 |
|
71 | + */ |
|
72 | + public function getAdded(): array { |
|
73 | + return $this->added; |
|
74 | + } |
|
75 | 75 | |
76 | - /** |
|
77 | - * @return list<string> |
|
78 | - * @since 20.0.0 |
|
79 | - */ |
|
80 | - public function getRemoved(): array { |
|
81 | - return $this->removed; |
|
82 | - } |
|
76 | + /** |
|
77 | + * @return list<string> |
|
78 | + * @since 20.0.0 |
|
79 | + */ |
|
80 | + public function getRemoved(): array { |
|
81 | + return $this->removed; |
|
82 | + } |
|
83 | 83 | } |