⚙ settings saving improvements + refactor - DB lock v0.1 #685
This commit is contained in:
@@ -26,7 +26,7 @@ services:
|
|||||||
- ${APP_DATA_LOCATION}/pihole/etc-pihole/pihole-FTL.db:/etc/pihole/pihole-FTL.db
|
- ${APP_DATA_LOCATION}/pihole/etc-pihole/pihole-FTL.db:/etc/pihole/pihole-FTL.db
|
||||||
- ${DEV_LOCATION}/server:/app/server
|
- ${DEV_LOCATION}/server:/app/server
|
||||||
- ${DEV_LOCATION}/dockerfiles:/app/dockerfiles
|
- ${DEV_LOCATION}/dockerfiles:/app/dockerfiles
|
||||||
- ${APP_DATA_LOCATION}/netalertx/php.ini:/etc/php/8.2/fpm/php.ini
|
# - ${APP_DATA_LOCATION}/netalertx/php.ini:/etc/php/8.2/fpm/php.ini
|
||||||
- ${DEV_LOCATION}/install:/app/install
|
- ${DEV_LOCATION}/install:/app/install
|
||||||
- ${DEV_LOCATION}/front/css:/app/front/css
|
- ${DEV_LOCATION}/front/css:/app/front/css
|
||||||
- ${DEV_LOCATION}/front/img:/app/front/img
|
- ${DEV_LOCATION}/front/img:/app/front/img
|
||||||
|
|||||||
@@ -492,28 +492,34 @@
|
|||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
Notification float banner
|
Notification float banner
|
||||||
----------------------------------------------------------------------------- */
|
----------------------------------------------------------------------------- */
|
||||||
.pa_alert_notification {
|
.notification_modal {
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: large;
|
left: 0;
|
||||||
font-weight: bold;
|
right: 0;
|
||||||
color: #258744;
|
|
||||||
|
|
||||||
background-color: #d4edda;
|
|
||||||
border-color: #c3e6cb;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
max-width: 1000px;
|
|
||||||
/* 80% wrapper 1250px */
|
|
||||||
width: 80%;
|
width: 80%;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 30px;
|
top: 100px;
|
||||||
margin: auto;
|
|
||||||
transform: translate(0, 0);
|
|
||||||
|
|
||||||
display: none;
|
display: none;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal_green
|
||||||
|
{
|
||||||
|
color: #258744;
|
||||||
|
background-color: #d4edda;
|
||||||
|
border-color: #c3e6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal_grey
|
||||||
|
{
|
||||||
|
color: white;
|
||||||
|
background-color: darkgrey;
|
||||||
|
border-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
/* ticker setup */
|
/* ticker setup */
|
||||||
.ticker-li
|
.ticker-li
|
||||||
{
|
{
|
||||||
@@ -979,6 +985,11 @@ input[readonly] {
|
|||||||
border-color: #258744;
|
border-color: #258744;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-sticky-bottom-section .form-group
|
||||||
|
{
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.clear-filter
|
.clear-filter
|
||||||
{
|
{
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
|||||||
@@ -186,17 +186,24 @@ function modalWarningOK() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
function showMessage(textMessage = "") {
|
function showMessage(textMessage = "", timeout = 3000, colorClass = "modal_green") {
|
||||||
if (textMessage.toLowerCase().includes("error")) {
|
if (textMessage.toLowerCase().includes("error")) {
|
||||||
// show error
|
// show error
|
||||||
alert(textMessage);
|
alert(textMessage);
|
||||||
} else {
|
} else {
|
||||||
// show temporal notification
|
// show temporary notification
|
||||||
|
$("#notification").removeClass(); // remove all classes
|
||||||
|
$("#notification").addClass("alert alert-dimissible notification_modal"); // add default ones
|
||||||
|
$("#notification").addClass(colorClass); // add color modifiers
|
||||||
|
|
||||||
|
// message
|
||||||
$("#alert-message").html(textMessage);
|
$("#alert-message").html(textMessage);
|
||||||
|
|
||||||
|
// timeout
|
||||||
$("#notification").fadeIn(1, function () {
|
$("#notification").fadeIn(1, function () {
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
$("#notification").fadeOut(500);
|
$("#notification").fadeOut(500);
|
||||||
}, 3000);
|
}, timeout);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,10 +201,94 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Validation
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
function settingsCollectedCorrectly(settingsArray, settingsJSON_DB) {
|
||||||
|
|
||||||
|
// check if the required UI_LANG setting is in the array - if not something went wrong
|
||||||
|
$.each(settingsArray, function(index, value) {
|
||||||
|
if (value[1] == "UI_LANG") {
|
||||||
|
if(isEmpty(value[3]) == true)
|
||||||
|
{
|
||||||
|
console.log(`⚠ Error: Required setting UI_LANG not found`);
|
||||||
|
showModalOk('ERROR', getString('settings_missing_block'));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const settingsCodeNames = settingsJSON_DB.map(setting => setting.Code_Name);
|
||||||
|
const detailedCodeNames = settingsArray.map(item => item[1]);
|
||||||
|
|
||||||
|
const missingCodeNamesOnPage = detailedCodeNames.filter(codeName => !settingsCodeNames.includes(codeName));
|
||||||
|
const missingCodeNamesInDB = settingsCodeNames.filter(codeName => !detailedCodeNames.includes(codeName));
|
||||||
|
|
||||||
|
// check if the number of settings on the page and in the DB are the same
|
||||||
|
if (missingCodeNamesOnPage.length !== missingCodeNamesInDB.length) {
|
||||||
|
|
||||||
|
console.log(`⚠ Error: The following settings are missing in the DB or on the page (Reload page to fix):`);
|
||||||
|
console.log(missingCodeNamesOnPage);
|
||||||
|
console.log(missingCodeNamesInDB);
|
||||||
|
|
||||||
|
showModalOk('ERROR', getString('settings_missing_block'));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all OK
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// Manipulating Editable List options
|
// Manipulating Editable List options
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
function addList(element)
|
||||||
|
{
|
||||||
|
|
||||||
|
const fromId = $(element).attr('my-input-from');
|
||||||
|
const toId = $(element).attr('my-input-to');
|
||||||
|
|
||||||
|
input = $(`#${fromId}`).val();
|
||||||
|
$(`#${toId}`).append($("<option ></option>").attr("value", input).text(input));
|
||||||
|
|
||||||
|
// clear input
|
||||||
|
$(`#${fromId}`).val("");
|
||||||
|
|
||||||
|
settingsChanged();
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
function removeFromList(element)
|
||||||
|
{
|
||||||
|
settingsChanged();
|
||||||
|
$(`#${$(element).attr('my-input')}`).find("option:last").remove();
|
||||||
|
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
function addInterface()
|
||||||
|
{
|
||||||
|
ipMask = $('#ipMask').val();
|
||||||
|
ipInterface = $('#ipInterface').val();
|
||||||
|
|
||||||
|
full = ipMask + " --interface=" + ipInterface;
|
||||||
|
|
||||||
|
console.log(full)
|
||||||
|
|
||||||
|
if(ipMask == "" || ipInterface == "")
|
||||||
|
{
|
||||||
|
showModalOk ('Validation error', 'Specify both, the network mask and the interface');
|
||||||
|
} else {
|
||||||
|
$('#SCAN_SUBNETS').append($('<option disabled></option>').attr('value', full).text(full));
|
||||||
|
|
||||||
|
$('#ipMask').val('');
|
||||||
|
$('#ipInterface').val('');
|
||||||
|
|
||||||
|
settingsChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// Function to remove an item from the select element
|
// Function to remove an item from the select element
|
||||||
@@ -305,32 +389,175 @@ function filterRows(inputText) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
||||||
// Event listener for input change
|
// Event listener for input change
|
||||||
$('#settingsSearch').on('input', function() {
|
$('#settingsSearch').on('input', function() {
|
||||||
var searchText = $(this).val();
|
var searchText = $(this).val();
|
||||||
// hide the setting overview dashboard
|
// hide the setting overview dashboard
|
||||||
$('#settingsOverview').collapse('hide');
|
$('#settingsOverview').collapse('hide');
|
||||||
|
|
||||||
filterRows(searchText);
|
filterRows(searchText);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Event listener for input focus
|
||||||
|
// var firstFocus = true;
|
||||||
|
$('#settingsSearch').on('focus', function() {
|
||||||
|
openAllSettings()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// handling events on the backend initiated by the front end START
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
modalEventStatusId = 'modal-message-front-event'
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Calls a backend function to add a front-end event (specified by the attributes 'data-myevent' and 'data-myparam-plugin' on the passed element) to an execution queue
|
||||||
|
function addToExecutionQueue(element)
|
||||||
|
{
|
||||||
|
|
||||||
|
// value has to be in format event|param. e.g. run|ARPSCAN
|
||||||
|
action = `${getGuid()}|${$(element).attr('data-myevent')}|${$(element).attr('data-myparam-plugin')}`
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: "POST",
|
||||||
|
url: "php/server/util.php",
|
||||||
|
data: { function: "addToExecutionQueue", action: action },
|
||||||
|
success: function(data, textStatus) {
|
||||||
|
// showModalOk ('Result', data );
|
||||||
|
|
||||||
|
// show message
|
||||||
|
showModalOk(getString("general_event_title"), `${getString("general_event_description")} <br/> <br/> <code id='${modalEventStatusId}'></code>`);
|
||||||
|
|
||||||
|
updateModalState()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Updating the execution queue in in modal pop-up
|
||||||
|
function updateModalState() {
|
||||||
|
setTimeout(function() {
|
||||||
|
// Fetch the content from the log file using an AJAX request
|
||||||
|
$.ajax({
|
||||||
|
url: '/log/execution_queue.log',
|
||||||
|
type: 'GET',
|
||||||
|
success: function(data) {
|
||||||
|
// Update the content of the HTML element (e.g., a div with id 'logContent')
|
||||||
|
$('#'+modalEventStatusId).html(data);
|
||||||
|
|
||||||
|
updateModalState();
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
// Handle error, such as the file not being found
|
||||||
|
$('#logContent').html('Error: Log file not found.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// handling events on the backend initiated by the front end END
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// UNUSED?
|
||||||
|
function getParam(targetId, key, skipCache = false) {
|
||||||
|
|
||||||
|
skipCacheQuery = "";
|
||||||
|
|
||||||
|
if(skipCache)
|
||||||
|
{
|
||||||
|
skipCacheQuery = "&skipcache";
|
||||||
|
}
|
||||||
|
|
||||||
|
// get parameter value
|
||||||
|
$.get('php/server/parameters.php?action=get&defaultValue=0¶meter='+ key + skipCacheQuery, function(data) {
|
||||||
|
|
||||||
|
var result = data;
|
||||||
|
|
||||||
|
result = result.replaceAll('"', '');
|
||||||
|
|
||||||
|
document.getElementById(targetId).innerHTML = result.replaceAll('"', '');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Event listener for input focus
|
// -----------------------------------------------------------------------------
|
||||||
// var firstFocus = true;
|
// Show/hide the metadata settings
|
||||||
$('#settingsSearch').on('focus', function() {
|
// -----------------------------------------------------------------------------
|
||||||
openAllSettings()
|
function toggleMetadata(element)
|
||||||
});
|
{
|
||||||
|
const id = $(element).attr('my-to-toggle');
|
||||||
|
|
||||||
|
$(`#${id}`).toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Helper methods
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Toggle readonly mode of the target element specified by the id in the "my-input-toggle-readonly" attribute
|
||||||
|
function overrideToggle(element) {
|
||||||
|
settingsChanged();
|
||||||
|
|
||||||
|
targetId = $(element).attr("my-input-toggle-readonly");
|
||||||
|
|
||||||
|
inputElement = $(`#${targetId}`)[0];
|
||||||
|
|
||||||
|
if (!inputElement) {
|
||||||
|
console.error("Input element not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputElement.type === "text" || inputElement.type === "password") {
|
||||||
|
inputElement.readOnly = !inputElement.readOnly;
|
||||||
|
} else if (inputElement.type === "checkbox") {
|
||||||
|
inputElement.disabled = !inputElement.disabled;
|
||||||
|
} else {
|
||||||
|
console.warn("Unsupported input type. Only text, password, and checkbox inputs are supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// generate a list of options for a input select
|
||||||
|
function generateInputOptions(pluginsData, set, input, isMultiSelect = false)
|
||||||
|
{
|
||||||
|
multi = isMultiSelect ? "multiple" : "";
|
||||||
|
|
||||||
|
// optionsArray = getSettingOptions(set['Code_Name'] )
|
||||||
|
valuesArray = createArray(set['Value']);
|
||||||
|
|
||||||
|
// create unique ID
|
||||||
|
var targetLocation = set['Code_Name'] + "_initSettingDropdown";
|
||||||
|
|
||||||
|
// execute AJAX callabck + SQL query resolution
|
||||||
|
initSettingDropdown(set['Code_Name'] , valuesArray, targetLocation, generateDropdownOptions)
|
||||||
|
|
||||||
|
// main selection dropdown wrapper
|
||||||
|
input += `
|
||||||
|
<select onChange="settingsChanged()"
|
||||||
|
my-data-type="${set['Type']}"
|
||||||
|
class="form-control"
|
||||||
|
name="${set['Code_Name']}"
|
||||||
|
id="${set['Code_Name']}" ${multi}>
|
||||||
|
|
||||||
|
<option id="${targetLocation}" temporary="temporary"></option>
|
||||||
|
|
||||||
|
</select>`;
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
// DB File Path
|
// DB File Path
|
||||||
$DBFILE = dirname(__FILE__).'/../../../db/app.db';
|
$DBFILE = dirname(__FILE__).'/../../../db/app.db';
|
||||||
|
|
||||||
|
$db_locked = false;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Connect DB
|
// Connect DB
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -20,12 +22,21 @@ function SQLite3_connect ($trytoreconnect) {
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// connect to database
|
// connect to database
|
||||||
|
|
||||||
|
global $db_locked;
|
||||||
|
|
||||||
|
$db_locked = false;
|
||||||
|
|
||||||
// return new SQLite3($DBFILE, SQLITE3_OPEN_READONLY);
|
// return new SQLite3($DBFILE, SQLITE3_OPEN_READONLY);
|
||||||
return new SQLite3($DBFILE, SQLITE3_OPEN_READWRITE);
|
return new SQLite3($DBFILE, SQLITE3_OPEN_READWRITE);
|
||||||
}
|
}
|
||||||
catch (Exception $exception)
|
catch (Exception $exception)
|
||||||
{
|
{
|
||||||
// sqlite3 throws an exception when it is unable to connect
|
// sqlite3 throws an exception when it is unable to connect
|
||||||
|
global $db_locked;
|
||||||
|
|
||||||
|
$db_locked = true;
|
||||||
|
|
||||||
// try to reconnect one time after 3 seconds
|
// try to reconnect one time after 3 seconds
|
||||||
|
|
||||||
if($trytoreconnect)
|
if($trytoreconnect)
|
||||||
|
|||||||
@@ -269,16 +269,26 @@ function delete($columnName, $id, $dbtable)
|
|||||||
// check if the database is locked
|
// check if the database is locked
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
function checkLock() {
|
function checkLock() {
|
||||||
global $db;
|
global $DBFILE, $db_locked;
|
||||||
try {
|
|
||||||
$db->exec('BEGIN EXCLUSIVE TRANSACTION');
|
$file = fopen($DBFILE, 'r+');
|
||||||
$db->exec('COMMIT');
|
|
||||||
echo 0; // Not locked
|
if (!$file or $db_locked) {
|
||||||
return 0;
|
echo 1; // Could not open the file
|
||||||
} catch (Exception $e) {
|
return;
|
||||||
echo 1; // Locked
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flock($file, LOCK_EX | LOCK_NB)) {
|
||||||
|
// Lock acquired, meaning the database is not locked by another process
|
||||||
|
flock($file, LOCK_UN); // Release the lock
|
||||||
|
echo 0; // Not locked
|
||||||
|
} else {
|
||||||
|
// Could not acquire lock, meaning the database is locked
|
||||||
|
echo 1; // Locked
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -387,8 +387,10 @@ function saveSettings()
|
|||||||
// Replace the original file with the temporary file
|
// Replace the original file with the temporary file
|
||||||
rename($tempConfPath, $fullConfPath);
|
rename($tempConfPath, $fullConfPath);
|
||||||
|
|
||||||
displayMessage("<br/>Settings saved to the <code>app.conf</code> file.<br/><br/>A time-stamped backup of the previous file created. <br/><br/> Reloading...<br/>",
|
// displayMessage(lang('settings_saved'),
|
||||||
FALSE, TRUE, TRUE, TRUE);
|
// FALSE, TRUE, TRUE, TRUE);
|
||||||
|
|
||||||
|
echo "OK";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -657,7 +657,7 @@
|
|||||||
"settings_imported": "Last time settings were imported from the app.conf file",
|
"settings_imported": "Last time settings were imported from the app.conf file",
|
||||||
"settings_imported_label": "Settings imported",
|
"settings_imported_label": "Settings imported",
|
||||||
"settings_missing": "Not all settings loaded! High load on the database or app startup sequence. Click the 🔄 reload button in the top.",
|
"settings_missing": "Not all settings loaded! High load on the database or app startup sequence. Click the 🔄 reload button in the top.",
|
||||||
"settings_missing_block": "Not all settings were loaded correctly. This is probably caused by a high load on the database. Click the 🔄 reload button in the top.",
|
"settings_missing_block": "Error: Settings not loaded correctly. Click the reload button 🔄 at the top, alternatively, check the browser log for details (F12).",
|
||||||
"settings_old": "Importing settings and re-initializing...",
|
"settings_old": "Importing settings and re-initializing...",
|
||||||
"settings_other_scanners": "Other, non-device scanner plugins that are currently enabled.",
|
"settings_other_scanners": "Other, non-device scanner plugins that are currently enabled.",
|
||||||
"settings_other_scanners_icon": "fa-solid fa-recycle",
|
"settings_other_scanners_icon": "fa-solid fa-recycle",
|
||||||
@@ -665,7 +665,7 @@
|
|||||||
"settings_publishers": "Enabled notification gateways - publishers, that will send a notification depending on your settings.",
|
"settings_publishers": "Enabled notification gateways - publishers, that will send a notification depending on your settings.",
|
||||||
"settings_publishers_icon": "fa-solid fa-comment-dots",
|
"settings_publishers_icon": "fa-solid fa-comment-dots",
|
||||||
"settings_publishers_label": "Publishers",
|
"settings_publishers_label": "Publishers",
|
||||||
"settings_saved": "<br/>Settings saved to the <code>app.conf</code> file.<br/><br/>A time-stamped backup of the previous file created. <br/><br/> Reloading...<br/>",
|
"settings_saved": "<br/>Settings saved. <br/><br/> Reloading... <br/><br/> <i class=\"ion ion-ios-loop-strong fa-spin fa-2x fa-fw\"></i> <br/>",
|
||||||
"settings_system_icon": "fa-solid fa-gear",
|
"settings_system_icon": "fa-solid fa-gear",
|
||||||
"settings_system_label": "System",
|
"settings_system_label": "System",
|
||||||
"settings_update_item_warning": "Update the value below. Be careful to follow the previous format. <b>Validation is not performed.</b>",
|
"settings_update_item_warning": "Update the value below. Be careful to follow the previous format. <b>Validation is not performed.</b>",
|
||||||
|
|||||||
@@ -147,7 +147,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- Alert float -->
|
<!-- Alert float -->
|
||||||
<div id="notification" class="alert alert-dimissible pa_alert_notification">
|
<div id="notification" >
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<div id="alert-message"> Alert message </div>
|
<div id="alert-message"> Alert message </div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -601,65 +601,6 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
// generate a list of options for a input select
|
|
||||||
function generateInputOptions(pluginsData, set, input, isMultiSelect = false)
|
|
||||||
{
|
|
||||||
multi = isMultiSelect ? "multiple" : "";
|
|
||||||
|
|
||||||
// optionsArray = getSettingOptions(set['Code_Name'] )
|
|
||||||
valuesArray = createArray(set['Value']);
|
|
||||||
|
|
||||||
// create unique ID
|
|
||||||
var targetLocation = set['Code_Name'] + "_initSettingDropdown";
|
|
||||||
|
|
||||||
// execute AJAX callabck + SQL query resolution
|
|
||||||
initSettingDropdown(set['Code_Name'] , valuesArray, targetLocation, generateDropdownOptions)
|
|
||||||
|
|
||||||
// main selection dropdown wrapper
|
|
||||||
input += `
|
|
||||||
<select onChange="settingsChanged()"
|
|
||||||
my-data-type="${set['Type']}"
|
|
||||||
class="form-control"
|
|
||||||
name="${set['Code_Name']}"
|
|
||||||
id="${set['Code_Name']}" ${multi}>
|
|
||||||
|
|
||||||
<option id="${targetLocation}" temporary="temporary"></option>
|
|
||||||
|
|
||||||
</select>`;
|
|
||||||
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
// Helper methods
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
// Toggle readonly mode of teh target element specified by the id in the "my-input-toggle-readonly" attribute
|
|
||||||
function overrideToggle(element) {
|
|
||||||
settingsChanged();
|
|
||||||
|
|
||||||
targetId = $(element).attr("my-input-toggle-readonly");
|
|
||||||
|
|
||||||
inputElement = $(`#${targetId}`)[0];
|
|
||||||
|
|
||||||
if (!inputElement) {
|
|
||||||
console.error("Input element not found!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputElement.type === "text" || inputElement.type === "password") {
|
|
||||||
inputElement.readOnly = !inputElement.readOnly;
|
|
||||||
} else if (inputElement.type === "checkbox") {
|
|
||||||
inputElement.disabled = !inputElement.disabled;
|
|
||||||
} else {
|
|
||||||
console.warn("Unsupported input type. Only text, password, and checkbox inputs are supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// number of settings has to be equal to
|
|
||||||
|
|
||||||
// display the name of the first person
|
// display the name of the first person
|
||||||
// echo $settingsJson[0]->name;
|
// echo $settingsJson[0]->name;
|
||||||
@@ -670,61 +611,16 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
// Wrong number of settings processing
|
// Wrong number of settings processing
|
||||||
if(settingsNumberJSON != settingsNumberDB)
|
if(settingsNumberJSON != settingsNumberDB)
|
||||||
{
|
{
|
||||||
showModalOk('WARNING', "<?= lang("settings_missing")?>");
|
showModalOk('WARNING', getString("settings_missing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
function addList(element)
|
|
||||||
{
|
|
||||||
|
|
||||||
const fromId = $(element).attr('my-input-from');
|
|
||||||
const toId = $(element).attr('my-input-to');
|
|
||||||
|
|
||||||
input = $(`#${fromId}`).val();
|
|
||||||
$(`#${toId}`).append($("<option ></option>").attr("value", input).text(input));
|
|
||||||
|
|
||||||
// clear input
|
|
||||||
$(`#${fromId}`).val("");
|
|
||||||
|
|
||||||
settingsChanged();
|
|
||||||
}
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
function removeFromList(element)
|
|
||||||
{
|
|
||||||
settingsChanged();
|
|
||||||
$(`#${$(element).attr('my-input')}`).find("option:last").remove();
|
|
||||||
|
|
||||||
}
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
function addInterface()
|
|
||||||
{
|
|
||||||
ipMask = $('#ipMask').val();
|
|
||||||
ipInterface = $('#ipInterface').val();
|
|
||||||
|
|
||||||
full = ipMask + " --interface=" + ipInterface;
|
|
||||||
|
|
||||||
console.log(full)
|
|
||||||
|
|
||||||
if(ipMask == "" || ipInterface == "")
|
|
||||||
{
|
|
||||||
showModalOk ('Validation error', 'Specify both, the network mask and the interface');
|
|
||||||
} else {
|
|
||||||
$('#SCAN_SUBNETS').append($('<option disabled></option>').attr('value', full).text(full));
|
|
||||||
|
|
||||||
$('#ipMask').val('');
|
|
||||||
$('#ipInterface').val('');
|
|
||||||
|
|
||||||
settingsChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
if(settingsNumberJSON != settingsNumberDB)
|
if(settingsNumberJSON != settingsNumberDB)
|
||||||
{
|
{
|
||||||
console.log(`Error settingsNumberJSON != settingsNumberDB: ${settingsNumberJSON} != ${settingsNumberDB}`);
|
console.log(`Error settingsNumberJSON != settingsNumberDB: ${settingsNumberJSON} != ${settingsNumberDB}`);
|
||||||
|
|
||||||
showModalOk('WARNING', "<?= lang("settings_missing_block")?>");
|
showModalOk('WARNING', getString("settings_missing_block"));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
clearCache()
|
clearCache()
|
||||||
@@ -778,49 +674,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(settingsArray);
|
|
||||||
|
|
||||||
// sanity check to make sure settings were loaded & collected correctly
|
// sanity check to make sure settings were loaded & collected correctly
|
||||||
sanityCheck_notOK = true
|
if(settingsCollectedCorrectly(settingsArray, settingsJSON_DB))
|
||||||
$.each(settingsArray, function(index, value) {
|
|
||||||
// Do something with each element of the array
|
|
||||||
if(value[1] == "UI_LANG")
|
|
||||||
{
|
|
||||||
sanityCheck_notOK = isEmpty(value[3])
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// if ok, double check the number collected of settings is correct
|
|
||||||
if(sanityCheck_notOK == false)
|
|
||||||
{
|
|
||||||
|
|
||||||
console.log(settingsArray);
|
|
||||||
|
|
||||||
// Step 1: Extract Code_Name values from settingsList
|
|
||||||
const settingsCodeNames = settingsJSON_DB.map(setting => setting.Code_Name);
|
|
||||||
|
|
||||||
// Step 2: Extract second elements from detailedList
|
|
||||||
const detailedCodeNames = settingsArray.map(item => item[1]);
|
|
||||||
|
|
||||||
// Step 3: Find missing Code_Name values
|
|
||||||
const missingCodeNamesOnPage = detailedCodeNames.filter(codeName => !settingsCodeNames.includes(codeName));
|
|
||||||
const missingCodeNamesInDB = settingsCodeNames.filter(codeName => !detailedCodeNames.includes(codeName));
|
|
||||||
|
|
||||||
|
|
||||||
if(missingCodeNamesOnPage.length != missingCodeNamesInDB.length)
|
|
||||||
{
|
|
||||||
sanityCheck_notOK = true;
|
|
||||||
|
|
||||||
console.log(`⚠ Error: The following settings are missing in the DB or on the page (Reload page to fix):`);
|
|
||||||
console.log(missingCodeNamesOnPage);
|
|
||||||
console.log(missingCodeNamesInDB);
|
|
||||||
|
|
||||||
showModalOk('WARNING', "<?= lang("settings_missing_block")?>");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sanityCheck_notOK == false && false)
|
|
||||||
{
|
{
|
||||||
// trigger a save settings event in the backend
|
// trigger a save settings event in the backend
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -831,7 +686,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
settings: JSON.stringify(settingsArray) },
|
settings: JSON.stringify(settingsArray) },
|
||||||
success: function(data, textStatus) {
|
success: function(data, textStatus) {
|
||||||
|
|
||||||
showModalOk ('Result', data );
|
showMessage (getString("settings_saved"), 5000, "modal_grey");
|
||||||
|
|
||||||
// Remove navigation prompt "Are you sure you want to leave..."
|
// Remove navigation prompt "Are you sure you want to leave..."
|
||||||
window.onbeforeunload = null;
|
window.onbeforeunload = null;
|
||||||
@@ -848,30 +703,15 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
function getParam(targetId, key, skipCache = false) {
|
|
||||||
|
|
||||||
skipCacheQuery = "";
|
|
||||||
|
|
||||||
if(skipCache)
|
</script>
|
||||||
{
|
|
||||||
skipCacheQuery = "&skipcache";
|
|
||||||
}
|
|
||||||
|
|
||||||
// get parameter value
|
|
||||||
$.get('php/server/parameters.php?action=get&defaultValue=0¶meter='+ key + skipCacheQuery, function(data) {
|
|
||||||
|
|
||||||
var result = data;
|
<!-- INIT THE PAGE -->
|
||||||
|
<script defer>
|
||||||
result = result.replaceAll('"', '');
|
|
||||||
|
|
||||||
document.getElementById(targetId).innerHTML = result.replaceAll('"', '');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
function handleLoadingDialog()
|
||||||
function handleLoadingDialog()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// check if config file has been updated
|
// check if config file has been updated
|
||||||
@@ -926,85 +766,9 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script defer>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// handling events on the backend initiated by the front end START
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
function toggleMetadata(element)
|
|
||||||
{
|
|
||||||
const id = $(element).attr('my-to-toggle');
|
|
||||||
|
|
||||||
$(`#${id}`).toggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// handling events on the backend initiated by the front end START
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
modalEventStatusId = 'modal-message-front-event'
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
|
||||||
// Calls a backend function to add a front-end event (specified by the attributes 'data-myevent' and 'data-myparam-plugin' on the passed element) to an execution queue
|
|
||||||
function addToExecutionQueue(element)
|
|
||||||
{
|
|
||||||
|
|
||||||
// value has to be in format event|param. e.g. run|ARPSCAN
|
|
||||||
action = `${getGuid()}|${$(element).attr('data-myevent')}|${$(element).attr('data-myparam-plugin')}`
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
method: "POST",
|
|
||||||
url: "php/server/util.php",
|
|
||||||
data: { function: "addToExecutionQueue", action: action },
|
|
||||||
success: function(data, textStatus) {
|
|
||||||
// showModalOk ('Result', data );
|
|
||||||
|
|
||||||
// show message
|
|
||||||
showModalOk(getString("general_event_title"), `${getString("general_event_description")} <br/> <br/> <code id='${modalEventStatusId}'></code>`);
|
|
||||||
|
|
||||||
updateModalState()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------
|
|
||||||
// Updating the execution queue in in modal pop-up
|
|
||||||
function updateModalState() {
|
|
||||||
setTimeout(function() {
|
|
||||||
// Fetch the content from the log file using an AJAX request
|
|
||||||
$.ajax({
|
|
||||||
url: '/log/execution_queue.log',
|
|
||||||
type: 'GET',
|
|
||||||
success: function(data) {
|
|
||||||
// Update the content of the HTML element (e.g., a div with id 'logContent')
|
|
||||||
$('#'+modalEventStatusId).html(data);
|
|
||||||
|
|
||||||
updateModalState();
|
|
||||||
},
|
|
||||||
error: function() {
|
|
||||||
// Handle error, such as the file not being found
|
|
||||||
$('#logContent').html('Error: Log file not found.');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// handling events on the backend initiated by the front end END
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
// Show last time settings have been imported
|
|
||||||
|
|
||||||
showSpinner()
|
showSpinner()
|
||||||
handleLoadingDialog()
|
handleLoadingDialog()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user