]> git.proxmox.com Git - pmg-gui.git/blame - js/SpamQuarantine.js
quarantine: restore behavior of selecting next in list after action
[pmg-gui.git] / js / SpamQuarantine.js
CommitLineData
d9c3552a
DM
1Ext.define('pmg-spam-archive', {
2 extend: 'Ext.data.Model',
3 fields: [
4 { type: 'number', name: 'spamavg' },
5 { type: 'integer', name: 'count' },
c87d46fb 6 { type: 'date', dateFormat: 'timestamp', name: 'day' },
d9c3552a
DM
7 ],
8 proxy: {
9 type: 'proxmox',
c87d46fb 10 url: "/api2/json/quarantine/spam",
d9c3552a 11 },
c87d46fb 12 idProperty: 'day',
d9c3552a
DM
13});
14
d9c3552a
DM
15Ext.define('pmg-spam-list', {
16 extend: 'Ext.data.Model',
c87d46fb 17 fields: ['id', 'envelope_sender', 'from', 'sender', 'receiver', 'subject',
af5aba0c 18 { type: 'number', name: 'spamlevel' },
d9c3552a 19 { type: 'integer', name: 'bytes' },
c96a22cb
DC
20 { type: 'date', dateFormat: 'timestamp', name: 'time' },
21 {
22 type: 'string',
23 name: 'day',
24 convert: function(v, rec) {
25 return Ext.Date.format(rec.get('time'), 'Y-m-d');
c87d46fb
TL
26 }, depends: ['time'],
27 },
d9c3552a
DM
28 ],
29 proxy: {
30 type: 'proxmox',
c87d46fb 31 url: "/api2/json/quarantine/spam",
d9c3552a 32 },
c87d46fb 33 idProperty: 'id',
d9c3552a
DM
34});
35
0277bfeb 36Ext.define('PMG.SpamQuarantine', {
d9c3552a 37 extend: 'Ext.container.Container',
0277bfeb
DM
38 xtype: 'pmgSpamQuarantine',
39
0277bfeb 40 border: false,
ea07c9aa 41 layout: { type: 'border' },
d9c3552a 42
0277bfeb
DM
43 defaults: { border: false },
44
207471c0
DC
45 // from mail link
46 cselect: undefined,
47
41a290e3
TL
48 viewModel: {
49 parent: null,
50 data: {
51 mailid: '',
52 },
53 formulas: {
54 downloadMailURL: get => '/api2/json/quarantine/download?mailid=' + encodeURIComponent(get('mailid')),
55 },
56 },
d9c3552a
DM
57 controller: {
58
2d1f7824 59 xclass: 'Ext.app.ViewController',
d9c3552a 60
2d1f7824 61 updatePreview: function(raw, rec) {
d9c3552a
DM
62 var preview = this.lookupReference('preview');
63
c87d46fb 64 if (!rec || !rec.data || !rec.data.id) {
d9c3552a 65 preview.update('');
2d1f7824 66 preview.setDisabled(true);
c96a22cb
DC
67 return;
68 }
69
c87d46fb 70 var url = '/api2/htmlmail/quarantine/content?id=' + rec.data.id + (raw?'&raw=1':'');
2d1f7824 71 preview.setDisabled(false);
d42f85b8
DC
72 this.lookupReference('raw').setDisabled(false);
73 this.lookupReference('spam').setDisabled(false);
7ad0de10 74 this.lookupReference('download').setDisabled(false);
c96a22cb
DC
75 preview.update("<iframe frameborder=0 width=100% height=100% sandbox='allow-same-origin' src='" + url +"'></iframe>");
76 },
77
b9a5e707 78 multiSelect: function(selection) {
d42f85b8
DC
79 var preview = this.lookupReference('preview');
80 var raw = this.lookupReference('raw');
81 var spam = this.lookupReference('spam');
82 var spaminfo = this.lookupReference('spaminfo');
ac6c1fb8 83 var mailinfo = this.lookupReference('mailinfo');
7ad0de10 84 var download = this.lookupReference('download');
d42f85b8
DC
85
86 preview.setDisabled(false);
b9a5e707 87 preview.update(`<h3 style="padding-left:5px;">${gettext('Multiple E-Mails selected')} (${selection.length})</h3>`);
d42f85b8
DC
88 raw.setDisabled(true);
89 spam.setDisabled(true);
90 spam.setPressed(false);
91 spaminfo.setVisible(false);
ac6c1fb8 92 mailinfo.setVisible(false);
7ad0de10 93 download.setDisabled(true);
d42f85b8
DC
94 },
95
2d1f7824 96 toggleRaw: function(button) {
c96a22cb 97 var me = this;
4935e8fb 98 var list = me.lookupReference('list');
b76ce2c5 99 var rec = list.selModel.getSelection()[0];
4935e8fb 100 me.lookupReference('mailinfo').setVisible(me.raw);
2d1f7824 101 me.raw = !me.raw;
b76ce2c5 102 me.updatePreview(me.raw, rec);
c96a22cb
DC
103 },
104
2d1f7824 105 btnHandler: function(button, e) {
7f0619ff
DC
106 var me = this;
107 var action = button.reference;
2d1f7824
DC
108 var list = this.lookupReference('list');
109 var selected = list.getSelection();
7f0619ff
DC
110 me.doAction(action, selected);
111 },
112
113 doAction: function(action, selected) {
2d1f7824 114 if (!selected.length) {
d9c3552a
DM
115 return;
116 }
117
7f0619ff 118 var list = this.lookupReference('list');
c96a22cb 119
d42f85b8 120 if (selected.length > 1) {
b61e38d7 121 let idlist = selected.map(item => item.data.id);
d42f85b8
DC
122 Ext.Msg.confirm(
123 gettext('Confirm'),
124 Ext.String.format(
125 gettext("Action '{0}' for '{1}' items"),
c87d46fb 126 action, selected.length,
d42f85b8 127 ),
3168b7f7 128 async function(button) {
d42f85b8
DC
129 if (button !== 'yes') {
130 return;
131 }
132
3168b7f7
TL
133 list.mask(gettext('Processing...'), 'x-mask-loading');
134
135 const sliceSize = 2500, maxInFlight = 2;
136 let batches = [], batchCount = Math.ceil(selected.length / sliceSize);
137 for (let i = 0; i * sliceSize < selected.length; i++) {
138 let sliceStart = i * sliceSize;
139 let sliceEnd = Math.min(sliceStart + sliceSize, selected.length);
140 batches.push(
141 PMG.Async.doQAction(
142 action,
143 idlist.slice(sliceStart, sliceEnd),
144 i + 1,
145 batchCount,
146 ),
147 );
148 if (batches.length >= maxInFlight) {
149 await Promise.allSettled(batches); // eslint-disable-line no-await-in-loop
150 batches = [];
151 }
152 }
153 await Promise.allSettled(batches); // await possible remaining ones
154 list.unmask();
155 // below can be slow, we could remove directly from the in-memory store, but
156 // with lots of elements and some failures we could be quite out of sync?
157 list.getController().load();
c87d46fb 158 },
d42f85b8
DC
159 );
160 return;
161 }
162
aac17b9b 163 PMG.Utils.doQuarantineAction(action, selected[0].data.id, function() {
9aed379a
TL
164 // success -> remove directly to avoid slow store reload for a single-element action
165 list.getStore().remove(selected[0]);
642aba90 166 list.getController().restoreSavedSelection();
2d1f7824 167 });
c96a22cb
DC
168 },
169
2d1f7824
DC
170 onSelectMail: function() {
171 var me = this;
172 var list = this.lookupReference('list');
d42f85b8
DC
173 var selection = list.selModel.getSelection();
174 if (selection.length > 1) {
b9a5e707 175 me.multiSelect(selection);
d42f85b8
DC
176 return;
177 }
2d1f7824 178
38771d94
DC
179 var rec = selection[0] || {};
180
181 me.getViewModel().set('mailid', rec.data ? rec.data.id : '');
2d1f7824
DC
182 me.updatePreview(me.raw || false, rec);
183 me.lookupReference('spaminfo').setID(rec);
72d8403c 184 me.lookupReference('mailinfo').setVisible(!!rec.data && !me.raw);
ac6c1fb8 185 me.lookupReference('mailinfo').update(rec.data);
d9c3552a 186 },
c96a22cb 187
2d1f7824
DC
188 toggleSpamInfo: function(btn) {
189 var grid = this.lookupReference('spaminfo');
190 grid.setVisible(!grid.isVisible());
8e89b895
DC
191 },
192
c627f092
DC
193 openContextMenu: function(table, record, tr, index, event) {
194 event.stopEvent();
0affcba5
TL
195 let me = this;
196 let list = me.lookup('list');
197 Ext.create('PMG.menu.SpamContextMenu', {
198 callback: action => me.doAction(action, list.getSelection()),
199 }).showAt(event.getXY());
c627f092
DC
200 },
201
c87d46fb 202 keyPress: function(table, record, item, index, event) {
0b9c0528
DC
203 var me = this;
204 var list = me.lookup('list');
205 var key = event.getKey();
206 var action = '';
c87d46fb 207 switch (key) {
0b9c0528
DC
208 case event.DELETE:
209 case 127:
210 action = 'delete';
211 break;
212 case Ext.event.Event.D:
213 case Ext.event.Event.D + 32:
214 action = 'deliver';
215 break;
216 case Ext.event.Event.W:
217 case Ext.event.Event.W + 32:
218 action = 'whitelist';
219 break;
220 case Ext.event.Event.B:
221 case Ext.event.Event.B + 32:
222 action = 'blacklist';
223 break;
224 }
225
226 if (action !== '') {
227 me.doAction(action, list.getSelection());
228 }
229 },
230
207471c0
DC
231 init: function(view) {
232 this.lookup('list').cselect = view.cselect;
233 },
234
c96a22cb 235 control: {
c96a22cb 236 'button[reference=raw]': {
c87d46fb 237 click: 'toggleRaw',
c96a22cb 238 },
2d1f7824 239 'button[reference=spam]': {
c87d46fb 240 click: 'toggleSpamInfo',
c96a22cb 241 },
2d1f7824 242 'pmgQuarantineList': {
c627f092 243 selectionChange: 'onSelectMail',
0b9c0528 244 itemkeypress: 'keyPress',
c87d46fb
TL
245 rowcontextmenu: 'openContextMenu',
246 },
247 },
d9c3552a
DM
248 },
249
0277bfeb
DM
250 items: [
251 {
56b4528f 252 title: gettext('Spam Quarantine'),
2d1f7824 253 xtype: 'pmgQuarantineList',
62651172 254 selModel: 'checkboxmodel',
2d1f7824
DC
255 emailSelection: true,
256 reference: 'list',
d9c3552a 257 region: 'west',
c96a22cb 258 width: 500,
d9c3552a 259 split: true,
c96a22cb 260 collapsible: false,
2d1f7824
DC
261 store: {
262 model: 'pmg-spam-list',
263 groupField: 'day',
264 groupDir: 'DESC',
265 sorters: [{
266 property: 'time',
c87d46fb
TL
267 direction: 'DESC',
268 }],
2d1f7824
DC
269 },
270
271 columns: [
272 {
273 header: gettext('Sender/Subject'),
274 dataIndex: 'subject',
275 renderer: PMG.Utils.sender_renderer,
c87d46fb 276 flex: 1,
2d1f7824
DC
277 },
278 {
279 header: gettext('Score'),
280 dataIndex: 'spamlevel',
281 align: 'right',
c87d46fb 282 width: 70,
2d1f7824
DC
283 },
284 {
285 header: gettext('Size') + ' (KB)',
286 renderer: function(v) { return Ext.Number.toFixed(v/1024, 0); },
287 dataIndex: 'bytes',
288 align: 'right',
c87d46fb 289 width: 90,
2d1f7824
DC
290 },
291 {
292 header: gettext('Date'),
293 dataIndex: 'day',
c87d46fb 294 hidden: true,
2d1f7824
DC
295 },
296 {
297 xtype: 'datecolumn',
298 header: gettext('Time'),
299 dataIndex: 'time',
c87d46fb
TL
300 format: 'H:i:s',
301 },
302 ],
0277bfeb
DM
303 },
304 {
c96a22cb 305 title: gettext('Selected Mail'),
ea07c9aa 306 border: false,
d9c3552a 307 region: 'center',
c96a22cb
DC
308 split: true,
309 reference: 'preview',
310 disabled: true,
2d1f7824
DC
311 dockedItems: [
312 {
313 xtype: 'toolbar',
314 dock: 'top',
315 items: [
316 {
317 xtype: 'button',
318 reference: 'raw',
319 text: gettext('Toggle Raw'),
320 enableToggle: true,
c87d46fb 321 iconCls: 'fa fa-file-code-o',
2d1f7824
DC
322 },
323 {
324 xtype: 'button',
325 reference: 'spam',
326 text: gettext('Toggle Spam Info'),
327 enableToggle: true,
c87d46fb 328 iconCls: 'fa fa-bullhorn',
2d1f7824
DC
329 },
330 '->',
7ad0de10
DC
331 {
332 xtype: 'button',
333 reference: 'download',
334 text: gettext('Download'),
07a9b445
TL
335 setDownload: function(id) {
336 this.el.dom.download = id + ".eml";
337 },
338 bind: {
339 href: '{downloadMailURL}',
340 download: '{mailid}',
341 },
c87d46fb 342 iconCls: 'fa fa-download',
7ad0de10
DC
343 },
344 '-',
2d1f7824
DC
345 {
346 reference: 'whitelist',
347 text: gettext('Whitelist'),
348 iconCls: 'fa fa-check',
c87d46fb 349 handler: 'btnHandler',
2d1f7824
DC
350 },
351 {
352 reference: 'blacklist',
353 text: gettext('Blacklist'),
354 iconCls: 'fa fa-times',
c87d46fb 355 handler: 'btnHandler',
2d1f7824
DC
356 },
357 {
358 reference: 'deliver',
359 text: gettext('Deliver'),
360 iconCls: 'fa fa-paper-plane-o',
c87d46fb 361 handler: 'btnHandler',
2d1f7824
DC
362 },
363 {
364 reference: 'delete',
365 text: gettext('Delete'),
366 iconCls: 'fa fa-trash-o',
c87d46fb
TL
367 handler: 'btnHandler',
368 },
369 ],
2d1f7824
DC
370 },
371 {
372 xtype: 'pmgSpamInfoGrid',
f34abaca 373 border: false,
c87d46fb 374 reference: 'spaminfo',
ac6c1fb8
DC
375 },
376 {
377 xtype: 'pmgMailInfo',
378 hidden: true,
379 reference: 'mailinfo',
380 },
c87d46fb
TL
381 ],
382 },
383 ],
0277bfeb 384});