@@ -0,0 +1,233 @@ | |||
/* global jQuery */ | |||
define(["jquery", "nprogress"], | |||
($, NProgress) => { | |||
"use strict"; | |||
const ui = { | |||
chartLegend: [ | |||
{label: "reject", color: "#FF0000"}, | |||
{label: "soft reject", color: "#BF8040"}, | |||
{label: "rewrite subject", color: "#FF6600"}, | |||
{label: "add header", color: "#FFAD00"}, | |||
{label: "greylist", color: "#436EEE"}, | |||
{label: "no action", color: "#66CC00"} | |||
], | |||
locale: (localStorage.getItem("selected_locale") === "custom") ? localStorage.getItem("custom_locale") : null, | |||
neighbours: [], | |||
page_size: { | |||
scan: 25, | |||
errors: 25, | |||
history: 25 | |||
}, | |||
symbols: { | |||
scan: [], | |||
history: [] | |||
}, | |||
tables: {} | |||
}; | |||
NProgress.configure({ | |||
minimum: 0.01, | |||
showSpinner: false, | |||
}); | |||
function getPassword() { | |||
return sessionStorage.getItem("Password"); | |||
} | |||
function alertMessage(alertClass, alertText) { | |||
const a = $("<div class=\"alert " + alertClass + " alert-dismissible fade in show\">" + | |||
"<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" title=\"Dismiss\"></button>" + | |||
"<strong>" + alertText + "</strong>"); | |||
$(".notification-area").append(a); | |||
setTimeout(() => { | |||
$(a).fadeTo(500, 0).slideUp(500, function () { | |||
$(this).alert("close"); | |||
}); | |||
}, 5000); | |||
} | |||
function queryServer(neighbours_status, ind, req_url, o) { | |||
neighbours_status[ind].checked = false; | |||
neighbours_status[ind].data = {}; | |||
neighbours_status[ind].status = false; | |||
const req_params = { | |||
jsonp: false, | |||
data: o.data, | |||
headers: $.extend({Password: getPassword()}, o.headers), | |||
url: neighbours_status[ind].url + req_url, | |||
xhr: function () { | |||
const xhr = $.ajaxSettings.xhr(); | |||
// Download progress | |||
if (req_url !== "neighbours") { | |||
xhr.addEventListener("progress", (e) => { | |||
if (e.lengthComputable) { | |||
neighbours_status[ind].percentComplete = e.loaded / e.total; | |||
const percentComplete = neighbours_status | |||
.reduce((prev, curr) => (curr.percentComplete ? curr.percentComplete + prev : prev), 0); | |||
NProgress.set(percentComplete / neighbours_status.length); | |||
} | |||
}, false); | |||
} | |||
return xhr; | |||
}, | |||
success: function (json) { | |||
neighbours_status[ind].checked = true; | |||
neighbours_status[ind].status = true; | |||
neighbours_status[ind].data = json; | |||
}, | |||
error: function (jqXHR, textStatus, errorThrown) { | |||
neighbours_status[ind].checked = true; | |||
function errorMessage() { | |||
alertMessage("alert-error", neighbours_status[ind].name + " > " + | |||
(o.errorMessage ? o.errorMessage : "Request failed") + | |||
(errorThrown ? ": " + errorThrown : "")); | |||
} | |||
if (o.error) { | |||
o.error(neighbours_status[ind], | |||
jqXHR, textStatus, errorThrown); | |||
} else if (o.errorOnceId) { | |||
const alert_status = o.errorOnceId + neighbours_status[ind].name; | |||
if (!(alert_status in sessionStorage)) { | |||
sessionStorage.setItem(alert_status, true); | |||
errorMessage(); | |||
} | |||
} else { | |||
errorMessage(); | |||
} | |||
}, | |||
complete: function (jqXHR) { | |||
if (neighbours_status.every((elt) => elt.checked)) { | |||
if (neighbours_status.some((elt) => elt.status)) { | |||
if (o.success) { | |||
o.success(neighbours_status, jqXHR); | |||
} else { | |||
alertMessage("alert-success", "Request completed"); | |||
} | |||
} else { | |||
alertMessage("alert-error", "Request failed"); | |||
} | |||
if (o.complete) o.complete(); | |||
NProgress.done(); | |||
} | |||
}, | |||
statusCode: o.statusCode | |||
}; | |||
if (o.method) { | |||
req_params.method = o.method; | |||
} | |||
if (o.params) { | |||
$.each(o.params, (k, v) => { | |||
req_params[k] = v; | |||
}); | |||
} | |||
$.ajax(req_params); | |||
} | |||
// Public functions | |||
ui.alertMessage = alertMessage; | |||
ui.getPassword = getPassword; | |||
// Get selectors' current state | |||
ui.getSelector = function (id) { | |||
const e = document.getElementById(id); | |||
return e.options[e.selectedIndex].value; | |||
}; | |||
/** | |||
* @param {string} url - A string containing the URL to which the request is sent | |||
* @param {Object} [options] - A set of key/value pairs that configure the Ajax request. All settings are optional. | |||
* | |||
* @param {Function} [options.complete] - A function to be called when the requests to all neighbours complete. | |||
* @param {Object|string|Array} [options.data] - Data to be sent to the server. | |||
* @param {Function} [options.error] - A function to be called if the request fails. | |||
* @param {string} [options.errorMessage] - Text to display in the alert message if the request fails. | |||
* @param {string} [options.errorOnceId] - A prefix of the alert ID to be added to the session storage. If the | |||
* parameter is set, the error for each server will be displayed only once per session. | |||
* @param {Object} [options.headers] - An object of additional header key/value pairs to send along with requests | |||
* using the XMLHttpRequest transport. | |||
* @param {string} [options.method] - The HTTP method to use for the request. | |||
* @param {Object} [options.params] - An object of additional jQuery.ajax() settings key/value pairs. | |||
* @param {string} [options.server] - A server to which send the request. | |||
* @param {Function} [options.success] - A function to be called if the request succeeds. | |||
* | |||
* @returns {undefined} | |||
*/ | |||
ui.query = function (url, options) { | |||
// Force options to be an object | |||
const o = options || {}; | |||
Object.keys(o).forEach((option) => { | |||
if (["complete", "data", "error", "errorMessage", "errorOnceId", "headers", "method", "params", "server", | |||
"statusCode", "success"] | |||
.indexOf(option) < 0) { | |||
throw new Error("Unknown option: " + option); | |||
} | |||
}); | |||
let neighbours_status = [{ | |||
name: "local", | |||
host: "local", | |||
url: "", | |||
}]; | |||
o.server = o.server || ui.getSelector("selSrv"); | |||
if (o.server === "All SERVERS") { | |||
queryServer(neighbours_status, 0, "neighbours", { | |||
success: function (json) { | |||
const [{data}] = json; | |||
if (jQuery.isEmptyObject(data)) { | |||
ui.neighbours = { | |||
local: { | |||
host: window.location.host, | |||
url: window.location.origin + window.location.pathname | |||
} | |||
}; | |||
} else { | |||
ui.neighbours = data; | |||
} | |||
neighbours_status = []; | |||
$.each(ui.neighbours, (ind) => { | |||
neighbours_status.push({ | |||
name: ind, | |||
host: ui.neighbours[ind].host, | |||
url: ui.neighbours[ind].url, | |||
}); | |||
}); | |||
$.each(neighbours_status, (ind) => { | |||
queryServer(neighbours_status, ind, url, o); | |||
}); | |||
}, | |||
errorMessage: "Cannot receive neighbours data" | |||
}); | |||
} else { | |||
if (o.server !== "local") { | |||
neighbours_status = [{ | |||
name: o.server, | |||
host: ui.neighbours[o.server].host, | |||
url: ui.neighbours[o.server].url, | |||
}]; | |||
} | |||
queryServer(neighbours_status, 0, url, o); | |||
} | |||
}; | |||
ui.escapeHTML = function (string) { | |||
const htmlEscaper = /[&<>"'/`=]/g; | |||
const htmlEscapes = { | |||
"&": "&", | |||
"<": "<", | |||
">": ">", | |||
"\"": """, | |||
"'": "'", | |||
"/": "/", | |||
"`": "`", | |||
"=": "=" | |||
}; | |||
return String(string).replace(htmlEscaper, (match) => htmlEscapes[match]); | |||
}; | |||
return ui; | |||
}); |
@@ -24,13 +24,13 @@ | |||
/* global require */ | |||
define(["jquery", "app/rspamd"], | |||
($, rspamd) => { | |||
define(["jquery", "app/common"], | |||
($, common) => { | |||
"use strict"; | |||
const ui = {}; | |||
ui.getActions = function getActions(checked_server) { | |||
rspamd.query("actions", { | |||
common.query("actions", { | |||
success: function (data) { | |||
$("#actionsFormField").empty(); | |||
const items = []; | |||
@@ -88,15 +88,15 @@ define(["jquery", "app/rspamd"], | |||
// String to array for comparison | |||
const eltsArray = JSON.parse(elts); | |||
if (eltsArray[0] < 0) { | |||
rspamd.alertMessage("alert-modal alert-error", "Spam can not be negative"); | |||
common.alertMessage("alert-modal alert-error", "Spam can not be negative"); | |||
} else if (eltsArray[1] < 0) { | |||
rspamd.alertMessage("alert-modal alert-error", "Rewrite subject can not be negative"); | |||
common.alertMessage("alert-modal alert-error", "Rewrite subject can not be negative"); | |||
} else if (eltsArray[2] < 0) { | |||
rspamd.alertMessage("alert-modal alert-error", "Probable spam can not be negative"); | |||
common.alertMessage("alert-modal alert-error", "Probable spam can not be negative"); | |||
} else if (eltsArray[3] < 0) { | |||
rspamd.alertMessage("alert-modal alert-error", "Greylist can not be negative"); | |||
common.alertMessage("alert-modal alert-error", "Greylist can not be negative"); | |||
} else if (descending(eltsArray)) { | |||
rspamd.query("saveactions", { | |||
common.query("saveactions", { | |||
method: "POST", | |||
params: { | |||
data: elts, | |||
@@ -105,14 +105,14 @@ define(["jquery", "app/rspamd"], | |||
server: server | |||
}); | |||
} else { | |||
rspamd.alertMessage("alert-modal alert-error", "Incorrect order of actions thresholds"); | |||
common.alertMessage("alert-modal alert-error", "Incorrect order of actions thresholds"); | |||
} | |||
}; | |||
ui.getMaps = function (checked_server) { | |||
const $listmaps = $("#listMaps"); | |||
$listmaps.closest(".card").hide(); | |||
rspamd.query("maps", { | |||
common.query("maps", { | |||
success: function (json) { | |||
const [{data}] = json; | |||
$listmaps.empty(); | |||
@@ -121,7 +121,7 @@ define(["jquery", "app/rspamd"], | |||
$.each(data, (i, item) => { | |||
let $td = '<td><span class="badge text-bg-secondary">Read</span></td>'; | |||
if (!(item.editable === false || rspamd.read_only)) { | |||
if (!(item.editable === false || common.read_only)) { | |||
$td = $($td).append(' <span class="badge text-bg-success">Write</span>'); | |||
} | |||
const $tr = $("<tr>").append($td); | |||
@@ -158,9 +158,9 @@ define(["jquery", "app/rspamd"], | |||
// Modal form for maps | |||
$(document).on("click", "[data-bs-toggle=\"modal\"]", function () { | |||
const checked_server = rspamd.getSelector("selSrv"); | |||
const checked_server = common.getSelector("selSrv"); | |||
const item = $(this).data("item"); | |||
rspamd.query("getmap", { | |||
common.query("getmap", { | |||
headers: { | |||
Map: item.map | |||
}, | |||
@@ -180,11 +180,11 @@ define(["jquery", "app/rspamd"], | |||
jar.updateCode(data[0].data); | |||
}); | |||
} else { | |||
document.querySelector("#editor").innerHTML = rspamd.escapeHTML(data[0].data); | |||
document.querySelector("#editor").innerHTML = common.escapeHTML(data[0].data); | |||
} | |||
let icon = "fa-edit"; | |||
if (item.editable === false || rspamd.read_only) { | |||
if (item.editable === false || common.read_only) { | |||
$("#editor").attr(editor[mode].readonly_attr); | |||
icon = "fa-eye"; | |||
$("#modalSaveGroup").hide(); | |||
@@ -218,9 +218,9 @@ define(["jquery", "app/rspamd"], | |||
}); | |||
function saveMap(server) { | |||
rspamd.query("savemap", { | |||
common.query("savemap", { | |||
success: function () { | |||
rspamd.alertMessage("alert-success", "Map data successfully saved"); | |||
common.alertMessage("alert-success", "Map data successfully saved"); | |||
$("#modalDialog").modal("hide"); | |||
}, | |||
errorMessage: "Save map error", |
@@ -25,8 +25,8 @@ | |||
/* global FooTable */ | |||
define(["jquery", "app/rspamd", "d3evolution", "d3pie", "d3", "footable"], | |||
($, rspamd, D3Evolution, D3Pie, d3) => { | |||
define(["jquery", "app/common", "d3evolution", "d3pie", "d3", "footable"], | |||
($, common, D3Evolution, D3Pie, d3) => { | |||
"use strict"; | |||
const rrd_pie_config = { | |||
@@ -68,16 +68,16 @@ define(["jquery", "app/rspamd", "d3evolution", "d3pie", "d3", "footable"], | |||
legend: { | |||
space: 140, | |||
entries: rspamd.chartLegend | |||
entries: common.chartLegend | |||
} | |||
}; | |||
function initGraph() { | |||
const graph = new D3Evolution("graph", $.extend({}, graph_options, { | |||
yScale: rspamd.getSelector("selYScale"), | |||
type: rspamd.getSelector("selType"), | |||
interpolate: rspamd.getSelector("selInterpolate"), | |||
convert: rspamd.getSelector("selConvert"), | |||
yScale: common.getSelector("selYScale"), | |||
type: common.getSelector("selType"), | |||
interpolate: common.getSelector("selInterpolate"), | |||
convert: common.getSelector("selConvert"), | |||
})); | |||
$("#selYScale").change(function () { | |||
graph.yScale(this.value); | |||
@@ -127,7 +127,7 @@ define(["jquery", "app/rspamd", "d3evolution", "d3pie", "d3", "footable"], | |||
} | |||
function initSummaryTable(rows, unit) { | |||
rspamd.tables.rrd_summary = FooTable.init("#rrd-table", { | |||
common.tables.rrd_summary = FooTable.init("#rrd-table", { | |||
sorting: { | |||
enabled: true | |||
}, | |||
@@ -151,8 +151,8 @@ define(["jquery", "app/rspamd", "d3evolution", "d3pie", "d3", "footable"], | |||
} | |||
function drawRrdTable(rows, unit) { | |||
if (Object.prototype.hasOwnProperty.call(rspamd.tables, "rrd_summary")) { | |||
$.each(rspamd.tables.rrd_summary.rows.all, (i, row) => { | |||
if (Object.prototype.hasOwnProperty.call(common.tables, "rrd_summary")) { | |||
$.each(common.tables.rrd_summary.rows.all, (i, row) => { | |||
row.val(rows[i], false, true); | |||
}); | |||
} else { | |||
@@ -199,7 +199,7 @@ define(["jquery", "app/rspamd", "d3evolution", "d3pie", "d3", "footable"], | |||
} | |||
rspamd.query("graph", { | |||
common.query("graph", { | |||
success: function (req_data) { | |||
let data = null; | |||
const neighbours_data = req_data | |||
@@ -214,7 +214,7 @@ define(["jquery", "app/rspamd", "d3evolution", "d3pie", "d3", "footable"], | |||
if ((curr[0][0].x !== res[0][0].x) || | |||
(curr[0][curr[0].length - 1].x !== res[0][res[0].length - 1].x)) { | |||
time_match = false; | |||
rspamd.alertMessage("alert-error", | |||
common.alertMessage("alert-error", | |||
"Neighbours time extents do not match. Check if time is synchronized on all servers."); | |||
arr.splice(1); // Break out of .reduce() by mutating the source array | |||
} |
@@ -24,8 +24,8 @@ | |||
/* global FooTable */ | |||
define(["jquery", "app/rspamd", "d3", "footable"], | |||
($, rspamd, d3) => { | |||
define(["jquery", "app/common", "app/libft", "d3", "footable"], | |||
($, common, libft, d3) => { | |||
"use strict"; | |||
const ui = {}; | |||
let prevVersion = null; | |||
@@ -38,15 +38,15 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
$("#selSymOrder_history, label[for='selSymOrder_history']").hide(); | |||
$.each(data, (i, item) => { | |||
item.time = rspamd.unix_time_format(item.unix_time); | |||
rspamd.preprocess_item(item); | |||
item.time = libft.unix_time_format(item.unix_time); | |||
libft.preprocess_item(item); | |||
item.symbols = Object.keys(item.symbols) | |||
.map((key) => item.symbols[key]) | |||
.sort(compare) | |||
.map((e) => e.name) | |||
.join(", "); | |||
item.time = { | |||
value: rspamd.unix_time_format(item.unix_time), | |||
value: libft.unix_time_format(item.unix_time), | |||
options: { | |||
sortValue: item.unix_time | |||
} | |||
@@ -234,7 +234,7 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
function process_history_data(data) { | |||
const process_functions = { | |||
2: rspamd.process_history_v2, | |||
2: libft.process_history_v2, | |||
legacy: process_history_legacy | |||
}; | |||
let pf = process_functions.legacy; | |||
@@ -263,12 +263,12 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
} | |||
ui.getHistory = function () { | |||
rspamd.query("history", { | |||
common.query("history", { | |||
success: function (req_data) { | |||
function differentVersions(neighbours_data) { | |||
const dv = neighbours_data.some((e) => e.version !== neighbours_data[0].version); | |||
if (dv) { | |||
rspamd.alertMessage("alert-error", | |||
common.alertMessage("alert-error", | |||
"Neighbours history backend versions do not match. Cannot display history."); | |||
return true; | |||
} | |||
@@ -293,21 +293,21 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
} | |||
const o = process_history_data(data); | |||
const {items} = o; | |||
rspamd.symbols.history = o.symbols; | |||
common.symbols.history = o.symbols; | |||
if (Object.prototype.hasOwnProperty.call(rspamd.tables, "history") && | |||
if (Object.prototype.hasOwnProperty.call(common.tables, "history") && | |||
version === prevVersion) { | |||
rspamd.tables.history.rows.load(items); | |||
common.tables.history.rows.load(items); | |||
} else { | |||
rspamd.destroyTable("history"); | |||
libft.destroyTable("history"); | |||
// Is there a way to get an event when the table is destroyed? | |||
setTimeout(() => { | |||
rspamd.initHistoryTable(data, items, "history", get_history_columns(data), false); | |||
libft.initHistoryTable(data, items, "history", get_history_columns(data), false); | |||
}, 200); | |||
} | |||
prevVersion = version; | |||
} else { | |||
rspamd.destroyTable("history"); | |||
libft.destroyTable("history"); | |||
} | |||
}, | |||
complete: function () { $("#refresh").removeAttr("disabled").removeClass("disabled"); }, | |||
@@ -316,7 +316,7 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
}; | |||
function initErrorsTable(rows) { | |||
rspamd.tables.errors = FooTable.init("#errorsLog", { | |||
common.tables.errors = FooTable.init("#errorsLog", { | |||
columns: [ | |||
{sorted: true, | |||
direction: "DESC", | |||
@@ -340,7 +340,7 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
paging: { | |||
enabled: true, | |||
limit: 5, | |||
size: rspamd.page_size.errors | |||
size: common.page_size.errors | |||
}, | |||
filtering: { | |||
enabled: true, | |||
@@ -354,9 +354,9 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
} | |||
ui.getErrors = function () { | |||
if (rspamd.read_only) return; | |||
if (common.read_only) return; | |||
rspamd.query("errors", { | |||
common.query("errors", { | |||
success: function (data) { | |||
const neighbours_data = data | |||
.filter((d) => d.status) // filter out unavailable neighbours | |||
@@ -364,14 +364,14 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
const rows = [].concat.apply([], neighbours_data); | |||
$.each(rows, (i, item) => { | |||
item.ts = { | |||
value: rspamd.unix_time_format(item.ts), | |||
value: libft.unix_time_format(item.ts), | |||
options: { | |||
sortValue: item.ts | |||
} | |||
}; | |||
}); | |||
if (Object.prototype.hasOwnProperty.call(rspamd.tables, "errors")) { | |||
rspamd.tables.errors.rows.load(rows); | |||
if (Object.prototype.hasOwnProperty.call(common.tables, "errors")) { | |||
common.tables.errors.rows.load(rows); | |||
} else { | |||
initErrorsTable(rows); | |||
} | |||
@@ -386,8 +386,8 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
}; | |||
rspamd.set_page_size("history", $("#history_page_size").val()); | |||
rspamd.bindHistoryTableEventHandlers("history", 8); | |||
libft.set_page_size("history", $("#history_page_size").val()); | |||
libft.bindHistoryTableEventHandlers("history", 8); | |||
$("#updateHistory").off("click"); | |||
$("#updateHistory").on("click", (e) => { | |||
@@ -402,10 +402,10 @@ define(["jquery", "app/rspamd", "d3", "footable"], | |||
if (!confirm("Are you sure you want to reset history log?")) { // eslint-disable-line no-alert | |||
return; | |||
} | |||
rspamd.destroyTable("history"); | |||
rspamd.destroyTable("errors"); | |||
libft.destroyTable("history"); | |||
libft.destroyTable("errors"); | |||
rspamd.query("historyreset", { | |||
common.query("historyreset", { | |||
success: function () { | |||
ui.getHistory(); | |||
ui.getErrors(); |
@@ -0,0 +1,380 @@ | |||
/* global FooTable */ | |||
define(["jquery", "app/common", "footable"], | |||
($, common) => { | |||
"use strict"; | |||
const ui = {}; | |||
let pageSizeTimerId = null; | |||
let pageSizeInvocationCounter = 0; | |||
function get_compare_function(table) { | |||
const compare_functions = { | |||
magnitude: function (e1, e2) { | |||
return Math.abs(e2.score) - Math.abs(e1.score); | |||
}, | |||
name: function (e1, e2) { | |||
return e1.name.localeCompare(e2.name); | |||
}, | |||
score: function (e1, e2) { | |||
return e2.score - e1.score; | |||
} | |||
}; | |||
return compare_functions[common.getSelector("selSymOrder_" + table)]; | |||
} | |||
function sort_symbols(o, compare_function) { | |||
return Object.keys(o) | |||
.map((key) => o[key]) | |||
.sort(compare_function) | |||
.map((e) => e.str) | |||
.join("<br>\n"); | |||
} | |||
// Public functions | |||
ui.set_page_size = function (table, page_size, changeTablePageSize) { | |||
const n = parseInt(page_size, 10); // HTML Input elements return string representing a number | |||
if (n > 0) { | |||
common.page_size[table] = n; | |||
if (changeTablePageSize && | |||
$("#historyTable_" + table + " tbody").is(":parent")) { // Table is not empty | |||
clearTimeout(pageSizeTimerId); | |||
const t = FooTable.get("#historyTable_" + table); | |||
if (t) { | |||
pageSizeInvocationCounter = 0; | |||
// Wait for input finish | |||
pageSizeTimerId = setTimeout(() => t.pageSize(n), 1000); | |||
} else if (++pageSizeInvocationCounter < 10) { | |||
// Wait for FooTable instance ready | |||
pageSizeTimerId = setTimeout(() => ui.set_page_size(table, n, true), 1000); | |||
} | |||
} | |||
} | |||
}; | |||
ui.bindHistoryTableEventHandlers = function (table, symbolsCol) { | |||
function change_symbols_order(order) { | |||
$(".btn-sym-" + table + "-" + order).addClass("active").siblings().removeClass("active"); | |||
const compare_function = get_compare_function(table); | |||
$.each(common.tables[table].rows.all, (i, row) => { | |||
const cell_val = sort_symbols(common.symbols[table][i], compare_function); | |||
row.cells[symbolsCol].val(cell_val, false, true); | |||
}); | |||
} | |||
$("#selSymOrder_" + table).unbind().change(function () { | |||
const order = this.value; | |||
change_symbols_order(order); | |||
}); | |||
$("#" + table + "_page_size").change((e) => ui.set_page_size(table, e.target.value, true)); | |||
$(document).on("click", ".btn-sym-order-" + table + " input", function () { | |||
const order = this.value; | |||
$("#selSymOrder_" + table).val(order); | |||
change_symbols_order(order); | |||
}); | |||
}; | |||
ui.destroyTable = function (table) { | |||
if (common.tables[table]) { | |||
common.tables[table].destroy(); | |||
delete common.tables[table]; | |||
} | |||
}; | |||
ui.initHistoryTable = function (data, items, table, columns, expandFirst) { | |||
/* eslint-disable no-underscore-dangle */ | |||
FooTable.Cell.extend("collapse", function () { | |||
// call the original method | |||
this._super(); | |||
// Copy cell classes to detail row tr element | |||
this._setClasses(this.$detail); | |||
}); | |||
/* eslint-enable no-underscore-dangle */ | |||
/* eslint-disable consistent-this, no-underscore-dangle, one-var-declaration-per-line */ | |||
FooTable.actionFilter = FooTable.Filtering.extend({ | |||
construct: function (instance) { | |||
this._super(instance); | |||
this.actions = ["reject", "add header", "greylist", | |||
"no action", "soft reject", "rewrite subject"]; | |||
this.def = "Any action"; | |||
this.$action = null; | |||
}, | |||
$create: function () { | |||
this._super(); | |||
const self = this; | |||
const $form_grp = $("<div/>", { | |||
class: "form-group d-inline-flex align-items-center" | |||
}).append($("<label/>", { | |||
class: "sr-only", | |||
text: "Action" | |||
})).prependTo(self.$form); | |||
$("<div/>", { | |||
class: "form-check form-check-inline", | |||
title: "Invert action match." | |||
}).append( | |||
self.$not = $("<input/>", { | |||
type: "checkbox", | |||
class: "form-check-input", | |||
id: "not_" + table | |||
}).on("change", {self: self}, self._onStatusDropdownChanged), | |||
$("<label/>", { | |||
class: "form-check-label", | |||
for: "not_" + table, | |||
text: "not" | |||
}) | |||
).appendTo($form_grp); | |||
self.$action = $("<select/>", { | |||
class: "form-select" | |||
}).on("change", { | |||
self: self | |||
}, self._onStatusDropdownChanged).append( | |||
$("<option/>", { | |||
text: self.def | |||
})).appendTo($form_grp); | |||
$.each(self.actions, (i, action) => { | |||
self.$action.append($("<option/>").text(action)); | |||
}); | |||
}, | |||
_onStatusDropdownChanged: function (e) { | |||
const {self} = e.data; | |||
const selected = self.$action.val(); | |||
if (selected !== self.def) { | |||
const not = self.$not.is(":checked"); | |||
let query = null; | |||
if (selected === "reject") { | |||
query = not ? "-reject OR soft" : "reject -soft"; | |||
} else { | |||
query = not ? selected.replace(/(\b\w+\b)/g, "-$1") : selected; | |||
} | |||
self.addFilter("action", query, ["action"]); | |||
} else { | |||
self.removeFilter("action"); | |||
} | |||
self.filter(); | |||
} | |||
}); | |||
/* eslint-enable consistent-this, no-underscore-dangle, one-var-declaration-per-line */ | |||
common.tables[table] = FooTable.init("#historyTable_" + table, { | |||
columns: columns, | |||
rows: items, | |||
expandFirst: expandFirst, | |||
paging: { | |||
enabled: true, | |||
limit: 5, | |||
size: common.page_size[table] | |||
}, | |||
filtering: { | |||
enabled: true, | |||
position: "left", | |||
connectors: false | |||
}, | |||
sorting: { | |||
enabled: true | |||
}, | |||
components: { | |||
filtering: FooTable.actionFilter | |||
}, | |||
on: { | |||
"expand.ft.row": function (e, ft, row) { | |||
setTimeout(() => { | |||
const detail_row = row.$el.next(); | |||
const order = common.getSelector("selSymOrder_" + table); | |||
detail_row.find(".btn-sym-" + table + "-" + order) | |||
.addClass("active").siblings().removeClass("active"); | |||
}, 5); | |||
} | |||
} | |||
}); | |||
}; | |||
ui.preprocess_item = function (item) { | |||
function escape_HTML_array(arr) { | |||
arr.forEach((d, i) => { arr[i] = common.escapeHTML(d); }); | |||
} | |||
for (const prop in item) { | |||
if (!{}.hasOwnProperty.call(item, prop)) continue; | |||
switch (prop) { | |||
case "rcpt_mime": | |||
case "rcpt_smtp": | |||
escape_HTML_array(item[prop]); | |||
break; | |||
case "symbols": | |||
Object.keys(item.symbols).forEach((key) => { | |||
const sym = item.symbols[key]; | |||
if (!sym.name) { | |||
sym.name = key; | |||
} | |||
sym.name = common.escapeHTML(sym.name); | |||
if (sym.description) { | |||
sym.description = common.escapeHTML(sym.description); | |||
} | |||
if (sym.options) { | |||
escape_HTML_array(sym.options); | |||
} | |||
}); | |||
break; | |||
default: | |||
if (typeof item[prop] === "string") { | |||
item[prop] = common.escapeHTML(item[prop]); | |||
} | |||
} | |||
} | |||
if (item.action === "clean" || item.action === "no action") { | |||
item.action = "<div style='font-size:11px' class='badge text-bg-success'>" + item.action + "</div>"; | |||
} else if (item.action === "rewrite subject" || item.action === "add header" || item.action === "probable spam") { | |||
item.action = "<div style='font-size:11px' class='badge text-bg-warning'>" + item.action + "</div>"; | |||
} else if (item.action === "spam" || item.action === "reject") { | |||
item.action = "<div style='font-size:11px' class='badge text-bg-danger'>" + item.action + "</div>"; | |||
} else { | |||
item.action = "<div style='font-size:11px' class='badge text-bg-info'>" + item.action + "</div>"; | |||
} | |||
const score_content = (item.score < item.required_score) | |||
? "<span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span>" | |||
: "<span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span>"; | |||
item.score = { | |||
options: { | |||
sortValue: item.score | |||
}, | |||
value: score_content | |||
}; | |||
}; | |||
ui.unix_time_format = function (tm) { | |||
const date = new Date(tm ? tm * 1000 : 0); | |||
return (common.locale) | |||
? date.toLocaleString(common.locale) | |||
: date.toLocaleString(); | |||
}; | |||
ui.process_history_v2 = function (data, table) { | |||
// Display no more than rcpt_lim recipients | |||
const rcpt_lim = 3; | |||
const items = []; | |||
const unsorted_symbols = []; | |||
const compare_function = get_compare_function(table); | |||
$("#selSymOrder_" + table + ", label[for='selSymOrder_" + table + "']").show(); | |||
$.each(data.rows, | |||
(i, item) => { | |||
function more(p) { | |||
const l = item[p].length; | |||
return (l > rcpt_lim) ? " … (" + l + ")" : ""; | |||
} | |||
function format_rcpt(smtp, mime) { | |||
let full = ""; | |||
let shrt = ""; | |||
if (smtp) { | |||
full = "[" + item.rcpt_smtp.join(", ") + "] "; | |||
shrt = "[" + item.rcpt_smtp.slice(0, rcpt_lim).join(",​") + more("rcpt_smtp") + "]"; | |||
if (mime) { | |||
full += " "; | |||
shrt += " "; | |||
} | |||
} | |||
if (mime) { | |||
full += item.rcpt_mime.join(", "); | |||
shrt += item.rcpt_mime.slice(0, rcpt_lim).join(",​") + more("rcpt_mime"); | |||
} | |||
return {full: full, shrt: shrt}; | |||
} | |||
function get_symbol_class(name, score) { | |||
if (name.match(/^GREYLIST$/)) { | |||
return "symbol-special"; | |||
} | |||
if (score < 0) { | |||
return "symbol-negative"; | |||
} else if (score > 0) { | |||
return "symbol-positive"; | |||
} | |||
return null; | |||
} | |||
ui.preprocess_item(item); | |||
Object.values(item.symbols).forEach((sym) => { | |||
sym.str = '<span class="symbol-default ' + get_symbol_class(sym.name, sym.score) + '"><strong>'; | |||
if (sym.description) { | |||
sym.str += '<abbr title="' + sym.description + '">' + sym.name + "</abbr>"; | |||
} else { | |||
sym.str += sym.name; | |||
} | |||
sym.str += "</strong> (" + sym.score + ")</span>"; | |||
if (sym.options) { | |||
sym.str += " [" + sym.options.join(",") + "]"; | |||
} | |||
}); | |||
unsorted_symbols.push(item.symbols); | |||
item.symbols = sort_symbols(item.symbols, compare_function); | |||
if (table === "scan") { | |||
item.unix_time = (new Date()).getTime() / 1000; | |||
} | |||
item.time = { | |||
value: ui.unix_time_format(item.unix_time), | |||
options: { | |||
sortValue: item.unix_time | |||
} | |||
}; | |||
item.time_real = item.time_real.toFixed(3); | |||
item.id = item["message-id"]; | |||
if (table === "history") { | |||
let rcpt = {}; | |||
if (!item.rcpt_mime.length) { | |||
rcpt = format_rcpt(true, false); | |||
} else if ( | |||
$(item.rcpt_mime).not(item.rcpt_smtp).length !== 0 || | |||
$(item.rcpt_smtp).not(item.rcpt_mime).length !== 0 | |||
) { | |||
rcpt = format_rcpt(true, true); | |||
} else { | |||
rcpt = format_rcpt(false, true); | |||
} | |||
item.rcpt_mime_short = rcpt.shrt; | |||
item.rcpt_mime = rcpt.full; | |||
if (item.sender_mime !== item.sender_smtp) { | |||
item.sender_mime = "[" + item.sender_smtp + "] " + item.sender_mime; | |||
} | |||
} | |||
items.push(item); | |||
}); | |||
return {items: items, symbols: unsorted_symbols}; | |||
}; | |||
ui.waitForRowsDisplayed = function (table, rows_total, callback, iteration) { | |||
let i = (typeof iteration === "undefined") ? 10 : iteration; | |||
const num_rows = $("#historyTable_" + table + " > tbody > tr:not(.footable-detail-row)").length; | |||
if (num_rows === common.page_size[table] || | |||
num_rows === rows_total) { | |||
return callback(); | |||
} else if (--i) { | |||
setTimeout(() => { | |||
ui.waitForRowsDisplayed(table, rows_total, callback, i); | |||
}, 500); | |||
} | |||
return null; | |||
}; | |||
return ui; | |||
}); |
@@ -23,48 +23,20 @@ | |||
THE SOFTWARE. | |||
*/ | |||
/* global jQuery, FooTable, require, Visibility */ | |||
/* global require, Visibility */ | |||
define(["jquery", "nprogress", "stickytabs", "visibility", | |||
define(["jquery", "app/common", "stickytabs", "visibility", | |||
"bootstrap", "fontawesome"], | |||
($, NProgress) => { | |||
($, common) => { | |||
"use strict"; | |||
const ui = { | |||
chartLegend: [ | |||
{label: "reject", color: "#FF0000"}, | |||
{label: "soft reject", color: "#BF8040"}, | |||
{label: "rewrite subject", color: "#FF6600"}, | |||
{label: "add header", color: "#FFAD00"}, | |||
{label: "greylist", color: "#436EEE"}, | |||
{label: "no action", color: "#66CC00"} | |||
], | |||
page_size: { | |||
scan: 25, | |||
errors: 25, | |||
history: 25 | |||
}, | |||
symbols: { | |||
scan: [], | |||
history: [] | |||
} | |||
}; | |||
const ui = {}; | |||
const defaultAjaxTimeout = 20000; | |||
const ajaxTimeoutBox = ".popover #settings-popover #ajax-timeout"; | |||
const graphs = {}; | |||
const tables = {}; | |||
let neighbours = []; // list of clusters | |||
let checked_server = "All SERVERS"; | |||
const timer_id = []; | |||
let pageSizeTimerId = null; | |||
let pageSizeInvocationCounter = 0; | |||
let locale = (localStorage.getItem("selected_locale") === "custom") ? localStorage.getItem("custom_locale") : null; | |||
NProgress.configure({ | |||
minimum: 0.01, | |||
showSpinner: false, | |||
}); | |||
function ajaxSetup(ajax_timeout, setFieldValue, saveToLocalStorage) { | |||
const timeout = (ajax_timeout && ajax_timeout >= 0) ? ajax_timeout : defaultAjaxTimeout; | |||
@@ -92,7 +64,7 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
} | |||
function disconnect() { | |||
[graphs, tables].forEach((o) => { | |||
[graphs, common.tables].forEach((o) => { | |||
Object.keys(o).forEach((key) => { | |||
o[key].destroy(); | |||
delete o[key]; | |||
@@ -108,12 +80,6 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
ui.connect(); | |||
} | |||
// Get selectors' current state | |||
function getSelector(id) { | |||
const e = document.getElementById(id); | |||
return e.options[e.selectedIndex].value; | |||
} | |||
function tabClick(id) { | |||
let tab_id = id; | |||
if ($(id).attr("disabled")) return; | |||
@@ -180,7 +146,7 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
break; | |||
case "#throughput_nav": | |||
require(["app/graph"], (module) => { | |||
const selData = getSelector("selData"); // Graph's dataset selector state | |||
const selData = common.getSelector("selData"); // Graph's dataset selector state | |||
const step = { | |||
day: 60000, | |||
week: 300000 | |||
@@ -192,8 +158,8 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
refreshInterval = null; | |||
} | |||
setAutoRefresh(refreshInterval, "throughput", | |||
() => module.draw(graphs, neighbours, checked_server, selData)); | |||
if (id !== "#autoRefresh") module.draw(graphs, neighbours, checked_server, selData); | |||
() => module.draw(graphs, common.neighbours, checked_server, selData)); | |||
if (id !== "#autoRefresh") module.draw(graphs, common.neighbours, checked_server, selData); | |||
$(".preset").hide(); | |||
$(".history").hide(); | |||
@@ -245,72 +211,16 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
}, (id === "#autoRefresh") ? 0 : 1000); | |||
} | |||
function getPassword() { | |||
return sessionStorage.getItem("Password"); | |||
} | |||
function get_compare_function(table) { | |||
const compare_functions = { | |||
magnitude: function (e1, e2) { | |||
return Math.abs(e2.score) - Math.abs(e1.score); | |||
}, | |||
name: function (e1, e2) { | |||
return e1.name.localeCompare(e2.name); | |||
}, | |||
score: function (e1, e2) { | |||
return e2.score - e1.score; | |||
} | |||
}; | |||
return compare_functions[getSelector("selSymOrder_" + table)]; | |||
} | |||
function saveCredentials(password) { | |||
sessionStorage.setItem("Password", password); | |||
} | |||
function set_page_size(table, page_size, changeTablePageSize) { | |||
const n = parseInt(page_size, 10); // HTML Input elements return string representing a number | |||
if (n > 0) { | |||
ui.page_size[table] = n; | |||
if (changeTablePageSize && | |||
$("#historyTable_" + table + " tbody").is(":parent")) { // Table is not empty | |||
clearTimeout(pageSizeTimerId); | |||
const t = FooTable.get("#historyTable_" + table); | |||
if (t) { | |||
pageSizeInvocationCounter = 0; | |||
// Wait for input finish | |||
pageSizeTimerId = setTimeout(() => t.pageSize(n), 1000); | |||
} else if (++pageSizeInvocationCounter < 10) { | |||
// Wait for FooTable instance ready | |||
pageSizeTimerId = setTimeout(() => set_page_size(table, n, true), 1000); | |||
} | |||
} | |||
} | |||
} | |||
function sort_symbols(o, compare_function) { | |||
return Object.keys(o) | |||
.map((key) => o[key]) | |||
.sort(compare_function) | |||
.map((e) => e.str) | |||
.join("<br>\n"); | |||
} | |||
function unix_time_format(tm) { | |||
const date = new Date(tm ? tm * 1000 : 0); | |||
return (locale) | |||
? date.toLocaleString(locale) | |||
: date.toLocaleString(); | |||
} | |||
function displayUI() { | |||
// In many browsers local storage can only store string. | |||
// So when we store the boolean true or false, it actually stores the strings "true" or "false". | |||
ui.read_only = sessionStorage.getItem("read_only") === "true"; | |||
common.read_only = sessionStorage.getItem("read_only") === "true"; | |||
ui.query("auth", { | |||
common.query("auth", { | |||
success: function (neighbours_status) { | |||
$("#selSrv").empty(); | |||
$("#selSrv").append($('<option value="All SERVERS">All SERVERS</option>')); | |||
@@ -326,7 +236,7 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
complete: function () { | |||
ajaxSetup(localStorage.getItem("ajax_timeout")); | |||
if (ui.read_only) { | |||
if (common.read_only) { | |||
$(".ro-disable").attr("disabled", true); | |||
$(".ro-hide").hide(); | |||
} else { | |||
@@ -343,98 +253,8 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
}); | |||
} | |||
function alertMessage(alertClass, alertText) { | |||
const a = $("<div class=\"alert " + alertClass + " alert-dismissible fade in show\">" + | |||
"<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"alert\" title=\"Dismiss\"></button>" + | |||
"<strong>" + alertText + "</strong>"); | |||
$(".notification-area").append(a); | |||
setTimeout(() => { | |||
$(a).fadeTo(500, 0).slideUp(500, function () { | |||
$(this).alert("close"); | |||
}); | |||
}, 5000); | |||
} | |||
function queryServer(neighbours_status, ind, req_url, o) { | |||
neighbours_status[ind].checked = false; | |||
neighbours_status[ind].data = {}; | |||
neighbours_status[ind].status = false; | |||
const req_params = { | |||
jsonp: false, | |||
data: o.data, | |||
headers: $.extend({Password: getPassword()}, o.headers), | |||
url: neighbours_status[ind].url + req_url, | |||
xhr: function () { | |||
const xhr = $.ajaxSettings.xhr(); | |||
// Download progress | |||
if (req_url !== "neighbours") { | |||
xhr.addEventListener("progress", (e) => { | |||
if (e.lengthComputable) { | |||
neighbours_status[ind].percentComplete = e.loaded / e.total; | |||
const percentComplete = neighbours_status | |||
.reduce((prev, curr) => (curr.percentComplete ? curr.percentComplete + prev : prev), 0); | |||
NProgress.set(percentComplete / neighbours_status.length); | |||
} | |||
}, false); | |||
} | |||
return xhr; | |||
}, | |||
success: function (json) { | |||
neighbours_status[ind].checked = true; | |||
neighbours_status[ind].status = true; | |||
neighbours_status[ind].data = json; | |||
}, | |||
error: function (jqXHR, textStatus, errorThrown) { | |||
neighbours_status[ind].checked = true; | |||
function errorMessage() { | |||
alertMessage("alert-error", neighbours_status[ind].name + " > " + | |||
(o.errorMessage ? o.errorMessage : "Request failed") + | |||
(errorThrown ? ": " + errorThrown : "")); | |||
} | |||
if (o.error) { | |||
o.error(neighbours_status[ind], | |||
jqXHR, textStatus, errorThrown); | |||
} else if (o.errorOnceId) { | |||
const alert_status = o.errorOnceId + neighbours_status[ind].name; | |||
if (!(alert_status in sessionStorage)) { | |||
sessionStorage.setItem(alert_status, true); | |||
errorMessage(); | |||
} | |||
} else { | |||
errorMessage(); | |||
} | |||
}, | |||
complete: function (jqXHR) { | |||
if (neighbours_status.every((elt) => elt.checked)) { | |||
if (neighbours_status.some((elt) => elt.status)) { | |||
if (o.success) { | |||
o.success(neighbours_status, jqXHR); | |||
} else { | |||
alertMessage("alert-success", "Request completed"); | |||
} | |||
} else { | |||
alertMessage("alert-error", "Request failed"); | |||
} | |||
if (o.complete) o.complete(); | |||
NProgress.done(); | |||
} | |||
}, | |||
statusCode: o.statusCode | |||
}; | |||
if (o.method) { | |||
req_params.method = o.method; | |||
} | |||
if (o.params) { | |||
$.each(o.params, (k, v) => { | |||
req_params[k] = v; | |||
}); | |||
} | |||
$.ajax(req_params); | |||
} | |||
// Public functions | |||
ui.alertMessage = alertMessage; | |||
ui.connect = function () { | |||
// Prevent locking out of the WebUI if timeout is too low. | |||
@@ -484,7 +304,7 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
return; | |||
} | |||
ui.query("auth", { | |||
common.query("auth", { | |||
headers: { | |||
Password: password | |||
}, | |||
@@ -503,7 +323,7 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
if (textStatus.statusText === "Unauthorized") { | |||
invalidFeedback("#authUnauthorizedFeedback"); | |||
} else { | |||
ui.alertMessage("alert-modal alert-error", textStatus.statusText); | |||
common.alertMessage("alert-modal alert-error", textStatus.statusText); | |||
} | |||
$("#connectPassword").val(""); | |||
$("#connectPassword").focus(); | |||
@@ -518,420 +338,6 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
}); | |||
}; | |||
ui.getPassword = getPassword; | |||
ui.getSelector = getSelector; | |||
/** | |||
* @param {string} url - A string containing the URL to which the request is sent | |||
* @param {Object} [options] - A set of key/value pairs that configure the Ajax request. All settings are optional. | |||
* | |||
* @param {Function} [options.complete] - A function to be called when the requests to all neighbours complete. | |||
* @param {Object|string|Array} [options.data] - Data to be sent to the server. | |||
* @param {Function} [options.error] - A function to be called if the request fails. | |||
* @param {string} [options.errorMessage] - Text to display in the alert message if the request fails. | |||
* @param {string} [options.errorOnceId] - A prefix of the alert ID to be added to the session storage. If the | |||
* parameter is set, the error for each server will be displayed only once per session. | |||
* @param {Object} [options.headers] - An object of additional header key/value pairs to send along with requests | |||
* using the XMLHttpRequest transport. | |||
* @param {string} [options.method] - The HTTP method to use for the request. | |||
* @param {Object} [options.params] - An object of additional jQuery.ajax() settings key/value pairs. | |||
* @param {string} [options.server] - A server to which send the request. | |||
* @param {Function} [options.success] - A function to be called if the request succeeds. | |||
* | |||
* @returns {undefined} | |||
*/ | |||
ui.query = function (url, options) { | |||
// Force options to be an object | |||
const o = options || {}; | |||
Object.keys(o).forEach((option) => { | |||
if (["complete", "data", "error", "errorMessage", "errorOnceId", "headers", "method", "params", "server", | |||
"statusCode", "success"] | |||
.indexOf(option) < 0) { | |||
throw new Error("Unknown option: " + option); | |||
} | |||
}); | |||
let neighbours_status = [{ | |||
name: "local", | |||
host: "local", | |||
url: "", | |||
}]; | |||
o.server = o.server || checked_server; | |||
if (o.server === "All SERVERS") { | |||
queryServer(neighbours_status, 0, "neighbours", { | |||
success: function (json) { | |||
const [{data}] = json; | |||
if (jQuery.isEmptyObject(data)) { | |||
neighbours = { | |||
local: { | |||
host: window.location.host, | |||
url: window.location.origin + window.location.pathname | |||
} | |||
}; | |||
} else { | |||
neighbours = data; | |||
} | |||
neighbours_status = []; | |||
$.each(neighbours, (ind) => { | |||
neighbours_status.push({ | |||
name: ind, | |||
host: neighbours[ind].host, | |||
url: neighbours[ind].url, | |||
}); | |||
}); | |||
$.each(neighbours_status, (ind) => { | |||
queryServer(neighbours_status, ind, url, o); | |||
}); | |||
}, | |||
errorMessage: "Cannot receive neighbours data" | |||
}); | |||
} else { | |||
if (o.server !== "local") { | |||
neighbours_status = [{ | |||
name: o.server, | |||
host: neighbours[o.server].host, | |||
url: neighbours[o.server].url, | |||
}]; | |||
} | |||
queryServer(neighbours_status, 0, url, o); | |||
} | |||
}; | |||
// Scan and History shared functions | |||
ui.tables = tables; | |||
ui.unix_time_format = unix_time_format; | |||
ui.set_page_size = set_page_size; | |||
ui.bindHistoryTableEventHandlers = function (table, symbolsCol) { | |||
function change_symbols_order(order) { | |||
$(".btn-sym-" + table + "-" + order).addClass("active").siblings().removeClass("active"); | |||
const compare_function = get_compare_function(table); | |||
$.each(tables[table].rows.all, (i, row) => { | |||
const cell_val = sort_symbols(ui.symbols[table][i], compare_function); | |||
row.cells[symbolsCol].val(cell_val, false, true); | |||
}); | |||
} | |||
$("#selSymOrder_" + table).unbind().change(function () { | |||
const order = this.value; | |||
change_symbols_order(order); | |||
}); | |||
$("#" + table + "_page_size").change((e) => set_page_size(table, e.target.value, true)); | |||
$(document).on("click", ".btn-sym-order-" + table + " input", function () { | |||
const order = this.value; | |||
$("#selSymOrder_" + table).val(order); | |||
change_symbols_order(order); | |||
}); | |||
}; | |||
ui.destroyTable = function (table) { | |||
if (tables[table]) { | |||
tables[table].destroy(); | |||
delete tables[table]; | |||
} | |||
}; | |||
ui.initHistoryTable = function (data, items, table, columns, expandFirst) { | |||
/* eslint-disable no-underscore-dangle */ | |||
FooTable.Cell.extend("collapse", function () { | |||
// call the original method | |||
this._super(); | |||
// Copy cell classes to detail row tr element | |||
this._setClasses(this.$detail); | |||
}); | |||
/* eslint-enable no-underscore-dangle */ | |||
/* eslint-disable consistent-this, no-underscore-dangle, one-var-declaration-per-line */ | |||
FooTable.actionFilter = FooTable.Filtering.extend({ | |||
construct: function (instance) { | |||
this._super(instance); | |||
this.actions = ["reject", "add header", "greylist", | |||
"no action", "soft reject", "rewrite subject"]; | |||
this.def = "Any action"; | |||
this.$action = null; | |||
}, | |||
$create: function () { | |||
this._super(); | |||
const self = this; | |||
const $form_grp = $("<div/>", { | |||
class: "form-group d-inline-flex align-items-center" | |||
}).append($("<label/>", { | |||
class: "sr-only", | |||
text: "Action" | |||
})).prependTo(self.$form); | |||
$("<div/>", { | |||
class: "form-check form-check-inline", | |||
title: "Invert action match." | |||
}).append( | |||
self.$not = $("<input/>", { | |||
type: "checkbox", | |||
class: "form-check-input", | |||
id: "not_" + table | |||
}).on("change", {self: self}, self._onStatusDropdownChanged), | |||
$("<label/>", { | |||
class: "form-check-label", | |||
for: "not_" + table, | |||
text: "not" | |||
}) | |||
).appendTo($form_grp); | |||
self.$action = $("<select/>", { | |||
class: "form-select" | |||
}).on("change", { | |||
self: self | |||
}, self._onStatusDropdownChanged).append( | |||
$("<option/>", { | |||
text: self.def | |||
})).appendTo($form_grp); | |||
$.each(self.actions, (i, action) => { | |||
self.$action.append($("<option/>").text(action)); | |||
}); | |||
}, | |||
_onStatusDropdownChanged: function (e) { | |||
const {self} = e.data; | |||
const selected = self.$action.val(); | |||
if (selected !== self.def) { | |||
const not = self.$not.is(":checked"); | |||
let query = null; | |||
if (selected === "reject") { | |||
query = not ? "-reject OR soft" : "reject -soft"; | |||
} else { | |||
query = not ? selected.replace(/(\b\w+\b)/g, "-$1") : selected; | |||
} | |||
self.addFilter("action", query, ["action"]); | |||
} else { | |||
self.removeFilter("action"); | |||
} | |||
self.filter(); | |||
} | |||
}); | |||
/* eslint-enable consistent-this, no-underscore-dangle, one-var-declaration-per-line */ | |||
tables[table] = FooTable.init("#historyTable_" + table, { | |||
columns: columns, | |||
rows: items, | |||
expandFirst: expandFirst, | |||
paging: { | |||
enabled: true, | |||
limit: 5, | |||
size: ui.page_size[table] | |||
}, | |||
filtering: { | |||
enabled: true, | |||
position: "left", | |||
connectors: false | |||
}, | |||
sorting: { | |||
enabled: true | |||
}, | |||
components: { | |||
filtering: FooTable.actionFilter | |||
}, | |||
on: { | |||
"expand.ft.row": function (e, ft, row) { | |||
setTimeout(() => { | |||
const detail_row = row.$el.next(); | |||
const order = getSelector("selSymOrder_" + table); | |||
detail_row.find(".btn-sym-" + table + "-" + order) | |||
.addClass("active").siblings().removeClass("active"); | |||
}, 5); | |||
} | |||
} | |||
}); | |||
}; | |||
ui.escapeHTML = function (string) { | |||
const htmlEscaper = /[&<>"'/`=]/g; | |||
const htmlEscapes = { | |||
"&": "&", | |||
"<": "<", | |||
">": ">", | |||
"\"": """, | |||
"'": "'", | |||
"/": "/", | |||
"`": "`", | |||
"=": "=" | |||
}; | |||
return String(string).replace(htmlEscaper, (match) => htmlEscapes[match]); | |||
}; | |||
ui.preprocess_item = function (item) { | |||
function escape_HTML_array(arr) { | |||
arr.forEach((d, i) => { arr[i] = ui.escapeHTML(d); }); | |||
} | |||
for (const prop in item) { | |||
if (!{}.hasOwnProperty.call(item, prop)) continue; | |||
switch (prop) { | |||
case "rcpt_mime": | |||
case "rcpt_smtp": | |||
escape_HTML_array(item[prop]); | |||
break; | |||
case "symbols": | |||
Object.keys(item.symbols).forEach((key) => { | |||
const sym = item.symbols[key]; | |||
if (!sym.name) { | |||
sym.name = key; | |||
} | |||
sym.name = ui.escapeHTML(sym.name); | |||
if (sym.description) { | |||
sym.description = ui.escapeHTML(sym.description); | |||
} | |||
if (sym.options) { | |||
escape_HTML_array(sym.options); | |||
} | |||
}); | |||
break; | |||
default: | |||
if (typeof item[prop] === "string") { | |||
item[prop] = ui.escapeHTML(item[prop]); | |||
} | |||
} | |||
} | |||
if (item.action === "clean" || item.action === "no action") { | |||
item.action = "<div style='font-size:11px' class='badge text-bg-success'>" + item.action + "</div>"; | |||
} else if (item.action === "rewrite subject" || item.action === "add header" || item.action === "probable spam") { | |||
item.action = "<div style='font-size:11px' class='badge text-bg-warning'>" + item.action + "</div>"; | |||
} else if (item.action === "spam" || item.action === "reject") { | |||
item.action = "<div style='font-size:11px' class='badge text-bg-danger'>" + item.action + "</div>"; | |||
} else { | |||
item.action = "<div style='font-size:11px' class='badge text-bg-info'>" + item.action + "</div>"; | |||
} | |||
const score_content = (item.score < item.required_score) | |||
? "<span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span>" | |||
: "<span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span>"; | |||
item.score = { | |||
options: { | |||
sortValue: item.score | |||
}, | |||
value: score_content | |||
}; | |||
}; | |||
ui.process_history_v2 = function (data, table) { | |||
// Display no more than rcpt_lim recipients | |||
const rcpt_lim = 3; | |||
const items = []; | |||
const unsorted_symbols = []; | |||
const compare_function = get_compare_function(table); | |||
$("#selSymOrder_" + table + ", label[for='selSymOrder_" + table + "']").show(); | |||
$.each(data.rows, | |||
(i, item) => { | |||
function more(p) { | |||
const l = item[p].length; | |||
return (l > rcpt_lim) ? " … (" + l + ")" : ""; | |||
} | |||
function format_rcpt(smtp, mime) { | |||
let full = ""; | |||
let shrt = ""; | |||
if (smtp) { | |||
full = "[" + item.rcpt_smtp.join(", ") + "] "; | |||
shrt = "[" + item.rcpt_smtp.slice(0, rcpt_lim).join(",​") + more("rcpt_smtp") + "]"; | |||
if (mime) { | |||
full += " "; | |||
shrt += " "; | |||
} | |||
} | |||
if (mime) { | |||
full += item.rcpt_mime.join(", "); | |||
shrt += item.rcpt_mime.slice(0, rcpt_lim).join(",​") + more("rcpt_mime"); | |||
} | |||
return {full: full, shrt: shrt}; | |||
} | |||
function get_symbol_class(name, score) { | |||
if (name.match(/^GREYLIST$/)) { | |||
return "symbol-special"; | |||
} | |||
if (score < 0) { | |||
return "symbol-negative"; | |||
} else if (score > 0) { | |||
return "symbol-positive"; | |||
} | |||
return null; | |||
} | |||
ui.preprocess_item(item); | |||
Object.values(item.symbols).forEach((sym) => { | |||
sym.str = '<span class="symbol-default ' + get_symbol_class(sym.name, sym.score) + '"><strong>'; | |||
if (sym.description) { | |||
sym.str += '<abbr title="' + sym.description + '">' + sym.name + "</abbr>"; | |||
} else { | |||
sym.str += sym.name; | |||
} | |||
sym.str += "</strong> (" + sym.score + ")</span>"; | |||
if (sym.options) { | |||
sym.str += " [" + sym.options.join(",") + "]"; | |||
} | |||
}); | |||
unsorted_symbols.push(item.symbols); | |||
item.symbols = sort_symbols(item.symbols, compare_function); | |||
if (table === "scan") { | |||
item.unix_time = (new Date()).getTime() / 1000; | |||
} | |||
item.time = { | |||
value: unix_time_format(item.unix_time), | |||
options: { | |||
sortValue: item.unix_time | |||
} | |||
}; | |||
item.time_real = item.time_real.toFixed(3); | |||
item.id = item["message-id"]; | |||
if (table === "history") { | |||
let rcpt = {}; | |||
if (!item.rcpt_mime.length) { | |||
rcpt = format_rcpt(true, false); | |||
} else if ( | |||
$(item.rcpt_mime).not(item.rcpt_smtp).length !== 0 || | |||
$(item.rcpt_smtp).not(item.rcpt_mime).length !== 0 | |||
) { | |||
rcpt = format_rcpt(true, true); | |||
} else { | |||
rcpt = format_rcpt(false, true); | |||
} | |||
item.rcpt_mime_short = rcpt.shrt; | |||
item.rcpt_mime = rcpt.full; | |||
if (item.sender_mime !== item.sender_smtp) { | |||
item.sender_mime = "[" + item.sender_smtp + "] " + item.sender_mime; | |||
} | |||
} | |||
items.push(item); | |||
}); | |||
return {items: items, symbols: unsorted_symbols}; | |||
}; | |||
ui.waitForRowsDisplayed = function (table, rows_total, callback, iteration) { | |||
let i = (typeof iteration === "undefined") ? 10 : iteration; | |||
const num_rows = $("#historyTable_" + table + " > tbody > tr:not(.footable-detail-row)").length; | |||
if (num_rows === ui.page_size[table] || | |||
num_rows === rows_total) { | |||
return callback(); | |||
} else if (--i) { | |||
setTimeout(() => { | |||
ui.waitForRowsDisplayed(table, rows_total, callback, i); | |||
}, 500); | |||
} | |||
return null; | |||
}; | |||
(function initSettings() { | |||
let selected_locale = null; | |||
@@ -950,22 +356,22 @@ define(["jquery", "nprogress", "stickytabs", "visibility", | |||
now.toLocaleString(custom_locale); | |||
if (saveToLocalStorage) localStorage.setItem("custom_locale", custom_locale); | |||
locale = (selected_locale === "custom") ? custom_locale : null; | |||
common.locale = (selected_locale === "custom") ? custom_locale : null; | |||
toggle_form_group_class("invalid", "valid"); | |||
} catch (err) { | |||
locale = null; | |||
common.locale = null; | |||
toggle_form_group_class("valid", "invalid"); | |||
} | |||
} else { | |||
if (saveToLocalStorage) localStorage.setItem("custom_locale", null); | |||
locale = null; | |||
common.locale = null; | |||
$(localeTextbox).removeClass("is-valid is-invalid"); | |||
} | |||
// Display date example | |||
$(".popover #settings-popover #date-example").text( | |||
(locale) | |||
? now.toLocaleString(locale) | |||
(common.locale) | |||
? now.toLocaleString(common.locale) | |||
: now.toLocaleString() | |||
); | |||
} |
@@ -1,5 +1,5 @@ | |||
define(["jquery", "app/rspamd"], | |||
($, rspamd) => { | |||
define(["jquery", "app/common"], | |||
($, common) => { | |||
"use strict"; | |||
const ui = {}; | |||
@@ -11,23 +11,23 @@ define(["jquery", "app/rspamd"], | |||
} | |||
function get_server() { | |||
const checked_server = rspamd.getSelector("selSrv"); | |||
const checked_server = common.getSelector("selSrv"); | |||
return (checked_server === "All SERVERS") ? "local" : checked_server; | |||
} | |||
function checkMsg(data) { | |||
const selector = $("#selectorsSelArea").val(); | |||
rspamd.query("plugins/selectors/check_message?selector=" + encodeURIComponent(selector), { | |||
common.query("plugins/selectors/check_message?selector=" + encodeURIComponent(selector), { | |||
data: data, | |||
method: "POST", | |||
success: function (neighbours_status) { | |||
const json = neighbours_status[0].data; | |||
if (json.success) { | |||
rspamd.alertMessage("alert-success", "Message successfully processed"); | |||
common.alertMessage("alert-success", "Message successfully processed"); | |||
$("#selectorsResArea") | |||
.val(Object.prototype.hasOwnProperty.call(json, "data") ? json.data.toString() : ""); | |||
} else { | |||
rspamd.alertMessage("alert-error", "Unexpected error processing message"); | |||
common.alertMessage("alert-error", "Unexpected error processing message"); | |||
} | |||
}, | |||
server: get_server() | |||
@@ -40,8 +40,8 @@ define(["jquery", "app/rspamd"], | |||
enable_disable_check_btn(); | |||
} | |||
const selector = $("#selectorsSelArea").val(); | |||
if (selector.length && !rspamd.read_only) { | |||
rspamd.query("plugins/selectors/check_selector?selector=" + encodeURIComponent(selector), { | |||
if (selector.length && !common.read_only) { | |||
common.query("plugins/selectors/check_selector?selector=" + encodeURIComponent(selector), { | |||
method: "GET", | |||
success: function (json) { | |||
if (json[0].data.success) { | |||
@@ -70,7 +70,7 @@ define(["jquery", "app/rspamd"], | |||
} | |||
function getList(list) { | |||
rspamd.query("plugins/selectors/list_" + list, { | |||
common.query("plugins/selectors/list_" + list, { | |||
method: "GET", | |||
success: function (neighbours_status) { | |||
const json = neighbours_status[0].data; | |||
@@ -85,7 +85,7 @@ define(["jquery", "app/rspamd"], | |||
} | |||
ui.displayUI = function () { | |||
if (!rspamd.read_only && | |||
if (!common.read_only && | |||
!$("#selectorsTable-extractors>tbody>tr").length && | |||
!$("#selectorsTable-transforms>tbody>tr").length) buildLists(); | |||
if (!$("#selectorsSelArea").is(".is-valid, .is-invalid")) checkSelectors(); |
@@ -22,8 +22,8 @@ | |||
THE SOFTWARE. | |||
*/ | |||
define(["jquery", "app/rspamd", "d3pie", "d3"], | |||
($, rspamd, D3Pie, d3) => { | |||
define(["jquery", "app/common", "d3pie", "d3"], | |||
($, common, D3Pie, d3) => { | |||
"use strict"; | |||
// @ ms to date | |||
function msToTime(seconds) { | |||
@@ -254,7 +254,7 @@ define(["jquery", "app/rspamd", "d3pie", "d3"], | |||
["no action", "soft reject", "add header", "rewrite subject", "greylist", "reject"] | |||
.forEach((action) => { | |||
data.push({ | |||
color: rspamd.chartLegend.find((item) => item.label === action).color, | |||
color: common.chartLegend.find((item) => item.label === action).color, | |||
label: action, | |||
value: actions[action] | |||
}); | |||
@@ -266,7 +266,7 @@ define(["jquery", "app/rspamd", "d3pie", "d3"], | |||
// Public API | |||
const ui = { | |||
statWidgets: function (graphs, checked_server) { | |||
rspamd.query("stat", { | |||
common.query("stat", { | |||
success: function (neighbours_status) { | |||
const neighbours_sum = { | |||
version: neighbours_status[0].data.version, | |||
@@ -315,7 +315,7 @@ define(["jquery", "app/rspamd", "d3pie", "d3"], | |||
const alerted = "alerted_stats_legacy_" + neighbours_status[e].name; | |||
promises.push($.ajax({ | |||
url: neighbours_status[e].url + "auth", | |||
headers: {Password: rspamd.getPassword()}, | |||
headers: {Password: common.getPassword()}, | |||
success: function (data) { | |||
sessionStorage.removeItem(alerted); | |||
["config_id", "version", "uptime"].forEach((p) => { | |||
@@ -326,7 +326,7 @@ define(["jquery", "app/rspamd", "d3pie", "d3"], | |||
error: function (jqXHR, textStatus, errorThrown) { | |||
if (!(alerted in sessionStorage)) { | |||
sessionStorage.setItem(alerted, true); | |||
rspamd.alertMessage("alert-error", neighbours_status[e].name + " > " + | |||
common.alertMessage("alert-error", neighbours_status[e].name + " > " + | |||
"Cannot receive legacy stats data" + (errorThrown ? ": " + errorThrown : "")); | |||
} | |||
process_node_stat(e); |
@@ -24,8 +24,8 @@ | |||
/* global FooTable */ | |||
define(["jquery", "app/rspamd", "footable"], | |||
($, rspamd) => { | |||
define(["jquery", "app/common", "footable"], | |||
($, common) => { | |||
"use strict"; | |||
const ui = {}; | |||
let altered = {}; | |||
@@ -41,10 +41,10 @@ define(["jquery", "app/rspamd", "footable"], | |||
const values = []; | |||
Object.entries(altered).forEach(([key, value]) => values.push({name: key, value: value})); | |||
rspamd.query("./savesymbols", { | |||
common.query("./savesymbols", { | |||
success: function () { | |||
clear_altered(); | |||
rspamd.alertMessage("alert-modal alert-success", "Symbols successfully saved"); | |||
common.alertMessage("alert-modal alert-success", "Symbols successfully saved"); | |||
}, | |||
complete: () => $("#save-alert button").removeAttr("disabled", true), | |||
errorMessage: "Save symbols error", | |||
@@ -124,7 +124,7 @@ define(["jquery", "app/rspamd", "footable"], | |||
// @get symbols into modal form | |||
ui.getSymbols = function (checked_server) { | |||
clear_altered(); | |||
rspamd.query("symbols", { | |||
common.query("symbols", { | |||
success: function (json) { | |||
const [{data}] = json; | |||
const items = process_symbols_data(data); | |||
@@ -182,7 +182,7 @@ define(["jquery", "app/rspamd", "footable"], | |||
}); | |||
/* eslint-enable consistent-this, no-underscore-dangle, one-var-declaration-per-line */ | |||
rspamd.tables.symbols = FooTable.init("#symbolsTable", { | |||
common.tables.symbols = FooTable.init("#symbolsTable", { | |||
columns: [ | |||
{sorted: true, direction: "ASC", name: "group", title: "Group"}, | |||
{name: "symbol", title: "Symbol"}, | |||
@@ -213,7 +213,7 @@ define(["jquery", "app/rspamd", "footable"], | |||
}, | |||
on: { | |||
"ready.ft.table": function () { | |||
if (rspamd.read_only) { | |||
if (common.read_only) { | |||
$(".mb-disabled").attr("disabled", true); | |||
} | |||
} | |||
@@ -228,11 +228,11 @@ define(["jquery", "app/rspamd", "footable"], | |||
$("#updateSymbols").on("click", (e) => { | |||
e.preventDefault(); | |||
clear_altered(); | |||
const checked_server = rspamd.getSelector("selSrv"); | |||
rspamd.query("symbols", { | |||
const checked_server = common.getSelector("selSrv"); | |||
common.query("symbols", { | |||
success: function (data) { | |||
const [items] = process_symbols_data(data[0].data); | |||
rspamd.tables.symbols.rows.load(items); | |||
common.tables.symbols.rows.load(items); | |||
}, | |||
server: (checked_server === "All SERVERS") ? "local" : checked_server | |||
}); |
@@ -24,8 +24,8 @@ | |||
/* global require */ | |||
define(["jquery", "app/rspamd"], | |||
($, rspamd) => { | |||
define(["jquery", "app/common", "app/libft"], | |||
($, common, libft) => { | |||
"use strict"; | |||
const ui = {}; | |||
@@ -47,15 +47,15 @@ define(["jquery", "app/rspamd"], | |||
} | |||
function server() { | |||
if (rspamd.getSelector("selSrv") === "All SERVERS" && | |||
rspamd.getSelector("selLearnServers") === "random") { | |||
if (common.getSelector("selSrv") === "All SERVERS" && | |||
common.getSelector("selLearnServers") === "random") { | |||
const servers = $("#selSrv option").slice(1).map((_, o) => o.value); | |||
return servers[Math.floor(Math.random() * servers.length)]; | |||
} | |||
return null; | |||
} | |||
rspamd.query(url, { | |||
common.query(url, { | |||
data: data, | |||
params: { | |||
processData: false, | |||
@@ -64,9 +64,9 @@ define(["jquery", "app/rspamd"], | |||
headers: headers, | |||
success: function (json, jqXHR) { | |||
cleanTextUpload(source); | |||
rspamd.alertMessage("alert-success", "Data successfully uploaded"); | |||
common.alertMessage("alert-success", "Data successfully uploaded"); | |||
if (jqXHR.status !== 200) { | |||
rspamd.alertMessage("alert-info", jqXHR.statusText); | |||
common.alertMessage("alert-info", jqXHR.statusText); | |||
} | |||
}, | |||
server: server() | |||
@@ -128,13 +128,13 @@ define(["jquery", "app/rspamd"], | |||
} | |||
function get_server() { | |||
const checked_server = rspamd.getSelector("selSrv"); | |||
const checked_server = common.getSelector("selSrv"); | |||
return (checked_server === "All SERVERS") ? "local" : checked_server; | |||
} | |||
// @upload text | |||
function scanText(data, headers) { | |||
rspamd.query("checkv2", { | |||
common.query("checkv2", { | |||
data: data, | |||
params: { | |||
processData: false, | |||
@@ -144,7 +144,7 @@ define(["jquery", "app/rspamd"], | |||
success: function (neighbours_status) { | |||
function scrollTop(rows_total) { | |||
// Is there a way to get an event when all rows are loaded? | |||
rspamd.waitForRowsDisplayed("scan", rows_total, () => { | |||
libft.waitForRowsDisplayed("scan", rows_total, () => { | |||
$("#cleanScanHistory").removeAttr("disabled", true); | |||
$("html, body").animate({ | |||
scrollTop: $("#scanResult").offset().top | |||
@@ -154,40 +154,40 @@ define(["jquery", "app/rspamd"], | |||
const json = neighbours_status[0].data; | |||
if (json.action) { | |||
rspamd.alertMessage("alert-success", "Data successfully scanned"); | |||
common.alertMessage("alert-success", "Data successfully scanned"); | |||
const rows_total = $("#historyTable_scan > tbody > tr:not(.footable-detail-row)").length + 1; | |||
const o = rspamd.process_history_v2({rows: [json]}, "scan"); | |||
const o = libft.process_history_v2({rows: [json]}, "scan"); | |||
const {items} = o; | |||
rspamd.symbols.scan.push(o.symbols[0]); | |||
common.symbols.scan.push(o.symbols[0]); | |||
if (Object.prototype.hasOwnProperty.call(rspamd.tables, "scan")) { | |||
rspamd.tables.scan.rows.load(items, true); | |||
if (Object.prototype.hasOwnProperty.call(common.tables, "scan")) { | |||
common.tables.scan.rows.load(items, true); | |||
scrollTop(rows_total); | |||
} else { | |||
rspamd.destroyTable("scan"); | |||
libft.destroyTable("scan"); | |||
require(["footable"], () => { | |||
// Is there a way to get an event when the table is destroyed? | |||
setTimeout(() => { | |||
rspamd.initHistoryTable(data, items, "scan", columns_v2(), true); | |||
libft.initHistoryTable(data, items, "scan", columns_v2(), true); | |||
scrollTop(rows_total); | |||
}, 200); | |||
}); | |||
} | |||
} else { | |||
rspamd.alertMessage("alert-error", "Cannot scan data"); | |||
common.alertMessage("alert-error", "Cannot scan data"); | |||
} | |||
}, | |||
errorMessage: "Cannot upload data", | |||
statusCode: { | |||
404: function () { | |||
rspamd.alertMessage("alert-error", "Cannot upload data, no server found"); | |||
common.alertMessage("alert-error", "Cannot upload data, no server found"); | |||
}, | |||
500: function () { | |||
rspamd.alertMessage("alert-error", "Cannot tokenize message: no text data"); | |||
common.alertMessage("alert-error", "Cannot tokenize message: no text data"); | |||
}, | |||
503: function () { | |||
rspamd.alertMessage("alert-error", "Cannot tokenize message: no text data"); | |||
common.alertMessage("alert-error", "Cannot tokenize message: no text data"); | |||
} | |||
}, | |||
server: get_server() | |||
@@ -207,7 +207,7 @@ define(["jquery", "app/rspamd"], | |||
$("#hash-card").slideDown(); | |||
} | |||
rspamd.query("plugins/fuzzy/hashes?flag=" + $("#fuzzy-flag").val(), { | |||
common.query("plugins/fuzzy/hashes?flag=" + $("#fuzzy-flag").val(), { | |||
data: data, | |||
params: { | |||
processData: false, | |||
@@ -216,10 +216,10 @@ define(["jquery", "app/rspamd"], | |||
success: function (neighbours_status) { | |||
const json = neighbours_status[0].data; | |||
if (json.success) { | |||
rspamd.alertMessage("alert-success", "Message successfully processed"); | |||
common.alertMessage("alert-success", "Message successfully processed"); | |||
fillHashTable(json.hashes); | |||
} else { | |||
rspamd.alertMessage("alert-error", "Unexpected error processing message"); | |||
common.alertMessage("alert-error", "Unexpected error processing message"); | |||
} | |||
}, | |||
server: get_server() | |||
@@ -227,8 +227,8 @@ define(["jquery", "app/rspamd"], | |||
} | |||
rspamd.set_page_size("scan", $("#scan_page_size").val()); | |||
rspamd.bindHistoryTableEventHandlers("scan", 3); | |||
libft.set_page_size("scan", $("#scan_page_size").val()); | |||
libft.bindHistoryTableEventHandlers("scan", 3); | |||
$("#cleanScanHistory").off("click"); | |||
$("#cleanScanHistory").on("click", (e) => { | |||
@@ -236,8 +236,8 @@ define(["jquery", "app/rspamd"], | |||
if (!confirm("Are you sure you want to clean scan history?")) { // eslint-disable-line no-alert | |||
return; | |||
} | |||
rspamd.destroyTable("scan"); | |||
rspamd.symbols.scan.length = 0; | |||
libft.destroyTable("scan"); | |||
common.symbols.scan.length = 0; | |||
$("#cleanScanHistory").attr("disabled", true); | |||
}); | |||
@@ -288,7 +288,7 @@ define(["jquery", "app/rspamd"], | |||
uploadText(data, source, headers); | |||
} | |||
} else { | |||
rspamd.alertMessage("alert-error", "Message source field cannot be blank"); | |||
common.alertMessage("alert-error", "Message source field cannot be blank"); | |||
} | |||
return false; | |||
}); |