]>
Commit | Line | Data |
---|---|---|
09195cb6 SR |
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; | |
c1c8cfa8 SR |
39 | case 'v': // virtual |
40 | icon = 'cube'; | |
41 | break; | |
09195cb6 SR |
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 | ||
fd8ed0d8 TL |
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 | ||
09195cb6 SR |
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; | |
68b29ade | 104 | let params = { ...view.extraParams }; |
09195cb6 SR |
105 | params.filepath = data.filepath; |
106 | atag.download = data.text; | |
107 | if (data.type === 'd') { | |
108 | atag.download += ".zip"; | |
109 | } | |
fd8ed0d8 | 110 | atag.href = me.buildUrl(view.downloadURL, params); |
09195cb6 SR |
111 | atag.click(); |
112 | }, | |
113 | ||
114 | fileChanged: function() { | |
115 | let me = this; | |
68b29ade | 116 | let view = me.getView(); |
09195cb6 SR |
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; | |
fd8ed0d8 | 122 | let canDownload = view.downloadURL && view.downloadableFileTypes[data.type]; |
09195cb6 SR |
123 | me.lookup('downloadBtn').setDisabled(!canDownload); |
124 | }, | |
125 | ||
6f9f9c71 SR |
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 | ||
09195cb6 SR |
136 | init: function(view) { |
137 | let me = this; | |
138 | let tree = me.lookup('tree'); | |
139 | ||
fd8ed0d8 | 140 | if (!view.listURL) { |
68b29ade | 141 | throw "no list URL given"; |
09195cb6 SR |
142 | } |
143 | ||
144 | let store = tree.getStore(); | |
145 | let proxy = store.getProxy(); | |
146 | ||
6f9f9c71 SR |
147 | let errorCallback = (error, msg) => me.errorHandler(error, msg); |
148 | Proxmox.Utils.monStoreErrors(tree, store, true, errorCallback); | |
fd8ed0d8 | 149 | proxy.setUrl(view.listURL); |
68b29ade | 150 | proxy.setExtraParams(view.extraParams); |
6f9f9c71 | 151 | store.load((rec, op, success) => { |
09195cb6 SR |
152 | let root = store.getRoot(); |
153 | root.expand(); // always expand invisible root node | |
6f9f9c71 SR |
154 | if (view.archive === 'all') { |
155 | root.expandChildren(false); | |
156 | } else if (view.archive) { | |
09195cb6 SR |
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 | } | |
6f9f9c71 | 168 | me.initialLoadDone = success; |
09195cb6 SR |
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'); | |
c1c8cfa8 | 237 | case 'v': return gettext('Virtual'); |
09195cb6 SR |
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 | }); |