Pi-hole web v5.17 (#2430)

This commit is contained in:
Adam Warner
2022-11-14 19:00:36 +00:00
committed by GitHub
28 changed files with 186 additions and 174 deletions

View File

@@ -21,7 +21,7 @@ jobs:
run: composer validate
- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3.0.10
uses: actions/cache@v3.0.11
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}

View File

@@ -22,7 +22,7 @@ jobs:
uses: actions/checkout@v3.1.0
- name: Set up Node.js
uses: actions/setup-node@v3.5.0
uses: actions/setup-node@v3.5.1
with:
node-version: "16.x"
cache: npm

View File

@@ -192,7 +192,7 @@ if (isset($_GET['topDomains']) && $auth) {
$limit = ' AND timestamp <= :until';
}
// Select top permitted domains only
$stmt = $db->prepare('SELECT domain,count(domain) FROM queries WHERE status IN (2,3,12,13,14)'.$limit.' GROUP by domain order by count(domain) desc limit 20');
$stmt = $db->prepare('SELECT domain,count(domain) FROM queries WHERE status IN (2,3,12,13,14,17)'.$limit.' GROUP by domain order by count(domain) desc limit 20');
$stmt->bindValue(':from', intval($_GET['from']), SQLITE3_INTEGER);
$stmt->bindValue(':until', intval($_GET['until']), SQLITE3_INTEGER);
$results = $stmt->execute();

View File

@@ -32,7 +32,7 @@ require 'scripts/pi-hole/php/header_authenticated.php';
<div class="row">
<div class="form-group col-md-6">
<label for="domain">Domain:</label>
<input id="domain" type="url" class="form-control" placeholder="Add a domain (example.com or sub.example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
<input id="domain" type="url" class="form-control" placeholder="Domain or comma-separated list of domains" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
</div>
<div class="form-group col-md-6">
<label for="target">Target Domain:</label>

View File

@@ -49,6 +49,7 @@ require 'scripts/pi-hole/php/header_authenticated.php';
<div class="col-md-3">
<div><input type="checkbox" id="type_forwarded" checked><label for="type_forwarded">Permitted: forwarded</label><br></div>
<div><input type="checkbox" id="type_cached" checked><label for="type_cached">Permitted: cached</label></div>
<div><input type="checkbox" id="type_cached_stale" checked><label for="type_cached_stale">Permitted: stale cache</label></div>
<div><input type="checkbox" id="type_retried" checked><label for="type_retried">Permitted: retried</label></div>
</div>
<div class="col-md-3">

View File

@@ -32,7 +32,7 @@ require 'scripts/pi-hole/php/header_authenticated.php';
<div class="row">
<div class="form-group col-md-6">
<label for="domain">Domain:</label>
<input id="domain" type="url" class="form-control" placeholder="Add a domain (example.com or sub.example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
<input id="domain" type="url" class="form-control" placeholder="Domain or comma-separated list of domains" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
</div>
<div class="form-group col-md-6">
<label for="ip">IP Address:</label>

View File

@@ -42,7 +42,7 @@ require 'scripts/pi-hole/php/header.php';
<form action="" id="loginform" method="post">
<div class="form-group login-options has-feedback<?php if ($wrongpassword) { ?> has-error<?php } ?>">
<div class="pwd-field">
<input type="password" id="loginpw" name="pw" class="form-control" placeholder="Password" autocomplete="current-password" autofocus>
<input type="password" id="loginpw" name="pw" class="form-control" placeholder="Password" spellcheck="false" autocomplete="current-password" autofocus>
<span class="fa fa-key form-control-feedback"></span>
</div>
<div>

32
package-lock.json generated
View File

