summaryrefslogtreecommitdiffstats
path: root/library/Icinga/Application/Config.php
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/Icinga/Application/Config.php497
1 files changed, 497 insertions, 0 deletions
diff --git a/library/Icinga/Application/Config.php b/library/Icinga/Application/Config.php
new file mode 100644
index 0000000..006c40f
--- /dev/null
+++ b/library/Icinga/Application/Config.php
@@ -0,0 +1,497 @@
+<?php
+/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Application;
+
+use Iterator;
+use Countable;
+use LogicException;
+use UnexpectedValueException;
+use Icinga\Util\File;
+use Icinga\Data\ConfigObject;
+use Icinga\Data\Selectable;
+use Icinga\Data\SimpleQuery;
+use Icinga\File\Ini\IniWriter;
+use Icinga\File\Ini\IniParser;
+use Icinga\Exception\IcingaException;
+use Icinga\Exception\NotReadableError;
+use Icinga\Web\Navigation\Navigation;
+
+/**
+ * Container for INI like configuration and global registry of application and module related configuration.
+ */
+class Config implements Countable, Iterator, Selectable
+{
+ /**
+ * Configuration directory where ALL (application and module) configuration is located
+ *
+ * @var string
+ */
+ public static $configDir;
+
+ /**
+ * Application config instances per file
+ *
+ * @var array
+ */
+ protected static $app = array();
+
+ /**
+ * Module config instances per file
+ *
+ * @var array
+ */
+ protected static $modules = array();
+
+ /**
+ * Navigation config instances per type
+ *
+ * @var array
+ */
+ protected static $navigation = array();
+
+ /**
+ * The internal ConfigObject
+ *
+ * @var ConfigObject
+ */
+ protected $config;
+
+ /**
+ * The INI file this config has been loaded from or should be written to
+ *
+ * @var string
+ */
+ protected $configFile;
+
+ /**
+ * Create a new config
+ *
+ * @param ConfigObject $config The config object to handle
+ */
+ public function __construct(ConfigObject $config = null)
+ {
+ $this->config = $config !== null ? $config : new ConfigObject();
+ }
+
+ /**
+ * Return this config's file path
+ *
+ * @return string
+ */
+ public function getConfigFile()
+ {
+ return $this->configFile;
+ }
+
+ /**
+ * Set this config's file path
+ *
+ * @param string $filepath The path to the ini file
+ *
+ * @return $this
+ */
+ public function setConfigFile($filepath)
+ {
+ $this->configFile = $filepath;
+ return $this;
+ }
+
+ /**
+ * Return the internal ConfigObject
+ *
+ * @return ConfigObject
+ */
+ public function getConfigObject()
+ {
+ return $this->config;
+ }
+
+ /**
+ * Provide a query for the internal config object
+ *
+ * @return SimpleQuery
+ */
+ public function select()
+ {
+ return $this->config->select();
+ }
+
+ /**
+ * Return the count of available sections
+ *
+ * @return int
+ */
+ public function count(): int
+ {
+ return $this->select()->count();
+ }
+
+ /**
+ * Reset the current position of the internal config object
+ *
+ * @return void
+ */
+ public function rewind(): void
+ {
+ $this->config->rewind();
+ }
+
+ /**
+ * Return the section of the current iteration
+ *
+ * @return ConfigObject
+ */
+ public function current(): ConfigObject
+ {
+ return $this->config->current();
+ }
+
+ /**
+ * Return whether the position of the current iteration is valid
+ *
+ * @return bool
+ */
+ public function valid(): bool
+ {
+ return $this->config->valid();
+ }
+
+ /**
+ * Return the section's name of the current iteration
+ *
+ * @return string
+ */
+ public function key(): string
+ {
+ return $this->config->key();
+ }
+
+ /**
+ * Advance the position of the current iteration and return the new section
+ *
+ * @return void
+ */
+ public function next(): void
+ {
+ $this->config->next();
+ }
+
+ /**
+ * Return whether this config has any sections
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return $this->config->isEmpty();
+ }
+
+ /**
+ * Return this config's section names
+ *
+ * @return array
+ */
+ public function keys()
+ {
+ return $this->config->keys();
+ }
+
+ /**
+ * Return this config's data as associative array
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->config->toArray();
+ }
+
+ /**
+ * Return the value from a section's property
+ *
+ * @param string $section The section where the given property can be found
+ * @param string $key The section's property to fetch the value from
+ * @param mixed $default The value to return in case the section or the property is missing
+ *
+ * @return mixed
+ *
+ * @throws UnexpectedValueException In case the given section does not hold any configuration
+ */
+ public function get($section, $key, $default = null)
+ {
+ $value = $this->config->$section;
+ if ($value instanceof ConfigObject) {
+ $value = $value->$key;
+ } elseif ($value !== null) {
+ throw new UnexpectedValueException(
+ sprintf('Value "%s" is not of type "%s" or a sub-type of it', $value, get_class($this->config))
+ );
+ }
+
+ if ($value === null && $default !== null) {
+ $value = $default;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Return the given section
+ *
+ * @param string $name The section's name
+ *
+ * @return ConfigObject
+ */
+ public function getSection($name)
+ {
+ $section = $this->config->get($name);
+ return $section !== null ? $section : new ConfigObject();
+ }
+
+ /**
+ * Set or replace a section
+ *
+ * @param string $name
+ * @param array|ConfigObject $config
+ *
+ * @return $this
+ */
+ public function setSection($name, $config = null)
+ {
+ if ($config === null) {
+ $config = new ConfigObject();
+ } elseif (! $config instanceof ConfigObject) {
+ $config = new ConfigObject($config);
+ }
+
+ $this->config->$name = $config;
+ return $this;
+ }
+
+ /**
+ * Remove a section
+ *
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function removeSection($name)
+ {
+ unset($this->config->$name);
+ return $this;
+ }
+
+ /**
+ * Return whether the given section exists
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ public function hasSection($name)
+ {
+ return isset($this->config->$name);
+ }
+
+ /**
+ * Initialize a new config using the given array
+ *
+ * The returned config has no file associated to it.
+ *
+ * @param array $array The array to initialize the config with
+ *
+ * @return Config
+ */
+ public static function fromArray(array $array)
+ {
+ return new static(new ConfigObject($array));
+ }
+
+ /**
+ * Load configuration from the given INI file
+ *
+ * @param string $file The file to parse
+ *
+ * @throws NotReadableError When the file cannot be read
+ */
+ public static function fromIni($file)
+ {
+ $emptyConfig = new static();
+
+ $filepath = realpath($file);
+ if ($filepath === false) {
+ $emptyConfig->setConfigFile($file);
+ } elseif (is_readable($filepath)) {
+ return IniParser::parseIniFile($filepath);
+ } elseif (@file_exists($filepath)) {
+ throw new NotReadableError(t('Cannot read config file "%s". Permission denied'), $filepath);
+ }
+
+ return $emptyConfig;
+ }
+
+ /**
+ * Save configuration to the given INI file
+ *
+ * @param string|null $filePath The path to the INI file or null in case this config's path should be used
+ * @param int $fileMode The file mode to store the file with
+ *
+ * @throws LogicException In case this config has no path and none is passed in either
+ * @throws NotWritableError In case the INI file cannot be written
+ *
+ * @todo create basepath and throw NotWritableError in case its not possible
+ */
+ public function saveIni($filePath = null, $fileMode = 0660)
+ {
+ if ($filePath === null && $this->configFile) {
+ $filePath = $this->configFile;
+ } elseif ($filePath === null) {
+ throw new LogicException('You need to pass $filePath or set a path using Config::setConfigFile()');
+ }
+
+ if (! file_exists($filePath)) {
+ File::create($filePath, $fileMode);
+ }
+
+ $this->getIniWriter($filePath, $fileMode)->write();
+ }
+
+ /**
+ * Return a IniWriter for this config
+ *
+ * @param string|null $filePath
+ * @param int $fileMode
+ *
+ * @return IniWriter
+ */
+ protected function getIniWriter($filePath = null, $fileMode = null)
+ {
+ return new IniWriter($this, $filePath, $fileMode);
+ }
+
+ /**
+ * Prepend configuration base dir to the given relative path
+ *
+ * @param string $path A relative path
+ *
+ * @return string
+ */
+ public static function resolvePath($path)
+ {
+ return self::$configDir . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR);
+ }
+
+ /**
+ * Retrieve a application config
+ *
+ * @param string $configname The configuration name (without ini suffix) to read and return
+ * @param bool $fromDisk When set true, the configuration will be read from disk, even
+ * if it already has been read
+ *
+ * @return Config The requested configuration
+ */
+ public static function app($configname = 'config', $fromDisk = false)
+ {
+ if (! isset(self::$app[$configname]) || $fromDisk) {
+ self::$app[$configname] = static::fromIni(static::resolvePath($configname . '.ini'));
+ }
+
+ return self::$app[$configname];
+ }
+
+ /**
+ * Retrieve a module config
+ *
+ * @param string $modulename The name of the module where to look for the requested configuration
+ * @param string $configname The configuration name (without ini suffix) to read and return
+ * @param string $fromDisk When set true, the configuration will be read from disk, even
+ * if it already has been read
+ *
+ * @return Config The requested configuration
+ */
+ public static function module($modulename, $configname = 'config', $fromDisk = false)
+ {
+ if (! isset(self::$modules[$modulename])) {
+ self::$modules[$modulename] = array();
+ }
+
+ if (! isset(self::$modules[$modulename][$configname]) || $fromDisk) {
+ self::$modules[$modulename][$configname] = static::fromIni(
+ static::resolvePath('modules/' . $modulename . '/' . $configname . '.ini')
+ );
+ }
+ return self::$modules[$modulename][$configname];
+ }
+
+ /**
+ * Retrieve a navigation config
+ *
+ * @param string $type The type identifier of the navigation item for which to return its config
+ * @param string $username A user's name or null if the shared config is desired
+ * @param bool $fromDisk If true, the configuration will be read from disk
+ *
+ * @return Config The requested configuration
+ */
+ public static function navigation($type, $username = null, $fromDisk = false)
+ {
+ if (! isset(self::$navigation[$type])) {
+ self::$navigation[$type] = array();
+ }
+
+ $branch = $username ?: 'shared';
+ $typeConfigs = self::$navigation[$type];
+ if (! isset($typeConfigs[$branch]) || $fromDisk) {
+ $typeConfigs[$branch] = static::fromIni(static::getNavigationConfigPath($type, $username));
+ }
+
+ return $typeConfigs[$branch];
+ }
+
+ /**
+ * Return the path to the configuration file for the given navigation item type and user
+ *
+ * @param string $type
+ * @param string $username
+ *
+ * @return string
+ *
+ * @throws IcingaException In case the given type is unknown
+ */
+ protected static function getNavigationConfigPath($type, $username = null)
+ {
+ $itemTypeConfig = Navigation::getItemTypeConfiguration();
+ if (! isset($itemTypeConfig[$type])) {
+ throw new IcingaException('Invalid navigation item type %s provided', $type);
+ }
+
+ if (isset($itemTypeConfig[$type]['config'])) {
+ $filename = $itemTypeConfig[$type]['config'] . '.ini';
+ } else {
+ $filename = $type . 's.ini';
+ }
+
+ if ($username) {
+ $path = static::resolvePath(implode(DIRECTORY_SEPARATOR, array('preferences', $username, $filename)));
+ if (realpath($path) === false) {
+ $path = static::resolvePath(implode(
+ DIRECTORY_SEPARATOR,
+ array('preferences', strtolower($username), $filename)
+ ));
+ }
+ } else {
+ $path = static::resolvePath('navigation' . DIRECTORY_SEPARATOR . $filename);
+ }
+ return $path;
+ }
+
+ /**
+ * Return this config rendered as a INI structured string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->getIniWriter()->render();
+ }
+}