* [pbs-devel] [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel
@ 2020-07-23 11:03 Dominik Csapak
2020-07-23 11:03 ` [pbs-devel] [PATCH proxmox-backup 2/3] ui: add search box to DataStore content Dominik Csapak
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Dominik Csapak @ 2020-07-23 11:03 UTC (permalink / raw)
To: pbs-devel
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
^ permalink raw reply [flat|nested] 4+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 2/3] ui: add search box to DataStore content
2020-07-23 11:03 [pbs-devel] [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel Dominik Csapak
@ 2020-07-23 11:03 ` 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
2 siblings, 0 replies; 4+ messages in thread
From: Dominik Csapak @ 2020-07-23 11:03 UTC (permalink / raw)
To: pbs-devel
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 <d.csapak@proxmox.com>
---
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
^ permalink raw reply [flat|nested] 4+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 3/3] ui: DataStoreContent: keep selection and expansion on reload
2020-07-23 11:03 [pbs-devel] [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel Dominik Csapak
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 ` Dominik Csapak
2020-07-27 10:57 ` [pbs-devel] applied: [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel Dietmar Maurer
2 siblings, 0 replies; 4+ messages in thread
From: Dominik Csapak @ 2020-07-23 11:03 UTC (permalink / raw)
To: pbs-devel
when clicking reload, we keep the existing selection
(if it still exists), and the previous expanded elements expanded
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
www/DataStoreContent.js | 42 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/www/DataStoreContent.js b/www/DataStoreContent.js
index e32c616..5f407e9 100644
--- a/www/DataStoreContent.js
+++ b/www/DataStoreContent.js
@@ -141,6 +141,30 @@ Ext.define('PBS.DataStoreContent', {
let groups = this.getRecordGroups(records);
+ let selected;
+ let expanded = {};
+
+ view.getSelection().some(function(item) {
+ let id = item.data.text;
+ if (item.data.leaf) {
+ id = item.parentNode.data.text + id;
+ }
+ selected = id;
+ return true;
+ });
+
+ view.getRootNode().cascadeBy({
+ before: item => {
+ if (item.isExpanded() && !item.data.leaf) {
+ let id = item.data.text;
+ expanded[id] = true;
+ return true;
+ }
+ return false;
+ },
+ after: () => {},
+ });
+
for (const item of records) {
let group = item.data["backup-type"] + "/" + item.data["backup-id"];
let children = groups[group].children;
@@ -152,6 +176,8 @@ Ext.define('PBS.DataStoreContent', {
data.cls = 'no-leaf-icons';
data.matchesFilter = true;
+ data.expanded = !!expanded[data.text];
+
data.children = [];
for (const file of data.files) {
file.text = file.filename,
@@ -166,7 +192,7 @@ Ext.define('PBS.DataStoreContent', {
}
let children = [];
- for (const [_key, group] of Object.entries(groups)) {
+ for (const [name, group] of Object.entries(groups)) {
let last_backup = 0;
let crypt = {
none: 0,
@@ -189,6 +215,7 @@ Ext.define('PBS.DataStoreContent', {
group.matchesFilter = true;
crypt.count = group.count;
group['crypt-mode'] = PBS.Utils.calculateCryptMode(crypt);
+ group.expanded = !!expanded[name];
children.push(group);
}
@@ -196,6 +223,19 @@ Ext.define('PBS.DataStoreContent', {
expanded: true,
children: children
});
+
+ if (selected !== undefined) {
+ let selection = view.getRootNode().findChildBy(function(item) {
+ let id = item.data.text;
+ if (item.data.leaf) {
+ id = item.parentNode.data.text + id;
+ }
+ return selected === id;
+ }, undefined, true);
+ view.setSelection(selection);
+ view.getView().focusRow(selection);
+ }
+
Proxmox.Utils.setErrorMask(view, false);
if (view.getStore().getFilters().length > 0) {
let searchBox = me.lookup("searchbox");
--
2.20.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [pbs-devel] applied: [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel
2020-07-23 11:03 [pbs-devel] [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel Dominik Csapak
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 ` Dietmar Maurer
2 siblings, 0 replies; 4+ messages in thread
From: Dietmar Maurer @ 2020-07-27 10:57 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak
applied all 3 patches
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2020-07-27 10:58 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-23 11:03 [pbs-devel] [PATCH proxmox-backup 1/3] ui: rework DataStore content Panel Dominik Csapak
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox