Skip to content

Commit ec76270

Browse files
committed
fix(files): Do not require files_trashbin in live photo sync listener
Fix #43299 Signed-off-by: Louis Chemineau <[email protected]> [skip ci]
1 parent e655e52 commit ec76270

File tree

9 files changed

+223
-130
lines changed

9 files changed

+223
-130
lines changed

apps/files/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
'OCA\\Files\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
6767
'OCA\\Files\\Search\\FilesSearchProvider' => $baseDir . '/../lib/Search/FilesSearchProvider.php',
6868
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php',
69+
'OCA\\Files\\Service\\LivePhotosService' => $baseDir . '/../lib/Service/LivePhotosService.php',
6970
'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php',
7071
'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php',
7172
'OCA\\Files\\Service\\UserConfig' => $baseDir . '/../lib/Service/UserConfig.php',

apps/files/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class ComposerStaticInitFiles
8181
'OCA\\Files\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
8282
'OCA\\Files\\Search\\FilesSearchProvider' => __DIR__ . '/..' . '/../lib/Search/FilesSearchProvider.php',
8383
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php',
84+
'OCA\\Files\\Service\\LivePhotosService' => __DIR__ . '/..' . '/../lib/Service/LivePhotosService.php',
8485
'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php',
8586
'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php',
8687
'OCA\\Files\\Service\\UserConfig' => __DIR__ . '/..' . '/../lib/Service/UserConfig.php',

apps/files/lib/AppInfo/Application.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
use OCA\Files\Service\TagService;
5050
use OCA\Files\Service\UserConfig;
5151
use OCA\Files\Service\ViewConfig;
52-
use OCA\Files_Trashbin\Events\BeforeNodeRestoredEvent;
5352
use OCP\Activity\IManager as IActivityManager;
5453
use OCP\AppFramework\App;
5554
use OCP\AppFramework\Bootstrap\IBootContext;
@@ -127,7 +126,6 @@ public function register(IRegistrationContext $context): void {
127126
$context->registerEventListener(RenderReferenceEvent::class, RenderReferenceEventListener::class);
128127
$context->registerEventListener(BeforeNodeRenamedEvent::class, SyncLivePhotosListener::class);
129128
$context->registerEventListener(BeforeNodeDeletedEvent::class, SyncLivePhotosListener::class);
130-
$context->registerEventListener(BeforeNodeRestoredEvent::class, SyncLivePhotosListener::class);
131129
$context->registerEventListener(CacheEntryRemovedEvent::class, SyncLivePhotosListener::class);
132130

133131
$context->registerSearchProvider(FilesSearchProvider::class);

apps/files/lib/Listener/SyncLivePhotosListener.php

Lines changed: 15 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@
2424

2525
namespace OCA\Files\Listener;
2626

27-
use OCA\Files_Trashbin\Events\BeforeNodeRestoredEvent;
28-
use OCA\Files_Trashbin\Trash\ITrashItem;
29-
use OCA\Files_Trashbin\Trash\ITrashManager;
27+
use OCA\Files\Service\LivePhotosService;
3028
use OCP\EventDispatcher\Event;
3129
use OCP\EventDispatcher\IEventListener;
3230
use OCP\Files\Cache\CacheEntryRemovedEvent;
@@ -35,10 +33,7 @@
3533
use OCP\Files\Folder;
3634
use OCP\Files\Node;
3735
use OCP\Files\NotFoundException;
38-
use OCP\Files\NotPermittedException;
39-
use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
4036
use OCP\FilesMetadata\IFilesMetadataManager;
41-
use OCP\IUserSession;
4237

4338
/**
4439
* @template-implements IEventListener<Event>
@@ -48,36 +43,38 @@ class SyncLivePhotosListener implements IEventListener {
4843
private array $pendingRenames = [];
4944
/** @var Array<int, bool> */
5045
private array $pendingDeletion = [];
51-
/** @var Array<int, bool> */
52-
private array $pendingRestores = [];
5346

5447
public function __construct(
5548
private ?Folder $userFolder,
56-
private ?IUserSession $userSession,
57-
private ITrashManager $trashManager,
5849
private IFilesMetadataManager $filesMetadataManager,
50+
private LivePhotosService $livePhotosService,
5951
) {
6052
}
6153

6254
public function handle(Event $event): void {
63-
if ($this->userFolder === null || $this->userSession === null) {
55+
if ($this->userFolder === null) {
6456
return;
6557
}
6658

67-
$peerFile = null;
59+
$peerFileId = null;
6860

6961
if ($event instanceof BeforeNodeRenamedEvent) {
70-
$peerFile = $this->getLivePhotoPeer($event->getSource()->getId());
71-
} elseif ($event instanceof BeforeNodeRestoredEvent) {
72-
$peerFile = $this->getLivePhotoPeer($event->getSource()->getId());
62+
$peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getSource()->getId());
7363
} elseif ($event instanceof BeforeNodeDeletedEvent) {
74-
$peerFile = $this->getLivePhotoPeer($event->getNode()->getId());
64+
$peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getNode()->getId());
7565
} elseif ($event instanceof CacheEntryRemovedEvent) {
76-
$peerFile = $this->getLivePhotoPeer($event->getFileId());
66+
$peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getFileId());
67+
}
68+
69+
if ($peerFileId === null) {
70+
return; // Not a live photo.
7771
}
7872

