]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/window/Edit.js
edit window: add submitUrl config
[proxmox-widget-toolkit.git] / src / window / Edit.js
1 Ext.define('Proxmox.window.Edit', {
2 extend: 'Ext.window.Window',
3 alias: 'widget.proxmoxWindowEdit',
4
5 // autoLoad trigger a load() after component creation
6 autoLoad: false,
7 // set extra options like params for the load request
8 autoLoadOptions: undefined,
9
10 // to submit extra params on load and submit, useful, e.g., if not all ID
11 // parameters are included in the URL
12 extraRequestParams: {},
13
14 resizable: false,
15
16 // use this to atimatically generate a title like `Create: <subject>`
17 subject: undefined,
18
19 // set isCreate to true if you want a Create button (instead OK and RESET)
20 isCreate: false,
21
22 // set to true if you want an Add button (instead of Create)
23 isAdd: false,
24
25 // set to true if you want an Remove button (instead of Create)
26 isRemove: false,
27
28 // custom submitText
29 submitText: undefined,
30
31 backgroundDelay: 0,
32
33 // string or function, called as (url, values) - useful if the ID of the
34 // new object is part of the URL, or that URL differs from GET/PUT URL
35 submitUrl: Ext.identityFn,
36
37 // needed for finding the reference to submitbutton
38 // because we do not have a controller
39 referenceHolder: true,
40 defaultButton: 'submitbutton',
41
42 // finds the first form field
43 defaultFocus: 'field:focusable[disabled=false][hidden=false]',
44
45 showProgress: false,
46
47 showTaskViewer: false,
48
49 // gets called if we have a progress bar or taskview and it detected that
50 // the task finished. function(success)
51 taskDone: Ext.emptyFn,
52
53 // gets called when the api call is finished, right at the beginning
54 // function(success, response, options)
55 apiCallDone: Ext.emptyFn,
56
57 // assign a reference from docs, to add a help button docked to the
58 // bottom of the window. If undefined we magically fall back to the
59 // onlineHelp of our first item, if set.
60 onlineHelp: undefined,
61
62 isValid: function() {
63 let me = this;
64
65 let form = me.formPanel.getForm();
66 return form.isValid();
67 },
68
69 getValues: function(dirtyOnly) {
70 let me = this;
71
72 let values = {};
73 Ext.apply(values, me.extraRequestParams);
74
75 let form = me.formPanel.getForm();
76
77 form.getFields().each(function(field) {
78 if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) {
79 Proxmox.Utils.assemble_field_data(values, field.getSubmitData());
80 }
81 });
82
83 Ext.Array.each(me.query('inputpanel'), function(panel) {
84 Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly));
85 });
86
87 return values;
88 },
89
90 setValues: function(values) {
91 let me = this;
92
93 let form = me.formPanel.getForm();
94 let formfields = form.getFields();
95
96 Ext.iterate(values, function(id, val) {
97 let fields = formfields.filterBy((f) =>
98 (f.id === id || f.name === id || f.dataIndex === id) && !f.up('inputpanel'),
99 );
100 fields.each((field) => {
101 field.setValue(val);
102 if (form.trackResetOnLoad) {
103 field.resetOriginalValue();
104 }
105 });
106 });
107
108 Ext.Array.each(me.query('inputpanel'), function(panel) {
109 panel.setValues(values);
110 });
111 },
112
113 setSubmitText: function(text) {
114 this.lookup('submitbutton').setText(text);
115 },
116
117 submit: function() {
118 let me = this;
119
120 let form = me.formPanel.getForm();
121
122 let values = me.getValues();
123 Ext.Object.each(values, function(name, val) {
124 if (Object.prototype.hasOwnProperty.call(values, name)) {
125 if (Ext.isArray(val) && !val.length) {
126 values[name] = '';
127 }
128 }
129 });
130
131 if (me.digest) {
132 values.digest = me.digest;
133 }
134
135 if (me.backgroundDelay) {
136 values.background_delay = me.backgroundDelay;
137 }
138
139 let url = Ext.isFunction(me.submitUrl)
140 ? me.submitUrl(me.url, values)
141 : me.submitUrl || me.url;
142 if (me.method === 'DELETE') {
143 url = url + "?" + Ext.Object.toQueryString(values);
144 values = undefined;
145 }
146
147 Proxmox.Utils.API2Request({
148 url: url,
149 waitMsgTarget: me,
150 method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'),
151 params: values,
152 failure: function(response, options) {
153 me.apiCallDone(false, response, options);
154
155 if (response.result && response.result.errors) {
156 form.markInvalid(response.result.errors);
157 }
158 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
159 },
160 success: function(response, options) {
161 let hasProgressBar =
162 (me.backgroundDelay || me.showProgress || me.showTaskViewer) &&
163 response.result.data;
164
165 me.apiCallDone(true, response, options);
166
167 if (hasProgressBar) {
168 // stay around so we can trigger our close events
169 // when background action is completed
170 me.hide();
171
172 let upid = response.result.data;
173 let viewerClass = me.showTaskViewer ? 'Viewer' : 'Progress';
174 Ext.create('Proxmox.window.Task' + viewerClass, {
175 autoShow: true,
176 upid: upid,
177 taskDone: me.taskDone,
178 listeners: {
179 destroy: function() {
180 me.close();
181 },
182 },
183 });
184 } else {
185 me.close();
186 }
187 },
188 });
189 },
190
191 load: function(options) {
192 let me = this;
193
194 let form = me.formPanel.getForm();
195
196 options = options || {};
197
198 let newopts = Ext.apply({
199 waitMsgTarget: me,
200 }, options);
201
202 if (Object.keys(me.extraRequestParams).length > 0) {
203 let params = newopts.params || {};
204 Ext.applyIf(params, me.extraRequestParams);
205 newopts.params = params;
206 }
207
208 let createWrapper = function(successFn) {
209 Ext.apply(newopts, {
210 url: me.url,
211 method: 'GET',
212 success: function(response, opts) {
213 form.clearInvalid();
214 me.digest = response.result.digest || response.result.data.digest;
215 if (successFn) {
216 successFn(response, opts);
217 } else {
218 me.setValues(response.result.data);
219 }
220 // hack: fix ExtJS bug
221 Ext.Array.each(me.query('radiofield'), function(f) {
222 f.resetOriginalValue();
223 });
224 },
225 failure: function(response, opts) {
226 Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() {
227 me.close();
228 });
229 },
230 });
231 };
232
233 createWrapper(options.success);
234
235 Proxmox.Utils.API2Request(newopts);
236 },
237
238 initComponent: function() {
239 let me = this;
240
241 if (!me.url) {
242 throw "no url specified";
243 }
244
245 if (me.create) {throw "deprecated parameter, use isCreate";}
246
247 let items = Ext.isArray(me.items) ? me.items : [me.items];
248
249 me.items = undefined;
250
251 me.formPanel = Ext.create('Ext.form.Panel', {
252 url: me.url,
253 method: me.method || 'PUT',
254 trackResetOnLoad: true,
255 bodyPadding: me.bodyPadding !== undefined ? me.bodyPadding : 10,
256 border: false,
257 defaults: Ext.apply({}, me.defaults, {
258 border: false,
259 }),
260 fieldDefaults: Ext.apply({}, me.fieldDefaults, {
261 labelWidth: 100,
262 anchor: '100%',
263 }),
264 items: items,
265 });
266
267 let inputPanel = me.formPanel.down('inputpanel');
268
269 let form = me.formPanel.getForm();
270
271 let submitText;
272 if (me.isCreate) {
273 if (me.submitText) {
274 submitText = me.submitText;
275 } else if (me.isAdd) {
276 submitText = gettext('Add');
277 } else if (me.isRemove) {
278 submitText = gettext('Remove');
279 } else {
280 submitText = gettext('Create');
281 }
282 } else {
283 submitText = me.submitText || gettext('OK');
284 }
285
286 let submitBtn = Ext.create('Ext.Button', {
287 reference: 'submitbutton',
288 text: submitText,
289 disabled: !me.isCreate,
290 handler: function() {
291 me.submit();
292 },
293 });
294
295 let resetBtn = Ext.create('Ext.Button', {
296 text: 'Reset',
297 disabled: true,
298 handler: function() {
299 form.reset();
300 },
301 });
302
303 let set_button_status = function() {
304 let valid = form.isValid();
305 let dirty = form.isDirty();
306 submitBtn.setDisabled(!valid || !(dirty || me.isCreate));
307 resetBtn.setDisabled(!dirty);
308
309 if (inputPanel && inputPanel.hasAdvanced) {
310 // we want to show the advanced options
311 // as soon as some of it is not valid
312 let advancedItems = me.down('#advancedContainer').query('field');
313 let allAdvancedValid = true;
314 advancedItems.forEach(function(field) {
315 if (!field.isValid()) {
316 allAdvancedValid = false;
317 }
318 });
319
320 if (!allAdvancedValid) {
321 inputPanel.setAdvancedVisible(true);
322 me.down('#advancedcb').setValue(true);
323 }
324 }
325 };
326
327 form.on('dirtychange', set_button_status);
328 form.on('validitychange', set_button_status);
329
330 let colwidth = 300;
331 if (me.fieldDefaults && me.fieldDefaults.labelWidth) {
332 colwidth += me.fieldDefaults.labelWidth - 100;
333 }
334
335 let twoColumn = inputPanel && (inputPanel.column1 || inputPanel.column2);
336
337 if (me.subject && !me.title) {
338 me.title = Proxmox.Utils.dialog_title(me.subject, me.isCreate, me.isAdd);
339 }
340
341 if (me.isCreate) {
342 me.buttons = [submitBtn];
343 } else {
344 me.buttons = [submitBtn, resetBtn];
345 }
346
347 if (inputPanel && inputPanel.hasAdvanced) {
348 let sp = Ext.state.Manager.getProvider();
349 let advchecked = sp.get('proxmox-advanced-cb');
350 inputPanel.setAdvancedVisible(advchecked);
351 me.buttons.unshift(
352 {
353 xtype: 'proxmoxcheckbox',
354 itemId: 'advancedcb',
355 boxLabelAlign: 'before',
356 boxLabel: gettext('Advanced'),
357 stateId: 'proxmox-advanced-cb',
358 value: advchecked,
359 listeners: {
360 change: function(cb, val) {
361 inputPanel.setAdvancedVisible(val);
362 sp.set('proxmox-advanced-cb', val);
363 },
364 },
365 },
366 );
367 }
368
369 let onlineHelp = me.onlineHelp;
370 if (!onlineHelp && inputPanel && inputPanel.onlineHelp) {
371 onlineHelp = inputPanel.onlineHelp;
372 }
373
374 if (onlineHelp) {
375 let helpButton = Ext.create('Proxmox.button.Help');
376 me.buttons.unshift(helpButton, '->');
377 Ext.GlobalEvents.fireEvent('proxmoxShowHelp', onlineHelp);
378 }
379
380 Ext.applyIf(me, {
381 modal: true,
382 width: twoColumn ? colwidth*2 : colwidth,
383 border: false,
384 items: [me.formPanel],
385 });
386
387 me.callParent();
388
389 // always mark invalid fields
390 me.on('afterlayout', function() {
391 // on touch devices, the isValid function
392 // triggers a layout, which triggers an isValid
393 // and so on
394 // to prevent this we disable the layouting here
395 // and enable it afterwards
396 me.suspendLayout = true;
397 me.isValid();
398 me.suspendLayout = false;
399 });
400
401 if (me.autoLoad) {
402 me.load(me.autoLoadOptions);
403 }
404 },
405 });