diff --git a/scripts/pi-hole/js/db_graph.js b/scripts/pi-hole/js/db_graph.js index ab859122..590788d0 100644 --- a/scripts/pi-hole/js/db_graph.js +++ b/scripts/pi-hole/js/db_graph.js @@ -160,23 +160,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); } } @@ -190,28 +190,30 @@ function updateQueriesOverTime() { $(document).ready(function() { var ctx = document.getElementById("queryOverTimeChart").getContext("2d"); + var blockedColor = "#999"; + var permittedColor = "#00a65a"; timeLineChart = new Chart(ctx, { type: "bar", 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,6 +224,9 @@ $(document).ready(function() { options: { tooltips: { enabled: true, + itemSort: function(a, b) { + return b.datasetIndex - a.datasetIndex; + }, mode: "x-axis", callbacks: { title: function(tooltipItem) { @@ -232,8 +237,8 @@ $(document).ready(function() { "-" + padNumber(time.getMonth() + 1) + "-" + - padNumber(time.getDate()) + - " " + + padNumber(time.getDate()); + var from_time = padNumber(time.getHours()) + ":" + padNumber(time.getMinutes()) + @@ -245,22 +250,50 @@ $(document).ready(function() { "-" + padNumber(time.getMonth() + 1) + "-" + - padNumber(time.getDate()) + - " " + + padNumber(time.getDate()); + var until_time = padNumber(time.getHours()) + ":" + padNumber(time.getMinutes()) + ":" + padNumber(time.getSeconds()); - return "Queries from " + from_date + " to " + until_date; + + if (from_date === until_date) { + // Abbreviated form for intervals on the same day + // We split title in two lines on small screens + if ($(window).width() < 992) { + until_time += "\n"; + } + + return ("Queries from " + from_time + " to " + until_time + " on " + from_date).split( + "\n " + ); + } + + // Full tooltip for intervals spanning more than one day + // We split title in two lines on small screens + if ($(window).width() < 992) { + from_date += "\n"; + } + + return ( + "Queries from " + + from_date + + " " + + from_time + + " to " + + until_date + + " " + + until_time + ).split("\n "); }, label: function(tooltipItems, data) { - if (tooltipItems.datasetIndex === 1) { + if (tooltipItems.datasetIndex === 0) { 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; + var permitted = parseInt(data.datasets[1].data[tooltipItems.index]); + var blocked = parseInt(data.datasets[0].data[tooltipItems.index]); + if (permitted + blocked > 0) { + percentage = (100.0 * blocked) / (permitted + blocked); } return ( diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 952c93bd..5fffa248 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -250,8 +250,8 @@ function updateQueriesOverTime() { timeLineChart.data.labels.push(d); var blocked = data.ads_over_time[1][hour]; var permitted = data.domains_over_time[1][hour] - blocked; - timeLineChart.data.datasets[0].data.push(permitted); - timeLineChart.data.datasets[1].data.push(blocked); + timeLineChart.data.datasets[0].data.push(blocked); + timeLineChart.data.datasets[1].data.push(permitted); } } @@ -835,6 +835,9 @@ $(document).ready(function() { // Pull in data via AJAX updateSummaryData(); + var blockedColor = "#999"; + var permittedColor = "#00a65a"; + var ctx = document.getElementById("queryOverTimeChart").getContext("2d"); timeLineChart = new Chart(ctx, { type: "bar", @@ -842,22 +845,22 @@ $(document).ready(function() { 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: [], @@ -869,6 +872,9 @@ $(document).ready(function() { tooltips: { enabled: true, mode: "x-axis", + itemSort: function(a, b) { + return b.datasetIndex - a.datasetIndex; + }, callbacks: { title: function(tooltipItem) { var label = tooltipItem[0].xLabel; @@ -877,13 +883,13 @@ $(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 "Upstreams from " + from + " to " + to; + return "Queries from " + from + " to " + to; }, label: function(tooltipItems, data) { - if (tooltipItems.datasetIndex === 1) { + if (tooltipItems.datasetIndex === 0) { var percentage = 0.0; - var permitted = parseInt(data.datasets[0].data[tooltipItems.index]); - var blocked = parseInt(data.datasets[1].data[tooltipItems.index]); + var permitted = parseInt(data.datasets[1].data[tooltipItems.index]); + var blocked = parseInt(data.datasets[0].data[tooltipItems.index]); var total = permitted + blocked; if (total > 0) { percentage = (100.0 * blocked) / total; diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 6f1dfdd2..1346b153 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -401,11 +401,31 @@ 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'].')'; } } @@ -440,9 +460,21 @@ if ($_POST['action'] == 'get_groups') { foreach ($domains as $domain) { $input = $domain; - // Convert domain name to IDNA ASCII form for international domains if intl package is available - if (extension_loaded("intl")) - $domain = idn_to_ascii($domain); + // Convert domain name to IDNA ASCII form for international domains + 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') { @@ -463,7 +495,7 @@ if ($_POST['action'] == 'get_groups') { $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 . '
'. 'Added ' . $added . " out of ". $total . " domains"); + throw new Exception($errormsg . '
Added ' . $added . " out of ". $total . " domains"); } }