diff --git a/groups-adlists.php b/groups-adlists.php index 94c26a7e..1fb04179 100644 --- a/groups-adlists.php +++ b/groups-adlists.php @@ -41,6 +41,7 @@
  1. Please run pihole -g or update your gravity list online after modifying your adlists.
  2. Multiple adlists can be added by separating each unique URL with a space
  3. +
  4. Click on the icon in the first column to get additional information about your lists. The icons correspond to the health of the list.
@@ -61,6 +62,7 @@ ID + Address Status Comment diff --git a/scripts/pi-hole/js/groups-adlists.js b/scripts/pi-hole/js/groups-adlists.js index 1f206874..6a5baf8a 100644 --- a/scripts/pi-hole/js/groups-adlists.js +++ b/scripts/pi-hole/js/groups-adlists.js @@ -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 (OK)'; + break; + case 2: + statusText = + 'List unchanged upstream, Pi-hole used a local copy (OK)'; + break; + case 3: + statusText = + 'List unavailable, Pi-hole used a local copy (check list)'; + break; + case 4: + statusText = + 'List unavailable, there is no local copy of this list available on your Pi-hole (replace list)'; + numbers = false; + break; + + default: + statusText = + 'Unknown (' + parseInt(data.status, 10) + ")"; + 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 ( + "" + + '" + + "
Health status of this list:' + + statusText + + '
This list was added to Pi-hole  ' + + utils.datetimeRelative(data.date_added) + + " (" + + utils.datetime(data.date_added, false) + + ')
Database entry was last modified  ' + + utils.datetimeRelative(data.date_modified) + + " (" + + utils.datetime(data.date_modified, false) + + ')
The list contents were last updated  ' + + (data.date_updated > 0 + ? utils.datetimeRelative(data.date_updated) + + " (" + + utils.datetime(data.date_updated) + + ")" + : "N/A") + + '
Number of valid domains on this list:  ' + + (data.number !== null && numbers === true ? parseInt(data.number, 10) : "N/A") + + '
Number of invalid domains on this list:  " + + (data.invalid_domains !== null && numbers === true + ? parseInt(data.invalid_domains, 10) + : "N/A") + + '
Database ID of this list:' + + data.id + + "
" + ); +} + function initTable() { table = $("#adlistsTable").DataTable({ ajax: { @@ -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" }, @@ -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; - $("td:eq(0)", row).html( - '' + - data.address + - "" - ); var disabled = data.enabled === 0; - $("td:eq(1)", row).html( + 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 = ""; + } + + $("td:eq(0)", row).addClass("list-status-" + statusCode); + $("td:eq(0)", row).html( + "" + extra + ); + + 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( + '' + data.address + "" + ); + } else { + $("td:eq(1)", row).html( + '' + + data.address + + "" + ); + } + + $("td:eq(2)", row).html( '" ); var statusEl = $("#status_" + data.id, row); @@ -86,13 +202,13 @@ function initTable() { }); statusEl.on("change", editAdlist); - $("td:eq(2)", row).html(''); + $("td:eq(3)", row).html(''); 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( '' ); var selectEl = $("#multiselect_" + data.id, row); @@ -162,7 +278,7 @@ function initTable() { '">' + '' + ""; - $("td:eq(4)", row).html(button); + $("td:eq(5)", row).html(button); }, dom: "<'row'<'col-sm-4'l><'col-sm-8'f>>" + @@ -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) { diff --git a/scripts/pi-hole/js/utils.js b/scripts/pi-hole/js/utils.js index cdc4bead..73e38258 100644 --- a/scripts/pi-hole/js/utils.js +++ b/scripts/pi-hole/js/utils.js @@ -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 []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() { @@ -331,6 +335,7 @@ window.utils = (function () { padNumber: padNumber, showAlert: showAlert, datetime: datetime, + datetimeRelative: datetimeRelative, disableAll: disableAll, enableAll: enableAll, validateIPv4CIDR: validateIPv4CIDR, diff --git a/style/pi-hole.css b/style/pi-hole.css index 63500631..b19e65ba 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -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; +}