io->askQuestion(new Question('Where is the location for the taskrc file?')); $taskrcFilePath = $filePath . '/.taskrc'; if (!file_exists($taskrcFilePath)) { throw new \RuntimeException("The taskrc file at $taskrcFilePath does not exist"); } $configs['taskrc'] = $taskrcFilePath; $filePath = $this->io->askQuestion(new Question('Where is the data location (taskdata)')); $taskDataFilePath = $filePath; if (!file_exists($taskDataFilePath)) { throw new \RuntimeException("The task.data location $taskDataFilePath does not exist"); } $configs['taskdata'] = $taskDataFilePath; $displayName = $this->io->askQuestion(new Question('What is the displayname for the default calendar (e.g default)')); $configs['default_calendar'] = isset($displayName) ? $displayName : 'default'; $this->configs['storages']['taskwarior'] = $configs; } protected function configure() { $this ->setName('create-config') ->setDescription('Create baikal storage plugin configuration file'); } protected function autocompleteFilePathCallback(string $input): array { // Strip any characters from the last slash to the end of the string // to keep only the last directory and generate suggestions for it $inputPath = preg_replace('%(/|^)[^/]*$%', '$1', $input); $inputPath = '' === $inputPath ? '.': $inputPath; $foundFilesAndDirs = scandir($inputPath) ?: []; // Excluded restricted directories $restrictedDirs = ['dev', 'bin', 'boot', 'etc', 'input', 'lib', 'lib64', 'mnt', 'proc', 'run', 'sbin', 'sys', 'srv', 'usr', 'var']; $foundSafeFileAndDirs = array_diff($foundFilesAndDirs, $restrictedDirs); return array_map(function($dirOrFile) use ($inputPath) { return $inputPath.$dirOrFile; }, $foundSafeFileAndDirs); } protected function execute(InputInterface $input, OutputInterface $output) { $this->io = new SymfonyStyle($input, $output); $this->io->title('Baikal Storage Plugin Configuration Creation'); // TODO: move create config file code block to function $question = new Question('Where to create `config.yaml` configuration file?'); $question->setAutocompleterCallback($this->autocompleteFilePathCallback); $filePath = $this->io->askQuestion($question); try { $this->fullFilePath = $this->verifyAndCreateFile($filePath, CreateConfigFileCommand::$CONFIG_FILE_NAME); $this->setupGeneralConfigs() ->setupStorages() ->saveConfigs(); } catch (\Exception $e) { return Command::FAILURE; } $this->output->writeln("`config.yaml file created at $filePath"); return Command::SUCCESS; } protected function getStorageNames() { $storageDir = __DIR__ . '/../Storages'; $storageFiles = scandir($storageDir); $storages = array_values(array_filter($storageFiles, function($storageFile) use ($storageDir) { return !preg_match('/^IStorage/', $storageFile) && !is_dir("$storageDir/$storageFile"); })); array_walk($storages, function(&$storage) { $storage = strtolower(preg_replace('/\.php/', '', $storage)); }); return $storages; } protected function verifyAndCreateFile($filePath, $fileName) { if (empty($filePath)) { throw new \RuntimeException('Configuration file path cannot be empty'); } if (!is_dir($filePath)) { throw new \RuntimeException("File path $filePath does not exist"); } if ((strrpos($filePath, '/') + 1) === strlen($filePath)) { $fullFilePath = substr($filePath, 0, -1) . $fileName; } else { $fullFilePath = $filePath . '/' . $fileName; } if (file_exists($fullFilePath)) { throw new \RuntimeException("The configuration file at $this->fullFilePath already exists"); } return $fullFilePath; } protected function setupGeneralConfigs() { $this->io->section('Setting up general configurations'); $this->configs['general'] = []; if ($this->io->confirm('Enable logging?')) { $this->configs['general']['logger'] = []; $logFilePath = $this->io->askQuestion(new Question('Where to create log file?')); $this->configs['general']['logger']['file'] = $this->verifyAndCreateFile($logFilePath, CreateConfigFileCommand::$LOGGER_FILE_NAME); $logLevelChoiceQuestion = new ChoiceQuestion('Log level (defaults to ERROR)', array_keys(Monolog::getLevels()), 4); $logLevelChoiceQuestion->setErrorMessage('Log level %s is invalid'); $logLevel = $this->io->askQuestion($logLevelChoiceQuestion); $this->configs['general']['logger']['level'] = $logLevel; } return $this; } protected function setupStorages() { $this->configs['storages'] = []; $this->io->section('Setup storage configurations'); $storagesMultiSelectQuestion = new ChoiceQuestion('Select the storages to add', $this->getStorageNames()); $storagesMultiSelectQuestion->setMultiselect(true); $storagesMultiSelectQuestion->setErrorMessage('Storages %s is not supported'); $storages = $this->io->askQuestion($storagesMultiSelectQuestion); foreach ($storages as $storage) { $methodName = "add${storage}Config"; $storageConfigMethod = new \ReflectionMethod(CreateConfigFileCommand::class, $methodName); $storageConfigMethod->invoke($this); } return $this; } protected function saveConfigs() { $configBuilder = new ConfigBuilder($this->fullFilePath); $configBuilder->saveConfigs($this->configs); } }