cleanup + drpdown fixes

This commit is contained in:
Jokob-sk
2024-03-11 23:15:15 +11:00
parent 27ae11c1bc
commit fb1e73d7d2
15 changed files with 229 additions and 130 deletions

View File

@@ -810,4 +810,3 @@ function multiEditDevices()
</script>
<script src="js/pialert_common.js"></script>

View File

@@ -1,7 +1,7 @@
<?php
require 'php/templates/header.php';
?>
<script src="js/pialert_common.js"></script>
<div id="donationsPage" class="content-wrapper">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">

View File

@@ -3,14 +3,14 @@
// -----------------------------------------------------------------------------
// Read data and place intotarget location, callback processies the results
function readData(sqlQuery, processDataCallback, targetLocation) {
function readData(sqlQuery, processDataCallback, valuesArray, targetLocation) {
var apiUrl = `php/server/dbHelper.php?action=read&rawSql=${encodeURIComponent(sqlQuery)}`;
$.get(apiUrl, function(data) {
// Process the JSON data using the provided callback function
data = JSON.parse(data)
var htmlResult = processDataCallback(data);
var htmlResult = processDataCallback(data, valuesArray);
// Place the resulting HTML into the specified placeholder div
$("#" + targetLocation).replaceWith(htmlResult);

View File

@@ -26,12 +26,12 @@ var settingsJSON = {}
function getCache(key, noCookie = false)
{
// check cache
if(sessionStorage.getItem(key))
if(localStorage.getItem(key))
{
// check if not expired
if(noCookie || getCookie(key + '_session_expiry') != "")
{
return sessionStorage.getItem(key);
return localStorage.getItem(key);
}
}
@@ -41,7 +41,7 @@ function getCache(key, noCookie = false)
// -----------------------------------------------------------------------------
function setCache(key, data, expirationMinutes='')
{
sessionStorage.setItem(key, data);
localStorage.setItem(key, data);
// create cookie if expiration set to handle refresh of data
if (expirationMinutes != '')
@@ -107,55 +107,77 @@ function deleteAllCookies() {
// -----------------------------------------------------------------------------
// Get settings from the .json file generated by the python backend
// Get settings from the .json file generated by the python backend
// and cache them, if available, with options
// -----------------------------------------------------------------------------
function cacheSettings()
{
$.get('api/table_settings.json?nocache=' + Date.now(), function(resSet) {
$.get('api/plugins.json?nocache=' + Date.now(), function(resPlug) {
$.get('api/plugins.json?nocache=' + Date.now(), function(resPlug) {
pluginsData = resPlug["data"];
settingsData = resSet["data"];
settingsData = resSet["data"];
settingsData.forEach((set) => {
resolvedValue = set.Value;
resolvedOptions = createArray(set.Options)
setPlugObj = {};
options_params = [];
setPlugObj = getPluginSettingObject(pluginsData, set.Code_Name)
if(setPlugObj != {} && setPlugObj["options_params"])
// proceed only if first option item contains something to resolve
if( !set.Code_Name.includes("__metadata") &&
resolvedOptions.length != 0 &&
resolvedOptions[0].includes("{value}"))
{
options_params = setPlugObj["options_params"]
// get setting definition from the plugin config if available
setPlugObj = getPluginSettingObject(pluginsData, set.Code_Name)
// check if options contains parameters and resolve
if(setPlugObj != {} && setPlugObj["options_params"])
{
// get option_params for {value} resolution
options_params = setPlugObj["options_params"]
if(options_params != [])
{
// handles only strings of length == 1
resolvedOptions = `["${resolveParams(options_params, resolvedOptions[0])}"]`
}
}
}
// check if options contains parameters and resolve
if(set.Value.includes("{value}"))
{
resolvedValue = resolveParams(options_params, set.Value)
console.log(resolvedValue)
}
setCache(`pia_set_${set.Code_Name}`, resolvedValue)
setCache(`pia_set_${set.Code_Name}`, set.Value)
setCache(`pia_set_opt_${set.Code_Name}`, resolvedOptions)
});
});
}).then(() => handleSuccess('cacheSettings')).catch(() => handleFailure('cacheSettings')); // handle AJAX synchronization
})
}
// -----------------------------------------------------------------------------
// Get a setting value by key
function getSettingOptions (key) {
// handle initial load to make sure everything is set-up and cached
// handleFirstLoad()
result = getCache(`pia_set_opt_${key}`, true);
if (result == "")
{
console.log(`Setting options with key "${key}" not found`)
}
return result;
}
// -----------------------------------------------------------------------------
// Get a setting value by key
function getSetting (key) {
// handle initial load to make sure everything is set-up and cached
handleFirstLoad()
// handleFirstLoad()
result = getCache(`pia_set_${key}`, true);
@@ -195,7 +217,7 @@ function cacheStrings()
data.forEach((langString) => {
setCache(`pia_lang_${langString.String_Key}_${langString.Language_Code}`, langString.String_Value)
});
})
}).then(() => handleSuccess('cacheStrings')).catch(() => handleFailure('cacheStrings')); // handle AJAX synchronization
}
@@ -203,7 +225,7 @@ function cacheStrings()
function getString (key) {
// handle initial laod to make sure everything is set-up and cached
handleFirstLoad()
// handleFirstLoad()
UI_LANG = getSetting("UI_LANG");
@@ -759,30 +781,51 @@ function isRandomMAC(mac)
if (input === '[]') {
return [];
}
// Regex patterns
// Regex pattern for brackets
const patternBrackets = /(^\s*\[)|(\]\s*$)/g;
const patternQuotes = /(^\s*')|('\s*$)/g;
const replacement = '';
// Remove brackets
const noBrackets = input.replace(patternBrackets, replacement);
const options = [];
// Create array
const optionsTmp = noBrackets.split(',');
// Handle only one item in array
if (optionsTmp.length === 0) {
return [noBrackets.replace(patternQuotes, replacement)];
// Detect the type of quote used after the opening bracket
const firstChar = noBrackets.trim()[0];
const isDoubleQuoted = firstChar === '"';
const isSingleQuoted = firstChar === "'";
// Create array while handling commas within quoted segments
let currentSegment = '';
let withinQuotes = false;
for (let i = 0; i < noBrackets.length; i++) {
const char = noBrackets[i];
if ((char === '"' && !isSingleQuoted) || (char === "'" && !isDoubleQuoted)) {
withinQuotes = !withinQuotes;
}
if (char === ',' && !withinQuotes) {
options.push(currentSegment.trim());
currentSegment = '';
} else {
currentSegment += char;
}
}
// Remove quotes
optionsTmp.forEach(item => {
options.push(item.replace(patternQuotes, replacement).trim());
// Push the last segment
options.push(currentSegment.trim());
// Remove quotes based on detected type
options.forEach((item, index) => {
let trimmedItem = item.trim();
// Check if the string starts and ends with the same type of quote
if ((isDoubleQuoted && trimmedItem.startsWith('"') && trimmedItem.endsWith('"')) ||
(isSingleQuoted && trimmedItem.startsWith("'") && trimmedItem.endsWith("'"))) {
// Remove the quotes
trimmedItem = trimmedItem.substring(1, trimmedItem.length - 1);
}
options[index] = trimmedItem;
});
return options;
}
@@ -832,7 +875,7 @@ function initDeviceListAll_JSON()
}
setCache('devicesListAll_JSON', devicesListAll_JSON_str)
});
}).then(() => handleSuccess('initDeviceListAll_JSON')).catch(() => handleFailure('initDeviceListAll_JSON')); // handle AJAX synchronization
}
@@ -1009,7 +1052,7 @@ function resolveParams(params, template) {
});
// Log the resolved template
console.log(template);
// console.log(template);
// Return the resolved template
return template;
@@ -1021,12 +1064,14 @@ function resolveParams(params, template) {
// Define a unique key for storing the flag in sessionStorage
var sessionStorageKey = "myScriptExecuted_pialert_common";
// -----------------------------------------------------------------------------
function resetInitializedFlag()
{
// Set the flag in sessionStorage to indicate that the code and cahce needs to be reloaded
sessionStorage.setItem(sessionStorageKey, "false");
}
// -----------------------------------------------------------------------------
// check if cache needs to be refreshed because of setting changes
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
@@ -1038,12 +1083,18 @@ $.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
if(importedMiliseconds > lastReloaded)
{
resetInitializedFlag()
location.reload();
console.log("Cache needs to be refreshed because of setting changes");
setTimeout(() => {
resetInitializedFlag()
location.reload();
}, 500);
}
});
// -----------------------------------------------------------------------------
// Display spinner and reload page if not yet initialized
function handleFirstLoad()
{
@@ -1056,6 +1107,7 @@ function handleFirstLoad()
}
}
// -----------------------------------------------------------------------------
// Check if the code has been executed before by checking sessionStorage
var pialert_common_init = sessionStorage.getItem(sessionStorageKey) === "true";
@@ -1066,20 +1118,46 @@ function executeOnce() {
showSpinner()
// Counter to keep track of completed AJAX calls
completedCalls = 0;
// Your initialization code here
cacheSettings();
cacheStrings();
initDeviceListAll_JSON();
}
}
// -----------------------------------------------------------------------------
// Function to handle successful completion of an AJAX call
const handleSuccess = (callName) => {
console.log(`AJAX call ${callName} successful`);
completedCalls++;
onAllCallsComplete();
};
// -----------------------------------------------------------------------------
// Function to handle failure of an AJAX call
const handleFailure = (callName) => {
// Handle AJAX call failure here
console.error(`AJAX call ${callName} failed`);
};
// -----------------------------------------------------------------------------
// Function to execute when all AJAX calls have completed
const onAllCallsComplete = () => {
// Check if all three AJAX calls have completed
if (completedCalls === 3) {
// Set the flag in sessionStorage to indicate that the code has been executed
// and save time when last time the page for initialized
// and save time when last time the page was initialized
sessionStorage.setItem(sessionStorageKey, "true");
const millisecondsNow = Date.now();
sessionStorage.setItem(sessionStorageKey + '_time', millisecondsNow);
console.log("init pialert_common.js");
}
}
};
// Call the function to execute the code
executeOnce();

