]>
Commit | Line | Data |
---|---|---|
787ae72a DM |
1 | /* |
2 | * Workspace base class | |
3 | * | |
4 | * popup login window when auth fails (call onLogin handler) | |
5 | * update (re-login) ticket every 15 minutes | |
6 | * | |
7 | */ | |
8 | ||
9 | Ext.define('PVE.Workspace', { | |
10 | extend: 'Ext.container.Viewport', | |
11 | ||
12 | title: 'Proxmox Virtual Environment', | |
13 | ||
14 | loginData: null, // Data from last login call | |
15 | ||
063997ed TL |
16 | onLogin: function(loginData) { |
17 | // override me | |
18 | }, | |
787ae72a DM |
19 | |
20 | // private | |
21 | updateLoginData: function(loginData) { | |
063997ed | 22 | let me = this; |
787ae72a | 23 | me.loginData = loginData; |
24d2ed8c | 24 | Proxmox.Utils.setAuthData(loginData); |
787ae72a | 25 | |
063997ed | 26 | let rt = me.down('pveResourceTree'); |
3d7b2aa9 | 27 | rt.setDatacenterText(loginData.clustername); |
9ebe2165 | 28 | PVE.ClusterName = loginData.clustername; |
3d7b2aa9 | 29 | |
787ae72a DM |
30 | if (loginData.cap) { |
31 | Ext.state.Manager.set('GuiCap', loginData.cap); | |
32 | } | |
1370f628 | 33 | me.response401count = 0; |
787ae72a | 34 | |
787ae72a DM |
35 | me.onLogin(loginData); |
36 | }, | |
37 | ||
38 | // private | |
39 | showLogin: function() { | |
063997ed | 40 | let me = this; |
787ae72a | 41 | |
e7ade592 | 42 | Proxmox.Utils.authClear(); |
9fd1198e | 43 | Ext.state.Manager.clear('GuiCap'); |
35a04562 | 44 | Proxmox.UserName = null; |
787ae72a DM |
45 | me.loginData = null; |
46 | ||
47 | if (!me.login) { | |
48 | me.login = Ext.create('PVE.window.LoginWindow', { | |
49 | handler: function(data) { | |
50 | me.login = null; | |
51 | me.updateLoginData(data); | |
063997ed | 52 | Proxmox.Utils.checked_command(Ext.emptyFn); // display subscription status |
f6710aac | 53 | }, |
787ae72a DM |
54 | }); |
55 | } | |
56 | me.onLogin(null); | |
57 | me.login.show(); | |
58 | }, | |
59 | ||
8058410f | 60 | initComponent: function() { |
063997ed | 61 | let me = this; |
787ae72a DM |
62 | |
63 | Ext.tip.QuickTipManager.init(); | |
64 | ||
65 | // fixme: what about other errors | |
66 | Ext.Ajax.on('requestexception', function(conn, response, options) { | |
063997ed | 67 | if ((response.status === 401 || response.status === '401') && !PVE.Utils.silenceAuthFailures) { // auth failure |
1370f628 TL |
68 | // don't immediately show as logged out to cope better with some big |
69 | // upgrades, which may temporarily produce a false positive 401 err | |
70 | me.response401count++; | |
71 | if (me.response401count > 5) { | |
72 | me.showLogin(); | |
73 | } | |
787ae72a DM |
74 | } |
75 | }); | |
76 | ||
787ae72a DM |
77 | me.callParent(); |
78 | ||
e7ade592 | 79 | if (!Proxmox.Utils.authOK()) { |
787ae72a | 80 | me.showLogin(); |
063997ed TL |
81 | } else if (me.loginData) { |
82 | me.onLogin(me.loginData); | |
787ae72a DM |
83 | } |
84 | ||
85 | Ext.TaskManager.start({ | |
86 | run: function() { | |
063997ed | 87 | let ticket = Proxmox.Utils.authOK(); |
35a04562 | 88 | if (!ticket || !Proxmox.UserName) { |
787ae72a DM |
89 | return; |
90 | } | |
91 | ||
92 | Ext.Ajax.request({ | |
7c8051b4 | 93 | params: { |
35a04562 | 94 | username: Proxmox.UserName, |
f6710aac | 95 | password: ticket, |
787ae72a DM |
96 | }, |
97 | url: '/api2/json/access/ticket', | |
98 | method: 'POST', | |
99 | success: function(response, opts) { | |
063997ed | 100 | let obj = Ext.decode(response.responseText); |
787ae72a | 101 | me.updateLoginData(obj.data); |
f6710aac | 102 | }, |
787ae72a DM |
103 | }); |
104 | }, | |
063997ed | 105 | interval: 15 * 60 * 1000, |
787ae72a | 106 | }); |
f6710aac | 107 | }, |
787ae72a DM |
108 | }); |
109 | ||
787ae72a DM |
110 | Ext.define('PVE.StdWorkspace', { |
111 | extend: 'PVE.Workspace', | |
112 | ||
113 | alias: ['widget.pveStdWorkspace'], | |
114 | ||
115 | // private | |
116 | setContent: function(comp) { | |
063997ed | 117 | let me = this; |
7c8051b4 | 118 | |
063997ed TL |
119 | let view = me.child('#content'); |
120 | let layout = view.getLayout(); | |
121 | let current = layout.getActiveItem(); | |
787ae72a DM |
122 | |
123 | if (comp) { | |
063997ed | 124 | Proxmox.Utils.setErrorMask(view, false); |
787ae72a | 125 | comp.border = false; |
063997ed TL |
126 | view.add(comp); |
127 | if (current !== null && layout.getNext()) { | |
128 | layout.next(); | |
129 | let task = Ext.create('Ext.util.DelayedTask', function() { | |
130 | view.remove(current); | |
834ba9e4 DC |
131 | }); |
132 | task.delay(10); | |
133 | } | |
8058410f | 134 | } else { |
063997ed | 135 | view.removeAll(); // helper for cleaning the content when logging out |
de7eeaac | 136 | } |
787ae72a DM |
137 | }, |
138 | ||
139 | selectById: function(nodeid) { | |
063997ed TL |
140 | let me = this; |
141 | me.down('pveResourceTree').selectById(nodeid); | |
787ae72a DM |
142 | }, |
143 | ||
787ae72a | 144 | onLogin: function(loginData) { |
063997ed | 145 | let me = this; |
787ae72a DM |
146 | |
147 | me.updateUserInfo(); | |
148 | ||
149 | if (loginData) { | |
150 | PVE.data.ResourceStore.startUpdate(); | |
151 | ||
e7ade592 | 152 | Proxmox.Utils.API2Request({ |
787ae72a DM |
153 | url: '/version', |
154 | method: 'GET', | |
155 | success: function(response) { | |
156 | PVE.VersionInfo = response.result.data; | |
157 | me.updateVersionInfo(); | |
f6710aac | 158 | }, |
787ae72a | 159 | }); |
f1ca55fb | 160 | |
731436ee | 161 | PVE.UIOptions.update(); |
b7f4cb7c | 162 | |
f1ca55fb AD |
163 | Proxmox.Utils.API2Request({ |
164 | url: '/cluster/sdn', | |
165 | method: 'GET', | |
166 | success: function(response) { | |
167 | PVE.SDNInfo = response.result.data; | |
35ffde01 TL |
168 | }, |
169 | failure: function(response) { | |
170 | PVE.SDNInfo = null; | |
171 | let ui = Ext.ComponentQuery.query('treelistitem[text="SDN"]')[0]; | |
172 | if (ui) { | |
173 | ui.addCls('x-hidden-display'); | |
174 | } | |
175 | }, | |
f1ca55fb | 176 | }); |
c5be8d39 DC |
177 | |
178 | Proxmox.Utils.API2Request({ | |
179 | url: '/access/domains', | |
180 | method: 'GET', | |
181 | success: function(response) { | |
182 | let [_username, realm] = Proxmox.Utils.parse_userid(Proxmox.UserName); | |
183 | response.result.data.forEach((domain) => { | |
184 | if (domain.realm === realm) { | |
185 | let schema = PVE.Utils.authSchema[domain.type]; | |
186 | if (schema) { | |
187 | me.query('#tfaitem')[0].setHidden(!schema.tfa); | |
188 | me.query('#passworditem')[0].setHidden(!schema.pwchange); | |
189 | } | |
190 | } | |
191 | }); | |
192 | }, | |
193 | }); | |
787ae72a DM |
194 | } |
195 | }, | |
196 | ||
197 | updateUserInfo: function() { | |
063997ed TL |
198 | let me = this; |
199 | let ui = me.query('#userinfo')[0]; | |
1011b569 | 200 | ui.setText(Ext.String.htmlEncode(Proxmox.UserName || '')); |
6a71fe01 | 201 | ui.updateLayout(); |
787ae72a DM |
202 | }, |
203 | ||
204 | updateVersionInfo: function() { | |
063997ed | 205 | let me = this; |
787ae72a | 206 | |
063997ed | 207 | let ui = me.query('#versioninfo')[0]; |
787ae72a DM |
208 | |
209 | if (PVE.VersionInfo) { | |
063997ed | 210 | let version = PVE.VersionInfo.version; |
55d727ca | 211 | ui.update('Virtual Environment ' + version); |
787ae72a | 212 | } else { |
55d727ca | 213 | ui.update('Virtual Environment'); |
787ae72a | 214 | } |
6a71fe01 | 215 | ui.updateLayout(); |
787ae72a DM |
216 | }, |
217 | ||
8058410f | 218 | initComponent: function() { |
063997ed | 219 | let me = this; |
787ae72a DM |
220 | |
221 | Ext.History.init(); | |
222 | ||
063997ed TL |
223 | let appState = Ext.create('PVE.StateProvider'); |
224 | Ext.state.Manager.setProvider(appState); | |
787ae72a | 225 | |
3a2a6964 DC |
226 | let selview = Ext.create('PVE.form.ViewSelector', { |
227 | flex: 1, | |
228 | padding: '0 5 0 0', | |
229 | }); | |
787ae72a | 230 | |
063997ed | 231 | let rtree = Ext.createWidget('pveResourceTree', { |
787ae72a DM |
232 | viewFilter: selview.getViewFilter(), |
233 | flex: 1, | |
aeb5e2f6 EK |
234 | selModel: { |
235 | selType: 'treemodel', | |
787ae72a DM |
236 | listeners: { |
237 | selectionchange: function(sm, selected) { | |
063997ed TL |
238 | if (selected.length <= 0) { |
239 | return; | |
787ae72a | 240 | } |
063997ed TL |
241 | let treeNode = selected[0]; |
242 | let treeTypeToClass = { | |
243 | root: 'PVE.dc.Config', | |
244 | node: 'PVE.node.Config', | |
245 | qemu: 'PVE.qemu.Config', | |
f31d010c | 246 | lxc: 'pveLXCConfig', |
063997ed TL |
247 | storage: 'PVE.storage.Browser', |
248 | sdn: 'PVE.sdn.Browser', | |
249 | pool: 'pvePoolConfig', | |
250 | }; | |
251 | PVE.curSelectedNode = treeNode; | |
252 | me.setContent({ | |
253 | xtype: treeTypeToClass[treeNode.data.type || 'root'] || 'pvePanelConfig', | |
254 | showSearch: treeNode.data.id === 'root' || Ext.isDefined(treeNode.data.groupbyid), | |
255 | pveSelNode: treeNode, | |
256 | workspace: me, | |
257 | viewFilter: selview.getViewFilter(), | |
258 | }); | |
f6710aac TL |
259 | }, |
260 | }, | |
261 | }, | |
787ae72a DM |
262 | }); |
263 | ||
7c8051b4 | 264 | selview.on('select', function(combo, records) { |
fb387756 | 265 | if (records) { |
063997ed | 266 | let view = combo.getViewFilter(); |
787ae72a DM |
267 | rtree.setViewFilter(view); |
268 | } | |
269 | }); | |
270 | ||
063997ed | 271 | let caps = appState.get('GuiCap'); |
787ae72a | 272 | |
063997ed | 273 | let createVM = Ext.createWidget('button', { |
787ae72a | 274 | pack: 'end', |
f01259ee | 275 | margin: '3 5 0 0', |
787ae72a | 276 | baseCls: 'x-btn', |
d1f155b8 | 277 | iconCls: 'fa fa-desktop', |
787ae72a DM |
278 | text: gettext("Create VM"), |
279 | disabled: !caps.vms['VM.Allocate'], | |
280 | handler: function() { | |
063997ed | 281 | let wiz = Ext.create('PVE.qemu.CreateWizard', {}); |
787ae72a | 282 | wiz.show(); |
f6710aac | 283 | }, |
787ae72a DM |
284 | }); |
285 | ||
063997ed | 286 | let createCT = Ext.createWidget('button', { |
787ae72a | 287 | pack: 'end', |
f01259ee | 288 | margin: '3 5 0 0', |
787ae72a | 289 | baseCls: 'x-btn', |
d1f155b8 | 290 | iconCls: 'fa fa-cube', |
787ae72a DM |
291 | text: gettext("Create CT"), |
292 | disabled: !caps.vms['VM.Allocate'], | |
293 | handler: function() { | |
063997ed | 294 | let wiz = Ext.create('PVE.lxc.CreateWizard', {}); |
787ae72a | 295 | wiz.show(); |
f6710aac | 296 | }, |
787ae72a DM |
297 | }); |
298 | ||
063997ed | 299 | appState.on('statechange', function(sp, key, value) { |
787ae72a DM |
300 | if (key === 'GuiCap' && value) { |
301 | caps = value; | |
302 | createVM.setDisabled(!caps.vms['VM.Allocate']); | |
303 | createCT.setDisabled(!caps.vms['VM.Allocate']); | |
304 | } | |
305 | }); | |
306 | ||
307 | Ext.apply(me, { | |
308 | layout: { type: 'border' }, | |
309 | border: false, | |
310 | items: [ | |
311 | { | |
312 | region: 'north', | |
916e739f TL |
313 | title: gettext('Header'), // for ARIA |
314 | header: false, // avoid rendering the title | |
7c8051b4 | 315 | layout: { |
787ae72a | 316 | type: 'hbox', |
f6710aac | 317 | align: 'middle', |
787ae72a | 318 | }, |
7c8051b4 | 319 | baseCls: 'x-plain', |
787ae72a | 320 | defaults: { |
f6710aac | 321 | baseCls: 'x-plain', |
787ae72a DM |
322 | }, |
323 | border: false, | |
f76884fd | 324 | margin: '2 0 2 5', |
787ae72a DM |
325 | items: [ |
326 | { | |
d1efcadf | 327 | xtype: 'proxmoxlogo', |
787ae72a DM |
328 | }, |
329 | { | |
bbcfa5ab | 330 | minWidth: 150, |
787ae72a | 331 | id: 'versioninfo', |
f6710aac | 332 | html: 'Virtual Environment', |
8de6ef28 TL |
333 | style: { |
334 | 'font-size': '14px', | |
335 | 'line-height': '18px', | |
336 | }, | |
787ae72a | 337 | }, |
839eed58 DC |
338 | { |
339 | xtype: 'pveGlobalSearchField', | |
f6710aac | 340 | tree: rtree, |
839eed58 DC |
341 | }, |
342 | { | |
f6710aac | 343 | flex: 1, |
839eed58 | 344 | }, |
3ef58611 | 345 | { |
672a6270 | 346 | xtype: 'proxmoxHelpButton', |
3ef58611 | 347 | hidden: false, |
1e4a853c | 348 | baseCls: 'x-btn', |
41e024ee | 349 | iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ', |
3ef58611 | 350 | listenToGlobalEvent: false, |
c8802a60 | 351 | onlineHelp: 'pve_documentation_index', |
41e024ee | 352 | text: gettext('Documentation'), |
f6710aac | 353 | margin: '0 5 0 0', |
3ef58611 | 354 | }, |
7c8051b4 | 355 | createVM, |
6a7465ae | 356 | createCT, |
787ae72a DM |
357 | { |
358 | pack: 'end', | |
f76884fd | 359 | margin: '0 5 0 0', |
d962846d | 360 | id: 'userinfo', |
787ae72a DM |
361 | xtype: 'button', |
362 | baseCls: 'x-btn', | |
404bf6c8 TL |
363 | style: { |
364 | // proxmox dark grey p light grey as border | |
365 | backgroundColor: '#464d4d', | |
f6710aac | 366 | borderColor: '#ABBABA', |
404bf6c8 | 367 | }, |
d962846d DC |
368 | iconCls: 'fa fa-user', |
369 | menu: [ | |
370 | { | |
371 | iconCls: 'fa fa-gear', | |
372 | text: gettext('My Settings'), | |
373 | handler: function() { | |
374 | var win = Ext.create('PVE.window.Settings'); | |
375 | win.show(); | |
f6710aac | 376 | }, |
d962846d DC |
377 | }, |
378 | { | |
379 | text: gettext('Password'), | |
c5be8d39 | 380 | itemId: 'passworditem', |
d962846d DC |
381 | iconCls: 'fa fa-fw fa-key', |
382 | handler: function() { | |
383 | var win = Ext.create('Proxmox.window.PasswordEdit', { | |
f6710aac | 384 | userid: Proxmox.UserName, |
d962846d DC |
385 | }); |
386 | win.show(); | |
f6710aac | 387 | }, |
d962846d DC |
388 | }, |
389 | { | |
390 | text: 'TFA', | |
c5be8d39 | 391 | itemId: 'tfaitem', |
d962846d DC |
392 | iconCls: 'fa fa-fw fa-lock', |
393 | handler: function(btn, event, rec) { | |
e90127be WB |
394 | Ext.state.Manager.getProvider().set('dctab', { value: 'tfa' }, true); |
395 | me.selectById('root'); | |
f6710aac | 396 | }, |
d962846d | 397 | }, |
d975d810 DT |
398 | { |
399 | iconCls: 'fa fa-paint-brush', | |
fce3b1be | 400 | text: gettext('Color Theme'), |
d975d810 DT |
401 | handler: function() { |
402 | Ext.create('Proxmox.window.ThemeEditWindow') | |
403 | .show(); | |
404 | }, | |
405 | }, | |
428d5e78 DC |
406 | { |
407 | iconCls: 'fa fa-language', | |
408 | text: gettext('Language'), | |
409 | handler: function() { | |
410 | Ext.create('Proxmox.window.LanguageEditWindow') | |
411 | .show(); | |
412 | }, | |
413 | }, | |
d962846d DC |
414 | '-', |
415 | { | |
416 | iconCls: 'fa fa-fw fa-sign-out', | |
417 | text: gettext("Logout"), | |
418 | handler: function() { | |
419 | PVE.data.ResourceStore.loadData([], false); | |
420 | me.showLogin(); | |
421 | me.setContent(null); | |
422 | var rt = me.down('pveResourceTree'); | |
423 | rt.setDatacenterText(undefined); | |
424 | rt.clearTree(); | |
425 | ||
426 | // empty the stores of the StatusPanel child items | |
427 | var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid'); | |
428 | Ext.Array.forEach(statusPanels, function(comp) { | |
429 | if (comp.getStore()) { | |
430 | comp.getStore().loadData([], false); | |
431 | } | |
432 | }); | |
f6710aac TL |
433 | }, |
434 | }, | |
435 | ], | |
436 | }, | |
437 | ], | |
787ae72a DM |
438 | }, |
439 | { | |
440 | region: 'center', | |
29aedb75 DC |
441 | stateful: true, |
442 | stateId: 'pvecenter', | |
443 | minWidth: 100, | |
444 | minHeight: 100, | |
787ae72a DM |
445 | id: 'content', |
446 | xtype: 'container', | |
834ba9e4 | 447 | layout: { type: 'card' }, |
787ae72a | 448 | border: false, |
f01259ee | 449 | margin: '0 5 0 0', |
f6710aac | 450 | items: [], |
787ae72a DM |
451 | }, |
452 | { | |
453 | region: 'west', | |
29aedb75 DC |
454 | stateful: true, |
455 | stateId: 'pvewest', | |
456 | itemId: 'west', | |
787ae72a DM |
457 | xtype: 'container', |
458 | border: false, | |
459 | layout: { type: 'vbox', align: 'stretch' }, | |
f01259ee | 460 | margin: '0 0 0 5', |
787ae72a | 461 | split: true, |
f64fa68d | 462 | width: 300, |
3a2a6964 DC |
463 | items: [ |
464 | { | |
465 | xtype: 'container', | |
466 | layout: 'hbox', | |
467 | padding: '0 0 5 0', | |
468 | items: [ | |
469 | selview, | |
470 | { | |
471 | xtype: 'button', | |
f73baabb SS |
472 | cls: 'x-btn-default-toolbar-small', |
473 | iconCls: 'fa fa-fw fa-gear x-btn-icon-el-default-toolbar-small', | |
3a2a6964 DC |
474 | handler: () => { |
475 | Ext.create('PVE.window.TreeSettingsEdit', { | |
476 | autoShow: true, | |
477 | apiCallDone: () => PVE.UIOptions.fireUIConfigChanged(), | |
478 | }); | |
479 | }, | |
480 | }, | |
481 | ], | |
482 | }, | |
483 | rtree, | |
484 | ], | |
29aedb75 DC |
485 | listeners: { |
486 | resize: function(panel, width, height) { | |
487 | var viewWidth = me.getSize().width; | |
488 | if (width > viewWidth - 100) { | |
489 | panel.setWidth(viewWidth - 100); | |
490 | } | |
f6710aac TL |
491 | }, |
492 | }, | |
787ae72a DM |
493 | }, |
494 | { | |
80e8b725 | 495 | xtype: 'pveStatusPanel', |
29aedb75 DC |
496 | stateful: true, |
497 | stateId: 'pvesouth', | |
498 | itemId: 'south', | |
787ae72a | 499 | region: 'south', |
8058410f | 500 | margin: '0 5 5 5', |
6a87871f DC |
501 | title: gettext('Logs'), |
502 | collapsible: true, | |
503 | header: false, | |
29aedb75 | 504 | height: 200, |
8058410f | 505 | split: true, |
29aedb75 DC |
506 | listeners: { |
507 | resize: function(panel, width, height) { | |
29aedb75 | 508 | var viewHeight = me.getSize().height; |
53e3ea84 | 509 | if (height > viewHeight - 150) { |
29aedb75 DC |
510 | panel.setHeight(viewHeight - 150); |
511 | } | |
f6710aac TL |
512 | }, |
513 | }, | |
514 | }, | |
515 | ], | |
787ae72a DM |
516 | }); |
517 | ||
518 | me.callParent(); | |
519 | ||
520 | me.updateUserInfo(); | |
6c18be66 DC |
521 | |
522 | // on resize, center all modal windows | |
8058410f | 523 | Ext.on('resize', function() { |
063997ed TL |
524 | let modalWindows = Ext.ComponentQuery.query('window[modal]'); |
525 | if (modalWindows.length > 0) { | |
526 | modalWindows.forEach(win => win.alignTo(me, 'c-c')); | |
6c18be66 DC |
527 | } |
528 | }); | |
e3bc13e1 DC |
529 | |
530 | let tagSelectors = []; | |
531 | ['circle', 'dense'].forEach((style) => { | |
532 | ['dark', 'light'].forEach((variant) => { | |
533 | tagSelectors.push(`.proxmox-tags-${style} .proxmox-tag-${variant}`); | |
534 | }); | |
535 | }); | |
536 | ||
537 | Ext.create('Ext.tip.ToolTip', { | |
538 | target: me.el, | |
539 | delegate: tagSelectors.join(', '), | |
540 | trackMouse: true, | |
541 | renderTo: Ext.getBody(), | |
542 | border: 0, | |
543 | minWidth: 0, | |
544 | padding: 0, | |
545 | bodyBorder: 0, | |
546 | bodyPadding: 0, | |
547 | dismissDelay: 0, | |
548 | userCls: 'pmx-tag-tooltip', | |
549 | shadow: false, | |
550 | listeners: { | |
551 | beforeshow: function(tip) { | |
552 | let tag = Ext.htmlEncode(tip.triggerElement.innerHTML); | |
553 | let tagEl = Proxmox.Utils.getTagElement(tag, PVE.UIOptions.tagOverrides); | |
554 | tip.update(`<span class="proxmox-tags-full">${tagEl}</span>`); | |
555 | }, | |
556 | }, | |
557 | }); | |
f6710aac | 558 | }, |
787ae72a DM |
559 | }); |
560 |