[pmg-devel] [PATCH pmg-gui 3/4] quarantine: add common controller for all quarantines

Stoiko Ivanov s.ivanov at proxmox.com
Thu Oct 20 21:14:59 CEST 2022


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

by adding the common class (which is mostly a copy of the controller
part of SpamQuarantine.js with changes of 'var' to 'let' and removal of the
spam-specific parts), we can reuse it for all the quarantines.

Signed-off-by: Stoiko Ivanov <s.ivanov at proxmox.com>
---
 js/Makefile                           |   1 +
 js/controller/QuarantineController.js | 170 ++++++++++++++++++++++++++
 2 files changed, 171 insertions(+)
 create mode 100644 js/controller/QuarantineController.js

diff --git a/js/Makefile b/js/Makefile
index b904598..9a2bcf2 100644
--- a/js/Makefile
+++ b/js/Makefile
@@ -19,6 +19,7 @@ JSSRC=							\
 	RuleInfo.js					\
 	RuleEditor.js					\
 	MainView.js					\
+	controller/QuarantineController.js		\
 	QuarantineContextMenu.js			\
 	QuarantineList.js				\
 	SpamInfoGrid.js					\
diff --git a/js/controller/QuarantineController.js b/js/controller/QuarantineController.js
new file mode 100644
index 0000000..dfe2915
--- /dev/null
+++ b/js/controller/QuarantineController.js
@@ -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',
+	},
+    },
+});
-- 
2.30.2





More information about the pmg-devel mailing list