diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml index c710df2fb1723..24ffe49a02f0b 100644 --- a/apps/files/appinfo/info.xml +++ b/apps/files/appinfo/info.xml @@ -46,6 +46,8 @@ OCA\Files\Command\Delete OCA\Files\Command\Copy OCA\Files\Command\Move + OCA\Files\Command\Mkdir + OCA\Files\Command\Touch OCA\Files\Command\SanitizeFilenames OCA\Files\Command\Mount\Refresh OCA\Files\Command\Mount\ListMounts diff --git a/apps/files/composer/composer/autoload_classmap.php b/apps/files/composer/composer/autoload_classmap.php index 6f4d17a78f13b..2e2cb6da8a601 100644 --- a/apps/files/composer/composer/autoload_classmap.php +++ b/apps/files/composer/composer/autoload_classmap.php @@ -32,6 +32,7 @@ 'OCA\\Files\\Command\\Delete' => $baseDir . '/../lib/Command/Delete.php', 'OCA\\Files\\Command\\DeleteOrphanedFiles' => $baseDir . '/../lib/Command/DeleteOrphanedFiles.php', 'OCA\\Files\\Command\\Get' => $baseDir . '/../lib/Command/Get.php', + 'OCA\\Files\\Command\\Mkdir' => $baseDir . '/../lib/Command/Mkdir.php', 'OCA\\Files\\Command\\Mount\\ListMounts' => $baseDir . '/../lib/Command/Mount/ListMounts.php', 'OCA\\Files\\Command\\Mount\\Refresh' => $baseDir . '/../lib/Command/Mount/Refresh.php', 'OCA\\Files\\Command\\Move' => $baseDir . '/../lib/Command/Move.php', @@ -49,6 +50,7 @@ 'OCA\\Files\\Command\\SanitizeFilenames' => $baseDir . '/../lib/Command/SanitizeFilenames.php', 'OCA\\Files\\Command\\Scan' => $baseDir . '/../lib/Command/Scan.php', 'OCA\\Files\\Command\\ScanAppData' => $baseDir . '/../lib/Command/ScanAppData.php', + 'OCA\\Files\\Command\\Touch' => $baseDir . '/../lib/Command/Touch.php', 'OCA\\Files\\Command\\TransferOwnership' => $baseDir . '/../lib/Command/TransferOwnership.php', 'OCA\\Files\\Command\\WindowsCompatibleFilenames' => $baseDir . '/../lib/Command/WindowsCompatibleFilenames.php', 'OCA\\Files\\ConfigLexicon' => $baseDir . '/../lib/ConfigLexicon.php', diff --git a/apps/files/composer/composer/autoload_static.php b/apps/files/composer/composer/autoload_static.php index 9bcefe76c340f..d7786af11072c 100644 --- a/apps/files/composer/composer/autoload_static.php +++ b/apps/files/composer/composer/autoload_static.php @@ -47,6 +47,7 @@ class ComposerStaticInitFiles 'OCA\\Files\\Command\\Delete' => __DIR__ . '/..' . '/../lib/Command/Delete.php', 'OCA\\Files\\Command\\DeleteOrphanedFiles' => __DIR__ . '/..' . '/../lib/Command/DeleteOrphanedFiles.php', 'OCA\\Files\\Command\\Get' => __DIR__ . '/..' . '/../lib/Command/Get.php', + 'OCA\\Files\\Command\\Mkdir' => __DIR__ . '/..' . '/../lib/Command/Mkdir.php', 'OCA\\Files\\Command\\Mount\\ListMounts' => __DIR__ . '/..' . '/../lib/Command/Mount/ListMounts.php', 'OCA\\Files\\Command\\Mount\\Refresh' => __DIR__ . '/..' . '/../lib/Command/Mount/Refresh.php', 'OCA\\Files\\Command\\Move' => __DIR__ . '/..' . '/../lib/Command/Move.php', @@ -64,6 +65,7 @@ class ComposerStaticInitFiles 'OCA\\Files\\Command\\SanitizeFilenames' => __DIR__ . '/..' . '/../lib/Command/SanitizeFilenames.php', 'OCA\\Files\\Command\\Scan' => __DIR__ . '/..' . '/../lib/Command/Scan.php', 'OCA\\Files\\Command\\ScanAppData' => __DIR__ . '/..' . '/../lib/Command/ScanAppData.php', + 'OCA\\Files\\Command\\Touch' => __DIR__ . '/..' . '/../lib/Command/Touch.php', 'OCA\\Files\\Command\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Command/TransferOwnership.php', 'OCA\\Files\\Command\\WindowsCompatibleFilenames' => __DIR__ . '/..' . '/../lib/Command/WindowsCompatibleFilenames.php', 'OCA\\Files\\ConfigLexicon' => __DIR__ . '/..' . '/../lib/ConfigLexicon.php', diff --git a/apps/files/lib/Command/Delete.php b/apps/files/lib/Command/Delete.php index 0558a6778b795..627b993bcce5d 100644 --- a/apps/files/lib/Command/Delete.php +++ b/apps/files/lib/Command/Delete.php @@ -10,6 +10,7 @@ use OC\Core\Command\Info\FileUtils; use OCA\Files_Sharing\SharedStorage; +use OCA\Files_Trashbin\Trash\ITrashManager; use OCP\Files\Folder; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\QuestionHelper; @@ -21,7 +22,8 @@ class Delete extends Command { public function __construct( - private FileUtils $fileUtils, + private readonly FileUtils $fileUtils, + private readonly ?ITrashManager $trashManager = null, ) { parent::__construct(); } @@ -32,7 +34,8 @@ protected function configure(): void { ->setName('files:delete') ->setDescription('Delete a file or folder') ->addArgument('file', InputArgument::REQUIRED, 'File id or path') - ->addOption('force', 'f', InputOption::VALUE_NONE, "Don't ask for configuration and don't output any warnings"); + ->addOption('force', 'f', InputOption::VALUE_NONE, "Don't ask for configuration and don't output any warnings") + ->addOption('skip-trash', null, InputOption::VALUE_NONE, 'Bypass the trashbin when deleting the file or folder'); } #[\Override] @@ -40,6 +43,7 @@ public function execute(InputInterface $input, OutputInterface $output): int { $fileInput = $input->getArgument('file'); $inputIsId = is_numeric($fileInput); $force = $input->getOption('force'); + $skipTrash = $input->getOption('skip-trash'); $node = $this->fileUtils->getNode($fileInput); if (!$node) { @@ -90,6 +94,10 @@ public function execute(InputInterface $input, OutputInterface $output): int { if ($deleteConfirmed) { if ($node->isDeletable()) { + if ($skipTrash && $this->trashManager) { + $this->trashManager->pauseTrash(); + } + $node->delete(); } else { $output->writeln('File cannot be deleted, insufficient permissions.'); diff --git a/apps/files/lib/Command/Mkdir.php b/apps/files/lib/Command/Mkdir.php new file mode 100644 index 0000000000000..a7c47ccd362b7 --- /dev/null +++ b/apps/files/lib/Command/Mkdir.php @@ -0,0 +1,54 @@ +setName('files:mkdir') + ->setDescription('Create a new directory') + ->addArgument('path', InputArgument::REQUIRED, 'Target Nextcloud path for the new folder'); + } + + #[\Override] + public function execute(InputInterface $input, OutputInterface $output): int { + $path = $input->getArgument('path'); + $node = $this->fileUtils->getNode($path); + + if ($node instanceof Folder) { + $output->writeln("$path already exists"); + return self::SUCCESS; + } + if ($node instanceof File) { + $output->writeln("$path is a file"); + return self::FAILURE; + } + + $this->rootFolder->newFolder($path); + + return self::SUCCESS; + } +} diff --git a/apps/files/lib/Command/Put.php b/apps/files/lib/Command/Put.php index 6de11c7a881d8..7907a3c6d2ee0 100644 --- a/apps/files/lib/Command/Put.php +++ b/apps/files/lib/Command/Put.php @@ -62,6 +62,11 @@ public function execute(InputInterface $input, OutputInterface $output): int { } stream_copy_to_stream($source, $target); } else { + $parentPath = dirname($fileOutput); + if (!$this->rootFolder->nodeExists($parentPath)) { + $this->rootFolder->newFolder($parentPath); + } + $this->rootFolder->newFile($fileOutput, $source); } return self::SUCCESS; diff --git a/apps/files/lib/Command/Touch.php b/apps/files/lib/Command/Touch.php new file mode 100644 index 0000000000000..217ce721aa4b4 --- /dev/null +++ b/apps/files/lib/Command/Touch.php @@ -0,0 +1,93 @@ +setName('files:touch') + ->setDescription('Update the last modified date of a file or folder, or create an empty file') + ->addArgument('file', InputArgument::REQUIRED, 'Nextcloud path or fileid for the file or folder to change the modified date of') + ->addOption('date', 'd', InputOption::VALUE_REQUIRED, 'Time to use as modified date instead of the current time. Acceptable formats are: ISO8601, "YYYY-MM-DD" and Unix time in seconds.') + ->addOption('no-create', 'c', InputOption::VALUE_NONE, 'Don\'t create an empty file if the target path doesn\'t exist'); + } + + #[\Override] + public function execute(InputInterface $input, OutputInterface $output): int { + $fileInput = $input->getArgument('file'); + $node = $this->fileUtils->getNode($fileInput); + $date = $input->getOption('date'); + $noCreate = $input->getOption('no-create'); + + if (!$node) { + if ($noCreate || is_numeric($fileInput)) { + $output->writeln("$fileInput doesn't exist"); + return self::FAILURE; + } + $node = $this->rootFolder->newFile($fileInput); + } + + if ($date) { + $mtime = $this->parseDateOption($date); + if (!$mtime) { + $output->writeln("Invalid date format '$date'. Acceptable formats are: ISO8601, \"YYYY-MM-DD\" and Unix time in seconds."); + } + } else { + $mtime = $this->clock->now(); + } + $node->touch($mtime->getTimestamp()); + + return self::SUCCESS; + } + + /** + * @return \DateTimeImmutable|false + */ + protected function parseDateOption(string $input) { + $date = false; + + // Handle Unix timestamp + if (filter_var($input, FILTER_VALIDATE_INT)) { + return new DateTimeImmutable('@' . $input); + } + + // ISO8601 + $date = DateTimeImmutable::createFromFormat(DateTimeImmutable::ATOM, $input); + if ($date) { + return $date; + } + // With fractions + $date = DateTimeImmutable::createFromFormat('Y-m-d\TH:i:s.uP', $input); + if ($date) { + return $date; + } + + // YYYY-MM-DD + return DateTimeImmutable::createFromFormat('!Y-m-d', $input); + } +}