[pve-devel] [PATCH 16/18] rewrite ruleinfo

Dominik Csapak d.csapak at proxmox.com
Tue Apr 4 13:48:56 CEST 2017


complete rewrite of the ruleinfo sidebar

* uses mvvm system
* show tabpanel with all unused objects, each category in a tab
* group the used objects by type
* allow for drag/drop or the actionbuttons for adding/removing

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 js/RuleInfo.js | 608 +++++++++++++++++++++++++++++++++++----------------------
 1 file changed, 379 insertions(+), 229 deletions(-)

diff --git a/js/RuleInfo.js b/js/RuleInfo.js
index 8d3b388..6989fe0 100644
--- a/js/RuleInfo.js
+++ b/js/RuleInfo.js
@@ -1,120 +1,73 @@
 Ext.define('PMG.RuleInfo', {
-    extend: 'Ext.grid.GridPanel',
-    alias: 'widget.pmgRuleInfo',
-
-    baseurl: undefined,
-
-    ruledata: undefined,
-
-    emptyText: gettext('Please select a rule.'),
-
-    setBaseUrl: function(baseurl) {
-	var me = this;
-
-	me.baseurl = baseurl;
-
-	me.reload();
-    },
-
-    reload: function() {
-	var me = this;
-
-	if (!me.baseurl) {
-	    me.setRuleInfo(undefined);
-	    return;
-	}
-
-	Proxmox.Utils.API2Request({
-	    url: me.baseurl + "/config",
-	    method: 'GET',
-	    waitMsgTarget: me,
-	    success: function(response, opts) {
-		me.setRuleInfo(response.result.data);
-	    },
-	    failure: function (response, opts) {
-		Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+    extend: 'Ext.panel.Panel',
+    xtype: 'pmgRuleInfo',
+
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	setBaseUrl: function(baseurl) {
+	    var me = this;
+	    me.getViewModel().set('baseurl', baseurl);
+	    me.reload();
+	},
+
+	reload: function() {
+	    var me = this;
+	    var viewmodel = me.getViewModel();
+	    var baseurl = viewmodel.get('baseurl');
+
+	    if (!baseurl) {
+		me.setRuleInfo(undefined);
+		return;
 	    }
-	});
-    },
 
-    setRuleInfo: function(ruledata) {
-	var me = this;
-
-	me.ruledata = ruledata;
-
-	me.down('#addFromButton').setDisabled(me.ruledata === undefined);
-	me.down('#addToButton').setDisabled(me.ruledata === undefined);
-	me.down('#addWhenButton').setDisabled(me.ruledata === undefined);
-	me.down('#addWhatButton').setDisabled(me.ruledata === undefined);
-	me.down('#addActionButton').setDisabled(me.ruledata === undefined);
-
-	if (me.ruledata === undefined) {
-
-	    me.store.setData([]);
-	    me.down('#ruleinfo').update(me.emtpyText);
-	    me.down('#ruledata').setHidden(true);
-
-	} else {
-
-	    var html = '<b>' + Ext.String.htmlEncode(me.ruledata.name) + '</b>';
-	    html += '<br><br>';
-	    html += 'Priority: ' +  me.ruledata.priority + '<br>';
-	    html += 'Direction: ' + PMG.Utils.format_rule_direction(me.ruledata.direction) + '<br>';
-	    html += 'Active: ' +  Proxmox.Utils.format_boolean(me.ruledata.active) + '<br>';
-
-	    var data = [];
-	    Ext.Array.each(['from', 'to', 'when', 'what', 'action'], function(oc) {
-		var list = ruledata[oc];
-		if (list === undefined) { return; }
-		Ext.Array.each(list, function(og) {
-		    data.push({ oclass: oc, name: og.name, id: og.id });
-		});
+	    Proxmox.Utils.API2Request({
+		url: baseurl + "/config",
+		method: 'GET',
+		success: function(response, opts) {
+		    me.setRuleInfo(response.result.data);
+		},
+		failure: function (response, opts) {
+		    Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+		}
 	    });
-
-	    me.store.setData(data);
-
-	    me.down('#ruleinfo').update(html);
-	    me.down('#ruledata').setHidden(false);
-	}
-    },
-
-    initComponent : function() {
-	var me = this;
-
-	me.store = new Ext.data.Store({
-	    fields: [ 'oclass', 'name' ]
-	});
-
-	me.columns = [
-	    {
-		header: gettext('Type'),
-		dataIndex: 'oclass',
-	    },
-	    {
-		header: gettext('name'),
-		dataIndex: 'name',
-		flex: 1
-	    }
-	];
-
-	me.selModel = Ext.create('Ext.selection.RowModel', {});
-
-	var remove_btn = Ext.createWidget('proxmoxStdRemoveButton', {
-	    selModel: me.selModel,
-	    getUrl: function(rec) {
-		return me.baseurl + '/' + rec.data.oclass + '/'+ rec.data.id;
-	    },
-	    callback: function() { me.reload(); },
-	    getRecordName: function(rec) { return rec.data.name; },
-	    waitMsgTarget: me
-	});
-
-	var add_object_group = function(url, ogroupId) {
+	},
+
+	removeObjectGroup: function(rec) {
+	    var me = this;
+	    Ext.Msg.confirm(
+		gettext('Confirm'),
+		Ext.String.format(
+		    gettext('Are you sure you want to remove entry {0}'),
+		    "'" + rec.data.name + "'"),
+		function(button) {
+		    if (button === 'yes') {
+			Proxmox.Utils.API2Request({
+			    url: me.getViewModel().get('baseurl') + '/' + rec.data.oclass + '/'+ rec.data.typeid,
+			    method: 'DELETE',
+			    waitMsgTarget: me.getView(),
+			    callback: function() {
+				me.reload();
+			    },
+			    failure: function (response, opts) {
+				Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+			    }
+			});
+		    }
+		}
+	    );
+	},
+
+	addObjectGroup: function(type, record) {
+	    var me = this;
+	    var baseurl = me.getViewModel().get('baseurl');
+	    var url = baseurl + '/' + type;
+	    var id = (type === 'action')?record.data.ogroup:record.data.id;
 	    Proxmox.Utils.API2Request({
 		url: url,
-		params: { ogroup: ogroupId },
+		params: { ogroup: id },
 		method: 'POST',
-		waitMsgTarget: me,
+		waitMsgTarget: me.getView(),
 		callback: function() {
 		    me.reload();
 		},
@@ -122,140 +75,337 @@ Ext.define('PMG.RuleInfo', {
 		    Ext.Msg.alert(gettext('Error'), response.htmlStatus);
 		}
 	    });
-	};
+	},
 
-	me.dockedItems = [];
+	setRuleInfo: function(ruledata) {
+	    var me = this;
 
-	me.dockedItems.push({
-	    xtype: 'toolbar',
-	    dock: 'top',
-	    items: [
-		{
-		    text: gettext('From'),
-		    disabled: true,
-		    itemId: 'addFromButton',
-		    handler: function() {
-			var win = Ext.create('PMG.ObjectGroupSelector', {
-			    rulegroup: 'from',
-			    listeners: {
-				selectObjectGroup: function(view, rec) {
-				    win.destroy();
-				    add_object_group(me.baseurl + '/from', rec.data.id);
-				}
-			    }
-			});
-			win.show();
+	    var viewmodel = me.getViewModel();
+
+	    if (ruledata === undefined) {
+
+		viewmodel.set('selectedRule', null);
+		viewmodel.get('objects').setData([]);
+
+	    } else {
+
+		ruledata.name = Ext.String.htmlEncode(ruledata.name);
+		viewmodel.set('selectedRule', ruledata);
+
+		var data = [];
+		Ext.Array.each(['from', 'to', 'when', 'what', 'action'], function(oc) {
+
+		    var store = viewmodel.get(oc + 'objects');
+		    if (ruledata[oc] === undefined || store === undefined) { return; }
+
+		    // we build a filter for the objects,
+		    // which are already added to the rule,
+		    // so what we only show the ones,
+		    // which are still available
+
+		    var ids = Ext.Array.pluck(ruledata[oc], 'id');
+		    // for the actions, we have a different id field
+		    var idField = (oc === 'action')?'ogroup':'id';
+		    store.clearFilter();
+		    store.addFilter({
+			filterFn:function(record){
+			    // FIXME
+			    // actions have the ogroup as a string
+			    // -> parseInt
+			    return (ids.indexOf(parseInt(record.data[idField])) === -1);
+			}
+		    });
+		    store.load();
+		    Ext.Array.each(ruledata[oc], function(og) {
+			data.push({ oclass: oc, name: og.name, typeid: og.id });
+		    });
+		});
+
+		viewmodel.get('objects').setData(data);
+	    }
+	},
+
+	removeIconClick: function(gridView, rowindex, colindex, button, event, record) {
+	    var me = this;
+	    me.removeObjectGroup(record);
+	},
+
+	removeDrop: function(gridView, data, overModel) {
+	    var me = this;
+	    var record = data.records[0]; // only one
+	    me.removeObjectGroup(record);
+	    return true;
+	},
+
+	addIconClick: function(gridView, rowindex, colindex, button, event, record) {
+	    var me = this;
+	    me.addObjectGroup(gridView.panel.type, record);
+	    return true;
+	},
+
+	addDrop: function(gridView, data, overModel) {
+	    var me = this;
+	    var record = data.records[0]; // only one
+	    me.addObjectGroup(data.view.panel.type, record);
+	    return true;
+	},
+
+	control: {
+	    'grid[reference=usedobjects]': {
+		drop: 'addDrop'
+	    },
+	    'tabpanel[reference=availobjects] > grid': {
+		drop: 'removeDrop'
+	    }
+	},
+    },
+
+    viewModel: {
+	data: {
+	    baseurl: undefined,
+	},
+
+	stores: {
+	    objects: {
+		fields: ['oclass', 'name', 'typeid'],
+		groupField: 'oclass',
+		sorters: 'name'
+	    },
+
+	    actionobjects: {
+		model: 'pmg-action-list',
+		proxy: {
+		    type: 'proxmox',
+		    url: "/api2/json/config/ruledb/action/objects",
+		},
+		sorters: 'name'
+	    },
+	    fromobjects: {
+		model: 'pmg-object-group',
+		proxy: {
+		    type: 'proxmox',
+		    url: "/api2/json/config/ruledb/who",
+		},
+		sorters: 'name'
+	    },
+	    toobjects: {
+		model: 'pmg-object-group',
+		proxy: {
+		    type: 'proxmox',
+		    url: "/api2/json/config/ruledb/who",
+		},
+		sorters: 'name'
+	    },
+	    whatobjects: {
+		model: 'pmg-object-group',
+		proxy: {
+		    type: 'proxmox',
+		    url: "/api2/json/config/ruledb/what",
+		},
+		sorters: 'name'
+	    },
+	    whenobjects: {
+		model: 'pmg-object-group',
+		proxy: {
+		    type: 'proxmox',
+		    url: "/api2/json/config/ruledb/when",
+		},
+		sorters: 'name'
+	    },
+	}
+    },
+
+
+    defaults: {
+	padding: '5 10 5 10',
+    },
+
+    bodyPadding: '5 0 5 0',
+
+    layout: {
+	type: 'vbox',
+	align: 'stretch'
+    },
+
+    scrollable: true,
+
+    items: [
+	{
+	    xtype: 'panel',
+	    bodyPadding: 10,
+	    data: {
+		name: false,
+	    },
+	    bind: {
+		data: {
+		    name: '{selectedRule.name}',
+		    priority: '{selectedRule.priority}',
+		    active: '{selectedRule.active}',
+		    direction: '{selectedRule.direction}',
+		    selected: '{selectedRule}'
+		}
+	    },
+	    tpl: [
+		'<tpl if="selected">',
+		'<b>{name}</b><br><br>',
+		'Priority: {priority}<br>',
+		'Direction: {[PMG.Utils.format_rule_direction(values.direction)]}<br>',
+		'Active: {[Proxmox.Utils.format_boolean(values.active)]}<br>',
+		'<tpl else>',
+		gettext('Please select a rule.'),
+		'</tpl>'
+	    ],
+	},
+	{
+	    xtype: 'grid',
+	    reference: 'usedobjects',
+	    hidden: true,
+	    emptyText: gettext('No Objects'),
+	    features: [{
+		id: 'group',
+		ftype: 'grouping',
+		enableGroupingMenu: false,
+		collapsible: false,
+		groupHeaderTpl: [
+		    '{[PMG.Utils.format_oclass(values.name)]}'
+		]
+	    }],
+
+	    title: gettext('Used Objects'),
+
+	    viewConfig: {
+		plugins: {
+		    ptype: 'gridviewdragdrop',
+		    copy: true,
+		    dragGroup: 'usedobjects',
+		    dropGroup: 'unusedobjects',
+
+		    // do not show default grid dragdrop behaviour
+		    dropZone: {
+			indicatorHtml: '',
+			indicatorCls: '',
+			handleNodeDrop: Ext.emptyFn
 		    }
+		}
+	    },
+
+	    columns: [
+		{
+		    header: gettext('Type'),
+		    dataIndex: 'oclass',
+		    hidden: true,
 		},
 		{
-		    text: gettext('To'),
-		    disabled: true,
-		    itemId: 'addToButton',
-		    handler: function() {
-			var win = Ext.create('PMG.ObjectGroupSelector', {
-			    rulegroup: 'to',
-			    listeners: {
-				selectObjectGroup: function(view, rec) {
-				    win.destroy();
-				    add_object_group(me.baseurl + '/to', rec.data.id);
-				}
-			    }
-			});
-			win.show();
-		    }
+		    header: gettext('Name'),
+		    dataIndex: 'name',
+		    flex: 1
 		},
 		{
-		    text: gettext('When'),
-		    disabled: true,
-		    itemId: 'addWhenButton',
-		    handler: function() {
-			var win = Ext.create('PMG.ObjectGroupSelector', {
-			    rulegroup: 'when',
-			    listeners: {
-				selectObjectGroup: function(view, rec) {
-				    win.destroy();
-				    add_object_group(me.baseurl + '/when', rec.data.id);
-				}
-			    }
-			});
-			win.show();
+		    header: gettext('Actions'),
+		    xtype: 'actioncolumn',
+		    width: 65,
+		    items: [
+			{
+			    iconCls: 'x-fa fa-fw fa-minus-circle',
+			    tooltip: gettext('Remove'),
+			    handler: 'removeIconClick'
+			}
+		    ]
+		}
+	    ],
+
+	    bind: {
+		store: '{objects}',
+		hidden: '{!selectedRule}'
+	    },
+	},
+	{
+	    xtype: 'tabpanel',
+	    title: gettext('Available Objects'),
+	    reference: 'availobjects',
+	    hidden: true,
+	    bind: {
+		hidden: '{!selectedRule}'
+	    },
+	    defaults: {
+		xtype: 'grid',
+		emptyText: gettext('No Objects'),
+		viewConfig: {
+		    plugins: {
+			ptype: 'gridviewdragdrop',
+			dragGroup: 'unusedobjects',
+			dropGroup: 'usedobjects',
+
+			// do not show default grid dragdrop behaviour
+			dropZone: {
+			    indicatorHtml: '',
+			    indicatorCls: '',
+			    handleNodeDrop: Ext.emptyFn
+			}
 		    }
 		},
-		{
-		    text: gettext('What'),
-		    disabled: true,
-		    itemId: 'addWhatButton',
-		    handler: function() {
-			var win = Ext.create('PMG.ObjectGroupSelector', {
-			    rulegroup: 'what',
-			    listeners: {
-				selectObjectGroup: function(view, rec) {
-				    win.destroy();
-				    add_object_group(me.baseurl + '/what', rec.data.id);
-				}
+		columns: [
+		    {
+			header: gettext('Name'),
+			dataIndex: 'name',
+			flex: 1
+		    },
+		    {
+			header: gettext('Actions'),
+			width: 65,
+			xtype: 'actioncolumn',
+			items: [
+			    {
+				iconCls: 'x-fa fa-fw fa-plus-circle',
+				tooltip: gettext('Add'),
+				handler: 'addIconClick'
 			    }
-			});
-			win.show();
+			]
 		    }
+		],
+	    },
+	    items: [
+		{
+		    title: gettext('Action'),
+		    bind: {
+			store: '{actionobjects}'
+		    },
+		    type: 'action',
+		    iconCls: 'fa fa-flag',
 		},
 		{
-		    text: gettext('Action'),
-		    disabled: true,
-		    itemId: 'addActionButton',
-		    handler: function() {
-			var win = Ext.create('PMG.ObjectGroupSelector', {
-			    rulegroup: 'action',
-			    listeners: {
-				selectObjectGroup: function(view, rec) {
-				    win.destroy();
-				    add_object_group(me.baseurl + '/action', rec.data.ogroup);
-				}
-			    }
-			});
-			win.show();
-		    }
+		    title: gettext('From'),
+		    iconCls: 'fa fa-user-circle',
+		    type: 'from',
+		    bind: {
+			store: '{fromobjects}'
+		    },
 		},
-		remove_btn
-	    ]
-	});
-
-	me.dockedItems.push({
-	    dock: 'top',
-	    border: 1,
-	    layout: 'anchor',
-	    itemId: 'ruledata',
-	    items: [
 		{
-		    xtype: 'component',
-		    anchor: '100%',
-		    itemId: 'ruleinfo',
-		    style: { 'white-space': 'pre' },
-		    padding: 10,
-		    html: me.emptyText,
-		    listeners: {
-			dblclick: {
-			    fn: function(e, t) {
-				if (me.ruledata === undefined) { return; }
-				me.fireEvent('dblclickRuleInfo', me, e, t, me.ruledata);
-			    },
-			    element: 'el',
-			    scope: this,
-			}
-		    }
-		}
+		    title: gettext('To'),
+		    iconCls: 'fa fa-user-circle',
+		    type: 'to',
+		    bind: {
+			store: '{toobjects}'
+		    },
+		},
+		{
+		    title: gettext('What'),
+		    iconCls: 'fa fa-cube',
+		    type: 'what',
+		    bind: {
+			store: '{whatobjects}'
+		    },
+		},
+		{
+		    title: gettext('When'),
+		    iconCls: 'fa fa-clock-o',
+		    type: 'when',
+		    bind: {
+			store: '{whenobjects}'
+		    },
+		},
 	    ]
-	});
-
-	Ext.apply(me, {
-	    listeners: {
-		activate: function() { me.reload() }
-	    }
-	});
-
-	me.callParent();
-
-	if (me.baseurl) {
-	    me.reload();
 	}
-    }
+    ]
 });
-- 
2.11.0





More information about the pve-devel mailing list