dynamic dropdown support in FE - core app feature 💠

This commit is contained in:
Jokob-sk
2024-03-10 21:50:04 +11:00
parent a66df76f74
commit e38d2f9055
17 changed files with 465 additions and 77 deletions

View File

@@ -189,12 +189,10 @@
<!-- ----------------------------------------------------------------------- -->
<!-- Datatable -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net/css/select.dataTables.min.css">
<script src="lib/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="lib/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/select/1.3.3/css/select.dataTables.min.css">
<script type="text/javascript" src="https://cdn.datatables.net/select/1.3.3/js/dataTables.select.min.js"></script>
<script src="lib/AdminLTE/bower_components/datatables.net/js/dataTables.select.min.js"></script>
<!-- page script ----------------------------------------------------------- -->
<script>

18
front/js/db_methods.js Executable file
View File

@@ -0,0 +1,18 @@
// -----------------------------------------------------------------------------
// General utilities to interact with teh database
// -----------------------------------------------------------------------------
// Read data and place intotarget location, callback processies the results
function readData(sqlQuery, processDataCallback, targetLocation) {
var apiUrl = `php/server/dbHelper.php?action=read&rawSql=${encodeURIComponent(sqlQuery)}`;
$.get(apiUrl, function(data) {
// Process the JSON data using the provided callback function
data = JSON.parse(data)
var htmlResult = processDataCallback(data);
// Place the resulting HTML into the specified placeholder div
$("#" + targetLocation).replaceWith(htmlResult);
});
}

View File