@@ -281,13 +281,13 @@
}
},
"autoprefixer": {
"version": "10.4.12",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz",
"integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==",
"version": "10.4.13",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
"integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==",
"dev": true,
"requires": {
"browserslist": "^4.21.4",
"caniuse-lite": "^1.0.30001407",
"caniuse-lite": "^1.0.30001426",
"fraction.js": "^4.2.0",
"normalize-range": "^0.1.2",
"picocolors": "^1.0.0",
@@ -307,15 +307,15 @@
}
},
"caniuse-lite": {
"version": "1.0.30001410",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz",
"integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==",
"version": "1.0.30001426",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz",
"integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==",
"dev": true
},
"electron-to-chromium": {
"version": "1.4.261",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.261.tgz",
"integrity": "sha512-fVXliNUGJ7XUVJSAasPseBbVgJIeyw5M1xIkgXdTSRjlmCqBbiSTsEdLOCJS31Fc8B7CaloQ/BFAg8By3ODLdg==",
"version": "1.4.284",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
"integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
"dev": true
},
"node-releases": {
@@ -2737,9 +2737,9 @@
"dev": true
},
"postcss": {
"version": "8.4.17",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
"integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
"version": "8.4.19",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
"dev": true,
"requires": {
"nanoid": "^3.3.4",
@@ -3371,9 +3371,9 @@
"dev": true
},
"update-browserslist-db": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz",
"integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==",
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
"dev": true,
"requires": {
"escalade": "^3.1.1",

View File

@@ -24,9 +24,9 @@
"testpr": "npm run prettier:fix && git diff --ws-error-highlight=all --color=always --exit-code && npm run xo"
},
"devDependencies": {
"autoprefixer": "^10.4.12",
"autoprefixer": "^10.4.13",
"eslint-plugin-compat": "^4.0.2",
"postcss": "^8.4.17",
"postcss": "^8.4.19",
"postcss-cli": "^10.0.0",
"prettier": "2.7.1",
"xo": "^0.52.4"

View File

@@ -236,12 +236,15 @@ $(function () {
},
options: {
responsive: true,
interaction: {
mode: "nearest",
axis: "x",
},
plugins: {
tooltip: {
enabled: true,
yAlign: "bottom",
intersect: false,
mode: "x",
itemSort: function (a, b) {
return b.datasetIndex - a.datasetIndex;
},
@@ -390,7 +393,7 @@ $("#querytime").on("apply.daterangepicker", function (ev, picker) {
updateQueriesOverTime();
});
$("#queryOverTimeChart").click(function (evt) {
$("#queryOverTimeChart").on("click", function (evt) {
var activePoints = timeLineChart.getElementsAtEventForMode(
evt,
"nearest",

View File

@@ -167,6 +167,10 @@ function excludeStatusTypes() {
statusType.push(16);
}
if ($("#type_cached_stale").prop("checked") === false) {
statusType.push(17);
}
return statusType.join(",");
}
@@ -176,7 +180,7 @@ var reloadCallback = function () {
var data = tableApi.rows().data();
for (var i = 0; i < data.length; i++) {
statistics[0]++; // TOTAL query
if (data[i][4] === 1 || (data[i][4] > 4 && ![10, 12, 13, 14].includes(data[i][4]))) {
if (data[i][4] === 1 || (data[i][4] > 4 && ![10, 12, 13, 14, 17].includes(data[i][4]))) {
statistics[2]++; // EXACT blocked
} else if (data[i][4] === 3) {
statistics[1]++; // CACHE query
@@ -349,6 +353,13 @@ $(function () {
"<span class='text-orange'>Blocked <br class='hidden-lg'>(special domain)</span>";
blocked = true;
break;
case 17:
fieldtext =
"<span class='text-orange'>OK</span> <br class='hidden-lg'>(stale cache)" +
dnssecStatus;
buttontext =
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
break;
default:
fieldtext = "Unknown (" + parseInt(data[4], 10) + ")";
}
@@ -464,12 +475,12 @@ $("#querytime").on("apply.daterangepicker", function (ev, picker) {
refreshTableData();
});
$("input[id^=type]").change(function () {
$("input[id^=type]").on("change", function () {
if (datepickerManuallySelected) {
reloadBox.show();
}
});
$(".bt-reload").click(function () {
$(".bt-reload").on("click", function () {
refreshTableData();
});

View File

@@ -150,7 +150,7 @@ function initCheckboxRadioStyle() {
var iCheckStyle = $("#iCheckStyle");
if (iCheckStyle !== null) {
iCheckStyle.val(chkboxStyle);
iCheckStyle.change(function () {
iCheckStyle.on("change", function () {
var themename = $(this).val();
localStorage.setItem("theme_icheck", themename);
applyCheckboxRadioStyle(themename);
@@ -197,7 +197,7 @@ function initCPUtemp() {
var tempunitSelector = $("#tempunit-selector");
if (tempunitSelector !== null) {
tempunitSelector.val(tempunit);
tempunitSelector.change(function () {
tempunitSelector.on("change", function () {
tempunit = $(this).val();
setCPUtemp(tempunit);
});
@@ -256,7 +256,7 @@ $("#pihole-disable-custom").on("click", function (e) {
});
// Handle Ctrl + Enter button on Login page
$(document).keypress(function (e) {
$(document).on("keypress", function (e) {
if ((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey && $("#loginpw").is(":focus")) {
$("#loginform").attr("action", "settings.php");
$("#loginform").submit();

View File

@@ -69,7 +69,7 @@ function showSuggestDomains(value) {
// Purposefully omit 'btn' class to save space on padding
return $('<button type="button" class="btn-link btn-block text-right">')
.append($("<i>").text(hostname))
.click(function () {
.on("click", function () {
hideSuggestDomains();
newDomainEl.val(hostname);
});

View File

@@ -724,17 +724,26 @@ function updateSummaryData(runOnce) {
}
function doughnutTooltip(tooltipLabel) {
var percentageTotalShown = tooltipLabel.chart._metasets[0].total.toFixed(2);
var label = " " + tooltipLabel.label;
var percentageTotalShown = tooltipLabel.chart._metasets[0].total.toFixed(1);
// tooltipLabel.chart._metasets[0].total returns the total percentage of the shown slices
// to compensate rounding errors we round to one decimal
if (percentageTotalShown >= 100) {
var label = " " + tooltipLabel.label;
// in case the item share is really small it could be rounded to 0.0
// we compensate for this
var itemPercentage =
tooltipLabel.parsed.toFixed(1) === 0 ? "< 0.1" : tooltipLabel.parsed.toFixed(1);
// even if no doughnut slice is hidden, sometimes percentageTotalShown is slightly less then 100
// we therefore use 99.9 to decide if slices are hidden (we only show with 0.1 precision)
if (percentageTotalShown > 99.9) {
// All items shown
return label + ": " + tooltipLabel.parsed.toFixed(1) + "%";
return label + ": " + itemPercentage + "%";
} else {
return (
label +
":<br>&bull; " +
tooltipLabel.parsed.toFixed(1) +
itemPercentage +
"% of all queries<br>&bull; " +
((tooltipLabel.parsed * 100) / percentageTotalShown).toFixed(1) +
"% of shown items"
@@ -855,6 +864,10 @@ $(function () {
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: "nearest",
axis: "x",
},
plugins: {
legend: {
display: false,
@@ -862,7 +875,6 @@ $(function () {
tooltip: {
enabled: true,
intersect: false,
mode: "x",
yAlign: "bottom",
itemSort: function (a, b) {
return b.datasetIndex - a.datasetIndex;
@@ -963,6 +975,10 @@ $(function () {
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: "nearest",
axis: "x",
},
plugins: {
legend: {
display: false,
@@ -971,7 +987,6 @@ $(function () {
// Disable the on-canvas tooltip
enabled: false,
intersect: false,
mode: "x",
external: customTooltips,
yAlign: "top",
itemSort: function (a, b) {
@@ -1056,7 +1071,7 @@ $(function () {
updateTopClientsChart();
}
$("#queryOverTimeChart").click(function (evt) {
$("#queryOverTimeChart").on("click", function (evt) {
var activePoints = timeLineChart.getElementsAtEventForMode(
evt,
"nearest",
@@ -1078,7 +1093,7 @@ $(function () {
return false;
});
$("#clientsChart").click(function (evt) {
$("#clientsChart").on("click", function (evt) {
var activePoints = clientsChart.getElementsAtEventForMode(
evt,
"nearest",

View File

@@ -178,7 +178,7 @@ $(function () {
}
$("td:eq(3)", row).html(names.join("<br>"));
$("td:eq(3)", row).hover(function () {
$("td:eq(3)", row).on("hover", function () {
this.title = allnames.join("\n");
});
}
@@ -201,7 +201,7 @@ $(function () {
}
$("td:eq(0)", row).html(ips.join("<br>"));
$("td:eq(0)", row).hover(function () {
$("td:eq(0)", row).on("hover", function () {
this.title = data.ip.join("\n");
});

View File

@@ -219,6 +219,13 @@ $(function () {
"<span class='text-orange'>Blocked <br class='hidden-lg'>(special domain)</span>";
blocked = true;
break;
case "17":
fieldtext =
"<span class='text-orange'>OK</span> <br class='hidden-lg'>(stale cache)" +
dnssecStatus;
buttontext =
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
break;
default:
fieldtext = "Unknown (" + parseInt(data[4], 10) + ")";
}
@@ -240,17 +247,18 @@ $(function () {
$("td:eq(6)", row).html(buttontext);
if (DomainlistLink) {
$("td:eq(4)", row).hover(
function () {
$("td:eq(4)", row).on(
"hover",
(function () {
this.title = "Click to show matching blacklist/whitelist domain";
this.style.color = "#72afd2";
},
function () {
this.style.color = "";
}
})
);
$("td:eq(4)", row).off(); // Release any possible previous onClick event handlers
$("td:eq(4)", row).click(function () {
$("td:eq(4)", row).on("click", function () {
var newTab = window.open("groups-domains.php?domainid=" + data[9], "_blank");
if (newTab) {
newTab.focus();
@@ -366,80 +374,85 @@ $(function () {
// Query type IPv4 / IPv6
api
.$("td:eq(1)")
.click(function (event) {
.on("click", function (event) {
addColumnFilter(event, 1, this.textContent);
})
.hover(
function () {
.on(
"hover",
(function () {
$(this).addClass("pointer").attr("title", tooltipText(1, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
})
);
// Domain
api
.$("td:eq(2)")
.click(function (event) {
.on("click", function (event) {
addColumnFilter(event, 2, this.textContent.split("\n")[0]);
})
.hover(
function () {
.on(
"hover",
(function () {
$(this).addClass("pointer").attr("title", tooltipText(2, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
})
);
// Client
api
.$("td:eq(3)")
.click(function (event) {
.on("click", function (event) {
addColumnFilter(event, 3, this.textContent);
})
.hover(
function () {
.on(
"hover",
(function () {
$(this).addClass("pointer").attr("title", tooltipText(3, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
})
);
// Status
api
.$("td:eq(4)")
.click(function (event) {
.on("click", function (event) {
var id = this.children.id.value;
var text = this.textContent;
addColumnFilter(event, 4, id + "#" + text);
})
.hover(
function () {
.on(
"hover",
(function () {
$(this).addClass("pointer").attr("title", tooltipText(4, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
})
);
// Reply type
api
.$("td:eq(5)")
.click(function (event) {
.on("click", function (event) {
var id = this.children.id.value;
var text = this.textContent.split(" ")[0];
addColumnFilter(event, 5, id + "#" + text);
})
.hover(
function () {
.on(
"hover",
(function () {
$(this).addClass("pointer").attr("title", tooltipText(5, this.textContent));
},
function () {
$(this).removeClass("pointer");
}
})
);
// Disable autocorrect in the search box
@@ -458,14 +471,14 @@ $(function () {
$("#all-queries tbody").on("click", "button", function () {
var data = tableApi.row($(this).parents("tr")).data();
if (data[4] === "2" || data[4] === "3" || data[4] === "14") {
if (data[4] === "2" || data[4] === "3" || data[4] === "14" || data[4] === "17") {
utils.addFromQueryLog(data[2], "black");
} else {
utils.addFromQueryLog(data[2], "white");
}
});
$("#resetButton").click(function () {
$("#resetButton").on("click", function () {
tableApi.search("");
resetColumnsFilters();
});

View File

@@ -87,7 +87,7 @@ function eventsource() {
}
// Handle enter key
$("#domain").keypress(function (e) {
$("#domain").on("keypress", function (e) {
if (e.which === 13) {
// Enter was pressed, and the input has focus
exact = "";

View File

@@ -59,7 +59,7 @@ $(function () {
});
// display selected import file on button's adjacent textfield
$("#zip_file").change(function () {
$("#zip_file").on("change", function () {
var fileName = $(this)[0].files.length === 1 ? $(this)[0].files[0].name : "";
$("#zip_filename").val(fileName);
});
@@ -194,7 +194,7 @@ $("#apiTokenModal").on("show.bs.modal", function () {
$('iframe[name="apiToken_iframe"]').contents().find("table").css(qrCodeStyle);
});
$("#DHCPchk").click(function () {
$("#DHCPchk").on("click", function () {
$("input.DHCPgroup").prop("disabled", !this.checked);
$("#dhcpnotice").prop("hidden", !this.checked).addClass("lookatme");
});
@@ -347,7 +347,7 @@ $(function () {
// En-/disable conditional forwarding input fields based
// on the checkbox state
$('input[name="rev_server"]').click(function () {
$('input[name="rev_server"]').on("click", function () {
$('input[name="rev_server_cidr"]').prop("disabled", !this.checked);
$('input[name="rev_server_target"]').prop("disabled", !this.checked);
$('input[name="rev_server_domain"]').prop("disabled", !this.checked);
@@ -377,7 +377,7 @@ $(function () {
}
}
bargraphs.click(function () {
bargraphs.on("click", function () {
localStorage.setItem("barchart_chkbox", bargraphs.prop("checked"));
});
});
@@ -397,7 +397,7 @@ $(function () {
}
}
colorfulQueryLog.click(function () {
colorfulQueryLog.on("click", function () {
localStorage.setItem("colorfulQueryLog_chkbox", colorfulQueryLog.prop("checked"));
});
});
@@ -463,7 +463,7 @@ $(function () {
}
}
nonfatalwarnigns.click(function () {
nonfatalwarnigns.on("click", function () {
localStorage.setItem("hideNonfatalDnsmasqWarnings_chkbox", nonfatalwarnigns.prop("checked"));
// Call check messages to make new setting effective
checkMessages();

View File

@@ -39,11 +39,11 @@ $(function () {
reloadData();
});
$("#chk1").click(function () {
$("#chk1").on("click", function () {
$("#chk2").prop("checked", this.checked);
scrolling = this.checked;
});
$("#chk2").click(function () {
$("#chk2").on("click", function () {
$("#chk1").prop("checked", this.checked);
scrolling = this.checked;
});

View File

@@ -39,11 +39,11 @@ $(function () {
reloadData();
});
$("#chk1").click(function () {
$("#chk1").on("click", function () {
$("#chk2").prop("checked", this.checked);
scrolling = this.checked;
});
$("#chk2").click(function () {
$("#chk2").on("click", function () {
$("#chk1").prop("checked", this.checked);
scrolling = this.checked;
});

View File

@@ -196,6 +196,7 @@ function getCustomDNSEntries()
$data = new \stdClass();
$data->ip = $explodedLine[0];
$data->domain = $explodedLine[1];
$data->domains = array_slice($explodedLine, 0, -1);
$entries[] = $data;
}
@@ -234,22 +235,39 @@ function addCustomDNSEntry($ip = '', $domain = '', $reload = '', $json = true)
return returnError('Domain must be set', $json);
}
if (!validDomain($domain)) {
return returnError('Domain must be valid', $json);
$num = 0;
// Check if each submitted domain is valid
$domains = array_map('trim', explode(',', $domain));
foreach ($domains as $d) {
if (!validDomain($d)) {
return returnError("Domain '{$d}' is not valid", $json);
}
++$num;
}
// Only check for duplicates if adding new records from the web UI (not through Teleporter)
if (isset($_REQUEST['ip']) || isset($_REQUEST['domain'])) {
$existingEntries = getCustomDNSEntries();
foreach ($existingEntries as $entry) {
if ($entry->domain == $domain && get_ip_type($entry->ip) == $ipType) {
return returnError('This domain already has a custom DNS entry for an IPv'.$ipType, $json);
foreach ($domains as $d) {
if ($entry->domain == $d && get_ip_type($entry->ip) == $ipType) {
return returnError("The domain {$d} already has a custom DNS entry for an IPv".$ipType, $json);
}
}
}
}
// Add record
pihole_execute('-a addcustomdns '.$ip.' '.$domain.' '.$reload);
// if $reload is not set and more then one domain is supplied, restart FTL only once after all entries were added
if (($num > 0) && (empty($reload))) {
$reload = 'false';
}
// Add records
foreach ($domains as $domain) {
pihole_execute('-a addcustomdns '.$ip.' '.$domain.' '.$reload);
}
if ($num > 0) {
pihole_execute('restartdns');
}
return returnSuccess('', $json);
} catch (\Exception $ex) {
@@ -392,12 +410,20 @@ function addCustomCNAMEEntry($domain = '', $target = '', $reload = '', $json = t
return returnError('Target must be valid', $json);
}
// Check if each submitted domain is valid
$num = 0;
$domains = array_map('trim', explode(',', $domain));
foreach ($domains as $d) {
// Check if each submitted domain is valid
if (!validDomain($d)) {
return returnError("Domain '{$d}' is not valid", $json);
}
// Check if each submitted domain is different than the target to avoid loops
if (strtolower($d) === strtolower($target)) {
return returnError('Domain and target cannot be the same', $json);
}
++$num;
}
$existingEntries = getCustomCNAMEEntries();
@@ -411,7 +437,18 @@ function addCustomCNAMEEntry($domain = '', $target = '', $reload = '', $json = t
}
}
pihole_execute('-a addcustomcname '.$domain.' '.$target.' '.$reload);
// if $reload is not set and more then one domain is supplied, restart FTL only once after all entries were added
if (($num > 0) && (empty($reload))) {
$reload = 'false';
}
// add records
foreach ($domains as $d) {
pihole_execute('-a addcustomcname '.$d.' '.$target.' '.$reload);
}
if ($num > 0) {
pihole_execute('restartdns');
}
return returnSuccess('', $json);
} catch (\Exception $ex) {

View File

@@ -1,77 +0,0 @@
<?php
/* 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.
*/
?>
<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 class="text-center">
<img src="img/logo.svg" alt="Pi-hole logo" class="loginpage-logo">
</div>
<br>
<div class="panel-title text-center"><span class="logo-lg" style="font-size: 25px;">Pi-<b>hole</b></span></div>
<p class="login-box-msg">Sign in to start your session</p>
<div id="cookieInfo" class="panel-title text-center text-red" style="font-size: 150%" hidden>Verify that cookies are allowed for <code><?php echo $_SERVER['HTTP_HOST']; ?></code></div>
<?php if ($wrongpassword) { ?>
<div class="form-group has-error login-box-msg">
<label class="control-label"><i class="fa fa-times-circle"></i> Wrong password!</label>
</div>
<?php } ?>
</div>
<div class="panel-body">
<form action="" id="loginform" method="post">
<div class="form-group login-options has-feedback<?php if ($wrongpassword) { ?> has-error<?php } ?>">
<div class="pwd-field">
<input type="password" id="loginpw" name="pw" class="form-control" placeholder="Password" autocomplete="current-password" autofocus>
<span class="fa fa-key form-control-feedback"></span>
</div>
<div>
<input type="checkbox" id="logincookie" name="persistentlogin">
<label for="logincookie">Remember me for 7 days</label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary form-control"><i class="fas fa-sign-in-alt"></i>&nbsp;&nbsp;&nbsp;Log in</button>
</div>
<div class="box login-help hidden-xs">
<p><kbd>Return</kbd> &#10140; Log in and go to requested page (<?php echo $scriptname; ?>)</p>
<p><kbd>Ctrl</kbd> + <kbd>Return</kbd> &#10140; Log in and go to Settings page</p>
</div>
<div class="row">
<div class="col-xs-12">
<div class="box box-<?php if (!$wrongpassword) { ?>info collapsed-box<?php } else { ?>danger<?php }?>">
<div class="box-header with-border pointer no-user-select" data-widget="collapse">
<h3 class="box-title">Forgot password?</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool"><i class="fa <?php if ($wrongpassword) { ?>fa-minus<?php } else { ?>fa-plus<?php } ?>"></i>
</button>
</div>
</div>
<div class="box-body">
<p>
After installing Pi-hole for the first time, a password is generated and displayed to the user. The
password cannot be retrieved later on, but it is possible to set a new password (or explicitly disable
the password by setting an empty password) using the command
</p>
<pre>sudo pihole -a -p</pre>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<?php
require 'scripts/pi-hole/php/footer.php';
?>

View File

@@ -43,6 +43,12 @@ if (!$file) {
if (isset($_GET['offset'])) {
$offset = intval($_GET['offset']);
if ($offset > 0) {
// If offset is grater then current file end it means the file was truncated (log rotation)
fseek($file, 0, SEEK_END);
if ($offset > ftell($file)) {
$offset = 0;
}
// Seeks on the file pointer where we want to continue reading is known
fseek($file, $offset);

View File

@@ -126,7 +126,11 @@ if (isset($FTL_commit)) {
}
if ($docker_current) {
$dockerVersionStr = '<a href="'.$dockerUrl.'/'.rawurlencode($docker_current).'" rel="noopener" target="_blank">'.htmlentities($docker_current).'</a>';
if ($docker_current == 'dev' || $docker_current == 'nightly') {
$dockerVersionStr = htmlentities($docker_current);
} else {
$dockerVersionStr = '<a href="'.$dockerUrl.'/'.rawurlencode($docker_current).'" rel="noopener" target="_blank">'.htmlentities($docker_current).'</a>';
}
} else {
$dockerVersionStr = '';
}

View File

@@ -1,7 +1,7 @@
/*!
* jquery.confirm
*
* @version 2.3.1
* @version 2.7.0
*
* @author My C-Labs
* @author Matthieu Napoli <matthieu@mnapoli.fr>
@@ -10,5 +10,4 @@
*
* @license MIT
* @url https://myclabs.github.io/jquery.confirm/
*/
(function(a){a.fn.confirm=function(b){if(typeof b==="undefined"){b={}}this.click(function(c){c.preventDefault();var d=a.extend({button:a(this)},b);a.confirm(d,c)});return this};a.confirm=function(k,g){if(typeof k=="undefined"){console.error("No options given.");return}if(a(".confirmation-modal").length>0){return}var j={};if(k.button){var c={title:"title",text:"text","confirm-button":"confirmButton","submit-form":"submitForm","cancel-button":"cancelButton","confirm-button-class":"confirmButtonClass","cancel-button-class":"cancelButtonClass","dialog-class":"dialogClass","modal-options-backdrop":"modalOptionsBackdrop","modal-options-keyboard":"modalOptionsKeyboard"};a.each(c,function(e,l){var m=k.button.data(e);if(typeof m!="undefined"){j[l]=m}})}var d=a.extend({},a.confirm.options,{confirm:function(){if(j.submitForm||(typeof j.submitForm=="undefined"&&k.submitForm)||(typeof j.submitForm=="undefined"&&typeof k.submitForm=="undefined"&&a.confirm.options.submitForm)){g.target.closest("form").submit()}else{var e=g&&(("string"===typeof g&&g)||(g.currentTarget&&g.currentTarget.attributes.href.value));if(e){if(k.post){var l=a('<form method="post" class="hide" action="'+e+'"></form>');a("body").append(l);l.submit()}else{window.location=e}}}},cancel:function(e){},button:null},k,j);var b="";if(d.title!==""){b='<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button><h4 class="modal-title">'+d.title+"</h4></div>"}var h="";if(d.cancelButton){h='<button class="cancel btn '+d.cancelButtonClass+'" type="button" data-dismiss="modal">'+d.cancelButton+"</button>"}var f='<div class="confirmation-modal modal fade" tabindex="-1" role="dialog"><div class="'+d.dialogClass+'"><div class="modal-content">'+b+'<div class="modal-body">'+d.text+'</div><div class="modal-footer"><button class="confirm btn '+d.confirmButtonClass+'" type="button" data-dismiss="modal">'+d.confirmButton+"</button>"+h+"</div></div></div></div>";var i=a(f);if(typeof d.modalOptionsBackdrop!="undefined"||typeof d.modalOptionsKeyboard!="undefined"){i.modal({backdrop:d.modalOptionsBackdrop,keyboard:d.modalOptionsKeyboard})}i.on("shown.bs.modal",function(){i.find(".btn-primary:first").focus()});i.on("hidden.bs.modal",function(){i.remove()});i.find(".confirm").click(function(){d.confirm(d.button)});i.find(".cancel").click(function(){d.cancel(d.button)});a("body").append(i);i.modal("show")};a.confirm.options={text:"Are you sure?",title:"",confirmButton:"Yes",cancelButton:"Cancel",post:false,submitForm:false,confirmButtonClass:"btn-primary",cancelButtonClass:"btn-default",dialogClass:"modal-dialog",modalOptionsBackdrop:true,modalOptionsKeyboard:true}})(jQuery);
*/ !function(o){o.fn.confirm=function(t){return void 0===t&&(t={}),this.click(function(n){n.preventDefault();var i=o.extend({button:o(this)},t);o.confirm(i,n)}),this},o.confirm=function(t,n){if(void 0===t){console.error("No options given.");return}if(!(o(".confirmation-modal").length>0)){var i={};t.button&&o.each({title:"title",text:"text","confirm-button":"confirmButton","submit-form":"submitForm","cancel-button":"cancelButton","confirm-button-class":"confirmButtonClass","cancel-button-class":"cancelButtonClass","dialog-class":"dialogClass","modal-options-backdrop":"modalOptionsBackdrop","modal-options-keyboard":"modalOptionsKeyboard"},function(o,n){var a=t.button.data(o);void 0!==a&&(i[n]=a)});var a=o.extend({},o.confirm.options,{confirm:function(){if(i.submitForm||void 0===i.submitForm&&t.submitForm||void 0===i.submitForm&&void 0===t.submitForm&&o.confirm.options.submitForm)n.target.closest("form").submit();else{var a=n&&("string"==typeof n&&n||n.currentTarget&&n.currentTarget.attributes.href.value);if(a){if(t.post){var s=o('<form method="post" class="hide" action="'+a+'"></form>');o("body").append(s),s.submit()}else window.location=a}}},cancel:function(o){},button:null},t,i),s="";""!==a.title&&(s='<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button><h4 class="modal-title">'+a.title+"</h4></div>");var c="";a.cancelButton&&(c='<button class="cancel btn '+a.cancelButtonClass+'" type="button" data-dismiss="modal">'+a.cancelButton+"</button>");var d=o('<div class="confirmation-modal modal fade" tabindex="-1" role="dialog"><div class="'+a.dialogClass+'"><div class="modal-content">'+s+'<div class="modal-body">'+a.text+'</div><div class="modal-footer"><button class="confirm btn '+a.confirmButtonClass+'" type="button" data-dismiss="modal">'+a.confirmButton+"</button>"+c+"</div></div></div></div>");(void 0!==a.modalOptionsBackdrop||void 0!==a.modalOptionsKeyboard)&&d.modal({backdrop:a.modalOptionsBackdrop,keyboard:a.modalOptionsKeyboard}),d.on("shown.bs.modal",function(){d.find(".btn-primary:first").focus()}),d.on("hidden.bs.modal",function(){d.remove()}),d.find(".confirm").click(function(){a.confirm(a.button)}),d.find(".cancel").click(function(){a.cancel(a.button)}),o("body").append(d),d.modal("show")}},o.confirm.options={text:"Are you sure?",title:"",confirmButton:"Yes",cancelButton:"Cancel",post:!1,submitForm:!1,confirmButtonClass:"btn-primary",cancelButtonClass:"btn-default",dialogClass:"modal-dialog",modalOptionsBackdrop:!0,modalOptionsKeyboard:!0}}(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -504,7 +504,7 @@ p.login-box-msg,
}
.dropdown-toggle {
z-index: 2000;
z-index: 1050;
}
.main-header li.user-header {