diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Menu/MenuCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Menu/MenuCommand.php index 4f771237ab7..3332c0cae8c 100644 --- a/module/VuFindConsole/src/VuFindConsole/Command/Menu/MenuCommand.php +++ b/module/VuFindConsole/src/VuFindConsole/Command/Menu/MenuCommand.php @@ -29,10 +29,15 @@ namespace VuFindConsole\Command\Menu; +use Exception; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; + +use function count; +use function in_array; /** * Console command: interactive menu. @@ -71,6 +76,50 @@ protected function configure() $this->setHelp('Display an interactive menu of commands.'); } + /** + * Display a submenu. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * @param array $options Menu options + * + * @return int + */ + protected function displaySubmenu(InputInterface $input, OutputInterface $output, array $options): int + { + $legalOptions = []; + foreach ($options as $i => $option) { + if (!isset($option['label'])) { + throw new Exception("Option $i is unlabeled!"); + } + if (in_array($option['label'], $legalOptions)) { + throw new Exception('Duplicate label!'); + } + $legalOptions[] = $option['label']; + } + $exitOption = 'Exit Menu'; + if (in_array($exitOption, $legalOptions)) { + throw new Exception("Reserved label '$exitOption' used in configuration."); + } + $legalOptions[] = $exitOption; + + $question = new ChoiceQuestion( + 'Choose an option: ', + $legalOptions, + count($legalOptions) - 1 + ); + while (true) { + $choice = $this->getHelper('question')->ask($input, $output, $question); + if ($choice === $exitOption) { + return 0; + } + $index = array_search($choice, $legalOptions); + if ($index !== false) { + $this->displayOptions($input, $output, $options[$index]); + } + } + } + /** * Display a menu or prompt for the provided configuration. * @@ -82,10 +131,12 @@ protected function configure() */ protected function displayOptions(InputInterface $input, OutputInterface $output, array $config): int { + $output->writeln($config['label'] ?? ''); $type = $config['type'] ?? 'unknown'; $label = $config['label'] ?? 'none'; switch ($type) { case 'menu': + return $this->displaySubmenu($input, $output, $config['contents'] ?? []); case 'command': default: $output->writeln("Unknown menu type '$type' with label '$label'");