From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <s.reiter@proxmox.com>
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 CC56870111;
 Thu,  1 Apr 2021 17:34:55 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
 by firstgate.proxmox.com (Proxmox) with ESMTP id C7C5282A5;
 Thu,  1 Apr 2021 17:34:55 +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 7594A8284;
 Thu,  1 Apr 2021 17:34:54 +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 3D96645990;
 Thu,  1 Apr 2021 17:34:54 +0200 (CEST)
From: Stefan Reiter <s.reiter@proxmox.com>
To: pve-devel@lists.proxmox.com,
	pbs-devel@lists.proxmox.com
Date: Thu,  1 Apr 2021 17:34:42 +0200
Message-Id: <20210401153444.8711-2-s.reiter@proxmox.com>
X-Mailer: git-send-email 2.20.1
In-Reply-To: <20210401153444.8711-1-s.reiter@proxmox.com>
References: <20210401153444.8711-1-s.reiter@proxmox.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-SPAM-LEVEL: Spam detection results:  0
 AWL -0.012 Adjusted score from AWL reputation of From: address
 KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment
 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_PASS               -0.001 SPF: sender matches SPF record
 URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See
 http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more
 information. [atag.download]
Subject: [pbs-devel] [PATCH proxmox-widget-toolkit 1/3] window: add
 FileBrowser
X-BeenThere: pbs-devel@lists.proxmox.com
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Proxmox Backup Server development discussion
 <pbs-devel.lists.proxmox.com>
List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pbs-devel>, 
 <mailto:pbs-devel-request@lists.proxmox.com?subject=unsubscribe>
List-Archive: <http://lists.proxmox.com/pipermail/pbs-devel/>
List-Post: <mailto:pbs-devel@lists.proxmox.com>
List-Help: <mailto:pbs-devel-request@lists.proxmox.com?subject=help>
List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel>, 
 <mailto:pbs-devel-request@lists.proxmox.com?subject=subscribe>
X-List-Received-Date: Thu, 01 Apr 2021 15:34:55 -0000

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