FE: regex validation for cron run schedules

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2025-11-27 12:10:33 +11:00
parent 8acb0a876a
commit b9d3f430fe
64 changed files with 666 additions and 592 deletions

View File

@@ -1,6 +1,6 @@
/* -----------------------------------------------------------------------------
# NetAlertX
# Open Source Network Guard / WIFI & LAN intrusion detector
# Open Source Network Guard / WIFI & LAN intrusion detector
#
# app.css - Front module. CSS styles
#-------------------------------------------------------------------------------
@@ -36,7 +36,7 @@ a[target="_blank"] {
display: inline-block; /* Needed for positioning */
padding-right: 0.6em; /* Space for the icon */
}
a[target="_blank"]::after {
content: '↗';
position: absolute;
@@ -55,7 +55,7 @@ a[target="_blank"] {
right: -7px;
top: 1px;
} */
/* .select2-container--default .select2-selection--multiple .select2-selection__choice
{
padding-right: 15px !important;
@@ -70,6 +70,11 @@ a[target="_blank"] {
opacity: 1;
}
[data-is-valid="0"] {
/* border: 1px solid red; */
background-color: #ff4b4b;
}
/* -----------------------------------------------------------------------------
Helper Classes
----------------------------------------------------------------------------- */
@@ -100,7 +105,7 @@ a[target="_blank"] {
background-color: black;
font-family: 'Courier New', monospace;
font-size: .85em;
}
.logs-row textarea
{
@@ -110,12 +115,12 @@ a[target="_blank"] {
display:contents;
position: relative;
padding: 0.4em
}
#tab_Logging .actions .toggle{
margin: 0.5em;
margin: 0.5em;
height: 3em;
}
@@ -134,8 +139,8 @@ a[target="_blank"] {
}
.log-area
{
padding: 3px;
width:100%;
padding: 3px;
width:100%;
border-bottom-width: 1px;
border-bottom-style: solid;
border-color: #606060;
@@ -246,7 +251,7 @@ a[target="_blank"] {
{
padding:8px;
color: white;
}
}
.header-status
{
@@ -262,7 +267,7 @@ a[target="_blank"] {
position: absolute;
top: 3px;
margin-left: 15px;
display: none;
display: none;
}
@@ -298,9 +303,9 @@ body
.NetAlertX-logo
{
border-color:transparent !important;
height: 50px !important;
width: 50px !important;
border-color:transparent !important;
height: 50px !important;
width: 50px !important;
margin-top:15px !important;
border-radius: 1px !important;
}
@@ -327,7 +332,7 @@ body
.content-wrapper,
.right-side,
.main-footer {
margin-left: 150px;
margin-left: 150px;
}
@@ -740,7 +745,7 @@ body
text-decoration: underline;
}
#ticker-message
#ticker-message
{
color:#FFFFFF;
}
@@ -774,7 +779,7 @@ body
.file-checking .icon-wrap{
width: 200px;
overflow: hidden;
text-overflow: ellipsis;
text-overflow: ellipsis;
display: block;
}
@@ -788,7 +793,7 @@ body
.file-checking .file-name-wrap{
overflow: hidden;
text-overflow: ellipsis;
text-overflow: ellipsis;
display: flex;
padding: 5px;
}
@@ -796,7 +801,7 @@ body
.file-checking{
display: block;
overflow: hidden;
text-overflow: ellipsis;
text-overflow: ellipsis;
}
@@ -854,16 +859,16 @@ body
.db_tools_table_cell_a {
display: table-cell;
text-align: center;
padding: 10px;
min-width: 180px;
width: 20%;
text-align: center;
padding: 10px;
min-width: 180px;
width: 20%;
vertical-align: middle;
}
.db_tools_table_cell_b {
display: table-cell;
text-align: justify;
font-size: 16px;
text-align: justify;
font-size: 16px;
vertical-align: middle;
padding: 10px;
}
@@ -876,12 +881,12 @@ height: 50px;
}
.nav-tabs-custom .tab-content {
background-color: white;
background-color: white;
}
@media (max-width: 767px) {
.nav-tabs-custom .tab-content {
.nav-tabs-custom .tab-content {
overflow: scroll;
}
}
@@ -898,7 +903,7 @@ height: 50px;
font-size: 16px !important;
}
.deviceSelector
.deviceSelector
{
display: block;
}
@@ -935,7 +940,7 @@ height: 50px;
height: 10px;
display: inline-block;
/* background: #fff; */
opacity: .75;
opacity: .75;
}
/* --------------------------------------------------------- */
@@ -979,32 +984,32 @@ height: 50px;
}
/* .setting_input{
width:70%;
}
.setting_name
{
width:30%;
width:30%;
} */
}
@media (min-width: 768px) {
.setting_description {
.setting_description {
/* color: green; */
display: block;
}
/* .setting_input{
width:40%;
width:40%;
}
.setting_name
{
width:19%;
width:19%;
} */
}
/* Hide unusable buttons on the settings page for the NEWDEV plugin*/
#settingsPage #add_option_NEWDEV_devGroup,
#settingsPage #add_option_NEWDEV_devLocation,
#settingsPage #add_option_NEWDEV_devGroup,
#settingsPage #add_option_NEWDEV_devLocation,
#settingsPage #add_option_NEWDEV_devOwner,
#settingsPage #copy_icons_NEWDEV_devIcon,
#settingsPage #add_icon_NEWDEV_devIcon,
@@ -1024,11 +1029,11 @@ height: 50px;
#settingsPage .small-box .inner .card-title {
overflow: hidden;
text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
color: white;
}
.settingswrap
{
@@ -1048,13 +1053,13 @@ height: 50px;
.padding-bottom
{
padding-bottom: 100px;
}
}
.settings-group
{
{
font-size: 20px;
padding-top: 7px;
padding-bottom: 9px;
padding-bottom: 9px;
}
.overview-section .small-box .icon
@@ -1069,7 +1074,7 @@ height: 50px;
}
.overview-group
{
{
font-size: 20px;
padding-top: 7px;
padding-bottom: 9px;
@@ -1082,8 +1087,8 @@ height: 50px;
}
#settingsPage .table_row {
padding: 3px;
#settingsPage .table_row {
padding: 3px;
/* width:100%; */
/* display: flex; */
border-bottom-width: 1px;
@@ -1102,7 +1107,7 @@ height: 50px;
.setting_name
{
/* width:19%; */
font-weight: 300;
font-weight: 300;
}
@@ -1111,24 +1116,24 @@ height: 50px;
display:none !important;
}
.center
.center
{
margin: 0;
position: relative;
position: relative;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.top-margin
.top-margin
{
margin-top: 50px;
}
/* Settings */
#settingsPage .overview-setting-value{
display:unset;
#settingsPage .overview-setting-value{
display:unset;
}
@@ -1165,7 +1170,7 @@ height: 50px;
}
.text-overflow-hidden
{
{
overflow: hidden;
text-overflow: clip;
}
@@ -1175,9 +1180,9 @@ height: 50px;
padding: 10px;
/* background-color: #272c30; */
margin: 10px;
}
#settingsPage .panel-heading:hover{
#settingsPage .panel-heading:hover{
background-color: #272c30;
}
@@ -1185,12 +1190,12 @@ height: 50px;
font-size: medium;
/* background-color: #272c30; */
margin: 10px;
}
.settings_content input[type=checkbox]
{
width: auto
.settings_content input[type=checkbox]
{
width: auto
}
.override{
@@ -1212,7 +1217,7 @@ height: 50px;
input[readonly] {
/* Apply styles to the readonly input */
background-color: #646566 !important;
color: #e6e6e6;
color: #e6e6e6;
cursor: not-allowed;
}
@@ -1300,7 +1305,7 @@ input[readonly] {
/* margin-bottom:20px; */
}
#settingsPage .select2-selection
#settingsPage .select2-selection
{
width: initial;
display: inline-block;
@@ -1314,8 +1319,8 @@ input[readonly] {
#settingsPage .select2-selection
{
background-color: rgb(96, 96, 96);
}
#settingsPage .select2-container
}
#settingsPage .select2-container
{
width: 100% !important;
}
@@ -1398,7 +1403,7 @@ input[readonly] {
backdrop-filter: brightness(50%);
}
.iconPreviewSelector
.iconPreviewSelector
{
text-align: center;
padding: 15px;
@@ -1440,7 +1445,7 @@ input[readonly] {
}
.dummyDevice
.dummyDevice
{
text-align: end;
}
@@ -1461,7 +1466,7 @@ input[readonly] {
}
.info-icon-nav
{
{
top: -6px;
position: absolute;
z-index: 1;
@@ -1538,7 +1543,7 @@ input[readonly] {
}
#panDetails .input-group {
min-height: 40px;
}
@@ -1583,7 +1588,7 @@ input[readonly] {
}
.devicePropAction
{
{
width: 1.2em;
height: 1.2em;
display: inline-block;
@@ -1593,11 +1598,11 @@ input[readonly] {
}
.devicePropAction:hover
{
{
font-size: larger;
padding: 0em;
margin: 0em;
}
@@ -1607,7 +1612,7 @@ input[readonly] {
display: block;
float:inline-end;
height: 2em;
}
}
#panDetails .dataTables_wrapper .bottom .dataTables_info
{
@@ -1636,22 +1641,22 @@ input[readonly] {
height: 14px;
}
#deviceDetailsEdit .select2-container--default .select2-selection--multiple .select2-selection__choice
#deviceDetailsEdit .select2-container--default .select2-selection--multiple .select2-selection__choice
{
height: 20px;
}
#deviceDetailsEdit .select2-container--disabled
#deviceDetailsEdit .select2-container--disabled
{
background-color: #606060;
background-color: #606060;
}
#deviceDetailsEdit .select2-container--default .select2-selection--multiple .select2-selection__choice span
#deviceDetailsEdit .select2-container--default .select2-selection--multiple .select2-selection__choice span
{
font-size: 14px;
}
#deviceDetailsEdit .select2-selection
#deviceDetailsEdit .select2-selection
{
width: initial;
display: inline-block;
@@ -1681,7 +1686,7 @@ input[readonly] {
font-size: 14px;
}
.custom-badge
{
{
border: 1px solid #aaa;
border-radius: 4px;
border-style: solid;
@@ -1716,7 +1721,7 @@ input[readonly] {
}
#deviceDetailsEdit .select2-container
#deviceDetailsEdit .select2-container
{
width: 100% !important;
}
@@ -1799,7 +1804,7 @@ input[readonly] {
z-index: 5;
}
#networkTree .netNodeText
{
{
position: absolute;
}
#networkTree .netPort
@@ -1812,7 +1817,7 @@ input[readonly] {
#networkTree .portBckgIcon
{
opacity: 0.3;
display: initial;
display: initial;
float: left;
width: 1em;
}
@@ -1822,7 +1827,7 @@ input[readonly] {
margin-left: 16px;
/* border: solid;
border-color:#606060; */
position: relative;
position: relative;
}
#networkTree .netIcon
{
@@ -1850,8 +1855,8 @@ input[readonly] {
}
#hover-box .devName
{
font-size: larger;
{
font-size: larger;
display: contents;
}
@@ -1910,7 +1915,7 @@ input[readonly] {
#networkTree .highlightedNode
{
/* border: solid; */
border-color:var(--color-lightblue);
border-color:var(--color-lightblue);
box-shadow: var(--color-lightblue) 0px 0px 20px;
}
@@ -1968,7 +1973,7 @@ input[readonly] {
}
.sort-btn {
right: 5px;
top: 50%;
transform: translateY(-50%);
@@ -2020,7 +2025,7 @@ input[readonly] {
}
.plugin-filters
{
{
margin: 7px;
margin-right: 7px;
margin-bottom: 9px;
@@ -2054,7 +2059,7 @@ input[readonly] {
}
.plugin-content #tabs-content-location
{
{
margin: 0px;
padding-top: 0;
}
@@ -2066,7 +2071,7 @@ input[readonly] {
}
.plugin-content .tab-content
{
{
padding-top: 10px;
}
@@ -2103,7 +2108,7 @@ input[readonly] {
@media (max-width: 500px) {
.header-server-time {
display: none;
display: none;
}
}
@@ -2234,12 +2239,12 @@ input[readonly] {
display: grid;
}
#workflowContainerWrap .panel-collapse
#workflowContainerWrap .panel-collapse
{
padding: 5px;
}
.workflows
.workflows
{
max-width: 800px;
}
@@ -2285,7 +2290,7 @@ input[readonly] {
color: #000;
}
.workflows .button-container
.workflows .button-container
{
/* display: contents; */
text-align: center;
@@ -2305,7 +2310,7 @@ input[readonly] {
margin: 5px;
}
.workflows .button-container
.workflows .button-container
{
padding-right: 0px !important;
padding-left: 0px !important;
@@ -2318,19 +2323,19 @@ input[readonly] {
/* .button-container button
{
width:100%;
width:100%;
} */
.red-hover-text:hover
{
color: var(--color-red) !important;
color: var(--color-red) !important;
}
.green-hover-text:hover
{
color: var(--color-green) !important;
}
.workflows .bckg-icon-1-line
{
font-size: 3em;
@@ -2362,7 +2367,7 @@ input[readonly] {
z-index: 1;
}
.workflows .workflow-card
.workflows .workflow-card
{
display: block;
}
@@ -2372,7 +2377,7 @@ input[readonly] {
padding: 10px;
}
.workflow-card, .actions-list
.workflow-card, .actions-list
{
display: contents;
padding: 5px;
@@ -2384,7 +2389,7 @@ input[readonly] {
z-index:1;
}
.condition
.condition
{
padding: 5px;
padding-left: 10px;

View File

@@ -96,7 +96,7 @@ function showModalInput(
btnOK = getString("Gen_Okay"),
callbackFunction = null,
triggeredBy = null,
defaultValue = ""
defaultValue = ""
) {
prefix = "modal-input";
@@ -121,7 +121,7 @@ function showModalInput(
setTimeout(function () {
$(`#${prefix}-textarea`).focus();
}, 500);
}
// -----------------------------------------------------------------------------
@@ -143,7 +143,7 @@ function showModalFieldInput(
$(`#${prefix}-OK`).html(btnOK);
if (callbackFunction != null) {
modalCallbackFunction = callbackFunction;
}
@@ -181,11 +181,11 @@ function showModalPopupForm(
$(`#${prefix}-cancel`).html(btnCancel);
$(`#${prefix}-OK`).html(btnOK);
// if curValue not null
// if curValue not null
if (curValue)
{
initialValues = JSON.parse(atob(curValue));
initialValues = JSON.parse(atob(curValue));
}
outputHtml = "";
@@ -193,7 +193,7 @@ function showModalPopupForm(
if (Array.isArray(popupFormJson)) {
popupFormJson.forEach((field, index) => {
// You'll need to define these or map them from `field`
const setKey = field.function || `field_${index}`;
const setKey = field.function || `field_${index}`;
const setName = getString(`${parentSettingKey}_popupform_${setKey}_name`);
const labelClasses = "col-sm-2"; // example, or from your obj.labelClasses
const inputClasses = "col-sm-10"; // example, or from your obj.inputClasses
@@ -207,9 +207,9 @@ function showModalPopupForm(
}
}
const fieldOptionsOverride = field.type?.elements[0]?.elementOptions || [];
const fieldOptionsOverride = field.type?.elements[0]?.elementOptions || [];
const setValue = initialValue;
const setType = JSON.stringify(field.type);
const setType = JSON.stringify(field.type);
const setEvents = field.events || []; // default to empty array if missing
const setObj = { setKey, setValue, setType, setEvents };
@@ -218,17 +218,17 @@ function showModalPopupForm(
<div class="form-group col-xs-12">
<label id="${setKey}_label" class="${labelClasses}"> ${setName}
<i my-set-key="${parentSettingKey}_popupform_${setKey}"
title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer helpIconSmallTopRight"
title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer helpIconSmallTopRight"
onclick="showDescriptionPopup(this)">
</i>
</label>
<div class="${inputClasses}">
${generateFormHtml(
null, // settingsData only required for datatables
setObj,
null,
fieldOptionsOverride,
setObj,
null,
fieldOptionsOverride,
null
)}
</div>
@@ -239,7 +239,7 @@ function showModalPopupForm(
outputHtml += inputFormHtml;
});
}
$(`#modal-form-plc`).html(outputHtml);
// Bind OK button click event
@@ -247,12 +247,19 @@ function showModalPopupForm(
let settingsArray = [];
if (Array.isArray(popupFormJson)) {
popupFormJson.forEach(field => {
collectSetting(
const result = collectSetting(
`${parentSettingKey}_popupform`, // prefix
field.function, // setCodeName
field.type, // setType (object)
settingsArray
);
settingsArray = result.settingsArray;
if (!result.dataIsValid) {
msg = getString("Gen_Invalid_Value") + ":" + result.failedSettingKey;
console.error(msg);
showModalOk("ERROR", msg);
}
});
}
@@ -276,7 +283,7 @@ function showModalPopupForm(
const newOption = $("<option class='interactable-option'></option>")
.attr("value", encodedValue)
.text(label);
$("#" + selectId).append(newOption);
initListInteractionOptions(newOption);
}
@@ -429,10 +436,10 @@ function safeDecodeURIComponent(content) {
return content; // Return the original content if decoding fails
}
}
// -----------------------------------------------------------------------------
// Backend notification Polling
// Backend notification Polling
// -----------------------------------------------------------------------------
// Function to check for notifications
function checkNotification() {
@@ -440,7 +447,7 @@ function checkNotification() {
const phpEndpoint = 'php/server/utilNotification.php';
$.ajax({
url: notificationEndpoint,
url: notificationEndpoint,
type: 'GET',
success: function(response) {
// console.log(response);
@@ -492,7 +499,7 @@ function checkNotification() {
},
error: function() {
console.warn(`🟥 Error checking ${notificationEndpoint}`)
}
});
}
@@ -582,7 +589,7 @@ const phpEndpoint = 'php/server/utilNotification.php';
// --------------------------------------------------
// Write a notification
function write_notification(content, level) {
function write_notification(content, level) {
$.ajax({
url: phpEndpoint, // Change this to the path of your PHP script
@@ -603,8 +610,8 @@ function write_notification(content, level) {
// --------------------------------------------------
// Write a notification
function markNotificationAsRead(guid) {
function markNotificationAsRead(guid) {
$.ajax({
url: phpEndpoint,
type: 'GET',
@@ -628,8 +635,8 @@ function markNotificationAsRead(guid) {
// --------------------------------------------------
// Remove a notification
function removeNotification(guid) {
function removeNotification(guid) {
$.ajax({
url: phpEndpoint,
type: 'GET',

View File

@@ -71,7 +71,7 @@ function getPluginConfig(pluginsData, prefix) {
// Show the description of a setting
function showDescriptionPopup(e) {
console.log($(e).attr("my-set-key"));
console.log($(e).attr("my-set-key"));
showModalOK("Info", getString($(e).attr("my-set-key") + '_description'))
}
@@ -92,13 +92,13 @@ function pluginCards(prefixesOfEnabledPlugins, includeSettings) {
prefix + "_" + set
}">
<code>${getSetting(prefix + "_" + set)}</code>
</div>
</div>
</a>
</div>
`;
});
html += `
html += `
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px">
<div class="small-box bg-green col-sm-12 " >
<div class="inner col-sm-12">
@@ -110,10 +110,10 @@ function pluginCards(prefixesOfEnabledPlugins, includeSettings) {
${includeSettings_html}
</div>
<a href="#${prefix}_header" onclick="toggleAllSettings('open')">
<div class="icon"> ${getString(prefix + "_icon")} </div>
</a>
<div class="icon"> ${getString(prefix + "_icon")} </div>
</a>
</div>
</div>
`;
});
@@ -251,17 +251,17 @@ function settingsCollectedCorrectly(settingsArray, settingsJSON_DB) {
function cloneDataTableRow(el){
console.log(el);
const id = "NEWDEV_devCustomProps_table"; // Your table ID
const table = $('#'+id).DataTable();
// Get the 'my-index' attribute from the closest tr element
const myIndex = parseInt($(el).closest("tr").attr("my-index"));
// Find the row in the table with the matching 'my-index'
const row = table.rows().nodes().to$().filter(`[my-index="${myIndex}"]`).first().get(0);
// Clone the row (including its data and controls)
let clonedRow = $(row).clone(true, true); // The true arguments copy the data and event handlers
@@ -270,7 +270,7 @@ function cloneDataTableRow(el){
console.log(clonedRow);
// Add the cloned row to the DataTable
table.row.add(clonedRow[0]).draw();
@@ -291,13 +291,13 @@ function removeDataTableRow(el) {
// Find the row in the table with the matching 'my-index'
const row = table.rows().nodes().to$().filter(`[my-index="${myIndex}"]`).first().get(0);
// Remove the row from the DataTable
table.row(row).remove().draw();
}
else
{
showMessage (getString("CustProps_cant_remove"), 3000, "modal_red");
showMessage (getString("CustProps_cant_remove"), 3000, "modal_red");
}
}
@@ -308,9 +308,9 @@ function addViaPopupForm(element) {
const toId = $(element).attr("my-input-to");
const curValue = $(`#${toId}`).val();
const parsed = JSON.parse(atob($(`#${toId}`).data("elementoptionsbase64")));
const parsed = JSON.parse(atob($(`#${toId}`).data("elementoptionsbase64")));
const popupFormJson = parsed.find(obj => "popupForm" in obj)?.popupForm ?? null;
console.log(`toId | curValue: ${toId} | ${curValue}`);
showModalPopupForm(
@@ -393,7 +393,7 @@ function selectAll(element) {
settingsChanged();
var selectElement = $(`#${$(element).attr("my-input-to")}`);
// Iterate over each option within the select element
selectElement.find('option').each(function() {
// Mark each option as selected
@@ -409,13 +409,13 @@ function selectAll(element) {
function unselectAll(element) {
settingsChanged();
var selectElement = $(`#${$(element).attr("my-input-to")}`);
// Iterate over each option within the select element
selectElement.find('option').each(function() {
// Unselect each option
$(this).prop('selected', false);
});
// Trigger the 'change' event to notify Bootstrap Select of the changes
selectElement.trigger('change');
}
@@ -426,7 +426,7 @@ function selectChange(element) {
settingsChanged();
var selectElement = $(`#${$(element).attr("my-input-to")}`);
selectElement.parent().find("input").focus().click();
}
@@ -464,9 +464,9 @@ function initListInteractionOptions(element) {
// Parent has my-transformers="name|base64"
const toId = $parent.attr("id");
const curValue = $option.val();
const parsed = JSON.parse(atob($parent.data("elementoptionsbase64")));
const parsed = JSON.parse(atob($parent.data("elementoptionsbase64")));
const popupFormJson = parsed.find(obj => "popupForm" in obj)?.popupForm ?? null;
showModalPopupForm(
`<i class="fa fa-pen-to-square"></i> ${getString("Gen_Update_Value")}`, // title
"", // message
@@ -515,8 +515,8 @@ function filterRows(inputText) {
var $panelHeader = $panel.find('.panel-heading');
var $panelBody = $panel.find('.panel-collapse');
$panel.show()
$panelHeader.show()
$panel.show()
$panelHeader.show()
$panelBody.collapse('show');
$panelBody.find(".table_row:not(.docs)").each(function () {
@@ -525,11 +525,11 @@ function filterRows(inputText) {
var isMetadataRow = rowId && rowId.endsWith("__metadata");
if (!isMetadataRow) {
$row.show()
}
}
});
});
} else{
// filter
@@ -537,25 +537,25 @@ function filterRows(inputText) {
var $panel = $(this);
var $panelHeader = $panel.find('.panel-heading');
var $panelBody = $panel.find('.panel-collapse');
var anyVisible = false; // Flag to check if any row is visible
$panelBody.find(".table_row:not(.docs)").each(function () {
var $row = $(this);
// Check if the row ID ends with "__metadata"
var rowId = $row.attr("id");
var isMetadataRow = rowId && rowId.endsWith("__metadata");
// Always hide metadata rows
if (isMetadataRow) {
$row.hide();
return; // Skip further processing for metadata rows
}
var description = $row.find(".setting_description").text().toLowerCase();
var setKey = $row.find(".setting_name code").text().toLowerCase();
if (
description.includes(inputText.toLowerCase()) ||
setKey.includes(inputText.toLowerCase())
@@ -566,7 +566,7 @@ function filterRows(inputText) {
$row.hide();
}
});
// Determine whether to hide or show the panel based on visibility of rows
if (anyVisible) {
$panelBody.collapse('show'); // Ensure the panel body is shown if there are visible rows
@@ -582,7 +582,7 @@ function filterRows(inputText) {
}
}
@@ -661,7 +661,7 @@ function generateOptionsOrSetOptions(
processDataCallback, // Callback function to generate entries based on options
targetField, // Target field or element where selected value should be applied or updated
transformers = [], // Transformers to be applied to the values
overrideOptions = null // override options if available
overrideOptions = null // override options if available
) {
// console.log(setKey);
@@ -712,7 +712,7 @@ function applyTransformers(val, transformers) {
break;
case "getString":
// no change
val = val;
val = val;
break;
default:
console.warn(`Unknown transformer: ${transformer}`);
@@ -745,13 +745,13 @@ function reverseTransformers(val, transformers) {
break;
case "getString":
// retrieve string
val = getString(val);
val = getString(val);
break;
case "deviceChip":
mac = val // value is mac
mac = val // value is mac
val = `${getDevDataByMac(mac, "devName")}`
break;
case "deviceRelType":
case "deviceRelType":
val = val; // nothing to do
break;
default:
@@ -779,10 +779,11 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
let getStringKey = "";
let onClick = "console.log('onClick - Not implemented');";
let onChange = "console.log('onChange - Not implemented');";
let focusout = "console.log('focusout - Not implemented');";
let customParams = "";
let customId = "";
let columns = [];
let base64Regex = "";
let base64Regex = "";
let elementOptionsBase64 = btoa(JSON.stringify(elementOptions));
elementOptions.forEach((option) => {
@@ -830,6 +831,9 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
if (option.onChange) {
onChange = option.onChange;
}
if (option.focusout) {
focusout = option.focusout;
}
if (option.customParams) {
customParams = option.customParams;
}
@@ -867,7 +871,8 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
customId,
columns,
base64Regex,
elementOptionsBase64
elementOptionsBase64,
focusout
};
};
@@ -877,7 +882,7 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
// -----------------------------------------------------------------------------
// --------------------------------------------------
// Creates an object from an array
// Creates an object from an array
function arrayToObject(array) {
const obj = [];
array.forEach((item, index) => {
@@ -895,18 +900,18 @@ function generateOptions(options, valuesArray, targetField, transformers, placeh
resultArray = []
selectedArray = []
cssClass = ""
cssClass = ""
// determine if options or values are used in the listing
if (valuesArray.length > 0 && options.length > 0){
// multiselect list -> options only + selected the ones in valuesArray
// multiselect list -> options only + selected the ones in valuesArray
resultArray = options;
selectedArray = valuesArray
} else if (valuesArray.length > 0 && options.length == 0){
// editable list -> values only
// editable list -> values only
resultArray = arrayToObject(valuesArray)
cssClass = "interactable-option" // generates [1x 📝 | 2x 🚮]
} else if (options.length > 0){
@@ -914,7 +919,7 @@ function generateOptions(options, valuesArray, targetField, transformers, placeh
// dropdown -> options only (value == 1 STRING not ARRAY)
resultArray = options;
}
// Create a map to track the index of each item in valuesArray
const orderMap = new Map(valuesArray.map((item, index) => [item, index]));
@@ -961,7 +966,7 @@ function generateList(options, valuesArray, targetField, transformers, placehold
listHtml += `<li ${selected}>${labelName}</li>`;
});
// Place the resulting HTML into the specified placeholder div
$("#" + placeholder).replaceWith(listHtml);
}
@@ -972,7 +977,7 @@ function genListWithInputSet(options, valuesArray, targetField, transformers, pl
var listHtml = "";
options.forEach(function(item) {
let selected = valuesArray.includes(item.id) ? 'selected' : '';
@@ -988,9 +993,9 @@ function genListWithInputSet(options, valuesArray, targetField, transformers, pl
}
listHtml += `<li ${selected}>
<a href="javascript:void(0)" onclick="setTextValue('${targetField}','${item.id}')">${labelName}</a>
<a href="javascript:void(0)" onclick="setTextValue('${targetField}','${item.id}')">${labelName}</a>
</li>`;
});
// Place the resulting HTML into the specified placeholder div
@@ -1001,8 +1006,8 @@ function genListWithInputSet(options, valuesArray, targetField, transformers, pl
// Collects a setting based on code name
function collectSetting(prefix, setCodeName, setType, settingsArray) {
// Parse setType if it's a JSON string
const setTypeObject = (typeof setType === "string")
? JSON.parse(processQuotes(setType))
const setTypeObject = (typeof setType === "string")
? JSON.parse(processQuotes(setType))
: setType;
const dataType = setTypeObject.dataType;
@@ -1015,6 +1020,20 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
// Check if validation failed
if (
$(`#${setCodeName}`)
&& $(`#${setCodeName}`).attr("data-is-valid")
&& $(`#${setCodeName}`).attr("data-is-valid") == 0
)
{
return {
"settingsArray": settingsArray,
"dataIsValid": false,
"failedSettingKey": setCodeName
};
}
const opts = handleElementOptions('none', elementOptions, transformers, val = "");
// Map of handlers
@@ -1038,7 +1057,7 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
let temps = [];
if (opts.isOrdeable) {
temps = $(`#${setCodeName}`).val();
} else {
} else {
const sel = $(`#${setCodeName}`).attr("my-editable") === "true" ? "" : ":selected";
$(`#${setCodeName} option${sel}`).each(function() {
const vl = $(this).val();
@@ -1066,7 +1085,7 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
let handlerKey;
if (dataType === "string" && elementType === "datatable") {
handlerKey = "datatableString";
} else if (dataType === "string" ||
} else if (dataType === "string" ||
(dataType === "integer" && (opts.inputType === "number" || opts.inputType === "text"))) {
handlerKey = "simpleValue";
} else if (opts.inputType === "checkbox") {
@@ -1084,7 +1103,11 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
const value = handlers[handlerKey]();
settingsArray.push([prefix, setCodeName, dataType, value]);
return settingsArray;
return {
"settingsArray": settingsArray,
"dataIsValid": true,
"failedSettingKey": ""
};
}
@@ -1093,22 +1116,22 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
function generateFormHtml(settingsData, set, overrideValue, overrideOptions, originalSetKey) {
let inputHtml = '';
isEmpty(overrideValue) ? inVal = set['setValue'] : inVal = overrideValue;
isEmpty(overrideValue) ? inVal = set['setValue'] : inVal = overrideValue;
const setKey = set['setKey'];
const setType = set['setType'];
// if (setKey == '') {
// console.log(setType);
// console.log(setKey);
// console.log(overrideValue);
// console.log(inVal);
// console.log(inVal);
// }
// Parse the setType JSON string
// console.log(processQuotes(setType));
const setTypeObject = JSON.parse(processQuotes(setType))
const dataType = setTypeObject.dataType;
const elements = setTypeObject.elements || [];
@@ -1137,20 +1160,21 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
customId,
columns,
base64Regex,
elementOptionsBase64
elementOptionsBase64,
focusout
} = handleElementOptions(setKey, elementOptions, transformers, inVal);
// Override value
let val = valRes;
// if (setKey == '') {
// console.log(setType);
// console.log(setKey);
// console.log(overrideValue);
// console.log(inVal);
// console.log(val);
// console.log(inVal);
// console.log(val);
// }
// Generate HTML based on elementType
@@ -1159,16 +1183,17 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
const multi = isMultiSelect ? "multiple" : "";
const addCss = isOrdeable ? "select2 select2-hidden-accessible" : "";
inputHtml += `<select onChange="settingsChanged();${onChange}"
my-data-type="${dataType}"
my-editable="${editable}"
class="form-control ${addCss} ${cssClasses}"
name="${setKey}"
id="${setKey}"
inputHtml += `<select onChange="settingsChanged();${onChange}"
onfocusout="${focusout}"
my-data-type="${dataType}"
my-editable="${editable}"
class="form-control ${addCss} ${cssClasses}"
name="${setKey}"
id="${setKey}"
my-transformers=${transformers}
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
data-elementoptionsbase64="${elementOptionsBase64}"
${multi}
${readOnly ? "disabled" : ""}>
@@ -1182,31 +1207,32 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
const checked = val === 'True' || val === 'true' || val === '1' ? 'checked' : '';
const inputClass = inputType === 'checkbox' ? 'checkbox' : 'form-control';
inputHtml += `<input
class="${inputClass} ${cssClasses}"
onChange="settingsChanged();${onChange}"
my-data-type="${dataType}"
my-customparams="${customParams}"
my-customid="${customId}"
inputHtml += `<input
class="${inputClass} ${cssClasses}"
onChange="settingsChanged();${onChange}"
onfocusout="${focusout}"
my-data-type="${dataType}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
my-base64Regex="${base64Regex}"
id="${setKey}${suffix}"
type="${inputType}"
value="${val}"
id="${setKey}${suffix}"
type="${inputType}"
value="${val}"
${readOnly}
${checked}
placeholder="${placeholder}"
placeholder="${placeholder}"
/>`;
break;
case 'button':
inputHtml += `<button
class="btn btn-primary ${cssClasses}"
my-customparams="${customParams}"
my-customid="${customId}"
inputHtml += `<button
class="btn btn-primary ${cssClasses}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
my-input-from="${sourceIds}"
my-input-to="${setKey}"
my-input-from="${sourceIds}"
my-input-to="${setKey}"
data-elementoptionsbase64="${elementOptionsBase64}"
onclick="${onClick}">
${getString(getStringKey)}
@@ -1214,21 +1240,23 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
break;
case 'textarea':
inputHtml += `<textarea
class="form-control input"
my-customparams="${customParams}"
my-customid="${customId}"
inputHtml += `<textarea
class="form-control input"
onChange="settingsChanged();${onChange}"
onfocusout="${focusout}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
my-data-type="${dataType}"
id="${setKey}"
my-data-type="${dataType}"
id="${setKey}"
${readOnly}>${val}</textarea>`;
break;
case 'span':
inputHtml += `<span
class="${cssClasses}"
my-data-type="${dataType}"
my-customparams="${customParams}"
inputHtml += `<span
class="${cssClasses}"
my-data-type="${dataType}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
onclick="${onClick}">
@@ -1264,13 +1292,13 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
columnSetting["setOptions"] = getSetting(column.optionsOverride.replace("setting.",""));
} else {
columnSetting["setOptions"] = column.optionsOverride;
}
}
}
columnSettings.push(columnSetting)
// helper for if val is empty
emptyVal.push('');
emptyVal.push('');
});
datatableHtml += '</tr></thead>';
@@ -1290,7 +1318,7 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
let index = 0;
val.forEach(rowData => {
datatableHtml += `<tr my-index="${index}">`;
let j = 0;
columnSettings.forEach(set => {
// Extract the value for the current column based on the new structure
@@ -1300,11 +1328,11 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
{
columnOverrideValue = ""
}
// Create unique key to prevent dropdown data duplication
const oldKey = set["setKey"];
set["setKey"] = oldKey + "_" + index;
// Generate the cell HTML using the extracted value
const cellHtml = generateFormHtml(
settingsData,
@@ -1314,17 +1342,17 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
oldKey
);
datatableHtml += `<td> <div class="input-group"> ${cellHtml} </div></td>`;
// Restore the original key
set["setKey"] = oldKey;
j++;
});
datatableHtml += '</tr>';
index++;
});
datatableHtml += '</tbody></table>';
inputHtml += datatableHtml;
@@ -1347,8 +1375,8 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
// Generate event HTML if applicable
let eventsHtml = '';
const eventsList = createArray(set['setEvents']);
const eventsList = createArray(set['setEvents']);
// inline buttons events
if (eventsList.length > 0) {
eventsList.forEach(event => {
@@ -1387,7 +1415,7 @@ if (eventsList.length > 0) {
data-myparam-setkey="${setKey}"
data-myparam="${setKey}"
data-myparam-plugin="${setKey.split('_')[0] || ''}"
data-myevent="${event}"
data-myevent="${event}"
onclick="execute_settingEvent(this)">
<i title="${getString(event + "_event_tooltip")}" class="fa ${eventIcon}"></i>
</span>`;
@@ -1406,15 +1434,15 @@ function getSetObject(settingsData, setKey) {
result = ""
settingsData.forEach(function(set) {
if (set.setKey == setKey) {
// console.log(set);
// console.log(set);
result = set;
return;
}
}
});
if(result == "")
@@ -1439,7 +1467,7 @@ function collectTableData(tableSelector) {
cells.each((index, cell) => {
const input = $(cell).find('input, select, textarea');
if (input.length) {
if (input.attr('type') === 'checkbox') {
// For checkboxes, check if they are checked
@@ -1455,10 +1483,10 @@ function collectTableData(tableSelector) {
}
});
tableData.push(rowData);
tableData.push(rowData);
});
return tableData;
return tableData;
}

View File

@@ -1,6 +1,6 @@
/* -----------------------------------------------------------------------------
* NetAlertX
* Open Source Network Guard / WIFI & LAN intrusion detector
* Open Source Network Guard / WIFI & LAN intrusion detector
*
* ui_components.js - Front module. Common UI components
*-------------------------------------------------------------------------------
@@ -56,7 +56,7 @@ function getRandomBytes(elem, length) {
window.crypto.getRandomValues(array);
// Convert bytes to hexadecimal string
let hexString = Array.from(array, byte =>
let hexString = Array.from(array, byte =>
byte.toString(16).padStart(2, '0')
).join('');
@@ -71,7 +71,7 @@ function getRandomBytes(elem, length) {
}
// ----------------------------------------------
// Updates the icon preview
// Updates the icon preview
function updateAllIconPreviews() {
$(".iconInputVal").each((index, el)=>{
updateIconPreview(el)
@@ -79,7 +79,7 @@ function updateAllIconPreviews() {
}
// ----------------------------------------------
// Updates the icon preview
// Updates the icon preview
function updateIconPreview(elem) {
const previewSpan = $(elem).parent().find(".iconPreview");
@@ -97,7 +97,7 @@ function updateIconPreview(elem) {
previewSpan.html(atob(newValue));
});
return; // Stop retrying if successful
}
}
attempts++;
if (attempts < 10) {
@@ -119,9 +119,9 @@ function validateRegex(elem) {
const iconSpan = $(elem).parent().find(".validityCheck");
const inputElem = $(elem);
const regexTmp = atob($(inputElem).attr("my-base64Regex")); // Decode base64 regex
const regex = new RegExp(regexTmp); // Convert to a valid RegExp object
let attempts = 0;
function tryUpdateValidityResultIcon() {
@@ -140,8 +140,11 @@ function validateRegex(elem) {
// Validate against regex
if (regex.test(value)) {
iconSpan.html("<i class='fa fa-check'></i>");
inputElem.attr("data-is-valid", "1");
} else {
iconSpan.html("<i class='fa fa-xmark'></i>");
showModalOk('WARNING', getString("Gen_Invalid_Value"));
inputElem.attr("data-is-valid", "0");
}
}
@@ -175,7 +178,7 @@ function initializeiCheck () {
increaseArea: '20%'
});
}
@@ -206,7 +209,7 @@ function copyToClipboard(buttonElement) {
}
// -----------------------------------------------------------------------------
// Simple Sortable Table columns
// Simple Sortable Table columns
// -----------------------------------------------------------------------------
// Function to handle column sorting when a user clicks on a table header
@@ -268,9 +271,9 @@ function ipToNum(ip) {
}
// -----------------------------------------------------------------------------
// handling events
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// handling events
// -----------------------------------------------------------------------------
modalEventStatusId = 'modal-message-front-event'
@@ -301,41 +304,41 @@ function execute_settingEvent(element) {
updateModalState()
}
})
} else if (["add_option"].includes(feEvent)) {
showModalFieldInput (
'<i class="fa fa-square-plus pointer"></i> ' + getString('Gen_Add'),
getString('Gen_Add'),
getString('Gen_Cancel'),
getString('Gen_Okay'),
getString('Gen_Cancel'),
getString('Gen_Okay'),
'', // curValue
'addOptionFromModalInput',
feSourceId // triggered by id
);
} else if (["add_icon"].includes(feEvent)) {
// Add new icon as base64 string
// Add new icon as base64 string
showModalInput (
'<i class="fa fa-square-plus pointer"></i> ' + getString('DevDetail_button_AddIcon'),
getString('DevDetail_button_AddIcon_Help'),
getString('Gen_Cancel'),
getString('Gen_Okay'),
getString('Gen_Cancel'),
getString('Gen_Okay'),
() => addIconAsBase64(element), // Wrap in an arrow function
feSourceId // triggered by id
);
} else if (["select_icon"].includes(feEvent)) {
showIconSelection(feSetKey)
// myparam-setkey
// myparam-setkey
} else if (["copy_icons"].includes(feEvent)) {
// Ask overwrite icon types
// Ask overwrite icon types
showModalWarning (
getString('DevDetail_button_OverwriteIcons'),
getString('DevDetail_button_OverwriteIcons'),
getString('DevDetail_button_OverwriteIcons_Warning'),
getString('Gen_Cancel'),
getString('Gen_Okay'),
getString('Gen_Cancel'),
getString('Gen_Okay'),
'overwriteIconType',
feSourceId // triggered by id
);
@@ -343,30 +346,30 @@ function execute_settingEvent(element) {
goToDevice(feValue);
} else if (["go_to_node"].includes(feEvent)) {
goToNetworkNode(feValue);
} else {
console.warn(`🔺Not implemented: ${feEvent}`)
}
}
}
// -----------------------------------------------------------------------------
// Go to the correct network node in the Network section
function overwriteIconType()
{
{
const mac = getMac();
if (!isValidMac(mac)) {
showModalOK("Error", getString("Gen_InvalidMac"))
showModalOK("Error", getString("Gen_InvalidMac"))
return;
}
// Construct SQL query
const rawSql = `
UPDATE Devices
UPDATE Devices
SET devIcon = (
SELECT devIcon FROM Devices WHERE devMac = "${mac}"
)
@@ -391,24 +394,24 @@ function overwriteIconType()
// -----------------------------------------------------------------------------
// Go to the correct network node in the Network section
function goToNetworkNode(mac)
{
{
setCache('activeNetworkTab', mac.replaceAll(":","_")+'_id');
window.location.href = './network.php';
}
// -----------------------------------------------------------------------------
// Go to the device
// Go to the device
function goToDevice(mac, newtab = false) {
const url = './deviceDetails.php?mac=' + encodeURIComponent(mac);
if (newtab) {
window.open(url, '_blank');
} else {
window.location.href = url;
}
}
// --------------------------------------------------------
// Updating the execution queue in in modal pop-up
@@ -437,7 +440,7 @@ function updateModalState() {
function addOptionFromModalInput() {
var inputVal = $(`#modal-field-input-field`).val();
console.log($('#modal-field-input-field'));
var triggeredBy = $('#modal-field-input').attr("data-myparam-triggered-by");
var targetId = $('#' + triggeredBy).attr("data-myparam-setkey");
@@ -475,16 +478,16 @@ function addIconAsBase64 (el) {
console.log($('#modal-field-input-field'));
var triggeredBy = $('#modal-input').attr("data-myparam-triggered-by");
var targetId = $('#' + triggeredBy).attr("data-myparam-setkey");
// $('#'+targetId).val(iconHtmlBase64);
// $('#'+targetId).val(iconHtmlBase64);
// Add new option and set it as selected
$('#' + targetId).append(new Option(iconHtmlBase64, iconHtmlBase64)).val(iconHtmlBase64);
updateIconPreview(el)
updateIconPreview(el)
}
@@ -522,8 +525,8 @@ function showIconSelection(setKey) {
// Populate the icon list
Array.from(selectElement.options).forEach(option => {
if (option.value != "") {
const value = option.value;
// Decode the base64 value
@@ -566,7 +569,7 @@ function showIconSelection(setKey) {
});
//
}
@@ -661,7 +664,7 @@ function getRelationshipConf(relType) {
// --color-red: #dd4b39;
switch (relType) {
case "child":
color = "#f39c12"; // yellow
cssClass = "text-yellow";
@@ -673,11 +676,11 @@ function getRelationshipConf(relType) {
case "virtual":
color = "#0060df"; // blue
cssClass = "text-blue";
break;
break;
case "logical":
color = "#00a65a"; // green
cssClass = "text-green";
break;
break;
default:
color = "#5B5B66"; // grey
cssClass = "text-light-grey";
@@ -703,13 +706,13 @@ function initSelect2() {
// check if cache ready
if(isValidJSON(devicesListAll_JSON))
{
// --------------------------------------------------------
//Initialize Select2 Elements and make them sortable
$(function () {
// Iterate over each Select2 dropdown
$('.select2').each(function() {
$('.select2').each(function() {
// handle Device chips, if my-transformers="deviceChip"
if($(this).attr("my-transformers") == "deviceChip")
{
@@ -721,7 +724,7 @@ function initSelect2() {
return m; // Allow HTML
}
});
} else if($(this).attr("my-transformers") == "deviceRelType") // handling dropdown for relationships
{
var selectEl = $(this).select2({
@@ -730,26 +733,26 @@ function initSelect2() {
if (!data.id) return data.text; // default for placeholder etc.
const relConf = getRelationshipConf(data.text);
// Custom HTML
const html = $(`
<span class="custom-chip ${relConf.cssClass}" >
${data.text}
</span>
const html = $(`
<span class="custom-chip ${relConf.cssClass}" >
${data.text}
</span>
`);
return html;
},
escapeMarkup: function (m) {
return m; // Allow HTML
}
});
} else // default handling - default template
{
var selectEl = $(this).select2();
}
// Apply sortable functionality to the dropdown's dropdown-container
selectEl.next().children().children().children().sortable({
containment: 'parent',
@@ -757,14 +760,14 @@ function initSelect2() {
var sortedValues = $(this).children().map(function() {
return $(this).attr('title');
}).get();
var sortedOptions = selectEl.find('option').sort(function(a, b) {
return sortedValues.indexOf($(a).text()) - sortedValues.indexOf($(b).text());
});
// Replace all options in selectEl
selectEl.empty().append(sortedOptions);
// Trigger change event on Select2
selectEl.trigger('change');
}
@@ -776,7 +779,7 @@ function initSelect2() {
setTimeout(() => {
initSelect2()
}, 1000);
}
}
}
// ------------------------------------------
@@ -816,7 +819,7 @@ function renderDeviceLink(data, container, useName = false) {
'data-alert': device.devAlertDown,
'data-icon': device.devIcon
});
return `
<a href="${badge.url}" target="_blank">
<span class="custom-chip">
@@ -866,7 +869,7 @@ function initHoverNodeInfo() {
$(document).on('mouseenter', '.hover-node-info', function (e) {
const $el = $(this);
lastTarget = this;
// use timeout to prevent a quick hover and exit toi flash a card when navigating to a target node with your mouse
clearTimeout(hoverTimeout);
@@ -893,25 +896,25 @@ function initHoverNodeInfo() {
<div class="line">
<b>Status:</b> <span>${status}</span><br>
</div>
<div class="line">
<div class="line">
<b>IP:</b> <span>${ip}</span><br>
</div>
<div class="line">
<div class="line">
<b>MAC:</b> <span>${mac}</span><br>
</div>
<div class="line">
<div class="line">
<b>Vendor:</b> <span>${vendor}</span><br>
</div>
<div class="line">
<div class="line">
<b>Type:</b> <span>${type}</span><br>
</div>
<div class="line">
<div class="line">
<b>First seen:</b> <span>${firstseen}</span><br>
</div>
<div class="line">
<div class="line">
<b>Last seen:</b> <span>${lastseen}</span><br>
</div>
<div class="line">
<div class="line">
<b>Relationship:</b> <span class="${getRelationshipConf(relationship).cssClass}">${relationship}</span>
</div>
`;

View File

@@ -18,19 +18,19 @@
</div>
<div class="deviceSelector col-md-11 col-sm-11" style="z-index:5">
<div class="db_info_table_row col-sm-12" >
<div class="form-group" >
<div class="input-group col-sm-12 " >
<div class="db_info_table_row col-sm-12" >
<div class="form-group" >
<div class="input-group col-sm-12 " >
<select class="form-control select2 select2-hidden-accessible" multiple="" style="width: 100%;" tabindex="-1" aria-hidden="true">
</select>
</div>
</div>
</div>
</div>
<div class="col-md-1 hoverHighlight">
<i class="fa-solid fa-circle-check hoverHighlight pointer" onclick="markAllSelected()" title="<?= lang('Gen_Add_All');?>"></i>
<i class="fa-solid fa-circle-xmark hoverHighlight pointer" onclick="markAllNotSelected()" title="<?= lang('Gen_Remove_All');?>"></i>
</div>
<div class="col-md-1 hoverHighlight">
<i class="fa-solid fa-circle-check hoverHighlight pointer" onclick="markAllSelected()" title="<?= lang('Gen_Add_All');?>"></i>
<i class="fa-solid fa-circle-xmark hoverHighlight pointer" onclick="markAllNotSelected()" title="<?= lang('Gen_Remove_All');?>"></i>
</div>
</div>
@@ -69,19 +69,19 @@
<script defer>
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// Get plugin and settings data from API endpoints
function getData(){
// some race condition, need to implement delay
setTimeout(() => {
$.get('php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) {
$.get('php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) {
settingsData = res["data"];
excludedColumns = ["NEWDEV_devMac", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devLastNotification", "NEWDEV_devScan", "NEWDEV_devPresentLastScan", "NEWDEV_devCustomProps", "NEWDEV_devChildrenNicsDynamic", "NEWDEV_devChildrenDynamic" ]
const relevantColumns = settingsData.filter(set =>
set.setGroup === "NEWDEV" &&
set.setKey.includes("_dev") &&
@@ -103,7 +103,7 @@
// Append form groups to the column
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, multiEditColumns.length); j++) {
const setTypeObject = JSON.parse(multiEditColumns[j].setType.replace(/'/g, '"'));
const setTypeObject = JSON.parse(multiEditColumns[j].setType.replace(/'/g, '"'));
// get the element with the input value(s)
let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
@@ -118,7 +118,7 @@
}
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
const {
const {
inputType,
readOnly,
isMultiSelect,
@@ -137,10 +137,11 @@
customId,
columns,
base64Regex,
elementOptionsBase64
elementOptionsBase64,
focusout
} = handleElementOptions('none', elementOptions, transformers, val = "");
// render based on element type
// render based on element type
if (elementType === 'select') {
targetLocation = multiEditColumns[j].setKey + "_generateSetOptions"
@@ -148,7 +149,7 @@
generateOptionsOrSetOptions(multiEditColumns[j].setKey, [], targetLocation, generateOptions, null)
console.log(multiEditColumns[j].setKey)
// Handle Icons as they need a preview
// Handle Icons as they need a preview
if(multiEditColumns[j].setKey == 'NEWDEV_devIcon')
{
input = `
@@ -157,37 +158,37 @@
onChange="updateIconPreview(this)"
my-customparams="NEWDEV_devIcon,NEWDEV_devIcon_preview"
id="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option>
</select>`
} else{
} else{
input = `<select class="form-control"
id="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option>
</select>`
}
} else if (elementType === 'input'){
} else if (elementType === 'input'){
// Add classes specifically for checkboxes
inputType === 'checkbox' ? inputClass = 'checkbox' : inputClass = 'form-control';
input = `<input class="${inputClass}"
id="${multiEditColumns[j].setKey}"
my-customid="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}"
input = `<input class="${inputClass}"
id="${multiEditColumns[j].setKey}"
my-customid="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}"
type="${inputType}">`
}
const inputEntry = `<div class="form-group col-sm-12" >
<label class="col-sm-3 control-label">${multiEditColumns[j].setName}</label>
<div class="col-sm-9">
@@ -200,7 +201,7 @@
</div>
</div>`
column.append(inputEntry);
}
@@ -215,11 +216,11 @@
initSelect2();
initDeviceSelectors();
})
}, 100);
}
// -----------------------------------------------------------------------------
@@ -262,10 +263,10 @@
var option = new Option($('.deviceSelector select option[value="' + mac + '"]').html(), mac, true, true);
$('.deviceSelector select').append(option).trigger('change');
});
});
}
}
}, 10);
}
@@ -282,7 +283,7 @@
function markAllSelected() {
// Get the <select> element with the class 'deviceSelector'
var selectElement = $('.deviceSelector select');
// Iterate over each option within the select element
selectElement.find('option').each(function() {
// Mark each option as selected
@@ -298,13 +299,13 @@
function markAllNotSelected() {
// Get the <select> element with the class 'deviceSelector'
var selectElement = $('.deviceSelector select');
// Iterate over each option within the select element
selectElement.find('option').each(function() {
// Unselect each option
$(this).prop('selected', false);
});
// Trigger the 'change' event to notify Bootstrap Select of the changes
selectElement.trigger('change');
}
@@ -341,13 +342,13 @@
// update selected
if(selectorMacs() != "")
{
{
executeAction('update', 'devMac', selectorMacs(), targetColumns, columnValue )
}
else
{
showModalWarning(getString("Gen_Error"), getString('Device_MultiEdit_No_Devices'));
}
showModalWarning(getString("Gen_Error"), getString('Device_MultiEdit_No_Devices'));
}
}
// -----------------------------------------------------------------------------
@@ -380,21 +381,21 @@ function executeAction(action, whereColumnName, key, targetColumns, newTargetCol
// -----------------------------------------------------------------------------
// Ask to delete selected devices
// Ask to delete selected devices
function askDeleteSelectedDevices () {
// Ask
// Ask
showModalWarning(
getString('Maintenance_Tool_del_alldev_noti'),
getString('Maintenance_Tool_del_alldev_noti'),
getString('Gen_AreYouSure'),
getString('Gen_Cancel'),
getString('Gen_Delete'),
getString('Gen_Cancel'),
getString('Gen_Delete'),
'deleteSelectedDevices');
}
// -----------------------------------------------------------------------------
// Delete selected devices
// Delete selected devices
function deleteSelectedDevices()
{
{
macs_tmp = selectorMacs()
executeAction('delete', 'devMac', macs_tmp )
write_notification('[Multi edit] Manually deleted devices with MACs:' + macs_tmp, 'info')

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "تصفية",
"Gen_Generate": "إنشاء",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "قاعدة البيانات مقفلة",
"Gen_NetworkMask": "",
"Gen_Offline": "غير متصل",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtrar",
"Gen_Generate": "Generar",
"Gen_InvalidMac": "Mac address invàlida.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERROR - DB podria estar bloquejada - Fes servir F12 Eines desenvolupament -> Consola o provar-ho més tard.",
"Gen_NetworkMask": "Màscara de xarxa",
"Gen_Offline": "Fora de línia",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtr",
"Gen_Generate": "Vygenerovat",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "CHYBA - Databáze je možná zamčená - Zkontrolujte F12 -> Nástroje pro vývojáře -> Konzole. nebo to zkuste později.",
"Gen_NetworkMask": "",
"Gen_Offline": "Offline",

View File

@@ -315,6 +315,7 @@
"Gen_Filter": "Filter",
"Gen_Generate": "Generieren",
"Gen_InvalidMac": "Ungültige MAC-Adresse.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERROR - DB eventuell gesperrt - Nutze die Konsole in den Entwickler Werkzeugen (F12) zur Überprüfung oder probiere es später erneut.",
"Gen_NetworkMask": "",
"Gen_Offline": "Offline",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filter",
"Gen_Generate": "Generate",
"Gen_InvalidMac": "Invalid Mac address.",
"Gen_Invalid_Value": "An invalid value was entered",
"Gen_LockedDB": "ERROR - DB might be locked - Check F12 Dev tools -> Console or try later.",
"Gen_NetworkMask": "Network mask",
"Gen_Offline": "Offline",

View File

@@ -313,6 +313,7 @@
"Gen_Filter": "Filtro",
"Gen_Generate": "Generar",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "Fallo - La base de datos puede estar bloqueada - Pulsa F1 -> Ajustes de desarrolladores -> Consola o prueba más tarde.",
"Gen_NetworkMask": "",
"Gen_Offline": "Desconectado",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "",
"Gen_Generate": "",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "",
"Gen_NetworkMask": "",
"Gen_Offline": "",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtrer",
"Gen_Generate": "Générer",
"Gen_InvalidMac": "Adresse MAC invalide.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "Erreur - La base de données est peut-être verrouillée - Vérifier avec les outils de dév via F12 -> Console ou essayer plus tard.",
"Gen_NetworkMask": "Masque réseau",
"Gen_Offline": "Hors ligne",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtro",
"Gen_Generate": "Genera",
"Gen_InvalidMac": "Indirizzo Mac non valido.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERRORE: il DB potrebbe essere bloccato, controlla F12 Strumenti di sviluppo -> Console o riprova più tardi.",
"Gen_NetworkMask": "Maschera di rete",
"Gen_Offline": "Offline",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "",
"Gen_Generate": "",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "",
"Gen_NetworkMask": "",
"Gen_Offline": "",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filter",
"Gen_Generate": "",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "FEIL - DB kan være låst - Sjekk F12 Dev tools -> Konsoll eller prøv senere.",
"Gen_NetworkMask": "",
"Gen_Offline": "Frakoblet",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtr",
"Gen_Generate": "Wygeneruj",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "Błąd - Baza danych może być zablokowana - Sprawdź narzędzia deweloperskie F12 -> Konsola lub spróbuj później.",
"Gen_NetworkMask": "",
"Gen_Offline": "Niedostępne",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtro",
"Gen_Generate": "Gerar",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERRO - O banco de dados pode estar bloqueado - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
"Gen_NetworkMask": "",
"Gen_Offline": "Offline",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtro",
"Gen_Generate": "Gerar",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERRO - A base de dados pode estar bloqueada - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
"Gen_NetworkMask": "",
"Gen_Offline": "Offline",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Фильтр",
"Gen_Generate": "Генерировать",
"Gen_InvalidMac": "Неверный Mac-адрес.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ОШИБКА - Возможно, база данных заблокирована. Проверьте инструменты разработчика F12 -> Консоль или повторите попытку позже.",
"Gen_NetworkMask": "Маска сети",
"Gen_Offline": "Оффлайн",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "",
"Gen_Generate": "",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "",
"Gen_NetworkMask": "",
"Gen_Offline": "",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtre",
"Gen_Generate": "Oluştur",
"Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "HATA - Veritabanı kilitlenmiş olabilir - F12 Geliştirici araçlarını -> Konsol kısmını kontrol edin veya daha sonra tekrar deneyin.",
"Gen_NetworkMask": "",
"Gen_Offline": "Çevrimdışı",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Фільтр",
"Gen_Generate": "Генерувати",
"Gen_InvalidMac": "Недійсна Mac-адреса.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ПОМИЛКА БД може бути заблоковано перевірте F12 Інструменти розробника -> Консоль або спробуйте пізніше.",
"Gen_NetworkMask": "Маска мережі",
"Gen_Offline": "Офлайн",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "筛选",
"Gen_Generate": "生成",
"Gen_InvalidMac": "无效的 Mac 地址。",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "错误 - DB 可能被锁定 - 检查 F12 开发工具 -> 控制台或稍后重试。",
"Gen_NetworkMask": "网络掩码",
"Gen_Offline": "离线",

View File

@@ -91,7 +91,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -377,7 +377,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -225,7 +225,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -100,7 +100,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -148,7 +148,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -185,7 +185,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -125,7 +125,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -175,7 +175,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -596,7 +596,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -397,7 +397,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -148,7 +148,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -103,7 +103,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -180,7 +180,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -203,7 +203,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -468,7 +468,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -103,7 +103,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -130,7 +130,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -130,7 +130,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -148,7 +148,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -227,7 +227,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -476,7 +476,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -148,7 +148,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -92,7 +92,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -84,7 +84,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -89,7 +89,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -182,7 +182,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -512,7 +512,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -98,7 +98,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -100,7 +100,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
@@ -221,7 +221,7 @@
},
{
"getStringKey": "Gen_Add"
}
}
],
"transformers": []
},

View File

@@ -140,7 +140,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -90,7 +90,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -425,7 +425,7 @@
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
"focusout": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -30,22 +30,22 @@ checkPermissions([$dbPath, $confPath]);
// path to your JSON file
$apiRoot = rtrim(getenv('NETALERTX_API') ?: '/tmp/api', '/');
$file = $apiRoot . '/table_settings.json';
$file = $apiRoot . '/table_settings.json';
// put the content of the file in a variable
$data = file_get_contents($file);
$data = file_get_contents($file);
// JSON decode
$settingsJson = json_decode($data);
$settingsJson = json_decode($data);
// get settings from the DB
global $db;
$result = $db->query("SELECT * FROM Settings");
$result = $db->query("SELECT * FROM Settings");
$settings = array();
while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// Push row data
while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// Push row data
$settings[] = array( 'setKey' => $row['setKey'],
'setName' => $row['setName'],
'setDescription' => $row['setDescription'],
@@ -54,7 +54,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
'setValue' => $row['setValue'],
'setGroup' => $row['setGroup'],
'setEvents' => $row['setEvents']
);
);
}
$settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
@@ -63,7 +63,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<!-- Page ------------------------------------------------------------------ -->
<!-- ----------------------------------------------------------------------- -->
<script src="lib/crypto/crypto-js.min.js"></script>
<script src="lib/bcrypt/bcrypt.min.js"></script>
@@ -74,21 +74,21 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<a style="cursor:pointer">
<span>
<i id='toggleSettings' onclick="toggleAllSettings()" class="settings-expand-icon fa fa-angle-double-down"></i>
</span>
</span>
</a>
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<div class ="bg-white color-palette box box-solid box-primary col-sm-12 panel panel-default panel-title" >
<div class ="bg-white color-palette box box-solid box-primary col-sm-12 panel panel-default panel-title" >
<a data-toggle="collapse" href="#settingsOverview">
<div class ="settings-group col-sm-12 panel-heading panel-title">
<i class="<?= lang("settings_enabled_icon");?>"></i> <?= lang("settings_enabled");?>
</div>
</a>
<div id="settingsOverview" class="panel-collapse collapse in">
<i class="<?= lang("settings_enabled_icon");?>"></i> <?= lang("settings_enabled");?>
</div>
</a>
<div id="settingsOverview" class="panel-collapse collapse in">
<div class="panel-body"></div>
<div class =" col-sm-12 " id=""></div>
</div>
@@ -96,43 +96,43 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="content settingswrap " id="accordion_gen">
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="core_content_header" >
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="core_content_header" >
<div class ="settings-group col-sm-12">
<i class="<?= lang("settings_core_icon");?>"></i> <?= lang("settings_core_label");?>
</div>
<i class="<?= lang("settings_core_icon");?>"></i> <?= lang("settings_core_label");?>
</div>
<div class =" col-sm-12" id="core_content"></div>
</div>
</div>
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="system_content_header" >
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="system_content_header" >
<div class ="settings-group col-sm-12">
<i class="<?= lang("settings_system_icon");?>"></i> <?= lang("settings_system_label");?>
</div>
<i class="<?= lang("settings_system_icon");?>"></i> <?= lang("settings_system_label");?>
</div>
<div class =" col-sm-12" id="system_content"></div>
</div>
</div>
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="device_scanners_content_header" >
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="device_scanners_content_header" >
<div class ="settings-group col-sm-12">
<i class="<?= lang("settings_device_scanners_icon");?>"></i> <?= lang("settings_device_scanners_label");?>
</div>
<i class="<?= lang("settings_device_scanners_icon");?>"></i> <?= lang("settings_device_scanners_label");?>
</div>
<div class =" col-sm-12" id="device_scanner_content"> <?= lang("settings_device_scanners_info");?> </div>
</div>
</div>
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="other_scanners_content_header">
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="other_scanners_content_header">
<div class ="settings-group col-sm-12">
<i class="<?= lang("settings_other_scanners_icon");?>"></i> <?= lang("settings_other_scanners_label");?>
</div>
<i class="<?= lang("settings_other_scanners_icon");?>"></i> <?= lang("settings_other_scanners_label");?>
</div>
<div class =" col-sm-12" id="other_content"></div>
</div>
</div>
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="publishers_content_header" >
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="publishers_content_header" >
<div class ="settings-group col-sm-12">
<i class="<?= lang("settings_publishers_icon");?>"></i> <?= lang("settings_publishers_label");?>
</div>
<i class="<?= lang("settings_publishers_icon");?>"></i> <?= lang("settings_publishers_label");?>
</div>
<div class =" col-sm-12" id="publisher_content"><?= lang("settings_publishers_info");?></div>
</div>
</div>
</div>
<!-- /.content -->
<section class=" padding-bottom col-sm-12">
@@ -141,10 +141,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="col-sm-7 settingsImportedTimestamp" style="display:none" title="<?= lang("settings_imported");?> ">
<div class="settingsImported ">
<?= lang("settings_imported_label");?>:
<?= lang("settings_imported_label");?>:
<span id="lastImportedTime"></span>
</div>
</div>
</div>
</section>
@@ -156,8 +156,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<input type="text" id="settingsSearch" class="form-control input-xs col-xs-12" placeholder="Filter Settings...">
<div class="clear-filter ">
<i class="fa-solid fa-circle-xmark" onclick="$('#settingsSearch').val('');filterRows();$('#settingsSearch').focus()"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-4 saveSettingsWrapper">
@@ -171,13 +171,13 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<!-- ----------------------------------------------------------------------- -->
<?php
require 'php/templates/footer.php';
require 'php/templates/footer.php';
?>
<script>
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// Get plugin and settings data from API endpoints
function getData(){
@@ -217,9 +217,9 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
console.log("Settings:", settingsData);
// Wrong number of settings processing
if(settingsNumberDB != settingsData.length)
if(settingsNumberDB != settingsData.length)
{
showModalOk('WARNING', "<?= lang("settings_old")?>");
showModalOk('WARNING', "<?= lang("settings_old")?>");
setTimeout(() => {
clearCache()
}, 3000);
@@ -227,7 +227,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
{
$.get('php/server/query_json.php', { file: 'plugins.json', nocache: Date.now() }, function(res) {
pluginsData = res["data"];
pluginsData = res["data"];
// Sort settingsData alphabetically, ensuring "General" is always first
settingsData.sort((a, b) => {
@@ -257,31 +257,31 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setKey = set['setKey']
try {
try {
const isMetadata = setKey.includes('__metadata');
// if this isn't a metadata entry, get corresponding metadata object from the dummy setting
const setObj = isMetadata ? {} : JSON.parse(getSetting(`${setKey}__metadata`));
} catch (error) {
console.error(`Error getting setting for ${setKey}:`, error);
showModalOk('WARNING', "Outdated cache - refreshing (refresh browser cache if needed)");
showModalOk('WARNING', "Outdated cache - refreshing (refresh browser cache if needed)");
setTimeout(() => {
clearCache()
}, 3000);
exception_occurred = true;
exception_occurred = true;
}
});
// only proceed if everything was loaded correctly
if(!exception_occurred)
{
initSettingsPage(settingsData, pluginsData);
}
})
}
}
}
},
error: function (xhr, status, error) {
@@ -296,9 +296,9 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
function initSettingsPage(settingsData, pluginsData){
const settingPluginPrefixes = [];
const enabledDeviceScanners = getPluginsByType(pluginsData, "device_scanner", true);
const enabledOthers = getPluginsByType(pluginsData, "other", true);
const enabledDeviceScanners = getPluginsByType(pluginsData, "device_scanner", true);
const enabledOthers = getPluginsByType(pluginsData, "other", true);
const enabledPublishers = getPluginsByType(pluginsData, "publisher", true);
@@ -310,18 +310,18 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
// settingPluginPrefixes
if (!settingPluginPrefixes.includes(set.setGroup)) {
settingPluginPrefixes.push(set.setGroup); // = Unique plugin prefix
}
});
}
});
// Init the overview section
overviewSections = [
'device_scanners',
'other_scanners',
'publishers'
'device_scanners',
'other_scanners',
'publishers'
]
overviewSectionsHtml = [
pluginCards(enabledDeviceScanners,['RUN', 'RUN_SCHD']),
pluginCards(enabledOthers, ['RUN', 'RUN_SCHD']),
pluginCards(enabledOthers, ['RUN', 'RUN_SCHD']),
pluginCards(enabledPublishers, []),
]
@@ -334,15 +334,15 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="col-sm-12 " title="${getString("settings_"+section)}">
<a href="#${section}_content_header">
<div class="overview-group col-sm-12 col-xs-12">
<i title="${section}" class="${getString("settings_"+section+"_icon")}"></i>
${getString("settings_"+section+"_label")}
</div>
</a>
<i title="${section}" class="${getString("settings_"+section+"_icon")}"></i>
${getString("settings_"+section+"_label")}
</div>
</a>
</div>
<div class="col-sm-12">
${overviewSectionsHtml[index]}
${overviewSectionsHtml[index]}
</div>
</div>`
index++;
@@ -350,7 +350,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
$('#settingsOverview .panel-body').append(overviewSections_html);
// Display warning
// Display warning
if(schedulesAreSynchronized(enabledDeviceScanners, pluginsData) == false)
{
$("#device_scanners").append(
@@ -359,9 +359,9 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
${getString('Settings_device_Scanners_desync')}
</small>
`)
}
}
for (const prefix of settingPluginPrefixes) {
for (const prefix of settingPluginPrefixes) {
// enabled / disabled icons
enabledHtml = ''
@@ -376,16 +376,16 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<i class="fa fa-${onOff}"></i>
</div>
`
}
}
// console.log(pluginsData);
// Start constructing the main settings HTML
// Start constructing the main settings HTML
let pluginHtml = `
<div class="row table_row docs">
<div class="table_cell bold">
<i class="fa fa-book fa-sm"></i>
${getString(prefix+'_description')}
${getString(prefix+'_description')}
<a href="https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/${getPluginCodeName(pluginsData, prefix)}" target="_blank">
${getString('Gen_ReadDocs')}
</a>
@@ -399,28 +399,28 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="panel-heading">
<h4 class="panel-title">
<div class="col-sm-1 col-xs-1">${getString(prefix+"_icon")} </div>
<div class="col-sm-10 col-xs-8">${getString(prefix+"_display_name")} </div>
<div class="col-sm-10 col-xs-8">${getString(prefix+"_display_name")} </div>
<div class="col-sm-1 col-xs-1">${enabledHtml} </div>
</h4>
</h4>
</div>
</a>
<div id="${prefix}" data-myid="collapsible" class="panel-collapse collapse ${prefix == "General" ? ' in ' : ""}">
<div class="panel-body">
${prefix != "General" ? pluginHtml: ""}
${prefix != "General" ? pluginHtml: ""}
</div>
</div>
</div>
`;
// generate headers/sections
// generate headers/sections
$('#'+getPluginType(pluginsData, prefix) + "_content").append(headerHtml);
}
}
// generate panel content
for (const prefix of settingPluginPrefixes) {
// go thru all settings and collect settings per settings prefix
// go thru all settings and collect settings per settings prefix
settingsData.forEach((set) => {
const valIn = set['setValue'];
@@ -443,18 +443,18 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
if(set["setGroup"] == prefix)
{
// hide metadata by default by assigning it a special class
// hide metadata by default by assigning it a special class
isMetadata ? metadataClass = 'metadata' : metadataClass = '';
isMetadata ? showMetadata = '' : showMetadata = `<i
isMetadata ? showMetadata = '' : showMetadata = `<i
my-to-toggle="row_${setKey}__metadata"
title="${getString("Settings_Metadata_Toggle")}"
class="fa fa-circle-question pointer hideOnMobile"
title="${getString("Settings_Metadata_Toggle")}"
class="fa fa-circle-question pointer hideOnMobile"
onclick="toggleMetadata(this)">
</i>` ;
infoIcon = `<i my-to-show="#row_${setKey} .setting_description"
title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer hideOnBigScreen"
title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer hideOnBigScreen"
onclick="showDescription(this)">
</i>` ;
@@ -477,12 +477,12 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
// surface settings override functionality if the setting is a template that can be overridden with user defined values
// if the setting is a json of the correct structure, handle like a template setting
let overrideHtml = "";
let overrideHtml = "";
//pre-check if this is a json object that needs value extraction
let overridable = false; // indicates if the setting is overridable
let override = false; // If the setting is set to be overridden by the user or by default
let override = false; // If the setting is set to be overridden by the user or by default
let readonly = ""; // helper variable to make text input readonly
let disabled = ""; // helper variable to make checkbox input readonly
@@ -490,7 +490,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
if ('override_value' in setObj) {
overridable = true;
overrideObj = setObj["override_value"]
override = overrideObj["override"];
override = overrideObj["override"];
console.log(setObj)
console.log(prefix)
@@ -498,8 +498,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
// prepare override checkbox and HTML
if(overridable)
{
let checked = override ? 'checked' : '';
{
let checked = override ? 'checked' : '';
overrideHtml = `<div class="override col-xs-12">
<div class="override-check col-xs-1">
@@ -508,10 +508,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="override-text col-xs-11" title="${getString("Setting_Override_Description")}">
${getString("Setting_Override")}
</div>
</div>`;
</div>`;
}
}
// INPUT
inputFormHtml = generateFormHtml(settingsData, set, valIn, null, null);
@@ -522,7 +522,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
`
// generate settings in the correct prefix (group) section
$(`#${prefix} .panel-body`).append(setHtml);
$(`#${prefix} .panel-body`).append(setHtml);
}
});
@@ -532,7 +532,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setTimeout(() => {
initListInteractionOptions() // init remove and edit listitem click gestures
}, 50);
setupSmoothScrolling();
// try to initialize select2
initSelect2();
@@ -548,29 +548,29 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
var settingsNumberJSON = <?php echo count($settingsJson->data)?>;
// Wrong number of settings processing
if(settingsNumberJSON != settingsNumberDB)
if(settingsNumberJSON != settingsNumberDB)
{
showModalOk('WARNING', getString("settings_missing"));
showModalOk('WARNING', getString("settings_missing"));
}
// ---------------------------------------------------------
function saveSettings() {
if(settingsNumberJSON != settingsNumberDB)
if(settingsNumberJSON != settingsNumberDB)
{
console.log(`Error settingsNumberJSON != settingsNumberDB: ${settingsNumberJSON} != ${settingsNumberDB}`);
showModalOk('WARNING', getString("settings_missing_block"));
showModalOk('WARNING', getString("settings_missing_block"));
setTimeout(() => {
clearCache()
clearCache()
}, 1500);
} else {
let settingsArray = [];
// collect values for each of the different input form controls
// get settings to determine setting type to store values appropriately
$.get('php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) {
$.get('php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) {
// loop through the settings definitions from the json
res["data"].forEach(set => {
@@ -578,7 +578,17 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setType = set["setType"]
setCodeName = set["setKey"]
settingsArray = collectSetting(prefix, setCodeName, setType, settingsArray)
// settingsArray = collectSetting(prefix, setCodeName, setType, settingsArray)
const collectSettingResult = collectSetting(prefix, setCodeName, setType, settingsArray);
settingsArray = collectSettingResult.settingsArray;
if (!collectSettingResult.dataIsValid) {
msg = getString("Gen_Invalid_Value") + ": " + collectSettingResult.failedSettingKey;
console.error(msg);
showMessage (msg, 3000, "modal_red");
// return early
return;
}
});
// sanity check to make sure settings were loaded & collected correctly
@@ -586,44 +596,45 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
{
console.log(settingsArray);
console.log(settingsJSON_DB);
console.log( JSON.stringify(settingsArray));
// return; // 🐛 🔺
// trigger a save settings event in the backend
$.ajax({
method: "POST",
url: "php/server/util.php",
data: {
function: 'savesettings',
data: {
function: 'savesettings',
settings: JSON.stringify(settingsArray) },
success: function(data, textStatus) {
success: function(data, textStatus) {
if(data == "OK")
{
{
// showMessage (getString("settings_saved"), 5000, "modal_grey");
// Remove navigation prompt "Are you sure you want to leave..."
window.onbeforeunload = null;
window.onbeforeunload = null;
// Reloads the current page
// setTimeout("clearCache()", 5000);
// setTimeout("clearCache()", 5000);
write_notification(`[Settings] Settings saved by the user`, 'info')
clearCache()
} else{
// something went wrong
// something went wrong
write_notification("[Important] Please take a screenshot of the Console tab in the browser (F12) and next error. Submit it (with the nginx and php error logs) as a new issue here: https://github.com/jokob-sk/NetAlertX/issues", 'interrupt')
write_notification(data, 'interrupt')
console.log("🔽");
console.log(settingsArray);
console.log(JSON.stringify(settingsArray));
console.log(data);
console.log(JSON.stringify(settingsArray));
console.log(data);
console.log("🔼");
}
}
});
}
}
})
}
@@ -647,30 +658,30 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
} else
{
// check if config file has been updated
$.get('php/server/query_json.php', { file: 'app_state.json', nocache: Date.now() }, function(appState) {
$.get('php/server/query_json.php', { file: 'app_state.json', nocache: Date.now() }, function(appState) {
console.log("Settings: Got app_state.json");
fileModificationTime = <?php echo filemtime($confPath)*1000;?>;
console.log("Settings: Got app_state.json");
fileModificationTime = <?php echo filemtime($confPath)*1000;?>;
// console.log(appState["settingsImported"]*1000)
importedMiliseconds = parseInt((appState["settingsImported"]*1000));
// check if displayed settings are outdated
if(appState["showSpinner"] || fileModificationTime > importedMiliseconds)
{
{
showSpinner("settings_old")
setTimeout("handleLoadingDialog()", 1000);
} else
{
{
checkInitialization();
}
humanReadable = (new Date(importedMiliseconds)).toLocaleString("en-UK", { timeZone: "<?php echo $timeZone?>" });
document.getElementById('lastImportedTime').innerHTML = humanReadable;
document.getElementById('lastImportedTime').innerHTML = humanReadable;
})
}
@@ -697,7 +708,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setTimeout(checkInitialization, 1000);
}
}
showSpinner()
handleLoadingDialog()