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 ability to specify domains, lists, clients and group names as arrays #2707

Merged
merged 4 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions scripts/pi-hole/js/groups-adlists.js
Original file line number Diff line number Diff line change
Expand Up @@ -499,13 +499,21 @@ function delItems(ids) {

function addAdlist(event) {
const type = event.data.type;
const address = utils.escapeHtml($("#new_address").val());
const comment = utils.escapeHtml($("#new_comment").val());

// Check if the user wants to add multiple domains (space or newline separated)
// If so, split the input and store it in an array
var addresses = utils.escapeHtml($("#new_address").val()).split(/[\s,]+/);
// Remove empty elements
addresses = addresses.filter(function (el) {
return el !== "";
});
const addressestr = JSON.stringify(addresses);

utils.disableAll();
utils.showAlert("info", "", "Adding subscribed " + type + "list...", address);
utils.showAlert("info", "", "Adding subscribed " + type + "list(s)...", addressestr);

if (address.length === 0) {
if (addresses.length === 0) {
// enable the ui elements again
utils.enableAll();
utils.showAlert("warning", "", "Warning", "Please specify " + type + "list address");
Expand All @@ -517,10 +525,10 @@ function addAdlist(event) {
method: "post",
dataType: "json",
processData: false,
data: JSON.stringify({ address: address, comment: comment, type: type }),
success: function () {
data: JSON.stringify({ address: addresses, comment: comment, type: type }),
success: function (data) {
utils.enableAll();
utils.showAlert("success", "fas fa-plus", "Successfully added " + type + "list", address);
utils.listsAlert("list", addresses, data);
table.ajax.reload(null, false);
table.rows().deselect();

Expand Down
57 changes: 35 additions & 22 deletions scripts/pi-hole/js/groups-clients.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,34 +405,47 @@ function delItems(ids) {
}

function addClient() {
var ip = utils.escapeHtml($("#select").val().trim());
const comment = utils.escapeHtml($("#new_comment").val());

utils.disableAll();
utils.showAlert("info", "", "Adding client...", ip);

if (ip.length === 0) {
utils.enableAll();
utils.showAlert("warning", "", "Warning", "Please specify a client IP or MAC address");
return;
}
// Check if the user wants to add multiple IPs (space or newline separated)
// If so, split the input and store it in an array
var ips = utils.escapeHtml($("#select").val().trim()).split(/[\s,]+/);
// Remove empty elements
ips = ips.filter(function (el) {
return el !== "";
});
const ipStr = JSON.stringify(ips);

// Validate input, can be:
// - IPv4 address (with and without CIDR)
// - IPv6 address (with and without CIDR)
// - MAC address (in the form AA:BB:CC:DD:EE:FF)
// - host name (arbitrary form, we're only checking against some reserved characters)
if (utils.validateIPv4CIDR(ip) || utils.validateIPv6CIDR(ip) || utils.validateMAC(ip)) {
// Convert input to upper case (important for MAC addresses)
ip = ip.toUpperCase();
} else if (!utils.validateHostname(ip)) {
for (var i = 0; i < ips.length; i++) {
if (
utils.validateIPv4CIDR(ips[i]) ||
utils.validateIPv6CIDR(ips[i]) ||
utils.validateMAC(ips[i])
) {
// Convert input to upper case (important for MAC addresses)
ips[i] = ips[i].toUpperCase();
} else if (!utils.validateHostname(ips[i])) {
utils.showAlert(
"warning",
"",
"Warning",
"Input is neither a valid IP or MAC address nor a valid host name!"
);
return;
}
}

utils.disableAll();
utils.showAlert("info", "", "Adding client(s)...", ipStr);

if (ips.length === 0) {
utils.enableAll();
utils.showAlert(
"warning",
"",
"Warning",
"Input is neither a valid IP or MAC address nor a valid host name!"
);
utils.showAlert("warning", "", "Warning", "Please specify a client IP or MAC address");
return;
}

Expand All @@ -441,10 +454,10 @@ function addClient() {
method: "post",
dataType: "json",
processData: false,
data: JSON.stringify({ client: ip, comment: comment }),
success: function () {
data: JSON.stringify({ client: ips, comment: comment }),
success: function (data) {
utils.enableAll();
utils.showAlert("success", "fas fa-plus", "Successfully added client", ip);
utils.listsAlert("client", ips, data);
reloadClientSuggestions();
table.ajax.reload(null, false);
table.rows().deselect();
Expand Down
40 changes: 24 additions & 16 deletions scripts/pi-hole/js/groups-domains.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,46 +497,54 @@ function addDomain() {
commentEl = $("#new_regex_comment");
}

var domain = utils.escapeHtml(domainEl.val());
const comment = utils.escapeHtml(commentEl.val());

// Check if the user wants to add multiple domains (space or newline separated)
// If so, split the input and store it in an array
var domains = utils.escapeHtml(domainEl.val()).split(/[\s,]+/);
// Remove empty elements
domains = domains.filter(function (el) {
return el !== "";
});
const domainStr = JSON.stringify(domains);

utils.disableAll();
utils.showAlert("info", "", "Adding domain...", domain);
utils.showAlert("info", "", "Adding domain(s)...", domainStr);

if (domain.length < 2) {
if (domains.length === 0) {
utils.enableAll();
utils.showAlert("warning", "", "Warning", "Please specify a domain");
utils.showAlert("warning", "", "Warning", "Please specify at least one domain");
return;
}

// strip "*." if specified by user in wildcard mode
if (kind === "exact" && wildcardChecked && domain.startsWith("*.")) {
domain = domain.substr(2);
for (var i = 0; i < domains.length; i++) {
if (kind === "exact" && wildcardChecked) {
// Transform domain to wildcard if specified by user
domains[i] = "(\\.|^)" + domains[i].replaceAll(".", "\\.") + "$";
kind = "regex";

// strip leading "*." if specified by user in wildcard mode
if (domains[i].startsWith("*.")) domains[i] = domains[i].substr(2);
}
}

// determine list type
const type = action === "add_deny" ? "deny" : "allow";

// Transform domain to wildcard if specified by user
if (kind === "exact" && wildcardChecked) {
domain = "(\\.|^)" + domain.replaceAll(".", "\\.") + "$";
kind = "regex";
}

$.ajax({
url: "/api/domains/" + type + "/" + kind,
method: "post",
dataType: "json",
processData: false,
data: JSON.stringify({
domain: domain,
domain: domains,
comment: comment,
type: type,
kind: kind,
}),
success: function () {
success: function (data) {
utils.enableAll();
utils.showAlert("success", "fas fa-plus", "Successfully added domain", domain);
utils.listsAlert("domain", domains, data);
table.ajax.reload(null, false);
table.rows().deselect();

Expand Down
23 changes: 17 additions & 6 deletions scripts/pi-hole/js/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,13 +277,24 @@ function delItems(ids) {
}

function addGroup() {
const name = utils.escapeHtml($("#new_name").val());
const comment = utils.escapeHtml($("#new_comment").val());

// Check if the user wants to add multiple groups (space or newline separated)
// If so, split the input and store it in an array
var names = utils
.escapeHtml($("#new_name"))
.val()
.split(/[\s,]+/);
// Remove empty elements
names = names.filter(function (el) {
return el !== "";
});
const groupStr = JSON.stringify(names);

utils.disableAll();
utils.showAlert("info", "", "Adding group...", name);
utils.showAlert("info", "", "Adding group(s)...", groupStr);

if (name.length === 0) {
if (names.length === 0) {
// enable the ui elements again
utils.enableAll();
utils.showAlert("warning", "", "Warning", "Please specify a group name");
Expand All @@ -296,13 +307,13 @@ function addGroup() {
dataType: "json",
processData: false,
data: JSON.stringify({
name: name,
name: names,
comment: comment,
enabled: true,
}),
success: function () {
success: function (data) {
utils.enableAll();
utils.showAlert("success", "fas fa-plus", "Successfully added group", name);
utils.listsAlert("group", names, data);
$("#new_name").val("");
$("#new_comment").val("");
table.ajax.reload();
Expand Down
76 changes: 76 additions & 0 deletions scripts/pi-hole/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,81 @@ function hexDecode(string) {
return back;
}

function listAlert(type, items, data) {
// Show simple success message if there is no "processed" object in "data" or
// if all items were processed successfully
if (data.processed === undefined || data.processed.success.length === items.length) {
showAlert(
"success",
"fas fa-plus",
"Successfully added " + type + (items.length !== 1 ? "s" : ""),
items.join(", ")
);
return;
}

// Show a more detailed message if there is a "processed" object in "data" and
// not all items were processed successfully
let message = "";

// Show a list of successful items if there are any
if (data.processed.success.length > 0) {
message +=
"<strong>Successfully added " +
data.processed.success.length +
" " +
type +
(data.processed.success.length !== 1 ? "s" : "") +
":</strong>";

// Loop over data.processed.success and print "item"
for (const item in data.processed.success) {
if (Object.prototype.hasOwnProperty.call(data.processed.success, item)) {
message += "<br>- <strong>" + data.processed.success[item].item + "</strong>";
}
}
}

// Add a line break if there are both successful and failed items
if (data.processed.success.length > 0 && data.processed.errors.length > 0) {
message += "<br><br>";
}

// Show a list of failed items if there are any
if (data.processed.errors.length > 0) {
message +=
"<strong>Failed to add " +
data.processed.errors.length +
" " +
type +
(data.processed.errors.length !== 1 ? "s" : "") +
":</strong>\n";

// Loop over data.processed.errors and print "item: error"
for (const item in data.processed.errors) {
if (Object.prototype.hasOwnProperty.call(data.processed.errors, item)) {
let error = data.processed.errors[item].error;
// Replace some error messages with a more user-friendly text
if (error.indexOf("UNIQUE constraint failed") > -1) {
error = "Already present";
}

message += "<br>- <strong>" + data.processed.errors[item].item + "</strong>: " + error;
}
}
}

// Show the warning message
const total = data.processed.success.length + data.processed.errors.length;
const processed = "(" + total + " " + type + (total !== 1 ? "s" : "") + " processed)";
showAlert(
"warning",
"fas fa-exclamation-triangle",
"Some " + type + (items.length !== 1 ? "s" : "") + " could not be added " + processed,
message
);
}

window.utils = (function () {
return {
escapeHtml: escapeHtml,
Expand Down Expand Up @@ -570,5 +645,6 @@ window.utils = (function () {
parseQueryString: parseQueryString,
hexEncode: hexEncode,
hexDecode: hexDecode,
listsAlert: listAlert,
};
})();