diff --git a/core/command/base.php b/core/command/base.php index f84dcb1aea..7e61c0cb9a 100644 --- a/core/command/base.php +++ b/core/command/base.php @@ -43,8 +43,9 @@ class Base extends Command { * @param InputInterface $input * @param OutputInterface $output * @param array $items + * @param string $prefix */ - protected function writeArrayInOutputFormat(InputInterface $input, OutputInterface $output, $items) { + protected function writeArrayInOutputFormat(InputInterface $input, OutputInterface $output, $items, $prefix = ' - ') { switch ($input->getOption('output')) { case 'json': $output->writeln(json_encode($items)); @@ -54,28 +55,57 @@ class Base extends Command { break; default: foreach ($items as $key => $item) { + if (is_array($item)) { + $output->writeln($prefix . $key . ':'); + $this->writeArrayInOutputFormat($input, $output, $item, ' ' . $prefix); + continue; + } if (!is_int($key)) { $value = $this->valueToString($item); if (!is_null($value)) { - $output->writeln(' - ' . $key . ': ' . $value); + $output->writeln($prefix . $key . ': ' . $value); } else { - $output->writeln(' - ' . $key); + $output->writeln($prefix . $key); } } else { - $output->writeln(' - ' . $this->valueToString($item)); + $output->writeln($prefix . $this->valueToString($item)); } } break; } } - protected function valueToString($value) { + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param mixed $item + */ + protected function writeMixedInOutputFormat(InputInterface $input, OutputInterface $output, $item) { + if (is_array($item)) { + $this->writeArrayInOutputFormat($input, $output, $item, ''); + return; + } + + switch ($input->getOption('output')) { + case 'json': + $output->writeln(json_encode($item)); + break; + case 'json_pretty': + $output->writeln(json_encode($item, JSON_PRETTY_PRINT)); + break; + default: + $output->writeln($this->valueToString($item, false)); + break; + } + } + + protected function valueToString($value, $returnNull = true) { if ($value === false) { return 'false'; } else if ($value === true) { return 'true'; } else if ($value === null) { - null; + return ($returnNull) ? null : 'null'; } else { return $value; } diff --git a/core/command/config/app/deleteconfig.php b/core/command/config/app/deleteconfig.php new file mode 100644 index 0000000000..0031142358 --- /dev/null +++ b/core/command/config/app/deleteconfig.php @@ -0,0 +1,81 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Config\App; + +use OC\Core\Command\Base; +use OCP\IConfig; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class DeleteConfig extends Base { + /** * @var IConfig */ + protected $config; + + /** + * @param IConfig $config + */ + public function __construct(IConfig $config) { + parent::__construct(); + $this->config = $config; + } + + protected function configure() { + parent::configure(); + + $this + ->setName('config:app:delete') + ->setDescription('Delete an app config value') + ->addArgument( + 'app', + InputArgument::REQUIRED, + 'Name of the app' + ) + ->addArgument( + 'name', + InputArgument::REQUIRED, + 'Name of the config to delete' + ) + ->addOption( + 'error-if-not-exists', + null, + InputOption::VALUE_NONE, + 'Checks whether the config exists before deleting it' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $configName = $input->getArgument('name'); + + if ($input->hasParameterOption('--error-if-not-exists') && !in_array($configName, $this->config->getAppKeys($appName))) { + $output->writeln('Config ' . $configName . ' could not be deleted because it did not exist'); + return 1; + } + + $this->config->deleteAppValue($appName, $configName); + $output->writeln('System config value ' . $configName . ' deleted'); + return 0; + } +} diff --git a/core/command/config/app/getconfig.php b/core/command/config/app/getconfig.php new file mode 100644 index 0000000000..186ed46999 --- /dev/null +++ b/core/command/config/app/getconfig.php @@ -0,0 +1,93 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Config\App; + +use OC\Core\Command\Base; +use OCP\IConfig; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class GetConfig extends Base { + /** * @var IConfig */ + protected $config; + + /** + * @param IConfig $config + */ + public function __construct(IConfig $config) { + parent::__construct(); + $this->config = $config; + } + + protected function configure() { + parent::configure(); + + $this + ->setName('config:app:get') + ->setDescription('Get an app config value') + ->addArgument( + 'app', + InputArgument::REQUIRED, + 'Name of the app' + ) + ->addArgument( + 'name', + InputArgument::REQUIRED, + 'Name of the config to get' + ) + ->addOption( + 'default-value', + null, + InputOption::VALUE_OPTIONAL, + 'If no default value is set and the config does not exist, the command will exit with 1' + ) + ; + } + + /** + * Executes the current command. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * @return null|int null or 0 if everything went fine, or an error code + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $configName = $input->getArgument('name'); + $defaultValue = $input->getOption('default-value'); + + if (!in_array($configName, $this->config->getAppKeys($appName)) && !$input->hasParameterOption('--default-value')) { + return 1; + } + + if (!in_array($configName, $this->config->getAppKeys($appName))) { + $configValue = $defaultValue; + } else { + $configValue = $this->config->getAppValue($appName, $configName); + } + + $this->writeMixedInOutputFormat($input, $output, $configValue); + return 0; + } +} diff --git a/core/command/config/app/setconfig.php b/core/command/config/app/setconfig.php new file mode 100644 index 0000000000..91775094fd --- /dev/null +++ b/core/command/config/app/setconfig.php @@ -0,0 +1,89 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Config\App; + +use OC\Core\Command\Base; +use OCP\IConfig; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class SetConfig extends Base { + /** * @var IConfig */ + protected $config; + + /** + * @param IConfig $config + */ + public function __construct(IConfig $config) { + parent::__construct(); + $this->config = $config; + } + + protected function configure() { + parent::configure(); + + $this + ->setName('config:app:set') + ->setDescription('Set an app config value') + ->addArgument( + 'app', + InputArgument::REQUIRED, + 'Name of the app' + ) + ->addArgument( + 'name', + InputArgument::REQUIRED, + 'Name of the config to set' + ) + ->addOption( + 'value', + null, + InputOption::VALUE_REQUIRED, + 'The new value of the config' + ) + ->addOption( + 'update-only', + null, + InputOption::VALUE_NONE, + 'Only updates the value, if it is not set before, it is not being added' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $configName = $input->getArgument('name'); + + if (!in_array($configName, $this->config->getAppKeys($appName)) && $input->hasParameterOption('--update-only')) { + $output->writeln('Value not updated, as it has not been set before.'); + return 1; + } + + $configValue = $input->getOption('value'); + $this->config->setAppValue($appName, $configName, $configValue); + + $output->writeln('Config value ' . $configName . ' for app ' . $appName . ' set to ' . $configValue . ''); + return 0; + } +} diff --git a/core/command/config/import.php b/core/command/config/import.php new file mode 100644 index 0000000000..c53fd7255f --- /dev/null +++ b/core/command/config/import.php @@ -0,0 +1,195 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Config; + +use OCP\IConfig; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Import extends Command { + protected $validRootKeys = ['system', 'apps']; + + /** @var IConfig */ + protected $config; + + /** + * @param IConfig $config + */ + public function __construct(IConfig $config) { + parent::__construct(); + $this->config = $config; + } + + protected function configure() { + $this + ->setName('config:import') + ->setDescription('Import a list of configs') + ->addArgument( + 'file', + InputArgument::OPTIONAL, + 'File with the json array to import' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $importFile = $input->getArgument('file'); + if ($importFile !== null) { + $content = $this->getArrayFromFile($importFile); + } else { + $content = $this->getArrayFromStdin(); + } + + try { + $configs = $this->validateFileContent($content); + } catch (\UnexpectedValueException $e) { + $output->writeln('' . $e->getMessage(). ''); + return; + } + + if (!empty($configs['system'])) { + $this->config->setSystemValues($configs['system']); + } + + if (!empty($configs['apps'])) { + foreach ($configs['apps'] as $app => $appConfigs) { + foreach ($appConfigs as $key => $value) { + if ($value === null) { + $this->config->deleteAppValue($app, $key); + } else { + $this->config->setAppValue($app, $key, $value); + } + } + } + } + + $output->writeln('Config successfully imported from: ' . $importFile . ''); + } + + /** + * Get the content from stdin ("config:import < file.json") + * + * @return string + */ + protected function getArrayFromStdin() { + // Read from stdin. stream_set_blocking is used to prevent blocking + // when nothing is passed via stdin. + stream_set_blocking(STDIN, 0); + $content = file_get_contents('php://stdin'); + stream_set_blocking(STDIN, 1); + return $content; + } + + /** + * Get the content of the specified file ("config:import file.json") + * + * @param string $importFile + * @return string + */ + protected function getArrayFromFile($importFile) { + $content = file_get_contents($importFile); + return $content; + } + + /** + * @param string $content + * @return array + * @throws \UnexpectedValueException when the array is invalid + */ + protected function validateFileContent($content) { + $decodedContent = json_decode($content, true); + if (!is_array($decodedContent) || empty($decodedContent)) { + throw new \UnexpectedValueException('The file must contain a valid json array'); + } + + $this->validateArray($decodedContent); + + return $decodedContent; + } + + /** + * Validates that the array only contains `system` and `apps` + * + * @param array $array + */ + protected function validateArray($array) { + $arrayKeys = array_keys($array); + $additionalKeys = array_diff($arrayKeys, $this->validRootKeys); + $commonKeys = array_intersect($arrayKeys, $this->validRootKeys); + if (!empty($additionalKeys)) { + throw new \UnexpectedValueException('Found invalid entries in root: ' . implode(', ', $additionalKeys)); + } + if (empty($commonKeys)) { + throw new \UnexpectedValueException('At least one key of the following is expected: ' . implode(', ', $this->validRootKeys)); + } + + if (isset($array['system'])) { + if (is_array($array['system'])) { + foreach ($array['system'] as $name => $value) { + $this->checkTypeRecursively($value, $name); + } + } else { + throw new \UnexpectedValueException('The system config array is not an array'); + } + } + + if (isset($array['apps'])) { + if (is_array($array['apps'])) { + $this->validateAppsArray($array['apps']); + } else { + throw new \UnexpectedValueException('The apps config array is not an array'); + } + } + } + + /** + * @param mixed $configValue + * @param string $configName + */ + protected function checkTypeRecursively($configValue, $configName) { + if (!is_array($configValue) && !is_bool($configValue) && !is_int($configValue) && !is_string($configValue) && !is_null($configValue)) { + throw new \UnexpectedValueException('Invalid system config value for "' . $configName . '". Only arrays, bools, integers, strings and null (delete) are allowed.'); + } + if (is_array($configValue)) { + foreach ($configValue as $key => $value) { + $this->checkTypeRecursively($value, $configName); + } + } + } + + /** + * Validates that app configs are only integers and strings + * + * @param array $array + */ + protected function validateAppsArray($array) { + foreach ($array as $app => $configs) { + foreach ($configs as $name => $value) { + if (!is_int($value) && !is_string($value) && !is_null($value)) { + throw new \UnexpectedValueException('Invalid app config value for "' . $app . '":"' . $name . '". Only integers, strings and null (delete) are allowed.'); + } + } + } + } +} diff --git a/core/command/config/listconfigs.php b/core/command/config/listconfigs.php new file mode 100644 index 0000000000..be3073e54d --- /dev/null +++ b/core/command/config/listconfigs.php @@ -0,0 +1,141 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Config; + +use OC\Core\Command\Base; +use OC\SystemConfig; +use OCP\IAppConfig; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class ListConfigs extends Base { + /** @var array */ + protected $sensitiveValues = [ + 'dbpassword', + 'dbuser', + 'mail_smtpname', + 'mail_smtppassword', + 'passwordsalt', + 'secret', + ]; + + /** * @var SystemConfig */ + protected $systemConfig; + + /** @var IAppConfig */ + protected $appConfig; + + /** + * @param SystemConfig $systemConfig + * @param IAppConfig $appConfig + */ + public function __construct(SystemConfig $systemConfig, IAppConfig $appConfig) { + parent::__construct(); + $this->systemConfig = $systemConfig; + $this->appConfig = $appConfig; + } + + protected function configure() { + parent::configure(); + + $this + ->setName('config:list') + ->setDescription('List all configs') + ->addArgument( + 'app', + InputArgument::OPTIONAL, + 'Name of the app ("system" to get the config.php values, "all" for all apps and system)', + 'all' + ) + ->addOption( + 'private', + null, + InputOption::VALUE_NONE, + 'Use this option when you want to include sensitive configs like passwords, salts, ...' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $app = $input->getArgument('app'); + $noSensitiveValues = !$input->getOption('private'); + + if ($noSensitiveValues && !$input->hasParameterOption('--output')) { + // If you post this publicly we prefer the json format + $input->setOption('output', 'json_pretty'); + } + + switch ($app) { + case 'system': + $configs = [ + 'system' => $this->getSystemConfigs($noSensitiveValues), + ]; + break; + + case 'all': + $apps = $this->appConfig->getApps(); + $configs = [ + 'system' => $this->getSystemConfigs($noSensitiveValues), + 'apps' => [], + ]; + foreach ($apps as $appName) { + $configs['apps'][$appName] = $this->appConfig->getValues($appName, false); + } + break; + + default: + $configs = [ + 'apps' => [ + $app => $this->appConfig->getValues($app, false), + ], + ]; + } + + $this->writeArrayInOutputFormat($input, $output, $configs); + } + + /** + * Get the system configs + * + * @param bool $noSensitiveValues + * @return array + */ + protected function getSystemConfigs($noSensitiveValues) { + $keys = $this->systemConfig->getKeys(); + + $configs = []; + foreach ($keys as $key) { + if ($noSensitiveValues && in_array($key, $this->sensitiveValues)) { + continue; + } + + $value = $this->systemConfig->getValue($key, serialize(null)); + if ($value !== 'N;') { + $configs[$key] = $value; + } + } + + return $configs; + } +} diff --git a/core/command/config/system/deleteconfig.php b/core/command/config/system/deleteconfig.php new file mode 100644 index 0000000000..49bc2bc8c0 --- /dev/null +++ b/core/command/config/system/deleteconfig.php @@ -0,0 +1,75 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Config\System; + +use OC\Core\Command\Base; +use OC\SystemConfig; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class DeleteConfig extends Base { + /** * @var SystemConfig */ + protected $systemConfig; + + /** + * @param SystemConfig $systemConfig + */ + public function __construct(SystemConfig $systemConfig) { + parent::__construct(); + $this->systemConfig = $systemConfig; + } + + protected function configure() { + parent::configure(); + + $this + ->setName('config:system:delete') + ->setDescription('Delete a system config value') + ->addArgument( + 'name', + InputArgument::REQUIRED, + 'Name of the config to delete' + ) + ->addOption( + 'error-if-not-exists', + null, + InputOption::VALUE_NONE, + 'Checks whether the config exists before deleting it' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $configName = $input->getArgument('name'); + + if ($input->hasParameterOption('--error-if-not-exists') && !in_array($configName, $this->systemConfig->getKeys())) { + $output->writeln('Config ' . $configName . ' could not be deleted because it did not exist'); + return 1; + } + + $this->systemConfig->deleteValue($configName); + $output->writeln('System config value ' . $configName . ' deleted'); + return 0; + } +} diff --git a/core/command/config/system/getconfig.php b/core/command/config/system/getconfig.php new file mode 100644 index 0000000000..d53de18a0b --- /dev/null +++ b/core/command/config/system/getconfig.php @@ -0,0 +1,87 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Config\System; + +use OC\Core\Command\Base; +use OC\SystemConfig; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class GetConfig extends Base { + /** * @var SystemConfig */ + protected $systemConfig; + + /** + * @param SystemConfig $systemConfig + */ + public function __construct(SystemConfig $systemConfig) { + parent::__construct(); + $this->systemConfig = $systemConfig; + } + + protected function configure() { + parent::configure(); + + $this + ->setName('config:system:get') + ->setDescription('Get a system config value') + ->addArgument( + 'name', + InputArgument::REQUIRED, + 'Name of the config to get' + ) + ->addOption( + 'default-value', + null, + InputOption::VALUE_OPTIONAL, + 'If no default value is set and the config does not exist, the command will exit with 1' + ) + ; + } + + /** + * Executes the current command. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * @return null|int null or 0 if everything went fine, or an error code + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $configName = $input->getArgument('name'); + $defaultValue = $input->getOption('default-value'); + + if (!in_array($configName, $this->systemConfig->getKeys()) && !$input->hasParameterOption('--default-value')) { + return 1; + } + + if (!in_array($configName, $this->systemConfig->getKeys())) { + $configValue = $defaultValue; + } else { + $configValue = $this->systemConfig->getValue($configName); + } + + $this->writeMixedInOutputFormat($input, $output, $configValue); + return 0; + } +} diff --git a/core/command/config/system/setconfig.php b/core/command/config/system/setconfig.php new file mode 100644 index 0000000000..cb2f9fe616 --- /dev/null +++ b/core/command/config/system/setconfig.php @@ -0,0 +1,82 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Config\System; + +use OC\Core\Command\Base; +use OC\SystemConfig; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class SetConfig extends Base { + /** * @var SystemConfig */ + protected $systemConfig; + + /** + * @param SystemConfig $systemConfig + */ + public function __construct(SystemConfig $systemConfig) { + parent::__construct(); + $this->systemConfig = $systemConfig; + } + + protected function configure() { + parent::configure(); + + $this + ->setName('config:system:set') + ->setDescription('Set a system config value') + ->addArgument( + 'name', + InputArgument::REQUIRED, + 'Name of the config to set' + ) + ->addOption( + 'value', + null, + InputOption::VALUE_REQUIRED, + 'The new value of the config' + ) + ->addOption( + 'update-only', + null, + InputOption::VALUE_NONE, + 'Only updates the value, if it is not set before, it is not being added' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $configName = $input->getArgument('name'); + + if (!in_array($configName, $this->systemConfig->getKeys()) && $input->hasParameterOption('--update-only')) { + $output->writeln('Value not updated, as it has not been set before.'); + return 1; + } + $configValue = $input->getOption('value'); + + $this->systemConfig->setValue($configName, $configValue); + $output->writeln('System config value ' . $configName . ' set to ' . $configValue . ''); + return 0; + } +} diff --git a/core/register_command.php b/core/register_command.php index 13010b93a9..8815eca6b6 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -32,31 +32,44 @@ $application->add(new OC\Core\Command\App\CheckCode()); $application->add(new OC\Core\Command\L10n\CreateJs()); if (\OC::$server->getConfig()->getSystemValue('installed', false)) { - $repair = new \OC\Repair(\OC\Repair::getRepairSteps()); - - $application->add(new OC\Core\Command\Db\GenerateChangeScript()); - $application->add(new OC\Core\Command\Db\ConvertType(\OC::$server->getConfig(), new \OC\DB\ConnectionFactory())); - $application->add(new OC\Core\Command\Upgrade(\OC::$server->getConfig())); - $application->add(new OC\Core\Command\Maintenance\SingleUser()); - $application->add(new OC\Core\Command\Maintenance\Mode(\OC::$server->getConfig())); $application->add(new OC\Core\Command\App\Disable()); $application->add(new OC\Core\Command\App\Enable()); $application->add(new OC\Core\Command\App\ListApps()); - $application->add(new OC\Core\Command\Maintenance\Repair($repair, \OC::$server->getConfig())); - $application->add(new OC\Core\Command\User\Add(\OC::$server->getUserManager(), \OC::$server->getGroupManager())); - $application->add(new OC\Core\Command\User\Delete(\OC::$server->getUserManager())); - $application->add(new OC\Core\Command\User\LastSeen(\OC::$server->getUserManager())); - $application->add(new OC\Core\Command\User\Report(\OC::$server->getUserManager())); - $application->add(new OC\Core\Command\User\ResetPassword(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\Background\Cron(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Background\WebCron(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Background\Ajax(\OC::$server->getConfig())); + + $application->add(new OC\Core\Command\Config\App\DeleteConfig(\OC::$server->getConfig())); + $application->add(new OC\Core\Command\Config\App\GetConfig(\OC::$server->getConfig())); + $application->add(new OC\Core\Command\Config\App\SetConfig(\OC::$server->getConfig())); + $application->add(new OC\Core\Command\Config\Import(\OC::$server->getConfig())); + $application->add(new OC\Core\Command\Config\ListConfigs(\OC::$server->getSystemConfig(), \OC::$server->getAppConfig())); + $application->add(new OC\Core\Command\Config\System\DeleteConfig(\OC::$server->getSystemConfig())); + $application->add(new OC\Core\Command\Config\System\GetConfig(\OC::$server->getSystemConfig())); + $application->add(new OC\Core\Command\Config\System\SetConfig(\OC::$server->getSystemConfig())); + + $application->add(new OC\Core\Command\Db\GenerateChangeScript()); + $application->add(new OC\Core\Command\Db\ConvertType(\OC::$server->getConfig(), new \OC\DB\ConnectionFactory())); + $application->add(new OC\Core\Command\Encryption\Disable(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Encryption\Enable(\OC::$server->getConfig(), \OC::$server->getEncryptionManager())); $application->add(new OC\Core\Command\Encryption\ListModules(\OC::$server->getEncryptionManager())); $application->add(new OC\Core\Command\Encryption\SetDefaultModule(\OC::$server->getEncryptionManager())); $application->add(new OC\Core\Command\Encryption\Status(\OC::$server->getEncryptionManager())); + $application->add(new OC\Core\Command\Maintenance\MimeTypesJS()); + $application->add(new OC\Core\Command\Maintenance\Mode(\OC::$server->getConfig())); + $application->add(new OC\Core\Command\Maintenance\Repair(new \OC\Repair(\OC\Repair::getRepairSteps()), \OC::$server->getConfig())); + $application->add(new OC\Core\Command\Maintenance\SingleUser()); + + $application->add(new OC\Core\Command\Upgrade(\OC::$server->getConfig())); + + $application->add(new OC\Core\Command\User\Add(\OC::$server->getUserManager(), \OC::$server->getGroupManager())); + $application->add(new OC\Core\Command\User\Delete(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\User\LastSeen(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\User\Report(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\User\ResetPassword(\OC::$server->getUserManager())); } else { $application->add(new OC\Core\Command\Maintenance\Install(\OC::$server->getConfig())); } diff --git a/issue_template.md b/issue_template.md index b0b24e56df..3859ea1c41 100644 --- a/issue_template.md +++ b/issue_template.md @@ -33,6 +33,12 @@ from within your ownCloud installation folder **The content of config/config.php:** ``` +If you have access to your command line run e.g.: +sudo -u www-data php occ config:list system --public +from within your ownCloud installation folder + +or + Insert your config.php content here (Without the database password, passwordsalt and secret) ``` diff --git a/lib/private/systemconfig.php b/lib/private/systemconfig.php index 451ccd85e2..13b0959768 100644 --- a/lib/private/systemconfig.php +++ b/lib/private/systemconfig.php @@ -28,6 +28,14 @@ namespace OC; * fixes cyclic DI: AllConfig needs AppConfig needs Database needs AllConfig */ class SystemConfig { + /** + * Lists all available config keys + * @return array an array of key names + */ + public function getKeys() { + return \OC_Config::getKeys(); + } + /** * Sets a new system wide value * diff --git a/tests/core/command/config/app/deleteconfigtest.php b/tests/core/command/config/app/deleteconfigtest.php new file mode 100644 index 0000000000..7056e1b1ff --- /dev/null +++ b/tests/core/command/config/app/deleteconfigtest.php @@ -0,0 +1,123 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace Tests\Core\Command\Config\App; + + +use OC\Core\Command\Config\App\DeleteConfig; +use Test\TestCase; + +class DeleteConfigTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $config; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $config = $this->config = $this->getMockBuilder('OCP\IConfig') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OCP\IConfig $config */ + $this->command = new DeleteConfig($config); + } + + + public function deleteData() { + return [ + [ + 'name', + true, + true, + 0, + 'info', + ], + [ + 'name', + true, + false, + 0, + 'info', + ], + [ + 'name', + false, + false, + 0, + 'info', + ], + [ + 'name', + false, + true, + 1, + 'error', + ], + ]; + } + + /** + * @dataProvider deleteData + * + * @param string $configName + * @param bool $configExists + * @param bool $checkIfExists + * @param int $expectedReturn + * @param string $expectedMessage + */ + public function testDelete($configName, $configExists, $checkIfExists, $expectedReturn, $expectedMessage) { + $this->config->expects(($checkIfExists) ? $this->once() : $this->never()) + ->method('getAppKeys') + ->with('app-name') + ->willReturn($configExists ? [$configName] : []); + + $this->config->expects(($expectedReturn === 0) ? $this->once() : $this->never()) + ->method('deleteAppValue') + ->with('app-name', $configName); + + $this->consoleInput->expects($this->exactly(2)) + ->method('getArgument') + ->willReturnMap([ + ['app', 'app-name'], + ['name', $configName], + ]); + $this->consoleInput->expects($this->any()) + ->method('hasParameterOption') + ->with('--error-if-not-exists') + ->willReturn($checkIfExists); + + $this->consoleOutput->expects($this->any()) + ->method('writeln') + ->with($this->stringContains($expectedMessage)); + + $this->assertSame($expectedReturn, $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput])); + } +} diff --git a/tests/core/command/config/app/getconfigtest.php b/tests/core/command/config/app/getconfigtest.php new file mode 100644 index 0000000000..1ceeb16ccf --- /dev/null +++ b/tests/core/command/config/app/getconfigtest.php @@ -0,0 +1,163 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace Tests\Core\Command\Config\App; + + +use OC\Core\Command\Config\App\GetConfig; +use Test\TestCase; + +class GetConfigTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $config; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $config = $this->config = $this->getMockBuilder('OCP\IConfig') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OCP\IConfig $config */ + $this->command = new GetConfig($config); + } + + + public function getData() { + return [ + // String output as json + ['name', 'newvalue', true, null, false, 'json', 0, json_encode('newvalue')], + // String output as plain text + ['name', 'newvalue', true, null, false, 'plain', 0, 'newvalue'], + // String falling back to default output as json + ['name', null, false, 'newvalue', true, 'json', 0, json_encode('newvalue')], + // String falling back without default: error + ['name', null, false, null, false, 'json', 1, null], + + // Int "0" output as json/plain + ['name', 0, true, null, false, 'json', 0, json_encode(0)], + ['name', 0, true, null, false, 'plain', 0, '0'], + // Int "1" output as json/plain + ['name', 1, true, null, false, 'json', 0, json_encode(1)], + ['name', 1, true, null, false, 'plain', 0, '1'], + + // Bool "true" output as json/plain + ['name', true, true, null, false, 'json', 0, json_encode(true)], + ['name', true, true, null, false, 'plain', 0, 'true'], + // Bool "false" output as json/plain + ['name', false, true, null, false, 'json', 0, json_encode(false)], + ['name', false, true, null, false, 'plain', 0, 'false'], + + // Null output as json/plain + ['name', null, true, null, false, 'json', 0, json_encode(null)], + ['name', null, true, null, false, 'plain', 0, 'null'], + + // Array output as json/plain + ['name', ['a', 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])], + ['name', ['a', 'b'], true, null, false, 'plain', 0, "a\nb"], + // Key array output as json/plain + ['name', [0 => 'a', 1 => 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])], + ['name', [0 => 'a', 1 => 'b'], true, null, false, 'plain', 0, "a\nb"], + // Associative array output as json/plain + ['name', ['a' => 1, 'b' => 2], true, null, false, 'json', 0, json_encode(['a' => 1, 'b' => 2])], + ['name', ['a' => 1, 'b' => 2], true, null, false, 'plain', 0, "a: 1\nb: 2"], + + ]; + } + + /** + * @dataProvider getData + * + * @param string $configName + * @param mixed $value + * @param bool $configExists + * @param mixed $defaultValue + * @param bool $hasDefault + * @param string $outputFormat + * @param int $expectedReturn + * @param string $expectedMessage + */ + public function testGet($configName, $value, $configExists, $defaultValue, $hasDefault, $outputFormat, $expectedReturn, $expectedMessage) { + $this->config->expects($this->atLeastOnce()) + ->method('getAppKeys') + ->with('app-name') + ->willReturn($configExists ? [$configName] : []); + + if (!$expectedReturn) { + if ($configExists) { + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('app-name', $configName) + ->willReturn($value); + } + } + + $this->consoleInput->expects($this->exactly(2)) + ->method('getArgument') + ->willReturnMap([ + ['app', 'app-name'], + ['name', $configName], + ]); + $this->consoleInput->expects($this->any()) + ->method('getOption') + ->willReturnMap([ + ['default-value', $defaultValue], + ['output', $outputFormat], + ]); + $this->consoleInput->expects($this->any()) + ->method('hasParameterOption') + ->willReturnMap([ + ['--output', true], + ['--default-value', $hasDefault], + ]); + + if ($expectedMessage !== null) { + global $output; + + $output = ''; + $this->consoleOutput->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function($value) { + global $output; + $output .= $value . "\n"; + return $output; + }); + } + + $this->assertSame($expectedReturn, $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput])); + + if ($expectedMessage !== null) { + global $output; + // Remove the trailing newline + $this->assertSame($expectedMessage, substr($output, 0, -1)); + } + } +} diff --git a/tests/core/command/config/app/setconfigtest.php b/tests/core/command/config/app/setconfigtest.php new file mode 100644 index 0000000000..14d7b0cb7b --- /dev/null +++ b/tests/core/command/config/app/setconfigtest.php @@ -0,0 +1,118 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace Tests\Core\Command\Config\App; + + +use OC\Core\Command\Config\App\SetConfig; +use Test\TestCase; + +class SetConfigTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $config; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $config = $this->config = $this->getMockBuilder('OCP\IConfig') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OCP\IConfig $config */ + $this->command = new SetConfig($config); + } + + + public function setData() { + return [ + [ + 'name', + 'newvalue', + true, + true, + true, + 'info', + ], + [ + 'name', + 'newvalue', + false, + true, + false, + 'comment', + ], + ]; + } + + /** + * @dataProvider setData + * + * @param string $configName + * @param mixed $newValue + * @param bool $configExists + * @param bool $updateOnly + * @param bool $updated + * @param string $expectedMessage + */ + public function testSet($configName, $newValue, $configExists, $updateOnly, $updated, $expectedMessage) { + $this->config->expects($this->once()) + ->method('getAppKeys') + ->with('app-name') + ->willReturn($configExists ? [$configName] : []); + + if ($updated) { + $this->config->expects($this->once()) + ->method('setAppValue') + ->with('app-name', $configName, $newValue); + } + + $this->consoleInput->expects($this->exactly(2)) + ->method('getArgument') + ->willReturnMap([ + ['app', 'app-name'], + ['name', $configName], + ]); + $this->consoleInput->expects($this->any()) + ->method('getOption') + ->with('value') + ->willReturn($newValue); + $this->consoleInput->expects($this->any()) + ->method('hasParameterOption') + ->with('--update-only') + ->willReturn($updateOnly); + + $this->consoleOutput->expects($this->any()) + ->method('writeln') + ->with($this->stringContains($expectedMessage)); + + $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } +} diff --git a/tests/core/command/config/importtest.php b/tests/core/command/config/importtest.php new file mode 100644 index 0000000000..f14880f8bf --- /dev/null +++ b/tests/core/command/config/importtest.php @@ -0,0 +1,185 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace Tests\Core\Command\Config; + + +use OC\Core\Command\Config\Import; +use Test\TestCase; + +class ImportTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $config; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $config = $this->config = $this->getMockBuilder('OCP\IConfig') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OCP\IConfig $config */ + $this->command = new Import($config); + } + + public function validateAppsArrayData() { + return [ + [0], + [1], + [null], + ['new \Exception()'], + [json_encode([])], + ]; + } + + /** + * @dataProvider validateAppsArrayData + * + * @param mixed $configValue + */ + public function testValidateAppsArray($configValue) { + $this->invokePrivate($this->command, 'validateAppsArray', [['app' => ['name' => $configValue]]]); + $this->assertTrue(true, 'Asserting that no exception is thrown'); + } + + public function validateAppsArrayThrowsData() { + return [ + [false], + [true], + [[]], + [new \Exception()], + ]; + } + + /** + * @dataProvider validateAppsArrayThrowsData + * + * @param mixed $configValue + */ + public function testValidateAppsArrayThrows($configValue) { + try { + $this->invokePrivate($this->command, 'validateAppsArray', [['app' => ['name' => $configValue]]]); + $this->fail('Did not throw expected UnexpectedValueException'); + } catch (\UnexpectedValueException $e) { + $this->assertStringStartsWith('Invalid app config value for "app":"name".', $e->getMessage()); + } + } + + public function checkTypeRecursivelyData() { + return [ + [0], + [1], + [null], + ['new \Exception()'], + [json_encode([])], + [false], + [true], + [[]], + [['string']], + [['test' => 'string']], + [['test' => ['sub' => 'string']]], + ]; + } + + /** + * @dataProvider checkTypeRecursivelyData + * + * @param mixed $configValue + */ + public function testCheckTypeRecursively($configValue) { + $this->invokePrivate($this->command, 'checkTypeRecursively', [$configValue, 'name']); + $this->assertTrue(true, 'Asserting that no exception is thrown'); + } + + public function checkTypeRecursivelyThrowsData() { + return [ + [new \Exception()], + [[new \Exception()]], + [['test' => new \Exception()]], + [['test' => ['sub' => new \Exception()]]], + ]; + } + + /** + * @dataProvider checkTypeRecursivelyThrowsData + * + * @param mixed $configValue + */ + public function testCheckTypeRecursivelyThrows($configValue) { + try { + $this->invokePrivate($this->command, 'checkTypeRecursively', [$configValue, 'name']); + $this->fail('Did not throw expected UnexpectedValueException'); + } catch (\UnexpectedValueException $e) { + $this->assertStringStartsWith('Invalid system config value for "name"', $e->getMessage()); + } + } + + public function validateArrayData() { + return [ + [['system' => []]], + [['apps' => []]], + [['system' => [], 'apps' => []]], + ]; + } + + /** + * @dataProvider validateArrayData + * + * @param array $configArray + */ + public function testValidateArray($configArray) { + $this->invokePrivate($this->command, 'validateArray', [$configArray]); + $this->assertTrue(true, 'Asserting that no exception is thrown'); + } + + public function validateArrayThrowsData() { + return [ + [[], 'At least one key of the following is expected:'], + [[0 => []], 'Found invalid entries in root'], + [['string' => []], 'Found invalid entries in root'], + ]; + } + + /** + * @dataProvider validateArrayThrowsData + * + * @param mixed $configArray + * @param string $expectedException + */ + public function testValidateArrayThrows($configArray, $expectedException) { + try { + $this->invokePrivate($this->command, 'validateArray', [$configArray]); + $this->fail('Did not throw expected UnexpectedValueException'); + } catch (\UnexpectedValueException $e) { + $this->assertStringStartsWith($expectedException, $e->getMessage()); + } + } +} diff --git a/tests/core/command/config/listconfigstest.php b/tests/core/command/config/listconfigstest.php new file mode 100644 index 0000000000..c9913d83f7 --- /dev/null +++ b/tests/core/command/config/listconfigstest.php @@ -0,0 +1,300 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace Tests\Core\Command\Config; + + +use OC\Core\Command\Config\ListConfigs; +use Test\TestCase; + +class ListConfigsTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $appConfig; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $systemConfig; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $systemConfig = $this->systemConfig = $this->getMockBuilder('OC\SystemConfig') + ->disableOriginalConstructor() + ->getMock(); + $appConfig = $this->appConfig = $this->getMockBuilder('OCP\IAppConfig') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OC\SystemConfig $systemConfig */ + /** @var \OCP\IAppConfig $appConfig */ + $this->command = new ListConfigs($systemConfig, $appConfig); + } + + public function listData() { + return [ + [ + 'all', + // config.php + [ + 'secret', + 'overwrite.cli.url', + ], + [ + ['secret', 'N;', 'my secret'], + ['overwrite.cli.url', 'N;', 'http://localhost'], + ], + // app config + [ + ['files', false, [ + 'enabled' => 'yes', + ]], + ['core', false, [ + 'global_cache_gc_lastrun' => '1430388388', + ]], + ], + false, + json_encode([ + 'system' => [ + 'overwrite.cli.url' => 'http://localhost', + ], + 'apps' => [ + 'core' => [ + 'global_cache_gc_lastrun' => '1430388388', + ], + 'files' => [ + 'enabled' => 'yes', + ], + ], + ]), + ], + [ + 'all', + // config.php + [ + 'secret', + 'overwrite.cli.url', + ], + [ + ['secret', 'N;', 'my secret'], + ['overwrite.cli.url', 'N;', 'http://localhost'], + ], + // app config + [ + ['files', false, [ + 'enabled' => 'yes', + ]], + ['core', false, [ + 'global_cache_gc_lastrun' => '1430388388', + ]], + ], + true, + json_encode([ + 'system' => [ + 'secret' => 'my secret', + 'overwrite.cli.url' => 'http://localhost', + ], + 'apps' => [ + 'core' => [ + 'global_cache_gc_lastrun' => '1430388388', + ], + 'files' => [ + 'enabled' => 'yes', + ], + ], + ]), + ], + [ + 'system', + // config.php + [ + 'secret', + 'overwrite.cli.url', + ], + [ + ['secret', 'N;', 'my secret'], + ['overwrite.cli.url', 'N;', 'http://localhost'], + ], + // app config + [ + ['files', false, [ + 'enabled' => 'yes', + ]], + ['core', false, [ + 'global_cache_gc_lastrun' => '1430388388', + ]], + ], + false, + json_encode([ + 'system' => [ + 'overwrite.cli.url' => 'http://localhost', + ], + ]), + ], + [ + 'system', + // config.php + [ + 'secret', + 'overwrite.cli.url', + ], + [ + ['secret', 'N;', 'my secret'], + ['overwrite.cli.url', 'N;', 'http://localhost'], + ], + // app config + [ + ['files', false, [ + 'enabled' => 'yes', + ]], + ['core', false, [ + 'global_cache_gc_lastrun' => '1430388388', + ]], + ], + true, + json_encode([ + 'system' => [ + 'secret' => 'my secret', + 'overwrite.cli.url' => 'http://localhost', + ], + ]), + ], + [ + 'files', + // config.php + [ + 'secret', + 'overwrite.cli.url', + ], + [ + ['secret', 'N;', 'my secret'], + ['overwrite.cli.url', 'N;', 'http://localhost'], + ], + // app config + [ + ['files', false, [ + 'enabled' => 'yes', + ]], + ['core', false, [ + 'global_cache_gc_lastrun' => '1430388388', + ]], + ], + false, + json_encode([ + 'apps' => [ + 'files' => [ + 'enabled' => 'yes', + ], + ], + ]), + ], + [ + 'files', + // config.php + [ + 'secret', + 'overwrite.cli.url', + ], + [ + ['secret', 'N;', 'my secret'], + ['overwrite.cli.url', 'N;', 'http://localhost'], + ], + // app config + [ + ['files', false, [ + 'enabled' => 'yes', + ]], + ['core', false, [ + 'global_cache_gc_lastrun' => '1430388388', + ]], + ], + true, + json_encode([ + 'apps' => [ + 'files' => [ + 'enabled' => 'yes', + ], + ], + ]), + ], + ]; + } + + /** + * @dataProvider listData + * + * @param string $app + * @param array $systemConfigs + * @param array $systemConfigMap + * @param array $appConfig + * @param bool $private + * @param string $expected + */ + public function testList($app, $systemConfigs, $systemConfigMap, $appConfig, $private, $expected) { + $this->systemConfig->expects($this->any()) + ->method('getKeys') + ->willReturn($systemConfigs); + $this->systemConfig->expects($this->any()) + ->method('getValue') + ->willReturnMap($systemConfigMap); + + $this->appConfig->expects($this->any()) + ->method('getApps') + ->willReturn(['core', 'files']); + $this->appConfig->expects($this->any()) + ->method('getValues') + ->willReturnMap($appConfig); + + $this->consoleInput->expects($this->once()) + ->method('getArgument') + ->with('app') + ->willReturn($app); + + $this->consoleInput->expects($this->any()) + ->method('getOption') + ->willReturnMap([ + ['output', 'json'], + ['private', $private], + ]); + + global $output; + + $output = ''; + $this->consoleOutput->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function($value) { + global $output; + $output .= $value . "\n"; + return $output; + }); + + $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + + $this->assertEquals($expected, trim($output, "\n")); + } +} diff --git a/tests/core/command/config/system/deleteconfigtest.php b/tests/core/command/config/system/deleteconfigtest.php new file mode 100644 index 0000000000..aa1f93274c --- /dev/null +++ b/tests/core/command/config/system/deleteconfigtest.php @@ -0,0 +1,120 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace Tests\Core\Command\Config\System; + + +use OC\Core\Command\Config\System\DeleteConfig; +use Test\TestCase; + +class DeleteConfigTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $systemConfig; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $systemConfig = $this->systemConfig = $this->getMockBuilder('OC\SystemConfig') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OC\SystemConfig $systemConfig */ + $this->command = new DeleteConfig($systemConfig); + } + + + public function deleteData() { + return [ + [ + 'name', + true, + true, + 0, + 'info', + ], + [ + 'name', + true, + false, + 0, + 'info', + ], + [ + 'name', + false, + false, + 0, + 'info', + ], + [ + 'name', + false, + true, + 1, + 'error', + ], + ]; + } + + /** + * @dataProvider deleteData + * + * @param string $configName + * @param bool $configExists + * @param bool $checkIfExists + * @param int $expectedReturn + * @param string $expectedMessage + */ + public function testDelete($configName, $configExists, $checkIfExists, $expectedReturn, $expectedMessage) { + $this->systemConfig->expects(($checkIfExists) ? $this->once() : $this->never()) + ->method('getKeys') + ->willReturn($configExists ? [$configName] : []); + + $this->systemConfig->expects(($expectedReturn === 0) ? $this->once() : $this->never()) + ->method('deleteValue') + ->with($configName); + + $this->consoleInput->expects($this->once()) + ->method('getArgument') + ->with('name') + ->willReturn($configName); + $this->consoleInput->expects($this->any()) + ->method('hasParameterOption') + ->with('--error-if-not-exists') + ->willReturn($checkIfExists); + + $this->consoleOutput->expects($this->any()) + ->method('writeln') + ->with($this->stringContains($expectedMessage)); + + $this->assertSame($expectedReturn, $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput])); + } +} diff --git a/tests/core/command/config/system/getconfigtest.php b/tests/core/command/config/system/getconfigtest.php new file mode 100644 index 0000000000..54dcdd434a --- /dev/null +++ b/tests/core/command/config/system/getconfigtest.php @@ -0,0 +1,160 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace Tests\Core\Command\Config\System; + + +use OC\Core\Command\Config\System\GetConfig; +use Test\TestCase; + +class GetConfigTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $systemConfig; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $systemConfig = $this->systemConfig = $this->getMockBuilder('OC\SystemConfig') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OC\SystemConfig $systemConfig */ + $this->command = new GetConfig($systemConfig); + } + + + public function getData() { + return [ + // String output as json + ['name', 'newvalue', true, null, false, 'json', 0, json_encode('newvalue')], + // String output as plain text + ['name', 'newvalue', true, null, false, 'plain', 0, 'newvalue'], + // String falling back to default output as json + ['name', null, false, 'newvalue', true, 'json', 0, json_encode('newvalue')], + // String falling back without default: error + ['name', null, false, null, false, 'json', 1, null], + + // Int "0" output as json/plain + ['name', 0, true, null, false, 'json', 0, json_encode(0)], + ['name', 0, true, null, false, 'plain', 0, '0'], + // Int "1" output as json/plain + ['name', 1, true, null, false, 'json', 0, json_encode(1)], + ['name', 1, true, null, false, 'plain', 0, '1'], + + // Bool "true" output as json/plain + ['name', true, true, null, false, 'json', 0, json_encode(true)], + ['name', true, true, null, false, 'plain', 0, 'true'], + // Bool "false" output as json/plain + ['name', false, true, null, false, 'json', 0, json_encode(false)], + ['name', false, true, null, false, 'plain', 0, 'false'], + + // Null output as json/plain + ['name', null, true, null, false, 'json', 0, json_encode(null)], + ['name', null, true, null, false, 'plain', 0, 'null'], + + // Array output as json/plain + ['name', ['a', 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])], + ['name', ['a', 'b'], true, null, false, 'plain', 0, "a\nb"], + // Key array output as json/plain + ['name', [0 => 'a', 1 => 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])], + ['name', [0 => 'a', 1 => 'b'], true, null, false, 'plain', 0, "a\nb"], + // Associative array output as json/plain + ['name', ['a' => 1, 'b' => 2], true, null, false, 'json', 0, json_encode(['a' => 1, 'b' => 2])], + ['name', ['a' => 1, 'b' => 2], true, null, false, 'plain', 0, "a: 1\nb: 2"], + + ]; + } + + /** + * @dataProvider getData + * + * @param string $configName + * @param mixed $value + * @param bool $configExists + * @param mixed $defaultValue + * @param bool $hasDefault + * @param string $outputFormat + * @param int $expectedReturn + * @param string $expectedMessage + */ + public function testGet($configName, $value, $configExists, $defaultValue, $hasDefault, $outputFormat, $expectedReturn, $expectedMessage) { + $this->systemConfig->expects($this->atLeastOnce()) + ->method('getKeys') + ->willReturn($configExists ? [$configName] : []); + + if (!$expectedReturn) { + if ($configExists) { + $this->systemConfig->expects($this->once()) + ->method('getValue') + ->with($configName) + ->willReturn($value); + } + } + + $this->consoleInput->expects($this->once()) + ->method('getArgument') + ->with('name') + ->willReturn($configName); + $this->consoleInput->expects($this->any()) + ->method('getOption') + ->willReturnMap([ + ['default-value', $defaultValue], + ['output', $outputFormat], + ]); + $this->consoleInput->expects($this->any()) + ->method('hasParameterOption') + ->willReturnMap([ + ['--output', true], + ['--default-value', $hasDefault], + ]); + + if ($expectedMessage !== null) { + global $output; + + $output = ''; + $this->consoleOutput->expects($this->any()) + ->method('writeln') + ->willReturnCallback(function($value) { + global $output; + $output .= $value . "\n"; + return $output; + }); + } + + $this->assertSame($expectedReturn, $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput])); + + if ($expectedMessage !== null) { + global $output; + // Remove the trailing newline + $this->assertSame($expectedMessage, substr($output, 0, -1)); + } + } +} diff --git a/tests/core/command/config/system/setconfigtest.php b/tests/core/command/config/system/setconfigtest.php new file mode 100644 index 0000000000..bbabee9b18 --- /dev/null +++ b/tests/core/command/config/system/setconfigtest.php @@ -0,0 +1,115 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace Tests\Core\Command\Config\System; + + +use OC\Core\Command\Config\System\SetConfig; +use Test\TestCase; + +class SetConfigTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $systemConfig; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $systemConfig = $this->systemConfig = $this->getMockBuilder('OC\SystemConfig') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OC\SystemConfig $systemConfig */ + $this->command = new SetConfig($systemConfig); + } + + + public function setData() { + return [ + [ + 'name', + 'newvalue', + true, + true, + true, + 'info', + ], + [ + 'name', + 'newvalue', + false, + true, + false, + 'comment', + ], + ]; + } + + /** + * @dataProvider setData + * + * @param string $configName + * @param mixed $newValue + * @param bool $configExists + * @param bool $updateOnly + * @param bool $updated + * @param string $expectedMessage + */ + public function testSet($configName, $newValue, $configExists, $updateOnly, $updated, $expectedMessage) { + $this->systemConfig->expects($this->once()) + ->method('getKeys') + ->willReturn($configExists ? [$configName] : []); + + if ($updated) { + $this->systemConfig->expects($this->once()) + ->method('setValue') + ->with($configName, $newValue); + } + + $this->consoleInput->expects($this->once()) + ->method('getArgument') + ->with('name') + ->willReturn($configName); + $this->consoleInput->expects($this->any()) + ->method('getOption') + ->with('value') + ->willReturn($newValue); + $this->consoleInput->expects($this->any()) + ->method('hasParameterOption') + ->with('--update-only') + ->willReturn($updateOnly); + + $this->consoleOutput->expects($this->any()) + ->method('writeln') + ->with($this->stringContains($expectedMessage)); + + $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } +}