]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/grid/FirewallRules.js
ui: qemu|lxc : fix firewall menu caps
[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',
392e3cf1 37 direction: 'ASC',
f6710aac 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
d09a68e4
ML
49Ext.define('PVE.form.ICMPTypeSelector', {
50 extend: 'Proxmox.form.ComboGrid',
51 alias: 'widget.pveICMPTypeSelector',
52 allowBlank: true,
53 autoSelect: false,
54 valueField: 'name',
55 displayField: 'name',
56 listConfig: {
57 columns: [
58 {
59 header: gettext('Type'),
60 dataIndex: 'type',
61 hideable: false,
62 sortable: false,
63 width: 50,
64 },
65 {
66 header: gettext('Name'),
67 dataIndex: 'name',
68 hideable: false,
69 sortable: false,
70 flex: 1,
71 },
72 ],
73 },
74 setName: function(value) {
75 this.name = value;
9882c956 76 },
d09a68e4
ML
77});
78
79let ICMP_TYPE_NAMES_STORE = Ext.create('Ext.data.Store', {
80 field: ['type', 'name'],
81 data: [
82 { type: 'any', name: 'any' },
83 { type: '0', name: 'echo-reply' },
84 { type: '3', name: 'destination-unreachable' },
85 { type: '3/0', name: 'network-unreachable' },
86 { type: '3/1', name: 'host-unreachable' },
87 { type: '3/2', name: 'protocol-unreachable' },
88 { type: '3/3', name: 'port-unreachable' },
89 { type: '3/4', name: 'fragmentation-needed' },
90 { type: '3/5', name: 'source-route-failed' },
91 { type: '3/6', name: 'network-unknown' },
92 { type: '3/7', name: 'host-unknown' },
93 { type: '3/9', name: 'network-prohibited' },
94 { type: '3/10', name: 'host-prohibited' },
95 { type: '3/11', name: 'TOS-network-unreachable' },
96 { type: '3/12', name: 'TOS-host-unreachable' },
97 { type: '3/13', name: 'communication-prohibited' },
98 { type: '3/14', name: 'host-precedence-violation' },
99 { type: '3/15', name: 'precedence-cutoff' },
100 { type: '4', name: 'source-quench' },
101 { type: '5', name: 'redirect' },
102 { type: '5/0', name: 'network-redirect' },
103 { type: '5/1', name: 'host-redirect' },
104 { type: '5/2', name: 'TOS-network-redirect' },
105 { type: '5/3', name: 'TOS-host-redirect' },
106 { type: '8', name: 'echo-request' },
107 { type: '9', name: 'router-advertisement' },
108 { type: '10', name: 'router-solicitation' },
109 { type: '11', name: 'time-exceeded' },
110 { type: '11/0', name: 'ttl-zero-during-transit' },
111 { type: '11/1', name: 'ttl-zero-during-reassembly' },
112 { type: '12', name: 'parameter-problem' },
113 { type: '12/0', name: 'ip-header-bad' },
114 { type: '12/1', name: 'required-option-missing' },
115 { type: '13', name: 'timestamp-request' },
116 { type: '14', name: 'timestamp-reply' },
117 { type: '17', name: 'address-mask-request' },
118 { type: '18', name: 'address-mask-reply' },
119 ],
120});
121let ICMPV6_TYPE_NAMES_STORE = Ext.create('Ext.data.Store', {
122 field: ['type', 'name'],
123 data: [
124 { type: '1', name: 'destination-unreachable' },
125 { type: '1/0', name: 'no-route' },
126 { type: '1/1', name: 'communication-prohibited' },
127 { type: '1/2', name: 'beyond-scope' },
128 { type: '1/3', name: 'address-unreachable' },
129 { type: '1/4', name: 'port-unreachable' },
130 { type: '1/5', name: 'failed-policy' },
9882c956 131 { type: '1/6', name: 'reject-route' },
d09a68e4
ML
132 { type: '2', name: 'packet-too-big' },
133 { type: '3', name: 'time-exceeded' },
134 { type: '3/0', name: 'ttl-zero-during-transit' },
135 { type: '3/1', name: 'ttl-zero-during-reassembly' },
136 { type: '4', name: 'parameter-problem' },
137 { type: '4/0', name: 'bad-header' },
138 { type: '4/1', name: 'unknown-header-type' },
139 { type: '4/2', name: 'unknown-option' },
140 { type: '128', name: 'echo-request' },
141 { type: '129', name: 'echo-reply' },
142 { type: '133', name: 'router-solicitation' },
143 { type: '134', name: 'router-advertisement' },
144 { type: '135', name: 'neighbour-solicitation' },
145 { type: '136', name: 'neighbour-advertisement' },
146 { type: '137', name: 'redirect' },
147 ],
148});
149
435cce27 150Ext.define('PVE.FirewallRulePanel', {
ef4ef788 151 extend: 'Proxmox.panel.InputPanel',
435cce27
DM
152
153 allow_iface: false,
154
155 list_refs_url: undefined,
156
157 onGetValues: function(values) {
158 var me = this;
159
160 // hack: editable ComboGrid returns nothing when empty, so we need to set ''
161 // Also, disabled text fields return nothing, so we need to set ''
162
d09a68e4 163 Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'icmp-type', 'log'], function(key) {
435cce27
DM
164 if (values[key] === undefined) {
165 values[key] = '';
166 }
167 });
168
169 delete values.modified_marker;
2a4971d8 170
435cce27
DM
171 return values;
172 },
173
8058410f 174 initComponent: function() {
435cce27
DM
175 var me = this;
176
177 if (!me.list_refs_url) {
178 throw "no list_refs_url specified";
179 }
180
181 me.column1 = [
182 {
183 // hack: we use this field to mark the form 'dirty' when the
2a4971d8 184 // record has errors- so that the user can safe the unmodified
435cce27
DM
185 // form again.
186 xtype: 'hiddenfield',
187 name: 'modified_marker',
f6710aac 188 value: '',
435cce27
DM
189 },
190 {
09cacce7 191 xtype: 'proxmoxKVComboBox',
435cce27
DM
192 name: 'type',
193 value: 'in',
e7bc7f31 194 comboItems: [['in', 'in'], ['out', 'out']],
435cce27 195 fieldLabel: gettext('Direction'),
f6710aac 196 allowBlank: false,
435cce27
DM
197 },
198 {
09cacce7 199 xtype: 'proxmoxKVComboBox',
435cce27
DM
200 name: 'action',
201 value: 'ACCEPT',
e7bc7f31 202 comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']],
435cce27 203 fieldLabel: gettext('Action'),
f6710aac
TL
204 allowBlank: false,
205 },
435cce27
DM
206 ];
207
208 if (me.allow_iface) {
209 me.column1.push({
dbed4c1c 210 xtype: 'proxmoxtextfield',
435cce27 211 name: 'iface',
d5e771ce 212 deleteEmpty: !me.isCreate,
435cce27 213 value: '',
f6710aac 214 fieldLabel: gettext('Interface'),
435cce27
DM
215 });
216 } else {
217 me.column1.push({
218 xtype: 'displayfield',
219 fieldLabel: '',
f6710aac 220 value: '',
435cce27
DM
221 });
222 }
223
fa94a977 224 me.column1.push(
435cce27
DM
225 {
226 xtype: 'displayfield',
227 fieldLabel: '',
228 height: 7,
f6710aac 229 value: '',
435cce27
DM
230 },
231 {
232 xtype: 'pveIPRefSelector',
233 name: 'source',
234 autoSelect: false,
235 editable: true,
236 base_url: me.list_refs_url,
237 value: '',
f6710aac 238 fieldLabel: gettext('Source'),
1490b5eb
AL
239 maxLength: 512,
240 maxLengthText: gettext('Too long, consider using IP sets.'),
435cce27
DM
241 },
242 {
243 xtype: 'pveIPRefSelector',
244 name: 'dest',
245 autoSelect: false,
246 editable: true,
247 base_url: me.list_refs_url,
248 value: '',
f6710aac 249 fieldLabel: gettext('Destination'),
1490b5eb
AL
250 maxLength: 512,
251 maxLengthText: gettext('Too long, consider using IP sets.'),
f6710aac 252 },
fa94a977 253 );
435cce27 254
2a4971d8 255
435cce27
DM
256 me.column2 = [
257 {
896c0d50 258 xtype: 'proxmoxcheckbox',
435cce27
DM
259 name: 'enable',
260 checked: false,
435cce27 261 uncheckedValue: 0,
f6710aac 262 fieldLabel: gettext('Enable'),
435cce27
DM
263 },
264 {
265 xtype: 'pveFWMacroSelector',
266 name: 'macro',
435cce27 267 fieldLabel: gettext('Macro'),
1fafdce8 268 editable: true,
435cce27
DM
269 allowBlank: true,
270 listeners: {
271 change: function(f, value) {
1fafdce8 272 if (value === null) {
435cce27
DM
273 me.down('field[name=proto]').setDisabled(false);
274 me.down('field[name=sport]').setDisabled(false);
275 me.down('field[name=dport]').setDisabled(false);
276 } else {
277 me.down('field[name=proto]').setDisabled(true);
278 me.down('field[name=proto]').setValue('');
279 me.down('field[name=sport]').setDisabled(true);
280 me.down('field[name=sport]').setValue('');
281 me.down('field[name=dport]').setDisabled(true);
7c7ae44f 282 me.down('field[name=dport]').setValue('');
435cce27 283 }
f6710aac
TL
284 },
285 },
435cce27
DM
286 },
287 {
288 xtype: 'pveIPProtocolSelector',
289 name: 'proto',
290 autoSelect: false,
291 editable: true,
292 value: '',
f6710aac 293 fieldLabel: gettext('Protocol'),
d09a68e4
ML
294 listeners: {
295 change: function(f, value) {
296 if (value === 'icmp' || value === 'icmpv6' || value === 'ipv6-icmp') {
297 me.down('field[name=dport]').setHidden(true);
298 me.down('field[name=dport]').setDisabled(true);
299 if (value === 'icmp') {
300 me.down('#icmpv4-type').setHidden(false);
301 me.down('#icmpv4-type').setDisabled(false);
302 me.down('#icmpv6-type').setHidden(true);
303 me.down('#icmpv6-type').setDisabled(true);
304 } else {
305 me.down('#icmpv6-type').setHidden(false);
306 me.down('#icmpv6-type').setDisabled(false);
307 me.down('#icmpv4-type').setHidden(true);
308 me.down('#icmpv4-type').setDisabled(true);
309 }
310 } else {
311 me.down('#icmpv4-type').setHidden(true);
312 me.down('#icmpv4-type').setDisabled(true);
313 me.down('#icmpv6-type').setHidden(true);
314 me.down('#icmpv6-type').setDisabled(true);
315 me.down('field[name=dport]').setHidden(false);
316 me.down('field[name=dport]').setDisabled(false);
317 }
9882c956 318 },
d09a68e4 319 },
435cce27
DM
320 },
321 {
322 xtype: 'displayfield',
323 fieldLabel: '',
324 height: 7,
f6710aac 325 value: '',
435cce27
DM
326 },
327 {
328 xtype: 'textfield',
329 name: 'sport',
330 value: '',
f6710aac 331 fieldLabel: gettext('Source port'),
435cce27
DM
332 },
333 {
334 xtype: 'textfield',
335 name: 'dport',
435cce27 336 value: '',
f6710aac
TL
337 fieldLabel: gettext('Dest. port'),
338 },
d09a68e4
ML
339 {
340 xtype: 'pveICMPTypeSelector',
341 name: 'icmp-type',
342 id: 'icmpv4-type',
343 autoSelect: false,
344 editable: true,
345 hidden: true,
346 disabled: true,
347 value: '',
348 fieldLabel: gettext('ICMP type'),
349 store: ICMP_TYPE_NAMES_STORE,
350 },
351 {
352 xtype: 'pveICMPTypeSelector',
353 name: 'icmp-type',
354 id: 'icmpv6-type',
355 autoSelect: false,
356 editable: true,
357 hidden: true,
358 disabled: true,
359 value: '',
360 fieldLabel: gettext('ICMP type'),
361 store: ICMPV6_TYPE_NAMES_STORE,
362 },
6db91b0f
TL
363 ];
364
365 me.advancedColumn1 = [
3c37fe48 366 {
f6710aac
TL
367 xtype: 'pveFirewallLogLevels',
368 },
435cce27 369 ];
3c37fe48 370
435cce27
DM
371 me.columnB = [
372 {
373 xtype: 'textfield',
374 name: 'comment',
375 value: '',
f6710aac
TL
376 fieldLabel: gettext('Comment'),
377 },
435cce27
DM
378 ];
379
380 me.callParent();
f6710aac 381 },
435cce27
DM
382});
383
384Ext.define('PVE.FirewallRuleEdit', {
9fccc702 385 extend: 'Proxmox.window.Edit',
435cce27
DM
386
387 base_url: undefined,
388 list_refs_url: undefined,
389
390 allow_iface: false,
391
8058410f 392 initComponent: function() {
435cce27
DM
393 var me = this;
394
395 if (!me.base_url) {
396 throw "no base_url specified";
397 }
398 if (!me.list_refs_url) {
399 throw "no list_refs_url specified";
400 }
401
53e3ea84 402 me.isCreate = me.rule_pos === undefined;
435cce27 403
d5e771ce 404 if (me.isCreate) {
435cce27
DM
405 me.url = '/api2/extjs' + me.base_url;
406 me.method = 'POST';
407 } else {
408 me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
409 me.method = 'PUT';
410 }
411
412 var ipanel = Ext.create('PVE.FirewallRulePanel', {
d5e771ce 413 isCreate: me.isCreate,
435cce27
DM
414 list_refs_url: me.list_refs_url,
415 allow_iface: me.allow_iface,
f6710aac 416 rule_pos: me.rule_pos,
435cce27
DM
417 });
418
419 Ext.apply(me, {
420 subject: gettext('Rule'),
421 isAdd: true,
8058410f 422 items: [ipanel],
435cce27
DM
423 });
424
425 me.callParent();
426
d5e771ce 427 if (!me.isCreate) {
435cce27
DM
428 me.load({
429 success: function(response, options) {
430 var values = response.result.data;
431 ipanel.setValues(values);
d09a68e4
ML
432 // set icmp-type again after protocol has been set
433 if (values["icmp-type"] !== undefined) {
9882c956 434 ipanel.setValues({ "icmp-type": values["icmp-type"] });
d09a68e4 435 }
435cce27
DM
436 if (values.errors) {
437 var field = me.query('[isFormField][name=modified_marker]')[0];
438 field.setValue(1);
439 Ext.Function.defer(function() {
440 var form = ipanel.up('form').getForm();
a764c5f7 441 form.markInvalid(values.errors);
435cce27
DM
442 }, 100);
443 }
f6710aac 444 },
435cce27 445 });
330020d2
WL
446 } else if (me.rec) {
447 ipanel.setValues(me.rec.data);
435cce27 448 }
f6710aac 449 },
435cce27
DM
450});
451
452Ext.define('PVE.FirewallGroupRuleEdit', {
9fccc702 453 extend: 'Proxmox.window.Edit',
435cce27
DM
454
455 base_url: undefined,
456
457 allow_iface: false,
458
8058410f 459 initComponent: function() {
435cce27
DM
460 var me = this;
461
53e3ea84 462 me.isCreate = me.rule_pos === undefined;
435cce27 463
d5e771ce 464 if (me.isCreate) {
435cce27
DM
465 me.url = '/api2/extjs' + me.base_url;
466 me.method = 'POST';
467 } else {
468 me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
469 me.method = 'PUT';
470 }
471
472 var column1 = [
473 {
474 xtype: 'hiddenfield',
475 name: 'type',
f6710aac 476 value: 'group',
435cce27
DM
477 },
478 {
479 xtype: 'pveSecurityGroupsSelector',
480 name: 'action',
481 value: '',
482 fieldLabel: gettext('Security Group'),
f6710aac
TL
483 allowBlank: false,
484 },
435cce27
DM
485 ];
486
487 if (me.allow_iface) {
488 column1.push({
dbed4c1c 489 xtype: 'proxmoxtextfield',
435cce27 490 name: 'iface',
d5e771ce 491 deleteEmpty: !me.isCreate,
435cce27 492 value: '',
f6710aac 493 fieldLabel: gettext('Interface'),
435cce27
DM
494 });
495 }
496
ef4ef788 497 var ipanel = Ext.create('Proxmox.panel.InputPanel', {
d5e771ce 498 isCreate: me.isCreate,
435cce27
DM
499 column1: column1,
500 column2: [
501 {
896c0d50 502 xtype: 'proxmoxcheckbox',
435cce27
DM
503 name: 'enable',
504 checked: false,
435cce27 505 uncheckedValue: 0,
f6710aac
TL
506 fieldLabel: gettext('Enable'),
507 },
435cce27
DM
508 ],
509 columnB: [
510 {
511 xtype: 'textfield',
512 name: 'comment',
513 value: '',
f6710aac
TL
514 fieldLabel: gettext('Comment'),
515 },
516 ],
435cce27
DM
517 });
518
519 Ext.apply(me, {
520 subject: gettext('Rule'),
521 isAdd: true,
8058410f 522 items: [ipanel],
435cce27
DM
523 });
524
525 me.callParent();
526
d5e771ce 527 if (!me.isCreate) {
435cce27 528 me.load({
8058410f 529 success: function(response, options) {
435cce27
DM
530 var values = response.result.data;
531 ipanel.setValues(values);
f6710aac 532 },
435cce27
DM
533 });
534 }
f6710aac 535 },
435cce27
DM
536});
537
538Ext.define('PVE.FirewallRules', {
539 extend: 'Ext.grid.Panel',
540 alias: 'widget.pveFirewallRules',
541
ba93a9c6
DC
542 onlineHelp: 'chapter_pve_firewall',
543
123e1c80
DC
544 stateful: true,
545 stateId: 'grid-firewall-rules',
546
435cce27
DM
547 base_url: undefined,
548 list_refs_url: undefined,
549
550 addBtn: undefined,
551 removeBtn: undefined,
552 editBtn: undefined,
553 groupBtn: undefined,
554
555 tbar_prefix: undefined,
556
557 allow_groups: true,
558 allow_iface: false,
559
560 setBaseUrl: function(url) {
561 var me = this;
562
563 me.base_url = url;
564
565 if (url === undefined) {
566 me.addBtn.setDisabled(true);
567 if (me.groupBtn) {
568 me.groupBtn.setDisabled(true);
569 }
570 me.store.removeAll();
571 } else {
572 me.addBtn.setDisabled(false);
3b1ca3ff 573 me.removeBtn.baseurl = url + '/';
435cce27
DM
574 if (me.groupBtn) {
575 me.groupBtn.setDisabled(false);
576 }
577 me.store.setProxy({
56a353b9 578 type: 'proxmox',
f6710aac 579 url: '/api2/json' + url,
435cce27
DM
580 });
581
582 me.store.load();
583 }
584 },
585
586 moveRule: function(from, to) {
587 var me = this;
588
2a4971d8 589 if (!me.base_url) {
435cce27
DM
590 return;
591 }
592
e7ade592 593 Proxmox.Utils.API2Request({
435cce27
DM
594 url: me.base_url + "/" + from,
595 method: 'PUT',
596 params: { moveto: to },
597 waitMsgTarget: me,
598 failure: function(response, options) {
599 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
600 },
601 callback: function() {
602 me.store.load();
f6710aac 603 },
435cce27
DM
604 });
605 },
606
607 updateRule: function(rule) {
608 var me = this;
609
2a4971d8 610 if (!me.base_url) {
435cce27
DM
611 return;
612 }
613
614 rule.enable = rule.enable ? 1 : 0;
615
616 var pos = rule.pos;
617 delete rule.pos;
618 delete rule.errors;
619
e7ade592 620 Proxmox.Utils.API2Request({
435cce27
DM
621 url: me.base_url + '/' + pos.toString(),
622 method: 'PUT',
623 params: rule,
624 waitMsgTarget: me,
625 failure: function(response, options) {
626 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
627 },
628 callback: function() {
629 me.store.load();
f6710aac 630 },
435cce27
DM
631 });
632 },
633
435cce27
DM
634
635 initComponent: function() {
435cce27
DM
636 var me = this;
637
638 if (!me.list_refs_url) {
639 throw "no list_refs_url specified";
640 }
641
f6710aac
TL
642 var store = Ext.create('Ext.data.Store', {
643 model: 'pve-fw-rule',
435cce27
DM
644 });
645
646 var reload = function() {
647 store.load();
648 };
649
650 var sm = Ext.create('Ext.selection.RowModel', {});
651
652 var run_editor = function() {
653 var rec = sm.getSelection()[0];
654 if (!rec) {
655 return;
656 }
657 var type = rec.data.type;
658
659 var editor;
660 if (type === 'in' || type === 'out') {
661 editor = 'PVE.FirewallRuleEdit';
662 } else if (type === 'group') {
663 editor = 'PVE.FirewallGroupRuleEdit';
664 } else {
665 return;
666 }
667
668 var win = Ext.create(editor, {
669 digest: rec.data.digest,
670 allow_iface: me.allow_iface,
671 base_url: me.base_url,
672 list_refs_url: me.list_refs_url,
f6710aac 673 rule_pos: rec.data.pos,
435cce27
DM
674 });
675
676 win.show();
677 win.on('destroy', reload);
678 };
679
f6710aac 680 me.editBtn = Ext.create('Proxmox.button.Button', {
435cce27
DM
681 text: gettext('Edit'),
682 disabled: true,
683 selModel: sm,
f6710aac 684 handler: run_editor,
435cce27
DM
685 });
686
8058410f 687 me.addBtn = Ext.create('Ext.Button', {
435cce27
DM
688 text: gettext('Add'),
689 disabled: true,
690 handler: function() {
691 var win = Ext.create('PVE.FirewallRuleEdit', {
692 allow_iface: me.allow_iface,
693 base_url: me.base_url,
f6710aac 694 list_refs_url: me.list_refs_url,
435cce27
DM
695 });
696 win.on('destroy', reload);
697 win.show();
f6710aac 698 },
435cce27
DM
699 });
700
330020d2 701 var run_copy_editor = function() {
5e7d2723 702 let rec = sm.getSelection()[0];
330020d2
WL
703 if (!rec) {
704 return;
705 }
5e7d2723 706 let type = rec.data.type;
330020d2
WL
707 if (!(type === 'in' || type === 'out')) {
708 return;
709 }
710
5e7d2723 711 let win = Ext.create('PVE.FirewallRuleEdit', {
330020d2
WL
712 allow_iface: me.allow_iface,
713 base_url: me.base_url,
714 list_refs_url: me.list_refs_url,
f6710aac 715 rec: rec,
330020d2 716 });
330020d2
WL
717 win.show();
718 win.on('destroy', reload);
719 };
720
f6710aac 721 me.copyBtn = Ext.create('Proxmox.button.Button', {
330020d2
WL
722 text: gettext('Copy'),
723 selModel: sm,
5e7d2723 724 enableFn: ({ data }) => data.type === 'in' || data.type === 'out',
330020d2 725 disabled: true,
f6710aac 726 handler: run_copy_editor,
330020d2
WL
727 });
728
435cce27 729 if (me.allow_groups) {
8058410f 730 me.groupBtn = Ext.create('Ext.Button', {
2a4971d8 731 text: gettext('Insert') + ': ' +
435cce27
DM
732 gettext('Security Group'),
733 disabled: true,
734 handler: function() {
735 var win = Ext.create('PVE.FirewallGroupRuleEdit', {
736 allow_iface: me.allow_iface,
f6710aac 737 base_url: me.base_url,
435cce27
DM
738 });
739 win.on('destroy', reload);
740 win.show();
f6710aac 741 },
435cce27
DM
742 });
743 }
744
f6710aac 745 me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', {
435cce27 746 selModel: sm,
3b1ca3ff
DC
747 baseurl: me.base_url + '/',
748 confirmMsg: false,
749 getRecordName: function(rec) {
750 var rule = rec.data;
751 return rule.pos.toString() +
752 '?digest=' + encodeURIComponent(rule.digest);
753 },
754 callback: function() {
755 me.store.load();
f6710aac 756 },
435cce27
DM
757 });
758
5e7d2723 759 let tbar = me.tbar_prefix ? [me.tbar_prefix] : [];
330020d2 760 tbar.push(me.addBtn, me.copyBtn);
435cce27
DM
761 if (me.groupBtn) {
762 tbar.push(me.groupBtn);
763 }
fa94a977 764 tbar.push(me.removeBtn, me.editBtn);
435cce27 765
5e7d2723
TL
766 let render_errors = function(name, value, metaData, record) {
767 let errors = record.data.errors;
435cce27 768 if (errors && errors[name]) {
3ab7e0ec 769 metaData.tdCls = 'proxmox-invalid-row';
5e7d2723
TL
770 let html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
771 metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + html + '"';
435cce27
DM
772 }
773 return value;
774 };
775
5e7d2723 776 let columns = [
435cce27
DM
777 {
778 // similar to xtype: 'rownumberer',
779 dataIndex: 'pos',
780 resizable: false,
4ad65791
DC
781 minWidth: 65,
782 maxWidth: 83,
ac69b28b 783 flex: 1,
435cce27 784 sortable: false,
435cce27
DM
785 hideable: false,
786 menuDisabled: true,
5e7d2723 787 renderer: function(value, metaData, record, rowIdx, colIdx) {
435cce27 788 metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
4ad65791 789 let dragHandle = "<i class='pve-grid-fa fa fa-fw fa-reorder cursor-move'></i>";
435cce27 790 if (value >= 0) {
4ad65791 791 return dragHandle + value;
435cce27 792 }
4ad65791 793 return dragHandle;
f6710aac 794 },
435cce27
DM
795 },
796 {
797 xtype: 'checkcolumn',
97a537de 798 header: gettext('On'),
435cce27
DM
799 dataIndex: 'enable',
800 listeners: {
7a4c3133
EK
801 checkchange: function(column, recordIndex, checked) {
802 var record = me.getStore().getData().items[recordIndex];
435cce27
DM
803 record.commit();
804 var data = {};
7a4c3133 805 Ext.Array.forEach(record.getFields(), function(field) {
435cce27
DM
806 data[field.name] = record.get(field.name);
807 });
808 if (!me.allow_iface || !data.iface) {
809 delete data.iface;
810 }
811 me.updateRule(data);
f6710aac 812 },
435cce27 813 },
97a537de 814 width: 40,
435cce27
DM
815 },
816 {
817 header: gettext('Type'),
818 dataIndex: 'type',
819 renderer: function(value, metaData, record) {
820 return render_errors('type', value, metaData, record);
821 },
33bf9791
TL
822 minWidth: 60,
823 maxWidth: 80,
ac69b28b 824 flex: 2,
435cce27
DM
825 },
826 {
827 header: gettext('Action'),
828 dataIndex: 'action',
829 renderer: function(value, metaData, record) {
830 return render_errors('action', value, metaData, record);
831 },
ac69b28b 832 minWidth: 80,
33bf9791
TL
833 maxWidth: 200,
834 flex: 2,
435cce27
DM
835 },
836 {
837 header: gettext('Macro'),
838 dataIndex: 'macro',
839 renderer: function(value, metaData, record) {
840 return render_errors('macro', value, metaData, record);
841 },
ac69b28b 842 minWidth: 80,
33bf9791 843 flex: 2,
f6710aac 844 },
435cce27
DM
845 ];
846
847 if (me.allow_iface) {
848 columns.push({
849 header: gettext('Interface'),
850 dataIndex: 'iface',
851 renderer: function(value, metaData, record) {
852 return render_errors('iface', value, metaData, record);
853 },
ac69b28b 854 minWidth: 80,
33bf9791 855 flex: 2,
435cce27
DM
856 });
857 }
858
fa94a977 859 columns.push(
33bf9791
TL
860 {
861 header: gettext('Protocol'),
862 dataIndex: 'proto',
863 renderer: function(value, metaData, record) {
864 return render_errors('proto', value, metaData, record);
865 },
866 width: 75,
867 },
435cce27
DM
868 {
869 header: gettext('Source'),
870 dataIndex: 'source',
871 renderer: function(value, metaData, record) {
872 return render_errors('source', value, metaData, record);
873 },
ac69b28b 874 minWidth: 100,
33bf9791 875 flex: 2,
435cce27
DM
876 },
877 {
33bf9791
TL
878 header: gettext('S.Port'),
879 dataIndex: 'sport',
435cce27 880 renderer: function(value, metaData, record) {
33bf9791 881 return render_errors('sport', value, metaData, record);
435cce27 882 },
33bf9791 883 width: 75,
435cce27
DM
884 },
885 {
33bf9791
TL
886 header: gettext('Destination'),
887 dataIndex: 'dest',
435cce27 888 renderer: function(value, metaData, record) {
33bf9791 889 return render_errors('dest', value, metaData, record);
435cce27 890 },
ac69b28b 891 minWidth: 100,
33bf9791 892 flex: 2,
435cce27
DM
893 },
894 {
33bf9791 895 header: gettext('D.Port'),
435cce27
DM
896 dataIndex: 'dport',
897 renderer: function(value, metaData, record) {
898 return render_errors('dport', value, metaData, record);
899 },
33bf9791 900 width: 75,
435cce27 901 },
3c37fe48
CE
902 {
903 header: gettext('Log level'),
904 dataIndex: 'log',
905 renderer: function(value, metaData, record) {
906 return render_errors('log', value, metaData, record);
907 },
33bf9791 908 width: 100,
3c37fe48 909 },
435cce27
DM
910 {
911 header: gettext('Comment'),
912 dataIndex: 'comment',
33bf9791
TL
913 flex: 10,
914 minWidth: 75,
435cce27 915 renderer: function(value, metaData, record) {
2d1ed7d3
TL
916 let comment = render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record) || '';
917 if (comment.length * 12 > metaData.column.cellWidth) {
918 comment = `<span data-qtip="${comment}">${comment}</span>`;
919 }
920 return comment;
921 },
f6710aac 922 },
fa94a977 923 );
435cce27
DM
924
925 Ext.apply(me, {
926 store: store,
927 selModel: sm,
928 tbar: tbar,
5e7d2723 929 viewConfig: {
435cce27
DM
930 plugins: [
931 {
932 ptype: 'gridviewdragdrop',
933 dragGroup: 'FWRuleDDGroup',
f6710aac
TL
934 dropGroup: 'FWRuleDDGroup',
935 },
435cce27
DM
936 ],
937 listeners: {
5e7d2723 938 beforedrop: function(node, data, dropRec, dropPosition) {
435cce27
DM
939 if (!dropRec) {
940 return false; // empty view
941 }
5e7d2723 942 let moveto = dropRec.get('pos');
435cce27
DM
943 if (dropPosition === 'after') {
944 moveto++;
945 }
5e7d2723 946 let pos = data.records[0].get('pos');
435cce27
DM
947 me.moveRule(pos, moveto);
948 return 0;
949 },
f6710aac
TL
950 itemdblclick: run_editor,
951 },
435cce27
DM
952 },
953 sortableColumns: false,
f6710aac 954 columns: columns,
435cce27
DM
955 });
956
957 me.callParent();
958
959 if (me.base_url) {
960 me.setBaseUrl(me.base_url); // load
961 }
f6710aac 962 },
435cce27 963}, function() {
435cce27
DM
964 Ext.define('pve-fw-rule', {
965 extend: 'Ext.data.Model',
5e7d2723
TL
966 fields: [
967 { name: 'enable', type: 'boolean' },
968 'type',
969 'action',
970 'macro',
971 'source',
972 'dest',
973 'proto',
974 'iface',
975 'dport',
976 'sport',
977 'comment',
978 'pos',
979 'digest',
980 'errors',
981 ],
f6710aac 982 idProperty: 'pos',
435cce27 983 });
435cce27 984});