From dd101b3b0a203f584a3620d8ba5b5dede316c3c7 Mon Sep 17 00:00:00 2001 From: TheME Date: Fri, 30 Jun 2017 12:07:34 +0200 Subject: [PATCH 1/7] header.php: set 'Tools' menu active correctly --- scripts/pi-hole/php/header.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php index 0bef5c26..b5bf39ca 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -491,7 +491,7 @@ if($auth) { Enable    -
  • active"> +
  • active"> Tools From 273f9373b2b32ab52393b2040564f524377c2f43 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 30 Jun 2017 13:40:13 +0200 Subject: [PATCH 2/7] Display time in 24H instead of 12H mode Signed-off-by: DL6ER --- scripts/pi-hole/js/db_lists.js | 2 +- scripts/pi-hole/js/db_queries.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/db_lists.js b/scripts/pi-hole/js/db_lists.js index 2bf3bad4..d7db5332 100644 --- a/scripts/pi-hole/js/db_lists.js +++ b/scripts/pi-hole/js/db_lists.js @@ -27,7 +27,7 @@ $(function () { $("#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")], diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index e2d57ab7..bacdedc1 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -27,7 +27,7 @@ $(function () { $("#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")], From 76e4cc6a40f07e8da8477555cd1b727eb535f2ec Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 30 Jun 2017 17:04:10 +0200 Subject: [PATCH 3/7] Fix datrangepicker for "Today" and "Yesterday". Add "All Time" option. (#535) Signed-off-by: DL6ER --- scripts/pi-hole/js/db_lists.js | 10 ++++++---- scripts/pi-hole/js/db_queries.js | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/pi-hole/js/db_lists.js b/scripts/pi-hole/js/db_lists.js index 2bf3bad4..19e71ba6 100644 --- a/scripts/pi-hole/js/db_lists.js +++ b/scripts/pi-hole/js/db_lists.js @@ -29,12 +29,14 @@ $(function () { timePicker: true, timePickerIncrement: 15, locale: { format: "MMMM Do YYYY, h:mm A" }, 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 diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index e2d57ab7..35eb9f15 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -29,12 +29,14 @@ $(function () { timePicker: true, timePickerIncrement: 15, locale: { format: "MMMM Do YYYY, h:mm A" }, 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 From 94bdb18fe1b749a32ae3c317957f048d78452184 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 1 Jul 2017 13:38:04 +0200 Subject: [PATCH 4/7] Fix domain validation function (#538) --- scripts/pi-hole/php/func.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 } From 93732f12b1b3365ba3201c56695842d1b18f2676 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 8 Jul 2017 20:18:26 +0200 Subject: [PATCH 5/7] Re-add doughnut charts (#544) * Re-add doughnut charts as this has been requested by some users * Smaller optimizations * Don't use rotation animation for further updates * Apply attribute to correct graph * Ask for unsorted list of forward destinations * :codacy: * Move setting to correct graph --- api_FTL.php | 9 +- index.php | 39 ++- scripts/pi-hole/js/index.js | 594 ++++++++++++++++++++++-------------- 3 files changed, 409 insertions(+), 233 deletions(-) diff --git a/api_FTL.php b/api_FTL.php index 149b61c0..55d8a17e 100644 --- a/api_FTL.php +++ b/api_FTL.php @@ -164,7 +164,14 @@ if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth) if (isset($_GET['getForwardDestinations']) && $auth) { - sendRequestFTL("forward-dest"); + if($_GET['getForwardDestinations'] === "unsorted") + { + sendRequestFTL("forward-dest unsorted"); + } + else + { + sendRequestFTL("forward-dest"); + } $return = getResponseFTL(); $forward_dest = array(); foreach($return as $line) diff --git a/index.php b/index.php index 452073c8..b97cd0bb 100644 --- a/index.php +++ b/index.php @@ -88,11 +88,46 @@ // show since the API will respect the privacy of the user if he defines // a password if($auth){ ?> +
    +
    +
    +
    +

    Query Types (integrated)

    +
    +
    +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +

    Forward Destinations (integrated)

    +
    +
    +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    -

    Query Types over Time

    +

    Query Types (over time)

    @@ -108,7 +143,7 @@
    -

    Forward Destinations over Time

    +

    Forward Destinations (over time)

    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(); + } +}); From a540c851c937f47e827b329da2c56aca0585827e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 8 Jul 2017 20:24:26 +0200 Subject: [PATCH 6/7] Add hostname in header (#541) * Add hostname in header * Removed class attribute of
  • element and replaced --- scripts/pi-hole/php/header.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php index b5bf39ca..ae150da5 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -235,7 +235,7 @@ if($auth) { - - - diff --git a/db_queries.php b/db_queries.php index bca28c4e..189c97cd 100644 --- a/db_queries.php +++ b/db_queries.php @@ -34,9 +34,6 @@ $token = $_SESSION['token']; - - - 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 c35b7244..9a7cdfbc 100644 --- a/scripts/pi-hole/js/db_lists.js +++ b/scripts/pi-hole/js/db_lists.js @@ -15,15 +15,6 @@ 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, @@ -38,7 +29,6 @@ $(function () { "This Year": [moment().startOf("year"), moment()], "All Time": [moment(0), moment()] }, - startDate: start__, endDate: end__, "opens": "center", "showDropdowns": true }, function (startt, endt) { @@ -184,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 ad89ec05..8551e9dc 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -13,18 +13,24 @@ 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, HH:mm" }, @@ -38,7 +44,6 @@ $(function () { "This Year": [moment().startOf("year"), moment()], "All Time": [moment(0), moment()] }, - startDate: start__, endDate: end__, "opens": "center", "showDropdowns": true }, function (startt, endt) { @@ -174,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) @@ -215,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, @@ -233,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(); @@ -246,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/php/header.php b/scripts/pi-hole/php/header.php index ae150da5..05f98af2 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -417,7 +417,7 @@ if($auth) { Query Log
  • -
  • active"> +
  • active"> Long term data @@ -425,6 +425,11 @@ if($auth) {