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