鏡像自
https://github.com/nextcloud/server.git
synced 2024-07-23 04:49:51 +02:00
Synchronize operation on live photo files
Signed-off-by: Louis Chemineau <louis@chmn.me>
This commit is contained in:
父節點
a1434af01e
當前提交
86d88f6b00
@ -59,6 +59,7 @@ return array(
|
||||
'OCA\\Files\\Helper' => $baseDir . '/../lib/Helper.php',
|
||||
'OCA\\Files\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
|
||||
'OCA\\Files\\Listener\\RenderReferenceEventListener' => $baseDir . '/../lib/Listener/RenderReferenceEventListener.php',
|
||||
'OCA\\Files\\Listener\\SyncLivePhotosListener' => $baseDir . '/../lib/Listener/SyncLivePhotosListener.php',
|
||||
'OCA\\Files\\Migration\\Version11301Date20191205150729' => $baseDir . '/../lib/Migration/Version11301Date20191205150729.php',
|
||||
'OCA\\Files\\Migration\\Version12101Date20221011153334' => $baseDir . '/../lib/Migration/Version12101Date20221011153334.php',
|
||||
'OCA\\Files\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
|
||||
|
@ -74,6 +74,7 @@ class ComposerStaticInitFiles
|
||||
'OCA\\Files\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
|
||||
'OCA\\Files\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
|
||||
'OCA\\Files\\Listener\\RenderReferenceEventListener' => __DIR__ . '/..' . '/../lib/Listener/RenderReferenceEventListener.php',
|
||||
'OCA\\Files\\Listener\\SyncLivePhotosListener' => __DIR__ . '/..' . '/../lib/Listener/SyncLivePhotosListener.php',
|
||||
'OCA\\Files\\Migration\\Version11301Date20191205150729' => __DIR__ . '/..' . '/../lib/Migration/Version11301Date20191205150729.php',
|
||||
'OCA\\Files\\Migration\\Version12101Date20221011153334' => __DIR__ . '/..' . '/../lib/Migration/Version12101Date20221011153334.php',
|
||||
'OCA\\Files\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
|
||||
|
@ -3,7 +3,7 @@
|
||||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => 'b1797842784b250fb01ed5e3bf130705eb94751b',
|
||||
'reference' => 'ba1af2b22e5409c62ea2bdf7eb0e13c282ed70e8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../',
|
||||
'aliases' => array(),
|
||||
@ -13,7 +13,7 @@
|
||||
'__root__' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => 'b1797842784b250fb01ed5e3bf130705eb94751b',
|
||||
'reference' => 'ba1af2b22e5409c62ea2bdf7eb0e13c282ed70e8',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../',
|
||||
'aliases' => array(),
|
||||
|
@ -43,11 +43,13 @@ use OCA\Files\DirectEditingCapabilities;
|
||||
use OCA\Files\Event\LoadSidebar;
|
||||
use OCA\Files\Listener\LoadSidebarListener;
|
||||
use OCA\Files\Listener\RenderReferenceEventListener;
|
||||
use OCA\Files\Listener\SyncLivePhotosListener;
|
||||
use OCA\Files\Notification\Notifier;
|
||||
use OCA\Files\Search\FilesSearchProvider;
|
||||
use OCA\Files\Service\TagService;
|
||||
use OCA\Files\Service\UserConfig;
|
||||
use OCA\Files\Service\ViewConfig;
|
||||
use OCA\Files_Trashbin\Events\BeforeNodeRestoredEvent;
|
||||
use OCP\Activity\IManager as IActivityManager;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
@ -56,6 +58,9 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
use OCP\Collaboration\Reference\RenderReferenceEvent;
|
||||
use OCP\Collaboration\Resources\IProviderManager;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\Cache\CacheEntryRemovedEvent;
|
||||
use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
|
||||
use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
|
||||
use OCP\IConfig;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
@ -120,6 +125,10 @@ class Application extends App implements IBootstrap {
|
||||
|
||||
$context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class);
|
||||
$context->registerEventListener(RenderReferenceEvent::class, RenderReferenceEventListener::class);
|
||||
$context->registerEventListener(BeforeNodeRenamedEvent::class, SyncLivePhotosListener::class);
|
||||
$context->registerEventListener(BeforeNodeDeletedEvent::class, SyncLivePhotosListener::class);
|
||||
$context->registerEventListener(BeforeNodeRestoredEvent::class, SyncLivePhotosListener::class);
|
||||
$context->registerEventListener(CacheEntryRemovedEvent::class, SyncLivePhotosListener::class);
|
||||
|
||||
$context->registerSearchProvider(FilesSearchProvider::class);
|
||||
|
||||
|
252
apps/files/lib/Listener/SyncLivePhotosListener.php
Normal file
252
apps/files/lib/Listener/SyncLivePhotosListener.php
Normal file
@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Louis Chemineau <louis@chmn.me>
|
||||
*
|
||||
* @author Louis Chemineau <louis@chmn.me>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace OCA\Files\Listener;
|
||||
|
||||
use OCA\Files_Trashbin\Events\BeforeNodeRestoredEvent;
|
||||
use OCA\Files_Trashbin\Trash\ITrashItem;
|
||||
use OCA\Files_Trashbin\Trash\ITrashManager;
|
||||
use OCP\EventDispatcher\Event;
|
||||
use OCP\EventDispatcher\IEventListener;
|
||||
use OCP\Files\Cache\CacheEntryRemovedEvent;
|
||||
use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
|
||||
use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\Node;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
|
||||
use OCP\FilesMetadata\IFilesMetadataManager;
|
||||
use OCP\IUserSession;
|
||||
|
||||
/**
|
||||
* @template-implements IEventListener<Event>
|
||||
*/
|
||||
class SyncLivePhotosListener implements IEventListener {
|
||||
/** @var Array<int, string> */
|
||||
private $pendingRenames = [];
|
||||
/** @var Array<int, bool> */
|
||||
private $pendingDeletion = [];
|
||||
/** @var Array<int, bool> */
|
||||
private $pendingRestores = [];
|
||||
|
||||
public function __construct(
|
||||
private ?Folder $userFolder,
|
||||
private ?IUserSession $userSession,
|
||||
private ITrashManager $trashManager,
|
||||
private IFilesMetadataManager $filesMetadataManager,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(Event $event): void {
|
||||
if ($this->userFolder === null || $this->userSession === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$peerFile = null;
|
||||
|
||||
if ($event instanceof BeforeNodeRenamedEvent) {
|
||||
$peerFile = $this->getLivePhotoPeer($event->getSource()->getId());
|
||||
} elseif ($event instanceof BeforeNodeRestoredEvent) {
|
||||
$peerFile = $this->getLivePhotoPeer($event->getSource()->getId());
|
||||
} elseif ($event instanceof BeforeNodeDeletedEvent) {
|
||||
$peerFile = $this->getLivePhotoPeer($event->getNode()->getId());
|
||||
} elseif ($event instanceof CacheEntryRemovedEvent) {
|
||||
$peerFile = $this->getLivePhotoPeer($event->getFileId());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($peerFile === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event instanceof BeforeNodeRenamedEvent) {
|
||||
$sourceFile = $event->getSource();
|
||||
$targetFile = $event->getTarget();
|
||||
$targetParent = $targetFile->getParent();
|
||||
$sourceExtension = $sourceFile->getExtension();
|
||||
$peerFileExtension = $peerFile->getExtension();
|
||||
$targetName = $targetFile->getName();
|
||||
$targetPath = $targetFile->getPath();
|
||||
|
||||
// Prevent rename of the .mov file if the peer file do not have the same path.
|
||||
if ($sourceFile->getMimetype() === 'video/quicktime') {
|
||||
$peerFilePath = $this->pendingRenames[$peerFile->getId()] ?? $peerFile->getPath();
|
||||
$targetPathWithoutExtension = preg_replace("/\.$sourceExtension$/", '', $targetPath);
|
||||
$peerFilePathWithoutExtension = preg_replace("/\.$peerFileExtension$/", '', $peerFilePath);
|
||||
|
||||
if ($targetPathWithoutExtension !== $peerFilePathWithoutExtension) {
|
||||
$event->abortOperation(new NotPermittedException("The video part of a live photo need to have the same name as the image"));
|
||||
}
|
||||
|
||||
unset($this->pendingRenames[$peerFile->getId()]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!str_ends_with($targetName, ".".$sourceExtension)) {
|
||||
$event->abortOperation(new NotPermittedException("Cannot change the extension of a live photo"));
|
||||
}
|
||||
|
||||
try {
|
||||
$targetParent->get($targetName);
|
||||
$event->abortOperation(new NotPermittedException("A file already exist at destination path"));
|
||||
} catch (NotFoundException $ex) {
|
||||
}
|
||||
try {
|
||||
$peerTargetName = preg_replace("/\.$sourceExtension$/", '.mov', $targetName);
|
||||
$targetParent->get($peerTargetName);
|
||||
$event->abortOperation(new NotPermittedException("A file already exist at destination path"));
|
||||
} catch (NotFoundException $ex) {
|
||||
}
|
||||
|
||||
$peerTargetPath = preg_replace("/\.$sourceExtension$/", '.mov', $targetPath);
|
||||
$this->pendingRenames[$sourceFile->getId()] = $targetPath;
|
||||
try {
|
||||
$peerFile->move($peerTargetPath);
|
||||
} catch (\Throwable $ex) {
|
||||
$event->abortOperation($ex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event instanceof BeforeNodeDeletedEvent) {
|
||||
$deletedFile = $event->getNode();
|
||||
if ($deletedFile->getMimetype() === 'video/quicktime') {
|
||||
if (isset($this->pendingDeletion[$peerFile->getId()])) {
|
||||
unset($this->pendingDeletion[$peerFile->getId()]);
|
||||
return;
|
||||
} else {
|
||||
$event->abortOperation(new NotPermittedException("Cannot delete the video part of a live photo"));
|
||||
}
|
||||
} else {
|
||||
$this->pendingDeletion[$deletedFile->getId()] = true;
|
||||
try {
|
||||
$peerFile->delete();
|
||||
} catch (\Throwable $ex) {
|
||||
$event->abortOperation($ex);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event instanceof CacheEntryRemovedEvent) {
|
||||
$peerFile->delete();
|
||||
}
|
||||
|
||||
if ($event instanceof BeforeNodeRestoredEvent) {
|
||||
$sourceFile = $event->getSource();
|
||||
|
||||
if ($sourceFile->getMimetype() === 'video/quicktime') {
|
||||
if (isset($this->pendingRestores[$peerFile->getId()])) {
|
||||
unset($this->pendingRestores[$peerFile->getId()]);
|
||||
return;
|
||||
} else {
|
||||
$event->abortOperation(new NotPermittedException("Cannot restore the video part of a live photo"));
|
||||
}
|
||||
} else {
|
||||
$user = $this->userSession->getUser();
|
||||
if ($user === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$peerTrashItem = $this->trashManager->getTrashNodeById($user, $peerFile->getId());
|
||||
|
||||
// Peer file in not in the bin, no need to restore it.
|
||||
if ($peerTrashItem === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$trashRoot = $this->trashManager->listTrashRoot($user);
|
||||
$trashItem = $this->getTrashItem($trashRoot, $peerFile->getInternalPath());
|
||||
|
||||
if ($trashItem === null) {
|
||||
$event->abortOperation(new NotFoundException("Couldn't find peer file in trashbin"));
|
||||
}
|
||||
|
||||
$this->pendingRestores[$sourceFile->getId()] = true;
|
||||
try {
|
||||
$this->trashManager->restoreItem($trashItem);
|
||||
} catch (\Throwable $ex) {
|
||||
$event->abortOperation($ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getLivePhotoPeer(int $nodeId): ?Node {
|
||||
if ($this->userFolder === null || $this->userSession === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$metadata = $this->filesMetadataManager->getMetadata($nodeId);
|
||||
} catch (FilesMetadataNotFoundException $ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$metadata->hasKey('files-live-photo')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$peerFileId = (int)$metadata->getString('files-live-photo');
|
||||
|
||||
// Check the user's folder.
|
||||
$nodes = $this->userFolder->getById($peerFileId);
|
||||
if (count($nodes) !== 0) {
|
||||
return $nodes[0];
|
||||
}
|
||||
|
||||
// Check the user's trashbin.
|
||||
$user = $this->userSession->getUser();
|
||||
if ($user !== null) {
|
||||
$peerFile = $this->trashManager->getTrashNodeById($user, $peerFileId);
|
||||
if ($peerFile !== null) {
|
||||
return $peerFile;
|
||||
}
|
||||
}
|
||||
|
||||
$metadata->unset('files-live-photo');
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getTrashItem(array $trashFolder, string $path): ?ITrashItem {
|
||||
foreach($trashFolder as $trashItem) {
|
||||
if (str_starts_with($path, "files_trashbin/files".$trashItem->getTrashPath())) {
|
||||
if ($path === "files_trashbin/files".$trashItem->getTrashPath()) {
|
||||
return $trashItem;
|
||||
}
|
||||
|
||||
if ($trashItem instanceof Folder) {
|
||||
$node = $this->getTrashItem($trashItem->getDirectoryListing(), $path);
|
||||
if ($node !== null) {
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -16,7 +16,9 @@ return array(
|
||||
'OCA\\Files_Trashbin\\Command\\RestoreAllFiles' => $baseDir . '/../lib/Command/RestoreAllFiles.php',
|
||||
'OCA\\Files_Trashbin\\Command\\Size' => $baseDir . '/../lib/Command/Size.php',
|
||||
'OCA\\Files_Trashbin\\Controller\\PreviewController' => $baseDir . '/../lib/Controller/PreviewController.php',
|
||||
'OCA\\Files_Trashbin\\Events\\BeforeNodeRestoredEvent' => $baseDir . '/../lib/Events/BeforeNodeRestoredEvent.php',
|
||||
'OCA\\Files_Trashbin\\Events\\MoveToTrashEvent' => $baseDir . '/../lib/Events/MoveToTrashEvent.php',
|
||||
'OCA\\Files_Trashbin\\Events\\NodeRestoredEvent' => $baseDir . '/../lib/Events/NodeRestoredEvent.php',
|
||||
'OCA\\Files_Trashbin\\Exceptions\\CopyRecursiveException' => $baseDir . '/../lib/Exceptions/CopyRecursiveException.php',
|
||||
'OCA\\Files_Trashbin\\Expiration' => $baseDir . '/../lib/Expiration.php',
|
||||
'OCA\\Files_Trashbin\\Helper' => $baseDir . '/../lib/Helper.php',
|
||||
|
@ -31,7 +31,9 @@ class ComposerStaticInitFiles_Trashbin
|
||||
'OCA\\Files_Trashbin\\Command\\RestoreAllFiles' => __DIR__ . '/..' . '/../lib/Command/RestoreAllFiles.php',
|
||||
'OCA\\Files_Trashbin\\Command\\Size' => __DIR__ . '/..' . '/../lib/Command/Size.php',
|
||||
'OCA\\Files_Trashbin\\Controller\\PreviewController' => __DIR__ . '/..' . '/../lib/Controller/PreviewController.php',
|
||||
'OCA\\Files_Trashbin\\Events\\BeforeNodeRestoredEvent' => __DIR__ . '/..' . '/../lib/Events/BeforeNodeRestoredEvent.php',
|
||||
'OCA\\Files_Trashbin\\Events\\MoveToTrashEvent' => __DIR__ . '/..' . '/../lib/Events/MoveToTrashEvent.php',
|
||||
'OCA\\Files_Trashbin\\Events\\NodeRestoredEvent' => __DIR__ . '/..' . '/../lib/Events/NodeRestoredEvent.php',
|
||||
'OCA\\Files_Trashbin\\Exceptions\\CopyRecursiveException' => __DIR__ . '/..' . '/../lib/Exceptions/CopyRecursiveException.php',
|
||||
'OCA\\Files_Trashbin\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php',
|
||||
'OCA\\Files_Trashbin\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
|
||||
|
52
apps/files_trashbin/lib/Events/BeforeNodeRestoredEvent.php
Normal file
52
apps/files_trashbin/lib/Events/BeforeNodeRestoredEvent.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Louis Chemineau <louis@chmn.me>
|
||||
*
|
||||
* @author Louis Chemineau <louis@chmn.me>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Files_Trashbin\Events;
|
||||
|
||||
use Exception;
|
||||
use OCP\Files\Events\Node\AbstractNodesEvent;
|
||||
use OCP\Files\Node;
|
||||
|
||||
/**
|
||||
* @since 28.0.0
|
||||
*/
|
||||
class BeforeNodeRestoredEvent extends AbstractNodesEvent {
|
||||
public function __construct(Node $source, Node $target, private bool &$run) {
|
||||
parent::__construct($source, $target);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return never
|
||||
*/
|
||||
public function abortOperation(\Throwable $ex = null) {
|
||||
$this->stopPropagation();
|
||||
$this->run = false;
|
||||
if ($ex !== null) {
|
||||
throw $ex;
|
||||
} else {
|
||||
throw new Exception('Operation aborted');
|
||||
}
|
||||
}
|
||||
}
|
38
apps/files_trashbin/lib/Events/NodeRestoredEvent.php
Normal file
38
apps/files_trashbin/lib/Events/NodeRestoredEvent.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Louis Chemineau <louis@chmn.me>
|
||||
*
|
||||
* @author Louis Chemineau <louis@chmn.me>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\Files_Trashbin\Events;
|
||||
|
||||
use OCP\Files\Events\Node\AbstractNodesEvent;
|
||||
use OCP\Files\Node;
|
||||
|
||||
/**
|
||||
* @since 28.0.0
|
||||
*/
|
||||
class NodeRestoredEvent extends AbstractNodesEvent {
|
||||
public function __construct(Node $source, Node $target) {
|
||||
parent::__construct($source, $target);
|
||||
}
|
||||
}
|
@ -44,19 +44,27 @@
|
||||
*/
|
||||
namespace OCA\Files_Trashbin;
|
||||
|
||||
use Exception;
|
||||
use OC\Files\Cache\Cache;
|
||||
use OC\Files\Cache\CacheEntry;
|
||||
use OC\Files\Cache\CacheQueryBuilder;
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Files\Node\File;
|
||||
use OC\Files\Node\Folder;
|
||||
use OC\Files\Node\NonExistingFile;
|
||||
use OC\Files\Node\NonExistingFolder;
|
||||
use OC\Files\ObjectStore\ObjectStoreStorage;
|
||||
use OC\Files\View;
|
||||
use OC_User;
|
||||
use OCA\Files_Trashbin\AppInfo\Application;
|
||||
use OCA\Files_Trashbin\Command\Expire;
|
||||
use OCA\Files_Trashbin\Events\BeforeNodeRestoredEvent;
|
||||
use OCA\Files_Trashbin\Events\NodeRestoredEvent;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Node;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use OCP\FilesMetadata\IFilesMetadataManager;
|
||||
@ -508,6 +516,21 @@ class Trashbin {
|
||||
if (!$view->isCreatable(dirname($target))) {
|
||||
throw new NotPermittedException("Can't restore trash item because the target folder is not writable");
|
||||
}
|
||||
|
||||
$sourcePath = Filesystem::normalizePath($file);
|
||||
$targetPath = Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename);
|
||||
|
||||
$sourceNode = self::getNodeForPath($sourcePath);
|
||||
$targetNode = self::getNodeForPath($targetPath);
|
||||
$run = true;
|
||||
$event = new BeforeNodeRestoredEvent($sourceNode, $targetNode, $run);
|
||||
$dispatcher = \OC::$server->get(IEventDispatcher::class);
|
||||
$dispatcher->dispatchTyped($event);
|
||||
|
||||
if (!$run) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$restoreResult = $view->rename($source, $target);
|
||||
|
||||
// handle the restore result
|
||||
@ -516,8 +539,13 @@ class Trashbin {
|
||||
$view->chroot('/' . $user . '/files');
|
||||
$view->touch('/' . $location . '/' . $uniqueFilename, $mtime);
|
||||
$view->chroot($fakeRoot);
|
||||
\OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', ['filePath' => Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename),
|
||||
'trashPath' => Filesystem::normalizePath($file)]);
|
||||
\OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', ['filePath' => $targetPath, 'trashPath' => $sourcePath]);
|
||||
|
||||
$sourceNode = self::getNodeForPath($sourcePath);
|
||||
$targetNode = self::getNodeForPath($targetPath);
|
||||
$event = new NodeRestoredEvent($sourceNode, $targetNode);
|
||||
$dispatcher = \OC::$server->get(IEventDispatcher::class);
|
||||
$dispatcher->dispatchTyped($event);
|
||||
|
||||
self::restoreVersions($view, $file, $filename, $uniqueFilename, $location, $timestamp);
|
||||
|
||||
@ -1151,4 +1179,33 @@ class Trashbin {
|
||||
}
|
||||
return $trashFilename;
|
||||
}
|
||||
|
||||
private static function getNodeForPath(string $path): Node {
|
||||
$user = OC_User::getUser();
|
||||
$rootFolder = \OC::$server->get(IRootFolder::class);
|
||||
|
||||
if ($user !== false) {
|
||||
$userFolder = $rootFolder->getUserFolder($user);
|
||||
/** @var Folder */
|
||||
$trashFolder = $userFolder->getParent()->get('files_trashbin/files');
|
||||
try {
|
||||
return $trashFolder->get($path);
|
||||
} catch (NotFoundException $ex) {
|
||||
}
|
||||
}
|
||||
|
||||
$view = \OC::$server->get(View::class);
|
||||
$fsView = Filesystem::getView();
|
||||
if ($fsView === null) {
|
||||
throw new Exception('View should not be null');
|
||||
}
|
||||
|
||||
$fullPath = $fsView->getAbsolutePath($path);
|
||||
|
||||
if (Filesystem::is_dir($path)) {
|
||||
return new NonExistingFolder($rootFolder, $view, $fullPath);
|
||||
} else {
|
||||
return new NonExistingFile($rootFolder, $view, $fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class HookConnector {
|
||||
$this->root->emit('\OC\Files', 'preDelete', [$node]);
|
||||
$this->dispatcher->dispatch('\OCP\Files::preDelete', new GenericEvent($node));
|
||||
|
||||
$event = new BeforeNodeDeletedEvent($node);
|
||||
$event = new BeforeNodeDeletedEvent($node, $arguments['run']);
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ class HookConnector {
|
||||
$this->root->emit('\OC\Files', 'preRename', [$source, $target]);
|
||||
$this->dispatcher->dispatch('\OCP\Files::preRename', new GenericEvent([$source, $target]));
|
||||
|
||||
$event = new BeforeNodeRenamedEvent($source, $target);
|
||||
$event = new BeforeNodeRenamedEvent($source, $target, $arguments['run']);
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,31 @@ declare(strict_types=1);
|
||||
*/
|
||||
namespace OCP\Files\Events\Node;
|
||||
|
||||
use Exception;
|
||||
use OCP\Files\Node;
|
||||
|
||||
/**
|
||||
* @since 20.0.0
|
||||
*/
|
||||
class BeforeNodeDeletedEvent extends AbstractNodeEvent {
|
||||
/**
|
||||
* @since 20.0.0
|
||||
*/
|
||||
public function __construct(Node $node, private bool &$run) {
|
||||
parent::__construct($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 28.0.0
|
||||
* @return never
|
||||
*/
|
||||
public function abortOperation(\Throwable $ex = null) {
|
||||
$this->stopPropagation();
|
||||
$this->run = false;
|
||||
if ($ex !== null) {
|
||||
throw $ex;
|
||||
} else {
|
||||
throw new Exception('Operation aborted');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,31 @@ declare(strict_types=1);
|
||||
*/
|
||||
namespace OCP\Files\Events\Node;
|
||||
|
||||
use Exception;
|
||||
use OCP\Files\Node;
|
||||
|
||||
/**
|
||||
* @since 20.0.0
|
||||
*/
|
||||
class BeforeNodeRenamedEvent extends AbstractNodesEvent {
|
||||
/**
|
||||
* @since 20.0.0
|
||||
*/
|
||||
public function __construct(Node $source, Node $target, private bool &$run) {
|
||||
parent::__construct($source, $target);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 28.0.0
|
||||
* @return never
|
||||
*/
|
||||
public function abortOperation(\Throwable $ex = null) {
|
||||
$this->stopPropagation();
|
||||
$this->run = false;
|
||||
if ($ex !== null) {
|
||||
throw $ex;
|
||||
} else {
|
||||
throw new Exception('Operation aborted');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
載入中…
新增問題並參考
Block a user