public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Dominik Csapak <d.csapak@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel
Date: Thu, 23 Jul 2020 13:03:49 +0200	[thread overview]
Message-ID: <20200723110351.31882-1-d.csapak@proxmox.com> (raw)

instead of having the files as a column, put the files into the tree
as a third level

with this, we can move the actions into an action column and remove
the top buttons (except reload)

clicking the download action now downloads directly, so we would
not need the download window anymore

clicking the browse action, opens the pxar browser like before,
but expands and selects (&focus) the selected pxar file

also changes the icon of 'signed' to the one to locked
but color codes them (singed => greyed out, encrypted => green),
similar to what browsers do/did for certificates

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/DataStoreContent.js   | 255 +++++++++++++++++++-------------------
 www/Utils.js              |   4 +-
 www/css/ext6-pbs.css      |  16 +++
 www/window/FileBrowser.js |  11 +-
 4 files changed, 156 insertions(+), 130 deletions(-)

diff --git a/www/DataStoreContent.js b/www/DataStoreContent.js
index a29436f..af5eca7 100644
--- a/www/DataStoreContent.js
+++ b/www/DataStoreContent.js
@@ -142,9 +142,18 @@ Ext.define('PBS.DataStoreContent', {
 		let data = item.data;
 
 		data.text = group + '/' + PBS.Utils.render_datetime_utc(data["backup-time"]);
-		data.leaf = true;
+		data.leaf = false;
 		data.cls = 'no-leaf-icons';
 
+		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;
+
+		    data.children.push(file);
+		}
+
 		children.push(data);
 	    }
 