@@ -112,15 +112,42 @@ function deleteAllCookies() {
function cacheSettings()
{
$.get('api/table_settings.json?nocache=' + Date.now(), function(res) {
settingsJSON = res;
data = settingsJSON["data"];
$.get('api/table_settings.json?nocache=' + Date.now(), function(resSet) {
data.forEach((set) => {
setCache(`pia_set_${set.Code_Name}`, set.Value)
});
$.get('api/plugins.json?nocache=' + Date.now(), function(resPlug) {
pluginsData = resPlug["data"];
settingsData = resSet["data"];
settingsData.forEach((set) => {
resolvedValue = set.Value;
setPlugObj = {};
options_params = [];
setPlugObj = getPluginSettingObject(pluginsData, set.Code_Name)
if(setPlugObj != {} && setPlugObj["options_params"])
{
options_params = setPlugObj["options_params"]
}
// check if options contains parameters and resolve
if(set.Value.includes("{value}"))
{
resolvedValue = resolveParams(options_params, set.Value)
console.log(resolvedValue)
}
setCache(`pia_set_${set.Code_Name}`, resolvedValue)
});
});
})
}
@@ -146,6 +173,8 @@ function getSetting (key) {
function cacheStrings()
{
console.log("aaaaaaaaaaaaaaaaaaaaaa")
// handle core strings and translations
var allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "ru_ru", "nb_no"]; // needs to be same as in lang.php
@@ -907,7 +936,77 @@ function setupSmoothScrolling() {
}
}
// -------------------------------------------------------------------
// Function to check if options_params contains a parameter with type "sql"
function hasSqlType(params) {
for (let param of params) {
if (param.type === "sql") {
return true; // Found a parameter with type "sql"
}
}
return false; // No parameter with type "sql" found
}
// -------------------------------------------------------------------
// Function to check if string is SQL query
function isSQLQuery(query) {
// Regular expression to match common SQL keywords and syntax with word boundaries
var sqlRegex = /\b(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|FROM|JOIN|WHERE|SET|VALUES|GROUP BY|ORDER BY|LIMIT)\b/i;
return sqlRegex.test(query);
}
// -------------------------------------------------------------------
// Get corresponding plugin setting object
function getPluginSettingObject(pluginsData, setting_key, unique_prefix ) {
result = {}
unique_prefix == undefined ? unique_prefix = setting_key.split("_")[0] : unique_prefix = unique_prefix;
$.each(pluginsData, function (i, plgnObj){
// go thru plugins
if(plgnObj.unique_prefix == unique_prefix)
{
// go thru plugin settings
$.each(plgnObj["settings"], function (j, setObj){
if(`${unique_prefix}_${setObj.function}` == setting_key)
{
result = setObj
}
});
}
});
return result
}
// -------------------------------------------------------------------
// Resolve all option parameters
function resolveParams(params, template) {
params.forEach(param => {
// Check if the template includes the parameter name
if (template.includes("{" + param.name + "}")) {
// If the parameter type is 'setting', retrieve setting value
if (param.type == "setting") {
var value = getSetting(param.value);
// Replace placeholder with setting value
template = template.replace("{" + param.name + "}", value);
} else {
// If the parameter type is not 'setting', use the provided value
template = template.replace("{" + param.name + "}", param.value);
}
}
});
// Log the resolved template
console.log(template);
// Return the resolved template
return template;
}
// -----------------------------------------------------------------------------
// initialize
@@ -955,8 +1054,11 @@ var pialert_common_init = sessionStorage.getItem(sessionStorageKey) === "true";
// Define a function that will execute the code only once
function executeOnce() {
if (!pialert_common_init) {
console.log("ffffffffffffffffffffffffffffffffffffff")
showSpinner()
// Your initialization code here
@@ -964,7 +1066,8 @@ function executeOnce() {
cacheStrings();
initDeviceListAll_JSON();
// Set the flag in sessionStorage to indicate that the code has been executed and save time when last time the page for initialized
// Set the flag in sessionStorage to indicate that the code has been executed
// and save time when last time the page for initialized
sessionStorage.setItem(sessionStorageKey, "true");
const millisecondsNow = Date.now();
sessionStorage.setItem(sessionStorageKey + '_time', millisecondsNow);

View File

@@ -163,4 +163,9 @@
}
return true; // Return true if no schedules are found
}
}

View File

@@ -107,6 +107,74 @@ $(function () {
});
// -----------------------------------------------------------------------------
// Initiate dropdown
function initSettingDropdown(settingKey, targetLocation)
{
var optionsHtml = ""
var targetLocation_options = settingKey + "_initSettingDropdown"
setVal = getSetting(settingKey)
// check if the result is a SQL query
if(isSQLQuery(setVal))
{
optionsHtml += `<option id="${targetLocation_options}"></option>`;
readData(setVal, generateDropdownOptions, targetLocation_options);
} else // this should be already an array, e.g. from a setting or pre-defined
{
options = createArray(setVal);
values = createArray(set['Value']);
options.forEach(option => {
let selected = values.includes(option) ? 'selected' : '';
optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
});
// Place the resulting HTML into the specified placeholder div
$("#" + targetLocation).replaceWith(optionsHtml);
}
}
// -----------------------------------------------------------------------------
// Data processors
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Processor to generate options for a dropdown menu
function generateDropdownOptions(data) {
var optionsHtml = "";
data.forEach(function(item) {
optionsHtml += `<option value="${item.id}">${item.name}</option>`;
});
return `${optionsHtml}`;
}
// -----------------------------------------------------------------------------
// Processor to generate a list
function generateList(data) {
var listHtml = "";
data.forEach(function(item) {
listHtml += `<li>${item.name}</li>`;
});
listHtml += "";
return listHtml;
}
// -----------------------------------------------------------------------------
// initialize
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1 @@
table.dataTable tbody>tr.selected,table.dataTable tbody>tr>.selected{background-color:#b0bed9}table.dataTable.stripe tbody>tr.odd.selected,table.dataTable.stripe tbody>tr.odd>.selected,table.dataTable.display tbody>tr.odd.selected,table.dataTable.display tbody>tr.odd>.selected{background-color:#acbad4}table.dataTable.hover tbody>tr.selected:hover,table.dataTable.hover tbody>tr>.selected:hover,table.dataTable.display tbody>tr.selected:hover,table.dataTable.display tbody>tr>.selected:hover{background-color:#aab7d1}table.dataTable.order-column tbody>tr.selected>.sorting_1,table.dataTable.order-column tbody>tr.selected>.sorting_2,table.dataTable.order-column tbody>tr.selected>.sorting_3,table.dataTable.order-column tbody>tr>.selected,table.dataTable.display tbody>tr.selected>.sorting_1,table.dataTable.display tbody>tr.selected>.sorting_2,table.dataTable.display tbody>tr.selected>.sorting_3,table.dataTable.display tbody>tr>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody>tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody>tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody>tr.odd>.selected,table.dataTable.order-column.stripe tbody>tr.odd>.selected{background-color:#a6b4cd}table.dataTable.display tbody>tr.even>.selected,table.dataTable.order-column.stripe tbody>tr.even>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.selected:hover>.sorting_1,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody>tr.selected:hover>.sorting_2,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody>tr.selected:hover>.sorting_3,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_3{background-color:#a5b2cb}table.dataTable.display tbody>tr:hover>.selected,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.order-column.hover tbody>tr:hover>.selected,table.dataTable.order-column.hover tbody>tr>.selected:hover{background-color:#a2aec7}table.dataTable tbody td.select-checkbox,table.dataTable tbody th.select-checkbox{position:relative}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody td.select-checkbox:after,table.dataTable tbody th.select-checkbox:before,table.dataTable tbody th.select-checkbox:after{display:block;position:absolute;top:1.2em;left:50%;width:12px;height:12px;box-sizing:border-box}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody th.select-checkbox:before{content:" ";margin-top:-6px;margin-left:-6px;border:1px solid black;border-radius:3px}table.dataTable tr.selected td.select-checkbox:after,table.dataTable tr.selected th.select-checkbox:after{content:"✓";font-size:20px;margin-top:-19px;margin-left:-6px;text-align:center;text-shadow:1px 1px #b0bed9,-1px -1px #b0bed9,1px -1px #b0bed9,-1px 1px #b0bed9}table.dataTable.compact tbody td.select-checkbox:before,table.dataTable.compact tbody th.select-checkbox:before{margin-top:-12px}table.dataTable.compact tr.selected td.select-checkbox:after,table.dataTable.compact tr.selected th.select-checkbox:after{margin-top:-16px}div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:.5em}@media screen and (max-width: 640px){div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0;display:block}}

View File

@@ -0,0 +1,38 @@
/*!
Copyright 2015-2021 SpryMedia Ltd.
This source file is free software, available under the following license:
MIT license - http://datatables.net/license/mit
This source file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
For details please refer to: http://www.datatables.net/extensions/select
Select for DataTables 1.3.3
2015-2021 SpryMedia Ltd - datatables.net/license/mit
*/
(function(h){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(q){return h(q,window,document)}):"object"===typeof exports?module.exports=function(q,t){q||(q=window);t&&t.fn.dataTable||(t=require("datatables.net")(q,t).$);return h(t,q,q.document)}:h(jQuery,window,document)})(function(h,q,t,n){function E(a,b,c){var d=function(g,f){if(g>f){var k=f;f=g;g=k}var l=!1;return a.columns(":visible").indexes().filter(function(p){p===g&&(l=!0);return p===f?(l=!1,!0):l})};var e=
function(g,f){var k=a.rows({search:"applied"}).indexes();if(k.indexOf(g)>k.indexOf(f)){var l=f;f=g;g=l}var p=!1;return k.filter(function(u){u===g&&(p=!0);return u===f?(p=!1,!0):p})};a.cells({selected:!0}).any()||c?(d=d(c.column,b.column),c=e(c.row,b.row)):(d=d(0,b.column),c=e(0,b.row));c=a.cells(c,d).flatten();a.cells(b,{selected:!0}).any()?a.cells(c).deselect():a.cells(c).select()}function A(a){var b=a.settings()[0]._select.selector;h(a.table().container()).off("mousedown.dtSelect",b).off("mouseup.dtSelect",
b).off("click.dtSelect",b);h("body").off("click.dtSelect"+a.table().node().id.replace(/[^a-zA-Z0-9\-_]/g,"-"))}function F(a){var b=h(a.table().container()),c=a.settings()[0],d=c._select.selector,e;b.on("mousedown.dtSelect",d,function(g){if(g.shiftKey||g.metaKey||g.ctrlKey)b.css("-moz-user-select","none").one("selectstart.dtSelect",d,function(){return!1});q.getSelection&&(e=q.getSelection())}).on("mouseup.dtSelect",d,function(){b.css("-moz-user-select","")}).on("click.dtSelect",d,function(g){var f=
a.select.items();if(e){var k=q.getSelection();if((!k.anchorNode||h(k.anchorNode).closest("table")[0]===a.table().node())&&k!==e)return}k=a.settings()[0];var l=a.settings()[0].oClasses.sWrapper.trim().replace(/ +/g,".");if(h(g.target).closest("div."+l)[0]==a.table().container()&&(l=a.cell(h(g.target).closest("td, th")),l.any())){var p=h.Event("user-select.dt");r(a,p,[f,l,g]);p.isDefaultPrevented()||(p=l.index(),"row"===f?(f=p.row,B(g,a,k,"row",f)):"column"===f?(f=l.index().column,B(g,a,k,"column",
f)):"cell"===f&&(f=l.index(),B(g,a,k,"cell",f)),k._select_lastCell=p)}});h("body").on("click.dtSelect"+a.table().node().id.replace(/[^a-zA-Z0-9\-_]/g,"-"),function(g){!c._select.blurable||h(g.target).parents().filter(a.table().container()).length||0===h(g.target).parents("html").length||h(g.target).parents("div.DTE").length||x(c,!0)})}function r(a,b,c,d){if(!d||a.flatten().length)"string"===typeof b&&(b+=".dt"),c.unshift(a),h(a.table().node()).trigger(b,c)}function I(a){var b=a.settings()[0];if(b._select.info&&
b.aanFeatures.i&&"api"!==a.select.style()){var c=a.rows({selected:!0}).flatten().length,d=a.columns({selected:!0}).flatten().length,e=a.cells({selected:!0}).flatten().length,g=function(f,k,l){f.append(h('<span class="select-item"/>').append(a.i18n("select."+k+"s",{_:"%d "+k+"s selected",0:"",1:"1 "+k+" selected"},l)))};h.each(b.aanFeatures.i,function(f,k){k=h(k);f=h('<span class="select-info"/>');g(f,"row",c);g(f,"column",d);g(f,"cell",e);var l=k.children("span.select-info");l.length&&l.remove();
""!==f.text()&&k.append(f)})}}function J(a){var b=new m.Api(a);a.aoRowCreatedCallback.push({fn:function(c,d,e){d=a.aoData[e];d._select_selected&&h(c).addClass(a._select.className);c=0;for(e=a.aoColumns.length;c<e;c++)(a.aoColumns[c]._select_selected||d._selected_cells&&d._selected_cells[c])&&h(d.anCells[c]).addClass(a._select.className)},sName:"select-deferRender"});b.on("preXhr.dt.dtSelect",function(c,d){if(d===b.settings()[0]){var e=b.rows({selected:!0}).ids(!0).filter(function(f){return f!==n}),
g=b.cells({selected:!0}).eq(0).map(function(f){var k=b.row(f.row).id(!0);return k?{row:k,column:f.column}:n}).filter(function(f){return f!==n});b.one("draw.dt.dtSelect",function(){b.rows(e).select();g.any()&&g.each(function(f){b.cells(f.row,f.column).select()})})}});b.on("draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt",function(){I(b)});b.on("destroy.dtSelect",function(){b.rows({selected:!0}).deselect();A(b);b.off(".dtSelect")})}function G(a,b,c,d){var e=a[b+"s"]({search:"applied"}).indexes();
d=h.inArray(d,e);var g=h.inArray(c,e);if(a[b+"s"]({selected:!0}).any()||-1!==d){if(d>g){var f=g;g=d;d=f}e.splice(g+1,e.length);e.splice(0,d)}else e.splice(h.inArray(c,e)+1,e.length);a[b](c,{selected:!0}).any()?(e.splice(h.inArray(c,e),1),a[b+"s"](e).deselect()):a[b+"s"](e).select()}function x(a,b){if(b||"single"===a._select.style)a=new m.Api(a),a.rows({selected:!0}).deselect(),a.columns({selected:!0}).deselect(),a.cells({selected:!0}).deselect()}function B(a,b,c,d,e){var g=b.select.style(),f=b.select.toggleable(),
k=b[d](e,{selected:!0}).any();if(!k||f)"os"===g?a.ctrlKey||a.metaKey?b[d](e).select(!k):a.shiftKey?"cell"===d?E(b,e,c._select_lastCell||null):G(b,d,e,c._select_lastCell?c._select_lastCell[d]:null):(a=b[d+"s"]({selected:!0}),k&&1===a.flatten().length?b[d](e).deselect():(a.deselect(),b[d](e).select())):"multi+shift"==g?a.shiftKey?"cell"===d?E(b,e,c._select_lastCell||null):G(b,d,e,c._select_lastCell?c._select_lastCell[d]:null):b[d](e).select(!k):b[d](e).select(!k)}function y(a,b){return function(c){return c.i18n("buttons."+
a,b)}}function C(a){a=a._eventNamespace;return"draw.dt.DT"+a+" select.dt.DT"+a+" deselect.dt.DT"+a}function K(a,b){return-1!==h.inArray("rows",b.limitTo)&&a.rows({selected:!0}).any()||-1!==h.inArray("columns",b.limitTo)&&a.columns({selected:!0}).any()||-1!==h.inArray("cells",b.limitTo)&&a.cells({selected:!0}).any()?!0:!1}var m=h.fn.dataTable;m.select={};m.select.version="1.3.3";m.select.init=function(a){var b=a.settings()[0],c=b.oInit.select,d=m.defaults.select;c=c===n?d:c;d="row";var e="api",g=!1,
f=!0,k=!0,l="td, th",p="selected",u=!1;b._select={};!0===c?(e="os",u=!0):"string"===typeof c?(e=c,u=!0):h.isPlainObject(c)&&(c.blurable!==n&&(g=c.blurable),c.toggleable!==n&&(f=c.toggleable),c.info!==n&&(k=c.info),c.items!==n&&(d=c.items),e=c.style!==n?c.style:"os",u=!0,c.selector!==n&&(l=c.selector),c.className!==n&&(p=c.className));a.select.selector(l);a.select.items(d);a.select.style(e);a.select.blurable(g);a.select.toggleable(f);a.select.info(k);b._select.className=p;h.fn.dataTable.ext.order["select-checkbox"]=
function(z,L){return this.api().column(L,{order:"index"}).nodes().map(function(H){return"row"===z._select.items?h(H).parent().hasClass(z._select.className):"cell"===z._select.items?h(H).hasClass(z._select.className):!1})};!u&&h(a.table().node()).hasClass("selectable")&&a.select.style("os")};h.each([{type:"row",prop:"aoData"},{type:"column",prop:"aoColumns"}],function(a,b){m.ext.selector[b.type].push(function(c,d,e){d=d.selected;var g=[];if(!0!==d&&!1!==d)return e;for(var f=0,k=e.length;f<k;f++){var l=
c[b.prop][e[f]];(!0===d&&!0===l._select_selected||!1===d&&!l._select_selected)&&g.push(e[f])}return g})});m.ext.selector.cell.push(function(a,b,c){b=b.selected;var d=[];if(b===n)return c;for(var e=0,g=c.length;e<g;e++){var f=a.aoData[c[e].row];(!0===b&&f._selected_cells&&!0===f._selected_cells[c[e].column]||!(!1!==b||f._selected_cells&&f._selected_cells[c[e].column]))&&d.push(c[e])}return d});var v=m.Api.register,w=m.Api.registerPlural;v("select()",function(){return this.iterator("table",function(a){m.select.init(new m.Api(a))})});
v("select.blurable()",function(a){return a===n?this.context[0]._select.blurable:this.iterator("table",function(b){b._select.blurable=a})});v("select.toggleable()",function(a){return a===n?this.context[0]._select.toggleable:this.iterator("table",function(b){b._select.toggleable=a})});v("select.info()",function(a){return a===n?this.context[0]._select.info:this.iterator("table",function(b){b._select.info=a})});v("select.items()",function(a){return a===n?this.context[0]._select.items:this.iterator("table",
function(b){b._select.items=a;r(new m.Api(b),"selectItems",[a])})});v("select.style()",function(a){return a===n?this.context[0]._select.style:this.iterator("table",function(b){b._select.style=a;b._select_init||J(b);var c=new m.Api(b);A(c);"api"!==a&&F(c);r(new m.Api(b),"selectStyle",[a])})});v("select.selector()",function(a){return a===n?this.context[0]._select.selector:this.iterator("table",function(b){A(new m.Api(b));b._select.selector=a;"api"!==b._select.style&&F(new m.Api(b))})});w("rows().select()",
"row().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("row",function(c,d){x(c);c.aoData[d]._select_selected=!0;h(c.aoData[d].nTr).addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",["row",b[d]],!0)});return this});w("columns().select()","column().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("column",function(c,d){x(c);c.aoColumns[d]._select_selected=!0;d=(new m.Api(c)).column(d);h(d.header()).addClass(c._select.className);
h(d.footer()).addClass(c._select.className);d.nodes().to$().addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",["column",b[d]],!0)});return this});w("cells().select()","cell().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("cell",function(c,d,e){x(c);d=c.aoData[d];d._selected_cells===n&&(d._selected_cells=[]);d._selected_cells[e]=!0;d.anCells&&h(d.anCells[e]).addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",
["cell",b.cells(b[d]).indexes().toArray()],!0)});return this});w("rows().deselect()","row().deselect()",function(){var a=this;this.iterator("row",function(b,c){b.aoData[c]._select_selected=!1;b._select_lastCell=null;h(b.aoData[c].nTr).removeClass(b._select.className)});this.iterator("table",function(b,c){r(a,"deselect",["row",a[c]],!0)});return this});w("columns().deselect()","column().deselect()",function(){var a=this;this.iterator("column",function(b,c){b.aoColumns[c]._select_selected=!1;var d=
new m.Api(b),e=d.column(c);h(e.header()).removeClass(b._select.className);h(e.footer()).removeClass(b._select.className);d.cells(null,c).indexes().each(function(g){var f=b.aoData[g.row],k=f._selected_cells;!f.anCells||k&&k[g.column]||h(f.anCells[g.column]).removeClass(b._select.className)})});this.iterator("table",function(b,c){r(a,"deselect",["column",a[c]],!0)});return this});w("cells().deselect()","cell().deselect()",function(){var a=this;this.iterator("cell",function(b,c,d){c=b.aoData[c];c._selected_cells[d]=
!1;c.anCells&&!b.aoColumns[d]._select_selected&&h(c.anCells[d]).removeClass(b._select.className)});this.iterator("table",function(b,c){r(a,"deselect",["cell",a[c]],!0)});return this});var D=0;h.extend(m.ext.buttons,{selected:{text:y("selected","Selected"),className:"buttons-selected",limitTo:["rows","columns","cells"],init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){d.enable(K(a,c))});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}},selectedSingle:{text:y("selectedSingle",
"Selected single"),className:"buttons-selected-single",init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){var e=a.rows({selected:!0}).flatten().length+a.columns({selected:!0}).flatten().length+a.cells({selected:!0}).flatten().length;d.enable(1===e)});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}},selectAll:{text:y("selectAll","Select all"),className:"buttons-select-all",action:function(){this[this.select.items()+"s"]().select()}},selectNone:{text:y("selectNone",
"Deselect all"),className:"buttons-select-none",action:function(){x(this.settings()[0],!0)},init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){var e=a.rows({selected:!0}).flatten().length+a.columns({selected:!0}).flatten().length+a.cells({selected:!0}).flatten().length;d.enable(0<e)});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}}});h.each(["Row","Column","Cell"],function(a,b){var c=b.toLowerCase();m.ext.buttons["select"+b+"s"]={text:y("select"+
b+"s","Select "+c+"s"),className:"buttons-select-"+c+"s",action:function(){this.select.items(c)},init:function(d){var e=this;d.on("selectItems.dt.DT",function(g,f,k){e.active(k===c)})}}});h(t).on("preInit.dt.dtSelect",function(a,b){"dt"===a.namespace&&m.select.init(new m.Api(b))});return m.select});

