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