mirror of
https://git.proxmox.com/git/pmg-gui
synced 2025-10-20 20:36:11 +00:00
quarantine: refactor spamquarantine controller
over the time the spam quarantine has gained quite a few nice usability improvments and features, which would be useful in the virus and attachment quarantines as well - e.g.: * multi-mail selection * keyboard actions * context menu * download mail as .eml this patch splits the controller part into a file of its own, while changing 'var' to 'let' and removing the parts only relevant for the spamquarantine and adapts the current SpamQuarantine.js to use it. Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
This commit is contained in:
parent
ce7acf1733
commit
e66c888b86
@ -19,6 +19,7 @@ JSSRC= \
|
|||||||
RuleInfo.js \
|
RuleInfo.js \
|
||||||
RuleEditor.js \
|
RuleEditor.js \
|
||||||
MainView.js \
|
MainView.js \
|
||||||
|
controller/QuarantineController.js \
|
||||||
QuarantineContextMenu.js \
|
QuarantineContextMenu.js \
|
||||||
QuarantineList.js \
|
QuarantineList.js \
|
||||||
SpamInfoGrid.js \
|
SpamInfoGrid.js \
|
||||||
|
@ -33,6 +33,101 @@ Ext.define('pmg-spam-list', {
|
|||||||
idProperty: 'id',
|
idProperty: 'id',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ext.define('PMG.SpamQuarantineController', {
|
||||||
|
extend: 'PMG.controller.QuarantineController',
|
||||||
|
xtype: 'pmgSpamQuarantineController',
|
||||||
|
alias: 'controller.spamquarantine',
|
||||||
|
|
||||||
|
updatePreview: function(raw, rec) {
|
||||||
|
let me = this;
|
||||||
|
me.lookupReference('spam').setDisabled(false);
|
||||||
|
|
||||||
|
me.callParent(arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
multiSelect: function(selection) {
|
||||||
|
let me = this;
|
||||||
|
let spam = me.lookupReference('spam');
|
||||||
|
spam.setDisabled(true);
|
||||||
|
spam.setPressed(false);
|
||||||
|
me.lookupReference('spaminfo').setVisible(false);
|
||||||
|
me.callParent(selection);
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelectMail: function() {
|
||||||
|
let me = this;
|
||||||
|
let list = me.lookupReference('list');
|
||||||
|
let selection = list.selModel.getSelection();
|
||||||
|
if (selection.length <= 1) {
|
||||||
|
let rec = selection[0] || {};
|
||||||
|
me.lookupReference('spaminfo').setID(rec);
|
||||||
|
}
|
||||||
|
me.callParent();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
toggleSpamInfo: function(btn) {
|
||||||
|
var grid = this.lookupReference('spaminfo');
|
||||||
|
grid.setVisible(!grid.isVisible());
|
||||||
|
},
|
||||||
|
|
||||||
|
openContextMenu: function(table, record, tr, index, event) {
|
||||||
|
event.stopEvent();
|
||||||
|
let me = this;
|
||||||
|
let list = me.lookup('list');
|
||||||
|
Ext.create('PMG.menu.SpamContextMenu', {
|
||||||
|
callback: action => me.doAction(action, list.getSelection()),
|
||||||
|
}).showAt(event.getXY());
|
||||||
|
},
|
||||||
|
|
||||||
|
keyPress: function(table, record, item, index, event) {
|
||||||
|
var me = this;
|
||||||
|
var list = me.lookup('list');
|
||||||
|
var key = event.getKey();
|
||||||
|
var action = '';
|
||||||
|
switch (key) {
|
||||||
|
case event.DELETE:
|
||||||
|
case 127:
|
||||||
|
action = 'delete';
|
||||||
|
break;
|
||||||
|
case Ext.event.Event.D:
|
||||||
|
case Ext.event.Event.D + 32:
|
||||||
|
action = 'deliver';
|
||||||
|
break;
|
||||||
|
case Ext.event.Event.W:
|
||||||
|
case Ext.event.Event.W + 32:
|
||||||
|
action = 'whitelist';
|
||||||
|
break;
|
||||||
|
case Ext.event.Event.B:
|
||||||
|
case Ext.event.Event.B + 32:
|
||||||
|
action = 'blacklist';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action !== '') {
|
||||||
|
me.doAction(action, list.getSelection());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function(view) {
|
||||||
|
this.lookup('list').cselect = view.cselect;
|
||||||
|
},
|
||||||
|
|
||||||
|
control: {
|
||||||
|
'button[reference=raw]': {
|
||||||
|
click: 'toggleRaw',
|
||||||
|
},
|
||||||
|
'button[reference=spam]': {
|
||||||
|
click: 'toggleSpamInfo',
|
||||||
|
},
|
||||||
|
'pmgQuarantineList': {
|
||||||
|
selectionChange: 'onSelectMail',
|
||||||
|
itemkeypress: 'keyPress',
|
||||||
|
rowcontextmenu: 'openContextMenu',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Ext.define('PMG.SpamQuarantine', {
|
Ext.define('PMG.SpamQuarantine', {
|
||||||
extend: 'Ext.container.Container',
|
extend: 'Ext.container.Container',
|
||||||
xtype: 'pmgSpamQuarantine',
|
xtype: 'pmgSpamQuarantine',
|
||||||
@ -54,204 +149,7 @@ Ext.define('PMG.SpamQuarantine', {
|
|||||||
downloadMailURL: get => '/api2/json/quarantine/download?mailid=' + encodeURIComponent(get('mailid')),
|
downloadMailURL: get => '/api2/json/quarantine/download?mailid=' + encodeURIComponent(get('mailid')),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
controller: {
|
controller: 'spamquarantine',
|
||||||
|
|
||||||
xclass: 'Ext.app.ViewController',
|
|
||||||
|
|
||||||
updatePreview: function(raw, rec) {
|
|
||||||
var preview = this.lookupReference('preview');
|
|
||||||
|
|
||||||
if (!rec || !rec.data || !rec.data.id) {
|
|
||||||
preview.update('');
|
|
||||||
preview.setDisabled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = `/api2/htmlmail/quarantine/content?id=${rec.data.id}`;
|
|
||||||
if (raw) {
|
|
||||||
url += '&raw=1';
|
|
||||||
}
|
|
||||||
preview.setDisabled(false);
|
|
||||||
this.lookupReference('raw').setDisabled(false);
|
|
||||||
this.lookupReference('spam').setDisabled(false);
|
|
||||||
this.lookupReference('download').setDisabled(false);
|
|
||||||
preview.update("<iframe frameborder=0 width=100% height=100% sandbox='allow-same-origin' src='" + url +"'></iframe>");
|
|
||||||
},
|
|
||||||
|
|
||||||
multiSelect: function(selection) {
|
|
||||||
var preview = this.lookupReference('preview');
|
|
||||||
var raw = this.lookupReference('raw');
|
|
||||||
var spam = this.lookupReference('spam');
|
|
||||||
var spaminfo = this.lookupReference('spaminfo');
|
|
||||||
var mailinfo = this.lookupReference('mailinfo');
|
|
||||||
var download = this.lookupReference('download');
|
|
||||||
|
|
||||||
preview.setDisabled(false);
|
|
||||||
preview.update(`<h3 style="padding-left:5px;">${gettext('Multiple E-Mails selected')} (${selection.length})</h3>`);
|
|
||||||
raw.setDisabled(true);
|
|
||||||
spam.setDisabled(true);
|
|
||||||
spam.setPressed(false);
|
|
||||||
spaminfo.setVisible(false);
|
|
||||||
mailinfo.setVisible(false);
|
|
||||||
download.setDisabled(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleRaw: function(button) {
|
|
||||||
var me = this;
|
|
||||||
var list = me.lookupReference('list');
|
|
||||||
var rec = list.selModel.getSelection()[0];
|
|
||||||
me.lookupReference('mailinfo').setVisible(me.raw);
|
|
||||||
me.raw = !me.raw;
|
|
||||||
me.updatePreview(me.raw, rec);
|
|
||||||
},
|
|
||||||
|
|
||||||
btnHandler: function(button, e) {
|
|
||||||
var me = this;
|
|
||||||
var action = button.reference;
|
|
||||||
var list = this.lookupReference('list');
|
|
||||||
var selected = list.getSelection();
|
|
||||||
me.doAction(action, selected);
|
|
||||||
},
|
|
||||||
|
|
||||||
doAction: function(action, selected) {
|
|
||||||
if (!selected.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = this.lookupReference('list');
|
|
||||||
|
|
||||||
if (selected.length > 1) {
|
|
||||||
let idlist = selected.map(item => item.data.id);
|
|
||||||
Ext.Msg.confirm(
|
|
||||||
gettext('Confirm'),
|
|
||||||
Ext.String.format(
|
|
||||||
gettext("Action '{0}' for '{1}' items"),
|
|
||||||
action, selected.length,
|
|
||||||
),
|
|
||||||
async function(button) {
|
|
||||||
if (button !== 'yes') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
list.mask(gettext('Processing...'), 'x-mask-loading');
|
|
||||||
|
|
||||||
const sliceSize = 2500, maxInFlight = 2;
|
|
||||||
let batches = [], batchCount = Math.ceil(selected.length / sliceSize);
|
|
||||||
for (let i = 0; i * sliceSize < selected.length; i++) {
|
|
||||||
let sliceStart = i * sliceSize;
|
|
||||||
let sliceEnd = Math.min(sliceStart + sliceSize, selected.length);
|
|
||||||
batches.push(
|
|
||||||
PMG.Async.doQAction(
|
|
||||||
action,
|
|
||||||
idlist.slice(sliceStart, sliceEnd),
|
|
||||||
i + 1,
|
|
||||||
batchCount,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (batches.length >= maxInFlight) {
|
|
||||||
await Promise.allSettled(batches); // eslint-disable-line no-await-in-loop
|
|
||||||
batches = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await Promise.allSettled(batches); // await possible remaining ones
|
|
||||||
list.unmask();
|
|
||||||
// below can be slow, we could remove directly from the in-memory store, but
|
|
||||||
// with lots of elements and some failures we could be quite out of sync?
|
|
||||||
list.getController().load();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PMG.Utils.doQuarantineAction(action, selected[0].data.id, function() {
|
|
||||||
let listController = list.getController();
|
|
||||||
listController.allowPositionSave = false;
|
|
||||||
// success -> remove directly to avoid slow store reload for a single-element action
|
|
||||||
list.getStore().remove(selected[0]);
|
|
||||||
listController.restoreSavedSelection();
|
|
||||||
listController.allowPositionSave = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onSelectMail: function() {
|
|
||||||
var me = this;
|
|
||||||
var list = this.lookupReference('list');
|
|
||||||
var selection = list.selModel.getSelection();
|
|
||||||
if (selection.length > 1) {
|
|
||||||
me.multiSelect(selection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rec = selection[0] || {};
|
|
||||||
|
|
||||||
me.getViewModel().set('mailid', rec.data ? rec.data.id : '');
|
|
||||||
me.updatePreview(me.raw || false, rec);
|
|
||||||
me.lookupReference('spaminfo').setID(rec);
|
|
||||||
me.lookupReference('mailinfo').setVisible(!!rec.data && !me.raw);
|
|
||||||
me.lookupReference('mailinfo').update(rec.data);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleSpamInfo: function(btn) {
|
|
||||||
var grid = this.lookupReference('spaminfo');
|
|
||||||
grid.setVisible(!grid.isVisible());
|
|
||||||
},
|
|
||||||
|
|
||||||
openContextMenu: function(table, record, tr, index, event) {
|
|
||||||
event.stopEvent();
|
|
||||||
let me = this;
|
|
||||||
let list = me.lookup('list');
|
|
||||||
Ext.create('PMG.menu.SpamContextMenu', {
|
|
||||||
callback: action => me.doAction(action, list.getSelection()),
|
|
||||||
}).showAt(event.getXY());
|
|
||||||
},
|
|
||||||
|
|
||||||
keyPress: function(table, record, item, index, event) {
|
|
||||||
var me = this;
|
|
||||||
var list = me.lookup('list');
|
|
||||||
var key = event.getKey();
|
|
||||||
var action = '';
|
|
||||||
switch (key) {
|
|
||||||
case event.DELETE:
|
|
||||||
case 127:
|
|
||||||
action = 'delete';
|
|
||||||
break;
|
|
||||||
case Ext.event.Event.D:
|
|
||||||
case Ext.event.Event.D + 32:
|
|
||||||
action = 'deliver';
|
|
||||||
break;
|
|
||||||
case Ext.event.Event.W:
|
|
||||||
case Ext.event.Event.W + 32:
|
|
||||||
action = 'whitelist';
|
|
||||||
break;
|
|
||||||
case Ext.event.Event.B:
|
|
||||||
case Ext.event.Event.B + 32:
|
|
||||||
action = 'blacklist';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action !== '') {
|
|
||||||
me.doAction(action, list.getSelection());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
init: function(view) {
|
|
||||||
this.lookup('list').cselect = view.cselect;
|
|
||||||
},
|
|
||||||
|
|
||||||
control: {
|
|
||||||
'button[reference=raw]': {
|
|
||||||
click: 'toggleRaw',
|
|
||||||
},
|
|
||||||
'button[reference=spam]': {
|
|
||||||
click: 'toggleSpamInfo',
|
|
||||||
},
|
|
||||||
'pmgQuarantineList': {
|
|
||||||
selectionChange: 'onSelectMail',
|
|
||||||
itemkeypress: 'keyPress',
|
|
||||||
rowcontextmenu: 'openContextMenu',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
170
js/controller/QuarantineController.js
Normal file
170
js/controller/QuarantineController.js
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
Ext.define('PMG.controller.QuarantineController', {
|
||||||
|
extend: 'Ext.app.ViewController',
|
||||||
|
xtype: 'controller.Quarantine',
|
||||||
|
alias: 'controller.quarantine',
|
||||||
|
|
||||||
|
updatePreview: function(raw, rec) {
|
||||||
|
let preview = this.lookupReference('preview');
|
||||||
|
|
||||||
|
if (!rec || !rec.data || !rec.data.id) {
|
||||||
|
preview.update('');
|
||||||
|
preview.setDisabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = `/api2/htmlmail/quarantine/content?id=${rec.data.id}`;
|
||||||
|
if (raw) {
|
||||||
|
url += '&raw=1';
|
||||||
|
}
|
||||||
|
preview.setDisabled(false);
|
||||||
|
this.lookupReference('raw').setDisabled(false);
|
||||||
|
this.lookupReference('download').setDisabled(false);
|
||||||
|
preview.update("<iframe frameborder=0 width=100% height=100% sandbox='allow-same-origin' src='" + url +"'></iframe>");
|
||||||
|
},
|
||||||
|
|
||||||
|
multiSelect: function(selection) {
|
||||||
|
let me = this;
|
||||||
|
me.lookupReference('raw').setDisabled(true);
|
||||||
|
me.lookupReference('download').setDisabled(true);
|
||||||
|
me.lookupReference('mailinfo').setVisible(false);
|
||||||
|
|
||||||
|
let preview = me.lookupReference('preview');
|
||||||
|
preview.setDisabled(false);
|
||||||
|
preview.update(`<h3 style="padding-left:5px;">${gettext('Multiple E-Mails selected')} (${selection.length})</h3>`);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleRaw: function(button) {
|
||||||
|
let me = this;
|
||||||
|
let list = me.lookupReference('list');
|
||||||
|
let rec = list.selModel.getSelection()[0];
|
||||||
|
me.lookupReference('mailinfo').setVisible(me.raw);
|
||||||
|
me.raw = !me.raw;
|
||||||
|
me.updatePreview(me.raw, rec);
|
||||||
|
},
|
||||||
|
|
||||||
|
btnHandler: function(button, e) {
|
||||||
|
let me = this;
|
||||||
|
let action = button.reference;
|
||||||
|
let list = me.lookupReference('list');
|
||||||
|
let selected = list.getSelection();
|
||||||
|
me.doAction(action, selected);
|
||||||
|
},
|
||||||
|
|
||||||
|
doAction: function(action, selected) {
|
||||||
|
if (!selected.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let list = this.lookupReference('list');
|
||||||
|
|
||||||
|
if (selected.length > 1) {
|
||||||
|
let idlist = selected.map(item => item.data.id);
|
||||||
|
Ext.Msg.confirm(
|
||||||
|
gettext('Confirm'),
|
||||||
|
Ext.String.format(
|
||||||
|
gettext("Action '{0}' for '{1}' items"),
|
||||||
|
action, selected.length,
|
||||||
|
),
|
||||||
|
async function(button) {
|
||||||
|
if (button !== 'yes') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.mask(gettext('Processing...'), 'x-mask-loading');
|
||||||
|
|
||||||
|
const sliceSize = 2500, maxInFlight = 2;
|
||||||
|
let batches = [], batchCount = Math.ceil(selected.length / sliceSize);
|
||||||
|
for (let i = 0; i * sliceSize < selected.length; i++) {
|
||||||
|
let sliceStart = i * sliceSize;
|
||||||
|
let sliceEnd = Math.min(sliceStart + sliceSize, selected.length);
|
||||||
|
batches.push(
|
||||||
|
PMG.Async.doQAction(
|
||||||
|
action,
|
||||||
|
idlist.slice(sliceStart, sliceEnd),
|
||||||
|
i + 1,
|
||||||
|
batchCount,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (batches.length >= maxInFlight) {
|
||||||
|
await Promise.allSettled(batches); // eslint-disable-line no-await-in-loop
|
||||||
|
batches = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Promise.allSettled(batches); // await possible remaining ones
|
||||||
|
list.unmask();
|
||||||
|
// below can be slow, we could remove directly from the in-memory store, but
|
||||||
|
// with lots of elements and some failures we could be quite out of sync?
|
||||||
|
list.getController().load();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PMG.Utils.doQuarantineAction(action, selected[0].data.id, function() {
|
||||||
|
let listController = list.getController();
|
||||||
|
listController.allowPositionSave = false;
|
||||||
|
// success -> remove directly to avoid slow store reload for a single-element action
|
||||||
|
list.getStore().remove(selected[0]);
|
||||||
|
listController.restoreSavedSelection();
|
||||||
|
listController.allowPositionSave = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onSelectMail: function() {
|
||||||
|
let me = this;
|
||||||
|
let list = this.lookupReference('list');
|
||||||
|
let selection = list.selModel.getSelection();
|
||||||
|
if (selection.length > 1) {
|
||||||
|
me.multiSelect(selection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rec = selection[0] || {};
|
||||||
|
|
||||||
|
me.getViewModel().set('mailid', rec.data ? rec.data.id : '');
|
||||||
|
me.updatePreview(me.raw || false, rec);
|
||||||
|
me.lookupReference('mailinfo').setVisible(!!rec.data && !me.raw);
|
||||||
|
me.lookupReference('mailinfo').update(rec.data);
|
||||||
|
},
|
||||||
|
|
||||||
|
openContextMenu: function(table, record, tr, index, event) {
|
||||||
|
event.stopEvent();
|
||||||
|
let me = this;
|
||||||
|
let list = me.lookup('list');
|
||||||
|
Ext.create('PMG.menu.QuarantineContextMenu', {
|
||||||
|
callback: action => me.doAction(action, list.getSelection()),
|
||||||
|
}).showAt(event.getXY());
|
||||||
|
},
|
||||||
|
|
||||||
|
keyPress: function(table, record, item, index, event) {
|
||||||
|
let me = this;
|
||||||
|
let list = me.lookup('list');
|
||||||
|
let key = event.getKey();
|
||||||
|
let action = '';
|
||||||
|
switch (key) {
|
||||||
|
case event.DELETE:
|
||||||
|
case 127:
|
||||||
|
action = 'delete';
|
||||||
|
break;
|
||||||
|
case Ext.event.Event.D:
|
||||||
|
case Ext.event.Event.D + 32:
|
||||||
|
action = 'deliver';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action !== '') {
|
||||||
|
me.doAction(action, list.getSelection());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
control: {
|
||||||
|
'button[reference=raw]': {
|
||||||
|
click: 'toggleRaw',
|
||||||
|
},
|
||||||
|
'pmgQuarantineList': {
|
||||||
|
selectionChange: 'onSelectMail',
|
||||||
|
itemkeypress: 'keyPress',
|
||||||
|
rowcontextmenu: 'openContextMenu',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user