diff --git a/.gitignore b/.gitignore index f7a1155aa..20c443dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ doc/api # Enabled modules config/enabledModules/ + +# User preferences +config/preferences/*.ini diff --git a/config/config.ini b/config/config.ini index cdfa4dba3..4bdd24277 100755 --- a/config/config.ini +++ b/config/config.ini @@ -17,3 +17,6 @@ target = /tmp/icinga2.log debug.enable = 1 debug.type = stream debug.target = /tmp/icinga2.debug.log + +[preferences] +type=ini diff --git a/config/preferences/KEEP.md b/config/preferences/KEEP.md new file mode 100644 index 000000000..b4ffa9070 --- /dev/null +++ b/config/preferences/KEEP.md @@ -0,0 +1 @@ +# Keep this directory diff --git a/library/Icinga/Application/ApplicationBootstrap.php b/library/Icinga/Application/ApplicationBootstrap.php index 18e6025bb..7ebaa55bc 100755 --- a/library/Icinga/Application/ApplicationBootstrap.php +++ b/library/Icinga/Application/ApplicationBootstrap.php @@ -140,7 +140,7 @@ abstract class ApplicationBootstrap Benchmark::measure('Bootstrap, autoloader registered'); Icinga::setApp($this); - $this->configDir = $configDir; + $this->configDir = realpath($configDir); require_once dirname(__FILE__) . '/functions.php'; } @@ -212,10 +212,32 @@ abstract class ApplicationBootstrap */ public function getApplicationDir($subdir = null) { - $dir = $this->appDir; + return $this->getDirWithSubDir($this->appDir, $subdir); + } + + /** + * Getter for config dir + * @param string|null $subdir + * @return string + */ + public function getConfigDir($subdir = null) + { + return $this->getDirWithSubDir($this->configDir, $subdir); + } + + /** + * Helper to glue directories together + * + * @param string $dir + * @param string|null $subdir + * @return string + */ + private function getDirWithSubDir($dir, $subdir=null) + { if ($subdir !== null) { $dir .= '/' . ltrim($subdir, '/'); } + return $dir; } diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index cd7a31aa6..8d376c8f3 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -30,6 +30,7 @@ namespace Icinga\Application; use Icinga\Authentication\Manager as AuthenticationManager; use Icinga\User\Preferences; +use Icinga\User; use Icinga\Web\Request; use Zend_Controller_Front; use Zend_Layout; @@ -39,6 +40,8 @@ use Zend_Controller_Action_HelperBroker; use Zend_Controller_Router_Route; use Zend_Controller_Action_Helper_ViewRenderer; use Icinga\Web\View; +use Icinga\User\Preferences\StorageFactory; +use Icinga\User\Preferences\SessionStore; /** * Use this if you want to make use of Icinga funtionality in other web projects @@ -198,15 +201,8 @@ class Web extends ApplicationBootstrap return $this; } - /** - * Inject dependencies into request - * - * @return self - */ - private function setupRequest() + private function setupUser() { - $this->request = new Request(); - $authenticationManager = AuthenticationManager::getInstance( null, array( @@ -217,9 +213,44 @@ class Web extends ApplicationBootstrap if ($authenticationManager->isAuthenticated() === true) { $user = $authenticationManager->getUser(); - $preferences = new Preferences(array()); + $this->getConfig()->preferences->configPath = $this->getConfigDir('preferences'); + + $preferenceStore = StorageFactory::create( + $this->getConfig()->preferences, + $user + ); + + $sessionStore = new SessionStore($authenticationManager->getSession()); + + $initialPreferences = (count($sessionStore->load())) + ? $sessionStore->load() : $preferenceStore->load(); + + $preferences = new Preferences($initialPreferences); + + $preferences->attach($sessionStore); + $preferences->attach($preferenceStore); + $user->setPreferences($preferences); + $requestCounter = $user->getPreferences()->get('test.request.counter', 0); + $requestCounter++; + $user->getPreferences()->set('test.request.counter', $requestCounter); + + return $user; + } + } + + /** + * Inject dependencies into request + * + * @return self + */ + private function setupRequest() + { + $this->request = new Request(); + + $user = $this->setupUser(); + if ($user instanceof User) { $this->request->setUser($user); } diff --git a/library/Icinga/Authentication/Manager.php b/library/Icinga/Authentication/Manager.php index ef84c0e24..3edc40a2f 100644 --- a/library/Icinga/Authentication/Manager.php +++ b/library/Icinga/Authentication/Manager.php @@ -31,6 +31,7 @@ namespace Icinga\Authentication; use Icinga\Application\Logger; use Icinga\Application\Config as IcingaConfig; use Icinga\Exception\ConfigurationError as ConfigError; +use Icinga\User; /** * The authentication manager allows to identify users and diff --git a/library/Icinga/User/Preferences/IniStore.php b/library/Icinga/User/Preferences/IniStore.php index f455eb738..823c8b07e 100644 --- a/library/Icinga/User/Preferences/IniStore.php +++ b/library/Icinga/User/Preferences/IniStore.php @@ -44,17 +44,12 @@ use \Zend_Config_Writer_Ini; */ class IniStore implements LoadInterface, FlushObserverInterface { - /** - * Name of preferences section in ini file - */ - const DEFAULT_SECTION = 'preferences'; - /** * Path to ini configuration * * @var string */ - private $configDir; + private $configPath; /** * Specific user file for preferences @@ -87,26 +82,28 @@ class IniStore implements LoadInterface, FlushObserverInterface /** * Create a new object * - * @param string $configDir + * @param string|null $configPath */ - public function __construct($configDir) + public function __construct($configPath=null) { - $this->setConfigDir($configDir); + if ($configPath !== null) { + $this->setConfigPath($configPath); + } } /** * Setter for config directory * - * @param string $configDir + * @param string $configPath * @throws \Icinga\Exception\ConfigurationError */ - public function setConfigDir($configDir) + public function setConfigPath($configPath) { - if (!is_dir($configDir)) { - throw new ConfigurationError('Config dir dos not exist: '. $configDir); + if (!is_dir($configPath)) { + throw new ConfigurationError('Config dir dos not exist: '. $configPath); } - $this->configDir = $configDir; + $this->configPath = $configPath; } /** @@ -120,7 +117,7 @@ class IniStore implements LoadInterface, FlushObserverInterface $this->preferencesFile = sprintf( '%s/%s.ini', - $this->configDir, + $this->configPath, $this->user->getUsername() ); @@ -146,17 +143,7 @@ class IniStore implements LoadInterface, FlushObserverInterface */ private function createDefaultIniFile() { - $writer = new Zend_Config_Writer_Ini( - array( - 'config' => new Zend_Config( - array( - self::DEFAULT_SECTION => array() - ) - ), - 'filename' => $this->preferencesFile - ) - ); - $writer->write(); + touch($this->preferencesFile); } /** diff --git a/library/Icinga/User/Preferences/SessionStore.php b/library/Icinga/User/Preferences/SessionStore.php new file mode 100644 index 000000000..0ac1424f5 --- /dev/null +++ b/library/Icinga/User/Preferences/SessionStore.php @@ -0,0 +1,107 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\User\Preferences; + +use Icinga\Authentication\Session; +use \SplObserver; +use \SplSubject; +use Icinga\User\Preferences; +use Icinga\Exception\ProgrammingError; + +/** + * Modify preferences into session + */ +class SessionStore implements SplObserver, LoadInterface +{ + /** + * Name of session var for preferences + */ + const DEFAULT_SESSION_NAMESPACE = 'preferences'; + + /** + * Session data + * + * @var Session + */ + private $session; + + /** + * Create a new object + * + * @param Session $session + */ + public function __construct(Session $session) + { + $this->session = $session; + } + + /** + * Receive update from subject + * + * @link http://php.net/manual/en/splobserver.update.php + * @param SplSubject $subject + * @throws ProgrammingError + */ + public function update(SplSubject $subject) + { + if (!$subject instanceof Preferences) { + throw new ProgrammingError('Not compatible with '. get_class($subject)); + } + + $changeSet = $subject->getChangeSet(); + + $data = $this->session->get(self::DEFAULT_SESSION_NAMESPACE, array()); + + foreach ($changeSet->getCreate() as $key => $value) { + $data[$key] = $value; + } + + foreach ($changeSet->getUpdate() as $key => $value) { + $data[$key] = $value; + } + + foreach ($changeSet->getDelete() as $key) { + unset($data[$key]); + } + + $this->session->set(self::DEFAULT_SESSION_NAMESPACE, $data); + + $this->session->write(); + } + + /** + * Load preferences from source + * + * @return array + */ + public function load() + { + return $this->session->get(self::DEFAULT_SESSION_NAMESPACE, array()); + } +} \ No newline at end of file diff --git a/library/Icinga/User/Preferences/StorageFactory.php b/library/Icinga/User/Preferences/StorageFactory.php new file mode 100644 index 000000000..84e2b853f --- /dev/null +++ b/library/Icinga/User/Preferences/StorageFactory.php @@ -0,0 +1,83 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\User\Preferences; + +use Icinga\User; +use Icinga\Exception\ProgrammingError; +use \Zend_Config; + +final class StorageFactory +{ + /** + * Prefix for classes containing namespace + */ + const CLASS_PREFIX = 'Icinga\\User\\Preferences\\'; + + /** + * Suffix for class + */ + const CLASS_SUFFIX = 'Store'; + + /** + * Create storage adapter from zend configuration + * + * @param Zend_Config $config + * @param User $user + * @return FlushObserverInterface + * @throws ProgrammingError + */ + public static function create(Zend_Config $config, User $user) + { + $class = self::CLASS_PREFIX. ucfirst($config->get('type')). self::CLASS_SUFFIX; + + if (class_exists($class)) { + $store = new $class(); + + if (!$store instanceof FlushObserverInterface) { + throw new ProgrammingError('Not instance of FlushObserverInterface: '. $class); + } + + $items = $config->toArray(); + unset($items['type']); + + foreach ($items as $key => $value) { + $setter = 'set'. ucfirst($key); + if (is_callable(array($store, $setter))) { + $store->$setter($value); + } + } + + $store->setUser($user); + + return $store; + } + + throw new ProgrammingError('Could not instantiate class: '. $class); + } +}