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