X-Git-Url: https://git.proxmox.com/?p=pve-manager.git;a=blobdiff_plain;f=www%2Fmanager6%2FWorkspace.js;h=d0f7ff76053f29b0a0dfd1dc2d9aea6fd69c505b;hp=b970223ac72709eafa21917d209ebaae58f95547;hb=refs%2Fheads%2Fmaster;hpb=f76884fda7413389247d5e23cfe2968602bd6047 diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js index b970223a..52c66108 100644 --- a/www/manager6/Workspace.js +++ b/www/manager6/Workspace.js @@ -13,31 +13,35 @@ Ext.define('PVE.Workspace', { loginData: null, // Data from last login call - onLogin: function(loginData) {}, + onLogin: function(loginData) { + // override me + }, // private updateLoginData: function(loginData) { - var me = this; + let me = this; me.loginData = loginData; - PVE.CSRFPreventionToken = loginData.CSRFPreventionToken; - PVE.UserName = loginData.username; + Proxmox.Utils.setAuthData(loginData); + + let rt = me.down('pveResourceTree'); + rt.setDatacenterText(loginData.clustername); + PVE.ClusterName = loginData.clustername; if (loginData.cap) { Ext.state.Manager.set('GuiCap', loginData.cap); } + me.response401count = 0; - // creates a session cookie (expire = null) - // that way the cookie gets deleted after browser window close - Ext.util.Cookies.set('PVEAuthCookie', loginData.ticket, null, '/', null, true); me.onLogin(loginData); }, // private showLogin: function() { - var me = this; + let me = this; - PVE.Utils.authClear(); - PVE.UserName = null; + Proxmox.Utils.authClear(); + Ext.state.Manager.clear('GuiCap'); + Proxmox.UserName = null; me.loginData = null; if (!me.login) { @@ -45,137 +49,62 @@ Ext.define('PVE.Workspace', { handler: function(data) { me.login = null; me.updateLoginData(data); - PVE.Utils.checked_command(function() {}); // display subscription status - } + Proxmox.Utils.checked_command(Ext.emptyFn); // display subscription status + }, }); } me.onLogin(null); me.login.show(); }, - initComponent : function() { - var me = this; + initComponent: function() { + let me = this; Ext.tip.QuickTipManager.init(); // fixme: what about other errors Ext.Ajax.on('requestexception', function(conn, response, options) { - if (response.status == 401) { // auth failure - me.showLogin(); + if ((response.status === 401 || response.status === '401') && !PVE.Utils.silenceAuthFailures) { // auth failure + // don't immediately show as logged out to cope better with some big + // upgrades, which may temporarily produce a false positive 401 err + me.response401count++; + if (me.response401count > 5) { + me.showLogin(); + } } }); me.callParent(); - if (!PVE.Utils.authOK()) { + if (!Proxmox.Utils.authOK()) { me.showLogin(); - } else { - if (me.loginData) { - me.onLogin(me.loginData); - } + } else if (me.loginData) { + me.onLogin(me.loginData); } Ext.TaskManager.start({ run: function() { - var ticket = PVE.Utils.authOK(); - if (!ticket || !PVE.UserName) { + let ticket = Proxmox.Utils.authOK(); + if (!ticket || !Proxmox.UserName) { return; } Ext.Ajax.request({ - params: { - username: PVE.UserName, - password: ticket + params: { + username: Proxmox.UserName, + password: ticket, }, url: '/api2/json/access/ticket', method: 'POST', success: function(response, opts) { - var obj = Ext.decode(response.responseText); + let obj = Ext.decode(response.responseText); me.updateLoginData(obj.data); - } + }, }); }, - interval: 15*60*1000 - }); - - } -}); - -Ext.define('PVE.ConsoleWorkspace', { - extend: 'PVE.Workspace', - - alias: ['widget.pveConsoleWorkspace'], - - title: gettext('Console'), - - initComponent : function() { - var me = this; - - var param = Ext.Object.fromQueryString(window.location.search); - var consoleType = me.consoleType || param.console; - - param.novnc = (param.novnc === '1') ? true : false; - - var content; - if (consoleType === 'kvm') { - me.title = "VM " + param.vmid; - if (param.vmname) { - me.title += " ('" + param.vmname + "')"; - } - content = { - xtype: 'pveKVMConsole', - novnc: param.novnc, - vmid: param.vmid, - nodename: param.node, - vmname: param.vmname, - toplevel: true - }; - } else if (consoleType === 'lxc') { - me.title = "CT " + param.vmid; - if (param.vmname) { - me.title += " ('" + param.vmname + "')"; - } - content = { - xtype: 'pveLxcConsole', - novnc: param.novnc, - vmid: param.vmid, - nodename: param.node, - vmname: param.vmname, - toplevel: true - }; - } else if (consoleType === 'shell') { - me.title = "node '" + param.node + "'"; - content = { - xtype: 'pveShell', - novnc: param.novnc, - nodename: param.node, - toplevel: true - }; - } else if (consoleType === 'upgrade') { - me.title = Ext.String.format(gettext('System upgrade on node {0}'), "'" + param.node + "'"); - content = { - xtype: 'pveShell', - novnc: param.novnc, - nodename: param.node, - ugradeSystem: true, - toplevel: true - }; - } else { - content = { - border: false, - bodyPadding: 10, - html: gettext('Error') + ': No such console type' - }; - } - - Ext.apply(me, { - layout: { type: 'fit' }, - border: false, - items: [ content ] + interval: 15 * 60 * 1000, }); - - me.callParent(); - } + }, }); Ext.define('PVE.StdWorkspace', { @@ -185,177 +114,189 @@ Ext.define('PVE.StdWorkspace', { // private setContent: function(comp) { - var me = this; - - var cont = me.child('#content'); - - var lay = cont.getLayout(); + let me = this; - var cur = lay.getActiveItem(); + let view = me.child('#content'); + let layout = view.getLayout(); + let current = layout.getActiveItem(); if (comp) { - PVE.Utils.setErrorMask(cont, false); + Proxmox.Utils.setErrorMask(view, false); comp.border = false; - cont.add(comp); - if (cur !== null && lay.getNext()) { - lay.next(); - var task = Ext.create('Ext.util.DelayedTask', function(){ - cont.remove(cur); + view.add(comp); + if (current !== null && layout.getNext()) { + layout.next(); + let task = Ext.create('Ext.util.DelayedTask', function() { + view.remove(current); }); task.delay(10); } + } else { + view.removeAll(); // helper for cleaning the content when logging out } - // else { - // TODO: display something useful - - // Note:: error mask has wrong zindex, so we do not - // use that - see bug 114 - // PVE.Utils.setErrorMask(cont, 'nothing selected'); - //} }, selectById: function(nodeid) { - var me = this; - var tree = me.down('pveResourceTree'); - tree.selectById(nodeid); - }, - - checkVmMigration: function(record) { - var me = this; - var tree = me.down('pveResourceTree'); - tree.checkVmMigration(record); + let me = this; + me.down('pveResourceTree').selectById(nodeid); }, onLogin: function(loginData) { - var me = this; + let me = this; me.updateUserInfo(); if (loginData) { PVE.data.ResourceStore.startUpdate(); - PVE.Utils.API2Request({ + Proxmox.Utils.API2Request({ url: '/version', method: 'GET', success: function(response) { PVE.VersionInfo = response.result.data; me.updateVersionInfo(); - } + }, }); - } - }, - updateUserInfo: function() { - var me = this; + PVE.UIOptions.update(); - var ui = me.query('#userinfo')[0]; + Proxmox.Utils.API2Request({ + url: '/cluster/sdn', + method: 'GET', + success: function(response) { + PVE.SDNInfo = response.result.data; + }, + failure: function(response) { + PVE.SDNInfo = null; + let ui = Ext.ComponentQuery.query('treelistitem[text="SDN"]')[0]; + if (ui) { + ui.addCls('x-hidden-display'); + } + }, + }); - if (PVE.UserName) { - var msg = Ext.String.format(gettext("You are logged in as {0}"), "'" + PVE.UserName + "'"); - ui.update('
' + msg + '
'); - } else { - ui.update(''); + Proxmox.Utils.API2Request({ + url: '/access/domains', + method: 'GET', + success: function(response) { + let [_username, realm] = Proxmox.Utils.parse_userid(Proxmox.UserName); + response.result.data.forEach((domain) => { + if (domain.realm === realm) { + let schema = PVE.Utils.authSchema[domain.type]; + if (schema) { + me.query('#tfaitem')[0].setHidden(!schema.tfa); + me.query('#passworditem')[0].setHidden(!schema.pwchange); + } + } + }); + }, + }); } + }, + + updateUserInfo: function() { + let me = this; + let ui = me.query('#userinfo')[0]; + ui.setText(Ext.String.htmlEncode(Proxmox.UserName || '')); ui.updateLayout(); }, updateVersionInfo: function() { - var me = this; + let me = this; - var ui = me.query('#versioninfo')[0]; + let ui = me.query('#versioninfo')[0]; if (PVE.VersionInfo) { - var version = PVE.VersionInfo.version + '-' + PVE.VersionInfo.release + '/' + - PVE.VersionInfo.repoid; - ui.update('Proxmox Virtual Environment ' + version); + let version = PVE.VersionInfo.version; + ui.update('Virtual Environment ' + version); } else { - ui.update('Proxmox Virtual Environment'); + ui.update('Virtual Environment'); } ui.updateLayout(); }, - initComponent : function() { - var me = this; + initComponent: function() { + let me = this; Ext.History.init(); - var sprovider = Ext.create('PVE.StateProvider'); - Ext.state.Manager.setProvider(sprovider); + let appState = Ext.create('PVE.StateProvider'); + Ext.state.Manager.setProvider(appState); - var selview = Ext.create('PVE.form.ViewSelector'); + let selview = Ext.create('PVE.form.ViewSelector', { + flex: 1, + padding: '0 5 0 0', + }); - var rtree = Ext.createWidget('pveResourceTree', { + let rtree = Ext.createWidget('pveResourceTree', { viewFilter: selview.getViewFilter(), flex: 1, selModel: { selType: 'treemodel', listeners: { selectionchange: function(sm, selected) { - var comp; - var tlckup = { + if (selected.length <= 0) { + return; + } + let treeNode = selected[0]; + let treeTypeToClass = { root: 'PVE.dc.Config', node: 'PVE.node.Config', qemu: 'PVE.qemu.Config', - lxc: 'PVE.lxc.Config', + lxc: 'pveLXCConfig', storage: 'PVE.storage.Browser', - pool: 'pvePoolConfig' + sdn: 'PVE.sdn.Browser', + pool: 'pvePoolConfig', }; - - if (selected.length > 0) { - var n = selected[0]; - comp = { - xtype: tlckup[n.data.type || 'root'] || - 'pvePanelConfig', - layout: { type: 'fit' }, - showSearch: (n.data.id === 'root') || - Ext.isDefined(n.data.groupbyid), - pveSelNode: n, - workspace: me, - viewFilter: selview.getViewFilter() - }; - PVE.curSelectedNode = n; - } - - me.setContent(comp); - } - } - } + PVE.curSelectedNode = treeNode; + me.setContent({ + xtype: treeTypeToClass[treeNode.data.type || 'root'] || 'pvePanelConfig', + showSearch: treeNode.data.id === 'root' || Ext.isDefined(treeNode.data.groupbyid), + pveSelNode: treeNode, + workspace: me, + viewFilter: selview.getViewFilter(), + }); + }, + }, + }, }); - selview.on('select', function(combo, records) { + selview.on('select', function(combo, records) { if (records) { - var view = combo.getViewFilter(); + let view = combo.getViewFilter(); rtree.setViewFilter(view); } }); - var caps = sprovider.get('GuiCap'); + let caps = appState.get('GuiCap'); - var createVM = Ext.createWidget('button', { + let createVM = Ext.createWidget('button', { pack: 'end', margin: '3 5 0 0', baseCls: 'x-btn', + iconCls: 'fa fa-desktop', text: gettext("Create VM"), disabled: !caps.vms['VM.Allocate'], handler: function() { - var wiz = Ext.create('PVE.qemu.CreateWizard', {}); + let wiz = Ext.create('PVE.qemu.CreateWizard', {}); wiz.show(); - } + }, }); - var createCT = Ext.createWidget('button', { + let createCT = Ext.createWidget('button', { pack: 'end', margin: '3 5 0 0', baseCls: 'x-btn', + iconCls: 'fa fa-cube', text: gettext("Create CT"), disabled: !caps.vms['VM.Allocate'], handler: function() { - var wiz = Ext.create('PVE.lxc.CreateWizard', {}); + let wiz = Ext.create('PVE.lxc.CreateWizard', {}); wiz.show(); - } + }, }); - sprovider.on('statechange', function(sp, key, value) { + appState.on('statechange', function(sp, key, value) { if (key === 'GuiCap' && value) { caps = value; createVM.setDisabled(!caps.vms['VM.Allocate']); @@ -369,84 +310,252 @@ Ext.define('PVE.StdWorkspace', { items: [ { region: 'north', - layout: { + title: gettext('Header'), // for ARIA + header: false, // avoid rendering the title + layout: { type: 'hbox', - align: 'middle' + align: 'middle', }, - baseCls: 'x-plain', + baseCls: 'x-plain', defaults: { - baseCls: 'x-plain' + baseCls: 'x-plain', }, border: false, margin: '2 0 2 5', items: [ { - html: '' + - '' + xtype: 'proxmoxlogo', }, { - minWidth: 200, - flex: 1, + minWidth: 150, id: 'versioninfo', - html: 'Proxmox Virtual Environment' + html: 'Virtual Environment', + style: { + 'font-size': '14px', + 'line-height': '18px', + }, }, { - pack: 'end', - margin: '0 10 0 0', - id: 'userinfo', - stateful: false + xtype: 'pveGlobalSearchField', + tree: rtree, + }, + { + flex: 1, + }, + { + xtype: 'proxmoxHelpButton', + hidden: false, + baseCls: 'x-btn', + iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ', + listenToGlobalEvent: false, + onlineHelp: 'pve_documentation_index', + text: gettext('Documentation'), + margin: '0 5 0 0', }, + createVM, + createCT, { pack: 'end', margin: '0 5 0 0', + id: 'userinfo', xtype: 'button', baseCls: 'x-btn', - text: gettext("Logout"), - handler: function() { - PVE.data.ResourceStore.stopUpdate(); - me.showLogin(); - me.setContent(); - var rt = me.down('pveResourceTree'); - rt.clearTree(); - } - }, - createVM, - createCT - ] + style: { + // proxmox dark grey p light grey as border + backgroundColor: '#464d4d', + borderColor: '#ABBABA', + }, + iconCls: 'fa fa-user', + menu: [ + { + iconCls: 'fa fa-gear', + text: gettext('My Settings'), + handler: function() { + var win = Ext.create('PVE.window.Settings'); + win.show(); + }, + }, + { + text: gettext('Password'), + itemId: 'passworditem', + iconCls: 'fa fa-fw fa-key', + handler: function() { + var win = Ext.create('Proxmox.window.PasswordEdit', { + userid: Proxmox.UserName, + confirmCurrentPassword: Proxmox.UserName !== 'root@pam', + }); + win.show(); + }, + }, + { + text: 'TFA', + itemId: 'tfaitem', + iconCls: 'fa fa-fw fa-lock', + handler: function(btn, event, rec) { + Ext.state.Manager.getProvider().set('dctab', { value: 'tfa' }, true); + me.selectById('root'); + }, + }, + { + iconCls: 'fa fa-paint-brush', + text: gettext('Color Theme'), + handler: function() { + Ext.create('Proxmox.window.ThemeEditWindow') + .show(); + }, + }, + { + iconCls: 'fa fa-language', + text: gettext('Language'), + handler: function() { + Ext.create('Proxmox.window.LanguageEditWindow') + .show(); + }, + }, + '-', + { + iconCls: 'fa fa-fw fa-sign-out', + text: gettext("Logout"), + handler: function() { + PVE.data.ResourceStore.loadData([], false); + me.showLogin(); + me.setContent(null); + var rt = me.down('pveResourceTree'); + rt.setDatacenterText(undefined); + rt.clearTree(); + + // empty the stores of the StatusPanel child items + var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid'); + Ext.Array.forEach(statusPanels, function(comp) { + if (comp.getStore()) { + comp.getStore().loadData([], false); + } + }); + }, + }, + ], + }, + ], }, { region: 'center', + stateful: true, + stateId: 'pvecenter', + minWidth: 100, + minHeight: 100, id: 'content', xtype: 'container', layout: { type: 'card' }, border: false, - stateful: false, margin: '0 5 0 0', - items: [] + items: [], }, { region: 'west', + stateful: true, + stateId: 'pvewest', + itemId: 'west', xtype: 'container', border: false, layout: { type: 'vbox', align: 'stretch' }, margin: '0 0 0 5', split: true, - width: 200, - items: [ selview, rtree ] + width: 300, + items: [ + { + xtype: 'container', + layout: 'hbox', + padding: '0 0 5 0', + items: [ + selview, + { + xtype: 'button', + cls: 'x-btn-default-toolbar-small', + iconCls: 'fa fa-fw fa-gear x-btn-icon-el-default-toolbar-small', + handler: () => { + Ext.create('PVE.window.TreeSettingsEdit', { + autoShow: true, + apiCallDone: () => PVE.UIOptions.fireUIConfigChanged(), + }); + }, + }, + ], + }, + rtree, + ], + listeners: { + resize: function(panel, width, height) { + var viewWidth = me.getSize().width; + if (width > viewWidth - 100 && viewWidth > 150) { + panel.setWidth(viewWidth - 100); + } + }, + }, }, { xtype: 'pveStatusPanel', + stateful: true, + stateId: 'pvesouth', + itemId: 'south', region: 'south', - margin:'0 5 5 5', - height: 200, - split:true - } - ] + margin: '0 5 5 5', + title: gettext('Logs'), + collapsible: true, + header: false, + height: 200, + split: true, + listeners: { + resize: function(panel, width, height) { + var viewHeight = me.getSize().height; + if (height > viewHeight - 150 && viewHeight > 200) { + panel.setHeight(viewHeight - 150); + } + }, + }, + }, + ], }); me.callParent(); me.updateUserInfo(); - } + + // on resize, center all modal windows + Ext.on('resize', function() { + let modalWindows = Ext.ComponentQuery.query('window[modal]'); + if (modalWindows.length > 0) { + modalWindows.forEach(win => win.alignTo(me, 'c-c')); + } + }); + + let tagSelectors = []; + ['circle', 'dense'].forEach((style) => { + ['dark', 'light'].forEach((variant) => { + tagSelectors.push(`.proxmox-tags-${style} .proxmox-tag-${variant}`); + }); + }); + + Ext.create('Ext.tip.ToolTip', { + target: me.el, + delegate: tagSelectors.join(', '), + trackMouse: true, + renderTo: Ext.getBody(), + border: 0, + minWidth: 0, + padding: 0, + bodyBorder: 0, + bodyPadding: 0, + dismissDelay: 0, + userCls: 'pmx-tag-tooltip', + shadow: false, + listeners: { + beforeshow: function(tip) { + let tag = Ext.htmlEncode(tip.triggerElement.innerHTML); + let tagEl = Proxmox.Utils.getTagElement(tag, PVE.UIOptions.tagOverrides); + tip.update(`${tagEl}`); + }, + }, + }); + }, });