From: Stefan Reiter <s.reiter@proxmox.com>
To: pve-devel@lists.proxmox.com, pbs-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox-widget-toolkit 1/3] window: add FileBrowser
Date: Thu, 1 Apr 2021 17:34:42 +0200 [thread overview]
Message-ID: <20210401153444.8711-2-s.reiter@proxmox.com> (raw)
In-Reply-To: <20210401153444.8711-1-s.reiter@proxmox.com>
from proxmox-backup, only names changed
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
src/Makefile | 1 +
src/window/FileBrowser.js | 248 ++++++++++++++++++++++++++++++++++++++
2 files changed, 249 insertions(+)
create mode 100644 src/window/FileBrowser.js
diff --git a/src/Makefile b/src/Makefile
index 44c11ea..f97c74a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -64,6 +64,7 @@ JSSRC= \
window/ACMEAccount.js \
window/ACMEPluginEdit.js \
window/ACMEDomains.js \
+ window/FileBrowser.js \
node/APT.js \
node/NetworkEdit.js \
node/NetworkView.js \
diff --git a/src/window/FileBrowser.js b/src/window/FileBrowser.js
new file mode 100644
index 0000000..82fd1b2
--- /dev/null
+++ b/src/window/FileBrowser.js
@@ -0,0 +1,248 @@
+Ext.define('proxmox-file-tree', {
+ extend: 'Ext.data.Model',
+
+ fields: ['filepath', 'text', 'type', 'size',
+ {
+ name: 'mtime',
+ type: 'date',
+ dateFormat: 'timestamp',
+ },
+ {
+ name: 'iconCls',
+ calculate: function(data) {
+ let icon = 'file-o';
+ switch (data.type) {
+ case 'b': // block device
+ icon = 'cube';
+ break;
+ case 'c': // char device
+ icon = 'tty';
+ break;
+ case 'd':
+ icon = data.expanded ? 'folder-open-o' : 'folder-o';
+ break;
+ case 'f': //regular file
+ icon = 'file-text-o';
+ break;
+ case 'h': // hardlink
+ icon = 'file-o';
+ break;
+ case 'l': // softlink
+ icon = 'link';
+ break;
+ case 'p': // pipe/fifo
+ icon = 'exchange';
+ break;
+ case 's': // socket
+ icon = 'plug';
+ break;
+ default:
+ icon = 'file-o';
+ break;
+ }
+
+ return `fa fa-${icon}`;
+ },
+ },
+ ],
+ idProperty: 'filepath',
+});
+
+Ext.define("Proxmox.window.FileBrowser", {
+ extend: "Ext.window.Window",
+
+ width: 800,
+ height: 600,
+
+ modal: true,
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ buildUrl: function(baseurl, params) {
+ let url = new URL(baseurl, window.location.origin);
+ for (const [key, value] of Object.entries(params)) {
+ url.searchParams.append(key, value);
+ }
+
+ return url.href;
+ },
+
+ downloadFile: function() {
+ let me = this;
+ let view = me.getView();
+ let tree = me.lookup('tree');
+ let selection = tree.getSelection();
+ if (!selection || selection.length < 1) return;
+
+ let data = selection[0].data;
+
+ let atag = document.createElement('a');
+
+ atag.download = data.text;
+ let params = {
+ 'backup-id': view['backup-id'],
+ 'backup-type': view['backup-type'],
+ 'backup-time': view['backup-time'],
+ };
+ params.filepath = data.filepath;
+ atag.download = data.text;
+ if (data.type === 'd') {
+ atag.download += ".zip";
+ }
+ atag.href = me
+ .buildUrl(`/api2/json/admin/datastore/${view.datastore}/pxar-file-download`, params);
+ atag.click();
+ },
+
+ fileChanged: function() {
+ let me = this;
+ let tree = me.lookup('tree');
+ let selection = tree.getSelection();
+ if (!selection || selection.length < 1) return;
+
+ let data = selection[0].data;
+
+ let canDownload = false;
+ switch (data.type) {
+ case 'h':
+ case 'f':
+ canDownload = true;
+ break;
+ case 'd':
+ if (data.depth > 1) {
+ canDownload = true;
+ }
+ break;
+ default: break;
+ }
+
+ me.lookup('downloadBtn').setDisabled(!canDownload);
+ },
+
+ init: function(view) {
+ let me = this;
+ let tree = me.lookup('tree');
+
+ if (!view['backup-id']) {
+ throw "no backup-id given";
+ }
+
+ if (!view['backup-type']) {
+ throw "no backup-id given";
+ }
+
+ if (!view['backup-time']) {
+ throw "no backup-id given";
+ }
+
+ let store = tree.getStore();
+ let proxy = store.getProxy();
+
+ Proxmox.Utils.monStoreErrors(tree, store, true);
+ proxy.setUrl(`/api2/json/admin/datastore/${view.datastore}/catalog`);
+ proxy.setExtraParams({
+ 'backup-id': view['backup-id'],
+ 'backup-type': view['backup-type'],
+ 'backup-time': view['backup-time'],
+ });
+ store.load(() => {
+ let root = store.getRoot();
+ root.expand(); // always expand invisible root node
+ 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();
+ }
+ });
+ },
+
+ control: {
+ 'treepanel': {
+ selectionchange: 'fileChanged',
+ },
+ },
+ },
+
+ layout: 'fit',
+ items: [
+ {
+ xtype: 'treepanel',
+ scrollable: true,
+ rootVisible: false,
+ reference: 'tree',
+ store: {
+ autoLoad: false,
+ model: 'proxmox-file-tree',
+ defaultRootId: '/',
+ nodeParam: 'filepath',
+ sorters: 'text',
+ proxy: {
+ appendId: false,
+ type: 'proxmox',
+ },
+ },
+
+ columns: [
+ {
+ text: gettext('Name'),
+ xtype: 'treecolumn',
+ flex: 1,
+ dataIndex: 'text',
+ renderer: Ext.String.htmlEncode,
+ },
+ {
+ text: gettext('Size'),
+ dataIndex: 'size',
+ renderer: value => value === undefined ? '' : Proxmox.Utils.format_size(value),
+ sorter: {
+ sorterFn: function(a, b) {
+ let asize = a.data.size || 0;
+ let bsize = b.data.size || 0;
+
+ return asize - bsize;
+ },
+ },
+ },
+ {
+ text: gettext('Modified'),
+ dataIndex: 'mtime',
+ minWidth: 200,
+ },
+ {
+ text: gettext('Type'),
+ dataIndex: 'type',
+ renderer: function(value) {
+ switch (value) {
+ case 'b': return gettext('Block Device');
+ case 'c': return gettext('Character Device');
+ case 'd': return gettext('Directory');
+ case 'f': return gettext('File');
+ case 'h': return gettext('Hardlink');
+ case 'l': return gettext('Softlink');
+ case 'p': return gettext('Pipe/Fifo');
+ case 's': return gettext('Socket');
+ default: return Proxmox.Utils.unknownText;
+ }
+ },
+ },
+ ],
+ },
+ ],
+
+ buttons: [
+ {
+ text: gettext('Download'),
+ handler: 'downloadFile',
+ reference: 'downloadBtn',
+ disabled: true,
+ },
+ ],
+});
--
2.20.1
next prev parent reply other threads:[~2021-04-01 15:34 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-04-01 15:34 [pve-devel] [PATCH 0/3] Move FileBrowser to widget-toolkit Stefan Reiter
2021-04-01 15:34 ` Stefan Reiter [this message]
2021-04-13 6:00 ` [pve-devel] applied: [pbs-devel] [PATCH proxmox-widget-toolkit 1/3] window: add FileBrowser Thomas Lamprecht
2021-04-01 15:34 ` [pve-devel] [PATCH proxmox-widget-toolkit 2/3] FileBrowser: remove PBS dependencies Stefan Reiter
2021-04-13 6:00 ` [pve-devel] applied: [pbs-devel] " Thomas Lamprecht
2021-04-01 15:34 ` [pve-devel] [PATCH proxmox-backup 3/3] use FileBrowser from proxmox-widget-toolkit Stefan Reiter
2021-04-13 7:15 ` [pve-devel] applied: [pbs-devel] " Thomas Lamprecht
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=20210401153444.8711-2-s.reiter@proxmox.com \
--to=s.reiter@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
--cc=pve-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