Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add details to adlist table #1673

Merged
merged 8 commits into from Mar 17, 2021
2 changes: 2 additions & 0 deletions groups-adlists.php
Expand Up @@ -41,6 +41,7 @@
<ol>
<li>Please run <code>pihole -g</code> or update your gravity list <a href="gravity.php">online</a> after modifying your adlists.</li>
<li>Multiple adlists can be added by separating each <i>unique</i> URL with a space</li>
<li>Click on the icon in the first column to get additional information about your lists. The icons correspond to the health of the list.</li>
</ol>
<button type="button" id="btnAdd" class="btn btn-primary pull-right">Add</button>
</div>
Expand All @@ -61,6 +62,7 @@
<thead>
<tr>
<th>ID</th>
<th></th>
<th>Address</th>
<th>Status</th>
<th>Comment</th>
Expand Down
176 changes: 154 additions & 22 deletions scripts/pi-hole/js/groups-adlists.js
Expand Up @@ -30,6 +30,83 @@ $(function () {
getGroups();
});

function format(data) {
// Generate human-friendly status string
var statusText = "Unknown";
var numbers = true;
if (data.status !== null) {
switch (parseInt(data.status, 10)) {
case 0:
statusText =
data.enabled === 0
? "List is disabled and not checked"
: "List was not downloaded so far";
numbers = false;
break;
case 1:
statusText = 'List download was successful (<span class="list-status-1">OK</span>)';
break;
case 2:
statusText =
'List unchanged upstream, Pi-hole used a local copy (<span class="list-status-2">OK</span>)';
break;
case 3:
statusText =
'List unavailable, Pi-hole used a local copy (<span class="list-status-3">check list</span>)';
break;
case 4:
statusText =
'List unavailable, there is no local copy of this list available on your Pi-hole (<span class="list-status-4">replace list</span>)';
numbers = false;
break;

default:
statusText =
'Unknown (<span class="list-status-0">' + parseInt(data.status, 10) + "</span>)";
break;
}
}

var invalidStyle =
data.invalid_domains !== null && data.invalid_domains > 0 && numbers === true
? ' style="color:red; font-weight:bold;"'
: "";

// Compile extra info for displaying
return (
"<table>" +
'<tr class="dataTables-child"><td>Health status of this list:</td><td>' +
statusText +
'</td></tr><tr class="dataTables-child"><td>This list was added to Pi-hole&nbsp;&nbsp;</td><td>' +
utils.datetimeRelative(data.date_added) +
"&nbsp;(" +
utils.datetime(data.date_added, false) +
')</td></tr><tr class="dataTables-child"><td>Database entry was last modified&nbsp;&nbsp;</td><td>' +
utils.datetimeRelative(data.date_modified) +
"&nbsp;(" +
utils.datetime(data.date_modified, false) +
')</td></tr><tr class="dataTables-child"><td>The list contents were last updated&nbsp;&nbsp;</td><td>' +
(data.date_updated > 0
? utils.datetimeRelative(data.date_updated) +
"&nbsp;(" +
utils.datetime(data.date_updated) +
")"
: "N/A") +
'</td></tr><tr class="dataTables-child"><td>Number of valid domains on this list:&nbsp;&nbsp;</td><td>' +
(data.number !== null && numbers === true ? parseInt(data.number, 10) : "N/A") +
'</td></tr><tr class="dataTables-child"' +
invalidStyle +
"><td>Number of invalid domains on this list:&nbsp;&nbsp;</td>" +
"<td>" +
(data.invalid_domains !== null && numbers === true
? parseInt(data.invalid_domains, 10)
: "N/A") +
'</td></tr><tr class="dataTables-child"><td>Database ID of this list:</td><td>' +
data.id +
"</td></tr></table>"
);
}

