feat: authoritative plugin fields

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2026-01-25 11:40:29 +11:00
parent 27f7bfd129
commit 96e4909bf0
8 changed files with 364 additions and 39 deletions

View File

@@ -174,6 +174,12 @@ $db->close();
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_del_allevents30_text');?></div>
</div>
<div class="db_info_table_row">
<div class="db_tools_table_cell_a" >
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnUnlockFields" onclick="askUnlockFields()"><?= lang('Maintenance_Tool_UnlockFields');?></button>
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_UnlockFields_text');?></div>
</div>
<div class="db_info_table_row">
<div class="db_tools_table_cell_a" >
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnDeleteActHistory" onclick="askDeleteActHistory()"><?= lang('Maintenance_Tool_del_ActHistory');?></button>
@@ -464,6 +470,46 @@ function deleteEvents30()
});
}
// -----------------------------------------------------------
// Unlock/clear sources
function askUnlockFields () {
// Ask
showModalWarning('<?= lang('Maintenance_Tool_UnlockFields_noti');?>', '<?= lang('Maintenance_Tool_UnlockFields_noti_text');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'unlockFields');
}
function unlockFields() {
const apiBase = getApiBase();
const apiToken = getSetting("API_TOKEN");
const url = `${apiBase}/devices/fields/unlock`;
// Payload: clear all sources for all devices and all fields
const payload = {
mac: null, // null = all devices
fields: null, // null = all tracked fields
clearAll: true // clear all source values
};
$.ajax({
url: url,
method: "POST",
contentType: "application/json",
headers: {
"Authorization": `Bearer ${apiToken}`
},
data: JSON.stringify(payload),
success: function(response) {
showMessage(response.success
? "All device fields unlocked/cleared successfully"
: (response.error || "Unknown error")
);
},
error: function(xhr, status, error) {
console.error("Error unlocking fields:", status, error);
showMessage("Error: " + (xhr.responseJSON?.error || error));
}
});
}
// -----------------------------------------------------------
// delete History
function askDeleteActHistory () {

View File

@@ -58,12 +58,18 @@
<h3 class="box-title"><?= lang('Device_MultiEdit_MassActions');?></h3>
</div>
<div class="box-body">
<div class="col-md-12">
<div class="col-md-2" style="">
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red" id="btnDeleteMAC" onclick="askDeleteSelectedDevices()"><?= lang('Maintenance_Tool_del_selecteddev');?></button>
</div>
<div class="col-md-10"><?= lang('Maintenance_Tool_del_selecteddev_text');?></div>
</div>
<div class="col-md-12">
<div class="col-md-2" style="">
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red" id="btnUnlockFieldsSelected" onclick="askUnlockFieldsSelected()"><?= lang('Maintenance_Tool_unlockFields_selecteddev');?></button>
</div>
<div class="col-md-10"><?= lang('Maintenance_Tool_del_unlockFields_selecteddev_text');?></div>
</div>
</div>
</div>
</div>
@@ -418,6 +424,77 @@ function executeAction(action, whereColumnName, key, targetColumns, newTargetCol
}
// -----------------------------------------------------------------------------
// Ask to unlock fields of selected devices
function askUnlockFieldsSelected () {
// Ask
showModalWarning(
getString('Maintenance_Tool_unlockFields_selecteddev_noti'),
getString('Gen_AreYouSure'),
getString('Gen_Cancel'),
getString('Gen_Okay'),
'unlockFieldsSelected');
}
// -----------------------------------------------------------------------------
// Unlock fields for selected devices
function unlockFieldsSelected(fields = null, clearAll = false) {
// Get selected MACs
const macs_tmp = selectorMacs(); // returns array of MACs
console.log(macs_tmp);
if (!macs_tmp || macs_tmp == "" || macs_tmp.length === 0) {
showMessage(textMessage = "No devices selected", timeout = 3000, colorClass = "modal_red")
return;
}
// API setup
const apiBase = getApiBase();
const apiToken = getSetting("API_TOKEN");
const url = `${apiBase}/devices/fields/unlock`;
// Convert string to array
const macsArray = macs_tmp.split(",").map(m => m.trim()).filter(Boolean);
const payload = {
mac: macsArray, // array of MACs for backend
fields: fields, // null for all tracked fields
clear_all: clearAll // true to clear all sources, false to clear only LOCKED/USER
};
$.ajax({
url: url,
method: "POST",
headers: { "Authorization": `Bearer ${apiToken}` },
contentType: "application/json",
data: JSON.stringify(payload),
success: function(response) {
if (response.success) {
showMessage(getString('Gen_DataUpdatedUITakesTime'));
write_notification(
`[Multi edit] Successfully unlocked fields of devices with MACs: ${macs_tmp}`,
"info"
);
} else {
write_notification(
`[Multi edit] Failed to unlock fields: ${response.error || "Unknown error"}`,
"interrupt"
);
}
},
error: function(xhr, status, error) {
console.error("Error unlocking fields:", status, error);
write_notification(
`[Multi edit] Error unlocking fields: ${xhr.responseJSON?.error || error}`,
"error"
);
}
});
}
// -----------------------------------------------------------------------------
// Ask to delete selected devices
function askDeleteSelectedDevices () {

View File

@@ -395,6 +395,10 @@
"Maintenance_Tool_DownloadConfig_text": "Download a full backup of your Settings configuration stored in the <code>app.conf</code> file.",
"Maintenance_Tool_DownloadWorkflows": "Workflows export",
"Maintenance_Tool_DownloadWorkflows_text": "Download a full backup of your Workflows stored in the <code>workflows.json</code> file.",
"Maintenance_Tool_UnlockFields": "Clear All Device Sources",
"Maintenance_Tool_UnlockFields_noti": "Clear All Device Sources",
"Maintenance_Tool_UnlockFields_noti_text": "Are you sure you want to clear all source values (LOCKED/USER) for all device fields on all devices? This action cannot be undone.",
"Maintenance_Tool_UnlockFields_text": "This tool will remove all source values from every tracked field for all devices, effectively unlocking all fields for plugins and users. Use this with caution, as it will affect your entire device inventory.",
"Maintenance_Tool_ExportCSV": "Devices export (csv)",
"Maintenance_Tool_ExportCSV_noti": "Devices export (csv)",
"Maintenance_Tool_ExportCSV_noti_text": "Are you sure you want to generate a CSV file?",
@@ -433,6 +437,9 @@
"Maintenance_Tool_del_alldev_text": "Before using this function, please make a backup. The deletion cannot be undone. All devices will be deleted from the database.",
"Maintenance_Tool_del_allevents": "Delete Events (Reset Presence)",
"Maintenance_Tool_del_allevents30": "Delete all Events older than 30 days",
"Maintenance_Tool_unlockFields_selecteddev": "Unlock device fields",
"Maintenance_Tool_unlockFields_selecteddev_noti": "Unlock fields",
"Maintenance_Tool_del_unlockFields_selecteddev_text": "This will unlock the LOCKED/USER fields of the selected devices. This action cannot be undone.",
"Maintenance_Tool_del_allevents30_noti": "Delete Events",
"Maintenance_Tool_del_allevents30_noti_text": "Are you sure you want to delete all Events older than 30 days? This resets presence of all devices.",
"Maintenance_Tool_del_allevents30_text": "Before using this function, please make a backup. The deletion cannot be undone. All events older than 30 days in the database will be deleted. At that moment the presence of all devices will be reset. This can lead to invalid sessions. This means that devices are displayed as \"present\" although they are offline. A scan while the device in question is online solves the problem.",