356 lines
10 KiB
JavaScript
356 lines
10 KiB
JavaScript
/**
|
|
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
|
|
* Copyright (C) 2020 Aleksey Andreev (liuch)
|
|
*
|
|
* Available at:
|
|
* https://github.com/liuch/dmarc-srg
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation, either version 3 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
class Settings {
|
|
constructor() {
|
|
this._table = null;
|
|
this._scrool = null;
|
|
this._sort = "ascent";
|
|
this._element = document.getElementById("main-block");
|
|
}
|
|
|
|
display() {
|
|
this._make_scroll_container();
|
|
this._make_table();
|
|
this._scroll.appendChild(this._table.element());
|
|
this._element.appendChild(this._scroll);
|
|
this._table.focus();
|
|
}
|
|
|
|
update() {
|
|
this._fetch_settings();
|
|
}
|
|
|
|
title() {
|
|
return "Advanced Settings";
|
|
}
|
|
|
|
_fetch_settings() {
|
|
this._table.display_status("wait");
|
|
let that = this;
|
|
|
|
let uparams = new URLSearchParams();
|
|
uparams.set("direction", this._sort);
|
|
|
|
return window.fetch("settings.php?" + uparams.toString(), {
|
|
method: "GET",
|
|
cache: "no-store",
|
|
headers: HTTP_HEADERS,
|
|
credentials: "same-origin"
|
|
}).then(function(resp) {
|
|
if (!resp.ok)
|
|
throw new Error("Failed to fetch the settings");
|
|
return resp.json();
|
|
}).then(function(data) {
|
|
that._table.display_status(null);
|
|
Common.checkResult(data);
|
|
let d = { more: data.more };
|
|
d.rows = data.settings.map(function(it) {
|
|
return that._make_row_data(it);
|
|
});
|
|
that._table.clear();
|
|
let fr = new ITableFrame(d, 0);
|
|
that._table.add_frame(fr);
|
|
that._table.focus();
|
|
}).catch(function(err) {
|
|
Common.displayError(err);
|
|
that._table.display_status("error", err.message);
|
|
});
|
|
}
|
|
|
|
_make_scroll_container() {
|
|
this._scroll = document.createElement("div");
|
|
this._scroll.setAttribute("class", "main-table-container");
|
|
}
|
|
|
|
_make_table() {
|
|
this._table = new ITable({
|
|
class: "main-table small-cards",
|
|
onclick: function(row) {
|
|
let data = row.userdata();
|
|
if (data) {
|
|
this._display_edit_dialog(data);
|
|
}
|
|
}.bind(this),
|
|
onsort: function(col) {
|
|
let dir = col.sorted() && "toggle" || "ascent";
|
|
this._table.set_sorted(col.name(), dir);
|
|
this._sort = col.sorted();
|
|
this.update();
|
|
}.bind(this),
|
|
onfocus: function(el) {
|
|
scroll_to_element(el, this._scroll);
|
|
}.bind(this)
|
|
});
|
|
[
|
|
{ content: "Name", name: "name", sortable: true },
|
|
{ content: "Value", name: "value" },
|
|
{ content: "Description", name: "descr" }
|
|
].forEach(function(col) {
|
|
let c = this._table.add_column(col);
|
|
if (c.name() === "name") {
|
|
c.sort(this._sort);
|
|
}
|
|
}, this);
|
|
}
|
|
|
|
_make_row_data(d) {
|
|
let rd = { cells: [], userdata: d.name };
|
|
rd.cells.push({ content: d.name, class: "setting-name", label: "Name " });
|
|
rd.cells.push({ content: d.value, class: "setting-value", label: "Value " });
|
|
rd.cells.push({ content: Settings._descriptions_short[d.name] || Settings._descriptions[d.name] || "No description", label: "Description " });
|
|
if (d.value !== d.default) {
|
|
rd.class = "custom-value";
|
|
}
|
|
return rd;
|
|
}
|
|
|
|
_display_edit_dialog(name) {
|
|
let dlg = new SettingEditDialog({
|
|
name: name,
|
|
description: Settings._descriptions[name]
|
|
});
|
|
this._element.appendChild(dlg.element());
|
|
let that = this;
|
|
dlg.show().then(function(d) {
|
|
if (d) {
|
|
that.update();
|
|
}
|
|
}).finally(function() {
|
|
dlg.element().remove();
|
|
that._table.focus();
|
|
});
|
|
}
|
|
|
|
static _descriptions = {
|
|
"status.emails-for-last-n-days": "The period in days for which statistics are displayed in the status block.",
|
|
"report-view.sort-records-by": "How records are sorted in the report view dialog.",
|
|
"log-view.sort-list-by": "How report log items are sorted by default in the log view dialog.",
|
|
"ui.datetime.offset": "Time zone offset of displayed dates in UI. Auto means that the report range is in UTC and all other dates are in local.",
|
|
"ui.ipv4.url": "The URL that will be used as a link when clicking on the IPv4 address. For example: https://somewhoisservice.net/ip/{$ip}, where {$ip} is IP address from the UI. Use {$eip} if you want to insert url encoded IP address. Use an empty string to disable.",
|
|
"ui.ipv6.url": "The URL that will be used as a link when clicking on the IPv6 address. For example: https://somewhoisservice.net/ip/{$ip}, where {$ip} is IP address from the UI. Use {$eip} if you want to insert url encoded IP address. Use an empty string to disable."
|
|
};
|
|
|
|
static _descriptions_short = {
|
|
"ui.datetime.offset": "Time zone offset of displayed dates in UI.",
|
|
"ui.ipv4.url": "The URL that will be used as a link when clicking on the IPv4 address.",
|
|
"ui.ipv6.url": "The URL that will be used as a link when clicking on the IPv6 address."
|
|
};
|
|
}
|
|
|
|
class SettingEditDialog extends ModalDialog {
|
|
constructor(param) {
|
|
super({ title: "Setting dialog", buttons: [ "ok", "close" ] });
|
|
this._data = param || {};
|
|
this._content = null;
|
|
this._table = null;
|
|
this._val_el = null;
|
|
this._val_tp = null;
|
|
this._desc_el = null;
|
|
this._save_bt = null;
|
|
this._fetched = false;
|
|
}
|
|
|
|
_gen_content() {
|
|
this._table = document.createElement("div");
|
|
this._table.setAttribute("class", "titled-input");
|
|
this._content.appendChild(this._table);
|
|
this._content.classList.add("vertical-content");
|
|
|
|
let nm = document.createElement("input");
|
|
nm.setAttribute("type", "text");
|
|
nm.setAttribute("disabled", "disabled");
|
|
nm.setAttribute("value", this._data.name);
|
|
this._insert_row("Name", nm);
|
|
|
|
let val = document.createElement("input");
|
|
val.setAttribute("type", "text");
|
|
val.disabled = true;
|
|
this._insert_row("Value", val);
|
|
this._val_el = val;
|
|
this._val_tp = "string";
|
|
|
|
let desc = document.createElement("textarea");
|
|
desc.setAttribute("disabled", "disabled");
|
|
if (this._data.description) {
|
|
desc.appendChild(document.createTextNode(this._data.description));
|
|
}
|
|
desc.classList.add("description");
|
|
this._insert_row("Description", desc);
|
|
this._desc_el = desc;
|
|
|
|
this._save_bt = this._buttons[1];
|
|
this._save_bt.disabled = true;
|
|
|
|
this._table.addEventListener("input", function(event) {
|
|
if (this._fetched && event.target == this._val_el) {
|
|
let e_val = null;
|
|
switch (this._val_tp) {
|
|
case "select":
|
|
e_val = this._val_el.value;
|
|
break;
|
|
case "integer":
|
|
e_val = this._val_el.valueAsNumber;
|
|
break;
|
|
}
|
|
this._save_bt.disabled = (e_val === this._data.value);
|
|
}
|
|
}.bind(this));
|
|
|
|
this._fetch_data();
|
|
}
|
|
|
|
_add_button(container, text, type) {
|
|
if (type == "submit") {
|
|
text = "Save";
|
|
}
|
|
super._add_button(container, text, type);
|
|
}
|
|
|
|
_insert_row(text, val_el) {
|
|
let lb = document.createElement("label");
|
|
let sp = document.createElement("span");
|
|
sp.appendChild(document.createTextNode(text + ": "));
|
|
lb.appendChild(sp);
|
|
lb.appendChild(val_el);
|
|
this._table.appendChild(lb);
|
|
}
|
|
|
|
_fetch_data() {
|
|
this._enable_ui(false);
|
|
this._content.appendChild(set_wait_status());
|
|
let uparams = new URLSearchParams();
|
|
uparams.set("name", this._data.name);
|
|
|
|
let that = this;
|
|
window.fetch("settings.php?" + uparams.toString(), {
|
|
method: "GET",
|
|
cache: "no-store",
|
|
headers: HTTP_HEADERS,
|
|
credentials: "same-origin"
|
|
}).then(function(resp) {
|
|
if (!resp.ok)
|
|
throw new Error("Failed to fetch setting data for " + that._data.name);
|
|
return resp.json();
|
|
}).then(function(data) {
|
|
Common.checkResult(data);
|
|
that._data.value = data.value;
|
|
that._update_ui(data);
|
|
that._enable_ui(true);
|
|
that._fetched = true;
|
|
}).catch(function(err) {
|
|
Common.displayError(err);
|
|
that._content.appendChild(set_error_status(null, err.message));
|
|
}).finally(function() {
|
|
that._content.querySelector(".wait-message").remove();
|
|
});
|
|
}
|
|
|
|
_enable_ui(en) {
|
|
this._val_el.disabled = !en;
|
|
this._update_first_last();
|
|
if (this._first) {
|
|
this._first.focus();
|
|
}
|
|
}
|
|
|
|
_update_ui(data) {
|
|
if (data.type !== this._val_tp) {
|
|
let new_el = null;
|
|
if (data.type == "integer") {
|
|
new_el = document.createElement("input");
|
|
new_el.setAttribute("type", "number");
|
|
if (typeof(data.minimum) == "number") {
|
|
new_el.setAttribute("min", data.minimum);
|
|
}
|
|
if (typeof(data.maximum) == "number") {
|
|
new_el.setAttribute("max", data.maximum);
|
|
}
|
|
} else if (data.type == "select") {
|
|
new_el = document.createElement("select");
|
|
data.options.forEach(function(op) {
|
|
let opt_el = document.createElement("option");
|
|
opt_el.setAttribute("value", op);
|
|
opt_el.appendChild(document.createTextNode(op));
|
|
if (op === data.value) {
|
|
opt_el.setAttribute("selected", "selected");
|
|
}
|
|
new_el.appendChild(opt_el);
|
|
});
|
|
}
|
|
if (new_el) {
|
|
new_el.setAttribute("required", "required");
|
|
this._val_el.replaceWith(new_el);
|
|
this._val_el = new_el;
|
|
}
|
|
this._val_tp = data.type;
|
|
}
|
|
this._val_el.value = data.value;
|
|
}
|
|
|
|
_submit() {
|
|
this._save_bt.disabled = true;
|
|
this._enable_ui(false);
|
|
let em = this._content.querySelector(".error-message");
|
|
if (em) {
|
|
em.remove();
|
|
}
|
|
this._content.appendChild(set_wait_status());
|
|
|
|
let body = {};
|
|
body.name = this._data.name;
|
|
if (this._val_tp == "integer") {
|
|
body.value = this._val_el.valueAsNumber;
|
|
}
|
|
else {
|
|
body.value = this._val_el.value;
|
|
}
|
|
body.action = "update";
|
|
|
|
let that = this;
|
|
window.fetch("settings.php", {
|
|
method: "POST",
|
|
cache: "no-store",
|
|
headers: Object.assign(HTTP_HEADERS, HTTP_HEADERS_POST),
|
|
credentials: "same-origin",
|
|
body: JSON.stringify(body)
|
|
}).then(function(resp) {
|
|
if (!resp.ok)
|
|
throw new Error("Failed to update the setting");
|
|
return resp.json();
|
|
}).then(function(data) {
|
|
Common.checkResult(data);
|
|
that._data.value = that._val_el.value;
|
|
that._result = body;
|
|
that.hide();
|
|
Notification.add({ type: "info", text: (data.message || "Updated successfully!") });
|
|
}).catch(function(err) {
|
|
Common.displayError(err);
|
|
that._content.appendChild(set_error_status(null, err.message));
|
|
Notification.add({ type: "error", text: err.message });
|
|
}).finally(function() {
|
|
that._content.querySelector(".wait-message").remove();
|
|
that._save_bt.disabled = false;
|
|
that._enable_ui(true);
|
|
});
|
|
}
|
|
}
|
|
|