View File

@@ -784,17 +784,28 @@ function performLogManage() {
// --------------------------------------------------------
function scrollDown()
{
var areaIDs = ['pialert_log', 'pialert_front_log', 'IP_changes_log', 'stdout_log', 'stderr_log', 'pialert_pholus_log', 'pialert_pholus_lastrun_log', 'pialert_php_log'];
var anchor = getUrlAnchor()
for (let i = 0; i < areaIDs.length; i++) {
var tempArea = $('#' + areaIDs[i]);
if (anchor == "tab_Logging")
{
setTimeout(() => {
var areaIDs = ['pialert_log', 'pialert_front_log', 'IP_changes_log', 'stdout_log', 'stderr_log', 'pialert_pholus_log', 'pialert_pholus_lastrun_log', 'pialert_php_log'];
if (tempArea.length > 0)
{
$(tempArea[0]).scrollTop(tempArea[0].scrollHeight);
}
for (let i = 0; i < areaIDs.length; i++) {
var tempArea = $('#' + areaIDs[i]);
if (tempArea.length > 0)
{
$(tempArea[0]).scrollTop(tempArea[0].scrollHeight);
}
}
}, 55);
}
}
@@ -900,12 +911,6 @@ function initializeTabs () {
// events on tab change
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href") // activated tab
// scroll to the latest log entrie sat teh bottom of the file
if(target == "#tab_Logging")
{
scrollDown();
}
});
}, 50);
@@ -918,17 +923,20 @@ window.onload = function asyncFooter()
{
initializeSelectedColumns();
scrollDown();
initializeTabs();
$("#lastCommit").append('<a href="https://github.com/jokob-sk/Pi.Alert/commits" target="_blank"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/jokob-sk/pi.alert/main?logo=github"></a>');
$("#lastDockerUpdate").append(
'<a href="https://hub.docker.com/r/jokobsk/pi.alert/tags" target="_blank"><img alt="Docker last pushed" src="https://img.shields.io/badge/dynamic/json?color=blue&label=Last%20pushed&query=last_updated&url=https%3A%2F%2Fhub.docker.com%2Fv2%2Frepositories%2Fjokobsk%2Fpi.alert%2F&logo=docker&?link=http://left&link=https://hub.docker.com/repository/docker/jokobsk/pi.alert"></a>');
'<a href="https://github.com/jokob-sk/Pi.Alert/releases" target="_blank"><img alt="Docker last pushed" src="https://img.shields.io/github/v/release/jokob-sk/Pi.Alert?color=0aa8d2&logoColor=fff&logo=GitHub&label=Latest"></a>');
}
// scroll to the latest log entrie sat teh bottom of the file
scrollDown();
</script>
<link rel="stylesheet" href="lib/AdminLTE/bower_components/select2/dist/css/select2.min.css">

