196 lines
5.8 KiB
JavaScript
196 lines
5.8 KiB
JavaScript
/* 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, apiFailure:false, i18n */
|
|
|
|
"use strict";
|
|
|
|
i18n.waitForTranslations(() => {
|
|
// Handle hiding of alerts
|
|
$("[data-hide]").on("click", function () {
|
|
$(this)
|
|
.closest("." + $(this).attr("data-hide"))
|
|
.hide();
|
|
});
|
|
|
|
// Handle saving of settings
|
|
$(".save-button").on("click", () => {
|
|
saveSettings();
|
|
});
|
|
});
|
|
|
|
// Globally available function to set config values
|
|
// eslint-disable-next-line no-unused-vars
|
|
function setConfigValues(topic, key, value) {
|
|
// If the value is an object, we need to recurse
|
|
if (!("description" in value)) {
|
|
for (const [subkey, subvalue] of Object.entries(value)) {
|
|
// If the key is empty, we are at the top level
|
|
const newKey = key === "" ? subkey : key + "." + subkey;
|
|
setConfigValues(topic, newKey, subvalue);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// else: we have a setting we can set
|
|
const escapedKey = key.replaceAll(".", "\\.");
|
|
const envTitle = $(`[data-configkeys~='${key}']`);
|
|
|
|
if (
|
|
envTitle.parents().parents().hasClass("settings-level-expert") &&
|
|
envTitle.find(".expert-warning").length === 0
|
|
) {
|
|
envTitle.append(
|
|
`<span class="expert-warning"> <i class="fas fa-wrench" title="${i18n.t("settings.expert_settings")}"></i></span>`
|
|
);
|
|
}
|
|
|
|
if (value.flags.restart_dnsmasq && envTitle.find(".restart-warning").length === 0) {
|
|
envTitle.append(
|
|
`<span class="restart-warning"> <i class="fas fa-redo text-orange" title="${i18n.t("settings.ftl_restart_required")}"></i></span>`
|
|
);
|
|
}
|
|
|
|
if (value.flags.env_var) {
|
|
// If this setting has been set by environment variable, display a padlock in the section title
|
|
if (envTitle.find(".env-warning").length === 0) {
|
|
envTitle.append(
|
|
`<span class="env-warning"> <i class="fas fa-lock text-orange env-warning" title="${i18n.t("settings.env_variables_are_read_only")}"></i></span>`
|
|
);
|
|
}
|
|
|
|
$(`#${escapedKey}`).prop("disabled", "disabled");
|
|
}
|
|
|
|
switch (value.type) {
|
|
case "enum (unsigned integer)": // fallthrough
|
|
case "enum (string)": {
|
|
// Remove all options from select
|
|
$("#" + escapedKey + " option").remove();
|
|
// Add allowed select items (if available)
|
|
for (const allowedValue of value.allowed) {
|
|
$("#" + escapedKey + "-" + allowedValue.item).prop("disabled", value.flags.env_var);
|
|
const newopt = $("<option></option>")
|
|
.attr("value", allowedValue.item)
|
|
.text(allowedValue.description);
|
|
$("#" + escapedKey).append(newopt);
|
|
}
|
|
|
|
// Select the current value
|
|
$("#" + escapedKey)
|
|
.val(value.value)
|
|
.trigger("click");
|
|
|
|
// Also select matching radio button (if any)
|
|
$("#" + escapedKey + "-" + value.value).prop("checked", true);
|
|
|
|
break;
|
|
}
|
|
|
|
case "boolean": {
|
|
// Select checkboxes (if available)
|
|
$("#" + escapedKey).prop("checked", value.value);
|
|
|
|
break;
|
|
}
|
|
|
|
case "string array": {
|
|
// Set input field values from array (if available)
|
|
$("#" + escapedKey).val(value.value.join("\n"));
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
// Set input field values (if available)
|
|
// Set text if this is a <span> or <code> element
|
|
if ($("#" + escapedKey).is("span") || $("#" + escapedKey).is("code")) {
|
|
$("#" + escapedKey).text(value.value);
|
|
} else {
|
|
// Set value if this is an <input> element
|
|
$("#" + escapedKey).val(value.value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function saveSettings() {
|
|
const settings = {};
|
|
utils.disableAll();
|
|
$("[data-key]").each(function () {
|
|
const key = $(this).data("key");
|
|
let value = $(this).val();
|
|
|
|
// If this is a checkbox, use the checked state
|
|
if ($(this).is(":checkbox")) {
|
|
value = $(this).is(":checked");
|
|
}
|
|
|
|
// If this is a radio button, skip all but the checked one
|
|
if ($(this).is(":radio") && !$(this).is(":checked")) return;
|
|
|
|
// If this is a string array, split the value into an array
|
|
if ($(this).is("textarea")) {
|
|
value = $(this).val();
|
|
value = value === "" ? [] : value.split("\n");
|
|
}
|
|
|
|
// If this is an integer number, parse it accordingly
|
|
if ($(this).data("type") === "integer") {
|
|
value = Number.parseInt(value, 10);
|
|
}
|
|
|
|
// If this is a floating point value, parse it accordingly
|
|
if ($(this).data("type") === "float") {
|
|
value = Number.parseFloat(value);
|
|
}
|
|
|
|
// Build deep object
|
|
// Transform "foo.bar.baz" into {foo: {bar: {baz: value}}}
|
|
const parts = key.split(".");
|
|
const obj = {};
|
|
let tmp = obj;
|
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
tmp[parts[i]] = {};
|
|
tmp = tmp[parts[i]];
|
|
}
|
|
|
|
tmp[parts.at(-1)] = value;
|
|
|
|
// Merge deep object into settings
|
|
$.extend(true, settings, obj);
|
|
});
|
|
|
|
// Apply changes
|
|
$.ajax({
|
|
url: document.body.dataset.apiurl + "/config",
|
|
method: "PATCH",
|
|
dataType: "json",
|
|
processData: false,
|
|
data: JSON.stringify({ config: settings }),
|
|
contentType: "application/json; charset=utf-8",
|
|
})
|
|
.done(() => {
|
|
utils.enableAll();
|
|
// Success
|
|
utils.showAlert(
|
|
"success",
|
|
"fa-solid fa-fw fa-floppy-disk",
|
|
i18n.t("settings.successfully_saved_settings"),
|
|
""
|
|
);
|
|
// Show loading overlay
|
|
utils.loadingOverlay(true);
|
|
})
|
|
.fail((data, exception) => {
|
|
utils.enableAll();
|
|
utils.showAlert("error", "", i18n.t("settings.error_applying_settings"), data.responseText);
|
|
console.log(exception); // eslint-disable-line no-console
|
|
apiFailure(data);
|
|
});
|
|
}
|