diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml
index a594278a787a3..66bc84fb52a5f 100644
--- a/apps/files/appinfo/info.xml
+++ b/apps/files/appinfo/info.xml
@@ -45,7 +45,8 @@
OCA\Files\Command\Object\Get
OCA\Files\Command\Object\Put
OCA\Files\Command\Object\Multi\Users
- OCA\Files\Command\Object\Multi\Rename
+ OCA\Files\Command\Object\Multi\Move
+ OCA\Files\Command\Object\Multi\PreMigrate
diff --git a/apps/files/composer/composer/autoload_classmap.php b/apps/files/composer/composer/autoload_classmap.php
index 97c21fb455068..3eaf12900193a 100644
--- a/apps/files/composer/composer/autoload_classmap.php
+++ b/apps/files/composer/composer/autoload_classmap.php
@@ -34,7 +34,8 @@
'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\\PreMigrate' => $baseDir . '/../lib/Command/Object/Multi/PreMigrate.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',
diff --git a/apps/files/composer/composer/autoload_static.php b/apps/files/composer/composer/autoload_static.php
index c406fadb948ba..5a919d03ad7ce 100644
--- a/apps/files/composer/composer/autoload_static.php
+++ b/apps/files/composer/composer/autoload_static.php
@@ -49,7 +49,8 @@ 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\\PreMigrate' => __DIR__ . '/..' . '/../lib/Command/Object/Multi/PreMigrate.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',
diff --git a/apps/files/lib/Command/Object/Multi/Move.php b/apps/files/lib/Command/Object/Multi/Move.php
new file mode 100644
index 0000000000000..a12c42e743faf
--- /dev/null
+++ b/apps/files/lib/Command/Object/Multi/Move.php
@@ -0,0 +1,76 @@
+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('Unknown object store configuration: ' . $objectStore . '');
+ return 1;
+ }
+
+ if ($input->getOption('all')) {
+ $users = $this->userManager->getSeenUsers();
+ } elseif ($userId = $input->getOption('user')) {
+ $user = $this->userManager->get($userId);
+ if (!$user) {
+ $output->writeln('User ' . $userId . ' not found');
+ return 1;
+ }
+ $users = new \ArrayIterator([$user]);
+ } else {
+ $output->writeln('Please specify a user id with --user or --all for all users');
+ return 1;
+ }
+
+ $count = 0;
+ foreach ($users as $user) {
+ if (!$input->getOption('dry-run')) {
+ $this->config->setUserValue($user->getUID(), 'homeobjectstore', 'objectstore', $objectStore);
+ }
+ $count++;
+ }
+ $output->writeln('Moved ' . $count . ' users to ' . $objectStore . ' object store');
+
+ return 0;
+ }
+}
diff --git a/apps/files/lib/Command/Object/Multi/PreMigrate.php b/apps/files/lib/Command/Object/Multi/PreMigrate.php
new file mode 100644
index 0000000000000..aba38a3ac3611
--- /dev/null
+++ b/apps/files/lib/Command/Object/Multi/PreMigrate.php
@@ -0,0 +1,76 @@
+setName('files:object:multi:pre-migrate')
+ ->setDescription('Assign a configured object store to users who don\'t have one assigned yet.')
+ ->addOption('object-store', 'o', InputOption::VALUE_REQUIRED, 'The name of the configured object store')
+ ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'The userId of the user to assign the object store')
+ ->addOption('all', 'a', InputOption::VALUE_NONE, 'Assign the object store to all users');
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output): int {
+ $objectStore = $input->getOption('object-store');
+ if (!$objectStore) {
+ $output->writeln('Please specify the object store');
+ return 1;
+ }
+
+ $configs = $this->objectStoreConfig->getObjectStoreConfigs();
+ if (!isset($configs[$objectStore])) {
+ $output->writeln('Unknown object store configuration: ' . $objectStore . '');
+ return 1;
+ }
+
+ if ($input->getOption('all')) {
+ $users = $this->userManager->getSeenUsers();
+ } elseif ($userId = $input->getOption('user')) {
+ $user = $this->userManager->get($userId);
+ if (!$user) {
+ $output->writeln('User ' . $userId . ' not found');
+ return 1;
+ }
+ $users = new \ArrayIterator([$user]);
+ } else {
+ $output->writeln('Please specify a user id with --user or --all for all users');
+ return 1;
+ }
+
+ $count = 0;
+ foreach ($users as $user) {
+ if (!$this->config->getUserValue($user->getUID(), 'homeobjectstore', 'objectstore', null)) {
+ $this->config->setUserValue($user->getUID(), 'homeobjectstore', 'objectstore', $objectStore);
+ $count++;
+ }
+ }
+ $output->writeln('Assigned object store ' . $objectStore . ' to ' . $count . ' users');
+
+ return 0;
+ }
+}
diff --git a/apps/files/lib/Command/Object/Multi/Rename.php b/apps/files/lib/Command/Object/Multi/Rename.php
deleted file mode 100644
index 078ed54c198b6..0000000000000
--- a/apps/files/lib/Command/Object/Multi/Rename.php
+++ /dev/null
@@ -1,108 +0,0 @@
-
- * 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\IDBConnection;
-use Symfony\Component\Console\Helper\QuestionHelper;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Question\ConfirmationQuestion;
-
-class Rename extends Base {
- public function __construct(
- private IDBConnection $connection,
- private PrimaryObjectStoreConfig $objectStoreConfig,
- private IConfig $config,
- ) {
- parent::__construct();
- }
-
- protected function configure(): void {
- parent::configure();
- $this
- ->setName('files:object:multi:rename-config')
- ->setDescription('Rename an object store configuration and move all users over to the new configuration,')
- ->addArgument('source', InputArgument::REQUIRED, 'Object store configuration to rename')
- ->addArgument('target', InputArgument::REQUIRED, 'New name for the object store configuration');
- }
-
- public function execute(InputInterface $input, OutputInterface $output): int {
- $source = $input->getArgument('source');
- $target = $input->getArgument('target');
-
- $configs = $this->objectStoreConfig->getObjectStoreConfigs();
- if (!isset($configs[$source])) {
- $output->writeln('Unknown object store configuration: ' . $source . '');
- return 1;
- }
-
- if ($source === 'root') {
- $output->writeln('Renaming the root configuration is not supported.');
- return 1;
- }
-
- if ($source === 'default') {
- $output->writeln('Renaming the default configuration is not supported.');
- return 1;
- }
-
- if (!isset($configs[$target])) {
- $output->writeln('Target object store configuration ' . $target . ' doesn\'t exist yet.');
- $output->writeln('The target configuration can be created automatically.');
- $output->writeln('However, as this depends on modifying the config.php, this only works as long as the instance runs on a single node or all nodes in a clustered setup have a shared config file (such as from a shared network mount).');
- $output->writeln('If the different nodes have a separate copy of the config.php file, the automatic object store configuration creation will lead to the configuration going out of sync.');
- $output->writeln('If these requirements are not met, you can manually create the target object store configuration in each node\'s configuration before running the command.');
- $output->writeln('');
- $output->writeln('Failure to check these requirements will lead to data loss for users.');
-
- /** @var QuestionHelper $helper */
- $helper = $this->getHelper('question');
- $question = new ConfirmationQuestion('Automatically create target object store configuration? [y/N] ', false);
- if ($helper->ask($input, $output, $question)) {
- $configs[$target] = $configs[$source];
-
- // update all aliases
- foreach ($configs as &$config) {
- if ($config === $source) {
- $config = $target;
- }
- }
- $this->config->setSystemValue('objectstore', $configs);
- } else {
- return 0;
- }
- } elseif (($configs[$source] !== $configs[$target]) || $configs[$source] !== $target) {
- $output->writeln('Source and target configuration differ.');
- $output->writeln('');
- $output->writeln('To ensure proper migration of users, the source and target configuration must be the same to ensure that the objects for the moved users exist on the target configuration.');
- $output->writeln('The usual migration process consists of creating a clone of the old configuration, moving the users from the old configuration to the new one, and then adjust the old configuration that is longer used.');
- return 1;
- }
-
- $query = $this->connection->getQueryBuilder();
- $query->update('preferences')
- ->set('configvalue', $query->createNamedParameter($target))
- ->where($query->expr()->eq('appid', $query->createNamedParameter('homeobjectstore')))
- ->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('objectstore')))
- ->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter($source)));
- $count = $query->executeStatement();
-
- if ($count > 0) {
- $output->writeln('Moved ' . $count . ' users');
- } else {
- $output->writeln('No users moved');
- }
-
- return 0;
- }
-}
diff --git a/apps/files/lib/Command/Object/Multi/Users.php b/apps/files/lib/Command/Object/Multi/Users.php
index a6cfa0326efa5..3d42165cca104 100644
--- a/apps/files/lib/Command/Object/Multi/Users.php
+++ b/apps/files/lib/Command/Object/Multi/Users.php
@@ -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("User $userId not found");
@@ -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("No option given. Please specify a user id with --user to show the mapping for the user or --all for all users");
+ return 0;
}
}