diff --git a/scripts/pi-hole/js/db_graph.js b/scripts/pi-hole/js/db_graph.js
new file mode 100644
index 00000000..201cad5d
--- /dev/null
+++ b/scripts/pi-hole/js/db_graph.js
@@ -0,0 +1,262 @@
+/* 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
+ moment
+*/
+
+var start__ = moment().subtract(6, "days");
+var from = moment(start__).utc().valueOf()/1000;
+var end__ = moment();
+var until = moment(end__).utc().valueOf()/1000;
+
+$(function () {
+ $("#querytime").daterangepicker(
+ {
+ timePicker: true, timePickerIncrement: 15,
+ locale: { format: "MMMM Do YYYY, HH:mm" },
+ ranges: {
+ "Today": [moment().startOf("day"), moment()],
+ "Yesterday": [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")],
+ "This Year": [moment().startOf("year"), moment()],
+ "All Time": [moment(0), moment()]
+ },
+ "opens": "center", "showDropdowns": true
+ },
+ 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 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) {
+ return a - b;
+}
+
+function updateQueriesOverTime() {
+ $("#queries-over-time .overlay").show();
+ $.getJSON("api_db.php?getGraphData&from="+from+"&until="+until, 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);
+ // Remove possibly already existing data
+ timeLineChart.data.labels = [];
+ timeLineChart.data.datasets[0].data = [];
+ timeLineChart.data.datasets[1].data = [];
+
+ var dates = [], hour;
+
+ for (hour in data.domains_over_time[0]) {
+ if ({}.hasOwnProperty.call(data.domains_over_time[0], hour)) {
+ dates.push(parseInt(data.domains_over_time[0][hour]));
+ }
+ }
+
+ for (hour in data.ads_over_time[0]) {
+ if ({}.hasOwnProperty.call(data.ads_over_time[0], hour)) {
+ if(!dates.includes(parseInt(data.ads_over_time[0][hour])))
+ {
+ dates.push(parseInt(data.ads_over_time[0][hour]));
+ }
+ }
+ }
+
+ dates.sort(compareNumbers);
+
+ // Add data for each hour that is available
+ for (hour in dates) {
+ if ({}.hasOwnProperty.call(dates, hour)) {
+ var d, dom = 0, ads = 0;
+ d = 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];
+ }
+
+ idx = data.ads_over_time[0].indexOf(dates[hour].toString());
+ if (idx > -1)
+ {
+ ads = data.ads_over_time[1][idx];
+ }
+
+ timeLineChart.data.labels.push(d);
+ timeLineChart.data.datasets[0].data.push(dom);
+ timeLineChart.data.datasets[1].data.push(ads);
+ }
+ }
+
+ timeLineChart.options.scales.xAxes[0].display=true;
+ $("#queries-over-time .overlay").hide();
+ timeLineChart.update();
+ });
+}
+
+/* global Chart */
+$(document).ready(function() {
+ var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
+ timeLineChart = new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: [ 0 ],
+ datasets: [
+ {
+ label: "Total DNS Queries",
+ fill: true,
+ backgroundColor: "rgba(220,220,220,0.5)",
+ borderColor: "rgba(0, 166, 90,.8)",
+ pointBorderColor: "rgba(0, 166, 90,.8)",
+ pointRadius: 1,
+ pointHoverRadius: 5,
+ data: [],
+ pointHitRadius: 5,
+ cubicInterpolationMode: "monotone"
+ },
+ {
+ label: "Blocked DNS Queries",
+ fill: true,
+ backgroundColor: "rgba(0,192,239,0.5)",
+ borderColor: "rgba(0,192,239,1)",
+ pointBorderColor: "rgba(0,192,239,1)",
+ pointRadius: 1,
+ pointHoverRadius: 5,
+ data: [],
+ pointHitRadius: 5,
+ cubicInterpolationMode: "monotone"
+ }
+ ]
+ },
+ options: {
+ tooltips: {
+ enabled: true,
+ responsive: true,
+ mode: "x-axis",
+ callbacks: {
+ title: function(tooltipItem, data) {
+ var label = tooltipItem[0].xLabel;
+ var time = new Date(label);
+ var date = time.getFullYear()+"-"+padNumber(time.getMonth())+"-"+padNumber(time.getDate());
+ var h = time.getHours();
+ var m = time.getMinutes();
+ var from = padNumber(h)+":"+padNumber(m)+":00";
+ var to = padNumber(h)+":"+padNumber(m+9)+":59";
+ return "Queries from "+from+" to "+to+" on "+date;
+ },
+ 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;
+ }
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel + " (" + percentage.toFixed(1) + "%)";
+ }
+ else
+ {
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
+ }
+ }
+ }
+ },
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{
+ type: "time",
+ display: false,
+ time: {
+ displayFormats: {
+ "minute": "HH:mm",
+ "hour": "HH:mm",
+ "day": "HH:mm",
+ "week": "MMM DD HH:mm",
+ "month": "MMM DD",
+ "quarter": "MMM DD",
+ "year": "MMM DD"
+ }
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ beginAtZero: true
+ }
+ }]
+ },
+ maintainAspectRatio: false
+ }
+ });
+});
+
+$("#querytime").on("apply.daterangepicker", function(ev, picker) {
+ $("#queries-over-time").show();
+ updateQueriesOverTime();
+});
+
+$("#queryOverTimeChart").click(function(evt){
+ var activePoints = timeLineChart.getElementAtEvent(evt);
+ if(activePoints.length > 0)
+ {
+ //get the internal index in the chart
+ var clickedElementindex = activePoints[0]["_index"];
+
+ //get specific label by index
+ var label = timeLineChart.data.labels[clickedElementindex];
+
+ //get value by index
+ var from = label/1000;
+ var until = label/1000 + 600;
+ window.location.href = "db_queries.php?from="+from+"&until="+until;
+ }
+ return false;
+});
diff --git a/scripts/pi-hole/js/db_lists.js b/scripts/pi-hole/js/db_lists.js
index 2bf3bad4..9a7cdfbc 100644
--- a/scripts/pi-hole/js/db_lists.js
+++ b/scripts/pi-hole/js/db_lists.js
@@ -15,28 +15,20 @@ var end__ = moment();
var until = moment(end__).utc().valueOf()/1000;
$(function () {
- // Get first time stamp we have valid data for to limit selectable date/time range
- $.getJSON("api_db.php?getMinTimestamp", function(data) {
- var minDate = parseInt(data.mintimestamp);
- if(!isNaN(minDate))
- {
- $("#querytime").data("daterangepicker").minDate = moment.unix(minDate);
- }
- });
-
$("#querytime").daterangepicker(
{
timePicker: true, timePickerIncrement: 15,
- locale: { format: "MMMM Do YYYY, h:mm A" },
+ locale: { format: "MMMM Do YYYY, HH:mm" },
ranges: {
- "Today": [moment(), moment()],
- "Yesterday": [moment().subtract(1, "days"), moment().subtract(1, "days")],
+ "Today": [moment().startOf("day"), moment()],
+ "Yesterday": [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().endOf("month")],
- "Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
+ "This Month": [moment().startOf("month"), moment()],
+ "Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")],
+ "This Year": [moment().startOf("year"), moment()],
+ "All Time": [moment(0), moment()]
},
- startDate: start__, endDate: end__,
"opens": "center", "showDropdowns": true
},
function (startt, endt) {
@@ -182,8 +174,7 @@ function updateTopAdsChart() {
});
}
-// Handle "Go" Button
-$("#btnGo").on("click", function() {
+$("#querytime").on("apply.daterangepicker", function(ev, picker) {
updateTopClientsChart();
updateTopDomainsChart();
updateTopAdsChart();
diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js
index e2d57ab7..8551e9dc 100644
--- a/scripts/pi-hole/js/db_queries.js
+++ b/scripts/pi-hole/js/db_queries.js
@@ -13,30 +13,37 @@ var start__ = moment().subtract(6, "days");
var from = moment(start__).utc().valueOf()/1000;
var end__ = moment();
var until = moment(end__).utc().valueOf()/1000;
+var instantquery = false;
+var daterange;
+
+// Do we want to filter queries?
+var GETDict = {};
+location.search.substr(1).split("&").forEach(function(item) {GETDict[item.split("=")[0]] = item.split("=")[1];});
+
+if("from" in GETDict && "until" in GETDict)
+{
+ from = parseInt(GETDict["from"]);
+ until = parseInt(GETDict["until"]);
+ start__ = moment(1000*from);
+ end__ = moment(1000*until);
+ instantquery = true;
+}
$(function () {
- // Get first time stamp we have valid data for to limit selectable date/time range
- $.getJSON("api_db.php?getMinTimestamp", function(data) {
- var minDate = parseInt(data.mintimestamp);
- if(!isNaN(minDate))
- {
- $("#querytime").data("daterangepicker").minDate = moment.unix(minDate);
- }
- });
-
- $("#querytime").daterangepicker(
+ daterange = $("#querytime").daterangepicker(
{
timePicker: true, timePickerIncrement: 15,
- locale: { format: "MMMM Do YYYY, h:mm A" },
+ locale: { format: "MMMM Do YYYY, HH:mm" },
ranges: {
- "Today": [moment(), moment()],
- "Yesterday": [moment().subtract(1, "days"), moment().subtract(1, "days")],
+ "Today": [moment().startOf("day"), moment()],
+ "Yesterday": [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().endOf("month")],
- "Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
+ "This Month": [moment().startOf("month"), moment()],
+ "Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")],
+ "This Year": [moment().startOf("year"), moment()],
+ "All Time": [moment(0), moment()]
},
- startDate: start__, endDate: end__,
"opens": "center", "showDropdowns": true
},
function (startt, endt) {
@@ -172,6 +179,16 @@ function refreshTableData() {
$(document).ready(function() {
var status;
+ var APIstring;
+ if(instantquery)
+ {
+ APIstring = "api_db.php?getAllQueries&from="+from+"&until="+until;
+ }
+ else
+ {
+ APIstring = "api_db.php?getAllQueries=empty";
+ }
+
tableApi = $("#all-queries").DataTable( {
"rowCallback": function( row, data, index ){
if (data[4] === 1)
@@ -213,7 +230,7 @@ $(document).ready(function() {
"<'row'<'col-sm-4'l><'col-sm-8'p>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
- "ajax": {"url": "api_db.php?getAllQueries=empty", "error": handleAjaxError },
+ "ajax": {"url": APIstring, "error": handleAjaxError },
"autoWidth" : false,
"processing": true,
"deferRender": true,
@@ -231,7 +248,8 @@ $(document).ready(function() {
"targets": -1,
"data": null,
"defaultContent": ""
- } ]
+ } ],
+ "initComplete": reloadCallback
});
$("#all-queries tbody").on( "click", "button", function () {
var data = tableApi.row( $(this).parents("tr") ).data();
@@ -244,9 +262,14 @@ $(document).ready(function() {
add(data[2],"black");
}
} );
+
+ if(instantquery)
+ {
+ daterange.val(start__.format("MMMM Do YYYY, HH:mm") + " - " + end__.format("MMMM Do YYYY, HH:mm"));
+ }
} );
-// Handle "Go" Button
-$("#btnGo").on("click", function() {
+$("#querytime").on("apply.daterangepicker", function(ev, picker) {
refreshTableData();
});
+
diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js
index 4b8acb28..6bbd6296 100644
--- a/scripts/pi-hole/js/index.js
+++ b/scripts/pi-hole/js/index.js
@@ -5,7 +5,9 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
// Define global variables
+/* global Chart */
var timeLineChart, queryTypeChart, forwardDestinationChart;
+var queryTypePieChart, forwardDestinationPieChart;
function padNumber(num) {
return ("00" + num).substr(-2,2);
@@ -145,6 +147,48 @@ function updateQueryTypesOverTime() {
}
});
}
+function updateQueryTypesPie() {
+ $.getJSON("api.php?getQueryTypes", function(data) {
+
+ if("FTLnotrunning" in data)
+ {
+ return;
+ }
+
+ var colors = [];
+ // Get colors from AdminLTE
+ $.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); });
+ var v = [], c = [], k = [], iter;
+ // Collect values and colors, and labels
+ if(data.hasOwnProperty("querytypes"))
+ {
+ iter = data.querytypes;
+ }
+ else
+ {
+ iter = data;
+ }
+ $.each(iter, function(key , value) {
+ v.push(value);
+ c.push(colors.shift());
+ k.push(key);
+ });
+ // Build a single dataset with the data to be pushed
+ var dd = {data: v, backgroundColor: c};
+ // and push it at once
+ queryTypePieChart.data.datasets[0] = dd;
+ queryTypePieChart.data.labels = k;
+ $("#query-types-pie .overlay").hide();
+ queryTypePieChart.update();
+ queryTypePieChart.chart.config.options.cutoutPercentage=50;
+ queryTypePieChart.update();
+ // Don't use rotation animation for further updates
+ queryTypePieChart.options.animation.duration=0;
+ }).done(function() {
+ // Reload graph after minute
+ setTimeout(updateQueryTypesPie, 60000);
+ });
+}
function updateForwardedOverTime() {
$.getJSON("api.php?overTimeDataForwards&getForwardDestinationNames", function(data) {
@@ -233,6 +277,46 @@ function updateForwardedOverTime() {
});
}
+function updateForwardDestinationsPie() {
+ $.getJSON("api.php?getForwardDestinations=unsorted", function(data) {
+
+ if("FTLnotrunning" in data)
+ {
+ return;
+ }
+
+ var colors = [];
+ // Get colors from AdminLTE
+ $.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); });
+ var v = [], c = [], k = [];
+ // Collect values and colors, immediately push individual labels
+ $.each(data.forward_destinations, function(key , value) {
+ v.push(value);
+ c.push(colors.shift());
+ if(key.indexOf("|") > -1)
+ {
+ key = key.substr(0, key.indexOf("|"));
+ }
+ k.push(key);
+ });
+ // Build a single dataset with the data to be pushed
+ var dd = {data: v, backgroundColor: c};
+ // and push it at once
+ forwardDestinationPieChart.data.labels = k;
+ forwardDestinationPieChart.data.datasets[0] = dd;
+ // and push it at once
+ $("#forward-destinations-pie .overlay").hide();
+ forwardDestinationPieChart.update();
+ forwardDestinationPieChart.chart.config.options.cutoutPercentage=50;
+ forwardDestinationPieChart.update();
+ // Don't use rotation animation for further updates
+ forwardDestinationPieChart.options.animation.duration=0;
+ }).done(function() {
+ // Reload graph after one minute
+ setTimeout(updateForwardDestinationsPie, 60000);
+ });
+}
+
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
function escapeHtml(text) {
var map = {
@@ -437,59 +521,130 @@ function updateSummaryData(runOnce) {
$(document).ready(function() {
- var isMobile = {
- Windows: function() {
- return /IEMobile/i.test(navigator.userAgent);
- },
- Android: function() {
- return /Android/i.test(navigator.userAgent);
- },
- BlackBerry: function() {
- return /BlackBerry/i.test(navigator.userAgent);
- },
- iOS: function() {
- return /iPhone|iPad|iPod/i.test(navigator.userAgent);
- },
- any: function() {
- return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Windows());
- }
- };
+ var isMobile = {
+ Windows: function() {
+ return /IEMobile/i.test(navigator.userAgent);
+ },
+ Android: function() {
+ return /Android/i.test(navigator.userAgent);
+ },
+ BlackBerry: function() {
+ return /BlackBerry/i.test(navigator.userAgent);
+ },
+ iOS: function() {
+ return /iPhone|iPad|iPod/i.test(navigator.userAgent);
+ },
+ any: function() {
+ return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Windows());
+ }
+ };
- // Pull in data via AJAX
+ // Pull in data via AJAX
- updateSummaryData();
+ updateSummaryData();
- var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
- timeLineChart = new Chart(ctx, {
+ var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
+ timeLineChart = new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: [],
+ datasets: [
+ {
+ label: "Total DNS Queries",
+ fill: true,
+ backgroundColor: "rgba(220,220,220,0.5)",
+ borderColor: "rgba(0, 166, 90,.8)",
+ pointBorderColor: "rgba(0, 166, 90,.8)",
+ pointRadius: 1,
+ pointHoverRadius: 5,
+ data: [],
+ pointHitRadius: 5,
+ cubicInterpolationMode: "monotone"
+ },
+ {
+ label: "Blocked DNS Queries",
+ fill: true,
+ backgroundColor: "rgba(0,192,239,0.5)",
+ borderColor: "rgba(0,192,239,1)",
+ pointBorderColor: "rgba(0,192,239,1)",
+ pointRadius: 1,
+ pointHoverRadius: 5,
+ data: [],
+ pointHitRadius: 5,
+ cubicInterpolationMode: "monotone"
+ }
+ ]
+ },
+ options: {
+ tooltips: {
+ enabled: true,
+ mode: "x-axis",
+ callbacks: {
+ title: function(tooltipItem, data) {
+ var label = tooltipItem[0].xLabel;
+ var time = label.match(/(\d?\d):?(\d?\d?)/);
+ var h = parseInt(time[1], 10);
+ var m = parseInt(time[2], 10) || 0;
+ var from = padNumber(h)+":"+padNumber(m-5)+":00";
+ var to = padNumber(h)+":"+padNumber(m+4)+":59";
+ return "Queries from "+from+" to "+to;
+ },
+ 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;
+ }
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel + " (" + percentage.toFixed(1) + "%)";
+ }
+ else
+ {
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
+ }
+ }
+ }
+ },
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{
+ type: "time",
+ time: {
+ unit: "hour",
+ displayFormats: {
+ hour: "HH:mm"
+ },
+ tooltipFormat: "HH:mm"
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ beginAtZero: true
+ }
+ }]
+ },
+ maintainAspectRatio: false
+ }
+ });
+
+ // Pull in data via AJAX
+
+ updateQueriesOverTime();
+
+ // Create / load "Forward Destinations over Time" only if authorized
+ if(document.getElementById("forwardDestinationChart"))
+ {
+ ctx = document.getElementById("forwardDestinationChart").getContext("2d");
+ forwardDestinationChart = new Chart(ctx, {
type: "line",
data: {
labels: [],
- datasets: [
- {
- label: "Total DNS Queries",
- fill: true,
- backgroundColor: "rgba(220,220,220,0.5)",
- borderColor: "rgba(0, 166, 90,.8)",
- pointBorderColor: "rgba(0, 166, 90,.8)",
- pointRadius: 1,
- pointHoverRadius: 5,
- data: [],
- pointHitRadius: 5,
- cubicInterpolationMode: "monotone"
- },
- {
- label: "Blocked DNS Queries",
- fill: true,
- backgroundColor: "rgba(0,192,239,0.5)",
- borderColor: "rgba(0,192,239,1)",
- pointBorderColor: "rgba(0,192,239,1)",
- pointRadius: 1,
- pointHoverRadius: 5,
- data: [],
- pointHitRadius: 5,
- cubicInterpolationMode: "monotone"
- }
- ]
+ datasets: [{ data: [] }]
},
options: {
tooltips: {
@@ -503,24 +658,10 @@ $(document).ready(function() {
var m = parseInt(time[2], 10) || 0;
var from = padNumber(h)+":"+padNumber(m-5)+":00";
var to = padNumber(h)+":"+padNumber(m+4)+":59";
- return "Queries from "+from+" to "+to;
+ return "Forward destinations from "+from+" to "+to;
},
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;
- }
- return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel + " (" + percentage.toFixed(1) + "%)";
- }
- else
- {
- return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
- }
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
}
}
},
@@ -540,187 +681,180 @@ $(document).ready(function() {
}],
yAxes: [{
ticks: {
- beginAtZero: true
- }
+ mix: 0.0,
+ max: 1.0,
+ beginAtZero: true,
+ callback: function(value, index, values) {
+ return Math.round(value*100) + " %";
+ }
+ },
+ stacked: true
}]
},
- maintainAspectRatio: false
+ maintainAspectRatio: true
}
});
// Pull in data via AJAX
+ updateForwardedOverTime();
+ }
- updateQueriesOverTime();
-
- // Create / load "Forward Destinations over Time" only if authorized
- if(document.getElementById("forwardDestinationChart"))
- {
- ctx = document.getElementById("forwardDestinationChart").getContext("2d");
- forwardDestinationChart = new Chart(ctx, {
- type: "line",
- data: {
- labels: [],
- datasets: [{ data: [] }]
- },
- options: {
- tooltips: {
- enabled: true,
- mode: "x-axis",
- callbacks: {
- title: function(tooltipItem, data) {
- var label = tooltipItem[0].xLabel;
- var time = label.match(/(\d?\d):?(\d?\d?)/);
- var h = parseInt(time[1], 10);
- var m = parseInt(time[2], 10) || 0;
- var from = padNumber(h)+":"+padNumber(m-5)+":00";
- var to = padNumber(h)+":"+padNumber(m+4)+":59";
- return "Forward destinations from "+from+" to "+to;
- },
- label: function(tooltipItems, data) {
- return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
- }
- }
+ // Create / load "Query Types over Time" only if authorized
+ if(document.getElementById("queryTypeChart"))
+ {
+ ctx = document.getElementById("queryTypeChart").getContext("2d");
+ queryTypeChart = new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: [],
+ datasets: [
+ {
+ label: "A: IPv4 queries",
+ pointRadius: 0,
+ pointHitRadius: 5,
+ pointHoverRadius: 5,
+ data: []
},
- legend: {
- display: false
- },
- scales: {
- xAxes: [{
- type: "time",
- time: {
- unit: "hour",
- displayFormats: {
- hour: "HH:mm"
- },
- tooltipFormat: "HH:mm"
- }
- }],
- yAxes: [{
- ticks: {
- mix: 0.0,
- max: 1.0,
- beginAtZero: true,
- callback: function(value, index, values) {
- return Math.round(value*100) + " %";
- }
- },
- stacked: true
- }]
- },
- maintainAspectRatio: true
- }
- });
-
- // Pull in data via AJAX
- updateForwardedOverTime();
- }
-
- // Create / load "Query Types over Time" only if authorized
- if(document.getElementById("queryTypeChart"))
- {
- ctx = document.getElementById("queryTypeChart").getContext("2d");
- queryTypeChart = new Chart(ctx, {
- type: "line",
- data: {
- labels: [],
- datasets: [
- {
- label: "A: IPv4 queries",
- pointRadius: 0,
- pointHitRadius: 5,
- pointHoverRadius: 5,
- data: []
+ {
+ label: "AAAA: IPv6 queries",
+ pointRadius: 0,
+ pointHitRadius: 5,
+ pointHoverRadius: 5,
+ data: []
+ }
+ ]
+ },
+ options: {
+ tooltips: {
+ enabled: true,
+ mode: "x-axis",
+ callbacks: {
+ title: function(tooltipItem, data) {
+ var label = tooltipItem[0].xLabel;
+ var time = label.match(/(\d?\d):?(\d?\d?)/);
+ var h = parseInt(time[1], 10);
+ var m = parseInt(time[2], 10) || 0;
+ var from = padNumber(h)+":"+padNumber(m-5)+":00";
+ var to = padNumber(h)+":"+padNumber(m+4)+":59";
+ return "Query types from "+from+" to "+to;
},
- {
- label: "AAAA: IPv6 queries",
- pointRadius: 0,
- pointHitRadius: 5,
- pointHoverRadius: 5,
- data: []
+ label: function(tooltipItems, data) {
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
}
- ]
+ }
},
- options: {
- tooltips: {
- enabled: true,
- mode: "x-axis",
- callbacks: {
- title: function(tooltipItem, data) {
- var label = tooltipItem[0].xLabel;
- var time = label.match(/(\d?\d):?(\d?\d?)/);
- var h = parseInt(time[1], 10);
- var m = parseInt(time[2], 10) || 0;
- var from = padNumber(h)+":"+padNumber(m-5)+":00";
- var to = padNumber(h)+":"+padNumber(m+4)+":59";
- return "Query types from "+from+" to "+to;
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{
+ type: "time",
+ time: {
+ unit: "hour",
+ displayFormats: {
+ hour: "HH:mm"
},
- label: function(tooltipItems, data) {
- return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
- }
+ tooltipFormat: "HH:mm"
}
- },
- legend: {
- display: false
- },
- scales: {
- xAxes: [{
- type: "time",
- time: {
- unit: "hour",
- displayFormats: {
- hour: "HH:mm"
- },
- tooltipFormat: "HH:mm"
+ }],
+ yAxes: [{
+ ticks: {
+ mix: 0.0,
+ max: 1.0,
+ beginAtZero: true,
+ callback: function(value, index, values) {
+ return Math.round(value*100) + " %";
}
- }],
- yAxes: [{
- ticks: {
- mix: 0.0,
- max: 1.0,
- beginAtZero: true,
- callback: function(value, index, values) {
- return Math.round(value*100) + " %";
- }
- },
- stacked: true
- }]
- },
- maintainAspectRatio: true
- }
- });
-
- // Pull in data via AJAX
- updateQueryTypesOverTime();
- }
-
- // Create / load "Top Domains" and "Top Advertisers" only if authorized
- if(document.getElementById("domain-frequency")
- && document.getElementById("ad-frequency"))
- {
- updateTopLists();
- }
-
- // Create / load "Top Clients" only if authorized
- if(document.getElementById("client-frequency"))
- {
- updateTopClientsChart();
- }
-
- $("#queryOverTimeChart").click(function(evt){
- var activePoints = timeLineChart.getElementAtEvent(evt);
- if(activePoints.length > 0)
- {
- //get the internal index of slice in pie chart
- var clickedElementindex = activePoints[0]["_index"];
-
- //get specific label by index
- var label = timeLineChart.data.labels[clickedElementindex];
-
- //get value by index
- var from = label/1000 - 300;
- var until = label/1000 + 300;
- window.location.href = "queries.php?from="+from+"&until="+until;
+ },
+ stacked: true
+ }]
+ },
+ maintainAspectRatio: true
}
- return false;
});
+
+ // Pull in data via AJAX
+ updateQueryTypesOverTime();
+ }
+
+ // Create / load "Top Domains" and "Top Advertisers" only if authorized
+ if(document.getElementById("domain-frequency")
+ && document.getElementById("ad-frequency"))
+ {
+ updateTopLists();
+ }
+
+ // Create / load "Top Clients" only if authorized
+ if(document.getElementById("client-frequency"))
+ {
+ updateTopClientsChart();
+ }
+
+ $("#queryOverTimeChart").click(function(evt){
+ var activePoints = timeLineChart.getElementAtEvent(evt);
+ if(activePoints.length > 0)
+ {
+ //get the internal index of slice in pie chart
+ var clickedElementindex = activePoints[0]["_index"];
+
+ //get specific label by index
+ var label = timeLineChart.data.labels[clickedElementindex];
+
+ //get value by index
+ var from = label/1000 - 300;
+ var until = label/1000 + 300;
+ window.location.href = "queries.php?from="+from+"&until="+until;
+ }
+ return false;
});
+
+ if(document.getElementById("queryTypePieChart"))
+ {
+ ctx = document.getElementById("queryTypePieChart").getContext("2d");
+ queryTypePieChart = new Chart(ctx, {
+ type: "doughnut",
+ data: {
+ labels: [],
+ datasets: [{ data: [] }]
+ },
+ options: {
+ legend: {
+ display: true,
+ position: "right"
+ },
+ animation: {
+ duration: 2000
+ },
+ cutoutPercentage: 0
+ }
+ });
+
+ // Pull in data via AJAX
+ updateQueryTypesPie();
+ }
+
+ if(document.getElementById("forwardDestinationPieChart"))
+ {
+ ctx = document.getElementById("forwardDestinationPieChart").getContext("2d");
+ forwardDestinationPieChart = new Chart(ctx, {
+ type: "doughnut",
+ data: {
+ labels: [],
+ datasets: [{ data: [] }]
+ },
+ options: {
+ legend: {
+ display: true,
+ position: "right"
+ },
+ animation: {
+ duration: 2000
+ },
+ cutoutPercentage: 0
+ }
+ });
+
+ // Pull in data via AJAX
+ updateForwardDestinationsPie();
+ }
+});
diff --git a/scripts/pi-hole/php/func.php b/scripts/pi-hole/php/func.php
index 22840bdb..f3beaf9e 100644
--- a/scripts/pi-hole/php/func.php
+++ b/scripts/pi-hole/php/func.php
@@ -8,7 +8,7 @@
function is_valid_domain_name($domain_name)
{
- return (preg_match("/^((-|_)*[a-z\d]((-|_)*[a-z\d])*(-|_)*)(\.(-|_)*([a-z\d]((-|_)*[a-z\d])*))*$/i", $domain_name) // Valid chars check
+ return (preg_match("/^((-|_)*[a-z\d]((-|_)*[a-z\d])*(-|_)*)(\.(-|_)*([a-z\d]((-|_)*[a-z\d])*))*$/i", $domain_name) && // Valid chars check
preg_match("/^.{1,253}$/", $domain_name) && // Overall length check
preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)); // Length of each label
}
diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php
index 0bef5c26..05f98af2 100644
--- a/scripts/pi-hole/php/header.php
+++ b/scripts/pi-hole/php/header.php
@@ -235,7 +235,7 @@ if($auth) {