Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/files/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<command>OCA\Files\Command\Object\Get</command>
<command>OCA\Files\Command\Object\Put</command>
<command>OCA\Files\Command\Object\Multi\Users</command>
<command>OCA\Files\Command\Object\Multi\Rename</command>
<command>OCA\Files\Command\Object\Multi\Move</command>
</commands>

<activity>
Expand Down
2 changes: 1 addition & 1 deletion apps/files/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
'OCA\\Files\\Command\\Move' => $baseDir . '/../lib/Command/Move.php',
'OCA\\Files\\Command\\Object\\Delete' => $baseDir . '/../lib/Command/Object/Delete.php',
'OCA\\Files\\Command\\Object\\Get' => $baseDir . '/../lib/Command/Object/Get.php',
'OCA\\Files\\Command\\Object\\Multi\\Rename' => $baseDir . '/../lib/Command/Object/Multi/Rename.php',
'OCA\\Files\\Command\\Object\\Multi\\Move' => $baseDir . '/../lib/Command/Object/Multi/Move.php',
'OCA\\Files\\Command\\Object\\Multi\\Users' => $baseDir . '/../lib/Command/Object/Multi/Users.php',
'OCA\\Files\\Command\\Object\\ObjectUtil' => $baseDir . '/../lib/Command/Object/ObjectUtil.php',
'OCA\\Files\\Command\\Object\\Put' => $baseDir . '/../lib/Command/Object/Put.php',
Expand Down
2 changes: 1 addition & 1 deletion apps/files/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class ComposerStaticInitFiles
'OCA\\Files\\Command\\Move' => __DIR__ . '/..' . '/../lib/Command/Move.php',
'OCA\\Files\\Command\\Object\\Delete' => __DIR__ . '/..' . '/../lib/Command/Object/Delete.php',
'OCA\\Files\\Command\\Object\\Get' => __DIR__ . '/..' . '/../lib/Command/Object/Get.php',
'OCA\\Files\\Command\\Object\\Multi\\Rename' => __DIR__ . '/..' . '/../lib/Command/Object/Multi/Rename.php',
'OCA\\Files\\Command\\Object\\Multi\\Move' => __DIR__ . '/..' . '/../lib/Command/Object/Multi/Move.php',
'OCA\\Files\\Command\\Object\\Multi\\Users' => __DIR__ . '/..' . '/../lib/Command/Object/Multi/Users.php',
'OCA\\Files\\Command\\Object\\ObjectUtil' => __DIR__ . '/..' . '/../lib/Command/Object/ObjectUtil.php',
'OCA\\Files\\Command\\Object\\Put' => __DIR__ . '/..' . '/../lib/Command/Object/Put.php',
Expand Down
76 changes: 76 additions & 0 deletions apps/files/lib/Command/Object/Multi/Move.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files\Command\Object\Multi;

use OC\Core\Command\Base;
use OC\Files\ObjectStore\PrimaryObjectStoreConfig;
use OCP\IConfig;
use OCP\IUserManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class Move extends Base {
public function __construct(
private PrimaryObjectStoreConfig $objectStoreConfig,
private IUserManager $userManager,
private IConfig $config,
) {
parent::__construct();
}

protected function configure(): void {
parent::configure();
$this
->setName('files:object:multi:move')
->setDescription('Migrate user to specified object-store')
->addOption('object-store', 'b', InputOption::VALUE_REQUIRED, 'The name of the object store')
->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'The user to migrate')
->addOption('all', 'a', InputOption::VALUE_NONE, 'Move all users to specified object-store')
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Run command without commiting any changes');
}

public function execute(InputInterface $input, OutputInterface $output): int {
$objectStore = $input->getOption('object-store');
if (!$objectStore) {
$output->writeln('Please specify the object store');
}

$configs = $this->objectStoreConfig->getObjectStoreConfigs();
if (!isset($configs[$objectStore])) {
$output->writeln('<error>Unknown object store configuration: ' . $objectStore . '</error>');
return 1;
}

if ($input->getOption('all')) {
$users = $this->userManager->getSeenUsers();
} elseif ($userId = $input->getOption('user')) {
$user = $this->userManager->get($userId);
if (!$user) {
$output->writeln('<error>User ' . $userId . ' not found</error>');
return 1;
}
$users = new \ArrayIterator([$user]);
} else {
$output->writeln('<comment>Please specify a user id with --user or --all for all users</comment>');
return 1;
}

$count = 0;
foreach ($users as $user) {
if (!$input->getOption('dry-run')) {
$this->config->setUserValue($user->getUID(), 'homeobjectstore', 'objectstore', $objectStore);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we go with this version of the move command, I think it makes sense to validate that the target object store is valid for the user.

We could get the fileid for one of the user's files (if the user has at least one file) and check if an object with the matching urn exists it the target object store.

Without any checks the command would risk being a big footgun.

}
$count++;
}
$output->writeln('Moved <info>' . $count . '</info> users to ' . $objectStore . ' object store');

return 0;
}
}
108 changes: 0 additions & 108 deletions apps/files/lib/Command/Object/Multi/Rename.php

This file was deleted.

10 changes: 7 additions & 3 deletions apps/files/lib/Command/Object/Multi/Users.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,14 @@ protected function configure(): void {
->setDescription('Get the mapping between users and object store buckets')
->addOption('bucket', 'b', InputOption::VALUE_REQUIRED, 'Only list users using the specified bucket')
->addOption('object-store', 'o', InputOption::VALUE_REQUIRED, 'Only list users using the specified object store configuration')
->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'Only show the mapping for the specified user, ignores all other options');
->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'Only show the mapping for the specified user, ignores all other options')
->addOption('all', 'a', InputOption::VALUE_NONE, 'Show the mapping for all users');
}

public function execute(InputInterface $input, OutputInterface $output): int {
if ($userId = $input->getOption('user')) {
if ($input->getOption('all')) {
$users = $this->userManager->getSeenUsers();
} elseif ($userId = $input->getOption('user')) {
$user = $this->userManager->get($userId);
if (!$user) {
$output->writeln("<error>User $userId not found</error>");
Expand All @@ -57,7 +60,8 @@ public function execute(InputInterface $input, OutputInterface $output): int {
$this->config->getUsersForUserValue('homeobjectstore', 'objectstore', $objectStore)
));
} else {
$users = $this->userManager->getSeenUsers();
$output->writeln("<comment>No option given. Please specify a user id with --user to show the mapping for the user or --all for all users</comment>");
return 0;
}
}

Expand Down
Loading