]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/dc/OptionView.js
ff96351d5e9ebd874f364137bc422acd272ae533
[pve-manager.git] / www / manager6 / dc / OptionView.js
1 Ext.define('PVE.dc.OptionView', {
2 extend: 'Proxmox.grid.ObjectGrid',
3 alias: ['widget.pveDcOptionView'],
4
5 onlineHelp: 'datacenter_configuration_file',
6
7 monStoreErrors: true,
8
9 add_inputpanel_row: function(name, text, opts) {
10 var me = this;
11
12 opts = opts || {};
13 me.rows = me.rows || {};
14
15 let canEdit = !Object.prototype.hasOwnProperty.call(opts, 'caps') || opts.caps;
16 me.rows[name] = {
17 required: true,
18 defaultValue: opts.defaultValue,
19 header: text,
20 renderer: opts.renderer,
21 editor: canEdit ? {
22 xtype: 'proxmoxWindowEdit',
23 width: opts.width || 350,
24 subject: text,
25 onlineHelp: opts.onlineHelp,
26 fieldDefaults: {
27 labelWidth: opts.labelWidth || 100,
28 },
29 setValues: function(values) {
30 var edit_value = values[name];
31
32 if (opts.parseBeforeSet) {
33 edit_value = PVE.Parser.parsePropertyString(edit_value);
34 }
35
36 Ext.Array.each(this.query('inputpanel'), function(panel) {
37 panel.setValues(edit_value);
38 });
39 },
40 url: opts.url,
41 items: [{
42 xtype: 'inputpanel',
43 onGetValues: function(values) {
44 if (values === undefined || Object.keys(values).length === 0) {
45 return { 'delete': name };
46 }
47 var ret_val = {};
48 ret_val[name] = PVE.Parser.printPropertyString(values);
49 return ret_val;
50 },
51 items: opts.items,
52 }],
53 } : undefined,
54 };
55 },
56
57 render_bwlimits: function(value) {
58 if (!value) {
59 return gettext("None");
60 }
61
62 let parsed = PVE.Parser.parsePropertyString(value);
63 return Object.entries(parsed)
64 .map(([k, v]) => k + ": " + Proxmox.Utils.format_size(v * 1024) + "/s")
65 .join(',');
66 },
67
68 initComponent: function() {
69 var me = this;
70
71 me.add_combobox_row('keyboard', gettext('Keyboard Layout'), {
72 renderer: PVE.Utils.render_kvm_language,
73 comboItems: Object.entries(PVE.Utils.kvm_keymaps),
74 defaultValue: '__default__',
75 deleteEmpty: true,
76 });
77 me.add_text_row('http_proxy', gettext('HTTP proxy'), {
78 defaultValue: Proxmox.Utils.noneText,
79 vtype: 'HttpProxy',
80 deleteEmpty: true,
81 });
82 me.add_combobox_row('console', gettext('Console Viewer'), {
83 renderer: PVE.Utils.render_console_viewer,
84 comboItems: Object.entries(PVE.Utils.console_map),
85 defaultValue: '__default__',
86 deleteEmpty: true,
87 });
88 me.add_text_row('email_from', gettext('Email from address'), {
89 deleteEmpty: true,
90 vtype: 'proxmoxMail',
91 defaultValue: 'root@$hostname',
92 });
93 me.add_text_row('mac_prefix', gettext('MAC address prefix'), {
94 deleteEmpty: true,
95 vtype: 'MacPrefix',
96 defaultValue: Proxmox.Utils.noneText,
97 });
98 me.add_inputpanel_row('migration', gettext('Migration Settings'), {
99 renderer: PVE.Utils.render_as_property_string,
100 labelWidth: 120,
101 url: "/api2/extjs/cluster/options",
102 defaultKey: 'type',
103 items: [{
104 xtype: 'displayfield',
105 name: 'type',
106 fieldLabel: gettext('Type'),
107 value: 'secure',
108 submitValue: true,
109 }, {
110 xtype: 'proxmoxNetworkSelector',
111 name: 'network',
112 fieldLabel: gettext('Network'),
113 value: null,
114 emptyText: Proxmox.Utils.defaultText,
115 autoSelect: false,
116 skipEmptyText: true,
117 }],
118 });
119 me.add_inputpanel_row('ha', gettext('HA Settings'), {
120 renderer: PVE.Utils.render_dc_ha_opts,
121 labelWidth: 120,
122 url: "/api2/extjs/cluster/options",
123 onlineHelp: 'ha_manager_shutdown_policy',
124 items: [{
125 xtype: 'proxmoxKVComboBox',
126 name: 'shutdown_policy',
127 fieldLabel: gettext('Shutdown Policy'),
128 deleteEmpty: false,
129 value: '__default__',
130 comboItems: [
131 ['__default__', Proxmox.Utils.defaultText + ' (conditional)'],
132 ['freeze', 'freeze'],
133 ['failover', 'failover'],
134 ['migrate', 'migrate'],
135 ['conditional', 'conditional'],
136 ],
137 defaultValue: '__default__',
138 }],
139 });
140 me.add_inputpanel_row('u2f', gettext('U2F Settings'), {
141 renderer: v => !v ? Proxmox.Utils.NoneText : PVE.Parser.printPropertyString(v),
142 width: 450,
143 url: "/api2/extjs/cluster/options",
144 onlineHelp: 'pveum_configure_u2f',
145 items: [{
146 xtype: 'textfield',
147 name: 'appid',
148 fieldLabel: gettext('U2F AppID URL'),
149 emptyText: gettext('Defaults to origin'),
150 value: '',
151 deleteEmpty: true,
152 skipEmptyText: true,
153 submitEmptyText: false,
154 }, {
155 xtype: 'textfield',
156 name: 'origin',
157 fieldLabel: gettext('U2F Origin'),
158 emptyText: gettext('Defaults to requesting host URI'),
159 value: '',
160 deleteEmpty: true,
161 skipEmptyText: true,
162 submitEmptyText: false,
163 },
164 {
165 xtype: 'box',
166 height: 25,
167 html: `<span class='pmx-hint'>${gettext('Note:')}</span> `
168 + Ext.String.format(gettext('{0} is deprecated, use {1}'), 'U2F', 'WebAuthn'),
169 },
170 {
171 xtype: 'displayfield',
172 userCls: 'pmx-hint',
173 value: gettext('NOTE: Changing an AppID breaks existing U2F registrations!'),
174 }],
175 });
176 me.add_inputpanel_row('webauthn', gettext('WebAuthn Settings'), {
177 renderer: v => !v ? Proxmox.Utils.NoneText : PVE.Parser.printPropertyString(v),
178 width: 450,
179 url: "/api2/extjs/cluster/options",
180 onlineHelp: 'pveum_configure_webauthn',
181 items: [{
182 xtype: 'textfield',
183 fieldLabel: gettext('Name'),
184 name: 'rp', // NOTE: relying party consists of name and id, this is the name
185 allowBlank: false,
186 },
187 {
188 xtype: 'textfield',
189 fieldLabel: gettext('Origin'),
190 emptyText: Ext.String.format(gettext("Domain Lockdown (e.g., {0})"), document.location.origin),
191 name: 'origin',
192 allowBlank: true,
193 },
194 {
195 xtype: 'textfield',
196 fieldLabel: 'ID',
197 name: 'id',
198 allowBlank: false,
199 listeners: {
200 dirtychange: (f, isDirty) =>
201 f.up('panel').down('box[id=idChangeWarning]').setHidden(!f.originalValue || !isDirty),
202 },
203 },
204 {
205 xtype: 'container',
206 layout: 'hbox',
207 items: [
208 {
209 xtype: 'box',
210 flex: 1,
211 },
212 {
213 xtype: 'button',
214 text: gettext('Auto-fill'),
215 iconCls: 'fa fa-fw fa-pencil-square-o',
216 handler: function(button, ev) {
217 let panel = this.up('panel');
218 let fqdn = document.location.hostname;
219
220 panel.down('field[name=rp]').setValue(fqdn);
221
222 let idField = panel.down('field[name=id]');
223 let currentID = idField.getValue();
224 if (!currentID || currentID.length === 0) {
225 idField.setValue(fqdn);
226 }
227 },
228 },
229 ],
230 },
231 {
232 xtype: 'box',
233 height: 25,
234 html: `<span class='pmx-hint'>${gettext('Note:')}</span> `
235 + gettext('WebAuthn requires using a trusted certificate.'),
236 },
237 {
238 xtype: 'box',
239 id: 'idChangeWarning',
240 hidden: true,
241 padding: '5 0 0 0',
242 html: '<i class="fa fa-exclamation-triangle warning"></i> '
243 + gettext('Changing the ID breaks existing WebAuthn TFA entries.'),
244 }],
245 });
246 me.add_inputpanel_row('bwlimit', gettext('Bandwidth Limits'), {
247 renderer: me.render_bwlimits,
248 width: 450,
249 url: "/api2/extjs/cluster/options",
250 parseBeforeSet: true,
251 labelWidth: 120,
252 items: [{
253 xtype: 'pveBandwidthField',
254 name: 'default',
255 fieldLabel: gettext('Default'),
256 emptyText: gettext('none'),
257 backendUnit: "KiB",
258 },
259 {
260 xtype: 'pveBandwidthField',
261 name: 'restore',
262 fieldLabel: gettext('Backup Restore'),
263 emptyText: gettext('default'),
264 backendUnit: "KiB",
265 },
266 {
267 xtype: 'pveBandwidthField',
268 name: 'migration',
269 fieldLabel: gettext('Migration'),
270 emptyText: gettext('default'),
271 backendUnit: "KiB",
272 },
273 {
274 xtype: 'pveBandwidthField',
275 name: 'clone',
276 fieldLabel: gettext('Clone'),
277 emptyText: gettext('default'),
278 backendUnit: "KiB",
279 },
280 {
281 xtype: 'pveBandwidthField',
282 name: 'move',
283 fieldLabel: gettext('Disk Move'),
284 emptyText: gettext('default'),
285 backendUnit: "KiB",
286 }],
287 });
288 me.add_integer_row('max_workers', gettext('Maximal Workers/bulk-action'), {
289 deleteEmpty: true,
290 defaultValue: 4,
291 minValue: 1,
292 maxValue: 64, // arbitrary but generous limit as limits are good
293 });
294 me.add_inputpanel_row('next-id', gettext('Next Free VMID Range'), {
295 renderer: PVE.Utils.render_as_property_string,
296 url: "/api2/extjs/cluster/options",
297 items: [{
298 xtype: 'proxmoxintegerfield',
299 name: 'lower',
300 fieldLabel: gettext('Lower'),
301 emptyText: '100',
302 minValue: 100,
303 maxValue: 1000 * 1000 * 1000 - 1,
304 submitValue: true,
305 }, {
306 xtype: 'proxmoxintegerfield',
307 name: 'upper',
308 fieldLabel: gettext('Upper'),
309 emptyText: '1.000.000',
310 minValue: 100,
311 maxValue: 1000 * 1000 * 1000 - 1,
312 submitValue: true,
313 }],
314 });
315
316 me.selModel = Ext.create('Ext.selection.RowModel', {});
317
318 Ext.apply(me, {
319 tbar: [{
320 text: gettext('Edit'),
321 xtype: 'proxmoxButton',
322 disabled: true,
323 handler: function() { me.run_editor(); },
324 selModel: me.selModel,
325 }],
326 url: "/api2/json/cluster/options",
327 editorConfig: {
328 url: "/api2/extjs/cluster/options",
329 },
330 interval: 5000,
331 cwidth1: 200,
332 listeners: {
333 itemdblclick: me.run_editor,
334 },
335 });
336
337 me.callParent();
338
339 // set the new value for the default console
340 me.mon(me.rstore, 'load', function(store, records, success) {
341 if (!success) {
342 return;
343 }
344
345 var rec = store.getById('console');
346 PVE.UIOptions.console = rec.data.value;
347 if (rec.data.value === '__default__') {
348 delete PVE.UIOptions.console;
349 }
350 });
351
352 me.on('activate', me.rstore.startUpdate);
353 me.on('destroy', me.rstore.stopUpdate);
354 me.on('deactivate', me.rstore.stopUpdate);
355 },
356 });