View File

@@ -1,3 +1,5 @@
<div class="col-md-12">
<div class="box box-default">
@@ -65,7 +67,7 @@
settingsData = res["data"];
excludedColumns = ["NEWDEV_dev_MAC", "NEWDEV_dev_FirstConnection", "NEWDEV_dev_LastConnection", "dev_LastNotification", "NEWDEV_dev_LastIP", "NEWDEV_dev_StaticIP", "NEWDEV_dev_ScanCycle", "NEWDEV_dev_PresentLastScan" ]
excludedColumns = ["NEWDEV_dev_MAC", "NEWDEV_dev_FirstConnection", "NEWDEV_dev_LastConnection", "NEWDEV_dev_LastNotification", "NEWDEV_dev_LastIP", "NEWDEV_dev_StaticIP", "NEWDEV_dev_ScanCycle", "NEWDEV_dev_PresentLastScan" ]
const relevantColumns = settingsData.filter(set =>
set.Group === "NEWDEV" &&
@@ -88,36 +90,69 @@
// Append form groups to the column
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) {
const inputType = columns[j].Type === 'integer.checkbox' ? 'checkbox' : 'text';
let inputType;
switch (columns[j].Type) {
case 'integer.checkbox':
case 'checkbox':
inputType = 'checkbox';
break;
case 'text.select':
inputType = 'text.select';
break;
default:
inputType = 'text';
break;
}
// Add classes specifically for checkboxes
if (inputType === 'text.select') {
targetLocation = columns[j].Code_Name + "_initSettingDropdown"
initSettingDropdown(columns[j].Code_Name, targetLocation)
input = `<select>
<option id="${targetLocation}"></option>
</select>`
} else {
// Add classes specifically for checkboxes
if (inputType === 'checkbox') {
inputClass = 'checkbox';
inputClass = 'checkbox';
} else {
inputClass = 'form-control';
inputClass = 'form-control';
}
input = `<input class="${inputClass}"
id="${columns[j].Code_Name}"
data-my-column="${columns[j].Code_Name}"
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}"
type="${inputType}">`
}
const inputEntry = `<div class="form-group col-sm-12" >
<label class="col-sm-3 control-label">${columns[j].Display_Name}</label>
<div class="col-sm-9">
<div class="input-group red-hover-border">
<input class="${inputClass}" id="${columns[j].Code_Name}" data-my-column="${columns[j].Code_Name}" data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}" type="${inputType}">
<span class="input-group-addon pointer red-hover-background" onclick="massUpdateField('${columns[j].Code_Name}');" title="${getString('Device_MultiEdit_Tooltip')}">
<i class="fa fa-save"></i>
</span>
</div>
const inputEntry = `<div class="form-group col-sm-12" >
<label class="col-sm-3 control-label">${columns[j].Display_Name}</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].Code_Name}');" title="${getString('Device_MultiEdit_Tooltip')}">
<i class="fa fa-save"></i>
</span>
</div>
</div>`
</div>
</div>`
column.append(inputEntry);
column.append(inputEntry);
}
form.append(column);
}
};
console.log(relevantColumns)
generateSimpleForm(relevantColumns);
@@ -219,4 +254,5 @@ getData();
<!-- ----------------------------------------------------------------------- -->
<script src="js/ui_components.js"></script>
<script src="js/ui_components.js"></script>
<script src="js/db_methods.js"></script>

