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..08b263627e --- /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('Set 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/system/deleteconfig.php b/core/command/config/system/deleteconfig.php index c4930a8d3a..49bc2bc8c0 100644 --- a/core/command/config/system/deleteconfig.php +++ b/core/command/config/system/deleteconfig.php @@ -49,7 +49,7 @@ class DeleteConfig extends Base { ->addArgument( 'name', InputArgument::REQUIRED, - 'Name of the config to set' + 'Name of the config to delete' ) ->addOption( 'error-if-not-exists', diff --git a/core/command/config/system/getconfig.php b/core/command/config/system/getconfig.php index 1ed3b36da3..df9b1a96ef 100644 --- a/core/command/config/system/getconfig.php +++ b/core/command/config/system/getconfig.php @@ -55,7 +55,7 @@ class GetConfig extends Base { 'default-value', null, InputOption::VALUE_OPTIONAL, - 'If no default value is set and the config does not exist, an error is thrown' + 'If no default value is set and the config does not exist, the command will exit with 1' ) ; } diff --git a/core/command/config/system/setconfig.php b/core/command/config/system/setconfig.php index 6eda9db8f9..cb2f9fe616 100644 --- a/core/command/config/system/setconfig.php +++ b/core/command/config/system/setconfig.php @@ -70,7 +70,7 @@ class SetConfig extends Base { $configName = $input->getArgument('name'); if (!in_array($configName, $this->systemConfig->getKeys()) && $input->hasParameterOption('--update-only')) { - $output->writeln('Value has not been updated, as it has not been set before.'); + $output->writeln('Value not updated, as it has not been set before.'); return 1; } $configValue = $input->getOption('value'); diff --git a/core/register_command.php b/core/register_command.php index 2f057ed56a..8815eca6b6 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -40,6 +40,9 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $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())); diff --git a/tests/core/command/config/app/deleteconfigtest.php b/tests/core/command/config/app/deleteconfigtest.php new file mode 100644 index 0000000000..3f369e8f40 --- /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, \Test_Helper::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..81d0eb7447 --- /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, \Test_Helper::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..0c4aa6850c --- /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)); + + \Test_Helper::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } +}