/* 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 utils:false, groups:false, apiFailure:false, updateFtlInfo:false, getGroups:false, processGroupResult:false, delGroupItems:false */ /* exported initTable */ let table; let GETDict = {}; $(() => { GETDict = utils.parseQueryString(); $("#btnAddAllow").on("click", { type: "allow" }, addList); $("#btnAddBlock").on("click", { type: "block" }, addList); utils.setBsSelectDefaults(); getGroups(); }); function format(data) { // Generate human-friendly status string const statusText = setStatusText(data, true); let numbers = true; if (data.status === 0 || data.status === 4) { numbers = false; } // Compile extra info for displaying const dateAddedISO = utils.datetime(data.date_added, false); const dateModifiedISO = utils.datetime(data.date_modified, false); const dateUpdated = data.date_updated > 0 ? utils.datetimeRelative(data.date_updated) + " (" + utils.datetime(data.date_updated, false) + ")" : "N/A"; const numberOfEntries = (data.number !== null && numbers === true ? Number.parseInt(data.number, 10).toLocaleString() : "N/A") + (data.abp_entries !== null && Number.parseInt(data.abp_entries, 10) > 0 && numbers === true ? " (out of which " + Number.parseInt(data.abp_entries, 10).toLocaleString() + " are in ABP-style)" : ""); const nonDomains = data.invalid_domains !== null && numbers === true ? Number.parseInt(data.invalid_domains, 10).toLocaleString() : "N/A"; return `
Type:  ${setTypeIcon(data.type)}${data.type}list
Health status:  ${statusText}
Added to Pi-hole:   ${utils.datetimeRelative(data.date_added)} (${dateAddedISO})
Database entry last modified:   ${utils.datetimeRelative(data.date_modified)} (${dateModifiedISO})
Content last updated on:  ${dateUpdated}
Number of entries:  ${numberOfEntries}
Number of non-domains:  ${nonDomains}
Database ID:${data.id}
`; } // Define the status icon element function setStatusIcon(data) { const statusCode = Number.parseInt(data.status, 10); const statusTitle = setStatusText(data) + "\nClick for details about this list"; let statusIcon; switch (statusCode) { case 1: statusIcon = "fa-check-circle"; break; case 2: statusIcon = "fa-history"; break; case 3: statusIcon = "fa-exclamation-circle"; break; case 4: statusIcon = "fa-times-circle"; break; default: statusIcon = "fa-question-circle"; break; } return ""; } // Define human-friendly status string function setStatusText(data, showdetails = false) { let statusText = "Unknown"; let statusDetails = ""; if (data.status !== null) { switch (Number.parseInt(data.status, 10)) { case 0: statusText = data.enabled === 0 ? "List is disabled and not checked" : "List was not downloaded so far"; break; case 1: statusText = "List download was successful"; statusDetails = ' (OK)'; break; case 2: statusText = "List unchanged upstream, Pi-hole used a local copy"; statusDetails = ' (OK)'; break; case 3: statusText = "List unavailable, Pi-hole used a local copy"; statusDetails = ' (check list)'; break; case 4: statusText = "List unavailable, there is no local copy of this list available on your Pi-hole"; statusDetails = ' (replace list)'; break; default: statusText = "Unknown"; statusDetails = ' (' + Number.parseInt(data.status, 10) + ")"; break; } } return statusText + (showdetails === true ? statusDetails : ""); } // Define the type icon element function setTypeIcon(type) { //Add red ban icon if data["type"] is "block" //Add green check icon if data["type"] is "allow" let iconClass = "fa-question text-orange"; let title = "This list is of unknown type"; if (type === "block") { iconClass = "fa-ban text-red"; title = "This is a blocklist"; } else if (type === "allow") { iconClass = "fa-check text-green"; title = "This is an allowlist"; } return ` `; } // eslint-disable-next-line no-unused-vars function initTable() { table = $("#listsTable").DataTable({ processing: true, ajax: { url: document.body.dataset.apiurl + "/lists", dataSrc: "lists", type: "GET", }, order: [[0, "asc"]], columns: [ { data: "id", visible: false }, { data: null, visible: true, orderable: false, width: "15px" }, { data: "status", searchable: false, class: "details-control" }, { data: "type", searchable: false, class: "details-control" }, { data: "address" }, { data: "enabled", searchable: false }, { data: "comment" }, { data: "groups", searchable: false }, { data: null, width: "22px", orderable: false }, ], columnDefs: [ { targets: 1, className: "select-checkbox", render() { return ""; }, }, { targets: "_all", render: $.fn.dataTable.render.text(), }, ], drawCallback() { // Hide buttons if all lists were deleted const hasRows = this.api().rows({ filter: "applied" }).data().length > 0; $(".datatable-bt").css("visibility", hasRows ? "visible" : "hidden"); $('button[id^="deleteList_"]').on("click", deleteList); // Remove visible dropdown to prevent orphaning $("body > .bootstrap-select.dropdown").remove(); }, rowCallback(row, data) { const dataId = utils.hexEncode(data.address + "_" + data.type); $(row).attr("data-id", dataId); $(row).attr("data-address", utils.hexEncode(data.address)); $(row).attr("data-type", data.type); let statusCode = 0; // If there is no status or the list is disabled, we keep // status 0 (== unknown) if (data.status !== null && data.enabled) { statusCode = Number.parseInt(data.status, 10); } $("td:eq(1)", row).addClass("list-status-" + statusCode); $("td:eq(1)", row).html(setStatusIcon(data)); $("td:eq(2)", row).addClass("list-type-" + statusCode); $("td:eq(2)", row).html(setTypeIcon(data.type)); 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(3)", row).html( '' + utils.escapeHtml(data.address) + "" ); } else { $("td:eq(3)", row).html( '' + utils.escapeHtml(data.address) + "" ); } $("td:eq(4)", row).html( '" ); const statusEl = $("#enabled_" + dataId, row); statusEl.bootstrapToggle({ on: "Enabled", off: "Disabled", size: "small", onstyle: "success", width: "80px", }); statusEl.on("change", editList); $("td:eq(5)", row).html(''); const commentEl = $("#comment_" + dataId, row); commentEl.val(data.comment); commentEl.on("change", editList); $("td:eq(6)", row).empty(); $("td:eq(6)", row).append( '' ); const selectEl = $("#multiselect_" + dataId, row); // Add all known groups for (const group of groups) { const dataSub = group.enabled ? "" : 'data-subtext="(disabled)"'; selectEl.append( $("