View File

@@ -56,6 +56,10 @@
$columns = $_REQUEST['columns'];
}
if (isset ($_REQUEST['rawSql'])) {
$rawSql = $_REQUEST['rawSql'];
}
if (isset ($_REQUEST['dbtable'])) {
$dbtable = $_REQUEST['dbtable'];
}
@@ -64,19 +68,56 @@
if (isset ($_REQUEST['action']) && !empty ($_REQUEST['action'])) {
$action = $_REQUEST['action'];
switch ($action) {
case 'create': create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values ); break;
// case 'read' : read($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
case 'update': update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
case 'create': create($defaultValue, $expireMinutes, $dbtable, $columns, $values ); break;
case 'read' : read($rawSql); break;
case 'update': update($columnName, $id, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
case 'delete': delete($columnName, $id, $dbtable); break;
default: logServerConsole ('Action: '. $action); break;
}
}
//------------------------------------------------------------------------------
// read
//------------------------------------------------------------------------------
function read($rawSql) {
global $db;
// Construct the SQL query to select values
$sql = $rawSql;
// Execute the SQL query
$result = $db->query($sql);
// Check if the query executed successfully
if (! $result == TRUE) {
// Output an error message if the query failed
echo "Error reading data\n\n " .$sql." \n\n". $db->lastErrorMsg();
return;
} else
{
// Output $result
// Fetching rows from the result object and storing them in an array
$rows = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$rows[] = $row;
}
// Converting the array to JSON
$json = json_encode($rows);
// Outputting the JSON
echo $json;
return;
}
}
//------------------------------------------------------------------------------
// update
//------------------------------------------------------------------------------
function update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
function update($columnName, $id, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
global $db;
@@ -138,7 +179,7 @@ function update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $db
$changes = $db->changes();
if ($changes == 0) {
// Insert new value
create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values);
create( $defaultValue, $expireMinutes, $dbtable, $columns, $values);
}
// update cache
@@ -152,7 +193,7 @@ function update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $db
//------------------------------------------------------------------------------
// create
//------------------------------------------------------------------------------
function create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values)
function create( $defaultValue, $expireMinutes, $dbtable, $columns, $values)
{
global $db;
@@ -211,7 +252,7 @@ function delete($columnName, $id, $dbtable)
// Check if the query executed successfully
if (! $result == TRUE) {
// Output an error message if the query failed
echo "Error deleting entry\n\n$sql \n\n". $db->lastErrorMsg();
echo "Error deleting entry\n\n".$sql." \n\n". $db->lastErrorMsg();
return;
} else
{

View File

@@ -294,7 +294,7 @@ if ($ENABLED_DARKMODE === True) {
</a>
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="maintenance.php#tab_Settings" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_Settings");?> </a>
<a href="maintenance.php#tab_Settings" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_UISettings");?> </a>
</li>
<li>
<a href="maintenance.php#tab_DBTools" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_Tools");?> </a>

0
front/php/templates/language/es_es.json Normal file → Executable file
View File

View File

@@ -648,6 +648,7 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
| See below for information on `threshold`, `replace`. | |
| | |
| `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. |
| `options_params` Property | Used in conjunction with a `"default_value": "{value}"` template and `text.select`. Can specify SQL query or Setting to populate the dropdown. Check example below. |
| `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is used for the value background color if it's less than the specified `maximum` but more than the previous one in the `options` array. |
| `replace` | The `options` array contains objects with an `equals` property, which is compared to the "value." If the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value". |
| `regex` | Applies a regex to the value. The `options` array contains objects with an `type` (must be set to `regex`) and `param` (must contain the regex itself) property. |
@@ -666,6 +667,21 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
> Supports chaining. You can chain multiple resolvers with `.`. For example `regex.url_http_https`. This will apply the `regex` resolver and then the `url_http_https` resolver.
```json
"options_params" : [
{
"name" : "value",
"type" : "sql",
"value" : "SELECT Dev_Name as name, dev_MAC as id FROM Devices WHERE EXISTS (SELECT 1 FROM Settings WHERE Code_Name = 'NETWORK_DEVICE_TYPES' AND LOWER(value) LIKE '%' || LOWER(dev_DeviceType) || '%' AND dev_DeviceType <> '')"
},
{
"name" : "target_macs",
"type" : "setting",
"value" : "KNWN_target_macs"
}
],
```
```json
{

View File

@@ -204,6 +204,8 @@ def publish_sensor(mqtt_client, sensorConfig):
global mqtt_sensors
icon = "mdi:" + sensorConfig.icon
message = {
"name" : sensorConfig.sensorName,
"state_topic" : "system-sensors/"+sensorConfig.sensorType+'/'+sensorConfig.deviceId+"/state",
@@ -215,7 +217,7 @@ def publish_sensor(mqtt_client, sensorConfig):
"manufacturer" : "PiAlert",
"name" : sensorConfig.deviceName
},
"icon":"mdi:'+sensorConfig.icon+'"
"icon": icon
}
topic='homeassistant/'+sensorConfig.sensorType+'/'+sensorConfig.deviceId+'/'+sensorConfig.sensorName+'/config'

View File

@@ -124,10 +124,22 @@
},
{
"function": "dev_DeviceType",
"type": "string",
"type": "text.select",
"maxLength": 30,
"default_value": "",
"default_value": "{value}",
"options": [],
"options_params" : [
{
"name" : "value",
"type" : "sql",
"value" : "SELECT DISTINCT dev_DeviceType as id, dev_DeviceType as name FROM Devices "
},
{
"name" : "uilang",
"type" : "setting",
"value" : "UI_LANG"
}
],
"localized": ["name", "description"],
"name": [
{
@@ -492,9 +504,21 @@
},
{
"function": "dev_Network_Node_MAC_ADDR",
"type": "string",
"default_value": "",
"type": "text.select",
"default_value": "{value}",
"options": [],
"options_params" : [
{
"name" : "value",
"type" : "sql",
"value" : "SELECT Dev_Name as name, dev_MAC as id FROM Devices WHERE EXISTS (SELECT 1 FROM Settings WHERE Code_Name = 'NETWORK_DEVICE_TYPES' AND LOWER(value) LIKE '%' || LOWER(dev_DeviceType) || '%' AND dev_DeviceType <> '')"
},
{
"name" : "target_macs",
"type" : "setting",
"value" : "KNWN_target_macs"
}
],
"localized": ["name", "description"],
"name": [
{

View File

@@ -55,6 +55,8 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
<script src="js/pialert_common.js"></script>
<script src="js/settings_utils.js"></script>
<script src="js/db_methods.js"></script>
<script src="js/ui_components.js"></script>
<div id="settingsPage" class="content-wrapper">
@@ -394,11 +396,11 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
if(setType.includes(".select"))
{
inputHtml = generateInputOptions(set, inputHtml, isMultiSelect = false)
inputHtml = generateInputOptions(pluginsData, set, inputHtml, isMultiSelect = false)
} else if(setType.includes(".multiselect"))
{
inputHtml = generateInputOptions(set, inputHtml, isMultiSelect = true)
inputHtml = generateInputOptions(pluginsData, set, inputHtml, isMultiSelect = true)
} else{
// if it's overridable set readonly accordingly
@@ -427,7 +429,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
inputHtml = `<input onChange="settingsChanged()" my-data-type="${setType}" class="checkbox" id="${codeName}" type="checkbox" value="${val}" ${checked} ${disabled}/>`;
} else if (setType === 'integer.select') {
inputHtml = generateInputOptions(set, inputHtml)
inputHtml = generateInputOptions(pluginsData, set, inputHtml)
} else if (setType === 'subnets') {
inputHtml = `
@@ -523,26 +525,56 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
}
// ---------------------------------------------------------
// generate a list of options for a input select
function generateInputOptions(set, input, isMultiSelect = false)
function generateInputOptions(pluginsData, set, input, isMultiSelect = false)
{
prefix = set["Group"]
var optionsHtml = ""
multi = isMultiSelect ? "multiple" : "";
input = `<select onChange="settingsChanged()" my-data-type="${set['Type']}" class="form-control" name="${set['Code_Name']}" id="${set['Code_Name']}" ${multi}>`;
values = createArray(set['Value']);
options = createArray(set['Options']);
tmpOptions = set['Options']
setVal = getSetting(set['Code_Name'] )
// check if the result is a SQL query
if(isSQLQuery(setVal))
{
var targetLocation = set['Code_Name'] + "_initSettingDropdown";
optionsHtml += `<option id="${targetLocation}"></option>`;
options.forEach(option => {
let selected = values.includes(option) ? 'selected' : '';
input += `<option value="${option}" ${selected}>${option}</option>`;
});
console.log(set['Code_Name'] )
console.log(setVal )
input += '</select>';
initSettingDropdown(set['Code_Name'] , targetLocation)
} else // this should be already an array, e.g. from a setting or pre-defined
{
options = createArray(tmpOptions);
values = createArray(set['Value']);
options.forEach(option => {
let selected = values.includes(option) ? 'selected' : '';
optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
});
}
input += `
<select onChange="settingsChanged()"
my-data-type="${set['Type']}"
class="form-control"
name="${set['Code_Name']}"
id="${set['Code_Name']}" ${multi}>
${optionsHtml}
</select>`;
return input;
}
@@ -702,10 +734,8 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
}
});
})
}
}