]>
Commit | Line | Data |
---|---|---|
33839735 | 1 | Ext.define('pbs-data-store-snapshots', { |
ca23a97f | 2 | extend: 'Ext.data.Model', |
d9c38ddc | 3 | fields: [ |
d9c38ddc | 4 | 'backup-type', |
507c39c5 DM |
5 | 'backup-id', |
6 | { | |
e8f0ad19 | 7 | name: 'backup-time', |
507c39c5 DM |
8 | type: 'date', |
9 | dateFormat: 'timestamp' | |
10 | }, | |
d9c38ddc | 11 | 'files', |
04b0ca8b | 12 | 'owner', |
7b212c1f | 13 | 'verification', |
a7a5f56d | 14 | { name: 'size', type: 'int', allowNull: true, }, |
e005f953 | 15 | { |
2774566b | 16 | name: 'crypt-mode', |
676b0fde | 17 | type: 'boolean', |
e005f953 DC |
18 | calculate: function(data) { |
19 | let encrypted = 0; | |
2774566b DC |
20 | let crypt = { |
21 | none: 0, | |
22 | mixed: 0, | |
23 | 'sign-only': 0, | |
24 | encrypt: 0, | |
106603c5 | 25 | count: 0, |
2774566b DC |
26 | }; |
27 | let signed = 0; | |
e005f953 DC |
28 | data.files.forEach(file => { |
29 | if (file.filename === 'index.json.blob') return; // is never encrypted | |
2774566b DC |
30 | let mode = PBS.Utils.cryptmap.indexOf(file['crypt-mode']); |
31 | if (mode !== -1) { | |
32 | crypt[file['crypt-mode']]++; | |
1bfdae79 | 33 | crypt.count++; |
e005f953 | 34 | } |
e005f953 DC |
35 | }); |
36 | ||
106603c5 | 37 | return PBS.Utils.calculateCryptMode(crypt); |
e005f953 | 38 | } |
6d55603d DC |
39 | }, |
40 | { | |
41 | name: 'matchesFilter', | |
42 | type: 'boolean', | |
43 | defaultValue: true, | |
44 | }, | |
33839735 | 45 | ] |
ca23a97f DM |
46 | }); |
47 | ||
48 | Ext.define('PBS.DataStoreContent', { | |
e8f0ad19 | 49 | extend: 'Ext.tree.Panel', |
ca23a97f DM |
50 | alias: 'widget.pbsDataStoreContent', |
51 | ||
e8f0ad19 | 52 | rootVisible: false, |
507c39c5 | 53 | |
c0ac2074 DC |
54 | title: gettext('Content'), |
55 | ||
f1baa7f4 TL |
56 | controller: { |
57 | xclass: 'Ext.app.ViewController', | |
58 | ||
59 | init: function(view) { | |
60 | if (!view.datastore) { | |
61 | throw "no datastore specified"; | |
62 | } | |
63 | ||
3f98b347 | 64 | this.store = Ext.create('Ext.data.Store', { |
33839735 | 65 | model: 'pbs-data-store-snapshots', |
e8f0ad19 DM |
66 | groupField: 'backup-group', |
67 | }); | |
3f98b347 | 68 | this.store.on('load', this.onLoad, this); |
e8f0ad19 | 69 | |
7b1e2669 DC |
70 | view.getStore().setSorters([ |
71 | 'backup-group', | |
72 | 'text', | |
73 | 'backup-time' | |
74 | ]); | |
90779237 | 75 | Proxmox.Utils.monStoreErrors(view, this.store); |
f1baa7f4 TL |
76 | this.reload(); // initial load |
77 | }, | |
78 | ||
79 | reload: function() { | |
3f98b347 TL |
80 | let view = this.getView(); |
81 | ||
82 | if (!view.store || !this.store) { | |
83 | console.warn('cannot reload, no store(s)'); | |
84 | return; | |
85 | } | |
f1baa7f4 | 86 | |
e8f0ad19 | 87 | let url = `/api2/json/admin/datastore/${view.datastore}/snapshots`; |
3f98b347 | 88 | this.store.setProxy({ |
f1baa7f4 | 89 | type: 'proxmox', |
26f499b1 | 90 | timeout: 300*1000, // 5 minutes, we should make that api call faster |
f1baa7f4 TL |
91 | url: url |
92 | }); | |
e8f0ad19 | 93 | |
3f98b347 TL |
94 | this.store.load(); |
95 | }, | |
e8f0ad19 | 96 | |
3f98b347 TL |
97 | getRecordGroups: function(records) { |
98 | let groups = {}; | |
99 | ||
100 | for (const item of records) { | |
101 | var btype = item.data["backup-type"]; | |
102 | let group = btype + "/" + item.data["backup-id"]; | |
103 | ||
104 | if (groups[group] !== undefined) { | |
105 | continue; | |
106 | } | |
107 | ||
108 | var cls = ''; | |
109 | if (btype === 'vm') { | |
110 | cls = 'fa-desktop'; | |
111 | } else if (btype === 'ct') { | |
112 | cls = 'fa-cube'; | |
113 | } else if (btype === 'host') { | |
114 | cls = 'fa-building'; | |
115 | } else { | |
add5861e | 116 | console.warn(`got unknown backup-type '${btype}'`); |
3f98b347 TL |
117 | continue; // FIXME: auto render? what do? |
118 | } | |
119 | ||
120 | groups[group] = { | |
121 | text: group, | |
122 | leaf: false, | |
123 | iconCls: "fa " + cls, | |
124 | expanded: false, | |
125 | backup_type: item.data["backup-type"], | |
126 | backup_id: item.data["backup-id"], | |
127 | children: [] | |
aeee4329 | 128 | }; |
3f98b347 | 129 | } |
aeee4329 | 130 | |
3f98b347 TL |
131 | return groups; |
132 | }, | |
e8f0ad19 | 133 | |
90779237 | 134 | onLoad: function(store, records, success, operation) { |
6d55603d | 135 | let me = this; |
3f98b347 TL |
136 | let view = this.getView(); |
137 | ||
138 | if (!success) { | |
90779237 | 139 | Proxmox.Utils.setErrorMask(view, Proxmox.Utils.getResponseErrorMessage(operation.getError())); |
3f98b347 TL |
140 | return; |
141 | } | |
142 | ||
143 | let groups = this.getRecordGroups(records); | |
e8f0ad19 | 144 | |
69d970a6 DC |
145 | let selected; |
146 | let expanded = {}; | |
147 | ||
148 | view.getSelection().some(function(item) { | |
149 | let id = item.data.text; | |
150 | if (item.data.leaf) { | |
151 | id = item.parentNode.data.text + id; | |
152 | } | |
153 | selected = id; | |
154 | return true; | |
155 | }); | |
156 | ||
157 | view.getRootNode().cascadeBy({ | |
158 | before: item => { | |
159 | if (item.isExpanded() && !item.data.leaf) { | |
160 | let id = item.data.text; | |
161 | expanded[id] = true; | |
162 | return true; | |
163 | } | |
164 | return false; | |
165 | }, | |
166 | after: () => {}, | |
167 | }); | |
168 | ||
3f98b347 TL |
169 | for (const item of records) { |
170 | let group = item.data["backup-type"] + "/" + item.data["backup-id"]; | |
171 | let children = groups[group].children; | |
172 | ||
173 | let data = item.data; | |
174 | ||
f68ae22c | 175 | data.text = group + '/' + PBS.Utils.render_datetime_utc(data["backup-time"]); |
3e395378 | 176 | data.leaf = false; |
3f98b347 | 177 | data.cls = 'no-leaf-icons'; |
6d55603d | 178 | data.matchesFilter = true; |
3f98b347 | 179 | |
69d970a6 DC |
180 | data.expanded = !!expanded[data.text]; |
181 | ||
3e395378 DC |
182 | data.children = []; |
183 | for (const file of data.files) { | |
b1149ebb | 184 | file.text = file.filename; |
3e395378 DC |
185 | file['crypt-mode'] = PBS.Utils.cryptmap.indexOf(file['crypt-mode']); |
186 | file.leaf = true; | |
6d55603d | 187 | file.matchesFilter = true; |
3e395378 DC |
188 | |
189 | data.children.push(file); | |
190 | } | |
191 | ||
3f98b347 TL |
192 | children.push(data); |
193 | } | |
194 | ||
195 | let children = []; | |
69d970a6 | 196 | for (const [name, group] of Object.entries(groups)) { |
3f98b347 | 197 | let last_backup = 0; |
2774566b DC |
198 | let crypt = { |
199 | none: 0, | |
200 | mixed: 0, | |
201 | 'sign-only': 0, | |
106603c5 | 202 | encrypt: 0, |
2774566b | 203 | }; |
3f98b347 | 204 | for (const item of group.children) { |
2774566b | 205 | crypt[PBS.Utils.cryptmap[item['crypt-mode']]]++; |
48e22a89 | 206 | if (item["backup-time"] > last_backup && item.size !== null) { |
3f98b347 TL |
207 | last_backup = item["backup-time"]; |
208 | group["backup-time"] = last_backup; | |
209 | group.files = item.files; | |
210 | group.size = item.size; | |
04b0ca8b | 211 | group.owner = item.owner; |
c879e5af | 212 | verify.lastFailed = item.verification && item.verification.state !== 'ok'; |
3f98b347 | 213 | } |
7b212c1f TL |
214 | if (item.verification && |
215 | (!group.verification || group.verification.state !== 'failed')) { | |
216 | group.verification = item.verification; | |
217 | } | |
e005f953 | 218 | |
3f98b347 TL |
219 | } |
220 | group.count = group.children.length; | |
6d55603d | 221 | group.matchesFilter = true; |
106603c5 DC |
222 | crypt.count = group.count; |
223 | group['crypt-mode'] = PBS.Utils.calculateCryptMode(crypt); | |
69d970a6 | 224 | group.expanded = !!expanded[name]; |
3f98b347 TL |
225 | children.push(group); |
226 | } | |
227 | ||
228 | view.setRootNode({ | |
229 | expanded: true, | |
230 | children: children | |
231 | }); | |
69d970a6 DC |
232 | |
233 | if (selected !== undefined) { | |
234 | let selection = view.getRootNode().findChildBy(function(item) { | |
235 | let id = item.data.text; | |
236 | if (item.data.leaf) { | |
237 | id = item.parentNode.data.text + id; | |
238 | } | |
239 | return selected === id; | |
240 | }, undefined, true); | |
80db161e SR |
241 | if (selection) { |
242 | view.setSelection(selection); | |
243 | view.getView().focusRow(selection); | |
244 | } | |
69d970a6 DC |
245 | } |
246 | ||
90779237 | 247 | Proxmox.Utils.setErrorMask(view, false); |
6d55603d DC |
248 | if (view.getStore().getFilters().length > 0) { |
249 | let searchBox = me.lookup("searchbox"); | |
250 | let searchvalue = searchBox.getValue();; | |
251 | me.search(searchBox, searchvalue); | |
252 | } | |
f1baa7f4 | 253 | }, |
5f448992 | 254 | |
3e395378 | 255 | onPrune: function(view, rI, cI, item, e, rec) { |
5f448992 DM |
256 | var view = this.getView(); |
257 | ||
5f448992 DM |
258 | if (!(rec && rec.data)) return; |
259 | let data = rec.data; | |
3e395378 | 260 | if (rec.parentNode.id !== 'root') return; |
5f448992 DM |
261 | |
262 | if (!view.datastore) return; | |
263 | ||
264 | let win = Ext.create('PBS.DataStorePrune', { | |
265 | datastore: view.datastore, | |
266 | backup_type: data.backup_type, | |
267 | backup_id: data.backup_id, | |
268 | }); | |
269 | win.on('destroy', this.reload, this); | |
270 | win.show(); | |
98425309 DC |
271 | }, |
272 | ||
3e395378 | 273 | onVerify: function(view, rI, cI, item, e, rec) { |
484d439a TL |
274 | let me = this; |
275 | view = me.getView(); | |
8f6088c1 DM |
276 | |
277 | if (!view.datastore) return; | |
278 | ||
8f6088c1 DM |
279 | if (!(rec && rec.data)) return; |
280 | let data = rec.data; | |
281 | ||
282 | let params; | |
283 | ||
3e395378 | 284 | if (rec.parentNode.id !== 'root') { |
8f6088c1 DM |
285 | params = { |
286 | "backup-type": data["backup-type"], | |
287 | "backup-id": data["backup-id"], | |
288 | "backup-time": (data['backup-time'].getTime()/1000).toFixed(0), | |
289 | }; | |
290 | } else { | |
291 | params = { | |
292 | "backup-type": data.backup_type, | |
293 | "backup-id": data.backup_id, | |
294 | }; | |
295 | } | |
296 | ||
297 | Proxmox.Utils.API2Request({ | |
298 | params: params, | |
299 | url: `/admin/datastore/${view.datastore}/verify`, | |
300 | method: 'POST', | |
301 | failure: function(response) { | |
302 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
303 | }, | |
304 | success: function(response, options) { | |
305 | Ext.create('Proxmox.window.TaskViewer', { | |
306 | upid: response.result.data, | |
484d439a | 307 | taskDone: () => me.reload(), |
8f6088c1 DM |
308 | }).show(); |
309 | }, | |
310 | }); | |
311 | }, | |
312 | ||
3e395378 DC |
313 | onForget: function(view, rI, cI, item, e, rec) { |
314 | let me = this; | |
4ff2c9b8 DM |
315 | var view = this.getView(); |
316 | ||
4ff2c9b8 DM |
317 | if (!(rec && rec.data)) return; |
318 | let data = rec.data; | |
4ff2c9b8 DM |
319 | if (!view.datastore) return; |
320 | ||
3e395378 DC |
321 | Ext.Msg.show({ |
322 | title: gettext('Confirm'), | |
323 | icon: Ext.Msg.WARNING, | |
324 | message: Ext.String.format(gettext('Are you sure you want to remove snapshot {0}'), `'${data.text}'`), | |
325 | buttons: Ext.Msg.YESNO, | |
326 | defaultFocus: 'no', | |
327 | callback: function(btn) { | |
328 | if (btn !== 'yes') { | |
329 | return; | |
330 | } | |
4ff2c9b8 | 331 | |
3e395378 DC |
332 | Proxmox.Utils.API2Request({ |
333 | params: { | |
334 | "backup-type": data["backup-type"], | |
335 | "backup-id": data["backup-id"], | |
336 | "backup-time": (data['backup-time'].getTime()/1000).toFixed(0), | |
337 | }, | |
338 | url: `/admin/datastore/${view.datastore}/snapshots`, | |
339 | method: 'DELETE', | |
340 | waitMsgTarget: view, | |
341 | failure: function(response, opts) { | |
342 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
343 | }, | |
344 | callback: me.reload.bind(me), | |
345 | }); | |
4ff2c9b8 | 346 | }, |
4ff2c9b8 DM |
347 | }); |
348 | }, | |
349 | ||
3e395378 | 350 | downloadFile: function(tV, rI, cI, item, e, rec) { |
98425309 DC |
351 | let me = this; |
352 | let view = me.getView(); | |
353 | ||
98425309 | 354 | if (!(rec && rec.data)) return; |
3e395378 DC |
355 | let data = rec.parentNode.data; |
356 | ||
357 | let file = rec.data.filename; | |
358 | let params = { | |
359 | 'backup-id': data['backup-id'], | |
360 | 'backup-type': data['backup-type'], | |
361 | 'backup-time': (data['backup-time'].getTime()/1000).toFixed(0), | |
362 | 'file-name': file, | |
363 | }; | |
364 | ||
365 | let idx = file.lastIndexOf('.'); | |
366 | let filename = file.slice(0, idx); | |
367 | let atag = document.createElement('a'); | |
368 | params['file-name'] = file; | |
369 | atag.download = filename; | |
370 | let url = new URL(`/api2/json/admin/datastore/${view.datastore}/download-decoded`, window.location.origin); | |
371 | for (const [key, value] of Object.entries(params)) { | |
372 | url.searchParams.append(key, value); | |
373 | } | |
374 | atag.href = url.href; | |
375 | atag.click(); | |
8567c0d2 DC |
376 | }, |
377 | ||
3e395378 | 378 | openPxarBrowser: function(tv, rI, Ci, item, e, rec) { |
8567c0d2 DC |
379 | let me = this; |
380 | let view = me.getView(); | |
381 | ||
8567c0d2 | 382 | if (!(rec && rec.data)) return; |
3e395378 | 383 | let data = rec.parentNode.data; |
8567c0d2 DC |
384 | |
385 | let id = data['backup-id']; | |
386 | let time = data['backup-time']; | |
387 | let type = data['backup-type']; | |
388 | let timetext = PBS.Utils.render_datetime_utc(data["backup-time"]); | |
389 | ||
390 | Ext.create('PBS.window.FileBrowser', { | |
391 | title: `${type}/${id}/${timetext}`, | |
392 | datastore: view.datastore, | |
393 | 'backup-id': id, | |
394 | 'backup-time': (time.getTime()/1000).toFixed(0), | |
395 | 'backup-type': type, | |
3e395378 | 396 | archive: rec.data.filename, |
8567c0d2 | 397 | }).show(); |
6d55603d DC |
398 | }, |
399 | ||
400 | filter: function(item, value) { | |
401 | if (item.data.text.indexOf(value) !== -1) { | |
402 | return true; | |
403 | } | |
404 | ||
405 | if (item.data.owner && item.data.owner.indexOf(value) !== -1) { | |
406 | return true; | |
407 | } | |
408 | ||
409 | return false; | |
410 | }, | |
411 | ||
412 | search: function(tf, value) { | |
413 | let me = this; | |
414 | let view = me.getView(); | |
415 | let store = view.getStore(); | |
416 | if (!value && value !== 0) { | |
417 | store.clearFilter(); | |
418 | store.getRoot().collapseChildren(true); | |
419 | tf.triggers.clear.setVisible(false); | |
420 | return; | |
421 | } | |
422 | tf.triggers.clear.setVisible(true); | |
423 | if (value.length < 2) return; | |
424 | Proxmox.Utils.setErrorMask(view, true); | |
425 | // we do it a little bit later for the error mask to work | |
426 | setTimeout(function() { | |
427 | store.clearFilter(); | |
428 | store.getRoot().collapseChildren(true); | |
429 | ||
430 | store.beginUpdate(); | |
431 | store.getRoot().cascadeBy({ | |
432 | before: function(item) { | |
433 | if(me.filter(item, value)) { | |
434 | item.set('matchesFilter', true); | |
435 | if (item.parentNode && item.parentNode.id !== 'root') { | |
436 | item.parentNode.childmatches = true; | |
437 | } | |
438 | return false; | |
439 | } | |
440 | return true; | |
441 | }, | |
442 | after: function(item) { | |
443 | if (me.filter(item, value) || item.id === 'root' || item.childmatches) { | |
444 | item.set('matchesFilter', true); | |
445 | if (item.parentNode && item.parentNode.id !== 'root') { | |
446 | item.parentNode.childmatches = true; | |
447 | } | |
448 | if (item.childmatches) { | |
449 | item.expand(); | |
450 | } | |
451 | } else { | |
452 | item.set('matchesFilter', false); | |
453 | } | |
454 | delete item.childmatches; | |
455 | }, | |
456 | }); | |
457 | store.endUpdate(); | |
458 | ||
459 | store.filter((item) => !!item.get('matchesFilter')); | |
460 | Proxmox.Utils.setErrorMask(view, false); | |
461 | }, 10); | |
462 | }, | |
f1baa7f4 TL |
463 | }, |
464 | ||
c879e5af TL |
465 | viewConfig: { |
466 | getRowClass: function(record, index) { | |
467 | let verify = record.get('verification'); | |
468 | if (verify && verify.lastFailed) { | |
469 | return 'proxmox-invalid-row'; | |
470 | } | |
471 | }, | |
472 | }, | |
473 | ||
04b0ca8b DC |
474 | columns: [ |
475 | { | |
476 | xtype: 'treecolumn', | |
477 | header: gettext("Backup Group"), | |
478 | dataIndex: 'text', | |
479 | flex: 1 | |
480 | }, | |
3e395378 DC |
481 | { |
482 | header: gettext('Actions'), | |
483 | xtype: 'actioncolumn', | |
484 | dataIndex: 'text', | |
485 | items: [ | |
486 | { | |
487 | handler: 'onVerify', | |
488 | tooltip: gettext('Verify'), | |
489 | getClass: (v, m, rec) => rec.data.leaf ? 'pmx-hidden' : 'fa fa-search', | |
490 | isDisabled: (v, r, c, i, rec) => !!rec.data.leaf, | |
491 | }, | |
492 | { | |
493 | handler: 'onPrune', | |
494 | tooltip: gettext('Prune'), | |
495 | getClass: (v, m, rec) => rec.parentNode.id ==='root' ? 'fa fa-scissors' : 'pmx-hidden', | |
496 | isDisabled: (v, r, c, i, rec) => rec.parentNode.id !=='root', | |
497 | }, | |
498 | { | |
499 | handler: 'onForget', | |
500 | tooltip: gettext('Forget Snapshot'), | |
501 | getClass: (v, m, rec) => !rec.data.leaf && rec.parentNode.id !== 'root' ? 'fa critical fa-trash-o' : 'pmx-hidden', | |
502 | isDisabled: (v, r, c, i, rec) => rec.data.leaf || rec.parentNode.id === 'root', | |
503 | }, | |
504 | { | |
505 | handler: 'downloadFile', | |
506 | tooltip: gettext('Download'), | |
507 | getClass: (v, m, rec) => rec.data.leaf && rec.data.filename ? 'fa fa-download' : 'pmx-hidden', | |
508 | isDisabled: (v, r, c, i, rec) => !rec.data.leaf || !rec.data.filename || rec.data['crypt-mode'] > 2, | |
509 | }, | |
510 | { | |
511 | handler: 'openPxarBrowser', | |
512 | tooltip: gettext('Browse'), | |
513 | getClass: (v, m, rec) => { | |
514 | let data = rec.data; | |
515 | if (data.leaf && data.filename && data.filename.endsWith('pxar.didx')) { | |
516 | return 'fa fa-folder-open-o'; | |
517 | } | |
518 | return 'pmx-hidden'; | |
519 | }, | |
520 | isDisabled: (v, r, c, i, rec) => { | |
521 | let data = rec.data; | |
522 | return !(data.leaf && | |
523 | data.filename && | |
524 | data.filename.endsWith('pxar.didx') && | |
c96b0de4 | 525 | data['crypt-mode'] < 3); |
3e395378 DC |
526 | } |
527 | }, | |
528 | ] | |
529 | }, | |
04b0ca8b DC |
530 | { |
531 | xtype: 'datecolumn', | |
532 | header: gettext('Backup Time'), | |
533 | sortable: true, | |
534 | dataIndex: 'backup-time', | |
535 | format: 'Y-m-d H:i:s', | |
536 | width: 150 | |
537 | }, | |
538 | { | |
539 | header: gettext("Size"), | |
540 | sortable: true, | |
541 | dataIndex: 'size', | |
a7a5f56d | 542 | renderer: (v, meta, record) => { |
3e395378 DC |
543 | if (record.data.text === 'client.log.blob' && v === undefined) { |
544 | return ''; | |
545 | } | |
a7a5f56d TL |
546 | if (v === undefined || v === null) { |
547 | meta.tdCls = "x-grid-row-loading"; | |
548 | return ''; | |
549 | } | |
550 | return Proxmox.Utils.format_size(v); | |
551 | }, | |
04b0ca8b DC |
552 | }, |
553 | { | |
554 | xtype: 'numbercolumn', | |
555 | format: '0', | |
556 | header: gettext("Count"), | |
557 | sortable: true, | |
46388e6a TL |
558 | width: 75, |
559 | align: 'right', | |
04b0ca8b DC |
560 | dataIndex: 'count', |
561 | }, | |
562 | { | |
563 | header: gettext("Owner"), | |
564 | sortable: true, | |
565 | dataIndex: 'owner', | |
566 | }, | |
e005f953 DC |
567 | { |
568 | header: gettext('Encrypted'), | |
2774566b | 569 | dataIndex: 'crypt-mode', |
3e395378 | 570 | renderer: (v, meta, record) => { |
71282dd9 SR |
571 | if (record.data.size === undefined || record.data.size === null) { |
572 | return ''; | |
573 | } | |
3e395378 DC |
574 | if (v === -1) { |
575 | return ''; | |
576 | } | |
577 | let iconCls = PBS.Utils.cryptIconCls[v] || ''; | |
578 | let iconTxt = ""; | |
579 | if (iconCls) { | |
580 | iconTxt = `<i class="fa fa-fw fa-${iconCls}"></i> `; | |
581 | } | |
582 | return (iconTxt + PBS.Utils.cryptText[v]) || Proxmox.Utils.unknownText | |
583 | } | |
04b0ca8b | 584 | }, |
7b212c1f TL |
585 | { |
586 | header: gettext('Verify State'), | |
587 | sortable: true, | |
588 | dataIndex: 'verification', | |
589 | renderer: (v, meta, record) => { | |
590 | if (v === undefined || v === null || !v.state) { | |
591 | //meta.tdCls = "x-grid-row-loading"; | |
592 | return record.data.leaf ? '' : gettext('None'); | |
593 | } | |
594 | let task = Proxmox.Utils.parse_task_upid(v.upid); | |
595 | let verify_time = Proxmox.Utils.render_timestamp(task.starttime); | |
596 | let iconCls = v.state === 'ok' ? 'check good' : 'times critical'; | |
597 | let tip = `Verify task started on ${verify_time}`; | |
598 | if (record.parentNode.id === 'root') { | |
599 | tip = v.state === 'ok' | |
600 | ? 'All verification OK in backup group' | |
601 | : 'At least one failed verification in backup group!'; | |
602 | } | |
603 | return `<span data-qtip="${tip}"> | |
604 | <i class="fa fa-fw fa-${iconCls}"></i> ${v.state} | |
605 | </span>`; | |
606 | }, | |
607 | listeners: { | |
608 | dblclick: function(view, el, row, col, ev, rec) { | |
609 | let data = rec.data || {}; | |
610 | let verify = data.verification; | |
611 | if (verify && verify.upid && rec.parentNode.id !== 'root') { | |
612 | let win = Ext.create('Proxmox.window.TaskViewer', { | |
613 | upid: verify.upid, | |
614 | }); | |
615 | win.show(); | |
616 | } | |
617 | }, | |
618 | }, | |
619 | }, | |
04b0ca8b | 620 | ], |
b1127fd0 | 621 | |
04b0ca8b DC |
622 | tbar: [ |
623 | { | |
624 | text: gettext('Reload'), | |
625 | iconCls: 'fa fa-refresh', | |
626 | handler: 'reload', | |
627 | }, | |
6d55603d DC |
628 | '->', |
629 | { | |
630 | xtype: 'tbtext', | |
631 | html: gettext('Search'), | |
632 | }, | |
633 | { | |
634 | xtype: 'textfield', | |
635 | reference: 'searchbox', | |
c3b1da9e | 636 | emptyText: gettext('group, date or owner'), |
6d55603d DC |
637 | triggers: { |
638 | clear: { | |
639 | cls: 'pmx-clear-trigger', | |
640 | weight: -1, | |
641 | hidden: true, | |
642 | handler: function() { | |
643 | this.triggers.clear.setVisible(false); | |
644 | this.setValue(''); | |
645 | }, | |
646 | } | |
647 | }, | |
648 | listeners: { | |
649 | change: { | |
650 | fn: 'search', | |
651 | buffer: 500, | |
652 | }, | |
653 | }, | |
654 | } | |
04b0ca8b | 655 | ], |
ca23a97f | 656 | }); |