@@ -181,13 +190,12 @@ Ext.define('PBS.DataStoreContent', {
 	    Proxmox.Utils.setErrorMask(view, false);
 	},
 
-	onPrune: function() {
+	onPrune: function(view, rI, cI, item, e, rec) {
 	    var view = this.getView();
 
-	    let rec = view.selModel.getSelection()[0];
 	    if (!(rec && rec.data)) return;
 	    let data = rec.data;
-	    if (data.leaf) return;
+	    if (rec.parentNode.id !== 'root') return;
 
 	    if (!view.datastore) return;
 
@@ -200,18 +208,17 @@ Ext.define('PBS.DataStoreContent', {
 	    win.show();
 	},
 
-	onVerify: function() {
+	onVerify: function(view, rI, cI, item, e, rec) {
 	    var view = this.getView();
 
 	    if (!view.datastore) return;
 
-	    let rec = view.selModel.getSelection()[0];
 	    if (!(rec && rec.data)) return;
 	    let data = rec.data;
 
 	    let params;
 
-	    if (data.leaf) {
+	    if (rec.parentNode.id !== 'root') {
 		params = {
 		    "backup-type": data["backup-type"],
 		    "backup-id": data["backup-id"],
@@ -239,75 +246,77 @@ Ext.define('PBS.DataStoreContent', {
 	    });
 	},
 
-	onForget: function() {
+	onForget: function(view, rI, cI, item, e, rec) {
+	    let me = this;
 	    var view = this.getView();
 
-	    let rec = view.selModel.getSelection()[0];
 	    if (!(rec && rec.data)) return;
 	    let data = rec.data;
-	    if (!data.leaf) return;
-
 	    if (!view.datastore) return;
 
-	    console.log(data);
+	    Ext.Msg.show({
+		title: gettext('Confirm'),
+		icon: Ext.Msg.WARNING,
+		message: Ext.String.format(gettext('Are you sure you want to remove snapshot {0}'), `'${data.text}'`),
+		buttons: Ext.Msg.YESNO,
+		defaultFocus: 'no',
+		callback: function(btn) {
+		    if (btn !== 'yes') {
+		        return;
+		    }
 
-	    Proxmox.Utils.API2Request({
-		params: {
-		    "backup-type": data["backup-type"],
-		    "backup-id": data["backup-id"],
-		    "backup-time": (data['backup-time'].getTime()/1000).toFixed(0),
-		},
-		url: `/admin/datastore/${view.datastore}/snapshots`,
-		method: 'DELETE',
-		waitMsgTarget: view,
-		failure: function(response, opts) {
-		    Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+		    Proxmox.Utils.API2Request({
+			params: {
+			    "backup-type": data["backup-type"],
+			    "backup-id": data["backup-id"],
+			    "backup-time": (data['backup-time'].getTime()/1000).toFixed(0),
+			},
+			url: `/admin/datastore/${view.datastore}/snapshots`,
+			method: 'DELETE',
+			waitMsgTarget: view,
+			failure: function(response, opts) {
+			    Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+			},
+			callback: me.reload.bind(me),
+		    });
 		},
-		callback: this.reload.bind(this),
 	    });
 	},
 
-	openBackupFileDownloader: function() {
+	downloadFile: function(tV, rI, cI, item, e, rec) {
 	    let me = this;
 	    let view = me.getView();
 
-	    let rec = view.selModel.getSelection()[0];
 	    if (!(rec && rec.data)) return;
-	    let data = rec.data;
-
-	    Ext.create('PBS.window.BackupFileDownloader', {
-		baseurl: `/api2/json/admin/datastore/${view.datastore}`,
-		params: {
-		    'backup-id': data['backup-id'],
-		    'backup-type': data['backup-type'],
-		    'backup-time': (data['backup-time'].getTime()/1000).toFixed(0),
-		},
-		files: data.files,
-	    }).show();
+	    let data = rec.parentNode.data;
+
+	    let file = rec.data.filename;
+	    let params = {
+		'backup-id': data['backup-id'],
+		'backup-type': data['backup-type'],
+		'backup-time': (data['backup-time'].getTime()/1000).toFixed(0),
+		'file-name': file,
+	    };
+
+	    let idx = file.lastIndexOf('.');
+	    let filename = file.slice(0, idx);
+	    let atag = document.createElement('a');
+	    params['file-name'] = file;
+	    atag.download = filename;
+	    let url = new URL(`/api2/json/admin/datastore/${view.datastore}/download-decoded`, window.location.origin);
+	    for (const [key, value] of Object.entries(params)) {
+		url.searchParams.append(key, value);
+	    }
+	    atag.href = url.href;
+	    atag.click();
 	},
 
-	openPxarBrowser: function() {
+	openPxarBrowser: function(tv, rI, Ci, item, e, rec) {
 	    let me = this;
 	    let view = me.getView();
 
-	    let rec = view.selModel.getSelection()[0];
 	    if (!(rec && rec.data)) return;
-	    let data = rec.data;
-
-	    let encrypted = false;
-	    data.files.forEach(file => {
-		if (file.filename === 'catalog.pcat1.didx' && file['crypt-mode'] === 'encrypt') {
-		    encrypted = true;
-		}
-	    });
-
-	    if (encrypted) {
-		Ext.Msg.alert(
-		    gettext('Cannot open Catalog'),
-		    gettext('Only unencrypted Backups can be opened on the server. Please use the client with the decryption key instead.'),
-		);
-		return;
-	    }
+	    let data = rec.parentNode.data;
 
 	    let id = data['backup-id'];
 	    let time = data['backup-time'];
@@ -320,6 +329,7 @@ Ext.define('PBS.DataStoreContent', {
 		'backup-id': id,
 		'backup-time': (time.getTime()/1000).toFixed(0),
 		'backup-type': type,
+		archive: rec.data.filename,
 	    }).show();
 	}
     },
@@ -331,6 +341,55 @@ Ext.define('PBS.DataStoreContent', {
 	    dataIndex: 'text',
 	    flex: 1
 	},
+	{
+	    header: gettext('Actions'),
+	    xtype: 'actioncolumn',
+	    dataIndex: 'text',
+	    items: [
+		{
+		    handler: 'onVerify',
+		    tooltip: gettext('Verify'),
+		    getClass: (v, m, rec) => rec.data.leaf ? 'pmx-hidden' : 'fa fa-search',
+		    isDisabled: (v, r, c, i, rec) => !!rec.data.leaf,
+		},
+		{
+		    handler: 'onPrune',
+		    tooltip: gettext('Prune'),
+		    getClass: (v, m, rec) => rec.parentNode.id ==='root' ? 'fa fa-scissors' : 'pmx-hidden',
+		    isDisabled: (v, r, c, i, rec) => rec.parentNode.id !=='root',
+		},
+		{
+		    handler: 'onForget',
+		    tooltip: gettext('Forget Snapshot'),
+		    getClass: (v, m, rec) => !rec.data.leaf && rec.parentNode.id !== 'root' ? 'fa critical fa-trash-o' : 'pmx-hidden',
+		    isDisabled: (v, r, c, i, rec) => rec.data.leaf || rec.parentNode.id === 'root',
+		},
+		{
+		    handler: 'downloadFile',
+		    tooltip: gettext('Download'),
+		    getClass: (v, m, rec) => rec.data.leaf && rec.data.filename ? 'fa fa-download' : 'pmx-hidden',
+		    isDisabled: (v, r, c, i, rec) => !rec.data.leaf || !rec.data.filename || rec.data['crypt-mode'] > 2,
+		},
+		{
+		    handler: 'openPxarBrowser',
+		    tooltip: gettext('Browse'),
+		    getClass: (v, m, rec) => {
+			let data = rec.data;
+			if (data.leaf && data.filename && data.filename.endsWith('pxar.didx')) {
+			    return 'fa fa-folder-open-o';
+			}
+			return 'pmx-hidden';
+		    },
+		    isDisabled: (v, r, c, i, rec) => {
+			let data = rec.data;
+			return !(data.leaf &&
+			    data.filename &&
+			    data.filename.endsWith('pxar.didx') &&
+			    data['crypt-mode'] < 2);
+		    }
+		},
+	    ]
+	},
 	{
 	    xtype: 'datecolumn',
 	    header: gettext('Backup Time'),
@@ -344,6 +403,9 @@ Ext.define('PBS.DataStoreContent', {
 	    sortable: true,
 	    dataIndex: 'size',
 	    renderer: (v, meta, record) => {
+		if (record.data.text === 'client.log.blob' && v === undefined) {
+		    return '';
+		}
 		if (v === undefined || v === null) {
 		    meta.tdCls = "x-grid-row-loading";
 		    return '';
@@ -366,28 +428,17 @@ Ext.define('PBS.DataStoreContent', {
 	{
 	    header: gettext('Encrypted'),
 	    dataIndex: 'crypt-mode',
-	    renderer: value => PBS.Utils.cryptText[value] || Proxmox.Utils.unknownText,
-	},
-	{
-	    header: gettext("Files"),
-	    sortable: false,
-	    dataIndex: 'files',
-	    renderer: function(files) {
-		return files.map((file) => {
-		    let icon = '';
-		    let size = '';
-		    let mode = PBS.Utils.cryptmap.indexOf(file['crypt-mode']);
-		    let iconCls = PBS.Utils.cryptIconCls[mode] || '';
-		    if (iconCls !== '') {
-			icon = `<i class="fa fa-${iconCls}"></i> `;
-		    }
-		    if (file.size)  {
-			size = ` (${Proxmox.Utils.format_size(file.size)})`;
-		    }
-		    return `${icon}${file.filename}${size}`;
-		}).join(', ');
-	    },
-	    flex: 2
+	    renderer: (v, meta, record) => {
+		if (v === -1) {
+		    return '';
+		}
+		let iconCls = PBS.Utils.cryptIconCls[v] || '';
+		let iconTxt = "";
+		if (iconCls) {
+		    iconTxt = `<i class="fa fa-fw fa-${iconCls}"></i> `;
+		}
+		return (iconTxt + PBS.Utils.cryptText[v]) || Proxmox.Utils.unknownText
+	    }
 	},
     ],
 
@@ -397,55 +448,5 @@ Ext.define('PBS.DataStoreContent', {
 	    iconCls: 'fa fa-refresh',
 	    handler: 'reload',
 	},
-	'-',
-	{
-	    xtype: 'proxmoxButton',
-	    text: gettext('Verify'),
-	    disabled: true,
-	    parentXType: 'pbsDataStoreContent',
-	    enableFn: (rec) => !!rec.data && rec.data.size !== null,
-	    handler: 'onVerify',
-	},
-	{
-	    xtype: 'proxmoxButton',
-	    text: gettext('Prune'),
-	    disabled: true,
-	    parentXType: 'pbsDataStoreContent',
-	    enableFn: (rec) => !rec.data.leaf,
-	    handler: 'onPrune',
-	},
-	{
-	    xtype: 'proxmoxButton',
-	    text: gettext('Forget'),
-	    disabled: true,
-	    parentXType: 'pbsDataStoreContent',
-	    handler: 'onForget',
-	    dangerous: true,
-	    confirmMsg: function(record) {
-		//console.log(record);
-		let name = record.data.text;
-		return Ext.String.format(gettext('Are you sure you want to remove snapshot {0}'), `'${name}'`);
-	    },
-	    enableFn: (rec) => !!rec.data.leaf && rec.data.size !== null,
-	},
-	'-',
-	{
-	    xtype: 'proxmoxButton',
-	    text: gettext('Download Files'),
-	    disabled: true,
-	    parentXType: 'pbsDataStoreContent',
-	    handler: 'openBackupFileDownloader',
-	    enableFn: (rec) => !!rec.data.leaf && rec.data.size !== null,
-	},
-	{
-	    xtype: "proxmoxButton",
-	    text: gettext('PXAR File Browser'),
-	    disabled: true,
-	    handler: 'openPxarBrowser',
-	    parentXType: 'pbsDataStoreContent',
-	    enableFn: function(record) {
-		return !!record.data.leaf && record.size !== null && record.data.files.some(el => el.filename.endsWith('pxar.didx'));
-	    },
-	}
     ],
 });
diff --git a/www/Utils.js b/www/Utils.js
index c75a779..dfd10cf 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -30,8 +30,8 @@ Ext.define('PBS.Utils', {
     cryptIconCls: [
 	'',
 	'',
-	'certificate',
-	'lock',
+	'lock faded',
+	'lock good',
     ],
 
     calculateCryptMode: function(data) {
diff --git a/www/css/ext6-pbs.css b/www/css/ext6-pbs.css
index 9a073d3..80a3620 100644
--- a/www/css/ext6-pbs.css
+++ b/www/css/ext6-pbs.css
@@ -208,3 +208,19 @@ p.logs {
 .pmx-button-badge.active {
     background-color: #464d4d;
 }
+
+.pmx-hidden {
+    cursor: default;
+}
+
+.x-action-col-icon.good:before {
+    color: #21BF4B;
+}
+
+.x-action-col-icon.warning:before {
+    color: #fc0;
+}
+
+.x-action-col-icon.critical:before {
+    color: #FF6C59;
+}
diff --git a/www/window/FileBrowser.js b/www/window/FileBrowser.js
index fb1c6b9..ee29fdd 100644
--- a/www/window/FileBrowser.js
+++ b/www/window/FileBrowser.js
@@ -145,7 +145,16 @@ Ext.define("PBS.window.FileBrowser", {
 	    store.load(() => {
 		let root = store.getRoot();
 		root.expand(); // always expand invisible root node
-		if (root.childNodes.length === 1) {
+		if (view.archive) {
+		    let child = root.findChild('text', view.archive);
+		    if (child) {
+			child.expand();
+			setTimeout(function() {
+			    tree.setSelection(child);
+			    tree.getView().focusRow(child);
+			}, 10);
+		    }
+		} else if (root.childNodes.length === 1) {
 		    root.firstChild.expand();
 		}
 	    });
-- 
2.20.1





             reply	other threads:[~2020-07-23 11:03 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-07-23 11:03 Dominik Csapak [this message]
2020-07-23 11:03 ` [pbs-devel] [PATCH proxmox-backup 2/3] ui: add search box to DataStore content Dominik Csapak
2020-07-23 11:03 ` [pbs-devel] [PATCH proxmox-backup 3/3] ui: DataStoreContent: keep selection and expansion on reload Dominik Csapak
2020-07-27 10:57 ` [pbs-devel] applied: [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel Dietmar Maurer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200723110351.31882-1-d.csapak@proxmox.com \
    --to=d.csapak@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal