@@ -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 | } |