diff options
Diffstat (limited to 'modules-available/debugconfig')
-rw-r--r-- | modules-available/debugconfig/api.inc.php | 115 | ||||
-rw-r--r-- | modules-available/debugconfig/config.json | 4 | ||||
-rw-r--r-- | modules-available/debugconfig/inc/baseconfigutil.inc.php | 83 | ||||
-rw-r--r-- | modules-available/debugconfig/inc/validator.inc.php | 106 | ||||
-rw-r--r-- | modules-available/debugconfig/install.inc.php | 58 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/de/messages.json | 5 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/de/module.json | 3 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/de/template-tags.json | 7 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/en/messages.json | 5 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/en/module.json | 3 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/en/template-tags.json | 7 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/pt/messages.json | 3 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/pt/module.json | 3 | ||||
-rw-r--r-- | modules-available/debugconfig/lang/pt/template-tags.json | 3 | ||||
-rw-r--r-- | modules-available/debugconfig/page.inc.php | 342 | ||||
-rw-r--r-- | modules-available/debugconfig/templates/_page.html | 122 |
16 files changed, 869 insertions, 0 deletions
diff --git a/modules-available/debugconfig/api.inc.php b/modules-available/debugconfig/api.inc.php new file mode 100644 index 00000000..af780d99 --- /dev/null +++ b/modules-available/debugconfig/api.inc.php @@ -0,0 +1,115 @@ +<?php + +$ip = $_SERVER['REMOTE_ADDR']; +if (substr($ip, 0, 7) === '::ffff:') { + $ip = substr($ip, 7); +} + +$uuid = Request::any('uuid', false, 'string'); +if ($uuid !== false && strlen($uuid) !== 36) { + $uuid = false; +} + +/** + * 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 + */ +function escape($string) +{ + return str_replace("'", "'\"'\"'", $string); +} + +/* + * We gather all config variables here. First, let other modules generate + * their desired config vars. Afterwards, add the global config vars from + * db. If a variable is already set, it will not be overridden by the + * global setting. + */ + +$configVars = array(); +function handleModule($file, $ip, $uuid) // Pass ip and uuid instead of global to make them read only +{ + global $configVars; + include_once $file; +} + +// 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); + $mod = Module::get($out[1]); + if ($mod === false) + continue; + $mod->activate(); + foreach ($mod->getDependencies() as $dep) { + $depFile = 'modules/' . $dep . '/baseconfig/getconfig.inc.php'; + if (file_exists($depFile) && Module::isAvailable($dep)) { + handleModule($depFile, $ip, $uuid); + } + } + handleModule($file, $ip, $uuid); +} + +// Rest is handled by module +$defaults = BaseConfigUtil::getVariables(); + +// Dump global config from DB +$res = Database::simpleQuery('SELECT setting, value, enabled FROM setting_global'); +while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if (isset($configVars[$row['setting']]) // Already set by a hook above, ignore + || !isset($defaults[$row['setting']])) // Setting is not defined in any <module>/baseconfig/settings.json + continue; + if ($row['enabled'] != 1) { + // Setting is disabled + $configVars[$row['setting']] = false; + } else { + $configVars[$row['setting']] = $row['value']; + } +} + +// Fallback to default values from json files +foreach ($defaults as $setting => $value) { + if (isset($configVars[$setting])) { + if ($configVars[$setting] === false) { + unset($configVars[$setting]); + } + } else { + $configVars[$setting] = $value['defaultvalue']; + } +} + +// All done, now output + +if (Request::any('save') === 'true') { + // output AND save to disk: Generate contents + $lines = ''; + foreach ($configVars as $setting => $value) { + $lines .= $setting . "='" . escape($value) . "'\n"; + } + // Save to all the locations + $data = Property::getVersionCheckInformation(); + if (is_array($data) && isset($data['systems'])) { + foreach ($data['systems'] as $system) { + $path = CONFIG_HTTP_DIR . '/' . $system['id'] . '/config'; + if (file_put_contents($path, $lines) > 0) { + echo "# Saved config to $path\n"; + } else { + echo "# Error saving config to $path\n"; + } + } + } + // Output to browser + echo $lines; +} else { + // Only output to client + foreach ($configVars as $setting => $value) { + echo $setting, "='", escape($value), "'\n"; + } +} + +// For quick testing or custom extensions: Include external file that should do nothing +// more than outputting more key-value-pairs. It's expected in the webroot of slxadmin +if (file_exists('client_config_additional.php')) @include('client_config_additional.php'); diff --git a/modules-available/debugconfig/config.json b/modules-available/debugconfig/config.json new file mode 100644 index 00000000..e4d906e1 --- /dev/null +++ b/modules-available/debugconfig/config.json @@ -0,0 +1,4 @@ +{ + "category": "main.settings-client", + "dependencies" : ["js_selectize", "bootstrap_multiselect"] +} diff --git a/modules-available/debugconfig/inc/baseconfigutil.inc.php b/modules-available/debugconfig/inc/baseconfigutil.inc.php new file mode 100644 index 00000000..3039ea12 --- /dev/null +++ b/modules-available/debugconfig/inc/baseconfigutil.inc.php @@ -0,0 +1,83 @@ +<?php + +class BaseConfigUtil +{ + + /** + * Return all config variables to be handled directly by the baseconfig edit module. + * The array will contain a list of mapping of type: + * VARNAME => array( + * catid => xx, + * defaultvalue => xx, + * permissions => xx, + * validator => xx, + * ) + * + * @param \Module $module optional, only consider given module, not all enabled modules + * @return array all known config variables + */ + public static function getVariables($module = false) + { + $settings = array(); + if ($module === false) { + $module = '*'; + } else { + $module = $module->getIdentifier(); + } + foreach (glob("modules/{$module}/baseconfig/settings.json", GLOB_NOSORT) as $file) { + $data = json_decode(file_get_contents($file), true); + if (!is_array($data)) + continue; + preg_match('#^modules/([^/]+)/#', $file, $out); + foreach ($data as &$entry) { + $entry['module'] = $out[1]; + } + $settings += $data; + } + return $settings; + } + + public static function getCategories($module = false) + { + $categories = array(); + if ($module === false) { + $module = '*'; + } else { + $module = $module->getIdentifier(); + } + foreach (glob("modules/{$module}/baseconfig/categories.json", GLOB_NOSORT) as $file) { + $data = json_decode(file_get_contents($file), true); + if (!is_array($data)) + continue; + preg_match('#^modules/([^/]+)/#', $file, $out); + foreach ($data as &$entry) { + $entry = array('module' => $out[1], 'sortpos' => $entry); + } + $categories += $data; + } + return $categories; + } + + /** + * Mark variables that would be shadowed according to the given values. + * + * @param $vars list of vars as obtained from BaseConfigUtil::getVariables() + * @param $values key-value-pairs of variable assignments to work with + */ + public static function markShadowedVars(&$vars, $values) { + foreach ($vars as $key => &$var) { + if (!isset($var['shadows'])) + continue; + foreach ($var['shadows'] as $triggerVal => $destSettings) { + if (isset($values[$key]) && $values[$key] !== $triggerVal) + continue; + foreach ($destSettings as $destSetting) { + if (isset($vars[$destSetting])) { + $vars[$destSetting]['shadowed'] = true; + } + } + } + } + } + +} diff --git a/modules-available/debugconfig/inc/validator.inc.php b/modules-available/debugconfig/inc/validator.inc.php new file mode 100644 index 00000000..ec7b95aa --- /dev/null +++ b/modules-available/debugconfig/inc/validator.inc.php @@ -0,0 +1,106 @@ +<?php + +/** + * This class contains all the helper functions that + * can be referenced by a config setting. Every function + * here is supposed to validate the given config value + * and either return the validated and possibly sanitized + * value, or false to indicate that the given value is invalid. + * The passed value is a reference, as it can also be modified + * by the validator to tweak the value that is being + * displayed in the web interface, compared to the returned + * value, which will only be used by the client directly, + * and is not displayed by the web interface. + */ +class Validator +{ + + public static function validate($condition, &$displayValue) + { + if (empty($condition)) + return $displayValue; + $data = explode(':', $condition, 2); + switch ($data[0]) { + case 'regex': + if (preg_match($data[1], $displayValue)) + return $displayValue; + return false; + case 'list': + return self::validateList($data[1], $displayValue); + case 'function': + return self::$data[1]($displayValue); + case 'multilist': + return self::validateMultiList($data[1], $displayValue); + case 'multiinput': + return self::validateMultiInput($data[1], $displayValue); + default: + Util::traceError('Unknown validation method: ' . $data[0]); + } + return false; // make code inspector happy - doesn't know traceError doesn't return + } + + + /** + * 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 + * password that is supposed to be hashed. + */ + private static function linuxPassword(&$displayValue) + { + if (empty($displayValue)) + return ''; + if (preg_match('/^\$[156]\$.+\$./', $displayValue)) + return $displayValue; + return Crypto::hash6($displayValue); + } + + /** + * "Fix" network share path for SMB shares where a backslash + * is used instead of a slash. + * @param string $displayValue network path + * @return string cleaned up path + */ + private static function networkShare(&$displayValue) + { + $displayValue = trim($displayValue); + if (substr($displayValue, 0, 2) === '\\\\') + $displayValue = str_replace('\\', '/', $displayValue); + $returnValue = $displayValue; + if (substr($returnValue, 0, 2) === '//') + $returnValue = str_replace(' ', '\\040', $returnValue); + return $returnValue; + } + + /** + * 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) + { + $list = explode('|', $list); + if (in_array($displayValue, $list)) + return $displayValue; + return false; + } + private static function validateMultiList($list, &$displayValue) + { + $allowedValues = explode('|', $list); + $values = []; + foreach ($displayValue as $v) { + if (in_array($v, $allowedValues)) { + $values[] = $v; + } + } + $displayValue = implode(' ', $values); + return $displayValue; + } + + private static function validateMultiInput(&$list, &$displayValue) + { + return $displayValue; + } +} diff --git a/modules-available/debugconfig/install.inc.php b/modules-available/debugconfig/install.inc.php new file mode 100644 index 00000000..b4eada5d --- /dev/null +++ b/modules-available/debugconfig/install.inc.php @@ -0,0 +1,58 @@ +<?php + +$res = array(); + +$res[] = tableCreate('setting_global', " + `setting` varchar(28) NOT NULL, + `value` text NOT NULL, + `displayvalue` text NOT NULL, + `enabled` tinyint(1) UNSIGNED NOT NULL DEFAULT '1', + PRIMARY KEY (`setting`) +"); + +// Update path + +// Add toggle field + +if (!tableHasColumn('setting_global', 'enabled')) { + if (tableHasColumn('setting_global', 'toggle')) { + $ret = Database::exec("ALTER TABLE `setting_global` CHANGE `toggle` `enabled` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1'"); + } else { + $ret = Database::exec("ALTER TABLE `setting_global` ADD COLUMN `enabled` tinyint(1) UNSIGNED NOT NULL DEFAULT '1'"); + } + if ($ret === false) { + finalResponse(UPDATE_FAILED, 'Adding enabled to setting_global failed: ' . Database::lastError()); + } +} + +// Add displayvalue field + +if (!tableHasColumn('setting_global', 'displayvalue')) { + Database::exec("ALTER TABLE `setting_global` ADD `displayvalue` TEXT NOT NULL"); + Database::exec("UPDATE `setting_global` SET `displayvalue` = `value`"); + $res[] = UPDATE_DONE; +} + +// Delete old tables + +/* +Keep disabled for a while, in case some customer made unexpected important changes etc... + +if (tableExists('setting')) { + Database::exec('DROP TABLE setting'); +} +if (tableExists('setting_distro')) { + Database::exec('DROP TABLE setting_distro'); +} +if (tableExists('cat_setting')) { + Database::exec('DROP TABLE cat_setting'); +} +*/ + +// Create response for browser + +if (in_array(UPDATE_DONE, $res)) { + finalResponse(UPDATE_DONE, 'Tables created successfully'); +} + +finalResponse(UPDATE_NOOP, 'Everything already up to date'); diff --git a/modules-available/debugconfig/lang/de/messages.json b/modules-available/debugconfig/lang/de/messages.json new file mode 100644 index 00000000..a0b8cdd4 --- /dev/null +++ b/modules-available/debugconfig/lang/de/messages.json @@ -0,0 +1,5 @@ +{ + "invalid-hook": "Ung\u00fcltiger Hook: {{0}}", + "no-module-hook": "Modul {{0}} hat keinen Hook", + "settings-updated": "Einstellungen wurden aktualisiert" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/lang/de/module.json b/modules-available/debugconfig/lang/de/module.json new file mode 100644 index 00000000..461bebdb --- /dev/null +++ b/modules-available/debugconfig/lang/de/module.json @@ -0,0 +1,3 @@ +{ + "module_name": "KonfigurationsVariablen" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/lang/de/template-tags.json b/modules-available/debugconfig/lang/de/template-tags.json new file mode 100644 index 00000000..cdd54f6a --- /dev/null +++ b/modules-available/debugconfig/lang/de/template-tags.json @@ -0,0 +1,7 @@ +{ + "lang_basicConfiguration": "Basiskonfiguration", + "lang_clientRelatedConfig": "Die Optionen auf dieser Seite beziehen sich auf das Verhalten der bwLehrpool-Clients.", + "lang_editOverrideNotice": "Sie bearbeiten die Einstellungen f\u00fcr einen Unterbereich", + "lang_enableOverride": "\u00dcberschreiben", + "lang_settingActive": "Einstellung aktiv" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/lang/en/messages.json b/modules-available/debugconfig/lang/en/messages.json new file mode 100644 index 00000000..0e2ac9fe --- /dev/null +++ b/modules-available/debugconfig/lang/en/messages.json @@ -0,0 +1,5 @@ +{ + "invalid-hook": "Module {{0}} has an invalid baseconfig hook", + "no-module-hook": "Module {{0}} doesn't have a baseconfig hook", + "settings-updated": "Settings have been updated" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/lang/en/module.json b/modules-available/debugconfig/lang/en/module.json new file mode 100644 index 00000000..48591f88 --- /dev/null +++ b/modules-available/debugconfig/lang/en/module.json @@ -0,0 +1,3 @@ +{ + "module_name": "Config variables" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/lang/en/template-tags.json b/modules-available/debugconfig/lang/en/template-tags.json new file mode 100644 index 00000000..8e75e5ac --- /dev/null +++ b/modules-available/debugconfig/lang/en/template-tags.json @@ -0,0 +1,7 @@ +{ + "lang_basicConfiguration": "Basic Configuration", + "lang_clientRelatedConfig": "The options on this page are related to the bwLehrpool client machines.", + "lang_editOverrideNotice": "You're editing the settings of a sub-section", + "lang_enableOverride": "Override", + "lang_settingActive": "Setting active" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/lang/pt/messages.json b/modules-available/debugconfig/lang/pt/messages.json new file mode 100644 index 00000000..f5cb96eb --- /dev/null +++ b/modules-available/debugconfig/lang/pt/messages.json @@ -0,0 +1,3 @@ +{ + "settings-updated": "As configura\u00e7\u00f5es foram atualizadas" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/lang/pt/module.json b/modules-available/debugconfig/lang/pt/module.json new file mode 100644 index 00000000..dca9eb8b --- /dev/null +++ b/modules-available/debugconfig/lang/pt/module.json @@ -0,0 +1,3 @@ +{ + "module_name": "Vari\u00e1veis" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/lang/pt/template-tags.json b/modules-available/debugconfig/lang/pt/template-tags.json new file mode 100644 index 00000000..14367665 --- /dev/null +++ b/modules-available/debugconfig/lang/pt/template-tags.json @@ -0,0 +1,3 @@ +{ + "lang_basicConfiguration": "Configura\u00e7\u00e3o B\u00e1sica" +}
\ No newline at end of file diff --git a/modules-available/debugconfig/page.inc.php b/modules-available/debugconfig/page.inc.php new file mode 100644 index 00000000..5e99f2a0 --- /dev/null +++ b/modules-available/debugconfig/page.inc.php @@ -0,0 +1,342 @@ +<?php + +class Page_BaseConfig extends Page +{ + private $qry_extra = array(); + private $categories; + + /** + * @var bool|string in case we're in module mode, set to the id of the module + */ + private $targetModule = false; + + protected function doPreprocess() + { + User::load(); + + // Determine if we're setting global or module specific + $this->getModuleSpecific(); + + $newValues = Request::post('setting'); + if (is_array($newValues)) { + if (!User::hasPermission('superadmin')) { + Message::addError('main.no-permission'); + Util::redirect('?do=baseconfig'); + } + // Build variables for specific sub-settings + if ($this->targetModule === false) { + // We're editing global settings - use the 'enabled' field + $qry_insert = ', enabled'; + $qry_values = ', :enabled'; + $qry_update = ', enabled = :enabled'; + $params = array(); + } elseif (empty($this->qry_extra['field'])) { + // Module specific, but module doesn't have an extra field + $qry_insert = ''; + $qry_values = ''; + $qry_update = ''; + } else { + // Module with extra field + $qry_insert = ', ' . $this->qry_extra['field']; + $qry_values = ', :field_value'; + $qry_update = ''; + $params = array('field_value' => $this->qry_extra['field_value']); + $delExtra = " AND {$this->qry_extra['field']} = :field_value "; + $delParams = array('field_value' => $this->qry_extra['field_value']); + // Not editing global settings + if ($this->getCurrentModuleName() === false) { + Message::addError('main.value-invalid', $this->qry_extra['field'], $this->qry_extra['field_value']); + Util::redirect('?do=BaseConfig'); + } + } + // Honor override/enabled checkbox + $override = Request::post('override', array()); + // Load all existing config options to validate input + $vars = BaseConfigUtil::getVariables(); + // First, handle shadowing so we don't create warnings for empty fields + BaseConfigUtil::markShadowedVars($vars, $newValues); + // Validate input + foreach ($vars as $key => $var) { + if (isset($var['shadowed'])) + continue; + if ($this->targetModule === false) { + // Global mode + $params['enabled'] = (is_array($override) && isset($override[$key]) && $override[$key] === 'on') ? 1 : 0; + } else { + // Module mode + if (is_array($override) && (!isset($override[$key]) || $override[$key] !== 'on')) { + // override not set - delete + $delParams['key'] = $key; + Database::exec("DELETE FROM {$this->qry_extra['table']} WHERE setting = :key $delExtra", $delParams); + continue; + } + } + $validator = $var['validator']; + $displayValue = (isset($newValues[$key]) ? $newValues[$key] : ''); + // Validate data first! + $mangledValue = Validator::validate($validator, $displayValue); + if ($mangledValue === false) { + Message::addWarning('main.value-invalid', $key, $displayValue); + continue; + } + // Now put into DB + Database::exec("INSERT INTO {$this->qry_extra['table']} (setting, value, displayvalue $qry_insert)" + . " VALUES (:key, :value, :displayvalue $qry_values)" + . " ON DUPLICATE KEY UPDATE value = :value, displayvalue = :displayvalue $qry_update", + array( + 'key' => $key, + 'value' => $mangledValue, + 'displayvalue' => $displayValue + ) + $params + ); + } + Message::addSuccess('settings-updated'); + if ($this->targetModule === false) { + Util::redirect('?do=BaseConfig'); + } elseif (empty($this->qry_extra['field'])) { + Util::redirect('?do=BaseConfig&module=' . $this->targetModule); + } else { + Util::redirect('?do=BaseConfig&module=' . $this->targetModule . '&' . $this->qry_extra['field'] . '=' . $this->qry_extra['field_value']); + } + } + // Load categories so we can define them as sub menu items + $this->categories = BaseConfigUtil::getCategories(); + asort($this->categories, SORT_DESC); + foreach ($this->categories as $catid => $val) { + Dashboard::addSubmenu( + '#category_' . $catid, + Dictionary::translateFileModule($this->categories[$catid]['module'], 'config-variable-categories', $catid, true) + ); + } + } + + protected function doRender() + { + if (!User::hasPermission('superadmin')) { + Message::addError('main.no-permission'); + Util::redirect('?do=Main'); + } + // Check if valid submodule mode, store name if any + if ($this->targetModule !== false) { + $this->qry_extra['subheading'] = $this->getCurrentModuleName(); + if ($this->qry_extra['subheading'] === false) { + Message::addError('main.value-invalid', $this->qry_extra['field'], $this->qry_extra['field_value']); + Util::redirect('?do=BaseConfig'); + } + } + // List config options + $settings = array(); + $vars = BaseConfigUtil::getVariables(); + // Get stuff that's set in DB already + if ($this->targetModule === false) { + $fields = ', enabled'; + $where = ''; + $params = array(); + } elseif (isset($this->qry_extra['field'])) { + $fields = ''; + $where = " WHERE {$this->qry_extra['field']} = :field_value"; + $params = array('field_value' => $this->qry_extra['field_value']); + } else { + $fields = ''; + $where = ''; + $params = array(); + } + // Populate structure with existing config from db + $res = Database::simpleQuery("SELECT setting, value, displayvalue $fields FROM {$this->qry_extra['table']} " + . " {$where} ORDER BY setting ASC", $params); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if (!isset($vars[$row['setting']]) || !is_array($vars[$row['setting']])) { + $unknown[] = $row['setting']; + continue; + } + $row += $vars[$row['setting']]; + if (!isset($row['catid'])) { + $row['catid'] = 'unknown'; + } + $settings[$row['catid']]['settings'][$row['setting']] = $row; + } + // Add entries that weren't in the db (global), setup override checkbox (module specific) + foreach ($vars as $key => $var) { + if ($this->targetModule === false) { + // Global settings - honor enabled field in db + if (!isset($settings[$var['catid']]['settings'][$key]['enabled']) || $settings[$var['catid']]['settings'][$key]['enabled'] == 1) { + $settings[$var['catid']]['settings'][$key]['checked'] = 'checked'; + } + } elseif (isset($settings[$var['catid']]['settings'][$key])) { + // Module specific - value is set in DB + $settings[$var['catid']]['settings'][$key]['checked'] = 'checked'; + } else { + // Module specific - value is not set in DB + $settings[$var['catid']]['settings'][$key] = $var + array( + 'setting' => $key + ); + } + if (!isset($settings[$var['catid']]['settings'][$key]['displayvalue'])) { + $settings[$var['catid']]['settings'][$key]['displayvalue'] = $var['defaultvalue']; + } + if (!isset($settings[$var['catid']]['settings'][$key]['shadows'])) { + $settings[$var['catid']]['settings'][$key]['shadows'] = null; + } + //echo "<pre>"; + //var_dump($settings[$var['catid']]['settings'][$key]); + //echo "</pre>"; + $settings[$var['catid']]['settings'][$key] += array( + 'item' => $this->makeInput( + $var['validator'], + $key, + $settings[$var['catid']]['settings'][$key]['displayvalue'], + $settings[$var['catid']]['settings'][$key]['shadows'] + ), + 'description' => Util::markup(Dictionary::translateFileModule($var['module'], 'config-variables', $key)), + 'setting' => $key, + ); + } + //die(); + + + // Sort categories + $sortvals = array(); + foreach ($settings as $catid => &$setting) { + $sortvals[] = isset($this->categories[$catid]) ? (int)$this->categories[$catid]['sortpos'] : 99999; + $setting['category_id'] = $catid; + $setting['category_name'] = Dictionary::translateFileModule($this->categories[$catid]['module'], 'config-variable-categories', $catid); + if ($setting['category_name'] === false) { + $setting['category_name'] = $catid; + } + ksort($setting['settings']); + $setting['settings'] = array_values($setting['settings']); + } + unset($setting); + array_multisort($sortvals, SORT_ASC, SORT_NUMERIC, $settings); + Render::addTemplate('_page', array( + 'override' => $this->targetModule !== false, + 'categories' => array_values($settings), + 'target_module' => $this->targetModule, + ) + $this->qry_extra); + Module::isAvailable('bootstrap_switch'); + } + + private function getCurrentModuleName() + { + if (isset($this->qry_extra['tostring'])) { + $method = explode('::', $this->qry_extra['tostring']); + return call_user_func($method, $this->qry_extra['field_value']); + } + if (isset($this->qry_extra['field'])) { + return $this->targetModule . ' // ' . $this->qry_extra['field'] . '=' . $this->qry_extra['field_value']; + } + return $this->targetModule; + } + + private function getModuleSpecific() + { + $module = Request::any('module', '', 'string'); + if ($module === '') { + $this->qry_extra = array( + 'table' => 'setting_global', + ); + return; + } + //\\//\\//\\ + if (!Module::isAvailable($module)) { + Message::addError('main.no-such-module', $module); + Util::redirect('?do=baseconfig'); + } + $file = 'modules/' . $module . '/baseconfig/hook.json'; + if (!file_exists($file)) { + Message::addError('no-module-hook', $module); + Util::redirect('?do=baseconfig'); + } + $hook = json_decode(file_get_contents($file), true); + if (empty($hook['table'])) { + Message::addError('invalid-hook', $module); + Util::redirect('?do=baseconfig'); + } + if (isset($hook['field'])) { + $hook['field_value'] = Request::any($hook['field'], '0', 'string'); + } + $this->targetModule = $module; + $this->qry_extra = $hook; + } + + /** + * Create html snippet for setting, based on given validator + * @param type $validator + * @return boolean + */ + private function makeInput($validator, $setting, $current, $shadows) + { + /* for the html snippet we need: */ + $args = array('class' => 'form-control', 'name' => "setting[$setting]", 'id' => $setting); + if (!empty($shadows)) { + $args['data-shadows'] = json_encode($shadows); + } + $inner = ""; + /* -- */ + + $parts = explode(':', $validator, 2); + + if ($parts[0] === 'list') { + + $items = explode('|', $parts[1]); + foreach ($items as $item) { + if ($item === $current) { + $inner .= "<option selected=\"selected\" value=\"$item\"> $item </option>"; + } else { + $inner .= "<option value=\"$item\"> $item </option>"; + } + } + + $tag = 'select'; + unset($args['type']); + $current = ''; + + } elseif ($parts[0] == 'multilist') { + + $items = explode('|', $parts[1]); + $args['multiple'] = 'multiple'; + $args['class'] .= " multilist"; + $args['name'] .= '[]'; + + $selected = explode(' ', $current); + + foreach ($items as $item) { + if (in_array($item, $selected)) { + $inner .= "<option selected=\"selected\" value=\"$item\"> $item </option>"; + } else { + $inner .= "<option value=\"$item\"> $item </option>"; + } + } + $tag = 'select'; + unset($args['type']); + $current = ''; + } else { + // Everything else is a text input for now + $tag = 'input'; + $args['value'] = $current; + $args['type'] = 'text'; + /* Password field guessing */ + if (stripos($validator, 'password') !== false) { + $args['type'] = Property::getPasswordFieldType(); + } + } + + /* multiinput: enter multiple free-form strings*/ + if ($validator === 'multiinput') { + $args['class'] .= " multiinput"; + } + + $output = "<$tag "; + foreach ($args as $key => $val) { + $output .= "$key=\"" . htmlspecialchars($val) . '" '; + } + if (empty($inner)) { + $output .= '/>'; + } else { + $output .= '>' . $inner . "</$tag>"; + } + + return $output; + } + +} diff --git a/modules-available/debugconfig/templates/_page.html b/modules-available/debugconfig/templates/_page.html new file mode 100644 index 00000000..7f380495 --- /dev/null +++ b/modules-available/debugconfig/templates/_page.html @@ -0,0 +1,122 @@ +<h1>{{lang_basicConfiguration}}</h1> +{{#override}} +<h2>{{subheading}}</h2> +<div class="alert alert-info">{{lang_editOverrideNotice}}</div> +{{/override}} +<p>{{lang_clientRelatedConfig}}</p> +<form action="?do=BaseConfig" method="post"> + <input type="hidden" name="token" value="{{token}}"> + {{#override}} + <input name="module" type="hidden" value="{{target_module}}"> + <input name="{{field}}" type="hidden" value="{{field_value}}"> + {{/override}} + <input type="text" name="prevent_autofill" id="prevent_autofill" value="" style="position:absolute;top:-2000px" tabindex="-1"> + <input type="password" name="password_fake" id="password_fake" value="" style="position:absolute;top:-2000px" tabindex="-1"> + {{#categories}} + <div class="panel panel-default"> + <div class="panel-heading" role="tab" id="heading{{category_id}}"> + <a name="category_{{category_id}}"></a> + {{category_name}} + </div> + <div class="panel-body"> + <div class="list-group"> + {{#settings}} + <div class="list-group-item"> + <div class="row"> + <div class="col-md-5 slx-cfg-toggle"> + <div>{{setting}}</div> + {{^override}} + <div class="slx-default"> + {{defaultvalue}} + </div> + <input class="bs-switch" name="override[{{setting}}]" id="CB_{{setting}}" type="checkbox" {{checked}}> <label for="CB_{{setting}}">{{lang_settingActive}}</label> + {{/override}} + {{#override}} + <input class="bs-switch" name="override[{{setting}}]" id="CB_{{setting}}" type="checkbox" {{checked}}> <label for="CB_{{setting}}">{{lang_enableOverride}}</label> + {{/override}} + </div> + <div class="col-md-5"> + {{{item}}} + </div> + <div class="col-md-2"> + <a class="btn btn-default" data-toggle="modal" data-target="#help-{{setting}}"><span class="glyphicon glyphicon-question-sign"></span></a> + </div> + </div> + </div> + <div class="modal fade" id="help-{{setting}}" tabindex="-1" role="dialog"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header">{{setting}}</div> + <div class="modal-body">{{{description}}}</div> + <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a></div> + </div> + </div> + </div> + {{/settings}} + </div> + </div> + </div> + {{/categories}} + <button class="btn btn-primary" type="submit">{{lang_save}}</button> + <button class="btn btn-default" type="reset">{{lang_reset}}</button> + {{^override}} + <a class="btn btn-default" href="api.php?do=baseconfig&user={{userid}}">Download</a> + {{/override}} + {{#override}} + <a class="btn btn-default" href="api.php?do=baseconfig&user={{userid}}&module={{target_module}}&value={{field_value}}&force=1">Download</a> + {{/override}} +</form> + +<script type="text/javascript"> + +function updateShadows(e) { + var rules = $(e).data('shadows'); + if (!rules) return; + var currentValue = $(e).val(); + for (var triggerVal in rules) { + var targets = rules[triggerVal]; + for (var i = 0; i < targets.length; ++i) { + var target = targets[i]; + var inp = $('#' + target); + var selitem = inp.data('selitem'); + + if (currentValue === triggerVal) { + inp.prop('disabled', true); + if (selitem) selitem.disable(); + $('#' + target + '.multilist').multiselect('disable'); + } else { + inp.prop('disabled', false); + if (selitem) selitem.enable(); + $('#' + target + '.multilist').multiselect('enable'); + } + } + } +} + + +document.addEventListener("DOMContentLoaded", function () { + /* apply selectize on all multiinput-selectize inputs */ + $("input.multiinput").each(function (idx, elem) { + var e = $(elem); + e.data('selitem', e.selectize({ + delimiter: ' ', + create: true, + plugins: ['restore_on_backspace', 'remove_button'], + maxItems: 10000 + + })[0].selectize); + }); + + var $multilists = $("select.multilist"); + $multilists.multiselect({ + includeSelectAllOption: true, + buttonWidth: '100%', + buttonClass: 'form-control' + }); + + /* data-shadowing bindings */ + $allShadowingFields = $('[data-shadows]'); + $allShadowingFields.on('change', function (event) { updateShadows(event.target); }); + $allShadowingFields.each(function (idx, elem) { updateShadows(elem); }); +}); +</script> |