diff --git a/www/DataStoreContent.js b/www/DataStoreContent.js index a29436fc..af5eca7d 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), + 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), + }); }, - url: `/admin/datastore/${view.datastore}/snapshots`, - method: 'DELETE', - waitMsgTarget: view, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - }, - 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; + let data = rec.parentNode.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 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, + }; - openPxarBrowser: function() { - 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 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(tv, rI, Ci, item, e, rec) { + let me = this; + let view = me.getView(); + + if (!(rec && rec.data)) 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 = ` `; - } - 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 = ` `; + } + 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 c75a7792..dfd10cf8 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 9a073d36..80a36208 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 fb1c6b9f..ee29fdd8 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(); } });