]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/form/VMSelector.js
guest import: allow setting VLAN-tag
[pve-manager.git] / www / manager6 / form / VMSelector.js
1 /* filter is a javascript builtin, but extjs calls it also filter */
2 Ext.define('PVE.form.VMSelector', {
3 extend: 'Ext.grid.Panel',
4 alias: 'widget.vmselector',
5
6 mixins: {
7 field: 'Ext.form.field.Field',
8 },
9
10 allowBlank: true,
11 selectAll: false,
12 isFormField: true,
13
14 plugins: 'gridfilters',
15
16 store: {
17 model: 'PVEResources',
18 sorters: 'vmid',
19 },
20
21 userCls: 'proxmox-tags-circle',
22
23 columnsDeclaration: [
24 {
25 header: 'ID',
26 dataIndex: 'vmid',
27 width: 80,
28 filter: {
29 type: 'number',
30 },
31 },
32 {
33 header: gettext('Node'),
34 dataIndex: 'node',
35 },
36 {
37 header: gettext('Status'),
38 dataIndex: 'status',
39 filter: {
40 type: 'list',
41 },
42 },
43 {
44 header: gettext('Name'),
45 dataIndex: 'name',
46 flex: 1,
47 filter: {
48 type: 'string',
49 },
50 },
51 {
52 header: gettext('Pool'),
53 dataIndex: 'pool',
54 filter: {
55 type: 'list',
56 },
57 },
58 {
59 header: gettext('Type'),
60 dataIndex: 'type',
61 width: 120,
62 renderer: function(value) {
63 if (value === 'qemu') {
64 return gettext('Virtual Machine');
65 } else if (value === 'lxc') {
66 return gettext('LXC Container');
67 }
68
69 return '';
70 },
71 filter: {
72 type: 'list',
73 store: {
74 data: [
75 { id: 'qemu', text: gettext('Virtual Machine') },
76 { id: 'lxc', text: gettext('LXC Container') },
77 ],
78 un: function() {
79 // Due to EXTJS-18711. we have to do a static list via a store but to avoid
80 // creating an object, we have to have an empty pseudo un function
81 },
82 },
83 },
84 },
85 {
86 header: gettext('Tags'),
87 dataIndex: 'tags',
88 renderer: tags => PVE.Utils.renderTags(tags, PVE.UIOptions.tagOverrides),
89 flex: 1,
90 },
91 {
92 header: 'HA ' + gettext('Status'),
93 dataIndex: 'hastate',
94 flex: 1,
95 filter: {
96 type: 'list',
97 },
98 },
99 ],
100
101 // should be a list of 'dataIndex' values, if 'undefined' all declared columns will be included
102 columnSelection: undefined,
103
104 selModel: {
105 selType: 'checkboxmodel',
106 mode: 'SIMPLE',
107 },
108
109 checkChangeEvents: [
110 'selectionchange',
111 'change',
112 ],
113
114 listeners: {
115 selectionchange: function() {
116 // to trigger validity and error checks
117 this.checkChange();
118 },
119 },
120
121 getValue: function() {
122 var me = this;
123 if (me.savedValue !== undefined) {
124 return me.savedValue;
125 }
126 var sm = me.getSelectionModel();
127 var selection = sm.getSelection();
128 var values = [];
129 var store = me.getStore();
130 selection.forEach(function(item) {
131 // only add if not filtered
132 if (store.findExact('vmid', item.data.vmid) !== -1) {
133 values.push(item.data.vmid);
134 }
135 });
136 return values;
137 },
138
139 setValueSelection: function(value) {
140 let me = this;
141
142 let store = me.getStore();
143 let notFound = [];
144 let selection = value.map(item => {
145 let found = store.findRecord('vmid', item, 0, false, true, true);
146 if (!found) {
147 if (Ext.isNumeric(item)) {
148 notFound.push(item);
149 } else {
150 console.warn(`invalid item in vm selection: ${item}`);
151 }
152 }
153 return found;
154 }).filter(r => r);
155
156 for (const vmid of notFound) {
157 let rec = store.add({
158 vmid,
159 node: 'unknown',
160 });
161 selection.push(rec[0]);
162 }
163
164 let sm = me.getSelectionModel();
165 if (selection.length) {
166 sm.select(selection);
167 } else {
168 sm.deselectAll();
169 }
170 // to correctly trigger invalid class
171 me.getErrors();
172 },
173
174 setValue: function(value) {
175 let me = this;
176 value ??= [];
177 if (!Ext.isArray(value)) {
178 value = value.split(',').filter(v => v !== '');
179 }
180
181 let store = me.getStore();
182 if (!store.isLoaded()) {
183 me.savedValue = value;
184 store.on('load', function() {
185 me.setValueSelection(value);
186 delete me.savedValue;
187 }, { single: true });
188 } else {
189 me.setValueSelection(value);
190 }
191 return me.mixins.field.setValue.call(me, value);
192 },
193
194 getErrors: function(value) {
195 let me = this;
196 if (!me.isDisabled() && me.allowBlank === false &&
197 me.getValue().length === 0) {
198 me.addBodyCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
199 return [gettext('No VM selected')];
200 }
201
202 me.removeBodyCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
203 return [];
204 },
205
206 setDisabled: function(disabled) {
207 let me = this;
208 let res = me.callParent([disabled]);
209 me.getErrors();
210 return res;
211 },
212
213 initComponent: function() {
214 let me = this;
215
216 let columns = me.columnsDeclaration.filter((column) =>
217 me.columnSelection ? me.columnSelection.indexOf(column.dataIndex) !== -1 : true,
218 ).map((x) => x);
219
220 me.columns = columns;
221
222 me.callParent();
223
224 me.getStore().load({ params: { type: 'vm' } });
225
226 if (me.nodename) {
227 me.getStore().addFilter({
228 property: 'node',
229 exactMatch: true,
230 value: me.nodename,
231 });
232 }
233
234 // only show the relevant guests by default
235 if (me.action) {
236 var statusfilter = '';
237 switch (me.action) {
238 case 'startall':
239 statusfilter = 'stopped';
240 break;
241 case 'stopall':
242 statusfilter = 'running';
243 break;
244 }
245 if (statusfilter !== '') {
246 me.getStore().addFilter([{
247 property: 'template',
248 value: 0,
249 }, {
250 id: 'x-gridfilter-status',
251 operator: 'in',
252 property: 'status',
253 value: [statusfilter],
254 }]);
255 }
256 }
257
258 if (me.selectAll) {
259 me.mon(me.getStore(), 'load', function() {
260 me.getSelectionModel().selectAll(false);
261 });
262 }
263 },
264 });
265
266
267 Ext.define('PVE.form.VMComboSelector', {
268 extend: 'Proxmox.form.ComboGrid',
269 alias: 'widget.vmComboSelector',
270
271 valueField: 'vmid',
272 displayField: 'vmid',
273
274 autoSelect: false,
275 editable: true,
276 anyMatch: true,
277 forceSelection: true,
278
279 store: {
280 model: 'PVEResources',
281 autoLoad: true,
282 sorters: 'vmid',
283 filters: [{
284 property: 'type',
285 value: /lxc|qemu/,
286 }],
287 },
288
289 listConfig: {
290 width: 600,
291 plugins: 'gridfilters',
292 columns: [
293 {
294 header: 'ID',
295 dataIndex: 'vmid',
296 width: 80,
297 filter: {
298 type: 'number',
299 },
300 },
301 {
302 header: gettext('Name'),
303 dataIndex: 'name',
304 flex: 1,
305 filter: {
306 type: 'string',
307 },
308 },
309 {
310 header: gettext('Node'),
311 dataIndex: 'node',
312 },
313 {
314 header: gettext('Status'),
315 dataIndex: 'status',
316 filter: {
317 type: 'list',
318 },
319 },
320 {
321 header: gettext('Pool'),
322 dataIndex: 'pool',
323 hidden: true,
324 filter: {
325 type: 'list',
326 },
327 },
328 {
329 header: gettext('Type'),
330 dataIndex: 'type',
331 width: 120,
332 renderer: function(value) {
333 if (value === 'qemu') {
334 return gettext('Virtual Machine');
335 } else if (value === 'lxc') {
336 return gettext('LXC Container');
337 }
338
339 return '';
340 },
341 filter: {
342 type: 'list',
343 store: {
344 data: [
345 { id: 'qemu', text: gettext('Virtual Machine') },
346 { id: 'lxc', text: gettext('LXC Container') },
347 ],
348 un: function() { /* due to EXTJS-18711 */ },
349 },
350 },
351 },
352 {
353 header: 'HA ' + gettext('Status'),
354 dataIndex: 'hastate',
355 hidden: true,
356 flex: 1,
357 filter: {
358 type: 'list',
359 },
360 },
361 ],
362 },
363 });