Move.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OCA\Files\Command;
  8. use OC\Core\Command\Info\FileUtils;
  9. use OCP\Files\File;
  10. use OCP\Files\Folder;
  11. use Symfony\Component\Console\Command\Command;
  12. use Symfony\Component\Console\Helper\QuestionHelper;
  13. use Symfony\Component\Console\Input\InputArgument;
  14. use Symfony\Component\Console\Input\InputInterface;
  15. use Symfony\Component\Console\Input\InputOption;
  16. use Symfony\Component\Console\Output\OutputInterface;
  17. use Symfony\Component\Console\Question\ConfirmationQuestion;
  18. class Move extends Command {
  19. private FileUtils $fileUtils;
  20. public function __construct(FileUtils $fileUtils) {
  21. $this->fileUtils = $fileUtils;
  22. parent::__construct();
  23. }
  24. protected function configure(): void {
  25. $this
  26. ->setName('files:move')
  27. ->setDescription('Move a file or folder')
  28. ->addArgument('source', InputArgument::REQUIRED, 'Source file id or path')
  29. ->addArgument('target', InputArgument::REQUIRED, 'Target path')
  30. ->addOption('force', 'f', InputOption::VALUE_NONE, "Don't ask for configuration and don't output any warnings");
  31. }
  32. public function execute(InputInterface $input, OutputInterface $output): int {
  33. $sourceInput = $input->getArgument('source');
  34. $targetInput = $input->getArgument('target');
  35. $force = $input->getOption('force');
  36. $node = $this->fileUtils->getNode($sourceInput);
  37. $targetNode = $this->fileUtils->getNode($targetInput);
  38. if (!$node) {
  39. $output->writeln("<error>file $sourceInput not found</error>");
  40. return 1;
  41. }
  42. $targetParentPath = dirname(rtrim($targetInput, '/'));
  43. $targetParent = $this->fileUtils->getNode($targetParentPath);
  44. if (!$targetParent) {
  45. $output->writeln("<error>Target parent path $targetParentPath doesn't exist</error>");
  46. return 1;
  47. }
  48. $wouldRequireDelete = false;
  49. if ($targetNode) {
  50. if (!$targetNode->isUpdateable()) {
  51. $output->writeln("<error>$targetInput already exists and isn't writable</error>");
  52. return 1;
  53. }
  54. if ($node instanceof Folder && $targetNode instanceof File) {
  55. $output->writeln("Warning: <info>$sourceInput</info> is a folder, but <info>$targetInput</info> is a file");
  56. $wouldRequireDelete = true;
  57. }
  58. if ($node instanceof File && $targetNode instanceof Folder) {
  59. $output->writeln("Warning: <info>$sourceInput</info> is a file, but <info>$targetInput</info> is a folder");
  60. $wouldRequireDelete = true;
  61. }
  62. if ($wouldRequireDelete && $targetNode->getInternalPath() === '') {
  63. $output->writeln("<error>Mount root can't be overwritten with a different type</error>");
  64. return 1;
  65. }
  66. if ($wouldRequireDelete && !$targetNode->isDeletable()) {
  67. $output->writeln("<error>$targetInput can't be deleted to be replaced with $sourceInput</error>");
  68. return 1;
  69. }
  70. if (!$force) {
  71. /** @var QuestionHelper $helper */
  72. $helper = $this->getHelper('question');
  73. $question = new ConfirmationQuestion('<info>' . $targetInput . '</info> already exists, overwrite? [y/N] ', false);
  74. if (!$helper->ask($input, $output, $question)) {
  75. return 1;
  76. }
  77. }
  78. }
  79. if ($wouldRequireDelete && $targetNode) {
  80. $targetNode->delete();
  81. }
  82. $node->move($targetInput);
  83. return 0;
  84. }
  85. }