Loading plugins v 0.5 🔌

This commit is contained in:
jokob-sk
2024-05-20 08:14:36 +10:00
parent 924ac72401
commit 03f82d9510
6 changed files with 101 additions and 48 deletions

View File

@@ -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)

View File

@@ -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. |

View File

@@ -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 = `<input class="form-control" onChange="settingsChanged()" my-data-type="${setType}" id="${codeName}" value="${val}" ${readonly}/>`;
}
} else if (setType === 'integer') {
// --- process integer ---
inputHtml = `<input onChange="settingsChanged()" my-data-type="${setType}" class="form-control" id="${codeName}" type="number" value="${val}"/>`;
} else if (setType.startsWith('password')) {
// --- process password ---
inputHtml = `<input onChange="settingsChanged()" my-data-type="${setType}" class="form-control input" id="${codeName}" type="password" value="${val}"/>`;
} else if (setType === 'readonly') {
// --- process readonly ---
inputHtml = `<input class="form-control input" my-data-type="${setType}" id="${codeName}" value="${val}" readonly/>`;
} 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 = `
<div class="row form-group">
<div class="col-xs-5">
@@ -471,14 +476,12 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
<select class="form-control" my-data-type="${setType}" name="${codeName}" id="${codeName}" onchange="initListInteractionOptions(${codeName})" multiple readonly>`;
options = createArray(val);
saved_values = createArray(val);
options.forEach(option => {
inputHtml += `<option value="${option}" >${option}</option>`;
saved_values.forEach(saved_val => {
inputHtml += `<option value="${saved_val}" >${saved_val}</option>`;
});
inputHtml += `</select>
</div>
<div class="col-xs-12">
@@ -489,27 +492,39 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
${getString("Gen_Remove_All")}
</button>
</div>`;
} else if (setType === 'list' || setType === 'list.readonly') {
} else if (setType === 'list' || setType === 'list.select' || setType === 'list.readonly') {
// --- process list ---
settingKeyOfLists.push(codeName);
inputHtml = `
inputHtml += `
<div class="row form-group">
<div class="col-xs-9">
<div class="col-xs-9">`
if(setType.includes(".select")) // not tested
{
inputHtml += generateInputOptions(pluginsData, set, inputHtml, isMultiSelect = false)
}
else
{
inputHtml += `
<input class="form-control" type="text" id="${codeName}_input" placeholder="Enter value"/>
</div>
`;
}
inputHtml += `</div>
<div class="col-xs-3">
<button class="btn btn-primary" my-input-from="${codeName}_input" my-input-to="${codeName}" onclick="addList(this);initListInteractionOptions('${codeName}')">${getString("Gen_Add")}</button>
</div>
</div>
<div class="form-group">
<select class="form-control" my-data-type="${setType}" name="${codeName}" id="${codeName}" multiple readonly>`;
<select class="form-control" my-data-type="${setType}" name="${codeName}" id="${codeName}" multiple readonly>`;
let options = createArray(val);
let saved_values = createArray(val);
options.forEach(option => {
saved_values.forEach(saved_val => {
inputHtml += `<option value="${option}" >${option}</option>`;
inputHtml += `<option value="${saved_val}" >${saved_val}</option>`;
});
inputHtml += '</select></div>' +
@@ -522,6 +537,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
</button>
</div>`;
} else if (setType === 'json') {
// --- process json ---
inputHtml = `<textarea class="form-control input" my-data-type="${setType}" id="${codeName}" readonly>${JSON.stringify(val, null, 2)}</textarea>`;
}
@@ -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($("<option disabled></option>").attr("value", input).text(input));
$(`#${toId}`).append($("<option ></option>").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) {

View File

@@ -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}"'])

View File

@@ -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
# -----------------

View File

@@ -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}')