]> git.proxmox.com Git - proxmox-widget-toolkit.git/blame - src/window/NotificationMatcherEdit.js
bump version to 4.2.3
[proxmox-widget-toolkit.git] / src / window / NotificationMatcherEdit.js
CommitLineData
03a54ebd 1Ext.define('Proxmox.panel.NotificationMatcherGeneralPanel', {
5d1b587f 2 extend: 'Proxmox.panel.InputPanel',
03a54ebd 3 xtype: 'pmxNotificationMatcherGeneralPanel',
5d1b587f
LW
4 mixins: ['Proxmox.Mixin.CBind'],
5
6 items: [
7 {
8 xtype: 'pmxDisplayEditField',
9 name: 'name',
10 cbind: {
11 value: '{name}',
12 editable: '{isCreate}',
13 },
e8f1954c 14 fieldLabel: gettext('Matcher Name'),
5d1b587f
LW
15 allowBlank: false,
16 },
5f7b28cb
LW
17 {
18 xtype: 'proxmoxcheckbox',
19 name: 'enable',
20 fieldLabel: gettext('Enable'),
21 allowBlank: false,
22 checked: true,
23 },
5d1b587f 24 {
03a54ebd
LW
25 xtype: 'proxmoxtextfield',
26 name: 'comment',
27 fieldLabel: gettext('Comment'),
5d1b587f
LW
28 cbind: {
29 deleteEmpty: '{!isCreate}',
30 },
5d1b587f 31 },
03a54ebd 32 ],
5f7b28cb
LW
33
34
35 onSetValues: function(values) {
36 values.enable = !values.disable;
37
38 delete values.disable;
39 return values;
40 },
41
42 onGetValues: function(values) {
43 let me = this;
44
45 if (values.enable) {
46 if (!me.isCreate) {
47 Proxmox.Utils.assemble_field_data(values, { 'delete': 'disable' });
48 }
49 } else {
50 values.disable = 1;
51 }
52 delete values.enable;
53
54 return values;
55 },
03a54ebd
LW
56});
57
58Ext.define('Proxmox.panel.NotificationMatcherTargetPanel', {
59 extend: 'Proxmox.panel.InputPanel',
60 xtype: 'pmxNotificationMatcherTargetPanel',
61 mixins: ['Proxmox.Mixin.CBind'],
62
63 items: [
78d21b71
LW
64 {
65 xtype: 'pmxNotificationTargetSelector',
66 name: 'target',
67 allowBlank: false,
68 },
5d1b587f
LW
69 ],
70});
71
e8f1954c 72Ext.define('Proxmox.window.NotificationMatcherEdit', {
5d1b587f
LW
73 extend: 'Proxmox.window.Edit',
74
75 isAdd: true,
c972de23 76 onlineHelp: 'notification_matchers',
5d1b587f
LW
77
78 fieldDefaults: {
79 labelWidth: 120,
80 },
81
03a54ebd 82 width: 700,
5d1b587f
LW
83
84 initComponent: function() {
85 let me = this;
86
87 me.isCreate = !me.name;
88
89 if (!me.baseUrl) {
90 throw "baseUrl not set";
91 }
92
e8f1954c 93 me.url = `/api2/extjs${me.baseUrl}/matchers`;
5d1b587f
LW
94
95 if (me.isCreate) {
96 me.method = 'POST';
97 } else {
98 me.url += `/${me.name}`;
99 me.method = 'PUT';
100 }
101
e8f1954c 102 me.subject = gettext('Notification Matcher');
5d1b587f
LW
103
104 Ext.apply(me, {
03a54ebd
LW
105 bodyPadding: 0,
106 items: [
107 {
108 xtype: 'tabpanel',
109 region: 'center',
110 layout: 'fit',
111 bodyPadding: 10,
112 items: [
113 {
114 name: me.name,
115 title: gettext('General'),
116 xtype: 'pmxNotificationMatcherGeneralPanel',
117 isCreate: me.isCreate,
118 baseUrl: me.baseUrl,
119 },
120 {
121 name: me.name,
122 title: gettext('Match Rules'),
123 xtype: 'pmxNotificationMatchRulesEditPanel',
124 isCreate: me.isCreate,
125 baseUrl: me.baseUrl,
126 },
127 {
128 name: me.name,
129 title: gettext('Targets to notify'),
130 xtype: 'pmxNotificationMatcherTargetPanel',
131 isCreate: me.isCreate,
132 baseUrl: me.baseUrl,
133 },
134 ],
135 },
136 ],
5d1b587f
LW
137 });
138
139 me.callParent();
140
141 if (!me.isCreate) {
142 me.load();
143 }
144 },
145});
78d21b71
LW
146
147Ext.define('Proxmox.form.NotificationTargetSelector', {
148 extend: 'Ext.grid.Panel',
149 alias: 'widget.pmxNotificationTargetSelector',
150
151 mixins: {
152 field: 'Ext.form.field.Field',
153 },
154
155 padding: '0 0 10 0',
156
157 allowBlank: true,
158 selectAll: false,
159 isFormField: true,
160
161 store: {
162 autoLoad: true,
163 model: 'proxmox-notification-endpoints',
164 sorters: 'name',
165 },
166
167 columns: [
168 {
169 header: gettext('Target Name'),
170 dataIndex: 'name',
171 flex: 1,
172 },
173 {
174 header: gettext('Type'),
175 dataIndex: 'type',
176 flex: 1,
177 },
178 {
179 header: gettext('Comment'),
180 dataIndex: 'comment',
181 flex: 3,
182 },
183 ],
184
185 selModel: {
186 selType: 'checkboxmodel',
187 mode: 'SIMPLE',
188 },
189
190 checkChangeEvents: [
191 'selectionchange',
192 'change',
193 ],
194
195 listeners: {
196 selectionchange: function() {
197 // to trigger validity and error checks
198 this.checkChange();
199 },
200 },
201
202 getSubmitData: function() {
203 let me = this;
204 let res = {};
205 res[me.name] = me.getValue();
206 return res;
207 },
208
209 getValue: function() {
210 let me = this;
211 if (me.savedValue !== undefined) {
212 return me.savedValue;
213 }
214 let sm = me.getSelectionModel();
215 return (sm.getSelection() ?? []).map(item => item.data.name);
216 },
217
218 setValueSelection: function(value) {
219 let me = this;
220
221 let store = me.getStore();
222
223 let notFound = [];
224 let selection = value.map(item => {
225 let found = store.findRecord('name', item, 0, false, true, true);
226 if (!found) {
227 notFound.push(item);
228 }
229 return found;
230 }).filter(r => r);
231
232 for (const name of notFound) {
233 let rec = store.add({
234 name,
235 type: '-',
236 comment: gettext('Included target does not exist!'),
237 });
238 selection.push(rec[0]);
239 }
240
241 let sm = me.getSelectionModel();
242 if (selection.length) {
243 sm.select(selection);
244 } else {
245 sm.deselectAll();
246 }
247 // to correctly trigger invalid class
248 me.getErrors();
249 },
250
251 setValue: function(value) {
252 let me = this;
253
254 let store = me.getStore();
255 if (!store.isLoaded()) {
256 me.savedValue = value;
257 store.on('load', function() {
258 me.setValueSelection(value);
259 delete me.savedValue;
260 }, { single: true });
261 } else {
262 me.setValueSelection(value);
263 }
264 return me.mixins.field.setValue.call(me, value);
265 },
266
267 getErrors: function(value) {
268 let me = this;
269 if (!me.isDisabled() && me.allowBlank === false &&
270 me.getSelectionModel().getCount() === 0) {
271 me.addBodyCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
272 return [gettext('No target selected')];
273 }
274
275 me.removeBodyCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
276 return [];
277 },
278
279 initComponent: function() {
280 let me = this;
281 me.callParent();
282 me.initField();
283 },
284
285});
03a54ebd
LW
286
287Ext.define('Proxmox.panel.NotificationRulesEditPanel', {
288 extend: 'Proxmox.panel.InputPanel',
289 xtype: 'pmxNotificationMatchRulesEditPanel',
290 mixins: ['Proxmox.Mixin.CBind'],
291
be532951
DC
292 controller: {
293 xclass: 'Ext.app.ViewController',
294
295 // we want to also set the empty value, but 'bind' does not do that so
296 // we have to set it then (and only then) to get the correct value in
297 // the tree
298 control: {
299 'field': {
300 change: function(cmp) {
301 let me = this;
302 let vm = me.getViewModel();
303 if (cmp.field) {
304 let record = vm.get('selectedRecord');
305 if (!record) {
306 return;
307 }
308 let data = Ext.apply({}, record.get('data'));
309 let value = cmp.getValue();
310 // only update if the value is empty (or empty array)
311 if (!value || !value.length) {
312 data[cmp.field] = value;
313 record.set({ data });
314 }
315 }
316 },
317 },
318 },
319 },
320
03a54ebd
LW
321 viewModel: {
322 data: {
323 selectedRecord: null,
324 matchFieldType: 'exact',
325 matchFieldField: '',
326 matchFieldValue: '',
327 rootMode: 'all',
328 },
329
330 formulas: {
331 nodeType: {
332 get: function(get) {
333 let record = get('selectedRecord');
334 return record?.get('type');
335 },
336 set: function(value) {
337 let me = this;
338 let record = me.get('selectedRecord');
339
340 let data;
341
342 switch (value) {
343 case 'match-severity':
344 data = {
6ed92b73 345 value: ['info', 'notice', 'warning', 'error', 'unknown'],
03a54ebd
LW
346 };
347 break;
348 case 'match-field':
349 data = {
350 type: 'exact',
351 field: '',
352 value: '',
353 };
354 break;
355 case 'match-calendar':
356 data = {
357 value: '',
358 };
359 break;
360 }
361
362 let node = {
363 type: value,
364 data,
365 };
366 record.set(node);
367 },
368 },
369 showMatchingMode: function(get) {
370 let record = get('selectedRecord');
371 if (!record) {
372 return false;
373 }
374 return record.isRoot();
375 },
376 showMatcherType: function(get) {
377 let record = get('selectedRecord');
378 if (!record) {
379 return false;
380 }
381 return !record.isRoot();
382 },
383 typeIsMatchField: {
384 bind: {
385 bindTo: '{selectedRecord}',
386 deep: true,
387 },
388 get: function(record) {
389 return record?.get('type') === 'match-field';
390 },
391 },
392 typeIsMatchSeverity: {
393 bind: {
394 bindTo: '{selectedRecord}',
395 deep: true,
396 },
397 get: function(record) {
398 return record?.get('type') === 'match-severity';
399 },
400 },
401 typeIsMatchCalendar: {
402 bind: {
403 bindTo: '{selectedRecord}',
404 deep: true,
405 },
406 get: function(record) {
407 return record?.get('type') === 'match-calendar';
408 },
409 },
410 matchFieldType: {
411 bind: {
412 bindTo: '{selectedRecord}',
413 deep: true,
414 },
415 set: function(value) {
416 let me = this;
417 let record = me.get('selectedRecord');
418 let currentData = record.get('data');
419 record.set({
420 data: {
421 ...currentData,
422 type: value,
423 },
424 });
425 },
426 get: function(record) {
427 return record?.get('data')?.type;
428 },
429 },
430 matchFieldField: {
431 bind: {
432 bindTo: '{selectedRecord}',
433 deep: true,
434 },
435 set: function(value) {
436 let me = this;
437 let record = me.get('selectedRecord');
438 let currentData = record.get('data');
439
440 record.set({
441 data: {
442 ...currentData,
443 field: value,
444 },
445 });
446 },
447 get: function(record) {
448 return record?.get('data')?.field;
449 },
450 },
451 matchFieldValue: {
452 bind: {
453 bindTo: '{selectedRecord}',
454 deep: true,
455 },
456 set: function(value) {
457 let me = this;
458 let record = me.get('selectedRecord');
459 let currentData = record.get('data');
460 record.set({
461 data: {
462 ...currentData,
463 value: value,
464 },
465 });
466 },
467 get: function(record) {
468 return record?.get('data')?.value;
469 },
470 },
471 matchSeverityValue: {
472 bind: {
473 bindTo: '{selectedRecord}',
474 deep: true,
475 },
476 set: function(value) {
477 let me = this;
478 let record = me.get('selectedRecord');
479 let currentData = record.get('data');
480 record.set({
481 data: {
482 ...currentData,
483 value: value,
484 },
485 });
486 },
487 get: function(record) {
488 return record?.get('data')?.value;
489 },
490 },
491 matchCalendarValue: {
492 bind: {
493 bindTo: '{selectedRecord}',
494 deep: true,
495 },
496 set: function(value) {
497 let me = this;
498 let record = me.get('selectedRecord');
499 let currentData = record.get('data');
500 record.set({
501 data: {
502 ...currentData,
503 value: value,
504 },
505 });
506 },
507 get: function(record) {
508 return record?.get('data')?.value;
509 },
510 },
511 rootMode: {
512 bind: {
513 bindTo: '{selectedRecord}',
514 deep: true,
515 },
516 set: function(value) {
517 let me = this;
518 let record = me.get('selectedRecord');
519 let currentData = record.get('data');
1f8bfa3b
DC
520 let invert = false;
521 if (value.startsWith('not')) {
522 value = value.substring(3);
523 invert = true;
524 }
03a54ebd
LW
525 record.set({
526 data: {
527 ...currentData,
528 value,
1f8bfa3b 529 invert,
03a54ebd
LW
530 },
531 });
532 },
533 get: function(record) {
1f8bfa3b
DC
534 let prefix = record?.get('data').invert ? 'not' : '';
535 return prefix + record?.get('data')?.value;
03a54ebd
LW
536 },
537 },
538 },
539 },
540
541 column1: [
542 {
543 xtype: 'pmxNotificationMatchRuleTree',
544 cbind: {
545 isCreate: '{isCreate}',
546 },
547 },
548 ],
549 column2: [
550 {
551 xtype: 'pmxNotificationMatchRuleSettings',
552 },
553
554 ],
555
556 onGetValues: function(values) {
557 let me = this;
558
559 let deleteArrayIfEmtpy = (field) => {
560 if (Ext.isArray(values[field])) {
561 if (values[field].length === 0) {
562 delete values[field];
563 if (!me.isCreate) {
564 Proxmox.Utils.assemble_field_data(values, { 'delete': field });
565 }
566 }
567 }
568 };
569 deleteArrayIfEmtpy('match-field');
570 deleteArrayIfEmtpy('match-severity');
571 deleteArrayIfEmtpy('match-calendar');
572
573 return values;
574 },
575});
576
577Ext.define('Proxmox.panel.NotificationMatchRuleTree', {
578 extend: 'Ext.panel.Panel',
579 xtype: 'pmxNotificationMatchRuleTree',
580 mixins: ['Proxmox.Mixin.CBind'],
581 border: false,
582
583 getNodeTextAndIcon: function(type, data) {
584 let text;
585 let iconCls;
586
587 switch (type) {
588 case 'match-severity': {
be532951
DC
589 let v = data.value;
590 if (Ext.isArray(data.value)) {
591 v = data.value.join(', ');
592 }
03a54ebd
LW
593 text = Ext.String.format(gettext("Match severity: {0}"), v);
594 iconCls = 'fa fa-exclamation';
be532951 595 if (!v) {
1fc3d8cd 596 iconCls += ' internal-error';
be532951 597 }
03a54ebd
LW
598 } break;
599 case 'match-field': {
600 let field = data.field;
601 let value = data.value;
602 text = Ext.String.format(gettext("Match field: {0}={1}"), field, value);
64821a10 603 iconCls = 'fa fa-square-o';
be532951 604 if (!field || !value) {
1fc3d8cd 605 iconCls += ' internal-error';
be532951 606 }
03a54ebd
LW
607 } break;
608 case 'match-calendar': {
609 let v = data.value;
610 text = Ext.String.format(gettext("Match calendar: {0}"), v);
611 iconCls = 'fa fa-calendar-o';
be532951 612 if (!v || !v.length) {
1fc3d8cd 613 iconCls += ' internal-error';
be532951 614 }
03a54ebd
LW
615 } break;
616 case 'mode':
617 if (data.value === 'all') {
618 text = gettext("All");
619 } else if (data.value === 'any') {
620 text = gettext("Any");
621 }
622 if (data.invert) {
623 text = `!${text}`;
624 }
625 iconCls = 'fa fa-filter';
626
627 break;
628 }
629
630 return [text, iconCls];
631 },
632
633 initComponent: function() {
634 let me = this;
635
636 let treeStore = Ext.create('Ext.data.TreeStore', {
637 root: {
638 expanded: true,
639 expandable: false,
640 text: '',
641 type: 'mode',
642 data: {
643 value: 'all',
644 invert: false,
645 },
646 children: [],
647 iconCls: 'fa fa-filter',
648 },
649 });
650
651 let realMatchFields = Ext.create({
652 xtype: 'hiddenfield',
653 setValue: function(value) {
654 this.value = value;
655 this.checkChange();
656 },
657 getValue: function() {
658 return this.value;
659 },
be532951
DC
660 getErrors: function() {
661 for (const matcher of this.value ?? []) {
662 let matches = matcher.match(/^([^:]+):([^=]+)=(.+)$/);
663 if (!matches) {
664 return [""]; // fake error for validation
665 }
666 }
667 return [];
668 },
03a54ebd
LW
669 getSubmitValue: function() {
670 let value = this.value;
671 if (!value) {
672 value = [];
673 }
674 return value;
675 },
676 name: 'match-field',
677 });
678
679 let realMatchSeverity = Ext.create({
680 xtype: 'hiddenfield',
681 setValue: function(value) {
682 this.value = value;
683 this.checkChange();
684 },
685 getValue: function() {
686 return this.value;
687 },
be532951
DC
688 getErrors: function() {
689 for (const severities of this.value ?? []) {
690 if (!severities) {
691 return [""]; // fake error for validation
692 }
693 }
694 return [];
695 },
03a54ebd
LW
696 getSubmitValue: function() {
697 let value = this.value;
698 if (!value) {
699 value = [];
700 }
701 return value;
702 },
703 name: 'match-severity',
704 });
705
706 let realMode = Ext.create({
707 xtype: 'hiddenfield',
708 name: 'mode',
709 setValue: function(value) {
710 this.value = value;
711 this.checkChange();
712 },
713 getValue: function() {
714 return this.value;
715 },
716 getSubmitValue: function() {
717 let value = this.value;
718 return value;
719 },
720 });
721
722 let realMatchCalendar = Ext.create({
723 xtype: 'hiddenfield',
724 name: 'match-calendar',
725
726 setValue: function(value) {
727 this.value = value;
728 this.checkChange();
729 },
730 getValue: function() {
731 return this.value;
732 },
be532951
DC
733 getErrors: function() {
734 for (const timespan of this.value ?? []) {
735 if (!timespan) {
736 return [""]; // fake error for validation
737 }
738 }
739 return [];
740 },
03a54ebd
LW
741 getSubmitValue: function() {
742 let value = this.value;
743 return value;
744 },
745 });
746
747 let realInvertMatch = Ext.create({
748 xtype: 'proxmoxcheckbox',
749 name: 'invert-match',
750 hidden: true,
751 deleteEmpty: !me.isCreate,
752 });
753
754 let storeChanged = function(store) {
755 store.suspendEvent('datachanged');
756
757 let matchFieldStmts = [];
758 let matchSeverityStmts = [];
759 let matchCalendarStmts = [];
760 let modeStmt = 'all';
761 let invertMatchStmt = false;
762
763 store.each(function(model) {
764 let type = model.get('type');
765 let data = model.get('data');
766
767 switch (type) {
768 case 'match-field':
be532951 769 matchFieldStmts.push(`${data.type}:${data.field ?? ''}=${data.value ?? ''}`);
03a54ebd
LW
770 break;
771 case 'match-severity':
be532951
DC
772 if (Ext.isArray(data.value)) {
773 matchSeverityStmts.push(data.value.join(','));
774 } else {
775 matchSeverityStmts.push(data.value);
776 }
03a54ebd
LW
777 break;
778 case 'match-calendar':
779 matchCalendarStmts.push(data.value);
780 break;
781 case 'mode':
edd98f94
FE
782 modeStmt = data.value;
783 invertMatchStmt = data.invert;
03a54ebd
LW
784 break;
785 }
786
787 let [text, iconCls] = me.getNodeTextAndIcon(type, data);
788 model.set({
789 text,
790 iconCls,
791 });
792 });
793
794 realMatchFields.suspendEvent('change');
795 realMatchFields.setValue(matchFieldStmts);
796 realMatchFields.resumeEvent('change');
797
798 realMatchCalendar.suspendEvent('change');
799 realMatchCalendar.setValue(matchCalendarStmts);
800 realMatchCalendar.resumeEvent('change');
801
802 realMode.suspendEvent('change');
803 realMode.setValue(modeStmt);
804 realMode.resumeEvent('change');
805
806 realInvertMatch.suspendEvent('change');
807 realInvertMatch.setValue(invertMatchStmt);
808 realInvertMatch.resumeEvent('change');
809
810 realMatchSeverity.suspendEvent('change');
811 realMatchSeverity.setValue(matchSeverityStmts);
812 realMatchSeverity.resumeEvent('change');
813
814 store.resumeEvent('datachanged');
815 };
816
817 realMatchFields.addListener('change', function(field, value) {
818 let parseMatchField = function(filter) {
819 let [, type, matchedField, matchedValue] =
820 filter.match(/^(?:(regex|exact):)?([A-Za-z0-9_][A-Za-z0-9._-]*)=(.+)$/);
821 if (type === undefined) {
822 type = "exact";
823 }
824 return {
825 type: 'match-field',
826 data: {
827 type,
828 field: matchedField,
829 value: matchedValue,
830 },
831 leaf: true,
832 };
833 };
834
835 for (let node of treeStore.queryBy(
836 record => record.get('type') === 'match-field',
837 ).getRange()) {
838 node.remove(true);
839 }
840
be532951
DC
841 if (!value) {
842 return;
843 }
03a54ebd
LW
844 let records = value.map(parseMatchField);
845
846 let rootNode = treeStore.getRootNode();
847
848 for (let record of records) {
849 rootNode.appendChild(record);
850 }
851 });
852
853 realMatchSeverity.addListener('change', function(field, value) {
854 let parseSeverity = function(severities) {
855 return {
856 type: 'match-severity',
857 data: {
858 value: severities.split(','),
859 },
860 leaf: true,
861 };
862 };
863
864 for (let node of treeStore.queryBy(
865 record => record.get('type') === 'match-severity').getRange()) {
866 node.remove(true);
867 }
868
869 let records = value.map(parseSeverity);
870 let rootNode = treeStore.getRootNode();
871
872 for (let record of records) {
873 rootNode.appendChild(record);
874 }
875 });
876
877 realMatchCalendar.addListener('change', function(field, value) {
878 let parseCalendar = function(timespan) {
879 return {
880 type: 'match-calendar',
881 data: {
882 value: timespan,
883 },
884 leaf: true,
885 };
886 };
887
888 for (let node of treeStore.queryBy(
889 record => record.get('type') === 'match-calendar').getRange()) {
890 node.remove(true);
891 }
892
893 let records = value.map(parseCalendar);
894 let rootNode = treeStore.getRootNode();
895
896 for (let record of records) {
897 rootNode.appendChild(record);
898 }
899 });
900
901 realMode.addListener('change', function(field, value) {
902 let data = treeStore.getRootNode().get('data');
903 treeStore.getRootNode().set('data', {
904 ...data,
905 value,
906 });
907 });
908
909 realInvertMatch.addListener('change', function(field, value) {
910 let data = treeStore.getRootNode().get('data');
911 treeStore.getRootNode().set('data', {
912 ...data,
913 invert: value,
914 });
915 });
916
917 treeStore.addListener('datachanged', storeChanged);
918
919 let treePanel = Ext.create({
920 xtype: 'treepanel',
921 store: treeStore,
922 minHeight: 300,
923 maxHeight: 300,
924 scrollable: true,
925
926 bind: {
927 selection: '{selectedRecord}',
928 },
929 });
930
931 let addNode = function() {
932 let node = {
933 type: 'match-field',
934 data: {
935 type: 'exact',
936 field: '',
937 value: '',
938 },
939 leaf: true,
940 };
941 treeStore.getRootNode().appendChild(node);
942 treePanel.setSelection(treeStore.getRootNode().lastChild);
943 };
944
945 let deleteNode = function() {
946 let selection = treePanel.getSelection();
947 for (let selected of selection) {
948 if (!selected.isRoot()) {
949 selected.remove(true);
950 }
951 }
952 };
953
954 Ext.apply(me, {
955 items: [
956 realMatchFields,
957 realMode,
958 realMatchSeverity,
959 realInvertMatch,
960 realMatchCalendar,
961 treePanel,
962 {
963 xtype: 'button',
964 margin: '5 5 5 0',
965 text: gettext('Add'),
966 iconCls: 'fa fa-plus-circle',
967 handler: addNode,
968 },
969 {
970 xtype: 'button',
971 margin: '5 5 5 0',
972 text: gettext('Remove'),
973 iconCls: 'fa fa-minus-circle',
974 handler: deleteNode,
975 },
976 ],
977 });
978 me.callParent();
979 },
980});
981
982Ext.define('Proxmox.panel.NotificationMatchRuleSettings', {
983 extend: 'Ext.panel.Panel',
984 xtype: 'pmxNotificationMatchRuleSettings',
985 border: false,
986
987 items: [
988 {
989 xtype: 'proxmoxKVComboBox',
990 name: 'mode',
991 fieldLabel: gettext('Match if'),
992 allowBlank: false,
993 isFormField: false,
994
1f8bfa3b
DC
995 matchFieldWidth: false,
996
03a54ebd
LW
997 comboItems: [
998 ['all', gettext('All rules match')],
999 ['any', gettext('Any rule matches')],
1f8bfa3b
DC
1000 ['notall', gettext('At least one rule does not match')],
1001 ['notany', gettext('No rule matches')],
03a54ebd
LW
1002 ],
1003 bind: {
1004 hidden: '{!showMatchingMode}',
1005 disabled: '{!showMatchingMode}',
1006 value: '{rootMode}',
1007 },
1008 },
03a54ebd
LW
1009 {
1010 xtype: 'proxmoxKVComboBox',
1011 fieldLabel: gettext('Node type'),
1012 isFormField: false,
1013 allowBlank: false,
1014
1015 bind: {
1016 value: '{nodeType}',
1017 hidden: '{!showMatcherType}',
1018 disabled: '{!showMatcherType}',
1019 },
1020
1021 comboItems: [
1022 ['match-field', gettext('Match Field')],
1023 ['match-severity', gettext('Match Severity')],
1024 ['match-calendar', gettext('Match Calendar')],
1025 ],
1026 },
1027 {
2834a05d 1028 fieldLabel: gettext('Match Type'),
03a54ebd
LW
1029 xtype: 'proxmoxKVComboBox',
1030 reference: 'type',
1031 isFormField: false,
1032 allowBlank: false,
1033 submitValue: false,
be532951 1034 field: 'type',
03a54ebd
LW
1035
1036 bind: {
1037 hidden: '{!typeIsMatchField}',
1038 disabled: '{!typeIsMatchField}',
1039 value: '{matchFieldType}',
1040 },
1041
1042 comboItems: [
1043 ['exact', gettext('Exact')],
1044 ['regex', gettext('Regex')],
1045 ],
1046 },
1047 {
1048 fieldLabel: gettext('Field'),
de0cec40 1049 xtype: 'proxmoxKVComboBox',
03a54ebd
LW
1050 isFormField: false,
1051 submitValue: false,
de0cec40
LW
1052 allowBlank: false,
1053 editable: true,
1054 displayField: 'key',
be532951 1055 field: 'field',
03a54ebd
LW
1056 bind: {
1057 hidden: '{!typeIsMatchField}',
1058 disabled: '{!typeIsMatchField}',
1059 value: '{matchFieldField}',
1060 },
de0cec40
LW
1061 // TODO: Once we have a 'notification registry', we should
1062 // retrive those via an API call.
1063 comboItems: [
1064 ['type', ''],
1065 ['hostname', ''],
1066 ],
03a54ebd
LW
1067 },
1068 {
1069 fieldLabel: gettext('Value'),
1070 xtype: 'textfield',
1071 isFormField: false,
1072 submitValue: false,
1073 allowBlank: false,
be532951 1074 field: 'value',
03a54ebd
LW
1075 bind: {
1076 hidden: '{!typeIsMatchField}',
1077 disabled: '{!typeIsMatchField}',
1078 value: '{matchFieldValue}',
1079 },
1080 },
1081 {
1082 xtype: 'proxmoxKVComboBox',
1083 fieldLabel: gettext('Severities to match'),
1084 isFormField: false,
1085 allowBlank: true,
1086 multiSelect: true,
be532951 1087 field: 'value',
03a54ebd
LW
1088
1089 bind: {
1090 value: '{matchSeverityValue}',
1091 hidden: '{!typeIsMatchSeverity}',
1092 disabled: '{!typeIsMatchSeverity}',
1093 },
1094
1095 comboItems: [
1096 ['info', gettext('Info')],
1097 ['notice', gettext('Notice')],
1098 ['warning', gettext('Warning')],
1099 ['error', gettext('Error')],
6ed92b73 1100 ['unknown', gettext('Unknown')],
03a54ebd
LW
1101 ],
1102 },
1103 {
1104 xtype: 'proxmoxKVComboBox',
1105 fieldLabel: gettext('Timespan to match'),
1106 isFormField: false,
1107 allowBlank: false,
1108 editable: true,
1109 displayField: 'key',
be532951 1110 field: 'value',
03a54ebd
LW
1111
1112 bind: {
1113 value: '{matchCalendarValue}',
1114 hidden: '{!typeIsMatchCalendar}',
1115 disabled: '{!typeIsMatchCalender}',
1116 },
1117
1118 comboItems: [
1119 ['mon 8-12', ''],
1120 ['tue..fri,sun 0:00-23:59', ''],
1121 ],
1122 },
1123 ],
1124});