]>
Commit | Line | Data |
---|---|---|
43f0b189 DC |
1 | class QuarantineView extends Component { |
2 | constructor(config = {}) { | |
3 | config.tpl = config.tpl || ` | |
4 | <div class="view view-quarantine"> | |
5 | <div data-name="quarantine-list" class="page"> | |
6 | <div class="navbar"> | |
7 | <div class="navbar-inner"> | |
8 | <div class="left"> | |
9 | <img class="logo-navbar" style="padding: 0 10px" src="pve2/images/logo-128.png" height=32 /> | |
10 | </div> | |
11 | <div class="title">Mail Gateway</div> | |
12 | </div> | |
13 | </div> | |
14 | <div class="settings-form elevation-5 fab-morph-target"> | |
15 | <div class="block-title block-title-medium">{{gettext "Range"}}</div> | |
16 | <div class="list no-hairlines-md"> | |
17 | <ul> | |
18 | <li class="item-content item-input"> | |
19 | <div class="item-inner"> | |
20 | <div class="item-title item-label">{{gettext "From"}}</div> | |
21 | <div class="item-input-wrap"> | |
22 | <input type="date" name="from" placeholder="from" required validate> | |
23 | </div> | |
24 | </div> | |
25 | </li> | |
26 | <li class="item-content item-input"> | |
27 | <div class="item-inner"> | |
28 | <div class="item-title item-label">{{gettext "To"}}</div> | |
29 | <div class="item-input-wrap"> | |
30 | <input type="date" name="to" placeholder="to" required validate> | |
31 | </div> | |
32 | </div> | |
33 | </li> | |
34 | </ul> | |
35 | <a class="button fab-close range-form">{{gettext "OK"}}</a> | |
36 | </div> | |
37 | </div> | |
38 | <div class="fab fab-morph fab-right-bottom" data-morph-to=".settings-form"> | |
39 | <a href="#"> | |
40 | <i class="icon f7-icons ios-only">calendar</i> | |
41 | <i class="icon material-icons md-only">date_range</i> | |
42 | </a> | |
43 | </div> | |
44 | <div class="toolbar subscription toolbar-hidden toolbar-bottom"> | |
45 | <div class="toolbar-inner"> | |
46 | <a class="button subscription"> | |
47 | <i class="icon f7-icons ios-only color-yellow">alert</i> | |
48 | <i class="icon material-icons md-only color-yellow">warning</i> | |
49 | <span class="subscription-text"> | |
50 | {{gettext "No valid subscription"}} | |
51 | </span> | |
52 | </a> | |
53 | </div> | |
54 | </div> | |
55 | <div class="page-content ptr-content"> | |
56 | <div class="ptr-preloader"> | |
57 | <div class="preloader"></div> | |
58 | <div class="ptr-arrow"></div> | |
59 | </div> | |
60 | <div class="list virtual-list"></div> | |
61 | </div> | |
62 | </div> | |
63 | </div>`; | |
64 | config.itemTemplate = config.itemTemplate || ` | |
65 | <li class="swipeout"> | |
66 | <div class="swipeout-content"> | |
67 | <a href="/mail/{{id}}/" class="item-link item-content"> | |
68 | <div class="item-inner"> | |
69 | <div class="item-title"> | |
70 | <div class="item-header">{{escape from}}</div> | |
71 | {{escape subject}} | |
72 | </div> | |
73 | <div class="item-after">Score: {{js "this.spamlevel || 0"}}</div> | |
74 | </div> | |
75 | </a> | |
76 | </div> | |
77 | <div class="swipeout-actions-left"> | |
78 | <a href="/mail/{{id}}/deliver" class="color-green swipeout-close"> | |
79 | <i class="icon f7-icons ios-only">paper_plane</i> | |
80 | <i class="icon material-icons md-only">send</i> | |
81 | {{gettext "Deliver"}} | |
82 | </a> | |
83 | <a href="/mail/{{id}}/whitelist" class="swipeout-close"> | |
84 | <i class="icon f7-icons ios-only">check</i> | |
85 | <i class="icon material-icons md-only">check</i> | |
86 | {{gettext "Whitelist"}} | |
87 | </a> | |
88 | </div> | |
89 | <div class="swipeout-actions-right"> | |
90 | <a href="/mail/{{id}}/blacklist" class="color-orange swipeout-close"> | |
91 | <i class="icon f7-icons ios-only">close</i> | |
92 | <i class="icon material-icons md-only">close</i> | |
93 | {{gettext "Blacklist"}} | |
94 | </a> | |
95 | <a href="/mail/{{id}}/delete" class="color-red swipeout-close"> | |
96 | <i class="icon f7-icons ios-only">trash</i> | |
97 | <i class="icon material-icons md-only">delete</i> | |
98 | {{gettext "Delete"}} | |
99 | </a> | |
100 | </div> | |
101 | </li>`; | |
102 | config.dividerTemplate = config.dividerTemplate || | |
103 | '<li class="item-divider">{{group}}</li>'; | |
104 | super(config); | |
105 | ||
106 | var me = this; | |
107 | ||
108 | me._compiledItemTemplate = Template7.compile(me.config.itemTemplate); | |
109 | me._compiledDividerTemplate = Template7.compile(me.config.dividerTemplate); | |
110 | me.skelTpl = ` | |
111 | <li class="skeleton-text skeleton-effect-fade"> | |
112 | <a href="#" class="item-content item-link"> | |
113 | <div class="item-inner"> | |
114 | <div class="item-title"> | |
115 | <div class="item-header">_______________________</div> | |
116 | ____ ______ __ _______ ____ _______ _______ ___ | |
117 | </div> | |
118 | <div class="item-after">Score: 15</div> | |
119 | </div> | |
120 | </a> | |
121 | </li>`; | |
122 | me.skelDividerTpl = '<li class="item-divider skeleton-text">____-__-__</li>'; | |
123 | me.setEndtime(new Date()); | |
124 | let startdate = new Date(); | |
125 | startdate.setDate(startdate.getDate() - 7); | |
126 | me.setStarttime(startdate); | |
127 | ||
128 | // add to dom | |
129 | $$(me.config.target || '#app').append(me.getEl()); | |
130 | ||
131 | $$(document).on('page:init', '.page[data-name=quarantine-list]', (e, page) => { | |
132 | me.vList = app.virtualList.create({ | |
133 | el: '.virtual-list', | |
134 | items: [], | |
135 | renderItem: function(item) { | |
136 | return me._renderItem(item); | |
137 | }, | |
7a0d8540 DC |
138 | height: function(item) { |
139 | return me._calculateHeight(item); | |
140 | }, | |
43f0b189 DC |
141 | emptyTemplate: '<div class="empty">No data in database</div>' |
142 | }); | |
143 | ||
144 | // setup pull to refresh | |
145 | $$('.ptr-content').on('ptr:refresh', (e) => { | |
146 | me.setItems([ | |
147 | { skel: true, divider: true }, | |
148 | { skel: true }, | |
149 | { skel: true }, | |
150 | { skel: true }, | |
151 | { skel: true, divider: true }, | |
152 | { skel: true }, | |
153 | { skel: true }, | |
154 | { skel: true }, | |
155 | { skel: true }, | |
156 | { skel: true }, | |
157 | { skel: true }, | |
158 | { skel: true }, | |
159 | ]); | |
160 | me.load().then(data => { | |
161 | me.setItems(data, { | |
162 | sorter: { | |
163 | property: 'time', | |
164 | numeric: true, | |
165 | direction: 'DESC' | |
166 | }, | |
167 | grouperFn: (val) => PMG.Utils.unixToIso(val['time']) | |
168 | }); | |
169 | }).catch(PMG.Utils.showError).then(() => { | |
170 | e.detail(); | |
171 | }); | |
172 | }); | |
173 | ||
174 | // process query parameters | |
175 | let { mail, action, date, username, ticket } = PMG.Utils.extractParams(); | |
176 | if (date) { | |
177 | me.setStarttime(date); | |
178 | } | |
179 | ||
180 | // setup range form | |
181 | $$('input[name=from]').val(PMG.Utils.unixToIso(me.starttime)); | |
182 | $$('input[name=to]').val(PMG.Utils.unixToIso(me.endtime)); | |
183 | ||
184 | $$('.fab').on('fab:close', () => { | |
185 | let fromChanged = me.setStarttime($$('input[name=from]').val()); | |
186 | let toChanged = me.setEndtime($$('input[name=to]').val()); | |
187 | if (fromChanged || toChanged) { | |
188 | app.ptr.refresh(); | |
189 | } | |
190 | }); | |
191 | ||
192 | // check login | |
193 | ||
194 | let loginInfo = { username, ticket }; | |
195 | let showPopup = (username && ticket) || !PMG.Utils.authOK(); | |
196 | me._loginScreen = new LoginScreen({ loginInfo }); | |
197 | ||
198 | me._loginScreen.open().then(data => { | |
199 | me._loginScreen.close(); | |
200 | PMG.Utils.setLoginInfo(data); | |
201 | return PMG.Utils.getSubscriptionInfo(); | |
202 | }).then(data => { | |
203 | return PMG.Utils.checkSubscription(data, showPopup); | |
204 | }).then(data => { | |
205 | app.ptr.refresh(); | |
206 | if (mail) { | |
207 | let url = "/mail/" + mail + "/" + (action || ""); | |
208 | me._view.router.navigate(url); | |
209 | } | |
210 | }).catch(PMG.Utils.showError); | |
211 | }); | |
212 | ||
213 | me._view = app.views.create('.view-quarantine', { | |
214 | main: me.config.mainView !== undefined ? me.config.mainView : true, | |
215 | url: '/', | |
216 | pushState: true, | |
217 | pushStateAnimateOnLoad: true | |
218 | }); | |
219 | } | |
220 | setStarttime(starttime) { | |
221 | var me = this; | |
222 | let date = starttime; | |
223 | if (!(starttime instanceof Date)) { | |
224 | // we assume an ISO string | |
225 | if (starttime == '') { | |
226 | return; | |
227 | } | |
228 | date = new Date(PMG.Utils.isoToUnix(starttime)*1000); | |
229 | } | |
230 | // starttime is at beginning of date | |
231 | date.setHours(0,0,0,0); | |
232 | let result = Math.round(date.getTime()/1000); | |
233 | if (result !== me.starttime) { | |
234 | me.starttime = result; | |
235 | return true; | |
236 | } | |
237 | return false | |
238 | } | |
239 | setEndtime(endtime) { | |
240 | var me = this; | |
241 | let date = endtime; | |
242 | if (!(endtime instanceof Date)) { | |
243 | if (endtime == '') { | |
244 | return; | |
245 | } | |
246 | // we assume an ISO string | |
247 | date = new Date(PMG.Utils.isoToUnix(endtime)*1000); | |
248 | } | |
249 | // endtime is at the end of the day | |
250 | date.setHours(23, 59, 59); | |
251 | let result = Math.round(date.getTime()/1000); | |
252 | if (result !== me.endtime) { | |
253 | me.endtime = result; | |
254 | return true; | |
255 | } | |
256 | return false; | |
257 | } | |
7a0d8540 DC |
258 | _calculateHeight(item) { |
259 | var me = this; | |
260 | ||
261 | let height = 48; // default | |
262 | ||
263 | if (typeof item === 'object') { | |
264 | let type = app.theme + '-' + (item.divider? "divider" : 'item'); | |
265 | switch (type) { | |
266 | case 'md-divider': | |
267 | height = 48; | |
268 | break; | |
269 | case 'md-item': | |
270 | height = 54; | |
271 | break; | |
272 | case 'ios-divider': | |
273 | height = 31; | |
274 | break; | |
275 | case 'ios-item': | |
276 | height = 53; | |
277 | break; | |
278 | default: ; | |
279 | } | |
280 | } | |
281 | ||
282 | return height; | |
283 | } | |
43f0b189 DC |
284 | _renderItem(item) { |
285 | var me = this; | |
286 | ||
287 | if(typeof item === 'object') { | |
288 | if (item.skel) { | |
289 | return item.divider? me.skelDividerTpl : me.skelTpl; | |
290 | } else if (item.divider) { | |
291 | return me._compiledDividerTemplate(item); | |
292 | } else { | |
293 | return me._compiledItemTemplate(item); | |
294 | } | |
295 | } | |
296 | ||
297 | return item.toString(); | |
298 | } | |
299 | setItems(items, options) { | |
300 | var me = this; | |
301 | if (options && options.sorter) { | |
302 | if (options.sorter.sorterFn) { | |
303 | items.sort(options.sorter.sorterFn); | |
304 | } else { | |
305 | let prop = options.sorter.property; | |
306 | let numeric = options.sorter.numeric; | |
307 | let dir = options.sorter.direction === "ASC" ? 1 : -1; | |
308 | items.sort((a,b) => { | |
309 | let result; | |
310 | ||
311 | if (numeric) { | |
312 | result = a[prop] - b[prop]; | |
313 | } else { | |
314 | result = a[prop] === b[prop] ? 0 : (a[prop] < b[prop] ? 1 : -1); | |
315 | } | |
316 | ||
317 | return result * dir; | |
318 | }); | |
319 | } | |
320 | } | |
321 | me.vList.replaceAllItems(items); | |
322 | if (options && options.grouperFn) { | |
323 | let lastgroup; | |
324 | let offset = 0; | |
325 | for (let i = 0; i+offset < items.length; i++) { | |
326 | let item = items[i+offset]; | |
327 | let curgroup = options.grouperFn(item); | |
328 | if (curgroup != lastgroup) { | |
329 | me.vList.insertItemBefore(i+(offset++), { | |
330 | divider: true, | |
331 | group: curgroup | |
332 | }); | |
333 | lastgroup = curgroup; | |
334 | } | |
335 | } | |
336 | } | |
337 | } | |
338 | load() { | |
339 | var me = this; | |
340 | return new Promise(function(resolve, reject) { | |
341 | app.request({ | |
342 | url: '/api2/json/quarantine/spam', | |
343 | data: { | |
344 | starttime: me.starttime, | |
345 | endtime: me.endtime | |
346 | }, | |
347 | dataType: 'json', | |
348 | success: (response, status, xhr) => { | |
349 | resolve(response.data); | |
350 | }, | |
351 | error: xhr => { | |
352 | reject(xhr); | |
353 | } | |
354 | }); | |
355 | }); | |
356 | } | |
357 | } | |
358 |