]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/window/FileBrowser.js
window/FileBrowser: add optional 'tar.zst' button
[proxmox-widget-toolkit.git] / src / window / FileBrowser.js
1 Ext.define('proxmox-file-tree', {
2 extend: 'Ext.data.Model',
3
4 fields: ['filepath', 'text', 'type', 'size',
5 {
6 name: 'mtime',
7 type: 'date',
8 dateFormat: 'timestamp',
9 },
10 {
11 name: 'iconCls',
12 calculate: function(data) {
13 let icon = 'file-o';
14 switch (data.type) {
15 case 'b': // block device
16 icon = 'cube';
17 break;
18 case 'c': // char device
19 icon = 'tty';
20 break;
21 case 'd':
22 icon = data.expanded ? 'folder-open-o' : 'folder-o';
23 break;
24 case 'f': //regular file
25 icon = 'file-text-o';
26 break;
27 case 'h': // hardlink
28 icon = 'file-o';
29 break;
30 case 'l': // softlink
31 icon = 'link';
32 break;
33 case 'p': // pipe/fifo
34 icon = 'exchange';
35 break;
36 case 's': // socket
37 icon = 'plug';
38 break;
39 case 'v': // virtual
40 icon = 'cube';
41 break;
42 default:
43 icon = 'file-o';
44 break;
45 }
46
47 return `fa fa-${icon}`;
48 },
49 },
50 ],
51 idProperty: 'filepath',
52 });
53
54 Ext.define("Proxmox.window.FileBrowser", {
55 extend: "Ext.window.Window",
56
57 width: 800,
58 height: 600,
59
60 modal: true,
61
62 config: {
63 // the base-URL to get the list of files. required.
64 listURL: '',
65
66 // the base download URL, e.g., something like '/api2/...'
67 downloadURL: '',
68
69 // extra parameters set as proxy paramns and for an actual download request
70 extraParams: {},
71
72 // the file types for which the download button should be enabled
73 downloadableFileTypes: {
74 'h': true, // hardlinks
75 'f': true, // "normal" files
76 'd': true, // directories
77 },
78
79 // set to true to show the tar download button
80 enableTar: false,
81 },
82
83 controller: {
84 xclass: 'Ext.app.ViewController',
85
86 buildUrl: function(baseurl, params) {
87 let url = new URL(baseurl, window.location.origin);
88 for (const [key, value] of Object.entries(params)) {
89 url.searchParams.append(key, value);
90 }
91
92 return url.href;
93 },
94
95 downloadTar: function() {
96 this.downloadFile(true);
97 },
98
99 downloadZip: function() {
100 this.downloadFile(false);
101 },
102
103 downloadFile: function(tar) {
104 let me = this;
105 let view = me.getView();
106 let tree = me.lookup('tree');
107 let selection = tree.getSelection();
108 if (!selection || selection.length < 1) return;
109
110 let data = selection[0].data;
111
112 let atag = document.createElement('a');
113
114 atag.download = data.text;
115 let params = { ...view.extraParams };
116 params.filepath = data.filepath;
117 atag.download = data.text;
118 if (data.type === 'd') {
119 if (tar) {
120 params.tar = 1;
121 atag.download += ".tar.zst";
122 } else {
123 atag.download += ".zip";
124 }
125 }
126 atag.href = me.buildUrl(view.downloadURL, params);
127 atag.click();
128 },
129
130 fileChanged: function() {
131 let me = this;
132 let view = me.getView();
133 let tree = me.lookup('tree');
134 let selection = tree.getSelection();
135 if (!selection || selection.length < 1) return;
136
137 let data = selection[0].data;
138 let canDownload = view.downloadURL && view.downloadableFileTypes[data.type];
139 let zipBtn = me.lookup('downloadBtn');
140 let tarBtn = me.lookup('downloadTar');
141 zipBtn.setDisabled(!canDownload);
142 tarBtn.setDisabled(!canDownload);
143 zipBtn.setText(data.type === 'd' ? gettext('Download .zip') : gettext('Download'));
144 tarBtn.setVisible(data.type === 'd' && view.enableTar);
145 },
146
147 errorHandler: function(error, msg) {
148 let me = this;
149 me.lookup('downloadBtn').setDisabled(true);
150 me.lookup('downloadTar').setDisabled(true);
151 if (me.initialLoadDone) {
152 Ext.Msg.alert(gettext('Error'), msg);
153 return true;
154 }
155 return false;
156 },
157
158 init: function(view) {
159 let me = this;
160 let tree = me.lookup('tree');
161
162 if (!view.listURL) {
163 throw "no list URL given";
164 }
165
166 let store = tree.getStore();
167 let proxy = store.getProxy();
168
169 let errorCallback = (error, msg) => me.errorHandler(error, msg);
170 Proxmox.Utils.monStoreErrors(tree, store, true, errorCallback);
171 proxy.setUrl(view.listURL);
172 proxy.setExtraParams(view.extraParams);
173 store.load((rec, op, success) => {
174 let root = store.getRoot();
175 root.expand(); // always expand invisible root node
176 if (view.archive === 'all') {
177 root.expandChildren(false);
178 } else if (view.archive) {
179 let child = root.findChild('text', view.archive);
180 if (child) {
181 child.expand();
182 setTimeout(function() {
183 tree.setSelection(child);
184 tree.getView().focusRow(child);
185 }, 10);
186 }
187 } else if (root.childNodes.length === 1) {
188 root.firstChild.expand();
189 }
190 me.initialLoadDone = success;
191 });
192 },
193
194 control: {
195 'treepanel': {
196 selectionchange: 'fileChanged',
197 },
198 },
199 },
200
201 layout: 'fit',
202 items: [
203 {
204 xtype: 'treepanel',
205 scrollable: true,
206 rootVisible: false,
207 reference: 'tree',
208 store: {
209 autoLoad: false,
210 model: 'proxmox-file-tree',
211 defaultRootId: '/',
212 nodeParam: 'filepath',
213 sorters: 'text',
214 proxy: {
215 appendId: false,
216 type: 'proxmox',
217 },
218 },
219
220 columns: [
221 {
222 text: gettext('Name'),
223 xtype: 'treecolumn',
224 flex: 1,
225 dataIndex: 'text',
226 renderer: Ext.String.htmlEncode,
227 },
228 {
229 text: gettext('Size'),
230 dataIndex: 'size',
231 renderer: value => value === undefined ? '' : Proxmox.Utils.format_size(value),
232 sorter: {
233 sorterFn: function(a, b) {
234 let asize = a.data.size || 0;
235 let bsize = b.data.size || 0;
236
237 return asize - bsize;
238 },
239 },
240 },
241 {
242 text: gettext('Modified'),
243 dataIndex: 'mtime',
244 minWidth: 200,
245 },
246 {
247 text: gettext('Type'),
248 dataIndex: 'type',
249 renderer: function(value) {
250 switch (value) {
251 case 'b': return gettext('Block Device');
252 case 'c': return gettext('Character Device');
253 case 'd': return gettext('Directory');
254 case 'f': return gettext('File');
255 case 'h': return gettext('Hardlink');
256 case 'l': return gettext('Softlink');
257 case 'p': return gettext('Pipe/Fifo');
258 case 's': return gettext('Socket');
259 case 'v': return gettext('Virtual');
260 default: return Proxmox.Utils.unknownText;
261 }
262 },
263 },
264 ],
265 },
266 ],
267
268 buttons: [
269 {
270 text: gettext('Download .tar.zst'),
271 handler: 'downloadTar',
272 reference: 'downloadTar',
273 hidden: true,
274 disabled: true,
275 },
276 {
277 text: gettext('Download .zip'),
278 handler: 'downloadZip',
279 reference: 'downloadBtn',
280 disabled: true,
281 },
282 ],
283 });