View File

@@ -111,40 +111,40 @@ $(function () {
// -----------------------------------------------------------------------------
// Initiate dropdown
function initSettingDropdown(settingKey, targetLocation)
function initSettingDropdown(settingKey, valuesArray, targetLocation)
{
var optionsHtml = ""
var targetLocation_options = settingKey + "_initSettingDropdown"
setVal = getSetting(settingKey)
console.log(setVal);
optionsArray = createArray(getSettingOptions(settingKey))
// check if the result is a SQL query
if(isSQLQuery(setVal))
if(isSQLQuery(optionsArray[0]))
{
optionsHtml += `<option id="${targetLocation_options}"></option>`;
readData(setVal, generateDropdownOptions, targetLocation_options);
readData(optionsArray[0], generateDropdownOptions, valuesArray, targetLocation_options);
} else // this should be already an array, e.g. from a setting or pre-defined
{
options = createArray(setVal);
values = createArray(setVal);
options.forEach(option => {
let selected = values.includes(option) ? 'selected' : '';
optionsArray.forEach(option => {
let selected = valuesArray.includes(option) ? 'selected' : '';
optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
});
});
// Place the resulting HTML into the specified placeholder div
$("#" + targetLocation).replaceWith(optionsHtml);
// Replace the specified placeholder div with the resulting HTML
setTimeout(() => {
$("#" + targetLocation).replaceWith(optionsHtml);
}, 50);
}
}
@@ -156,10 +156,13 @@ function initSettingDropdown(settingKey, targetLocation)
// -----------------------------------------------------------------------------
// Processor to generate options for a dropdown menu
function generateDropdownOptions(data) {
function generateDropdownOptions(data, valuesArray) {
var optionsHtml = "";
data.forEach(function(item) {
optionsHtml += `<option value="${item.id}">${item.name}</option>`;
let selected = valuesArray.includes(item.id) ? 'selected' : '';
optionsHtml += `<option value="${item.id}" ${selected}>${item.name}</option>`;
});
return `${optionsHtml}`;
}
@@ -167,10 +170,13 @@ function generateDropdownOptions(data) {
// -----------------------------------------------------------------------------
// Processor to generate a list
function generateList(data) {
function generateList(data, valuesArray) {
var listHtml = "";
data.forEach(function(item) {
listHtml += `<li>${item.name}</li>`;
let selected = valuesArray.includes(item.id) ? 'selected' : '';
listHtml += `<li ${selected}>${item.name}</li>`;
});
listHtml += "";
return listHtml;

View File

@@ -807,7 +807,7 @@ function scrollDown()
}
}
}, 100);
}, 200);
}
@@ -946,6 +946,3 @@ window.onload = function asyncFooter()
<script src="lib/AdminLTE/bower_components/jquery-ui/jquery-ui.min.js"></script>
<!-- ----------------------------------------------------------------------- -->
<script src="js/pialert_common.js"></script>

