diff options
Diffstat (limited to 'library/Icinga/User')
-rw-r--r-- | library/Icinga/User/Preferences.php | 169 | ||||
-rw-r--r-- | library/Icinga/User/Preferences/PreferencesStore.php | 344 |
2 files changed, 513 insertions, 0 deletions
diff --git a/library/Icinga/User/Preferences.php b/library/Icinga/User/Preferences.php new file mode 100644 index 0000000..b09462b --- /dev/null +++ b/library/Icinga/User/Preferences.php @@ -0,0 +1,169 @@ +<?php +/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ + +namespace Icinga\User; + +use Countable; + +/** + * User preferences container + * + * Usage example: + * <code> + * <?php + * + * use Icinga\User\Preferences; + * + * $preferences = new Preferences(); // Start with empty preferences + * + * $preferences = new Preferences(array('aPreference' => 'value')); // Start with initial preferences + * + * $preferences->aNewPreference = 'value'; // Set a preference + * + * unset($preferences->aPreference); // Unset a preference + * + * // Retrieve a preference and return a default value if the preference does not exist + * $anotherPreference = $preferences->get('anotherPreference', 'defaultValue'); + */ +class Preferences implements Countable +{ + /** + * Preferences key-value array + * + * @var array + */ + protected $preferences = array(); + + /** + * Constructor + * + * @param array $preferences Preferences key-value array + */ + public function __construct(array $preferences = array()) + { + $this->preferences = $preferences; + } + + /** + * Count all preferences + * + * @return int The number of preferences + */ + public function count(): int + { + return count($this->preferences); + } + + /** + * Determine whether a preference exists + * + * @param string $name + * + * @return bool + */ + public function has($name) + { + return array_key_exists($name, $this->preferences); + } + + /** + * Write data to a preference + * + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->preferences[$name] = $value; + } + + /** + * Retrieve a preference section + * + * @param string $name + * + * @return array|null + */ + public function get($name) + { + if (array_key_exists($name, $this->preferences)) { + return $this->preferences[$name]; + } + + return null; + } + + /** + * Retrieve a value from a specific section + * + * @param string $section + * @param string $name + * @param null $default + * + * @return array|null + */ + public function getValue($section, $name, $default = null) + { + if (array_key_exists($section, $this->preferences) + && array_key_exists($name, $this->preferences[$section]) + ) { + return $this->preferences[$section][$name]; + } + + return $default; + } + + /** + * Magic method so that $obj->value will work. + * + * @param string $name + * + * @return mixed + */ + public function __get($name) + { + return $this->get($name); + } + + /** + * Remove a given preference + * + * @param string $name Preference name + */ + public function remove($name) + { + unset($this->preferences[$name]); + } + + /** + * Determine if a preference is set and is not NULL + * + * @param string $name Preference name + * + * @return bool + */ + public function __isset($name) + { + return isset($this->preferences[$name]); + } + + /** + * Unset a given preference + * + * @param string $name Preference name + */ + public function __unset($name) + { + $this->remove($name); + } + + /** + * Get preferences as array + * + * @return array + */ + public function toArray() + { + return $this->preferences; + } +} 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); + } +} |