]> git.proxmox.com Git - pmg-gui.git/blob - js/MailTracker.js
user view: disable unlock TFA button initially
[pmg-gui.git] / js / MailTracker.js
1 Ext.define('pmg-mail-tracker', {
2 extend: 'Ext.data.Model',
3 fields: [
4 'id', 'from', 'to', 'dstatus', 'rstatus', 'qid', 'msgid', 'client',
5 { type: 'number', name: 'size' },
6 { type: 'date', dateFormat: 'timestamp', name: 'time' },
7 ],
8 proxy: {
9 type: 'proxmox',
10 },
11 // do not use field 'id', because "id/to" is the unique Id
12 // this way we display an entry for each receiver
13 idProperty: 'none',
14 });
15
16
17 Ext.define('PMG.MailTrackerFilter', {
18 extend: 'Ext.container.Container',
19 xtype: 'pmgMailTrackerFilter',
20
21 layout: {
22 type: 'hbox',
23 },
24
25 controller: {
26
27 xclass: 'Ext.app.ViewController',
28
29 onFilterChange: function() {
30 let view = this.getView();
31 view.fireEvent('filterChanged');
32 },
33
34 onSpecialKey: function(field, e) {
35 if (e.getKey() === e.ENTER) {
36 this.onFilterChange();
37 }
38 },
39 },
40
41 viewModel: {},
42
43 getFilterParams: function() {
44 let me = this;
45 let param = {};
46
47 let names = ['from', 'target', 'xfilter', 'starttime', 'endtime', 'ndr', 'greylist'];
48 Ext.Array.each(names, function(name) {
49 let value = me.lookupReference(name).getSubmitValue();
50 if (value) { param[name] = value; }
51 });
52
53 // there must always be a start and endtime, otherwise the field was invalid
54 if (!param.starttime || !param.endtime) {
55 return undefined;
56 }
57 return param;
58 },
59
60 items: [
61 {
62 width: 400,
63 border: false,
64 padding: 10,
65 layout: {
66 type: 'vbox',
67 align: 'stretch',
68 },
69 items: [
70 {
71 fieldLabel: gettext('Sender'),
72 xtype: 'textfield',
73 listeners: { specialkey: 'onSpecialKey' },
74 reference: 'from',
75 },
76 {
77 fieldLabel: gettext('Receiver'),
78 xtype: 'textfield',
79 listeners: { specialkey: 'onSpecialKey' },
80 reference: 'target',
81 },
82 {
83 fieldLabel: gettext('Filter'),
84 xtype: 'textfield',
85 listeners: { specialkey: 'onSpecialKey' },
86 reference: 'xfilter',
87 },
88 ],
89 },
90 {
91 border: false,
92 padding: 10,
93 layout: {
94 type: 'vbox',
95 align: 'stretch',
96 },
97 items: [
98 {
99 fieldLabel: gettext('Start'),
100 reference: 'starttime',
101 listeners: {
102 change: {
103 fn: 'onFilterChange',
104 buffer: 500,
105 },
106 },
107 value: (function() {
108 let now = new Date();
109 return new Date(now.getTime() - 3600000);
110 }()),
111 xtype: 'promxoxDateTimeField',
112 },
113 {
114 fieldLabel: gettext('End'),
115 reference: 'endtime',
116 listeners: {
117 change: {
118 fn: 'onFilterChange',
119 buffer: 500,
120 },
121 },
122 value: (function() {
123 let now = new Date();
124 let tomorrow = new Date();
125 tomorrow.setDate(now.getDate()+1);
126 tomorrow.setHours(0);
127 tomorrow.setMinutes(0);
128 tomorrow.setSeconds(0);
129 return tomorrow;
130 }()),
131 xtype: 'promxoxDateTimeField',
132 },
133 {
134 layout: {
135 type: 'hbox',
136 },
137 border: false,
138 items: [
139 {
140 boxLabel: gettext('Include Empty Senders'),
141 xtype: 'proxmoxcheckbox',
142 listeners: { change: 'onFilterChange' },
143 reference: 'ndr',
144 name: 'ndrs',
145 },
146 {
147 boxLabel: gettext('Include Greylist'),
148 xtype: 'proxmoxcheckbox',
149 listeners: { change: 'onFilterChange' },
150 margin: { left: 20 },
151 reference: 'greylist',
152 name: 'greylist',
153 },
154 ],
155 },
156 ],
157 },
158 ],
159 });
160
161 Ext.define('PMG.MaiLogWindow', {
162 extend: 'Ext.window.Window',
163 xtype: 'pmgMaiLogWindow',
164
165 title: gettext('Syslog'),
166
167 logid: undefined,
168 starttime: undefined,
169 endtime: undefined,
170
171 width: 1024,
172 height: 400,
173 scrollable: true,
174
175 layout: {
176 type: 'auto',
177 },
178 modal: true,
179 bodyPadding: 5,
180
181 load: function() {
182 let me = this;
183
184 Proxmox.Utils.API2Request({
185 method: 'GET',
186 params: { starttime: me.starttime, endtime: me.endtime },
187 url: '/nodes/' + Proxmox.NodeName + '/tracker/' + me.logid,
188 waitMsgTarget: me,
189 failure: function(response, opts) {
190 me.update(gettext('Error') + " " + response.htmlStatus);
191 },
192 success: function(response, opts) {
193 let data = response.result.data;
194
195 let logs = "<pre style='margin: 0;'>";
196 Ext.Array.each(data.logs, function(line) {
197 logs += Ext.htmlEncode(line) + "\n";
198 });
199 logs += "</pre>";
200 me.update(logs);
201 },
202 });
203 },
204
205 initComponent: function() {
206 let me = this;
207
208 if (!me.logid) {
209 throw "no logid specified";
210 }
211
212 if (!me.starttime) {
213 throw "no starttime specified";
214 }
215 if (!me.endtime) {
216 throw "no endtime specified";
217 }
218
219 me.callParent();
220
221 me.setHtml('Loading...');
222 me.load();
223 },
224 });
225
226 Ext.define('PMG.MailTracker', {
227 extend: 'Ext.grid.GridPanel',
228 xtype: 'pmgMailTracker',
229
230 title: gettext('Tracking Center'),
231
232 border: false,
233
234 emptyText: gettext("Please enter your search parameters and press 'Search'."),
235 disableSelection: true,
236
237 viewConfig: {
238 deferEmptyText: false,
239 enableTextSelection: true,
240 getRowClass: function(record, index) {
241 let status = record.data.rstatus || record.data.dstatus;
242 return PMG.Utils.mail_status_map[status];
243 },
244 },
245
246 plugins: [
247 {
248 ptype: 'rowexpander',
249 expandOnDblClick: false,
250 rowBodyTpl: '<p class="logs">{logs}</p>',
251 },
252 ],
253
254 store: {
255 autoDestroy: true,
256 model: 'pmg-mail-tracker',
257 sorters: 'time',
258 },
259
260 controller: {
261
262 xclass: 'Ext.app.ViewController',
263
264 onSearch: function() {
265 let view = this.getView();
266 view.setEmptyText(gettext('No data in database'));
267 let filter = this.lookupReference('filter');
268 let status = this.lookupReference('status');
269 let params = filter.getFilterParams();
270 if (params === undefined) {
271 return; // something went wrong with the filters bail out
272 }
273 status.update(''); // clear status before load
274 view.store.proxy.setExtraParams(params);
275 view.store.proxy.setUrl('/api2/json/nodes/' + Proxmox.NodeName + '/tracker');
276 view.store.load(function(records, operation, success) {
277 let response = operation.getResponse();
278 if (success) {
279 // fixme: howto avoid duplicate Ext.decode ?
280 let result = Ext.decode(response.responseText);
281 if (result.changes) {
282 status.update(result.changes);
283 }
284 }
285 });
286 },
287
288 showDetails: function(rowNode, record) {
289 let view = this.getView();
290
291 let params = view.store.proxy.getExtraParams();
292
293 Proxmox.Utils.API2Request({
294 method: 'GET',
295 params: { starttime: params.starttime, endtime: params.endtime },
296 url: '/nodes/' + Proxmox.NodeName + '/tracker/' + record.data.id,
297 waitMsgTarget: view,
298 failure: function(response, opts) {
299 record.set('logs', gettext('Error') + " " + response.htmlStatus);
300 },
301 success: function(response, opts) {
302 let data = response.result.data;
303 let logs = "";
304
305 Ext.Array.each(data.logs, function(line) {
306 logs += Ext.htmlEncode(line) + "<br>";
307 });
308
309 record.set('logs', logs);
310 },
311 });
312 },
313
314 // only expand row on dblclick, but do not collapse
315 expand: function(view, record, row, rowIdx, e) {
316 // inspired by RowExpander.js
317 let rowNode = view.getNode(rowIdx);
318 let normalRow = Ext.fly(rowNode);
319
320 let collapsedCls = view.rowBodyFeature.rowCollapsedCls;
321
322 if (normalRow.hasCls(collapsedCls)) {
323 view.rowBodyFeature.rowExpander.toggleRow(rowIdx, record);
324 }
325 },
326
327 control: {
328 'gridview': {
329 expandbody: 'showDetails',
330 itemdblclick: 'expand',
331 },
332 },
333 },
334
335 // extjs has no method to dynamically change the emptytext on
336 // grids, so we have to do it this way
337 setEmptyText: function(emptyText) {
338 let me = this;
339 let tableview = me.getView();
340 tableview.emptyText = `<div class="x-grid-empty">${emptyText || ""}</div>`;
341 },
342
343 dockedItems: [
344 {
345 xtype: 'pmgMailTrackerFilter',
346 reference: 'filter',
347 listeners: { filterChanged: 'onSearch' },
348 border: false,
349 dock: 'top',
350 },
351 {
352 xtype: 'toolbar',
353 items: [
354 { text: 'Search', handler: 'onSearch' },
355 { xtype: 'component', html: '', reference: 'status' },
356 ],
357 },
358 ],
359
360 columns: [
361 {
362 xtype: 'datecolumn',
363 header: gettext('Time'),
364 width: 120,
365 dataIndex: 'time',
366 format: 'M d H:i:s',
367 },
368 {
369 header: gettext('From'),
370 flex: 1,
371 dataIndex: 'from',
372 renderer: Ext.htmlEncode,
373 },
374 {
375 header: gettext('To'),
376 flex: 1,
377 dataIndex: 'to',
378 renderer: Ext.htmlEncode,
379 },
380 {
381 header: gettext('Status'),
382 width: 150,
383 renderer: function(v, metaData, rec) {
384 let returntext = 'unknown';
385 let icon = 'question-circle';
386 let rstatus = rec.data.rstatus;
387 if (v !== undefined && v !== '') {
388 let vtext = PMG.Utils.mail_status_map[v] || v;
389 icon = v;
390 if (v === 'Q' || v === 'B') {
391 returntext = vtext;
392 } else if (rstatus !== undefined && rstatus !== '') {
393 let rtext = PMG.Utils.mail_status_map[rstatus] || rstatus;
394 returntext = vtext + '/' + rtext;
395 icon = rstatus;
396 } else if (rec.data.qid !== undefined) {
397 returntext = 'queued/' + vtext;
398 } else {
399 returntext = vtext;
400 }
401 }
402
403 return PMG.Utils.format_status_icon(icon) + returntext;
404 },
405 dataIndex: 'dstatus',
406 },
407 {
408 header: gettext('Size'),
409 hidden: true,
410 dataIndex: 'size',
411 },
412 {
413 header: 'MSGID',
414 width: 300,
415 hidden: true,
416 dataIndex: 'msgid',
417 renderer: Ext.htmlEncode,
418 },
419 {
420 header: gettext('Client'),
421 width: 200,
422 hidden: true,
423 dataIndex: 'client',
424 renderer: Ext.htmlEncode,
425 },
426 ],
427
428 initComponent: function() {
429 let me = this;
430
431 me.callParent();
432
433 Proxmox.Utils.monStoreErrors(me.getView(), me.store);
434 },
435 });