View File

@@ -110,7 +110,7 @@
targetLocation = columns[j].Code_Name + "_initSettingDropdown"
initSettingDropdown(columns[j].Code_Name, targetLocation)
initSettingDropdown(columns[j].Code_Name, [], targetLocation)
input = `<select class="form-control"
id="${columns[j].Code_Name}"

View File

@@ -454,7 +454,7 @@
<script src="lib/treeviz/index.js"></script>
<script src="lib/treeviz/require.js"></script>
<script src="js/pialert_common.js"></script>
<script>
$.get('php/server/devices.php?action=getDevicesList&status=all&forceDefaultOrder', function(data) {

View File

@@ -55,7 +55,7 @@
<!-- <script src="lib/AdminLTE/bower_components/fastclick/lib/fastclick.js"></script> -->
<!-- Pi.Alert -------------------------------------------------------------- -->
<script src="js/pialert_common.js"></script>
<script src="js/handle_version.js"></script>
</body>

View File

@@ -28,10 +28,11 @@ require dirname(__FILE__).'/security.php';
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- ----------------------------------------------------------------------- -->
<!-- REQUIRED JS SCRIPTS -->
<!-- jQuery 3 -->
<script src="lib/AdminLTE/bower_components/jquery/dist/jquery.min.js"></script>
<script src="js/pialert_common.js"></script>
<!-- Bootstrap 3.3.7 -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/bootstrap/dist/css/bootstrap.min.css">
@@ -376,7 +377,7 @@ if ($ENABLED_DARKMODE === True) {
<!-- /.sidebar -->
</aside>
<script src="js/pialert_common.js"></script>
<script defer>
// Generate work-in-progress icons

View File

@@ -4,8 +4,6 @@
require 'php/templates/notification.php';
?>
<script src="js/pialert_common.js"></script>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">

View File

@@ -648,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 `"default_value": "{value}"` template and `text.select`. Can specify SQL query or Setting to populate the dropdown. Check example below. |
| `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. |
| `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. |
@@ -668,18 +668,23 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
```json
"options_params" : [
"function": "dev_DeviceType",
"type": "text.select",
"maxLength": 30,
"default_value": "",
"options": ["{value}"],
"options_params" : [
{
"name" : "value",
"type" : "sql",
"value" : "SELECT Dev_Name as name, dev_MAC as id FROM Devices WHERE EXISTS (SELECT 1 FROM Settings WHERE Code_Name = 'NETWORK_DEVICE_TYPES' AND LOWER(value) LIKE '%' || LOWER(dev_DeviceType) || '%' AND dev_DeviceType <> '')"
"name" : "value",
"type" : "sql",
"value" : "SELECT '' as id, '' as name UNION SELECT dev_DeviceType as id, dev_DeviceType as name FROM (SELECT dev_DeviceType FROM Devices UNION SELECT 'Smartphone' UNION SELECT 'Tablet' UNION SELECT 'Laptop' UNION SELECT 'PC' UNION SELECT 'Printer' UNION SELECT 'Server' UNION SELECT 'NAS' UNION SELECT 'Domotic' UNION SELECT 'Game Console' UNION SELECT 'SmartTV' UNION SELECT 'Clock' UNION SELECT 'House Appliance' UNION SELECT 'Phone' UNION SELECT 'AP' UNION SELECT 'Gateway' UNION SELECT 'Firewall' UNION SELECT 'Switch' UNION SELECT 'WLAN' UNION SELECT 'Router' UNION SELECT 'Other') AS all_devices ORDER BY id;"
},
{
"name" : "target_macs",
"type" : "setting",
"value" : "KNWN_target_macs"
"name" : "uilang",
"type" : "setting",
"value" : "UI_LANG"
}
],
]
```

View File

@@ -104,10 +104,17 @@
},
{
"function": "dev_Owner",
"type": "string",
"type": "text.select",
"maxLength": 30,
"default_value": "House",
"options": [],
"default_value": "",
"options": ["{value}"],
"options_params" : [
{
"name" : "value",
"type" : "sql",
"value" : "SELECT DISTINCT '' as id, '' as name UNION SELECT dev_Owner as id, dev_Owner as name FROM (SELECT dev_Owner FROM Devices UNION SELECT 'House' ) AS all_devices ORDER BY id;"
}
],
"localized": ["name", "description"],
"name": [
{
@@ -126,8 +133,8 @@
"function": "dev_DeviceType",
"type": "text.select",
"maxLength": 30,
"default_value": "{value}",
"options": [],
"default_value": "",
"options": ["{value}"],
"options_params" : [
{
"name" : "value",
@@ -195,10 +202,17 @@
},
{
"function": "dev_Group",
"type": "string",
"type": "text.select",
"maxLength": 10,
"default_value": "",
"options": [],
"options": ["{value}"],
"options_params" : [
{
"name" : "value",
"type" : "sql",
"value" : "SELECT DISTINCT '' as id, '' as name UNION SELECT dev_Group as id, dev_Group as name FROM (SELECT dev_Group FROM Devices UNION SELECT 'Personal' ) AS all_devices ORDER BY id;"
}
],
"localized": ["name", "description"],
"name": [
{
@@ -468,7 +482,14 @@
"type": "string",
"maxLength": 250,
"default_value": "",
"options": [],
"options": ["{value}"],
"options_params" : [
{
"name" : "value",
"type" : "sql",
"value" : "SELECT DISTINCT '' as id, '' as name UNION SELECT dev_Location as id, dev_Location as name FROM (SELECT dev_Location FROM Devices UNION SELECT 'Bathroom' UNION SELECT 'Bedroom' UNION SELECT 'Dining room' UNION SELECT 'Hall' UNION SELECT 'Kitchen' UNION SELECT 'Laundry' UNION SELECT 'Living room' UNION SELECT 'Study' UNION SELECT 'Attic' UNION SELECT 'Basement' UNION SELECT 'Garage' UNION SELECT 'Back yard' UNION SELECT 'Garden' UNION SELECT 'Terrace') AS all_devices ORDER BY id; "
}
],
"localized": ["name", "description"],
"name": [
{
@@ -505,8 +526,8 @@
{
"function": "dev_Network_Node_MAC_ADDR",
"type": "text.select",
"default_value": "{value}",
"options": [],
"default_value": "",
"options": ["{value}"],
"options_params" : [
{
"name" : "value",

View File

@@ -53,7 +53,6 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
<!-- Page ------------------------------------------------------------------ -->
<!-- Page ------------------------------------------------------------------ -->
<script src="js/pialert_common.js"></script>
<script src="js/settings_utils.js"></script>
<script src="js/db_methods.js"></script>
<script src="js/ui_components.js"></script>
@@ -530,41 +529,37 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
function generateInputOptions(pluginsData, set, input, isMultiSelect = false)
{
prefix = set["Group"]
var optionsHtml = ""
multi = isMultiSelect ? "multiple" : "";
tmpOptions = set['Options']
optionsArray = getSettingOptions(set['Code_Name'] )
valuesArray = createArray(set['Value']);
setVal = getSetting(set['Code_Name'] )
// check if the result is a SQL query
if(isSQLQuery(setVal))
{
// // check if the result is a SQL query - if so, dropdown will be populated async with AJAX
// if(isSQLQuery(optionsArray))
// {
var targetLocation = set['Code_Name'] + "_initSettingDropdown";
optionsHtml += `<option id="${targetLocation}"></option>`;
// placeholder option which will be replaced on callback
optionsHtml += `<option id="${targetLocation}" temporary="temporary"></option>`;
console.log(set['Code_Name'] )
console.log(setVal )
// execute AJAX callabck + SQL query resolution
initSettingDropdown(set['Code_Name'] , valuesArray, targetLocation)
initSettingDropdown(set['Code_Name'] , targetLocation)
// }
// else // it's a string without a SQL resolution requirements
// {
// options = createArray(optionsArray);
} else // this should be already an array, e.g. from a setting or pre-defined
{
options = createArray(tmpOptions);
values = createArray(set['Value']);
// options.forEach(option => {
// let selected = valuesArray.includes(option) ? 'selected' : '';
// optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
// });
// }
options.forEach(option => {
let selected = values.includes(option) ? 'selected' : '';
optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
});
}
// main selection dropdown wrapper
input += `
<select onChange="settingsChanged()"
my-data-type="${set['Type']}"

View File

@@ -4,7 +4,6 @@
require 'php/templates/notification.php';
?>
<script src="js/pialert_common.js"></script>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">