mirror of
https://github.com/pi-hole/web.git
synced 2025-12-24 20:55:28 +00:00
Merge branch 'devel' into tweak/exact_searching
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
@@ -5,51 +5,37 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global utils:false */
|
||||
|
||||
// Define global variables
|
||||
var auditTimeout = null;
|
||||
|
||||
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
|
||||
function escapeHtml(text) {
|
||||
var map = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'"
|
||||
};
|
||||
|
||||
return text.replace(/[&<>"']/g, function(m) {
|
||||
return map[m];
|
||||
});
|
||||
}
|
||||
|
||||
function updateTopLists() {
|
||||
$.getJSON("api.php?topItems=audit", function(data) {
|
||||
$.getJSON("api.php?topItems=audit", function (data) {
|
||||
if ("FTLnotrunning" in data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear tables before filling them with data
|
||||
$("#domain-frequency td")
|
||||
.parent()
|
||||
.remove();
|
||||
$("#ad-frequency td")
|
||||
.parent()
|
||||
.remove();
|
||||
$("#domain-frequency td").parent().remove();
|
||||
$("#ad-frequency td").parent().remove();
|
||||
var domaintable = $("#domain-frequency").find("tbody:last");
|
||||
var adtable = $("#ad-frequency").find("tbody:last");
|
||||
var url, domain;
|
||||
for (domain in data.top_queries) {
|
||||
if (Object.prototype.hasOwnProperty.call(data.top_queries, domain)) {
|
||||
// Sanitize domain
|
||||
domain = escapeHtml(domain);
|
||||
domain = utils.escapeHtml(domain);
|
||||
url = '<a href="queries.php?domain=' + domain + '">' + domain + "</a>";
|
||||
domaintable.append(
|
||||
"<tr><td>" +
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_queries[domain] +
|
||||
'</td> <td> <button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button> <button class="btn btn-default btn-sm text-orange"><i class="fa fa-balance-scale"></i> Audit</button> </td> </tr> '
|
||||
"</td> <td>" +
|
||||
'<button type="button" class="btn btn-default btn-xs text-red"><i class="fa fa-ban"></i> Blacklist</button>' +
|
||||
'<button type="button" class="btn btn-default btn-xs text-orange"><i class="fa fa-balance-scale"></i> Audit</button>' +
|
||||
"</td> </tr> "
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -58,7 +44,7 @@ function updateTopLists() {
|
||||
if (Object.prototype.hasOwnProperty.call(data.top_ads, domain)) {
|
||||
var input = domain.split(" ");
|
||||
// Sanitize domain
|
||||
var printdomain = escapeHtml(input[0]);
|
||||
var printdomain = utils.escapeHtml(input[0]);
|
||||
if (input.length > 1) {
|
||||
url =
|
||||
'<a href="queries.php?domain=' +
|
||||
@@ -71,7 +57,9 @@ function updateTopLists() {
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_ads[domain] +
|
||||
'</td> <td> <button type="button" class="btn btn-default btn-sm text-orange"><i class="fa fa-balance-scale"></i> Audit</button> </td> </tr> '
|
||||
"</td> <td>" +
|
||||
'<button type="button" class="btn btn-default btn-sm text-orange"><i class="fa fa-balance-scale"></i> Audit</button>' +
|
||||
"</td> </tr> "
|
||||
);
|
||||
} else {
|
||||
url = '<a href="queries.php?domain=' + printdomain + '">' + printdomain + "</a>";
|
||||
@@ -80,7 +68,10 @@ function updateTopLists() {
|
||||
url +
|
||||
"</td> <td>" +
|
||||
data.top_ads[domain] +
|
||||
'</td> <td> <button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button> <button class="btn btn-default btn-sm text-orange"><i class="fa fa-balance-scale"></i> Audit</button> </td> </tr> '
|
||||
"</td> <td>" +
|
||||
'<button type="button" class="btn btn-default btn-xs text-green"><i class="fas fa-check"></i> Whitelist</button>' +
|
||||
'<button type="button" class="btn btn-default btn-xs text-orange"><i class="fa fa-balance-scale"></i> Audit</button>' +
|
||||
"</td> </tr> "
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -102,14 +93,20 @@ function updateTopLists() {
|
||||
function add(domain, list) {
|
||||
var token = $("#token").text();
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/add.php",
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
data: { domain: domain, list: list, token: token },
|
||||
success: function() {
|
||||
data: {
|
||||
domain: domain,
|
||||
list: list,
|
||||
token: token,
|
||||
action: "add_domain",
|
||||
comment: "Added from Audit Log"
|
||||
},
|
||||
success: function () {
|
||||
updateTopLists();
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
console.log(exception);
|
||||
error: function (jqXHR, exception) {
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -130,14 +127,12 @@ function auditUrl(url) {
|
||||
add(url, "audit");
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
// Pull in data via AJAX
|
||||
updateTopLists();
|
||||
|
||||
$("#domain-frequency tbody").on("click", "button", function() {
|
||||
var url = $(this)
|
||||
.parents("tr")[0]
|
||||
.textContent.split(" ")[0];
|
||||
$("#domain-frequency tbody").on("click", "button", function () {
|
||||
var url = $(this).parents("tr")[0].textContent.split(" ")[0];
|
||||
if ($(this).context.textContent === " Blacklist") {
|
||||
blacklistUrl(url);
|
||||
} else {
|
||||
@@ -145,10 +140,8 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
|
||||
$("#ad-frequency tbody").on("click", "button", function() {
|
||||
var url = $(this)
|
||||
.parents("tr")[0]
|
||||
.textContent.split(" ")[0];
|
||||
$("#ad-frequency tbody").on("click", "button", function () {
|
||||
var url = $(this).parents("tr")[0].textContent.split(" ")[0];
|
||||
if ($(this).context.textContent === " Whitelist") {
|
||||
whitelistUrl(url);
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
var table;
|
||||
var token = $("#token").text();
|
||||
|
||||
function showAlert(type, message) {
|
||||
var alertElement = null;
|
||||
@@ -36,32 +37,42 @@ function showAlert(type, message) {
|
||||
alertElement.delay(8000).fadeOut(2000);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
$("#btnAdd").on("click", addCustomDNS);
|
||||
|
||||
table = $("#customDNSTable").DataTable({
|
||||
ajax: "scripts/pi-hole/php/customdns.php?action=get",
|
||||
columns: [{}, {}, { orderable: false, searchable: false }],
|
||||
ajax: {
|
||||
url: "scripts/pi-hole/php/customdns.php",
|
||||
data: { action: "get", token: token },
|
||||
type: "POST"
|
||||
},
|
||||
columns: [{}, { type: "ip-address" }, { orderable: false, searchable: false }],
|
||||
columnDefs: [
|
||||
{
|
||||
targets: 2,
|
||||
render: function(data, type, row) {
|
||||
render: function (data, type, row) {
|
||||
return (
|
||||
'<button class="btn btn-danger btn-xs deleteCustomDNS" type="button" data-domain=\'' +
|
||||
'<button type="button" class="btn btn-danger btn-xs deleteCustomDNS" data-domain=\'' +
|
||||
row[0] +
|
||||
"' data-ip='" +
|
||||
row[1] +
|
||||
"'>" +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>"
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
drawCallback: function() {
|
||||
drawCallback: function () {
|
||||
$(".deleteCustomDNS").on("click", deleteCustomDNS);
|
||||
}
|
||||
});
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
});
|
||||
|
||||
function addCustomDNS() {
|
||||
@@ -73,14 +84,14 @@ function addCustomDNS() {
|
||||
url: "scripts/pi-hole/php/customdns.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "add", ip: ip, domain: domain },
|
||||
success: function(response) {
|
||||
data: { action: "add", ip: ip, domain: domain, token: token },
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
showAlert("success");
|
||||
table.ajax.reload();
|
||||
} else showAlert("error", response.message);
|
||||
},
|
||||
error: function() {
|
||||
error: function () {
|
||||
showAlert("error", "Error while adding this custom DNS entry");
|
||||
}
|
||||
});
|
||||
@@ -95,16 +106,16 @@ function deleteCustomDNS() {
|
||||
url: "scripts/pi-hole/php/customdns.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete", domain: domain, ip: ip },
|
||||
success: function(response) {
|
||||
data: { action: "delete", domain: domain, ip: ip, token: token },
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
showAlert("success");
|
||||
table.ajax.reload();
|
||||
} else showAlert("error", response.message);
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
showAlert("error", "Error while deleting this custom DNS entry");
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,25 +5,17 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global Chart:false, moment:false */
|
||||
/* global utils:false, Chart:false, moment:false */
|
||||
|
||||
var start__ = moment().subtract(6, "days");
|
||||
var from =
|
||||
moment(start__)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
var from = moment(start__).utc().valueOf() / 1000;
|
||||
var end__ = moment();
|
||||
var until =
|
||||
moment(end__)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
var until = moment(end__).utc().valueOf() / 1000;
|
||||
var interval = 0;
|
||||
|
||||
var timeoutWarning = $("#timeoutWarning");
|
||||
|
||||
var dateformat = "MMMM Do YYYY, HH:mm";
|
||||
|
||||
$(function() {
|
||||
$(function () {
|
||||
$("#querytime").daterangepicker(
|
||||
{
|
||||
timePicker: true,
|
||||
@@ -34,23 +26,15 @@ $(function() {
|
||||
ranges: {
|
||||
Today: [moment().startOf("day"), moment()],
|
||||
Yesterday: [
|
||||
moment()
|
||||
.subtract(1, "days")
|
||||
.startOf("day"),
|
||||
moment()
|
||||
.subtract(1, "days")
|
||||
.endOf("day")
|
||||
moment().subtract(1, "days").startOf("day"),
|
||||
moment().subtract(1, "days").endOf("day")
|
||||
],
|
||||
"Last 7 Days": [moment().subtract(6, "days"), moment()],
|
||||
"Last 30 Days": [moment().subtract(29, "days"), moment()],
|
||||
"This Month": [moment().startOf("month"), moment()],
|
||||
"Last Month": [
|
||||
moment()
|
||||
.subtract(1, "month")
|
||||
.startOf("month"),
|
||||
moment()
|
||||
.subtract(1, "month")
|
||||
.endOf("month")
|
||||
moment().subtract(1, "month").startOf("month"),
|
||||
moment().subtract(1, "month").endOf("month")
|
||||
],
|
||||
"This Year": [moment().startOf("year"), moment()],
|
||||
"All Time": [moment(0), moment()]
|
||||
@@ -59,41 +43,13 @@ $(function() {
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false
|
||||
},
|
||||
function(startt, endt) {
|
||||
from =
|
||||
moment(startt)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
until =
|
||||
moment(endt)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
function (startt, endt) {
|
||||
from = moment(startt).utc().valueOf() / 1000;
|
||||
until = moment(endt).utc().valueOf() / 1000;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function padNumber(num) {
|
||||
return ("00" + num).substr(-2, 2);
|
||||
}
|
||||
|
||||
// Helper function needed for converting the Objects to Arrays
|
||||
|
||||
function objectToArray(p) {
|
||||
var keys = Object.keys(p);
|
||||
keys.sort(function(a, b) {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
var arr = [],
|
||||
idx = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
arr.push(p[keys[i]]);
|
||||
idx.push(keys[i]);
|
||||
}
|
||||
|
||||
return [idx, arr];
|
||||
}
|
||||
|
||||
var timeLineChart;
|
||||
|
||||
function compareNumbers(a, b) {
|
||||
@@ -101,6 +57,8 @@ function compareNumbers(a, b) {
|
||||
}
|
||||
|
||||
function updateQueriesOverTime() {
|
||||
var timeoutWarning = $("#timeoutWarning");
|
||||
|
||||
$("#queries-over-time .overlay").show();
|
||||
timeoutWarning.show();
|
||||
|
||||
@@ -128,10 +86,10 @@ function updateQueriesOverTime() {
|
||||
|
||||
$.getJSON(
|
||||
"api_db.php?getGraphData&from=" + from + "&until=" + until + "&interval=" + interval,
|
||||
function(data) {
|
||||
function (data) {
|
||||
// convert received objects to arrays
|
||||
data.domains_over_time = objectToArray(data.domains_over_time);
|
||||
data.ads_over_time = objectToArray(data.ads_over_time);
|
||||
data.domains_over_time = utils.objectToArray(data.domains_over_time);
|
||||
data.ads_over_time = utils.objectToArray(data.ads_over_time);
|
||||
// Remove possibly already existing data
|
||||
timeLineChart.data.labels = [];
|
||||
timeLineChart.data.datasets[0].data = [];
|
||||
@@ -160,23 +118,23 @@ function updateQueriesOverTime() {
|
||||
for (hour in dates) {
|
||||
if (Object.prototype.hasOwnProperty.call(dates, hour)) {
|
||||
var date,
|
||||
dom = 0,
|
||||
ads = 0;
|
||||
total = 0,
|
||||
blocked = 0;
|
||||
date = new Date(1000 * dates[hour]);
|
||||
|
||||
var idx = data.domains_over_time[0].indexOf(dates[hour].toString());
|
||||
if (idx > -1) {
|
||||
dom = data.domains_over_time[1][idx];
|
||||
total = data.domains_over_time[1][idx];
|
||||
}
|
||||
|
||||
idx = data.ads_over_time[0].indexOf(dates[hour].toString());
|
||||
if (idx > -1) {
|
||||
ads = data.ads_over_time[1][idx];
|
||||
blocked = data.ads_over_time[1][idx];
|
||||
}
|
||||
|
||||
timeLineChart.data.labels.push(date);
|
||||
timeLineChart.data.datasets[0].data.push(dom - ads);
|
||||
timeLineChart.data.datasets[1].data.push(ads);
|
||||
timeLineChart.data.datasets[0].data.push(blocked);
|
||||
timeLineChart.data.datasets[1].data.push(total - blocked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,30 +146,32 @@ function updateQueriesOverTime() {
|
||||
);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
|
||||
var blockedColor = "#999";
|
||||
var permittedColor = "#00a65a";
|
||||
timeLineChart = new Chart(ctx, {
|
||||
type: "bar",
|
||||
type: utils.getGraphType(),
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "Permitted DNS Queries",
|
||||
label: "Blocked DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0, 166, 90,.8)",
|
||||
borderColor: "rgba(0, 166, 90,.8)",
|
||||
pointBorderColor: "rgba(0, 166, 90,.8)",
|
||||
backgroundColor: blockedColor,
|
||||
borderColor: blockedColor,
|
||||
pointBorderColor: blockedColor,
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5
|
||||
},
|
||||
{
|
||||
label: "Blocked DNS Queries",
|
||||
label: "Permitted DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0,192,239,1)",
|
||||
borderColor: "rgba(0,192,239,1)",
|
||||
pointBorderColor: "rgba(0,192,239,1)",
|
||||
backgroundColor: permittedColor,
|
||||
borderColor: permittedColor,
|
||||
pointBorderColor: permittedColor,
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
@@ -222,45 +182,76 @@ $(document).ready(function() {
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
itemSort: function (a, b) {
|
||||
return b.datasetIndex - a.datasetIndex;
|
||||
},
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function(tooltipItem) {
|
||||
title: function (tooltipItem) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = new Date(label);
|
||||
var from_date =
|
||||
var fromDate =
|
||||
time.getFullYear() +
|
||||
"-" +
|
||||
padNumber(time.getMonth() + 1) +
|
||||
utils.padNumber(time.getMonth() + 1) +
|
||||
"-" +
|
||||
padNumber(time.getDate()) +
|
||||
" " +
|
||||
padNumber(time.getHours()) +
|
||||
utils.padNumber(time.getDate());
|
||||
var fromTime =
|
||||
utils.padNumber(time.getHours()) +
|
||||
":" +
|
||||
padNumber(time.getMinutes()) +
|
||||
utils.padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
padNumber(time.getSeconds());
|
||||
utils.padNumber(time.getSeconds());
|
||||
time = new Date(time.valueOf() + 1000 * interval);
|
||||
var until_date =
|
||||
var untilDate =
|
||||
time.getFullYear() +
|
||||
"-" +
|
||||
padNumber(time.getMonth() + 1) +
|
||||
utils.padNumber(time.getMonth() + 1) +
|
||||
"-" +
|
||||
padNumber(time.getDate()) +
|
||||
utils.padNumber(time.getDate());
|
||||
var untilTime =
|
||||
utils.padNumber(time.getHours()) +
|
||||
":" +
|
||||
utils.padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
utils.padNumber(time.getSeconds());
|
||||
|
||||
if (fromDate === untilDate) {
|
||||
// Abbreviated form for intervals on the same day
|
||||
// We split title in two lines on small screens
|
||||
if ($(window).width() < 992) {
|
||||
untilTime += "\n";
|
||||
}
|
||||
|
||||
return ("Queries from " + fromTime + " to " + untilTime + " on " + fromDate).split(
|
||||
"\n "
|
||||
);
|
||||
}
|
||||
|
||||
// Full tooltip for intervals spanning more than one day
|
||||
// We split title in two lines on small screens
|
||||
if ($(window).width() < 992) {
|
||||
fromDate += "\n";
|
||||
}
|
||||
|
||||
return (
|
||||
"Queries from " +
|
||||
fromDate +
|
||||
" " +
|
||||
padNumber(time.getHours()) +
|
||||
":" +
|
||||
padNumber(time.getMinutes()) +
|
||||
":" +
|
||||
padNumber(time.getSeconds());
|
||||
return "Queries from " + from_date + " to " + until_date;
|
||||
fromTime +
|
||||
" to " +
|
||||
untilDate +
|
||||
" " +
|
||||
untilTime
|
||||
).split("\n ");
|
||||
},
|
||||
label: function(tooltipItems, data) {
|
||||
if (tooltipItems.datasetIndex === 1) {
|
||||
var percentage = 0.0;
|
||||
var total = parseInt(data.datasets[0].data[tooltipItems.index]);
|
||||
var blocked = parseInt(data.datasets[1].data[tooltipItems.index]);
|
||||
if (total > 0) {
|
||||
percentage = (100.0 * blocked) / total;
|
||||
label: function (tooltipItems, data) {
|
||||
if (tooltipItems.datasetIndex === 0) {
|
||||
var percentage = 0;
|
||||
var permitted = parseInt(data.datasets[1].data[tooltipItems.index]);
|
||||
var blocked = parseInt(data.datasets[0].data[tooltipItems.index]);
|
||||
if (permitted + blocked > 0) {
|
||||
percentage = (100 * blocked) / (permitted + blocked);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -313,13 +304,13 @@ $(document).ready(function() {
|
||||
});
|
||||
});
|
||||
|
||||
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
|
||||
$("#querytime").on("apply.daterangepicker", function (ev, picker) {
|
||||
$(this).val(picker.startDate.format(dateformat) + " to " + picker.endDate.format(dateformat));
|
||||
$("#queries-over-time").show();
|
||||
updateQueriesOverTime();
|
||||
});
|
||||
|
||||
$("#queryOverTimeChart").click(function(evt) {
|
||||
$("#queryOverTimeChart").click(function (evt) {
|
||||
var activePoints = timeLineChart.getElementAtEvent(evt);
|
||||
if (activePoints.length > 0) {
|
||||
//get the internal index in the chart
|
||||
|
||||
@@ -5,25 +5,19 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
/* global utils:false, moment:false */
|
||||
|
||||
var start__ = moment().subtract(6, "days");
|
||||
var from =
|
||||
moment(start__)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
var from = moment(start__).utc().valueOf() / 1000;
|
||||
var end__ = moment();
|
||||
var until =
|
||||
moment(end__)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
var until = moment(end__).utc().valueOf() / 1000;
|
||||
|
||||
var timeoutWarning = $("#timeoutWarning");
|
||||
var listsStillLoading = 0;
|
||||
|
||||
var dateformat = "MMMM Do YYYY, HH:mm";
|
||||
|
||||
$(function() {
|
||||
$(function () {
|
||||
$("#querytime").daterangepicker(
|
||||
{
|
||||
timePicker: true,
|
||||
@@ -34,23 +28,15 @@ $(function() {
|
||||
ranges: {
|
||||
Today: [moment().startOf("day"), moment()],
|
||||
Yesterday: [
|
||||
moment()
|
||||
.subtract(1, "days")
|
||||
.startOf("day"),
|
||||
moment()
|
||||
.subtract(1, "days")
|
||||
.endOf("day")
|
||||
moment().subtract(1, "days").startOf("day"),
|
||||
moment().subtract(1, "days").endOf("day")
|
||||
],
|
||||
"Last 7 Days": [moment().subtract(6, "days"), moment()],
|
||||
"Last 30 Days": [moment().subtract(29, "days"), moment()],
|
||||
"This Month": [moment().startOf("month"), moment()],
|
||||
"Last Month": [
|
||||
moment()
|
||||
.subtract(1, "month")
|
||||
.startOf("month"),
|
||||
moment()
|
||||
.subtract(1, "month")
|
||||
.endOf("month")
|
||||
moment().subtract(1, "month").startOf("month"),
|
||||
moment().subtract(1, "month").endOf("month")
|
||||
],
|
||||
"This Year": [moment().startOf("year"), moment()],
|
||||
"All Time": [moment(0), moment()]
|
||||
@@ -59,41 +45,18 @@ $(function() {
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false
|
||||
},
|
||||
function(startt, endt) {
|
||||
from =
|
||||
moment(startt)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
until =
|
||||
moment(endt)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
function (startt, endt) {
|
||||
from = moment(startt).utc().valueOf() / 1000;
|
||||
until = moment(endt).utc().valueOf() / 1000;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
|
||||
function escapeHtml(text) {
|
||||
var map = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'"
|
||||
};
|
||||
|
||||
return text.replace(/[&<>"']/g, function(m) {
|
||||
return map[m];
|
||||
});
|
||||
}
|
||||
|
||||
function updateTopClientsChart() {
|
||||
$("#client-frequency .overlay").show();
|
||||
$.getJSON("api_db.php?topClients&from=" + from + "&until=" + until, function(data) {
|
||||
$.getJSON("api_db.php?topClients&from=" + from + "&until=" + until, function (data) {
|
||||
// Clear tables before filling them with data
|
||||
$("#client-frequency td")
|
||||
.parent()
|
||||
.remove();
|
||||
$("#client-frequency td").parent().remove();
|
||||
var clienttable = $("#client-frequency").find("tbody:last");
|
||||
var client, percentage, clientname;
|
||||
var sum = 0;
|
||||
@@ -106,20 +69,20 @@ function updateTopClientsChart() {
|
||||
for (client in data.top_sources) {
|
||||
if (Object.prototype.hasOwnProperty.call(data.top_sources, client)) {
|
||||
// Sanitize client
|
||||
client = escapeHtml(client);
|
||||
if (escapeHtml(client) !== client) {
|
||||
client = utils.escapeHtml(client);
|
||||
if (utils.escapeHtml(client) !== client) {
|
||||
// Make a copy with the escaped index if necessary
|
||||
data.top_sources[escapeHtml(client)] = data.top_sources[client];
|
||||
data.top_sources[utils.escapeHtml(client)] = data.top_sources[client];
|
||||
}
|
||||
|
||||
if (client.indexOf("|") > -1) {
|
||||
if (client.indexOf("|") !== -1) {
|
||||
var idx = client.indexOf("|");
|
||||
clientname = client.substr(0, idx);
|
||||
} else {
|
||||
clientname = client;
|
||||
}
|
||||
|
||||
percentage = (data.top_sources[client] / sum) * 100.0;
|
||||
percentage = (data.top_sources[client] / sum) * 100;
|
||||
clienttable.append(
|
||||
"<tr> <td>" +
|
||||
clientname +
|
||||
@@ -145,11 +108,9 @@ function updateTopClientsChart() {
|
||||
|
||||
function updateTopDomainsChart() {
|
||||
$("#domain-frequency .overlay").show();
|
||||
$.getJSON("api_db.php?topDomains&from=" + from + "&until=" + until, function(data) {
|
||||
$.getJSON("api_db.php?topDomains&from=" + from + "&until=" + until, function (data) {
|
||||
// Clear tables before filling them with data
|
||||
$("#domain-frequency td")
|
||||
.parent()
|
||||
.remove();
|
||||
$("#domain-frequency td").parent().remove();
|
||||
var domaintable = $("#domain-frequency").find("tbody:last");
|
||||
var domain, percentage;
|
||||
var sum = 0;
|
||||
@@ -162,13 +123,13 @@ function updateTopDomainsChart() {
|
||||
for (domain in data.top_domains) {
|
||||
if (Object.prototype.hasOwnProperty.call(data.top_domains, domain)) {
|
||||
// Sanitize domain
|
||||
domain = escapeHtml(domain);
|
||||
if (escapeHtml(domain) !== domain) {
|
||||
domain = utils.escapeHtml(domain);
|
||||
if (utils.escapeHtml(domain) !== domain) {
|
||||
// Make a copy with the escaped index if necessary
|
||||
data.top_domains[escapeHtml(domain)] = data.top_domains[domain];
|
||||
data.top_domains[utils.escapeHtml(domain)] = data.top_domains[domain];
|
||||
}
|
||||
|
||||
percentage = (data.top_domains[domain] / sum) * 100.0;
|
||||
percentage = (data.top_domains[domain] / sum) * 100;
|
||||
domaintable.append(
|
||||
"<tr> <td>" +
|
||||
domain +
|
||||
@@ -178,7 +139,7 @@ function updateTopDomainsChart() {
|
||||
percentage.toFixed(1) +
|
||||
"% of " +
|
||||
sum +
|
||||
'"> <div class="progress-bar progress-bar-blue" style="width: ' +
|
||||
'"> <div class="progress-bar queries-blocked" style="width: ' +
|
||||
percentage +
|
||||
'%"></div> </div> </td> </tr> '
|
||||
);
|
||||
@@ -194,11 +155,9 @@ function updateTopDomainsChart() {
|
||||
|
||||
function updateTopAdsChart() {
|
||||
$("#ad-frequency .overlay").show();
|
||||
$.getJSON("api_db.php?topAds&from=" + from + "&until=" + until, function(data) {
|
||||
$.getJSON("api_db.php?topAds&from=" + from + "&until=" + until, function (data) {
|
||||
// Clear tables before filling them with data
|
||||
$("#ad-frequency td")
|
||||
.parent()
|
||||
.remove();
|
||||
$("#ad-frequency td").parent().remove();
|
||||
var adtable = $("#ad-frequency").find("tbody:last");
|
||||
var ad, percentage;
|
||||
var sum = 0;
|
||||
@@ -211,13 +170,13 @@ function updateTopAdsChart() {
|
||||
for (ad in data.top_ads) {
|
||||
if (Object.prototype.hasOwnProperty.call(data.top_ads, ad)) {
|
||||
// Sanitize ad
|
||||
ad = escapeHtml(ad);
|
||||
if (escapeHtml(ad) !== ad) {
|
||||
ad = utils.escapeHtml(ad);
|
||||
if (utils.escapeHtml(ad) !== ad) {
|
||||
// Make a copy with the escaped index if necessary
|
||||
data.top_ads[escapeHtml(ad)] = data.top_ads[ad];
|
||||
data.top_ads[utils.escapeHtml(ad)] = data.top_ads[ad];
|
||||
}
|
||||
|
||||
percentage = (data.top_ads[ad] / sum) * 100.0;
|
||||
percentage = (data.top_ads[ad] / sum) * 100;
|
||||
adtable.append(
|
||||
"<tr> <td>" +
|
||||
ad +
|
||||
@@ -227,7 +186,7 @@ function updateTopAdsChart() {
|
||||
percentage.toFixed(1) +
|
||||
"% of " +
|
||||
sum +
|
||||
'"> <div class="progress-bar progress-bar-blue" style="width: ' +
|
||||
'"> <div class="progress-bar queries-permitted" style="width: ' +
|
||||
percentage +
|
||||
'%"></div> </div> </td> </tr> '
|
||||
);
|
||||
@@ -241,7 +200,7 @@ function updateTopAdsChart() {
|
||||
});
|
||||
}
|
||||
|
||||
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
|
||||
$("#querytime").on("apply.daterangepicker", function (ev, picker) {
|
||||
$(this).val(picker.startDate.format(dateformat) + " to " + picker.endDate.format(dateformat));
|
||||
timeoutWarning.show();
|
||||
listsStillLoading = 3;
|
||||
|
||||
@@ -8,15 +8,9 @@
|
||||
/* global moment:false */
|
||||
|
||||
var start__ = moment().subtract(6, "days");
|
||||
var from =
|
||||
moment(start__)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
var from = moment(start__).utc().valueOf() / 1000;
|
||||
var end__ = moment();
|
||||
var until =
|
||||
moment(end__)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
var until = moment(end__).utc().valueOf() / 1000;
|
||||
var instantquery = false;
|
||||
var daterange;
|
||||
|
||||
@@ -29,7 +23,7 @@ var GETDict = {};
|
||||
window.location.search
|
||||
.substr(1)
|
||||
.split("&")
|
||||
.forEach(function(item) {
|
||||
.forEach(function (item) {
|
||||
GETDict[item.split("=")[0]] = item.split("=")[1];
|
||||
});
|
||||
|
||||
@@ -41,7 +35,7 @@ if ("from" in GETDict && "until" in GETDict) {
|
||||
instantquery = true;
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$(function () {
|
||||
daterange = $("#querytime").daterangepicker(
|
||||
{
|
||||
timePicker: true,
|
||||
@@ -52,23 +46,15 @@ $(function() {
|
||||
ranges: {
|
||||
Today: [moment().startOf("day"), moment()],
|
||||
Yesterday: [
|
||||
moment()
|
||||
.subtract(1, "days")
|
||||
.startOf("day"),
|
||||
moment()
|
||||
.subtract(1, "days")
|
||||
.endOf("day")
|
||||
moment().subtract(1, "days").startOf("day"),
|
||||
moment().subtract(1, "days").endOf("day")
|
||||
],
|
||||
"Last 7 Days": [moment().subtract(6, "days"), moment()],
|
||||
"Last 30 Days": [moment().subtract(29, "days"), moment()],
|
||||
"This Month": [moment().startOf("month"), moment()],
|
||||
"Last Month": [
|
||||
moment()
|
||||
.subtract(1, "month")
|
||||
.startOf("month"),
|
||||
moment()
|
||||
.subtract(1, "month")
|
||||
.endOf("month")
|
||||
moment().subtract(1, "month").startOf("month"),
|
||||
moment().subtract(1, "month").endOf("month")
|
||||
],
|
||||
"This Year": [moment().startOf("year"), moment()],
|
||||
"All Time": [moment(0), moment()]
|
||||
@@ -77,15 +63,9 @@ $(function() {
|
||||
showDropdowns: true,
|
||||
autoUpdateInput: false
|
||||
},
|
||||
function(startt, endt) {
|
||||
from =
|
||||
moment(startt)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
until =
|
||||
moment(endt)
|
||||
.utc()
|
||||
.valueOf() / 1000;
|
||||
function (startt, endt) {
|
||||
from = moment(startt).utc().valueOf() / 1000;
|
||||
until = moment(endt).utc().valueOf() / 1000;
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -112,39 +92,42 @@ function add(domain, list) {
|
||||
alSuccess.hide();
|
||||
alFailure.hide();
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/add.php",
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
data: { domain: domain, list: list, token: token },
|
||||
success: function(response) {
|
||||
if (
|
||||
response.indexOf("not a valid argument") >= 0 ||
|
||||
response.indexOf("is not a valid domain") >= 0
|
||||
) {
|
||||
data: {
|
||||
domain: domain,
|
||||
list: list,
|
||||
token: token,
|
||||
action: "add_domain",
|
||||
comment: "Added from Long-Term-Data Query Log"
|
||||
},
|
||||
success: function (response) {
|
||||
if (!response.success) {
|
||||
alFailure.show();
|
||||
err.html(response);
|
||||
alFailure.delay(4000).fadeOut(2000, function() {
|
||||
err.html(response.message);
|
||||
alFailure.delay(4000).fadeOut(2000, function () {
|
||||
alFailure.hide();
|
||||
});
|
||||
} else {
|
||||
alSuccess.show();
|
||||
alSuccess.delay(1000).fadeOut(2000, function() {
|
||||
alSuccess.delay(1000).fadeOut(2000, function () {
|
||||
alSuccess.hide();
|
||||
});
|
||||
}
|
||||
|
||||
alInfo.delay(1000).fadeOut(2000, function() {
|
||||
alInfo.delay(1000).fadeOut(2000, function () {
|
||||
alInfo.hide();
|
||||
alList.html("");
|
||||
alDomain.html("");
|
||||
});
|
||||
},
|
||||
error: function() {
|
||||
error: function () {
|
||||
alFailure.show();
|
||||
err.html("");
|
||||
alFailure.delay(1000).fadeOut(2000, function() {
|
||||
alFailure.delay(1000).fadeOut(2000, function () {
|
||||
alFailure.hide();
|
||||
});
|
||||
alInfo.delay(1000).fadeOut(2000, function() {
|
||||
alInfo.delay(1000).fadeOut(2000, function () {
|
||||
alInfo.hide();
|
||||
alList.html("");
|
||||
alDomain.html("");
|
||||
@@ -156,7 +139,7 @@ function add(domain, list) {
|
||||
function handleAjaxError(xhr, textStatus) {
|
||||
if (textStatus === "timeout") {
|
||||
alert("The server took too long to send the data.");
|
||||
} else if (xhr.responseText.indexOf("Connection refused") >= 0) {
|
||||
} else if (xhr.responseText.indexOf("Connection refused") !== -1) {
|
||||
alert("An error occurred while loading the data: Connection refused. Is FTL running?");
|
||||
} else {
|
||||
alert("An unknown error occurred while loading the data.\n" + xhr.responseText);
|
||||
@@ -210,18 +193,18 @@ function getQueryTypes() {
|
||||
return queryType.join(",");
|
||||
}
|
||||
|
||||
var reloadCallback = function() {
|
||||
var reloadCallback = function () {
|
||||
timeoutWarning.hide();
|
||||
statistics = [0, 0, 0, 0];
|
||||
var data = tableApi.rows().data();
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
statistics[0]++;
|
||||
if (data[i][4] === 1) {
|
||||
statistics[2]++;
|
||||
statistics[0]++; // TOTAL query
|
||||
if (data[i][4] === 1 || (data[i][4] > 4 && data[i][4] !== 10)) {
|
||||
statistics[2]++; // EXACT blocked
|
||||
} else if (data[i][4] === 3) {
|
||||
statistics[1]++;
|
||||
} else if (data[i][4] === 4) {
|
||||
statistics[3]++;
|
||||
statistics[1]++; // CACHE query
|
||||
} else if (data[i][4] === 4 || data[i][4] === 10) {
|
||||
statistics[3]++; // REGEX blocked
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,16 +212,12 @@ var reloadCallback = function() {
|
||||
$("h3#ads_blocked_exact").text(statistics[2].toLocaleString());
|
||||
$("h3#ads_wildcard_blocked").text(statistics[3].toLocaleString());
|
||||
|
||||
var percent = 0.0;
|
||||
var percent = 0;
|
||||
if (statistics[2] + statistics[3] > 0) {
|
||||
percent = (100.0 * (statistics[2] + statistics[3])) / statistics[0];
|
||||
percent = (100 * (statistics[2] + statistics[3])) / statistics[0];
|
||||
}
|
||||
|
||||
$("h3#ads_percentage_today").text(
|
||||
parseFloat(percent)
|
||||
.toFixed(1)
|
||||
.toLocaleString() + " %"
|
||||
);
|
||||
$("h3#ads_percentage_today").text(parseFloat(percent).toFixed(1).toLocaleString() + " %");
|
||||
};
|
||||
|
||||
function refreshTableData() {
|
||||
@@ -254,7 +233,7 @@ function refreshTableData() {
|
||||
tableApi.ajax.url(APIstring).load(reloadCallback);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
var APIstring;
|
||||
|
||||
if (instantquery) {
|
||||
@@ -271,7 +250,7 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
tableApi = $("#all-queries").DataTable({
|
||||
rowCallback: function(row, data) {
|
||||
rowCallback: function (row, data) {
|
||||
var fieldtext, buttontext, color;
|
||||
switch (data[4]) {
|
||||
case 1:
|
||||
@@ -346,6 +325,14 @@ $(document).ready(function() {
|
||||
$(row).css("color", color);
|
||||
$("td:eq(4)", row).html(fieldtext);
|
||||
$("td:eq(5)", row).html(buttontext);
|
||||
|
||||
// Substitute domain by "." if empty
|
||||
var domain = data[2];
|
||||
if (domain.length === 0) {
|
||||
domain = ".";
|
||||
}
|
||||
|
||||
$("td:eq(2)", row).text(domain);
|
||||
},
|
||||
dom:
|
||||
"<'row'<'col-sm-12'f>>" +
|
||||
@@ -355,9 +342,9 @@ $(document).ready(function() {
|
||||
ajax: {
|
||||
url: APIstring,
|
||||
error: handleAjaxError,
|
||||
dataSrc: function(data) {
|
||||
dataSrc: function (data) {
|
||||
var dataIndex = 0;
|
||||
return data.data.map(function(x) {
|
||||
return data.data.map(function (x) {
|
||||
x[0] = x[0] * 1e6 + dataIndex++;
|
||||
return x;
|
||||
});
|
||||
@@ -370,7 +357,7 @@ $(document).ready(function() {
|
||||
columns: [
|
||||
{
|
||||
width: "15%",
|
||||
render: function(data, type) {
|
||||
render: function (data, type) {
|
||||
if (type === "display") {
|
||||
return moment
|
||||
.unix(Math.floor(data / 1e6))
|
||||
@@ -399,7 +386,7 @@ $(document).ready(function() {
|
||||
],
|
||||
initComplete: reloadCallback
|
||||
});
|
||||
$("#all-queries tbody").on("click", "button", function() {
|
||||
$("#all-queries tbody").on("click", "button", function () {
|
||||
var data = tableApi.row($(this).parents("tr")).data();
|
||||
if (data[4] === 1 || data[4] === 4 || data[5] === 5) {
|
||||
add(data[2], "white");
|
||||
@@ -413,7 +400,7 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
|
||||
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
|
||||
$("#querytime").on("apply.daterangepicker", function (ev, picker) {
|
||||
$(this).val(picker.startDate.format(dateformat) + " to " + picker.endDate.format(dateformat));
|
||||
refreshTableData();
|
||||
});
|
||||
|
||||
@@ -5,31 +5,6 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global ActiveXObject: false */
|
||||
|
||||
// Credit: http://stackoverflow.com/a/10642418/2087442
|
||||
function httpGet(ta, theUrl) {
|
||||
var xmlhttp;
|
||||
if (window.XMLHttpRequest) {
|
||||
// code for IE7+
|
||||
xmlhttp = new XMLHttpRequest();
|
||||
} else {
|
||||
// code for IE6, IE5
|
||||
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
|
||||
ta.show();
|
||||
ta.empty();
|
||||
ta.append(xmlhttp.responseText);
|
||||
}
|
||||
};
|
||||
|
||||
xmlhttp.open("GET", theUrl, false);
|
||||
xmlhttp.send();
|
||||
}
|
||||
|
||||
function eventsource() {
|
||||
var ta = $("#output");
|
||||
var upload = $("#upload");
|
||||
@@ -42,10 +17,19 @@ function eventsource() {
|
||||
|
||||
// IE does not support EventSource - load whole content at once
|
||||
if (typeof EventSource !== "function") {
|
||||
httpGet(ta, "scripts/pi-hole/php/debug.php?IE&token=" + token + "&" + checked);
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: "scripts/pi-hole/php/debug.php?IE&token=" + token + "&" + checked,
|
||||
async: false
|
||||
}).done(function (data) {
|
||||
ta.show();
|
||||
ta.empty();
|
||||
ta.append(data);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line compat/compat
|
||||
var source = new EventSource("scripts/pi-hole/php/debug.php?&token=" + token + "&" + checked);
|
||||
|
||||
// Reset and show field
|
||||
@@ -54,7 +38,7 @@ function eventsource() {
|
||||
|
||||
source.addEventListener(
|
||||
"message",
|
||||
function(e) {
|
||||
function (e) {
|
||||
ta.append(e.data);
|
||||
},
|
||||
false
|
||||
@@ -63,15 +47,15 @@ function eventsource() {
|
||||
// Will be called when script has finished
|
||||
source.addEventListener(
|
||||
"error",
|
||||
function() {
|
||||
function () {
|
||||
source.close();
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
$("#debugBtn").on("click", function() {
|
||||
$("#debugBtn").attr("disabled", true);
|
||||
$("#upload").attr("disabled", true);
|
||||
$("#debugBtn").on("click", function () {
|
||||
$("#debugBtn").prop("disabled", true);
|
||||
$("#upload").prop("disabled", true);
|
||||
eventsource();
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
/* global initpage:false */
|
||||
|
||||
//The following functions allow us to display time until pi-hole is enabled after disabling.
|
||||
//Works between all pages
|
||||
@@ -65,7 +66,7 @@ function piholeChange(action, duration) {
|
||||
case "enable":
|
||||
btnStatus = $("#flip-status-enable");
|
||||
btnStatus.html("<i class='fa fa-spinner'> </i>");
|
||||
$.getJSON("api.php?enable&token=" + token, function(data) {
|
||||
$.getJSON("api.php?enable&token=" + token, function (data) {
|
||||
if (data.status === "enabled") {
|
||||
btnStatus.html("");
|
||||
piholeChanged("enabled");
|
||||
@@ -76,7 +77,7 @@ function piholeChange(action, duration) {
|
||||
case "disable":
|
||||
btnStatus = $("#flip-status-disable");
|
||||
btnStatus.html("<i class='fa fa-spinner'> </i>");
|
||||
$.getJSON("api.php?disable=" + duration + "&token=" + token, function(data) {
|
||||
$.getJSON("api.php?disable=" + duration + "&token=" + token, function (data) {
|
||||
if (data.status === "disabled") {
|
||||
btnStatus.html("");
|
||||
piholeChanged("disabled");
|
||||
@@ -93,38 +94,166 @@ function piholeChange(action, duration) {
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
function checkMessages() {
|
||||
$.getJSON("api_db.php?status", function (data) {
|
||||
if ("message_count" in data && data.message_count > 0) {
|
||||
var title;
|
||||
if (data.message_count > 1) {
|
||||
title = "There are " + data.message_count + " warnings. Click for further details.";
|
||||
} else {
|
||||
title = "There is one warning. Click for further details.";
|
||||
}
|
||||
|
||||
$("#pihole-diagnosis").prop("title", title);
|
||||
$("#pihole-diagnosis-count").text(data.message_count);
|
||||
$("#pihole-diagnosis").removeClass("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testCookies() {
|
||||
if (navigator.cookieEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// set and read cookie
|
||||
document.cookie = "cookietest=1";
|
||||
var ret = document.cookie.indexOf("cookietest=") !== -1;
|
||||
|
||||
// delete cookie
|
||||
document.cookie = "cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function initCheckboxRadioStyle() {
|
||||
function getCheckboxURL(style) {
|
||||
var extra = style.startsWith("material-") ? "material" : "bootstrap";
|
||||
return "style/vendor/icheck-" + extra + ".min.css";
|
||||
}
|
||||
|
||||
function applyCheckboxRadioStyle(style) {
|
||||
boxsheet.attr("href", getCheckboxURL(style));
|
||||
var sel = $("input[type='radio'],input[type='checkbox']");
|
||||
sel.parent().removeClass();
|
||||
sel.parent().addClass("icheck-" + style);
|
||||
}
|
||||
|
||||
// Read from local storage, initialize if needed
|
||||
var chkboxStyle = localStorage.getItem("theme_icheck");
|
||||
if (chkboxStyle === null) {
|
||||
chkboxStyle = "primary";
|
||||
}
|
||||
|
||||
var boxsheet = $('<link href="' + getCheckboxURL(chkboxStyle) + '" rel="stylesheet" />');
|
||||
boxsheet.appendTo("head");
|
||||
|
||||
applyCheckboxRadioStyle(chkboxStyle);
|
||||
|
||||
// Add handler when on settings page
|
||||
var iCheckStyle = $("#iCheckStyle");
|
||||
if (iCheckStyle !== null) {
|
||||
iCheckStyle.val(chkboxStyle);
|
||||
iCheckStyle.change(function () {
|
||||
var themename = $(this).val();
|
||||
localStorage.setItem("theme_icheck", themename);
|
||||
applyCheckboxRadioStyle(themename);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initCPUtemp() {
|
||||
function setCPUtemp(unit) {
|
||||
localStorage.setItem("tempunit", tempunit);
|
||||
var temperature = parseFloat($("#rawtemp").text());
|
||||
var displaytemp = $("#tempdisplay");
|
||||
if (!isNaN(temperature)) {
|
||||
switch (unit) {
|
||||
case "K":
|
||||
temperature += 273.15;
|
||||
displaytemp.html(temperature.toFixed(1) + " °K");
|
||||
break;
|
||||
|
||||
case "F":
|
||||
temperature = (temperature * 9) / 5 + 32;
|
||||
displaytemp.html(temperature.toFixed(1) + " °F");
|
||||
break;
|
||||
|
||||
default:
|
||||
displaytemp.html(temperature.toFixed(1) + " °C");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read from local storage, initialize if needed
|
||||
var tempunit = localStorage.getItem("tempunit");
|
||||
if (tempunit === null) {
|
||||
tempunit = "C";
|
||||
}
|
||||
|
||||
setCPUtemp(tempunit);
|
||||
|
||||
// Add handler when on settings page
|
||||
var tempunitSelector = $("#tempunit-selector");
|
||||
if (tempunitSelector !== null) {
|
||||
tempunitSelector.val(tempunit);
|
||||
tempunitSelector.change(function () {
|
||||
tempunit = $(this).val();
|
||||
setCPUtemp(tempunit);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
var enaT = $("#enableTimer");
|
||||
var target = new Date(parseInt(enaT.html()));
|
||||
var seconds = Math.round((target.getTime() - new Date().getTime()) / 1000);
|
||||
if (seconds > 0) {
|
||||
setTimeout(countDown, 100);
|
||||
}
|
||||
|
||||
if (!testCookies() && $("#cookieInfo").length > 0) {
|
||||
$("#cookieInfo").show();
|
||||
}
|
||||
|
||||
// Apply per-browser styling settings
|
||||
initCheckboxRadioStyle();
|
||||
initCPUtemp();
|
||||
|
||||
if (typeof initpage === "function") {
|
||||
setTimeout(initpage, 100);
|
||||
}
|
||||
|
||||
// Run check immediately after page loading ...
|
||||
checkMessages();
|
||||
// ... and once again with five seconds delay
|
||||
setTimeout(checkMessages, 5000);
|
||||
});
|
||||
|
||||
// Handle Enable/Disable
|
||||
$("#pihole-enable").on("click", function(e) {
|
||||
$("#pihole-enable").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
localStorage.removeItem("countDownTarget");
|
||||
piholeChange("enable", "");
|
||||
});
|
||||
$("#pihole-disable-permanently").on("click", function(e) {
|
||||
$("#pihole-disable-indefinitely").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
piholeChange("disable", "0");
|
||||
});
|
||||
$("#pihole-disable-10s").on("click", function(e) {
|
||||
$("#pihole-disable-10s").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
piholeChange("disable", "10");
|
||||
});
|
||||
$("#pihole-disable-30s").on("click", function(e) {
|
||||
$("#pihole-disable-30s").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
piholeChange("disable", "30");
|
||||
});
|
||||
$("#pihole-disable-5m").on("click", function(e) {
|
||||
$("#pihole-disable-5m").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
piholeChange("disable", "300");
|
||||
});
|
||||
$("#pihole-disable-custom").on("click", function(e) {
|
||||
$("#pihole-disable-custom").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
var custVal = $("#customTimeout").val();
|
||||
custVal = $("#btnMins").hasClass("active") ? custVal * 60 : custVal;
|
||||
@@ -145,7 +274,7 @@ if (sessionvalidity > 0) {
|
||||
// setSeconds will correctly handle wrap-around cases
|
||||
updateSessionTimer();
|
||||
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
var current = new Date();
|
||||
var totalseconds = (start - current) / 1000;
|
||||
var minutes = Math.floor(totalseconds / 60);
|
||||
@@ -169,30 +298,9 @@ if (sessionvalidity > 0) {
|
||||
}
|
||||
|
||||
// Handle Strg + Enter button on Login page
|
||||
$(document).keypress(function(e) {
|
||||
$(document).keypress(function (e) {
|
||||
if ((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey && $("#loginpw").is(":focus")) {
|
||||
$("#loginform").attr("action", "settings.php");
|
||||
$("#loginform").submit();
|
||||
}
|
||||
});
|
||||
|
||||
function testCookies() {
|
||||
if (navigator.cookieEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// set and read cookie
|
||||
document.cookie = "cookietest=1";
|
||||
var ret = document.cookie.indexOf("cookietest=") !== -1;
|
||||
|
||||
// delete cookie
|
||||
document.cookie = "cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
$(function() {
|
||||
if (!testCookies() && $("#cookieInfo").length) {
|
||||
$("#cookieInfo").show();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ function eventsource() {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line compat/compat
|
||||
var source = new EventSource("scripts/pi-hole/php/gravity.sh.php");
|
||||
|
||||
ta.html("");
|
||||
@@ -26,16 +27,17 @@ function eventsource() {
|
||||
|
||||
source.addEventListener(
|
||||
"message",
|
||||
function(e) {
|
||||
function (e) {
|
||||
if (e.data.indexOf("Pi-hole blocking is") !== -1) {
|
||||
alSuccess.show();
|
||||
}
|
||||
|
||||
// Detect ${OVER}
|
||||
if (e.data.indexOf("<------") !== -1) {
|
||||
var newString = "<------";
|
||||
|
||||
if (e.data.indexOf(newString) !== -1) {
|
||||
ta.text(ta.text().substring(0, ta.text().lastIndexOf("\n")) + "\n");
|
||||
var new_string = e.data.replace("<------", "");
|
||||
ta.append(new_string);
|
||||
ta.append(e.data.replace(newString, ""));
|
||||
} else {
|
||||
ta.append(e.data);
|
||||
}
|
||||
@@ -46,25 +48,25 @@ function eventsource() {
|
||||
// Will be called when script has finished
|
||||
source.addEventListener(
|
||||
"error",
|
||||
function() {
|
||||
alInfo.delay(1000).fadeOut(2000, function() {
|
||||
function () {
|
||||
alInfo.delay(1000).fadeOut(2000, function () {
|
||||
alInfo.hide();
|
||||
});
|
||||
source.close();
|
||||
$("#gravityBtn").removeAttr("disabled");
|
||||
$("#gravityBtn").prop("disabled", false);
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
$("#gravityBtn").on("click", function() {
|
||||
$("#gravityBtn").attr("disabled", true);
|
||||
$("#gravityBtn").on("click", function () {
|
||||
$("#gravityBtn").prop("disabled", true);
|
||||
eventsource();
|
||||
});
|
||||
|
||||
// Handle hiding of alerts
|
||||
$(function() {
|
||||
$("[data-hide]").on("click", function() {
|
||||
$(function () {
|
||||
$("[data-hide]").on("click", function () {
|
||||
$(this)
|
||||
.closest("." + $(this).attr("data-hide"))
|
||||
.hide();
|
||||
@@ -74,7 +76,7 @@ $(function() {
|
||||
// gravity.php?go
|
||||
var searchString = window.location.search.substring(1);
|
||||
if (searchString.indexOf("go") !== -1) {
|
||||
$("#gravityBtn").attr("disabled", true);
|
||||
$("#gravityBtn").prop("disabled", true);
|
||||
eventsource();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
var table;
|
||||
var groups = [];
|
||||
var token = $("#token").html();
|
||||
var token = $("#token").text();
|
||||
|
||||
function get_groups() {
|
||||
function getGroups() {
|
||||
$.post(
|
||||
"scripts/pi-hole/php/groups.php",
|
||||
{ action: "get_groups", token: token },
|
||||
function(data) {
|
||||
function (data) {
|
||||
groups = data.data;
|
||||
initTable();
|
||||
},
|
||||
@@ -23,11 +23,11 @@ function get_groups() {
|
||||
);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
$("#btnAdd").on("click", addAdlist);
|
||||
|
||||
utils.bsSelect_defaults();
|
||||
get_groups();
|
||||
utils.setBsSelectDefaults();
|
||||
getGroups();
|
||||
});
|
||||
|
||||
function initTable() {
|
||||
@@ -46,12 +46,12 @@ function initTable() {
|
||||
{ data: "groups", searchable: false },
|
||||
{ data: null, width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function() {
|
||||
drawCallback: function () {
|
||||
$('button[id^="deleteAdlist_"]').on("click", deleteAdlist);
|
||||
// Remove visible dropdown to prevent orphaning
|
||||
$("body > .bootstrap-select.dropdown").remove();
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
rowCallback: function (row, data) {
|
||||
$(row).attr("data-id", data.id);
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
@@ -96,13 +96,13 @@ function initTable() {
|
||||
var selectEl = $("#multiselect_" + data.id, row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var data_sub = "";
|
||||
var dataSub = "";
|
||||
if (!groups[i].enabled) {
|
||||
data_sub = 'data-subtext="(disabled)"';
|
||||
dataSub = 'data-subtext="(disabled)"';
|
||||
}
|
||||
|
||||
selectEl.append(
|
||||
$("<option " + data_sub + "/>")
|
||||
$("<option " + dataSub + "/>")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name)
|
||||
);
|
||||
@@ -113,7 +113,7 @@ function initTable() {
|
||||
// Initialize bootstrap-select
|
||||
selectEl
|
||||
// fix dropdown if it would stick out right of the viewport
|
||||
.on("show.bs.select", function() {
|
||||
.on("show.bs.select", function () {
|
||||
var winWidth = $(window).width();
|
||||
var dropdownEl = $("body > .bootstrap-select.dropdown");
|
||||
if (dropdownEl.length > 0) {
|
||||
@@ -125,27 +125,22 @@ function initTable() {
|
||||
}
|
||||
}
|
||||
})
|
||||
.on("changed.bs.select", function() {
|
||||
.on("changed.bs.select", function () {
|
||||
// enable Apply button
|
||||
if ($(ApplyBtn).prop("disabled")) {
|
||||
$(ApplyBtn)
|
||||
if ($(applyBtn).prop("disabled")) {
|
||||
$(applyBtn)
|
||||
.addClass("btn-success")
|
||||
.prop("disabled", false)
|
||||
.on("click", function() {
|
||||
.on("click", function () {
|
||||
editAdlist.call(selectEl);
|
||||
});
|
||||
}
|
||||
})
|
||||
.on("hide.bs.select", function() {
|
||||
.on("hide.bs.select", function () {
|
||||
// Restore values if drop-down menu is closed without clicking the Apply button
|
||||
if (!$(ApplyBtn).prop("disabled")) {
|
||||
$(this)
|
||||
.val(data.groups)
|
||||
.selectpicker("refresh");
|
||||
$(ApplyBtn)
|
||||
.removeClass("btn-success")
|
||||
.prop("disabled", true)
|
||||
.off("click");
|
||||
if (!$(applyBtn).prop("disabled")) {
|
||||
$(this).val(data.groups).selectpicker("refresh");
|
||||
$(applyBtn).removeClass("btn-success").prop("disabled", true).off("click");
|
||||
}
|
||||
})
|
||||
.selectpicker()
|
||||
@@ -157,13 +152,13 @@ function initTable() {
|
||||
' class="btn btn-block btn-sm" disabled>Apply</button>'
|
||||
);
|
||||
|
||||
var ApplyBtn = "#btn_apply_" + data.id;
|
||||
var applyBtn = "#btn_apply_" + data.id;
|
||||
|
||||
var button =
|
||||
'<button class="btn btn-danger btn-xs" type="button" id="deleteAdlist_' +
|
||||
'<button type="button" class="btn btn-danger btn-xs" id="deleteAdlist_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(4)", row).html(button);
|
||||
},
|
||||
@@ -176,23 +171,16 @@ function initTable() {
|
||||
[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));
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("groups-adlists-table", data);
|
||||
},
|
||||
stateLoadCallback: function() {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("groups-adlists-table");
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("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
|
||||
@@ -200,18 +188,27 @@ function initTable() {
|
||||
}
|
||||
});
|
||||
|
||||
table.on("order.dt", function() {
|
||||
table.on("order.dt", function () {
|
||||
var order = table.order();
|
||||
if (order[0][0] !== 0 || order[0][1] !== "asc") {
|
||||
$("#resetButton").show();
|
||||
$("#resetButton").removeClass("hidden");
|
||||
} else {
|
||||
$("#resetButton").hide();
|
||||
$("#resetButton").addClass("hidden");
|
||||
}
|
||||
});
|
||||
$("#resetButton").on("click", function() {
|
||||
$("#resetButton").on("click", function () {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").hide();
|
||||
$("#resetButton").addClass("hidden");
|
||||
});
|
||||
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
if (input !== null) {
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
}
|
||||
}
|
||||
|
||||
function addAdlist() {
|
||||
@@ -236,15 +233,10 @@ function addAdlist() {
|
||||
comment: comment,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-plus",
|
||||
"Successfully added adlist",
|
||||
address
|
||||
);
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully added adlist", address);
|
||||
table.ajax.reload(null, false);
|
||||
$("#new_address").val("");
|
||||
$("#new_comment").val("");
|
||||
@@ -253,10 +245,10 @@ function addAdlist() {
|
||||
utils.showAlert("error", "", "Error while adding new adlist: ", response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while adding new adlist: ", jqXHR.responseText);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -271,25 +263,25 @@ function editAdlist() {
|
||||
var address = tr.find("#address_" + id).text();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
var notDone = "editing";
|
||||
switch (elem) {
|
||||
case "status_" + id:
|
||||
if (status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
notDone = "disabling";
|
||||
} else if (status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
notDone = "enabling";
|
||||
}
|
||||
|
||||
break;
|
||||
case "comment_" + id:
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
notDone = "editing comment of";
|
||||
break;
|
||||
case "multiselect_" + id:
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
notDone = "editing groups of";
|
||||
break;
|
||||
default:
|
||||
alert("bad element or invalid data-id!");
|
||||
@@ -311,12 +303,12 @@ function editAdlist() {
|
||||
groups: groups,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-pencil",
|
||||
"fas fa-pencil-alt",
|
||||
"Successfully " + done + " adlist ",
|
||||
address
|
||||
);
|
||||
@@ -325,20 +317,20 @@ function editAdlist() {
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " adlist with ID " + id,
|
||||
"Error while " + notDone + " adlist with ID " + id,
|
||||
Number(response.message)
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " adlist with ID " + id,
|
||||
"Error while " + notDone + " adlist with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -355,28 +347,19 @@ function deleteAdlist() {
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_adlist", id: id, token: token },
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-trash",
|
||||
"Successfully deleted adlist ",
|
||||
address
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false)
|
||||
.ajax.reload(null, false);
|
||||
utils.showAlert("success", "far fa-trash-alt", "Successfully deleted adlist ", address);
|
||||
table.row(tr).remove().draw(false).ajax.reload(null, false);
|
||||
} else {
|
||||
utils.showAlert("error", "", "Error while deleting adlist with ID " + id, response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while deleting adlist with ID " + id, jqXHR.responseText);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
var table;
|
||||
var groups = [];
|
||||
var token = $("#token").html();
|
||||
var token = $("#token").text();
|
||||
|
||||
function reload_client_suggestions() {
|
||||
function reloadClientSuggestions() {
|
||||
$.post(
|
||||
"scripts/pi-hole/php/groups.php",
|
||||
{ action: "get_unconfigured_clients", token: token },
|
||||
function(data) {
|
||||
function (data) {
|
||||
var sel = $("#select");
|
||||
var customWasSelected = sel.val() === "custom";
|
||||
sel.empty();
|
||||
@@ -29,18 +29,14 @@ function reload_client_suggestions() {
|
||||
text += " (" + data[key] + ")";
|
||||
}
|
||||
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val(key)
|
||||
.text(text)
|
||||
);
|
||||
sel.append($("<option />").val(key).text(text));
|
||||
}
|
||||
|
||||
sel.append(
|
||||
$("<option />")
|
||||
.val("custom")
|
||||
.text("Custom, specified below...")
|
||||
);
|
||||
if (data.length === 0) {
|
||||
$("#ip-custom").prop("disabled", false);
|
||||
}
|
||||
|
||||
sel.append($("<option />").val("custom").text("Custom, specified below..."));
|
||||
if (customWasSelected) {
|
||||
sel.val("custom");
|
||||
}
|
||||
@@ -49,11 +45,11 @@ function reload_client_suggestions() {
|
||||
);
|
||||
}
|
||||
|
||||
function get_groups() {
|
||||
function getGroups() {
|
||||
$.post(
|
||||
"scripts/pi-hole/php/groups.php",
|
||||
{ action: "get_groups", token: token },
|
||||
function(data) {
|
||||
function (data) {
|
||||
groups = data.data;
|
||||
initTable();
|
||||
},
|
||||
@@ -61,14 +57,14 @@ function get_groups() {
|
||||
);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
$("#btnAdd").on("click", addClient);
|
||||
|
||||
reload_client_suggestions();
|
||||
utils.bsSelect_defaults();
|
||||
get_groups();
|
||||
reloadClientSuggestions();
|
||||
utils.setBsSelectDefaults();
|
||||
getGroups();
|
||||
|
||||
$("#select").on("change", function() {
|
||||
$("#select").on("change", function () {
|
||||
$("#ip-custom").val("");
|
||||
$("#ip-custom").prop("disabled", $("#select option:selected").val() !== "custom");
|
||||
});
|
||||
@@ -84,17 +80,17 @@ function initTable() {
|
||||
order: [[0, "asc"]],
|
||||
columns: [
|
||||
{ data: "id", visible: false },
|
||||
{ data: "ip" },
|
||||
{ data: "ip", type: "ip-address" },
|
||||
{ data: "comment" },
|
||||
{ data: "groups", searchable: false },
|
||||
{ data: "name", width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function() {
|
||||
drawCallback: function () {
|
||||
$('button[id^="deleteClient_"]').on("click", deleteClient);
|
||||
// Remove visible dropdown to prevent orphaning
|
||||
$("body > .bootstrap-select.dropdown").remove();
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
rowCallback: function (row, data) {
|
||||
$(row).attr("data-id", data.id);
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
@@ -103,7 +99,7 @@ function initTable() {
|
||||
utils.datetime(data.date_modified) +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
var ip_name =
|
||||
var ipName =
|
||||
'<code id="ip_' +
|
||||
data.id +
|
||||
'" title="' +
|
||||
@@ -112,7 +108,7 @@ function initTable() {
|
||||
data.ip +
|
||||
"</code>";
|
||||
if (data.name !== null && data.name.length > 0)
|
||||
ip_name +=
|
||||
ipName +=
|
||||
'<br><code id="name_' +
|
||||
data.id +
|
||||
'" title="' +
|
||||
@@ -120,7 +116,7 @@ function initTable() {
|
||||
'" class="breakall">' +
|
||||
data.name +
|
||||
"</code>";
|
||||
$("td:eq(0)", row).html(ip_name);
|
||||
$("td:eq(0)", row).html(ipName);
|
||||
|
||||
$("td:eq(1)", row).html('<input id="comment_' + data.id + '" class="form-control">');
|
||||
var commentEl = $("#comment_" + data.id, row);
|
||||
@@ -134,13 +130,13 @@ function initTable() {
|
||||
var selectEl = $("#multiselect_" + data.id, row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var data_sub = "";
|
||||
var dataSub = "";
|
||||
if (!groups[i].enabled) {
|
||||
data_sub = 'data-subtext="(disabled)"';
|
||||
dataSub = 'data-subtext="(disabled)"';
|
||||
}
|
||||
|
||||
selectEl.append(
|
||||
$("<option " + data_sub + "/>")
|
||||
$("<option " + dataSub + "/>")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name)
|
||||
);
|
||||
@@ -151,7 +147,7 @@ function initTable() {
|
||||
// Initialize bootstrap-select
|
||||
selectEl
|
||||
// fix dropdown if it would stick out right of the viewport
|
||||
.on("show.bs.select", function() {
|
||||
.on("show.bs.select", function () {
|
||||
var winWidth = $(window).width();
|
||||
var dropdownEl = $("body > .bootstrap-select.dropdown");
|
||||
if (dropdownEl.length > 0) {
|
||||
@@ -163,27 +159,22 @@ function initTable() {
|
||||
}
|
||||
}
|
||||
})
|
||||
.on("changed.bs.select", function() {
|
||||
.on("changed.bs.select", function () {
|
||||
// enable Apply button
|
||||
if ($(ApplyBtn).prop("disabled")) {
|
||||
$(ApplyBtn)
|
||||
if ($(applyBtn).prop("disabled")) {
|
||||
$(applyBtn)
|
||||
.addClass("btn-success")
|
||||
.prop("disabled", false)
|
||||
.on("click", function() {
|
||||
.on("click", function () {
|
||||
editClient.call(selectEl);
|
||||
});
|
||||
}
|
||||
})
|
||||
.on("hide.bs.select", function() {
|
||||
.on("hide.bs.select", function () {
|
||||
// Restore values if drop-down menu is closed without clicking the Apply button
|
||||
if (!$(ApplyBtn).prop("disabled")) {
|
||||
$(this)
|
||||
.val(data.groups)
|
||||
.selectpicker("refresh");
|
||||
$(ApplyBtn)
|
||||
.removeClass("btn-success")
|
||||
.prop("disabled", true)
|
||||
.off("click");
|
||||
if (!$(applyBtn).prop("disabled")) {
|
||||
$(this).val(data.groups).selectpicker("refresh");
|
||||
$(applyBtn).removeClass("btn-success").prop("disabled", true).off("click");
|
||||
}
|
||||
})
|
||||
.selectpicker()
|
||||
@@ -195,13 +186,13 @@ function initTable() {
|
||||
' class="btn btn-block btn-sm" disabled>Apply</button>'
|
||||
);
|
||||
|
||||
var ApplyBtn = "#btn_apply_" + data.id;
|
||||
var applyBtn = "#btn_apply_" + data.id;
|
||||
|
||||
var button =
|
||||
'<button class="btn btn-danger btn-xs" type="button" id="deleteClient_' +
|
||||
'<button type="button" class="btn btn-danger btn-xs" id="deleteClient_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(3)", row).html(button);
|
||||
},
|
||||
@@ -214,41 +205,42 @@ function initTable() {
|
||||
[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));
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("groups-clients-table", data);
|
||||
},
|
||||
stateLoadCallback: function() {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("groups-clients-table");
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("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;
|
||||
}
|
||||
});
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
if (input !== null) {
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
}
|
||||
|
||||
table.on("order.dt", function() {
|
||||
table.on("order.dt", function () {
|
||||
var order = table.order();
|
||||
if (order[0][0] !== 0 || order[0][1] !== "asc") {
|
||||
$("#resetButton").show();
|
||||
$("#resetButton").removeClass("hidden");
|
||||
} else {
|
||||
$("#resetButton").hide();
|
||||
$("#resetButton").addClass("hidden");
|
||||
}
|
||||
});
|
||||
$("#resetButton").on("click", function() {
|
||||
$("#resetButton").on("click", function () {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").hide();
|
||||
$("#resetButton").addClass("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -256,7 +248,7 @@ function addClient() {
|
||||
var ip = $("#select").val();
|
||||
var comment = $("#new_comment").val();
|
||||
if (ip === "custom") {
|
||||
ip = $("#ip-custom").val();
|
||||
ip = $("#ip-custom").val().trim();
|
||||
}
|
||||
|
||||
utils.disableAll();
|
||||
@@ -288,20 +280,20 @@ function addClient() {
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "add_client", ip: ip, comment: comment, token: token },
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert("success", "glyphicon glyphicon-plus", "Successfully added client", ip);
|
||||
reload_client_suggestions();
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully added client", ip);
|
||||
reloadClientSuggestions();
|
||||
table.ajax.reload(null, false);
|
||||
} else {
|
||||
utils.showAlert("error", "", "Error while adding new client", response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while adding new client", jqXHR.responseText);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -316,28 +308,27 @@ function editClient() {
|
||||
var comment = tr.find("#comment_" + id).val();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
var notDone = "editing";
|
||||
switch (elem) {
|
||||
case "multiselect_" + id:
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
notDone = "editing groups of";
|
||||
break;
|
||||
case "comment_" + id:
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
notDone = "editing comment of";
|
||||
break;
|
||||
default:
|
||||
alert("bad element or invalid data-id!");
|
||||
return;
|
||||
}
|
||||
|
||||
var ip_name = ip;
|
||||
if (name.length > 0) {
|
||||
ip_name += " (" + name + ")";
|
||||
ip += " (" + name + ")";
|
||||
}
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Editing client...", ip_name);
|
||||
utils.showAlert("info", "", "Editing client...", ip);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
@@ -349,33 +340,28 @@ function editClient() {
|
||||
token: token,
|
||||
comment: comment
|
||||
},
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-pencil",
|
||||
"Successfully " + done + " client",
|
||||
ip_name
|
||||
);
|
||||
utils.showAlert("success", "fas fa-pencil-alt", "Successfully " + done + " client", ip);
|
||||
table.ajax.reload(null, false);
|
||||
} else {
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"Error while " + not_done + " client with ID " + id,
|
||||
"Error while " + notDone + " client with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " client with ID " + id,
|
||||
"Error while " + notDone + " client with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -386,41 +372,31 @@ function deleteClient() {
|
||||
var ip = tr.find("#ip_" + id).text();
|
||||
var name = tr.find("#name_" + id).text();
|
||||
|
||||
var ip_name = ip;
|
||||
if (name.length > 0) {
|
||||
ip_name += " (" + name + ")";
|
||||
ip += " (" + name + ")";
|
||||
}
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Deleting client...", ip_name);
|
||||
utils.showAlert("info", "", "Deleting client...", ip);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_client", id: id, token: token },
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-trash",
|
||||
"Successfully deleted client ",
|
||||
ip_name
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false)
|
||||
.ajax.reload(null, false);
|
||||
reload_client_suggestions();
|
||||
utils.showAlert("success", "far fa-trash-alt", "Successfully deleted client ", ip);
|
||||
table.row(tr).remove().draw(false).ajax.reload(null, false);
|
||||
reloadClientSuggestions();
|
||||
} else {
|
||||
utils.showAlert("error", "", "Error while deleting client with ID " + id, response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while deleting client with ID " + id, jqXHR.responseText);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2020 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
|
||||
var info = null;
|
||||
function showAlert(type, icon, title, message) {
|
||||
var 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:
|
||||
}
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD HH:mm:ss z");
|
||||
}
|
||||
|
||||
function disableAll() {
|
||||
$("input").attr("disabled", true);
|
||||
$("select").attr("disabled", true);
|
||||
$("button").attr("disabled", true);
|
||||
$("textarea").attr("disabled", true);
|
||||
}
|
||||
|
||||
function enableAll() {
|
||||
$("input").attr("disabled", false);
|
||||
$("select").attr("disabled", false);
|
||||
$("button").attr("disabled", false);
|
||||
$("textarea").attr("disabled", false);
|
||||
|
||||
// Enable custom input field only if applicable
|
||||
var ip = $("#select") ? $("#select").val() : null;
|
||||
if (ip !== null && ip !== "custom") {
|
||||
ip = $("#ip-custom").attr("disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Pi-hole IPv4/CIDR validator by DL6ER, see regexr.com/50csh
|
||||
function validateIPv4CIDR(ip) {
|
||||
// One IPv4 element is 8bit: 0 - 256
|
||||
var ipv4elem = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)";
|
||||
// CIDR for IPv4 is 1 - 32 bit
|
||||
var v4cidr = "(\\/([1-9]|[1-2][0-9]|3[0-2])){0,1}";
|
||||
var ipv4validator = new RegExp(
|
||||
"^" + ipv4elem + "\\." + ipv4elem + "\\." + ipv4elem + "\\." + ipv4elem + v4cidr + "$"
|
||||
);
|
||||
return ipv4validator.test(ip);
|
||||
}
|
||||
|
||||
// Pi-hole IPv6/CIDR validator by DL6ER, see regexr.com/50csn
|
||||
function validateIPv6CIDR(ip) {
|
||||
// One IPv6 element is 16bit: 0000 - FFFF
|
||||
var ipv6elem = "[0-9A-Fa-f]{1,4}";
|
||||
// CIDR for IPv6 is 1- 128 bit
|
||||
var v6cidr = "(\\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}";
|
||||
var ipv6validator = new RegExp(
|
||||
"^(((?:" +
|
||||
ipv6elem +
|
||||
"))((?::" +
|
||||
ipv6elem +
|
||||
"))*::((?:" +
|
||||
ipv6elem +
|
||||
"))*((?::" +
|
||||
ipv6elem +
|
||||
"))*|((?:" +
|
||||
ipv6elem +
|
||||
"))((?::" +
|
||||
ipv6elem +
|
||||
")){7})" +
|
||||
v6cidr +
|
||||
"$"
|
||||
);
|
||||
return ipv6validator.test(ip);
|
||||
}
|
||||
|
||||
function bsSelect_defaults() {
|
||||
// set bootstrap-select defaults
|
||||
var pickerDEFAULTS = $.fn.selectpicker.Constructor.DEFAULTS;
|
||||
pickerDEFAULTS.noneSelectedText = "none selected";
|
||||
pickerDEFAULTS.selectedTextFormat = "count > 1";
|
||||
pickerDEFAULTS.actionsBox = true;
|
||||
pickerDEFAULTS.width = "fit";
|
||||
pickerDEFAULTS.container = "body";
|
||||
pickerDEFAULTS.dropdownAlignRight = "auto";
|
||||
pickerDEFAULTS.selectAllText = "All";
|
||||
pickerDEFAULTS.deselectAllText = "None";
|
||||
pickerDEFAULTS.countSelectedText = function(num, total) {
|
||||
if (num === total) {
|
||||
return "All selected (" + num + ")";
|
||||
}
|
||||
|
||||
return num + " selected";
|
||||
};
|
||||
}
|
||||
|
||||
window.utils = (function() {
|
||||
return {
|
||||
showAlert: showAlert,
|
||||
datetime: datetime,
|
||||
disableAll: disableAll,
|
||||
enableAll: enableAll,
|
||||
validateIPv4CIDR: validateIPv4CIDR,
|
||||
validateIPv6CIDR: validateIPv6CIDR,
|
||||
bsSelect_defaults: bsSelect_defaults
|
||||
};
|
||||
})();
|
||||
@@ -9,15 +9,15 @@
|
||||
|
||||
var table;
|
||||
var groups = [];
|
||||
var token = $("#token").html();
|
||||
var token = $("#token").text();
|
||||
var GETDict = {};
|
||||
var showtype = "all";
|
||||
|
||||
function get_groups() {
|
||||
function getGroups() {
|
||||
$.post(
|
||||
"scripts/pi-hole/php/groups.php",
|
||||
{ action: "get_groups", token: token },
|
||||
function(data) {
|
||||
function (data) {
|
||||
groups = data.data;
|
||||
initTable();
|
||||
},
|
||||
@@ -25,11 +25,11 @@ function get_groups() {
|
||||
);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
window.location.search
|
||||
.substr(1)
|
||||
.split("&")
|
||||
.forEach(function(item) {
|
||||
.forEach(function (item) {
|
||||
GETDict[item.split("=")[0]] = item.split("=")[1];
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
// sync description fields, reset inactive inputs on tab change
|
||||
$('a[data-toggle="tab"]').on("shown.bs.tab", function() {
|
||||
$('a[data-toggle="tab"]').on("shown.bs.tab", function () {
|
||||
var tabHref = $(this).attr("href");
|
||||
var val;
|
||||
if (tabHref === "#tab_domain") {
|
||||
@@ -55,8 +55,8 @@ $(document).ready(function() {
|
||||
|
||||
$("#add2black, #add2white").on("click", addDomain);
|
||||
|
||||
utils.bsSelect_defaults();
|
||||
get_groups();
|
||||
utils.setBsSelectDefaults();
|
||||
getGroups();
|
||||
});
|
||||
|
||||
function initTable() {
|
||||
@@ -76,12 +76,12 @@ function initTable() {
|
||||
{ data: "groups", searchable: false },
|
||||
{ data: null, width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function() {
|
||||
drawCallback: function () {
|
||||
$('button[id^="deleteDomain_"]').on("click", deleteDomain);
|
||||
// Remove visible dropdown to prevent orphaning
|
||||
$("body > .bootstrap-select.dropdown").remove();
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
rowCallback: function (row, data) {
|
||||
$(row).attr("data-id", data.id);
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
@@ -100,9 +100,9 @@ function initTable() {
|
||||
"</code>"
|
||||
);
|
||||
|
||||
var whitelist_options = "";
|
||||
var whitelistOptions = "";
|
||||
if (showtype === "all" || showtype === "white") {
|
||||
whitelist_options =
|
||||
whitelistOptions =
|
||||
'<option value="0"' +
|
||||
(data.type === 0 ? " selected" : "") +
|
||||
">Exact whitelist</option>" +
|
||||
@@ -111,9 +111,9 @@ function initTable() {
|
||||
">Regex whitelist</option>";
|
||||
}
|
||||
|
||||
var blacklist_options = "";
|
||||
var blacklistOptions = "";
|
||||
if (showtype === "all" || showtype === "black") {
|
||||
blacklist_options =
|
||||
blacklistOptions =
|
||||
'<option value="1"' +
|
||||
(data.type === 1 ? " selected " : " ") +
|
||||
">Exact blacklist</option>" +
|
||||
@@ -126,8 +126,8 @@ function initTable() {
|
||||
'<select id="type_' +
|
||||
data.id +
|
||||
'" class="form-control">' +
|
||||
whitelist_options +
|
||||
blacklist_options +
|
||||
whitelistOptions +
|
||||
blacklistOptions +
|
||||
"</select>"
|
||||
);
|
||||
var typeEl = $("#type_" + data.id, row);
|
||||
@@ -161,13 +161,13 @@ function initTable() {
|
||||
var selectEl = $("#multiselect_" + data.id, row);
|
||||
// Add all known groups
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
var data_sub = "";
|
||||
var dataSub = "";
|
||||
if (!groups[i].enabled) {
|
||||
data_sub = 'data-subtext="(disabled)"';
|
||||
dataSub = 'data-subtext="(disabled)"';
|
||||
}
|
||||
|
||||
selectEl.append(
|
||||
$("<option " + data_sub + "/>")
|
||||
$("<option " + dataSub + "/>")
|
||||
.val(groups[i].id)
|
||||
.text(groups[i].name)
|
||||
);
|
||||
@@ -178,7 +178,7 @@ function initTable() {
|
||||
// Initialize bootstrap-select
|
||||
selectEl
|
||||
// fix dropdown if it would stick out right of the viewport
|
||||
.on("show.bs.select", function() {
|
||||
.on("show.bs.select", function () {
|
||||
var winWidth = $(window).width();
|
||||
var dropdownEl = $("body > .bootstrap-select.dropdown");
|
||||
if (dropdownEl.length > 0) {
|
||||
@@ -190,27 +190,22 @@ function initTable() {
|
||||
}
|
||||
}
|
||||
})
|
||||
.on("changed.bs.select", function() {
|
||||
.on("changed.bs.select", function () {
|
||||
// enable Apply button
|
||||
if ($(ApplyBtn).prop("disabled")) {
|
||||
$(ApplyBtn)
|
||||
if ($(applyBtn).prop("disabled")) {
|
||||
$(applyBtn)
|
||||
.addClass("btn-success")
|
||||
.prop("disabled", false)
|
||||
.on("click", function() {
|
||||
.on("click", function () {
|
||||
editDomain.call(selectEl);
|
||||
});
|
||||
}
|
||||
})
|
||||
.on("hide.bs.select", function() {
|
||||
.on("hide.bs.select", function () {
|
||||
// Restore values if drop-down menu is closed without clicking the Apply button
|
||||
if (!$(ApplyBtn).prop("disabled")) {
|
||||
$(this)
|
||||
.val(data.groups)
|
||||
.selectpicker("refresh");
|
||||
$(ApplyBtn)
|
||||
.removeClass("btn-success")
|
||||
.prop("disabled", true)
|
||||
.off("click");
|
||||
if (!$(applyBtn).prop("disabled")) {
|
||||
$(this).val(data.groups).selectpicker("refresh");
|
||||
$(applyBtn).removeClass("btn-success").prop("disabled", true).off("click");
|
||||
}
|
||||
})
|
||||
.selectpicker()
|
||||
@@ -223,20 +218,18 @@ function initTable() {
|
||||
);
|
||||
}
|
||||
|
||||
var ApplyBtn = "#btn_apply_" + data.id;
|
||||
var applyBtn = "#btn_apply_" + data.id;
|
||||
|
||||
// Highlight row (if url parameter "domainid=" is used)
|
||||
if ("domainid" in GETDict && data.id === parseInt(GETDict.domainid)) {
|
||||
$(row)
|
||||
.find("td")
|
||||
.addClass("highlight");
|
||||
$(row).find("td").addClass("highlight");
|
||||
}
|
||||
|
||||
var button =
|
||||
'<button class="btn btn-danger btn-xs" type="button" id="deleteDomain_' +
|
||||
'<button type="button" class="btn btn-danger btn-xs" id="deleteDomain_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>";
|
||||
if (table.column(5).visible()) {
|
||||
$("td:eq(5)", row).html(button);
|
||||
@@ -253,23 +246,16 @@ function initTable() {
|
||||
[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));
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("groups-domains-table", data);
|
||||
},
|
||||
stateLoadCallback: function() {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("groups-domains-table");
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("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;
|
||||
// Show group assignment column only on full page
|
||||
@@ -277,12 +263,9 @@ function initTable() {
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
},
|
||||
initComplete: function() {
|
||||
initComplete: function () {
|
||||
if ("domainid" in GETDict) {
|
||||
var pos = table
|
||||
.column(0, { order: "current" })
|
||||
.data()
|
||||
.indexOf(parseInt(GETDict.domainid));
|
||||
var pos = table.column(0, { order: "current" }).data().indexOf(parseInt(GETDict.domainid));
|
||||
if (pos >= 0) {
|
||||
var page = Math.floor(pos / table.page.info().length);
|
||||
table.page(page).draw(false);
|
||||
@@ -290,18 +273,26 @@ function initTable() {
|
||||
}
|
||||
}
|
||||
});
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
if (input !== null) {
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
}
|
||||
|
||||
table.on("order.dt", function() {
|
||||
table.on("order.dt", function () {
|
||||
var order = table.order();
|
||||
if (order[0][0] !== 0 || order[0][1] !== "asc") {
|
||||
$("#resetButton").show();
|
||||
$("#resetButton").removeClass("hidden");
|
||||
} else {
|
||||
$("#resetButton").hide();
|
||||
$("#resetButton").addClass("hidden");
|
||||
}
|
||||
});
|
||||
$("#resetButton").on("click", function() {
|
||||
$("#resetButton").on("click", function () {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").hide();
|
||||
$("#resetButton").addClass("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -309,17 +300,17 @@ function addDomain() {
|
||||
var action = this.id;
|
||||
var tabHref = $('a[data-toggle="tab"][aria-expanded="true"]').attr("href");
|
||||
var wildcardEl = $("#wildcard_checkbox");
|
||||
var wildcard_checked = wildcardEl.prop("checked");
|
||||
var wildcardChecked = wildcardEl.prop("checked");
|
||||
var type;
|
||||
|
||||
// current tab's inputs
|
||||
var domain_regex, domainEl, commentEl;
|
||||
var domainRegex, domainEl, commentEl;
|
||||
if (tabHref === "#tab_domain") {
|
||||
domain_regex = "domain";
|
||||
domainRegex = "domain";
|
||||
domainEl = $("#new_domain");
|
||||
commentEl = $("#new_domain_comment");
|
||||
} else if (tabHref === "#tab_regex") {
|
||||
domain_regex = "regex";
|
||||
domainRegex = "regex";
|
||||
domainEl = $("#new_regex");
|
||||
commentEl = $("#new_regex_comment");
|
||||
}
|
||||
@@ -328,31 +319,31 @@ function addDomain() {
|
||||
var comment = commentEl.val();
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Adding " + domain_regex + "...", domain);
|
||||
utils.showAlert("info", "", "Adding " + domainRegex + "...", domain);
|
||||
|
||||
if (domain.length > 0) {
|
||||
// strip "*." if specified by user in wildcard mode
|
||||
if (domain_regex === "domain" && wildcard_checked && domain.startsWith("*.")) {
|
||||
if (domainRegex === "domain" && wildcardChecked && domain.startsWith("*.")) {
|
||||
domain = domain.substr(2);
|
||||
}
|
||||
|
||||
// determine list type
|
||||
if (domain_regex === "domain" && action === "add2black" && wildcard_checked) {
|
||||
if (domainRegex === "domain" && action === "add2black" && wildcardChecked) {
|
||||
type = "3W";
|
||||
} else if (domain_regex === "domain" && action === "add2black" && !wildcard_checked) {
|
||||
} else if (domainRegex === "domain" && action === "add2black" && !wildcardChecked) {
|
||||
type = "1";
|
||||
} else if (domain_regex === "domain" && action === "add2white" && wildcard_checked) {
|
||||
} else if (domainRegex === "domain" && action === "add2white" && wildcardChecked) {
|
||||
type = "2W";
|
||||
} else if (domain_regex === "domain" && action === "add2white" && !wildcard_checked) {
|
||||
} else if (domainRegex === "domain" && action === "add2white" && !wildcardChecked) {
|
||||
type = "0";
|
||||
} else if (domain_regex === "regex" && action === "add2black") {
|
||||
} else if (domainRegex === "regex" && action === "add2black") {
|
||||
type = "3";
|
||||
} else if (domain_regex === "regex" && action === "add2white") {
|
||||
} else if (domainRegex === "regex" && action === "add2white") {
|
||||
type = "2";
|
||||
}
|
||||
} else {
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Please specify a " + domain_regex);
|
||||
utils.showAlert("warning", "", "Warning", "Please specify a " + domainRegex);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -367,27 +358,22 @@ function addDomain() {
|
||||
comment: comment,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-plus",
|
||||
"Successfully added " + domain_regex,
|
||||
domain
|
||||
);
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully added " + domainRegex, domain);
|
||||
domainEl.val("");
|
||||
commentEl.val("");
|
||||
wildcardEl.prop("checked", false);
|
||||
table.ajax.reload(null, false);
|
||||
} else {
|
||||
utils.showAlert("error", "", "Error while adding new " + domain_regex, response.message);
|
||||
utils.showAlert("error", "", "Error while adding new " + domainRegex, response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while adding new " + domain_regex, jqXHR.responseText);
|
||||
console.log(exception);
|
||||
utils.showAlert("error", "", "Error while adding new " + domainRegex, jqXHR.responseText);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -406,41 +392,41 @@ function editDomain() {
|
||||
var rowData = table.row(tr).data();
|
||||
var groups = table.column(5).visible() ? tr.find("#multiselect_" + id).val() : rowData.groups;
|
||||
|
||||
var domain_regex;
|
||||
var domainRegex;
|
||||
if (type === "0" || type === "1") {
|
||||
domain_regex = "domain";
|
||||
domainRegex = "domain";
|
||||
} else if (type === "2" || type === "3") {
|
||||
domain_regex = "regex";
|
||||
domainRegex = "regex";
|
||||
}
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
var notDone = "editing";
|
||||
switch (elem) {
|
||||
case "status_" + id:
|
||||
if (status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
notDone = "disabling";
|
||||
} else if (status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
notDone = "enabling";
|
||||
}
|
||||
|
||||
break;
|
||||
case "name_" + id:
|
||||
done = "edited name of";
|
||||
not_done = "editing name of";
|
||||
notDone = "editing name of";
|
||||
break;
|
||||
case "comment_" + id:
|
||||
done = "edited comment of";
|
||||
not_done = "editing comment of";
|
||||
notDone = "editing comment of";
|
||||
break;
|
||||
case "type_" + id:
|
||||
done = "edited type of";
|
||||
not_done = "editing type of";
|
||||
notDone = "editing type of";
|
||||
break;
|
||||
case "multiselect_" + id:
|
||||
done = "edited groups of";
|
||||
not_done = "editing groups of";
|
||||
notDone = "editing groups of";
|
||||
break;
|
||||
default:
|
||||
alert("bad element or invalid data-id!");
|
||||
@@ -448,7 +434,7 @@ function editDomain() {
|
||||
}
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Editing " + domain_regex + "...", name);
|
||||
utils.showAlert("info", "", "Editing " + domainRegex + "...", name);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
@@ -462,13 +448,13 @@ function editDomain() {
|
||||
groups: groups,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-pencil",
|
||||
"Successfully " + done + " " + domain_regex,
|
||||
"fas fa-pencil-alt",
|
||||
"Successfully " + done + " " + domainRegex,
|
||||
domain
|
||||
);
|
||||
table.ajax.reload(null, false);
|
||||
@@ -476,19 +462,19 @@ function editDomain() {
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " " + domain_regex + " with ID " + id,
|
||||
"Error while " + notDone + " " + domainRegex + " with ID " + id,
|
||||
response.message
|
||||
);
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " " + domain_regex + " with ID " + id,
|
||||
"Error while " + notDone + " " + domainRegex + " with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -499,52 +485,48 @@ function deleteDomain() {
|
||||
var domain = tr.find("#domain_" + id).text();
|
||||
var type = tr.find("#type_" + id).val();
|
||||
|
||||
var domain_regex;
|
||||
var domainRegex;
|
||||
if (type === "0" || type === "1") {
|
||||
domain_regex = "domain";
|
||||
domainRegex = "domain";
|
||||
} else if (type === "2" || type === "3") {
|
||||
domain_regex = "regex";
|
||||
domainRegex = "regex";
|
||||
}
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Deleting " + domain_regex + "...", domain);
|
||||
utils.showAlert("info", "", "Deleting " + domainRegex + "...", domain);
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_domain", id: id, token: token },
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-trash",
|
||||
"Successfully deleted " + domain_regex,
|
||||
"far fa-trash-alt",
|
||||
"Successfully deleted " + domainRegex,
|
||||
domain
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false)
|
||||
.ajax.reload(null, false);
|
||||
table.row(tr).remove().draw(false).ajax.reload(null, false);
|
||||
} else {
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting " + domain_regex + " with ID " + id,
|
||||
"Error while deleting " + domainRegex + " with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while deleting " + domain_regex + " with ID " + id,
|
||||
"Error while deleting " + domainRegex + " with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
/* global utils:false */
|
||||
|
||||
var table;
|
||||
var token = $("#token").html();
|
||||
var token = $("#token").text();
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
$("#btnAdd").on("click", addGroup);
|
||||
|
||||
table = $("#groupsTable").DataTable({
|
||||
@@ -27,10 +27,10 @@ $(document).ready(function() {
|
||||
{ data: "description" },
|
||||
{ data: null, width: "60px", orderable: false }
|
||||
],
|
||||
drawCallback: function() {
|
||||
drawCallback: function () {
|
||||
$('button[id^="deleteGroup_"]').on("click", deleteGroup);
|
||||
},
|
||||
rowCallback: function(row, data) {
|
||||
rowCallback: function (row, data) {
|
||||
$(row).attr("data-id", data.id);
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
@@ -69,10 +69,10 @@ $(document).ready(function() {
|
||||
$("td:eq(3)", row).empty();
|
||||
if (data.id !== 0) {
|
||||
var button =
|
||||
'<button class="btn btn-danger btn-xs" type="button" id="deleteGroup_' +
|
||||
'<button type="button" class="btn btn-danger btn-xs" id="deleteGroup_' +
|
||||
data.id +
|
||||
'">' +
|
||||
'<span class="glyphicon glyphicon-trash"></span>' +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>";
|
||||
$("td:eq(3)", row).html(button);
|
||||
}
|
||||
@@ -86,23 +86,16 @@ $(document).ready(function() {
|
||||
[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));
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("groups-table", data);
|
||||
},
|
||||
stateLoadCallback: function() {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("groups-table");
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("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
|
||||
@@ -110,17 +103,26 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
|
||||
table.on("order.dt", function() {
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
if (input !== null) {
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
}
|
||||
|
||||
table.on("order.dt", function () {
|
||||
var order = table.order();
|
||||
if (order[0][0] !== 0 || order[0][1] !== "asc") {
|
||||
$("#resetButton").show();
|
||||
$("#resetButton").removeClass("hidden");
|
||||
} else {
|
||||
$("#resetButton").hide();
|
||||
$("#resetButton").addClass("hidden");
|
||||
}
|
||||
});
|
||||
$("#resetButton").on("click", function() {
|
||||
$("#resetButton").on("click", function () {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").hide();
|
||||
$("#resetButton").addClass("hidden");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -141,10 +143,10 @@ function addGroup() {
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "add_group", name: name, desc: desc, token: token },
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert("success", "glyphicon glyphicon-plus", "Successfully added group", name);
|
||||
utils.showAlert("success", "fas fa-plus", "Successfully added group", name);
|
||||
$("#new_name").val("");
|
||||
$("#new_desc").val("");
|
||||
table.ajax.reload();
|
||||
@@ -152,10 +154,10 @@ function addGroup() {
|
||||
utils.showAlert("error", "", "Error while adding new group", response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while adding new group", jqXHR.responseText);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -169,25 +171,25 @@ function editGroup() {
|
||||
var desc = tr.find("#desc_" + id).val();
|
||||
|
||||
var done = "edited";
|
||||
var not_done = "editing";
|
||||
var notDone = "editing";
|
||||
switch (elem) {
|
||||
case "status_" + id:
|
||||
if (status === 0) {
|
||||
done = "disabled";
|
||||
not_done = "disabling";
|
||||
notDone = "disabling";
|
||||
} else if (status === 1) {
|
||||
done = "enabled";
|
||||
not_done = "enabling";
|
||||
notDone = "enabling";
|
||||
}
|
||||
|
||||
break;
|
||||
case "name_" + id:
|
||||
done = "edited name of";
|
||||
not_done = "editing name of";
|
||||
notDone = "editing name of";
|
||||
break;
|
||||
case "desc_" + id:
|
||||
done = "edited description of";
|
||||
not_done = "editing description of";
|
||||
notDone = "editing description of";
|
||||
break;
|
||||
default:
|
||||
alert("bad element or invalid data-id!");
|
||||
@@ -208,33 +210,28 @@ function editGroup() {
|
||||
status: status,
|
||||
token: token
|
||||
},
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-pencil",
|
||||
"Successfully " + done + " group",
|
||||
name
|
||||
);
|
||||
utils.showAlert("success", "fas fa-pencil-alt", "Successfully " + done + " group", name);
|
||||
} else {
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " group with ID " + id,
|
||||
"Error while " + notDone + " group with ID " + id,
|
||||
response.message
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert(
|
||||
"error",
|
||||
"",
|
||||
"Error while " + not_done + " group with ID " + id,
|
||||
"Error while " + notDone + " group with ID " + id,
|
||||
jqXHR.responseText
|
||||
);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -251,27 +248,19 @@ function deleteGroup() {
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete_group", id: id, token: token },
|
||||
success: function(response) {
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.success) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"glyphicon glyphicon-trash",
|
||||
"Successfully deleted group ",
|
||||
name
|
||||
);
|
||||
table
|
||||
.row(tr)
|
||||
.remove()
|
||||
.draw(false);
|
||||
utils.showAlert("success", "far fa-trash-alt", "Successfully deleted group ", name);
|
||||
table.row(tr).remove().draw(false);
|
||||
} else {
|
||||
utils.showAlert("error", "", "Error while deleting group with ID " + id, response.message);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "", "Error while deleting group with ID " + id, jqXHR.responseText);
|
||||
console.log(exception);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,10 +5,10 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
// This code has been taken from
|
||||
// This code has been adapted from
|
||||
// https://datatables.net/plug-ins/sorting/ip-address
|
||||
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
||||
"ip-address-pre": function(a) {
|
||||
$.extend($.fn.dataTableExt.oSort, {
|
||||
"ip-address-pre": function (a) {
|
||||
if (!a) {
|
||||
return 0;
|
||||
}
|
||||
@@ -23,9 +23,16 @@ jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
||||
var m = a.split("."),
|
||||
n = a.split(":"),
|
||||
x = "",
|
||||
xa = "";
|
||||
xa = "",
|
||||
cidr = [];
|
||||
if (m.length === 4) {
|
||||
// IPV4
|
||||
// IPV4 (possibly with CIDR)
|
||||
cidr = m[3].split("/");
|
||||
if (cidr.length === 2) {
|
||||
m.pop();
|
||||
m = m.concat(cidr);
|
||||
}
|
||||
|
||||
for (i = 0; i < m.length; i++) {
|
||||
item = m[i];
|
||||
|
||||
@@ -38,7 +45,7 @@ jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
||||
}
|
||||
}
|
||||
} else if (n.length > 0) {
|
||||
// IPV6
|
||||
// IPV6 (possibly with CIDR)
|
||||
var count = 0;
|
||||
for (i = 0; i < n.length; i++) {
|
||||
item = n[i];
|
||||
@@ -79,16 +86,29 @@ jQuery.extend(jQuery.fn.dataTableExt.oSort, {
|
||||
x += item;
|
||||
}
|
||||
}
|
||||
|
||||
cidr = x.split("/");
|
||||
x = cidr[0];
|
||||
if (cidr.length === 2) {
|
||||
item = cidr[1];
|
||||
if (item.length === 1) {
|
||||
x += "00" + item;
|
||||
} else if (item.length === 2) {
|
||||
x += "0" + item;
|
||||
} else {
|
||||
x += item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return x;
|
||||
},
|
||||
|
||||
"ip-address-asc": function(a, b) {
|
||||
"ip-address-asc": function (a, b) {
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
},
|
||||
|
||||
"ip-address-desc": function(a, b) {
|
||||
"ip-address-desc": function (a, b) {
|
||||
return a < b ? 1 : a > b ? -1 : 0;
|
||||
}
|
||||
});
|
||||
|
||||
119
scripts/pi-hole/js/messages.js
Normal file
119
scripts/pi-hole/js/messages.js
Normal file
@@ -0,0 +1,119 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global utils:false */
|
||||
|
||||
var token = $("#token").text();
|
||||
|
||||
function renderTimestamp(data, type) {
|
||||
// Display and search content
|
||||
if (type === "display" || type === "filter") {
|
||||
return utils.datetime(data);
|
||||
}
|
||||
|
||||
// Sorting content
|
||||
return data;
|
||||
}
|
||||
|
||||
function multline(input) {
|
||||
return input.split(",").join("\n");
|
||||
}
|
||||
|
||||
function renderMessage(data, type, row) {
|
||||
// Display and search content
|
||||
switch (row.type) {
|
||||
case "REGEX":
|
||||
return (
|
||||
'Encountered an error when processing <a href="groups-domains.php?domainid=' +
|
||||
row.blob3 +
|
||||
'">' +
|
||||
row.blob1 +
|
||||
" regex filter with ID " +
|
||||
row.blob3 +
|
||||
"</a>:<pre>" +
|
||||
row.blob2 +
|
||||
"</pre>Error message: <pre>" +
|
||||
row.message +
|
||||
"</pre>"
|
||||
);
|
||||
|
||||
case "SUBNET":
|
||||
return (
|
||||
"Client <code>" +
|
||||
row.message +
|
||||
"</code> is managed by " +
|
||||
row.blob1 +
|
||||
" groups (database IDs [" +
|
||||
row.blob3 +
|
||||
"]):<pre>" +
|
||||
multline(row.blob2) +
|
||||
"</pre>" +
|
||||
"FTL chose the most recent entry <pre>" +
|
||||
row.blob4 +
|
||||
"</pre> to get the group configuration for this client."
|
||||
);
|
||||
|
||||
default:
|
||||
return "Unknown message type<pre>" + JSON.stringify(row) + "</pre>";
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$("#messagesTable").DataTable({
|
||||
ajax: {
|
||||
url: "api_db.php?messages",
|
||||
data: { token: token },
|
||||
type: "POST",
|
||||
dataSrc: "messages"
|
||||
},
|
||||
order: [[0, "asc"]],
|
||||
columns: [
|
||||
{ data: "id", visible: false },
|
||||
{ data: "timestamp", width: "8%", render: renderTimestamp },
|
||||
{ data: "type", width: "8%" },
|
||||
{ data: "message", orderable: false, render: renderMessage },
|
||||
{ data: "blob1", visible: false },
|
||||
{ data: "blob2", visible: false },
|
||||
{ data: "blob3", visible: false },
|
||||
{ data: "blob4", visible: false },
|
||||
{ data: "blob5", visible: false }
|
||||
],
|
||||
dom:
|
||||
"<'row'<'col-sm-4'l><'col-sm-8'f>>" +
|
||||
"<'row'<'col-sm-12'<'table-responsive'tr>>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
language: {
|
||||
emptyTable: "No issues found."
|
||||
},
|
||||
stateSave: true,
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("messages-table", data);
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("messages-table");
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Reset visibility of ID and blob columns
|
||||
var hiddenCols = [0, 4, 5, 6, 7, 8];
|
||||
for (var key in hiddenCols) {
|
||||
if (Object.prototype.hasOwnProperty.call(hiddenCols, key)) {
|
||||
data.columns[hiddenCols[key]].visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -5,19 +5,21 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
/* global moment:false, utils:false */
|
||||
|
||||
var tableApi;
|
||||
|
||||
var APIstring = "api_db.php?network";
|
||||
var API_STRING = "api_db.php?network";
|
||||
|
||||
// How many IPs do we show at most per device?
|
||||
var MAXIPDISPLAY = 3;
|
||||
|
||||
var DAY_IN_SECONDS = 24 * 60 * 60;
|
||||
|
||||
function handleAjaxError(xhr, textStatus) {
|
||||
if (textStatus === "timeout") {
|
||||
alert("The server took too long to send the data.");
|
||||
} else if (xhr.responseText.indexOf("Connection refused") >= 0) {
|
||||
} else if (xhr.responseText.indexOf("Connection refused") !== -1) {
|
||||
alert("An error occured while loading the data: Connection refused. Is FTL running?");
|
||||
} else {
|
||||
alert("An unknown error occured while loading the data.\n" + xhr.responseText);
|
||||
@@ -30,7 +32,7 @@ function handleAjaxError(xhr, textStatus) {
|
||||
|
||||
function getTimestamp() {
|
||||
if (!Date.now) {
|
||||
Date.now = function() {
|
||||
Date.now = function () {
|
||||
return new Date().getTime();
|
||||
};
|
||||
}
|
||||
@@ -49,43 +51,54 @@ function rgbToHex(values) {
|
||||
|
||||
function mixColors(ratio, rgb1, rgb2) {
|
||||
return [
|
||||
(1.0 - ratio) * rgb1[0] + ratio * rgb2[0],
|
||||
(1.0 - ratio) * rgb1[1] + ratio * rgb2[1],
|
||||
(1.0 - ratio) * rgb1[2] + ratio * rgb2[2]
|
||||
(1 - ratio) * rgb1[0] + ratio * rgb2[0],
|
||||
(1 - ratio) * rgb1[1] + ratio * rgb2[1],
|
||||
(1 - ratio) * rgb1[2] + ratio * rgb2[2]
|
||||
];
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
function parseColor(input) {
|
||||
var match = input.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);
|
||||
|
||||
if (match) {
|
||||
return [match[1], match[2], match[3]];
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
tableApi = $("#network-entries").DataTable({
|
||||
rowCallback: function(row, data) {
|
||||
var color,
|
||||
mark,
|
||||
lastQuery = parseInt(data.lastQuery);
|
||||
rowCallback: function (row, data) {
|
||||
var color;
|
||||
var iconClasses;
|
||||
var lastQuery = parseInt(data.lastQuery);
|
||||
var diff = getTimestamp() - lastQuery;
|
||||
var networkRecent = $(".network-recent").css("background-color");
|
||||
var networkOld = $(".network-old").css("background-color");
|
||||
var networkOlder = $(".network-older").css("background-color");
|
||||
var networkNever = $(".network-never").css("background-color");
|
||||
|
||||
if (lastQuery > 0) {
|
||||
var diff = getTimestamp() - lastQuery;
|
||||
if (diff <= 86400) {
|
||||
// Last query came in within the last 24 hours (24*60*60 = 86400)
|
||||
if (diff <= DAY_IN_SECONDS) {
|
||||
// Last query came in within the last 24 hours
|
||||
// Color: light-green to light-yellow
|
||||
var ratio = Number(diff) / 86400;
|
||||
var lightgreen = [0xe7, 0xff, 0xde];
|
||||
var lightyellow = [0xff, 0xff, 0xdf];
|
||||
color = rgbToHex(mixColors(ratio, lightgreen, lightyellow));
|
||||
mark = "✔";
|
||||
var ratio = Number(diff) / DAY_IN_SECONDS;
|
||||
color = rgbToHex(mixColors(ratio, parseColor(networkRecent), parseColor(networkOld)));
|
||||
iconClasses = "fas fa-check";
|
||||
} else {
|
||||
// Last query was longer than 24 hours ago
|
||||
// Color: light-orange
|
||||
color = "#ffedd9";
|
||||
mark = "<strong>?</strong>";
|
||||
color = networkOlder;
|
||||
iconClasses = "fas fa-question";
|
||||
}
|
||||
} else {
|
||||
// This client has never sent a query to Pi-hole, color light-red
|
||||
color = "#ffbfaa";
|
||||
mark = "✘";
|
||||
color = networkNever;
|
||||
iconClasses = "fas fa-check";
|
||||
}
|
||||
|
||||
// Set determined background color
|
||||
$(row).css("background-color", color);
|
||||
$("td:eq(7)", row).html(mark);
|
||||
$("td:eq(7)", row).html('<i class="' + iconClasses + '"></i>');
|
||||
|
||||
// Insert "Never" into Last Query field when we have
|
||||
// never seen a query from this device
|
||||
@@ -115,7 +128,7 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
$("td:eq(0)", row).html(ips.join("<br>"));
|
||||
$("td:eq(0)", row).hover(function() {
|
||||
$("td:eq(0)", row).hover(function () {
|
||||
this.title = data.ip.join("\n");
|
||||
});
|
||||
|
||||
@@ -134,7 +147,7 @@ $(document).ready(function() {
|
||||
"<'row'<'col-sm-4'l><'col-sm-8'p>>" +
|
||||
"<'row'<'col-sm-12'<'table-responsive'tr>>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
ajax: { url: APIstring, error: handleAjaxError, dataSrc: "network" },
|
||||
ajax: { url: API_STRING, error: handleAjaxError, dataSrc: "network" },
|
||||
autoWidth: false,
|
||||
processing: true,
|
||||
order: [[5, "desc"]],
|
||||
@@ -146,7 +159,7 @@ $(document).ready(function() {
|
||||
{
|
||||
data: "firstSeen",
|
||||
width: "8%",
|
||||
render: function(data, type) {
|
||||
render: function (data, type) {
|
||||
if (type === "display") {
|
||||
return moment.unix(data).format("Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z");
|
||||
}
|
||||
@@ -157,7 +170,7 @@ $(document).ready(function() {
|
||||
{
|
||||
data: "lastQuery",
|
||||
width: "8%",
|
||||
render: function(data, type) {
|
||||
render: function (data, type) {
|
||||
if (type === "display") {
|
||||
return moment.unix(data).format("Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z");
|
||||
}
|
||||
@@ -173,25 +186,11 @@ $(document).ready(function() {
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function(settings, data) {
|
||||
// Store current state in client's local storage area
|
||||
localStorage.setItem("network_table", JSON.stringify(data));
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("network_table", data);
|
||||
},
|
||||
stateLoadCallback: function() {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("network_table");
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
data = JSON.parse(data);
|
||||
// Always start on the first page
|
||||
data.start = 0;
|
||||
// Always start with empty search field
|
||||
data.search.search = "";
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
stateLoadCallback: function () {
|
||||
return utils.stateLoadCallback("network_table");
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
@@ -201,4 +200,10 @@ $(document).ready(function() {
|
||||
}
|
||||
]
|
||||
});
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
/* global moment:false, utils:false */
|
||||
|
||||
var tableApi;
|
||||
var tableFilters = [];
|
||||
@@ -53,23 +53,25 @@ function add(domain, list) {
|
||||
alertModal.modal("show");
|
||||
|
||||
// add Domain to List after Modal has faded in
|
||||
alertModal.one("shown.bs.modal", function() {
|
||||
alertModal.one("shown.bs.modal", function () {
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/add.php",
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
data: { domain: domain, list: list, token: token },
|
||||
success: function(response) {
|
||||
data: {
|
||||
domain: domain,
|
||||
list: list,
|
||||
token: token,
|
||||
action: "add_domain",
|
||||
comment: "Added from Query Log"
|
||||
},
|
||||
success: function (response) {
|
||||
alProcessing.hide();
|
||||
if (
|
||||
response.indexOf("not a valid argument") >= 0 ||
|
||||
response.indexOf("is not a valid domain") >= 0 ||
|
||||
response.indexOf("Wrong token") >= 0
|
||||
) {
|
||||
if (!response.success) {
|
||||
// Failure
|
||||
alNetworkErr.hide();
|
||||
alCustomErr.html(response.replace("[✗]", ""));
|
||||
alCustomErr.html(response.message);
|
||||
alFailure.fadeIn(1000);
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 3000);
|
||||
} else {
|
||||
@@ -77,17 +79,17 @@ function add(domain, list) {
|
||||
alSuccess.children(alDomain).html(domain);
|
||||
alSuccess.children(alList).html(listtype);
|
||||
alSuccess.fadeIn(1000);
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
error: function () {
|
||||
// Network Error
|
||||
alProcessing.hide();
|
||||
alNetworkErr.show();
|
||||
alFailure.fadeIn(1000);
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 3000);
|
||||
}
|
||||
@@ -95,16 +97,10 @@ function add(domain, list) {
|
||||
});
|
||||
|
||||
// Reset Modal after it has faded out
|
||||
alertModal.one("hidden.bs.modal", function() {
|
||||
alertModal.one("hidden.bs.modal", function () {
|
||||
alProcessing.show();
|
||||
alSuccess.add(alFailure).hide();
|
||||
alProcessing
|
||||
.add(alSuccess)
|
||||
.children(alDomain)
|
||||
.html("")
|
||||
.end()
|
||||
.children(alList)
|
||||
.html("");
|
||||
alProcessing.add(alSuccess).children(alDomain).html("").end().children(alList).html("");
|
||||
alCustomErr.html("");
|
||||
});
|
||||
}
|
||||
@@ -112,7 +108,7 @@ function add(domain, list) {
|
||||
function handleAjaxError(xhr, textStatus) {
|
||||
if (textStatus === "timeout") {
|
||||
alert("The server took too long to send the data.");
|
||||
} else if (xhr.responseText.indexOf("Connection refused") >= 0) {
|
||||
} else if (xhr.responseText.indexOf("Connection refused") !== -1) {
|
||||
alert("An error occured while loading the data: Connection refused. Is FTL running?");
|
||||
} else {
|
||||
alert("An unknown error occured while loading the data.\n" + xhr.responseText);
|
||||
@@ -123,13 +119,13 @@ function handleAjaxError(xhr, textStatus) {
|
||||
tableApi.draw();
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
// Do we want to filter queries?
|
||||
var GETDict = {};
|
||||
window.location.search
|
||||
.substr(1)
|
||||
.split("&")
|
||||
.forEach(function(item) {
|
||||
.forEach(function (item) {
|
||||
GETDict[item.split("=")[0]] = item.split("=")[1];
|
||||
});
|
||||
|
||||
@@ -153,28 +149,28 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
tableApi = $("#all-queries").DataTable({
|
||||
rowCallback: function(row, data) {
|
||||
rowCallback: function (row, data) {
|
||||
// DNSSEC status
|
||||
var dnssec_status;
|
||||
var dnssecStatus;
|
||||
switch (data[6]) {
|
||||
case "1":
|
||||
dnssec_status = '<br><span class="text-green">SECURE</span>';
|
||||
dnssecStatus = '<br><span class="text-green">SECURE</span>';
|
||||
break;
|
||||
case "2":
|
||||
dnssec_status = '<br><span class="text-orange">INSECURE</span>';
|
||||
dnssecStatus = '<br><span class="text-orange">INSECURE</span>';
|
||||
break;
|
||||
case "3":
|
||||
dnssec_status = '<br><span class="text-red">BOGUS</span>';
|
||||
dnssecStatus = '<br><span class="text-red">BOGUS</span>';
|
||||
break;
|
||||
case "4":
|
||||
dnssec_status = '<br><span class="text-red">ABANDONED</span>';
|
||||
dnssecStatus = '<br><span class="text-red">ABANDONED</span>';
|
||||
break;
|
||||
case "5":
|
||||
dnssec_status = '<br><span class="text-orange">UNKNOWN</span>';
|
||||
dnssecStatus = '<br><span class="text-orange">UNKNOWN</span>';
|
||||
break;
|
||||
default:
|
||||
// No DNSSEC
|
||||
dnssec_status = "";
|
||||
dnssecStatus = "";
|
||||
}
|
||||
|
||||
// Query status
|
||||
@@ -193,13 +189,13 @@ $(document).ready(function() {
|
||||
break;
|
||||
case "2":
|
||||
colorClass = "text-green";
|
||||
fieldtext = "OK <br class='hidden-lg'>(forwarded)" + dnssec_status;
|
||||
fieldtext = "OK <br class='hidden-lg'>(forwarded)" + dnssecStatus;
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
|
||||
break;
|
||||
case "3":
|
||||
colorClass = "text-green";
|
||||
fieldtext = "OK <br class='hidden-lg'>(cached)" + dnssec_status;
|
||||
fieldtext = "OK <br class='hidden-lg'>(cached)" + dnssecStatus;
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
|
||||
break;
|
||||
@@ -262,42 +258,52 @@ $(document).ready(function() {
|
||||
isCNAME = true;
|
||||
break;
|
||||
default:
|
||||
colorClass = "text-black";
|
||||
colorClass = false;
|
||||
fieldtext = "Unknown (" + parseInt(data[4]) + ")";
|
||||
buttontext = "";
|
||||
}
|
||||
|
||||
fieldtext += '<input type="hidden" name="id" value="' + parseInt(data[4]) + '">';
|
||||
|
||||
$(row).addClass(colorClass);
|
||||
if (colorClass !== false) {
|
||||
$(row).addClass(colorClass);
|
||||
}
|
||||
|
||||
$("td:eq(4)", row).html(fieldtext);
|
||||
$("td:eq(6)", row).html(buttontext);
|
||||
|
||||
if (regexLink) {
|
||||
$("td:eq(4)", row).hover(
|
||||
function() {
|
||||
function () {
|
||||
this.title = "Click to show matching regex filter";
|
||||
this.style.color = "#72afd2";
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
this.style.color = "";
|
||||
}
|
||||
);
|
||||
$("td:eq(4)", row).click(function() {
|
||||
var new_tab = window.open("groups-domains.php?domainid=" + data[9], "_blank");
|
||||
if (new_tab) {
|
||||
new_tab.focus();
|
||||
$("td:eq(4)", row).off(); // Release any possible previous onClick event handlers
|
||||
$("td:eq(4)", row).click(function () {
|
||||
var newTab = window.open("groups-domains.php?domainid=" + data[9], "_blank");
|
||||
if (newTab) {
|
||||
newTab.focus();
|
||||
}
|
||||
});
|
||||
$("td:eq(4)", row).addClass("underline");
|
||||
$("td:eq(4)", row).addClass("pointer");
|
||||
$("td:eq(4)", row).addClass("text-underline pointer");
|
||||
}
|
||||
|
||||
// Add domain in CNAME chain causing the query to have been blocked
|
||||
// Substitute domain by "." if empty
|
||||
var domain = data[2];
|
||||
var CNAME_domain = data[8];
|
||||
if (domain.length === 0) {
|
||||
domain = ".";
|
||||
}
|
||||
|
||||
if (isCNAME) {
|
||||
$("td:eq(2)", row).text(domain + "\n(blocked " + CNAME_domain + ")");
|
||||
var CNAMEDomain = data[8];
|
||||
// Add domain in CNAME chain causing the query to have been blocked
|
||||
$("td:eq(2)", row).text(domain + "\n(blocked " + CNAMEDomain + ")");
|
||||
} else {
|
||||
$("td:eq(2)", row).text(domain);
|
||||
}
|
||||
|
||||
// Check for existence of sixth column and display only if not Pi-holed
|
||||
@@ -315,7 +321,7 @@ $(document).ready(function() {
|
||||
$("td:eq(5)", row).addClass("text-black");
|
||||
$("td:eq(5)", row).html(replytext);
|
||||
|
||||
if (data.length > 7 && data[7] > 0) {
|
||||
if (data.length > 7) {
|
||||
var content = $("td:eq(5)", row).html();
|
||||
$("td:eq(5)", row).html(content + " (" + (0.1 * data[7]).toFixed(1) + "ms)");
|
||||
}
|
||||
@@ -328,9 +334,9 @@ $(document).ready(function() {
|
||||
ajax: {
|
||||
url: APIstring,
|
||||
error: handleAjaxError,
|
||||
dataSrc: function(data) {
|
||||
dataSrc: function (data) {
|
||||
var dataIndex = 0;
|
||||
return data.data.map(function(x) {
|
||||
return data.data.map(function (x) {
|
||||
x[0] = x[0] * 1e6 + dataIndex++;
|
||||
var dnssec = x[5];
|
||||
var reply = x[6];
|
||||
@@ -346,7 +352,7 @@ $(document).ready(function() {
|
||||
columns: [
|
||||
{
|
||||
width: "15%",
|
||||
render: function(data, type) {
|
||||
render: function (data, type) {
|
||||
if (type === "display") {
|
||||
return moment
|
||||
.unix(Math.floor(data / 1e6))
|
||||
@@ -368,32 +374,11 @@ $(document).ready(function() {
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function(settings, data) {
|
||||
// Clear possible filtering settings
|
||||
data.columns.forEach(function(value, index) {
|
||||
data.columns[index].search.search = "";
|
||||
});
|
||||
|
||||
// Always start on the first page to show most recent queries
|
||||
data.start = 0;
|
||||
|
||||
// Always start with empty search field
|
||||
data.search.search = "";
|
||||
|
||||
// Store current state in client's local storage area
|
||||
localStorage.setItem("query_log_table", JSON.stringify(data));
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("query_log_table", data);
|
||||
},
|
||||
stateLoadCallback: function() {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem("query_log_table");
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
data = JSON.parse(data);
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
stateLoadCallback: function () {
|
||||
return utils.stateLoadCallback("query_log_table");
|
||||
},
|
||||
columnDefs: [
|
||||
{
|
||||
@@ -402,33 +387,33 @@ $(document).ready(function() {
|
||||
defaultContent: ""
|
||||
}
|
||||
],
|
||||
initComplete: function() {
|
||||
initComplete: function () {
|
||||
var api = this.api();
|
||||
|
||||
// Query type IPv4 / IPv6
|
||||
api.$("td:eq(1)").click(function(event) {
|
||||
api.$("td:eq(1)").click(function (event) {
|
||||
addColumnFilter(event, 1, this.textContent);
|
||||
});
|
||||
|
||||
// Domain
|
||||
api.$("td:eq(2)").click(function(event) {
|
||||
api.$("td:eq(2)").click(function (event) {
|
||||
addColumnFilter(event, 2, this.textContent.split("\n")[0]);
|
||||
});
|
||||
|
||||
// Client
|
||||
api.$("td:eq(3)").click(function(event) {
|
||||
api.$("td:eq(3)").click(function (event) {
|
||||
addColumnFilter(event, 3, this.textContent);
|
||||
});
|
||||
|
||||
// Status
|
||||
api.$("td:eq(4)").click(function(event) {
|
||||
api.$("td:eq(4)").click(function (event) {
|
||||
var id = this.children.id.value;
|
||||
var text = this.textContent;
|
||||
addColumnFilter(event, 4, id + "#" + text);
|
||||
});
|
||||
|
||||
// Reply type
|
||||
api.$("td:eq(5)").click(function(event) {
|
||||
api.$("td:eq(5)").click(function (event) {
|
||||
var id = this.children.id.value;
|
||||
var text = this.textContent.split(" ")[0];
|
||||
addColumnFilter(event, 5, id + "#" + text);
|
||||
@@ -438,7 +423,7 @@ $(document).ready(function() {
|
||||
|
||||
resetColumnsFilters();
|
||||
|
||||
$("#all-queries tbody").on("click", "button", function() {
|
||||
$("#all-queries tbody").on("click", "button", function () {
|
||||
var data = tableApi.row($(this).parents("tr")).data();
|
||||
if (data[4] === "2" || data[4] === "3") {
|
||||
add(data[2], "black");
|
||||
@@ -447,9 +432,19 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
|
||||
$("#resetButton").click(function() {
|
||||
$("#resetButton").click(function () {
|
||||
tableApi.search("");
|
||||
resetColumnsFilters();
|
||||
});
|
||||
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
if (input !== null) {
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
}
|
||||
});
|
||||
|
||||
function addColumnFilter(event, colID, filterstring) {
|
||||
@@ -468,7 +463,7 @@ function addColumnFilter(event, colID, filterstring) {
|
||||
}
|
||||
|
||||
function resetColumnsFilters() {
|
||||
tableFilters.forEach(function(value, index) {
|
||||
tableFilters.forEach(function (value, index) {
|
||||
tableFilters[index] = "";
|
||||
});
|
||||
|
||||
@@ -478,7 +473,7 @@ function resetColumnsFilters() {
|
||||
|
||||
function applyColumnFiltering() {
|
||||
var showReset = false;
|
||||
tableFilters.forEach(function(value, index) {
|
||||
tableFilters.forEach(function (value, index) {
|
||||
// Prepare regex filter string
|
||||
var regex = "";
|
||||
|
||||
@@ -519,7 +514,7 @@ function applyColumnFiltering() {
|
||||
function showResetButton() {
|
||||
var button = $("#resetButton");
|
||||
var text = "";
|
||||
tableFilters.forEach(function(value, index) {
|
||||
tableFilters.forEach(function (value, index) {
|
||||
// Split filter string if we received a combined ID#Name column
|
||||
var valArr = value.split("#");
|
||||
if (valArr.length > 1) {
|
||||
@@ -535,11 +530,11 @@ function showResetButton() {
|
||||
});
|
||||
|
||||
button.text(text);
|
||||
button.show();
|
||||
button.removeClass("hidden");
|
||||
}
|
||||
|
||||
function hideResetButton() {
|
||||
var button = $("#resetButton");
|
||||
button.text("");
|
||||
button.hide();
|
||||
button.addClass("hidden");
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global ActiveXObject: false */
|
||||
|
||||
var exact = "";
|
||||
|
||||
function quietfilter(ta, data) {
|
||||
@@ -21,38 +19,9 @@ function quietfilter(ta, data) {
|
||||
}
|
||||
}
|
||||
|
||||
// Credit: http://stackoverflow.com/a/10642418/2087442
|
||||
function httpGet(ta, quiet, theUrl) {
|
||||
var xmlhttp;
|
||||
if (window.XMLHttpRequest) {
|
||||
// code for IE7+
|
||||
xmlhttp = new XMLHttpRequest();
|
||||
} else {
|
||||
// code for IE6, IE5
|
||||
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
|
||||
ta.show();
|
||||
ta.empty();
|
||||
if (!quiet) {
|
||||
ta.append(xmlhttp.responseText);
|
||||
} else {
|
||||
quietfilter(ta, xmlhttp.responseText);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xmlhttp.open("GET", theUrl, false);
|
||||
xmlhttp.send();
|
||||
}
|
||||
|
||||
function eventsource() {
|
||||
var ta = $("#output");
|
||||
var domain = $("#domain")
|
||||
.val()
|
||||
.trim();
|
||||
var domain = $("#domain").val().trim();
|
||||
var q = $("#quiet");
|
||||
|
||||
if (domain.length === 0) {
|
||||
@@ -67,14 +36,23 @@ function eventsource() {
|
||||
|
||||
// IE does not support EventSource - load whole content at once
|
||||
if (typeof EventSource !== "function") {
|
||||
httpGet(
|
||||
ta,
|
||||
quiet,
|
||||
"scripts/pi-hole/php/queryads.php?domain=" + domain.toLowerCase() + exact + "&IE"
|
||||
);
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: "scripts/pi-hole/php/queryads.php?domain=" + domain.toLowerCase() + exact + "&IE",
|
||||
async: false
|
||||
}).done(function (data) {
|
||||
ta.show();
|
||||
ta.empty();
|
||||
if (!quiet) {
|
||||
ta.append(data);
|
||||
} else {
|
||||
quietfilter(ta, data);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line compat/compat
|
||||
var source = new EventSource(
|
||||
"scripts/pi-hole/php/queryads.php?domain=" + domain.toLowerCase() + "&" + exact
|
||||
);
|
||||
@@ -85,7 +63,7 @@ function eventsource() {
|
||||
|
||||
source.addEventListener(
|
||||
"message",
|
||||
function(e) {
|
||||
function (e) {
|
||||
if (!quiet) {
|
||||
ta.append(e.data);
|
||||
} else {
|
||||
@@ -98,7 +76,7 @@ function eventsource() {
|
||||
// Will be called when script has finished
|
||||
source.addEventListener(
|
||||
"error",
|
||||
function() {
|
||||
function () {
|
||||
source.close();
|
||||
},
|
||||
false
|
||||
@@ -109,7 +87,7 @@ function eventsource() {
|
||||
}
|
||||
|
||||
// Handle enter button
|
||||
$(document).keypress(function(e) {
|
||||
$(document).keypress(function (e) {
|
||||
if (e.which === 13 && $("#domain").is(":focus")) {
|
||||
// Enter was pressed, and the input has focus
|
||||
exact = "";
|
||||
@@ -117,36 +95,32 @@ $(document).keypress(function(e) {
|
||||
}
|
||||
});
|
||||
// Handle button
|
||||
$("#btnSearch").on("click", function() {
|
||||
$("#btnSearch").on("click", function () {
|
||||
exact = "";
|
||||
eventsource();
|
||||
});
|
||||
// Handle exact button
|
||||
$("#btnSearchExact").on("click", function() {
|
||||
$("#btnSearchExact").on("click", function () {
|
||||
exact = "exact";
|
||||
eventsource();
|
||||
});
|
||||
|
||||
// Wrap form-group's buttons to next line when viewed on a small screen
|
||||
$(window).on("resize", function() {
|
||||
$(window).on("resize", function () {
|
||||
if ($(window).width() < 991) {
|
||||
$(".form-group.input-group")
|
||||
.removeClass("input-group")
|
||||
.addClass("input-group-block");
|
||||
$(".form-group.input-group").removeClass("input-group").addClass("input-group-block");
|
||||
$(".form-group.input-group-block > input").css("margin-bottom", "5px");
|
||||
$(".form-group.input-group-block > .input-group-btn")
|
||||
.removeClass("input-group-btn")
|
||||
.addClass("btn-block text-center");
|
||||
} else {
|
||||
$(".form-group.input-group-block")
|
||||
.removeClass("input-group-block")
|
||||
.addClass("input-group");
|
||||
$(".form-group.input-group-block").removeClass("input-group-block").addClass("input-group");
|
||||
$(".form-group.input-group > input").css("margin-bottom", "");
|
||||
$(".form-group.input-group > .btn-block.text-center")
|
||||
.removeClass("btn-block text-center")
|
||||
.addClass("input-group-btn");
|
||||
}
|
||||
});
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
$(window).trigger("resize");
|
||||
});
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
$(function() {
|
||||
$("[data-static]").on("click", function() {
|
||||
/* global utils:false */
|
||||
|
||||
$(function () {
|
||||
$("[data-static]").on("click", function () {
|
||||
var row = $(this).closest("tr");
|
||||
var mac = row.find("#MAC").text();
|
||||
var ip = row.find("#IP").text();
|
||||
@@ -17,12 +19,12 @@ $(function() {
|
||||
});
|
||||
});
|
||||
$(".confirm-poweroff").confirm({
|
||||
text: "Are you sure you want to send a poweroff command to your Pi-Hole?",
|
||||
text: "Are you sure you want to send a poweroff command to your Pi-hole?",
|
||||
title: "Confirmation required",
|
||||
confirm: function() {
|
||||
confirm: function () {
|
||||
$("#poweroffform").submit();
|
||||
},
|
||||
cancel: function() {
|
||||
cancel: function () {
|
||||
// nothing to do
|
||||
},
|
||||
confirmButton: "Yes, poweroff",
|
||||
@@ -30,15 +32,15 @@ $(".confirm-poweroff").confirm({
|
||||
post: true,
|
||||
confirmButtonClass: "btn-danger",
|
||||
cancelButtonClass: "btn-success",
|
||||
dialogClass: "modal-dialog modal-mg" // Bootstrap classes for mid-size modal
|
||||
dialogClass: "modal-dialog"
|
||||
});
|
||||
$(".confirm-reboot").confirm({
|
||||
text: "Are you sure you want to send a reboot command to your Pi-Hole?",
|
||||
text: "Are you sure you want to send a reboot command to your Pi-hole?",
|
||||
title: "Confirmation required",
|
||||
confirm: function() {
|
||||
confirm: function () {
|
||||
$("#rebootform").submit();
|
||||
},
|
||||
cancel: function() {
|
||||
cancel: function () {
|
||||
// nothing to do
|
||||
},
|
||||
confirmButton: "Yes, reboot",
|
||||
@@ -46,16 +48,16 @@ $(".confirm-reboot").confirm({
|
||||
post: true,
|
||||
confirmButtonClass: "btn-danger",
|
||||
cancelButtonClass: "btn-success",
|
||||
dialogClass: "modal-dialog modal-mg" // Bootstrap classes for mid-size modal
|
||||
dialogClass: "modal-dialog"
|
||||
});
|
||||
|
||||
$(".confirm-restartdns").confirm({
|
||||
text: "Are you sure you want to send a restart command to your DNS server?",
|
||||
title: "Confirmation required",
|
||||
confirm: function() {
|
||||
confirm: function () {
|
||||
$("#restartdnsform").submit();
|
||||
},
|
||||
cancel: function() {
|
||||
cancel: function () {
|
||||
// nothing to do
|
||||
},
|
||||
confirmButton: "Yes, restart DNS",
|
||||
@@ -63,16 +65,16 @@ $(".confirm-restartdns").confirm({
|
||||
post: true,
|
||||
confirmButtonClass: "btn-danger",
|
||||
cancelButtonClass: "btn-success",
|
||||
dialogClass: "modal-dialog modal-mg"
|
||||
dialogClass: "modal-dialog"
|
||||
});
|
||||
|
||||
$(".confirm-flushlogs").confirm({
|
||||
text: "Are you sure you want to flush your logs?",
|
||||
title: "Confirmation required",
|
||||
confirm: function() {
|
||||
confirm: function () {
|
||||
$("#flushlogsform").submit();
|
||||
},
|
||||
cancel: function() {
|
||||
cancel: function () {
|
||||
// nothing to do
|
||||
},
|
||||
confirmButton: "Yes, flush logs",
|
||||
@@ -80,16 +82,16 @@ $(".confirm-flushlogs").confirm({
|
||||
post: true,
|
||||
confirmButtonClass: "btn-danger",
|
||||
cancelButtonClass: "btn-success",
|
||||
dialogClass: "modal-dialog modal-mg"
|
||||
dialogClass: "modal-dialog"
|
||||
});
|
||||
|
||||
$(".confirm-flusharp").confirm({
|
||||
text: "Are you sure you want to flush your network table?",
|
||||
title: "Confirmation required",
|
||||
confirm: function() {
|
||||
confirm: function () {
|
||||
$("#flusharpform").submit();
|
||||
},
|
||||
cancel: function() {
|
||||
cancel: function () {
|
||||
// nothing to do
|
||||
},
|
||||
confirmButton: "Yes, flush my network table",
|
||||
@@ -97,16 +99,16 @@ $(".confirm-flusharp").confirm({
|
||||
post: true,
|
||||
confirmButtonClass: "btn-warning",
|
||||
cancelButtonClass: "btn-success",
|
||||
dialogClass: "modal-dialog modal-mg"
|
||||
dialogClass: "modal-dialog"
|
||||
});
|
||||
|
||||
$(".confirm-disablelogging-noflush").confirm({
|
||||
text: "Are you sure you want to disable logging?",
|
||||
title: "Confirmation required",
|
||||
confirm: function() {
|
||||
confirm: function () {
|
||||
$("#disablelogsform-noflush").submit();
|
||||
},
|
||||
cancel: function() {
|
||||
cancel: function () {
|
||||
// nothing to do
|
||||
},
|
||||
confirmButton: "Yes, disable logs",
|
||||
@@ -114,17 +116,17 @@ $(".confirm-disablelogging-noflush").confirm({
|
||||
post: true,
|
||||
confirmButtonClass: "btn-warning",
|
||||
cancelButtonClass: "btn-success",
|
||||
dialogClass: "modal-dialog modal-mg"
|
||||
dialogClass: "modal-dialog"
|
||||
});
|
||||
|
||||
$(".api-token").confirm({
|
||||
text:
|
||||
"Make sure that nobody else can scan this code around you. They will have full access to the API without having to know the password. Note that the generation of the QR code will take some time.",
|
||||
title: "Confirmation required",
|
||||
confirm: function() {
|
||||
confirm: function () {
|
||||
window.open("scripts/pi-hole/php/api_token.php");
|
||||
},
|
||||
cancel: function() {
|
||||
cancel: function () {
|
||||
// nothing to do
|
||||
},
|
||||
confirmButton: "Yes, show API token",
|
||||
@@ -132,18 +134,16 @@ $(".api-token").confirm({
|
||||
post: true,
|
||||
confirmButtonClass: "btn-danger",
|
||||
cancelButtonClass: "btn-success",
|
||||
dialogClass: "modal-dialog modal-mg"
|
||||
dialogClass: "modal-dialog"
|
||||
});
|
||||
|
||||
$("#DHCPchk").click(function() {
|
||||
$("#DHCPchk").click(function () {
|
||||
$("input.DHCPgroup").prop("disabled", !this.checked);
|
||||
$("#dhcpnotice")
|
||||
.prop("hidden", !this.checked)
|
||||
.addClass("lookatme");
|
||||
$("#dhcpnotice").prop("hidden", !this.checked).addClass("lookatme");
|
||||
});
|
||||
|
||||
function loadCacheInfo() {
|
||||
$.getJSON("api.php?getCacheInfo", function(data) {
|
||||
$.getJSON("api.php?getCacheInfo", function (data) {
|
||||
if ("FTLnotrunning" in data) {
|
||||
return;
|
||||
}
|
||||
@@ -156,13 +156,9 @@ function loadCacheInfo() {
|
||||
var cachelivefreed = parseInt(data.cacheinfo["cache-live-freed"]);
|
||||
$("#cache-live-freed").text(cachelivefreed);
|
||||
if (cachelivefreed > 0) {
|
||||
$("#cache-live-freed")
|
||||
.parent("tr")
|
||||
.addClass("lookatme");
|
||||
$("#cache-live-freed").parent("tr").addClass("lookatme");
|
||||
} else {
|
||||
$("#cache-live-freed")
|
||||
.parent("tr")
|
||||
.removeClass("lookatme");
|
||||
$("#cache-live-freed").parent("tr").removeClass("lookatme");
|
||||
}
|
||||
|
||||
// Update cache info every 10 seconds
|
||||
@@ -171,7 +167,7 @@ function loadCacheInfo() {
|
||||
}
|
||||
|
||||
var leasetable, staticleasetable;
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
if (document.getElementById("DHCPLeasesTable")) {
|
||||
leasetable = $("#DHCPLeasesTable").DataTable({
|
||||
dom: "<'row'<'col-sm-12'tr>><'row'<'col-sm-6'i><'col-sm-6'f>>",
|
||||
@@ -179,7 +175,15 @@ $(document).ready(function() {
|
||||
paging: false,
|
||||
scrollCollapse: true,
|
||||
scrollY: "200px",
|
||||
scrollX: true
|
||||
scrollX: true,
|
||||
order: [[2, "asc"]],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("activeDhcpLeaseTable", data);
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
return utils.stateLoadCallback("activeDhcpLeaseTable");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -190,12 +194,20 @@ $(document).ready(function() {
|
||||
paging: false,
|
||||
scrollCollapse: true,
|
||||
scrollY: "200px",
|
||||
scrollX: true
|
||||
scrollX: true,
|
||||
order: [[2, "asc"]],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("staticDhcpLeaseTable", data);
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
return utils.stateLoadCallback("staticDhcpLeaseTable");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//call draw() on each table... they don't render properly with scrollX and scrollY set... ¯\_(ツ)_/¯
|
||||
$('a[data-toggle="tab"]').on("shown.bs.tab", function() {
|
||||
$('a[data-toggle="tab"]').on("shown.bs.tab", function () {
|
||||
leasetable.draw();
|
||||
staticleasetable.draw();
|
||||
});
|
||||
@@ -204,8 +216,8 @@ $(document).ready(function() {
|
||||
});
|
||||
|
||||
// Handle hiding of alerts
|
||||
$(function() {
|
||||
$("[data-hide]").on("click", function() {
|
||||
$(function () {
|
||||
$("[data-hide]").on("click", function () {
|
||||
$(this)
|
||||
.closest("." + $(this).attr("data-hide"))
|
||||
.hide();
|
||||
@@ -213,12 +225,12 @@ $(function() {
|
||||
});
|
||||
|
||||
// DHCP leases tooltips
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip({ html: true, container: "body" });
|
||||
});
|
||||
|
||||
// Change "?tab=" parameter in URL for save and reload
|
||||
$(".nav-tabs a").on("shown.bs.tab", function(e) {
|
||||
$(".nav-tabs a").on("shown.bs.tab", function (e) {
|
||||
var tab = e.target.hash.substring(1);
|
||||
window.history.pushState("", "", "?tab=" + tab);
|
||||
if (tab === "piholedhcp") {
|
||||
@@ -229,11 +241,37 @@ $(".nav-tabs a").on("shown.bs.tab", function(e) {
|
||||
});
|
||||
|
||||
// Auto dismissal for info notifications
|
||||
$(document).ready(function() {
|
||||
$(function () {
|
||||
var alInfo = $("#alInfo");
|
||||
if (alInfo.length) {
|
||||
alInfo.delay(3000).fadeOut(2000, function() {
|
||||
if (alInfo.length > 0) {
|
||||
alInfo.delay(3000).fadeOut(2000, function () {
|
||||
alInfo.hide();
|
||||
});
|
||||
}
|
||||
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
});
|
||||
|
||||
// Bar/Smooth chart toggle
|
||||
$(function () {
|
||||
var bargraphs = $("#bargraphs");
|
||||
var chkboxData = localStorage.getItem("barchart_chkbox");
|
||||
|
||||
if (chkboxData !== null) {
|
||||
// Restore checkbox state
|
||||
bargraphs.prop("checked", chkboxData === "true");
|
||||
} else {
|
||||
// Initialize checkbox
|
||||
bargraphs.prop("checked", true);
|
||||
localStorage.setItem("barchart_chkbox", true);
|
||||
}
|
||||
|
||||
bargraphs.click(function () {
|
||||
localStorage.setItem("barchart_chkbox", bargraphs.prop("checked"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ var interval = 200;
|
||||
// Function that asks the API for new data
|
||||
function reloadData() {
|
||||
clearTimeout(timer);
|
||||
$.getJSON("scripts/pi-hole/php/tailLog.php?FTL&offset=" + offset, function(data) {
|
||||
$.getJSON("scripts/pi-hole/php/tailLog.php?FTL&offset=" + offset, function (data) {
|
||||
pre.append(data.lines);
|
||||
|
||||
if (scrolling && offset !== data.offset) {
|
||||
@@ -29,9 +29,9 @@ function reloadData() {
|
||||
timer = setTimeout(reloadData, interval);
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$(function () {
|
||||
// Get offset at first loading of page
|
||||
$.getJSON("scripts/pi-hole/php/tailLog.php?FTL", function(data) {
|
||||
$.getJSON("scripts/pi-hole/php/tailLog.php?FTL", function (data) {
|
||||
offset = data.offset;
|
||||
});
|
||||
pre = $("#output");
|
||||
@@ -39,11 +39,11 @@ $(function() {
|
||||
reloadData();
|
||||
});
|
||||
|
||||
$("#chk1").click(function() {
|
||||
$("#chk1").click(function () {
|
||||
$("#chk2").prop("checked", this.checked);
|
||||
scrolling = this.checked;
|
||||
});
|
||||
$("#chk2").click(function() {
|
||||
$("#chk2").click(function () {
|
||||
$("#chk1").prop("checked", this.checked);
|
||||
scrolling = this.checked;
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ var interval = 200;
|
||||
// Function that asks the API for new data
|
||||
function reloadData() {
|
||||
clearTimeout(timer);
|
||||
$.getJSON("scripts/pi-hole/php/tailLog.php?offset=" + offset, function(data) {
|
||||
$.getJSON("scripts/pi-hole/php/tailLog.php?offset=" + offset, function (data) {
|
||||
pre.append(data.lines);
|
||||
|
||||
if (scrolling && offset !== data.offset) {
|
||||
@@ -29,9 +29,9 @@ function reloadData() {
|
||||
timer = setTimeout(reloadData, interval);
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$(function () {
|
||||
// Get offset at first loading of page
|
||||
$.getJSON("scripts/pi-hole/php/tailLog.php", function(data) {
|
||||
$.getJSON("scripts/pi-hole/php/tailLog.php", function (data) {
|
||||
offset = data.offset;
|
||||
});
|
||||
pre = $("#output");
|
||||
@@ -39,11 +39,11 @@ $(function() {
|
||||
reloadData();
|
||||
});
|
||||
|
||||
$("#chk1").click(function() {
|
||||
$("#chk1").click(function () {
|
||||
$("#chk2").prop("checked", this.checked);
|
||||
scrolling = this.checked;
|
||||
});
|
||||
$("#chk2").click(function() {
|
||||
$("#chk2").click(function () {
|
||||
$("#chk1").prop("checked", this.checked);
|
||||
scrolling = this.checked;
|
||||
});
|
||||
|
||||
238
scripts/pi-hole/js/utils.js
Normal file
238
scripts/pi-hole/js/utils.js
Normal file
@@ -0,0 +1,238 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2020 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
|
||||
// Credit: https://stackoverflow.com/a/4835406
|
||||
function escapeHtml(text) {
|
||||
var map = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'"
|
||||
};
|
||||
|
||||
return text.replace(/[&<>"']/g, function (m) {
|
||||
return map[m];
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function for converting Objects to Arrays after sorting the keys
|
||||
function objectToArray(obj) {
|
||||
var arr = [];
|
||||
var idx = [];
|
||||
var keys = Object.keys(obj);
|
||||
|
||||
keys.sort(function (a, b) {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
arr.push(obj[keys[i]]);
|
||||
idx.push(keys[i]);
|
||||
}
|
||||
|
||||
return [idx, arr];
|
||||
}
|
||||
|
||||
function padNumber(num) {
|
||||
return ("00" + num).substr(-2, 2);
|
||||
}
|
||||
|
||||
var info = null; // TODO clear this up; there shouldn't be a global var here
|
||||
function showAlert(type, icon, title, message) {
|
||||
var opts = {};
|
||||
title = " <strong>" + title + "</strong><br>";
|
||||
switch (type) {
|
||||
case "info":
|
||||
opts = {
|
||||
type: "info",
|
||||
icon: "far fa-clock",
|
||||
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: "fas fa-exclamation-triangle",
|
||||
title: title,
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
case "error":
|
||||
opts = {
|
||||
type: "danger",
|
||||
icon: "fas fa-times",
|
||||
title: " <strong>Error, something went wrong!</strong><br>",
|
||||
message: message
|
||||
};
|
||||
if (info) {
|
||||
info.update(opts);
|
||||
} else {
|
||||
$.notify(opts);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
function datetime(date) {
|
||||
return moment.unix(Math.floor(date)).format("Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z");
|
||||
}
|
||||
|
||||
function disableAll() {
|
||||
$("input").prop("disabled", true);
|
||||
$("select").prop("disabled", true);
|
||||
$("button").prop("disabled", true);
|
||||
$("textarea").prop("disabled", true);
|
||||
}
|
||||
|
||||
function enableAll() {
|
||||
$("input").prop("disabled", false);
|
||||
$("select").prop("disabled", false);
|
||||
$("button").prop("disabled", false);
|
||||
$("textarea").prop("disabled", false);
|
||||
|
||||
// Enable custom input field only if applicable
|
||||
var ip = $("#select") ? $("#select").val() : null;
|
||||
if (ip !== null && ip !== "custom") {
|
||||
$("#ip-custom").prop("disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Pi-hole IPv4/CIDR validator by DL6ER, see regexr.com/50csh
|
||||
function validateIPv4CIDR(ip) {
|
||||
// One IPv4 element is 8bit: 0 - 256
|
||||
var ipv4elem = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)";
|
||||
// CIDR for IPv4 is 1 - 32 bit
|
||||
var v4cidr = "(\\/([1-9]|[1-2][0-9]|3[0-2])){0,1}";
|
||||
var ipv4validator = new RegExp(
|
||||
"^" + ipv4elem + "\\." + ipv4elem + "\\." + ipv4elem + "\\." + ipv4elem + v4cidr + "$"
|
||||
);
|
||||
return ipv4validator.test(ip);
|
||||
}
|
||||
|
||||
// Pi-hole IPv6/CIDR validator by DL6ER, see regexr.com/50csn
|
||||
function validateIPv6CIDR(ip) {
|
||||
// One IPv6 element is 16bit: 0000 - FFFF
|
||||
var ipv6elem = "[0-9A-Fa-f]{1,4}";
|
||||
// CIDR for IPv6 is 1- 128 bit
|
||||
var v6cidr = "(\\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}";
|
||||
var ipv6validator = new RegExp(
|
||||
"^(((?:" +
|
||||
ipv6elem +
|
||||
"))((?::" +
|
||||
ipv6elem +
|
||||
"))*::((?:" +
|
||||
ipv6elem +
|
||||
"))*((?::" +
|
||||
ipv6elem +
|
||||
"))*|((?:" +
|
||||
ipv6elem +
|
||||
"))((?::" +
|
||||
ipv6elem +
|
||||
")){7})" +
|
||||
v6cidr +
|
||||
"$"
|
||||
);
|
||||
return ipv6validator.test(ip);
|
||||
}
|
||||
|
||||
// set bootstrap-select defaults
|
||||
function setBsSelectDefaults() {
|
||||
var bsSelectDefaults = $.fn.selectpicker.Constructor.DEFAULTS;
|
||||
bsSelectDefaults.noneSelectedText = "none selected";
|
||||
bsSelectDefaults.selectedTextFormat = "count > 1";
|
||||
bsSelectDefaults.actionsBox = true;
|
||||
bsSelectDefaults.width = "fit";
|
||||
bsSelectDefaults.container = "body";
|
||||
bsSelectDefaults.dropdownAlignRight = "auto";
|
||||
bsSelectDefaults.selectAllText = "All";
|
||||
bsSelectDefaults.deselectAllText = "None";
|
||||
bsSelectDefaults.countSelectedText = function (num, total) {
|
||||
if (num === total) {
|
||||
return "All selected (" + num + ")";
|
||||
}
|
||||
|
||||
return num + " selected";
|
||||
};
|
||||
}
|
||||
|
||||
function stateSaveCallback(itemName, data) {
|
||||
localStorage.setItem(itemName, JSON.stringify(data));
|
||||
}
|
||||
|
||||
function stateLoadCallback(itemName) {
|
||||
// Receive previous state from client's local storage area
|
||||
var data = localStorage.getItem(itemName);
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse JSON string
|
||||
data = JSON.parse(data);
|
||||
|
||||
// Clear possible filtering settings
|
||||
data.columns.forEach(function (value, index) {
|
||||
data.columns[index].search.search = "";
|
||||
});
|
||||
|
||||
// Always start on the first page to show most recent queries
|
||||
data.start = 0;
|
||||
// Always start with empty search field
|
||||
data.search.search = "";
|
||||
// Apply loaded state to table
|
||||
return data;
|
||||
}
|
||||
|
||||
function getGraphType() {
|
||||
// Only return line if `barchart_chkbox` is explicitly set to false. Else return bar
|
||||
return localStorage.getItem("barchart_chkbox") === "false" ? "line" : "bar";
|
||||
}
|
||||
|
||||
window.utils = (function () {
|
||||
return {
|
||||
escapeHtml: escapeHtml,
|
||||
objectToArray: objectToArray,
|
||||
padNumber: padNumber,
|
||||
showAlert: showAlert,
|
||||
datetime: datetime,
|
||||
disableAll: disableAll,
|
||||
enableAll: enableAll,
|
||||
validateIPv4CIDR: validateIPv4CIDR,
|
||||
validateIPv6CIDR: validateIPv6CIDR,
|
||||
setBsSelectDefaults: setBsSelectDefaults,
|
||||
stateSaveCallback: stateSaveCallback,
|
||||
stateLoadCallback: stateLoadCallback,
|
||||
getGraphType: getGraphType
|
||||
};
|
||||
})();
|
||||
@@ -6,14 +6,43 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
$piholeFTLConfFile = "/etc/pihole/pihole-FTL.conf";
|
||||
|
||||
function piholeFTLConfig()
|
||||
{
|
||||
static $piholeFTLConfig;
|
||||
global $piholeFTLConfFile;
|
||||
|
||||
if(isset($piholeFTLConfig))
|
||||
{
|
||||
return $piholeFTLConfig;
|
||||
}
|
||||
|
||||
if(is_readable($piholeFTLConfFile))
|
||||
{
|
||||
$piholeFTLConfig = parse_ini_file($piholeFTLConfFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
$piholeFTLConfig = array();
|
||||
}
|
||||
|
||||
return $piholeFTLConfig;
|
||||
}
|
||||
|
||||
function connectFTL($address, $port=4711)
|
||||
{
|
||||
if($address == "127.0.0.1")
|
||||
{
|
||||
$config = piholeFTLConfig();
|
||||
// Read port
|
||||
$portfile = file_get_contents("/var/run/pihole-FTL.port");
|
||||
if(is_numeric($portfile))
|
||||
$port = intval($portfile);
|
||||
$portfileName = isset($config['PORTFILE']) ? $config['PORTFILE'] : '';
|
||||
if ($portfileName != '')
|
||||
{
|
||||
$portfileContents = file_get_contents($portfileName);
|
||||
if(is_numeric($portfileContents))
|
||||
$port = intval($portfileContents);
|
||||
}
|
||||
}
|
||||
|
||||
// Open Internet socket connection
|
||||
@@ -21,6 +50,7 @@ function connectFTL($address, $port=4711)
|
||||
|
||||
return $socket;
|
||||
}
|
||||
|
||||
function sendRequestFTL($requestin)
|
||||
{
|
||||
global $socket;
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
require_once('auth.php');
|
||||
|
||||
$list = $_POST['list'];
|
||||
|
||||
// Perform all of the authentication for list editing
|
||||
// when NOT invoked and authenticated from API
|
||||
if (empty($api)) {
|
||||
list_verify($list);
|
||||
}
|
||||
|
||||
// Split individual domains into array
|
||||
$domains = preg_split('/\s+/', trim($_POST['domain']));
|
||||
|
||||
// Get comment if available
|
||||
$comment = null;
|
||||
if(isset($_POST['comment'])) {
|
||||
$comment = trim($_POST['comment']);
|
||||
}
|
||||
|
||||
// Convert domain name to IDNA ASCII form for international domains
|
||||
// Do this only for exact domains, not for regex filters
|
||||
// Only do it when the php-intl extension is available
|
||||
if (extension_loaded("intl") && ($list === "white" || $list === "black")) {
|
||||
foreach($domains as &$domain)
|
||||
{
|
||||
$domain = idn_to_ascii($domain);
|
||||
}
|
||||
}
|
||||
|
||||
// Only check domains we add to the exact lists.
|
||||
// Regex are validated by FTL during import
|
||||
$check_lists = ["white","black","audit"];
|
||||
if(in_array($list, $check_lists)) {
|
||||
check_domain($domains);
|
||||
}
|
||||
|
||||
require_once("func.php");
|
||||
require_once("database.php");
|
||||
$GRAVITYDB = getGravityDBFilename();
|
||||
$db = SQLite3_connect($GRAVITYDB, SQLITE3_OPEN_READWRITE);
|
||||
|
||||
$reload = true;
|
||||
switch($list) {
|
||||
case "white":
|
||||
$domains = array_map('strtolower', $domains);
|
||||
echo add_to_table($db, "domainlist", $domains, $comment, false, false, ListType::whitelist);
|
||||
break;
|
||||
|
||||
case "black":
|
||||
$domains = array_map('strtolower', $domains);
|
||||
echo add_to_table($db, "domainlist", $domains, $comment, false, false, ListType::blacklist);
|
||||
break;
|
||||
|
||||
case "white_regex":
|
||||
echo add_to_table($db, "domainlist", $domains, $comment, false, false, ListType::regex_whitelist);
|
||||
break;
|
||||
|
||||
case "white_wild":
|
||||
echo add_to_table($db, "domainlist", $domains, $comment, true, false, ListType::regex_whitelist);
|
||||
break;
|
||||
|
||||
case "black_regex":
|
||||
echo add_to_table($db, "domainlist", $domains, $comment, false, false, ListType::regex_blacklist);
|
||||
break;
|
||||
|
||||
case "black_wild":
|
||||
echo add_to_table($db, "domainlist", $domains, $comment, true, false, ListType::regex_blacklist);
|
||||
break;
|
||||
|
||||
case "audit":
|
||||
$reload = false;
|
||||
echo add_to_table($db, "domain_audit", $domains);
|
||||
break;
|
||||
|
||||
default:
|
||||
die("Invalid list!");
|
||||
}
|
||||
|
||||
// Reload lists in pihole-FTL after having added something
|
||||
if ($reload) {
|
||||
echo shell_exec("sudo pihole restartdns reload-lists");
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -4,136 +4,25 @@
|
||||
|
||||
$customDNSFile = "/etc/pihole/custom.list";
|
||||
|
||||
switch ($_REQUEST['action'])
|
||||
require_once('auth.php');
|
||||
|
||||
// Authentication checks
|
||||
if (isset($_POST['token'])) {
|
||||
check_cors();
|
||||
check_csrf($_POST['token']);
|
||||
} else {
|
||||
log_and_die('Not allowed (login session invalid or expired, please relogin on the Pi-hole dashboard)!');
|
||||
}
|
||||
|
||||
|
||||
switch ($_POST['action'])
|
||||
{
|
||||
case 'get': echo json_encode(echoCustomDNSEntries()); break;
|
||||
case 'add': echo json_encode(addCustomDNSEntry()); break;
|
||||
case 'delete': echo json_encode(deleteCustomDNSEntry()); break;
|
||||
case 'get': echo json_encode(echoCustomDNSEntries()); break;
|
||||
case 'add': echo json_encode(addCustomDNSEntry()); break;
|
||||
case 'delete': echo json_encode(deleteCustomDNSEntry()); break;
|
||||
default:
|
||||
die("Wrong action");
|
||||
}
|
||||
|
||||
function echoCustomDNSEntries()
|
||||
{
|
||||
$entries = getCustomDNSEntries();
|
||||
|
||||
$data = [];
|
||||
foreach ($entries as $entry)
|
||||
$data[] = [ $entry->domain, $entry->ip ];
|
||||
|
||||
return [ "data" => $data ];
|
||||
}
|
||||
|
||||
function getCustomDNSEntries()
|
||||
{
|
||||
global $customDNSFile;
|
||||
|
||||
$entries = [];
|
||||
|
||||
$handle = fopen($customDNSFile, "r");
|
||||
if ($handle)
|
||||
{
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$line = str_replace("\r","", $line);
|
||||
$line = str_replace("\n","", $line);
|
||||
$explodedLine = explode (" ", $line);
|
||||
|
||||
if (count($explodedLine) != 2)
|
||||
continue;
|
||||
|
||||
$data = new \stdClass();
|
||||
$data->ip = $explodedLine[0];
|
||||
$data->domain = $explodedLine[1];
|
||||
$entries[] = $data;
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
function addCustomDNSEntry()
|
||||
{
|
||||
try
|
||||
{
|
||||
$ip = !empty($_REQUEST['ip']) ? $_REQUEST['ip']: "";
|
||||
$domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: "";
|
||||
|
||||
if (empty($ip))
|
||||
return errorJsonResponse("IP must be set");
|
||||
|
||||
$ipType = get_ip_type($ip);
|
||||
|
||||
if (!$ipType)
|
||||
return errorJsonResponse("IP must be valid");
|
||||
|
||||
if (empty($domain))
|
||||
return errorJsonResponse("Domain must be set");
|
||||
|
||||
if (!is_valid_domain_name($domain))
|
||||
return errorJsonResponse("Domain must be valid");
|
||||
|
||||
$existingEntries = getCustomDNSEntries();
|
||||
|
||||
foreach ($existingEntries as $entry)
|
||||
if ($entry->domain == $domain)
|
||||
if (get_ip_type($entry->ip) == $ipType)
|
||||
return errorJsonResponse("This domain already has a custom DNS entry for an IPv" . $ipType);
|
||||
|
||||
exec("sudo pihole -a addcustomdns ".$ip." ".$domain);
|
||||
|
||||
return successJsonResponse();
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return errorJsonResponse($ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCustomDNSEntry()
|
||||
{
|
||||
try
|
||||
{
|
||||
$ip = !empty($_REQUEST['ip']) ? $_REQUEST['ip']: "";
|
||||
$domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: "";
|
||||
|
||||
if (empty($ip))
|
||||
return errorJsonResponse("IP must be set");
|
||||
|
||||
if (empty($domain))
|
||||
return errorJsonResponse("Domain must be set");
|
||||
|
||||
$existingEntries = getCustomDNSEntries();
|
||||
|
||||
$found = false;
|
||||
foreach ($existingEntries as $entry)
|
||||
if ($entry->domain == $domain)
|
||||
if ($entry->ip == $ip) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$found)
|
||||
return errorJsonResponse("This domain/ip association does not exist");
|
||||
|
||||
exec("sudo pihole -a removecustomdns ".$ip." ".$domain);
|
||||
|
||||
return successJsonResponse();
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return errorJsonResponse($ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function successJsonResponse($message = "")
|
||||
{
|
||||
return [ "success" => true, "message" => $message ];
|
||||
}
|
||||
|
||||
function errorJsonResponse($message = "")
|
||||
{
|
||||
return [ "success" => false, "message" => $message ];
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -136,7 +136,7 @@ function add_to_table($db, $table, $domains, $comment=null, $wildcardstyle=false
|
||||
$querystr = "INSERT OR IGNORE INTO $table ($field,comment) VALUES (:$field, :comment);";
|
||||
$bindcomment = true;
|
||||
} else {
|
||||
$querystr = "INSERT OR IGNORE INTO $table ($field,comment,type) VALUES (:$field, :comment, $type);";
|
||||
$querystr = "REPLACE INTO $table ($field,comment,type) VALUES (:$field, :comment, $type);";
|
||||
$bindcomment = true;
|
||||
}
|
||||
$stmt = $db->prepare($querystr);
|
||||
@@ -311,9 +311,11 @@ function remove_from_table($db, $table, $domains, $returnnum=false, $type=-1)
|
||||
}
|
||||
}
|
||||
|
||||
class ListType{
|
||||
const whitelist = 0;
|
||||
const blacklist = 1;
|
||||
const regex_whitelist = 2;
|
||||
const regex_blacklist = 3;
|
||||
}
|
||||
if (!class_exists("ListType")) {
|
||||
class ListType{
|
||||
const whitelist = 0;
|
||||
const blacklist = 1;
|
||||
const regex_whitelist = 2;
|
||||
const regex_blacklist = 3;
|
||||
}
|
||||
}
|
||||
@@ -32,38 +32,71 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button id="pihole-disable-custom" type="button" class="btn btn-primary" data-dismiss="modal">Submit</button>
|
||||
<button type="button" id="pihole-disable-custom" class="btn btn-primary" data-dismiss="modal">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- /.content-wrapper -->
|
||||
|
||||
<?php
|
||||
// Flushes the system write buffers of PHP. This attempts to push everything we have so far all the way to the client's browser.
|
||||
flush();
|
||||
// Run update checker
|
||||
// - determines local branch each time,
|
||||
// - determines local and remote version every 30 minutes
|
||||
require "scripts/pi-hole/php/update_checker.php";
|
||||
// Flushes the system write buffers of PHP. This attempts to push everything we have so far all the way to the client's browser.
|
||||
flush();
|
||||
// Run update checker
|
||||
// - determines local branch each time,
|
||||
// - determines local and remote version every 30 minutes
|
||||
require "scripts/pi-hole/php/update_checker.php";
|
||||
|
||||
$coreVersionStr = $core_current . (isset($core_commit) ? " (" . $core_branch . ", " . $core_commit . ")" : "");
|
||||
$webVersionStr = $web_current . (isset($web_commit) ? " (" . $web_branch . ", " . $web_commit . ")" : "");
|
||||
$ftlVersionStr = $FTL_current . (isset($FTL_commit) ? " (" . $FTL_branch . ", " . $FTL_commit . ")" : "");
|
||||
|
||||
$githubBaseUrl = "https://github.com/pi-hole";
|
||||
$coreUrl = $githubBaseUrl . "/pi-hole";
|
||||
$webUrl = $githubBaseUrl . "/AdminLTE";
|
||||
$ftlUrl = $githubBaseUrl . "/FTL";
|
||||
|
||||
$coreReleasesUrl = $coreUrl . "/releases";
|
||||
$webReleasesUrl = $webUrl . "/releases";
|
||||
$ftlReleasesUrl = $ftlUrl . "/releases";
|
||||
?>
|
||||
<!-- /.content-wrapper -->
|
||||
<footer class="main-footer">
|
||||
<!-- Version Infos -->
|
||||
<div class="pull-right hidden-xs hidden-sm<?php if(isset($core_commit) || isset($web_commit) || isset($FTL_commit)) { ?> hidden-md<?php } ?>">
|
||||
<b>Pi-hole Version </b> <?php
|
||||
echo $core_current;
|
||||
if(isset($core_commit)) { echo " (".$core_branch.", ".$core_commit.")"; }
|
||||
if($core_update){ ?> <a class="alert-link lookatme" href="https://github.com/pi-hole/pi-hole/releases" rel="noopener" target="_blank">(Update available!)</a><?php } ?>
|
||||
<b>Web Interface Version </b><?php
|
||||
echo $web_current;
|
||||
if(isset($web_commit)) { echo " (".$web_branch.", ".$web_commit.")"; }
|
||||
if($web_update){ ?> <a class="alert-link lookatme" href="https://github.com/pi-hole/AdminLTE/releases" rel="noopener" target="_blank">(Update available!)</a><?php } ?>
|
||||
<b>FTL Version </b> <?php
|
||||
echo $FTL_current;
|
||||
if(isset($FTL_commit)) { echo " (".$FTL_branch.", ".$FTL_commit.")"; }
|
||||
if($FTL_update){ ?> <a class="alert-link lookatme" href="https://github.com/pi-hole/FTL/releases" rel="noopener" target="_blank">(Update available!)</a><?php } ?>
|
||||
<div class="row row-centered text-center">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<strong><a href="https://pi-hole.net/donate/" rel="noopener" target="_blank"><i class="fa fa-heart text-red"></i> Donate</a></strong> if you found this useful.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-centered text-center version-info">
|
||||
<div class="col-xs-12 col-sm-8 col-md-6">
|
||||
<?php if (isset($core_commit) || isset($web_commit) || isset($FTL_commit)) { ?>
|
||||
<ul class="list-unstyled">
|
||||
<li><strong>Pi-hole</strong> <?php echo $coreVersionStr; ?></li>
|
||||
<li><strong>Web Interface</strong> <?php echo $webVersionStr; ?></li>
|
||||
<li><strong>FTL</strong> <?php echo $ftlVersionStr; ?></li>
|
||||
</ul>
|
||||
<?php } else { ?>
|
||||
<ul class="list-inline">
|
||||
<li>
|
||||
<strong>Pi-hole</strong>
|
||||
<a href="<?php echo $coreReleasesUrl . "/" . $core_current; ?>" rel="noopener" target="_blank"><?php echo $core_current; ?></a>
|
||||
<?php if ($core_update) { ?> · <a class="lookatme" href="<?php echo $coreReleasesUrl . "/latest"; ?>" rel="noopener" target="_blank">Update available!</a><?php } ?>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Web Interface</strong>
|
||||
<a href="<?php echo $webReleasesUrl . "/" . $web_current; ?>" rel="noopener" target="_blank"><?php echo $web_current; ?></a>
|
||||
<?php if ($web_update) { ?> · <a class="lookatme" href="<?php echo $webReleasesUrl . "/latest"; ?>" rel="noopener" target="_blank">Update available!</a><?php } ?>
|
||||
</li>
|
||||
<li>
|
||||
<strong>FTL</strong>
|
||||
<a href="<?php echo $ftlReleasesUrl . "/" . $FTL_current; ?>" rel="noopener" target="_blank"><?php echo $FTL_current; ?></a>
|
||||
<?php if ($FTL_update) { ?> · <a class="lookatme" href="<?php echo $ftlReleasesUrl . "/latest"; ?>" rel="noopener" target="_blank">Update available!</a><?php } ?>
|
||||
</li>
|
||||
</ul>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: inline-block"><strong><a href="https://pi-hole.net/donate/" rel="noopener" target="_blank"><i class="fa fa-heart"></i> Donate</a></strong> if you found this useful.</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<!-- ./wrapper -->
|
||||
<script src="scripts/pi-hole/js/footer.js"></script>
|
||||
|
||||
@@ -52,4 +52,206 @@ if(!function_exists('hash_equals')) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* More safely execute a command with pihole shell script.
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* pihole_execute("-h");
|
||||
*
|
||||
* would execute command
|
||||
*
|
||||
* sudo pihole -h
|
||||
*
|
||||
* and returns output of that command as a string.
|
||||
*
|
||||
* @param $argument_string String of arguments to run pihole with.
|
||||
* @param $error_on_failure If true, a warning is raised if command execution fails. Defaults to true.
|
||||
*/
|
||||
function pihole_execute($argument_string, $error_on_failure = true) {
|
||||
$escaped = escapeshellcmd($argument_string);
|
||||
$output = null;
|
||||
$return_status = -1;
|
||||
$command = "sudo pihole " . $escaped;
|
||||
exec($command, $output, $return_status);
|
||||
if($return_status !== 0)
|
||||
{
|
||||
trigger_error("Executing {$command} failed.", E_USER_WARNING);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
function echoCustomDNSEntries()
|
||||
{
|
||||
$entries = getCustomDNSEntries();
|
||||
|
||||
$data = [];
|
||||
foreach ($entries as $entry)
|
||||
$data[] = [ $entry->domain, $entry->ip ];
|
||||
|
||||
return [ "data" => $data ];
|
||||
}
|
||||
|
||||
function getCustomDNSEntries()
|
||||
{
|
||||
global $customDNSFile;
|
||||
|
||||
$entries = [];
|
||||
|
||||
$handle = fopen($customDNSFile, "r");
|
||||
if ($handle)
|
||||
{
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$line = str_replace("\r","", $line);
|
||||
$line = str_replace("\n","", $line);
|
||||
$explodedLine = explode (" ", $line);
|
||||
|
||||
if (count($explodedLine) != 2)
|
||||
continue;
|
||||
|
||||
$data = new \stdClass();
|
||||
$data->ip = $explodedLine[0];
|
||||
$data->domain = $explodedLine[1];
|
||||
$entries[] = $data;
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
function addCustomDNSEntry($ip="", $domain="", $json_reply=true)
|
||||
{
|
||||
function error($msg)
|
||||
{
|
||||
global $json_reply;
|
||||
if($json_reply)
|
||||
return errorJsonResponse($msg);
|
||||
else {
|
||||
echo $msg."<br>";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(isset($_REQUEST['ip']))
|
||||
$ip = trim($_REQUEST['ip']);
|
||||
|
||||
if(isset($_REQUEST['domain']))
|
||||
$domain = trim($_REQUEST['domain']);
|
||||
|
||||
if (empty($ip))
|
||||
return error("IP must be set");
|
||||
|
||||
$ipType = get_ip_type($ip);
|
||||
|
||||
if (!$ipType)
|
||||
return error("IP must be valid");
|
||||
|
||||
if (empty($domain))
|
||||
return error("Domain must be set");
|
||||
|
||||
if (!is_valid_domain_name($domain))
|
||||
return error("Domain must be valid");
|
||||
|
||||
// Only check for duplicates if adding new records from the web UI (not through Teleporter)
|
||||
if(isset($_REQUEST['ip']) || isset($_REQUEST['domain']))
|
||||
{
|
||||
$existingEntries = getCustomDNSEntries();
|
||||
foreach ($existingEntries as $entry)
|
||||
if ($entry->domain == $domain && get_ip_type($entry->ip) == $ipType)
|
||||
return error("This domain already has a custom DNS entry for an IPv" . $ipType);
|
||||
}
|
||||
|
||||
// Add record
|
||||
pihole_execute("-a addcustomdns ".$ip." ".$domain);
|
||||
|
||||
return $json_reply ? successJsonResponse() : true;
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return error($ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCustomDNSEntry()
|
||||
{
|
||||
try
|
||||
{
|
||||
$ip = !empty($_REQUEST['ip']) ? $_REQUEST['ip']: "";
|
||||
$domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: "";
|
||||
|
||||
if (empty($ip))
|
||||
return errorJsonResponse("IP must be set");
|
||||
|
||||
if (empty($domain))
|
||||
return errorJsonResponse("Domain must be set");
|
||||
|
||||
$existingEntries = getCustomDNSEntries();
|
||||
|
||||
$found = false;
|
||||
foreach ($existingEntries as $entry)
|
||||
if ($entry->domain == $domain)
|
||||
if ($entry->ip == $ip) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$found)
|
||||
return errorJsonResponse("This domain/ip association does not exist");
|
||||
|
||||
pihole_execute("-a removecustomdns ".$ip." ".$domain);
|
||||
|
||||
return successJsonResponse();
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return errorJsonResponse($ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function deleteAllCustomDNSEntries()
|
||||
{
|
||||
$handle = fopen($customDNSFile, "r");
|
||||
if ($handle)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$line = str_replace("\r","", $line);
|
||||
$line = str_replace("\n","", $line);
|
||||
$explodedLine = explode (" ", $line);
|
||||
|
||||
if (count($explodedLine) != 2)
|
||||
continue;
|
||||
|
||||
$ip = $explodedLine[0];
|
||||
$domain = $explodedLine[1];
|
||||
|
||||
pihole_execute("-a removecustomdns ".$ip." ".$domain);
|
||||
}
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return errorJsonResponse($ex->getMessage());
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
return successJsonResponse();
|
||||
}
|
||||
|
||||
function successJsonResponse($message = "")
|
||||
{
|
||||
return [ "success" => true, "message" => $message ];
|
||||
}
|
||||
|
||||
function errorJsonResponse($message = "")
|
||||
{
|
||||
return [ "success" => false, "message" => $message ];
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
require "scripts/pi-hole/php/database.php";
|
||||
require_once("scripts/pi-hole/php/database.php");
|
||||
|
||||
function gravity_last_update($raw = false)
|
||||
{
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
require_once('auth.php');
|
||||
|
||||
// Authentication checks
|
||||
if (isset($_POST['token'])) {
|
||||
check_cors();
|
||||
check_csrf($_POST['token']);
|
||||
} else {
|
||||
log_and_die('Not allowed (login session invalid or expired, please relogin on the Pi-hole dashboard)!');
|
||||
if (!isset($api)) {
|
||||
if (isset($_POST['token'])) {
|
||||
check_cors();
|
||||
check_csrf($_POST['token']);
|
||||
} else {
|
||||
log_and_die('Not allowed (login session invalid or expired, please relogin on the Pi-hole dashboard)!');
|
||||
}
|
||||
}
|
||||
|
||||
$reload = false;
|
||||
@@ -39,6 +41,37 @@ function JSON_error($message = null)
|
||||
echo json_encode($response);
|
||||
}
|
||||
|
||||
function space_aware_explode($input)
|
||||
{
|
||||
$ret = array();
|
||||
$quoted = false;
|
||||
$pos = 0;
|
||||
|
||||
// Loop over input string
|
||||
for ($i = 0; $i < strlen($input); $i++)
|
||||
{
|
||||
// Get current character
|
||||
$c = $input[$i];
|
||||
|
||||
// If current character is a space (or comma) and we're outside
|
||||
// of a quoted region, we accept this character as separator
|
||||
if (($c == ' ' || $c == ',') && !$quoted) {
|
||||
$ret[] = str_replace('"', '', substr($input, $pos, $i - $pos));
|
||||
$pos = $i+1;
|
||||
}
|
||||
elseif($c == '"' && !$quoted)
|
||||
$quoted = true; // Quotation begins
|
||||
elseif($c == '"' && $quoted)
|
||||
$quoted = false; // Quotation ends here
|
||||
}
|
||||
// Get last element of the string
|
||||
if ($pos > 0) {
|
||||
$ret[] = substr($input, $pos);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if ($_POST['action'] == 'get_groups') {
|
||||
// List all available groups
|
||||
try {
|
||||
@@ -47,6 +80,8 @@ if ($_POST['action'] == 'get_groups') {
|
||||
while (($res = $query->fetchArray(SQLITE3_ASSOC)) !== false) {
|
||||
array_push($data, $res);
|
||||
}
|
||||
|
||||
header('Content-type: application/json');
|
||||
echo json_encode(array('data' => $data));
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
@@ -54,7 +89,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
} elseif ($_POST['action'] == 'add_group') {
|
||||
// Add new group
|
||||
try {
|
||||
$names = explode(' ', $_POST['name']);
|
||||
$names = space_aware_explode(trim($_POST['name']));
|
||||
$total = count($names);
|
||||
$added = 0;
|
||||
$stmt = $db->prepare('INSERT INTO "group" (name,description) VALUES (:name,:desc)');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
@@ -66,12 +103,15 @@ if ($_POST['action'] == 'get_groups') {
|
||||
|
||||
foreach ($names as $name) {
|
||||
if (!$stmt->bindValue(':name', $name, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding name: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While binding name: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " groups");
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While executing: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " groups");
|
||||
}
|
||||
$added++;
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
@@ -193,6 +233,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
array_push($data, $res);
|
||||
}
|
||||
|
||||
header('Content-type: application/json');
|
||||
echo json_encode(array('data' => $data));
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
@@ -227,6 +268,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-type: application/json');
|
||||
echo json_encode($ips);
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
@@ -234,7 +276,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
} elseif ($_POST['action'] == 'add_client') {
|
||||
// Add new client
|
||||
try {
|
||||
$ips = explode(' ', $_POST['ip']);
|
||||
$ips = explode(' ', trim($_POST['ip']));
|
||||
$total = count($ips);
|
||||
$added = 0;
|
||||
$stmt = $db->prepare('INSERT INTO client (ip,comment) VALUES (:ip,:comment)');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
@@ -251,12 +295,15 @@ if ($_POST['action'] == 'get_groups') {
|
||||
$comment = null;
|
||||
}
|
||||
if (!$stmt->bindValue(':comment', $comment, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding comment: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While binding comment: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " clients");
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While executing: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " clients");
|
||||
}
|
||||
$added++;
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
@@ -370,6 +417,8 @@ if ($_POST['action'] == 'get_groups') {
|
||||
$limit = " WHERE type = 0 OR type = 2";
|
||||
} elseif (isset($_POST["showtype"]) && $_POST["showtype"] === "black"){
|
||||
$limit = " WHERE type = 1 OR type = 3";
|
||||
} elseif (isset($_POST["type"]) && is_numeric($_POST["type"])){
|
||||
$limit = " WHERE type = " . $_POST["type"];
|
||||
}
|
||||
$query = $db->query('SELECT * FROM domainlist'.$limit);
|
||||
if (!$query) {
|
||||
@@ -391,18 +440,38 @@ if ($_POST['action'] == 'get_groups') {
|
||||
if (extension_loaded("intl") &&
|
||||
($res['type'] === ListType::whitelist ||
|
||||
$res['type'] === ListType::blacklist) ) {
|
||||
$utf8_domain = idn_to_utf8($res['domain']);
|
||||
|
||||
// Try to convert possible IDNA domain to Unicode, we try the UTS #46 standard first
|
||||
// as this is the new default, see https://sourceforge.net/p/icu/mailman/message/32980778/
|
||||
// We know that this fails for some Google domains violating the standard
|
||||
// see https://github.com/pi-hole/AdminLTE/issues/1223
|
||||
$utf8_domain = false;
|
||||
if (defined("INTL_IDNA_VARIANT_UTS46")) {
|
||||
// We have to use the option IDNA_NONTRANSITIONAL_TO_ASCII here
|
||||
// to ensure sparkasse-gießen.de is not converted into
|
||||
// sparkass-giessen.de but into xn--sparkasse-gieen-2ib.de
|
||||
// as mandated by the UTS #46 standard
|
||||
$utf8_domain = idn_to_utf8($res['domain'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||
}
|
||||
|
||||
// If conversion failed, try with the (deprecated!) IDNA 2003 variant
|
||||
// We have to check for its existance as support of this variant is
|
||||
// scheduled for removal with PHP 8.0
|
||||
// see https://wiki.php.net/rfc/deprecate-and-remove-intl_idna_variant_2003
|
||||
if ($utf8_domain === false && defined("INTL_IDNA_VARIANT_2003")) {
|
||||
$utf8_domain = idn_to_utf8($res['domain'], IDNA_DEFAULT, INTL_IDNA_VARIANT_2003);
|
||||
}
|
||||
|
||||
// Convert domain name to international form
|
||||
// if applicable and extension is available
|
||||
if($res['domain'] !== $utf8_domain)
|
||||
{
|
||||
if ($utf8_domain !== false && $res['domain'] !== $utf8_domain) {
|
||||
$res['domain'] = $utf8_domain.' ('.$res['domain'].')';
|
||||
}
|
||||
}
|
||||
array_push($data, $res);
|
||||
}
|
||||
|
||||
|
||||
header('Content-type: application/json');
|
||||
echo json_encode(array('data' => $data));
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
@@ -410,13 +479,21 @@ if ($_POST['action'] == 'get_groups') {
|
||||
} elseif ($_POST['action'] == 'add_domain') {
|
||||
// Add new domain
|
||||
try {
|
||||
$domains = explode(' ', $_POST['domain']);
|
||||
$stmt = $db->prepare('INSERT INTO domainlist (domain,type,comment) VALUES (:domain,:type,:comment)');
|
||||
$domains = explode(' ', trim($_POST['domain']));
|
||||
$total = count($domains);
|
||||
$added = 0;
|
||||
$stmt = $db->prepare('REPLACE INTO domainlist (domain,type,comment) VALUES (:domain,:type,:comment)');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$type = intval($_POST['type']);
|
||||
if (isset($_POST['type'])) {
|
||||
$type = intval($_POST['type']);
|
||||
} else if (isset($_POST['list']) && $_POST['list'] === "white") {
|
||||
$type = ListType::whitelist;
|
||||
} else if (isset($_POST['list']) && $_POST['list'] === "black") {
|
||||
$type = ListType::blacklist;
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':type', $type, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding type: ' . $db->lastErrorMsg());
|
||||
@@ -427,8 +504,22 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
foreach ($domains as $domain) {
|
||||
$input = $domain;
|
||||
// Convert domain name to IDNA ASCII form for international domains
|
||||
$domain = idn_to_ascii($domain);
|
||||
if (extension_loaded("intl")) {
|
||||
// Be prepared that this may fail and see our comments above
|
||||
// (search for "idn_to_utf8)
|
||||
$idn_domain = false;
|
||||
if (defined("INTL_IDNA_VARIANT_UTS46")) {
|
||||
$idn_domain = idn_to_ascii($domain, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||
}
|
||||
if ($idn_domain === false && defined("INTL_IDNA_VARIANT_2003")) {
|
||||
$idn_domain = idn_to_ascii($domain, IDNA_DEFAULT, INTL_IDNA_VARIANT_2003);
|
||||
}
|
||||
if($idn_domain !== false) {
|
||||
$domain = $idn_domain;
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen($_POST['type']) === 2 && $_POST['type'][1] === 'W')
|
||||
{
|
||||
@@ -442,17 +533,27 @@ if ($_POST['action'] == 'get_groups') {
|
||||
$domain = strtolower($domain);
|
||||
if(filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) === false)
|
||||
{
|
||||
throw new Exception('Domain ' . htmlentities(utf8_encode($domain)) . 'is not a valid domain.');
|
||||
// This is the case when idn_to_ascii() modified the string
|
||||
if($input !== $domain && strlen($domain) > 0)
|
||||
$errormsg = 'Domain ' . htmlentities($input) . ' (converted to "' . htmlentities(utf8_encode($domain)) . '") is not a valid domain.';
|
||||
elseif($input !== $domain)
|
||||
$errormsg = 'Domain ' . htmlentities($input) . ' is not a valid domain.';
|
||||
else
|
||||
$errormsg = 'Domain ' . htmlentities(utf8_encode($domain)) . ' is not a valid domain.';
|
||||
throw new Exception($errormsg . '<br>Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While binding domain: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While executing: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
$added++;
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
@@ -568,6 +669,48 @@ if ($_POST['action'] == 'get_groups') {
|
||||
throw new Exception('While executing domainlist statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'delete_domain_string') {
|
||||
// Delete domain identified by the domain string itself
|
||||
try {
|
||||
$stmt = $db->prepare('DELETE FROM domainlist_by_group WHERE domainlist_id=(SELECT id FROM domainlist WHERE domain=:domain AND type=:type);');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing domainlist_by_group statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':domain', $_POST['domain'], SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain to domainlist_by_group statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':type', intval($_POST['type']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding type to domainlist_by_group statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing domainlist_by_group statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$stmt = $db->prepare('DELETE FROM domainlist WHERE domain=:domain AND type=:type');
|
||||
if (!$stmt) {
|
||||
throw new Exception('While preparing domainlist statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':domain', $_POST['domain'], SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain to domainlist statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':type', intval($_POST['type']), SQLITE3_INTEGER)) {
|
||||
throw new Exception('While binding type to domainlist statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing domainlist statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
JSON_success();
|
||||
} catch (\Exception $ex) {
|
||||
@@ -596,7 +739,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
array_push($data, $res);
|
||||
}
|
||||
|
||||
|
||||
header('Content-type: application/json');
|
||||
echo json_encode(array('data' => $data));
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
@@ -604,7 +747,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
} elseif ($_POST['action'] == 'add_adlist') {
|
||||
// Add new adlist
|
||||
try {
|
||||
$addresses = explode(' ', $_POST['address']);
|
||||
$addresses = explode(' ', trim($_POST['address']));
|
||||
$total = count($addresses);
|
||||
$added = 0;
|
||||
|
||||
$stmt = $db->prepare('INSERT INTO adlist (address,comment) VALUES (:address,:comment)');
|
||||
if (!$stmt) {
|
||||
@@ -616,17 +761,21 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
foreach ($addresses as $address) {
|
||||
if(preg_match("/[^a-zA-Z0-9:\/?&%=~._-]/", $address) !== 0) {
|
||||
throw new Exception('Invalid adlist URL');
|
||||
if(preg_match("/[^a-zA-Z0-9:\/?&%=~._()-;]/", $address) !== 0) {
|
||||
throw new Exception('<strong>Invalid adlist URL ' . htmlentities($address) . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " adlists");
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':address', $address, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding address: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While binding address: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " adlists");
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: ' . $db->lastErrorMsg());
|
||||
throw new Exception('While executing: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " adlists");
|
||||
}
|
||||
$added++;
|
||||
}
|
||||
|
||||
$reload = true;
|
||||
@@ -746,5 +895,6 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
// Reload lists in pihole-FTL after having added something
|
||||
if ($reload) {
|
||||
echo shell_exec('sudo pihole restartdns reload-lists');
|
||||
$output = pihole_execute('restartdns reload-lists');
|
||||
echo implode("\n", $output);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
|
||||
require "scripts/pi-hole/php/auth.php";
|
||||
require "scripts/pi-hole/php/password.php";
|
||||
require_once "scripts/pi-hole/php/FTL.php";
|
||||
require "scripts/pi-hole/php/theme.php";
|
||||
$scriptname = basename($_SERVER['SCRIPT_FILENAME']);
|
||||
$hostname = gethostname() ? gethostname() : "";
|
||||
|
||||
check_cors();
|
||||
|
||||
@@ -45,22 +48,6 @@
|
||||
$celsius *= 1e-3;
|
||||
}
|
||||
|
||||
$kelvin = $celsius + 273.15;
|
||||
$fahrenheit = ($celsius*9./5)+32.0;
|
||||
|
||||
if(isset($setupVars['TEMPERATUREUNIT']))
|
||||
{
|
||||
$temperatureunit = $setupVars['TEMPERATUREUNIT'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$temperatureunit = "C";
|
||||
}
|
||||
// Override temperature unit setting if it is changed via Settings page
|
||||
if(isset($_POST["tempunit"]))
|
||||
{
|
||||
$temperatureunit = $_POST["tempunit"];
|
||||
}
|
||||
// Get user-defined temperature limit if set
|
||||
if(isset($setupVars['TEMPERATURE_LIMIT']))
|
||||
{
|
||||
@@ -162,16 +149,7 @@
|
||||
$FTLpid = intval(pidofFTL());
|
||||
$FTL = ($FTLpid !== 0 ? true : false);
|
||||
|
||||
$piholeFTLConfFile = "/etc/pihole/pihole-FTL.conf";
|
||||
if(is_readable($piholeFTLConfFile))
|
||||
{
|
||||
$piholeFTLConf = parse_ini_file($piholeFTLConfFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
$piholeFTLConf = array();
|
||||
}
|
||||
|
||||
$piholeFTLConf = piholeFTLConfig();
|
||||
?>
|
||||
<!doctype html>
|
||||
<!-- Pi-hole: A black hole for Internet advertisements
|
||||
@@ -183,13 +161,14 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://api.github.com; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'">
|
||||
<title>Pi-hole Admin Console</title>
|
||||
<!-- Usually browsers proactively perform domain name resolution on links that the user may choose to follow. We disable DNS prefetching here -->
|
||||
<meta http-equiv="x-dns-prefetch-control" content="off">
|
||||
<meta http-equiv="cache-control" content="max-age=60,private">
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Pi-hole<?php echo $hostname ? " - " . $hostname : "" ?></title>
|
||||
|
||||
<link rel="apple-touch-icon" href="img/favicons/apple-touch-icon.png" sizes="180x180">
|
||||
<link rel="icon" href="img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
|
||||
@@ -201,50 +180,46 @@
|
||||
<meta name="msapplication-TileImage" content="img/favicons/mstile-150x150.png">
|
||||
<meta name="theme-color" content="#367fa9">
|
||||
|
||||
<?php if ($darkmode) { ?>
|
||||
<style>
|
||||
html { background-color: #000; }
|
||||
</style>
|
||||
<?php } ?>
|
||||
<link rel="stylesheet" href="style/vendor/SourceSansPro/SourceSansPro.css">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap/css/bootstrap.min.css">
|
||||
|
||||
<link rel="stylesheet" href="style/vendor/font-awesome-5.11.2/css/all.min.css">
|
||||
|
||||
<link rel="stylesheet" href="style/vendor/dataTables.bootstrap.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/daterangepicker.css">
|
||||
|
||||
<link rel="stylesheet" href="style/vendor/font-awesome/css/all.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/datatables.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/daterangepicker.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/AdminLTE.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/skin-blue.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/animate.css">
|
||||
|
||||
<?php if (in_array($scriptname, array("groups.php", "groups-adlists.php", "groups-clients.php", "groups-domains.php"))){ ?>
|
||||
<link rel="stylesheet" href="style/vendor/animate.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap-select.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap-toggle.min.css">
|
||||
<?php } ?>
|
||||
<link rel="stylesheet" href="style/pi-hole.css">
|
||||
<link rel="stylesheet" href="style/themes/<?php echo $theme; ?>.css">
|
||||
<noscript><link rel="stylesheet" href="style/vendor/js-warn.css"></noscript>
|
||||
|
||||
<script src="scripts/vendor/jquery.min.js"></script>
|
||||
<script src="scripts/vendor/jquery-ui.min.js"></script>
|
||||
<script src="style/vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="scripts/vendor/app.min.js"></script>
|
||||
<script src="scripts/vendor/adminlte.min.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-notify.min.js"></script>
|
||||
|
||||
<?php if(in_array($scriptname, array("groups.php", "groups-clients.php", "groups-domains.php", "groups-adlists.php"))){ ?>
|
||||
<script src="style/vendor/bootstrap/js/bootstrap-select.min.js"></script>
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap/css/bootstrap-select.min.css">
|
||||
<script src="style/vendor/bootstrap/js/bootstrap-toggle.min.js"></script>
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap/css/bootstrap-toggle.min.css">
|
||||
<script src="scripts/vendor/datatables.min.js"></script>
|
||||
<script src="scripts/vendor/moment.min.js"></script>
|
||||
<?php } ?>
|
||||
|
||||
<script src="scripts/vendor/jquery.dataTables.min.js"></script>
|
||||
<script src="scripts/vendor/dataTables.bootstrap.min.js"></script>
|
||||
<script src="scripts/vendor/Chart.bundle.min.js"></script>
|
||||
<script src="scripts/vendor/Chart.min.js"></script>
|
||||
</head>
|
||||
<body class="skin-blue sidebar-mini <?php if($boxedlayout){ ?>layout-boxed<?php } ?>">
|
||||
<noscript>
|
||||
<!-- JS Warning -->
|
||||
<div>
|
||||
<input type="checkbox" id="js-hide">
|
||||
<div class="js-warn" id="js-warn-exit"><h1>JavaScript Is Disabled</h1><p>JavaScript is required for the site to function.</p>
|
||||
<p>To learn how to enable JavaScript click <a href="https://www.enable-javascript.com/" rel="noopener" target="_blank">here</a></p><label for="js-hide">Close</label>
|
||||
<body class="hold-transition sidebar-mini <?php if($boxedlayout){ ?>layout-boxed<?php } ?>">
|
||||
<noscript>
|
||||
<!-- JS Warning -->
|
||||
<div>
|
||||
<input type="checkbox" id="js-hide">
|
||||
<div class="js-warn" id="js-warn-exit"><h1>JavaScript Is Disabled</h1><p>JavaScript is required for the site to function.</p>
|
||||
<p>To learn how to enable JavaScript click <a href="https://www.enable-javascript.com/" rel="noopener" target="_blank">here</a></p><label for="js-hide">Close</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /JS Warning -->
|
||||
</noscript>
|
||||
<!-- /JS Warning -->
|
||||
</noscript>
|
||||
<?php
|
||||
if($auth) {
|
||||
echo "<div id=\"token\" hidden>$token</div>";
|
||||
@@ -252,40 +227,45 @@ if($auth) {
|
||||
?>
|
||||
|
||||
<!-- Send token to JS -->
|
||||
<div id="token" hidden><?php if($auth) echo $token; ?></div>
|
||||
<div id="enableTimer" hidden><?php if(file_exists("../custom_disable_timer")){ echo file_get_contents("../custom_disable_timer"); } ?></div>
|
||||
<div class="wrapper">
|
||||
<header class="main-header">
|
||||
<!-- Logo -->
|
||||
<a href="index.php" class="logo">
|
||||
<!-- mini logo for sidebar mini 50x50 pixels -->
|
||||
<span class="logo-mini">P<b>h</b></span>
|
||||
<span class="logo-mini">P<strong>h</strong></span>
|
||||
<!-- logo for regular state and mobile devices -->
|
||||
<span class="logo-lg">Pi-<b>hole</b></span>
|
||||
<span class="logo-lg">Pi-<strong>hole</strong></span>
|
||||
</a>
|
||||
<!-- Header Navbar: style can be found in header.less -->
|
||||
<nav class="navbar navbar-static-top">
|
||||
<!-- Sidebar toggle button-->
|
||||
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
|
||||
<a href="#" class="sidebar-toggle fa5" data-toggle="push-menu" role="button">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
</a>
|
||||
<div class="navbar-custom-menu">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<a style="pointer-events:none;">
|
||||
<span class="hidden-xs hidden-sm">hostname:</span>
|
||||
<code><?php echo gethostname(); ?></code>
|
||||
<li id="pihole-diagnosis" class="hidden">
|
||||
<a href="messages.php">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
<span class="label label-warning" id="pihole-diagnosis-count"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li<?php echo !$hostname ? ' class="hidden"' : "" ?>>
|
||||
<p class="navbar-text">
|
||||
<span class="hidden-xs hidden-sm">hostname:</span>
|
||||
<code><?php echo $hostname; ?></code>
|
||||
</p>
|
||||
</li>
|
||||
<li class="dropdown user user-menu">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="true">
|
||||
<img src="img/logo.svg" class="user-image" style="border-radius: 0" alt="Pi-hole logo" width="25" height="25">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
<img src="img/logo.svg" class="user-image" alt="Pi-hole logo" style="border-radius: 0" width="25" height="25">
|
||||
<span class="hidden-xs">Pi-hole</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu" style="right:0">
|
||||
<ul class="dropdown-menu">
|
||||
<!-- User image -->
|
||||
<li class="user-header">
|
||||
<img src="img/logo.svg" alt="User Image" style="border-color:transparent" width="90" height="90">
|
||||
<img src="img/logo.svg" alt="Pi-hole Logo" style="border: 0" width="90" height="90">
|
||||
<p>
|
||||
Open Source Ad Blocker
|
||||
<small>Designed For Raspberry Pi</small>
|
||||
@@ -293,41 +273,27 @@ if($auth) {
|
||||
</li>
|
||||
<!-- Menu Body -->
|
||||
<li class="user-body">
|
||||
<div class="col-xs-4 text-center">
|
||||
<a class="btn-link" href="https://github.com/pi-hole" rel="noopener" target="_blank">GitHub</a>
|
||||
</div>
|
||||
<div class="col-xs-4 text-center">
|
||||
<a class="btn-link" href="https://pi-hole.net/" rel="noopener" target="_blank">Website</a>
|
||||
</div>
|
||||
<div class="col-xs-4 text-center">
|
||||
<a class="btn-link" href="https://github.com/pi-hole/pi-hole/releases" rel="noopener" target="_blank">Updates</a>
|
||||
</div>
|
||||
<div class="col-xs-12 text-center" id="sessiontimer">
|
||||
<b>Session is valid for <span id="sessiontimercounter"><?php if($auth && strlen($pwhash) > 0){echo $maxlifetime;}else{echo "0";} ?></span></b>
|
||||
<div class="row">
|
||||
<div class="col-xs-4 text-center">
|
||||
<a class="btn-link" href="https://github.com/pi-hole" rel="noopener" target="_blank">GitHub</a>
|
||||
</div>
|
||||
<div class="col-xs-4 text-center">
|
||||
<a class="btn-link" href="https://pi-hole.net/" rel="noopener" target="_blank">Website</a>
|
||||
</div>
|
||||
<div class="col-xs-4 text-center">
|
||||
<a class="btn-link" href="https://github.com/pi-hole/pi-hole/releases" rel="noopener" target="_blank">Updates</a>
|
||||
</div>
|
||||
<div id="sessiontimer" class="col-xs-12 text-center">
|
||||
<strong>Session is valid for <span id="sessiontimercounter"><?php if($auth && strlen($pwhash) > 0){echo $maxlifetime;}else{echo "0";} ?></span></strong>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<!-- Menu Footer -->
|
||||
<li class="user-footer">
|
||||
<!-- Version Infos -->
|
||||
<?php /*
|
||||
<div class="<?php if(!isset($core_commit) && !isset($web_commit)) { ?>hidden-md <?php } ?>hidden-lg">
|
||||
<b>Pi-hole Version </b> <?php
|
||||
echo $core_current;
|
||||
if(isset($core_commit)) { echo "<br>(".$core_branch.", ".$core_commit.")"; }
|
||||
if($core_update){ ?> <a class="alert-link lookatme btn-link" href="https://github.com/pi-hole/pi-hole/releases" rel="noopener" target="_blank" style="background:none">(Update available!)</a><?php } ?><br>
|
||||
<b>Web Interface Version </b><?php
|
||||
echo $web_current;
|
||||
if(isset($web_commit)) { echo "<br>(".$web_branch.", ".$web_commit.")"; }
|
||||
if($web_update){ ?> <a class="alert-link lookatme btn-link" href="https://github.com/pi-hole/AdminLTE/releases" rel="noopener" target="_blank" style="background:none">(Update available!)</a><?php } ?><br>
|
||||
<b>FTL Version </b> <?php
|
||||
echo $FTL_current;
|
||||
if($FTL_update){ ?> <a class="alert-link lookatme btn-link" href="https://github.com/pi-hole/FTL/releases" rel="noopener" target="_blank" style="background:none">(Update available!)</a><?php } ?><br><br>
|
||||
</div>
|
||||
*/ ?>
|
||||
<!-- PayPal -->
|
||||
<div class="text-center">
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY" rel="noopener" target="_blank" style="background:none">
|
||||
<img src="img/donate.gif" alt="Donate">
|
||||
<a href="https://pi-hole.net/donate/" rel="noopener" target="_blank">
|
||||
<img src="img/donate.gif" alt="Donate" width="147" height="47">
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
@@ -344,27 +310,32 @@ if($auth) {
|
||||
<!-- Sidebar user panel -->
|
||||
<div class="user-panel">
|
||||
<div class="pull-left image">
|
||||
<img src="img/logo.svg" class="img-responsive" alt="Pi-hole logo" style="display: table; table-layout: fixed; height: 67px;">
|
||||
<img src="img/logo.svg" alt="Pi-hole logo" width="45" height="67" style="height: 67px;">
|
||||
</div>
|
||||
<div class="pull-left info">
|
||||
<p>Status</p>
|
||||
<?php
|
||||
$pistatus = exec('sudo pihole status web');
|
||||
if ($pistatus == "1") {
|
||||
echo '<a id="status"><i class="fa fa-circle text-green-light"></i> Active</a>';
|
||||
} elseif ($pistatus == "0") {
|
||||
echo '<a id="status"><i class="fa fa-circle text-red"></i> Offline</a>';
|
||||
} elseif ($pistatus == "-1") {
|
||||
echo '<a id="status"><i class="fa fa-circle text-red"></i> DNS service not running</a>';
|
||||
$pistatus = pihole_execute('status web');
|
||||
if (isset($pistatus[0])) {
|
||||
$pistatus = $pistatus[0];
|
||||
} else {
|
||||
echo '<a id="status"><i class="fa fa-circle text-orange"></i> Unknown</a>';
|
||||
$pistatus = null;
|
||||
}
|
||||
if ($pistatus === "1") {
|
||||
echo '<span id="status"><i class="fa fa-circle text-green-light"></i> Active</span>';
|
||||
} elseif ($pistatus === "0") {
|
||||
echo '<span id="status"><i class="fa fa-circle text-red"></i> Offline</span>';
|
||||
} elseif ($pistatus === "-1") {
|
||||
echo '<span id="status"><i class="fa fa-circle text-red"></i> DNS service not running</span>';
|
||||
} else {
|
||||
echo '<span id="status"><i class="fa fa-circle text-orange"></i> Unknown</span>';
|
||||
}
|
||||
|
||||
// CPU Temp
|
||||
if($FTL)
|
||||
{
|
||||
if ($celsius >= -273.15) {
|
||||
echo "<a id=\"temperature\"><i class=\"fa fa-fire ";
|
||||
echo "<span id=\"temperature\"><i class=\"fa fa-fire ";
|
||||
if ($celsius > $temperaturelimit) {
|
||||
echo "text-red";
|
||||
}
|
||||
@@ -372,30 +343,17 @@ if($auth) {
|
||||
{
|
||||
echo "text-vivid-blue";
|
||||
}
|
||||
echo "\"></i> Temp: ";
|
||||
if($temperatureunit === "F")
|
||||
{
|
||||
echo round($fahrenheit,1) . " °F";
|
||||
}
|
||||
elseif($temperatureunit === "K")
|
||||
{
|
||||
echo round($kelvin,1) . " K";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo round($celsius,1) . " °C";
|
||||
}
|
||||
echo "</a>";
|
||||
?>"\"></i> Temp: <span id="rawtemp" hidden><?php echo $celsius;?></span><span id="tempdisplay"></span><?php
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo '<a id=\"temperature\"><i class="fa fa-circle text-red"></i> FTL offline</a>';
|
||||
echo '<span id=\"temperature\"><i class="fa fa-circle text-red"></i> FTL offline</span>';
|
||||
}
|
||||
?>
|
||||
<br/>
|
||||
<?php
|
||||
echo "<a title=\"Detected $nproc cores\"><i class=\"fa fa-circle ";
|
||||
echo "<span title=\"Detected $nproc cores\"><i class=\"fa fa-circle ";
|
||||
if ($loaddata[0] > $nproc) {
|
||||
echo "text-red";
|
||||
}
|
||||
@@ -403,11 +361,11 @@ if($auth) {
|
||||
{
|
||||
echo "text-green-light";
|
||||
}
|
||||
echo "\"></i> Load: " . $loaddata[0] . " " . $loaddata[1] . " ". $loaddata[2] . "</a>";
|
||||
echo "\"></i> Load: " . $loaddata[0] . " " . $loaddata[1] . " ". $loaddata[2] . "</span>";
|
||||
?>
|
||||
<br/>
|
||||
<?php
|
||||
echo "<a><i class=\"fa fa-circle ";
|
||||
echo "<span><i class=\"fa fa-circle ";
|
||||
if ($memory_usage > 0.75 || $memory_usage < 0.0) {
|
||||
echo "text-red";
|
||||
}
|
||||
@@ -417,11 +375,11 @@ if($auth) {
|
||||
}
|
||||
if($memory_usage > 0.0)
|
||||
{
|
||||
echo "\"></i> Memory usage: " . sprintf("%.1f",100.0*$memory_usage) . " %</a>";
|
||||
echo "\"></i> Memory usage: " . sprintf("%.1f",100.0*$memory_usage) . " %</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "\"></i> Memory usage: N/A</a>";
|
||||
echo "\"></i> Memory usage: N/A</span>";
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
@@ -444,8 +402,8 @@ if($auth) {
|
||||
$scriptname = "login";
|
||||
}
|
||||
?>
|
||||
<ul class="sidebar-menu">
|
||||
<li class="header">MAIN NAVIGATION</li>
|
||||
<ul class="sidebar-menu" data-widget="tree">
|
||||
<li class="header text-uppercase">Main navigation</li>
|
||||
<!-- Home Page -->
|
||||
<li<?php if($scriptname === "index.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="index.php">
|
||||
@@ -461,25 +419,25 @@ if($auth) {
|
||||
</li>
|
||||
<li class="treeview<?php if($scriptname === "db_queries.php" || $scriptname === "db_lists.php" || $scriptname === "db_graph.php"){ ?> active<?php } ?>">
|
||||
<a href="#">
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right" style="padding-right: 5px;"></i>
|
||||
</span>
|
||||
<i class="fa fa-clock"></i> <span>Long term data</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li<?php if($scriptname === "db_graph.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="db_graph.php">
|
||||
<i class="fa fa-file-alt"></i> <span>Graphics</span>
|
||||
<i class="fa fa-file-alt"></i> Graphics
|
||||
</a>
|
||||
</li>
|
||||
<li<?php if($scriptname === "db_queries.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="db_queries.php">
|
||||
<i class="fa fa-file-alt"></i> <span>Query Log</span>
|
||||
<i class="fa fa-file-alt"></i> Query Log
|
||||
</a>
|
||||
</li>
|
||||
<li<?php if($scriptname === "db_lists.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="db_lists.php">
|
||||
<i class="fa fa-file-alt"></i> <span>Top Lists</span>
|
||||
<i class="fa fa-file-alt"></i> Top Lists
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -496,64 +454,70 @@ if($auth) {
|
||||
<i class="fa fa-ban"></i> <span>Blacklist</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Local DNS Records -->
|
||||
<li<?php if($scriptname === "dns_records.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="dns_records.php">
|
||||
<i class="fa fa-address-book"></i> <span>Local DNS Records</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Group Management -->
|
||||
<li class="treeview <?php if(in_array($scriptname, array("groups.php", "groups-clients.php", "groups-domains.php", "groups-adlists.php"))){ ?>active<?php } ?>">
|
||||
<li class="treeview<?php if (in_array($scriptname, array("groups.php", "groups-adlists.php", "groups-clients.php", "groups-domains.php"))){ ?> active<?php } ?>">
|
||||
<a href="#">
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right" style="padding-right: 5px;"></i>
|
||||
</span>
|
||||
<i class="fa fa-users-cog"></i> <span>Group Management</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li<?php if($scriptname === "groups.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="groups.php">
|
||||
<i class="fa fa-user-friends"></i> <span>Groups</span>
|
||||
<i class="fa fa-user-friends"></i> Groups
|
||||
</a>
|
||||
</li>
|
||||
<li<?php if($scriptname === "groups-clients.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="groups-clients.php">
|
||||
<i class="fa fa-laptop"></i> <span>Clients</span>
|
||||
<i class="fa fa-laptop"></i> Clients
|
||||
</a>
|
||||
</li>
|
||||
<li<?php if($scriptname === "groups-domains.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="groups-domains.php">
|
||||
<i class="fa fa-list"></i> <span>Domains</span>
|
||||
<i class="fa fa-list"></i> Domains
|
||||
</a>
|
||||
</li>
|
||||
<li<?php if($scriptname === "groups-adlists.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="groups-adlists.php">
|
||||
<i class="fa fa-shield-alt"></i> <span>Adlists</span>
|
||||
<i class="fa fa-shield-alt"></i> Adlists
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!-- Toggle -->
|
||||
<li id="pihole-disable" class="treeview"<?php if ($pistatus == "0") { ?> hidden="true"<?php } ?>>
|
||||
<li id="pihole-disable" class="treeview"<?php if ($pistatus == "0") { ?> hidden<?php } ?>>
|
||||
<a href="#">
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right" style="padding-right: 5px;"></i>
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
<i class="fa fa-stop"></i> <span>Disable <span id="flip-status-disable"></span></span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li>
|
||||
<a href="#" id="pihole-disable-permanently">
|
||||
<i class="fa fa-stop"></i> <span>Permanently</span>
|
||||
<a href="#" id="pihole-disable-indefinitely">
|
||||
<i class="fa fa-stop"></i> Indefinitely
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="pihole-disable-10s">
|
||||
<i class="fa fa-clock"></i> <span>For 10 seconds</span>
|
||||
<i class="fa fa-clock"></i> For 10 seconds
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="pihole-disable-30s">
|
||||
<i class="fa fa-clock"></i> <span>For 30 seconds</span>
|
||||
<i class="fa fa-clock"></i> For 30 seconds
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="pihole-disable-5m">
|
||||
<i class="fa fas fa-clock"></i> <span>For 5 minutes</span>
|
||||
<i class="fa fas fa-clock"></i> For 5 minutes
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
@@ -564,74 +528,79 @@ if($auth) {
|
||||
</ul>
|
||||
<!-- <a href="#" id="flip-status"><i class="fa fa-stop"></i> <span>Disable</span></a> -->
|
||||
</li>
|
||||
<li id="pihole-enable" class="treeview"<?php if ($pistatus == "1") { ?> hidden="true"<?php } ?>>
|
||||
<a href="#"><i class="fa fa-play"></i> <span id="enableLabel">Enable <span id="flip-status-enable"></span></span></a>
|
||||
<li id="pihole-enable" class="treeview"<?php if ($pistatus == "1") { ?> hidden<?php } ?>>
|
||||
<a href="#">
|
||||
<i class="fa fa-play"></i>
|
||||
<span id="enableLabel">Enable
|
||||
<span id="flip-status-enable"></span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Tools -->
|
||||
<li class="treeview <?php if(in_array($scriptname, array("gravity.php", "queryads.php", "auditlog.php", "taillog.php", "taillog-FTL.php", "debug.php"))){ ?>active<?php } ?>">
|
||||
<li class="treeview<?php if (in_array($scriptname, array("messages.php", "gravity.php", "queryads.php", "auditlog.php", "taillog.php", "taillog-FTL.php", "debug.php", "network.php"))){ ?> active<?php } ?>">
|
||||
<a href="#">
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right" style="padding-right: 5px;"></i>
|
||||
</span>
|
||||
<i class="fa fa-folder"></i> <span>Tools</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<!-- Pi-hole diagnosis -->
|
||||
<li<?php if($scriptname === "messages.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="messages.php">
|
||||
<i class="fa fa-stethoscope"></i> Pi-hole diagnosis
|
||||
</a>
|
||||
</li>
|
||||
<!-- Run gravity.sh -->
|
||||
<li<?php if($scriptname === "gravity.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="gravity.php">
|
||||
<i class="fa fa-arrow-circle-down"></i> <span>Update Gravity</span>
|
||||
<i class="fa fa-arrow-circle-down"></i> Update Gravity
|
||||
</a>
|
||||
</li>
|
||||
<!-- Query Lists -->
|
||||
<li<?php if($scriptname === "queryads.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="queryads.php">
|
||||
<i class="fa fa-search"></i> <span>Query Lists</span>
|
||||
<i class="fa fa-search"></i> Query Lists
|
||||
</a>
|
||||
</li>
|
||||
<!-- Audit log -->
|
||||
<li<?php if($scriptname === "auditlog.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="auditlog.php">
|
||||
<i class="fa fa-balance-scale"></i> <span>Audit log</span>
|
||||
<i class="fa fa-balance-scale"></i> Audit log
|
||||
</a>
|
||||
</li>
|
||||
<!-- Tail pihole.log -->
|
||||
<li<?php if($scriptname === "taillog.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="taillog.php">
|
||||
<i class="fa fa-list-ul"></i> <span>Tail pihole.log</span>
|
||||
<i class="fa fa-list-ul"></i> Tail pihole.log
|
||||
</a>
|
||||
</li>
|
||||
<!-- Tail pihole-FTL.log -->
|
||||
<li<?php if($scriptname === "taillog-FTL.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="taillog-FTL.php">
|
||||
<i class="fa fa-list-ul"></i> <span>Tail pihole-FTL.log</span>
|
||||
<i class="fa fa-list-ul"></i> Tail pihole-FTL.log
|
||||
</a>
|
||||
</li>
|
||||
<!-- Generate debug log -->
|
||||
<li<?php if($scriptname === "debug.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="debug.php">
|
||||
<i class="fa fa-ambulance"></i> <span>Generate debug log</span>
|
||||
<i class="fa fa-ambulance"></i> Generate debug log
|
||||
</a>
|
||||
</li>
|
||||
<!-- Network -->
|
||||
<li<?php if($scriptname === "network.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="network.php">
|
||||
<i class="fa fa-network-wired"></i> Network
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!-- Network -->
|
||||
<li<?php if($scriptname === "network.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="network.php">
|
||||
<i class="fa fa-network-wired"></i> <span>Network</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Settings -->
|
||||
<li<?php if($scriptname === "settings.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="settings.php">
|
||||
<i class="fa fa-cogs"></i> <span>Settings</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Custom DNS -->
|
||||
<li<?php if($scriptname === "custom_dns.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="custom_dns.php">
|
||||
<i class="fa fa-address-book"></i> <span>Custom DNS</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Logout -->
|
||||
<?php
|
||||
// Show Logout button if $auth is set and authorization is required
|
||||
@@ -649,24 +618,22 @@ if($auth) {
|
||||
if(strlen($pwhash) > 0 && !$auth) { ?>
|
||||
<li<?php if($scriptname === "login"){ ?> class="active"<?php } ?>>
|
||||
<a href="index.php?login">
|
||||
<i class="fa far fa-user"></i> <span>Login</span>
|
||||
<i class="fa fa-user"></i> <span>Login</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<!-- Donate -->
|
||||
<li>
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY" rel="noopener" target="_blank">
|
||||
<i class="fa-paypal-icon fab fa-paypal"></i> <span>Donate</span>
|
||||
<a href="https://pi-hole.net/donate/" rel="noopener" target="_blank">
|
||||
<i class="fab fa-paypal"></i> <span>Donate</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php if($auth){ ?>
|
||||
<!-- Help -->
|
||||
<li<?php if($scriptname === "help.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="help.php">
|
||||
<i class="fa fa-question-circle"></i> <span>Help</span>
|
||||
<!-- Docs -->
|
||||
<li>
|
||||
<a href="https://docs.pi-hole.net/" rel="noopener" target="_blank">
|
||||
<i class="fa fa-question-circle"></i> <span>Documentation</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</section>
|
||||
<!-- /.sidebar -->
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="text-center">
|
||||
<img src="img/logo.svg" alt="" style="width: <?php if ($boxedlayout) { ?>50%<?php } else { ?>30%<?php } ?>;">
|
||||
<img src="img/logo.svg" alt="Pi-hole logo" style="width: <?php if ($boxedlayout) { ?>50%<?php } else { ?>30%<?php } ?>;">
|
||||
</div>
|
||||
<br>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<div class="panel-body">
|
||||
<form action="" id="loginform" method="post">
|
||||
<div class="form-group has-feedback <?php if ($wrongpassword) { ?>has-error<?php } ?> ">
|
||||
<div class="form-group has-feedback <?php if ($wrongpassword) { ?>has-error<?php } ?>">
|
||||
<input type="password" id="loginpw" name="pw" class="form-control" placeholder="Password" autofocus>
|
||||
<span class="fa fa-key form-control-feedback"></span>
|
||||
</div>
|
||||
@@ -38,10 +38,13 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<div class="form-group pull-left">
|
||||
<div class="checkbox pull-right"><label><input type="checkbox" id="logincookie" name="persistentlogin">Remember me for 7 days</label></div>
|
||||
<div class="pull-right">
|
||||
<div>
|
||||
<input type="checkbox" id="logincookie" name="persistentlogin">
|
||||
<label for="logincookie">Remember me for 7 days</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary pull-right"><i class="glyphicon glyphicon-log-in"></i> Log in</button>
|
||||
<button type="submit" class="btn btn-primary pull-right"><i class="fas fa-sign-in-alt"></i> Log in</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
if(isset($_GET["logout"]))
|
||||
{
|
||||
session_unset();
|
||||
setcookie('persistentlogin', '');
|
||||
setcookie('persistentlogin', '', 1);
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
@@ -52,7 +52,7 @@
|
||||
{
|
||||
// Invalid cookie
|
||||
$auth = false;
|
||||
setcookie('persistentlogin', '');
|
||||
setcookie('persistentlogin', '', 1);
|
||||
}
|
||||
}
|
||||
// Compare doubly hashes password input with saved hash
|
||||
|
||||
@@ -79,16 +79,10 @@ function readStaticLeasesFile($origin_file="/etc/dnsmasq.d/04-pihole-static-dhcp
|
||||
{
|
||||
global $dhcp_static_leases;
|
||||
$dhcp_static_leases = array();
|
||||
try
|
||||
{
|
||||
$dhcpstatic = @fopen($origin_file, 'r');
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
echo "Warning: Failed to read ".$origin_file.", this is not an error";
|
||||
if(!file_exists($origin_file) || !is_readable($origin_file))
|
||||
return false;
|
||||
}
|
||||
|
||||
$dhcpstatic = @fopen($origin_file, 'r');
|
||||
if(!is_resource($dhcpstatic))
|
||||
return false;
|
||||
|
||||
@@ -154,16 +148,16 @@ function readDNSserversList()
|
||||
$line = explode(';', $line);
|
||||
$name = $line[0];
|
||||
$values = [];
|
||||
if (!empty($line[1])) {
|
||||
if (!empty($line[1]) && validIP($line[1])) {
|
||||
$values["v4_1"] = $line[1];
|
||||
}
|
||||
if (!empty($line[2])) {
|
||||
if (!empty($line[2]) && validIP($line[2])) {
|
||||
$values["v4_2"] = $line[2];
|
||||
}
|
||||
if (!empty($line[3])) {
|
||||
if (!empty($line[3]) && validIP($line[3])) {
|
||||
$values["v6_1"] = $line[3];
|
||||
}
|
||||
if (!empty($line[4])) {
|
||||
if (!empty($line[4]) && validIP($line[4])) {
|
||||
$values["v6_2"] = $line[4];
|
||||
}
|
||||
$list[$name] = $values;
|
||||
@@ -208,7 +202,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
|
||||
// Test if this lease is already included
|
||||
readStaticLeasesFile();
|
||||
|
||||
|
||||
foreach($dhcp_static_leases as $lease) {
|
||||
if($lease["hwaddr"] === $mac)
|
||||
{
|
||||
@@ -224,7 +218,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
}
|
||||
}
|
||||
|
||||
exec("sudo pihole -a addstaticdhcp ".$mac." ".$ip." ".$hostname);
|
||||
pihole_execute("-a addstaticdhcp ".$mac." ".$ip." ".$hostname);
|
||||
$success .= "A new static address has been added";
|
||||
return true;
|
||||
} catch(Exception $exception) {
|
||||
@@ -268,10 +262,10 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
if(array_key_exists("custom".$i,$_POST))
|
||||
{
|
||||
$exploded = explode("#", $_POST["custom".$i."val"], 2);
|
||||
$IP = $exploded[0];
|
||||
$IP = trim($exploded[0]);
|
||||
if(count($exploded) > 1)
|
||||
{
|
||||
$port = $exploded[1];
|
||||
$port = trim($exploded[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -332,22 +326,25 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
// Check if Conditional Forwarding is requested
|
||||
if(isset($_POST["conditionalForwarding"]))
|
||||
{
|
||||
$conditionalForwardingIP = trim($_POST["conditionalForwardingIP"]);
|
||||
$conditionalForwardingDomain = trim($_POST["conditionalForwardingDomain"]);
|
||||
|
||||
// Validate conditional forwarding IP
|
||||
if (!validIP($_POST["conditionalForwardingIP"]))
|
||||
if (!validIP($conditionalForwardingIP))
|
||||
{
|
||||
$error .= "Conditional forwarding IP (".htmlspecialchars($_POST["conditionalForwardingIP"]).") is invalid!<br>";
|
||||
$error .= "Conditional forwarding IP (".htmlspecialchars($conditionalForwardingIP).") is invalid!<br>";
|
||||
}
|
||||
|
||||
// Validate conditional forwarding domain name
|
||||
if(!validDomain($_POST["conditionalForwardingDomain"]))
|
||||
if(!validDomain($conditionalForwardingDomain))
|
||||
{
|
||||
$error .= "Conditional forwarding domain name (".htmlspecialchars($_POST["conditionalForwardingDomain"]).") is invalid!<br>";
|
||||
$error .= "Conditional forwarding domain name (".htmlspecialchars($conditionalForwardingDomain).") is invalid!<br>";
|
||||
}
|
||||
if(!$error)
|
||||
{
|
||||
$addressArray = explode(".", $_POST["conditionalForwardingIP"]);
|
||||
$addressArray = explode(".", $conditionalForwardingIP);
|
||||
$reverseAddress = $addressArray[2].".".$addressArray[1].".".$addressArray[0].".in-addr.arpa";
|
||||
$extra .= " conditional_forwarding ".$_POST["conditionalForwardingIP"]." ".$_POST["conditionalForwardingDomain"]." $reverseAddress";
|
||||
$extra .= " conditional_forwarding ".$conditionalForwardingIP." ".$conditionalForwardingDomain." $reverseAddress";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,15 +369,23 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
// Fallback
|
||||
$DNSinterface = "local";
|
||||
}
|
||||
exec("sudo pihole -a -i ".$DNSinterface." -web");
|
||||
pihole_execute("-a -i ".$DNSinterface." -web");
|
||||
|
||||
// If there has been no error we can save the new DNS server IPs
|
||||
if(!strlen($error))
|
||||
{
|
||||
$IPs = implode (",", $DNSservers);
|
||||
$return = exec("sudo pihole -a setdns \"".$IPs."\" ".$extra);
|
||||
$success .= htmlspecialchars($return)."<br>";
|
||||
$success .= "The DNS settings have been updated (using ".$DNSservercount." DNS servers)";
|
||||
$return = pihole_execute("-a setdns \"".$IPs."\" ".$extra);
|
||||
if(!empty($return))
|
||||
{
|
||||
$success .= htmlspecialchars(end($return))."<br>";
|
||||
$success .= "The DNS settings have been updated (using ".$DNSservercount." DNS servers)";
|
||||
}
|
||||
else
|
||||
{
|
||||
$success .= "Updating DNS settings failed. Result:";
|
||||
$success .= implode($return);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -394,17 +399,17 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
|
||||
if($_POST["action"] === "Disable")
|
||||
{
|
||||
exec("sudo pihole -l off");
|
||||
pihole_execute("-l off");
|
||||
$success .= "Logging has been disabled and logs have been flushed";
|
||||
}
|
||||
elseif($_POST["action"] === "Disable-noflush")
|
||||
{
|
||||
exec("sudo pihole -l off noflush");
|
||||
pihole_execute("-l off noflush");
|
||||
$success .= "Logging has been disabled, your logs have <strong>not</strong> been flushed";
|
||||
}
|
||||
else
|
||||
{
|
||||
exec("sudo pihole -l on");
|
||||
pihole_execute("-l on");
|
||||
$success .= "Logging has been enabled";
|
||||
}
|
||||
|
||||
@@ -461,8 +466,8 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
if(!strlen($error))
|
||||
{
|
||||
// All entries are okay
|
||||
exec("sudo pihole -a setexcludedomains ".$domainlist);
|
||||
exec("sudo pihole -a setexcludeclients ".$clientlist);
|
||||
pihole_execute("-a setexcludedomains ".$domainlist);
|
||||
pihole_execute("-a setexcludeclients ".$clientlist);
|
||||
$success .= "The API settings have been updated<br>";
|
||||
}
|
||||
else
|
||||
@@ -473,7 +478,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
// Set query log options
|
||||
if(isset($_POST["querylog-permitted"]) && isset($_POST["querylog-blocked"]))
|
||||
{
|
||||
exec("sudo pihole -a setquerylog all");
|
||||
pihole_execute("-a setquerylog all");
|
||||
if(!isset($_POST["privacyMode"]))
|
||||
{
|
||||
$success .= "All entries will be shown in Query Log";
|
||||
@@ -485,7 +490,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
}
|
||||
elseif(isset($_POST["querylog-permitted"]))
|
||||
{
|
||||
exec("sudo pihole -a setquerylog permittedonly");
|
||||
pihole_execute("-a setquerylog permittedonly");
|
||||
if(!isset($_POST["privacyMode"]))
|
||||
{
|
||||
$success .= "Only permitted will be shown in Query Log";
|
||||
@@ -497,41 +502,29 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
}
|
||||
elseif(isset($_POST["querylog-blocked"]))
|
||||
{
|
||||
exec("sudo pihole -a setquerylog blockedonly");
|
||||
pihole_execute("-a setquerylog blockedonly");
|
||||
$success .= "Only blocked entries will be shown in Query Log";
|
||||
}
|
||||
else
|
||||
{
|
||||
exec("sudo pihole -a setquerylog nothing");
|
||||
pihole_execute("-a setquerylog nothing");
|
||||
$success .= "No entries will be shown in Query Log";
|
||||
}
|
||||
|
||||
|
||||
if(isset($_POST["privacyMode"]))
|
||||
{
|
||||
exec("sudo pihole -a privacymode true");
|
||||
pihole_execute("-a privacymode true");
|
||||
$success .= " (privacy mode enabled)";
|
||||
}
|
||||
else
|
||||
{
|
||||
exec("sudo pihole -a privacymode false");
|
||||
pihole_execute("-a privacymode false");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "webUI":
|
||||
if($_POST["tempunit"] == "F")
|
||||
{
|
||||
exec('sudo pihole -a -f');
|
||||
}
|
||||
elseif($_POST["tempunit"] == "K")
|
||||
{
|
||||
exec('sudo pihole -a -k');
|
||||
}
|
||||
else
|
||||
{
|
||||
exec('sudo pihole -a -c');
|
||||
}
|
||||
$adminemail = trim($_POST["adminemail"]);
|
||||
if(strlen($adminemail) == 0 || !isset($adminemail))
|
||||
{
|
||||
@@ -543,36 +536,42 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
}
|
||||
else
|
||||
{
|
||||
exec('sudo pihole -a -e \''.$adminemail.'\'');
|
||||
pihole_execute('-a -e \''.$adminemail.'\'');
|
||||
}
|
||||
if(isset($_POST["boxedlayout"]))
|
||||
{
|
||||
exec('sudo pihole -a layout boxed');
|
||||
pihole_execute('-a layout boxed');
|
||||
}
|
||||
else
|
||||
{
|
||||
exec('sudo pihole -a layout traditional');
|
||||
pihole_execute('-a layout traditional');
|
||||
}
|
||||
if(isset($_POST["webtheme"]))
|
||||
{
|
||||
global $available_themes;
|
||||
if(array_key_exists($_POST["webtheme"], $available_themes))
|
||||
exec('sudo pihole -a theme '.$_POST["webtheme"]);
|
||||
}
|
||||
$success .= "The webUI settings have been updated";
|
||||
break;
|
||||
|
||||
case "poweroff":
|
||||
exec("sudo pihole -a poweroff");
|
||||
pihole_execute("-a poweroff");
|
||||
$success = "The system will poweroff in 5 seconds...";
|
||||
break;
|
||||
|
||||
case "reboot":
|
||||
exec("sudo pihole -a reboot");
|
||||
pihole_execute("-a reboot");
|
||||
$success = "The system will reboot in 5 seconds...";
|
||||
break;
|
||||
|
||||
case "restartdns":
|
||||
exec("sudo pihole -a restartdns");
|
||||
pihole_execute("-a restartdns");
|
||||
$success = "The DNS server has been restarted";
|
||||
break;
|
||||
|
||||
case "flushlogs":
|
||||
exec("sudo pihole -f");
|
||||
pihole_execute("-f");
|
||||
$success = "The Pi-hole log file has been flushed";
|
||||
break;
|
||||
|
||||
@@ -580,9 +579,9 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
|
||||
if(isset($_POST["addstatic"]))
|
||||
{
|
||||
$mac = $_POST["AddMAC"];
|
||||
$ip = $_POST["AddIP"];
|
||||
$hostname = $_POST["AddHostname"];
|
||||
$mac = trim($_POST["AddMAC"]);
|
||||
$ip = trim($_POST["AddIP"]);
|
||||
$hostname = trim($_POST["AddHostname"]);
|
||||
|
||||
addStaticDHCPLease($mac, $ip, $hostname);
|
||||
break;
|
||||
@@ -599,7 +598,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
|
||||
if(!strlen($error))
|
||||
{
|
||||
exec("sudo pihole -a removestaticdhcp ".$mac);
|
||||
pihole_execute("-a removestaticdhcp ".$mac);
|
||||
$success .= "The static address with MAC address ".htmlspecialchars($mac)." has been removed";
|
||||
}
|
||||
break;
|
||||
@@ -666,13 +665,13 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
|
||||
if(!strlen($error))
|
||||
{
|
||||
exec("sudo pihole -a enabledhcp ".$from." ".$to." ".$router." ".$leasetime." ".$domain." ".$ipv6." ".$rapidcommit);
|
||||
pihole_execute("-a enabledhcp ".$from." ".$to." ".$router." ".$leasetime." ".$domain." ".$ipv6." ".$rapidcommit);
|
||||
$success .= "The DHCP server has been activated ".htmlspecialchars($type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
exec("sudo pihole -a disabledhcp");
|
||||
pihole_execute("-a disabledhcp");
|
||||
$success = "The DHCP server has been deactivated";
|
||||
}
|
||||
|
||||
@@ -690,11 +689,11 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
}
|
||||
|
||||
// Store privacy level
|
||||
exec("sudo pihole -a privacylevel ".$level);
|
||||
pihole_execute("-a privacylevel ".$level);
|
||||
|
||||
if($privacylevel > $level)
|
||||
{
|
||||
exec("sudo pihole -a restartdns");
|
||||
pihole_execute("-a restartdns");
|
||||
$success .= "The privacy level has been decreased and the DNS resolver has been restarted";
|
||||
}
|
||||
elseif($privacylevel < $level)
|
||||
@@ -713,7 +712,7 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
break;
|
||||
// Flush network table
|
||||
case "flusharp":
|
||||
exec("sudo pihole arpflush quiet", $output);
|
||||
pihole_execute("arpflush quiet", $output);
|
||||
$error = implode("<br>", $output);
|
||||
if(strlen($error) == 0)
|
||||
{
|
||||
|
||||
@@ -40,11 +40,11 @@ function archive_add_table($name, $table, $type=-1)
|
||||
|
||||
if($type > -1)
|
||||
{
|
||||
$querystr = "SELECT * FROM $table WHERE type = $type;";
|
||||
$querystr = "SELECT * FROM \"$table\" WHERE type = $type;";
|
||||
}
|
||||
else
|
||||
{
|
||||
$querystr = "SELECT * FROM $table;";
|
||||
$querystr = "SELECT * FROM \"$table\";";
|
||||
}
|
||||
$results = $db->query($querystr);
|
||||
|
||||
@@ -87,31 +87,58 @@ function archive_restore_table($file, $table, $flush=false)
|
||||
// Flush table if requested, only flush each table once
|
||||
if($flush && !in_array($table, $flushed_tables))
|
||||
{
|
||||
$db->exec("DELETE FROM ".$table);
|
||||
$db->exec("DELETE FROM \"".$table."\"");
|
||||
array_push($flushed_tables, $table);
|
||||
}
|
||||
|
||||
// Prepare field name for domain/address depending on the table we restore to
|
||||
// Prepare fields depending on the table we restore to
|
||||
if($table === "adlist")
|
||||
{
|
||||
$sql = "INSERT OR IGNORE INTO adlist";
|
||||
$sql .= " (id,address,enabled,date_added,comment)";
|
||||
$sql .= " VALUES (:id,:address,:enabled,:date_added,:comment);";
|
||||
$field = "address";
|
||||
}
|
||||
elseif($table === "domain_audit")
|
||||
{
|
||||
$sql = "INSERT OR IGNORE INTO domain_audit";
|
||||
$sql .= " (id,domain,date_added)";
|
||||
$sql .= " VALUES (:id,:domain,:date_added);";
|
||||
$field = "domain";
|
||||
}
|
||||
elseif($table === "domainlist")
|
||||
{
|
||||
$sql = "INSERT OR IGNORE INTO domainlist";
|
||||
$sql .= " (id,domain,enabled,date_added,comment,type)";
|
||||
$sql .= " VALUES (:id,:domain,:enabled,:date_added,:comment,:type);";
|
||||
$field = "domain";
|
||||
}
|
||||
elseif($table === "group")
|
||||
{
|
||||
$sql = "INSERT OR IGNORE INTO \"group\"";
|
||||
$sql .= " (id,name,date_added,description)";
|
||||
$sql .= " VALUES (:id,:name,:date_added,:description);";
|
||||
}
|
||||
elseif($table === "client")
|
||||
{
|
||||
$sql = "INSERT OR IGNORE INTO client";
|
||||
$sql .= " (id,ip,date_added,comment)";
|
||||
$sql .= " VALUES (:id,:ip,:date_added,:comment);";
|
||||
}
|
||||
elseif($table === "domainlist_by_group")
|
||||
{
|
||||
$sql = "INSERT OR IGNORE INTO domainlist_by_group";
|
||||
$sql .= " (domainlist_id,group_id)";
|
||||
$sql .= " VALUES (:domainlist_id,:group_id);";
|
||||
}
|
||||
elseif($table === "client_by_group")
|
||||
{
|
||||
$sql = "INSERT OR IGNORE INTO client_by_group";
|
||||
$sql .= " (client_id,group_id)";
|
||||
$sql .= " VALUES (:client_id,:group_id);";
|
||||
}
|
||||
elseif($table === "adlist_by_group")
|
||||
{
|
||||
$sql = "INSERT OR IGNORE INTO adlist_by_group";
|
||||
$sql .= " (adlist_id,group_id)";
|
||||
$sql .= " VALUES (:adlist_id,:group_id);";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -149,23 +176,27 @@ function archive_restore_table($file, $table, $flush=false)
|
||||
if(strlen($row[$field]) > 253)
|
||||
continue;
|
||||
|
||||
$stmt->bindValue(":id", $row["id"], SQLITE3_INTEGER);
|
||||
$stmt->bindValue(":date_added", $row["date_added"], SQLITE3_INTEGER);
|
||||
$stmt->bindValue(":".$field, $row[$field], SQLITE3_TEXT);
|
||||
|
||||
if($table !== "domain_audit")
|
||||
{
|
||||
$stmt->bindValue(":enabled", $row["enabled"], SQLITE3_INTEGER);
|
||||
if(is_null($row["comment"]))
|
||||
$type = SQLITE3_NULL;
|
||||
else
|
||||
$type = SQLITE3_TEXT;
|
||||
$stmt->bindValue(":comment", $row["comment"], $type);
|
||||
}
|
||||
|
||||
if($table === "domainlist")
|
||||
{
|
||||
$stmt->bindValue(":type", $row["type"], SQLITE3_INTEGER);
|
||||
// Bind properties from JSON data
|
||||
// Note that only defined above are actually used
|
||||
// so even maliciously modified Teleporter files
|
||||
// cannot be dangerous in any way
|
||||
foreach($row as $key => $value) {
|
||||
$type = gettype($value);
|
||||
$sqltype=NULL;
|
||||
switch($type) {
|
||||
case "integer":
|
||||
$sqltype = SQLITE3_INTEGER;
|
||||
break;
|
||||
case "string":
|
||||
$sqltype = SQLITE3_TEXT;
|
||||
break;
|
||||
case "NULL":
|
||||
$sqltype = SQLITE3_NULL;
|
||||
break;
|
||||
default:
|
||||
$sqltype = "UNK";
|
||||
}
|
||||
$stmt->bindValue(":".$key, $value, $sqltype);
|
||||
}
|
||||
|
||||
if($stmt->execute() && $stmt->reset() && $stmt->clear())
|
||||
@@ -248,10 +279,10 @@ function flush_table($table, $type=null)
|
||||
if(!in_array($table, $flushed_tables))
|
||||
{
|
||||
if($type !== null) {
|
||||
$sql = "DELETE FROM ".$table." WHERE type = ".$type;
|
||||
$sql = "DELETE FROM \"".$table."\" WHERE type = ".$type;
|
||||
array_push($flushed_tables, $table.$type);
|
||||
} else {
|
||||
$sql = "DELETE FROM ".$table;
|
||||
$sql = "DELETE FROM \"".$table."\"";
|
||||
array_push($flushed_tables, $table);
|
||||
}
|
||||
$db->exec($sql);
|
||||
@@ -411,6 +442,43 @@ if(isset($_POST["action"]))
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if(isset($_POST["group"]) && $file->getFilename() === "group.json")
|
||||
{
|
||||
$num = archive_restore_table($file, "group", $flushtables);
|
||||
echo "Processed group (".$num." entries)<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if(isset($_POST["client"]) && $file->getFilename() === "client.json")
|
||||
{
|
||||
$num = archive_restore_table($file, "client", $flushtables);
|
||||
echo "Processed client (".$num." entries)<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if(isset($_POST["client"]) && $file->getFilename() === "client_by_group.json")
|
||||
{
|
||||
$num = archive_restore_table($file, "client_by_group", $flushtables);
|
||||
echo "Processed client group assignments (".$num." entries)<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if((isset($_POST["whitelist"]) || isset($_POST["regex_whitelist"]) ||
|
||||
isset($_POST["blacklist"]) || isset($_POST["regex_blacklist"])) &&
|
||||
$file->getFilename() === "domainlist_by_group.json")
|
||||
{
|
||||
$num = archive_restore_table($file, "domainlist_by_group", $flushtables);
|
||||
echo "Processed black-/whitelist group assignments (".$num." entries)<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if(isset($_POST["adlist"]) && $file->getFilename() === "adlist_by_group.json")
|
||||
{
|
||||
$num = archive_restore_table($file, "adlist_by_group", $flushtables);
|
||||
echo "Processed adlist group assignments (".$num." entries)<br>\n";
|
||||
$importedsomething = true;
|
||||
}
|
||||
|
||||
if(isset($_POST["staticdhcpleases"]) && $file->getFilename() === "04-pihole-static-dhcp.conf")
|
||||
{
|
||||
if($flushtables) {
|
||||
@@ -435,11 +503,31 @@ if(isset($_POST["action"]))
|
||||
$importedsomething = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_POST["localdnsrecords"]) && $file->getFilename() === "custom.list")
|
||||
{
|
||||
if($flushtables) {
|
||||
// Defined in func.php included via auth.php
|
||||
deleteAllCustomDNSEntries();
|
||||
}
|
||||
$num = 0;
|
||||
$localdnsrecords = process_file(file_get_contents($file));
|
||||
foreach($localdnsrecords as $record) {
|
||||
list($ip,$domain) = explode(" ",$record);
|
||||
if(addCustomDNSEntry($ip, $domain, false))
|
||||
$num++;
|
||||
}
|
||||
|
||||
echo "Processed local DNS records (".$num." entries)<br>\n";
|
||||
if($num > 0) {
|
||||
$importedsomething = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($importedsomething)
|
||||
{
|
||||
exec("sudo pihole restartdns reload");
|
||||
pihole_execute("restartdns reload");
|
||||
}
|
||||
|
||||
unlink($fullfilename);
|
||||
@@ -452,7 +540,8 @@ if(isset($_POST["action"]))
|
||||
}
|
||||
else
|
||||
{
|
||||
$tarname = "pi-hole-teleporter_".date("Y-m-d_h-i-s").".tar";
|
||||
$hostname = gethostname() ? gethostname()."-" : "";
|
||||
$tarname = "pi-hole-".$hostname."teleporter_".date("Y-m-d_H-i-s").".tar";
|
||||
$filename = $tarname.".gz";
|
||||
$archive_file_name = sys_get_temp_dir() ."/". $tarname;
|
||||
$archive = new PharData($archive_file_name);
|
||||
@@ -467,8 +556,18 @@ else
|
||||
archive_add_table("blacklist.regex.json", "domainlist", ListType::regex_blacklist);
|
||||
archive_add_table("adlist.json", "adlist");
|
||||
archive_add_table("domain_audit.json", "domain_audit");
|
||||
archive_add_table("group.json", "group");
|
||||
archive_add_table("client.json", "client");
|
||||
|
||||
// Group linking tables
|
||||
archive_add_table("domainlist_by_group.json", "domainlist_by_group");
|
||||
archive_add_table("adlist_by_group.json", "adlist_by_group");
|
||||
archive_add_table("client_by_group.json", "client_by_group");
|
||||
|
||||
archive_add_file("/etc/pihole/","setupVars.conf");
|
||||
archive_add_file("/etc/pihole/","dhcp.leases");
|
||||
archive_add_file("/etc/pihole/","custom.list");
|
||||
archive_add_file("/etc/pihole/","pihole-FTL.conf");
|
||||
archive_add_file("/etc/","hosts","etc/");
|
||||
archive_add_directory("/etc/dnsmasq.d/","dnsmasq.d/");
|
||||
|
||||
|
||||
47
scripts/pi-hole/php/theme.php
Normal file
47
scripts/pi-hole/php/theme.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2020 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
// Array of available themes and their description
|
||||
$available_themes = array();
|
||||
/* Array key = name used internally, not shown to the user
|
||||
* Array[0] = Description
|
||||
* Array[1] = Is this a dark mode theme? (Sets background to black during page reloading to avoid white "flashing")
|
||||
* Array[2] = Style sheet name
|
||||
*/
|
||||
$available_themes["default-light"] = array("Pi-hole default theme (light, default)", false, "default-light");
|
||||
$available_themes["default-dark"] = array("Pi-hole midnight theme (dark)", true, "default-dark");
|
||||
|
||||
$webtheme = "";
|
||||
// Try to load theme settings from setupVars.conf
|
||||
if(isset($setupVars['WEBTHEME']))
|
||||
$webtheme = $setupVars['WEBTHEME'];
|
||||
|
||||
// May be overwritten by settings tab
|
||||
if(isset($_POST["field"]) &&
|
||||
$_POST["field"] === "webUI" &&
|
||||
isset($_POST["webtheme"])) {
|
||||
$webtheme = $_POST["webtheme"];
|
||||
}
|
||||
|
||||
if(!array_key_exists($webtheme,$available_themes)) {
|
||||
// Fallback to default (light) theme is property is not set
|
||||
// or requested theme is not among the available
|
||||
$webtheme = "default-light";
|
||||
}
|
||||
|
||||
$darkmode = $available_themes[$webtheme][1];
|
||||
$theme = $available_themes[$webtheme][2];
|
||||
|
||||
function theme_selection() {
|
||||
global $available_themes, $webtheme;
|
||||
foreach ($available_themes as $key => $value) {
|
||||
?><div><input type="radio" name="webtheme" value="<?php echo $key; ?>" id="webtheme_<?php echo $key; ?>" <?php if ($key === $webtheme){ ?>checked<?php } ?>>
|
||||
<label for="webtheme_<?php echo $key; ?>"><strong><?php echo $value[0]; ?></strong></label></div><?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user