]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/window/FileBrowser.js
FileBrowser: show errors in messagebox and allow expand 'all'
[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
80 controller: {
81 xclass: 'Ext.app.ViewController',
82
83 buildUrl: function(baseurl, params) {
84 let url = new URL(baseurl, window.location.origin);
85 for (const [key, value] of Object.entries(params)) {
86 url.searchParams.append(key, value);
87 }
88
89 return url.href;
90 },
91
92 downloadFile: function() {
93 let me = this;
94 let view = me.getView();
95 let tree = me.lookup('tree');
96 let selection = tree.getSelection();
97 if (!selection || selection.length < 1) return;
98
99 let data = selection[0].data;
100
101 let atag = document.createElement('a');
102
103 atag.download = data.text;
104 let params = { ...view.extraParams };
105 params.filepath = data.filepath;
106 atag.download = data.text;
107 if (data.type === 'd') {
108 atag.download += ".zip";
109 }
110 atag.href = me.buildUrl(view.downloadURL, params);
111 atag.click();
112 },
113
114 fileChanged: function() {
115 let me = this;
116 let view = me.getView();
117 let tree = me.lookup('tree');
118 let selection = tree.getSelection();
119 if (!selection || selection.length < 1) return;
120
121 let data = selection[0].data;
122 let canDownload = view.downloadURL && view.downloadableFileTypes[data.type];
123 me.lookup('downloadBtn').setDisabled(!canDownload);
124 },
125
126 errorHandler: function(error, msg) {
127 let me = this;
128 me.lookup('downloadBtn').setDisabled(true);
129 if (me.initialLoadDone) {
130 Ext.Msg.alert(gettext('Error'), msg);
131 return true;
132 }
133 return false;
134 },
135
136 init: function(view) {
137 let me = this;
138 let tree = me.lookup('tree');
139
140 if (!view.listURL) {
141 throw "no list URL given";
142 }
143
144 let store = tree.getStore();
145 let proxy = store.getProxy();
146
147 let errorCallback = (error, msg) => me.errorHandler(error, msg);
148 Proxmox.Utils.monStoreErrors(tree, store, true, errorCallback);
149 proxy.setUrl(view.listURL);
150 proxy.setExtraParams(view.extraParams);
151 store.load((rec, op, success) => {
152 let root = store.getRoot();
153 root.expand(); // always expand invisible root node
154 if (view.archive === 'all') {
155 root.expandChildren(false);
156 } else if (view.archive) {
157 let child = root.findChild('text', view.archive);
158 if (child) {
159 child.expand();
160 setTimeout(function() {
161 tree.setSelection(child);
162 tree.getView().focusRow(child);
163 }, 10);
164 }
165 } else if (root.childNodes.length === 1) {
166 root.firstChild.expand();
167 }
168 me.initialLoadDone = success;
169 });
170 },
171
172 control: {
173 'treepanel': {
174 selectionchange: 'fileChanged',
175 },
176 },
177 },
178
179 layout: 'fit',
180 items: [
181 {
182 xtype: 'treepanel',
183 scrollable: true,
184 rootVisible: false,
185 reference: 'tree',
186 store: {
187 autoLoad: false,
188 model: 'proxmox-file-tree',
189 defaultRootId: '/',
190 nodeParam: 'filepath',
191 sorters: 'text',
192 proxy: {
193 appendId: false,
194 type: 'proxmox',
195 },
196 },
197
198 columns: [
199 {
200 text: gettext('Name'),
201 xtype: 'treecolumn',
202 flex: 1,
203 dataIndex: 'text',
204 renderer: Ext.String.htmlEncode,
205 },
206 {
207 text: gettext('Size'),
208 dataIndex: 'size',
209 renderer: value => value === undefined ? '' : Proxmox.Utils.format_size(value),
210 sorter: {
211 sorterFn: function(a, b) {
212 let asize = a.data.size || 0;
213 let bsize = b.data.size || 0;
214
215 return asize - bsize;
216 },
217 },
218 },
219 {
220 text: gettext('Modified'),
221 dataIndex: 'mtime',
222 minWidth: 200,
223 },
224 {
225 text: gettext('Type'),
226 dataIndex: 'type',
227 renderer: function(value) {
228 switch (value) {
229 case 'b': return gettext('Block Device');
230 case 'c': return gettext('Character Device');
231 case 'd': return gettext('Directory');
232 case 'f': return gettext('File');
233 case 'h': return gettext('Hardlink');
234 case 'l': return gettext('Softlink');
235 case 'p': return gettext('Pipe/Fifo');
236 case 's': return gettext('Socket');
237 case 'v': return gettext('Virtual');
238 default: return Proxmox.Utils.unknownText;
239 }
240 },
241 },
242 ],
243 },
244 ],
245
246 buttons: [
247 {
248 text: gettext('Download'),
249 handler: 'downloadFile',
250 reference: 'downloadBtn',
251 disabled: true,
252 },
253 ],
254 });