@@ -31,126 +31,126 @@ |
||
| 31 | 31 | |
| 32 | 32 | /** @template-implements IEventListener<Event> */ |
| 33 | 33 | class VersionStorageMoveListener implements IEventListener { |
| 34 | - /** @var File[] */ |
|
| 35 | - private array $movedNodes = []; |
|
| 36 | - |
|
| 37 | - public function __construct( |
|
| 38 | - private IVersionManager $versionManager, |
|
| 39 | - private IUserSession $userSession, |
|
| 40 | - private LoggerInterface $logger, |
|
| 41 | - ) { |
|
| 42 | - } |
|
| 43 | - |
|
| 44 | - /** |
|
| 45 | - * @abstract Moves version across storages if necessary. |
|
| 46 | - * @throws Exception No user in session |
|
| 47 | - */ |
|
| 48 | - public function handle(Event $event): void { |
|
| 49 | - if (!($event instanceof AbstractNodesEvent)) { |
|
| 50 | - return; |
|
| 51 | - } |
|
| 52 | - |
|
| 53 | - $source = $event->getSource(); |
|
| 54 | - $target = $event->getTarget(); |
|
| 55 | - |
|
| 56 | - $sourceStorage = $this->getNodeStorage($source); |
|
| 57 | - $targetStorage = $this->getNodeStorage($target); |
|
| 58 | - |
|
| 59 | - $sourceBackend = $this->versionManager->getBackendForStorage($sourceStorage); |
|
| 60 | - $targetBackend = $this->versionManager->getBackendForStorage($targetStorage); |
|
| 61 | - |
|
| 62 | - // If same backend, nothing to do. |
|
| 63 | - if ($sourceBackend === $targetBackend) { |
|
| 64 | - return; |
|
| 65 | - } |
|
| 66 | - |
|
| 67 | - $user = $this->userSession->getUser() ?? $source->getOwner(); |
|
| 68 | - |
|
| 69 | - if ($user === null) { |
|
| 70 | - throw new Exception('Cannot move versions across storages without a user.'); |
|
| 71 | - } |
|
| 72 | - |
|
| 73 | - if ($event instanceof BeforeNodeRenamedEvent) { |
|
| 74 | - $this->recursivelyPrepareMove($source); |
|
| 75 | - } elseif ($event instanceof NodeRenamedEvent || $event instanceof NodeCopiedEvent) { |
|
| 76 | - $this->recursivelyHandleMoveOrCopy($event, $user, $source, $target, $sourceBackend, $targetBackend); |
|
| 77 | - } |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - /** |
|
| 81 | - * Store all sub files in this->movedNodes so their info can be used after the operation. |
|
| 82 | - */ |
|
| 83 | - private function recursivelyPrepareMove(Node $source): void { |
|
| 84 | - if ($source instanceof File) { |
|
| 85 | - $this->movedNodes[$source->getId()] = $source; |
|
| 86 | - } elseif ($source instanceof Folder) { |
|
| 87 | - foreach ($source->getDirectoryListing() as $child) { |
|
| 88 | - $this->recursivelyPrepareMove($child); |
|
| 89 | - } |
|
| 90 | - } |
|
| 91 | - } |
|
| 92 | - |
|
| 93 | - /** |
|
| 94 | - * Call handleMoveOrCopy on each sub files |
|
| 95 | - * @param NodeRenamedEvent|NodeCopiedEvent $event |
|
| 96 | - */ |
|
| 97 | - private function recursivelyHandleMoveOrCopy(Event $event, IUser $user, ?Node $source, Node $target, IVersionBackend $sourceBackend, IVersionBackend $targetBackend): void { |
|
| 98 | - if ($target instanceof File) { |
|
| 99 | - if ($event instanceof NodeRenamedEvent) { |
|
| 100 | - $source = $this->movedNodes[$target->getId()]; |
|
| 101 | - } |
|
| 102 | - |
|
| 103 | - if ($source === null) { |
|
| 104 | - $this->logger->warning( |
|
| 105 | - 'Failed to retrieve source file during version move/copy.', |
|
| 106 | - [ |
|
| 107 | - 'eventClass' => get_class($event), |
|
| 108 | - 'targetPath' => $target->getPath(), |
|
| 109 | - 'targetId' => $target->getId(), |
|
| 110 | - 'movedNodesKeys' => array_keys($this->movedNodes), |
|
| 111 | - 'sourceBackendClass' => get_class($sourceBackend), |
|
| 112 | - 'targetBackendClass' => get_class($targetBackend), |
|
| 113 | - ] |
|
| 114 | - ); |
|
| 115 | - return; |
|
| 116 | - } |
|
| 117 | - |
|
| 118 | - $this->handleMoveOrCopy($event, $user, $source, $target, $sourceBackend, $targetBackend); |
|
| 119 | - } elseif ($target instanceof Folder) { |
|
| 120 | - /** @var Folder $source */ |
|
| 121 | - foreach ($target->getDirectoryListing() as $targetChild) { |
|
| 122 | - if ($event instanceof NodeCopiedEvent) { |
|
| 123 | - $sourceChild = $source->get($targetChild->getName()); |
|
| 124 | - } else { |
|
| 125 | - $sourceChild = null; |
|
| 126 | - } |
|
| 127 | - |
|
| 128 | - $this->recursivelyHandleMoveOrCopy($event, $user, $sourceChild, $targetChild, $sourceBackend, $targetBackend); |
|
| 129 | - } |
|
| 130 | - } |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - /** |
|
| 134 | - * Called only during NodeRenamedEvent or NodeCopiedEvent |
|
| 135 | - * Will send the source node versions to the new backend, and then delete them from the old backend. |
|
| 136 | - * @param NodeRenamedEvent|NodeCopiedEvent $event |
|
| 137 | - */ |
|
| 138 | - private function handleMoveOrCopy(Event $event, IUser $user, File $source, File $target, IVersionBackend $sourceBackend, IVersionBackend $targetBackend): void { |
|
| 139 | - if ($targetBackend instanceof IVersionsImporterBackend) { |
|
| 140 | - $versions = $sourceBackend->getVersionsForFile($user, $source); |
|
| 141 | - $targetBackend->importVersionsForFile($user, $source, $target, $versions); |
|
| 142 | - } |
|
| 143 | - |
|
| 144 | - if ($event instanceof NodeRenamedEvent && $sourceBackend instanceof IVersionsImporterBackend) { |
|
| 145 | - $sourceBackend->clearVersionsForFile($user, $source, $target); |
|
| 146 | - } |
|
| 147 | - } |
|
| 148 | - |
|
| 149 | - private function getNodeStorage(Node $node): IStorage { |
|
| 150 | - if ($node instanceof NonExistingFile || $node instanceof NonExistingFolder) { |
|
| 151 | - return $node->getParent()->getStorage(); |
|
| 152 | - } else { |
|
| 153 | - return $node->getStorage(); |
|
| 154 | - } |
|
| 155 | - } |
|
| 34 | + /** @var File[] */ |
|
| 35 | + private array $movedNodes = []; |
|
| 36 | + |
|
| 37 | + public function __construct( |
|
| 38 | + private IVersionManager $versionManager, |
|
| 39 | + private IUserSession $userSession, |
|
| 40 | + private LoggerInterface $logger, |
|
| 41 | + ) { |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + /** |
|
| 45 | + * @abstract Moves version across storages if necessary. |
|
| 46 | + * @throws Exception No user in session |
|
| 47 | + */ |
|
| 48 | + public function handle(Event $event): void { |
|
| 49 | + if (!($event instanceof AbstractNodesEvent)) { |
|
| 50 | + return; |
|
| 51 | + } |
|
| 52 | + |
|
| 53 | + $source = $event->getSource(); |
|
| 54 | + $target = $event->getTarget(); |
|
| 55 | + |
|
| 56 | + $sourceStorage = $this->getNodeStorage($source); |
|
| 57 | + $targetStorage = $this->getNodeStorage($target); |
|
| 58 | + |
|
| 59 | + $sourceBackend = $this->versionManager->getBackendForStorage($sourceStorage); |
|
| 60 | + $targetBackend = $this->versionManager->getBackendForStorage($targetStorage); |
|
| 61 | + |
|
| 62 | + // If same backend, nothing to do. |
|
| 63 | + if ($sourceBackend === $targetBackend) { |
|
| 64 | + return; |
|
| 65 | + } |
|
| 66 | + |
|
| 67 | + $user = $this->userSession->getUser() ?? $source->getOwner(); |
|
| 68 | + |
|
| 69 | + if ($user === null) { |
|
| 70 | + throw new Exception('Cannot move versions across storages without a user.'); |
|
| 71 | + } |
|
| 72 | + |
|
| 73 | + if ($event instanceof BeforeNodeRenamedEvent) { |
|
| 74 | + $this->recursivelyPrepareMove($source); |
|
| 75 | + } elseif ($event instanceof NodeRenamedEvent || $event instanceof NodeCopiedEvent) { |
|
| 76 | + $this->recursivelyHandleMoveOrCopy($event, $user, $source, $target, $sourceBackend, $targetBackend); |
|
| 77 | + } |
|
| 78 | + } |
|
| 79 | + |
|
| 80 | + /** |
|
| 81 | + * Store all sub files in this->movedNodes so their info can be used after the operation. |
|
| 82 | + */ |
|
| 83 | + private function recursivelyPrepareMove(Node $source): void { |
|
| 84 | + if ($source instanceof File) { |
|
| 85 | + $this->movedNodes[$source->getId()] = $source; |
|
| 86 | + } elseif ($source instanceof Folder) { |
|
| 87 | + foreach ($source->getDirectoryListing() as $child) { |
|
| 88 | + $this->recursivelyPrepareMove($child); |
|
| 89 | + } |
|
| 90 | + } |
|
| 91 | + } |
|
| 92 | + |
|
| 93 | + /** |
|
| 94 | + * Call handleMoveOrCopy on each sub files |
|
| 95 | + * @param NodeRenamedEvent|NodeCopiedEvent $event |
|
| 96 | + */ |
|
| 97 | + private function recursivelyHandleMoveOrCopy(Event $event, IUser $user, ?Node $source, Node $target, IVersionBackend $sourceBackend, IVersionBackend $targetBackend): void { |
|
| 98 | + if ($target instanceof File) { |
|
| 99 | + if ($event instanceof NodeRenamedEvent) { |
|
| 100 | + $source = $this->movedNodes[$target->getId()]; |
|
| 101 | + } |
|
| 102 | + |
|
| 103 | + if ($source === null) { |
|
| 104 | + $this->logger->warning( |
|
| 105 | + 'Failed to retrieve source file during version move/copy.', |
|
| 106 | + [ |
|
| 107 | + 'eventClass' => get_class($event), |
|
| 108 | + 'targetPath' => $target->getPath(), |
|
| 109 | + 'targetId' => $target->getId(), |
|
| 110 | + 'movedNodesKeys' => array_keys($this->movedNodes), |
|
| 111 | + 'sourceBackendClass' => get_class($sourceBackend), |
|
| 112 | + 'targetBackendClass' => get_class($targetBackend), |
|
| 113 | + ] |
|
| 114 | + ); |
|
| 115 | + return; |
|
| 116 | + } |
|
| 117 | + |
|
| 118 | + $this->handleMoveOrCopy($event, $user, $source, $target, $sourceBackend, $targetBackend); |
|
| 119 | + } elseif ($target instanceof Folder) { |
|
| 120 | + /** @var Folder $source */ |
|
| 121 | + foreach ($target->getDirectoryListing() as $targetChild) { |
|
| 122 | + if ($event instanceof NodeCopiedEvent) { |
|
| 123 | + $sourceChild = $source->get($targetChild->getName()); |
|
| 124 | + } else { |
|
| 125 | + $sourceChild = null; |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + $this->recursivelyHandleMoveOrCopy($event, $user, $sourceChild, $targetChild, $sourceBackend, $targetBackend); |
|
| 129 | + } |
|
| 130 | + } |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + /** |
|
| 134 | + * Called only during NodeRenamedEvent or NodeCopiedEvent |
|
| 135 | + * Will send the source node versions to the new backend, and then delete them from the old backend. |
|
| 136 | + * @param NodeRenamedEvent|NodeCopiedEvent $event |
|
| 137 | + */ |
|
| 138 | + private function handleMoveOrCopy(Event $event, IUser $user, File $source, File $target, IVersionBackend $sourceBackend, IVersionBackend $targetBackend): void { |
|
| 139 | + if ($targetBackend instanceof IVersionsImporterBackend) { |
|
| 140 | + $versions = $sourceBackend->getVersionsForFile($user, $source); |
|
| 141 | + $targetBackend->importVersionsForFile($user, $source, $target, $versions); |
|
| 142 | + } |
|
| 143 | + |
|
| 144 | + if ($event instanceof NodeRenamedEvent && $sourceBackend instanceof IVersionsImporterBackend) { |
|
| 145 | + $sourceBackend->clearVersionsForFile($user, $source, $target); |
|
| 146 | + } |
|
| 147 | + } |
|
| 148 | + |
|
| 149 | + private function getNodeStorage(Node $node): IStorage { |
|
| 150 | + if ($node instanceof NonExistingFile || $node instanceof NonExistingFolder) { |
|
| 151 | + return $node->getParent()->getStorage(); |
|
| 152 | + } else { |
|
| 153 | + return $node->getStorage(); |
|
| 154 | + } |
|
| 155 | + } |
|
| 156 | 156 | } |