]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/grid/FirewallRules.js
ui: qemu/OS defaults: code cleanup/refactoring
[pve-manager.git] / www / manager6 / grid / FirewallRules.js
CommitLineData
435cce27 1Ext.define('PVE.form.FWMacroSelector', {
0fc95a12 2 extend: 'Proxmox.form.ComboGrid',
435cce27 3 alias: 'widget.pveFWMacroSelector',
3d990919
EK
4 allowBlank: true,
5 autoSelect: false,
6 valueField: 'macro',
7 displayField: 'macro',
8 listConfig: {
9 columns: [
10 {
11 header: gettext('Macro'),
12 dataIndex: 'macro',
13 hideable: false,
f6710aac 14 width: 100,
3d990919
EK
15 },
16 {
17 header: gettext('Description'),
91535f2b 18 renderer: Ext.String.htmlEncode,
3d990919 19 flex: 1,
f6710aac
TL
20 dataIndex: 'descr',
21 },
22 ],
3d990919 23 },
435cce27
DM
24 initComponent: function() {
25 var me = this;
26
27 var store = Ext.create('Ext.data.Store', {
28 autoLoad: true,
8058410f 29 fields: ['macro', 'descr'],
435cce27
DM
30 idProperty: 'macro',
31 proxy: {
56a353b9 32 type: 'proxmox',
f6710aac 33 url: "/api2/json/cluster/firewall/macros",
435cce27
DM
34 },
35 sorters: {
36 property: 'macro',
f6710aac
TL
37 order: 'DESC',
38 },
435cce27
DM
39 });
40
41 Ext.apply(me, {
f6710aac 42 store: store,
435cce27
DM
43 });
44
45 me.callParent();
f6710aac 46 },
435cce27
DM
47});
48
49Ext.define('PVE.FirewallRulePanel', {
ef4ef788 50 extend: 'Proxmox.panel.InputPanel',
435cce27
DM
51
52 allow_iface: false,
53
54 list_refs_url: undefined,
55
56 onGetValues: function(values) {
57 var me = this;
58
59 // hack: editable ComboGrid returns nothing when empty, so we need to set ''
60 // Also, disabled text fields return nothing, so we need to set ''
61
3c37fe48 62 Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'log'], function(key) {
435cce27
DM
63 if (values[key] === undefined) {
64 values[key] = '';
65 }
66 });
67
68 delete values.modified_marker;
2a4971d8 69
435cce27
DM
70 return values;
71 },
72
8058410f 73 initComponent: function() {
435cce27
DM
74 var me = this;
75
76 if (!me.list_refs_url) {
77 throw "no list_refs_url specified";
78 }
79
80 me.column1 = [
81 {
82 // hack: we use this field to mark the form 'dirty' when the
2a4971d8 83 // record has errors- so that the user can safe the unmodified
435cce27
DM
84 // form again.
85 xtype: 'hiddenfield',
86 name: 'modified_marker',
f6710aac 87 value: '',
435cce27
DM
88 },
89 {
09cacce7 90 xtype: 'proxmoxKVComboBox',
435cce27
DM
91 name: 'type',
92 value: 'in',
e7bc7f31 93 comboItems: [['in', 'in'], ['out', 'out']],
435cce27 94 fieldLabel: gettext('Direction'),
f6710aac 95 allowBlank: false,
435cce27
DM
96 },
97 {
09cacce7 98 xtype: 'proxmoxKVComboBox',
435cce27
DM
99 name: 'action',
100 value: 'ACCEPT',
e7bc7f31 101 comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']],
435cce27 102 fieldLabel: gettext('Action'),
f6710aac
TL
103 allowBlank: false,
104 },
435cce27
DM
105 ];
106
107 if (me.allow_iface) {
108 me.column1.push({
dbed4c1c 109 xtype: 'proxmoxtextfield',
435cce27 110 name: 'iface',
d5e771ce 111 deleteEmpty: !me.isCreate,
435cce27 112 value: '',
f6710aac 113 fieldLabel: gettext('Interface'),
435cce27
DM
114 });
115 } else {
116 me.column1.push({
117 xtype: 'displayfield',
118 fieldLabel: '',
f6710aac 119 value: '',
435cce27
DM
120 });
121 }
122
fa94a977 123 me.column1.push(
435cce27
DM
124 {
125 xtype: 'displayfield',
126 fieldLabel: '',
127 height: 7,
f6710aac 128 value: '',
435cce27
DM
129 },
130 {
131 xtype: 'pveIPRefSelector',
132 name: 'source',
133 autoSelect: false,
134 editable: true,
135 base_url: me.list_refs_url,
136 value: '',
f6710aac 137 fieldLabel: gettext('Source'),
1490b5eb
AL
138 maxLength: 512,
139 maxLengthText: gettext('Too long, consider using IP sets.'),
435cce27
DM
140 },
141 {
142 xtype: 'pveIPRefSelector',
143 name: 'dest',
144 autoSelect: false,
145 editable: true,
146 base_url: me.list_refs_url,
147 value: '',
f6710aac 148 fieldLabel: gettext('Destination'),
1490b5eb
AL
149 maxLength: 512,
150 maxLengthText: gettext('Too long, consider using IP sets.'),
f6710aac 151 },
fa94a977 152 );
435cce27 153
2a4971d8 154
435cce27
DM
155 me.column2 = [
156 {
896c0d50 157 xtype: 'proxmoxcheckbox',
435cce27
DM
158 name: 'enable',
159 checked: false,
435cce27 160 uncheckedValue: 0,
f6710aac 161 fieldLabel: gettext('Enable'),
435cce27
DM
162 },
163 {
164 xtype: 'pveFWMacroSelector',
165 name: 'macro',
435cce27 166 fieldLabel: gettext('Macro'),
1fafdce8 167 editable: true,
435cce27
DM
168 allowBlank: true,
169 listeners: {
170 change: function(f, value) {
1fafdce8 171 if (value === null) {
435cce27
DM
172 me.down('field[name=proto]').setDisabled(false);
173 me.down('field[name=sport]').setDisabled(false);
174 me.down('field[name=dport]').setDisabled(false);
175 } else {
176 me.down('field[name=proto]').setDisabled(true);
177 me.down('field[name=proto]').setValue('');
178 me.down('field[name=sport]').setDisabled(true);
179 me.down('field[name=sport]').setValue('');
180 me.down('field[name=dport]').setDisabled(true);
7c7ae44f 181 me.down('field[name=dport]').setValue('');
435cce27 182 }
f6710aac
TL
183 },
184 },
435cce27
DM
185 },
186 {
187 xtype: 'pveIPProtocolSelector',
188 name: 'proto',
189 autoSelect: false,
190 editable: true,
191 value: '',
f6710aac 192 fieldLabel: gettext('Protocol'),
435cce27
DM
193 },
194 {
195 xtype: 'displayfield',
196 fieldLabel: '',
197 height: 7,
f6710aac 198 value: '',
435cce27
DM
199 },
200 {
201 xtype: 'textfield',
202 name: 'sport',
203 value: '',
f6710aac 204 fieldLabel: gettext('Source port'),
435cce27
DM
205 },
206 {
207 xtype: 'textfield',
208 name: 'dport',
435cce27 209 value: '',
f6710aac
TL
210 fieldLabel: gettext('Dest. port'),
211 },
6db91b0f
TL
212 ];
213
214 me.advancedColumn1 = [
3c37fe48 215 {
f6710aac
TL
216 xtype: 'pveFirewallLogLevels',
217 },
435cce27 218 ];
3c37fe48 219
435cce27
DM
220 me.columnB = [
221 {
222 xtype: 'textfield',
223 name: 'comment',
224 value: '',
f6710aac
TL
225 fieldLabel: gettext('Comment'),
226 },
435cce27
DM
227 ];
228
229 me.callParent();
f6710aac 230 },
435cce27
DM
231});
232
233Ext.define('PVE.FirewallRuleEdit', {
9fccc702 234 extend: 'Proxmox.window.Edit',
435cce27
DM
235
236 base_url: undefined,
237 list_refs_url: undefined,
238
239 allow_iface: false,
240
8058410f 241 initComponent: function() {
435cce27
DM
242 var me = this;
243
244 if (!me.base_url) {
245 throw "no base_url specified";
246 }
247 if (!me.list_refs_url) {
248 throw "no list_refs_url specified";
249 }
250
53e3ea84 251 me.isCreate = me.rule_pos === undefined;
435cce27 252
d5e771ce 253 if (me.isCreate) {
435cce27
DM
254 me.url = '/api2/extjs' + me.base_url;
255 me.method = 'POST';
256 } else {
257 me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
258 me.method = 'PUT';
259 }
260
261 var ipanel = Ext.create('PVE.FirewallRulePanel', {
d5e771ce 262 isCreate: me.isCreate,
435cce27
DM
263 list_refs_url: me.list_refs_url,
264 allow_iface: me.allow_iface,
f6710aac 265 rule_pos: me.rule_pos,
435cce27
DM
266 });
267
268 Ext.apply(me, {
269 subject: gettext('Rule'),
270 isAdd: true,
8058410f 271 items: [ipanel],
435cce27
DM
272 });
273
274 me.callParent();
275
d5e771ce 276 if (!me.isCreate) {
435cce27
DM
277 me.load({
278 success: function(response, options) {
279 var values = response.result.data;
280 ipanel.setValues(values);
281 if (values.errors) {
282 var field = me.query('[isFormField][name=modified_marker]')[0];
283 field.setValue(1);
284 Ext.Function.defer(function() {
285 var form = ipanel.up('form').getForm();
a764c5f7 286 form.markInvalid(values.errors);
435cce27
DM
287 }, 100);
288 }
f6710aac 289 },
435cce27 290 });
330020d2
WL
291 } else if (me.rec) {
292 ipanel.setValues(me.rec.data);
435cce27 293 }
f6710aac 294 },
435cce27
DM
295});
296
297Ext.define('PVE.FirewallGroupRuleEdit', {
9fccc702 298 extend: 'Proxmox.window.Edit',
435cce27
DM
299
300 base_url: undefined,
301
302 allow_iface: false,
303
8058410f 304 initComponent: function() {
435cce27
DM
305 var me = this;
306
53e3ea84 307 me.isCreate = me.rule_pos === undefined;
435cce27 308
d5e771ce 309 if (me.isCreate) {
435cce27
DM
310 me.url = '/api2/extjs' + me.base_url;
311 me.method = 'POST';
312 } else {
313 me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
314 me.method = 'PUT';
315 }
316
317 var column1 = [
318 {
319 xtype: 'hiddenfield',
320 name: 'type',
f6710aac 321 value: 'group',
435cce27
DM
322 },
323 {
324 xtype: 'pveSecurityGroupsSelector',
325 name: 'action',
326 value: '',
327 fieldLabel: gettext('Security Group'),
f6710aac
TL
328 allowBlank: false,
329 },
435cce27
DM
330 ];
331
332 if (me.allow_iface) {
333 column1.push({
dbed4c1c 334 xtype: 'proxmoxtextfield',
435cce27 335 name: 'iface',
d5e771ce 336 deleteEmpty: !me.isCreate,
435cce27 337 value: '',
f6710aac 338 fieldLabel: gettext('Interface'),
435cce27
DM
339 });
340 }
341
ef4ef788 342 var ipanel = Ext.create('Proxmox.panel.InputPanel', {
d5e771ce 343 isCreate: me.isCreate,
435cce27
DM
344 column1: column1,
345 column2: [
346 {
896c0d50 347 xtype: 'proxmoxcheckbox',
435cce27
DM
348 name: 'enable',
349 checked: false,
435cce27 350 uncheckedValue: 0,
f6710aac
TL
351 fieldLabel: gettext('Enable'),
352 },
435cce27
DM
353 ],
354 columnB: [
355 {
356 xtype: 'textfield',
357 name: 'comment',
358 value: '',
f6710aac
TL
359 fieldLabel: gettext('Comment'),
360 },
361 ],
435cce27
DM
362 });
363
364 Ext.apply(me, {
365 subject: gettext('Rule'),
366 isAdd: true,
8058410f 367 items: [ipanel],
435cce27
DM
368 });
369
370 me.callParent();
371
d5e771ce 372 if (!me.isCreate) {
435cce27 373 me.load({
8058410f 374 success: function(response, options) {
435cce27
DM
375 var values = response.result.data;
376 ipanel.setValues(values);
f6710aac 377 },
435cce27
DM
378 });
379 }
f6710aac 380 },
435cce27
DM
381});
382
383Ext.define('PVE.FirewallRules', {
384 extend: 'Ext.grid.Panel',
385 alias: 'widget.pveFirewallRules',
386
ba93a9c6
DC
387 onlineHelp: 'chapter_pve_firewall',
388
123e1c80
DC
389 stateful: true,
390 stateId: 'grid-firewall-rules',
391
435cce27
DM
392 base_url: undefined,
393 list_refs_url: undefined,
394
395 addBtn: undefined,
396 removeBtn: undefined,
397 editBtn: undefined,
398 groupBtn: undefined,
399
400 tbar_prefix: undefined,
401
402 allow_groups: true,
403 allow_iface: false,
404
405 setBaseUrl: function(url) {
406 var me = this;
407
408 me.base_url = url;
409
410 if (url === undefined) {
411 me.addBtn.setDisabled(true);
412 if (me.groupBtn) {
413 me.groupBtn.setDisabled(true);
414 }
415 me.store.removeAll();
416 } else {
417 me.addBtn.setDisabled(false);
3b1ca3ff 418 me.removeBtn.baseurl = url + '/';
435cce27
DM
419 if (me.groupBtn) {
420 me.groupBtn.setDisabled(false);
421 }
422 me.store.setProxy({
56a353b9 423 type: 'proxmox',
f6710aac 424 url: '/api2/json' + url,
435cce27
DM
425 });
426
427 me.store.load();
428 }
429 },
430
431 moveRule: function(from, to) {
432 var me = this;
433
2a4971d8 434 if (!me.base_url) {
435cce27
DM
435 return;
436 }
437
e7ade592 438 Proxmox.Utils.API2Request({
435cce27
DM
439 url: me.base_url + "/" + from,
440 method: 'PUT',
441 params: { moveto: to },
442 waitMsgTarget: me,
443 failure: function(response, options) {
444 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
445 },
446 callback: function() {
447 me.store.load();
f6710aac 448 },
435cce27
DM
449 });
450 },
451
452 updateRule: function(rule) {
453 var me = this;
454
2a4971d8 455 if (!me.base_url) {
435cce27
DM
456 return;
457 }
458
459 rule.enable = rule.enable ? 1 : 0;
460
461 var pos = rule.pos;
462 delete rule.pos;
463 delete rule.errors;
464
e7ade592 465 Proxmox.Utils.API2Request({
435cce27
DM
466 url: me.base_url + '/' + pos.toString(),
467 method: 'PUT',
468 params: rule,
469 waitMsgTarget: me,
470 failure: function(response, options) {
471 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
472 },
473 callback: function() {
474 me.store.load();
f6710aac 475 },
435cce27
DM
476 });
477 },
478
435cce27
DM
479
480 initComponent: function() {
435cce27
DM
481 var me = this;
482
483 if (!me.list_refs_url) {
484 throw "no list_refs_url specified";
485 }
486
f6710aac
TL
487 var store = Ext.create('Ext.data.Store', {
488 model: 'pve-fw-rule',
435cce27
DM
489 });
490
491 var reload = function() {
492 store.load();
493 };
494
495 var sm = Ext.create('Ext.selection.RowModel', {});
496
497 var run_editor = function() {
498 var rec = sm.getSelection()[0];
499 if (!rec) {
500 return;
501 }
502 var type = rec.data.type;
503
504 var editor;
505 if (type === 'in' || type === 'out') {
506 editor = 'PVE.FirewallRuleEdit';
507 } else if (type === 'group') {
508 editor = 'PVE.FirewallGroupRuleEdit';
509 } else {
510 return;
511 }
512
513 var win = Ext.create(editor, {
514 digest: rec.data.digest,
515 allow_iface: me.allow_iface,
516 base_url: me.base_url,
517 list_refs_url: me.list_refs_url,
f6710aac 518 rule_pos: rec.data.pos,
435cce27
DM
519 });
520
521 win.show();
522 win.on('destroy', reload);
523 };
524
f6710aac 525 me.editBtn = Ext.create('Proxmox.button.Button', {
435cce27
DM
526 text: gettext('Edit'),
527 disabled: true,
528 selModel: sm,
f6710aac 529 handler: run_editor,
435cce27
DM
530 });
531
8058410f 532 me.addBtn = Ext.create('Ext.Button', {
435cce27
DM
533 text: gettext('Add'),
534 disabled: true,
535 handler: function() {
536 var win = Ext.create('PVE.FirewallRuleEdit', {
537 allow_iface: me.allow_iface,
538 base_url: me.base_url,
f6710aac 539 list_refs_url: me.list_refs_url,
435cce27
DM
540 });
541 win.on('destroy', reload);
542 win.show();
f6710aac 543 },
435cce27
DM
544 });
545
330020d2
WL
546 var run_copy_editor = function() {
547 var rec = sm.getSelection()[0];
548
549 if (!rec) {
550 return;
551 }
552 var type = rec.data.type;
553
554
555 if (!(type === 'in' || type === 'out')) {
556 return;
557 }
558
559 var win = Ext.create('PVE.FirewallRuleEdit', {
560 allow_iface: me.allow_iface,
561 base_url: me.base_url,
562 list_refs_url: me.list_refs_url,
f6710aac 563 rec: rec,
330020d2
WL
564 });
565
566 win.show();
567 win.on('destroy', reload);
568 };
569
f6710aac 570 me.copyBtn = Ext.create('Proxmox.button.Button', {
330020d2
WL
571 text: gettext('Copy'),
572 selModel: sm,
03375edf 573 enableFn: function(rec) {
53e3ea84 574 return rec.data.type === 'in' || rec.data.type === 'out';
03375edf 575 },
330020d2 576 disabled: true,
f6710aac 577 handler: run_copy_editor,
330020d2
WL
578 });
579
435cce27 580 if (me.allow_groups) {
8058410f 581 me.groupBtn = Ext.create('Ext.Button', {
2a4971d8 582 text: gettext('Insert') + ': ' +
435cce27
DM
583 gettext('Security Group'),
584 disabled: true,
585 handler: function() {
586 var win = Ext.create('PVE.FirewallGroupRuleEdit', {
587 allow_iface: me.allow_iface,
f6710aac 588 base_url: me.base_url,
435cce27
DM
589 });
590 win.on('destroy', reload);
591 win.show();
f6710aac 592 },
435cce27
DM
593 });
594 }
595
f6710aac 596 me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', {
435cce27 597 selModel: sm,
3b1ca3ff
DC
598 baseurl: me.base_url + '/',
599 confirmMsg: false,
600 getRecordName: function(rec) {
601 var rule = rec.data;
602 return rule.pos.toString() +
603 '?digest=' + encodeURIComponent(rule.digest);
604 },
605 callback: function() {
606 me.store.load();
f6710aac 607 },
435cce27
DM
608 });
609
8058410f 610 var tbar = me.tbar_prefix ? [me.tbar_prefix] : [];
330020d2 611 tbar.push(me.addBtn, me.copyBtn);
435cce27
DM
612 if (me.groupBtn) {
613 tbar.push(me.groupBtn);
614 }
fa94a977 615 tbar.push(me.removeBtn, me.editBtn);
435cce27
DM
616
617 var render_errors = function(name, value, metaData, record) {
618 var errors = record.data.errors;
619 if (errors && errors[name]) {
3ab7e0ec 620 metaData.tdCls = 'proxmox-invalid-row';
8058410f 621 var html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
2a4971d8 622 metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' +
f6710aac 623 html.replace(/\"/g, '&quot;') + '"';
435cce27
DM
624 }
625 return value;
626 };
627
628 var columns = [
629 {
630 // similar to xtype: 'rownumberer',
631 dataIndex: 'pos',
632 resizable: false,
ac69b28b 633 minWidth: 42,
33bf9791 634 maxWidth: 60,
ac69b28b 635 flex: 1,
435cce27
DM
636 sortable: false,
637 align: 'right',
638 hideable: false,
639 menuDisabled: true,
640 renderer: function(value, metaData, record, rowIdx, colIdx, store) {
641 metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
642 if (value >= 0) {
643 return value;
644 }
645 return '';
f6710aac 646 },
435cce27
DM
647 },
648 {
649 xtype: 'checkcolumn',
97a537de 650 header: gettext('On'),
435cce27
DM
651 dataIndex: 'enable',
652 listeners: {
7a4c3133
EK
653 checkchange: function(column, recordIndex, checked) {
654 var record = me.getStore().getData().items[recordIndex];
435cce27
DM
655 record.commit();
656 var data = {};
7a4c3133 657 Ext.Array.forEach(record.getFields(), function(field) {
435cce27
DM
658 data[field.name] = record.get(field.name);
659 });
660 if (!me.allow_iface || !data.iface) {
661 delete data.iface;
662 }
663 me.updateRule(data);
f6710aac 664 },
435cce27 665 },
97a537de 666 width: 40,
435cce27
DM
667 },
668 {
669 header: gettext('Type'),
670 dataIndex: 'type',
671 renderer: function(value, metaData, record) {
672 return render_errors('type', value, metaData, record);
673 },
33bf9791
TL
674 minWidth: 60,
675 maxWidth: 80,
ac69b28b 676 flex: 2,
435cce27
DM
677 },
678 {
679 header: gettext('Action'),
680 dataIndex: 'action',
681 renderer: function(value, metaData, record) {
682 return render_errors('action', value, metaData, record);
683 },
ac69b28b 684 minWidth: 80,
33bf9791
TL
685 maxWidth: 200,
686 flex: 2,
435cce27
DM
687 },
688 {
689 header: gettext('Macro'),
690 dataIndex: 'macro',
691 renderer: function(value, metaData, record) {
692 return render_errors('macro', value, metaData, record);
693 },
ac69b28b 694 minWidth: 80,
33bf9791 695 flex: 2,
f6710aac 696 },
435cce27
DM
697 ];
698
699 if (me.allow_iface) {
700 columns.push({
701 header: gettext('Interface'),
702 dataIndex: 'iface',
703 renderer: function(value, metaData, record) {
704 return render_errors('iface', value, metaData, record);
705 },
ac69b28b 706 minWidth: 80,
33bf9791 707 flex: 2,
435cce27
DM
708 });
709 }
710
fa94a977 711 columns.push(
33bf9791
TL
712 {
713 header: gettext('Protocol'),
714 dataIndex: 'proto',
715 renderer: function(value, metaData, record) {
716 return render_errors('proto', value, metaData, record);
717 },
718 width: 75,
719 },
435cce27
DM
720 {
721 header: gettext('Source'),
722 dataIndex: 'source',
723 renderer: function(value, metaData, record) {
724 return render_errors('source', value, metaData, record);
725 },
ac69b28b 726 minWidth: 100,
33bf9791 727 flex: 2,
435cce27
DM
728 },
729 {
33bf9791
TL
730 header: gettext('S.Port'),
731 dataIndex: 'sport',
435cce27 732 renderer: function(value, metaData, record) {
33bf9791 733 return render_errors('sport', value, metaData, record);
435cce27 734 },
33bf9791 735 width: 75,
435cce27
DM
736 },
737 {
33bf9791
TL
738 header: gettext('Destination'),
739 dataIndex: 'dest',
435cce27 740 renderer: function(value, metaData, record) {
33bf9791 741 return render_errors('dest', value, metaData, record);
435cce27 742 },
ac69b28b 743 minWidth: 100,
33bf9791 744 flex: 2,
435cce27
DM
745 },
746 {
33bf9791 747 header: gettext('D.Port'),
435cce27
DM
748 dataIndex: 'dport',
749 renderer: function(value, metaData, record) {
750 return render_errors('dport', value, metaData, record);
751 },
33bf9791 752 width: 75,
435cce27 753 },
3c37fe48
CE
754 {
755 header: gettext('Log level'),
756 dataIndex: 'log',
757 renderer: function(value, metaData, record) {
758 return render_errors('log', value, metaData, record);
759 },
33bf9791 760 width: 100,
3c37fe48 761 },
435cce27
DM
762 {
763 header: gettext('Comment'),
764 dataIndex: 'comment',
33bf9791
TL
765 flex: 10,
766 minWidth: 75,
435cce27 767 renderer: function(value, metaData, record) {
2d1ed7d3
TL
768 let comment = render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record) || '';
769 if (comment.length * 12 > metaData.column.cellWidth) {
770 comment = `<span data-qtip="${comment}">${comment}</span>`;
771 }
772 return comment;
773 },
f6710aac 774 },
fa94a977 775 );
435cce27
DM
776
777 Ext.apply(me, {
778 store: store,
779 selModel: sm,
780 tbar: tbar,
781 viewConfig: {
782 plugins: [
783 {
784 ptype: 'gridviewdragdrop',
785 dragGroup: 'FWRuleDDGroup',
f6710aac
TL
786 dropGroup: 'FWRuleDDGroup',
787 },
435cce27
DM
788 ],
789 listeners: {
790 beforedrop: function(node, data, dropRec, dropPosition) {
791 if (!dropRec) {
792 return false; // empty view
793 }
794 var moveto = dropRec.get('pos');
795 if (dropPosition === 'after') {
796 moveto++;
797 }
798 var pos = data.records[0].get('pos');
799 me.moveRule(pos, moveto);
800 return 0;
801 },
f6710aac
TL
802 itemdblclick: run_editor,
803 },
435cce27
DM
804 },
805 sortableColumns: false,
f6710aac 806 columns: columns,
435cce27
DM
807 });
808
809 me.callParent();
810
811 if (me.base_url) {
812 me.setBaseUrl(me.base_url); // load
813 }
f6710aac 814 },
435cce27 815}, function() {
435cce27
DM
816 Ext.define('pve-fw-rule', {
817 extend: 'Ext.data.Model',
8058410f 818 fields: [{ name: 'enable', type: 'boolean' },
435cce27 819 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface',
8058410f 820 'dport', 'sport', 'comment', 'pos', 'digest', 'errors'],
f6710aac 821 idProperty: 'pos',
435cce27 822 });
435cce27 823});