]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/grid/FirewallRules.js
daily update timer: start already on 01:00 to avoid dst change issue
[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
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 546 var run_copy_editor = function() {
5e7d2723 547 let rec = sm.getSelection()[0];
330020d2
WL
548 if (!rec) {
549 return;
550 }
5e7d2723 551 let type = rec.data.type;
330020d2
WL
552 if (!(type === 'in' || type === 'out')) {
553 return;
554 }
555
5e7d2723 556 let win = Ext.create('PVE.FirewallRuleEdit', {
330020d2
WL
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 561 });
330020d2
WL
562 win.show();
563 win.on('destroy', reload);
564 };
565
f6710aac 566 me.copyBtn = Ext.create('Proxmox.button.Button', {
330020d2
WL
567 text: gettext('Copy'),
568 selModel: sm,
5e7d2723 569 enableFn: ({ data }) => data.type === 'in' || data.type === 'out',
330020d2 570 disabled: true,
f6710aac 571 handler: run_copy_editor,
330020d2
WL
572 });
573
435cce27 574 if (me.allow_groups) {
8058410f 575 me.groupBtn = Ext.create('Ext.Button', {
2a4971d8 576 text: gettext('Insert') + ': ' +
435cce27
DM
577 gettext('Security Group'),
578 disabled: true,
579 handler: function() {
580 var win = Ext.create('PVE.FirewallGroupRuleEdit', {
581 allow_iface: me.allow_iface,
f6710aac 582 base_url: me.base_url,
435cce27
DM
583 });
584 win.on('destroy', reload);
585 win.show();
f6710aac 586 },
435cce27
DM
587 });
588 }
589
f6710aac 590 me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton', {
435cce27 591 selModel: sm,
3b1ca3ff
DC
592 baseurl: me.base_url + '/',
593 confirmMsg: false,
594 getRecordName: function(rec) {
595 var rule = rec.data;
596 return rule.pos.toString() +
597 '?digest=' + encodeURIComponent(rule.digest);
598 },
599 callback: function() {
600 me.store.load();
f6710aac 601 },
435cce27
DM
602 });
603
5e7d2723 604 let tbar = me.tbar_prefix ? [me.tbar_prefix] : [];
330020d2 605 tbar.push(me.addBtn, me.copyBtn);
435cce27
DM
606 if (me.groupBtn) {
607 tbar.push(me.groupBtn);
608 }
fa94a977 609 tbar.push(me.removeBtn, me.editBtn);
435cce27 610
5e7d2723
TL
611 let render_errors = function(name, value, metaData, record) {
612 let errors = record.data.errors;
435cce27 613 if (errors && errors[name]) {
3ab7e0ec 614 metaData.tdCls = 'proxmox-invalid-row';
5e7d2723
TL
615 let html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
616 metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + html + '"';
435cce27
DM
617 }
618 return value;
619 };
620
5e7d2723 621 let columns = [
435cce27
DM
622 {
623 // similar to xtype: 'rownumberer',
624 dataIndex: 'pos',
625 resizable: false,
4ad65791
DC
626 minWidth: 65,
627 maxWidth: 83,
ac69b28b 628 flex: 1,
435cce27 629 sortable: false,
435cce27
DM
630 hideable: false,
631 menuDisabled: true,
5e7d2723 632 renderer: function(value, metaData, record, rowIdx, colIdx) {
435cce27 633 metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
4ad65791 634 let dragHandle = "<i class='pve-grid-fa fa fa-fw fa-reorder cursor-move'></i>";
435cce27 635 if (value >= 0) {
4ad65791 636 return dragHandle + value;
435cce27 637 }
4ad65791 638 return dragHandle;
f6710aac 639 },
435cce27
DM
640 },
641 {
642 xtype: 'checkcolumn',
97a537de 643 header: gettext('On'),
435cce27
DM
644 dataIndex: 'enable',
645 listeners: {
7a4c3133
EK
646 checkchange: function(column, recordIndex, checked) {
647 var record = me.getStore().getData().items[recordIndex];
435cce27
DM
648 record.commit();
649 var data = {};
7a4c3133 650 Ext.Array.forEach(record.getFields(), function(field) {
435cce27
DM
651 data[field.name] = record.get(field.name);
652 });
653 if (!me.allow_iface || !data.iface) {
654 delete data.iface;
655 }
656 me.updateRule(data);
f6710aac 657 },
435cce27 658 },
97a537de 659 width: 40,
435cce27
DM
660 },
661 {
662 header: gettext('Type'),
663 dataIndex: 'type',
664 renderer: function(value, metaData, record) {
665 return render_errors('type', value, metaData, record);
666 },
33bf9791
TL
667 minWidth: 60,
668 maxWidth: 80,
ac69b28b 669 flex: 2,
435cce27
DM
670 },
671 {
672 header: gettext('Action'),
673 dataIndex: 'action',
674 renderer: function(value, metaData, record) {
675 return render_errors('action', value, metaData, record);
676 },
ac69b28b 677 minWidth: 80,
33bf9791
TL
678 maxWidth: 200,
679 flex: 2,
435cce27
DM
680 },
681 {
682 header: gettext('Macro'),
683 dataIndex: 'macro',
684 renderer: function(value, metaData, record) {
685 return render_errors('macro', value, metaData, record);
686 },
ac69b28b 687 minWidth: 80,
33bf9791 688 flex: 2,
f6710aac 689 },
435cce27
DM
690 ];
691
692 if (me.allow_iface) {
693 columns.push({
694 header: gettext('Interface'),
695 dataIndex: 'iface',
696 renderer: function(value, metaData, record) {
697 return render_errors('iface', value, metaData, record);
698 },
ac69b28b 699 minWidth: 80,
33bf9791 700 flex: 2,
435cce27
DM
701 });
702 }
703
fa94a977 704 columns.push(
33bf9791
TL
705 {
706 header: gettext('Protocol'),
707 dataIndex: 'proto',
708 renderer: function(value, metaData, record) {
709 return render_errors('proto', value, metaData, record);
710 },
711 width: 75,
712 },
435cce27
DM
713 {
714 header: gettext('Source'),
715 dataIndex: 'source',
716 renderer: function(value, metaData, record) {
717 return render_errors('source', value, metaData, record);
718 },
ac69b28b 719 minWidth: 100,
33bf9791 720 flex: 2,
435cce27
DM
721 },
722 {
33bf9791
TL
723 header: gettext('S.Port'),
724 dataIndex: 'sport',
435cce27 725 renderer: function(value, metaData, record) {
33bf9791 726 return render_errors('sport', value, metaData, record);
435cce27 727 },
33bf9791 728 width: 75,
435cce27
DM
729 },
730 {
33bf9791
TL
731 header: gettext('Destination'),
732 dataIndex: 'dest',
435cce27 733 renderer: function(value, metaData, record) {
33bf9791 734 return render_errors('dest', value, metaData, record);
435cce27 735 },
ac69b28b 736 minWidth: 100,
33bf9791 737 flex: 2,
435cce27
DM
738 },
739 {
33bf9791 740 header: gettext('D.Port'),
435cce27
DM
741 dataIndex: 'dport',
742 renderer: function(value, metaData, record) {
743 return render_errors('dport', value, metaData, record);
744 },
33bf9791 745 width: 75,
435cce27 746 },
3c37fe48
CE
747 {
748 header: gettext('Log level'),
749 dataIndex: 'log',
750 renderer: function(value, metaData, record) {
751 return render_errors('log', value, metaData, record);
752 },
33bf9791 753 width: 100,
3c37fe48 754 },
435cce27
DM
755 {
756 header: gettext('Comment'),
757 dataIndex: 'comment',
33bf9791
TL
758 flex: 10,
759 minWidth: 75,
435cce27 760 renderer: function(value, metaData, record) {
2d1ed7d3
TL
761 let comment = render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record) || '';
762 if (comment.length * 12 > metaData.column.cellWidth) {
763 comment = `<span data-qtip="${comment}">${comment}</span>`;
764 }
765 return comment;
766 },
f6710aac 767 },
fa94a977 768 );
435cce27
DM
769
770 Ext.apply(me, {
771 store: store,
772 selModel: sm,
773 tbar: tbar,
5e7d2723 774 viewConfig: {
435cce27
DM
775 plugins: [
776 {
777 ptype: 'gridviewdragdrop',
778 dragGroup: 'FWRuleDDGroup',
f6710aac
TL
779 dropGroup: 'FWRuleDDGroup',
780 },
435cce27
DM
781 ],
782 listeners: {
5e7d2723 783 beforedrop: function(node, data, dropRec, dropPosition) {
435cce27
DM
784 if (!dropRec) {
785 return false; // empty view
786 }
5e7d2723 787 let moveto = dropRec.get('pos');
435cce27
DM
788 if (dropPosition === 'after') {
789 moveto++;
790 }
5e7d2723 791 let pos = data.records[0].get('pos');
435cce27
DM
792 me.moveRule(pos, moveto);
793 return 0;
794 },
f6710aac
TL
795 itemdblclick: run_editor,
796 },
435cce27
DM
797 },
798 sortableColumns: false,
f6710aac 799 columns: columns,
435cce27
DM
800 });
801
802 me.callParent();
803
804 if (me.base_url) {
805 me.setBaseUrl(me.base_url); // load
806 }
f6710aac 807 },
435cce27 808}, function() {
435cce27
DM
809 Ext.define('pve-fw-rule', {
810 extend: 'Ext.data.Model',
5e7d2723
TL
811 fields: [
812 { name: 'enable', type: 'boolean' },
813 'type',
814 'action',
815 'macro',
816 'source',
817 'dest',
818 'proto',
819 'iface',
820 'dport',
821 'sport',
822 'comment',
823 'pos',
824 'digest',
825 'errors',
826 ],
f6710aac 827 idProperty: 'pos',
435cce27 828 });
435cce27 829});