73+
// Check the user's folder.
74+
$peerFile = $this->userFolder->getFirstNodeById($peerFileId);
75+
7976
if ($peerFile === null) {
80-
return; // not a Live Photo
77+
return; // Peer file not found.
8178
}
8279

8380
if ($event instanceof BeforeNodeRenamedEvent) {
@@ -164,114 +161,4 @@ private function handleDeletion(BeforeNodeDeletedEvent $event, Node $peerFile):
164161
}
165162
return;
166163
}
167-
168-
/**
169-
* During restore event, we trigger another recursive restore on the peer file.
170-
* Restore operations on the .mov file directly are currently blocked.
171-
* The event listener being singleton, we can store the current state
172-
* of pending restores inside the 'pendingRestores' property,
173-
* to prevent infinite recursivity.
174-
*/
175-
private function handleRestore(BeforeNodeRestoredEvent $event, Node $peerFile): void {
176-
$sourceFile = $event->getSource();
177-
178-
if ($sourceFile->getMimetype() === 'video/quicktime') {
179-
if (isset($this->pendingRestores[$peerFile->getId()])) {
180-
unset($this->pendingRestores[$peerFile->getId()]);
181-
return;
182-
} else {
183-
$event->abortOperation(new NotPermittedException("Cannot restore the video part of a live photo"));
184-
}
185-
} else {
186-
$user = $this->userSession->getUser();
187-
if ($user === null) {
188-
return;
189-
}
190-
191-
$peerTrashItem = $this->trashManager->getTrashNodeById($user, $peerFile->getId());
192-
// Peer file is not in the bin, no need to restore it.
193-
if ($peerTrashItem === null) {
194-
return;
195-
}
196-
197-
$trashRoot = $this->trashManager->listTrashRoot($user);
198-
$trashItem = $this->getTrashItem($trashRoot, $peerFile->getInternalPath());
199-
200-
if ($trashItem === null) {
201-
$event->abortOperation(new NotFoundException("Couldn't find peer file in trashbin"));
202-
}
203-
204-
$this->pendingRestores[$sourceFile->getId()] = true;
205-
try {
206-
$this->trashManager->restoreItem($trashItem);
207-
} catch (\Throwable $ex) {
208-
$event->abortOperation($ex);
209-
}
210-
}
211-
}
212-
213-
/**
214-
* Helper method to get the associated live photo file.
215-
* We first look for it in the user folder, and if we
216-
* cannot find it here, we look for it in the user's trashbin.
217-
*/
218-
private function getLivePhotoPeer(int $nodeId): ?Node {
219-
if ($this->userFolder === null || $this->userSession === null) {
220-
return null;
221-
}
222-
223-
try {
224-
$metadata = $this->filesMetadataManager->getMetadata($nodeId);
225-
} catch (FilesMetadataNotFoundException $ex) {
226-
return null;
227-
}
228-
229-
if (!$metadata->hasKey('files-live-photo')) {
230-
return null;
231-
}
232-
233-
$peerFileId = (int)$metadata->getString('files-live-photo');
234-
235-
// Check the user's folder.
236-
$nodes = $this->userFolder->getById($peerFileId);
237-
if (count($nodes) !== 0) {
238-
return $nodes[0];
239-
}
240-
241-
// Check the user's trashbin.
242-
$user = $this->userSession->getUser();
243-
if ($user !== null) {
244-
$peerFile = $this->trashManager->getTrashNodeById($user, $peerFileId);
245-
if ($peerFile !== null) {
246-
return $peerFile;
247-
}
248-
}
249-
250-
$metadata->unset('files-live-photo');
251-
return null;
252-
}
253-
254-
/**
255-
* There is currently no method to restore a file based on its fileId or path.
256-
* So we have to manually find a ITrashItem from the trash item list.
257-
* TODO: This should be replaced by a proper method in the TrashManager.
258-
*/
259-
private function getTrashItem(array $trashFolder, string $path): ?ITrashItem {
260-
foreach($trashFolder as $trashItem) {
261-
if (str_starts_with($path, "files_trashbin/files".$trashItem->getTrashPath())) {
262-
if ($path === "files_trashbin/files".$trashItem->getTrashPath()) {
263-
return $trashItem;
264-
}
265-
266-
if ($trashItem instanceof Folder) {
267-
$node = $this->getTrashItem($trashItem->getDirectoryListing(), $path);
268-
if ($node !== null) {
269-
return $node;
270-
}
271-
}
272-
}
273-
}
274-
275-
return null;
276-
}
277164
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2024 Louis Chemineau <[email protected]>
6+
*
7+
* @author Louis Chemineau <[email protected]>
8+
*
9+
* @license GNU AGPL version 3 or any later version
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License as
13+
* published by the Free Software Foundation, either version 3 of the
14+
* License, or (at your option) any later version.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License
22+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
23+
*/
24+
25+
namespace OCA\Files\Service;
26+
27+
use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
28+
use OCP\FilesMetadata\IFilesMetadataManager;
29+
30+
class LivePhotosService {
31+
public function __construct(
32+
private IFilesMetadataManager $filesMetadataManager,
33+
) {
34+
}
35+
36+
/**
37+
* Get the associated live photo for a given file id
38+
*/
39+
public function getLivePhotoPeerId(int $fileId): ?int {
40+
try {
41+
$metadata = $this->filesMetadataManager->getMetadata($fileId);
42+
} catch (FilesMetadataNotFoundException $ex) {
43+
return null;
44+
}
45+
46+
if (!$metadata->hasKey('files-live-photo')) {
47+
return null;
48+
}
49+
50+
return (int)$metadata->getString('files-live-photo');
51+
}
52+
}

