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 33D7565750 for ; Thu, 23 Jul 2020 13:03:54 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 3189F286D5 for ; Thu, 23 Jul 2020 13:03:54 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (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 id CE957286C3 for ; Thu, 23 Jul 2020 13:03:52 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 928C54333A for ; Thu, 23 Jul 2020 13:03:52 +0200 (CEST) From: Dominik Csapak To: pbs-devel@lists.proxmox.com Date: Thu, 23 Jul 2020 13:03:50 +0200 Message-Id: <20200723110351.31882-2-d.csapak@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200723110351.31882-1-d.csapak@proxmox.com> References: <20200723110351.31882-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.001 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods NO_DNS_FOR_FROM 0.379 Envelope sender has no MX or A DNS records RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pbs-devel] [PATCH proxmox-backup 2/3] ui: add search box to DataStore content 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: Thu, 23 Jul 2020 11:03:54 -0000 which searches the whole tree (name & owner) we do this by traversing the tree and marking elements as matches, then afterwards make a simple filter that matches on a boolean worst case cost of this is O(2n) since we have to traverse the tree (in the worst) case one time, and the filter function does it again Signed-off-by: Dominik Csapak --- www/DataStoreContent.js | 108 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/www/DataStoreContent.js b/www/DataStoreContent.js index af5eca7..e32c616 100644 --- a/www/DataStoreContent.js +++ b/www/DataStoreContent.js @@ -35,7 +35,12 @@ Ext.define('pbs-data-store-snapshots', { return PBS.Utils.calculateCryptMode(crypt); } - } + }, + { + name: 'matchesFilter', + type: 'boolean', + defaultValue: true, + }, ] }); @@ -126,6 +131,7 @@ Ext.define('PBS.DataStoreContent', { }, onLoad: function(store, records, success, operation) { + let me = this; let view = this.getView(); if (!success) { @@ -144,12 +150,14 @@ Ext.define('PBS.DataStoreContent', { data.text = group + '/' + PBS.Utils.render_datetime_utc(data["backup-time"]); data.leaf = false; data.cls = 'no-leaf-icons'; + data.matchesFilter = true; data.children = []; for (const file of data.files) { file.text = file.filename, file['crypt-mode'] = PBS.Utils.cryptmap.indexOf(file['crypt-mode']); file.leaf = true; + file.matchesFilter = true; data.children.push(file); } @@ -178,6 +186,7 @@ Ext.define('PBS.DataStoreContent', { } group.count = group.children.length; + group.matchesFilter = true; crypt.count = group.count; group['crypt-mode'] = PBS.Utils.calculateCryptMode(crypt); children.push(group); @@ -188,6 +197,11 @@ Ext.define('PBS.DataStoreContent', { children: children }); Proxmox.Utils.setErrorMask(view, false); + if (view.getStore().getFilters().length > 0) { + let searchBox = me.lookup("searchbox"); + let searchvalue = searchBox.getValue();; + me.search(searchBox, searchvalue); + } }, onPrune: function(view, rI, cI, item, e, rec) { @@ -331,7 +345,71 @@ Ext.define('PBS.DataStoreContent', { 'backup-type': type, archive: rec.data.filename, }).show(); - } + }, + + filter: function(item, value) { + if (item.data.text.indexOf(value) !== -1) { + return true; + } + + if (item.data.owner && item.data.owner.indexOf(value) !== -1) { + return true; + } + + return false; + }, + + search: function(tf, value) { + let me = this; + let view = me.getView(); + let store = view.getStore(); + if (!value && value !== 0) { + store.clearFilter(); + store.getRoot().collapseChildren(true); + tf.triggers.clear.setVisible(false); + return; + } + tf.triggers.clear.setVisible(true); + if (value.length < 2) return; + Proxmox.Utils.setErrorMask(view, true); + // we do it a little bit later for the error mask to work + setTimeout(function() { + store.clearFilter(); + store.getRoot().collapseChildren(true); + + store.beginUpdate(); + store.getRoot().cascadeBy({ + before: function(item) { + if(me.filter(item, value)) { + item.set('matchesFilter', true); + if (item.parentNode && item.parentNode.id !== 'root') { + item.parentNode.childmatches = true; + } + return false; + } + return true; + }, + after: function(item) { + if (me.filter(item, value) || item.id === 'root' || item.childmatches) { + item.set('matchesFilter', true); + if (item.parentNode && item.parentNode.id !== 'root') { + item.parentNode.childmatches = true; + } + if (item.childmatches) { + item.expand(); + } + } else { + item.set('matchesFilter', false); + } + delete item.childmatches; + }, + }); + store.endUpdate(); + + store.filter((item) => !!item.get('matchesFilter')); + Proxmox.Utils.setErrorMask(view, false); + }, 10); + }, }, columns: [ @@ -448,5 +526,31 @@ Ext.define('PBS.DataStoreContent', { iconCls: 'fa fa-refresh', handler: 'reload', }, + '->', + { + xtype: 'tbtext', + html: gettext('Search'), + }, + { + xtype: 'textfield', + reference: 'searchbox', + triggers: { + clear: { + cls: 'pmx-clear-trigger', + weight: -1, + hidden: true, + handler: function() { + this.triggers.clear.setVisible(false); + this.setValue(''); + }, + } + }, + listeners: { + change: { + fn: 'search', + buffer: 500, + }, + }, + } ], }); -- 2.20.1