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