mirror of
https://github.com/pi-hole/web.git
synced 2024-12-06 19:36:21 +01:00
Merge branch 'devel' into tweak/blockpage
This commit is contained in:
@@ -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 = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
"\"": """,
|
||||
"\'": "'"
|
||||
};
|
||||
|
||||
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");
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
@@ -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 = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
"\"": """,
|
||||
"\'": "'"
|
||||
};
|
||||
|
||||
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();
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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&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&hosted_button_id=3J2L3Z4DHW9UY" target="_blank">Donate</a></strong> if you found this useful.</div>
|
||||
</footer>
|
||||
</div>
|
||||
<!-- ./wrapper -->
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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&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> <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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
?>
|
||||
Vendored
+1542
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user