]> git.proxmox.com Git - pmg-gui.git/blob - js/Utils.js
fix #4818: Revert "UserEdit: add minLength of 4 to username field"
[pmg-gui.git] / js / Utils.js
1 Ext.ns('PMG');
2
3 console.log("Starting PMG Manager");
4
5
6 Ext.define('PMG.Utils', {
7 singleton: true,
8
9 // this singleton contains miscellaneous utilities
10
11 // use in panels with object spread (...) operator, for example:
12 // ...PMG.Utils.onlineHelpTool('sysadmin_certificate_management'),
13 onlineHelpTool: function(blockid) {
14 let info = Proxmox.Utils.get_help_info(blockid);
15 if (info === undefined) {
16 info = Proxmox.Utils.get_help_info('pmg_documentation_index');
17 if (info === undefined) {
18 throw "get_help_info failed"; // should not happen
19 }
20 }
21
22 let docsURI = window.location.origin + info.link;
23 let title = info.title || gettext('Help');
24 if (info.subtitle) {
25 title += ' - ' + info.subtitle;
26 }
27 return {
28 tools: [
29 {
30 type: 'help',
31 tooltip: title,
32 handler: () => window.open(docsURI),
33 },
34 ],
35 };
36 },
37
38 senderText: gettext('Sender'),
39 receiverText: gettext('Receiver'),
40 scoreText: gettext('Score'),
41
42 user_role_text: {
43 root: gettext('Superuser'),
44 admin: gettext('Administrator'),
45 helpdesk: gettext('Help Desk'),
46 qmanager: gettext('Quarantine Manager'),
47 audit: gettext('Auditor'),
48 },
49
50 format_user_role: function(role) {
51 return PMG.Utils.user_role_text[role] || role;
52 },
53
54 oclass_text: {
55 who: gettext('Who Objects'),
56 what: gettext('What Objects'),
57 when: gettext('When Objects'),
58 action: gettext('Action Objects'),
59 from: gettext('From'),
60 to: gettext('To'),
61 },
62
63 oclass_icon: {
64 who: '<span class="fa fa-fw fa-user-circle"></span> ',
65 what: '<span class="fa fa-fw fa-cube"></span> ',
66 when: '<span class="fa fa-fw fa-clock-o"></span> ',
67 action: '<span class="fa fa-fw fa-flag"></span> ',
68 from: '<span class="fa fa-fw fa-user-circle"></span> ',
69 to: '<span class="fa fa-fw fa-user-circle"></span> ',
70 },
71
72 mail_status_map: {
73 2: 'delivered',
74 4: 'deferred',
75 5: 'bounced',
76 N: 'rejected',
77 G: 'greylisted',
78 A: 'accepted',
79 B: 'blocked',
80 Q: 'quarantine',
81 },
82
83 icon_status_map_class: {
84 2: 'check-circle',
85 4: 'clock-o',
86 5: 'mail-reply',
87 N: 'times-circle',
88 G: 'list',
89 A: 'check',
90 B: 'ban',
91 Q: 'cube',
92 },
93
94 icon_status_map_color: {
95 2: 'green',
96 5: 'gray',
97 A: 'green',
98 B: 'red',
99 },
100
101 format_status_icon: function(status) {
102 var icon = PMG.Utils.icon_status_map_class[status] || 'question-circle';
103 var color = PMG.Utils.icon_status_map_color[status] || '';
104 return '<i class="fa fa-' + icon + ' ' + color + '"></i> ';
105 },
106
107 format_oclass: function(oclass) {
108 var icon = PMG.Utils.oclass_icon[oclass] || '';
109 var text = PMG.Utils.oclass_text[oclass] || oclass;
110 return icon + text;
111 },
112
113 rule_direction_text: {
114 0: gettext('In'),
115 1: gettext('Out'),
116 2: gettext('In & Out'),
117 },
118
119 rule_direction_icon: {
120 0: '<span class="fa fa-fw fa-long-arrow-left"></span> ',
121 1: '<span class="fa fa-fw fa-long-arrow-right"></span> ',
122 2: '<span class="fa fa-fw fa-exchange"></span> ',
123 },
124
125 format_rule_direction: function(dir) {
126 var icon = PMG.Utils.rule_direction_icon[dir] || '';
127 var text = PMG.Utils.rule_direction_text[dir] || dir;
128 return icon + text;
129 },
130
131 format_otype: function(otype) {
132 let editor = PMG.Utils.object_editors[otype];
133 let iconCls = 'fa fa-question-circle';
134 if (editor) {
135 return `<span class="fa-fw ${editor.iconCls || iconCls}"></span> ${editor.subject}`;
136 }
137 return `<span class="fa-fw ${iconCls}"></span> unknown`;
138 },
139
140 format_ldap_protocol: function(p) {
141 if (p === undefined) { return 'LDAP'; }
142 if (p === 'ldap') { return 'LDAP'; }
143 if (p === 'ldaps') { return 'LDAPS'; }
144 if (p === 'ldap+starttls') { return 'LDAP+STARTTLS'; }
145 return 'unknown';
146 },
147
148 convert_field_to_per_min: function(value, record) {
149 return value / (record.data.timespan / 60);
150 },
151
152 object_editors: {
153 1000: {
154 onlineHelp: 'pmg_mailfilter_regex',
155 iconCls: 'fa fa-filter',
156 xtype: 'proxmoxWindowEdit',
157 subdir: 'regex',
158 subject: gettext("Regular Expression"),
159 width: 400,
160 items: [
161 {
162 xtype: 'textfield',
163 name: 'regex',
164 reference: 'regex',
165 fieldLabel: gettext("Regex"),
166 },
167 {
168 xtype: 'pmgRegexTester',
169 fieldLabel: gettext('Test String'),
170 wholeMatch: true,
171 regexFieldReference: 'regex',
172 },
173 ],
174 },
175 1005: {
176 onlineHelp: 'pmgconfig_ldap',
177 iconCls: 'fa fa-users',
178 xtype: 'pmgLDAPGroupEditor',
179 subdir: 'ldap',
180 subject: gettext("LDAP Group"),
181 },
182 1006: {
183 onlineHelp: 'pmgconfig_ldap',
184 iconCls: 'fa fa-user',
185 xtype: 'pmgLDAPUserEditor',
186 subdir: 'ldapuser',
187 subject: gettext("LDAP User"),
188 },
189 1009: {
190 onlineHelp: 'pmg_mailfilter_regex',
191 iconCls: 'fa fa-filter',
192 xtype: 'proxmoxWindowEdit',
193 subdir: 'receiver_regex',
194 subject: gettext("Regular Expression"),
195 receivertest: true,
196 width: 400,
197 items: [
198 {
199 xtype: 'textfield',
200 name: 'regex',
201 fieldLabel: gettext("Regex"),
202 },
203 ],
204 },
205 1001: {
206 onlineHelp: 'pmg_mailfilter_who',
207 iconCls: 'fa fa-envelope-o',
208 xtype: 'proxmoxWindowEdit',
209 subdir: 'email',
210 subject: gettext("E-Mail"),
211 width: 400,
212 items: [
213 {
214 xtype: 'textfield',
215 name: 'email',
216 fieldLabel: gettext("E-Mail"),
217 },
218 ],
219 },
220 1007: {
221 onlineHelp: 'pmg_mailfilter_who',
222 iconCls: 'fa fa-envelope-o',
223 xtype: 'proxmoxWindowEdit',
224 subdir: 'receiver',
225 subject: gettext("E-Mail"),
226 receivertest: true,
227 width: 400,
228 items: [
229 {
230 xtype: 'textfield',
231 name: 'email',
232 fieldLabel: gettext("E-Mail"),
233 },
234 ],
235 },
236 1002: {
237 onlineHelp: 'pmg_mailfilter_who',
238 iconCls: 'fa fa-globe',
239 xtype: 'proxmoxWindowEdit',
240 subdir: 'domain',
241 subject: gettext("Domain"),
242 width: 400,
243 items: [
244 {
245 xtype: 'textfield',
246 name: 'domain',
247 fieldLabel: gettext("Domain"),
248 },
249 ],
250 },
251 1008: {
252 onlineHelp: 'pmg_mailfilter_who',
253 iconCls: 'fa fa-globe',
254 xtype: 'proxmoxWindowEdit',
255 subdir: 'receiver_domain',
256 subject: gettext("Domain"),
257 receivertest: true,
258 width: 400,
259 items: [
260 {
261 xtype: 'textfield',
262 name: 'domain',
263 fieldLabel: gettext("Domain"),
264 },
265 ],
266 },
267 1003: {
268 onlineHelp: 'pmg_mailfilter_who',
269 iconCls: 'fa fa-globe',
270 xtype: 'proxmoxWindowEdit',
271 subdir: 'ip',
272 subject: gettext("IP Address"),
273 width: 400,
274 items: [
275 {
276 xtype: 'textfield',
277 name: 'ip',
278 fieldLabel: gettext("IP Address"),
279 },
280 ],
281 },
282 1004: {
283 onlineHelp: 'pmg_mailfilter_who',
284 iconCls: 'fa fa-globe',
285 xtype: 'proxmoxWindowEdit',
286 subdir: 'network',
287 subject: gettext("IP Network"),
288 width: 400,
289 items: [
290 {
291 xtype: 'textfield',
292 name: 'cidr',
293 fieldLabel: gettext("IP Network"),
294 },
295 ],
296 },
297 2000: {
298 onlineHelp: 'pmg_mailfilter_when',
299 iconCls: 'fa fa-clock-o',
300 xtype: 'proxmoxWindowEdit',
301 subdir: 'timeframe',
302 subject: gettext("TimeFrame"),
303 items: [
304 {
305 xtype: 'timefield',
306 name: 'start',
307 format: 'H:i',
308 fieldLabel: gettext("Start Time"),
309 },
310 {
311 xtype: 'timefield',
312 name: 'end',
313 format: 'H:i',
314 fieldLabel: gettext("End Time"),
315 },
316 ],
317 },
318 3000: {
319 onlineHelp: 'pmg_mailfilter_what',
320 iconCls: 'fa fa-bullhorn',
321 xtype: 'proxmoxWindowEdit',
322 subdir: 'spamfilter',
323 subject: gettext('Spam Filter'),
324 items: [
325 {
326 xtype: 'proxmoxintegerfield',
327 name: 'spamlevel',
328 allowBlank: false,
329 minValue: 0,
330 fieldLabel: gettext('Level'),
331 },
332 ],
333 },
334 3001: {
335 onlineHelp: 'pmg_mailfilter_what',
336 iconCls: 'fa fa-bug',
337 xtype: 'proxmoxWindowEdit',
338 subdir: 'virusfilter',
339 subject: gettext('Virus Filter'),
340 uneditable: true,
341 // there are no parameters to give, so we simply submit it
342 listeners: {
343 show: function(win) {
344 win.submit();
345 },
346 },
347 },
348 3002: {
349 onlineHelp: 'pmg_mailfilter_regex',
350 iconCls: 'fa fa-code',
351 xtype: 'proxmoxWindowEdit',
352 subdir: 'matchfield',
353 subject: gettext('Match Field'),
354 width: 400,
355 items: [
356 {
357 xtype: 'textfield',
358 name: 'field',
359 allowBlank: false,
360 fieldLabel: gettext('Field'),
361 },
362 {
363 xtype: 'textfield',
364 name: 'value',
365 reference: 'value',
366 allowBlank: false,
367 fieldLabel: gettext('Value'),
368 },
369 {
370 xtype: 'pmgRegexTester',
371 fieldLabel: gettext('Test String'),
372 regexFieldReference: 'value',
373 },
374 ],
375 },
376 3003: {
377 onlineHelp: 'pmg_mailfilter_what',
378 iconCls: 'fa fa-file-image-o',
379 xtype: 'proxmoxWindowEdit',
380 subdir: 'contenttype',
381 width: 400,
382 subject: gettext('Content Type Filter'),
383 items: [
384 {
385 xtype: 'combobox',
386 displayField: 'text',
387 valueField: 'mimetype',
388 name: 'contenttype',
389 editable: true,
390 queryMode: 'local',
391 store: {
392 autoLoad: true,
393 proxy: {
394 type: 'proxmox',
395 url: '/api2/json/config/mimetypes',
396 },
397 },
398 fieldLabel: gettext('Content Type'),
399 anyMatch: true,
400 matchFieldWidth: false,
401 listeners: {
402 change: function(cb, value) {
403 var me = this;
404 me.up().down('displayfield').setValue(value);
405 },
406 },
407 },
408 {
409 xtype: 'displayfield',
410 fieldLabel: gettext('Value'),
411 allowBlank: false,
412 reset: Ext.emptyFn,
413 },
414 ],
415 },
416 3004: {
417 onlineHelp: 'pmg_mailfilter_regex',
418 iconCls: 'fa fa-file-o',
419 xtype: 'proxmoxWindowEdit',
420 subdir: 'filenamefilter',
421 width: 400,
422 subject: gettext('Match Filename'),
423 items: [
424 {
425 xtype: 'textfield',
426 name: 'filename',
427 reference: 'filename',
428 fieldLabel: gettext('Filename'),
429 allowBlank: false,
430 },
431 {
432 xtype: 'pmgRegexTester',
433 fieldLabel: gettext('Test String'),
434 wholeMatch: true,
435 regexFieldReference: 'filename',
436 },
437 ],
438 },
439 3005: {
440 onlineHelp: 'pmg_mailfilter_what',
441 iconCls: 'fa fa-file-archive-o',
442 xtype: 'proxmoxWindowEdit',
443 subdir: 'archivefilter',
444 width: 400,
445 subject: gettext('Archive Filter'),
446 items: [
447 {
448 xtype: 'combobox',
449 displayField: 'text',
450 valueField: 'mimetype',
451 name: 'contenttype',
452 editable: true,
453 queryMode: 'local',
454 store: {
455 autoLoad: true,
456 proxy: {
457 type: 'proxmox',
458 url: '/api2/json/config/mimetypes',
459 },
460 },
461 fieldLabel: gettext('Content Type'),
462 anyMatch: true,
463 matchFieldWidth: false,
464 listeners: {
465 change: function(cb, value) {
466 var me = this;
467 me.up().down('displayfield').setValue(value);
468 },
469 },
470 },
471 {
472 xtype: 'displayfield',
473 fieldLabel: gettext('Value'),
474 allowBlank: false,
475 reset: Ext.emptyFn,
476 },
477 ],
478 },
479 3006: {
480 onlineHelp: 'pmg_mailfilter_regex',
481 iconCls: 'fa fa-file-archive-o',
482 xtype: 'proxmoxWindowEdit',
483 subdir: 'archivefilenamefilter',
484 width: 400,
485 subject: gettext('Match Archive Filename'),
486 items: [
487 {
488 xtype: 'textfield',
489 name: 'filename',
490 reference: 'filename',
491 fieldLabel: gettext('Filename'),
492 allowBlank: false,
493 },
494 {
495 xtype: 'pmgRegexTester',
496 fieldLabel: gettext('Test String'),
497 wholeMatch: true,
498 regexFieldReference: 'filename',
499 },
500 ],
501 },
502 4002: {
503 onlineHelp: 'pmg_mailfilter_action',
504 xtype: 'proxmoxWindowEdit',
505 subdir: 'notification',
506 subject: gettext('Notification'),
507 width: 400,
508 items: [
509 {
510 xtype: 'textfield',
511 name: 'name',
512 allowBlank: false,
513 fieldLabel: gettext('Name'),
514 },
515 {
516 xtype: 'textareafield',
517 name: 'info',
518 fieldLabel: gettext("Comment"),
519 },
520 {
521 xtype: 'textfield',
522 name: 'to',
523 allowBlank: false,
524 value: '__ADMIN__',
525 fieldLabel: gettext('Receiver'),
526 },
527 {
528 xtype: 'textfield',
529 name: 'subject',
530 allowBlank: false,
531 value: 'Notification: __SUBJECT__',
532 fieldLabel: gettext('Subject'),
533 },
534 {
535 xtype: 'textarea',
536 name: 'body',
537 allowBlank: false,
538 grow: true,
539 growMax: 250,
540 value:
541 "Proxmox Notifcation:\n\n" +
542 "Sender: __SENDER__\n" +
543 "Receiver: __RECEIVERS__\n" +
544 "Targets: __TARGETS__\n\n" +
545 "Subject: __SUBJECT__\n\n" +
546 "Matching Rule: __RULE__\n\n" +
547 "__RULE_INFO__\n\n" +
548 "__VIRUS_INFO__\n" +
549 "__SPAM_INFO__\n",
550 fieldLabel: gettext('Body'),
551 },
552 {
553 xtype: 'proxmoxcheckbox',
554 name: 'attach',
555 fieldLabel: gettext("Attach orig. Mail"),
556 },
557 ],
558 },
559 4003: {
560 onlineHelp: 'pmg_mailfilter_action',
561 xtype: 'proxmoxWindowEdit',
562 subdir: 'field',
563 subject: gettext('Header Attribute'),
564 width: 400,
565 items: [
566 {
567 xtype: 'textfield',
568 name: 'name',
569 allowBlank: false,
570 fieldLabel: gettext('Name'),
571 },
572 {
573 xtype: 'textareafield',
574 name: 'info',
575 fieldLabel: gettext("Comment"),
576 },
577 {
578 xtype: 'textfield',
579 name: 'field',
580 allowBlank: false,
581 fieldLabel: gettext('Field'),
582 },
583 {
584 xtype: 'textfield',
585 reference: 'value',
586 name: 'value',
587 allowBlank: false,
588 fieldLabel: gettext('Value'),
589 },
590 ],
591 },
592 4005: {
593 onlineHelp: 'pmg_mailfilter_action',
594 xtype: 'proxmoxWindowEdit',
595 subdir: 'bcc',
596 subject: gettext('BCC'),
597 width: 400,
598 items: [
599 {
600 xtype: 'textfield',
601 name: 'name',
602 allowBlank: false,
603 fieldLabel: gettext('Name'),
604 },
605 {
606 xtype: 'textareafield',
607 name: 'info',
608 fieldLabel: gettext("Comment"),
609 },
610 {
611 xtype: 'textfield',
612 name: 'target',
613 allowBlank: false,
614 fieldLabel: gettext("Target"),
615 },
616 {
617 xtype: 'proxmoxcheckbox',
618 checked: true,
619 name: 'original',
620 boxLabel: gettext("Send Original Mail"),
621 },
622 ],
623
624 },
625 4007: {
626 onlineHelp: 'pmg_mailfilter_action',
627 xtype: 'proxmoxWindowEdit',
628 subdir: 'removeattachments',
629 subject: gettext('Remove Attachments'),
630 width: 500,
631 items: [
632 {
633 xtype: 'textfield',
634 name: 'name',
635 allowBlank: false,
636 fieldLabel: gettext('Name'),
637 },
638 {
639 xtype: 'textareafield',
640 name: 'info',
641 fieldLabel: gettext("Comment"),
642 },
643 {
644 xtype: 'textareafield',
645 name: 'text',
646 grow: true,
647 growMax: 250,
648 fieldLabel: gettext("Text Replacement"),
649 },
650 {
651 xtype: 'proxmoxcheckbox',
652 checked: true,
653 name: 'all',
654 boxLabel: gettext("Remove all Attachments"),
655 },
656 {
657 xtype: 'proxmoxcheckbox',
658 checked: false,
659 name: 'quarantine',
660 boxLabel: gettext("Copy original mail to Attachment Quarantine"),
661 },
662 ],
663 },
664 4009: {
665 onlineHelp: 'pmg_mailfilter_action',
666 xtype: 'proxmoxWindowEdit',
667 subdir: 'disclaimer',
668 subject: gettext('Disclaimer'),
669 width: 400,
670 items: [
671 {
672 xtype: 'textfield',
673 name: 'name',
674 allowBlank: false,
675 fieldLabel: gettext('Name'),
676 },
677 {
678 xtype: 'textareafield',
679 name: 'info',
680 fieldLabel: gettext("Comment"),
681 },
682 {
683 xtype: 'textareafield',
684 name: 'disclaimer',
685 grow: true,
686 growMax: 250,
687 fieldLabel: gettext("Disclaimer"),
688 },
689 ],
690 },
691 },
692
693 updateLoginData: function(data) {
694 Proxmox.CSRFPreventionToken = data.CSRFPreventionToken;
695 Proxmox.UserName = data.username;
696 Ext.util.Cookies.set('PMGAuthCookie', data.ticket, null, '/', null, true);
697 },
698
699 quarantineActionExtracted: false,
700
701 extractQuarantineAction: function() {
702 if (PMG.Utils.quarantineActionExtracted) {
703 return null;
704 }
705
706 PMG.Utils.quarantineActionExtracted = true;
707
708 let qs = Ext.Object.fromQueryString(location.search);
709
710 let cselect = qs.cselect;
711 let action = qs.action;
712 let dateString = qs.date;
713
714 if (dateString) {
715 let date = new Date(dateString).getTime()/1000;
716
717 // set from date for QuarantineList
718 PMG.QuarantineList.from = date;
719 }
720
721 delete qs.cselect;
722 delete qs.action;
723 delete qs.ticket;
724 delete qs.date;
725
726 var newsearch = Ext.Object.toQueryString(qs);
727
728 var newurl = location.protocol + "//" + location.host + location.pathname;
729 if (newsearch) { newurl += '?' + newsearch; }
730 newurl += location.hash;
731
732 if (window.history) {
733 window.history.pushState({ path: newurl }, '', newurl);
734 }
735
736 if (action || cselect) {
737 return {
738 action: action,
739 cselect: cselect,
740 };
741 }
742 return null;
743 },
744
745 doQuarantineAction: function(action, id, callback) {
746 Proxmox.Utils.API2Request({
747 url: '/quarantine/content/',
748 params: {
749 action: action,
750 id: id,
751 },
752 method: 'POST',
753 failure: function(response, opts) {
754 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
755 },
756 success: function(response, opts) {
757 let count = id.split(';').length;
758 let fmt = count > 1
759 ? gettext("Action '{0}' for '{1}' items successful")
760 : gettext("Action '{0}' successful")
761 ;
762 let message = Ext.String.format(fmt, action, count);
763 let title = Ext.String.format("{0} successful", Ext.String.capitalize(action));
764
765 Ext.toast({
766 html: message,
767 title: title,
768 minWidth: 200,
769 hideDuration: 250,
770 slideBackDuration: 250,
771 slideBackAnimation: 'easeOut',
772 iconCls: 'fa fa-check',
773 shadow: true,
774 align: 'br',
775 });
776
777 if (Ext.isFunction(callback)) {
778 callback();
779 }
780 },
781 });
782 },
783
784 render_filetype: function(value) {
785 let iconCls = 'fa-file-o';
786 let text = Proxmox.Utils.unknownText;
787
788 if (!value) {
789 return `<i class='fa ${iconCls}'></i> ${text}`;
790 }
791
792 text = value.toString().toLowerCase();
793 const type = text.split('/')[0];
794
795 switch (type) {
796 case 'audio':
797 case 'image':
798 case 'video':
799 case 'text':
800 iconCls = `fa-file-${type}-o`;
801 break;
802 case 'application': {
803 const subtypes = ['excel', 'pdf', 'word', 'powerpoint'];
804 let found = subtypes.find(st => text.includes(st));
805 if (found !== undefined) {
806 iconCls = `fa-file-${found}-o`;
807 }
808 } break;
809 default:
810 break;
811 }
812
813 return `<i class='fa ${iconCls}'></i> ${text}`;
814 },
815
816 render_envelope: function(value, { data }, render_receiver) {
817 let subject = Ext.htmlEncode(value);
818 let from = Ext.htmlEncode(data.from);
819 if (data.sender) {
820 let sender = Ext.htmlEncode(data.sender);
821 from = Ext.String.format(gettext("{0} on behalf of {1}"), sender, from);
822 }
823 if (render_receiver) {
824 let receiver = Ext.htmlEncode(data.receiver);
825 return `<small>${from}<br>To: ${receiver}</small><br>${subject}`;
826 }
827 return `<small>${from}</small><br>${subject}`;
828 },
829
830 render_sender: (value, _meta, rec) => PMG.Utils.render_envelope(value, rec, false),
831 render_sender_receiver: (value, _meta, rec) => PMG.Utils.render_envelope(value, rec, true),
832
833 constructor: function() {
834 var me = this;
835
836 // do whatever you want here
837 Proxmox.Utils.override_task_descriptions({
838 applycustomscores: ['', gettext('Apply custom SpamAssassin scores')],
839 avupdate: ['', gettext('ClamAV update')],
840 backup: ['', gettext('Backup')],
841 clustercreate: ['', gettext('Create Cluster')],
842 clusterjoin: ['', gettext('Join Cluster')],
843 restore: ['', gettext('Restore')],
844 saupdate: ['', gettext('SpamAssassin update')],
845 });
846 },
847 });
848
849 Ext.define('PMG.Async', {
850 singleton: true,
851
852 // Returns a Promise which executes a quarantine action when awaited.
853 // Shows a Toast message box once completed, if batchNumber and batchTotal
854 // are set, they will be included into the title of that toast.
855 doQAction: function(action, ids, batchNumber, batchTotal) {
856 if (!Ext.isArray(ids)) {
857 ids = [ids];
858 }
859 return Proxmox.Async.api2({
860 url: '/quarantine/content/',
861 params: {
862 action: action,
863 id: ids.join(';'),
864 },
865 method: 'POST',
866 }).then(
867 response => {
868 let count = ids.length;
869 let fmt = count > 1
870 ? gettext("Action '{0}' for '{1}' items successful")
871 : gettext("Action '{0}' successful")
872 ;
873 let message = Ext.String.format(fmt, action, count);
874 let titleFmt = batchNumber !== undefined && batchTotal > 1
875 ? gettext("{0} ({1}/{2}) successful")
876 : gettext("{0} successful")
877 ;
878 let title = Ext.String.format(
879 titleFmt,
880 Ext.String.capitalize(action),
881 batchNumber,
882 batchTotal,
883 );
884
885 Ext.toast({
886 html: message,
887 title: title,
888 minWidth: 200,
889 hideDuration: 250,
890 slideBackDuration: 250,
891 slideBackAnimation: 'easeOut',
892 iconCls: 'fa fa-check',
893 shadow: true,
894 align: 'br',
895 });
896 },
897 response => Proxmox.Utils.alertResponseFailure(response),
898 );
899 },
900 });
901
902 // custom Vtypes
903 Ext.apply(Ext.form.field.VTypes, {
904 // matches the pmg-email-address in pmg-api
905 PMGMail: function(v) {
906 return (/[^\s\\@]+@[^\s/\\@]+/).test(v);
907 },
908 PMGMailText: gettext('Example') + ": user@example.com",
909 });