diff options
Diffstat (limited to 'modules-available/baseconfig/inc')
-rw-r--r-- | modules-available/baseconfig/inc/baseconfig.inc.php | 151 | ||||
-rw-r--r-- | modules-available/baseconfig/inc/baseconfigutil.inc.php | 14 | ||||
-rw-r--r-- | modules-available/baseconfig/inc/configholder.inc.php | 134 | ||||
-rw-r--r-- | modules-available/baseconfig/inc/validator.inc.php | 20 |
4 files changed, 302 insertions, 17 deletions
diff --git a/modules-available/baseconfig/inc/baseconfig.inc.php b/modules-available/baseconfig/inc/baseconfig.inc.php new file mode 100644 index 00000000..36622dce --- /dev/null +++ b/modules-available/baseconfig/inc/baseconfig.inc.php @@ -0,0 +1,151 @@ +<?php + +class BaseConfig +{ + + private static $modulesDone = []; + + /** + * @var array key-value-pairs of override vars, can be accessed by hooks + */ + private static $overrides = []; + + /** + * Fill the ConfigHolder with values from various hooks, while taking + * into account UUID and IP-address of the client making the current + * HTTP request. + */ + public static function prepareFromRequest() + { + $ip = $_SERVER['REMOTE_ADDR'] ?? null; + if ($ip === null) + ErrorHandler::traceError('No REMOTE_ADDR given in $_SERVER'); + if (substr($ip, 0, 7) === '::ffff:') { + $ip = substr($ip, 7); + } + $uuid = Request::any('uuid', null, 'string'); + if ($uuid !== null && strlen($uuid) !== 36) { + $uuid = null; + } + // Handle any hooks by other modules first + // other modules should generally only populate $configVars + foreach (glob('modules/*/baseconfig/getconfig.inc.php') as $file) { + preg_match('#^modules/([^/]+)/#', $file, $out); + ConfigHolder::setContext($out[1]); + self::handleModule($out[1], $ip, $uuid, false); + } + self::commonBase(); + } + + /** + * Fill the ConfigHolder with data from various hooks that supply + * static overrides for config variables. The overrides can be used + * to make the hooks behave in certain ways, by setting specific values like + * 'locationid' + * @param array $overrides key value pairs of overrides + */ + public static function prepareWithOverrides(array $overrides): void + { + self::$overrides = $overrides; + $ip = $uuid = null; + if (self::hasOverride('ip')) { + $ip = self::getOverride('ip'); + } + if (self::hasOverride('uuid')) { + $uuid = self::getOverride('uuid'); + } + // Handle only static hooks that don't dynamically generate output + foreach (glob('modules/*/baseconfig/hook.json') as $file) { + preg_match('#^modules/([^/]+)/#', $file, $out); + ConfigHolder::setContext($out[1]); + self::handleModule($out[1], $ip, $uuid, true); + } + self::commonBase(); + } + + /** + * Just fill the ConfigHolder with the defaults from all the json files + * that define config variables. + */ + public static function prepareDefaults() + { + $defaults = BaseConfigUtil::getVariables(); + self::addDefaults($defaults); + } + + private static function commonBase() + { + $defaults = BaseConfigUtil::getVariables(); + + // Dump global config from DB + ConfigHolder::setContext('<global>', function($id) { + return [ + 'name' => Dictionary::translate('source-global'), + 'locationid' => 0, + ]; + }); + $res = Database::simpleQuery('SELECT setting, value, displayvalue FROM setting_global'); + foreach ($res as $row) { + if (!isset($defaults[$row['setting']])) + continue; // Setting is not defined in any <module>/baseconfig/settings.json + ConfigHolder::add($row['setting'], $row, -1); + } + + // Fallback to default values from json files + self::addDefaults($defaults); + + } + + private static function addDefaults($defaults) + { + ConfigHolder::setContext('<default>', function($id) { + return [ + 'name' => Dictionary::translate('source-default'), + 'locationid' => 0, + ]; + }); + foreach ($defaults as $setting => $value) { + ConfigHolder::add($setting, $value['defaultvalue'], -1000); + } + } + + private static function handleModule(string $name, ?string $ip, ?string $uuid, bool $needJsonHook): void + { + // Pass ip and uuid instead of global to make them read only + if (isset(self::$modulesDone[$name])) + return; + self::$modulesDone[$name] = true; + // Module has getconfig hook + $file = 'modules/' . $name . '/baseconfig/getconfig.inc.php'; + if (!is_file($file)) + return; + // We want only static hooks that have a json config (currently used for displaying inheritance tree) + if ($needJsonHook && !is_file('modules/' . $name . '/baseconfig/hook.json')) + return; + // Properly registered and can be activated + $mod = Module::get($name); + if ($mod === false) + return; + if (!$mod->activate(1, false)) + return; + // Process dependencies first + foreach ($mod->getDependencies() as $dep) { + self::handleModule($dep, $ip, $uuid, $needJsonHook); + } + ConfigHolder::setContext($name); + (function (string $file, ?string $ip, ?string $uuid) { + include_once($file); + })($file, $ip, $uuid); + } + + public static function hasOverride($key): bool + { + return array_key_exists($key, self::$overrides); + } + + public static function getOverride($key) + { + return self::$overrides[$key]; + } + +}
\ No newline at end of file diff --git a/modules-available/baseconfig/inc/baseconfigutil.inc.php b/modules-available/baseconfig/inc/baseconfigutil.inc.php index a48eb93b..4d1ef8c1 100644 --- a/modules-available/baseconfig/inc/baseconfigutil.inc.php +++ b/modules-available/baseconfig/inc/baseconfigutil.inc.php @@ -16,7 +16,7 @@ class BaseConfigUtil * @param \Module $module optional, only consider given module, not all enabled modules * @return array all known config variables */ - public static function getVariables($module = false) + public static function getVariables($module = false): array { $settings = array(); if ($module === false) { @@ -39,16 +39,13 @@ class BaseConfigUtil /** * Get configuration categories for given module, or all modules if false is passed. - * - * @param \Module $module - * @return array */ - public static function getCategories($module = false) + public static function getCategories(Module $module = null): array { $categories = array(); - if ($module === false) { + if ($module === null) { $module = '*'; - } elseif (is_object($module)) { + } else { $module = $module->getIdentifier(); } foreach (glob("modules/{$module}/baseconfig/categories.json", GLOB_NOSORT) as $file) { @@ -70,7 +67,8 @@ class BaseConfigUtil * @param array $vars list of vars as obtained from BaseConfigUtil::getVariables() * @param array $values key-value-pairs of variable assignments to work with */ - public static function markShadowedVars(&$vars, $values) { + public static function markShadowedVars(array &$vars, array $values): void + { foreach ($vars as $key => &$var) { if (!isset($var['shadows'])) continue; diff --git a/modules-available/baseconfig/inc/configholder.inc.php b/modules-available/baseconfig/inc/configholder.inc.php new file mode 100644 index 00000000..75b43460 --- /dev/null +++ b/modules-available/baseconfig/inc/configholder.inc.php @@ -0,0 +1,134 @@ +<?php + +class ConfigHolder +{ + private static $config = []; + + private static $context = ''; + + private static $postHooks = []; + + public static function setContext($id, $resolver = false) + { + $tmp = ['id' => $id, 'resolver' => $resolver]; + self::$context =& $tmp; + } + + /** + * @param string $key config variable name + * @param false|string|array $value false to unset, string value, or array with keys value and displayvalue + * @param int $prio priority of this value, in case the same key gets set multiple times + */ + public static function add(string $key, $value, int $prio = 0): void + { + if (!isset(self::$config[$key])) { + self::$config[$key] = []; + } + $new = [ + 'prio' => $prio, + 'context' => &self::$context, + ]; + if (is_array($value)) { + $new['value'] = $value['value']; + $new['displayvalue'] = $value['displayvalue']; + } else { + $new['value'] = $value; + } + if (empty(self::$config[$key]) || self::$config[$key][0]['prio'] > $prio) { + // Existing is higher, append new one + array_push(self::$config[$key], $new); + } else { + // New one has highest prio or matches existing, put in front + array_unshift(self::$config[$key], $new); + } + } + + public static function get(string $key): ?string + { + if (!isset(self::$config[$key])) + return null; + return self::$config[$key][0]['value']; + } + + public static function addPostHook(callable $func): void + { + self::$postHooks[] = array('context' => &self::$context, 'function' => $func); + } + + public static function applyPostHooks(): void + { + foreach (self::$postHooks as $hook) { + $newContext = $hook['context']; + $newContext['post'] = true; + self::$context =& $newContext; + $hook['function'](); + } + self::$postHooks = []; + } + + public static function getRecursiveConfig(bool $prettyPrint = true): array + { + $ret = []; + foreach (self::$config as $key => $list) { + $last = false; + foreach ($list as $entry) { + if ($last !== false && $last['context']['id'] === '<global>' + && $entry['context']['id'] === '<default>' && $last['value'] === $entry['value']) + continue; + $cb =& $entry['context']['resolver']; + $valueKey = 'value'; + if ($prettyPrint && is_callable($cb)) { + $data = $cb($entry['context']['id']); + $name = $data['name']; + if (isset($data['locationid']) && isset($entry['displayvalue']) + && User::hasPermission('.baseconfig.view', $data['locationid'])) { + $valueKey = 'displayvalue'; + } + } else { + $name = $entry['context']['id']; + } + $ret[$key][] = ['name' => $name, 'value' => $entry[$valueKey]]; + $last = $entry; + } + } + return $ret; + } + + public static function outputConfig(): void + { + foreach (self::$config as $key => $list) { + echo str_pad('# ' . $key . ' ', 35, '#', STR_PAD_BOTH), "\n"; + foreach ($list as $pos => $item) { + $ctx = $item['context']['id']; + if (isset($item['context']['post'])) { + $ctx .= '[post-hook]'; + } + $ctx .= ' :: ' . $item['prio']; + if ($pos != 0 || $item['value'] === false) { + echo '# (', $ctx, ')'; + if ($pos == 0) { + echo " <disabled this setting>\n"; + } else { + echo " <overridden>\n"; + } + } else { + echo $key, "='", self::escape($item['value']), "'\n"; + echo '# (', $ctx, ") <active>\n"; + } + } + } + } + + /** + * Escape given string so it is a valid string in sh that can be surrounded + * by single quotes ('). This basically turns _'_ into _'"'"'_ + * + * @param string $string input + * @return string escaped sh string + */ + private static function escape(string $string): string + { + return str_replace(["'", "\n", "\r"], ["'\"'\"'", ' ', ' '], $string); + } + +} diff --git a/modules-available/baseconfig/inc/validator.inc.php b/modules-available/baseconfig/inc/validator.inc.php index 2dfeed7c..be71c3df 100644 --- a/modules-available/baseconfig/inc/validator.inc.php +++ b/modules-available/baseconfig/inc/validator.inc.php @@ -32,11 +32,12 @@ class Validator case 'multilist': return self::validateMultiList($data[1], $displayValue); case 'multiinput': - return self::validateMultiInput($data[1], $displayValue); + return self::validateMultiInput($data[1] ?? [], $displayValue); + case 'suggestions': + return true; default: - Util::traceError('Unknown validation method: ' . $data[0]); + ErrorHandler::traceError('Unknown validation method: ' . $data[0]); } - return false; // make code inspector happy - doesn't know traceError doesn't return } @@ -44,10 +45,10 @@ class Validator * Validate linux password. If already in $6$ hash form, * the unchanged value will be returned. * if empty, an empty string will also be returned. - * Otherwise it it assumed that the value is a plain text + * Otherwise, it is assumed that the value is a plain text * password that is supposed to be hashed. */ - private static function linuxPassword(&$displayValue) + private static function linuxPassword($displayValue) { if (empty($displayValue)) return ''; @@ -62,7 +63,7 @@ class Validator * @param string $displayValue network path * @return string cleaned up path */ - private static function networkShare(&$displayValue) + private static function networkShare(string &$displayValue): string { $displayValue = trim($displayValue); if (substr($displayValue, 0, 2) === '\\\\') @@ -75,18 +76,19 @@ class Validator /** * Validate value against list. + * * @param string $list The list as a string of items, separated by "|" * @param string $displayValue The value to validate * @return boolean|string The value, if in list, false otherwise */ - private static function validateList($list, &$displayValue) + private static function validateList(string $list, string $displayValue) { $list = explode('|', $list); if (in_array($displayValue, $list)) return $displayValue; return false; } - private static function validateMultiList($list, &$displayValue) + private static function validateMultiList(string $list, array &$displayValue): string { $allowedValues = explode('|', $list); $values = []; @@ -99,7 +101,7 @@ class Validator return $displayValue; } - private static function validateMultiInput($list, &$displayValue) + private static function validateMultiInput($list, $displayValue) { return $displayValue; } |