From 03f82d95103aee9bdca0417a07f338c232ddceba Mon Sep 17 00:00:00 2001 From: jokob-sk Date: Mon, 20 May 2024 08:14:36 +1000 Subject: [PATCH] =?UTF-8?q?Loading=20plugins=20v=200.5=20=F0=9F=94=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/php/server/util.php | 2 +- front/plugins/README.md | 3 +- front/settings.php | 50 +++++++++++++++-------- server/helper.py | 6 +-- server/initialise.py | 86 +++++++++++++++++++++++++++------------ server/plugin.py | 2 +- 6 files changed, 101 insertions(+), 48 deletions(-) diff --git a/front/php/server/util.php b/front/php/server/util.php index 5d2c840c..ee9fbaa4 100755 --- a/front/php/server/util.php +++ b/front/php/server/util.php @@ -342,7 +342,7 @@ function saveSettings() } $txt .= $settingKey . "=" . $val . "\n"; - } elseif ($settingType == 'text.multiselect' || $settingType == 'subnets' || $settingType == 'list') { + } elseif ($settingType == 'text.multiselect' || $settingType == 'subnets' || $settingType == 'list' || $settingType == 'list.select') { $temp = ''; if(is_array($settingValue) == FALSE) diff --git a/front/plugins/README.md b/front/plugins/README.md index 9dc14e7f..887c7281 100755 --- a/front/plugins/README.md +++ b/front/plugins/README.md @@ -559,6 +559,7 @@ Required attributes are: | | - `text.select` | | | - `text.multiselect` | | | - `list` | +| | - `list.select` | | | - `integer.checkbox` | | | - `text.template` | | `"localized"` | A list of properties on the current JSON level that need to be localized. | @@ -647,7 +648,7 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de | See below for information on `threshold`, `replace`. | | | | | | `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. | -| `options_params` Property | Used in conjunction with a `"options": "[{value}]"` template and `text.select`. Can specify SQL query (needs to return 2 columns `SELECT dev_Name as name, dev_Mac as id`) or Setting (not tested) to populate the dropdown. Check example below or have a look at the `NEWDEV` plugin `config.json` file. | +| `options_params` Property | Used in conjunction with a `"options": "[{value}]"` template and `text.select`/`list.select`. Can specify SQL query (needs to return 2 columns `SELECT dev_Name as name, dev_Mac as id`) or Setting (not tested) to populate the dropdown. Check example below or have a look at the `NEWDEV` plugin `config.json` file. | | `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is used for the value background color if it's less than the specified `maximum` but more than the previous one in the `options` array. | | `replace` | The `options` array contains objects with an `equals` property, which is compared to the "value." If the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value". | | `regex` | Applies a regex to the value. The `options` array contains objects with an `type` (must be set to `regex`) and `param` (must contain the regex itself) property. | diff --git a/front/settings.php b/front/settings.php index 689d4bd8..680f1dc3 100755 --- a/front/settings.php +++ b/front/settings.php @@ -416,7 +416,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { let inputHtml = ""; if (setType.startsWith('text') || setType.startsWith('string') || setType.startsWith('date-time') ) { - + // --- process text --- if(setType.includes(".select")) { inputHtml = generateInputOptions(pluginsData, set, inputHtml, isMultiSelect = false) @@ -435,12 +435,16 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { inputHtml = ``; } } else if (setType === 'integer') { + // --- process integer --- inputHtml = ``; } else if (setType.startsWith('password')) { + // --- process password --- inputHtml = ``; } else if (setType === 'readonly') { + // --- process readonly --- inputHtml = ``; } else if (setType === 'boolean' || setType === 'integer.checkbox') { + // --- process boolean --- let checked = val === 'True' || val === '1' ? 'checked' : ''; // if it's overridable set readonly accordingly @@ -455,6 +459,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { inputHtml = generateInputOptions(pluginsData, set, inputHtml) } else if (setType === 'subnets') { + // --- process subnets --- inputHtml = `
@@ -471,14 +476,12 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
@@ -489,27 +492,39 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { ${getString("Gen_Remove_All")}
`; - } else if (setType === 'list' || setType === 'list.readonly') { + } else if (setType === 'list' || setType === 'list.select' || setType === 'list.readonly') { + // --- process list --- settingKeyOfLists.push(codeName); - inputHtml = ` + inputHtml += `
-
+
` + + if(setType.includes(".select")) // not tested + { + inputHtml += generateInputOptions(pluginsData, set, inputHtml, isMultiSelect = false) + } + else + { + inputHtml += ` -
+ `; + } + + inputHtml += `
- `; - let options = createArray(val); + let saved_values = createArray(val); - options.forEach(option => { + saved_values.forEach(saved_val => { - inputHtml += ``; + inputHtml += ``; }); inputHtml += '
' + @@ -522,6 +537,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
`; } else if (setType === 'json') { + // --- process json --- inputHtml = ``; } @@ -578,7 +594,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { { multi = isMultiSelect ? "multiple" : ""; - optionsArray = getSettingOptions(set['Code_Name'] ) + // optionsArray = getSettingOptions(set['Code_Name'] ) valuesArray = createArray(set['Value']); // create unique ID @@ -650,7 +666,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { const toId = $(element).attr('my-input-to'); input = $(`#${fromId}`).val(); - $(`#${toId}`).append($("").attr("value", input).text(input)); + $(`#${toId}`).append($("").attr("value", input).text(input)); // clear input $(`#${fromId}`).val(""); @@ -697,7 +713,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { var settingsArray = []; // collect values for each of the different input form controls - const noConversion = ['text', 'integer', 'string', 'password', 'readonly', 'text.select', 'integer.select', 'text.multiselect']; + const noConversion = ['text', 'integer', 'string', 'password', 'readonly', 'text.select', 'list.select', 'integer.select', 'text.multiselect']; // get settings to determine setting type to store values appropriately $.get('api/table_settings.json', function(res) { diff --git a/server/helper.py b/server/helper.py index a45a4b61..2cee7055 100755 --- a/server/helper.py +++ b/server/helper.py @@ -300,8 +300,8 @@ def get_setting_value(key): set_type = 'Error: Not handled' set_value = 'Error: Not handled' - set_value = setting["Value"] # Setting value - set_type = setting["Type"] # Setting type + set_value = setting["Value"] # Setting value (Value (upper case) = user overriden default_value) + set_type = setting["Type"] # Setting type # lower case "type" - default json value vs uppper-case "Type" (= from user defined settings) value = setting_value_to_python_type(set_type, set_value) @@ -329,7 +329,7 @@ def setting_value_to_python_type(set_type, set_value): elif set_type in ['integer.select', 'integer']: value = int(set_value) - elif set_type in ['text.multiselect', 'list', 'subnets']: + elif set_type in ['text.multiselect', 'list', 'subnets', 'list.select']: # Handle string mylog('debug', [f'[SETTINGS] Handling set_type: "{set_type}", set_value: "{set_value}"']) diff --git a/server/initialise.py b/server/initialise.py index a68ef54b..501c966d 100755 --- a/server/initialise.py +++ b/server/initialise.py @@ -26,33 +26,64 @@ from plugin_utils import get_plugins_configs, get_plugin_setting_obj #------------------------------------------------------------------------------- # Import user values # Check config dictionary -def ccd(key, default, config_dir, name, inputtype, options, group, events=[], desc = "", regex = "", setJsonMetadata = {}, overrideTemplate = {}): - - # use default inintialization value - result = default +#------------------------------------------------------------------------------- +def ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None): if events is None: events = [] + if setJsonMetadata is None: + setJsonMetadata = {} + if overrideTemplate is None: + overrideTemplate = {} - # use existing value if already supplied, otherwise default value is used + # Use default initialization value + result = default + + # Use existing value if already supplied, otherwise default value is used if key in config_dir: - result = config_dir[key] + result = config_dir[key] + # Single quotes might break SQL queries, replacing them if inputtype == 'text': result = result.replace('\'', "{s-quote}") - conf.mySettingsSQLsafe.append((key, name, desc, inputtype, options, regex, str(result), group, str(events))) - conf.mySettings.append((key, name, desc, inputtype, options, regex, result, group, str(events))) + # Create the tuples + sql_safe_tuple = (key, name, desc, inputtype, options, regex, str(result), group, str(events)) + settings_tuple = (key, name, desc, inputtype, options, regex, result, group, str(events)) - # save metadata in dummy setting + # Update or append the tuples in the lists + conf.mySettingsSQLsafe = update_or_append(conf.mySettingsSQLsafe, sql_safe_tuple, key) + conf.mySettings = update_or_append(conf.mySettings, settings_tuple, key) + + # Save metadata in dummy setting if not a metadata key if '__metadata' not in key: - tuple = (f'{key}__metadata', "metadata name", "metadata desc", 'json', "", "", json.dumps(setJsonMetadata), group, '[]') - conf.mySettingsSQLsafe.append(tuple) - conf.mySettings.append(tuple) - - + metadata_tuple = (f'{key}__metadata', "metadata name", "metadata desc", 'json', "", "", json.dumps(setJsonMetadata), group, '[]') + conf.mySettingsSQLsafe = update_or_append(conf.mySettingsSQLsafe, metadata_tuple, f'{key}__metadata') + conf.mySettings = update_or_append(conf.mySettings, metadata_tuple, f'{key}__metadata') return result + +#------------------------------------------------------------------------------- +# Function to find and update the existing key in the list +def update_or_append(settings_list, item_tuple, key): + if settings_list is None: + settings_list = [] + + # mylog('debug', ['[Import Config] update_or_append debug ']) + # mylog('debug', ['[Import Config] update_or_append ', settings_list]) + # mylog('debug', ['[Import Config] update_or_append item_tuple ' , item_tuple]) + + for index, item in enumerate(settings_list): + if item[0] == key: + settings_list[index] = item_tuple + mylog('debug', ['[Import Config] FOUND key : ', key]) + return settings_list + + settings_list.append(item_tuple) + return settings_list + + + #------------------------------------------------------------------------------- def importConfigs (db, all_plugins): @@ -100,7 +131,7 @@ def importConfigs (db, all_plugins): # ---------------------------------------- # ccd(key, default, config_dir, name, inputtype, options, group, events=[], desc = "", regex = "", setJsonMetadata = {}, overrideTemplate = {}) - conf.LOADED_PLUGINS = ccd('LOADED_PLUGINS', [] , c_d, 'Loaded plugins', 'list', '', 'General') + conf.LOADED_PLUGINS = ccd('LOADED_PLUGINS', [] , c_d, 'Loaded plugins', 'text.multiselect', '', 'General') conf.SCAN_SUBNETS = ccd('SCAN_SUBNETS', ['192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0'] , c_d, 'Subnets to scan', 'subnets', '', 'General') conf.LOG_LEVEL = ccd('LOG_LEVEL', 'verbose' , c_d, 'Log verboseness', 'text.select', "['none', 'minimal', 'verbose', 'debug']", 'General') conf.TIMEZONE = ccd('TIMEZONE', 'Europe/Berlin' , c_d, 'Time zone', 'text', '', 'General') @@ -150,6 +181,8 @@ def importConfigs (db, all_plugins): mylog('none', ['[Config] Plugins: Number of all plugins (including not loaded): ', len(all_plugins)]) plugin_indexes_to_remove = [] + all_plugins_prefixes = [] # to init the LOADED_PLUGINS setting with correct options + loaded_plugins_prefixes = [] # to init the LOADED_PLUGINS setting with correct initially seelcted values # handle plugins index = 0 @@ -163,6 +196,7 @@ def importConfigs (db, all_plugins): pref = plugin["unique_prefix"] print_plugin_info(plugin, ['display_name','description']) + all_plugins_prefixes.append(pref) # The below lines are used to determine if the plugin should be loaded, or skipped based on user settings (conf.LOADED_PLUGINS) # ...or based on if is already enabled, or if the default configuration loads the plugin (RUN function != disabled ) @@ -172,22 +206,15 @@ def importConfigs (db, all_plugins): setting_obj = get_plugin_setting_obj(plugin, "RUN") if setting_obj is not None: - set_type = setting_obj.get('type') + set_type = setting_obj.get('type') # lower case "type" - default json value vs uppper-case "Type" (= from user defined settings) set_value = setting_obj.get('default_value') plugin_run = setting_value_to_python_type(set_type, set_value) - if plugin_run is None: - mylog('none', ['[Config] plugin_run (default_value): None']) - else: - mylog('none', ['[Config] plugin_run (default_value): ', plugin_run]) - # get user-defined run value if available if pref + "_RUN" in c_d: plugin_run = c_d[pref + "_RUN" ] - mylog('none', ['[Config] plugin_run (user defined): ', plugin_run]) - # only include loaded plugins, and the ones that are enabled if pref in conf.LOADED_PLUGINS or plugin_run != 'disabled' or setting_obj is None or plugin_run is None: @@ -245,8 +272,6 @@ def importConfigs (db, all_plugins): plugin_indexes_to_remove.append(index_to_remove) - - # remove plugin at index_to_remove from list # Sort the list of indexes in descending order to avoid index shifting issues plugin_indexes_to_remove.sort(reverse=True) @@ -255,6 +280,17 @@ def importConfigs (db, all_plugins): mylog('none', [f'[Config] ⛔ Unloading plugin {pref} because not in the LOADED_PLUGINS setting or disabled by default']) all_plugins.pop(indx) + # all_plugins has now only initialized plugins, get all prefixes + for plugin in all_plugins: + pref = plugin["unique_prefix"] + loaded_plugins_prefixes.append(pref) + + + # save the newly discovered plugins as options and default values + conf.LOADED_PLUGINS = ccd('LOADED_PLUGINS', loaded_plugins_prefixes , c_d, 'Loaded plugins', 'text.multiselect', str(all_plugins_prefixes), 'General') + + mylog('none', ['[Config] Number of Plugins to load: ', len(loaded_plugins_prefixes)]) + mylog('none', ['[Config] Plugins to load: ', loaded_plugins_prefixes]) conf.plugins_once_run = False # ----------------- diff --git a/server/plugin.py b/server/plugin.py index eee868a3..0f3d2d9d 100755 --- a/server/plugin.py +++ b/server/plugin.py @@ -32,7 +32,7 @@ class plugin_param: setTyp = inputValue["Type"] # setting type noConversion = ['text', 'string', 'integer', 'boolean', 'password', 'password.SHA256', 'readonly', 'integer.select', 'text.select', 'integer.checkbox' ] - arrayConversion = ['text.multiselect', 'list', 'subnets'] + arrayConversion = ['text.multiselect', 'list', 'list.select', 'subnets'] jsonConversion = ['.template'] mylog('debug', f'[Plugins] setTyp: {setTyp}')