From 6cfa659bd4ec92a7c451579ccae4348f3b8dfe11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Thu, 14 May 2026 17:10:17 +0200 Subject: [PATCH 1/2] docs(app_development): add occ commands guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #5460 Signed-off-by: John Molakvoæ (skjnldsv) Signed-off-by: skjnldsv --- developer_manual/app_development/commands.rst | 174 ++++++++++++++++++ developer_manual/app_development/index.rst | 1 + 2 files changed, 175 insertions(+) create mode 100644 developer_manual/app_development/commands.rst diff --git a/developer_manual/app_development/commands.rst b/developer_manual/app_development/commands.rst new file mode 100644 index 00000000000..9c98c202c37 --- /dev/null +++ b/developer_manual/app_development/commands.rst @@ -0,0 +1,174 @@ +.. _occ_commands: + +============ +occ commands +============ + +Nextcloud apps can register custom :ref:`occ ` commands that administrators can run from the command line. Commands extend ``OC\Core\Command\Base``, which wraps +`Symfony Console `_ and adds bash completion support, so the full Symfony +Console API is available. + + +Registering a command +--------------------- + +List every command class in ``appinfo/info.xml`` under the ```` element: + +.. code-block:: xml + :caption: appinfo/info.xml + + + ... + + OCA\MyApp\Command\Greet + + + +Nextcloud reads this list at startup and wires each class through the dependency +injection container, so constructor injection works automatically. + + +Creating a command class +------------------------ + +Place command classes in ``lib/Command/``. Each class must extend +``OC\Core\Command\Base`` and implement two methods: + +- ``configure()`` — declare the name, description, arguments, and options. +- ``execute()`` — run the command logic and return an exit code. + +.. code-block:: php + :caption: lib/Command/Greet.php + + setName('myapp:greet') + ->setDescription('Print a greeting for a Nextcloud user') + ->addArgument( + 'user-id', + InputArgument::REQUIRED, + 'The user to greet', + ) + ->addOption( + 'shout', + null, + InputOption::VALUE_NONE, + 'Print the greeting in uppercase', + ); + } + + #[\Override] + protected function execute(InputInterface $input, OutputInterface $output): int { + $userId = $input->getArgument('user-id'); + $user = $this->userManager->get($userId); + + if ($user === null) { + $output->writeln("User \"$userId\" not found."); + return self::FAILURE; + } + + $greeting = 'Hello, ' . $user->getDisplayName() . '!'; + + if ($input->getOption('shout')) { + $greeting = strtoupper($greeting); + } + + $output->writeln($greeting); + return self::SUCCESS; + } + } + +Command naming +-------------- + +Use ``appid:command-name`` as the command name. For apps with many commands, group them +with an extra segment: ``appid:group:command-name``. Names must be lowercase and use +hyphens as word separators. + + +Arguments and options +--------------------- + +Arguments are positional. Options are prefixed with ``--``. + ++-----------------------------------------+-------------------------------------------+ +| Constant | Meaning | ++=========================================+===========================================+ +| ``InputArgument::REQUIRED`` | Argument must be provided | ++-----------------------------------------+-------------------------------------------+ +| ``InputArgument::OPTIONAL`` | Argument may be omitted | ++-----------------------------------------+-------------------------------------------+ +| ``InputArgument::IS_ARRAY`` | Argument accepts multiple values | ++-----------------------------------------+-------------------------------------------+ +| ``InputOption::VALUE_NONE`` | Flag — present or absent, no value | ++-----------------------------------------+-------------------------------------------+ +| ``InputOption::VALUE_REQUIRED`` | Option requires a value | ++-----------------------------------------+-------------------------------------------+ +| ``InputOption::VALUE_OPTIONAL`` | Option value is optional | ++-----------------------------------------+-------------------------------------------+ +| ``InputOption::VALUE_IS_ARRAY`` | Option can be repeated | ++-----------------------------------------+-------------------------------------------+ + +See the `Symfony Console documentation `_ +for the full reference. + + +Return codes +------------ + +``execute()`` must return an integer. Use the constants defined by +``OC\Core\Command\Base`` (inherited from Symfony): + +- ``self::SUCCESS`` (``0``) — command completed successfully. +- ``self::FAILURE`` (``1``) — command encountered an error. +- ``self::INVALID`` (``2``) — command was called with invalid input. + + +Interactive commands +-------------------- + +Commands can ask for confirmation or prompt for values using Symfony's +`question helper `_: + +.. code-block:: php + + use Symfony\Component\Console\Helper\QuestionHelper; + use Symfony\Component\Console\Question\ConfirmationQuestion; + + // In execute(): + /** @var QuestionHelper $helper */ + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion('Are you sure? (y/n) ', false); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Aborted.'); + return self::FAILURE; + } + +.. note:: + + Interactive prompts are skipped when occ is run non-interactively (e.g. from a + cron job). Guard against this with ``$input->isInteractive()`` or use + ``--yes``/``--no`` options so administrators can automate the command. diff --git a/developer_manual/app_development/index.rst b/developer_manual/app_development/index.rst index 7a16ead00b8..7a7df8c7782 100644 --- a/developer_manual/app_development/index.rst +++ b/developer_manual/app_development/index.rst @@ -11,5 +11,6 @@ App development info init dependency_management + commands dav_extension translation From c08ad1861aff125d222878f6cebd799fc03bdfff Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Fri, 15 May 2026 09:46:02 +0200 Subject: [PATCH 2/2] fix(commands): replace undefined :ref:`occ` with external link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `occ` label is defined in the admin manual which is a separate Sphinx build, causing an undefined label warning. Replace with an absolute URL per the cross-manual linking convention. Also fix the style guide which incorrectly documented `/latest/` instead of `/server/latest/` as the cross-manual URL pattern. Signed-off-by: John Molakvoæ (skjnldsv) Signed-off-by: skjnldsv --- developer_manual/app_development/commands.rst | 2 +- style_guide.rst | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/developer_manual/app_development/commands.rst b/developer_manual/app_development/commands.rst index 9c98c202c37..7b8bd939aca 100644 --- a/developer_manual/app_development/commands.rst +++ b/developer_manual/app_development/commands.rst @@ -4,7 +4,7 @@ occ commands ============ -Nextcloud apps can register custom :ref:`occ ` commands that administrators can run from the command line. Commands extend ``OC\Core\Command\Base``, which wraps +Nextcloud apps can register custom `occ `_ commands that administrators can run from the command line. Commands extend ``OC\Core\Command\Base``, which wraps `Symfony Console `_ and adds bash completion support, so the full Symfony Console API is available. diff --git a/style_guide.rst b/style_guide.rst index 01a48db5f1d..7fffb025459 100644 --- a/style_guide.rst +++ b/style_guide.rst @@ -291,13 +291,12 @@ page moves. See :doc:`../configuration_server/config_sample_php_parameters` for all available options. -**Different manual — use a** ``docs.nextcloud.com/latest/`` **URL** +**Different manual — use a** ``docs.nextcloud.com/server/latest/`` **URL** When linking from one manual to a page in a different manual (e.g. from the User Manual to the Administration Manual), use an absolute URL with - ``/latest/`` as the version segment. The build system automatically replaces - ``/latest/`` with the correct version number for stable branches:: + ``/server/latest/`` as the version segment:: - See `Backup configuration `_ + See `Backup configuration `_ for details. ``:doc:``