]> git.proxmox.com Git - pmg-gui.git/blob - js/QuarantineList.js
fix #3287: add recipient filter for virus & attachment quarantines
[pmg-gui.git] / js / QuarantineList.js
1 Ext.define('PMG.QuarantineList', {
2 extend: 'Ext.grid.GridPanel',
3 xtype: 'pmgQuarantineList',
4
5 emptyText: gettext('No E-Mail address selected'),
6 viewConfig: {
7 deferEmptyText: false,
8 },
9
10 config: {
11 quarantineType: 'spam',
12 notFoundText: gettext('No data in database'),
13 },
14
15 statics: {
16 from: 0,
17 to: 0,
18 },
19
20 allowPositionSave: false,
21
22 controller: {
23 xclass: 'Ext.app.ViewController',
24
25 init: function(view) {
26 let me = this;
27 let emailCombobox = me.lookupReference('email');
28 if (PMG.view === 'quarantineview') {
29 view.autoLoadAll = false;
30 me.setEmptyText();
31 } else {
32 emailCombobox.setVisible(true);
33 emailCombobox.setDisabled(false);
34 emailCombobox.getStore().on('load', me.injectAllOption, me);
35 }
36
37
38 if (view.quarantineType) {
39 emailCombobox.getStore().getProxy().setExtraParams({
40 'quarantine-type': view.quarantineType,
41 });
42 }
43
44 let from;
45 if (PMG.QuarantineList.from !== 0) {
46 from = new Date(PMG.QuarantineList.from * 1000);
47 } else {
48 from = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
49 }
50
51 let to;
52 if (PMG.QuarantineList.to !== 0) {
53 to = new Date(PMG.QuarantineList.to * 1000);
54 } else {
55 to = new Date();
56 }
57
58 // we to this to trigger the change event of those fields
59 me.lookupReference('from').setValue(from);
60 me.lookupReference('to').setValue(to);
61
62 Proxmox.Utils.monStoreErrors(view.getView(), view.getStore());
63 me.load(function() {
64 if (view.cselect) {
65 view.setSelection(view.getStore().getById(view.cselect));
66 }
67 });
68 },
69 // ExtJS cannot dynamically change the emptyText on grids, so implement ourself
70 setEmptyText: function(emptyText) {
71 let me = this;
72 let view = me.getView();
73 let tableview = view.getView();
74 tableview.emptyText = `<div class="x-grid-empty">${emptyText || view.notFoundText}</div>`;
75 },
76
77 load: function(callback) {
78 let me = this;
79 me.allowPositionSave = false;
80 let view = me.getView();
81 let store = view.getStore();
82 if (view.quarantineType === 'spam' && PMG.view !== 'quarantineview') {
83 if (!me.lookupReference('email').getSelection()) {
84 return; // if the combobox has no selection we do not reload
85 }
86 me.setEmptyText();
87 }
88 // deselect all first, else ExtJS does some funky O(n^3) comparissions as it tries
89 // to keep the selection, but we do not care for that on a new load anyway
90 view.getSelectionModel().deselectAll();
91
92 store.load(() => {
93 me.restoreSavedSelection();
94 if (Ext.isFunction(callback)) {
95 callback();
96 }
97 me.allowPositionSave = true;
98 });
99 },
100
101 restoreSavedSelection: function() {
102 let me = this;
103 let view = me.getView();
104 if (me.savedPosition !== undefined) {
105 let store = view.getStore();
106 if (store.getCount() - 1 < me.savedPosition) {
107 me.savedPosition = store.getCount() - 1;
108 }
109 view.setSelection(store.getAt(me.savedPosition));
110 } else {
111 view.setSelection();
112 }
113 },
114
115 setFrom: function(from) {
116 let view = this.getView();
117 let params = view.getStore().getProxy().getExtraParams();
118 params.starttime = from;
119 PMG.QuarantineList.from = from;
120 view.getStore().getProxy().setExtraParams(params);
121 },
122
123 setTo: function(to) {
124 let end_of_to = to + 24*60*60; // we want the end of the day
125 let view = this.getView();
126 let params = view.getStore().getProxy().getExtraParams();
127 params.endtime = end_of_to;
128 PMG.QuarantineList.to = to; // we save the start of the day here
129 view.getStore().getProxy().setExtraParams(params);
130 },
131
132 setUser: function(user) {
133 let view = this.getView();
134 let params = view.getStore().getProxy().getExtraParams();
135 if (!user) {
136 delete params.pmail;
137 } else {
138 params.pmail = user;
139 }
140 view.getStore().getProxy().setExtraParams(params);
141 view.user = user;
142 },
143
144 changeTime: function(field, value) {
145 let me = this;
146
147 me.allowPositionSave = false;
148 me.savedPosition = undefined;
149
150 if (!value) {
151 return;
152 }
153
154 let val = value.getTime() / 1000;
155 let combobox = me.lookupReference('email');
156 let params = combobox.getStore().getProxy().getExtraParams();
157
158 let to = me.lookupReference('to');
159 let from = me.lookupReference('from');
160
161 if (field.name === 'from') {
162 me.setFrom(val);
163 params.starttime = val;
164 to.setMinValue(value);
165 } else if (field.name === 'to') {
166 me.setTo(val);
167 params.endtime = val + 24*60*60;
168 from.setMaxValue(value);
169 } else {
170 return;
171 }
172
173 combobox.getStore().getProxy().setExtraParams(params);
174 combobox.getStore().load();
175
176 me.load();
177 },
178
179 resetEmail: function() {
180 let me = this;
181 me.setUser(undefined);
182 },
183
184 changeEmail: function(tb, value) {
185 let me = this;
186 me.savedPosition = undefined;
187 me.allowPositionSave = false;
188 if (value === 'all') {
189 me.setUser(null);
190 } else {
191 me.setUser(value);
192 }
193 me.load();
194 },
195
196 savePosition: function(grid, selected, eopts) {
197 let me = this;
198 if (!me.allowPositionSave) {
199 return;
200 }
201 if (selected.length <= 0) {
202 me.savedPosition = undefined;
203 return;
204 }
205
206 let view = me.getView();
207 let id = view.getStore().indexOf(selected[0]);
208
209 me.savedPosition = id;
210 },
211
212 doFilter: function(searchValue, store, sm) {
213 const selected = sm.getSelection();
214 const selectedRecordId = selected.length === 1 ? selected[0].id : null;
215 let clearSelectedMail = true;
216 let toDeselect = [];
217 store.filterBy(function(record) {
218 let match = false;
219
220 Ext.each(['subject', 'from'], property => {
221 if (record.data[property] === null) {
222 return;
223 }
224
225 let v = record.data[property].toString();
226 if (v !== undefined) {
227 v = v.toLowerCase();
228 if (v.includes(searchValue)) {
229 match = true;
230 if (record.id === selectedRecordId) {
231 clearSelectedMail = false;
232 }
233 }
234 }
235 });
236 if (!match && sm.isSelected(record)) {
237 toDeselect.push(record);
238 }
239 return match;
240 });
241 if (toDeselect.length > 0) {
242 sm.deselect(toDeselect, true);
243 sm.maybeFireSelectionChange(true);
244 }
245 return selectedRecordId !== null && clearSelectedMail;
246 },
247
248 updateFilter: async function(field) {
249 let me = this;
250 let view = me.getView();
251 let store = view.getStore();
252 let sm = view.getSelectionModel();
253
254 let searchValue = field.getValue().toLowerCase();
255
256 // supress store event if not empty, let filterBy below trigger it to avoid glitches
257 store.clearFilter(searchValue.length > 0);
258 field.triggers.clear.setVisible(searchValue.length > 0);
259
260 if (searchValue.length === 0) {
261 me.setEmptyText();
262 return;
263 }
264 me.setEmptyText(gettext('No match found'));
265
266 let clearSelection = me.doFilter(searchValue, store, sm);
267
268 if (clearSelection) {
269 view.setSelection();
270 }
271 },
272
273 injectAllOption: function(store, records, successfull) {
274 let me = this;
275 let view = me.getView();
276 if (successfull && records.length > 1) {
277 store.insert(0, { mail: 'all' });
278 }
279 let emailCombobox = me.lookup('email');
280 if (!emailCombobox.getSelection() && view.quarantineType !== 'spam') {
281 emailCombobox.setSelection(store.getAt(0));
282 }
283 },
284
285 control: {
286 '#': {
287 beforedestroy: 'resetEmail',
288 selectionchange: 'savePosition',
289 },
290 'combobox[reference=email]': {
291 change: 'changeEmail',
292 },
293 datefield: {
294 change: {
295 fn: 'changeTime',
296 },
297 },
298
299 },
300 },
301
302 features: [
303 {
304 ftype: 'grouping',
305 groupHeaderTpl: '{columnName}: {name} ({children.length})',
306 },
307 ],
308
309 tbar: {
310 layout: {
311 type: 'vbox',
312 align: 'stretch',
313 },
314 defaults: {
315 margin: 2,
316 },
317 items: [
318 {
319 xtype: 'datefield',
320 name: 'from',
321 fieldLabel: gettext('Since'),
322 reference: 'from',
323 format: 'Y-m-d',
324 },
325 {
326 xtype: 'datefield',
327 name: 'to',
328 fieldLabel: gettext('Until'),
329 reference: 'to',
330 format: 'Y-m-d',
331 },
332 {
333 xtype: 'combobox',
334 hidden: true,
335 disabled: true,
336 displayField: 'mail',
337 valueField: 'mail',
338 listConfig: {
339 emptyText:
340 '<div class="x-grid-empty">' +
341 gettext('No data in database') +
342 '</div>',
343 },
344 store: {
345 proxy: {
346 type: 'proxmox',
347 url: '/api2/json/quarantine/spamusers',
348 },
349 fields: [
350 {
351 name: 'mail',
352 renderer: Ext.htmlEncode,
353 },
354 ],
355 },
356 queryMode: 'local',
357 editable: true,
358 typeAhead: true,
359 forceSelection: true,
360 autoSelect: true,
361 anyMatch: true,
362 selectOnFocus: true,
363 reference: 'email',
364 fieldLabel: 'E-Mail',
365 },
366 {
367 xtype: 'textfield',
368 name: 'filter',
369 fieldLabel: gettext('Search'),
370 emptyText: gettext('Subject, Sender'),
371 enableKeyEvents: true,
372 triggers: {
373 clear: {
374 cls: 'pmx-clear-trigger',
375 weight: -1,
376 hidden: true,
377 handler: function() {
378 let me = this;
379 me.setValue('');
380 // setValue does not results in a keyup event, so trigger manually
381 me.up('grid').getController().updateFilter(me);
382 },
383 },
384 },
385 listeners: {
386 buffer: 500,
387 keyup: 'updateFilter',
388 },
389 },
390 ],
391 },
392 });