1 Ext
.define('PVE.dc.OptionView', {
2 extend
: 'Proxmox.grid.ObjectGrid',
3 alias
: ['widget.pveDcOptionView'],
5 onlineHelp
: 'datacenter_configuration_file',
8 userCls
: 'proxmox-tags-full',
10 add_inputpanel_row: function(name
, text
, opts
) {
14 me
.rows
= me
.rows
|| {};
16 let canEdit
= !Object
.prototype.hasOwnProperty
.call(opts
, 'caps') || opts
.caps
;
19 defaultValue
: opts
.defaultValue
,
21 renderer
: opts
.renderer
,
23 xtype
: 'proxmoxWindowEdit',
24 width
: opts
.width
|| 350,
26 onlineHelp
: opts
.onlineHelp
,
28 labelWidth
: opts
.labelWidth
|| 100,
30 setValues: function(values
) {
31 var edit_value
= values
[name
];
33 if (opts
.parseBeforeSet
) {
34 edit_value
= PVE
.Parser
.parsePropertyString(edit_value
);
37 Ext
.Array
.each(this.query('inputpanel'), function(panel
) {
38 panel
.setValues(edit_value
);
44 onGetValues: function(values
) {
45 if (values
=== undefined || Object
.keys(values
).length
=== 0) {
46 return { 'delete': name
};
49 ret_val
[name
] = PVE
.Parser
.printPropertyString(values
);
58 render_bwlimits: function(value
) {
60 return gettext("None");
63 let parsed
= PVE
.Parser
.parsePropertyString(value
);
64 return Object
.entries(parsed
)
65 .map(([k
, v
]) => k
+ ": " + Proxmox
.Utils
.format_size(v
* 1024) + "/s")
69 initComponent: function() {
72 me
.add_combobox_row('keyboard', gettext('Keyboard Layout'), {
73 renderer
: PVE
.Utils
.render_kvm_language
,
74 comboItems
: Object
.entries(PVE
.Utils
.kvm_keymaps
),
75 defaultValue
: '__default__',
78 me
.add_text_row('http_proxy', gettext('HTTP proxy'), {
79 defaultValue
: Proxmox
.Utils
.noneText
,
83 me
.add_combobox_row('console', gettext('Console Viewer'), {
84 renderer
: PVE
.Utils
.render_console_viewer
,
85 comboItems
: Object
.entries(PVE
.Utils
.console_map
),
86 defaultValue
: '__default__',
89 me
.add_text_row('email_from', gettext('Email from address'), {
92 defaultValue
: 'root@$hostname',
94 me
.add_inputpanel_row('notify', gettext('Notify'), {
95 renderer
: v
=> !v
? 'package-updates=auto' : PVE
.Parser
.printPropertyString(v
),
97 url
: "/api2/extjs/cluster/options",
98 //onlineHelp: 'ha_manager_shutdown_policy',
100 xtype
: 'proxmoxKVComboBox',
101 name
: 'package-updates',
102 fieldLabel
: gettext('Package Updates'),
104 value
: '__default__',
106 ['__default__', Proxmox
.Utils
.defaultText
+ ' (auto)'],
107 ['auto', gettext('Automatically')],
108 ['always', gettext('Always')],
109 ['never', gettext('Never')],
111 defaultValue
: '__default__',
114 me
.add_text_row('mac_prefix', gettext('MAC address prefix'), {
117 defaultValue
: Proxmox
.Utils
.noneText
,
119 me
.add_inputpanel_row('migration', gettext('Migration Settings'), {
120 renderer
: PVE
.Utils
.render_as_property_string
,
122 url
: "/api2/extjs/cluster/options",
125 xtype
: 'displayfield',
127 fieldLabel
: gettext('Type'),
131 xtype
: 'proxmoxNetworkSelector',
133 fieldLabel
: gettext('Network'),
135 emptyText
: Proxmox
.Utils
.defaultText
,
140 me
.add_inputpanel_row('ha', gettext('HA Settings'), {
141 renderer
: PVE
.Utils
.render_dc_ha_opts
,
143 url
: "/api2/extjs/cluster/options",
144 onlineHelp
: 'ha_manager_shutdown_policy',
146 xtype
: 'proxmoxKVComboBox',
147 name
: 'shutdown_policy',
148 fieldLabel
: gettext('Shutdown Policy'),
150 value
: '__default__',
152 ['__default__', Proxmox
.Utils
.defaultText
+ ' (conditional)'],
153 ['freeze', 'freeze'],
154 ['failover', 'failover'],
155 ['migrate', 'migrate'],
156 ['conditional', 'conditional'],
158 defaultValue
: '__default__',
161 me
.add_inputpanel_row('crs', gettext('Cluster Resource Scheduling'), {
162 renderer
: PVE
.Utils
.render_as_property_string
,
165 url
: "/api2/extjs/cluster/options",
166 onlineHelp
: 'ha_manager_crs',
168 xtype
: 'proxmoxKVComboBox',
170 fieldLabel
: gettext('HA Scheduling'),
172 value
: '__default__',
174 ['__default__', Proxmox
.Utils
.defaultText
+ ' (basic)'],
175 ['basic', 'Basic (Resource Count)'],
176 ['static', 'Static Load'],
178 defaultValue
: '__default__',
180 xtype
: 'proxmoxcheckbox',
181 name
: 'ha-rebalance-on-start',
182 fieldLabel
: gettext('Rebalance on Start'),
183 boxLabel
: gettext('Use CRS to select the least loaded node when starting an HA service'),
187 me
.add_inputpanel_row('u2f', gettext('U2F Settings'), {
188 renderer
: v
=> !v
? Proxmox
.Utils
.NoneText
: PVE
.Parser
.printPropertyString(v
),
190 url
: "/api2/extjs/cluster/options",
191 onlineHelp
: 'pveum_configure_u2f',
195 fieldLabel
: gettext('U2F AppID URL'),
196 emptyText
: gettext('Defaults to origin'),
200 submitEmptyText
: false,
204 fieldLabel
: gettext('U2F Origin'),
205 emptyText
: gettext('Defaults to requesting host URI'),
209 submitEmptyText
: false,
214 html
: `<span class='pmx-hint'>${gettext('Note:')}</span> `
215 + Ext
.String
.format(gettext('{0} is deprecated, use {1}'), 'U2F', 'WebAuthn'),
218 xtype
: 'displayfield',
220 value
: gettext('NOTE: Changing an AppID breaks existing U2F registrations!'),
223 me
.add_inputpanel_row('webauthn', gettext('WebAuthn Settings'), {
224 renderer
: v
=> !v
? Proxmox
.Utils
.NoneText
: PVE
.Parser
.printPropertyString(v
),
226 url
: "/api2/extjs/cluster/options",
227 onlineHelp
: 'pveum_configure_webauthn',
230 fieldLabel
: gettext('Name'),
231 name
: 'rp', // NOTE: relying party consists of name and id, this is the name
236 fieldLabel
: gettext('Origin'),
237 emptyText
: Ext
.String
.format(gettext("Domain Lockdown (e.g., {0})"), document
.location
.origin
),
247 dirtychange
: (f
, isDirty
) =>
248 f
.up('panel').down('box[id=idChangeWarning]').setHidden(!f
.originalValue
|| !isDirty
),
261 text
: gettext('Auto-fill'),
262 iconCls
: 'fa fa-fw fa-pencil-square-o',
263 handler: function(button
, ev
) {
264 let panel
= this.up('panel');
265 let fqdn
= document
.location
.hostname
;
267 panel
.down('field[name=rp]').setValue(fqdn
);
269 let idField
= panel
.down('field[name=id]');
270 let currentID
= idField
.getValue();
271 if (!currentID
|| currentID
.length
=== 0) {
272 idField
.setValue(fqdn
);
281 html
: `<span class='pmx-hint'>${gettext('Note:')}</span> `
282 + gettext('WebAuthn requires using a trusted certificate.'),
286 id
: 'idChangeWarning',
289 html
: '<i class="fa fa-exclamation-triangle warning"></i> '
290 + gettext('Changing the ID breaks existing WebAuthn TFA entries.'),
293 me
.add_inputpanel_row('bwlimit', gettext('Bandwidth Limits'), {
294 renderer
: me
.render_bwlimits
,
296 url
: "/api2/extjs/cluster/options",
297 parseBeforeSet
: true,
300 xtype
: 'pveBandwidthField',
302 fieldLabel
: gettext('Default'),
303 emptyText
: gettext('none'),
307 xtype
: 'pveBandwidthField',
309 fieldLabel
: gettext('Backup Restore'),
310 emptyText
: gettext('default'),
314 xtype
: 'pveBandwidthField',
316 fieldLabel
: gettext('Migration'),
317 emptyText
: gettext('default'),
321 xtype
: 'pveBandwidthField',
323 fieldLabel
: gettext('Clone'),
324 emptyText
: gettext('default'),
328 xtype
: 'pveBandwidthField',
330 fieldLabel
: gettext('Disk Move'),
331 emptyText
: gettext('default'),
335 me
.add_integer_row('max_workers', gettext('Maximal Workers/bulk-action'), {
339 maxValue
: 64, // arbitrary but generous limit as limits are good
341 me
.add_inputpanel_row('next-id', gettext('Next Free VMID Range'), {
342 renderer
: PVE
.Utils
.render_as_property_string
,
343 url
: "/api2/extjs/cluster/options",
345 xtype
: 'proxmoxintegerfield',
347 fieldLabel
: gettext('Lower'),
350 maxValue
: 1000 * 1000 * 1000 - 1,
353 xtype
: 'proxmoxintegerfield',
355 fieldLabel
: gettext('Upper'),
356 emptyText
: '1.000.000',
358 maxValue
: 1000 * 1000 * 1000 - 1,
362 me
.rows
['tag-style'] = {
364 renderer
: (value
) => {
365 if (value
=== undefined) {
366 return gettext('No Overrides');
368 let colors
= PVE
.UIOptions
.parseTagOverrides(value
?.['color-map']);
369 let shape
= value
.shape
;
370 let shapeText
= PVE
.UIOptions
.tagTreeStyles
[shape
?? '__default__'];
371 let txt
= Ext
.String
.format(gettext("Tree Shape: {0}"), shapeText
);
372 let orderText
= PVE
.UIOptions
.tagOrderOptions
[value
.ordering
?? '__default__'];
373 txt
+= `, ${Ext.String.format(gettext("Ordering: {0}"), orderText)}`;
374 if (value
['case-sensitive']) {
375 txt
+= `, ${gettext('Case-Sensitive')}`;
377 if (Object
.keys(colors
).length
> 0) {
378 txt
+= `, ${gettext('Color Overrides')}: `;
379 for (const tag
of Object
.keys(colors
)) {
380 txt
+= Proxmox
.Utils
.getTagElement(tag
, colors
);
385 header
: gettext('Tag Style Override'),
387 xtype
: 'proxmoxWindowEdit',
389 subject
: gettext('Tag Color Override'),
390 onlineHelp
: 'datacenter_configuration_file',
394 url
: '/api2/extjs/cluster/options',
398 setValues: function(values
) {
399 if (values
=== undefined) {
402 values
= values
?.['tag-style'] ?? {};
403 values
.shape
= values
.shape
|| '__default__';
404 values
.colors
= values
['color-map'];
405 return Proxmox
.panel
.InputPanel
.prototype.setValues
.call(this, values
);
407 onGetValues: function(values
) {
410 style
['color-map'] = values
.colors
;
412 if (values
.shape
&& values
.shape
!== '__default__') {
413 style
.shape
= values
.shape
;
415 if (values
.ordering
) {
416 style
.ordering
= values
.ordering
;
418 if (values
['case-sensitive']) {
419 style
['case-sensitive'] = 1;
421 let value
= PVE
.Parser
.printPropertyString(style
);
424 'delete': 'tag-style',
435 xtype
: 'proxmoxComboGrid',
436 fieldLabel
: gettext('Tree Shape'),
438 displayField
: 'display',
443 header
: gettext('Option'),
444 dataIndex
: 'display',
448 header
: gettext('Preview'),
450 renderer: function(value
) {
451 let cls
= value
?? '__default__';
452 if (value
=== '__default__') {
455 let tags
= PVE
.Utils
.renderTags('preview');
456 return `<div class="proxmox-tags-${cls}">${tags}</div>`;
463 data
: Object
.entries(PVE
.UIOptions
.tagTreeStyles
).map(v
=> ({
469 defaultValue
: '__default__',
474 xtype
: 'proxmoxKVComboBox',
475 fieldLabel
: gettext('Ordering'),
476 comboItems
: Object
.entries(PVE
.UIOptions
.tagOrderOptions
),
477 defaultValue
: '__default__',
478 value
: '__default__',
482 name
: 'case-sensitive',
483 xtype
: 'proxmoxcheckbox',
484 fieldLabel
: gettext('Case-Sensitive'),
485 boxLabel
: gettext('Applies to new edits'),
489 xtype
: 'displayfield',
490 fieldLabel
: gettext('Color Overrides'),
494 xtype
: 'pveTagColorGrid',
504 me
.rows
['user-tag-access'] = {
506 renderer
: (value
) => {
507 if (value
=== undefined) {
508 return Ext
.String
.format(gettext('Mode: {0}'), 'free');
510 let mode
= value
?.['user-allow'] ?? 'free';
511 let list
= value
?.['user-allow-list']?.join(',') ?? '';
512 let modeTxt
= Ext
.String
.format(gettext('Mode: {0}'), mode
);
513 let overrides
= PVE
.UIOptions
.tagOverrides
;
514 let tags
= PVE
.Utils
.renderTags(list
, overrides
);
515 let listTxt
= tags
!== '' ? `, ${gettext('Pre-defined:')} ${tags}` : '';
516 return `${modeTxt}${listTxt}`;
518 header
: gettext('User Tag Access'),
520 xtype
: 'pveUserTagAccessEdit',
524 me
.rows
['registered-tags'] = {
526 renderer
: (value
) => {
527 if (value
=== undefined) {
528 return gettext('No Registered Tags');
530 let overrides
= PVE
.UIOptions
.tagOverrides
;
531 return PVE
.Utils
.renderTags(value
.join(','), overrides
);
533 header
: gettext('Registered Tags'),
535 xtype
: 'pveRegisteredTagEdit',
539 me
.selModel
= Ext
.create('Ext.selection.RowModel', {});
543 text
: gettext('Edit'),
544 xtype
: 'proxmoxButton',
546 handler: function() { me
.run_editor(); },
547 selModel
: me
.selModel
,
549 url
: "/api2/json/cluster/options",
551 url
: "/api2/extjs/cluster/options",
556 itemdblclick
: me
.run_editor
,
562 // set the new value for the default console
563 me
.mon(me
.rstore
, 'load', function(store
, records
, success
) {
568 var rec
= store
.getById('console');
569 PVE
.UIOptions
.options
.console
= rec
.data
.value
;
570 if (rec
.data
.value
=== '__default__') {
571 delete PVE
.UIOptions
.options
.console
;
574 PVE
.UIOptions
.options
['tag-style'] = store
.getById('tag-style')?.data
?.value
;
575 PVE
.UIOptions
.updateTagSettings(PVE
.UIOptions
.options
['tag-style']);
576 PVE
.UIOptions
.fireUIConfigChanged();
579 me
.on('activate', me
.rstore
.startUpdate
);
580 me
.on('destroy', me
.rstore
.stopUpdate
);
581 me
.on('deactivate', me
.rstore
.stopUpdate
);