[pmg-devel] [PATCH pmg-gui 1/1] add Custom Scores panel to the Spam Detector

Stoiko Ivanov s.ivanov at proxmox.com
Wed Nov 13 15:13:29 CET 2019


On Fri,  8 Nov 2019 14:29:32 +0100
Dominik Csapak <d.csapak at proxmox.com> wrote:

> with this panel, users can manually override a rule score for
> SpamAssassin rules. This can be useful sometimes, e.g. if
> a certain rule always triggers in a certain environment which
> is considered spam by the SpamAssassin rules
> 
> after adding/editing a rule, we show diff, similar to the network
> panel, and offer a button to apply those changes (and restart
> pmg-smtp-filter). the changes can also be reverted
> 
> Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
> ---
>  js/Makefile                     |   1 +
>  js/SpamDetectorConfiguration.js |   7 +-
>  js/SpamDetectorCustom.js        | 308 ++++++++++++++++++++++++++++++++
>  3 files changed, 315 insertions(+), 1 deletion(-)
>  create mode 100644 js/SpamDetectorCustom.js
> 
> diff --git a/js/Makefile b/js/Makefile
> index 762b43d..e0ac026 100644
> --- a/js/Makefile
> +++ b/js/Makefile
> @@ -54,6 +54,7 @@ JSSRC=							\
>  	SpamQuarantineOptions.js			\
>  	SpamDetectorStatus.js				\
>  	SpamDetectorConfiguration.js			\
> +	SpamDetectorCustom.js				\
>  	VirusDetectorOptions.js				\
>  	VirusQuarantineOptions.js			\
>  	VirusQuarantine.js				\
> diff --git a/js/SpamDetectorConfiguration.js b/js/SpamDetectorConfiguration.js
> index f10c8dc..2c0d755 100644
> --- a/js/SpamDetectorConfiguration.js
> +++ b/js/SpamDetectorConfiguration.js
> @@ -23,7 +23,12 @@ Ext.define('PMG.SpamDetectorConfiguration', {
>  	    title: gettext('Status'),
>  	    itemId: 'status',
>  	    xtype: 'pmgSpamDetectorStatus'
> -	}
> +	},
> +	{
> +	    title: gettext('Custom Scores'),
> +	    itemId: 'scores',
> +	    xtype: 'pmgSpamDetectorCustomScores'
> +	},
>      ]
>  });
>  
> diff --git a/js/SpamDetectorCustom.js b/js/SpamDetectorCustom.js
> new file mode 100644
> index 0000000..a65fcdf
> --- /dev/null
> +++ b/js/SpamDetectorCustom.js
> @@ -0,0 +1,308 @@
> +Ext.define('pmg-sa-custom', {
> +    extend: 'Ext.data.Model',
> +    fields: [ 'name', 'score', 'comment', 'digest' ],
> +    idProperty: 'name'
> +});
> +
> +Ext.define('PMG.SpamDetectorCustomScores', {
> +    extend: 'Ext.panel.Panel',
> +    xtype: 'pmgSpamDetectorCustomScores',
> +
> +    layout: 'border',
> +
> +    viewModel: {
> +	data: {
> +	    applied: true,
> +	    changetext: '',
> +	    digest: null,
> +	},
> +    },
> +
> +    controller: {
> +	xclass: 'Ext.app.ViewController',
> +
> +	reload: function() {
> +	    var me = this;
> +	    var vm = this.getViewModel();
> +	    var grid = me.lookup('grid');
> +
> +	    Proxmox.Utils.API2Request({
> +		url: '/config/customscores',
> +		failure: function(response, opts) {
> +		    grid.getStore().loadData({});
> +		    Proxmox.Utils.setErrorMask(grid, response.htmlStatus);
> +		    vm.set('digest', null);
> +		    vm.set('applied', true);
> +		    vm.set('changtext', '');
small typo: s/changtext/changetext/
> +		},
> +		success: function(response, opts) {
> +		    let data = response.result.data;
> +		    let digestel = data.pop(); // last element is digest
> +		    let changes = response.result.changes;
> +		    grid.getStore().loadData(data);
> +
> +		    vm.set('digest', digestel.digest);
> +		    vm.set('applied', !changes);
> +		    vm.set('changetext', `<pre>${changes || ''}</pre>`);
> +		}
> +	    });
> +	},
> +
> +	revert: function() {
> +	    var me = this;
> +	    var vm = this.getViewModel();
> +
> +	    Proxmox.Utils.API2Request({
> +		url: '/config/customscores',
> +		method: 'DELETE',
> +		param: {
> +		    digest: vm.get('digest'),
> +		},
> +		failure: function(response, opts) {
> +		    grid.getStore().loadData({});
> +		    Proxmox.Utils.setErrorMask(grid, response.htmlStatus);
> +		    vm.set('digest', null);
> +		    vm.set('applied', true);
> +		    vm.set('changtext', '');
also here
> +		},
> +		success: () => { me.reload(); },
> +	    });
> +	},
> +
> +	restart: function() {
> +	    var me = this;
> +	    var vm = this.getViewModel();
> +
> +	    var win = Ext.createWidget('proxmoxWindowEdit', {
> +		method: 'PUT',
> +		url: "/api2/extjs/config/customscores",
> +		isCreate: true,
> +		submitText: gettext('Apply'),
> +		showProgress: true,
> +		taskDone: () => { me.reload(); },
> +
> +		title: gettext("Apply Custom Scores"),
> +
> +		items: [
> +		    {
> +			xtype: 'proxmoxcheckbox',
> +			name: 'restart-daemon',
> +			fieldLabel: gettext('Restart pmg-smtp-filter'),
> +			labelWidth: 150,
> +			checked: true,
> +		    },
> +		    {
> +			xtype: 'hiddenfield',
> +			name: 'digest',
> +			value: vm.get('digest'),
> +		    }
> +		]
> +	    }).show();
> +	},
> +
> +	create_custom: function() {
> +	    var me = this;
> +	    var vm = this.getViewModel();
> +
> +	    var win = Ext.createWidget('proxmoxWindowEdit', {
> +		method: 'POST',
> +		url: "/api2/extjs/config/customscores",
> +		isCreate: true,
> +		subject: gettext("Custom Rule Score"),
> +		items: [
> +		    {
> +			xtype: 'proxmoxtextfield',
> +			name: 'name',
> +			allowBlank: false,
> +			fieldLabel: gettext('Name')
> +		    },
> +		    {
> +			xtype: 'numberfield',
> +			name: 'score',
> +			allowBlank: false,
> +			fieldLabel: gettext('Score')
> +		    },
> +
> +		    {
> +			xtype: 'proxmoxtextfield',
> +			name: 'comment',
> +			fieldLabel: gettext("Comment")
> +		    },
> +		    {
> +			xtype: 'hiddenfield',
> +			name: 'digest',
> +			value: vm.get('digest'),
> +		    }
> +		]
> +	    });
> +
> +	    win.on('destroy', me.reload, me);
> +	    win.show();
> +	},
> +
> +	run_editor: function() {
> +	    var me = this;
> +	    var vm = this.getViewModel();
> +	    var grid = me.lookup('grid');
> +	    var rec = grid.getSelection()[0];
> +	    if (!rec) {
> +		return;
> +	    }
> +
> +	    var win = Ext.createWidget('proxmoxWindowEdit', {
> +		url: "/api2/extjs/config/customscores/" + rec.data.name,
> +		method: 'PUT',
> +		subject: gettext("Custom Rule Score"),
> +		items: [
> +		    {
> +			xtype: 'displayfield',
> +			name: 'name',
> +			fieldLabel: gettext('Name')
> +		    },
> +		    {
> +			xtype: 'numberfield',
> +			name: 'score',
> +			allowBlank: false,
> +			fieldLabel: gettext('Score')
> +		    },
> +
> +		    {
> +			xtype: 'proxmoxtextfield',
> +			name: 'comment',
> +			fieldLabel: gettext("Comment")
> +		    },
> +		    {
> +			xtype: 'hiddenfield',
> +			name: 'digest',
> +			value: vm.get('digest'),
> +		    }
> +		]
> +	    });
> +
> +	    win.load();
> +	    win.on('destroy', me.reload, me);
> +	    win.show();
> +	},
> +    },
> +
> +    listeners: {
> +	activate: 'reload',
> +    },
> +
> +    defaults: {
> +	border: 0,
> +    },
> +
> +    items: [
> +	{
> +	    xtype: 'gridpanel',
> +	    region: 'center',
> +	    reference: 'grid',
> +
> +	    store: {
> +		model: 'pmg-sa-custom',
> +		proxy: {
> +		    type: 'proxmox',
> +		    url: "/api2/json/config/customscores"
> +		},
> +		sorters: {
> +		    property: 'name',
> +		}
> +	    },
> +
> +	    tbar: [
> +		{
> +		    xtype: 'proxmoxButton',
> +		    text: gettext('Edit'),
> +		    disabled: true,
> +		    handler: 'run_editor'
> +		},
> +		{
> +		    text: gettext('Create'),
> +		    handler: 'create_custom',
> +		},
> +		{
> +		    xtype: 'proxmoxStdRemoveButton',
> +		    getUrl: function(rec) {
> +			let digest = this.up('grid').digest;
> +			let url = `/config/customscores/${rec.getId()}`;
> +			if (digest) {
> +			    url += `?digest=${digest}`
> +			}
> +			return url;
> +		    },
> +		    callback: 'reload',
> +		},
> +		' ',
> +		{
> +		    text: gettext('Revert'),
> +		    reference: 'revert_btn',
> +		    handler: 'revert',
> +		    disabled: true,
> +		    bind: {
> +			disabled: '{applied}',
> +		    },
> +		},
> +		'-',
> +		{
> +		    text: gettext('Apply Custom Scores'),
> +		    reference: 'restart_btn',
> +		    disabled: true,
> +		    bind: {
> +			disabled: '{applied}',
> +		    },
> +		    handler: 'restart',
> +		}
> +	    ],
> +
> +	    viewConfig: {
> +		trackOver: false
> +	    },
> +
> +	    columns: [
> +		{
> +		    header: gettext('Name'),
> +		    width: 200,
> +		    sortable: true,
> +		    dataIndex: 'name'
> +		},
> +		{
> +		    header: gettext('Score'),
> +		    width: 200,
> +		    sortable: true,
> +		    dataIndex: 'score'
> +		},
> +		{
> +		    header: gettext('Comment'),
> +		    sortable: false,
> +		    renderer: Ext.String.htmlEncode,
> +		    dataIndex: 'comment',
> +		    flex: 1
> +		}
> +	    ],
> +
> +	    listeners: {
> +		itemdblclick: 'run_editor',
> +	    }
> +	},
> +	{
> +	    xtype: 'panel',
> +	    bodyPadding: 5,
> +	    region: 'south',
> +	    autoScroll: true,
> +	    flex: 0.5,
> +	    hidden: true,
> +	    bind: {
> +		hidden: '{applied}',
> +		html: '{changetext}'
> +	    },
> +	    reference: 'changes',
> +	    tbar: [
> +		gettext('Pending changes') + ' (' +
> +		gettext('Please restart pmg-smtp-filter to activate changes') + ')'
> +	    ],
> +	    split: true,
> +	}
> +    ],
> +
> +});




More information about the pmg-devel mailing list