]>
Commit | Line | Data |
---|---|---|
09195cb6 SR |
1 | Ext.define('proxmox-file-tree', { |
2 | extend: 'Ext.data.Model', | |
3 | ||
427685c6 TL |
4 | fields: [ |
5 | 'filepath', 'text', 'type', 'size', | |
f5be46bc SS |
6 | { |
7 | name: 'sizedisplay', | |
8 | calculate: data => { | |
9 | if (data.size === undefined) { | |
10 | return ''; | |
11 | } else if (data.type === 'd') { | |
12 | let fs = data.size === 1 ? gettext('{0} item') : gettext('{0} items'); | |
13 | return Ext.String.format(fs, data.size); | |
14 | } | |
15 | ||
16 | return Proxmox.Utils.format_size(data.size); | |
17 | }, | |
18 | }, | |
09195cb6 SR |
19 | { |
20 | name: 'mtime', | |
21 | type: 'date', | |
22 | dateFormat: 'timestamp', | |
23 | }, | |
24 | { | |
25 | name: 'iconCls', | |
26 | calculate: function(data) { | |
427685c6 TL |
27 | let icon = Proxmox.Schema.pxarFileTypes[data.type]?.icon ?? 'file-o'; |
28 | if (data.expanded && data.type === 'd') { | |
29 | icon = 'folder-open-o'; | |
09195cb6 | 30 | } |
09195cb6 SR |
31 | return `fa fa-${icon}`; |
32 | }, | |
33 | }, | |
34 | ], | |
35 | idProperty: 'filepath', | |
36 | }); | |
37 | ||
38 | Ext.define("Proxmox.window.FileBrowser", { | |
39 | extend: "Ext.window.Window", | |
40 | ||
41 | width: 800, | |
42 | height: 600, | |
43 | ||
44 | modal: true, | |
45 | ||
fd8ed0d8 TL |
46 | config: { |
47 | // the base-URL to get the list of files. required. | |
48 | listURL: '', | |
49 | ||
50 | // the base download URL, e.g., something like '/api2/...' | |
51 | downloadURL: '', | |
52 | ||
53 | // extra parameters set as proxy paramns and for an actual download request | |
54 | extraParams: {}, | |
55 | ||
56 | // the file types for which the download button should be enabled | |
57 | downloadableFileTypes: { | |
58 | 'h': true, // hardlinks | |
59 | 'f': true, // "normal" files | |
60 | 'd': true, // directories | |
61 | }, | |
3b974606 | 62 | |
a3faf027 SS |
63 | // enable tar download, this will add a menu to the |
64 | // "Download" button when the selection can be downloaded as | |
65 | // .tar files | |
3b974606 | 66 | enableTar: false, |
614b3cd4 SS |
67 | |
68 | // prefix to prepend to downloaded file names | |
69 | downloadPrefix: '', | |
fd8ed0d8 TL |
70 | }, |
71 | ||
09195cb6 SR |
72 | controller: { |
73 | xclass: 'Ext.app.ViewController', | |
74 | ||
75 | buildUrl: function(baseurl, params) { | |
76 | let url = new URL(baseurl, window.location.origin); | |
77 | for (const [key, value] of Object.entries(params)) { | |
78 | url.searchParams.append(key, value); | |
79 | } | |
80 | ||
81 | return url.href; | |
82 | }, | |
83 | ||
3b974606 DC |
84 | downloadTar: function() { |
85 | this.downloadFile(true); | |
86 | }, | |
87 | ||
88 | downloadZip: function() { | |
89 | this.downloadFile(false); | |
90 | }, | |
91 | ||
92 | downloadFile: function(tar) { | |
09195cb6 SR |
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 | ||
68b29ade | 101 | let params = { ...view.extraParams }; |
09195cb6 | 102 | params.filepath = data.filepath; |
614b3cd4 SS |
103 | |
104 | let atag = document.createElement('a'); | |
105 | atag.download = view.downloadPrefix + data.text; | |
09195cb6 | 106 | if (data.type === 'd') { |
3b974606 DC |
107 | if (tar) { |
108 | params.tar = 1; | |
109 | atag.download += ".tar.zst"; | |
110 | } else { | |
111 | atag.download += ".zip"; | |
112 | } | |
09195cb6 | 113 | } |
fd8ed0d8 | 114 | atag.href = me.buildUrl(view.downloadURL, params); |
09195cb6 SR |
115 | atag.click(); |
116 | }, | |
117 | ||
118 | fileChanged: function() { | |
119 | let me = this; | |
68b29ade | 120 | let view = me.getView(); |
09195cb6 SR |
121 | let tree = me.lookup('tree'); |
122 | let selection = tree.getSelection(); | |
123 | if (!selection || selection.length < 1) return; | |
124 | ||
125 | let data = selection[0].data; | |
a3faf027 SS |
126 | let st = Ext.String.format(gettext('Selected "{0}"'), atob(data.filepath)); |
127 | view.lookup('selectText').setText(st); | |
128 | ||
fd8ed0d8 | 129 | let canDownload = view.downloadURL && view.downloadableFileTypes[data.type]; |
a3faf027 SS |
130 | let enableMenu = view.enableTar && data.type === 'd'; |
131 | ||
132 | let downloadBtn = view.lookup('downloadBtn'); | |
133 | downloadBtn.setDisabled(!canDownload || enableMenu); | |
134 | downloadBtn.setHidden(!canDownload || enableMenu); | |
135 | ||
136 | let menuBtn = view.lookup('menuBtn'); | |
137 | menuBtn.setDisabled(!canDownload || !enableMenu); | |
138 | menuBtn.setHidden(!canDownload || !enableMenu); | |
09195cb6 SR |
139 | }, |
140 | ||
6f9f9c71 SR |
141 | errorHandler: function(error, msg) { |
142 | let me = this; | |
a69f2358 DC |
143 | if (error?.status === 503) { |
144 | return false; | |
145 | } | |
6f9f9c71 | 146 | me.lookup('downloadBtn').setDisabled(true); |
a3faf027 | 147 | me.lookup('menuBtn').setDisabled(true); |
6f9f9c71 SR |
148 | if (me.initialLoadDone) { |
149 | Ext.Msg.alert(gettext('Error'), msg); | |
150 | return true; | |
151 | } | |
152 | return false; | |
153 | }, | |
154 | ||
09195cb6 SR |
155 | init: function(view) { |
156 | let me = this; | |
157 | let tree = me.lookup('tree'); | |
158 | ||
fd8ed0d8 | 159 | if (!view.listURL) { |
68b29ade | 160 | throw "no list URL given"; |
09195cb6 SR |
161 | } |
162 | ||
163 | let store = tree.getStore(); | |
164 | let proxy = store.getProxy(); | |
165 | ||
6f9f9c71 | 166 | let errorCallback = (error, msg) => me.errorHandler(error, msg); |
fd8ed0d8 | 167 | proxy.setUrl(view.listURL); |
a69f2358 | 168 | proxy.setTimeout(60*1000); |
68b29ade | 169 | proxy.setExtraParams(view.extraParams); |
a69f2358 DC |
170 | |
171 | tree.mon(store, 'beforeload', () => { | |
172 | Proxmox.Utils.setErrorMask(tree, true); | |
173 | }); | |
174 | tree.mon(store, 'load', (treestore, rec, success, operation, node) => { | |
175 | if (success) { | |
176 | Proxmox.Utils.setErrorMask(tree, false); | |
177 | return; | |
178 | } | |
179 | if (!node.loadCount) { | |
180 | node.loadCount = 0; // ensure its numeric | |
181 | } | |
182 | // trigger a reload if we got a 503 answer from the proxy | |
183 | if (operation?.error?.status === 503 && node.loadCount < 10) { | |
184 | node.collapse(); | |
185 | node.expand(); | |
186 | node.loadCount++; | |
187 | return; | |
188 | } | |
189 | ||
190 | let error = operation.getError(); | |
191 | let msg = Proxmox.Utils.getResponseErrorMessage(error); | |
192 | if (!errorCallback(error, msg)) { | |
193 | Proxmox.Utils.setErrorMask(tree, msg); | |
194 | } else { | |
195 | Proxmox.Utils.setErrorMask(tree, false); | |
196 | } | |
197 | }); | |
6f9f9c71 | 198 | store.load((rec, op, success) => { |
09195cb6 SR |
199 | let root = store.getRoot(); |
200 | root.expand(); // always expand invisible root node | |
6f9f9c71 SR |
201 | if (view.archive === 'all') { |
202 | root.expandChildren(false); | |
203 | } else if (view.archive) { | |
09195cb6 SR |
204 | let child = root.findChild('text', view.archive); |
205 | if (child) { | |
206 | child.expand(); | |
207 | setTimeout(function() { | |
208 | tree.setSelection(child); | |
209 | tree.getView().focusRow(child); | |
210 | }, 10); | |
211 | } | |
212 | } else if (root.childNodes.length === 1) { | |
213 | root.firstChild.expand(); | |
214 | } | |
6f9f9c71 | 215 | me.initialLoadDone = success; |
09195cb6 SR |
216 | }); |
217 | }, | |
218 | ||
219 | control: { | |
220 | 'treepanel': { | |
221 | selectionchange: 'fileChanged', | |
222 | }, | |
223 | }, | |
224 | }, | |
225 | ||
226 | layout: 'fit', | |
227 | items: [ | |
228 | { | |
229 | xtype: 'treepanel', | |
230 | scrollable: true, | |
231 | rootVisible: false, | |
232 | reference: 'tree', | |
233 | store: { | |
234 | autoLoad: false, | |
235 | model: 'proxmox-file-tree', | |
236 | defaultRootId: '/', | |
237 | nodeParam: 'filepath', | |
238 | sorters: 'text', | |
239 | proxy: { | |
240 | appendId: false, | |
241 | type: 'proxmox', | |
242 | }, | |
243 | }, | |
244 | ||
a69f2358 DC |
245 | viewConfig: { |
246 | loadMask: false, | |
247 | }, | |
248 | ||
09195cb6 SR |
249 | columns: [ |
250 | { | |
251 | text: gettext('Name'), | |
252 | xtype: 'treecolumn', | |
253 | flex: 1, | |
254 | dataIndex: 'text', | |
255 | renderer: Ext.String.htmlEncode, | |
256 | }, | |
257 | { | |
258 | text: gettext('Size'), | |
f5be46bc | 259 | dataIndex: 'sizedisplay', |
b33c1d60 | 260 | align: 'end', |
09195cb6 SR |
261 | sorter: { |
262 | sorterFn: function(a, b) { | |
f5be46bc SS |
263 | if (a.data.type === 'd' && b.data.type !== 'd') { |
264 | return -1; | |
265 | } else if (a.data.type !== 'd' && b.data.type === 'd') { | |
266 | return 1; | |
267 | } | |
268 | ||
09195cb6 SR |
269 | let asize = a.data.size || 0; |
270 | let bsize = b.data.size || 0; | |
271 | ||
272 | return asize - bsize; | |
273 | }, | |
274 | }, | |
275 | }, | |
276 | { | |
277 | text: gettext('Modified'), | |
278 | dataIndex: 'mtime', | |
279 | minWidth: 200, | |
280 | }, | |
281 | { | |
282 | text: gettext('Type'), | |
283 | dataIndex: 'type', | |
427685c6 | 284 | renderer: (v) => Proxmox.Schema.pxarFileTypes[v]?.label ?? Proxmox.Utils.unknownText, |
09195cb6 SR |
285 | }, |
286 | ], | |
287 | }, | |
288 | ], | |
289 | ||
a3faf027 | 290 | fbar: [ |
09195cb6 | 291 | { |
a3faf027 SS |
292 | text: '', |
293 | xtype: 'label', | |
294 | reference: 'selectText', | |
3b974606 DC |
295 | }, |
296 | { | |
a3faf027 SS |
297 | text: gettext('Download'), |
298 | xtype: 'button', | |
3b974606 | 299 | handler: 'downloadZip', |
09195cb6 SR |
300 | reference: 'downloadBtn', |
301 | disabled: true, | |
a3faf027 SS |
302 | hidden: true, |
303 | }, | |
304 | { | |
305 | text: gettext('Download as'), | |
306 | xtype: 'button', | |
307 | reference: 'menuBtn', | |
308 | menu: { | |
309 | items: [ | |
310 | { | |
311 | iconCls: 'fa fa-fw fa-file-zip-o', | |
312 | text: gettext('.zip'), | |
313 | handler: 'downloadZip', | |
314 | reference: 'downloadZip', | |
315 | }, | |
316 | { | |
317 | iconCls: 'fa fa-fw fa-archive', | |
318 | text: gettext('.tar.zst'), | |
319 | handler: 'downloadTar', | |
320 | reference: 'downloadTar', | |
321 | }, | |
322 | ], | |
323 | }, | |
09195cb6 SR |
324 | }, |
325 | ], | |
326 | }); |