/* Pi-hole: A black hole for Internet advertisements * (c) 2023 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, applyCheckboxRadioStyle: false, saveSettings:false */ /* exported createDynamicConfigTabs */ function addAllowedValues(allowed) { if (typeof allowed === "object") { return ( "

Available options:

" ); } else if (typeof allowed === "string") { return "

Allowed value: " + utils.escapeHtml(allowed) + "

"; } } function boxIcons(value) { return ( '' + (value.modified ? '' : "") + (value.flags.restart_dnsmasq ? '' : "") + (value.flags.env_var ? '' : "") + "" ); } function valueDetails(key, value) { // Define default hint text let defaultValueHint = ""; if (value.modified) { defaultValueHint = ""; if (value.default !== null) { let defVal = utils.escapeHtml(JSON.stringify(value.default)); switch (defVal) { case "true": { defVal = "enabled"; break; } case "false": { defVal = "disabled"; break; } case '""': case "[]": { defVal = "empty"; break; } // No default } defaultValueHint = "

Default Value: " + defVal + "

"; } } // Define extraAttributes, if needed let extraAttributes = ""; if (value.flags.env_var) { extraAttributes = " disabled"; } // Format the output depending on the value type let content = ""; switch (value.type) { case "IPv4 address": case "IPv6 address": case "string": { content += '' + '
' + ' " + defaultValueHint + addAllowedValues(value.allowed) + "
"; break; } case "boolean": { content += '
' + '
" + "
"; break; } case "double": { content += '' + '
' + ' " + defaultValueHint + "
"; break; } case "integer": { content += '' + '
' + ' " + defaultValueHint + "
"; break; } case "unsigned integer": { content += '' + '
' + ' " + defaultValueHint + "
"; break; } case "unsigned integer (16 bit)": { content += '' + '
' + ' " + defaultValueHint + "
"; break; } case "string array": { content += '' + '
' + ' " + defaultValueHint + addAllowedValues(value.allowed) + "
"; break; } case "enum (unsigned integer)": // fallthrough case "enum (string)": { content += '
'; value.allowed.forEach(function (option, i) { content += "
" + // Radio button '" + // Label `" + // Paragraph with description `

${option.description}

` + "
"; }); content += "
"; break; } case "password (write-only string)": { content += '' + '
' + ' " + defaultValueHint + addAllowedValues(value.allowed) + "
"; break; } default: { content += "TYPE " + value.type + " NOT DEFINED"; } } return '
' + content + "
"; } function generateRow(topic, key, value) { // If the value is an object, we need to recurse if (!("description" in value)) { Object.keys(value).forEach(function (subkey) { var subvalue = value[subkey]; generateRow(topic, key + "." + subkey, subvalue); }); return; } // else: we have a setting we can display var box = '
' + '
' + '

' + key + boxIcons(value) + "

" + "
" + '
' + utils.escapeHtml(value.description).replaceAll("\n", "
") + "
" + '
"; var topKey = key.split(".")[0]; var elem = $("#advanced-content-" + topKey + "-flex"); elem.append(box); } function createDynamicConfigTabs() { $.ajax({ url: document.body.dataset.apiurl + "/config?detailed=true", }) .done(function (data) { // Create the tabs for the advanced dynamic config topics Object.keys(data.topics).forEach(function (n) { var topic = data.topics[n]; $("#advanced-settings-tabs").append(`
`); // Dynamically create the settings menu $("#advanced-settings-menu ul").append(`
  • ${topic.description.replace(" settings", "")}
  • `); }); // Dynamically fill the tabs with config topics Object.keys(data.config).forEach(function (topic) { var value = data.config[topic]; generateRow(topic, topic, value); }); $("#advanced-overlay").hide(); // Select the first tab and show the content $("#advanced-settings-menu ul li:first-child").addClass("active"); $("#advanced-settings-tabs > div:first-child").addClass("active in"); $("button[id='save']").on("click", function () { saveSettings(); }); applyCheckboxRadioStyle(); applyOnlyChanged(); }) .fail(function (data) { apiFailure(data); }); } function initOnlyChanged() { const elem = $("#only-changed"); // Restore settings level from local storage (if available) or default to "false" if (localStorage.getItem("only-changed") === null) { localStorage.setItem("only-changed", "false"); } elem.prop("checked", localStorage.getItem("only-changed") === "true"); elem.bootstrapToggle({ on: "Modified settings", off: "All settings", onstyle: "primary", offstyle: "success", size: "small", width: "180px", }); elem.on("change", function () { localStorage.setItem("only-changed", $(this).prop("checked") ? "true" : "false"); applyOnlyChanged(); }); elem.bootstrapToggle(localStorage.getItem("only-changed") === "true" ? "on" : "off"); elem.trigger("change"); } function applyOnlyChanged() { if (localStorage.getItem("only-changed") === "true") { // Show only modified settings (hide tabs menu and empty tabs). // Hide the tabs menu $("#advanced-settings-menu").hide(); // Show all tabs, except the ones containing "data-modified='true'" attribute // to prevent empty tabs (using the same classes used by Boostrap3) $("#advanced-settings-tabs > .tab-pane").addClass("in active"); $("#advanced-settings-tabs > .tab-pane:not(:has(h3[data-modified='true']))").removeClass( "in active" ); // Hide all boxes with data-key attribute, except the ones with "data-modified='true'" attribute $(".box-title[data-key]").not("[data-modified='true']").closest(".box").hide(); } else { // Show the tabs menu and activate only the first button (deactivate other buttons) $("#advanced-settings-menu").show(); $("#advanced-settings-menu ul li").removeClass("active"); $("#advanced-settings-menu ul li:first-child").addClass("active"); // Hide all tabs, except the first one (removing the classes used by Boostrap3) $("#advanced-settings-tabs > .tab-pane:not(:first-child)").removeClass("in active"); // Show all boxes with data-key attribute $(".box-title[data-key]").closest(".box").show(); } } $(document).ready(function () { createDynamicConfigTabs(); initOnlyChanged(); });