mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-07-28 08:14:03 +02:00
Merge pull request #4765 from Icinga/drop-ini-backend-support
Drop ini backend support
This commit is contained in:
commit
db51fd79ab
@ -69,9 +69,8 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
$form = new PreferenceForm();
|
$form = new PreferenceForm();
|
||||||
$form->setPreferences($user->getPreferences());
|
$form->setPreferences($user->getPreferences());
|
||||||
if ($config->get('config_backend', 'db') !== 'none' && isset($config->config_resource)) {
|
if (isset($config->config_resource)) {
|
||||||
$form->setStore(PreferencesStore::create(new ConfigObject(array(
|
$form->setStore(PreferencesStore::create(new ConfigObject(array(
|
||||||
'store' => $config->get('config_backend', 'db'),
|
|
||||||
'resource' => $config->config_resource
|
'resource' => $config->config_resource
|
||||||
)), $user));
|
)), $user));
|
||||||
}
|
}
|
||||||
|
@ -70,57 +70,23 @@ class ApplicationConfigForm extends Form
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// we do not need this form for setup because we set the database there as default.
|
$backends = array_keys(ResourceFactory::getResourceConfigs()->toArray());
|
||||||
// this form is only displayed in configuration -> application if preferences backend type of ini is recognized
|
$backends = array_combine($backends, $backends);
|
||||||
if (isset($formData['global_config_backend']) && $formData['global_config_backend'] === 'ini') {
|
|
||||||
$this->addElement(
|
|
||||||
'select',
|
|
||||||
'global_config_backend',
|
|
||||||
[
|
|
||||||
'required' => true,
|
|
||||||
'autosubmit' => true,
|
|
||||||
'label' => $this->translate('User Preference Storage Type'),
|
|
||||||
'multiOptions' => [
|
|
||||||
'ini' => $this->translate('File System (INI Files)'),
|
|
||||||
'db' => $this->translate('Database')
|
|
||||||
]
|
|
||||||
]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->addElement(
|
|
||||||
'hidden',
|
|
||||||
'global_config_backend',
|
|
||||||
[
|
|
||||||
'required' => true,
|
|
||||||
'value' => 'db',
|
|
||||||
'disabled' => true
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! isset($formData['global_config_backend']) || $formData['global_config_backend'] === 'db') {
|
$this->addElement(
|
||||||
$backends = array();
|
'select',
|
||||||
foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) {
|
'global_config_resource',
|
||||||
if ($resource['type'] === 'db') {
|
array(
|
||||||
$backends[$name] = $name;
|
'required' => true,
|
||||||
}
|
'multiOptions' => array_merge(
|
||||||
}
|
['' => sprintf(' - %s - ', $this->translate('Please choose'))],
|
||||||
|
$backends
|
||||||
$this->addElement(
|
),
|
||||||
'select',
|
'disable' => [''],
|
||||||
'global_config_resource',
|
'value' => '',
|
||||||
array(
|
'label' => $this->translate('Configuration Database')
|
||||||
'required' => true,
|
)
|
||||||
'multiOptions' => array_merge(
|
);
|
||||||
['' => sprintf(' - %s - ', $this->translate('Please choose'))],
|
|
||||||
$backends
|
|
||||||
),
|
|
||||||
'disable' => [''],
|
|
||||||
'value' => '',
|
|
||||||
'label' => $this->translate('Configuration Database')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -37,13 +37,4 @@ class GeneralConfigForm extends ConfigForm
|
|||||||
$this->addSubForm($themingConfigForm->create($formData));
|
$this->addSubForm($themingConfigForm->create($formData));
|
||||||
$this->addSubForm($domainConfigForm->create($formData));
|
$this->addSubForm($domainConfigForm->create($formData));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onRequest()
|
|
||||||
{
|
|
||||||
parent::onRequest();
|
|
||||||
|
|
||||||
if ($this->config->get('global', 'config_backend') === 'ini') {
|
|
||||||
$this->warning('The preferences backend of type INI is deprecated and will be removed with version 2.11');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,7 @@ Option | Description
|
|||||||
-------------------------|-----------------------------------------------
|
-------------------------|-----------------------------------------------
|
||||||
show\_stacktraces | **Optional.** Whether to show debug stacktraces. Defaults to `0`.
|
show\_stacktraces | **Optional.** Whether to show debug stacktraces. Defaults to `0`.
|
||||||
module\_path | **Optional.** Specifies the directories where modules can be installed. Multiple directories must be separated with colons.
|
module\_path | **Optional.** Specifies the directories where modules can be installed. Multiple directories must be separated with colons.
|
||||||
config\_backend | **Optional.** Select the user preference storage. Can be set to `ini` (default), `db` or `none`. If `db` is selected, this requires the `config_resource` attribute.
|
config\_resource | **Required.** Specify a defined [resource](04-Resources.md#resources-configuration-database) name.
|
||||||
config\_resource | **Optional.** Specify a defined [resource](04-Resources.md#resources-configuration-database) name. Can only be used if `config_backend` is set to `db`.
|
|
||||||
|
|
||||||
|
|
||||||
Example for storing the user preferences in the database resource `icingaweb_db`:
|
Example for storing the user preferences in the database resource `icingaweb_db`:
|
||||||
@ -37,7 +36,6 @@ Example for storing the user preferences in the database resource `icingaweb_db`
|
|||||||
```
|
```
|
||||||
[global]
|
[global]
|
||||||
show_stacktraces = "0"
|
show_stacktraces = "0"
|
||||||
config_backend = "db"
|
|
||||||
config_resource = "icingaweb_db"
|
config_resource = "icingaweb_db"
|
||||||
module_path = "/usr/share/icingaweb2/modules"
|
module_path = "/usr/share/icingaweb2/modules"
|
||||||
```
|
```
|
||||||
|
@ -3,35 +3,13 @@
|
|||||||
Preferences are settings a user can set for their account only,
|
Preferences are settings a user can set for their account only,
|
||||||
for example the language and time zone.
|
for example the language and time zone.
|
||||||
|
|
||||||
Preferences can be stored either in INI files or in a MySQL or in a PostgreSQL database. By default, Icinga Web 2 stores
|
Preferences can be stored either in a MySQL or in a PostgreSQL database. The database must be configured.
|
||||||
preferences in INI files beneath Icinga Web 2's configuration directory.
|
|
||||||
|
|
||||||
```
|
|
||||||
/etc/icingaweb2/<username>/config.ini
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration <a id="preferences-configuration"></a>
|
## Configuration <a id="preferences-configuration"></a>
|
||||||
|
|
||||||
The preference configuration backend is defined in the global [config.ini](03-Configuration.md#configuration-general-global) file.
|
The preference configuration backend is defined in the global [config.ini](03-Configuration.md#configuration-general-global) file.
|
||||||
|
|
||||||
### Store Preferences in INI Files <a id="preferences-configuration-ini"></a>
|
You have to define a [database resource](04-Resources.md#resources-configuration-database)
|
||||||
|
|
||||||
If preferences are stored in INI Files, Icinga Web 2 automatically creates one file per user using the username as
|
|
||||||
file name for storing preferences. A INI file is created once a user saves changed preferences the first time.
|
|
||||||
The files are located beneath the `preferences` directory beneath Icinga Web 2's configuration directory.
|
|
||||||
|
|
||||||
You need to add the following section to the global [config.ini](03-Configuration.md#configuration-general-global) file
|
|
||||||
in order to store preferences in a file.
|
|
||||||
|
|
||||||
```
|
|
||||||
[global]
|
|
||||||
config_backend = "ini"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Store Preferences in a Database <a id="preferences-configuration-db"></a>
|
|
||||||
|
|
||||||
In order to be more flexible in distributed setups you can store preferences in a MySQL or in a PostgreSQL database.
|
|
||||||
For storing preferences in a database, you have to define a [database resource](04-Resources.md#resources-configuration-database)
|
|
||||||
which will be referenced as resource for the preferences storage.
|
which will be referenced as resource for the preferences storage.
|
||||||
|
|
||||||
You need to add the following section to the global [config.ini](03-Configuration.md#configuration-general-global) file
|
You need to add the following section to the global [config.ini](03-Configuration.md#configuration-general-global) file
|
||||||
@ -39,6 +17,5 @@ in order to store preferences in a database.
|
|||||||
|
|
||||||
```
|
```
|
||||||
[global]
|
[global]
|
||||||
config_backend = "db"
|
|
||||||
config_resource = "icingaweb_db"
|
config_resource = "icingaweb_db"
|
||||||
```
|
```
|
||||||
|
@ -6,6 +6,8 @@ v2.6 to v2.8 requires to follow the instructions for v2.7 too.
|
|||||||
## Upgrading to Icinga Web 2 2.11.x
|
## Upgrading to Icinga Web 2 2.11.x
|
||||||
|
|
||||||
* The Vagrant file and all its assets have been removed.
|
* The Vagrant file and all its assets have been removed.
|
||||||
|
* The `IniStore` class has been removed due to the deprecation of the Preferences ini backend.
|
||||||
|
* The `DbStore` class has been removed and its methods have been added to `PreferencesStore` class.
|
||||||
|
|
||||||
**Database Schema**
|
**Database Schema**
|
||||||
|
|
||||||
|
@ -376,26 +376,21 @@ class Auth
|
|||||||
$config = new Config();
|
$config = new Config();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($config->get('global', 'config_backend', 'db') !== 'none') {
|
$preferencesConfig = new ConfigObject([
|
||||||
$preferencesConfig = new ConfigObject([
|
'resource' => $config->get('global', 'config_resource')
|
||||||
'store' => $config->get('global', 'config_backend', 'db'),
|
]);
|
||||||
'resource' => $config->get('global', 'config_resource')
|
|
||||||
]);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$preferencesStore = PreferencesStore::create($preferencesConfig, $user);
|
$preferencesStore = PreferencesStore::create($preferencesConfig, $user);
|
||||||
$preferences = new Preferences($preferencesStore->load());
|
$preferences = new Preferences($preferencesStore->load());
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Logger::error(
|
Logger::error(
|
||||||
new IcingaException(
|
new IcingaException(
|
||||||
'Cannot load preferences for user "%s". An exception was thrown: %s',
|
'Cannot load preferences for user "%s". An exception was thrown: %s',
|
||||||
$user->getUsername(),
|
$user->getUsername(),
|
||||||
$e
|
$e
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$preferences = new Preferences();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$preferences = new Preferences();
|
$preferences = new Preferences();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,18 +3,21 @@
|
|||||||
|
|
||||||
namespace Icinga\User\Preferences;
|
namespace Icinga\User\Preferences;
|
||||||
|
|
||||||
use Icinga\Application\Config;
|
use Exception;
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Exception\NotReadableError;
|
||||||
|
use Icinga\Exception\NotWritableError;
|
||||||
use Icinga\User;
|
use Icinga\User;
|
||||||
use Icinga\User\Preferences;
|
use Icinga\User\Preferences;
|
||||||
use Icinga\Data\ConfigObject;
|
use Icinga\Data\ConfigObject;
|
||||||
use Icinga\Data\ResourceFactory;
|
use Icinga\Data\ResourceFactory;
|
||||||
use Icinga\Exception\ConfigurationError;
|
use Icinga\Exception\ConfigurationError;
|
||||||
use Icinga\Data\Db\DbConnection;
|
use Zend_Db_Expr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preferences store factory
|
* Preferences store factory
|
||||||
*
|
*
|
||||||
|
* Load and save user preferences by using a database
|
||||||
|
*
|
||||||
* Usage example:
|
* Usage example:
|
||||||
* <code>
|
* <code>
|
||||||
* <?php
|
* <?php
|
||||||
@ -23,11 +26,10 @@ use Icinga\Data\Db\DbConnection;
|
|||||||
* use Icinga\User\Preferences;
|
* use Icinga\User\Preferences;
|
||||||
* use Icinga\User\Preferences\PreferencesStore;
|
* use Icinga\User\Preferences\PreferencesStore;
|
||||||
*
|
*
|
||||||
* // Create a INI store
|
* // Create a db store
|
||||||
* $store = PreferencesStore::create(
|
* $store = PreferencesStore::create(
|
||||||
* new ConfigObject(
|
* new ConfigObject(
|
||||||
* 'store' => 'ini',
|
* 'resource' => 'resource name'
|
||||||
* 'config_path' => '/path/to/preferences'
|
|
||||||
* ),
|
* ),
|
||||||
* $user // Instance of \Icinga\User
|
* $user // Instance of \Icinga\User
|
||||||
* );
|
* );
|
||||||
@ -37,8 +39,52 @@ use Icinga\Data\Db\DbConnection;
|
|||||||
* $store->save($preferences);
|
* $store->save($preferences);
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
abstract class PreferencesStore
|
class PreferencesStore
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Column name for username
|
||||||
|
*/
|
||||||
|
const COLUMN_USERNAME = 'username';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column name for section
|
||||||
|
*/
|
||||||
|
const COLUMN_SECTION = 'section';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column name for preference
|
||||||
|
*/
|
||||||
|
const COLUMN_PREFERENCE = 'name';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column name for value
|
||||||
|
*/
|
||||||
|
const COLUMN_VALUE = 'value';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column name for created time
|
||||||
|
*/
|
||||||
|
const COLUMN_CREATED_TIME = 'ctime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Column name for modified time
|
||||||
|
*/
|
||||||
|
const COLUMN_MODIFIED_TIME = 'mtime';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table name
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $table = 'icingaweb_user_preference';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stored preferences
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $preferences = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store config
|
* Store config
|
||||||
*
|
*
|
||||||
@ -71,7 +117,7 @@ abstract class PreferencesStore
|
|||||||
*
|
*
|
||||||
* @return ConfigObject
|
* @return ConfigObject
|
||||||
*/
|
*/
|
||||||
public function getStoreConfig()
|
public function getStoreConfig(): ConfigObject
|
||||||
{
|
{
|
||||||
return $this->config;
|
return $this->config;
|
||||||
}
|
}
|
||||||
@ -81,7 +127,7 @@ abstract class PreferencesStore
|
|||||||
*
|
*
|
||||||
* @return User
|
* @return User
|
||||||
*/
|
*/
|
||||||
public function getUser()
|
public function getUser(): User
|
||||||
{
|
{
|
||||||
return $this->user;
|
return $this->user;
|
||||||
}
|
}
|
||||||
@ -89,21 +135,190 @@ abstract class PreferencesStore
|
|||||||
/**
|
/**
|
||||||
* Initialize the store
|
* Initialize the store
|
||||||
*/
|
*/
|
||||||
abstract protected function init();
|
protected function init(): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load preferences from source
|
* Load preferences from the database
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
|
*
|
||||||
|
* @throws NotReadableError In case the database operation failed
|
||||||
*/
|
*/
|
||||||
abstract public function load();
|
public function load(): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$select = $this->getStoreConfig()->connection->getDbAdapter()->select();
|
||||||
|
$result = $select
|
||||||
|
->from($this->table, [self::COLUMN_SECTION, self::COLUMN_PREFERENCE, self::COLUMN_VALUE])
|
||||||
|
->where(self::COLUMN_USERNAME . ' = ?', $this->getUser()->getUsername())
|
||||||
|
->query()
|
||||||
|
->fetchAll();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new NotReadableError(
|
||||||
|
'Cannot fetch preferences for user %s from database',
|
||||||
|
$this->getUser()->getUsername(),
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($result !== false) {
|
||||||
|
$values = [];
|
||||||
|
foreach ($result as $row) {
|
||||||
|
$values[$row->{self::COLUMN_SECTION}][$row->{self::COLUMN_PREFERENCE}] = $row->{self::COLUMN_VALUE};
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->preferences = $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->preferences;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the given preferences
|
* Save the given preferences in the database
|
||||||
*
|
*
|
||||||
* @param Preferences $preferences The preferences to save
|
* @param Preferences $preferences The preferences to save
|
||||||
*/
|
*/
|
||||||
abstract public function save(Preferences $preferences);
|
public function save(Preferences $preferences): void
|
||||||
|
{
|
||||||
|
$preferences = $preferences->toArray();
|
||||||
|
|
||||||
|
$sections = array_keys($preferences);
|
||||||
|
|
||||||
|
foreach ($sections as $section) {
|
||||||
|
if (! array_key_exists($section, $this->preferences)) {
|
||||||
|
$this->preferences[$section] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! array_key_exists($section, $preferences)) {
|
||||||
|
$preferences[$section] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$toBeInserted = array_diff_key($preferences[$section], $this->preferences[$section]);
|
||||||
|
if (!empty($toBeInserted)) {
|
||||||
|
$this->insert($toBeInserted, $section);
|
||||||
|
}
|
||||||
|
|
||||||
|
$toBeUpdated = array_intersect_key(
|
||||||
|
array_diff_assoc($preferences[$section], $this->preferences[$section]),
|
||||||
|
array_diff_assoc($this->preferences[$section], $preferences[$section])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!empty($toBeUpdated)) {
|
||||||
|
$this->update($toBeUpdated, $section);
|
||||||
|
}
|
||||||
|
|
||||||
|
$toBeDeleted = array_keys(array_diff_key($this->preferences[$section], $preferences[$section]));
|
||||||
|
if (!empty($toBeDeleted)) {
|
||||||
|
$this->delete($toBeDeleted, $section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the given preferences into the database
|
||||||
|
*
|
||||||
|
* @param array $preferences The preferences to insert
|
||||||
|
* @param string $section The preferences in section to update
|
||||||
|
*
|
||||||
|
* @throws NotWritableError In case the database operation failed
|
||||||
|
*/
|
||||||
|
protected function insert(array $preferences, string $section): void
|
||||||
|
{
|
||||||
|
/** @var \Zend_Db_Adapter_Abstract $db */
|
||||||
|
$db = $this->getStoreConfig()->connection->getDbAdapter();
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($preferences as $key => $value) {
|
||||||
|
$db->insert(
|
||||||
|
$this->table,
|
||||||
|
[
|
||||||
|
self::COLUMN_USERNAME => $this->getUser()->getUsername(),
|
||||||
|
$db->quoteIdentifier(self::COLUMN_SECTION) => $section,
|
||||||
|
$db->quoteIdentifier(self::COLUMN_PREFERENCE) => $key,
|
||||||
|
self::COLUMN_VALUE => $value,
|
||||||
|
self::COLUMN_CREATED_TIME => new Zend_Db_Expr('NOW()'),
|
||||||
|
self::COLUMN_MODIFIED_TIME => new Zend_Db_Expr('NOW()')
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new NotWritableError(
|
||||||
|
'Cannot insert preferences for user %s into database',
|
||||||
|
$this->getUser()->getUsername(),
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the given preferences in the database
|
||||||
|
*
|
||||||
|
* @param array $preferences The preferences to update
|
||||||
|
* @param string $section The preferences in section to update
|
||||||
|
*
|
||||||
|
* @throws NotWritableError In case the database operation failed
|
||||||
|
*/
|
||||||
|
protected function update(array $preferences, string $section): void
|
||||||
|
{
|
||||||
|
/** @var \Zend_Db_Adapter_Abstract $db */
|
||||||
|
$db = $this->getStoreConfig()->connection->getDbAdapter();
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($preferences as $key => $value) {
|
||||||
|
$db->update(
|
||||||
|
$this->table,
|
||||||
|
[
|
||||||
|
self::COLUMN_VALUE => $value,
|
||||||
|
self::COLUMN_MODIFIED_TIME => new Zend_Db_Expr('NOW()')
|
||||||
|
],
|
||||||
|
[
|
||||||
|
self::COLUMN_USERNAME . '=?' => $this->getUser()->getUsername(),
|
||||||
|
$db->quoteIdentifier(self::COLUMN_SECTION) . '=?' => $section,
|
||||||
|
$db->quoteIdentifier(self::COLUMN_PREFERENCE) . '=?' => $key
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new NotWritableError(
|
||||||
|
'Cannot update preferences for user %s in database',
|
||||||
|
$this->getUser()->getUsername(),
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the given preference names from the database
|
||||||
|
*
|
||||||
|
* @param array $preferenceKeys The preference names to delete
|
||||||
|
* @param string $section The preferences in section to update
|
||||||
|
*
|
||||||
|
* @throws NotWritableError In case the database operation failed
|
||||||
|
*/
|
||||||
|
protected function delete(array $preferenceKeys, string $section): void
|
||||||
|
{
|
||||||
|
/** @var \Zend_Db_Adapter_Abstract $db */
|
||||||
|
$db = $this->getStoreConfig()->connection->getDbAdapter();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db->delete(
|
||||||
|
$this->table,
|
||||||
|
[
|
||||||
|
self::COLUMN_USERNAME . '=?' => $this->getUser()->getUsername(),
|
||||||
|
$db->quoteIdentifier(self::COLUMN_SECTION) . '=?' => $section,
|
||||||
|
$db->quoteIdentifier(self::COLUMN_PREFERENCE) . ' IN (?)' => $preferenceKeys
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new NotWritableError(
|
||||||
|
'Cannot delete preferences for user %s from database',
|
||||||
|
$this->getUser()->getUsername(),
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create preferences storage adapter from config
|
* Create preferences storage adapter from config
|
||||||
@ -115,29 +330,15 @@ abstract class PreferencesStore
|
|||||||
*
|
*
|
||||||
* @throws ConfigurationError When the configuration defines an invalid storage type
|
* @throws ConfigurationError When the configuration defines an invalid storage type
|
||||||
*/
|
*/
|
||||||
public static function create(ConfigObject $config, User $user)
|
public static function create(ConfigObject $config, User $user): self
|
||||||
{
|
{
|
||||||
$type = ucfirst(strtolower($config->get('store', 'db')));
|
$resourceConfig = ResourceFactory::getResourceConfig($config->resource);
|
||||||
$storeClass = 'Icinga\\User\\Preferences\\Store\\' . $type . 'Store';
|
if ($resourceConfig->db === 'mysql') {
|
||||||
if (!class_exists($storeClass)) {
|
$resourceConfig->charset = 'utf8mb4';
|
||||||
throw new ConfigurationError(
|
|
||||||
'Preferences configuration defines an invalid storage type. Storage type %s not found',
|
|
||||||
$type
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type === 'Ini') {
|
$config->connection = ResourceFactory::createResource($resourceConfig);
|
||||||
Logger::warning('The preferences backend of type INI is deprecated and will be removed with version 2.11');
|
|
||||||
$config->location = Config::resolvePath('preferences');
|
|
||||||
} elseif ($type === 'Db') {
|
|
||||||
$resourceConfig = ResourceFactory::getResourceConfig($config->resource);
|
|
||||||
if ($resourceConfig->db === 'mysql') {
|
|
||||||
$resourceConfig->charset = 'utf8mb4';
|
|
||||||
}
|
|
||||||
|
|
||||||
$config->connection = ResourceFactory::createResource($resourceConfig);
|
return new self($config, $user);
|
||||||
}
|
|
||||||
|
|
||||||
return new $storeClass($config, $user);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,255 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
namespace Icinga\User\Preferences\Store;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use Icinga\Exception\NotReadableError;
|
|
||||||
use Icinga\Exception\NotWritableError;
|
|
||||||
use Icinga\User\Preferences;
|
|
||||||
use Icinga\User\Preferences\PreferencesStore;
|
|
||||||
use Zend_Db_Expr;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load and save user preferences by using a database
|
|
||||||
*/
|
|
||||||
class DbStore extends PreferencesStore
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Column name for username
|
|
||||||
*/
|
|
||||||
const COLUMN_USERNAME = 'username';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Column name for section
|
|
||||||
*/
|
|
||||||
const COLUMN_SECTION = 'section';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Column name for preference
|
|
||||||
*/
|
|
||||||
const COLUMN_PREFERENCE = 'name';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Column name for value
|
|
||||||
*/
|
|
||||||
const COLUMN_VALUE = 'value';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Column name for created time
|
|
||||||
*/
|
|
||||||
const COLUMN_CREATED_TIME = 'ctime';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Column name for modified time
|
|
||||||
*/
|
|
||||||
const COLUMN_MODIFIED_TIME = 'mtime';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Table name
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $table = 'icingaweb_user_preference';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stored preferences
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $preferences = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the table to use
|
|
||||||
*
|
|
||||||
* @param string $table The table name
|
|
||||||
*/
|
|
||||||
public function setTable($table)
|
|
||||||
{
|
|
||||||
$this->table = $table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the store
|
|
||||||
*/
|
|
||||||
protected function init()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load preferences from the database
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @throws NotReadableError In case the database operation failed
|
|
||||||
*/
|
|
||||||
public function load()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$select = $this->getStoreConfig()->connection->getDbAdapter()->select();
|
|
||||||
$result = $select
|
|
||||||
->from($this->table, array(self::COLUMN_SECTION, self::COLUMN_PREFERENCE, self::COLUMN_VALUE))
|
|
||||||
->where(self::COLUMN_USERNAME . ' = ?', $this->getUser()->getUsername())
|
|
||||||
->query()
|
|
||||||
->fetchAll();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
throw new NotReadableError(
|
|
||||||
'Cannot fetch preferences for user %s from database',
|
|
||||||
$this->getUser()->getUsername(),
|
|
||||||
$e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($result !== false) {
|
|
||||||
$values = array();
|
|
||||||
foreach ($result as $row) {
|
|
||||||
$values[$row->{self::COLUMN_SECTION}][$row->{self::COLUMN_PREFERENCE}] = $row->{self::COLUMN_VALUE};
|
|
||||||
}
|
|
||||||
$this->preferences = $values;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->preferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the given preferences in the database
|
|
||||||
*
|
|
||||||
* @param Preferences $preferences The preferences to save
|
|
||||||
*/
|
|
||||||
public function save(Preferences $preferences)
|
|
||||||
{
|
|
||||||
$preferences = $preferences->toArray();
|
|
||||||
|
|
||||||
$sections = array_keys($preferences);
|
|
||||||
|
|
||||||
foreach ($sections as $section) {
|
|
||||||
if (! array_key_exists($section, $this->preferences)) {
|
|
||||||
$this->preferences[$section] = array();
|
|
||||||
}
|
|
||||||
if (! array_key_exists($section, $preferences)) {
|
|
||||||
$preferences[$section] = array();
|
|
||||||
}
|
|
||||||
$toBeInserted = array_diff_key($preferences[$section], $this->preferences[$section]);
|
|
||||||
if (!empty($toBeInserted)) {
|
|
||||||
$this->insert($toBeInserted, $section);
|
|
||||||
}
|
|
||||||
|
|
||||||
$toBeUpdated = array_intersect_key(
|
|
||||||
array_diff_assoc($preferences[$section], $this->preferences[$section]),
|
|
||||||
array_diff_assoc($this->preferences[$section], $preferences[$section])
|
|
||||||
);
|
|
||||||
if (!empty($toBeUpdated)) {
|
|
||||||
$this->update($toBeUpdated, $section);
|
|
||||||
}
|
|
||||||
|
|
||||||
$toBeDeleted = array_keys(array_diff_key($this->preferences[$section], $preferences[$section]));
|
|
||||||
if (!empty($toBeDeleted)) {
|
|
||||||
$this->delete($toBeDeleted, $section);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert the given preferences into the database
|
|
||||||
*
|
|
||||||
* @param array $preferences The preferences to insert
|
|
||||||
* @param string $section The preferences in section to update
|
|
||||||
*
|
|
||||||
* @throws NotWritableError In case the database operation failed
|
|
||||||
*/
|
|
||||||
protected function insert(array $preferences, $section)
|
|
||||||
{
|
|
||||||
/** @var \Zend_Db_Adapter_Abstract $db */
|
|
||||||
$db = $this->getStoreConfig()->connection->getDbAdapter();
|
|
||||||
|
|
||||||
try {
|
|
||||||
foreach ($preferences as $key => $value) {
|
|
||||||
$db->insert(
|
|
||||||
$this->table,
|
|
||||||
array(
|
|
||||||
self::COLUMN_USERNAME => $this->getUser()->getUsername(),
|
|
||||||
$db->quoteIdentifier(self::COLUMN_SECTION) => $section,
|
|
||||||
$db->quoteIdentifier(self::COLUMN_PREFERENCE) => $key,
|
|
||||||
self::COLUMN_VALUE => $value,
|
|
||||||
self::COLUMN_CREATED_TIME => new Zend_Db_Expr('NOW()'),
|
|
||||||
self::COLUMN_MODIFIED_TIME => new Zend_Db_Expr('NOW()')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
throw new NotWritableError(
|
|
||||||
'Cannot insert preferences for user %s into database',
|
|
||||||
$this->getUser()->getUsername(),
|
|
||||||
$e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the given preferences in the database
|
|
||||||
*
|
|
||||||
* @param array $preferences The preferences to update
|
|
||||||
* @param string $section The preferences in section to update
|
|
||||||
*
|
|
||||||
* @throws NotWritableError In case the database operation failed
|
|
||||||
*/
|
|
||||||
protected function update(array $preferences, $section)
|
|
||||||
{
|
|
||||||
/** @var \Zend_Db_Adapter_Abstract $db */
|
|
||||||
$db = $this->getStoreConfig()->connection->getDbAdapter();
|
|
||||||
|
|
||||||
try {
|
|
||||||
foreach ($preferences as $key => $value) {
|
|
||||||
$db->update(
|
|
||||||
$this->table,
|
|
||||||
array(
|
|
||||||
self::COLUMN_VALUE => $value,
|
|
||||||
self::COLUMN_MODIFIED_TIME => new Zend_Db_Expr('NOW()')
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
self::COLUMN_USERNAME . '=?' => $this->getUser()->getUsername(),
|
|
||||||
$db->quoteIdentifier(self::COLUMN_SECTION) . '=?' => $section,
|
|
||||||
$db->quoteIdentifier(self::COLUMN_PREFERENCE) . '=?' => $key
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
throw new NotWritableError(
|
|
||||||
'Cannot update preferences for user %s in database',
|
|
||||||
$this->getUser()->getUsername(),
|
|
||||||
$e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the given preference names from the database
|
|
||||||
*
|
|
||||||
* @param array $preferenceKeys The preference names to delete
|
|
||||||
* @param string $section The preferences in section to update
|
|
||||||
*
|
|
||||||
* @throws NotWritableError In case the database operation failed
|
|
||||||
*/
|
|
||||||
protected function delete(array $preferenceKeys, $section)
|
|
||||||
{
|
|
||||||
/** @var \Zend_Db_Adapter_Abstract $db */
|
|
||||||
$db = $this->getStoreConfig()->connection->getDbAdapter();
|
|
||||||
|
|
||||||
try {
|
|
||||||
$db->delete(
|
|
||||||
$this->table,
|
|
||||||
array(
|
|
||||||
self::COLUMN_USERNAME . '=?' => $this->getUser()->getUsername(),
|
|
||||||
$db->quoteIdentifier(self::COLUMN_SECTION) . '=?' => $section,
|
|
||||||
$db->quoteIdentifier(self::COLUMN_PREFERENCE) . ' IN (?)' => $preferenceKeys
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
throw new NotWritableError(
|
|
||||||
'Cannot delete preferences for user %s from database',
|
|
||||||
$this->getUser()->getUsername(),
|
|
||||||
$e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
namespace Icinga\User\Preferences\Store;
|
|
||||||
|
|
||||||
use Icinga\Application\Config;
|
|
||||||
use Icinga\Exception\NotReadableError;
|
|
||||||
use Icinga\Exception\NotWritableError;
|
|
||||||
use Icinga\User\Preferences;
|
|
||||||
use Icinga\User\Preferences\PreferencesStore;
|
|
||||||
use Icinga\File\Ini\IniParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load and save user preferences from and to INI files
|
|
||||||
*/
|
|
||||||
class IniStore extends PreferencesStore
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Preferences file of the given user
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $preferencesFile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stored preferences
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $preferences = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the store
|
|
||||||
*/
|
|
||||||
protected function init()
|
|
||||||
{
|
|
||||||
$this->preferencesFile = sprintf(
|
|
||||||
'%s/%s/config.ini',
|
|
||||||
$this->getStoreConfig()->location,
|
|
||||||
strtolower($this->getUser()->getUsername())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load preferences from source
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @throws NotReadableError When the INI file of the user exists and is not readable
|
|
||||||
*/
|
|
||||||
public function load()
|
|
||||||
{
|
|
||||||
if (file_exists($this->preferencesFile)) {
|
|
||||||
if (! is_readable($this->preferencesFile)) {
|
|
||||||
throw new NotReadableError(
|
|
||||||
'Preferences INI file %s for user %s is not readable',
|
|
||||||
$this->preferencesFile,
|
|
||||||
$this->getUser()->getUsername()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->preferences = IniParser::parseIniFile($this->preferencesFile)->toArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->preferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the given preferences
|
|
||||||
*
|
|
||||||
* @param Preferences $preferences The preferences to save
|
|
||||||
*/
|
|
||||||
public function save(Preferences $preferences)
|
|
||||||
{
|
|
||||||
$this->preferences = $preferences->toArray();
|
|
||||||
|
|
||||||
// TODO: Elaborate whether we need to patch the contents
|
|
||||||
// $preferences = $preferences->toArray();
|
|
||||||
// $this->update(array_diff_assoc($preferences, $this->preferences));
|
|
||||||
// $this->delete(array_keys(array_diff_key($this->preferences, $preferences)));
|
|
||||||
|
|
||||||
$this->write();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the preferences
|
|
||||||
*
|
|
||||||
* @throws NotWritableError In case the INI file cannot be written
|
|
||||||
*/
|
|
||||||
public function write()
|
|
||||||
{
|
|
||||||
Config::fromArray($this->preferences)->saveIni($this->preferencesFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add or update the given preferences
|
|
||||||
*
|
|
||||||
* @param array $preferences The preferences to set
|
|
||||||
*/
|
|
||||||
protected function update(array $preferences)
|
|
||||||
{
|
|
||||||
foreach ($preferences as $key => $value) {
|
|
||||||
$this->preferences[$key] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the given preferences by name
|
|
||||||
*
|
|
||||||
* @param array $preferenceKeys The preference names to delete
|
|
||||||
*/
|
|
||||||
protected function delete(array $preferenceKeys)
|
|
||||||
{
|
|
||||||
foreach ($preferenceKeys as $key) {
|
|
||||||
unset($this->preferences[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,9 +10,9 @@ use Icinga\Data\ConfigObject;
|
|||||||
use Icinga\Data\ResourceFactory;
|
use Icinga\Data\ResourceFactory;
|
||||||
use Icinga\Exception\NotReadableError;
|
use Icinga\Exception\NotReadableError;
|
||||||
use Icinga\Exception\NotWritableError;
|
use Icinga\Exception\NotWritableError;
|
||||||
|
use Icinga\File\Ini\IniParser;
|
||||||
use Icinga\User;
|
use Icinga\User;
|
||||||
use Icinga\User\Preferences\Store\IniStore;
|
use Icinga\User\Preferences\PreferencesStore;
|
||||||
use Icinga\User\Preferences\Store\DbStore;
|
|
||||||
use Icinga\Util\DirectoryIterator;
|
use Icinga\Util\DirectoryIterator;
|
||||||
|
|
||||||
class PreferencesCommand extends Command
|
class PreferencesCommand extends Command
|
||||||
@ -61,12 +61,15 @@ class PreferencesCommand extends Command
|
|||||||
|
|
||||||
Logger::info('Migrating INI preferences for user "%s" to database...', $userName);
|
Logger::info('Migrating INI preferences for user "%s" to database...', $userName);
|
||||||
|
|
||||||
$iniStore = new IniStore(new ConfigObject(['location' => $preferencesPath]), new User($userName));
|
$dbStore = new PreferencesStore(new ConfigObject(['connection' => $connection]), new User($userName));
|
||||||
$dbStore = new DbStore(new ConfigObject(['connection' => $connection]), new User($userName));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$dbStore->load();
|
$dbStore->load();
|
||||||
$dbStore->save(new User\Preferences($iniStore->load()));
|
$dbStore->save(
|
||||||
|
new User\Preferences(
|
||||||
|
$this->loadIniFile($preferencesPath, (new User($userName))->getUsername())
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (NotReadableError $e) {
|
} catch (NotReadableError $e) {
|
||||||
if ($e->getPrevious() !== null) {
|
if ($e->getPrevious() !== null) {
|
||||||
Logger::error('%s: %s', $e->getMessage(), $e->getPrevious()->getMessage());
|
Logger::error('%s: %s', $e->getMessage(), $e->getPrevious()->getMessage());
|
||||||
@ -89,7 +92,6 @@ class PreferencesCommand extends Command
|
|||||||
if ($this->params->has('resource') && ! $this->params->has('no-set-config-backend')) {
|
if ($this->params->has('resource') && ! $this->params->has('no-set-config-backend')) {
|
||||||
$appConfig = Config::app();
|
$appConfig = Config::app();
|
||||||
$globalConfig = $appConfig->getSection('global');
|
$globalConfig = $appConfig->getSection('global');
|
||||||
$globalConfig['config_backend'] = 'db';
|
|
||||||
$globalConfig['config_resource'] = $resource;
|
$globalConfig['config_resource'] = $resource;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -102,4 +104,28 @@ class PreferencesCommand extends Command
|
|||||||
|
|
||||||
Logger::info('Successfully migrated all local user preferences to database');
|
Logger::info('Successfully migrated all local user preferences to database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function loadIniFile(string $filePath, string $username): array
|
||||||
|
{
|
||||||
|
$preferences = [];
|
||||||
|
$preferencesFile = sprintf(
|
||||||
|
'%s/%s/config.ini',
|
||||||
|
$filePath,
|
||||||
|
strtolower($username)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (file_exists($preferencesFile)) {
|
||||||
|
if (! is_readable($preferencesFile)) {
|
||||||
|
throw new NotReadableError(
|
||||||
|
'Preferences INI file %s for user %s is not readable',
|
||||||
|
$preferencesFile,
|
||||||
|
$username
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$preferences = IniParser::parseIniFile($preferencesFile)->toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $preferences;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,80 +178,51 @@ class UserDomainMigration
|
|||||||
{
|
{
|
||||||
$config = Config::app();
|
$config = Config::app();
|
||||||
|
|
||||||
$type = $config->get('global', 'config_backend', 'ini');
|
$resourceConfig = ResourceFactory::getResourceConfig($config->get('global', 'config_resource'));
|
||||||
|
if ($resourceConfig->db === 'mysql') {
|
||||||
|
$resourceConfig->charset = 'utf8mb4';
|
||||||
|
}
|
||||||
|
|
||||||
switch ($type) {
|
/** @var DbConnection $conn */
|
||||||
case 'ini':
|
$conn = ResourceFactory::createResource($resourceConfig);
|
||||||
$directory = Config::resolvePath('preferences');
|
|
||||||
|
|
||||||
$migration = array();
|
$query = $conn
|
||||||
|
->select()
|
||||||
|
->from('icingaweb_user_preference', array('username'))
|
||||||
|
->group('username');
|
||||||
|
|
||||||
if (DirectoryIterator::isReadable($directory)) {
|
if ($this->map !== null) {
|
||||||
foreach (new DirectoryIterator($directory) as $username => $path) {
|
$query->applyFilter(Filter::matchAny(Filter::where('username', array_keys($this->map))));
|
||||||
$user = new User($username);
|
}
|
||||||
|
|
||||||
if (! $this->mustMigrate($user)) {
|
$users = $query->fetchColumn();
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$migrated = $this->migrateUser($user);
|
$migration = array();
|
||||||
|
|
||||||
$migration[$path] = dirname($path) . '/' . $migrated->getUsername();
|
foreach ($users as $username) {
|
||||||
}
|
$user = new User($username);
|
||||||
|
|
||||||
foreach ($migration as $from => $to) {
|
if (! $this->mustMigrate($user)) {
|
||||||
rename($from, $to);
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
$migrated = $this->migrateUser($user);
|
||||||
case 'db':
|
|
||||||
$resourceConfig = ResourceFactory::getResourceConfig($config->get('global', 'config_resource'));
|
|
||||||
if ($resourceConfig->db === 'mysql') {
|
|
||||||
$resourceConfig->charset = 'utf8mb4';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var DbConnection $conn */
|
$migration[$username] = $migrated->getUsername();
|
||||||
$conn = ResourceFactory::createResource($resourceConfig);
|
}
|
||||||
|
|
||||||
$query = $conn
|
if (! empty($migration)) {
|
||||||
->select()
|
$conn->getDbAdapter()->beginTransaction();
|
||||||
->from('icingaweb_user_preference', array('username'))
|
|
||||||
->group('username');
|
|
||||||
|
|
||||||
if ($this->map !== null) {
|
foreach ($migration as $originalUsername => $username) {
|
||||||
$query->applyFilter(Filter::matchAny(Filter::where('username', array_keys($this->map))));
|
$conn->update(
|
||||||
}
|
'icingaweb_user_preference',
|
||||||
|
array('username' => $username),
|
||||||
|
Filter::where('username', $originalUsername)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$users = $query->fetchColumn();
|
$conn->getDbAdapter()->commit();
|
||||||
|
|
||||||
$migration = array();
|
|
||||||
|
|
||||||
foreach ($users as $username) {
|
|
||||||
$user = new User($username);
|
|
||||||
|
|
||||||
if (! $this->mustMigrate($user)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$migrated = $this->migrateUser($user);
|
|
||||||
|
|
||||||
$migration[$username] = $migrated->getUsername();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! empty($migration)) {
|
|
||||||
$conn->getDbAdapter()->beginTransaction();
|
|
||||||
|
|
||||||
foreach ($migration as $originalUsername => $username) {
|
|
||||||
$conn->update(
|
|
||||||
'icingaweb_user_preference',
|
|
||||||
array('username' => $username),
|
|
||||||
Filter::where('username', $originalUsername)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn->getDbAdapter()->commit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +28,7 @@ class GeneralConfigStep extends Step
|
|||||||
$config[$section][$property] = $value;
|
$config[$section][$property] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($config['global']['config_backend'] === 'db') {
|
$config['global']['config_resource'] = $this->data['resourceName'];
|
||||||
$config['global']['config_resource'] = $this->data['resourceName'];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Config::fromArray($config)
|
Config::fromArray($config)
|
||||||
@ -57,12 +55,7 @@ class GeneralConfigStep extends Step
|
|||||||
? t('An exception\'s stacktrace is shown to every user by default.')
|
? t('An exception\'s stacktrace is shown to every user by default.')
|
||||||
: t('An exception\'s stacktrace is hidden from every user by default.')
|
: t('An exception\'s stacktrace is hidden from every user by default.')
|
||||||
) . '</li>'
|
) . '</li>'
|
||||||
. '<li>' . sprintf(
|
. '<li>' . t('Preferences will be stored using a database.') . '</li>'
|
||||||
$this->data['generalConfig']['global_config_backend'] === 'ini' ? sprintf(
|
|
||||||
t('Preferences will be stored per user account in INI files at: %s'),
|
|
||||||
Config::resolvePath('preferences')
|
|
||||||
) : t('Preferences will be stored using a database.')
|
|
||||||
) . '</li>'
|
|
||||||
. '</ul>';
|
. '</ul>';
|
||||||
|
|
||||||
$type = $this->data['generalConfig']['logging_log'];
|
$type = $this->data['generalConfig']['logging_log'];
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
|
|
||||||
|
|
||||||
namespace Tests\Icinga\User\Preferences\Store;
|
|
||||||
|
|
||||||
use Mockery;
|
|
||||||
use Icinga\Data\ConfigObject;
|
|
||||||
use Icinga\Test\BaseTestCase;
|
|
||||||
use Icinga\User\Preferences\Store\IniStore;
|
|
||||||
|
|
||||||
class IniStoreWithSetGetPreferencesAndEmptyWrite extends IniStore
|
|
||||||
{
|
|
||||||
public function write()
|
|
||||||
{
|
|
||||||
// Gets called by IniStore::save
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setPreferences($preferences)
|
|
||||||
{
|
|
||||||
$this->preferences = $preferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPreferences()
|
|
||||||
{
|
|
||||||
return $this->preferences;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IniStoreTest extends BaseTestCase
|
|
||||||
{
|
|
||||||
public function testWhetherPreferenceChangesAreApplied()
|
|
||||||
{
|
|
||||||
$store = $this->getStore();
|
|
||||||
$store->setPreferences(array('testsection' => array('key1' => '1')));
|
|
||||||
|
|
||||||
$store->save(
|
|
||||||
Mockery::mock('Icinga\User\Preferences', array(
|
|
||||||
'toArray' => array('testsection' => array('key1' => '11', 'key2' => '2'))
|
|
||||||
))
|
|
||||||
);
|
|
||||||
$this->assertEquals(
|
|
||||||
array('testsection' => array('key1' => '11', 'key2' => '2')),
|
|
||||||
$store->getPreferences(),
|
|
||||||
'IniStore::save does not properly apply changed preferences'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testWhetherPreferenceDeletionsAreApplied()
|
|
||||||
{
|
|
||||||
$store = $this->getStore();
|
|
||||||
$store->setPreferences(array('testsection' => array('key' => 'value')));
|
|
||||||
|
|
||||||
$store->save(Mockery::mock('Icinga\User\Preferences', array('toArray' => array('testsection' => array()))));
|
|
||||||
|
|
||||||
$result = $store->getPreferences();
|
|
||||||
|
|
||||||
$this->assertEmpty($result['testsection'], 'IniStore::save does not delete removed preferences');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getStore()
|
|
||||||
{
|
|
||||||
return new IniStoreWithSetGetPreferencesAndEmptyWrite(
|
|
||||||
new ConfigObject(
|
|
||||||
array(
|
|
||||||
'location' => 'some/Path/To/Some/Directory'
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Mockery::mock('Icinga\User', array('getUsername' => 'unittest'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
|
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
namespace Tests\Icinga\User\Preferences\Store;
|
namespace Tests\Icinga\User\Preferences;
|
||||||
|
|
||||||
|
use Icinga\User\Preferences\PreferencesStore;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
use Icinga\Data\ConfigObject;
|
use Icinga\Data\ConfigObject;
|
||||||
use Icinga\Exception\NotWritableError;
|
use Icinga\Exception\NotWritableError;
|
||||||
use Icinga\Test\BaseTestCase;
|
use Icinga\Test\BaseTestCase;
|
||||||
use Icinga\User\Preferences\Store\DbStore;
|
|
||||||
|
|
||||||
class DatabaseMock
|
class DatabaseMock
|
||||||
{
|
{
|
||||||
@ -22,19 +22,19 @@ class DatabaseMock
|
|||||||
|
|
||||||
public function insert($table, $row)
|
public function insert($table, $row)
|
||||||
{
|
{
|
||||||
$this->insertions[$row[DbStore::COLUMN_PREFERENCE]] = $row[DbStore::COLUMN_VALUE];
|
$this->insertions[$row[PreferencesStore::COLUMN_PREFERENCE]] = $row[PreferencesStore::COLUMN_VALUE];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update($table, $columns, $where)
|
public function update($table, $columns, $where)
|
||||||
{
|
{
|
||||||
$this->updates[$where[DbStore::COLUMN_PREFERENCE . '=?']] = $columns[DbStore::COLUMN_VALUE];
|
$this->updates[$where[PreferencesStore::COLUMN_PREFERENCE . '=?']] = $columns[PreferencesStore::COLUMN_VALUE];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($table, $where)
|
public function delete($table, $where)
|
||||||
{
|
{
|
||||||
$this->deletions = array_merge(
|
$this->deletions = array_merge(
|
||||||
$this->deletions,
|
$this->deletions,
|
||||||
$where[DbStore::COLUMN_PREFERENCE . ' IN (?)']
|
$where[PreferencesStore::COLUMN_PREFERENCE . ' IN (?)']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ class FaultyDatabaseMock extends DatabaseMock
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DbStoreWithSetPreferences extends DbStore
|
class PreferencesStoreWithSetPreferences extends PreferencesStore
|
||||||
{
|
{
|
||||||
public function setPreferences(array $preferences)
|
public function setPreferences(array $preferences)
|
||||||
{
|
{
|
||||||
@ -65,7 +65,7 @@ class DbStoreWithSetPreferences extends DbStore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DbStoreTest extends BaseTestCase
|
class PreferencesStoreTest extends BaseTestCase
|
||||||
{
|
{
|
||||||
public function testWhetherPreferenceInsertionWorks()
|
public function testWhetherPreferenceInsertionWorks()
|
||||||
{
|
{
|
||||||
@ -78,9 +78,9 @@ class DbStoreTest extends BaseTestCase
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertArrayHasKey('key', $dbMock->insertions, 'DbStore::save does not insert new preferences');
|
$this->assertArrayHasKey('key', $dbMock->insertions, 'PreferencesStore::save does not insert new preferences');
|
||||||
$this->assertEmpty($dbMock->updates, 'DbStore::save updates *new* preferences');
|
$this->assertEmpty($dbMock->updates, 'PreferencesStore::save updates *new* preferences');
|
||||||
$this->assertEmpty($dbMock->deletions, 'DbStore::save deletes *new* preferences');
|
$this->assertEmpty($dbMock->deletions, 'PreferencesStore::save deletes *new* preferences');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWhetherPreferenceInsertionThrowsNotWritableError()
|
public function testWhetherPreferenceInsertionThrowsNotWritableError()
|
||||||
@ -108,9 +108,9 @@ class DbStoreTest extends BaseTestCase
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertArrayHasKey('key', $dbMock->updates, 'DbStore::save does not update existing preferences');
|
$this->assertArrayHasKey('key', $dbMock->updates, 'PreferencesStore::save does not update existing preferences');
|
||||||
$this->assertEmpty($dbMock->insertions, 'DbStore::save inserts *existing* preferences');
|
$this->assertEmpty($dbMock->insertions, 'PreferencesStore::save inserts *existing* preferences');
|
||||||
$this->assertEmpty($dbMock->deletions, 'DbStore::save inserts *existing* preferneces');
|
$this->assertEmpty($dbMock->deletions, 'PreferencesStore::save inserts *existing* preferneces');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWhetherPreferenceUpdatesThrowNotWritableError()
|
public function testWhetherPreferenceUpdatesThrowNotWritableError()
|
||||||
@ -139,9 +139,9 @@ class DbStoreTest extends BaseTestCase
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContains('key', $dbMock->deletions, 'DbStore::save does not delete removed preferences');
|
$this->assertContains('key', $dbMock->deletions, 'PreferencesStore::save does not delete removed preferences');
|
||||||
$this->assertEmpty($dbMock->insertions, 'DbStore::save inserts *removed* preferences');
|
$this->assertEmpty($dbMock->insertions, 'PreferencesStore::save inserts *removed* preferences');
|
||||||
$this->assertEmpty($dbMock->updates, 'DbStore::save updates *removed* preferences');
|
$this->assertEmpty($dbMock->updates, 'PreferencesStore::save updates *removed* preferences');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWhetherPreferenceDeletionThrowsNotWritableError()
|
public function testWhetherPreferenceDeletionThrowsNotWritableError()
|
||||||
@ -160,7 +160,7 @@ class DbStoreTest extends BaseTestCase
|
|||||||
|
|
||||||
protected function getStore($dbMock)
|
protected function getStore($dbMock)
|
||||||
{
|
{
|
||||||
return new DbStoreWithSetPreferences(
|
return new PreferencesStoreWithSetPreferences(
|
||||||
new ConfigObject(
|
new ConfigObject(
|
||||||
array(
|
array(
|
||||||
'connection' => Mockery::mock(array('getDbAdapter' => $dbMock))
|
'connection' => Mockery::mock(array('getDbAdapter' => $dbMock))
|
Loading…
x
Reference in New Issue
Block a user