From b74b96bfbd0b778ac50fd17f5e107c51435b1678 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 29 May 2016 12:32:14 +0200 Subject: [PATCH] Adds ConfigJson which handle the configuration in JSON format. Also use the Updater to make the transition --- application/PageBuilder.php | 1 + application/Updater.php | 43 ++++- application/config/ConfigIO.php | 2 - application/config/ConfigJson.php | 66 ++++++++ application/config/ConfigManager.php | 7 +- tests/Updater/UpdaterTest.php | 66 +++++++- tests/config/ConfigJsonTest.php | 125 +++++++++++++++ tests/config/ConfigManagerTest.php | 160 +++++++++++++++++-- tests/utils/config/configInvalid.json.php | 4 + tests/utils/config/configJson.json.php | 19 +++ tests/utils/config/configUpdateDone.json.php | 4 + tpl/linklist.html | 2 +- tpl/tools.html | 2 +- 13 files changed, 469 insertions(+), 32 deletions(-) create mode 100644 application/config/ConfigJson.php create mode 100644 tests/config/ConfigJsonTest.php create mode 100644 tests/utils/config/configInvalid.json.php create mode 100644 tests/utils/config/configJson.json.php create mode 100644 tests/utils/config/configUpdateDone.json.php diff --git a/application/PageBuilder.php b/application/PageBuilder.php index cf13c71..1d3ba9e 100644 --- a/application/PageBuilder.php +++ b/application/PageBuilder.php @@ -75,6 +75,7 @@ class PageBuilder $this->tpl->assign('shaarlititle', $conf->get('title', 'Shaarli')); $this->tpl->assign('openshaarli', $conf->get('config.OPEN_SHAARLI', false)); $this->tpl->assign('showatom', $conf->get('config.SHOW_ATOM', false)); + $this->tpl->assign('hide_timestamps', $conf->get('config.HIDE_TIMESTAMPS', false)); // FIXME! Globals if (!empty($GLOBALS['plugin_errors'])) { $this->tpl->assign('plugin_errors', $GLOBALS['plugin_errors']); diff --git a/application/Updater.php b/application/Updater.php index 6b92af3..8552850 100644 --- a/application/Updater.php +++ b/application/Updater.php @@ -142,6 +142,48 @@ class Updater $this->linkDB->savedb($conf->get('config.PAGECACHE')); return true; } + + /** + * Move old configuration in PHP to the new config system in JSON format. + * + * Will rename 'config.php' into 'config.save.php' and create 'config.json'. + */ + public function updateMethodConfigToJson() + { + $conf = ConfigManager::getInstance(); + + // JSON config already exists, nothing to do. + if ($conf->getConfigIO() instanceof ConfigJson) { + return true; + } + + $configPhp = new ConfigPhp(); + $configJson = new ConfigJson(); + $oldConfig = $configPhp->read($conf::$CONFIG_FILE . '.php'); + rename($conf->getConfigFile(), $conf::$CONFIG_FILE . '.save.php'); + $conf->setConfigIO($configJson); + $conf->reload(); + + foreach (ConfigPhp::$ROOT_KEYS as $key) { + $conf->set($key, $oldConfig[$key]); + } + + // Set sub config keys (config and plugins) + $subConfig = array('config', 'plugins'); + foreach ($subConfig as $sub) { + foreach ($oldConfig[$sub] as $key => $value) { + $conf->set($sub .'.'. $key, $value); + } + } + + try{ + $conf->write($this->isLoggedIn); + return true; + } catch (IOException $e) { + error_log($e->getMessage()); + return false; + } + } } /** @@ -199,7 +241,6 @@ class UpdaterException extends Exception } } - /** * Read the updates file, and return already done updates. * diff --git a/application/config/ConfigIO.php b/application/config/ConfigIO.php index 4b1c990..2b68fe6 100644 --- a/application/config/ConfigIO.php +++ b/application/config/ConfigIO.php @@ -21,8 +21,6 @@ interface ConfigIO * * @param string $filepath Config file absolute path. * @param array $conf All configuration in an array. - * - * @return bool True if the configuration has been successfully written, false otherwise. */ function write($filepath, $conf); diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php new file mode 100644 index 0000000..cbafbf6 --- /dev/null +++ b/application/config/ConfigJson.php @@ -0,0 +1,66 @@ +configIO = new ConfigJson(); } else { $this->configIO = new ConfigPhp(); - }*/ - $this->configIO = new ConfigPhp(); + } $this->load(); } diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php index 8bfb4ba..f8de2f7 100644 --- a/tests/Updater/UpdaterTest.php +++ b/tests/Updater/UpdaterTest.php @@ -20,9 +20,9 @@ class UpdaterTest extends PHPUnit_Framework_TestCase protected static $testDatastore = 'sandbox/datastore.php'; /** - * @var string Config file path. + * @var string Config file path (without extension). */ - protected static $configFile = 'tests/Updater/config.php'; + protected static $configFile = 'tests/utils/config/configUpdater'; /** * @var ConfigManager @@ -52,8 +52,9 @@ class UpdaterTest extends PHPUnit_Framework_TestCase ) ); - ConfigManager::$CONFIG_FILE = 'tests/Updater/config'; - $this->conf = ConfigManager::getInstance(); + ConfigManager::$CONFIG_FILE = self::$configFile; + $this->conf = ConfigManager::reset(); + $this->conf->reload(); foreach (self::$configFields as $key => $value) { $this->conf->set($key, $value); } @@ -67,8 +68,8 @@ class UpdaterTest extends PHPUnit_Framework_TestCase */ public function tearDown() { - if (is_file(self::$configFile)) { - unlink(self::$configFile); + if (is_file('tests/Updater/config.json')) { + unlink('tests/Updater/config.json'); } if (is_file(self::$configFields['config']['DATADIR'] . '/options.php')) { @@ -214,6 +215,8 @@ class UpdaterTest extends PHPUnit_Framework_TestCase { // Use writeConfig to create a options.php ConfigManager::$CONFIG_FILE = 'tests/Updater/options'; + $this->conf->setConfigIO(new ConfigPhp()); + $invert = !$this->conf->get('privateLinkByDefault'); $this->conf->set('privateLinkByDefault', $invert); $this->conf->write(true); @@ -225,12 +228,15 @@ class UpdaterTest extends PHPUnit_Framework_TestCase // merge configs $updater = new Updater(array(), array(), true); + // This writes a new config file in tests/Updater/config.php $updater->updateMethodMergeDeprecatedConfigFile(); // make sure updated field is changed $this->conf->reload(); $this->assertEquals($invert, $this->conf->get('privateLinkByDefault')); $this->assertFalse(is_file($optionsFile)); + // Delete the generated file. + unlink($this->conf->getConfigFile()); } /** @@ -257,4 +263,52 @@ class UpdaterTest extends PHPUnit_Framework_TestCase $updater->updateMethodRenameDashTags(); $this->assertNotEmpty($linkDB->filterSearch(array('searchtags' => 'exclude'))); } + + /** + * Convert old PHP config file to JSON config. + */ + public function testConfigToJson() + { + $configFile = 'tests/utils/config/configPhp'; + ConfigManager::$CONFIG_FILE = $configFile; + $conf = ConfigManager::reset(); + + // The ConfigIO is initialized with ConfigPhp. + $this->assertTrue($conf->getConfigIO() instanceof ConfigPhp); + + $updater = new Updater(array(), array(), false); + $done = $updater->updateMethodConfigToJson(); + $this->assertTrue($done); + + // The ConfigIO has been updated to ConfigJson. + $this->assertTrue($conf->getConfigIO() instanceof ConfigJson); + $this->assertTrue(file_exists($conf->getConfigFile())); + + // Check JSON config data. + $conf->reload(); + $this->assertEquals('root', $conf->get('login')); + $this->assertEquals('lala', $conf->get('redirector')); + $this->assertEquals('data/datastore.php', $conf->get('config.DATASTORE')); + $this->assertEquals('1', $conf->get('plugins.WALLABAG_VERSION')); + + rename($configFile . '.save.php', $configFile . '.php'); + unlink($conf->getConfigFile()); + } + + /** + * Launch config conversion update with an existing JSON file => nothing to do. + */ + public function testConfigToJsonNothingToDo() + { + $configFile = 'tests/utils/config/configUpdateDone'; + ConfigManager::$CONFIG_FILE = $configFile; + $conf = ConfigManager::reset(); + $conf->reload(); + $filetime = filemtime($conf->getConfigFile()); + $updater = new Updater(array(), array(), false); + $done = $updater->updateMethodConfigToJson(); + $this->assertTrue($done); + $expected = filemtime($conf->getConfigFile()); + $this->assertEquals($expected, $filetime); + } } diff --git a/tests/config/ConfigJsonTest.php b/tests/config/ConfigJsonTest.php new file mode 100644 index 0000000..5b3bce4 --- /dev/null +++ b/tests/config/ConfigJsonTest.php @@ -0,0 +1,125 @@ +configIO = new ConfigJson(); + } + + /** + * Read a simple existing config file. + */ + public function testRead() + { + $conf = $this->configIO->read('tests/utils/config/configJson.json.php'); + $this->assertEquals('root', $conf['login']); + $this->assertEquals('lala', $conf['redirector']); + $this->assertEquals('data/datastore.php', $conf['config']['DATASTORE']); + $this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']); + } + + /** + * Read a non existent config file -> empty array. + */ + public function testReadNonExistent() + { + $this->assertEquals(array(), $this->configIO->read('nope')); + } + + /** + * Read a non existent config file -> empty array. + * + * @expectedException Exception + * @expectedExceptionMessage An error occured while parsing JSON file: error code #4 + */ + public function testReadInvalidJson() + { + $this->configIO->read('tests/utils/config/configInvalid.json.php'); + } + + /** + * Write a new config file. + */ + public function testWriteNew() + { + $dataFile = 'tests/utils/config/configWrite.json.php'; + $data = array( + 'login' => 'root', + 'redirector' => 'lala', + 'config' => array( + 'DATASTORE' => 'data/datastore.php', + ), + 'plugins' => array( + 'WALLABAG_VERSION' => '1', + ) + ); + $this->configIO->write($dataFile, $data); + // PHP 5.3 doesn't support json pretty print. + if (defined('JSON_PRETTY_PRINT')) { + $expected = '{ + "login": "root", + "redirector": "lala", + "config": { + "DATASTORE": "data\/datastore.php" + }, + "plugins": { + "WALLABAG_VERSION": "1" + } +}'; + } else { + $expected = '{"login":"root","redirector":"lala","config":{"DATASTORE":"data\/datastore.php"},"plugins":{"WALLABAG_VERSION":"1"}}'; + } + $expected = ConfigJson::$PHP_HEADER . $expected; + $this->assertEquals($expected, file_get_contents($dataFile)); + unlink($dataFile); + } + + /** + * Overwrite an existing setting. + */ + public function testOverwrite() + { + $source = 'tests/utils/config/configJson.json.php'; + $dest = 'tests/utils/config/configOverwrite.json.php'; + copy($source, $dest); + $conf = $this->configIO->read($dest); + $conf['redirector'] = 'blabla'; + $this->configIO->write($dest, $conf); + $conf = $this->configIO->read($dest); + $this->assertEquals('blabla', $conf['redirector']); + unlink($dest); + } + + /** + * Write to invalid path. + * + * @expectedException IOException + */ + public function testWriteInvalidArray() + { + $conf = array('conf' => 'value'); + @$this->configIO->write(array(), $conf); + } + + /** + * Write to invalid path. + * + * @expectedException IOException + */ + public function testWriteInvalidBlank() + { + $conf = array('conf' => 'value'); + @$this->configIO->write('', $conf); + } +} diff --git a/tests/config/ConfigManagerTest.php b/tests/config/ConfigManagerTest.php index 1b6358f..7390699 100644 --- a/tests/config/ConfigManagerTest.php +++ b/tests/config/ConfigManagerTest.php @@ -6,7 +6,7 @@ * Note: it only test the manager with ConfigJson, * ConfigPhp is only a workaround to handle the transition to JSON type. */ -class ConfigManagerTest extends \PHPUnit_Framework_TestCase +class ConfigManagerTest extends PHPUnit_Framework_TestCase { /** * @var ConfigManager @@ -15,34 +15,160 @@ class ConfigManagerTest extends \PHPUnit_Framework_TestCase public function setUp() { - ConfigManager::$CONFIG_FILE = 'tests/config/config'; - $this->conf = ConfigManager::getInstance(); + ConfigManager::$CONFIG_FILE = 'tests/utils/config/configJson'; + $this->conf = ConfigManager::reset(); } - public function tearDown() + /** + * Simple config test: + * 1. Set settings. + * 2. Check settings value. + */ + public function testSetGet() { - @unlink($this->conf->getConfigFile()); - } - - public function testSetWriteGet() - { - // This won't work with ConfigPhp. - $this->markTestIncomplete(); - $this->conf->set('paramInt', 42); $this->conf->set('paramString', 'value1'); $this->conf->set('paramBool', false); $this->conf->set('paramArray', array('foo' => 'bar')); $this->conf->set('paramNull', null); - $this->conf->write(true); - $this->conf->reload(); - $this->assertEquals(42, $this->conf->get('paramInt')); $this->assertEquals('value1', $this->conf->get('paramString')); $this->assertFalse($this->conf->get('paramBool')); $this->assertEquals(array('foo' => 'bar'), $this->conf->get('paramArray')); $this->assertEquals(null, $this->conf->get('paramNull')); } - -} \ No newline at end of file + + /** + * Set/write/get config test: + * 1. Set settings. + * 2. Write it to the config file. + * 3. Read the file. + * 4. Check settings value. + */ + public function testSetWriteGet() + { + $this->conf->set('paramInt', 42); + $this->conf->set('paramString', 'value1'); + $this->conf->set('paramBool', false); + $this->conf->set('paramArray', array('foo' => 'bar')); + $this->conf->set('paramNull', null); + + ConfigManager::$CONFIG_FILE = 'tests/utils/config/configTmp'; + $this->conf->write(true); + $this->conf->reload(); + unlink($this->conf->getConfigFile()); + + $this->assertEquals(42, $this->conf->get('paramInt')); + $this->assertEquals('value1', $this->conf->get('paramString')); + $this->assertFalse($this->conf->get('paramBool')); + $this->assertEquals(array('foo' => 'bar'), $this->conf->get('paramArray')); + $this->assertEquals(null, $this->conf->get('paramNull')); + } + + /** + * Test set/write/get with nested keys. + */ + public function testSetWriteGetNested() + { + $this->conf->set('foo.bar.key.stuff', 'testSetWriteGetNested'); + + ConfigManager::$CONFIG_FILE = 'tests/utils/config/configTmp'; + $this->conf->write(true); + $this->conf->reload(); + unlink($this->conf->getConfigFile()); + + $this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff')); + } + + /** + * Set with an empty key. + * + * @expectedException Exception + * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*# + */ + public function testSetEmptyKey() + { + $this->conf->set('', 'stuff'); + } + + /** + * Set with an array key. + * + * @expectedException Exception + * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*# + */ + public function testSetArrayKey() + { + $this->conf->set(array('foo' => 'bar'), 'stuff'); + } + + /** + * Try to write the config without mandatory parameter (e.g. 'login'). + * + * @expectedException MissingFieldConfigException + */ + public function testWriteMissingParameter() + { + ConfigManager::$CONFIG_FILE = 'tests/utils/config/configTmp'; + $this->assertFalse(file_exists($this->conf->getConfigFile())); + $this->conf->reload(); + + $this->conf->write(true); + } + + /** + * Try to get non existent config keys. + */ + public function testGetNonExistent() + { + $this->assertEquals('', $this->conf->get('nope.test')); + $this->assertEquals('default', $this->conf->get('nope.test', 'default')); + } + + /** + * Test the 'exists' method with existent values. + */ + public function testExistsOk() + { + $this->assertTrue($this->conf->exists('login')); + $this->assertTrue($this->conf->exists('config.foo')); + } + + /** + * Test the 'exists' method with non existent or invalid values. + */ + public function testExistsKo() + { + $this->assertFalse($this->conf->exists('nope')); + $this->assertFalse($this->conf->exists('nope.nope')); + $this->assertFalse($this->conf->exists('')); + $this->assertFalse($this->conf->exists(false)); + } + + /** + * Reset the ConfigManager instance. + */ + public function testReset() + { + $conf = $this->conf; + $this->assertTrue($conf === ConfigManager::getInstance()); + $this->assertFalse($conf === $this->conf->reset()); + $this->assertFalse($conf === ConfigManager::getInstance()); + } + + /** + * Reload the config from file. + */ + public function testReload() + { + ConfigManager::$CONFIG_FILE = 'tests/utils/config/configTmp'; + $newConf = ConfigJson::$PHP_HEADER . '{ "key": "value" }'; + file_put_contents($this->conf->getConfigFile(), $newConf); + $this->conf->reload(); + unlink($this->conf->getConfigFile()); + // Previous conf no longer exists, and new values have been loaded. + $this->assertFalse($this->conf->exists('login')); + $this->assertEquals('value', $this->conf->get('key')); + } +} diff --git a/tests/utils/config/configInvalid.json.php b/tests/utils/config/configInvalid.json.php new file mode 100644 index 0000000..c53e471 --- /dev/null +++ b/tests/utils/config/configInvalid.json.php @@ -0,0 +1,4 @@ +
{if="$value.description"}
{$value.description}
{/if} - {if="!$GLOBALS['config']['HIDE_TIMESTAMPS'] || isLoggedIn()"} + {if="!$hide_timestamps || isLoggedIn()"} {function="strftime('%c', $value.timestamp)"} - permalink - {else} permalink - diff --git a/tpl/tools.html b/tpl/tools.html index 78b8166..29ade72 100644 --- a/tpl/tools.html +++ b/tpl/tools.html @@ -9,7 +9,7 @@

Plugin administration: Enable, disable and configure plugins.

- {if="!$GLOBALS['config']['OPEN_SHAARLI']"}Change password: Change your password. + {if="$openshaarli"}Change password: Change your password.

{/if} Rename/delete tags: Rename or delete a tag in all links