mirror of
https://github.com/pi-hole/web.git
synced 2026-05-02 14:42:23 +01:00
Merge pull request #1222 from pi-hole/tweak/exact_searching
Improve filtering on table pages
This commit is contained in:
@@ -8,6 +8,22 @@
|
||||
/* global moment:false, utils:false */
|
||||
|
||||
var tableApi;
|
||||
var tableFilters = [];
|
||||
|
||||
var replyTypes = [
|
||||
"N/A",
|
||||
"NODATA",
|
||||
"NXDOMAIN",
|
||||
"CNAME",
|
||||
"IP",
|
||||
"DOMAIN",
|
||||
"RRNAME",
|
||||
"SERVFAIL",
|
||||
"REFUSED",
|
||||
"NOTIMP",
|
||||
"upstream error"
|
||||
];
|
||||
var colTypes = ["time", "query type", "domain", "client", "status", "reply type"];
|
||||
|
||||
function add(domain, list) {
|
||||
var token = $("#token").text();
|
||||
@@ -103,10 +119,6 @@ function handleAjaxError(xhr, textStatus) {
|
||||
tableApi.draw();
|
||||
}
|
||||
|
||||
function autofilter() {
|
||||
return $("#autofilter").prop("checked");
|
||||
}
|
||||
|
||||
$(function () {
|
||||
// Do we want to filter queries?
|
||||
var GETDict = {};
|
||||
@@ -140,7 +152,7 @@ $(function () {
|
||||
rowCallback: function (row, data) {
|
||||
// DNSSEC status
|
||||
var dnssecStatus;
|
||||
switch (data[5]) {
|
||||
switch (data[6]) {
|
||||
case "1":
|
||||
dnssecStatus = '<br><span class="text-green">SECURE</span>';
|
||||
break;
|
||||
@@ -162,37 +174,32 @@ $(function () {
|
||||
}
|
||||
|
||||
// Query status
|
||||
var blocked,
|
||||
fieldtext,
|
||||
var fieldtext,
|
||||
buttontext,
|
||||
colorClass,
|
||||
colorClass = false,
|
||||
isCNAME = false,
|
||||
regexLink = false;
|
||||
|
||||
switch (data[4]) {
|
||||
case "1":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked (gravity)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
case "2":
|
||||
blocked = false;
|
||||
colorClass = "text-green";
|
||||
fieldtext = "OK <br class='hidden-lg'>(forwarded)" + dnssecStatus;
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
|
||||
break;
|
||||
case "3":
|
||||
blocked = false;
|
||||
colorClass = "text-green";
|
||||
fieldtext = "OK <br class='hidden-lg'>(cached)" + dnssecStatus;
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
|
||||
break;
|
||||
case "4":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist)";
|
||||
|
||||
@@ -204,32 +211,27 @@ $(function () {
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
case "5":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist)";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
case "6":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(external, IP)";
|
||||
buttontext = "";
|
||||
break;
|
||||
case "7":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(external, NULL)";
|
||||
buttontext = "";
|
||||
break;
|
||||
case "8":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(external, NXRA)";
|
||||
buttontext = "";
|
||||
break;
|
||||
case "9":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked (gravity, CNAME)";
|
||||
buttontext =
|
||||
@@ -237,7 +239,6 @@ $(function () {
|
||||
isCNAME = true;
|
||||
break;
|
||||
case "10":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(regex blacklist, CNAME)";
|
||||
|
||||
@@ -250,7 +251,6 @@ $(function () {
|
||||
isCNAME = true;
|
||||
break;
|
||||
case "11":
|
||||
blocked = true;
|
||||
colorClass = "text-red";
|
||||
fieldtext = "Blocked <br class='hidden-lg'>(exact blacklist, CNAME)";
|
||||
buttontext =
|
||||
@@ -258,12 +258,13 @@ $(function () {
|
||||
isCNAME = true;
|
||||
break;
|
||||
default:
|
||||
blocked = false;
|
||||
colorClass = false;
|
||||
fieldtext = "Unknown (" + parseInt(data[4], 10) + ")";
|
||||
buttontext = "";
|
||||
}
|
||||
|
||||
fieldtext += '<input type="hidden" name="id" value="' + data[4] + '">';
|
||||
|
||||
if (colorClass !== false) {
|
||||
$(row).addClass(colorClass);
|
||||
}
|
||||
@@ -306,49 +307,17 @@ $(function () {
|
||||
}
|
||||
|
||||
// Check for existence of sixth column and display only if not Pi-holed
|
||||
var replytext;
|
||||
if (data.length > 6 && !blocked) {
|
||||
switch (data[6]) {
|
||||
case "0":
|
||||
replytext = "N/A";
|
||||
break;
|
||||
case "1":
|
||||
replytext = "NODATA";
|
||||
break;
|
||||
case "2":
|
||||
replytext = "NXDOMAIN";
|
||||
break;
|
||||
case "3":
|
||||
replytext = "CNAME";
|
||||
break;
|
||||
case "4":
|
||||
replytext = "IP";
|
||||
break;
|
||||
case "5":
|
||||
replytext = "DOMAIN";
|
||||
break;
|
||||
case "6":
|
||||
replytext = "RRNAME";
|
||||
break;
|
||||
case "7":
|
||||
replytext = "SERVFAIL";
|
||||
break;
|
||||
case "8":
|
||||
replytext = "REFUSED";
|
||||
break;
|
||||
case "9":
|
||||
replytext = "NOTIMP";
|
||||
break;
|
||||
case "10":
|
||||
replytext = "upstream error";
|
||||
break;
|
||||
default:
|
||||
replytext = "? (" + parseInt(data[6], 10) + ")";
|
||||
}
|
||||
var replytext,
|
||||
replyid = data[5];
|
||||
|
||||
if (replyid >= 0 && replyid < replyTypes.length) {
|
||||
replytext = replyTypes[replyid];
|
||||
} else {
|
||||
replytext = "-";
|
||||
replytext = "? (" + replyid + ")";
|
||||
}
|
||||
|
||||
replytext += '<input type="hidden" name="id" value="' + replyid + '">';
|
||||
|
||||
$("td:eq(5)", row).html(replytext);
|
||||
|
||||
if (data.length > 7) {
|
||||
@@ -368,6 +337,10 @@ $(function () {
|
||||
var dataIndex = 0;
|
||||
return data.data.map(function (x) {
|
||||
x[0] = x[0] * 1e6 + dataIndex++;
|
||||
var dnssec = x[5];
|
||||
var reply = x[6];
|
||||
x[5] = reply;
|
||||
x[6] = dnssec;
|
||||
return x;
|
||||
});
|
||||
}
|
||||
@@ -415,77 +388,100 @@ $(function () {
|
||||
],
|
||||
initComplete: function () {
|
||||
var api = this.api();
|
||||
|
||||
// Query type IPv4 / IPv6
|
||||
api.$("td:eq(1)").click(function () {
|
||||
if (autofilter()) {
|
||||
api.search(this.textContent).draw();
|
||||
$("#resetButton").removeClass("hidden");
|
||||
}
|
||||
});
|
||||
api.$("td:eq(1)").hover(
|
||||
function () {
|
||||
if (autofilter()) {
|
||||
this.title = "Click to show only " + this.textContent + " queries";
|
||||
this.style.color = "#72afd2";
|
||||
} else {
|
||||
this.title = "";
|
||||
this.style.color = "";
|
||||
api
|
||||
.$("td:eq(1)")
|
||||
.click(function (event) {
|
||||
addColumnFilter(event, 1, this.textContent);
|
||||
})
|
||||
.hover(
|
||||
function () {
|
||||
$(this).addClass("pointer").attr("title", tooltipText(1, this.textContent));
|
||||
},
|
||||
function () {
|
||||
$(this).removeClass("pointer");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
this.style.color = "";
|
||||
}
|
||||
);
|
||||
api.$("td:eq(1)").addClass("pointer");
|
||||
);
|
||||
|
||||
// Domain
|
||||
api.$("td:eq(2)").click(function () {
|
||||
if (autofilter()) {
|
||||
var domain = this.textContent.split("\n")[0];
|
||||
api.search(domain).draw();
|
||||
$("#resetButton").removeClass("hidden");
|
||||
}
|
||||
});
|
||||
api.$("td:eq(2)").hover(
|
||||
function () {
|
||||
if (autofilter()) {
|
||||
var domain = this.textContent.split("\n")[0];
|
||||
this.title = "Click to show only queries with domain " + domain;
|
||||
this.style.color = "#72afd2";
|
||||
} else {
|
||||
this.title = "";
|
||||
this.style.color = "";
|
||||
api
|
||||
.$("td:eq(2)")
|
||||
.click(function (event) {
|
||||
addColumnFilter(event, 2, this.textContent.split("\n")[0]);
|
||||
})
|
||||
.hover(
|
||||
function () {
|
||||
$(this).addClass("pointer").attr("title", tooltipText(2, this.textContent));
|
||||
},
|
||||
function () {
|
||||
$(this).removeClass("pointer");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
this.style.color = "";
|
||||
}
|
||||
);
|
||||
api.$("td:eq(2)").addClass("pointer");
|
||||
);
|
||||
|
||||
// Client
|
||||
api.$("td:eq(3)").click(function () {
|
||||
if (autofilter()) {
|
||||
api.search(this.textContent).draw();
|
||||
$("#resetButton").removeClass("hidden");
|
||||
}
|
||||
});
|
||||
api.$("td:eq(3)").hover(
|
||||
function () {
|
||||
if (autofilter()) {
|
||||
this.title = "Click to show only queries made by " + this.textContent;
|
||||
this.style.color = "#72afd2";
|
||||
} else {
|
||||
this.title = "";
|
||||
this.style.color = "";
|
||||
api
|
||||
.$("td:eq(3)")
|
||||
.click(function (event) {
|
||||
addColumnFilter(event, 3, this.textContent);
|
||||
})
|
||||
.hover(
|
||||
function () {
|
||||
$(this).addClass("pointer").attr("title", tooltipText(3, this.textContent));
|
||||
},
|
||||
function () {
|
||||
$(this).removeClass("pointer");
|
||||
}
|
||||
},
|
||||
function () {
|
||||
this.style.color = "";
|
||||
}
|
||||
);
|
||||
api.$("td:eq(3)").addClass("pointer");
|
||||
);
|
||||
|
||||
// Status
|
||||
api
|
||||
.$("td:eq(4)")
|
||||
.click(function (event) {
|
||||
var id = this.children.id.value;
|
||||
var text = this.textContent;
|
||||
addColumnFilter(event, 4, id + "#" + text);
|
||||
})
|
||||
.hover(
|
||||
function () {
|
||||
$(this).addClass("pointer").attr("title", tooltipText(4, this.textContent));
|
||||
},
|
||||
function () {
|
||||
$(this).removeClass("pointer");
|
||||
}
|
||||
);
|
||||
|
||||
// Reply type
|
||||
api
|
||||
.$("td:eq(5)")
|
||||
.click(function (event) {
|
||||
var id = this.children.id.value;
|
||||
var text = this.textContent.split(" ")[0];
|
||||
addColumnFilter(event, 5, id + "#" + text);
|
||||
})
|
||||
.hover(
|
||||
function () {
|
||||
$(this).addClass("pointer").attr("title", tooltipText(5, this.textContent));
|
||||
},
|
||||
function () {
|
||||
$(this).removeClass("pointer");
|
||||
}
|
||||
);
|
||||
|
||||
// Disable autocorrect in the search box
|
||||
var input = $("input[type=search]");
|
||||
if (input !== null) {
|
||||
input.attr("autocomplete", "off");
|
||||
input.attr("autocorrect", "off");
|
||||
input.attr("autocapitalize", "off");
|
||||
input.attr("spellcheck", false);
|
||||
input.attr("placeholder", "Type / Domain / Client");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
resetColumnsFilters();
|
||||
|
||||
$("#all-queries tbody").on("click", "button", function () {
|
||||
var data = tableApi.row($(this).parents("tr")).data();
|
||||
if (data[4] === "2" || data[4] === "3") {
|
||||
@@ -496,28 +492,84 @@ $(function () {
|
||||
});
|
||||
|
||||
$("#resetButton").click(function () {
|
||||
tableApi.search("").draw();
|
||||
$("#resetButton").addClass("hidden");
|
||||
});
|
||||
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
|
||||
var chkboxData = localStorage.getItem("query_log_filter_chkbox");
|
||||
if (chkboxData !== null) {
|
||||
// Restore checkbox state
|
||||
$("#autofilter").prop("checked", chkboxData === "true");
|
||||
} else {
|
||||
// Initialize checkbox
|
||||
$("#autofilter").prop("checked", true);
|
||||
localStorage.setItem("query_log_filter_chkbox", true);
|
||||
}
|
||||
|
||||
$("#autofilter").click(function () {
|
||||
localStorage.setItem("query_log_filter_chkbox", $("#autofilter").prop("checked"));
|
||||
tableApi.search("");
|
||||
resetColumnsFilters();
|
||||
});
|
||||
});
|
||||
|
||||
function tooltipText(index, text) {
|
||||
if (index === 5) {
|
||||
// Strip reply time from tooltip text
|
||||
text = text.split(" ")[0];
|
||||
}
|
||||
|
||||
if (index in tableFilters && tableFilters[index].length > 0) {
|
||||
return "Clear filter on " + colTypes[index] + ' "' + text + '" using Shift + Click.';
|
||||
}
|
||||
|
||||
return "Add filter on " + colTypes[index] + ' "' + text + '" using Ctrl + Click.';
|
||||
}
|
||||
|
||||
function addColumnFilter(event, colID, filterstring) {
|
||||
// Don't do anything when NOT explicitly requesting multi-selection functions
|
||||
if (!event.ctrlKey && !event.metaKey && !event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
filterstring = "";
|
||||
}
|
||||
|
||||
tableFilters[colID] = filterstring;
|
||||
|
||||
applyColumnFiltering();
|
||||
}
|
||||
|
||||
function resetColumnsFilters() {
|
||||
tableFilters.forEach(function (value, index) {
|
||||
tableFilters[index] = "";
|
||||
});
|
||||
|
||||
// Clear filter reset button
|
||||
applyColumnFiltering();
|
||||
}
|
||||
|
||||
function applyColumnFiltering() {
|
||||
var showReset = false;
|
||||
tableFilters.forEach(function (value, index) {
|
||||
// Prepare regex filter string
|
||||
var regex = "";
|
||||
|
||||
// Split filter string if we received a combined ID#Name column
|
||||
var valArr = value.split("#");
|
||||
if (valArr.length > 0) {
|
||||
value = valArr[0];
|
||||
}
|
||||
|
||||
if (value.length > 0) {
|
||||
// Exact matching
|
||||
regex = "^" + value + "$";
|
||||
|
||||
// Add background color
|
||||
tableApi.$("td:eq(" + index + ")").addClass("highlight");
|
||||
|
||||
// Remember to show reset button
|
||||
showReset = true;
|
||||
} else {
|
||||
// Clear background color
|
||||
tableApi.$("td:eq(" + index + ")").removeClass("highlight");
|
||||
}
|
||||
|
||||
// Apply filtering on this column (regex may be empty -> no filtering)
|
||||
tableApi.column(index).search(regex, true, true);
|
||||
});
|
||||
|
||||
if (showReset) {
|
||||
$("#resetButton").removeClass("hidden");
|
||||
} else {
|
||||
$("#resetButton").addClass("hidden");
|
||||
}
|
||||
|
||||
// Trigger table update
|
||||
tableApi.draw();
|
||||
}
|
||||
|
||||
@@ -198,7 +198,14 @@ function stateLoadCallback(itemName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse JSON string
|
||||
data = JSON.parse(data);
|
||||
|
||||
// Clear possible filtering settings
|
||||
data.columns.forEach(function (value, index) {
|
||||
data.columns[index].search.search = "";
|
||||
});
|
||||
|
||||
// Always start on the first page to show most recent queries
|
||||
data.start = 0;
|
||||
// Always start with empty search field
|
||||
|
||||
Reference in New Issue
Block a user