From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 4CF14B00E5 for ; Tue, 28 Nov 2023 15:34:49 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 29FC518DEF for ; Tue, 28 Nov 2023 15:34:19 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Tue, 28 Nov 2023 15:34:18 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id F13A041059 for ; Tue, 28 Nov 2023 15:34:17 +0100 (CET) From: Philipp Hufnagl To: pbs-devel@lists.proxmox.com Date: Tue, 28 Nov 2023 15:34:02 +0100 Message-Id: <20231128143403.443330-3-p.hufnagl@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20231128143403.443330-1-p.hufnagl@proxmox.com> References: <20231128143403.443330-1-p.hufnagl@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.046 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pbs-devel] [PATCH proxmox-backup v4 2/3] ui: Show if Filter includes or excludes X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 28 Nov 2023 14:34:49 -0000 To make the UI compatible, the Group Filter dialogue has been extended by a second list, so it now features a list for all include filter and one for all exclude filters. Internally, all include as well as exclude filter are managed into one list. The 2 list view is just for a cleaner representation in the UI. Signed-off-by: Philipp Hufnagl --- www/form/GroupFilter.js | 233 +++++++++++++++++++++++++++++----------- 1 file changed, 169 insertions(+), 64 deletions(-) diff --git a/www/form/GroupFilter.js b/www/form/GroupFilter.js index dee37b0b..70bd1a56 100644 --- a/www/form/GroupFilter.js +++ b/www/form/GroupFilter.js @@ -35,13 +35,36 @@ Ext.define('PBS.form.GroupFilter', { // break cyclic reference me.removeReferences(record); - me.lookup('grid').getStore().remove(record); + me.lookup('grid-include').getStore().remove(record); + me.lookup('grid-exclude').getStore().remove(record); me.updateRealField(); }, - addFilter: function() { + addIncludeFilter: function() { let me = this; - me.lookup('grid').getStore().add({}); + me.lookup('grid-include').getStore().add({ behavior: 'include' }); + me.updateRealField(); + }, + + addExcludeFilter: function() { + let me = this; + me.lookup('grid-exclude').getStore().add({ behavior: 'exclude' }); + me.updateRealField(); + }, + + + onBehaviorChange: function(field, value) { + let me = this; + let record = field.getWidgetRecord(); + if (record === undefined) { + return; + } + + record.set('behavior', value); + record.commit(); + if (record.widgets) { + me.setInputValue(record.widgets, record); + } me.updateRealField(); }, @@ -77,8 +100,12 @@ Ext.define('PBS.form.GroupFilter', { }, parseGroupFilter: function(filter) { - let [, type, input] = filter.match(/^(type|group|regex):(.*)$/); + let [, behavior, type, input] = filter.match(/^(?:(exclude|include):)?(type|group|regex):(.*)$/); + if (behavior === undefined) { + behavior = "include"; + } return { + behavior, type, input, }; @@ -86,13 +113,16 @@ Ext.define('PBS.form.GroupFilter', { onValueChange: function(field, values) { let me = this; - let grid = me.lookup('grid'); + let grid_include = me.lookup('grid-include'); + let grid_exclude = me.lookup('grid-exclude'); if (!values || values.length === 0) { - grid.getStore().removeAll(); + grid_include.getStore().removeAll(); + grid_exclude.getStore().removeAll(); return; } let records = values.map((filter) => me.parseGroupFilter(filter)); - grid.getStore().setData(records); + grid_include.getStore().setData(records); + grid_exclude.getStore().setData(records); }, setInputValue: function(widgets, rec) { @@ -162,9 +192,18 @@ Ext.define('PBS.form.GroupFilter', { let me = this; let filter = []; - me.lookup('grid').getStore().each((rec) => { - if (rec.data.type && rec.data.input) { - filter.push(`${rec.data.type}:${rec.data.input}`); + me.lookup('grid-include').getStore().each((rec) => { + if (rec.data.type && rec.data.input) { + filter.push(`${rec.data.type}:${rec.data.input}`); + } + }); + me.lookup('grid-exclude').getStore().each((rec) => { + if (rec.data.type && rec.data.input && rec.data.behavior) { + let behavior_string = ''; + if (rec.data.behavior === 'exclude') { + behavior_string = 'exclude:'; + } + filter.push(`${behavior_string}${rec.data.type}:${rec.data.input}`); } }); @@ -175,6 +214,9 @@ Ext.define('PBS.form.GroupFilter', { }, control: { + 'grid pbsGroupBehaviorSelector': { + change: 'onBehaviorChange', + }, 'grid pbsGroupFilterTypeSelector': { change: 'onTypeChange', }, @@ -264,72 +306,59 @@ Ext.define('PBS.form.GroupFilter', { items: [ { - xtype: 'grid', - reference: 'grid', + xtype: 'pbsGroupFilterGrid', + title: 'Include filters', margin: '0 0 5 0', - scrollable: true, - height: 300, + reference: 'grid-include', store: { - fields: ['type', 'input'], - }, + filters: [ + function(item) { + return item.data.behavior === "include"; + }, + ], + }, emptyText: gettext('Include all groups'), viewConfig: { deferEmptyText: false, }, - columns: [ + }, + { + xtype: 'container', + layout: { + type: 'hbox', + }, + items: [ { - text: gettext('Filter Type'), - xtype: 'widgetcolumn', - dataIndex: 'type', - flex: 1, - widget: { - xtype: 'pbsGroupFilterTypeSelector', - isFormField: false, - }, + xtype: 'button', + text: gettext('Add include'), + iconCls: 'fa fa-plus-circle', + handler: 'addIncludeFilter', }, { - text: gettext('Filter Value'), - xtype: 'widgetcolumn', + xtype: 'box', flex: 1, - onWidgetAttach: 'newInputColumn', - widget: { - padding: 0, - bodyPadding: 0, - xtype: 'fieldcontainer', - layout: 'fit', - defaults: { - margin: 0, - }, - items: [ - { - hidden: true, - xtype: 'pbsGroupTypeSelector', - isFormField: false, - }, - { - hidden: true, - xtype: 'textfield', - type: 'regex', - isFormField: false, - }, - { - hidden: true, - xtype: 'pbsGroupSelector', - isFormField: false, - }, - ], - }, }, { - xtype: 'widgetcolumn', - width: 40, - widget: { - xtype: 'button', - iconCls: 'fa fa-trash-o', - }, + xtype: 'box', + style: 'margin: 3px 0px;', + html: `${gettext('Note')}: ` + + gettext('Filters are additive'), }, ], }, + { + xtype: 'pbsGroupFilterGrid', + title: 'Exclude filters', + margin: '10 0 5 0', + reference: 'grid-exclude', + store: { + filters: [ + function(item) { + return item.data.behavior === "exclude"; + }, + ], + }, + }, { xtype: 'hiddenfield', reference: 'realfield', @@ -356,9 +385,9 @@ Ext.define('PBS.form.GroupFilter', { items: [ { xtype: 'button', - text: gettext('Add'), + text: gettext('Add exclude'), iconCls: 'fa fa-plus-circle', - handler: 'addFilter', + handler: 'addExcludeFilter', }, { xtype: 'box', @@ -368,7 +397,7 @@ Ext.define('PBS.form.GroupFilter', { xtype: 'box', style: 'margin: 3px 0px;', html: `${gettext('Note')}: ` - + gettext('Filters are additive (OR-like)'), + + gettext('Exclude filters will be applied after include filters'), }, ], }, @@ -384,6 +413,82 @@ Ext.define('PBS.form.GroupFilter', { }, }); +Ext.define('PBS.form.pbsGroupBehaviorSelector', { + extend: 'Proxmox.form.KVComboBox', + alias: 'widget.pbsGroupBehaviorSelector', + + allowBlank: false, + + comboItems: [ + ['include', gettext('Include')], + ['exclude', gettext('Exclude')], + ], +}); +Ext.define('PBS.form.GroupFilterGrid', { + extend: 'Ext.grid.Panel', + alias: 'widget.pbsGroupFilterGrid', + + scrollable: true, + height: 200, + store: { + fields: ['type', 'input'], + }, + columns: [ + { + text: gettext('Filter Type'), + xtype: 'widgetcolumn', + dataIndex: 'type', + flex: 1, + widget: { + xtype: 'pbsGroupFilterTypeSelector', + isFormField: false, + }, + }, + { + text: gettext('Filter Value'), + xtype: 'widgetcolumn', + flex: 1, + onWidgetAttach: 'newInputColumn', + widget: { + padding: 0, + bodyPadding: 0, + xtype: 'fieldcontainer', + layout: 'fit', + defaults: + { + margin: 0, + }, + items: [ + { + hidden: true, + xtype: 'pbsGroupTypeSelector', + isFormField: false, + }, + { + hidden: true, + xtype: 'textfield', + type: 'regex', + isFormField: false, + }, + { + hidden: true, + xtype: 'pbsGroupSelector', + isFormField: false, + }, + ], + }, + }, + { + xtype: 'widgetcolumn', + width: 30, + widget: { + xtype: 'button', + iconCls: 'fa fa-trash-o', + }, + }, + ], +}); + Ext.define('PBS.form.GroupFilterTypeSelector', { extend: 'Proxmox.form.KVComboBox', alias: 'widget.pbsGroupFilterTypeSelector', -- 2.39.2