diff options
author | Faraz Samapoor <f.samapoor@gmail.com> | 2023-08-01 12:02:12 +0330 |
---|---|---|
committer | Faraz Samapoor <fsa@adlas.at> | 2023-08-01 12:39:10 +0330 |
commit | b353b3f4252c6e62f623609571d4a60fb1e1895d (patch) | |
tree | 2344e3777f78e3a89e5bcb0f3e2987ae5039dc19 /apps/files/lib | |
parent | 270035ff44d7c8054d065ad0ac8835854ac13522 (diff) | |
parent | 9241a38fc2ee6d285e96819389c7b941c60e7451 (diff) | |
download | nextcloud-server-b353b3f4252c6e62f623609571d4a60fb1e1895d.tar.gz nextcloud-server-b353b3f4252c6e62f623609571d4a60fb1e1895d.zip |
Merge branch 'master' into refactor_files_app_commands
Signed-off-by: Faraz Samapoor <f.samapoor@gmail.com>
Diffstat (limited to 'apps/files/lib')
-rw-r--r-- | apps/files/lib/Collaboration/Resources/Listener.php | 4 | ||||
-rw-r--r-- | apps/files/lib/Command/Copy.php | 133 | ||||
-rw-r--r-- | apps/files/lib/Command/Move.php | 122 | ||||
-rw-r--r-- | apps/files/lib/Command/Scan.php | 28 |
4 files changed, 284 insertions, 3 deletions
diff --git a/apps/files/lib/Collaboration/Resources/Listener.php b/apps/files/lib/Collaboration/Resources/Listener.php index 64dd693a4da..28ce5f87897 100644 --- a/apps/files/lib/Collaboration/Resources/Listener.php +++ b/apps/files/lib/Collaboration/Resources/Listener.php @@ -25,12 +25,12 @@ declare(strict_types=1); */ namespace OCA\Files\Collaboration\Resources; +use OCP\EventDispatcher\IEventDispatcher; use OCP\Server; use OCP\Collaboration\Resources\IManager; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; class Listener { - public static function register(EventDispatcherInterface $dispatcher): void { + public static function register(IEventDispatcher $dispatcher): void { $dispatcher->addListener('OCP\Share::postShare', [self::class, 'shareModification']); $dispatcher->addListener('OCP\Share::postUnshare', [self::class, 'shareModification']); $dispatcher->addListener('OCP\Share::postUnshareFromSelf', [self::class, 'shareModification']); diff --git a/apps/files/lib/Command/Copy.php b/apps/files/lib/Command/Copy.php new file mode 100644 index 00000000000..678c82a138f --- /dev/null +++ b/apps/files/lib/Command/Copy.php @@ -0,0 +1,133 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2023 Robin Appelman <robin@icewind.nl> + * + * @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\Command; + +use OC\Core\Command\Info\FileUtils; +use OCP\Files\Folder; +use OCP\Files\File; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; + +class Copy extends Command { + private FileUtils $fileUtils; + + public function __construct(FileUtils $fileUtils) { + $this->fileUtils = $fileUtils; + parent::__construct(); + } + + protected function configure(): void { + $this + ->setName('files:copy') + ->setDescription('Copy a file or folder') + ->addArgument('source', InputArgument::REQUIRED, "Source file id or path") + ->addArgument('target', InputArgument::REQUIRED, "Target path") + ->addOption('force', 'f', InputOption::VALUE_NONE, "Don't ask for confirmation and don't output any warnings") + ->addOption('no-target-directory', 'T', InputOption::VALUE_NONE, "When target path is folder, overwrite the folder instead of copying into the folder"); + } + + public function execute(InputInterface $input, OutputInterface $output): int { + $sourceInput = $input->getArgument('source'); + $targetInput = $input->getArgument('target'); + $force = $input->getOption('force'); + $noTargetDir = $input->getOption('no-target-directory'); + + $node = $this->fileUtils->getNode($sourceInput); + $targetNode = $this->fileUtils->getNode($targetInput); + + if (!$node) { + $output->writeln("<error>file $sourceInput not found</error>"); + return 1; + } + + $targetParentPath = dirname(rtrim($targetInput, '/')); + $targetParent = $this->fileUtils->getNode($targetParentPath); + if (!$targetParent) { + $output->writeln("<error>Target parent path $targetParentPath doesn't exist</error>"); + return 1; + } + + $wouldRequireDelete = false; + + if ($targetNode) { + if (!$targetNode->isUpdateable()) { + $output->writeln("<error>$targetInput isn't writable</error>"); + return 1; + } + + if ($targetNode instanceof Folder) { + if ($noTargetDir) { + if (!$force) { + $output->writeln("Warning: <info>$sourceInput</info> is a file, but <info>$targetInput</info> is a folder"); + } + $wouldRequireDelete = true; + } else { + $targetInput = $targetNode->getFullPath($node->getName()); + $targetNode = $this->fileUtils->getNode($targetInput); + } + } else { + if ($node instanceof Folder) { + if (!$force) { + $output->writeln("Warning: <info>$sourceInput</info> is a folder, but <info>$targetInput</info> is a file"); + } + $wouldRequireDelete = true; + } + } + + if ($wouldRequireDelete && $targetNode->getInternalPath() === '') { + $output->writeln("<error>Mount root can't be overwritten with a different type</error>"); + return 1; + } + + if ($wouldRequireDelete && !$targetNode->isDeletable()) { + $output->writeln("<error>$targetInput can't be deleted to be replaced with $sourceInput</error>"); + return 1; + } + + if (!$force && $targetNode) { + /** @var QuestionHelper $helper */ + $helper = $this->getHelper('question'); + + $question = new ConfirmationQuestion("<info>" . $targetInput . "</info> already exists, overwrite? [y/N] ", false); + if (!$helper->ask($input, $output, $question)) { + return 1; + } + } + } + + if ($wouldRequireDelete && $targetNode) { + $targetNode->delete(); + } + + $node->copy($targetInput); + + return 0; + } + +} diff --git a/apps/files/lib/Command/Move.php b/apps/files/lib/Command/Move.php new file mode 100644 index 00000000000..ac84dfa19b3 --- /dev/null +++ b/apps/files/lib/Command/Move.php @@ -0,0 +1,122 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2023 Robin Appelman <robin@icewind.nl> + * + * @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\Command; + +use OC\Core\Command\Info\FileUtils; +use OCP\Files\Folder; +use OCP\Files\File; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; + +class Move extends Command { + private FileUtils $fileUtils; + + public function __construct(FileUtils $fileUtils) { + $this->fileUtils = $fileUtils; + parent::__construct(); + } + + protected function configure(): void { + $this + ->setName('files:move') + ->setDescription('Move a file or folder') + ->addArgument('source', InputArgument::REQUIRED, "Source file id or path") + ->addArgument('target', InputArgument::REQUIRED, "Target path") + ->addOption('force', 'f', InputOption::VALUE_NONE, "Don't ask for configuration and don't output any warnings"); + } + + public function execute(InputInterface $input, OutputInterface $output): int { + $sourceInput = $input->getArgument('source'); + $targetInput = $input->getArgument('target'); + $force = $input->getOption('force'); + + $node = $this->fileUtils->getNode($sourceInput); + $targetNode = $this->fileUtils->getNode($targetInput); + + if (!$node) { + $output->writeln("<error>file $sourceInput not found</error>"); + return 1; + } + + $targetParentPath = dirname(rtrim($targetInput, '/')); + $targetParent = $this->fileUtils->getNode($targetParentPath); + if (!$targetParent) { + $output->writeln("<error>Target parent path $targetParentPath doesn't exist</error>"); + return 1; + } + + $wouldRequireDelete = false; + + if ($targetNode) { + if (!$targetNode->isUpdateable()) { + $output->writeln("<error>$targetInput already exists and isn't writable</error>"); + return 1; + } + + if ($node instanceof Folder && $targetNode instanceof File) { + $output->writeln("Warning: <info>$sourceInput</info> is a folder, but <info>$targetInput</info> is a file"); + $wouldRequireDelete = true; + } + + if ($node instanceof File && $targetNode instanceof Folder) { + $output->writeln("Warning: <info>$sourceInput</info> is a file, but <info>$targetInput</info> is a folder"); + $wouldRequireDelete = true; + } + + if ($wouldRequireDelete && $targetNode->getInternalPath() === '') { + $output->writeln("<error>Mount root can't be overwritten with a different type</error>"); + return 1; + } + + if ($wouldRequireDelete && !$targetNode->isDeletable()) { + $output->writeln("<error>$targetInput can't be deleted to be replaced with $sourceInput</error>"); + return 1; + } + + if (!$force) { + /** @var QuestionHelper $helper */ + $helper = $this->getHelper('question'); + + $question = new ConfirmationQuestion("<info>" . $targetInput . "</info> already exists, overwrite? [y/N] ", false); + if (!$helper->ask($input, $output, $question)) { + return 1; + } + } + } + + if ($wouldRequireDelete && $targetNode) { + $targetNode->delete(); + } + + $node->move($targetInput); + + return 0; + } + +} diff --git a/apps/files/lib/Command/Scan.php b/apps/files/lib/Command/Scan.php index 8c3574d7608..f5ac3627196 100644 --- a/apps/files/lib/Command/Scan.php +++ b/apps/files/lib/Command/Scan.php @@ -37,6 +37,9 @@ use OC\Core\Command\Base; use OC\Core\Command\InterruptedException; use OC\DB\Connection; use OC\DB\ConnectionAdapter; +use OCP\Files\Events\FileCacheUpdated; +use OCP\Files\Events\NodeAddedToCache; +use OCP\Files\Events\NodeRemovedFromCache; use OCP\Files\File; use OC\ForbiddenException; use OC\Metadata\MetadataManager; @@ -58,11 +61,16 @@ class Scan extends Base { protected int $foldersCounter = 0; protected int $filesCounter = 0; protected int $errorsCounter = 0; + protected int $newCounter = 0; + protected int $updatedCounter = 0; + protected int $removedCounter = 0; public function __construct( private IUserManager $userManager, private IRootFolder $rootFolder, - private MetadataManager $metadataManager + private MetadataManager $metadataManager, + private IEventDispatcher $eventDispatcher, + private LoggerInterface $logger, ) { parent::__construct(); } @@ -151,6 +159,16 @@ class Scan extends Base { ++$this->errorsCounter; }); + $this->eventDispatcher->addListener(NodeAddedToCache::class, function() { + ++$this->newCounter; + }); + $this->eventDispatcher->addListener(FileCacheUpdated::class, function() { + ++$this->updatedCounter; + }); + $this->eventDispatcher->addListener(NodeRemovedFromCache::class, function() { + ++$this->removedCounter; + }); + try { if ($backgroundScan) { $scanner->backgroundScan($path); @@ -268,9 +286,14 @@ class Scan extends Base { // Stop the timer $this->execTime += microtime(true); + $this->logger->info("Completed scan of {$this->filesCounter} files in {$this->foldersCounter} folder. Found {$this->newCounter} new, {$this->updatedCounter} updated and {$this->removedCounter} removed items"); + $headers = [ 'Folders', 'Files', + 'New', + 'Updated', + 'Removed', 'Errors', 'Elapsed time', ]; @@ -278,6 +301,9 @@ class Scan extends Base { $rows = [ $this->foldersCounter, $this->filesCounter, + $this->newCounter, + $this->updatedCounter, + $this->removedCounter, $this->errorsCounter, $niceDate, ]; |