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) {
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();
+ }
}
});
- document.title = me.title;
-
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
+ 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 ]
- });
-
- me.callParent();
- }
+ },
});
Ext.define('PVE.StdWorkspace', {
// private
setContent: function(comp) {
- var me = this;
-
- var cont = me.child('#content');
- cont.removeAll(true);
+ let me = this;
+
+ 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);
- cont.updateLayout();
- }
- // 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');
- //}
+ 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
+ }
},
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('<div class="x-unselectable" style="white-space:nowrap;">' + msg + '</div>');
- } 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<br>' + gettext('Version') + ': ' + 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']);
items: [
{
region: 'north',
- height: 30,
- layout: {
+ title: gettext('Header'), // for ARIA
+ header: false, // avoid rendering the title
+ layout: {
type: 'hbox',
+ align: 'middle',
},
- baseCls: 'x-plain',
+ baseCls: 'x-plain',
defaults: {
- baseCls: 'x-plain'
+ baseCls: 'x-plain',
},
border: false,
- margin: '2 0 5 0',
+ margin: '2 0 2 5',
items: [
{
- margin: '0 0 0 4',
- html: '<a class="x-unselectable" target=_blank href="http://www.proxmox.com">' +
- '<img height=30 width=209 src="/pve2/images/proxmox_logo.png"/></a>'
+ xtype: 'proxmoxlogo',
},
{
- minWidth: 200,
- flex: 1,
+ minWidth: 150,
id: 'versioninfo',
- html: 'Proxmox Virtual Environment',
- height: 30
+ html: 'Virtual Environment',
+ style: {
+ 'font-size': '14px',
+ 'line-height': '18px',
+ },
},
{
- pack: 'end',
- margin: '8 10 0 10',
- 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: '3 5 0 0',
+ 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: 'fit' },
+ 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(`<span class="proxmox-tags-full">${tagEl}</span>`);
+ },
+ },
+ });
+ },
});