f64fa68d8c
we got much more wide screens and higher resolutions nowadays than when this width was decided (before the famous imported from SVN commit in 2011). Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
500 lines
11 KiB
JavaScript
500 lines
11 KiB
JavaScript
/*
|
|
* Workspace base class
|
|
*
|
|
* popup login window when auth fails (call onLogin handler)
|
|
* update (re-login) ticket every 15 minutes
|
|
*
|
|
*/
|
|
|
|
Ext.define('PVE.Workspace', {
|
|
extend: 'Ext.container.Viewport',
|
|
|
|
title: 'Proxmox Virtual Environment',
|
|
|
|
loginData: null, // Data from last login call
|
|
|
|
onLogin: function(loginData) {
|
|
// override me
|
|
},
|
|
|
|
// private
|
|
updateLoginData: function(loginData) {
|
|
let me = this;
|
|
me.loginData = loginData;
|
|
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;
|
|
|
|
me.onLogin(loginData);
|
|
},
|
|
|
|
// private
|
|
showLogin: function() {
|
|
let me = this;
|
|
|
|
Proxmox.Utils.authClear();
|
|
Ext.state.Manager.clear('GuiCap');
|
|
Proxmox.UserName = null;
|
|
me.loginData = null;
|
|
|
|
if (!me.login) {
|
|
me.login = Ext.create('PVE.window.LoginWindow', {
|
|
handler: function(data) {
|
|
me.login = null;
|
|
me.updateLoginData(data);
|
|
Proxmox.Utils.checked_command(Ext.emptyFn); // display subscription status
|
|
},
|
|
});
|
|
}
|
|
me.onLogin(null);
|
|
me.login.show();
|
|
},
|
|
|
|
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 || 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 (!Proxmox.Utils.authOK()) {
|
|
me.showLogin();
|
|
} else if (me.loginData) {
|
|
me.onLogin(me.loginData);
|
|
}
|
|
|
|
Ext.TaskManager.start({
|
|
run: function() {
|
|
let ticket = Proxmox.Utils.authOK();
|
|
if (!ticket || !Proxmox.UserName) {
|
|
return;
|
|
}
|
|
|
|
Ext.Ajax.request({
|
|
params: {
|
|
username: Proxmox.UserName,
|
|
password: ticket,
|
|
},
|
|
url: '/api2/json/access/ticket',
|
|
method: 'POST',
|
|
success: function(response, opts) {
|
|
let obj = Ext.decode(response.responseText);
|
|
me.updateLoginData(obj.data);
|
|
},
|
|
});
|
|
},
|
|
interval: 15 * 60 * 1000,
|
|
});
|
|
},
|
|
});
|
|
|
|
Ext.define('PVE.StdWorkspace', {
|
|
extend: 'PVE.Workspace',
|
|
|
|
alias: ['widget.pveStdWorkspace'],
|
|
|
|
// private
|
|
setContent: function(comp) {
|
|
let me = this;
|
|
|
|
let view = me.child('#content');
|
|
let layout = view.getLayout();
|
|
let current = layout.getActiveItem();
|
|
|
|
if (comp) {
|
|
Proxmox.Utils.setErrorMask(view, false);
|
|
comp.border = false;
|
|
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) {
|
|
let me = this;
|
|
me.down('pveResourceTree').selectById(nodeid);
|
|
},
|
|
|
|
onLogin: function(loginData) {
|
|
let me = this;
|
|
|
|
me.updateUserInfo();
|
|
|
|
if (loginData) {
|
|
PVE.data.ResourceStore.startUpdate();
|
|
|
|
Proxmox.Utils.API2Request({
|
|
url: '/version',
|
|
method: 'GET',
|
|
success: function(response) {
|
|
PVE.VersionInfo = response.result.data;
|
|
me.updateVersionInfo();
|
|
},
|
|
});
|
|
|
|
PVE.Utils.updateUIOptions();
|
|
|
|
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');
|
|
}
|
|
},
|
|
});
|
|
|
|
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() {
|
|
let me = this;
|
|
|
|
let ui = me.query('#versioninfo')[0];
|
|
|
|
if (PVE.VersionInfo) {
|
|
let version = PVE.VersionInfo.version;
|
|
ui.update('Virtual Environment ' + version);
|
|
} else {
|
|
ui.update('Virtual Environment');
|
|
}
|
|
ui.updateLayout();
|
|
},
|
|
|
|
initComponent: function() {
|
|
let me = this;
|
|
|
|
Ext.History.init();
|
|
|
|
let appState = Ext.create('PVE.StateProvider');
|
|
Ext.state.Manager.setProvider(appState);
|
|
|
|
let selview = Ext.create('PVE.form.ViewSelector');
|
|
|
|
let rtree = Ext.createWidget('pveResourceTree', {
|
|
viewFilter: selview.getViewFilter(),
|
|
flex: 1,
|
|
selModel: {
|
|
selType: 'treemodel',
|
|
listeners: {
|
|
selectionchange: function(sm, selected) {
|
|
if (selected.length <= 0) {
|
|
return;
|
|
}
|
|
let treeNode = selected[0];
|
|
let treeTypeToClass = {
|
|
root: 'PVE.dc.Config',
|
|
node: 'PVE.node.Config',
|
|
qemu: 'PVE.qemu.Config',
|
|
lxc: 'pveLXCConfig',
|
|
storage: 'PVE.storage.Browser',
|
|
sdn: 'PVE.sdn.Browser',
|
|
pool: 'pvePoolConfig',
|
|
};
|
|
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) {
|
|
if (records) {
|
|
let view = combo.getViewFilter();
|
|
rtree.setViewFilter(view);
|
|
}
|
|
});
|
|
|
|
let caps = appState.get('GuiCap');
|
|
|
|
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() {
|
|
let wiz = Ext.create('PVE.qemu.CreateWizard', {});
|
|
wiz.show();
|
|
},
|
|
});
|
|
|
|
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() {
|
|
let wiz = Ext.create('PVE.lxc.CreateWizard', {});
|
|
wiz.show();
|
|
},
|
|
});
|
|
|
|
appState.on('statechange', function(sp, key, value) {
|
|
if (key === 'GuiCap' && value) {
|
|
caps = value;
|
|
createVM.setDisabled(!caps.vms['VM.Allocate']);
|
|
createCT.setDisabled(!caps.vms['VM.Allocate']);
|
|
}
|
|
});
|
|
|
|
Ext.apply(me, {
|
|
layout: { type: 'border' },
|
|
border: false,
|
|
items: [
|
|
{
|
|
region: 'north',
|
|
title: gettext('Header'), // for ARIA
|
|
header: false, // avoid rendering the title
|
|
layout: {
|
|
type: 'hbox',
|
|
align: 'middle',
|
|
},
|
|
baseCls: 'x-plain',
|
|
defaults: {
|
|
baseCls: 'x-plain',
|
|
},
|
|
border: false,
|
|
margin: '2 0 2 5',
|
|
items: [
|
|
{
|
|
xtype: 'proxmoxlogo',
|
|
},
|
|
{
|
|
minWidth: 150,
|
|
id: 'versioninfo',
|
|
html: 'Virtual Environment',
|
|
style: {
|
|
'font-size': '14px',
|
|
'line-height': '18px',
|
|
},
|
|
},
|
|
{
|
|
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',
|
|
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,
|
|
});
|
|
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-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,
|
|
margin: '0 5 0 0',
|
|
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: 300,
|
|
items: [selview, rtree],
|
|
listeners: {
|
|
resize: function(panel, width, height) {
|
|
var viewWidth = me.getSize().width;
|
|
if (width > viewWidth - 100) {
|
|
panel.setWidth(viewWidth - 100);
|
|
}
|
|
},
|
|
},
|
|
},
|
|
{
|
|
xtype: 'pveStatusPanel',
|
|
stateful: true,
|
|
stateId: 'pvesouth',
|
|
itemId: 'south',
|
|
region: 'south',
|
|
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) {
|
|
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'));
|
|
}
|
|
});
|
|
},
|
|
});
|
|
|