diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index 10f78d8332a2b..0295529dd26f9 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -50,7 +50,6 @@ OCA\DAV\Command\CreateCalendar OCA\DAV\Command\DeleteCalendar OCA\DAV\Command\MoveCalendar - OCA\DAV\Command\ListCalendars OCA\DAV\Command\RetentionCleanupCommand OCA\DAV\Command\SendEventReminders OCA\DAV\Command\SyncBirthdayCalendar diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index 580918a645008..f3ed664941738 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -52,6 +52,7 @@ use OCA\DAV\CardDAV\ContactsManager; use OCA\DAV\CardDAV\PhotoCache; use OCA\DAV\CardDAV\SyncService; +use OCA\DAV\Command\ListCalendars; use OCA\DAV\Events\AddressBookCreatedEvent; use OCA\DAV\Events\AddressBookDeletedEvent; use OCA\DAV\Events\AddressBookShareUpdatedEvent; @@ -122,6 +123,11 @@ public function register(IRegistrationContext $context): void { */ $context->registerCapability(Capabilities::class); + /** + * Register commands + */ + $context->registerCommand(ListCalendars::class); + /* * Register Search Providers */ diff --git a/apps/dav/lib/Command/ListCalendars.php b/apps/dav/lib/Command/ListCalendars.php index 35581c2d4b21e..e9436544e8962 100644 --- a/apps/dav/lib/Command/ListCalendars.php +++ b/apps/dav/lib/Command/ListCalendars.php @@ -1,4 +1,7 @@ userManager = $userManager; $this->caldav = $caldav; } - protected function configure() { - $this - ->setName('dav:list-calendars') - ->setDescription('List all calendars of a user') - ->addArgument('uid', - InputArgument::REQUIRED, - 'User for whom all calendars will be listed'); + public function getName(): string { + return 'list-calendars'; + } + + public function getDescription(): string { + return 'List all calendars of a user'; + } + + public function configure(IConfiguration $configuration): void { + $configuration->addArgument( + 'uid', + true, + 'User for whom all calendars will be listed' + ); } - protected function execute(InputInterface $input, OutputInterface $output): int { + public function execute(IInput $input, IOutput $output): int { $user = $input->getArgument('uid'); if (!$this->userManager->userExists($user)) { throw new \InvalidArgumentException("User <$user> is unknown."); @@ -92,11 +98,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if (count($calendarTableData) > 0) { - $table = new Table($output); - $table->setHeaders(['uri', 'displayname', 'owner\'s userid', 'owner\'s displayname', 'writable']) - ->setRows($calendarTableData); + //$table = new Table($output); + //$table->setHeaders(['uri', 'displayname', 'owner\'s userid', 'owner\'s displayname', 'writable']) + // ->setRows($calendarTableData); - $table->render(); + //$table->render(); } else { $output->writeln("User <$user> has no calendars"); } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index f12ee47642af9..15e0ec73589c7 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -150,8 +150,12 @@ 'OCP\\Collaboration\\Resources\\IResource' => $baseDir . '/lib/public/Collaboration/Resources/IResource.php', 'OCP\\Collaboration\\Resources\\LoadAdditionalScriptsEvent' => $baseDir . '/lib/public/Collaboration/Resources/LoadAdditionalScriptsEvent.php', 'OCP\\Collaboration\\Resources\\ResourceException' => $baseDir . '/lib/public/Collaboration/Resources/ResourceException.php', + 'OCP\\Command\\Command' => $baseDir . '/lib/public/Command/Command.php', 'OCP\\Command\\IBus' => $baseDir . '/lib/public/Command/IBus.php', 'OCP\\Command\\ICommand' => $baseDir . '/lib/public/Command/ICommand.php', + 'OCP\\Command\\IConfiguration' => $baseDir . '/lib/public/Command/IConfiguration.php', + 'OCP\\Command\\IInput' => $baseDir . '/lib/public/Command/IInput.php', + 'OCP\\Command\\IOutput' => $baseDir . '/lib/public/Command/IOutput.php', 'OCP\\Comments\\CommentsEntityEvent' => $baseDir . '/lib/public/Comments/CommentsEntityEvent.php', 'OCP\\Comments\\CommentsEvent' => $baseDir . '/lib/public/Comments/CommentsEvent.php', 'OCP\\Comments\\IComment' => $baseDir . '/lib/public/Comments/IComment.php', @@ -827,6 +831,10 @@ 'OC\\Command\\CronBus' => $baseDir . '/lib/private/Command/CronBus.php', 'OC\\Command\\FileAccess' => $baseDir . '/lib/private/Command/FileAccess.php', 'OC\\Command\\QueueBus' => $baseDir . '/lib/private/Command/QueueBus.php', + 'OC\\Command\\SymfonyCommandAdapter' => $baseDir . '/lib/private/Command/SymfonyCommandAdapter.php', + 'OC\\Command\\SymfonyConfigurationAdapter' => $baseDir . '/lib/private/Command/SymfonyConfigurationAdapter.php', + 'OC\\Command\\SymfonyInputAdapter' => $baseDir . '/lib/private/Command/SymfonyInputAdapter.php', + 'OC\\Command\\SymfonyOutputAdapter' => $baseDir . '/lib/private/Command/SymfonyOutputAdapter.php', 'OC\\Comments\\Comment' => $baseDir . '/lib/private/Comments/Comment.php', 'OC\\Comments\\Manager' => $baseDir . '/lib/private/Comments/Manager.php', 'OC\\Comments\\ManagerFactory' => $baseDir . '/lib/private/Comments/ManagerFactory.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 502556d3f7fb5..cdfe44613ad61 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -183,8 +183,12 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Collaboration\\Resources\\IResource' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/IResource.php', 'OCP\\Collaboration\\Resources\\LoadAdditionalScriptsEvent' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/LoadAdditionalScriptsEvent.php', 'OCP\\Collaboration\\Resources\\ResourceException' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/ResourceException.php', + 'OCP\\Command\\Command' => __DIR__ . '/../../..' . '/lib/public/Command/Command.php', 'OCP\\Command\\IBus' => __DIR__ . '/../../..' . '/lib/public/Command/IBus.php', 'OCP\\Command\\ICommand' => __DIR__ . '/../../..' . '/lib/public/Command/ICommand.php', + 'OCP\\Command\\IConfiguration' => __DIR__ . '/../../..' . '/lib/public/Command/IConfiguration.php', + 'OCP\\Command\\IInput' => __DIR__ . '/../../..' . '/lib/public/Command/IInput.php', + 'OCP\\Command\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Command/IOutput.php', 'OCP\\Comments\\CommentsEntityEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEntityEvent.php', 'OCP\\Comments\\CommentsEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEvent.php', 'OCP\\Comments\\IComment' => __DIR__ . '/../../..' . '/lib/public/Comments/IComment.php', @@ -860,6 +864,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Command\\CronBus' => __DIR__ . '/../../..' . '/lib/private/Command/CronBus.php', 'OC\\Command\\FileAccess' => __DIR__ . '/../../..' . '/lib/private/Command/FileAccess.php', 'OC\\Command\\QueueBus' => __DIR__ . '/../../..' . '/lib/private/Command/QueueBus.php', + 'OC\\Command\\SymfonyCommandAdapter' => __DIR__ . '/../../..' . '/lib/private/Command/SymfonyCommandAdapter.php', + 'OC\\Command\\SymfonyConfigurationAdapter' => __DIR__ . '/../../..' . '/lib/private/Command/SymfonyConfigurationAdapter.php', + 'OC\\Command\\SymfonyInputAdapter' => __DIR__ . '/../../..' . '/lib/private/Command/SymfonyInputAdapter.php', + 'OC\\Command\\SymfonyOutputAdapter' => __DIR__ . '/../../..' . '/lib/private/Command/SymfonyOutputAdapter.php', 'OC\\Comments\\Comment' => __DIR__ . '/../../..' . '/lib/private/Comments/Comment.php', 'OC\\Comments\\Manager' => __DIR__ . '/../../..' . '/lib/private/Comments/Manager.php', 'OC\\Comments\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/Comments/ManagerFactory.php', diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 7b4d24036e80d..2117f31e984e9 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -32,6 +32,7 @@ use Closure; use OCP\Calendar\Resource\IBackend as IResourceBackend; use OCP\Calendar\Room\IBackend as IRoomBackend; +use OCP\Command\Command; use OCP\Talk\ITalkBackend; use RuntimeException; use function array_shift; @@ -61,6 +62,9 @@ class RegistrationContext { /** @var ServiceRegistration[] */ private $capabilities = []; + /** @var ServiceRegistration[] */ + private $commands = []; + /** @var ServiceRegistration[] */ private $crashReporters = []; @@ -151,6 +155,13 @@ public function registerCapability(string $capability): void { ); } + public function registerCommand(string $command): void { + $this->context->registerCommand( + $this->appId, + $command + ); + } + public function registerCrashReporter(string $reporterClass): void { $this->context->registerCrashReporter( $this->appId, @@ -314,6 +325,13 @@ public function registerCapability(string $appId, string $capability): void { $this->capabilities[] = new ServiceRegistration($appId, $capability); } + /** + * @psalm-param class-string $capability + */ + public function registerCommand(string $appId, string $command): void { + $this->commands[] = new ServiceRegistration($appId, $command); + } + /** * @psalm-param class-string $capability */ @@ -612,6 +630,13 @@ public function delegateMiddlewareRegistrations(array $apps): void { } } + /** + * @return ServiceRegistration[] + */ + public function getCommands(): array { + return $this->commands; + } + /** * @return ServiceRegistration[] */ diff --git a/lib/private/Command/SymfonyCommandAdapter.php b/lib/private/Command/SymfonyCommandAdapter.php new file mode 100644 index 0000000000000..a0385a3ca15f4 --- /dev/null +++ b/lib/private/Command/SymfonyCommandAdapter.php @@ -0,0 +1,59 @@ + + * + * @author 2022 Christoph Wurst + * + * @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 . + */ + +namespace OC\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class SymfonyCommandAdapter extends Command { + + private \OCP\Command\Command $command; + + public function __construct(\OCP\Command\Command $command) { + $this->command = $command; + + parent::__construct($command->getFullyQualifiedName()); + } + + protected function configure() { + parent::configure(); + + $this->setDescription($this->command->getDescription()); + + $this->command->configure( + new SymfonyConfigurationAdapter($this) + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $this->command->execute( + new SymfonyInputAdapter($input), + new SymfonyOutputAdapter($output) + ); + } + +} diff --git a/lib/private/Command/SymfonyConfigurationAdapter.php b/lib/private/Command/SymfonyConfigurationAdapter.php new file mode 100644 index 0000000000000..88575be572643 --- /dev/null +++ b/lib/private/Command/SymfonyConfigurationAdapter.php @@ -0,0 +1,52 @@ + + * + * @author 2022 Christoph Wurst + * + * @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 . + */ + +namespace OC\Command; + +use OCP\Command\IConfiguration; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; + +class SymfonyConfigurationAdapter implements IConfiguration { + + private Command $command; + + public function __construct(Command $command) { + $this->command = $command; + } + + public function addArgument(string $name, + bool $required = false, + string $description = '', + $default = null): void { + $this->command->addArgument( + $name, + $required ? InputArgument::REQUIRED : InputArgument::OPTIONAL, + $description, + $default + ); + } + +} diff --git a/lib/private/Command/SymfonyInputAdapter.php b/lib/private/Command/SymfonyInputAdapter.php new file mode 100644 index 0000000000000..e1c9df3a4260b --- /dev/null +++ b/lib/private/Command/SymfonyInputAdapter.php @@ -0,0 +1,47 @@ + + * + * @author 2022 Christoph Wurst + * + * @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 . + */ + +namespace OC\Command; + +use OCP\Command\IInput; +use Symfony\Component\Console\Input\InputInterface; + +class SymfonyInputAdapter implements IInput { + + private InputInterface $input; + + public function __construct(InputInterface $input) { + $this->input = $input; + } + + public function isInteractive(): bool { + return $this->input->isInteractive(); + } + + public function getArgument(string $name) { + return $this->input->getArgument($name); + } + +} diff --git a/lib/private/Command/SymfonyOutputAdapter.php b/lib/private/Command/SymfonyOutputAdapter.php new file mode 100644 index 0000000000000..e4a641742b05a --- /dev/null +++ b/lib/private/Command/SymfonyOutputAdapter.php @@ -0,0 +1,50 @@ + + * + * @author 2022 Christoph Wurst + * + * @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 . + */ + +namespace OC\Command; + +use OCP\Command\IOutput; +use Symfony\Component\Console\Output\OutputInterface; + +class SymfonyOutputAdapter implements IOutput { + + private OutputInterface $output; + + public function __construct(OutputInterface $output) { + $this->output = $output; + } + + public function formatError(string $message): string { + return '' . $message . ''; + } + + public function formatInfo(string $message): string { + return '' . $message . ''; + } + + public function writeLn(string $message): void { + $this->output->writeln($message); + } +} diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php index 12d54b48fa97e..bb3efd470fea2 100644 --- a/lib/private/Console/Application.php +++ b/lib/private/Console/Application.php @@ -30,6 +30,8 @@ */ namespace OC\Console; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\Command\SymfonyCommandAdapter; use OC\MemoryInfo; use OC\NeedsUpdateException; use OC_App; @@ -44,6 +46,7 @@ use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Throwable; class Application { /** @var IConfig */ @@ -140,6 +143,21 @@ public function loadCommands( } } } + /** @var Coordinator $coordinator */ + $coordinator = \OC::$server->get(Coordinator::class); + $registrationContext = $coordinator->getRegistrationContext(); + foreach ($registrationContext->getCommands() as $commandRegistration) { + try { + $command = \OC::$server->get($commandRegistration->getService()); + $adapter = new SymfonyCommandAdapter($command); + $this->application->add($adapter); + } catch (Throwable $e) { + $this->logger->error("Could not load command: " . $e->getMessage(), [ + 'exception' => $e, + 'app' => $commandRegistration->getAppId(), + ]); + } + } } } elseif ($input->getArgument('command') !== '_completion' && $input->getArgument('command') !== 'maintenance:install') { $errorOutput = $output->getErrorOutput(); diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index a5d675f14c74c..171537993e447 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -56,6 +56,8 @@ interface IRegistrationContext { */ public function registerCapability(string $capability): void; + public function registerCommand(string $command): void; + /** * Register an implementation of \OCP\Support\CrashReport\IReporter that * will receive unhandled exceptions and throwables diff --git a/lib/public/Command/Command.php b/lib/public/Command/Command.php new file mode 100644 index 0000000000000..2f20e3e37c3aa --- /dev/null +++ b/lib/public/Command/Command.php @@ -0,0 +1,69 @@ + + * + * @author 2022 Christoph Wurst + * + * @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 . + */ + +namespace OCP\Command; + +use function implode; + +abstract class Command { + + public const EXIT_CODE_SUCCESS = 0; + public const EXIT_CODE_ERROR = 1; + + private string $appId; + + public function __construct(string $appId) { + $this->appId = $appId; + } + + public function getNamespace(): ?string { + return $this->appId; + } + + public abstract function getName(): string; + + public abstract function getDescription(): string; + + public function getFullyQualifiedName(): string { + if ($this->getNamespace() === null) { + return $this->getName(); + } + + return implode(':', [ + $this->getNamespace(), + $this->getName() + ]); + } + + public function isEnabled(): bool { + return true; + } + + public function configure(IConfiguration $config): void { + } + + public abstract function execute(IInput $input, IOutput $output); + +} diff --git a/lib/public/Command/IConfiguration.php b/lib/public/Command/IConfiguration.php new file mode 100644 index 0000000000000..1aa3406e8ae10 --- /dev/null +++ b/lib/public/Command/IConfiguration.php @@ -0,0 +1,35 @@ + + * + * @author 2022 Christoph Wurst + * + * @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 . + */ + +namespace OCP\Command; + +interface IConfiguration { + + public function addArgument(string $name, + bool $required = false, + string $description = '', + $default = null): void; + +} diff --git a/lib/public/Command/IInput.php b/lib/public/Command/IInput.php new file mode 100644 index 0000000000000..b6c4e4744b4cf --- /dev/null +++ b/lib/public/Command/IInput.php @@ -0,0 +1,37 @@ + + * + * @author 2022 Christoph Wurst + * + * @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 . + */ + +namespace OCP\Command; + +interface IInput { + + public function isInteractive(): bool; + + /** + * @return mixed + */ + public function getArgument(string $name); + +} diff --git a/lib/public/Command/IOutput.php b/lib/public/Command/IOutput.php new file mode 100644 index 0000000000000..d55cb784f278c --- /dev/null +++ b/lib/public/Command/IOutput.php @@ -0,0 +1,36 @@ + + * + * @author 2022 Christoph Wurst + * + * @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 . + */ + +namespace OCP\Command; + +interface IOutput { + + public function formatError(string $message): string; + + public function formatInfo(string $message): string; + + public function writeLn(string $message): void; + +}