]>
Commit | Line | Data |
---|---|---|
442d6da8 | 1 | // for Toolkit.js |
d7386690 | 2 | function gettext(val) { return val; }; |
67fd0979 FE |
3 | |
4 | Ext.onReady(function() { | |
67fd0979 FE |
5 | const COLORS = { |
6 | 'keep-last': 'orange', | |
7 | 'keep-hourly': 'purple', | |
8 | 'keep-daily': 'yellow', | |
9 | 'keep-weekly': 'green', | |
10 | 'keep-monthly': 'blue', | |
11 | 'keep-yearly': 'red', | |
12 | 'all zero': 'white', | |
13 | }; | |
14 | const TEXT_COLORS = { | |
15 | 'keep-last': 'black', | |
16 | 'keep-hourly': 'white', | |
17 | 'keep-daily': 'black', | |
18 | 'keep-weekly': 'white', | |
19 | 'keep-monthly': 'white', | |
20 | 'keep-yearly': 'white', | |
21 | 'all zero': 'black', | |
22 | }; | |
23 | ||
24 | Ext.define('PBS.prunesimulator.Documentation', { | |
25 | extend: 'Ext.Panel', | |
26 | alias: 'widget.prunesimulatorDocumentation', | |
27 | ||
6823fdc7 | 28 | html: '<iframe style="width:100%;height:100%;border:0px;" src="./documentation.html"/>', |
67fd0979 FE |
29 | }); |
30 | ||
31 | Ext.define('PBS.prunesimulator.CalendarEvent', { | |
32 | extend: 'Ext.form.field.ComboBox', | |
33 | alias: 'widget.prunesimulatorCalendarEvent', | |
34 | ||
35 | editable: true, | |
36 | ||
67fd0979 FE |
37 | valueField: 'value', |
38 | queryMode: 'local', | |
39 | ||
40 | store: { | |
41 | field: ['value', 'text'], | |
42 | data: [ | |
43 | { value: '0/2:00', text: "Every two hours" }, | |
44 | { value: '0/6:00', text: "Every six hours" }, | |
45 | { value: '2,22:30', text: "At 02:30 and 22:30" }, | |
b83b12cf | 46 | { value: '00:00', text: "At 00:00" }, |
67fd0979 FE |
47 | { value: '08..17:00/30', text: "From 08:00 to 17:30 every 30 minutes" }, |
48 | { value: 'HOUR:MINUTE', text: "Custom schedule" }, | |
49 | ], | |
50 | }, | |
51 | ||
52 | tpl: [ | |
53 | '<ul class="x-list-plain"><tpl for=".">', | |
54 | '<li role="option" class="x-boundlist-item">{text}</li>', | |
55 | '</tpl></ul>', | |
56 | ], | |
57 | ||
58 | displayTpl: [ | |
59 | '<tpl for=".">', | |
60 | '{value}', | |
61 | '</tpl>', | |
62 | ], | |
63 | }); | |
64 | ||
65 | Ext.define('PBS.prunesimulator.DayOfWeekSelector', { | |
66 | extend: 'Ext.form.field.ComboBox', | |
67 | alias: 'widget.prunesimulatorDayOfWeekSelector', | |
68 | ||
69 | editable: false, | |
70 | ||
71 | displayField: 'text', | |
72 | valueField: 'value', | |
73 | queryMode: 'local', | |
74 | ||
75 | store: { | |
76 | field: ['value', 'text'], | |
77 | data: [ | |
78 | { value: 'mon', text: Ext.util.Format.htmlDecode(Ext.Date.dayNames[1]) }, | |
79 | { value: 'tue', text: Ext.util.Format.htmlDecode(Ext.Date.dayNames[2]) }, | |
80 | { value: 'wed', text: Ext.util.Format.htmlDecode(Ext.Date.dayNames[3]) }, | |
81 | { value: 'thu', text: Ext.util.Format.htmlDecode(Ext.Date.dayNames[4]) }, | |
82 | { value: 'fri', text: Ext.util.Format.htmlDecode(Ext.Date.dayNames[5]) }, | |
83 | { value: 'sat', text: Ext.util.Format.htmlDecode(Ext.Date.dayNames[6]) }, | |
84 | { value: 'sun', text: Ext.util.Format.htmlDecode(Ext.Date.dayNames[0]) }, | |
85 | ], | |
86 | }, | |
87 | }); | |
88 | ||
89 | Ext.define('pbs-prune-list', { | |
90 | extend: 'Ext.data.Model', | |
91 | fields: [ | |
92 | { | |
93 | name: 'backuptime', | |
94 | type: 'date', | |
95 | dateFormat: 'timestamp', | |
96 | }, | |
97 | { | |
98 | name: 'mark', | |
99 | type: 'string', | |
100 | }, | |
101 | { | |
102 | name: 'keepName', | |
103 | type: 'string', | |
104 | }, | |
105 | ], | |
106 | }); | |
107 | ||
108 | Ext.define('PBS.prunesimulator.PruneList', { | |
109 | extend: 'Ext.panel.Panel', | |
110 | alias: 'widget.prunesimulatorPruneList', | |
111 | ||
bb044304 TL |
112 | viewModel: {}, |
113 | ||
114 | items: [{ | |
115 | xtype: 'grid', | |
116 | bind: { | |
117 | store: '{store}', | |
118 | }, | |
119 | border: false, | |
120 | columns: [ | |
121 | { | |
122 | header: 'Backup Time', | |
123 | dataIndex: 'backuptime', | |
124 | renderer: function(value, metaData, { data }) { | |
125 | let text = Ext.Date.format(value, 'Y-m-d H:i:s'); | |
126 | if (data.mark !== 'keep') { | |
127 | return `<div style="text-decoration: line-through;">${text}</div>`; | |
128 | } | |
129 | if (me.useColors) { | |
130 | let bgColor = COLORS[data.keepName]; | |
131 | let textColor = TEXT_COLORS[data.keepName]; | |
132 | return `<div style="background-color: ${bgColor};color: ${textColor};">${text}</div>`; | |
133 | } else { | |
134 | return text; | |
135 | } | |
136 | }, | |
137 | flex: 1, | |
138 | sortable: false, | |
139 | }, | |
140 | { | |
141 | header: 'Keep (reason)', | |
142 | dataIndex: 'mark', | |
143 | renderer: function(value, metaData, { data }) { | |
144 | if (data.mark !== 'keep') { | |
145 | return value; | |
146 | } | |
147 | if (data.keepCount) { | |
148 | return `keep (${data.keepName}: ${data.keepCount})`; | |
149 | } else { | |
150 | return `keep (${data.keepName})`; | |
151 | } | |
152 | }, | |
153 | width: 200, | |
154 | sortable: false, | |
155 | }, | |
156 | ], | |
157 | }], | |
158 | ||
67fd0979 | 159 | initComponent: function() { |
60d2a615 | 160 | let me = this; |
67fd0979 FE |
161 | |
162 | if (!me.store) { | |
163 | throw "no store specified"; | |
164 | } | |
67fd0979 | 165 | me.callParent(); |
bb044304 | 166 | me.getViewModel().set('store', me.store); |
67fd0979 FE |
167 | }, |
168 | }); | |
169 | ||
170 | Ext.define('PBS.prunesimulator.WeekTable', { | |
171 | extend: 'Ext.panel.Panel', | |
172 | alias: 'widget.prunesimulatorWeekTable', | |
173 | ||
174 | reload: function() { | |
175 | let me = this; | |
176 | let backups = me.store.data.items; | |
177 | ||
435a6c5e | 178 | let html = '<table class="cal">'; |
67fd0979 | 179 | |
f44e2386 | 180 | let now = new Date(me.up().getViewModel().get('now')); |
67fd0979 FE |
181 | let skip = 7 - parseInt(Ext.Date.format(now, 'N'), 10); |
182 | let tableStartDate = Ext.Date.add(now, Ext.Date.DAY, skip); | |
183 | ||
184 | let bIndex = 0; | |
185 | ||
186 | for (let i = 0; bIndex < backups.length; i++) { | |
187 | html += '<tr>'; | |
188 | ||
189 | for (let j = 0; j < 7; j++) { | |
67fd0979 FE |
190 | let date = Ext.Date.subtract(tableStartDate, Ext.Date.DAY, j + 7 * i); |
191 | let currentDay = Ext.Date.format(date, 'd/m/Y'); | |
192 | ||
435a6c5e TL |
193 | let dayOfWeekCls = Ext.Date.format(date, 'D').toLowerCase(); |
194 | let firstOfMonthCls = Ext.Date.format(date, 'd') === '01' | |
195 | ? 'first-of-month' | |
196 | : ''; | |
197 | html += `<td class="cal-day ${dayOfWeekCls} ${firstOfMonthCls}">`; | |
198 | ||
199 | const isBackupOnDay = function(backup, day) { | |
67fd0979 FE |
200 | return backup && Ext.Date.format(backup.data.backuptime, 'd/m/Y') === day; |
201 | }; | |
202 | ||
203 | let backup = backups[bIndex]; | |
204 | ||
435a6c5e TL |
205 | html += '<table><tr>'; |
206 | html += `<th class="cal-day-date">${Ext.Date.format(date, 'D, d M Y')}</th>`; | |
67fd0979 FE |
207 | |
208 | while (isBackupOnDay(backup, currentDay)) { | |
209 | html += '<tr><td>'; | |
210 | ||
211 | let text = Ext.Date.format(backup.data.backuptime, 'H:i'); | |
212 | if (backup.data.mark === 'remove') { | |
435a6c5e | 213 | html += `<span class="strikethrough">${text}</span>`; |
67fd0979 | 214 | } else { |
924d6d40 | 215 | if (backup.data.keepCount) { |
0a0ba078 | 216 | text += ` (${backup.data.keepName} ${backup.data.keepCount})`; |
924d6d40 FE |
217 | } else { |
218 | text += ` (${backup.data.keepName})`; | |
219 | } | |
67fd0979 FE |
220 | if (me.useColors) { |
221 | let bgColor = COLORS[backup.data.keepName]; | |
222 | let textColor = TEXT_COLORS[backup.data.keepName]; | |
bb044304 | 223 | html += `<span style="background-color: ${bgColor}; color: ${textColor};">${text}</span>`; |
67fd0979 | 224 | } else { |
435a6c5e | 225 | html += `<span class="black">${text}</span>`; |
67fd0979 FE |
226 | } |
227 | } | |
228 | html += '</td></tr>'; | |
229 | backup = backups[++bIndex]; | |
230 | } | |
231 | html += '</table>'; | |
232 | html += '</div>'; | |
233 | html += '</td>'; | |
234 | } | |
235 | ||
236 | html += '</tr>'; | |
237 | } | |
238 | ||
239 | me.setHtml(html); | |
240 | }, | |
241 | ||
242 | initComponent: function() { | |
243 | let me = this; | |
244 | ||
245 | if (!me.store) { | |
246 | throw "no store specified"; | |
247 | } | |
248 | ||
249 | let reload = function() { | |
250 | me.reload(); | |
251 | }; | |
252 | ||
253 | me.store.on("datachanged", reload); | |
254 | ||
255 | me.callParent(); | |
256 | ||
257 | me.reload(); | |
258 | }, | |
259 | }); | |
260 | ||
21b55284 FE |
261 | Ext.define('PBS.PruneSimulatorKeepInput', { |
262 | extend: 'Ext.form.field.Number', | |
263 | alias: 'widget.prunesimulatorKeepInput', | |
264 | ||
265 | allowBlank: true, | |
266 | fieldGroup: 'keep', | |
267 | minValue: 1, | |
268 | ||
269 | listeners: { | |
270 | afterrender: function(field) { | |
271 | this.triggers.clear.setVisible(field.value !== null); | |
272 | }, | |
273 | change: function(field, newValue, oldValue) { | |
274 | this.triggers.clear.setVisible(newValue !== null); | |
275 | }, | |
276 | }, | |
277 | triggers: { | |
278 | clear: { | |
279 | cls: 'clear-trigger', | |
280 | weight: -1, | |
281 | handler: function() { | |
282 | this.triggers.clear.setVisible(false); | |
283 | this.setValue(null); | |
284 | }, | |
285 | }, | |
286 | }, | |
287 | }); | |
288 | ||
67fd0979 FE |
289 | Ext.define('PBS.PruneSimulatorPanel', { |
290 | extend: 'Ext.panel.Panel', | |
291 | alias: 'widget.prunesimulatorPanel', | |
292 | ||
293 | viewModel: { | |
f44e2386 MH |
294 | data: { |
295 | now: new Date(), | |
296 | }, | |
67fd0979 FE |
297 | }, |
298 | ||
299 | getValues: function() { | |
300 | let me = this; | |
301 | ||
302 | let values = {}; | |
303 | ||
304 | Ext.Array.each(me.query('[isFormField]'), function(field) { | |
305 | let data = field.getSubmitData(); | |
306 | Ext.Object.each(data, function(name, val) { | |
307 | values[name] = val; | |
308 | }); | |
309 | }); | |
310 | ||
311 | return values; | |
312 | }, | |
313 | ||
314 | controller: { | |
315 | xclass: 'Ext.app.ViewController', | |
316 | ||
317 | init: function(view) { | |
318 | this.reloadFull(); // initial load | |
bad26df1 | 319 | this.switchColor(true); |
67fd0979 FE |
320 | }, |
321 | ||
322 | control: { | |
323 | 'field[fieldGroup=keep]': { change: 'reloadPrune' }, | |
324 | }, | |
325 | ||
326 | reloadFull: function() { | |
327 | let me = this; | |
328 | let view = me.getView(); | |
329 | ||
330 | let params = view.getValues(); | |
331 | ||
332 | let [hourSpec, minuteSpec] = params['schedule-time'].split(':'); | |
333 | ||
334 | if (!hourSpec || !minuteSpec) { | |
335 | Ext.Msg.alert('Error', 'Invalid schedule'); | |
336 | return; | |
337 | } | |
338 | ||
339 | let matchTimeSpec = function(timeSpec, rangeMin, rangeMax) { | |
340 | let specValues = timeSpec.split(','); | |
341 | let matches = {}; | |
342 | ||
343 | let assertValid = function(value) { | |
344 | let num = Number(value); | |
345 | if (isNaN(num)) { | |
346 | throw value + " is not an integer"; | |
347 | } else if (value < rangeMin || value > rangeMax) { | |
348 | throw "number '" + value + "' is not in the range '" + rangeMin + ".." + rangeMax + "'"; | |
349 | } | |
350 | return num; | |
351 | }; | |
352 | ||
353 | specValues.forEach(function(value) { | |
354 | if (value.includes('..')) { | |
355 | let [start, end] = value.split('..'); | |
356 | start = assertValid(start); | |
357 | end = assertValid(end); | |
358 | if (start > end) { | |
359 | throw "interval start is bigger then interval end '" + start + " > " + end + "'"; | |
360 | } | |
361 | for (let i = start; i <= end; i++) { | |
362 | matches[i] = 1; | |
363 | } | |
364 | } else if (value.includes('/')) { | |
365 | let [start, step] = value.split('/'); | |
366 | start = assertValid(start); | |
367 | step = assertValid(step); | |
368 | for (let i = start; i <= rangeMax; i += step) { | |
369 | matches[i] = 1; | |
370 | } | |
371 | } else if (value === '*') { | |
372 | for (let i = rangeMin; i <= rangeMax; i++) { | |
373 | matches[i] = 1; | |
374 | } | |
375 | } else { | |
376 | value = assertValid(value); | |
377 | matches[value] = 1; | |
378 | } | |
379 | }); | |
380 | ||
381 | return Object.keys(matches); | |
382 | }; | |
383 | ||
384 | let hours, minutes; | |
385 | ||
386 | try { | |
387 | hours = matchTimeSpec(hourSpec, 0, 23); | |
388 | minutes = matchTimeSpec(minuteSpec, 0, 59); | |
389 | } catch (err) { | |
390 | Ext.Msg.alert('Error', err); | |
7f0f3666 | 391 | return; |
67fd0979 FE |
392 | } |
393 | ||
394 | let backups = me.populateFromSchedule( | |
395 | params['schedule-weekdays'], | |
396 | hours, | |
397 | minutes, | |
398 | params.numberOfWeeks, | |
399 | ); | |
400 | ||
401 | me.pruneSelect(backups, params); | |
402 | ||
403 | view.pruneStore.setData(backups); | |
404 | }, | |
405 | ||
406 | reloadPrune: function() { | |
407 | let me = this; | |
408 | let view = me.getView(); | |
409 | ||
410 | let params = view.getValues(); | |
411 | ||
412 | let backups = []; | |
413 | view.pruneStore.getData().items.forEach(function(item) { | |
414 | backups.push({ | |
415 | backuptime: item.data.backuptime, | |
416 | }); | |
417 | }); | |
418 | ||
419 | me.pruneSelect(backups, params); | |
420 | ||
421 | view.pruneStore.setData(backups); | |
422 | }, | |
423 | ||
424 | // backups are sorted descending by date | |
425 | populateFromSchedule: function(weekdays, hours, minutes, weekCount) { | |
f44e2386 MH |
426 | const me = this; |
427 | ||
67fd0979 FE |
428 | let weekdayFlags = [ |
429 | weekdays.includes('sun'), | |
430 | weekdays.includes('mon'), | |
431 | weekdays.includes('tue'), | |
432 | weekdays.includes('wed'), | |
433 | weekdays.includes('thu'), | |
434 | weekdays.includes('fri'), | |
435 | weekdays.includes('sat'), | |
436 | ]; | |
437 | ||
f44e2386 MH |
438 | const vmDate = me.getViewModel().get('now'); |
439 | let todaysDate = new Date(vmDate); | |
67fd0979 FE |
440 | |
441 | let timesOnSingleDay = []; | |
442 | ||
443 | hours.forEach(function(hour) { | |
444 | minutes.forEach(function(minute) { | |
445 | todaysDate.setHours(hour); | |
446 | todaysDate.setMinutes(minute); | |
447 | timesOnSingleDay.push(todaysDate.getTime()); | |
448 | }); | |
449 | }); | |
450 | ||
6a99b930 | 451 | // sort recent times first, backups array below is ordered now -> past |
7680525e | 452 | timesOnSingleDay.sort((a, b) => b - a); |
67fd0979 FE |
453 | |
454 | let backups = []; | |
455 | ||
456 | for (let i = 0; i < 7 * weekCount; i++) { | |
457 | let daysDate = Ext.Date.subtract(todaysDate, Ext.Date.DAY, i); | |
458 | let weekday = parseInt(Ext.Date.format(daysDate, 'w'), 10); | |
459 | if (weekdayFlags[weekday]) { | |
460 | timesOnSingleDay.forEach(function(time) { | |
f44e2386 MH |
461 | const backuptime = Ext.Date.subtract(new Date(time), Ext.Date.DAY, i); |
462 | if (backuptime <= vmDate) { | |
463 | backups.push({ backuptime: backuptime }); | |
464 | } | |
67fd0979 FE |
465 | }); |
466 | } | |
467 | } | |
468 | ||
469 | return backups; | |
470 | }, | |
471 | ||
472 | pruneMark: function(backups, keepCount, keepName, idFunc) { | |
473 | if (!keepCount) { | |
474 | return; | |
475 | } | |
476 | ||
477 | let alreadyIncluded = {}; | |
478 | let newlyIncluded = {}; | |
479 | let newlyIncludedCount = 0; | |
480 | ||
481 | let finished = false; | |
482 | ||
483 | backups.forEach(function(backup) { | |
484 | let mark = backup.mark; | |
39478aa5 FE |
485 | if (mark && mark === 'keep') { |
486 | let id = idFunc(backup); | |
487 | alreadyIncluded[id] = true; | |
67fd0979 | 488 | } |
39478aa5 | 489 | }); |
67fd0979 | 490 | |
39478aa5 FE |
491 | backups.forEach(function(backup) { |
492 | let mark = backup.mark; | |
493 | let id = idFunc(backup); | |
494 | ||
495 | if (finished || alreadyIncluded[id] || mark) { | |
67fd0979 FE |
496 | return; |
497 | } | |
498 | ||
499 | if (!newlyIncluded[id]) { | |
500 | if (newlyIncludedCount >= keepCount) { | |
501 | finished = true; | |
502 | return; | |
503 | } | |
504 | newlyIncluded[id] = true; | |
505 | newlyIncludedCount++; | |
506 | backup.mark = 'keep'; | |
507 | backup.keepName = keepName; | |
924d6d40 | 508 | backup.keepCount = newlyIncludedCount; |
67fd0979 FE |
509 | } else { |
510 | backup.mark = 'remove'; | |
511 | } | |
512 | }); | |
513 | }, | |
514 | ||
515 | // backups need to be sorted descending by date | |
516 | pruneSelect: function(backups, keepParams) { | |
517 | let me = this; | |
518 | ||
519 | if (Number(keepParams['keep-last']) + | |
520 | Number(keepParams['keep-hourly']) + | |
521 | Number(keepParams['keep-daily']) + | |
522 | Number(keepParams['keep-weekly']) + | |
523 | Number(keepParams['keep-monthly']) + | |
524 | Number(keepParams['keep-yearly']) === 0) { | |
525 | backups.forEach(function(backup) { | |
526 | backup.mark = 'keep'; | |
924d6d40 | 527 | backup.keepName = 'keep-all'; |
67fd0979 FE |
528 | }); |
529 | ||
530 | return; | |
531 | } | |
532 | ||
533 | me.pruneMark(backups, keepParams['keep-last'], 'keep-last', function(backup) { | |
534 | return backup.backuptime; | |
535 | }); | |
536 | me.pruneMark(backups, keepParams['keep-hourly'], 'keep-hourly', function(backup) { | |
537 | return Ext.Date.format(backup.backuptime, 'H/d/m/Y'); | |
538 | }); | |
539 | me.pruneMark(backups, keepParams['keep-daily'], 'keep-daily', function(backup) { | |
540 | return Ext.Date.format(backup.backuptime, 'd/m/Y'); | |
541 | }); | |
542 | me.pruneMark(backups, keepParams['keep-weekly'], 'keep-weekly', function(backup) { | |
543 | // ISO-8601 week and week-based year | |
544 | return Ext.Date.format(backup.backuptime, 'W/o'); | |
545 | }); | |
546 | me.pruneMark(backups, keepParams['keep-monthly'], 'keep-monthly', function(backup) { | |
547 | return Ext.Date.format(backup.backuptime, 'm/Y'); | |
548 | }); | |
549 | me.pruneMark(backups, keepParams['keep-yearly'], 'keep-yearly', function(backup) { | |
550 | return Ext.Date.format(backup.backuptime, 'Y'); | |
551 | }); | |
552 | ||
553 | backups.forEach(function(backup) { | |
554 | backup.mark = backup.mark || 'remove'; | |
555 | }); | |
556 | }, | |
bad26df1 TL |
557 | |
558 | toggleColors: function(checkbox, checked) { | |
559 | this.switchColor(checked); | |
560 | }, | |
561 | ||
562 | switchColor: function(useColors) { | |
563 | let me = this; | |
564 | let view = me.getView(); | |
565 | ||
566 | const getStyle = name => | |
567 | `background-color: ${COLORS[name]}; color: ${TEXT_COLORS[name]};`; | |
568 | ||
569 | for (const field of view.query('[isFormField]')) { | |
570 | if (field.fieldGroup !== 'keep') { | |
571 | continue; | |
572 | } | |
573 | if (useColors) { | |
574 | field.setFieldStyle(getStyle(field.name)); | |
575 | } else { | |
576 | field.setFieldStyle('background-color: white; color: #444;'); | |
577 | } | |
578 | } | |
579 | ||
580 | me.lookup('weekTable').useColors = useColors; | |
581 | me.lookup('pruneList').useColors = useColors; | |
582 | ||
583 | me.reloadPrune(); | |
584 | }, | |
67fd0979 FE |
585 | }, |
586 | ||
587 | keepItems: [ | |
588 | { | |
21b55284 | 589 | xtype: 'prunesimulatorKeepInput', |
67fd0979 | 590 | name: 'keep-last', |
67fd0979 | 591 | fieldLabel: 'keep-last', |
67fd0979 | 592 | value: 4, |
67fd0979 FE |
593 | }, |
594 | { | |
21b55284 | 595 | xtype: 'prunesimulatorKeepInput', |
67fd0979 | 596 | name: 'keep-hourly', |
67fd0979 | 597 | fieldLabel: 'keep-hourly', |
67fd0979 FE |
598 | }, |
599 | { | |
21b55284 | 600 | xtype: 'prunesimulatorKeepInput', |
67fd0979 | 601 | name: 'keep-daily', |
67fd0979 | 602 | fieldLabel: 'keep-daily', |
67fd0979 | 603 | value: 5, |
67fd0979 FE |
604 | }, |
605 | { | |
21b55284 | 606 | xtype: 'prunesimulatorKeepInput', |
67fd0979 | 607 | name: 'keep-weekly', |
67fd0979 | 608 | fieldLabel: 'keep-weekly', |
67fd0979 | 609 | value: 2, |
67fd0979 FE |
610 | }, |
611 | { | |
21b55284 | 612 | xtype: 'prunesimulatorKeepInput', |
67fd0979 | 613 | name: 'keep-monthly', |
67fd0979 | 614 | fieldLabel: 'keep-monthly', |
67fd0979 FE |
615 | }, |
616 | { | |
21b55284 | 617 | xtype: 'prunesimulatorKeepInput', |
67fd0979 | 618 | name: 'keep-yearly', |
67fd0979 | 619 | fieldLabel: 'keep-yearly', |
67fd0979 FE |
620 | }, |
621 | ], | |
622 | ||
623 | initComponent: function() { | |
624 | var me = this; | |
f44e2386 | 625 | const vm = me.getViewModel(); |
67fd0979 FE |
626 | |
627 | me.pruneStore = Ext.create('Ext.data.Store', { | |
628 | model: 'pbs-prune-list', | |
629 | sorters: { property: 'backuptime', direction: 'DESC' }, | |
630 | }); | |
631 | ||
67fd0979 FE |
632 | me.items = [ |
633 | { | |
634 | xtype: 'panel', | |
6823fdc7 DM |
635 | layout: { |
636 | type: 'hbox', | |
637 | align: 'stretch', | |
638 | }, | |
639 | border: false, | |
67fd0979 FE |
640 | items: [ |
641 | { | |
87cbd8c4 | 642 | title: 'View Options', |
67fd0979 FE |
643 | layout: 'anchor', |
644 | flex: 1, | |
6823fdc7 DM |
645 | border: false, |
646 | bodyPadding: 10, | |
67fd0979 FE |
647 | items: [ |
648 | { | |
67fd0979 FE |
649 | xtype: 'checkbox', |
650 | name: 'showCalendar', | |
651 | reference: 'showCalendar', | |
652 | fieldLabel: 'Show Calendar:', | |
1f4befe1 | 653 | checked: true, |
67fd0979 FE |
654 | }, |
655 | { | |
67fd0979 FE |
656 | xtype: 'checkbox', |
657 | name: 'showColors', | |
658 | reference: 'showColors', | |
659 | fieldLabel: 'Show Colors:', | |
bad26df1 TL |
660 | checked: true, |
661 | handler: 'toggleColors', | |
67fd0979 FE |
662 | }, |
663 | ], | |
664 | }, | |
6823fdc7 | 665 | { xtype: "panel", width: 1, border: 1 }, |
67fd0979 | 666 | { |
6ed79592 | 667 | xtype: 'form', |
f44e2386 MH |
668 | layout: 'hbox', |
669 | flex: 2, | |
6823fdc7 | 670 | border: false, |
87cbd8c4 TL |
671 | title: 'Backup Job Simulation', |
672 | dockedItems: [{ | |
673 | xtype: 'button', | |
674 | text: 'Update Simulation', | |
675 | handler: 'reloadFull', | |
676 | formBind: true, | |
677 | dock: 'bottom', | |
678 | margin: '1 15', | |
679 | }], | |
680 | bodyPadding: 3, | |
4c75ee34 TL |
681 | items: [ |
682 | { | |
87cbd8c4 TL |
683 | xtype: 'fieldset', |
684 | title: 'Backup Job', | |
f44e2386 | 685 | layout: 'anchor', |
87cbd8c4 TL |
686 | flex: 4, |
687 | height: 110, | |
f44e2386 | 688 | defaults: { |
87cbd8c4 TL |
689 | labelWidth: 90, |
690 | padding: '0 0 0 10', | |
691 | width: '95%', | |
692 | minWidth: 150, | |
f44e2386 MH |
693 | }, |
694 | items: [ | |
695 | { | |
696 | xtype: 'prunesimulatorDayOfWeekSelector', | |
697 | name: 'schedule-weekdays', | |
698 | fieldLabel: 'Day of week', | |
699 | value: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'], | |
700 | allowBlank: false, | |
701 | multiSelect: true, | |
f44e2386 MH |
702 | }, |
703 | { | |
704 | xtype: 'prunesimulatorCalendarEvent', | |
705 | name: 'schedule-time', | |
706 | allowBlank: false, | |
707 | value: '0/6:00', | |
87cbd8c4 | 708 | fieldLabel: 'Schedule', |
f44e2386 MH |
709 | }, |
710 | ], | |
4c75ee34 TL |
711 | }, |
712 | { | |
87cbd8c4 TL |
713 | xtype: 'fieldset', |
714 | title: 'Simulation Time Range', | |
f44e2386 | 715 | layout: 'anchor', |
87cbd8c4 TL |
716 | flex: 3, |
717 | height: 110, | |
f44e2386 | 718 | defaults: { |
87cbd8c4 TL |
719 | labelWidth: 70, |
720 | width: 220, | |
721 | padding: '0 0 0 10', | |
722 | width: '95%', | |
723 | minWidth: 150, | |
f44e2386 MH |
724 | }, |
725 | items: [ | |
726 | { | |
727 | xtype: 'datefield', | |
728 | name: 'currentDate', | |
87cbd8c4 | 729 | fieldLabel: 'End Date', |
f44e2386 | 730 | allowBlank: false, |
f44e2386 MH |
731 | format: 'Y-m-d', |
732 | value: vm.get('now'), | |
733 | listeners: { | |
734 | change: function(self, newDate) { | |
735 | if (!self.isValid()) { | |
736 | return; | |
737 | } | |
738 | const date = me.getViewModel().get('now'); | |
739 | date.setFullYear( | |
740 | newDate.getFullYear(), | |
741 | newDate.getMonth(), | |
742 | newDate.getDate(), | |
743 | ); | |
744 | }, | |
745 | }, | |
746 | }, | |
747 | { | |
748 | xtype: 'timefield', | |
749 | name: 'currentTime', | |
750 | reference: 'currentTime', | |
87cbd8c4 | 751 | fieldLabel: 'End Time', |
f44e2386 | 752 | allowBlank: false, |
f44e2386 | 753 | format: 'H:i', |
87cbd8c4 TL |
754 | // cant bind value because ExtJS sets the year to 2008 to |
755 | // protect against DST issues and date picker zeroes hour/minute | |
f44e2386 MH |
756 | value: vm.get('now'), |
757 | listeners: { | |
758 | change: function(self, time) { | |
759 | if (!self.isValid()) { | |
760 | return; | |
761 | } | |
762 | const date = me.getViewModel().get('now'); | |
763 | date.setHours(time.getHours()); | |
764 | date.setMinutes(time.getMinutes()); | |
765 | }, | |
766 | }, | |
767 | }, | |
87cbd8c4 TL |
768 | { |
769 | xtype: 'fieldcontainer', | |
770 | fieldLabel: 'Duration', | |
771 | layout: 'hbox', | |
772 | items: [{ | |
773 | xtype: 'numberfield', | |
774 | name: 'numberOfWeeks', | |
775 | hideLabel: true, | |
776 | allowBlank: false, | |
777 | minValue: 1, | |
778 | value: 15, | |
779 | maxValue: 260, // five years | |
780 | flex: 1, | |
781 | }, { | |
782 | xtype: 'displayfield', | |
783 | value: 'Weeks', | |
784 | submitValue: false, | |
785 | hideLabel: true, | |
786 | padding: '0 0 0 5', | |
787 | width: 40, | |
788 | }], | |
789 | }, | |
f44e2386 | 790 | ], |
4c75ee34 TL |
791 | }, |
792 | ], | |
67fd0979 FE |
793 | }, |
794 | ], | |
795 | }, | |
796 | { | |
797 | xtype: 'panel', | |
6823fdc7 DM |
798 | layout: { |
799 | type: 'hbox', | |
800 | align: 'stretch', | |
801 | }, | |
67fd0979 | 802 | flex: 1, |
6823fdc7 | 803 | border: false, |
67fd0979 FE |
804 | items: [ |
805 | { | |
806 | layout: 'anchor', | |
807 | title: 'Prune Options', | |
6823fdc7 DM |
808 | border: false, |
809 | bodyPadding: 10, | |
60d2a615 | 810 | scrollable: true, |
67fd0979 FE |
811 | items: me.keepItems, |
812 | flex: 1, | |
813 | }, | |
6823fdc7 | 814 | { xtype: "panel", width: 1, border: 1 }, |
67fd0979 FE |
815 | { |
816 | layout: 'fit', | |
817 | title: 'Backups', | |
6823fdc7 | 818 | border: false, |
67fd0979 FE |
819 | xtype: 'prunesimulatorPruneList', |
820 | store: me.pruneStore, | |
821 | reference: 'pruneList', | |
f44e2386 | 822 | flex: 2, |
67fd0979 FE |
823 | }, |
824 | ], | |
825 | }, | |
826 | { | |
827 | layout: 'anchor', | |
828 | title: 'Calendar', | |
829 | autoScroll: true, | |
830 | flex: 2, | |
831 | xtype: 'prunesimulatorWeekTable', | |
832 | reference: 'weekTable', | |
833 | store: me.pruneStore, | |
834 | bind: { | |
790627b4 | 835 | hidden: '{!showCalendar.checked}', |
67fd0979 FE |
836 | }, |
837 | }, | |
838 | ]; | |
839 | ||
840 | me.callParent(); | |
841 | }, | |
842 | }); | |
843 | ||
844 | Ext.create('Ext.container.Viewport', { | |
845 | layout: 'border', | |
846 | renderTo: Ext.getBody(), | |
847 | items: [ | |
848 | { | |
849 | xtype: 'prunesimulatorPanel', | |
1b03910d | 850 | title: 'Proxmox Backup Server - Prune Simulator', |
67fd0979 FE |
851 | region: 'west', |
852 | layout: { | |
853 | type: 'vbox', | |
854 | align: 'stretch', | |
855 | pack: 'start', | |
856 | }, | |
f5c6a2c9 TL |
857 | flex: 3, |
858 | maxWidth: 1090, | |
67fd0979 FE |
859 | }, |
860 | { | |
861 | xtype: 'prunesimulatorDocumentation', | |
862 | title: 'Usage', | |
6823fdc7 | 863 | border: false, |
f5c6a2c9 | 864 | flex: 2, |
67fd0979 FE |
865 | region: 'center', |
866 | }, | |
867 | ], | |
868 | }); | |
869 | }); | |
870 |