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