Completed
Push — master ( 5862d5...e2a54a )
by
unknown
18:18 queued 14s
created
apps/files_versions/lib/Listener/FileEventsListener.php 2 patches
Indentation   +362 added lines, -362 removed lines patch added patch discarded remove patch
@@ -41,366 +41,366 @@
 block discarded – undo
41 41
 
42 42
 /** @template-implements IEventListener<BeforeNodeCopiedEvent|BeforeNodeDeletedEvent|BeforeNodeRenamedEvent|BeforeNodeTouchedEvent|BeforeNodeWrittenEvent|NodeCopiedEvent|NodeCreatedEvent|NodeDeletedEvent|NodeRenamedEvent|NodeTouchedEvent|NodeWrittenEvent> */
43 43
 class FileEventsListener implements IEventListener {
44
-	/**
45
-	 * @var array<int, array>
46
-	 */
47
-	private array $writeHookInfo = [];
48
-	/**
49
-	 * @var array<int, Node>
50
-	 */
51
-	private array $nodesTouched = [];
52
-	/**
53
-	 * @var array<string, Node>
54
-	 */
55
-	private array $versionsDeleted = [];
56
-
57
-	public function __construct(
58
-		private IRootFolder $rootFolder,
59
-		private IVersionManager $versionManager,
60
-		private IMimeTypeLoader $mimeTypeLoader,
61
-		private IUserSession $userSession,
62
-		private LoggerInterface $logger,
63
-	) {
64
-	}
65
-
66
-	public function handle(Event $event): void {
67
-		if ($event instanceof NodeCreatedEvent) {
68
-			$this->created($event->getNode());
69
-		}
70
-
71
-		if ($event instanceof BeforeNodeTouchedEvent) {
72
-			$this->pre_touch_hook($event->getNode());
73
-		}
74
-
75
-		if ($event instanceof NodeTouchedEvent) {
76
-			$this->touch_hook($event->getNode());
77
-		}
78
-
79
-		if ($event instanceof BeforeNodeWrittenEvent) {
80
-			$this->write_hook($event->getNode());
81
-		}
82
-
83
-		if ($event instanceof NodeWrittenEvent) {
84
-			$this->post_write_hook($event->getNode());
85
-		}
86
-
87
-		if ($event instanceof BeforeNodeDeletedEvent) {
88
-			$this->pre_remove_hook($event->getNode());
89
-		}
90
-
91
-		if ($event instanceof NodeDeletedEvent) {
92
-			$this->remove_hook($event->getNode());
93
-		}
94
-
95
-		if ($event instanceof NodeRenamedEvent) {
96
-			$this->rename_hook($event->getSource(), $event->getTarget());
97
-		}
98
-
99
-		if ($event instanceof NodeCopiedEvent) {
100
-			$this->copy_hook($event->getSource(), $event->getTarget());
101
-		}
102
-
103
-		if ($event instanceof BeforeNodeRenamedEvent) {
104
-			$this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget());
105
-		}
106
-
107
-		if ($event instanceof BeforeNodeCopiedEvent) {
108
-			$this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget());
109
-		}
110
-	}
111
-
112
-	public function pre_touch_hook(Node $node): void {
113
-		// Do not handle folders.
114
-		if ($node instanceof Folder) {
115
-			return;
116
-		}
117
-
118
-		// $node is a non-existing on file creation.
119
-		if ($node instanceof NonExistingFile) {
120
-			return;
121
-		}
122
-
123
-		$this->nodesTouched[$node->getId()] = $node;
124
-	}
125
-
126
-	public function touch_hook(Node $node): void {
127
-		$previousNode = $this->nodesTouched[$node->getId()] ?? null;
128
-
129
-		if ($previousNode === null) {
130
-			return;
131
-		}
132
-
133
-		unset($this->nodesTouched[$node->getId()]);
134
-
135
-		try {
136
-			if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
137
-				// We update the timestamp of the version entity associated with the previousNode.
138
-				$this->versionManager->updateVersionEntity($node, $previousNode->getMTime(), ['timestamp' => $node->getMTime()]);
139
-			}
140
-		} catch (DbalException $ex) {
141
-			// Ignore UniqueConstraintViolationException, as we are probably in the middle of a rollback
142
-			// Where the previous node would temporary have the mtime of the old version, so the rollback touches it to fix it.
143
-			if (!($ex->getPrevious() instanceof UniqueConstraintViolationException)) {
144
-				throw $ex;
145
-			}
146
-		} catch (DoesNotExistException $ex) {
147
-			// Ignore DoesNotExistException, as we are probably in the middle of a rollback
148
-			// Where the previous node would temporary have a wrong mtime, so the rollback touches it to fix it.
149
-		}
150
-	}
151
-
152
-	public function created(Node $node): void {
153
-		// Do not handle folders.
154
-		if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
155
-			$this->versionManager->createVersionEntity($node);
156
-		}
157
-	}
158
-
159
-	/**
160
-	 * listen to write event.
161
-	 */
162
-	public function write_hook(Node $node): void {
163
-		// Do not handle folders.
164
-		if ($node instanceof Folder) {
165
-			return;
166
-		}
167
-
168
-		// $node is a non-existing on file creation.
169
-		if ($node instanceof NonExistingFile) {
170
-			return;
171
-		}
172
-
173
-		$path = $this->getPathForNode($node);
174
-		$result = Storage::store($path);
175
-
176
-		// Store the result of the version creation so it can be used in post_write_hook.
177
-		$this->writeHookInfo[$node->getId()] = [
178
-			'previousNode' => $node,
179
-			'versionCreated' => $result !== false
180
-		];
181
-	}
182
-
183
-	/**
184
-	 * listen to post_write event.
185
-	 */
186
-	public function post_write_hook(Node $node): void {
187
-		// Do not handle folders.
188
-		if ($node instanceof Folder) {
189
-			return;
190
-		}
191
-
192
-		$writeHookInfo = $this->writeHookInfo[$node->getId()] ?? null;
193
-
194
-		if ($writeHookInfo === null) {
195
-			return;
196
-		}
197
-
198
-		if (
199
-			$writeHookInfo['versionCreated']
200
-			&& $node->getMTime() !== $writeHookInfo['previousNode']->getMTime()
201
-		) {
202
-			// If a new version was created, insert a version in the DB for the current content.
203
-			// If both versions have the same mtime, it means the latest version file simply got overrode,
204
-			// so no need to create a new version.
205
-			$this->created($node);
206
-		} else {
207
-			try {
208
-				// If no new version was stored in the FS, no new version should be added in the DB.
209
-				// So we simply update the associated version.
210
-				if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
211
-					$this->versionManager->updateVersionEntity(
212
-						$node,
213
-						$writeHookInfo['previousNode']->getMtime(),
214
-						[
215
-							'timestamp' => $node->getMTime(),
216
-							'size' => $node->getSize(),
217
-							'mimetype' => $this->mimeTypeLoader->getId($node->getMimetype()),
218
-						],
219
-					);
220
-				}
221
-			} catch (DoesNotExistException $e) {
222
-				// This happens if the versions app was not enabled while the file was created or updated the last time.
223
-				// meaning there is no such revision and we need to create this file.
224
-				if ($writeHookInfo['versionCreated']) {
225
-					$this->created($node);
226
-				} else {
227
-					// Normally this should not happen so we re-throw the exception to not hide any potential issues.
228
-					throw $e;
229
-				}
230
-			} catch (Exception $e) {
231
-				$this->logger->error('Failed to update existing version for ' . $node->getPath(), [
232
-					'exception' => $e,
233
-					'versionCreated' => $writeHookInfo['versionCreated'],
234
-					'previousNode' => [
235
-						'size' => $writeHookInfo['previousNode']->getSize(),
236
-						'mtime' => $writeHookInfo['previousNode']->getMTime(),
237
-					],
238
-					'node' => [
239
-						'size' => $node->getSize(),
240
-						'mtime' => $node->getMTime(),
241
-					]
242
-				]);
243
-				throw $e;
244
-			}
245
-		}
246
-
247
-		unset($this->writeHookInfo[$node->getId()]);
248
-	}
249
-
250
-	/**
251
-	 * Erase versions of deleted file
252
-	 *
253
-	 * This function is connected to the delete signal of OC_Filesystem
254
-	 * cleanup the versions directory if the actual file gets deleted
255
-	 */
256
-	public function remove_hook(Node $node): void {
257
-		// Need to normalize the path as there is an issue with path concatenation in View.php::getAbsolutePath.
258
-		$path = Filesystem::normalizePath($node->getPath());
259
-		if (!array_key_exists($path, $this->versionsDeleted)) {
260
-			return;
261
-		}
262
-		$node = $this->versionsDeleted[$path];
263
-		$relativePath = $this->getPathForNode($node);
264
-		unset($this->versionsDeleted[$path]);
265
-		Storage::delete($relativePath);
266
-		// If no new version was stored in the FS, no new version should be added in the DB.
267
-		// So we simply update the associated version.
268
-		if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
269
-			$this->versionManager->deleteVersionsEntity($node);
270
-		}
271
-	}
272
-
273
-	/**
274
-	 * mark file as "deleted" so that we can clean up the versions if the file is gone
275
-	 */
276
-	public function pre_remove_hook(Node $node): void {
277
-		$path = $this->getPathForNode($node);
278
-		Storage::markDeletedFile($path);
279
-		$this->versionsDeleted[$node->getPath()] = $node;
280
-	}
281
-
282
-	/**
283
-	 * rename/move versions of renamed/moved files
284
-	 *
285
-	 * This function is connected to the rename signal of OC_Filesystem and adjust the name and location
286
-	 * of the stored versions along the actual file
287
-	 */
288
-	public function rename_hook(Node $source, Node $target): void {
289
-		$sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage());
290
-		$targetBackend = $this->versionManager->getBackendForStorage($target->getStorage());
291
-		// If different backends, do nothing.
292
-		if ($sourceBackend !== $targetBackend) {
293
-			return;
294
-		}
295
-
296
-		$oldPath = $this->getPathForNode($source);
297
-		$newPath = $this->getPathForNode($target);
298
-		Storage::renameOrCopy($oldPath, $newPath, 'rename');
299
-	}
300
-
301
-	/**
302
-	 * copy versions of copied files
303
-	 *
304
-	 * This function is connected to the copy signal of OC_Filesystem and copies the
305
-	 * the stored versions to the new location
306
-	 */
307
-	public function copy_hook(Node $source, Node $target): void {
308
-		$sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage());
309
-		$targetBackend = $this->versionManager->getBackendForStorage($target->getStorage());
310
-		// If different backends, do nothing.
311
-		if ($sourceBackend !== $targetBackend) {
312
-			return;
313
-		}
314
-
315
-		$oldPath = $this->getPathForNode($source);
316
-		$newPath = $this->getPathForNode($target);
317
-		Storage::renameOrCopy($oldPath, $newPath, 'copy');
318
-	}
319
-
320
-	/**
321
-	 * Remember owner and the owner path of the source file.
322
-	 * If the file already exists, then it was a upload of a existing file
323
-	 * over the web interface and we call Storage::store() directly
324
-	 *
325
-	 *
326
-	 */
327
-	public function pre_renameOrCopy_hook(Node $source, Node $target): void {
328
-		$sourceBackend = $this->versionManager->getBackendForStorage($source->getStorage());
329
-		$targetBackend = $this->versionManager->getBackendForStorage($target->getParent()->getStorage());
330
-		// If different backends, do nothing.
331
-		if ($sourceBackend !== $targetBackend) {
332
-			return;
333
-		}
334
-
335
-		// if we rename a movable mount point, then the versions don't have to be renamed
336
-		$oldPath = $this->getPathForNode($source);
337
-		$newPath = $this->getPathForNode($target);
338
-		if ($oldPath === null || $newPath === null) {
339
-			return;
340
-		}
341
-
342
-		$user = $this->userSession->getUser()?->getUID();
343
-		if ($user === null) {
344
-			return;
345
-		}
346
-
347
-		$absOldPath = Filesystem::normalizePath('/' . $user . '/files' . $oldPath);
348
-		$manager = Filesystem::getMountManager();
349
-		$mount = $manager->find($absOldPath);
350
-		$internalPath = $mount->getInternalPath($absOldPath);
351
-		if ($internalPath === '' and $mount instanceof MoveableMount) {
352
-			return;
353
-		}
354
-
355
-		$view = new View($user . '/files');
356
-		if ($view->file_exists($newPath)) {
357
-			Storage::store($newPath);
358
-		} else {
359
-			Storage::setSourcePathAndUser($oldPath);
360
-		}
361
-	}
362
-
363
-	/**
364
-	 * Retrieve the path relative to the current user root folder.
365
-	 * If no user is connected, try to use the node's owner.
366
-	 */
367
-	private function getPathForNode(Node $node): ?string {
368
-		$user = $this->userSession->getUser()?->getUID();
369
-		if ($user) {
370
-			$path = $this->rootFolder
371
-				->getUserFolder($user)
372
-				->getRelativePath($node->getPath());
373
-
374
-			if ($path !== null) {
375
-				return $path;
376
-			}
377
-		}
378
-
379
-		try {
380
-			$owner = $node->getOwner()?->getUid();
381
-		} catch (\OCP\Files\NotFoundException) {
382
-			$owner = null;
383
-		}
384
-
385
-		// If no owner, extract it from the path.
386
-		// e.g. /user/files/foobar.txt
387
-		if (!$owner) {
388
-			$parts = explode('/', $node->getPath(), 4);
389
-			if (count($parts) === 4) {
390
-				$owner = $parts[1];
391
-			}
392
-		}
393
-
394
-		if ($owner) {
395
-			$path = $this->rootFolder
396
-				->getUserFolder($owner)
397
-				->getRelativePath($node->getPath());
398
-
399
-			if ($path !== null) {
400
-				return $path;
401
-			}
402
-		}
403
-
404
-		return null;
405
-	}
44
+    /**
45
+     * @var array<int, array>
46
+     */
47
+    private array $writeHookInfo = [];
48
+    /**
49
+     * @var array<int, Node>
50
+     */
51
+    private array $nodesTouched = [];
52
+    /**
53
+     * @var array<string, Node>
54
+     */
55
+    private array $versionsDeleted = [];
56
+
57
+    public function __construct(
58
+        private IRootFolder $rootFolder,
59
+        private IVersionManager $versionManager,
60
+        private IMimeTypeLoader $mimeTypeLoader,
61
+        private IUserSession $userSession,
62
+        private LoggerInterface $logger,
63
+    ) {
64
+    }
65
+
66
+    public function handle(Event $event): void {
67
+        if ($event instanceof NodeCreatedEvent) {
68
+            $this->created($event->getNode());
69
+        }
70
+
71
+        if ($event instanceof BeforeNodeTouchedEvent) {
72
+            $this->pre_touch_hook($event->getNode());
73
+        }
74
+
75
+        if ($event instanceof NodeTouchedEvent) {
76
+            $this->touch_hook($event->getNode());
77
+        }
78
+
79
+        if ($event instanceof BeforeNodeWrittenEvent) {
80
+            $this->write_hook($event->getNode());
81
+        }
82
+
83
+        if ($event instanceof NodeWrittenEvent) {
84
+            $this->post_write_hook($event->getNode());
85
+        }
86
+
87
+        if ($event instanceof BeforeNodeDeletedEvent) {
88
+            $this->pre_remove_hook($event->getNode());
89
+        }
90
+
91
+        if ($event instanceof NodeDeletedEvent) {
92
+            $this->remove_hook($event->getNode());
93
+        }
94
+
95
+        if ($event instanceof NodeRenamedEvent) {
96
+            $this->rename_hook($event->getSource(), $event->getTarget());
97
+        }
98
+
99
+        if ($event instanceof NodeCopiedEvent) {
100
+            $this->copy_hook($event->getSource(), $event->getTarget());
101
+        }
102
+
103
+        if ($event instanceof BeforeNodeRenamedEvent) {
104
+            $this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget());
105
+        }
106
+
107
+        if ($event instanceof BeforeNodeCopiedEvent) {
108
+            $this->pre_renameOrCopy_hook($event->getSource(), $event->getTarget());
109
+        }
110
+    }
111
+
112
+    public function pre_touch_hook(Node $node): void {
113
+        // Do not handle folders.
114
+        if ($node instanceof Folder) {
115
+            return;
116
+        }
117
+
118
+        // $node is a non-existing on file creation.
119
+        if ($node instanceof NonExistingFile) {
120
+            return;
121
+        }
122
+
123
+        $this->nodesTouched[$node->getId()] = $node;
124
+    }
125
+
126
+    public function touch_hook(Node $node): void {
127
+        $previousNode = $this->nodesTouched[$node->getId()] ?? null;
128
+
129
+        if ($previousNode === null) {
130
+            return;
131
+        }
132
+
133
+        unset($this->nodesTouched[$node->getId()]);
134
+
135
+        try {
136
+            if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
137
+                // We update the timestamp of the version entity associated with the previousNode.
138
+                $this->versionManager->updateVersionEntity($node, $previousNode->getMTime(), ['timestamp' => $node->getMTime()]);
139
+            }
140
+        } catch (DbalException $ex) {
141
+            // Ignore UniqueConstraintViolationException, as we are probably in the middle of a rollback
142
+            // Where the previous node would temporary have the mtime of the old version, so the rollback touches it to fix it.
143
+            if (!($ex->getPrevious() instanceof UniqueConstraintViolationException)) {
144
+                throw $ex;
145
+            }
146
+        } catch (DoesNotExistException $ex) {
147
+            // Ignore DoesNotExistException, as we are probably in the middle of a rollback
148
+            // Where the previous node would temporary have a wrong mtime, so the rollback touches it to fix it.
149
+        }
150
+    }
151
+
152
+    public function created(Node $node): void {
153
+        // Do not handle folders.
154
+        if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
155
+            $this->versionManager->createVersionEntity($node);
156
+        }
157
+    }
158
+
159
+    /**
160
+     * listen to write event.
161
+     */
162
+    public function write_hook(Node $node): void {
163
+        // Do not handle folders.
164
+        if ($node instanceof Folder) {
165
+            return;
166
+        }
167
+
168
+        // $node is a non-existing on file creation.
169
+        if ($node instanceof NonExistingFile) {
170
+            return;
171
+        }
172
+
173
+        $path = $this->getPathForNode($node);
174
+        $result = Storage::store($path);
175
+
176
+        // Store the result of the version creation so it can be used in post_write_hook.
177
+        $this->writeHookInfo[$node->getId()] = [
178
+            'previousNode' => $node,
179
+            'versionCreated' => $result !== false
180
+        ];
181
+    }
182
+
183
+    /**
184
+     * listen to post_write event.
185
+     */
186
+    public function post_write_hook(Node $node): void {
187
+        // Do not handle folders.
188
+        if ($node instanceof Folder) {
189
+            return;
190
+        }
191
+
192
+        $writeHookInfo = $this->writeHookInfo[$node->getId()] ?? null;
193
+
194
+        if ($writeHookInfo === null) {
195
+            return;
196
+        }
197
+
198
+        if (
199
+            $writeHookInfo['versionCreated']
200
+            && $node->getMTime() !== $writeHookInfo['previousNode']->getMTime()
201
+        ) {
202
+            // If a new version was created, insert a version in the DB for the current content.
203
+            // If both versions have the same mtime, it means the latest version file simply got overrode,
204
+            // so no need to create a new version.
205
+            $this->created($node);
206
+        } else {
207
+            try {
208
+                // If no new version was stored in the FS, no new version should be added in the DB.
209
+                // So we simply update the associated version.
210
+                if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
211
+                    $this->versionManager->updateVersionEntity(
212
+                        $node,
213
+                        $writeHookInfo['previousNode']->getMtime(),
214
+                        [
215
+                            'timestamp' => $node->getMTime(),
216
+                            'size' => $node->getSize(),
217
+                            'mimetype' => $this->mimeTypeLoader->getId($node->getMimetype()),
218
+                        ],
219
+                    );
220
+                }
221
+            } catch (DoesNotExistException $e) {
222
+                // This happens if the versions app was not enabled while the file was created or updated the last time.
223
+                // meaning there is no such revision and we need to create this file.
224
+                if ($writeHookInfo['versionCreated']) {
225
+                    $this->created($node);
226
+                } else {
227
+                    // Normally this should not happen so we re-throw the exception to not hide any potential issues.
228
+                    throw $e;
229
+                }
230
+            } catch (Exception $e) {
231
+                $this->logger->error('Failed to update existing version for ' . $node->getPath(), [
232
+                    'exception' => $e,
233
+                    'versionCreated' => $writeHookInfo['versionCreated'],
234
+                    'previousNode' => [
235
+                        'size' => $writeHookInfo['previousNode']->getSize(),
236
+                        'mtime' => $writeHookInfo['previousNode']->getMTime(),
237
+                    ],
238
+                    'node' => [
239
+                        'size' => $node->getSize(),
240
+                        'mtime' => $node->getMTime(),
241
+                    ]
242
+                ]);
243
+                throw $e;
244
+            }
245
+        }
246
+
247
+        unset($this->writeHookInfo[$node->getId()]);
248
+    }
249
+
250
+    /**
251
+     * Erase versions of deleted file
252
+     *
253
+     * This function is connected to the delete signal of OC_Filesystem
254
+     * cleanup the versions directory if the actual file gets deleted
255
+     */
256
+    public function remove_hook(Node $node): void {
257
+        // Need to normalize the path as there is an issue with path concatenation in View.php::getAbsolutePath.
258
+        $path = Filesystem::normalizePath($node->getPath());
259
+        if (!array_key_exists($path, $this->versionsDeleted)) {
260
+            return;
261
+        }
262
+        $node = $this->versionsDeleted[$path];
263
+        $relativePath = $this->getPathForNode($node);
264
+        unset($this->versionsDeleted[$path]);
265
+        Storage::delete($relativePath);
266
+        // If no new version was stored in the FS, no new version should be added in the DB.
267
+        // So we simply update the associated version.
268
+        if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
269
+            $this->versionManager->deleteVersionsEntity($node);
270
+        }
271
+    }
272
+
273
+    /**
274
+     * mark file as "deleted" so that we can clean up the versions if the file is gone
275
+     */
276
+    public function pre_remove_hook(Node $node): void {
277
+        $path = $this->getPathForNode($node);
278
+        Storage::markDeletedFile($path);
279
+        $this->versionsDeleted[$node->getPath()] = $node;
280
+    }
281
+
282
+    /**
283
+     * rename/move versions of renamed/moved files
284
+     *
285
+     * This function is connected to the rename signal of OC_Filesystem and adjust the name and location
286
+     * of the stored versions along the actual file
287
+     */
288
+    public function rename_hook(Node $source, Node $target): void {
289
+        $sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage());
290
+        $targetBackend = $this->versionManager->getBackendForStorage($target->getStorage());
291
+        // If different backends, do nothing.
292
+        if ($sourceBackend !== $targetBackend) {
293
+            return;
294
+        }
295
+
296
+        $oldPath = $this->getPathForNode($source);
297
+        $newPath = $this->getPathForNode($target);
298
+        Storage::renameOrCopy($oldPath, $newPath, 'rename');
299
+    }
300
+
301
+    /**
302
+     * copy versions of copied files
303
+     *
304
+     * This function is connected to the copy signal of OC_Filesystem and copies the
305
+     * the stored versions to the new location
306
+     */
307
+    public function copy_hook(Node $source, Node $target): void {
308
+        $sourceBackend = $this->versionManager->getBackendForStorage($source->getParent()->getStorage());
309
+        $targetBackend = $this->versionManager->getBackendForStorage($target->getStorage());
310
+        // If different backends, do nothing.
311
+        if ($sourceBackend !== $targetBackend) {
312
+            return;
313
+        }
314
+
315
+        $oldPath = $this->getPathForNode($source);
316
+        $newPath = $this->getPathForNode($target);
317
+        Storage::renameOrCopy($oldPath, $newPath, 'copy');
318
+    }
319
+
320
+    /**
321
+     * Remember owner and the owner path of the source file.
322
+     * If the file already exists, then it was a upload of a existing file
323
+     * over the web interface and we call Storage::store() directly
324
+     *
325
+     *
326
+     */
327
+    public function pre_renameOrCopy_hook(Node $source, Node $target): void {
328
+        $sourceBackend = $this->versionManager->getBackendForStorage($source->getStorage());
329
+        $targetBackend = $this->versionManager->getBackendForStorage($target->getParent()->getStorage());
330
+        // If different backends, do nothing.
331
+        if ($sourceBackend !== $targetBackend) {
332
+            return;
333
+        }
334
+
335
+        // if we rename a movable mount point, then the versions don't have to be renamed
336
+        $oldPath = $this->getPathForNode($source);
337
+        $newPath = $this->getPathForNode($target);
338
+        if ($oldPath === null || $newPath === null) {
339
+            return;
340
+        }
341
+
342
+        $user = $this->userSession->getUser()?->getUID();
343
+        if ($user === null) {
344
+            return;
345
+        }
346
+
347
+        $absOldPath = Filesystem::normalizePath('/' . $user . '/files' . $oldPath);
348
+        $manager = Filesystem::getMountManager();
349
+        $mount = $manager->find($absOldPath);
350
+        $internalPath = $mount->getInternalPath($absOldPath);
351
+        if ($internalPath === '' and $mount instanceof MoveableMount) {
352
+            return;
353
+        }
354
+
355
+        $view = new View($user . '/files');
356
+        if ($view->file_exists($newPath)) {
357
+            Storage::store($newPath);
358
+        } else {
359
+            Storage::setSourcePathAndUser($oldPath);
360
+        }
361
+    }
362
+
363
+    /**
364
+     * Retrieve the path relative to the current user root folder.
365
+     * If no user is connected, try to use the node's owner.
366
+     */
367
+    private function getPathForNode(Node $node): ?string {
368
+        $user = $this->userSession->getUser()?->getUID();
369
+        if ($user) {
370
+            $path = $this->rootFolder
371
+                ->getUserFolder($user)
372
+                ->getRelativePath($node->getPath());
373
+
374
+            if ($path !== null) {
375
+                return $path;
376
+            }
377
+        }
378
+
379
+        try {
380
+            $owner = $node->getOwner()?->getUid();
381
+        } catch (\OCP\Files\NotFoundException) {
382
+            $owner = null;
383
+        }
384
+
385
+        // If no owner, extract it from the path.
386
+        // e.g. /user/files/foobar.txt
387
+        if (!$owner) {
388
+            $parts = explode('/', $node->getPath(), 4);
389
+            if (count($parts) === 4) {
390
+                $owner = $parts[1];
391
+            }
392
+        }
393
+
394
+        if ($owner) {
395
+            $path = $this->rootFolder
396
+                ->getUserFolder($owner)
397
+                ->getRelativePath($node->getPath());
398
+
399
+            if ($path !== null) {
400
+                return $path;
401
+            }
402
+        }
403
+
404
+        return null;
405
+    }
406 406
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -228,7 +228,7 @@  discard block
 block discarded – undo