apps/files_trashbin/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
'OCA\\Files_Trashbin\\Helper' => $baseDir . '/../lib/Helper.php',
2525
'OCA\\Files_Trashbin\\Hooks' => $baseDir . '/../lib/Hooks.php',
2626
'OCA\\Files_Trashbin\\Listeners\\LoadAdditionalScripts' => $baseDir . '/../lib/Listeners/LoadAdditionalScripts.php',
27+
'OCA\\Files_Trashbin\\Listeners\\SyncLivePhotosListener' => $baseDir . '/../lib/Listeners/SyncLivePhotosListener.php',
2728
'OCA\\Files_Trashbin\\Migration\\Version1010Date20200630192639' => $baseDir . '/../lib/Migration/Version1010Date20200630192639.php',
2829
'OCA\\Files_Trashbin\\Sabre\\AbstractTrash' => $baseDir . '/../lib/Sabre/AbstractTrash.php',
2930
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFile' => $baseDir . '/../lib/Sabre/AbstractTrashFile.php',

apps/files_trashbin/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class ComposerStaticInitFiles_Trashbin
3939
'OCA\\Files_Trashbin\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
4040
'OCA\\Files_Trashbin\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.php',
4141
'OCA\\Files_Trashbin\\Listeners\\LoadAdditionalScripts' => __DIR__ . '/..' . '/../lib/Listeners/LoadAdditionalScripts.php',
42+
'OCA\\Files_Trashbin\\Listeners\\SyncLivePhotosListener' => __DIR__ . '/..' . '/../lib/Listeners/SyncLivePhotosListener.php',
4243
'OCA\\Files_Trashbin\\Migration\\Version1010Date20200630192639' => __DIR__ . '/..' . '/../lib/Migration/Version1010Date20200630192639.php',
4344
'OCA\\Files_Trashbin\\Sabre\\AbstractTrash' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrash.php',
4445
'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFile' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrashFile.php',

apps/files_trashbin/lib/AppInfo/Application.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
use OCA\DAV\Connector\Sabre\Principal;
2929
use OCA\Files\Event\LoadAdditionalScriptsEvent;
3030
use OCA\Files_Trashbin\Capabilities;
31+
use OCA\Files_Trashbin\Events\BeforeNodeRestoredEvent;
3132
use OCA\Files_Trashbin\Expiration;
3233
use OCA\Files_Trashbin\Listeners\LoadAdditionalScripts;
34+
use OCA\Files_Trashbin\Listeners\SyncLivePhotosListener;
3335
use OCA\Files_Trashbin\Trash\ITrashManager;
3436
use OCA\Files_Trashbin\Trash\TrashManager;
3537
use OCA\Files_Trashbin\UserMigration\TrashbinMigrator;
@@ -62,6 +64,8 @@ public function register(IRegistrationContext $context): void {
6264
LoadAdditionalScriptsEvent::class,
6365
LoadAdditionalScripts::class
6466
);
67+
68+
$context->registerEventListener(BeforeNodeRestoredEvent::class, SyncLivePhotosListener::class);
6569
}
6670

6771
public function boot(IBootContext $context): void {

0 commit comments

Comments
 (0)