mirror of
https://github.com/pi-hole/web.git
synced 2025-12-26 05:26:19 +00:00
Merge branch 'devel' into patch-1
Signed-off-by: Ghalid <24234921+Ghalid@users.noreply.github.com>
This commit is contained in:
@@ -34,6 +34,11 @@ $(document).ready(function() {
|
||||
|
||||
table = $("#customDNSTable").DataTable( {
|
||||
"ajax": "scripts/pi-hole/php/customdns.php?action=get",
|
||||
columns: [
|
||||
{},
|
||||
{},
|
||||
{orderable: false, searchable: false}
|
||||
],
|
||||
"columnDefs": [ {
|
||||
"targets": 2,
|
||||
"render": function ( data, type, row ) {
|
||||
|
||||
@@ -11,6 +11,7 @@ var start__ = moment().subtract(6, "days");
|
||||
var from = moment(start__).utc().valueOf()/1000;
|
||||
var end__ = moment();
|
||||
var until = moment(end__).utc().valueOf()/1000;
|
||||
var interval = 0;
|
||||
|
||||
var timeoutWarning = $("#timeoutWarning");
|
||||
|
||||
@@ -71,7 +72,35 @@ function compareNumbers(a, b) {
|
||||
function updateQueriesOverTime() {
|
||||
$("#queries-over-time .overlay").show();
|
||||
timeoutWarning.show();
|
||||
$.getJSON("api_db.php?getGraphData&from="+from+"&until="+until, function(data) {
|
||||
|
||||
// Compute interval to obtain about 200 values
|
||||
var num = 200;
|
||||
interval = (until-from)/num;
|
||||
// Default displaying axis scaling
|
||||
timeLineChart.options.scales.xAxes[0].time.unit="hour"
|
||||
|
||||
if(num*interval >= 6*29*24*60*60)
|
||||
{
|
||||
// If the requested data is more than 3 months, set ticks interval to quarterly
|
||||
timeLineChart.options.scales.xAxes[0].time.unit="quarter"
|
||||
}
|
||||
else if(num*interval >= 3*29*24*60*60)
|
||||
{
|
||||
// If the requested data is more than 3 months, set ticks interval to months
|
||||
timeLineChart.options.scales.xAxes[0].time.unit="month"
|
||||
}
|
||||
if(num*interval >= 29*24*60*60)
|
||||
{
|
||||
// If the requested data is more than 1 month, set ticks interval to weeks
|
||||
timeLineChart.options.scales.xAxes[0].time.unit="week"
|
||||
}
|
||||
else if(num*interval >= 6*24*60*60)
|
||||
{
|
||||
// If the requested data is more than 1 week, set ticks interval to days
|
||||
timeLineChart.options.scales.xAxes[0].time.unit="day"
|
||||
}
|
||||
|
||||
$.getJSON("api_db.php?getGraphData&from="+from+"&until="+until+"&interval="+interval, function(data) {
|
||||
|
||||
// convert received objects to arrays
|
||||
data.domains_over_time = objectToArray(data.domains_over_time);
|
||||
@@ -103,8 +132,8 @@ function updateQueriesOverTime() {
|
||||
// Add data for each hour that is available
|
||||
for (hour in dates) {
|
||||
if (Object.prototype.hasOwnProperty.call(dates, hour)) {
|
||||
var d, dom = 0, ads = 0;
|
||||
d = new Date(1000*dates[hour]);
|
||||
var date, dom = 0, ads = 0;
|
||||
date = new Date(1000*dates[hour]);
|
||||
|
||||
var idx = data.domains_over_time[0].indexOf(dates[hour].toString());
|
||||
if (idx > -1)
|
||||
@@ -118,8 +147,8 @@ function updateQueriesOverTime() {
|
||||
ads = data.ads_over_time[1][idx];
|
||||
}
|
||||
|
||||
timeLineChart.data.labels.push(d);
|
||||
timeLineChart.data.datasets[0].data.push(dom);
|
||||
timeLineChart.data.labels.push(date);
|
||||
timeLineChart.data.datasets[0].data.push(dom - ads);
|
||||
timeLineChart.data.datasets[1].data.push(ads);
|
||||
}
|
||||
}
|
||||
@@ -134,51 +163,66 @@ function updateQueriesOverTime() {
|
||||
$(document).ready(function() {
|
||||
var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
|
||||
timeLineChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: [ 0 ],
|
||||
labels: [ ],
|
||||
datasets: [
|
||||
{
|
||||
label: "Total DNS Queries",
|
||||
label: "Permitted DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
backgroundColor: "rgba(0, 166, 90,.8)",
|
||||
borderColor: "rgba(0, 166, 90,.8)",
|
||||
pointBorderColor: "rgba(0, 166, 90,.8)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
pointHitRadius: 5
|
||||
},
|
||||
{
|
||||
label: "Blocked DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0,192,239,0.5)",
|
||||
backgroundColor: "rgba(0,192,239,1)",
|
||||
borderColor: "rgba(0,192,239,1)",
|
||||
pointBorderColor: "rgba(0,192,239,1)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
pointHitRadius: 5
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
responsive: true,
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function(tooltipItem) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = new Date(label);
|
||||
var date = time.getFullYear()+"-"+padNumber(time.getMonth()+1)+"-"+padNumber(time.getDate());
|
||||
var h = time.getHours();
|
||||
var m = time.getMinutes();
|
||||
var from = padNumber(h)+":"+padNumber(m)+":00";
|
||||
var to = padNumber(h)+":"+padNumber(m+9)+":59";
|
||||
return "Queries from "+from+" to "+to+" on "+date;
|
||||
var from_date = time.getFullYear() +
|
||||
"-" +
|
||||
padNumber(time.getMonth()+1) +
|
||||
"-" +
|
||||
padNumber(time.getDate()) +
|
||||
" " +
|
||||
padNumber(time.getHours()) +
|
||||
":" +
|
||||
padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
padNumber(time.getSeconds());
|
||||
time = new Date(time.valueOf() + 1000 * interval);
|
||||
var until_date = time.getFullYear() +
|
||||
"-" +
|
||||
padNumber(time.getMonth()+1) +
|
||||
"-" +
|
||||
padNumber(time.getDate()) +
|
||||
" " +
|
||||
padNumber(time.getHours()) +
|
||||
":" +
|
||||
padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
padNumber(time.getSeconds());
|
||||
return "Queries from " + from_date + " to " + until_date;
|
||||
},
|
||||
label: function(tooltipItems, data) {
|
||||
if(tooltipItems.datasetIndex === 1)
|
||||
@@ -203,20 +247,22 @@ $(document).ready(function() {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
display: false,
|
||||
stacked: true,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
"minute": "HH:mm",
|
||||
"hour": "HH:mm",
|
||||
"day": "HH:mm",
|
||||
"week": "MMM DD HH:mm",
|
||||
"month": "MMM DD",
|
||||
"quarter": "MMM DD",
|
||||
"year": "MMM DD"
|
||||
"day": "MMM DD",
|
||||
"week": "MMM DD",
|
||||
"month": "MMM",
|
||||
"quarter": "MMM",
|
||||
"year": "YYYY MMM"
|
||||
}
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
stacked: true,
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ var dateformat = "MMMM Do YYYY, HH:mm";
|
||||
|
||||
// Do we want to filter queries?
|
||||
var GETDict = {};
|
||||
location.search.substr(1).split("&").forEach(function(item) {GETDict[item.split("=")[0]] = item.split("=")[1];});
|
||||
window.location.search.substr(1).split("&").forEach(function(item) {GETDict[item.split("=")[0]] = item.split("=")[1];});
|
||||
|
||||
if("from" in GETDict && "until" in GETDict)
|
||||
{
|
||||
|
||||
384
scripts/pi-hole/js/groups-adlists.js
Normal file
384
scripts/pi-hole/js/groups-adlists.js
Normal file
@@ -0,0 +1,384 @@
|
||||
var table;
|
||||
var groups = [];
|
||||
const token = $("#token").html();
|
||||
var info = null;
|
||||
|
||||
function showAlert(type, icon, title, message) {
|
||||
let opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "glyphicon glyphicon-time",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "glyphicon glyphicon-warning-sign",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "glyphicon glyphicon-remove",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function get_groups() {
|
||||
$.post(
|
||||
"scripts/pi-hole/php/groups.php",
|
||||
{ action: "get_groups", token: token },
|
||||
function(data) {
|
||||
groups = data.data;
|
||||
initTable();
|
||||
},
|
||||
"json"
|
||||
);
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#btnAdd").on("click", addAdlist);
|
||||
|
||||
get_groups();
|
||||
|
||||
$("#select").on("change", function() {
|
||||
$("#ip-custom").val("");
|
||||
$("#ip-custom").prop(
|
||||
"disabled",
|
||||
$("#select option:selected").val() !== "custom"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function initTable() {
|
||||
table = $("#adlistsTable").DataTable({
|
||||
ajax: {
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
data: { action: "get_adlists", token: token },
|
||||
type: "POST"
|
||||
},
|
||||
order: [[0, "asc"]],
|
||||
columns: [
|
||||
{ data: "id", visible: false },
|
||||
{ data: "address" },
|
||||
{ data: "enabled", searchable: false },
|
||||
{ data: "comment" },
|
||||
{ data: "groups", searchable: false },
|
||||
{ data: null, width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function(settings) {
|
||||
$(".deleteAdlist").on("click", deleteAdlist);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
const tooltip =
|
||||
"Added: " +
|
||||
datetime(data.date_added) +
|
||||
"\nLast modified: " +
|
||||
datetime(data.date_modified) +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
$("td:eq(0)", row).html(
|
||||
'<code id="address" title="' + tooltip + '">' + data.address + "</code>"
|
||||
);
|
||||
|
||||
const disabled = data.enabled === 0;
|
||||
$("td:eq(1)", row).html(
|
||||
'<input type="checkbox" id="status"' +
|
||||
(disabled ? "" : " checked") +
|
||||
">"
|
||||
);
|
||||
var status = $("#status", row);
|
||||
status.bootstrapToggle({
|
||||
on: "Enabled",
|
||||
off: "Disabled",
|
||||
size: "small",
|
||||
onstyle: "success",
|
||||
width: "80px"
|
||||
});
|
||||
status.on("change", editAdlist);
|
||||
|
||||
$("td:eq(2)", row).html(
|
||||
'<input id="comment" class="form-control"><input id="id" type="hidden" value="' +
|
||||
data.id +
|
||||
'">'
|
||||
);
|
||||
var comment = $("#comment", row);
|
||||
comment.val(data.comment);
|
||||
comment.on("change", editAdlist);
|
||||
|
||||
$("td:eq(3)", row).empty();
|
||||
$("td:eq(3)", row).append(
|
||||
'<select id="multiselect" multiple="multiple"></select>'
|
||||
);
|
||||
var sel = $("#multiselect", row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var extra = "";
|
||||
if (!groups[i].enabled) {
|
||||
extra = " (disabled)";
|
||||
}
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name + extra)
|
||||
);
|
||||
}
|
||||
// Select assigned groups
|
||||
sel.val(data.groups);
|
||||
// Initialize multiselect
|
||||
sel.multiselect({ includeSelectAllOption: true });
|
||||
sel.on("change", editAdlist);
|
||||
|
||||
let button =
|
||||
'<button class="btn btn-danger btn-xs deleteAdlist" type="button" data-id="' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(4)", row).html(button);
|
||||
},
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function(settings, data) {
|
||||
// Store current state in client's local storage area
|
||||
localStorage.setItem("groups-adlists-table", JSON.stringify(data));
|
||||
},
|
||||
stateLoadCallback: function(settings) {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("groups-adlists-table");
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
}
|
||||
data = JSON.parse(data);
|
||||
// Always start on the first page to show most recent queries
|
||||
data.start = 0;
|
||||
// Always start with empty search field
|
||||
data.search.search = "";
|
||||
// Reset visibility of ID column
|
||||
data.columns[0].visible = false;
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
table.on("order.dt", function() {
|
||||
var order = table.order();
|
||||
if (order[0][0] !== 0 || order[0][1] !== "asc") {
|
||||
$("#resetButton").show();
|
||||
} else {
|
||||
$("#resetButton").hide();
|
||||
}
|
||||
});
|
||||
$("#resetButton").on("click", function() {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").hide();
|
||||
});
|
||||
}
|
||||
|
||||
function addAdlist() {
|
||||
var address = $("#new_address").val();
|
||||
var comment = $("#new_comment").val();
|
||||
|
||||
showAlert("info", "", "Adding adlist...", address);
|
||||
|
||||
if (address.length === 0) {
|
||||
showAlert("warning", "", "Warning", "Please specify an adlist address");
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: {
|
||||
action: "add_adlist",
|
||||
address: address,
|
||||
comment: comment,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-plus",
|
||||
"Successfully added adlist",
|
||||
address
|
||||
);
|
||||
$("#new_address").val("");
|
||||
$("#new_comment").val("");
|
||||
table.ajax.reload();
|
||||
} else {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while adding new adlist: ",
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while adding new adlist: ",
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function editAdlist() {
|
||||
var elem = $(this).attr("id");
|
||||
var tr = $(this).closest("tr");
|
||||
var id = tr.find("#id").val();
|
||||
var status = tr.find("#status").is(":checked") ? 1 : 0;
|
||||
var comment = tr.find("#comment").val();
|
||||
var groups = tr.find("#multiselect").val();
|
||||
var address = tr.find("#address").text();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
if (elem === "status" && status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
} else if (elem === "status" && status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (elem === "comment") {
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
} else if (elem === "multiselect") {
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
}
|
||||
|
||||
showAlert("info", "", "Editing adlist...", address);
|
||||
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: {
|
||||
action: "edit_adlist",
|
||||
id: id,
|
||||
comment: comment,
|
||||
status: status,
|
||||
groups: groups,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-pencil",
|
||||
"Successfully " + done + " adlist ",
|
||||
address
|
||||
);
|
||||
} else {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " adlist with ID " + id,
|
||||
+response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " adlist with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAdlist() {
|
||||
var id = $(this).attr("data-id");
|
||||
var tr = $(this).closest("tr");
|
||||
var address = tr.find("#address").text();
|
||||
|
||||
showAlert("info", "", "Deleting adlist...", address);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_adlist", id: id, token: token },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-trash",
|
||||
"Successfully deleted adlist ",
|
||||
address
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
} else
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting adlist with ID " + id,
|
||||
response.message
|
||||
);
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting adlist with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
381
scripts/pi-hole/js/groups-clients.js
Normal file
381
scripts/pi-hole/js/groups-clients.js
Normal file
@@ -0,0 +1,381 @@
|
||||
var table;
|
||||
var groups = [];
|
||||
const token = $("#token").html();
|
||||
var info = null;
|
||||
|
||||
function showAlert(type, icon, title, message) {
|
||||
let opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "glyphicon glyphicon-time",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "glyphicon glyphicon-warning-sign",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "glyphicon glyphicon-remove",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function reload_client_suggestions() {
|
||||
$.post(
|
||||
"scripts/pi-hole/php/groups.php",
|
||||
{ action: "get_unconfigured_clients", token: token },
|
||||
function(data) {
|
||||
var sel = $("#select");
|
||||
sel.empty();
|
||||
for (var key in data) {
|
||||
if (!data.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
var text = key;
|
||||
if (data[key].length > 0) {
|
||||
text += " (" + data[key] + ")";
|
||||
}
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val(key)
|
||||
.text(text)
|
||||
);
|
||||
}
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val("custom")
|
||||
.text("Custom, specified on the right")
|
||||
);
|
||||
},
|
||||
"json"
|
||||
);
|
||||
}
|
||||
|
||||
function get_groups() {
|
||||
$.post(
|
||||
"scripts/pi-hole/php/groups.php",
|
||||
{ action: "get_groups", token: token },
|
||||
function(data) {
|
||||
groups = data.data;
|
||||
initTable();
|
||||
},
|
||||
"json"
|
||||
);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#btnAdd").on("click", addClient);
|
||||
|
||||
reload_client_suggestions();
|
||||
get_groups();
|
||||
|
||||
$("#select").on("change", function() {
|
||||
$("#ip-custom").val("");
|
||||
$("#ip-custom").prop(
|
||||
"disabled",
|
||||
$("#select option:selected").val() !== "custom"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function initTable() {
|
||||
table = $("#clientsTable").DataTable({
|
||||
ajax: {
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
data: { action: "get_clients", token: token },
|
||||
type: "POST"
|
||||
},
|
||||
order: [[0, "asc"]],
|
||||
columns: [
|
||||
{ data: "id", visible: false },
|
||||
{ data: "ip" },
|
||||
{ data: "groups", searchable: false },
|
||||
{ data: "name", width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function(settings) {
|
||||
$(".deleteClient").on("click", deleteClient);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
const tooltip = "Database ID: " + data.id;
|
||||
var ip_name =
|
||||
'<code id="ip" title="' +
|
||||
tooltip +
|
||||
'">' +
|
||||
data.ip +
|
||||
'</code><input id="id" type="hidden" value="' +
|
||||
data.id +
|
||||
'">';
|
||||
if (data.name !== null && data.name.length > 0)
|
||||
ip_name +=
|
||||
'<br><code id="name" title="' +
|
||||
tooltip +
|
||||
'">' +
|
||||
data.name +
|
||||
"</code>";
|
||||
$("td:eq(0)", row).html(ip_name);
|
||||
|
||||
$("td:eq(1)", row).empty();
|
||||
$("td:eq(1)", row).append(
|
||||
'<select id="multiselect" multiple="multiple"></select>'
|
||||
);
|
||||
var sel = $("#multiselect", row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var extra = "";
|
||||
if (!groups[i].enabled) {
|
||||
extra = " (disabled)";
|
||||
}
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name + extra)
|
||||
);
|
||||
}
|
||||
// Select assigned groups
|
||||
sel.val(data.groups);
|
||||
// Initialize multiselect
|
||||
sel.multiselect({ includeSelectAllOption: true });
|
||||
sel.on("change", editClient);
|
||||
|
||||
let button =
|
||||
'<button class="btn btn-danger btn-xs deleteClient" type="button" data-id="' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(2)", row).html(button);
|
||||
},
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function(settings, data) {
|
||||
// Store current state in client's local storage area
|
||||
localStorage.setItem("groups-clients-table", JSON.stringify(data));
|
||||
},
|
||||
stateLoadCallback: function(settings) {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("groups-clients-table");
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
}
|
||||
data = JSON.parse(data);
|
||||
// Always start on the first page to show most recent queries
|
||||
data.start = 0;
|
||||
// Always start with empty search field
|
||||
data.search.search = "";
|
||||
// Reset visibility of ID column
|
||||
data.columns[0].visible = false;
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
table.on("order.dt", function() {
|
||||
var order = table.order();
|
||||
if (order[0][0] !== 0 || order[0][1] !== "asc") {
|
||||
$("#resetButton").show();
|
||||
} else {
|
||||
$("#resetButton").hide();
|
||||
}
|
||||
});
|
||||
$("#resetButton").on("click", function() {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").hide();
|
||||
});
|
||||
}
|
||||
|
||||
function addClient() {
|
||||
var ip = $("#select").val();
|
||||
if (ip === "custom") {
|
||||
ip = $("#ip-custom").val();
|
||||
}
|
||||
|
||||
showAlert("info", "", "Adding client...", ip);
|
||||
|
||||
if (ip.length === 0) {
|
||||
showAlert("warning", "", "Warning", "Please specify a client IP address");
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "add_client", ip: ip, token: token },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-plus",
|
||||
"Successfully added client",
|
||||
ip
|
||||
);
|
||||
reload_client_suggestions();
|
||||
table.ajax.reload();
|
||||
} else {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while adding new client",
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while adding new client",
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function editClient() {
|
||||
var elem = $(this).attr("id");
|
||||
var tr = $(this).closest("tr");
|
||||
var id = tr.find("#id").val();
|
||||
var groups = tr.find("#multiselect").val();
|
||||
var ip = tr.find("#ip").text();
|
||||
var name = tr.find("#name").text();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
if (elem === "multiselect") {
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
}
|
||||
|
||||
var ip_name = ip;
|
||||
if (name.length > 0) {
|
||||
ip_name += " (" + name + ")";
|
||||
}
|
||||
|
||||
showAlert("info", "", "Editing client...", ip_name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "edit_client", id: id, groups: groups, token: token },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-plus",
|
||||
"Successfully " + done + " client",
|
||||
ip_name
|
||||
);
|
||||
} else {
|
||||
showAlert(
|
||||
"error",
|
||||
"Error while " + not_done + " client with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " client with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteClient() {
|
||||
var id = $(this).attr("data-id");
|
||||
var tr = $(this).closest("tr");
|
||||
var ip = tr.find("#ip").text();
|
||||
var name = tr.find("#name").text();
|
||||
|
||||
var ip_name = ip;
|
||||
if (name.length > 0) {
|
||||
ip_name += " (" + name + ")";
|
||||
}
|
||||
showAlert("info", "", "Deleting client...", ip_name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_client", id: id, token: token },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-trash",
|
||||
"Successfully deleted client ",
|
||||
ip_name
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
reload_client_suggestions();
|
||||
} else {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting client with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting client with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
408
scripts/pi-hole/js/groups-domains.js
Normal file
408
scripts/pi-hole/js/groups-domains.js
Normal file
@@ -0,0 +1,408 @@
|
||||
var table;
|
||||
var groups = [];
|
||||
const token = $("#token").html();
|
||||
var info = null;
|
||||
|
||||
function showAlert(type, icon, title, message) {
|
||||
let opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "glyphicon glyphicon-time",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "glyphicon glyphicon-warning-sign",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "glyphicon glyphicon-remove",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function get_groups() {
|
||||
$.post(
|
||||
"scripts/pi-hole/php/groups.php",
|
||||
{ action: "get_groups", token: token },
|
||||
function(data) {
|
||||
groups = data.data;
|
||||
initTable();
|
||||
},
|
||||
"json"
|
||||
);
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#btnAdd").on("click", addDomain);
|
||||
|
||||
get_groups();
|
||||
|
||||
$("#select").on("change", function() {
|
||||
$("#ip-custom").val("");
|
||||
$("#ip-custom").prop(
|
||||
"disabled",
|
||||
$("#select option:selected").val() !== "custom"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function initTable() {
|
||||
table = $("#domainsTable").DataTable({
|
||||
ajax: {
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
data: { action: "get_domains", token: token },
|
||||
type: "POST"
|
||||
},
|
||||
order: [[0, "asc"]],
|
||||
columns: [
|
||||
{ data: "id", visible: false },
|
||||
{ data: "domain" },
|
||||
{ data: "type", searchable: false },
|
||||
{ data: "enabled", searchable: false },
|
||||
{ data: "comment" },
|
||||
{ data: "groups", searchable: false },
|
||||
{ data: null, width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function(settings) {
|
||||
$(".deleteDomain").on("click", deleteDomain);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
const tooltip =
|
||||
"Added: " +
|
||||
datetime(data.date_added) +
|
||||
"\nLast modified: " +
|
||||
datetime(data.date_modified) +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
$("td:eq(0)", row).html(
|
||||
'<code id="domain" title="' + tooltip + '">' + data.domain + "</code>"
|
||||
);
|
||||
|
||||
$("td:eq(1)", row).html(
|
||||
'<select id="type" class="form-control">' +
|
||||
'<option value="0"' +
|
||||
(data.type === 0 ? " selected" : "") +
|
||||
">Exact whitelist</option>" +
|
||||
'<option value="1"' +
|
||||
(data.type === 1 ? " selected" : "") +
|
||||
">Exact blacklist</option>" +
|
||||
'<option value="2"' +
|
||||
(data.type === 2 ? " selected" : "") +
|
||||
">Regex whitelist</option>" +
|
||||
'<option value="3"' +
|
||||
(data.type === 3 ? " selected" : "") +
|
||||
">Regex blacklist</option>" +
|
||||
"</select>"
|
||||
);
|
||||
$("#type", row).on("change", editDomain);
|
||||
|
||||
const disabled = data.enabled === 0;
|
||||
$("td:eq(2)", row).html(
|
||||
'<input type="checkbox" id="status"' +
|
||||
(disabled ? "" : " checked") +
|
||||
">"
|
||||
);
|
||||
$("#status", row).bootstrapToggle({
|
||||
on: "Enabled",
|
||||
off: "Disabled",
|
||||
size: "small",
|
||||
onstyle: "success",
|
||||
width: "80px"
|
||||
});
|
||||
$("#status", row).on("change", editDomain);
|
||||
|
||||
$("td:eq(3)", row).html(
|
||||
'<input id="comment" class="form-control"><input id="id" type="hidden" value="' +
|
||||
data.id +
|
||||
'">'
|
||||
);
|
||||
$("#comment", row).val(data.comment);
|
||||
$("#comment", row).on("change", editDomain);
|
||||
|
||||
$("td:eq(4)", row).empty();
|
||||
$("td:eq(4)", row).append(
|
||||
'<select id="multiselect" multiple="multiple"></select>'
|
||||
);
|
||||
var sel = $("#multiselect", row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var extra = "";
|
||||
if (!groups[i].enabled) {
|
||||
extra = " (disabled)";
|
||||
}
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name + extra)
|
||||
);
|
||||
}
|
||||
// Select assigned groups
|
||||
sel.val(data.groups);
|
||||
// Initialize multiselect
|
||||
sel.multiselect({ includeSelectAllOption: true });
|
||||
sel.on("change", editDomain);
|
||||
|
||||
let button =
|
||||
'<button class="btn btn-danger btn-xs deleteDomain" type="button" data-id="' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(5)", row).html(button);
|
||||
},
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function(settings, data) {
|
||||
// Store current state in client's local storage area
|
||||
localStorage.setItem("groups-domains-table", JSON.stringify(data));
|
||||
},
|
||||
stateLoadCallback: function(settings) {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("groups-domains-table");
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
}
|
||||
data = JSON.parse(data);
|
||||
// Always start on the first page to show most recent queries
|
||||
data.start = 0;
|
||||
// Always start with empty search field
|
||||
data.search.search = "";
|
||||
// Reset visibility of ID column
|
||||
data.columns[0].visible = false;
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
table.on("order.dt", function() {
|
||||
var order = table.order();
|
||||
if (order[0][0] !== 0 || order[0][1] !== "asc") {
|
||||
$("#resetButton").show();
|
||||
} else {
|
||||
$("#resetButton").hide();
|
||||
}
|
||||
});
|
||||
$("#resetButton").on("click", function() {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").hide();
|
||||
});
|
||||
}
|
||||
|
||||
function addDomain() {
|
||||
var domain = $("#new_domain").val();
|
||||
var type = $("#new_type").val();
|
||||
var comment = $("#new_comment").val();
|
||||
|
||||
showAlert("info", "", "Adding domain...", domain);
|
||||
|
||||
if (domain.length === 0) {
|
||||
showAlert("warning", "", "Warning", "Please specify a domain");
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: {
|
||||
action: "add_domain",
|
||||
domain: domain,
|
||||
type: type,
|
||||
comment: comment,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-plus",
|
||||
"Successfully added domain",
|
||||
domain
|
||||
);
|
||||
$("#new_domain").val("");
|
||||
$("#new_comment").val("");
|
||||
table.ajax.reload();
|
||||
} else
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while adding new domain",
|
||||
response.message
|
||||
);
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while adding new domain",
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function editDomain() {
|
||||
var elem = $(this).attr("id");
|
||||
var tr = $(this).closest("tr");
|
||||
var domain = tr.find("#domain").text();
|
||||
var id = tr.find("#id").val();
|
||||
var type = tr.find("#type").val();
|
||||
var status = tr.find("#status").is(":checked") ? 1 : 0;
|
||||
var comment = tr.find("#comment").val();
|
||||
var groups = tr.find("#multiselect").val();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
if (elem === "status" && status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
} else if (elem === "status" && status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (elem === "name") {
|
||||
done = "edited name of";
|
||||
not_done = "editing name of";
|
||||
} else if (elem === "comment") {
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
} else if (elem === "type") {
|
||||
done = "edited type of";
|
||||
not_done = "editing type of";
|
||||
} else if (elem === "multiselect") {
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
}
|
||||
|
||||
showAlert("info", "", "Editing domain...", name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: {
|
||||
action: "edit_domain",
|
||||
id: id,
|
||||
type: type,
|
||||
comment: comment,
|
||||
status: status,
|
||||
groups: groups,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-pencil",
|
||||
"Successfully " + done + " domain",
|
||||
domain
|
||||
);
|
||||
} else
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " domain with ID " + id,
|
||||
response.message
|
||||
);
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " domain with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteDomain() {
|
||||
var id = $(this).attr("data-id");
|
||||
var tr = $(this).closest("tr");
|
||||
var domain = tr.find("#domain").text();
|
||||
|
||||
showAlert("info", "", "Deleting domain...", domain);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_domain", id: id, token: token },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-trash",
|
||||
"Successfully deleted domain",
|
||||
domain
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
} else
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting domain with ID " + id,
|
||||
response.message
|
||||
);
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting domain with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
336
scripts/pi-hole/js/groups.js
Normal file
336
scripts/pi-hole/js/groups.js
Normal file
@@ -0,0 +1,336 @@
|
||||
var table;
|
||||
const token = $("#token").html();
|
||||
var info = null;
|
||||
|
||||
function showAlert(type, icon, title, message) {
|
||||
let opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "glyphicon glyphicon-time",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
info = $.notify(opts);
|
||||
break;
|
||||
case "success":
|
||||
opts = {
|
||||
type: "success",
|
||||
icon: icon,
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
case "warning":
|
||||
opts = {
|
||||
type: "warning",
|
||||
icon: "glyphicon glyphicon-warning-sign",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "glyphicon glyphicon-remove",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#btnAdd").on("click", addGroup);
|
||||
|
||||
table = $("#groupsTable").DataTable({
|
||||
ajax: {
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
data: { action: "get_groups", token: token },
|
||||
type: "POST"
|
||||
},
|
||||
order: [[0, "asc"]],
|
||||
columns: [
|
||||
{ data: "id", visible: false },
|
||||
{ data: "name" },
|
||||
{ data: "enabled", searchable: false },
|
||||
{ data: "description" },
|
||||
{ data: null, width: "60px", orderable: false }
|
||||
],
|
||||
drawCallback: function(settings) {
|
||||
$(".deleteGroup").on("click", deleteGroup);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
const tooltip =
|
||||
"Added: " +
|
||||
datetime(data.date_added) +
|
||||
"\nLast modified: " +
|
||||
datetime(data.date_modified) +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
$("td:eq(0)", row).html(
|
||||
'<input id="name" title="' +
|
||||
tooltip +
|
||||
'" class="form-control"><input id="id" type="hidden" value="' +
|
||||
data.id +
|
||||
'">'
|
||||
);
|
||||
var name = $("#name", row);
|
||||
name.val(data.name);
|
||||
name.on("change", editGroup);
|
||||
|
||||
const disabled = data.enabled === 0;
|
||||
$("td:eq(1)", row).html(
|
||||
'<input type="checkbox" id="status"' +
|
||||
(disabled ? "" : " checked") +
|
||||
">"
|
||||
);
|
||||
var status = $("#status", row);
|
||||
status.bootstrapToggle({
|
||||
on: "Enabled",
|
||||
off: "Disabled",
|
||||
size: "small",
|
||||
onstyle: "success",
|
||||
width: "80px"
|
||||
});
|
||||
status.on("change", editGroup);
|
||||
|
||||
$("td:eq(2)", row).html('<input id="desc" class="form-control">');
|
||||
const desc = data.description !== null ? data.description : "";
|
||||
$("#desc", row).val(desc);
|
||||
$("#desc", row).on("change", editGroup);
|
||||
|
||||
$("td:eq(3)", row).empty();
|
||||
if (data.id !== 0) {
|
||||
let button =
|
||||
" " +
|
||||
'<button class="btn btn-danger btn-xs deleteGroup" type="button" data-id="' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(3)", row).html(button);
|
||||
}
|
||||
},
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function(settings, data) {
|
||||
// Store current state in client's local storage area
|
||||
localStorage.setItem("groups-table", JSON.stringify(data));
|
||||
},
|
||||
stateLoadCallback: function(settings) {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("groups-table");
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
}
|
||||
data = JSON.parse(data);
|
||||
// Always start on the first page to show most recent queries
|
||||
data.start = 0;
|
||||
// Always start with empty search field
|
||||
data.search.search = "";
|
||||
// Reset visibility of ID column
|
||||
data.columns[0].visible = false;
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
table.on("order.dt", function() {
|
||||
var order = table.order();
|
||||
if (order[0][0] !== 0 || order[0][1] !== "asc") {
|
||||
$("#resetButton").show();
|
||||
} else {
|
||||
$("#resetButton").hide();
|
||||
}
|
||||
});
|
||||
$("#resetButton").on("click", function() {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").hide();
|
||||
});
|
||||
});
|
||||
|
||||
function addGroup() {
|
||||
var name = $("#new_name").val();
|
||||
var desc = $("#new_desc").val();
|
||||
|
||||
showAlert("info", "", "Adding group...", name);
|
||||
|
||||
if (name.length === 0) {
|
||||
showAlert("warning", "", "Warning", "Please specify a group name");
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "add_group", name: name, desc: desc, token: token },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-plus",
|
||||
"Successfully added group",
|
||||
name
|
||||
);
|
||||
$("#new_name").val("");
|
||||
$("#new_desc").val("");
|
||||
table.ajax.reload();
|
||||
} else {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while adding new group",
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while adding new group",
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function editGroup() {
|
||||
var elem = $(this).attr("id");
|
||||
var tr = $(this).closest("tr");
|
||||
var id = tr.find("#id").val();
|
||||
var name = tr.find("#name").val();
|
||||
var status = tr.find("#status").is(":checked") ? 1 : 0;
|
||||
var desc = tr.find("#desc").val();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
if (elem === "status" && status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
} else if (elem === "status" && status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
} else if (elem === "name") {
|
||||
done = "edited name of";
|
||||
not_done = "editing name of";
|
||||
} else if (elem === "desc") {
|
||||
done = "edited description of";
|
||||
not_done = "editing description of";
|
||||
}
|
||||
|
||||
showAlert("info", "", "Editing group...", name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: {
|
||||
action: "edit_group",
|
||||
id: id,
|
||||
name: name,
|
||||
desc: desc,
|
||||
status: status,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-pencil",
|
||||
"Successfully " + done + " group",
|
||||
name
|
||||
);
|
||||
} else {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " group with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " group with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteGroup() {
|
||||
var id = $(this).attr("data-id");
|
||||
var tr = $(this).closest("tr");
|
||||
var name = tr.find("#name").val();
|
||||
|
||||
showAlert("info", "", "Deleting group...", name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_group", id: id, token: token },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-trash",
|
||||
"Successfully deleted group ",
|
||||
name
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
} else {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting group with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting group with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
// Define global variables
|
||||
/* global Chart:false, updateSessionTimer:false */
|
||||
var timeLineChart, forwardDestinationChart;
|
||||
var queryTypePieChart, forwardDestinationPieChart, clientsChart;
|
||||
var timeLineChart, clientsChart;
|
||||
var queryTypePieChart, forwardDestinationPieChart;
|
||||
|
||||
function padNumber(num) {
|
||||
return ("00" + num).substr(-2,2);
|
||||
@@ -166,8 +166,10 @@ function updateQueriesOverTime() {
|
||||
}
|
||||
|
||||
timeLineChart.data.labels.push(d);
|
||||
timeLineChart.data.datasets[0].data.push(data.domains_over_time[1][hour]);
|
||||
timeLineChart.data.datasets[1].data.push(data.ads_over_time[1][hour]);
|
||||
var blocked = data.ads_over_time[1][hour];
|
||||
var permitted = data.domains_over_time[1][hour] - blocked;
|
||||
timeLineChart.data.datasets[0].data.push(permitted);
|
||||
timeLineChart.data.datasets[1].data.push(blocked);
|
||||
}
|
||||
}
|
||||
$("#queries-over-time .overlay").hide();
|
||||
@@ -718,33 +720,31 @@ $(document).ready(function() {
|
||||
|
||||
var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
|
||||
timeLineChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: [],
|
||||
labels: [ ],
|
||||
datasets: [
|
||||
{
|
||||
label: "Total DNS Queries",
|
||||
label: "Permitted DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
backgroundColor: "rgba(0, 166, 90,.8)",
|
||||
borderColor: "rgba(0, 166, 90,.8)",
|
||||
pointBorderColor: "rgba(0, 166, 90,.8)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
pointHitRadius: 5
|
||||
},
|
||||
{
|
||||
label: "Blocked DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0,192,239,0.5)",
|
||||
backgroundColor: "rgba(0,192,239,1)",
|
||||
borderColor: "rgba(0,192,239,1)",
|
||||
pointBorderColor: "rgba(0,192,239,1)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
pointHitRadius: 5
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -785,6 +785,7 @@ $(document).ready(function() {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
stacked: true,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
@@ -794,6 +795,7 @@ $(document).ready(function() {
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
stacked: true,
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
@@ -807,73 +809,13 @@ $(document).ready(function() {
|
||||
|
||||
updateQueriesOverTime();
|
||||
|
||||
// Create / load "Forward Destinations over Time" only if authorized
|
||||
if(document.getElementById("forwardDestinationChart"))
|
||||
{
|
||||
ctx = document.getElementById("forwardDestinationChart").getContext("2d");
|
||||
forwardDestinationChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }]
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function(tooltipItem) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = padNumber(h)+":"+padNumber(m-5)+":00";
|
||||
var to = padNumber(h)+":"+padNumber(m+4)+":59";
|
||||
return "Forward destinations from "+from+" to "+to;
|
||||
},
|
||||
label: function(tooltipItems, data) {
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm"
|
||||
},
|
||||
tooltipFormat: "HH:mm"
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
mix: 0.0,
|
||||
max: 1.0,
|
||||
beginAtZero: true,
|
||||
callback: function(value) {
|
||||
return Math.round(value*100) + " %";
|
||||
}
|
||||
},
|
||||
stacked: true
|
||||
}]
|
||||
},
|
||||
maintainAspectRatio: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create / load "Top Clients over Time" only if authorized
|
||||
var clientsChartEl = document.getElementById("clientsChart");
|
||||
if(clientsChartEl)
|
||||
{
|
||||
ctx = clientsChartEl.getContext("2d");
|
||||
clientsChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }]
|
||||
@@ -907,6 +849,7 @@ $(document).ready(function() {
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
stacked: true,
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
|
||||
@@ -106,7 +106,7 @@ function autofilter(){
|
||||
$(document).ready(function() {
|
||||
// Do we want to filter queries?
|
||||
var GETDict = {};
|
||||
location.search.substr(1).split("&").forEach(function(item) {GETDict[item.split("=")[0]] = item.split("=")[1];});
|
||||
window.location.search.substr(1).split("&").forEach(function(item) {GETDict[item.split("=")[0]] = item.split("=")[1];});
|
||||
|
||||
var APIstring = "api.php?getAllQueries";
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ $(".nav-tabs a").on("shown.bs.tab", function (e) {
|
||||
window.history.pushState("", "", "?tab=" + tab);
|
||||
if(tab === "piholedhcp")
|
||||
{
|
||||
location.reload();
|
||||
window.location.reload();
|
||||
}
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user