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