Custom Device Properties v0.1 #876

This commit is contained in:
jokob-sk
2024-12-27 12:42:15 +11:00
parent 3732416616
commit 0f474fb884
42 changed files with 2014 additions and 465 deletions

85
docs/CUSTOM_PROPERTIES.md Executable file
View File

@@ -0,0 +1,85 @@
# Custom Properties for Network Devices
![Custom Properties](/docs/img/CUSTOM_PROPERTIES/Device_Custom_Properties.png)
## Overview
This functionality allows you to define **custom properties** for network devices, which can store and display additional information on the device listing page. By marking properties as visible, you can enhance the user interface with quick actions, notes, or external links.
### Key Features:
- **Customizable Properties**: Define specific properties for each device.
- **Visibility Control**: Choose which properties are displayed on the device listing page.
- **Interactive Elements**: Include actions like links, modals, and device management directly in the interface.
---
## Defining Custom Properties
Custom properties are structured as a list of objects, where each property includes the following fields:
| Field | Description |
|--------------------|-----------------------------------------------------------------------------|
| `CUSTPROP_icon` | The icon (Base64-encoded HTML) displayed for the property. |
| `CUSTPROP_type` | The action type (e.g., `show_notes`, `link`, `delete_dev`). |
| `CUSTPROP_name` | A short name or title for the property. |
| `CUSTPROP_args` | Arguments for the action (e.g., URL or modal text). |
| `CUSTPROP_notes` | Additional notes or details displayed when applicable. |
| `CUSTPROP_show` | A boolean to control visibility (`true` to show on the listing page). |
---
## Available Action Types
- **Show Notes**: Displays a modal with a title and additional notes.
- **Example**: Show firmware details or custom messages.
- **Link**: Redirects to a specified URL in the current browser tab. (**Arguments** Needs to contain the full URL.)
- **Link (New Tab)**: Opens a specified URL in a new browser tab. (**Arguments** Needs to contain the full URL.)
- **Delete Device**: Deletes the device using its MAC address.
- **Run Plugin**: Placeholder for executing custom plugins (not implemented yet).
---
## Usage on the Device Listing Page
![Custom Properties](/docs/img/CUSTOM_PROPERTIES/Device_Custom_Properties_vid.gif)
Visible properties (`CUSTPROP_show: true`) are displayed as interactive icons in the device listing. Each icon can perform one of the following actions based on the `CUSTPROP_type`:
1. **Modals (e.g., Show Notes)**:
- Displays detailed information in a popup modal.
- Example: Firmware version details.
2. **Links**:
- Redirect to an external or internal URL.
- Example: Open a device's documentation or external site.
3. **Device Actions**:
- Manage devices with actions like delete.
- Example: Quickly remove a device from the network.
4. **Plugins**:
- Future placeholder for running custom plugin scripts.
- **Note**: Not implemented yet.
---
## Example Scenarios
1. **Device Documentation Link**:
- Add a custom property with `CUSTPROP_type` set to `link` or `link_new_tab` to allow quick navigation to the documentation.
2. **Firmware Details**:
- Use `CUSTPROP_type: show_notes` to display firmware versions or upgrade instructions in a modal.
3. **Device Removal**:
- Enable device removal functionality using `CUSTPROP_type: delete_dev`.
---
## Notes
- **Plugin Functionality**: The `run_plugin` action type is currently not implemented and will show an alert if used.
- **Custom Icons (Experimental 🧪)**: Use Base64-encoded HTML to provide custom icons for each property. You can add your icons in Setttings via the `CUSTPROP_icon` settings
- **Visibility Control**: Only properties with `CUSTPROP_show: true` will appear on the listing page.
This feature provides a flexible way to enhance device management and display with interactive elements tailored to your needs.

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -1382,9 +1382,36 @@ input[readonly] {
}
/* ----------------------------------------------------------------- */
/* MODAL popups */
/* Device details */
/* ----------------------------------------------------------------- */
.dataTables_length label .form-control, .dataTables_filter label .form-control
{
float: none;
}
.form-inline .input-group
{
width: 100%;
}
.input-group .actionIcon
{
height: 2.4em;
}
.input-group .actionIcon:hover
{
backdrop-filter: brightness(50%);
background-color: rgba(0, 0, 0, 0.2); /* darkens the background */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transition: box-shadow 0.1s ease-in-out, background-color 0.1s ease-in-out;
}
/* ----------------------------------------------------------------- */
/* MODAL popups */
/* ----------------------------------------------------------------- */
#modal-input-textarea
{
width: 100%;

View File

@@ -14,26 +14,21 @@
<!-- Buttons -->
<div class="col-xs-12">
<div class="pull-right">
<button type="button"
class="btn btn-default pa-btn pa-btn-delete"
style="margin-left:0px;"
id="btnDeleteEvents"
onclick="askDeleteDeviceEvents()">
<?= lang('DevDetail_button_DeleteEvents');?>
</button>
<button type="button"
class="btn btn-default pa-btn pa-btn-delete"
style="margin-left:0px;"
id="btnDelete"
onclick="askDeleteDevice()">
<?= lang('DevDetail_button_Delete');?>
<i class="fas fa-trash-alt"></i>
<?= lang('DevDetail_button_Delete');?>
</button>
<button type="button"
class="btn btn-primary pa-btn"
style="margin-left:6px; "
id="btnSave"
onclick="setDeviceData()" >
<?= lang('DevDetail_button_Save');?>
<i class="fas fa-save"></i>
<?= lang('DevDetail_button_Save');?>
</button>
</div>
</div>
@@ -83,32 +78,55 @@
DevDetail_MainInfo_Title: {
data: ["devMac", "devLastIP", "devName", "devOwner", "devType", "devVendor", "devGroup", "devIcon", "devLocation", "devComments"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICE_MANAGEMENT.md",
iconClass: "fa fa-pencil"
iconClass: "fa fa-pencil",
inputGroupClasses: "field-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
},
// Group for session information
DevDetail_SessionInfo_Title: {
data: ["devStatus", "devLastConnection", "devFirstConnection"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/SESSION_INFO.md",
iconClass: "fa fa-calendar"
iconClass: "fa fa-calendar",
inputGroupClasses: "field-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
},
// Group for event and alert settings
DevDetail_EveandAl_Title: {
data: ["devAlertEvents", "devAlertDown", "devSkipRepeated"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NOTIFICATIONS.md",
iconClass: "fa fa-bell"
iconClass: "fa fa-bell",
inputGroupClasses: "field-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
},
// Group for network details
DevDetail_MainInfo_Network_Title: {
data: ["devParentMAC", "devParentPort", "devSSID", "devSite"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NETWORK_TREE.md",
iconClass: "fa fa-network-wired"
iconClass: "fa fa-network-wired",
inputGroupClasses: "field-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
},
// Group for other fields like static IP, archived status, etc.
DevDetail_DisplayFields_Title: {
data: ["devStaticIP", "devIsNew", "devFavorite", "devIsArchived"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICE_DISPLAY_SETTINGS.md",
iconClass: "fa fa-list-check"
iconClass: "fa fa-list-check",
inputGroupClasses: "field-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
},
// Group for Custom properties.
DevDetail_CustomProperties_Title: {
data: ["devCustomProps"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/CUSTOM_PROPERTIES.md",
iconClass: "fa fa-list",
inputGroupClasses: "field-group col-lg-12 col-sm-12 col-xs-12",
labelClasses: "col-sm-12 col-xs-12 control-label",
inputClasses: "col-sm-12 col-xs-12 input-group"
}
};
@@ -126,7 +144,7 @@
// Loop over each field group to generate sections for each category
Object.entries(fieldGroups).forEach(([groupName, obj]) => {
const groupDiv = $('<div>').addClass('field-group col-lg-4 col-sm-6 col-xs-12'); // Create a div for each group with responsive Bootstrap classes
const groupDiv = $('<div>').addClass(obj.inputGroupClasses); // Create a div for each group with responsive Bootstrap classes
// Add group title and documentation link
groupDiv.append(`<h5><i class="${obj.iconClass}"></i> ${getString(groupName)}
@@ -137,7 +155,7 @@
</span>
</h5>
<hr>
`);
`);
// Filter relevant settings for the current group
const groupSettings = settings.filter(set => obj.data.includes(set.setKey.replace('NEWDEV_', '')));
@@ -193,15 +211,15 @@
// Generate the input field HTML
const inputFormHtml = `<div class="form-group col-xs-12">
<label class="col-sm-4 col-xs-12 control-label"> ${setting.setName}
<label class="${obj.labelClasses}"> ${setting.setName}
<i my-set-key="${setting.setKey}"
title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer helpIconSmallTopRight"
onclick="showDescriptionPopup(this)">
</i>
</label>
<div class="col-sm-8 col-xs-12 input-group">
${generateFormHtml(setting, fieldData.toString())}
<div class="${obj.inputClasses}">
${generateFormHtml(settingsData, setting, fieldData.toString(), null, null)}
${inlineControl}
</div>
</div>`;
@@ -214,8 +232,8 @@
});
// wait until everything is initialized to update icon
updateIconPreview();
// wait until everything is initialized to update icons
updateAllIconPreviews();
// update readonly fields
handleReadOnly(settingsData, disabledFields);
@@ -317,64 +335,7 @@
showModalOK("Info", getString($(e).attr("my-set-key") + '_description'))
}
// -----------------------------------------------------------------------------
function askDeleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device Events
showModalWarning ('<?= lang('DevDetail_button_DeleteEvents');?>', '<?= lang('DevDetail_button_DeleteEvents_Warning');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteDeviceEvents');
}
function deleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Delete device events
$.get('php/server/devices.php?action=deleteDeviceEvents&mac='+ mac, function(msg) {
showMessage (msg);
});
// Deactivate controls
$('#panDetails :input').attr('disabled', true);
}
// -----------------------------------------------------------------------------
function askDeleteDevice () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device
showModalWarning ('Delete Device', 'Are you sure you want to delete this device?<br>(maybe you prefer to archive it)',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteDevice');
}
// -----------------------------------------------------------------------------
function deleteDevice () {
// Check MAC
if (mac == '') {
return;
}
// Delete device
$.get('php/server/devices.php?action=deleteDevice&mac='+ mac, function(msg) {
showMessage (msg);
});
// Deactivate controls
$('#panDetails :input').attr('disabled', true);
// refresh API
updateApi("devices,appevents")
}
// -----------------------------------------------------------------------------
function setDeviceData(direction = '', refreshCallback = '') {
@@ -396,51 +357,53 @@
showSpinner();
// update data to server
$.get('php/server/devices.php?action=setDeviceData&mac='+ $('#NEWDEV_devMac').val()
+ '&name=' + encodeURIComponent($('#NEWDEV_devName').val().replace(/'/g, ""))
+ '&owner=' + encodeURIComponent($('#NEWDEV_devOwner').val().replace(/'/g, ""))
+ '&type=' + $('#NEWDEV_devType').val().replace(/'/g, "")
+ '&vendor=' + encodeURIComponent($('#NEWDEV_devVendor').val().replace(/'/g, ""))
+ '&icon=' + encodeURIComponent($('#NEWDEV_devIcon').val())
+ '&favorite=' + ($('#NEWDEV_devFavorite')[0].checked * 1)
+ '&group=' + encodeURIComponent($('#NEWDEV_devGroup').val().replace(/'/g, ""))
+ '&location=' + encodeURIComponent($('#NEWDEV_devLocation').val().replace(/'/g, ""))
+ '&comments=' + encodeURIComponent(encodeSpecialChars($('#NEWDEV_devComments').val()))
+ '&networknode=' + $('#NEWDEV_devParentMAC').val()
+ '&networknodeport=' + $('#NEWDEV_devParentPort').val()
+ '&ssid=' + $('#NEWDEV_devSSID').val()
+ '&networksite=' + $('#NEWDEV_devSite').val()
+ '&staticIP=' + ($('#NEWDEV_devStaticIP')[0].checked * 1)
+ '&scancycle=' + "1"
+ '&alertevents=' + ($('#NEWDEV_devAlertEvents')[0].checked * 1)
+ '&alertdown=' + ($('#NEWDEV_devAlertDown')[0].checked * 1)
+ '&skiprepeated=' + $('#NEWDEV_devSkipRepeated').val().split(' ')[0]
+ '&newdevice=' + ($('#NEWDEV_devIsNew')[0].checked * 1)
+ '&archived=' + ($('#NEWDEV_devIsArchived')[0].checked * 1)
+ '&devFirstConnection=' + ($('#NEWDEV_devFirstConnection').val())
+ '&devLastConnection=' + ($('#NEWDEV_devLastConnection').val())
+ '&ip=' + ($('#NEWDEV_devLastIP').val())
+ '&createNew=' + createNew
, function(msg) {
showMessage (msg);
// Update data to server using POST
$.post('php/server/devices.php?action=setDeviceData', {
mac: $('#NEWDEV_devMac').val(),
name: encodeURIComponent($('#NEWDEV_devName').val().replace(/'/g, "")),
owner: encodeURIComponent($('#NEWDEV_devOwner').val().replace(/'/g, "")),
type: $('#NEWDEV_devType').val().replace(/'/g, ""),
vendor: encodeURIComponent($('#NEWDEV_devVendor').val().replace(/'/g, "")),
icon: encodeURIComponent($('#NEWDEV_devIcon').val()),
favorite: ($('#NEWDEV_devFavorite')[0].checked * 1),
group: encodeURIComponent($('#NEWDEV_devGroup').val().replace(/'/g, "")),
location: encodeURIComponent($('#NEWDEV_devLocation').val().replace(/'/g, "")),
comments: encodeURIComponent(encodeSpecialChars($('#NEWDEV_devComments').val())),
networknode: $('#NEWDEV_devParentMAC').val(),
networknodeport: $('#NEWDEV_devParentPort').val(),
ssid: $('#NEWDEV_devSSID').val(),
networksite: $('#NEWDEV_devSite').val(),
staticIP: ($('#NEWDEV_devStaticIP')[0].checked * 1),
scancycle: "1",
alertevents: ($('#NEWDEV_devAlertEvents')[0].checked * 1),
alertdown: ($('#NEWDEV_devAlertDown')[0].checked * 1),
skiprepeated: $('#NEWDEV_devSkipRepeated').val().split(' ')[0],
newdevice: ($('#NEWDEV_devIsNew')[0].checked * 1),
archived: ($('#NEWDEV_devIsArchived')[0].checked * 1),
devFirstConnection: ($('#NEWDEV_devFirstConnection').val()),
devLastConnection: ($('#NEWDEV_devLastConnection').val()),
devCustomProps: btoa(JSON.stringify(collectTableData("#NEWDEV_devCustomProps_table"))),
ip: ($('#NEWDEV_devLastIP').val()),
createNew: createNew
}, function(msg) {
showMessage(msg);
// Remove navigation prompt "Are you sure you want to leave..."
window.onbeforeunload = null;
somethingChanged = false;
// Remove navigation prompt "Are you sure you want to leave..."
window.onbeforeunload = null;
somethingChanged = false;
// refresh API
updateApi("devices,appevents")
// refresh API
updateApi("devices,appevents");
// Callback fuction
if (typeof refreshCallback == 'function') {
refreshCallback(direction);
}
// Callback function
if (typeof refreshCallback == 'function') {
refreshCallback(direction);
}
// Everything loaded
hideSpinner();
});
// everything loaded
hideSpinner();
});
}
//-----------------------------------------------------------------------------------

View File

@@ -227,11 +227,7 @@ function initializeCalendar() {
loading: function( isLoading, view ) {
if (isLoading) {
showSpinner()
} else {
// setTimeout(() => {
// updateIconPreview($('#txtIcon'))
// }, 500);
} else {
hideSpinner()
}
}

View File

@@ -65,6 +65,48 @@
<?php } ?>
<!-- Delete Events -->
<h4 class=""><i class="fa-solid fa-bell"></i>
<?= lang("DevDetail_button_DeleteEvents") ?>
</h4>
<h5 class="">
<?= lang("DevDetail_button_DeleteEvents_Warning") ?>
</h5>
<br>
<div style="width:100%; text-align: center; margin-bottom: 50px;">
<button type="button"
class="btn btn-default pa-btn pa-btn-delete"
style="margin-left:0px;"
id="btnDeleteEvents"
onclick="askDeleteDeviceEvents()">
<?= lang('DevDetail_button_DeleteEvents');?>
</button>
<br>
<div id="wol_output" style="margin-top: 10px;"></div>
</div>
<!-- Reset Custom Proprties -->
<h4 class=""><i class="fa-solid fa-list"></i>
<?= lang("DevDetail_CustomProperties_Title") ?>
</h4>
<h5 class="">
<?= lang("DevDetail_CustomProps_reset_info") ?>
</h5>
<br>
<div style="width:100%; text-align: center; margin-bottom: 50px;">
<button type="button"
class="btn btn-default pa-btn pa-btn-delete"
style="margin-left:0px;"
id="btnDeleteEvents"
onclick="askResetDeviceProps()">
<?= lang("Gen_Reset") ?>
</button>
<br>
<div id="wol_output" style="margin-top: 10px;"></div>
</div>
<!-- SPEEDTEST -->
<?php if ($_REQUEST["mac"] == "Internet") { ?>
<h4 class=""><i class="fa-solid fa-gauge-high"></i>
@@ -336,6 +378,56 @@
return devicesList;
}
// ----------------------------------------------------------------
// -----------------------------------------------------------------------------
function askDeleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device Events
showModalWarning ('<?= lang('DevDetail_button_DeleteEvents');?>', '<?= lang('DevDetail_button_DeleteEvents_Warning');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteDeviceEvents');
}
function deleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Delete device events
$.get('php/server/devices.php?action=deleteDeviceEvents&mac='+ mac, function(msg) {
showMessage (msg);
});
}
// -----------------------------------------------------------------------------
function askResetDeviceProps () {
// Check MAC
if (mac == '') {
return;
}
// Ask Resert Custom properties
showModalWarning ('<?= lang('Gen_Reset');?>', '<?= lang('DevDetail_CustomProps_reset_info');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'resetDeviceProps');
}
function resetDeviceProps () {
// Check MAC
if (mac == '') {
return;
}
// Execute
$.get('php/server/devices.php?action=resetDeviceProps&mac='+ mac, function(msg) {
showMessage (msg);
});
}
// ----------------------------------------------------------------
function internetinfo() {
$( "#internetinfooutput" ).empty();

View File

@@ -144,6 +144,10 @@ function main () {
//initialize the table headers in the correct order
var availableColumns = getSettingOptions("UI_device_columns").split(",");
headersDefaultOrder = availableColumns.map(val => getString(val));
console.log(headersDefaultOrder);
var selectedColumns = JSON.parse(getSetting("UI_device_columns").replace(/'/g, '"'));
// generate default order lists of given length
@@ -181,6 +185,9 @@ function main () {
// mapping the default order to the user specified one
function mapIndx(oldIndex)
{
// console.log(oldIndex);
// console.log(tableColumnOrder);
for(i=0;i<tableColumnOrder.length;i++)
{
if(tableColumnOrder[i] == oldIndex)
@@ -359,7 +366,8 @@ function mapColumnIndexToFieldName(index, tableColumnVisible) {
"devSSID",
"devSourcePlugin",
"devPresentLastScan",
"devAlertDown"
"devAlertDown",
"devCustomProps"
];
// console.log("OrderBy: " + columnNames[tableColumnOrder[index]]);
@@ -469,6 +477,7 @@ function initializeDatatable (status) {
devStatus
devParentChildrenCount
devIpLong
devCustomProps
}
count
}
@@ -535,7 +544,8 @@ function initializeDatatable (status) {
device.devSSID || "",
device.devSourcePlugin || "",
device.devPresentLastScan || "",
device.devAlertDown || ""
device.devAlertDown || "",
device.devCustomProps || ""
];
const newRow = [];
@@ -652,6 +662,17 @@ function initializeDatatable (status) {
}
}
},
// Custom Properties
{targets: [mapIndx(26)],
'createdCell': function (td, cellData, rowData, row, col) {
if (!emptyArr.includes(cellData)){
$(td).html (`<span>${renderCustomProps(cellData, rowData[mapIndx(11)])}<span>`);
} else {
$(td).html ('');
}
}
},
// Favorite
{targets: [mapIndx(4)],
@@ -830,34 +851,88 @@ function multiEditDevices()
// -----------------------------------------------------------------------------
// Function collects shown devices from the DataTable
function getMacsOfShownDevices() {
rows = $('#tableDevices')[0].rows
macs = []
rows = $('#tableDevices')[0].rows;
macs = [];
// var devicesDataTableData = $('#tableDevices').dataTable().fnGetData();
var devicesDataTableData = $('#tableDevices').DataTable().rows({ selected: false, page: 'current' }).data().toArray();
console.log(devicesDataTableData);
var selectedDevices = [];
// first row is the heading, skip
for (var i = 1; i < rows.length; i++) {
// console.log(rows[i]._DT_RowIndex);
var rowIndex = rows[i]._DT_RowIndex;
selectedDevices.push(devicesDataTableData[rows[i]._DT_RowIndex]);
}
// Ensure the rowIndex is valid and within bounds of devicesDataTableData
if (rowIndex >= 0 && rowIndex < devicesDataTableData.length) {
selectedDevices.push(devicesDataTableData[rowIndex]);
} else {
console.log(`Invalid rowIndex: ${rowIndex} at row ${i}`);
}
}
for (var j = 0; j < selectedDevices.length; j++) {
macs.push(selectedDevices[j][mapIndx(11)]); // mapIndx(11) == MAC
// Ensure that selectedDevices[j] is not undefined
if (selectedDevices[j]) {
macs.push(selectedDevices[j][mapIndx(11)]); // mapIndx(11) == MAC
} else {
console.log(`selectedDevices[${j}] is undefined`);
}
}
return macs;
}
// -----------------------------------------------------------------------------
// Handle custom actions/properties on a device
// -----------------------------------------------------------------------------
// Handle custom actions/properties on a device
function renderCustomProps(custProps, mac) {
// Decode and parse the custom properties
// console.log(custProps);
const props = JSON.parse(atob(custProps));
let html = "";
props.forEach((propGroup, index) => {
const propMap = Object.fromEntries(
propGroup.map(prop => Object.entries(prop)[0]) // Convert array of objects to key-value pairs
);
if (propMap["CUSTPROP_show"] === true) { // Render if visible
let onClickEvent = "";
switch (propMap["CUSTPROP_type"]) {
case "show_notes":
onClickEvent = `showModalOK('${propMap["CUSTPROP_name"]}','${propMap["CUSTPROP_notes"]}')`;
break;
case "link":
onClickEvent = `window.location.href='${propMap["CUSTPROP_args"]}';`;
break;
case "link_new_tab":
onClickEvent = `openInNewTab('${propMap["CUSTPROP_args"]}')`;
break;
case "run_plugin":
onClickEvent = `alert('Not implemented')`;
break;
case "delete_dev":
onClickEvent = `deleteDeviceByMac('${mac}')`;
break;
default:
break;
}
html += `<span class="pointer" onclick="${onClickEvent}" title="${propMap["CUSTPROP_name"]} ${propMap["CUSTPROP_args"]}"> ${atob(propMap["CUSTPROP_icon"])} </span>`;
}
});
return html;
}
// -----------------------------------------------------------------------------
// Update cache with shown devices before navigating away

View File

@@ -171,7 +171,7 @@ function cacheSettings()
}
// -----------------------------------------------------------------------------
// Get a setting value by key
// Get a setting options value by key
function getSettingOptions (key) {
// handle initial load to make sure everything is set-up and cached
@@ -353,6 +353,30 @@ function getLangCode() {
// String utilities
// -----------------------------------------------------------------------------
// ----------------------------------------------------
/**
* Replaces double quotes within single-quoted strings, then converts all single quotes to double quotes,
* while preserving the intended structure.
*
* @param {string} inputString - The input string to process.
* @returns {string} - The processed string with transformations applied.
*/
function processQuotes(inputString) {
// Step 1: Replace double quotes within single-quoted strings
let tempString = inputString.replace(/'([^']*?)'/g, (match, p1) => {
const escapedContent = p1.replace(/"/g, '_escaped_double_quote_'); // Temporarily replace double quotes
return `'${escapedContent}'`;
});
// Step 2: Replace all single quotes with double quotes
tempString = tempString.replace(/'/g, '"');
// Step 3: Restore escaped double quotes
const processedString = tempString.replace(/_escaped_double_quote_/g, "'");
return processedString;
}
// ----------------------------------------------------
function jsonSyntaxHighlight(json) {
if (typeof json != 'string') {

47
front/js/device.js Executable file
View File

@@ -0,0 +1,47 @@
// -----------------------------------------------------------------------------
function askDeleteDevice () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device
showModalWarning ('Delete Device', 'Are you sure you want to delete this device?<br>(maybe you prefer to archive it)',
getString('Gen_Cancel'), getString('Gen_Delete'), 'deleteDevice');
}
// -----------------------------------------------------------------------------
function deleteDevice () {
// Check MAC
if (mac == '') {
return;
}
// Delete device
$.get('php/server/devices.php?action=deleteDevice&mac='+ mac, function(msg) {
showMessage (msg);
});
// refresh API
updateApi("devices,appevents")
}
// -----------------------------------------------------------------------------
function deleteDeviceByMac (mac) {
// Check MAC
if (mac == '') {
return;
}
// Delete device
$.get('php/server/devices.php?action=deleteDevice&mac='+ mac, function(msg) {
showMessage (msg);
});
// refresh API
updateApi("devices,appevents")
}

View File

@@ -245,6 +245,68 @@ function settingsCollectedCorrectly(settingsArray, settingsJSON_DB) {
// Manipulating Editable List options
// -------------------------------------------------------------------
// ---------------------------------------------------------
// Add row to datatable
function addDataTableRow(el)
{
alert("a")
}
// ---------------------------------------------------------
// Clone datatable row
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
$(clonedRow).attr("my-index",table.rows().count())
console.log(clonedRow);
// Add the cloned row to the DataTable
table.row.add(clonedRow[0]).draw();
}
// ---------------------------------------------------------
// Remove current datatable row
function removeDataTableRow(el) {
console.log(el);
const id = "NEWDEV_devCustomProps_table"; // Your table ID
const table = $('#'+id).DataTable();
if(table.rows().count() > 1)
{
// 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);
// Remove the row from the DataTable
table.row(row).remove().draw();
}
else
{
showMessage (getString("CustProps_cant_remove"), 3000, "modal_red");
}
}
// ---------------------------------------------------------
// Add item to list
function addList(element, clearInput = true) {
@@ -551,7 +613,7 @@ function overrideToggle(element) {
}
// ---------------------------------------------------------
// Generate options or set options based on the provided parameters
function generateOptionsOrSetOptions(
setKey,
@@ -559,14 +621,21 @@ function generateOptionsOrSetOptions(
placeholder, // ID of the HTML element where dropdown should be rendered (will be replaced)
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
transformers = [], // Transformers to be applied to the values
overrideOptions = null // override options if available
) {
// console.log(setKey);
// NOTE {value} options to replace with a setting or SQL value are handled in the cacheSettings() function
options = arrayToObject(createArray(getSettingOptions(setKey)))
// console.log(overrideOptions);
// console.log( getSettingOptions(setKey));
// console.log( setKey);
// NOTE {value} options to replace with a setting or SQL value are handled in the cacheSettings() function
options = arrayToObject(createArray(overrideOptions ? overrideOptions : getSettingOptions(setKey)))
// Call to render lists
renderList(
options,
@@ -655,6 +724,7 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
let onChange = "console.log('onChange - Not implemented');";
let customParams = "";
let customId = "";
let columns = [];
elementOptions.forEach((option) => {
@@ -708,6 +778,9 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
if (option.customId) {
customId = option.customId;
}
if (option.columns) {
columns = option.columns;
}
});
if (transformers.includes("sha256")) {
@@ -730,7 +803,8 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
onClick,
onChange,
customParams,
customId
customId,
columns
};
};
@@ -863,7 +937,7 @@ function genListWithInputSet(options, valuesArray, targetField, transformers, pl
// ------------------------------------------------------------------------------
// Generate the form control for setting
function generateFormHtml(set, overrideValue) {
function generateFormHtml(settingsData, set, overrideValue, overrideOptions, originalSetKey) {
let inputHtml = '';
@@ -872,13 +946,13 @@ function generateFormHtml(set, overrideValue) {
const setType = set['setType'];
// console.log(setType);
// console.log(setTypeEscaped); // Final transformed result
// console.log(setKey);
// console.log(overrideValue);
// console.log(inVal);
// console.log(inVal);
// Parse the setType JSON string
const setTypeObject = JSON.parse(setType.replace(/'/g, '"'));
const setTypeObject = JSON.parse(processQuotes(setType))
const dataType = setTypeObject.dataType;
const elements = setTypeObject.elements || [];
@@ -903,14 +977,12 @@ function generateFormHtml(set, overrideValue) {
onClick,
onChange,
customParams,
customId
customId,
columns
} = handleElementOptions(setKey, elementOptions, transformers, inVal);
// Override value
const val = valRes;
// console.log(val);
let val = valRes;
// Generate HTML based on elementType
switch (elementType) {
@@ -921,20 +993,21 @@ function generateFormHtml(set, overrideValue) {
inputHtml += `<select onChange="settingsChanged();${onChange}"
my-data-type="${dataType}"
my-editable="${editable}"
class="form-control ${addCss}"
class="form-control ${addCss} ${cssClasses}"
name="${setKey}"
id="${setKey}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
${multi}>
<option value="" id="${setKey + "_temp_"}"></option>
</select>`;
generateOptionsOrSetOptions(setKey, createArray(val), `${setKey}_temp_`, generateOptions, null, transformers);
generateOptionsOrSetOptions(setKey, createArray(val), `${setKey}_temp_`, generateOptions, null, transformers, overrideOptions);
break;
case 'input':
const checked = val === 'True' || val === '1' ? 'checked' : '';
const checked = val === 'True' || val === 'true' || val === '1' ? 'checked' : '';
const inputClass = inputType === 'checkbox' ? 'checkbox' : 'form-control';
inputHtml += `<input
@@ -943,6 +1016,7 @@ function generateFormHtml(set, overrideValue) {
my-data-type="${dataType}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
id="${setKey}${suffix}"
type="${inputType}"
value="${val}"
@@ -957,6 +1031,7 @@ function generateFormHtml(set, overrideValue) {
class="btn btn-primary ${cssClasses}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
my-input-from="${sourceIds}"
my-input-to="${setKey}"
onclick="${onClick}">
@@ -969,6 +1044,7 @@ function generateFormHtml(set, overrideValue) {
class="form-control input"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
my-data-type="${dataType}"
id="${setKey}"
${readOnly}>${val}</textarea>`;
@@ -979,10 +1055,115 @@ function generateFormHtml(set, overrideValue) {
class="${cssClasses}"
my-data-type="${dataType}"
my-customparams="${customParams}"
my-customid="${customId}">
${getString(getStringKey)}
my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
onclick="${onClick}">
${getString(getStringKey)}${placeholder}
</span>`;
break;
case 'datatable':
const tableId = `${setKey}_table`;
let datatableHtml = `<table id="${tableId}" class="table table-striped">`;
// Dynamic array creation
let emptyVal = [];
let columnSettings = [];
// Generate table headers
datatableHtml += '<thead><tr>';
columns.forEach(column => {
let columnSetting = getSetObject(settingsData, column.settingKey) || {};
datatableHtml += `<th>${columnSetting.setName}</th>`;
if(column.typeOverride)
{
columnSetting["setType"] = JSON.stringify(column.typeOverride);
}
if(column.optionsOverride)
{
if (column.optionsOverride.startsWith("setting.")) {
columnSetting["setOptions"] = getSetting(column.optionsOverride.replace("setting.",""));
} else {
columnSetting["setOptions"] = column.optionsOverride;
}
}
columnSettings.push(columnSetting)
// helper for if val is empty
emptyVal.push('');
});
datatableHtml += '</tr></thead>';
// Generate table body
datatableHtml += '<tbody>';
if(val.length > 0 && isBase64(val))
{
val = atob(val)
// console.log(val);
val = JSON.parse(val)
}else{
// init empty
val = [emptyVal]
}
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
let columnOverrideValue = rowData[j] && Object.values(rowData[j])[0];
if(columnOverrideValue == undefined)
{
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,
set,
columnOverrideValue.toString(),
set["setOptions"],
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;
// Initialize DataTable with jQuery
$(document).ready(() => {
$(`#${tableId}`).DataTable({
ordering: false, // Disables sorting on all columns
searching: false // Disables the search box
});
});
break;
default:
console.warn(`🟥 Unknown element type: ${elementType}`);
@@ -991,10 +1172,6 @@ function generateFormHtml(set, overrideValue) {
// Generate event HTML if applicable
let eventsHtml = '';
// console.log(setTypeObject);
// console.log(set);
const eventsList = createArray(set['setEvents']);
// inline buttons events
@@ -1019,4 +1196,68 @@ function generateFormHtml(set, overrideValue) {
return inputHtml + eventsHtml;
}
// -----------------------------------------------------
// Return the setting object by setKey
function getSetObject(settingsData, setKey) {
found = false;
result = ""
settingsData.forEach(function(set) {
if (set.setKey == setKey) {
// console.log(set);
result = set;
return;
}
});
if(result == "")
{
console.error(settingsData);
console.error(`Setting not found: ${setKey}`);
}
return result;
}
// ---------------------------------------
// Collect DataTable data
function collectTableData(tableSelector) {
const table = $(tableSelector).DataTable();
let tableData = [];
table.rows().every(function () {
const rowData = [];
const cells = $(this.node()).find('td');
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
rowData[index] = { [input.attr("my-originalsetkey")] : input.prop('checked') };
} else {
// Generic sync for other inputs (text, select, textarea)
rowData[index] = { [input.attr("my-originalsetkey")] : input.val() };
}
} else {
// Handle plain text
// rowData[index] = { [input.attr("my-originalsetkey")] : $(cell).text()};
console.log(`Nothig to collect: ${$(cell).html()}`)
}
});
tableData.push(rowData);
});
return tableData;
}

View File

@@ -98,11 +98,22 @@ function generateApiToken(elem, length) {
}
// ----------------------------------------------
// Updates the icon preview
function updateAllIconPreviews() {
$(".iconInputVal").each((index, el)=>{
updateIconPreview(el)
})
}
// ----------------------------------------------
// Updates the icon preview
function updateIconPreview(elem) {
const targetElement = $('[my-customid="NEWDEV_devIcon_preview"]');
const iconInput = $("#NEWDEV_devIcon");
const previewSpan = $(elem).parent().find(".iconPreview");
const iconInput = $(elem);
let attempts = 0;
@@ -110,10 +121,10 @@ function updateIconPreview(elem) {
let value = iconInput.val();
if (value) {
targetElement.html(atob(value));
previewSpan.html(atob(value));
iconInput.off('change input').on('change input', function () {
let newValue = $(this).val();
targetElement.html(atob(newValue));
let newValue = $(elem).val();
previewSpan.html(atob(newValue));
});
return; // Stop retrying if successful
}
@@ -449,7 +460,7 @@ function showIconSelection() {
iconDiv.addEventListener('click', () => {
selectElement.value = value; // Update the select element value
$(`#${modalId}`).modal('hide'); // Hide the modal
updateIconPreview();
updateAllIconPreviews();
});
}
});

View File

@@ -81,7 +81,7 @@
settingsData = res["data"];
excludedColumns = ["NEWDEV_devMac", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devLastNotification", "NEWDEV_devLastIP", "NEWDEV_devStaticIP", "NEWDEV_devScan", "NEWDEV_devPresentLastScan" ]
excludedColumns = ["NEWDEV_devMac", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devLastNotification", "NEWDEV_devLastIP", "NEWDEV_devStaticIP", "NEWDEV_devScan", "NEWDEV_devPresentLastScan", "NEWDEV_devCustomProps" ]
const relevantColumns = settingsData.filter(set =>
set.setGroup === "NEWDEV" &&
@@ -90,21 +90,21 @@
!set.setKey.includes("__metadata")
);
const generateSimpleForm = columns => {
const generateSimpleForm = multiEditColumns => {
const form = $('#multi-edit-form');
const numColumns = 2; // Number of columns
// Calculate number of elements per column
const elementsPerColumn = Math.ceil(columns.length / numColumns);
const elementsPerColumn = Math.ceil(multiEditColumns.length / numColumns);
// Divide columns equally
for (let i = 0; i < numColumns; i++) {
const column = $('<div>').addClass('col-md-6');
// Append form groups to the column
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) {
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, multiEditColumns.length); j++) {
const setTypeObject = JSON.parse(columns[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);
@@ -135,37 +135,38 @@
onClick,
onChange,
customParams,
customId
customId,
columns
} = handleElementOptions('none', elementOptions, transformers, val = "");
// render based on element type
if (elementType === 'select') {
targetLocation = columns[j].setKey + "_generateSetOptions"
targetLocation = multiEditColumns[j].setKey + "_generateSetOptions"
generateOptionsOrSetOptions(columns[j].setKey, [], targetLocation, generateOptions)
generateOptionsOrSetOptions(multiEditColumns[j].setKey, [], targetLocation, generateOptions, null)
console.log(columns[j].setKey)
console.log(multiEditColumns[j].setKey)
// Handle Icons as they need a preview
if(columns[j].setKey == 'NEWDEV_devIcon')
if(multiEditColumns[j].setKey == 'NEWDEV_devIcon')
{
input = `
<span class="input-group-addon iconPreview" my-customid="NEWDEV_devIcon_preview"></span>
<select class="form-control"
onChange="updateIconPreview(this)"
my-customparams="NEWDEV_devIcon,NEWDEV_devIcon_preview"
id="${columns[j].setKey}"
data-my-column="${columns[j].setKey}"
data-my-targetColumns="${columns[j].setKey.replace('NEWDEV_','')}" >
id="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option>
</select>`
} else{
input = `<select class="form-control"
id="${columns[j].setKey}"
data-my-column="${columns[j].setKey}"
data-my-targetColumns="${columns[j].setKey.replace('NEWDEV_','')}" >
id="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option>
</select>`
}
@@ -178,20 +179,20 @@
input = `<input class="${inputClass}"
id="${columns[j].setKey}"
my-customid="${columns[j].setKey}"
data-my-column="${columns[j].setKey}"
data-my-targetColumns="${columns[j].setKey.replace('NEWDEV_','')}"
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">${columns[j].setName}</label>
<label class="col-sm-3 control-label">${multiEditColumns[j].setName}</label>
<div class="col-sm-9">
<div class="input-group red-hover-border">
${input}
<span class="input-group-addon pointer red-hover-background" onclick="massUpdateField('${columns[j].setKey}');" title="${getString('Device_MultiEdit_Tooltip')}">
<span class="input-group-addon pointer red-hover-background" onclick="massUpdateField('${multiEditColumns[j].setKey}');" title="${getString('Device_MultiEdit_Tooltip')}">
<i class="fa fa-save"></i>
</span>
</div>

View File

@@ -37,6 +37,7 @@
case 'deleteEvents30': deleteEvents30(); break;
case 'deleteActHistory': deleteActHistory(); break;
case 'deleteDeviceEvents': deleteDeviceEvents(); break;
case 'resetDeviceProps': resetDeviceProps(); break;
case 'PiaBackupDBtoArchive': PiaBackupDBtoArchive(); break;
case 'PiaRestoreDBfromArchive': PiaRestoreDBfromArchive(); break;
case 'PiaPurgeDBBackups': PiaPurgeDBBackups(); break;
@@ -105,6 +106,7 @@ function getServerDeviceData() {
"devSSID" => "",
"devSyncHubNode" => "",
"devSourcePlugin" => "",
"devCustomProps" => "",
"devStatus" => "Unknown",
"devIsRandomMAC" => false,
"devSessions" => 0,
@@ -190,129 +192,131 @@ function setDeviceData() {
global $db;
// Sanitize input
$mac = quotes($_REQUEST['mac']);
$name = quotes($_REQUEST['name']);
$owner = quotes($_REQUEST['owner']);
$type = quotes($_REQUEST['type']);
$vendor = quotes($_REQUEST['vendor']);
$icon = quotes($_REQUEST['icon']);
$favorite = quotes($_REQUEST['favorite']);
$group = quotes($_REQUEST['group']);
$location = quotes($_REQUEST['location']);
$comments = quotes($_REQUEST['comments']);
$parentMac = quotes($_REQUEST['networknode']);
$parentPort = quotes($_REQUEST['networknodeport']);
$ssid = quotes($_REQUEST['ssid']);
$site = quotes($_REQUEST['networksite']);
$staticIP = quotes($_REQUEST['staticIP']);
$scancycle = quotes($_REQUEST['scancycle']);
$alertevents = quotes($_REQUEST['alertevents']);
$alertdown = quotes($_REQUEST['alertdown']);
$skiprepeated = quotes($_REQUEST['skiprepeated']);
$newdevice = quotes($_REQUEST['newdevice']);
$archived = quotes($_REQUEST['archived']);
$devFirstConnection = quotes($_REQUEST['devFirstConnection']);
$devLastConnection = quotes($_REQUEST['devLastConnection']);
$ip = quotes($_REQUEST['ip']);
$createNew = quotes($_REQUEST['createNew']);
$mac = quotes($_POST['mac']);
$name = urldecode(quotes($_POST['name']));
$owner = urldecode(quotes($_POST['owner']));
$type = urldecode(quotes($_POST['type']));
$vendor = urldecode(quotes($_POST['vendor']));
$icon = urldecode(quotes($_POST['icon']));
$favorite = quotes($_POST['favorite']);
$group = urldecode(quotes($_POST['group']));
$location = urldecode(quotes($_POST['location']));
$comments = urldecode(quotes($_POST['comments']));
$parentMac = quotes($_POST['networknode']);
$parentPort = quotes($_POST['networknodeport']);
$ssid = urldecode(quotes($_POST['ssid']));
$site = quotes($_POST['networksite']);
$staticIP = quotes($_POST['staticIP']);
$scancycle = quotes($_POST['scancycle']);
$alertevents = quotes($_POST['alertevents']);
$alertdown = quotes($_POST['alertdown']);
$skiprepeated = quotes($_POST['skiprepeated']);
$newdevice = quotes($_POST['newdevice']);
$archived = quotes($_POST['archived']);
$devFirstConnection = quotes($_POST['devFirstConnection']);
$devLastConnection = quotes($_POST['devLastConnection']);
$ip = quotes($_POST['ip']);
$devCustomProps = quotes($_POST['devCustomProps']);
$createNew = quotes($_POST['createNew']);
$devNewGuid = generateGUID();
// an update
if ($_REQUEST['createNew'] == 0)
{
// UPDATE SQL query
$sql = "UPDATE Devices SET
devName = '$name',
devOwner = '$owner',
devType = '$type',
devVendor = '$vendor',
devIcon = '$icon',
devFavorite = '$favorite',
devGroup = '$group',
devLocation = '$location',
devComments = '$comments',
devParentMAC = '$parentMac',
devParentPort = '$parentPort',
devSSID = '$ssid',
devSite = '$site',
devStaticIP = '$staticIP',
devScan = '$scancycle',
devAlertEvents = '$alertevents',
devAlertDown = '$alertdown',
devSkipRepeated = '$skiprepeated',
devIsNew = '$newdevice',
devIsArchived = '$archived'
WHERE devMac = '$mac'";
} else // an INSERT
{
$sql = "INSERT INTO Devices (
devMac,
devName,
devOwner,
devType,
devVendor,
devIcon,
devFavorite,
devGroup,
devLocation,
devComments,
devParentMAC,
devParentPort,
devSSID,
devSite,
devStaticIP,
devScan,
devAlertEvents,
devAlertDown,
devSkipRepeated,
devIsNew,
devIsArchived,
devLastConnection,
devFirstConnection,
devLastIP,
devGUID
) VALUES (
'$mac',
'$name',
'$owner',
'$type',
'$vendor',
'$icon',
'$favorite',
'$group',
'$location',
'$comments',
'$parentMac',
'$parentPort',
'$ssid',
'$site',
'$staticIP',
'$scancycle',
'$alertevents',
'$alertdown',
'$skiprepeated',
'$newdevice',
'$archived',
'$devLastConnection',
'$devFirstConnection',
'$ip',
'$devNewGuid'
)
";
// An update
if ($_POST['createNew'] == 0) {
// UPDATE SQL query
$sql = "UPDATE Devices SET
devName = '$name',
devOwner = '$owner',
devType = '$type',
devVendor = '$vendor',
devIcon = '$icon',
devFavorite = '$favorite',
devGroup = '$group',
devLocation = '$location',
devComments = '$comments',
devParentMAC = '$parentMac',
devParentPort = '$parentPort',
devSSID = '$ssid',
devSite = '$site',
devStaticIP = '$staticIP',
devScan = '$scancycle',
devAlertEvents = '$alertevents',
devAlertDown = '$alertdown',
devSkipRepeated = '$skiprepeated',
devIsNew = '$newdevice',
devIsArchived = '$archived',
devCustomProps = '$devCustomProps'
WHERE devMac = '$mac'";
} else { // An INSERT
$sql = "INSERT INTO Devices (
devMac,
devName,
devOwner,
devType,
devVendor,
devIcon,
devFavorite,
devGroup,
devLocation,
devComments,
devParentMAC,
devParentPort,
devSSID,
devSite,
devStaticIP,
devScan,
devAlertEvents,
devAlertDown,
devSkipRepeated,
devIsNew,
devIsArchived,
devLastConnection,
devFirstConnection,
devLastIP,
devGUID,
devCustomProps
) VALUES (
'$mac',
'$name',
'$owner',
'$type',
'$vendor',
'$icon',
'$favorite',
'$group',
'$location',
'$comments',
'$parentMac',
'$parentPort',
'$ssid',
'$site',
'$staticIP',
'$scancycle',
'$alertevents',
'$alertdown',
'$skiprepeated',
'$newdevice',
'$archived',
'$devLastConnection',
'$devFirstConnection',
'$ip',
'$devNewGuid',
'$devCustomProps'
)";
}
// update Data
// Execute the query
$result = $db->query($sql);
// check result
// Check the result
if ($result == TRUE) {
echo lang('BackDevices_DBTools_UpdDev');
echo lang('BackDevices_DBTools_UpdDev');
} else {
echo lang('BackDevices_DBTools_UpdDevError')."\n\n$sql \n\n". $db->lastErrorMsg();
echo lang('BackDevices_DBTools_UpdDevError')."\n\n$sql \n\n". $db->lastErrorMsg();
}
}
//------------------------------------------------------------------------------
// Delete Device
//------------------------------------------------------------------------------
@@ -389,6 +393,26 @@ function deleteDeviceEvents() {
}
}
//------------------------------------------------------------------------------
// Delete Device Properties
//------------------------------------------------------------------------------
function resetDeviceProps() {
global $db;
// sql
$sql = 'UPDATE Devices set devCustomProps = "'.getSettingValue("NEWDEV_devCustomProps").'" WHERE devMac="' . $_REQUEST['mac'] .'"';
// execute sql
$result = $db->query($sql);
// check result
if ($result == TRUE) {
echo lang('Gen_Okay');
} else {
echo lang('Gen_Error')."\n\n$sql \n\n". $db->lastErrorMsg();
}
}
//------------------------------------------------------------------------------
// Delete all devices
//------------------------------------------------------------------------------

View File

@@ -356,6 +356,8 @@ function saveSettings()
$txt .= $setKey . "='" . $val . "'\n";
} elseif ($dataType == 'integer') {
$txt .= $setKey . "=" . $settingValue . "\n";
} elseif ($dataType == 'none') {
$txt .= $setKey . "=''\n";
} elseif ($dataType == 'json') {
$txt .= $setKey . "=" . $settingValue . "\n";
} elseif ($dataType == 'boolean') {
@@ -551,6 +553,7 @@ function decodeSpecialChars($str) {
// -------------------------------------------------------------------------------------------
// used in Export CSV
function getDevicesColumns(){
$columns = ["devMac",
@@ -582,7 +585,8 @@ function getDevicesColumns(){
"devSyncHubNode",
"devSite",
"devSSID",
"devSourcePlugin"
"devSourcePlugin",
"devCustomProps"
];
return $columns;

View File

@@ -48,6 +48,7 @@
<script src="js/tests.js?v=<?php include 'php/templates/version.php'; ?>"></script>
<script src="js/db_methods.js?v=<?php include 'php/templates/version.php'; ?>"></script>
<script src="js/settings_utils.js?v=<?php include 'php/templates/version.php'; ?>"></script>
<script src="js/device.js?v=<?php include 'php/templates/version.php'; ?>"></script>

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "",
"CLEAR_NEW_FLAG_description": "",
"CLEAR_NEW_FLAG_name": "",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "",
"DAYS_TO_KEEP_EVENTS_name": "",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "",
"DevDetail_Copy_Device_Tooltip": "",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "",
"DevDetail_EveandAl_AlertAllEvents": "",
"DevDetail_EveandAl_AlertDown": "",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "",
"Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "",
"Device_TableHead_FirstSession": "",
"Device_TableHead_GUID": "",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "",
"Gen_Remove_All": "",
"Gen_Remove_Last": "",
"Gen_Reset": "",
"Gen_Restore": "",
"Gen_Run": "",
"Gen_Save": "",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Mode fosc Activat",
"CLEAR_NEW_FLAG_description": "Si està habilitat (<code>0</code> està desactivat), els dispositius marcats com <b>Nou dispositiu</b> es desmarcaran si el temps límit (especificat en hores) supera <b>Primera sessió</b>.",
"CLEAR_NEW_FLAG_name": "Netejar indicador de nou",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Això és una configuració de manteniment. Especifica el nombre de dies que es conservaran els esdeveniments. Els esdeveniments antics s'esborraran periòdicament. També aplica als esdeveniments dels Connectors (Plugins).",
"DAYS_TO_KEEP_EVENTS_name": "Esborrar esdeveniments més vells de",
"DISCOVER_PLUGINS_description": "Desactiva aquesta opció per accelerar la inicialització i l'estalvi de configuració. Quan està desactivat, els connectors no es descobreixen, i no podeu afegir nous connectors a la configuració <code>LOADED_PLUGINS</code>.",
"DISCOVER_PLUGINS_name": "Descobreix els plugins",
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copiar detalls des del dispositiu",
"DevDetail_Copy_Device_Tooltip": "Copiar detalls del dispositius des de la llista desplegable. Tot el d'aquesta pàgina es sobre-escriurà",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "Pantalla",
"DevDetail_EveandAl_AlertAllEvents": "Alertes",
"DevDetail_EveandAl_AlertDown": "Cancel·lar alerta",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Presència de dispositius",
"Device_TableHead_AlertDown": "Cancel·lar alerta",
"Device_TableHead_Connected_Devices": "Connexions",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Favorit",
"Device_TableHead_FirstSession": "Primera Sessió",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Llegit més dins el docs.",
"Gen_Remove_All": "Esborra tot",
"Gen_Remove_Last": "Esborra el darrer",
"Gen_Reset": "",
"Gen_Restore": "Executar Restaurar",
"Gen_Run": "Executar",
"Gen_Save": "Guardar",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "",
"CLEAR_NEW_FLAG_description": "",
"CLEAR_NEW_FLAG_name": "",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "",
"DAYS_TO_KEEP_EVENTS_name": "",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "",
"DevDetail_Copy_Device_Tooltip": "",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "",
"DevDetail_EveandAl_AlertAllEvents": "",
"DevDetail_EveandAl_AlertDown": "",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "",
"Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "",
"Device_TableHead_FirstSession": "",
"Device_TableHead_GUID": "",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "",
"Gen_Remove_All": "",
"Gen_Remove_Last": "",
"Gen_Reset": "",
"Gen_Restore": "",
"Gen_Run": "",
"Gen_Save": "",

View File

@@ -72,12 +72,15 @@
"BackDevices_darkmode_enabled": "Dunkler Modus aktiviert",
"CLEAR_NEW_FLAG_description": "Wenn aktiviert (<code>0</code> bedeutet deaktiviert), werden Geräte, die als <b>Neues Gerät</b> gekennzeichnet sind, wieder freigegeben, wenn das Zeitlimit (in Stunden) die Zeit ihrer <b>Ersten Sitzung</b> überschreitet.",
"CLEAR_NEW_FLAG_name": "Neues Flag löschen",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Dies ist eine Wartungseinstellung. Spezifiziert wie viele Tage Events gespeichert bleiben. Alle älteren Events werden periodisch gelöscht. Wird auch auf die Plugins History angewendet.",
"DAYS_TO_KEEP_EVENTS_name": "Ereignisse löschen, die älter sind als",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "Details von Gerät kopieren",
"DevDetail_Copy_Device_Tooltip": "Details vom Gerät aus der Dropdown-Liste kopieren. Alles auf dieser Seite wird überschrieben",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "Anzeige",
"DevDetail_EveandAl_AlertAllEvents": "Alarmereignisse",
"DevDetail_EveandAl_AlertDown": "Melde Down",
@@ -217,6 +220,7 @@
"Device_Shortcut_OnlineChart": "Gerätepräsenz im Laufe der Zeit",
"Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "Verbindungen",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Favorit",
"Device_TableHead_FirstSession": "Erste Sitzung",
"Device_TableHead_GUID": "GUID",
@@ -317,6 +321,7 @@
"Gen_ReadDocs": "Mehr in der Dokumentation.",
"Gen_Remove_All": "Alle entfernen",
"Gen_Remove_Last": "Letzte entfernen",
"Gen_Reset": "",
"Gen_Restore": "Wiederherstellen",
"Gen_Run": "Ausführen",
"Gen_Save": "Speichern",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Darkmode Enabled",
"CLEAR_NEW_FLAG_description": "If enabled (<code>0</code> is disabled), devices flagged as <b>New Device</b> will be unflagged if the time limit (specified in hours) exceeds their <b>First Session</b> time.",
"CLEAR_NEW_FLAG_name": "Clear new flag",
"CustProps_cant_remove": "Can't remove, at least one property is needed.",
"DAYS_TO_KEEP_EVENTS_description": "This is a maintenance setting. This specifies the number of days worth of event entries that will be kept. All older events will be deleted periodically. Also applies on Plugin Events History.",
"DAYS_TO_KEEP_EVENTS_name": "Delete events older than",
"DISCOVER_PLUGINS_description": "Disable this option to speed up initialization and settings saving. When disabled, plugins are not discovered, and you cannot add new plugins to the <code>LOADED_PLUGINS</code> setting.",
"DISCOVER_PLUGINS_name": "Discover plugins",
"DevDetail_Copy_Device_Title": "Copy details from device",
"DevDetail_Copy_Device_Tooltip": "Copy details from device from the dropdown list. Everything on this page will be overwritten",
"DevDetail_CustomProperties_Title": "Custom Properties",
"DevDetail_CustomProps_reset_info": "This will remove your custom properties on this device and reset them to the default value.",
"DevDetail_DisplayFields_Title": "Display",
"DevDetail_EveandAl_AlertAllEvents": "Alert Events",
"DevDetail_EveandAl_AlertDown": "Alert Down",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Device presence",
"Device_TableHead_AlertDown": "Alert Down",
"Device_TableHead_Connected_Devices": "Connections",
"Device_TableHead_CustomProps": "Props / Actions",
"Device_TableHead_Favorite": "Favorite",
"Device_TableHead_FirstSession": "First Session",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Read more in the docs.",
"Gen_Remove_All": "Remove all",
"Gen_Remove_Last": "Remove last",
"Gen_Reset": "Reset",
"Gen_Restore": "Run Restore",
"Gen_Run": "Run",
"Gen_Save": "Save",

View File

@@ -70,12 +70,15 @@
"BackDevices_darkmode_enabled": "Darkmode Activado",
"CLEAR_NEW_FLAG_description": "Si está habilitado (<code>0</code> está desactivado), los dispositivos marcados como <b>Nuevo dispositivo</b> se desmarcarán si el límite de tiempo (especificado en horas) excede su tiempo de <b>primera sesión</b>.",
"CLEAR_NEW_FLAG_name": "Eliminar la nueva bandera",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Esta es una configuración de mantenimiento. Esto especifica el número de días de entradas de eventos que se guardarán. Todos los eventos anteriores se eliminarán periódicamente.",
"DAYS_TO_KEEP_EVENTS_name": "Eliminar eventos anteriores a",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "Copiar detalles del dispositivo",
"DevDetail_Copy_Device_Tooltip": "Copiar detalles del dispositivo de la lista desplegable. Todo en esta página se sobrescribirá",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "Mostrar",
"DevDetail_EveandAl_AlertAllEvents": "Notificaciones de eventos",
"DevDetail_EveandAl_AlertDown": "Alerta de caída",
@@ -215,6 +218,7 @@
"Device_Shortcut_OnlineChart": "Presencia del dispositivo a lo largo del tiempo",
"Device_TableHead_AlertDown": "Alerta desactivada",
"Device_TableHead_Connected_Devices": "Conexiones",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Favorito",
"Device_TableHead_FirstSession": "1ra. sesión",
"Device_TableHead_GUID": "GUID",
@@ -315,6 +319,7 @@
"Gen_ReadDocs": "Lee más en los documentos.",
"Gen_Remove_All": "Quitar todo",
"Gen_Remove_Last": "Quitar el último",
"Gen_Reset": "",
"Gen_Restore": "Ejecutar restauración",
"Gen_Run": "Ejecutar",
"Gen_Save": "Guardar",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Mode sombre activé",
"CLEAR_NEW_FLAG_description": "Si activé (<code>0</code> est désactivé), les appareils marqués comme <b>Nouvel appareil</b> seront démarqués si la limite de temps (spécifiée en heures) dépasse la durée de <b>Première Session</b>.",
"CLEAR_NEW_FLAG_name": "Supprime le nouveau drapeau",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Il s'agit d'un paramètre de maintenance. Il indique le nombre de jours pendant lesquels les entrées d'événements seront conservées. Tous les événements plus anciens seront supprimés périodiquement. S'applique également à l'historique des événements du plugin.",
"DAYS_TO_KEEP_EVENTS_name": "Supprimer les événements plus anciens que",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "Copier les détails de l'appareil",
"DevDetail_Copy_Device_Tooltip": "Copier les détails de l'appareil dans la liste déroulante. Tout ce qui se trouve sur cette page sera remplacé",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "Afficher",
"DevDetail_EveandAl_AlertAllEvents": "Alerter les événements",
"DevDetail_EveandAl_AlertDown": "Alerte de panne",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Présence de l'appareil",
"Device_TableHead_AlertDown": "Alerter si En panne",
"Device_TableHead_Connected_Devices": "Connexions",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Favori",
"Device_TableHead_FirstSession": "Première session",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Plus d'infos dans la documentation.",
"Gen_Remove_All": "Enlever tous",
"Gen_Remove_Last": "Enlever le dernier",
"Gen_Reset": "",
"Gen_Restore": "Lancer une restauration",
"Gen_Run": "Lancer",
"Gen_Save": "Enregistrer",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Modalità scura abilitata",
"CLEAR_NEW_FLAG_description": "Se abilitato (<code>0</code> è disabilitato), i dispositivi contrassegnati come <b>Nuovo dispositivo</b> verranno deselezionati se il limite di tempo (specificato in ore) supera il tempo della <b>Prima sessione</b>.",
"CLEAR_NEW_FLAG_name": "Cancella nuova bandiera",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Questa è un'impostazione di manutenzione. Specifica il numero di giorni delle voci degli eventi che verranno conservati. Tutti gli eventi più vecchi verranno eliminati periodicamente. Si applica anche alla cronologia degli eventi del plugin (Plugin Events History).",
"DAYS_TO_KEEP_EVENTS_name": "Elimina eventi più vecchi di",
"DISCOVER_PLUGINS_description": "Disattiva questa opzione per velocizzare l'inizializzazione e il salvataggio delle impostazioni. Quando è disattivata, i plugin non vengono scoperti e non puoi aggiungere nuovi plugin all'impostazione <code>LOADED_PLUGINS</code>.",
"DISCOVER_PLUGINS_name": "Scopri i plugin",
"DevDetail_Copy_Device_Title": "Copia dettagli dal dispositivo",
"DevDetail_Copy_Device_Tooltip": "Copia i dettagli dal dispositivo dall'elenco a discesa. Tutto in questa pagina verrà sovrascritto",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "Visualizza",
"DevDetail_EveandAl_AlertAllEvents": "Notifica eventi",
"DevDetail_EveandAl_AlertDown": "Avviso disconnessione",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Presenza dispositivo",
"Device_TableHead_AlertDown": "Avviso disconnessione",
"Device_TableHead_Connected_Devices": "Connessioni",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Preferito",
"Device_TableHead_FirstSession": "Prima sessione",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Maggiori informazioni nella documentazione.",
"Gen_Remove_All": "Rimuovi tutti",
"Gen_Remove_Last": "Rimuovi ultimo",
"Gen_Reset": "",
"Gen_Restore": "Esegui ripristino",
"Gen_Run": "Esegui",
"Gen_Save": "Salva",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Mørk modus Aktivert",
"CLEAR_NEW_FLAG_description": "",
"CLEAR_NEW_FLAG_name": "",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Dette er en vedlikeholdsinnstilling. Dette spesifiserer antall dager verdt med hendelsesoppføringer som vil beholdes. Alle eldre hendelser vil bli slettet med jevne mellomrom. Gjelder også for plugin-hendelseshistorikk.",
"DAYS_TO_KEEP_EVENTS_name": "Slett hendelser eldre enn",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Kopier detaljer fra enhet",
"DevDetail_Copy_Device_Tooltip": "Kopier detaljer fra enheten via nedtrekks menyen. Alt på denne siden vil bli overskrevet",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "",
"DevDetail_EveandAl_AlertAllEvents": "Varsel Alle Hendelser",
"DevDetail_EveandAl_AlertDown": "Varsel Nede",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Enhetens tilstedeværelse",
"Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "Tilkoblinger",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Favoritt",
"Device_TableHead_FirstSession": "Første Økt",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Les mer i dokumentasjonen.",
"Gen_Remove_All": "Fjern alle",
"Gen_Remove_Last": "Fjern sist",
"Gen_Reset": "",
"Gen_Restore": "Kjør Gjenopprett",
"Gen_Run": "Kjør",
"Gen_Save": "Lagre",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Tryb ciemny Włączony",
"CLEAR_NEW_FLAG_description": "",
"CLEAR_NEW_FLAG_name": "",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "To jest ustawienie konserwacji. Określa ile dni mają być utrzymywane wpisy wydarzeń. Wszystkie starsze wpisy wydarzeń zostaną usunięte okresowo. Dotyczy także Historii Wydarzeń Pluginów.",
"DAYS_TO_KEEP_EVENTS_name": "Usuń wydarzenia starsze niż",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i>Kopiuj opis z urządzenia",
"DevDetail_Copy_Device_Tooltip": "Kopiuj opis z urządzenia z listy rozwijanej. Wszystko na tej stronie zostanie nadpisane",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "",
"DevDetail_EveandAl_AlertAllEvents": "Powiadamiaj o wszystkich wydarzeniach",
"DevDetail_EveandAl_AlertDown": "Wyłącz powiadomienia",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Obecność urządzenia",
"Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "Połączenia",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Ulubione",
"Device_TableHead_FirstSession": "Pierwsza Sesja",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Przeczytaj więcej w dokumentacji.",
"Gen_Remove_All": "Usuń wszystko",
"Gen_Remove_Last": "Usuń ostatnie",
"Gen_Reset": "",
"Gen_Restore": "Wykonaj Przywracanie",
"Gen_Run": "Wykonaj",
"Gen_Save": "Zapisz",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Modo Noturno Habilitado",
"CLEAR_NEW_FLAG_description": "Se habilitado (<code>0</code> está desativado), dispositivos marcados como<b>Novo Dispositivo</b> serão desmarcados se o limite (especificado em horas) exceder o tempo da <b>Primeira Sessão </b>.",
"CLEAR_NEW_FLAG_name": "",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Esta é uma definição de manutenção. Especifica o número de dias de entradas de eventos que serão mantidas. Todos os eventos mais antigos serão eliminados periodicamente. Também se aplica ao Histórico de eventos do plug-in.",
"DAYS_TO_KEEP_EVENTS_name": "Excluir eventos mais antigos que",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copiar detalhes do dispositivo",
"DevDetail_Copy_Device_Tooltip": "Copiar detalhes do dispositivo a partir da lista pendente. Tudo o que se encontra nesta página será substituído",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "",
"DevDetail_EveandAl_AlertAllEvents": "Alerte Todos os Eventos",
"DevDetail_EveandAl_AlertDown": "Alerta Desligado",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Presença do dispositivo",
"Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "Conexões",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Favorito",
"Device_TableHead_FirstSession": "Primeira sessão",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Leia mais em documentos.",
"Gen_Remove_All": "Remover tudo",
"Gen_Remove_Last": "Remover o último",
"Gen_Reset": "",
"Gen_Restore": "Executar restauração",
"Gen_Run": "Executar",
"Gen_Save": "Salvar",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Темный режим включен",
"CLEAR_NEW_FLAG_description": "Если этот параметр включен (<code>0</code> отключен), устройства, помеченные как <b>Новое устройство</b>, станут неотмеченными, если лимит времени, указанный в часах, превышает время их <b>первой сессии</b>.",
"CLEAR_NEW_FLAG_name": "Удалить новый флаг",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Это настройка обслуживания. Здесь указывается количество дней, в течение которых будут храниться записи о событиях. Все старые события будут периодически удаляться. Также применимо к истории событий плагина.",
"DAYS_TO_KEEP_EVENTS_name": "Удалить события старше",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "Скопировать данные с устройства",
"DevDetail_Copy_Device_Tooltip": "Скопируйте данные с устройства из раскрывающегося списка. Все на этой странице будет перезаписано",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "Дисплей",
"DevDetail_EveandAl_AlertAllEvents": "Оповещения о событиях",
"DevDetail_EveandAl_AlertDown": "Оповещение о доступности",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Присутствие устройств",
"Device_TableHead_AlertDown": "Оповещение о сост. ВЫКЛ",
"Device_TableHead_Connected_Devices": "Соединения",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "Избранное",
"Device_TableHead_FirstSession": "Первый сеанс",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Подробнее читайте в документации.",
"Gen_Remove_All": "Удалить все",
"Gen_Remove_Last": "Удалить последний",
"Gen_Reset": "",
"Gen_Restore": "Запустить восстановление",
"Gen_Run": "Запустить",
"Gen_Save": "Сохранить",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "",
"CLEAR_NEW_FLAG_description": "",
"CLEAR_NEW_FLAG_name": "",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "",
"DAYS_TO_KEEP_EVENTS_name": "",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "",
"DevDetail_Copy_Device_Tooltip": "",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "",
"DevDetail_EveandAl_AlertAllEvents": "",
"DevDetail_EveandAl_AlertDown": "",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "",
"Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "Bağlantılar",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "",
"Device_TableHead_FirstSession": "İlk Oturum",
"Device_TableHead_GUID": "",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "",
"Gen_Remove_All": "Tümünü kaldır",
"Gen_Remove_Last": "",
"Gen_Reset": "",
"Gen_Restore": "",
"Gen_Run": "Çalıştır",
"Gen_Save": "Kaydet",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "Темний режим увімкнено",
"CLEAR_NEW_FLAG_description": "Якщо ввімкнено (<code>0</code> вимкнено), пристрої, позначені як <b>Новий пристрій</b>, не будуть позначені, якщо ліміт часу (вказаний у годинах) перевищує їхній <b>Перший сеанс</b> час.",
"CLEAR_NEW_FLAG_name": "Очистити новий прапор",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "Це налаштування обслуговування. Це визначає кількість днів записів про події, які зберігатимуться. Усі старіші події періодично видалятимуться. Також застосовується до історії подій плагінів.",
"DAYS_TO_KEEP_EVENTS_name": "Видалити події, старші за",
"DISCOVER_PLUGINS_description": "Вимкніть цю опцію, щоб прискорити ініціалізацію та збереження налаштувань. Якщо вимкнено, плагіни не виявляються, і ви не можете додавати нові плагіни до параметра <code>LOADED_PLUGINS</code>.",
"DISCOVER_PLUGINS_name": "Відкрийте для себе плагіни",
"DevDetail_Copy_Device_Title": "Скопіюйте деталі з пристрою",
"DevDetail_Copy_Device_Tooltip": "Скопіюйте деталі пристрою зі спадного списку. Усе на цій сторінці буде перезаписано",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "Дисплей",
"DevDetail_EveandAl_AlertAllEvents": "Повідомлення про події",
"DevDetail_EveandAl_AlertDown": "Сповіщення вниз",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "Наявність пристрою",
"Device_TableHead_AlertDown": "Сповіщення вниз",
"Device_TableHead_Connected_Devices": "Зв'язки",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "улюблений",
"Device_TableHead_FirstSession": "Перша сесія",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "Детальніше читайте в документах.",
"Gen_Remove_All": "Видалити Все",
"Gen_Remove_Last": "Видаліть останнім",
"Gen_Reset": "",
"Gen_Restore": "Запустіть Restore",
"Gen_Run": "бігти",
"Gen_Save": "зберегти",

View File

@@ -60,12 +60,15 @@
"BackDevices_darkmode_enabled": "已启用暗黑模式",
"CLEAR_NEW_FLAG_description": "",
"CLEAR_NEW_FLAG_name": "",
"CustProps_cant_remove": "",
"DAYS_TO_KEEP_EVENTS_description": "这是维护设置。它指定将保留的事件条目的天数。所有较旧的事件将被定期删除。也适用于插件事件历史记录。",
"DAYS_TO_KEEP_EVENTS_name": "删除早于",
"DISCOVER_PLUGINS_description": "",
"DISCOVER_PLUGINS_name": "",
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> 从设备复制详细信息",
"DevDetail_Copy_Device_Tooltip": "从下拉列表中复制设备的详细信息。此页面上的所有内容都将被覆盖",
"DevDetail_CustomProperties_Title": "",
"DevDetail_CustomProps_reset_info": "",
"DevDetail_DisplayFields_Title": "",
"DevDetail_EveandAl_AlertAllEvents": "提醒所有事件",
"DevDetail_EveandAl_AlertDown": "警报关闭",
@@ -205,6 +208,7 @@
"Device_Shortcut_OnlineChart": "设备统计",
"Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "链接",
"Device_TableHead_CustomProps": "",
"Device_TableHead_Favorite": "收藏",
"Device_TableHead_FirstSession": "加入",
"Device_TableHead_GUID": "GUID",
@@ -305,6 +309,7 @@
"Gen_ReadDocs": "在文档中阅读更多内容。",
"Gen_Remove_All": "全部删除",
"Gen_Remove_Last": "删除最后一个",
"Gen_Reset": "",
"Gen_Restore": "运行恢复",
"Gen_Run": "运行",
"Gen_Save": "保存",

View File

@@ -0,0 +1,8 @@
## Overview
Settings related to the custom properties functionality on a device.
### Usage
- Head to **Settings** > **Custom Properties** to adjust the default values.

View File

@@ -0,0 +1,370 @@
{
"code_name": "custom_props",
"unique_prefix": "CUSTPROP",
"plugin_type": "system",
"enabled": true,
"data_source": "template",
"show_ui": false,
"localized": ["display_name", "description", "icon"],
"display_name": [
{
"language_code": "en_us",
"string": "Custom properties"
}
],
"description": [
{
"language_code": "en_us",
"string": "Settings related to the custom properties functionality on a device."
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa fa-list\"></i>"
}
],
"params": [],
"settings": [
{
"function": "icon",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "input",
"elementOptions": [
{ "placeholder": "Enter value" },
{ "suffix": "_in" },
{ "cssClasses": "col-sm-10" },
{ "prefillValue": "null" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": ["_in"] },
{ "separator": "" },
{ "cssClasses": "col-xs-12" },
{ "onClick": "addList(this,false)" },
{ "getStringKey": "Gen_Add" }
],
"transformers": []
},
{
"elementType": "select",
"elementHasInputValue": 1,
"elementOptions": [
{ "multiple": "true" },
{ "readonly": "true" },
{ "editable": "true" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeAllOptions(this)" },
{ "getStringKey": "Gen_Remove_All" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeFromList(this)" },
{ "getStringKey": "Gen_Remove_Last" }
],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": [
"PGkgY2xhc3M9ImZhcyBmYS10cmFzaC1hbHQiPjwvaT4=",
"PGkgY2xhc3M9ImZhcyBmYS1leHRlcm5hbC1saW5rLWFsdCI+PC9pPg==",
"PGkgY2xhc3M9ImZhcyBmYS1hcnJvdy1yaWdodCI+PC9pPg==",
"PGkgY2xhc3M9ImZhcyBmYS1zZWFyY2giPjwvaT4=",
"PGkgY2xhc3M9ImZhcyBmYS1jb2dzIj48L2k+",
"PGkgY2xhc3M9ImZhcyBmYS1leWUiPjwvaT4=",
"PGkgY2xhc3M9ImZhcyBmYS1kYXRhYmFzZSI+PC9pPg=="
],
"options": [],
"localized": ["name","description"],
"name": [
{
"language_code": "en_us",
"string": "Icon"
}
],
"description": [
{
"language_code": "en_us",
"string": "List of icons that can be used on a custom property."
}
]
},
{
"function": "type",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "input",
"elementOptions": [
{ "placeholder": "Enter value" },
{ "suffix": "_in" },
{ "cssClasses": "col-sm-10" },
{ "prefillValue": "null" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": ["_in"] },
{ "separator": "" },
{ "cssClasses": "col-xs-12" },
{ "onClick": "addList(this,false)" },
{ "getStringKey": "Gen_Add" }
],
"transformers": []
},
{
"elementType": "select",
"elementHasInputValue": 1,
"elementOptions": [
{ "multiple": "true" },
{ "readonly": "true" },
{ "editable": "true" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeAllOptions(this)" },
{ "getStringKey": "Gen_Remove_All" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeFromList(this)" },
{ "getStringKey": "Gen_Remove_Last" }
],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": [
"none",
"data",
"link",
"link_new_tab",
"show_notes",
"delete_dev",
"run_plugin"
],
"options": [],
"localized": ["name","description"],
"name": [
{
"language_code": "en_us",
"string": "Type"
}
],
"description": [
{
"language_code": "en_us",
"string": "List of property types. The default ones have specific functionality associated with it."
}
]
},
{
"function": "args",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": "",
"options": [],
"localized": ["name","description"],
"name": [
{
"language_code": "en_us",
"string": "Arguments"
}
],
"description": [
{
"language_code": "en_us",
"string": "Arguments for the property type."
}
]
},
{
"function": "name",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": "",
"options": [],
"localized": ["name","description"],
"name": [
{
"language_code": "en_us",
"string": "Name"
}
],
"description": [
{
"language_code": "en_us",
"string": "Name of the property displayed on hover."
}
]
},
{
"function": "notes",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": "",
"options": [],
"localized": ["name","description"],
"name": [
{
"language_code": "en_us",
"string": "Notes"
}
],
"description": [
{
"language_code": "en_us",
"string": "Additional notes."
}
]
},
{
"function": "show",
"type": {
"dataType": "boolean",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "checkbox" }],
"transformers": []
}
]
},
"default_value": true,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Show"
}
],
"description": [
{
"language_code": "en_us",
"string": "Show in device list."
}
]
},
{
"function": "actions",
"type": {
"dataType": "none",
"elements": [
{
"elementType": "span",
"elementOptions": [
{
"cssClasses": "input-group-addon pointer actionIcon"
},
{
"placeholder": "<i class=\"far fa-trash-alt\"></i>"
},
{
"onClick": "removeDataTableRow(this)"
}
],
"transformers": []
},
{
"elementType": "span",
"elementOptions": [
{
"cssClasses": "input-group-addon pointer actionIcon"
},
{
"placeholder": "<i class=\"far fa-clone\"></i>"
},
{
"onClick": "cloneDataTableRow(this)"
}
],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": [
],
"options": [],
"localized": ["name","description"],
"name": [
{
"language_code": "en_us",
"string": "Action"
}
],
"description": [
{
"language_code": "en_us",
"string": "Actions on the property."
}
]
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -347,7 +347,8 @@
"Device_TableHead_LastIP",
"Device_TableHead_Status",
"Device_TableHead_MAC_full",
"Device_TableHead_SourcePlugin"
"Device_TableHead_SourcePlugin",
"Device_TableHead_CustomProps"
],
"options": [
"Device_TableHead_Name",
@@ -375,7 +376,8 @@
"Device_TableHead_SSID",
"Device_TableHead_SourcePlugin",
"Device_TableHead_PresentLastScan",
"Device_TableHead_AlertDown"
"Device_TableHead_AlertDown",
"Device_TableHead_CustomProps"
],
"localized": ["name", "description"],
"name": [

View File

@@ -501,7 +501,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
}
// INPUT
inputFormHtml = generateFormHtml(set, valIn);
inputFormHtml = generateFormHtml(settingsData, set, valIn, null, null);
// construct final HTML for the setting
setHtml += inputFormHtml + overrideHtml + `
@@ -565,13 +565,12 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setType = set["setType"]
setCodeName = set["setKey"]
console.log(prefix);
// console.log(prefix);
const setTypeObject = JSON.parse(setType.replace(/'/g, '"'));
const setTypeObject = JSON.parse(processQuotes(setType))
// console.log(setTypeObject);
const dataType = setTypeObject.dataType;
// get the element with the input value(s)
let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
@@ -602,12 +601,18 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
onClick,
onChange,
customParams,
customId
customId,
columns
} = handleElementOptions('none', elementOptions, transformers, val = "");
let value;
if (dataType === "string" ||
if (dataType === "string" && elementWithInputValue.elementType === "datatable" ) {
value = collectTableData(`#${setCodeName}_table`)
settingsArray.push([prefix, setCodeName, dataType, btoa(JSON.stringify(value))]);
} else if (dataType === "string" ||
(dataType === "integer" && (inputType === "number" || inputType === "text"))) {
value = $('#' + setCodeName).val();
@@ -646,11 +651,17 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
}
});
}
value = JSON.stringify(temps);
settingsArray.push([prefix, setCodeName, dataType, value]);
} else if (dataType === "none") {
// no value to save
value = ""
settingsArray.push([prefix, setCodeName, dataType, value]);
} else if (dataType === "json") {
value = $('#' + setCodeName).val();
@@ -675,7 +686,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
console.log(settingsArray);
console.log( JSON.stringify(settingsArray));
// return;
// return; // 🐛 🔺
// trigger a save settings event in the backend
$.ajax({
method: "POST",
@@ -735,12 +746,14 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
{
// check if config file has been updated
$.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(appState["settingsImported"]*1000)
importedMiliseconds = parseInt((appState["settingsImported"]*1000));
humanReadable = (new Date(importedMiliseconds)).toLocaleString("en-UK", { timeZone: "<?php echo $timeZone?>" });
// check if displayed settings are outdated
if(appState["showSpinner"] || fileModificationTime > importedMiliseconds)
@@ -754,6 +767,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
checkInitialization();
}
humanReadable = (new Date(importedMiliseconds)).toLocaleString("en-UK", { timeZone: "<?php echo $timeZone?>" });
document.getElementById('lastImportedTime').innerHTML = humanReadable;
})

View File

@@ -59,6 +59,7 @@ sql_devices_all = """
IFNULL(devSSID, '') AS devSSID,
IFNULL(devSyncHubNode, '') AS devSyncHubNode,
IFNULL(devSourcePlugin, '') AS devSourcePlugin,
IFNULL(devCustomProps, '') AS devCustomProps,
CASE
WHEN devIsNew = 1 THEN 'New'
WHEN devPresentLastScan = 1 THEN 'On-line'

View File

@@ -475,6 +475,20 @@ class DB():
self.sql.execute(""" DROP VIEW IF EXISTS Sessions_Devices;""")
self.sql.execute("""CREATE VIEW Sessions_Devices AS SELECT * FROM Sessions LEFT JOIN "Devices" ON ses_MAC = devMac;""")
# add fields if missing
# devCustomProps column
devCustomProps_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='devCustomProps'
""").fetchone()[0] == 0
if devCustomProps_missing :
mylog('verbose', ["[upgradeDB] Adding devCustomProps to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "devCustomProps" TEXT
""")
# -------------------------------------------------------------------------
# Settings table setup

View File

@@ -226,7 +226,8 @@ def create_new_devices (db):
devGroup,
devComments,
devLogEvents,
devLocation"""
devLocation,
devCustomProps"""
newDevDefaults = f"""{get_setting_value('NEWDEV_devAlertEvents')},
{get_setting_value('NEWDEV_devAlertDown')},
@@ -240,7 +241,9 @@ def create_new_devices (db):
'{sanitize_SQL_input(get_setting_value('NEWDEV_devGroup'))}',
'{sanitize_SQL_input(get_setting_value('NEWDEV_devComments'))}',
{get_setting_value('NEWDEV_devLogEvents')},
'{sanitize_SQL_input(get_setting_value('NEWDEV_devLocation'))}'"""
'{sanitize_SQL_input(get_setting_value('NEWDEV_devLocation'))}',
'{sanitize_SQL_input(get_setting_value('NEWDEV_devCustomProps'))}'
"""
# Fetch data from CurrentScan skipping ignored devices by IP and MAC
query = f"""SELECT cur_MAC, cur_Name, cur_Vendor, cur_ScanMethod, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type

View File

@@ -62,6 +62,7 @@ class Device(ObjectType):
devSSID = String()
devSyncHubNode = String()
devSourcePlugin = String()
devCustomProps = String()
devStatus = String()
devIsRandomMac = Int()
devParentChildrenCount = Int()

View File

@@ -202,7 +202,7 @@ def importConfigs (db, all_plugins):
# Plugins START
# -----------------
necessary_plugins = ['UI', 'DBCLNP', 'INTRNT','MAINT','NEWDEV', 'SETPWD', 'SYNC', 'VNDRPDT', 'WORKFLOWS']
necessary_plugins = ['UI', 'CUSTPROP', 'DBCLNP', 'INTRNT','MAINT','NEWDEV', 'SETPWD', 'SYNC', 'VNDRPDT', 'WORKFLOWS']
# make sure necessary plugins are loaded
conf.LOADED_PLUGINS += [plugin for plugin in necessary_plugins if plugin not in conf.LOADED_PLUGINS]

View File

@@ -29,7 +29,7 @@ def test_updateSubnets():
assert len(result) == 2
# -------------------------------------------------------------------------------
# Function to insert 10,000 random device entries
# Function to insert N random device entries
def insert_devices(db_path, num_entries=1):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
@@ -85,9 +85,10 @@ def insert_devices(db_path, num_entries=1):
devSite,
devSSID,
devSyncHubNode,
devSourcePlugin
devSourcePlugin,
devCustomProps
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
"""
# List of device types, vendors, groups, locations
@@ -128,6 +129,7 @@ def insert_devices(db_path, num_entries=1):
dev_ssid = "" # Left as NULL
dev_sync_hub_node = "" # Left as NULL
dev_source_plugin = "" # Left as NULL
dev_devCustomProps = "" # Left as NULL
# Execute the insert query
cursor.execute(insert_query, (
@@ -160,7 +162,8 @@ def insert_devices(db_path, num_entries=1):
dev_site,
dev_ssid,
dev_sync_hub_node,
dev_source_plugin
dev_source_plugin,
dev_devCustomProps
))
# Commit after every 1000 rows to improve performance