228 228
 					throw $e;
229 229
 				}
230 230
 			} catch (Exception $e) {
231
-				$this->logger->error('Failed to update existing version for ' . $node->getPath(), [
231
+				$this->logger->error('Failed to update existing version for '.$node->getPath(), [
232 232
 					'exception' => $e,
233 233
 					'versionCreated' => $writeHookInfo['versionCreated'],
234 234
 					'previousNode' => [
@@ -344,7 +344,7 @@  discard block
 block discarded – undo
344 344
 			return;
345 345
 		}
346 346
 
347
-		$absOldPath = Filesystem::normalizePath('/' . $user . '/files' . $oldPath);
347
+		$absOldPath = Filesystem::normalizePath('/'.$user.'/files'.$oldPath);
348 348
 		$manager = Filesystem::getMountManager();
349 349
 		$mount = $manager->find($absOldPath);
350 350
 		$internalPath = $mount->getInternalPath($absOldPath);
@@ -352,7 +352,7 @@  discard block
 block discarded – undo
352 352
 			return;
353 353
 		}
354 354
 
355
-		$view = new View($user . '/files');
355
+		$view = new View($user.'/files');
356 356
 		if ($view->file_exists($newPath)) {
357 357
 			Storage::store($newPath);
358 358
 		} else {
Please login to merge, or discard this patch.