From 8daf28a8fe9c920d1dfb9cb8e465c0d2b3f63afc Mon Sep 17 00:00:00 2001 From: Christoph Schulthess Date: Thu, 19 Jan 2017 15:39:51 +0100 Subject: preliminary debugrequest version --- apis/debugrequest.php | 14 +- modules-available/debugconfig/api.inc.php | 115 +++++++ modules-available/debugconfig/config.json | 4 + .../debugconfig/inc/baseconfigutil.inc.php | 83 +++++ .../debugconfig/inc/validator.inc.php | 106 +++++++ modules-available/debugconfig/install.inc.php | 58 ++++ .../debugconfig/lang/de/messages.json | 5 + modules-available/debugconfig/lang/de/module.json | 3 + .../debugconfig/lang/de/template-tags.json | 7 + .../debugconfig/lang/en/messages.json | 5 + modules-available/debugconfig/lang/en/module.json | 3 + .../debugconfig/lang/en/template-tags.json | 7 + .../debugconfig/lang/pt/messages.json | 3 + modules-available/debugconfig/lang/pt/module.json | 3 + .../debugconfig/lang/pt/template-tags.json | 3 + modules-available/debugconfig/page.inc.php | 342 +++++++++++++++++++++ modules-available/debugconfig/templates/_page.html | 122 ++++++++ 17 files changed, 876 insertions(+), 7 deletions(-) create mode 100644 modules-available/debugconfig/api.inc.php create mode 100644 modules-available/debugconfig/config.json create mode 100644 modules-available/debugconfig/inc/baseconfigutil.inc.php create mode 100644 modules-available/debugconfig/inc/validator.inc.php create mode 100644 modules-available/debugconfig/install.inc.php create mode 100644 modules-available/debugconfig/lang/de/messages.json create mode 100644 modules-available/debugconfig/lang/de/module.json create mode 100644 modules-available/debugconfig/lang/de/template-tags.json create mode 100644 modules-available/debugconfig/lang/en/messages.json create mode 100644 modules-available/debugconfig/lang/en/module.json create mode 100644 modules-available/debugconfig/lang/en/template-tags.json create mode 100644 modules-available/debugconfig/lang/pt/messages.json create mode 100644 modules-available/debugconfig/lang/pt/module.json create mode 100644 modules-available/debugconfig/lang/pt/template-tags.json create mode 100644 modules-available/debugconfig/page.inc.php create mode 100644 modules-available/debugconfig/templates/_page.html diff --git a/apis/debugrequest.php b/apis/debugrequest.php index 8e0fd78b..4449e6eb 100644 --- a/apis/debugrequest.php +++ b/apis/debugrequest.php @@ -1,19 +1,19 @@ $debug_request["uuid"], ":ip" => \'$_SERVER["REMOTE_ADDR"]\')); -$validclient = true; +$debug_request = split(":", file_get_contents("php://input"), 2); +$uuid = $debug_request[0]; +$port = 5900 + $debug_request[1]; +$validclient = Database::queryFirst("SELECT machineuuid, clientip FROM machine WHERE machineuuid = :uuid AND clientip = :ip", array(":uuid" => "$debug_request[0]", ":ip" => $_SERVER["REMOTE_ADDR"])); +//$validclient = Database::simpleQuery("SELECT machineuuid, clientip FROM machine LIMIT 1;"); if ($validclient == false) { http_response_code(400); } else { http_response_code(200); - //$data = json_encode(array("ip" => $_SERVER["REMOTE_ADDR"], "port" => 5900 + $debug_request["display"])); - //Taskmanager::submit('relay', $data, true); + $data = json_encode(array("ip" => $_SERVER["REMOTE_ADDR"], "port" => 5900 + $debug_request["display"])); + Taskmanager::submit('relay', $data, true); } -//echo file_get_contents("php://input"); 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 @@ +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 /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 @@ + 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 @@ +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 "
";
+			//var_dump($settings[$var['catid']]['settings'][$key]);
+			//echo "
"; + $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 .= ""; + } else { + $inner .= ""; + } + } + + $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 .= ""; + } else { + $inner .= ""; + } + } + $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 . ""; + } + + 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 @@ +

{{lang_basicConfiguration}}

+{{#override}} +

{{subheading}}

+
{{lang_editOverrideNotice}}
+{{/override}} +

{{lang_clientRelatedConfig}}

+
+ + {{#override}} + + + {{/override}} + + + {{#categories}} +
+ +
+
+ {{#settings}} +
+
+
+
{{setting}}
+ {{^override}} +
+ {{defaultvalue}} +
+ + {{/override}} + {{#override}} + + {{/override}} +
+
+ {{{item}}} +
+
+ +
+
+
+ + {{/settings}} +
+
+
+ {{/categories}} + + + {{^override}} + Download + {{/override}} + {{#override}} + Download + {{/override}} +
+ + -- cgit v1.2.3-55-g7522