Merge branch 'devel' into tweak/blockpage

This commit is contained in:
Adam Warner
2017-07-29 16:44:01 +01:00
committed by GitHub
31 changed files with 4107 additions and 474 deletions
+120
View File
@@ -0,0 +1,120 @@
/* 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. */
// Define global variables
var timeLineChart, queryTypeChart, forwardDestinationChart;
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
function escapeHtml(text) {
var map = {
"&": "&",
"<": "&lt;",
">": "&gt;",
"\"": "&quot;",
"\'": "&#039;"
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function updateTopLists() {
$.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();
var domaintable = $("#domain-frequency").find("tbody:last");
var adtable = $("#ad-frequency").find("tbody:last");
var url, domain, percentage;
for (domain in data.top_queries) {
if ({}.hasOwnProperty.call(data.top_queries,domain)){
// Sanitize domain
domain = escapeHtml(domain);
url = "<a href=\"queries.php?domain="+domain+"\">"+domain+"</a>";
percentage = data.top_queries[domain] / data.dns_queries_today * 100;
domaintable.append("<tr> <td>" + url +
"</td> <td>" + data.top_queries[domain] + "</td> <td> <button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button> <button style=\"color:orange; white-space: nowrap;\"><i class=\"fa fa-balance-scale\"></i> Audit</button> </td> </tr> ");
}
}
for (domain in data.top_ads) {
if ({}.hasOwnProperty.call(data.top_ads,domain)){
var input = domain.split(" ");
// Sanitize domain
var printdomain = escapeHtml(input[0]);
if(input.length > 1)
{
url = "<a href=\"queries.php?domain="+printdomain+"\">"+printdomain+"</a> (wildcard blocked)";
adtable.append("<tr> <td>" + url +
"</td> <td>" + data.top_ads[domain] + "</td> <td> <button style=\"color:orange; white-space: nowrap;\"><i class=\"fa fa-balance-scale\"></i> Audit</button> </td> </tr> ");
}
else
{
url = "<a href=\"queries.php?domain="+printdomain+"\">"+printdomain+"</a>";
adtable.append("<tr> <td>" + url +
"</td> <td>" + data.top_ads[domain] + "</td> <td> <button style=\"color:green; white-space: nowrap;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button> <button style=\"color:orange; white-space: nowrap;\"><i class=\"fa fa-balance-scale\"></i> Audit</button> </td> </tr> ");
}
}
}
$("#domain-frequency .overlay").hide();
$("#ad-frequency .overlay").hide();
// Update top lists data every second
setTimeout(updateTopLists, 1000);
});
}
function add(domain,list) {
var token = $("#token").html();
$.ajax({
url: "scripts/pi-hole/php/add.php",
method: "post",
data: {"domain":domain, "list":list, "token":token, "auditlog":1}
});
}
$(document).ready(function() {
// Pull in data via AJAX
updateTopLists();
$("#domain-frequency tbody").on( "click", "button", function () {
var url = ($(this).parents("tr"))[0].innerText.split(" ")[0];
if($(this).context.innerText === " Blacklist")
{
add(url,"black");
$("#gravityBtn").prop("disabled", false);
}
else
{
add(url,"audit");
}
});
$("#ad-frequency tbody").on( "click", "button", function () {
var url = ($(this).parents("tr"))[0].innerText.split(" ")[0].split(" ")[0];
if($(this).context.innerText === " Whitelist")
{
add(url,"white");
$("#gravityBtn").prop("disabled", false);
}
else
{
add(url,"audit");
}
});
});
$("#gravityBtn").on("click", function() {
window.location.replace("gravity.php?go");
});
+262
View File
@@ -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 = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"\"": "&quot;",
"\'": "&#039;"
};
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;
});
+181
View File
@@ -0,0 +1,181 @@
/* 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 = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"\"": "&quot;",
"\'": "&#039;"
};
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) {
// Clear tables before filling them with data
$("#client-frequency td").parent().remove();
var clienttable = $("#client-frequency").find("tbody:last");
var client, percentage, clientname, clientip;
var sum = 0;
for (client in data.top_sources) {
if ({}.hasOwnProperty.call(data.top_sources, client)){
sum += data.top_sources[client];
}
}
for (client in data.top_sources) {
if ({}.hasOwnProperty.call(data.top_sources, client)){
// Sanitize client
client = escapeHtml(client);
if(escapeHtml(client) !== client)
{
// Make a copy with the escaped index if necessary
data.top_sources[escapeHtml(client)] = data.top_sources[client];
}
if(client.indexOf("|") > -1)
{
var idx = client.indexOf("|");
clientname = client.substr(0, idx);
clientip = client.substr(idx+1, client.length-idx);
}
else
{
clientname = client;
clientip = client;
}
var url = clientname;
percentage = data.top_sources[client] / sum * 100.0;
clienttable.append("<tr> <td>" + url +
"</td> <td>" + data.top_sources[client] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"% of " + data.dns_queries_today + "\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " +
percentage + "%\"></div> </div> </td> </tr> ");
}
}
$("#client-frequency .overlay").hide();
});
}
function updateTopDomainsChart() {
$("#domain-frequency .overlay").show();
$.getJSON("api_db.php?topDomains&from="+from+"&until="+until, function(data) {
// Clear tables before filling them with data
$("#domain-frequency td").parent().remove();
var domaintable = $("#domain-frequency").find("tbody:last");
var domain, percentage;
var sum = 0;
for (domain in data.top_domains) {
if ({}.hasOwnProperty.call(data.top_domains, domain)){
sum += data.top_domains[domain];
}
}
for (domain in data.top_domains) {
if ({}.hasOwnProperty.call(data.top_domains, domain)){
// Sanitize domain
domain = escapeHtml(domain);
if(escapeHtml(domain) !== domain)
{
// Make a copy with the escaped index if necessary
data.top_domains[escapeHtml(domain)] = data.top_domains[domain];
}
percentage = data.top_domains[domain] / sum * 100.0;
domaintable.append("<tr> <td>" + domain +
"</td> <td>" + data.top_domains[domain] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"% of " + data.dns_queries_today + "\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " +
percentage + "%\"></div> </div> </td> </tr> ");
}
}
$("#domain-frequency .overlay").hide();
});
}
function updateTopAdsChart() {
$("#ad-frequency .overlay").show();
$.getJSON("api_db.php?topAds&from="+from+"&until="+until, function(data) {
// Clear tables before filling them with data
$("#ad-frequency td").parent().remove();
var adtable = $("#ad-frequency").find("tbody:last");
var ad, percentage;
var sum = 0;
for (ad in data.top_ads) {
if ({}.hasOwnProperty.call(data.top_ads, ad)){
sum += data.top_ads[ad];
}
}
for (ad in data.top_ads) {
if ({}.hasOwnProperty.call(data.top_ads, ad)){
// Sanitize ad
ad = escapeHtml(ad);
if(escapeHtml(ad) !== ad)
{
// Make a copy with the escaped index if necessary
data.top_ads[escapeHtml(ad)] = data.top_ads[ad];
}
percentage = data.top_ads[ad] / sum * 100.0;
adtable.append("<tr> <td>" + ad + "</td> <td>" + data.top_ads[ad] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"% of " + data.dns_queries_today + "\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " + percentage + "%\"></div> </div> </td> </tr> ");
}
}
$("#ad-frequency .overlay").hide();
});
}
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
updateTopClientsChart();
updateTopDomainsChart();
updateTopAdsChart();
});
+275
View File
@@ -0,0 +1,275 @@
/* 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;
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 () {
daterange = $("#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;
});
});
var tableApi, statistics;
function escapeRegex(text) {
var map = {
"(": "\\(",
")": "\\)",
".": "\\.",
};
return text.replace(/[().]/g, function(m) { return map[m]; });
}
function add(domain,list) {
var token = $("#token").html();
var alInfo = $("#alInfo");
var alList = $("#alList");
var alDomain = $("#alDomain");
alDomain.html(domain);
var alSuccess = $("#alSuccess");
var alFailure = $("#alFailure");
var err = $("#err");
if(list === "white")
{
alList.html("Whitelist");
}
else
{
alList.html("Blacklist");
}
alInfo.show();
alSuccess.hide();
alFailure.hide();
$.ajax({
url: "scripts/pi-hole/php/add.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)
{
alFailure.show();
err.html(response);
alFailure.delay(4000).fadeOut(2000, function() { alFailure.hide(); });
}
else
{
alSuccess.show();
alSuccess.delay(1000).fadeOut(2000, function() { alSuccess.hide(); });
}
alInfo.delay(1000).fadeOut(2000, function() {
alInfo.hide();
alList.html("");
alDomain.html("");
});
},
error: function(jqXHR, exception) {
alFailure.show();
err.html("");
alFailure.delay(1000).fadeOut(2000, function() {
alFailure.hide();
});
alInfo.delay(1000).fadeOut(2000, function() {
alInfo.hide();
alList.html("");
alDomain.html("");
});
}
});
}
function handleAjaxError( xhr, textStatus, error ) {
if ( textStatus === "timeout" )
{
alert( "The server took too long to send the data." );
}
else if(xhr.responseText.indexOf("Connection refused") >= 0)
{
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 );
}
$("#all-queries_processing").hide();
tableApi.clear();
tableApi.draw();
}
var reloadCallback = function()
{
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]++;
}
else if(data[i][4] === 3)
{
statistics[1]++;
}
else if(data[i][4] === 4)
{
statistics[3]++;
}
}
$("h3#dns_queries").text(statistics[0]);
$("h3#ads_blocked_exact").text(statistics[2]);
$("h3#ads_wildcard_blocked").text(statistics[3]);
var percent = 0.0;
if(statistics[2] + statistics[3] > 0)
{
percent = 100.0*(statistics[2] + statistics[3]) / statistics[0];
}
$("h3#ads_percentage_today").text(parseFloat(percent).toFixed(1)+" %");
};
function refreshTableData() {
var APIstring = "api_db.php?getAllQueries&from="+from+"&until="+until;
statistics = [0,0,0];
tableApi.ajax.url(APIstring).load(reloadCallback);
}
$(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)
{
$(row).css("color","red");
$("td:eq(4)", row).html( "Pi-holed" );
$("td:eq(5)", row).html( "<button style=\"color:green; white-space: nowrap;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button>" );
// statistics[2]++;
}
else if (data[4] === 2)
{
$(row).css("color","green");
$("td:eq(4)", row).html( "OK (forwarded)" );
$("td:eq(5)", row).html( "<button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button>" );
}
else if (data[4] === 3)
{
$(row).css("color","green");
$("td:eq(4)", row).html( "OK (cached)" );
$("td:eq(5)", row).html( "<button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button>" );
// statistics[1]++;
}
else if (data[4] === 4)
{
$(row).css("color","red");
$("td:eq(4)", row).html( "Pi-holed (wildcard)" );
$("td:eq(5)", row).html( "" );
// statistics[3]++;
}
else
{
$("td:eq(4)", row).html( "Unknown ("+data[4]+")" );
$("td:eq(5)", row).html( "" );
}
// statistics[0]++;
},
dom: "<'row'<'col-sm-12'f>>" +
"<'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": APIstring, "error": handleAjaxError },
"autoWidth" : false,
"processing": true,
"deferRender": true,
"order" : [[0, "desc"]],
"columns": [
{ "width" : "20%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(data).format("Y-MM-DD HH:mm:ss z");}else{return data;} }},
{ "width" : "10%" },
{ "width" : "40%" },
{ "width" : "10%" },
{ "width" : "10%" },
{ "width" : "10%" },
],
"lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
"columnDefs": [ {
"targets": -1,
"data": null,
"defaultContent": ""
} ],
"initComplete": reloadCallback
});
$("#all-queries tbody").on( "click", "button", function () {
var data = tableApi.row( $(this).parents("tr") ).data();
if (data[4] === "1")
{
add(data[2],"white");
}
else
{
add(data[2],"black");
}
} );
if(instantquery)
{
daterange.val(start__.format("MMMM Do YYYY, HH:mm") + " - " + end__.format("MMMM Do YYYY, HH:mm"));
}
} );
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
refreshTableData();
});
-86
View File
@@ -4,16 +4,6 @@
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
// User menu toggle
$("#dropdown-menu a").on("click", function(event) {
$(this).parent().toggleClass("open");
});
$("body").on("click", function(event) {
if(!$("#dropdown-menu").is(event.target) && $("#dropdown-menu").has(event.target).length === 0) {
$("#dropdown-menu").removeClass("open");
}
});
//The following functions allow us to display time until pi-hole is enabled after disabling.
//Works between all pages
@@ -141,76 +131,6 @@ $("#pihole-disable-custom").on("click", function(e){
piholeChange("disable",custVal);
});
var piholeVersion = $("#piholeVersion").html();
var webVersion = $("#webVersion").html();
var FTLVersion = $("#FTLVersion").html();
// Credit for following function: https://gist.github.com/alexey-bass/1115557
// Modified to discard any possible "v" in the string
function versionCompare(left, right) {
if (typeof left + typeof right !== "stringstring")
{
return false;
}
// If we are on vDev then we assume that it is always
// newer than the latest online release, i.e. version
// comparison should return 1
if(left === "vDev")
{
return 1;
}
var aa = left.split("v"),
bb = right.split("v");
var a = aa[aa.length-1].split(".")
, b = bb[bb.length-1].split(".")
, i = 0, len = Math.max(a.length, b.length);
for (; i < len; i++) {
if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) {
return 1;
} else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) {
return -1;
}
}
return 0;
}
// Update check
$.getJSON("https://api.github.com/repos/pi-hole/pi-hole/releases/latest", function(json) {
if(versionCompare(piholeVersion, json.tag_name.slice(1)) < 0) {
// Alert user
$("#piholeVersion").html($("#piholeVersion").text() + " <a class=\"alert-link lookatme\" href=\"https://github.com/pi-hole/pi-hole/releases\">(Update available!)</a>");
$("#alPiholeUpdate").show();
}
});
$.getJSON("https://api.github.com/repos/pi-hole/AdminLTE/releases/latest", function(json) {
if(versionCompare(webVersion, json.tag_name.slice(1)) < 0) {
// Alert user
$("#webVersion").html($("#webVersion").text() + " <a class=\"alert-link lookatme\" href=\"https://github.com/pi-hole/adminLTE/releases\">(Update available!)</a>");
$("#alWebUpdate").show();
}
});
$.getJSON("https://api.github.com/repos/pi-hole/FTL/releases/latest", function(json) {
if(versionCompare(FTLVersion, json.tag_name.slice(1)) < 0) {
// Alert user
$("#FTLVersion").html($("#FTLVersion").text() + " <a class=\"alert-link lookatme\" href=\"https://github.com/pi-hole/FTL/releases\">(Update available!)</a>");
$("#alFTLUpdate").show();
}
});
/*
* Make sure that Pi-hole is updated to at least v2.7, since that is needed to use the sudo
* features of the interface. Skip if on dev
*/
if(versionCompare(piholeVersion, "v2.7") < 0) {
alert("Pi-hole needs to be updated to at least v2.7 before you can use features such as whitelisting/blacklisting from this web interface!");
}
// Session timer
var sessionvalidity = parseInt(document.getElementById("sessiontimercounter").textContent);
var start = new Date;
@@ -255,12 +175,6 @@ else
document.getElementById("sessiontimer").style.display = "none";
}
// Hide "exact match" button on queryads.php page if version is 2.9.5 or lower
if(versionCompare(piholeVersion, "v2.9.5") < 1)
{
$("#btnSearchExact").hide();
}
// Handle Strg + Enter button on Login page
$(document).keypress(function(e) {
if((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey && $("#loginpw").is(":focus")) {
-5
View File
@@ -45,11 +45,6 @@ $("#gravityBtn").on("click", function(){
eventsource();
});
$("#gravityBtn").on("click", () => {
$("#gravityBtn").attr("disabled", true);
eventsource();
});
// Handle hiding of alerts
$(function(){
$("[data-hide]").on("click", function(){
+392 -195
View File
@@ -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) {
@@ -186,11 +230,13 @@ function updateForwardedOverTime() {
// Collect values and colors, and labels
forwardDestinationChart.data.datasets[0].backgroundColor = colors[0];
forwardDestinationChart.data.datasets[0].pointRadius = 0;
forwardDestinationChart.data.datasets[0].pointHitRadius = 5;
forwardDestinationChart.data.datasets[0].pointHoverRadius = 5;
forwardDestinationChart.data.datasets[0].label = labels[0];
for (i = forwardDestinationChart.data.datasets.length; i < plotdata[0].length; i++)
{
forwardDestinationChart.data.datasets.push({data: [], backgroundColor: colors[i], pointRadius: 0, label: labels[i]});
forwardDestinationChart.data.datasets.push({data: [], backgroundColor: colors[i], pointRadius: 0, pointHitRadius: 5, pointHoverRadius: 5, label: labels[i]});
}
// Add data for each dataset that is available
@@ -231,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 = {
@@ -260,6 +346,11 @@ function updateTopClientsChart() {
if ({}.hasOwnProperty.call(data.top_sources, client)){
// Sanitize client
if(escapeHtml(client) !== client)
{
// Make a copy with the escaped index if necessary
data.top_sources[escapeHtml(client)] = data.top_sources[client];
}
client = escapeHtml(client);
if(client.indexOf("|") > -1)
{
@@ -305,6 +396,11 @@ function updateTopLists() {
for (domain in data.top_queries) {
if ({}.hasOwnProperty.call(data.top_queries,domain)){
// Sanitize domain
if(escapeHtml(domain) !== domain)
{
// Make a copy with the escaped index if necessary
data.top_queries[escapeHtml(domain)] = data.top_queries[domain];
}
domain = escapeHtml(domain);
url = "<a href=\"queries.php?domain="+domain+"\">"+domain+"</a>";
percentage = data.top_queries[domain] / data.dns_queries_today * 100;
@@ -323,6 +419,11 @@ function updateTopLists() {
for (domain in data.top_ads) {
if ({}.hasOwnProperty.call(data.top_ads,domain)){
// Sanitize domain
if(escapeHtml(domain) !== domain)
{
// Make a copy with the escaped index if necessary
data.top_ads[escapeHtml(domain)] = data.top_ads[domain];
}
domain = escapeHtml(domain);
url = "<a href=\"queries.php?domain="+domain+"\">"+domain+"</a>";
percentage = data.top_ads[domain] / data.ads_blocked_today * 100;
@@ -358,8 +459,8 @@ function updateSummaryData(runOnce) {
if("FTLnotrunning" in data)
{
data["ads_blocked_today"] = "Lost";
data["dns_queries_today"] = "connection";
data["dns_queries_today"] = "Lost";
data["ads_blocked_today"] = "connection";
data["ads_percentage_today"] = "to";
data["domains_being_blocked"] = "API";
// Adjust text
@@ -389,19 +490,19 @@ function updateSummaryData(runOnce) {
}
}
["ads_blocked_today", "dns_queries_today", "ads_percentage_today"].forEach(function(today) {
var todayElement = $("h3#" + today);
["ads_blocked_today", "dns_queries_today", "ads_percentage_today", "unique_clients"].forEach(function(today) {
var todayElement = $("span#" + today);
todayElement.text() !== data[today] &&
todayElement.text() !== data[today] + "%" &&
todayElement.addClass("glow");
$("span#" + today).addClass("glow");
});
window.setTimeout(function() {
["ads_blocked_today", "dns_queries_today", "domains_being_blocked", "ads_percentage_today"].forEach(function(header, idx) {
["ads_blocked_today", "dns_queries_today", "domains_being_blocked", "ads_percentage_today", "unique_clients"].forEach(function(header, idx) {
var textData = (idx === 3 && data[header] !== "to") ? data[header] + "%" : data[header];
$("h3#" + header).text(textData);
$("span#" + header).text(textData);
});
$("h3.statistic.glow").removeClass("glow");
$("span.glow").removeClass("glow");
}, 500);
}).done(function() {
@@ -420,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: {
@@ -486,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) + "%";
}
}
},
@@ -523,141 +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: {
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
}]
// 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: []
},
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,
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,
data: []
label: function(tooltipItems, data) {
return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
}
]
}
},
options: {
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) + " %";
}
legend: {
display: false
},
scales: {
xAxes: [{
type: "time",
time: {
unit: "hour",
displayFormats: {
hour: "HH:mm"
},
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;
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
}
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();
}
});
+13 -5
View File
@@ -61,7 +61,7 @@ function refresh(fade) {
{
listw.html("");
}
var data = JSON.parse(response);
var data = JSON.parse(response).sort();
if(data.length === 0) {
$("h3").hide();
@@ -208,11 +208,19 @@ $(function(){
});
});
$(document).ready(function () {
if (screen.width < 576) {
$(".input-group-btn").css("display", "initial");
// Wrap form-group's buttons to next line when viewed on a small screen
$(window).on("resize",function() {
if ($(window).width() < 991) {
$(".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 {
$(".input-group-btn").css("display", "table-cell");
$(".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() {
$(window).trigger("resize");
});
+9 -3
View File
@@ -152,6 +152,12 @@ $(document).ready(function() {
$("td:eq(4)", row).html( "Pi-holed (wildcard)" );
$("td:eq(5)", row).html( "" );
}
else if (data[4] === "5")
{
$(row).css("color","red");
$("td:eq(4)", row).html( "Pi-holed (blacklist)" );
$("td:eq(5)", row).html( "<button style=\"color:green; white-space: nowrap;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button>" );
}
else
{
$("td:eq(4)", row).html( "Unknown" );
@@ -169,8 +175,8 @@ $(document).ready(function() {
"columns": [
{ "width" : "20%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(data).format("Y-MM-DD HH:mm:ss z");}else{return data;} }},
{ "width" : "10%" },
{ "width" : "40%" },
{ "width" : "10%" },
{ "width" : "40%", "render": $.fn.dataTable.render.text() },
{ "width" : "10%", "render": $.fn.dataTable.render.text() },
{ "width" : "10%" },
{ "width" : "10%" },
],
@@ -183,7 +189,7 @@ $(document).ready(function() {
});
$("#all-queries tbody").on( "click", "button", function () {
var data = tableApi.row( $(this).parents("tr") ).data();
if (data[4] === "1")
if (data[4] === "1" || data[4] === "5")
{
add(data[2],"white");
}
+17
View File
@@ -122,3 +122,20 @@ $("#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() {
if ($(window).width() < 991) {
$(".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 > input").css("margin-bottom","");
$(".form-group.input-group > .btn-block.text-center").removeClass("btn-block text-center").addClass("input-group-btn");
}
});
$(document).ready(function() {
$(window).trigger("resize");
});
+23 -3
View File
@@ -16,13 +16,33 @@ list_verify($type);
switch($type) {
case "white":
echo exec("sudo pihole -w -q ${_POST['domain']}");
if(!isset($_POST["auditlog"]))
echo exec("sudo pihole -w -q ${_POST['domain']}");
else
{
echo exec("sudo pihole -w -q -n ${_POST['domain']}");
echo exec("sudo pihole -a audit ${_POST['domain']}");
}
break;
case "black":
echo exec("sudo pihole -b -q ${_POST['domain']}");
if(!isset($_POST["auditlog"]))
echo exec("sudo pihole -b -q ${_POST['domain']}");
else
{
echo exec("sudo pihole -b -q -n ${_POST['domain']}");
echo exec("sudo pihole -a audit ${_POST['domain']}");
}
break;
case "wild":
echo exec("sudo pihole -wild -q ${_POST['domain']}");
if(!isset($_POST["auditlog"]))
echo exec("sudo pihole -wild -q ${_POST['domain']}");
else
{
echo exec("sudo pihole -wild -q -n ${_POST['domain']}");
echo exec("sudo pihole -a audit ${_POST['domain']}");
}
case "audit":
echo exec("sudo pihole -a audit ${_POST['domain']}");
break;
}
+22 -41
View File
@@ -39,50 +39,31 @@
</div>
</div>
</div>
<?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";
?>
<!-- /.content-wrapper -->
<footer class="main-footer">
<?php
// Check if on a dev branch
$piholeBranch = exec("cd /etc/.pihole/ && git rev-parse --abbrev-ref HEAD");
$webBranch = exec("git rev-parse --abbrev-ref HEAD");
// Use vDev if not on master
if($piholeBranch !== "master") {
$piholeVersion = "vDev";
$piholeCommit = exec("cd /etc/.pihole/ && git describe --long --dirty --tags");
}
else {
$piholeVersion = exec("cd /etc/.pihole/ && git describe --tags --abbrev=0");
}
if($webBranch !== "master") {
$webVersion = "vDev";
$webCommit = exec("git describe --long --dirty --tags");
}
else {
$webVersion = exec("git describe --tags --abbrev=0");
}
$FTLVersion = exec("pihole-FTL version");
?>
<style type="text/css">
@-webkit-keyframes Pulse{
from {color:#630030;-webkit-text-shadow:0 0 9px #333;}
50% {color:#e33100;-webkit-text-shadow:0 0 32px #e33100;}
to {color:#630030;-webkit-text-shadow:0 0 9px #333;}
}
a.lookatme {
-webkit-animation-name: Pulse;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: infinite;
}
</style>
<div class="pull-right hidden-xs <?php if(isset($piholeCommit) || isset($webCommit)) { ?>hidden-md<?php } ?>">
<b>Pi-hole Version </b> <span id="piholeVersion"><?php echo $piholeVersion; ?></span><?php if(isset($piholeCommit)) { echo " (".$piholeBranch.", ".$piholeCommit.")"; } ?>
<b>Web Interface Version </b> <span id="webVersion"><?php echo $webVersion; ?></span><?php if(isset($webCommit)) { echo " (".$webBranch.", ".$webCommit.")"; } ?>
<b>FTL Version </b> <span id="FTLVersion"><?php echo $FTLVersion; ?></span>
<!-- Version Infos -->
<div class="pull-right hidden-xs hidden-sm<?php if(isset($core_commit) || isset($web_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" 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" target="_blank">(Update available!)</a><?php } ?>
<b>FTL Version </b> <?php
echo $FTL_current;
if($FTL_update){ ?> <a class="alert-link lookatme" href="https://github.com/pi-hole/FTL/releases" target="_blank">(Update available!)</a><?php } ?>
</div>
<div><a href="https://github.com/pi-hole"><i class="fa fa-github"></i></a> <strong><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=3J2L3Z4DHW9UY">Donate</a></strong> if you found this useful.</div>
<div style="display: inline-block"><a href="https://github.com/pi-hole" target="_blank"><i class="fa fa-github"></i></a> <strong><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=3J2L3Z4DHW9UY" target="_blank">Donate</a></strong> if you found this useful.</div>
</footer>
</div>
<!-- ./wrapper -->
+3 -3
View File
@@ -8,9 +8,9 @@
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
preg_match("/^.{1,253}$/", $domain_name) && //overall length check
preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)); //length of each label
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
}
function checkfile($filename) {
+71 -27
View File
@@ -183,6 +183,7 @@
<link href="style/vendor/font-awesome-4.5.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<link href="style/vendor/ionicons-2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css" />
<link href="style/vendor/dataTables.bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="style/vendor/daterangepicker.css" rel="stylesheet" type="text/css" />
<link href="style/vendor/AdminLTE.min.css" rel="stylesheet" type="text/css" />
<link href="style/vendor/skin-blue.min.css" rel="stylesheet" type="text/css" />
@@ -190,7 +191,7 @@
<link rel="icon" type="image/png" sizes="160x160" href="img/logo.svg" />
<style type="text/css">
.glow { text-shadow: 0px 0px 5px #fff; }
h3 { transition-duration: 500ms }
.small-box span { transition-duration: 500ms }
</style>
<!--[if lt IE 9]>
@@ -219,7 +220,7 @@ if($auth) {
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="http://pi-hole.net" class="logo">
<a href="http://pi-hole.net" class="logo" target="_blank">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini">P<b>h</b></span>
<!-- logo for regular state and mobile devices -->
@@ -233,16 +234,16 @@ if($auth) {
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<!-- User Account: style can be found in dropdown.less -->
<li id="dropdown-menu" class="dropdown user user-menu">
<a href="#" class="dropdown-toggle">
<li><a style="pointer-events:none;"><samp><?php echo gethostname(); ?></samp></a></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: initial" sizes="160x160" alt="Pi-hole logo" />
<span class="hidden-xs">Pi-hole</span>
</a>
<ul class="dropdown-menu">
<ul class="dropdown-menu" style="right:0">
<!-- User image -->
<li class="user-header">
<img src="img/logo.svg" sizes="160x160" alt="User Image" />
<img src="img/logo.svg" sizes="160x160" alt="User Image" style="border-color:transparent" />
<p>
Open Source Ad Blocker
<small>Designed For Raspberry Pi</small>
@@ -251,33 +252,41 @@ if($auth) {
<!-- Menu Body -->
<li class="user-body">
<div class="col-xs-4 text-center">
<a href="https://github.com/pi-hole/pi-hole">GitHub</a>
<a class="btn-link" href="https://github.com/pi-hole/pi-hole" target="_blank">GitHub</a>
</div>
<div class="col-xs-4 text-center">
<a href="http://jacobsalmela.com/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0/">Details</a>
<a class="btn-link" href="http://jacobsalmela.com/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0/" target="_blank">Details</a>
</div>
<div class="col-xs-4 text-center">
<a href="https://github.com/pi-hole/pi-hole/releases">Updates</a>
<a class="btn-link" href="https://github.com/pi-hole/pi-hole/releases" 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>
<div class="col-xs-12 text-center" id="sessiontimer">Session is valid for <span id="sessiontimercounter"><?php if($auth && strlen($pwhash) > 0){echo $maxlifetime;}else{echo "0";} ?></span></div>
</li>
<!-- Menu Footer -->
<li class="user-footer">
<!-- Update alerts -->
<div id="alPiholeUpdate" class="alert alert-info alert-dismissible fade in" role="alert" hidden>
<a class="alert-link" href="https://github.com/pi-hole/pi-hole/releases">There's an update available for this Pi-hole!</a>
<!-- 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" 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" 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" target="_blank" style="background:none">(Update available!)</a><?php } ?><br><br>
</div>
<div id="alWebUpdate" class="alert alert-info alert-dismissible fade in" role="alert" hidden>
<a class="alert-link" href="https://github.com/pi-hole/AdminLTE/releases">There's an update available for this Web Interface!</a>
</div>
*/ ?>
<!-- PayPal -->
<div>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="3J2L3Z4DHW9UY">
<input style="display: block; margin: 0 auto;" type="image" src="img/donate.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
</form>
<div class="text-center">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=3J2L3Z4DHW9UY" target="_blank" style="background:none">
<img src="img/donate.gif" alt="Donate">
</a>
</div>
</li>
</ul>
@@ -389,6 +398,10 @@ if($auth) {
$scriptname = "blacklist";
}
}
if(!$auth && (!isset($indexpage) || isset($_GET['login'])))
{
$scriptname = "login";
}
?>
<ul class="sidebar-menu">
<li class="header">MAIN NAVIGATION</li>
@@ -405,6 +418,31 @@ if($auth) {
<i class="fa fa-file-text-o"></i> <span>Query Log</span>
</a>
</li>
<li class="treeview<?php if($scriptname === "db_queries.php" || $scriptname === "db_lists.php" || $scriptname === "db_graph.php"){ ?> active<?php } ?>">
<a href="#">
<i class="fa fa-clock-o"></i> <span>Long term data</span>
<span class="pull-right-container">
<i class="fa fa-angle-down pull-right" style="padding-right: 5px;"></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-text-o"></i> <span>Graphics</span>
</a>
</li>
<li<?php if($scriptname === "db_queries.php"){ ?> class="active"<?php } ?>>
<a href="db_queries.php">
<i class="fa fa-file-text-o"></i> <span>Query Log</span>
</a>
</li>
<li<?php if($scriptname === "db_lists.php"){ ?> class="active"<?php } ?>>
<a href="db_lists.php">
<i class="fa fa-file-text-o"></i> <span>Top Lists</span>
</a>
</li>
</ul>
</li>
<!-- Whitelist -->
<li<?php if($scriptname === "whitelist"){ ?> class="active"<?php } ?>>
<a href="list.php?l=white">
@@ -459,7 +497,7 @@ if($auth) {
<a href="#"><i class="fa fa-play"></i> <span id="enableLabel">Enable</span>&nbsp;&nbsp;&nbsp;<span id="flip-status-enable"></span></a>
</li>
<!-- Tools -->
<li class="treeview <?php if($scriptname === "gravity.php" || $scriptname === "queryads.php" || $scriptname === "debug.php"){ ?>active<?php } ?>">
<li class="treeview <?php if(in_array($scriptname, array("gravity.php", "queryads.php", "auditlog.php", "taillog.php", "taillog-FTL.php", "debug.php"))){ ?>active<?php } ?>">
<a href="#">
<i class="fa fa-folder"></i> <span>Tools</span>
<span class="pull-right-container">
@@ -479,6 +517,12 @@ if($auth) {
<i class="fa fa-search"></i> <span>Query adlists</span>
</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>
</a>
</li>
<!-- Tail pihole.log -->
<li<?php if($scriptname === "taillog.php"){ ?> class="active"<?php } ?>>
<a href="taillog.php">
@@ -520,7 +564,7 @@ if($auth) {
<?php
// Show Login button if $auth is *not* set and authorization is required
if(strlen($pwhash) > 0 && !$auth) { ?>
<li>
<li<?php if($scriptname === "login"){ ?> class="active"<?php } ?>>
<a href="index.php?login">
<i class="fa fa-user"></i> <span>Login</span>
</a>
@@ -528,7 +572,7 @@ if($auth) {
<?php } ?>
<!-- Donate -->
<li>
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY">
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY" target="_blank">
<i class="fa fa-paypal"></i> <span>Donate</span>
</a>
</li>
+1 -1
View File
@@ -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. */ ?>
<div class="mainbox col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3">
<div class="mainbox col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2" style="float:none">
<div class="panel panel-default">
<div class="panel-heading">
<div style="text-align: center;"><img src="img/logo.svg" width="<?php if ($boxedlayout) { ?>50%<?php } else { ?>30%<?php } ?>"></div><br>
+6 -1
View File
@@ -21,6 +21,11 @@ else
$file = fopen("/var/log/pihole.log","r");
}
if(!$file)
{
die(json_encode(array("offset" => 0, "lines" => array("Failed to open log file. Check permissions!\n"))));
}
if(isset($_GET["offset"]))
{
$offset = intval($_GET['offset']);
@@ -30,7 +35,7 @@ if(isset($_GET["offset"]))
fseek($file, $offset);
$lines = [];
while (!feof($file))
array_push($lines,fgets($file));
array_push($lines, htmlspecialchars(fgets($file)));
die(json_encode(array("offset" => ftell($file), "lines" => $lines)));
}
}
+118
View File
@@ -0,0 +1,118 @@
<?php
// Allow file_get_contents to read remote sources via URL
ini_set("allow_url_fopen", 1);
// Function that grabs latest tag from GitHub
function get_github_version($url)
{
// Have to fake the user agent to be able to query from Github's API
$context = stream_context_create(array("http" => array("user_agent" => "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36")));
$json = file_get_contents($url, false, $context);
$result = json_decode($json);
return $result->tag_name;
}
/********** Get Pi-hole core branch / version / commit **********/
// Check if on a dev branch
$core_branch = exec("cd /etc/.pihole/ && git rev-parse --abbrev-ref HEAD");
if($core_branch !== "master") {
$core_current = "vDev";
$core_commit = exec("cd /etc/.pihole/ && git describe --long --dirty --tags");
}
else {
$core_current = exec("cd /etc/.pihole/ && git describe --tags --abbrev=0");
}
/********** Get Pi-hole web branch / version / commit **********/
$web_branch = exec("git rev-parse --abbrev-ref HEAD");
if($web_branch !== "master") {
$web_current = "vDev";
$web_commit = exec("git describe --long --dirty --tags");
}
else {
$web_current = exec("git describe --tags --abbrev=0");
}
/********** Get Pi-hole FTL version (not a git repository) **********/
$FTL_current = exec("pihole-FTL version");
// can write only in the root web dir, i.e. /var/www/html
$versionfile = "../versions";
$check_version = false;
// Check version if version buffer file does not exist
if(is_readable($versionfile))
{
// Obtain latest time stamp from buffer file
$versions = explode(",",file_get_contents($versionfile));
$date = date_create();
$timestamp = date_timestamp_get($date);
// Is last check for updates older than 30 minutes?
if($timestamp >= intval($versions[0]) + 1800)
{
// Yes: Retrieve new/updated version data
$check_version = true;
}
else
{
// No: We can use the buffered data
$check_version = false;
}
}
else
{
// No buffer file or not readable: Request version data
$check_version = true;
}
// Get data from GitHub if requested
if($check_version){
$core_latest = get_github_version("https://api.github.com/repos/pi-hole/pi-hole/releases/latest");
$web_latest = get_github_version("https://api.github.com/repos/pi-hole/AdminLTE/releases/latest");
$FTL_latest = get_github_version("https://api.github.com/repos/pi-hole/FTL/releases/latest");
// Save to buffer file
file_put_contents($versionfile, array($timestamp,",",$core_latest,",",$web_latest,",",$FTL_latest));
}
else
{
// Use data from buffer file
$core_latest = $versions[1];
$web_latest = $versions[2];
$FTL_latest = $versions[3];
}
// Core version comparison
if($core_current !== "vDev")
{
// This logic allows the local core version to be newer than the upstream version
// The update indicator is only shown if the upstream version is NEWER
$core_update = (version_compare($core_current, $core_latest) < 0);
}
else
{
$core_update = false;
}
// Web version comparison
if($web_current !== "vDev")
{
// This logic allows the local core version to be newer than the upstream version
// The update indicator is only shown if the upstream version is NEWER
$web_update = (version_compare($web_current, $web_latest) < 0);
}
else
{
$web_update = false;
}
// FTL version comparison
// This logic allows the local core version to be newer than the upstream version
// The update indicator is only shown if the upstream version is NEWER
$FTL_update = (version_compare($FTL_current, $FTL_latest) < 0);
?>
+1542
View File
File diff suppressed because it is too large Load Diff