function initTable() {
table = $("#adlistsTable").DataTable({
ajax: {
Expand All @@ -40,6 +117,7 @@ function initTable() {
order: [[0, "asc"]],
columns: [
{ data: "id", visible: false },
{ data: "status", searchable: false, class: "details-control" },
{ data: "address" },
{ data: "enabled", searchable: false },
{ data: "comment" },
Expand All @@ -53,27 +131,65 @@ function initTable() {
},
rowCallback: function (row, data) {
$(row).attr("data-id", data.id);
var tooltip =
"Added: " +
utils.datetime(data.date_added, false) +
"\nLast modified (database entry): " +
utils.datetime(data.date_modified, false) +
"\nLast updated (list content): " +
(data.date_updated !== null ? utils.datetime(data.date_updated, false) : "N/A") +
"\nDatabase ID: " +
data.id;

var disabled = data.enabled === 0;
var statusCode = 0,
statusIcon;
// If there is no status or the list is disabled, we keep
// status 0 (== unknown)
if (data.status !== null && disabled !== true) {
statusCode = parseInt(data.status, 10);
}

switch (statusCode) {
case 1:
statusIcon = "fa-check";
break;
case 2:
statusIcon = "fa-history";
break;
case 3:
statusIcon = "fa-exclamation-circle";
break;
case 4:
statusIcon = "fa-times";
break;
case 0:
default:
statusIcon = "fa-question-circle";
break;
}

// Append red exclamation-triangle when there are invalid lines on the list
var extra = "";
if (data.invalid_domains !== null && data.invalid_domains > 0) {
extra = "<i class='fa fa-exclamation-triangle list-status-3'></i>";
}

$("td:eq(0)", row).addClass("list-status-" + statusCode);
$("td:eq(0)", row).html(
'<code id="address_' +
data.id +
'" title="' +
tooltip +
'" class="breakall">' +
data.address +
"</code>"
"<i class='fa " + statusIcon + "' title='Click for details about this list'></i>" + extra
);

var disabled = data.enabled === 0;
$("td:eq(1)", row).html(
if (data.address.startsWith("file://")) {
// Local files cannot be downloaded from a distant client so don't show
// a link to such a list here
$("td:eq(1)", row).html(
'<code id="address_' + data.id + '" class="breakall">' + data.address + "</code>"
);
} else {
$("td:eq(1)", row).html(
'<a id="address_' +
data.id +
'" class="breakall" href="' +
data.address +
'" target="_blank" rel="noopener noreferrer">' +
data.address +
"</a>"
);
}

$("td:eq(2)", row).html(
'<input type="checkbox" id="status_' + data.id + '"' + (disabled ? "" : " checked") + ">"
);
var statusEl = $("#status_" + data.id, row);
Expand All @@ -86,13 +202,13 @@ function initTable() {
});
statusEl.on("change", editAdlist);

$("td:eq(2)", row).html('<input id="comment_' + data.id + '" class="form-control">');
$("td:eq(3)", row).html('<input id="comment_' + data.id + '" class="form-control">');
var commentEl = $("#comment_" + data.id, row);
commentEl.val(utils.unescapeHtml(data.comment));
commentEl.on("change", editAdlist);

$("td:eq(3)", row).empty();
$("td:eq(3)", row).append(
$("td:eq(4)", row).empty();
$("td:eq(4)", row).append(
'<select class="selectpicker" id="multiselect_' + data.id + '" multiple></select>'
);
var selectEl = $("#multiselect_" + data.id, row);
Expand Down Expand Up @@ -162,7 +278,7 @@ function initTable() {
'">' +
'<span class="far fa-trash-alt"></span>' +
"</button>";
$("td:eq(4)", row).html(button);
$("td:eq(5)", row).html(button);
},
dom:
"<'row'<'col-sm-4'l><'col-sm-8'f>>" +
Expand Down Expand Up @@ -204,6 +320,22 @@ function initTable() {
$("#resetButton").addClass("hidden");
});

// Add event listener for opening and closing details
$("#adlistsTable tbody").on("click", "td.details-control", function () {
var tr = $(this).closest("tr");
var row = table.row(tr);

if (row.child.isShown()) {
// This row is already open - close it
row.child.hide();
tr.removeClass("shown");
} else {
// Open this row
row.child(format(row.data())).show();
tr.addClass("shown");
}
});

// Disable autocorrect in the search box
var input = document.querySelector("input[type=search]");
if (input !== null) {
Expand Down
7 changes: 6 additions & 1 deletion scripts/pi-hole/js/utils.js
Expand Up @@ -124,7 +124,11 @@ function showAlert(type, icon, title, message) {

function datetime(date, html) {
var format = html === false ? "Y-MM-DD HH:mm:ss z" : "Y-MM-DD [<br class='hidden-lg'>]HH:mm:ss z";
return moment.unix(Math.floor(date)).format(format);
return moment.unix(Math.floor(date)).format(format).trim();
}

function datetimeRelative(date) {
return moment.unix(Math.floor(date)).fromNow();
}

function disableAll() {
Expand Down Expand Up @@ -331,6 +335,7 @@ window.utils = (function () {
padNumber: padNumber,
showAlert: showAlert,
datetime: datetime,
datetimeRelative: datetimeRelative,
disableAll: disableAll,
enableAll: enableAll,
validateIPv4CIDR: validateIPv4CIDR,
Expand Down
28 changes: 28 additions & 0 deletions style/pi-hole.css
Expand Up @@ -330,3 +330,31 @@
.small-box {
border-radius: 10px;
}

.list-status-0 {
color: #7d7d7d;
}

.list-status-1 {
color: #74c700;
}

.list-status-2 {
color: #98ca52;
}

.list-status-3 {
color: #ff8c00;
}

.list-status-4 {
color: #cc0000;
}

td.details-control {
cursor: pointer;
}

.dataTables-child td {
padding: 2px 5px;
}