summaryrefslogtreecommitdiffstats
path: root/library/Icinga/User/Preferences/PreferencesStore.php
diff options
context:
space:
mode:
Diffstat (limited to 'library/Icinga/User/Preferences/PreferencesStore.php')
-rw-r--r--library/Icinga/User/Preferences/PreferencesStore.php344
1 files changed, 344 insertions, 0 deletions
diff --git a/library/Icinga/User/Preferences/PreferencesStore.php b/library/Icinga/User/Preferences/PreferencesStore.php
new file mode 100644
index 0000000..8ecc677
--- /dev/null
+++ b/library/Icinga/User/Preferences/PreferencesStore.php
@@ -0,0 +1,344 @@
+<?php
+/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\User\Preferences;
+
+use Exception;
+use Icinga\Exception\NotReadableError;
+use Icinga\Exception\NotWritableError;
+use Icinga\User;
+use Icinga\User\Preferences;
+use Icinga\Data\ConfigObject;
+use Icinga\Data\ResourceFactory;
+use Icinga\Exception\ConfigurationError;
+use Zend_Db_Expr;
+
+/**
+ * Preferences store factory
+ *
+ * Load and save user preferences by using a database
+ *
+ * Usage example:
+ * <code>
+ * <?php
+ *
+ * use Icinga\Data\ConfigObject;
+ * use Icinga\User\Preferences;
+ * use Icinga\User\Preferences\PreferencesStore;
+ *
+ * // Create a db store
+ * $store = PreferencesStore::create(
+ * new ConfigObject(
+ * 'resource' => 'resource name'
+ * ),
+ * $user // Instance of \Icinga\User
+ * );
+ *
+ * $preferences = new Preferences($store->load());
+ * $preferences->aPreference = 'value';
+ * $store->save($preferences);
+ * </code>
+ */
+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
+ *
+ * @var ConfigObject
+ */
+ protected $config;
+
+ /**
+ * Given user
+ *
+ * @var User
+ */
+ protected $user;
+
+ /**
+ * Create a new store
+ *
+ * @param ConfigObject $config The config for this adapter
+ * @param User $user The user to which these preferences belong
+ */
+ public function __construct(ConfigObject $config, User $user)
+ {
+ $this->config = $config;
+ $this->user = $user;
+ $this->init();
+ }
+
+ /**
+ * Getter for the store config
+ *
+ * @return ConfigObject
+ */
+ public function getStoreConfig(): ConfigObject
+ {
+ return $this->config;
+ }
+
+ /**
+ * Getter for the user
+ *
+ * @return User
+ */
+ public function getUser(): User
+ {
+ return $this->user;
+ }
+
+ /**
+ * Initialize the store
+ */
+ protected function init(): void
+ {
+ }
+
+ /**
+ * Load preferences from the database
+ *
+ * @return array
+ *
+ * @throws NotReadableError In case the database operation failed
+ */
+ 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 in the database
+ *
+ * @param Preferences $preferences The preferences to save
+ */
+ 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
+ *
+ * @param ConfigObject $config The config for the adapter
+ * @param User $user The user to which these preferences belong
+ *
+ * @return self
+ *
+ * @throws ConfigurationError When the configuration defines an invalid storage type
+ */
+ public static function create(ConfigObject $config, User $user): self
+ {
+ $resourceConfig = ResourceFactory::getResourceConfig($config->resource);
+ if ($resourceConfig->db === 'mysql') {
+ $resourceConfig->charset = 'utf8mb4';
+ }
+
+ $config->connection = ResourceFactory::createResource($resourceConfig);
+
+ return new self($config, $user);
+ }
+}