]>
Commit | Line | Data |
---|---|---|
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 | tb.triggers.clear.setVisible(value?.length > 0 && value !== 'all'); | |
194 | me.load(); | |
195 | }, | |
196 | ||
197 | savePosition: function(grid, selected, eopts) { | |
198 | let me = this; | |
199 | if (!me.allowPositionSave) { | |
200 | return; | |
201 | } | |
202 | if (selected.length <= 0) { | |
203 | me.savedPosition = undefined; | |
204 | return; | |
205 | } | |
206 | ||
207 | let view = me.getView(); | |
208 | let id = view.getStore().indexOf(selected[0]); | |
209 | ||
210 | me.savedPosition = id; | |
211 | }, | |
212 | ||
213 | doFilter: function(searchValue, store, sm) { | |
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 | ||
221 | Ext.each(['subject', 'from'], property => { | |
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) { | |
243 | sm.deselect(toDeselect, true); | |
244 | sm.maybeFireSelectionChange(true); | |
245 | } | |
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) { | |
270 | view.setSelection(); | |
271 | } | |
272 | }, | |
273 | ||
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 | ||
286 | control: { | |
287 | '#': { | |
288 | beforedestroy: 'resetEmail', | |
289 | selectionchange: 'savePosition', | |
290 | }, | |
291 | 'combobox[reference=email]': { | |
292 | change: 'changeEmail', | |
293 | }, | |
294 | datefield: { | |
295 | change: { | |
296 | fn: 'changeTime', | |
297 | }, | |
298 | }, | |
299 | ||
300 | }, | |
301 | }, | |
302 | ||
303 | features: [ | |
304 | { | |
305 | ftype: 'grouping', | |
306 | groupHeaderTpl: '{columnName}: {name} ({children.length})', | |
307 | }, | |
308 | ], | |
309 | ||
310 | tbar: { | |
311 | layout: { | |
312 | type: 'vbox', | |
313 | align: 'stretch', | |
314 | }, | |
315 | defaults: { | |
316 | margin: 2, | |
317 | }, | |
318 | items: [ | |
319 | { | |
320 | xtype: 'datefield', | |
321 | name: 'from', | |
322 | fieldLabel: gettext('Since'), | |
323 | reference: 'from', | |
324 | format: 'Y-m-d', | |
325 | }, | |
326 | { | |
327 | xtype: 'datefield', | |
328 | name: 'to', | |
329 | fieldLabel: gettext('Until'), | |
330 | reference: 'to', | |
331 | format: 'Y-m-d', | |
332 | }, | |
333 | { | |
334 | xtype: 'combobox', | |
335 | hidden: true, | |
336 | disabled: true, | |
337 | displayField: 'mail', | |
338 | valueField: 'mail', | |
339 | listConfig: { | |
340 | emptyText: `<div class="x-grid-empty">${gettext('No data in database')}</div>`, | |
341 | }, | |
342 | store: { | |
343 | proxy: { | |
344 | type: 'proxmox', | |
345 | url: '/api2/json/quarantine/spamusers', | |
346 | }, | |
347 | fields: [ | |
348 | { | |
349 | name: 'mail', | |
350 | renderer: Ext.htmlEncode, | |
351 | }, | |
352 | ], | |
353 | }, | |
354 | queryMode: 'local', | |
355 | editable: true, | |
356 | typeAhead: true, | |
357 | forceSelection: true, | |
358 | autoSelect: true, | |
359 | anyMatch: true, | |
360 | selectOnFocus: true, | |
361 | reference: 'email', | |
362 | fieldLabel: gettext('E-Mail'), | |
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 | }, | |
375 | }, | |
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 | }, | |
400 | ], | |
401 | }, | |
402 | }); |