Notification Report page rewrite v0.1📩

This commit is contained in:
Jokob-sk
2023-10-25 22:35:07 +11:00
parent 0ed24dac0a
commit fd162ff98a
13 changed files with 245 additions and 115 deletions

View File

@@ -585,8 +585,24 @@ height: 50px;
.infobox_label { .infobox_label {
font-size: 16px !important; font-size: 16px !important;
} }
/* --------------------------------------------------------- */
/* report */
/* --------------------------------------------------------- */
/*settings*/ #notificationData textarea{
width: 100%;
}
#notificationData pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
.string { color: green; }
.number { color: darkorange; }
.boolean { color: blue; }
.null { color: magenta; }
.key { color: red; }
/* --------------------------------------------------------- */
/* settings */
/* --------------------------------------------------------- */
@media (max-width: 767px) { @media (max-width: 767px) {
/* hide on mobile */ /* hide on mobile */

View File

@@ -304,6 +304,32 @@ function showMessage (textMessage="") {
} }
// -----------------------------------------------------------------------------
// String utilities
// -----------------------------------------------------------------------------
function jsonSyntaxHighlight(json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2);
}
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// General utilities // General utilities
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -20,21 +20,21 @@ if(array_key_exists('function', $_REQUEST) != FALSE)
{ {
$FUNCTION = $_REQUEST['function']; $FUNCTION = $_REQUEST['function'];
} }
if(array_key_exists('settings', $_REQUEST) != FALSE)
{
$SETTINGS = $_REQUEST['settings'];
}
// call functions based on requested params // call functions based on requested params
switch ($FUNCTION) { switch ($FUNCTION) {
case 'savesettings': case 'savesettings':
saveSettings(); saveSettings();
break; break;
case 'cleanLog': case 'cleanLog':
if(array_key_exists('settings', $_REQUEST) != FALSE)
{
$SETTINGS = $_REQUEST['settings'];
}
cleanLog($SETTINGS); cleanLog($SETTINGS);
break; break;

View File

@@ -452,7 +452,7 @@
"run_event_tooltip" : "Enable the setting and save your changes at first before you run it.", "run_event_tooltip" : "Enable the setting and save your changes at first before you run it.",
"run_event_icon" : "fa-play", "run_event_icon" : "fa-play",
"general_event_title" : "Executing an ad-hoc event", "general_event_title" : "Executing an ad-hoc event",
"general_event_description" : " The event you nove triggered might take a while until background processes finish. The execution ended once you see <code>finished</code> below. Check the <a href='/maintenance.php#tab_Logging'>error log</a> if you didn not get the expected result. <br/> <br/> Status: ", "general_event_description" : " The event you nove triggered might take a while until background processes finish. The execution ended once the below execution queue empties (Check the <a href='/maintenance.php#tab_Logging'>error log</a> if you envounter issues). <br/> <br/> Execution queue:",
"Plugins_Unprocessed_Events" : "Unprocessed Events", "Plugins_Unprocessed_Events" : "Unprocessed Events",
"Plugins_Objects" : "Plugin Objects", "Plugins_Objects" : "Plugin Objects",
"Plugins_DeleteAll" : "Delete all (filters are ignored)", "Plugins_DeleteAll" : "Delete all (filters are ignored)",
@@ -602,7 +602,9 @@
"Systeminfo_System_System": "System:", "Systeminfo_System_System": "System:",
"Systeminfo_System_Uname": "Uname:", "Systeminfo_System_Uname": "Uname:",
"Systeminfo_System_Uptime": "Uptime:", "Systeminfo_System_Uptime": "Uptime:",
"Systeminfo_USB_Devices" : "USB Devices", "Systeminfo_USB_Devices" : "USB Devices",
"report_select_format": "Select Format:",
"report_time": "Notification time:",
"Donations_Title" : "Donations", "Donations_Title" : "Donations",
"Donations_Text" : "Hey 👋! </br> Thanks for clicking on this menu item 😅 </br> </br> I'm trying to collect some donations to make you better software. Also, it would help me not to get burned out. Me burning out might mean end of support for this app. Any small (recurring or not) sponsorship makes me want ot put more effort into this app. I don't want to lock features (new plugins) behind paywalls 🔐. </br> Currently, I'm waking up 2h before work so I contribute to the app a bit. If I had some recurring income I could shorten my workweek and in the remaining time fully focus on PiAlert. You'd get more functionality, a more polished app and less bugs. </br> </br> Thanks for reading - I'm super grateful for any support ❤🙏 </br> </br> TL;DR: By supporting me you get: </br> </br> <ul><li>Regular updates to keep your data and family safe 🔄</li><li>Less bugs 🐛🔫</li><li>Better and more functionality</li><li>I don't get burned out 🔥🤯</li><li>Less rushed releases 💨</li><li>Better docs📚</li><li>Quicker and better support with issues 🆘</li><li>Less grumpy me 😄</li></ul> </br> 📧Email me to <a href='mailto:jokob@duck.com?subject=PiAlert'>jokob@duck.com</a> if you want to get in touch or if I should add other sponsorship platforms. </br>", "Donations_Text" : "Hey 👋! </br> Thanks for clicking on this menu item 😅 </br> </br> I'm trying to collect some donations to make you better software. Also, it would help me not to get burned out. Me burning out might mean end of support for this app. Any small (recurring or not) sponsorship makes me want ot put more effort into this app. I don't want to lock features (new plugins) behind paywalls 🔐. </br> Currently, I'm waking up 2h before work so I contribute to the app a bit. If I had some recurring income I could shorten my workweek and in the remaining time fully focus on PiAlert. You'd get more functionality, a more polished app and less bugs. </br> </br> Thanks for reading - I'm super grateful for any support ❤🙏 </br> </br> TL;DR: By supporting me you get: </br> </br> <ul><li>Regular updates to keep your data and family safe 🔄</li><li>Less bugs 🐛🔫</li><li>Better and more functionality</li><li>I don't get burned out 🔥🤯</li><li>Less rushed releases 💨</li><li>Better docs📚</li><li>Quicker and better support with issues 🆘</li><li>Less grumpy me 😄</li></ul> </br> 📧Email me to <a href='mailto:jokob@duck.com?subject=PiAlert'>jokob@duck.com</a> if you want to get in touch or if I should add other sponsorship platforms. </br>",
"Donations_Platforms" : "Sponsor platforms", "Donations_Platforms" : "Sponsor platforms",

View File

@@ -127,9 +127,14 @@
"column": "Watched_Value1", "column": "Watched_Value1",
"css_classes": "col-sm-2", "css_classes": "col-sm-2",
"show": true, "show": true,
"type": "label", "type": "eval",
"default_value":"", "default_value":"",
"options": [], "options": [
{
"type": "eval",
"param": "`<a href='/report?guid=${value}'>${value}</a>`"
}
],
"localized": ["name"], "localized": ["name"],
"name":[{ "name":[{
"language_code": "en_us", "language_code": "en_us",

View File

@@ -156,9 +156,14 @@
"column": "Watched_Value1", "column": "Watched_Value1",
"css_classes": "col-sm-2", "css_classes": "col-sm-2",
"show": true, "show": true,
"type": "label", "type": "eval",
"default_value": "", "default_value":"",
"options": [], "options": [
{
"type": "eval",
"param": "`<a href='/report?guid=${value}'>${value}</a>`"
}
],
"localized": [ "localized": [
"name" "name"
], ],

View File

@@ -94,9 +94,14 @@
"column": "Watched_Value1", "column": "Watched_Value1",
"css_classes": "col-sm-3", "css_classes": "col-sm-3",
"show": true, "show": true,
"type": "label", "type": "eval",
"default_value":"", "default_value":"",
"options": [], "options": [
{
"type": "eval",
"param": "`<a href='/report?guid=${value}'>${value}</a>`"
}
],
"localized": ["name"], "localized": ["name"],
"name":[{ "name":[{
"language_code": "en_us", "language_code": "en_us",

View File

@@ -94,9 +94,14 @@
"column": "Watched_Value1", "column": "Watched_Value1",
"css_classes": "col-sm-3", "css_classes": "col-sm-3",
"show": true, "show": true,
"type": "label", "type": "eval",
"default_value":"", "default_value":"",
"options": [], "options": [
{
"type": "eval",
"param": "`<a href='/report?guid=${value}'>${value}</a>`"
}
],
"localized": ["name"], "localized": ["name"],
"name":[{ "name":[{
"language_code": "en_us", "language_code": "en_us",

View File

@@ -94,9 +94,14 @@
"column": "Watched_Value1", "column": "Watched_Value1",
"css_classes": "col-sm-3", "css_classes": "col-sm-3",
"show": true, "show": true,
"type": "label", "type": "eval",
"default_value":"", "default_value":"",
"options": [], "options": [
{
"type": "eval",
"param": "`<a href='/report?guid=${value}'>${value}</a>`"
}
],
"localized": ["name"], "localized": ["name"],
"name":[{ "name":[{
"language_code": "en_us", "language_code": "en_us",

View File

@@ -169,6 +169,14 @@ function processColumnValue(dbColumnDef, value, index, type) {
} }
} }
break; break;
case 'eval':
for (const option of dbColumnDef.options) {
if (option.type === type) {
value = eval(value);
}
}
break;
default: default:
value = value + `<div style='text-align:center' title="${getString("Plugins_no_control")}"><i class='fa-solid fa-circle-question'></i></div>` ; value = value + `<div style='text-align:center' title="${getString("Plugins_no_control")}"><i class='fa-solid fa-circle-question'></i></div>` ;

View File

@@ -30,20 +30,145 @@
<!-- Main content ---------------------------------------------------------- --> <!-- Main content ---------------------------------------------------------- -->
<section class="content"> <section class="content">
<?php <div class="col-sm-2">
// Check if the page exists <!-- Display data and navigation buttons -->
if (file_exists("api/notification_text.html")) { <div id="notificationContainer">
// Load the page <div id="navigationButtons">
include("api/notification_text.html"); <button class="btn btn-default text-gray50" id="prevButton">
} else { <i class="fa fa-chevron-left"></i>
// Display an error message </button>
echo "<h2>Error</h2>"; <span id="notificationOutOff"></span>
echo lang('REPORT_ERROR'); <button class="btn btn-default text-gray50" id="nextButton">
} <i class="fa fa-chevron-right"></i>
?> </button>
</div>
</div>
</div>
</div> <!-- Select format -->
</section> <div class="col-sm-2 ">
<label for="formatSelect">
<?= lang('report_select_format') ;?>
</label>
<select id="formatSelect" class="pointer">
<option value="HTML">HTML</option>
<option value="JSON">JSON</option>
<option value="Text">Text</option>
</select>
</div>
<div class="col-sm-8">
<label><?= lang('report_time') ;?></label>
<span id="timestamp">Timestamp</span>
</div>
<div class="col-sm-12" id="notificationData">
<!-- Data will be displayed here -->
</div>
</div>
</section>
<!-- JavaScript to fetch and display data based on selected format -->
<script>
// JavaScript to fetch and display data based on selected format
document.addEventListener('DOMContentLoaded', function() {
const notificationData = document.getElementById('notificationData');
const timestamp = document.getElementById('timestamp');
const prevButton = document.getElementById('prevButton');
const nextButton = document.getElementById('nextButton');
const formatSelect = document.getElementById('formatSelect');
let currentIndex = -1; // Current report index
// Function to update the displayed data and timestamp based on the selected format and index
function updateData(format, index) {
// Fetch data from the API endpoint
fetch('/api/table_notifications.json')
.then(response => response.json())
.then(data => {
if (index < 0) {
index = data.data.length - 1;
} else if (index >= data.data.length) {
index = 0;
}
const notification = data.data[index];
const formatData = notification[format];
// Display the selected format data and update timestamp
switch (format) {
case 'HTML':
notificationData.innerHTML = formatData;
break;
case 'JSON':
notificationData.innerHTML = `<pre class="logs" cols="70" rows="10" wrap="off" readonly="">
${jsonSyntaxHighlight(JSON.stringify(JSON.parse(formatData), undefined, 4))}
</pre>`;
break;
case 'Text':
notificationData.innerHTML = `<pre class="logs" cols="70" rows="10" wrap="off" readonly">${formatData}</pre>`;
break;
}
timestamp.textContent = notification.DateTimeCreated;
currentIndex = index;
$("#notificationOutOff").html(`${currentIndex + 1}/${data.data.length}`);
})
.catch(error => {
console.error('Error:', error);
});
}
// Function to find the index of a notification by GUID
function findIndexByGUID(data, guid) {
return data.findIndex(notification => notification.GUID === guid);
}
// Listen for format selection changes
formatSelect.addEventListener('change', () => {
updateData(formatSelect.value, currentIndex);
});
// Listen for previous button click
prevButton.addEventListener('click', () => {
updateData(formatSelect.value, currentIndex - 1);
});
// Listen for next button click
nextButton.addEventListener('click', () => {
updateData(formatSelect.value, currentIndex + 1);
});
// Check if there is a GUID query parameter
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('guid')) {
const guid = urlParams.get('guid');
fetch('/api/table_notifications.json')
.then(response => response.json())
.then(data => {
const index = findIndexByGUID(data.data, guid);
if (index !== -1) {
// Load the notification with the specified GUID
updateData(formatSelect.value, index);
}
})
.catch(error => {
console.error('Error:', error);
});
} else {
// Initial data load
updateData('HTML', -1); // Default format to HTML and load the latest report
}
});
</script>
<!-- /.content --> <!-- /.content -->
<?php <?php

View File

@@ -839,41 +839,6 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
modalEventStatusId = 'modal-message-front-event' modalEventStatusId = 'modal-message-front-event'
function handleEvent (element){
// value has to be in format event|param. e.g. run|ARPSCAN
value = $(element).attr('data-myevent') + '|'+ $(element).attr('data-myparam-plugin')
setParameter ('Front_Event', value)
// show message
showModalOk("<?= lang("general_event_title")?>", "<?= lang("general_event_description")?> <code id='"+modalEventStatusId+"'></code>");
// Periodically update state of the requested action
getParam(modalEventStatusId,"Front_Event", true, updateModalState)
updateModalState()
}
// function updateModalState(){
// setTimeout(function(){
// displayedEvent = $('#'+modalEventStatusId).html()
// // loop until finished
// if(displayedEvent.indexOf('finished') == -1) // if the message is different from finished, check again in 2s
// {
// getParam(modalEventStatusId,"Front_Event", true)
// updateModalState()
// }
// }, 2000);
// }
// -------------------------------------------------------- // --------------------------------------------------------
// Calls a backend function to add a front-end event (specified by the attributes 'data-myevent' and 'data-myparam-plugin' on the passed element) to an execution queue // Calls a backend function to add a front-end event (specified by the attributes 'data-myevent' and 'data-myparam-plugin' on the passed element) to an execution queue
function addToExecutionQueue(element) function addToExecutionQueue(element)
@@ -882,34 +847,34 @@ function addToExecutionQueue(element)
// value has to be in format event|param. e.g. run|ARPSCAN // value has to be in format event|param. e.g. run|ARPSCAN
action = `${getGuid()}|${$(element).attr('data-myevent')}|${$(element).attr('data-myparam-plugin')}` action = `${getGuid()}|${$(element).attr('data-myevent')}|${$(element).attr('data-myparam-plugin')}`
// addToExecutionQueue(action)
$.ajax({ $.ajax({
method: "POST", method: "POST",
url: "php/server/util.php", url: "php/server/util.php",
data: { function: "addToExecutionQueue", action: action }, data: { function: "addToExecutionQueue", action: action },
success: function(data, textStatus) { success: function(data, textStatus) {
showModalOk ('Result', data ); // showModalOk ('Result', data );
// show message
showModalOk(getString("general_event_title"), `${getString("general_event_description")} <br/> <br/> <code id='${modalEventStatusId}'></code>`);
updateModalState()
} }
}) })
} }
// TODO // --------------------------------------------------------
// Updating the execution queue in in modal pop-up
function updateModalState() { function updateModalState() {
setTimeout(function() { setTimeout(function() {
// Fetch the content from the log file using an AJAX request // Fetch the content from the log file using an AJAX request
$.ajax({ $.ajax({
url: '~/log/execution_queue.log', url: '/log/execution_queue.log',
type: 'GET', type: 'GET',
success: function(data) { success: function(data) {
// Update the content of the HTML element (e.g., a div with id 'logContent') // Update the content of the HTML element (e.g., a div with id 'logContent')
$('#logContent').html(data); $('#'+modalEventStatusId).html(data);
// Check if the displayed content contains 'finished' updateModalState();
if (data.indexOf('finished') === -1) {
// If not finished, continue to update
updateModalState();
}
}, },
error: function() { error: function() {
// Handle error, such as the file not being found // Handle error, such as the file not being found
@@ -919,11 +884,6 @@ function updateModalState() {
}, 2000); }, 2000);
} }
// Call the function to start the periodic updates
updateModalState();
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// handling events on the backend initiated by the front end END // handling events on the backend initiated by the front end END

View File

@@ -727,38 +727,6 @@ class plugin_object_class:
#=============================================================================== #===============================================================================
# Handling of user initialized front-end events # Handling of user initialized front-end events
#=============================================================================== #===============================================================================
#-------------------------------------------------------------------------------
# def check_and_run_user_event(db, pluginsState):
# sql = db.sql # TO-DO
# sql.execute(""" select * from Parameters where par_ID = "Front_Event" """)
# rows = sql.fetchall()
# event, param = ['','']
# if len(rows) > 0 and rows[0]['par_Value'] != 'finished':
# keyValue = rows[0]['par_Value'].split('|')
# if len(keyValue) == 2:
# event = keyValue[0]
# param = keyValue[1]
# else:
# return pluginsState
# if event == 'test':
# pluginsState = handle_test(param, db, pluginsState)
# if event == 'run':
# pluginsState = handle_run(param, db, pluginsState)
# # clear event execution flag
# sql.execute ("UPDATE Parameters SET par_Value='finished' WHERE par_ID='Front_Event'")
# # commit to DB
# db.commitDB()
# return pluginsState
def check_and_run_user_event(db, pluginsState): def check_and_run_user_event(db, pluginsState):
# Check if the log file exists # Check if the log file exists
logFile = os.path.join(logPath, "execution_queue.log") logFile = os.path.join(logPath, "execution_queue.log")