]> git.proxmox.com Git - proxmox-widget-toolkit.git/blame - src/node/APTRepositories.js
bump version to 4.2.3
[proxmox-widget-toolkit.git] / src / node / APTRepositories.js
CommitLineData
24313a9d
FE
1Ext.define('apt-repolist', {
2 extend: 'Ext.data.Model',
3 fields: [
4 'Path',
5 'Index',
d91987a5 6 'Origin',
24313a9d
FE
7 'FileType',
8 'Enabled',
9 'Comment',
10 'Types',
11 'URIs',
12 'Suites',
13 'Components',
14 'Options',
15 ],
16});
17
21860ea4
FE
18Ext.define('Proxmox.window.APTRepositoryAdd', {
19 extend: 'Proxmox.window.Edit',
20 alias: 'widget.pmxAPTRepositoryAdd',
21
22 isCreate: true,
23 isAdd: true,
24
25 subject: gettext('Repository'),
5e9eb245 26 width: 600,
21860ea4
FE
27
28 initComponent: function() {
29 let me = this;
30
31 if (!me.repoInfo || me.repoInfo.length === 0) {
32 throw "repository information not initialized";
33 }
34
35 let description = Ext.create('Ext.form.field.Display', {
36 fieldLabel: gettext('Description'),
37 name: 'description',
38 });
39
40 let status = Ext.create('Ext.form.field.Display', {
41 fieldLabel: gettext('Status'),
42 name: 'status',
43 renderer: function(value) {
44 let statusText = gettext('Not yet configured');
45 if (value !== '') {
46 statusText = Ext.String.format(
47 '{0}: {1}',
48 gettext('Configured'),
49 value ? gettext('enabled') : gettext('disabled'),
50 );
51 }
52
53 return statusText;
54 },
55 });
56
57 let repoSelector = Ext.create('Proxmox.form.KVComboBox', {
58 fieldLabel: gettext('Repository'),
59 xtype: 'proxmoxKVComboBox',
60 name: 'handle',
61 allowBlank: false,
62 comboItems: me.repoInfo.map(info => [info.handle, info.name]),
824f9977
TL
63 validator: function(renderedValue) {
64 let handle = this.value;
65 // we cannot use this.callParent in instantiations
66 let valid = Proxmox.form.KVComboBox.prototype.validator.call(this, renderedValue);
21860ea4 67
824f9977 68 if (!valid || !handle) {
21860ea4
FE
69 return false;
70 }
71
72 const info = me.repoInfo.find(elem => elem.handle === handle);
21860ea4
FE
73 if (!info) {
74 return false;
75 }
76
58b71860 77 if (info.status) {
824f9977
TL
78 return Ext.String.format(gettext('{0} is already configured'), renderedValue);
79 }
80 return valid;
21860ea4
FE
81 },
82 listeners: {
83 change: function(f, value) {
84 const info = me.repoInfo.find(elem => elem.handle === value);
85 description.setValue(info.description);
86 status.setValue(info.status);
87 },
88 },
89 });
90
91 repoSelector.setValue(me.repoInfo[0].handle);
92
21860ea4 93 Ext.apply(me, {
5e9eb245
TL
94 items: [
95 repoSelector,
96 description,
97 status,
98 ],
21860ea4
FE
99 repoSelector: repoSelector,
100 });
101
102 me.callParent();
103 },
104});
105
24313a9d
FE
106Ext.define('Proxmox.node.APTRepositoriesErrors', {
107 extend: 'Ext.grid.GridPanel',
108
109 xtype: 'proxmoxNodeAPTRepositoriesErrors',
110
24313a9d
FE
111 store: {},
112
f59d1076 113 scrollable: true,
d8b5cd80 114
24313a9d
FE
115 viewConfig: {
116 stripeRows: false,
5e0cecb7
DC
117 getRowClass: (record) => {
118 switch (record.data.status) {
119 case 'warning': return 'proxmox-warning-row';
120 case 'critical': return 'proxmox-invalid-row';
121 default: return '';
122 }
123 },
24313a9d
FE
124 },
125
5e0cecb7
DC
126 hideHeaders: true,
127
24313a9d
FE
128 columns: [
129 {
5e0cecb7
DC
130 dataIndex: 'status',
131 renderer: (value) => `<i class="fa fa-fw ${Proxmox.Utils.get_health_icon(value, true)}"></i>`,
132 width: 50,
24313a9d
FE
133 },
134 {
5e0cecb7 135 dataIndex: 'message',
24313a9d
FE
136 flex: 1,
137 },
138 ],
139});
140
141Ext.define('Proxmox.node.APTRepositoriesGrid', {
142 extend: 'Ext.grid.GridPanel',
24313a9d 143 xtype: 'proxmoxNodeAPTRepositoriesGrid',
5e76f730 144 mixins: ['Proxmox.Mixin.CBind'],
24313a9d
FE
145
146 title: gettext('APT Repositories'),
147
994fe897
TL
148 cls: 'proxmox-apt-repos', // to allow applying styling to general components with local effect
149
d8b5cd80
DC
150 border: false,
151
24313a9d
FE
152 tbar: [
153 {
154 text: gettext('Reload'),
155 iconCls: 'fa fa-refresh',
156 handler: function() {
157 let me = this;
158 me.up('proxmoxNodeAPTRepositories').reload();
159 },
160 },
d76eedb4
FE
161 {
162 text: gettext('Add'),
faacb77f 163 name: 'addRepo',
21860ea4
FE
164 disabled: true,
165 repoInfo: undefined,
5e76f730
FE
166 cbind: {
167 onlineHelp: '{onlineHelp}',
168 },
21860ea4
FE
169 handler: function(button, event, record) {
170 Proxmox.Utils.checked_command(() => {
171 let me = this;
172 let panel = me.up('proxmoxNodeAPTRepositories');
173
174 let extraParams = {};
175 if (panel.digest !== undefined) {
176 extraParams.digest = panel.digest;
177 }
178
179 Ext.create('Proxmox.window.APTRepositoryAdd', {
180 repoInfo: me.repoInfo,
d7aeb02f 181 url: `/api2/extjs/nodes/${panel.nodename}/apt/repositories`,
21860ea4
FE
182 method: 'PUT',
183 extraRequestParams: extraParams,
5e76f730 184 onlineHelp: me.onlineHelp,
21860ea4
FE
185 listeners: {
186 destroy: function() {
187 panel.reload();
188 },
189 },
190 }).show();
191 });
d76eedb4
FE
192 },
193 },
af48de6b 194 '-',
d76eedb4 195 {
8832b590 196 xtype: 'proxmoxAltTextButton',
bb64cd03
TL
197 defaultText: gettext('Enable'),
198 altText: gettext('Disable'),
faacb77f 199 name: 'repoEnable',
d76eedb4 200 disabled: true,
003c4982
DC
201 bind: {
202 text: '{enableButtonText}',
203 },
d76eedb4
FE
204 handler: function(button, event, record) {
205 let me = this;
206 let panel = me.up('proxmoxNodeAPTRepositories');
207
208 let params = {
209 path: record.data.Path,
210 index: record.data.Index,
211 enabled: record.data.Enabled ? 0 : 1, // invert
212 };
213
214 if (panel.digest !== undefined) {
215 params.digest = panel.digest;
216 }
217
218 Proxmox.Utils.API2Request({
219 url: `/nodes/${panel.nodename}/apt/repositories`,
220 method: 'POST',
221 params: params,
222 failure: function(response, opts) {
223 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
224 panel.reload();
225 },
226 success: function(response, opts) {
227 panel.reload();
228 },
229 });
230 },
231 },
24313a9d
FE
232 ],
233
234 sortableColumns: false,
205d2751
TL
235 viewConfig: {
236 stripeRows: false,
237 getRowClass: (record, index) => record.get('Enabled') ? '' : 'proxmox-disabled-row',
238 },
24313a9d
FE
239
240 columns: [
241 {
24313a9d
FE
242 header: gettext('Enabled'),
243 dataIndex: 'Enabled',
64f65c02
LW
244 align: 'center',
245 renderer: Proxmox.Utils.renderEnabledIcon,
24313a9d
FE
246 width: 90,
247 },
248 {
249 header: gettext('Types'),
250 dataIndex: 'Types',
251 renderer: function(types, cell, record) {
252 return types.join(' ');
253 },
254 width: 100,
255 },
256 {
257 header: gettext('URIs'),
258 dataIndex: 'URIs',
259 renderer: function(uris, cell, record) {
260 return uris.join(' ');
261 },
262 width: 350,
263 },
264 {
265 header: gettext('Suites'),
266 dataIndex: 'Suites',
e71fc6e4
DC
267 renderer: function(suites, metaData, record) {
268 let err = '';
269 if (record.data.warnings && record.data.warnings.length > 0) {
270 let txt = [gettext('Warning')];
271 record.data.warnings.forEach((warning) => {
272 if (warning.property === 'Suites') {
273 txt.push(warning.message);
274 }
275 });
276 metaData.tdAttr = `data-qtip="${Ext.htmlEncode(txt.join('<br>'))}"`;
5a1fddb6
TL
277 if (record.data.Enabled) {
278 metaData.tdCls = 'proxmox-invalid-row';
279 err = '<i class="fa fa-fw critical fa-exclamation-circle"></i> ';
280 } else {
281 metaData.tdCls = 'proxmox-warning-row';
282 err = '<i class="fa fa-fw warning fa-exclamation-circle"></i> ';
283 }
e71fc6e4
DC
284 }
285 return suites.join(' ') + err;
24313a9d
FE
286 },
287 width: 130,
288 },
289 {
290 header: gettext('Components'),
291 dataIndex: 'Components',
06689819 292 renderer: function(components, metaData, record) {
65f4704b
TL
293 if (components === undefined) {
294 return '';
295 }
06689819
TL
296 let err = '';
297 if (components.length === 1) {
298 // FIXME: this should be a flag set to the actual repsotiories, i.e., a tristate
4fc57df4 299 // like production-ready = <yes|no|other> (Option<bool>)
06689819
TL
300 if (components[0].match(/\w+(-no-subscription|test)\s*$/i)) {
301 metaData.tdCls = 'proxmox-warning-row';
302 err = '<i class="fa fa-fw warning fa-exclamation-circle"></i> ';
303
304 let qtip = components[0].match(/no-subscription/)
305 ? gettext('The no-subscription repository is NOT production-ready')
306 : gettext('The test repository may contain unstable updates')
307 ;
308 metaData.tdAttr = `data-qtip="${Ext.htmlEncode(qtip)}"`;
309 }
310 }
311 return components.join(' ') + err;
24313a9d
FE
312 },
313 width: 170,
314 },
315 {
316 header: gettext('Options'),
317 dataIndex: 'Options',
318 renderer: function(options, cell, record) {
319 if (!options) {
320 return '';
321 }
322
323 let filetype = record.data.FileType;
324 let text = '';
325
326 options.forEach(function(option) {
327 let key = option.Key;
328 if (filetype === 'list') {
329 let values = option.Values.join(',');
330 text += `${key}=${values} `;
331 } else if (filetype === 'sources') {
332 let values = option.Values.join(' ');
333 text += `${key}: ${values}<br>`;
334 } else {
017a6376 335 throw "unknown file type";
24313a9d
FE
336 }
337 });
338 return text;
339 },
340 flex: 1,
341 },
03c4c65b 342 {
d91987a5
FE
343 header: gettext('Origin'),
344 dataIndex: 'Origin',
036f48c1 345 width: 120,
3d6b76ee 346 renderer: function(value, meta, rec) {
f0966f29
TL
347 if (typeof value !== 'string' || value.length === 0) {
348 value = gettext('Other');
349 }
036f48c1 350 let cls = 'fa fa-fw fa-question-circle-o';
3d6b76ee
FE
351 let originType = this.up('proxmoxNodeAPTRepositories').classifyOrigin(value);
352 if (originType === 'Proxmox') {
036f48c1 353 cls = 'pmx-itype-icon pmx-itype-icon-proxmox-x';
3d6b76ee 354 } else if (originType === 'Debian') {
036f48c1
TL
355 cls = 'pmx-itype-icon pmx-itype-icon-debian-swirl';
356 }
357 return `<i class='${cls}'></i> ${value}`;
358 },
03c4c65b 359 },
24313a9d
FE
360 {
361 header: gettext('Comment'),
362 dataIndex: 'Comment',
363 flex: 2,
e61ffdd6 364 renderer: Ext.String.htmlEncode,
24313a9d
FE
365 },
366 ],
367
7705801f
TL
368 features: [
369 {
370 ftype: 'grouping',
371 groupHeaderTpl: '{[ "File: " + values.name ]} ({rows.length} repositor{[values.rows.length > 1 ? "ies" : "y"]})',
372 enableGroupingMenu: false,
373 },
374 ],
375
eb1fff82
TL
376 store: {
377 model: 'apt-repolist',
378 groupField: 'Path',
379 sorters: [
380 {
381 property: 'Index',
382 direction: 'ASC',
383 },
384 ],
385 },
386
24313a9d
FE
387 initComponent: function() {
388 let me = this;
389
390 if (!me.nodename) {
391 throw "no node name specified";
392 }
393
24313a9d
FE
394 me.callParent();
395 },
396});
397
398Ext.define('Proxmox.node.APTRepositories', {
399 extend: 'Ext.panel.Panel',
24313a9d
FE
400 xtype: 'proxmoxNodeAPTRepositories',
401 mixins: ['Proxmox.Mixin.CBind'],
402
403 digest: undefined,
404
5e76f730
FE
405 onlineHelp: undefined,
406
3fc020f4
TL
407 product: 'Proxmox VE', // default
408
3d6b76ee 409 classifyOrigin: function(origin) {
a14bafec 410 origin ||= '';
3d6b76ee
FE
411 if (origin.match(/^\s*Proxmox\s*$/i)) {
412 return 'Proxmox';
413 } else if (origin.match(/^\s*Debian\s*(:?Backports)?$/i)) {
414 return 'Debian';
415 }
416 return 'Other';
417 },
418
003c4982
DC
419 controller: {
420 xclass: 'Ext.app.ViewController',
421
422 selectionChange: function(grid, selection) {
423 let me = this;
424 if (!selection || selection.length < 1) {
425 return;
426 }
427 let rec = selection[0];
428 let vm = me.getViewModel();
429 vm.set('selectionenabled', rec.get('Enabled'));
f59d1076
DC
430 vm.notify();
431 },
432
433 updateState: function() {
434 let me = this;
435 let vm = me.getViewModel();
436
5e0cecb7
DC
437 let store = vm.get('errorstore');
438 store.removeAll();
439
df7def01
TL
440 let status = 'good'; // start with best, the helper below will downgrade if needed
441 let text = gettext('All OK, you have production-ready repositories configured!');
442
df7def01 443 let addGood = message => store.add({ status: 'good', message });
46231d0d 444 let addWarn = (message, important) => {
fe787c8c 445 if (status !== 'critical') {
df7def01 446 status = 'warning';
46231d0d 447 text = important ? message : gettext('Warning');
df7def01
TL
448 }
449 store.add({ status: 'warning', message });
450 };
6c9af178
TL
451 let addCritical = (message, important) => {
452 status = 'critical';
453 text = important ? message : gettext('Error');
454 store.add({ status: 'critical', message });
455 };
456
457 let errors = vm.get('errors');
458 errors.forEach(error => addCritical(`${error.path} - ${error.error}`));
f59d1076
DC
459
460 let activeSubscription = vm.get('subscriptionActive');
461 let enterprise = vm.get('enterpriseRepo');
462 let nosubscription = vm.get('noSubscriptionRepo');
463 let test = vm.get('testRepo');
7d5201a3
FE
464 let cephRepos = {
465 enterprise: vm.get('cephEnterpriseRepo'),
466 nosubscription: vm.get('cephNoSubscriptionRepo'),
467 test: vm.get('cephTestRepo'),
468 };
f59d1076 469 let wrongSuites = vm.get('suitesWarning');
e6ed4498 470 let mixedSuites = vm.get('mixedSuites');
f59d1076 471
5e0cecb7 472 if (!enterprise && !nosubscription && !test) {
6c9af178
TL
473 addCritical(
474 Ext.String.format(gettext('No {0} repository is enabled, you do not get any updates!'), vm.get('product')),
475 );
6bac17e3
TL
476 } else if (errors.length > 0) {
477 // nothing extra, just avoid that we show "get updates"
5e0cecb7
DC
478 } else if (enterprise && !nosubscription && !test && activeSubscription) {
479 addGood(Ext.String.format(gettext('You get supported updates for {0}'), vm.get('product')));
480 } else if (nosubscription || test) {
481 addGood(Ext.String.format(gettext('You get updates for {0}'), vm.get('product')));
482 }
483
484 if (wrongSuites) {
96ecd62a 485 addWarn(gettext('Some suites are misconfigured'));
5e0cecb7
DC
486 }
487
e6ed4498
FE
488 if (mixedSuites) {
489 addWarn(gettext('Detected mixed suites before upgrade'));
490 }
491
7d5201a3
FE
492 let productionReadyCheck = (repos, type, noSubAlternateName) => {
493 if (!activeSubscription && repos.enterprise) {
494 addWarn(Ext.String.format(
495 gettext('The {0}enterprise repository is enabled, but there is no active subscription!'),
496 type,
497 ));
498 }
5e0cecb7 499
7d5201a3
FE
500 if (repos.nosubscription) {
501 addWarn(Ext.String.format(
502 gettext('The {0}no-subscription{1} repository is not recommended for production use!'),
503 type,
504 noSubAlternateName,
505 ));
506 }
5e0cecb7 507
7d5201a3
FE
508 if (repos.test) {
509 addWarn(Ext.String.format(
510 gettext('The {0}test repository may pull in unstable updates and is not recommended for production use!'),
511 type,
512 ));
513 }
514 };
515
516 productionReadyCheck({ enterprise, nosubscription, test }, '', '');
517 // TODO drop alternate 'main' name when no longer relevant
518 productionReadyCheck(cephRepos, 'Ceph ', '/main');
5e0cecb7
DC
519
520 if (errors.length > 0) {
4227a557 521 text = gettext('Fatal parsing error for at least one repository');
f59d1076
DC
522 }
523
524 let iconCls = Proxmox.Utils.get_health_icon(status, true);
525
526 vm.set('state', {
527 iconCls,
528 text,
529 });
003c4982
DC
530 },
531 },
532
24313a9d
FE
533 viewModel: {
534 data: {
3fc020f4 535 product: 'Proxmox VE', // default
5e0cecb7 536 errors: [],
f59d1076 537 suitesWarning: false,
e6ed4498 538 mixedSuites: false, // used before major upgrade
24313a9d
FE
539 subscriptionActive: '',
540 noSubscriptionRepo: '',
541 enterpriseRepo: '',
f59d1076 542 testRepo: '',
7d5201a3
FE
543 cephEnterpriseRepo: '',
544 cephNoSubscriptionRepo: '',
545 cephTestRepo: '',
003c4982 546 selectionenabled: false,
f59d1076 547 state: {},
24313a9d
FE
548 },
549 formulas: {
003c4982
DC
550 enableButtonText: (get) => get('selectionenabled')
551 ? gettext('Disable') : gettext('Enable'),
24313a9d 552 },
5e0cecb7
DC
553 stores: {
554 errorstore: {
555 fields: ['status', 'message'],
556 },
557 },
24313a9d
FE
558 },
559
82071150
DC
560 scrollable: true,
561 layout: {
562 type: 'vbox',
563 align: 'stretch',
564 },
565
24313a9d
FE
566 items: [
567 {
f59d1076
DC
568 xtype: 'panel',
569 border: false,
570 layout: {
571 type: 'hbox',
572 align: 'stretch',
24313a9d 573 },
5e0cecb7 574 height: 200,
f59d1076
DC
575 title: gettext('Status'),
576 items: [
577 {
578 xtype: 'box',
f411afb4 579 flex: 2,
f59d1076
DC
580 margin: 10,
581 data: {
582 iconCls: Proxmox.Utils.get_health_icon(undefined, true),
583 text: '',
584 },
585 bind: {
586 data: '{state}',
587 },
588 tpl: [
f411afb4 589 '<center class="centered-flex-column" style="font-size:15px;line-height: 25px;">',
f59d1076 590 '<i class="fa fa-4x {iconCls}"></i>',
f59d1076
DC
591 '{text}',
592 '</center>',
593 ],
594 },
595 {
596 xtype: 'proxmoxNodeAPTRepositoriesErrors',
597 name: 'repositoriesErrors',
f411afb4 598 flex: 7,
f59d1076
DC
599 margin: 10,
600 bind: {
5e0cecb7 601 store: '{errorstore}',
f59d1076
DC
602 },
603 },
604 ],
24313a9d
FE
605 },
606 {
607 xtype: 'proxmoxNodeAPTRepositoriesGrid',
608 name: 'repositoriesGrid',
f59d1076 609 flex: 1,
24313a9d
FE
610 cbind: {
611 nodename: '{nodename}',
5e76f730 612 onlineHelp: '{onlineHelp}',
24313a9d
FE
613 },
614 majorUpgradeAllowed: false, // TODO get release information from an API call?
003c4982
DC
615 listeners: {
616 selectionchange: 'selectionChange',
bb64cd03 617 },
24313a9d
FE
618 },
619 ],
620
621 check_subscription: function() {
622 let me = this;
623 let vm = me.getViewModel();
624
625 Proxmox.Utils.API2Request({
626 url: `/nodes/${me.nodename}/subscription`,
627 method: 'GET',
af48de6b 628 failure: (response, opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
24313a9d
FE
629 success: function(response, opts) {
630 const res = response.result;
af48de6b 631 const subscription = !(!res || !res.data || res.data.status.toLowerCase() !== 'active');
24313a9d 632 vm.set('subscriptionActive', subscription);
f59d1076 633 me.getController().updateState();
24313a9d
FE
634 },
635 });
636 },
637
638 updateStandardRepos: function(standardRepos) {
639 let me = this;
640 let vm = me.getViewModel();
641
faacb77f 642 let addButton = me.down('button[name=addRepo]');
d76eedb4 643
faacb77f 644 addButton.repoInfo = [];
24313a9d
FE
645 for (const standardRepo of standardRepos) {
646 const handle = standardRepo.handle;
647 const status = standardRepo.status;
648
649 if (handle === "enterprise") {
650 vm.set('enterpriseRepo', status);
651 } else if (handle === "no-subscription") {
652 vm.set('noSubscriptionRepo', status);
f59d1076
DC
653 } else if (handle === 'test') {
654 vm.set('testRepo', status);
7d5201a3
FE
655 } else if (handle.match(/^ceph-[a-zA-Z]+-enterprise$/)) {
656 vm.set('cephEnterpriseRepo', status);
657 } else if (handle.match(/^ceph-[a-zA-Z]+-no-subscription$/)) {
658 vm.set('cephNoSubscriptionRepo', status);
659 } else if (handle.match(/^ceph-[a-zA-Z]+-test$/)) {
660 vm.set('cephTestRepo', status);
24313a9d 661 }
f59d1076 662 me.getController().updateState();
d76eedb4 663
21860ea4
FE
664 addButton.repoInfo.push(standardRepo);
665 addButton.digest = me.digest;
24313a9d 666 }
21860ea4
FE
667
668 addButton.setDisabled(false);
24313a9d
FE
669 },
670
671 reload: function() {
672 let me = this;
673 let vm = me.getViewModel();
674 let repoGrid = me.down('proxmoxNodeAPTRepositoriesGrid');
24313a9d
FE
675
676 me.store.load(function(records, operation, success) {
677 let gridData = [];
678 let errors = [];
679 let digest;
f59d1076 680 let suitesWarning = false;
24313a9d 681
e6ed4498
FE
682 // Usually different suites will give errors anyways, but before a major upgrade the
683 // current and the next suite are allowed, so it makes sense to check for mixed suites.
684 let checkMixedSuites = false;
685 let mixedSuites = false;
686
24313a9d
FE
687 if (success && records.length > 0) {
688 let data = records[0].data;
689 let files = data.files;
690 errors = data.errors;
691 digest = data.digest;
692
e71fc6e4
DC
693 let infos = {};
694 for (const info of data.infos) {
695 let path = info.path;
696 let idx = info.index;
697
698 if (!infos[path]) {
699 infos[path] = {};
700 }
701 if (!infos[path][idx]) {
702 infos[path][idx] = {
703 origin: '',
704 warnings: [],
e6ed4498
FE
705 // Used as a heuristic to detect mixed repositories pre-upgrade. The
706 // warning is set on all repositories that do configure the next suite.
707 gotIgnorePreUpgradeWarning: false,
e71fc6e4
DC
708 };
709 }
710
711 if (info.kind === 'origin') {
712 infos[path][idx].origin = info.message;
fd468868 713 } else if (info.kind === 'warning') {
e71fc6e4 714 infos[path][idx].warnings.push(info);
fd468868 715 } else if (info.kind === 'ignore-pre-upgrade-warning') {
e6ed4498 716 infos[path][idx].gotIgnorePreUpgradeWarning = true;
fd468868
FE
717 if (!repoGrid.majorUpgradeAllowed) {
718 infos[path][idx].warnings.push(info);
e6ed4498
FE
719 } else {
720 checkMixedSuites = true;
fd468868 721 }
e71fc6e4
DC
722 }
723 }
724
725
24313a9d
FE
726 files.forEach(function(file) {
727 for (let n = 0; n < file.repositories.length; n++) {
728 let repo = file.repositories[n];
729 repo.Path = file.path;
730 repo.Index = n;
e71fc6e4 731 if (infos[file.path] && infos[file.path][n]) {
78be60a0 732 repo.Origin = infos[file.path][n].origin || Proxmox.Utils.unknownText;
e71fc6e4 733 repo.warnings = infos[file.path][n].warnings || [];
e455399a 734
e6ed4498
FE
735 if (repo.Enabled) {
736 if (repo.warnings.some(w => w.property === 'Suites')) {
737 suitesWarning = true;
738 }
739
740 let originType = me.classifyOrigin(repo.Origin);
741 // Only Proxmox and Debian repositories checked here, because the
742 // warning can be missing for others for a different reason (e.g.
743 // using 'stable' or non-Debian code names).
744 if (checkMixedSuites && repo.Types.includes('deb') &&
745 (originType === 'Proxmox' || originType === 'Debian') &&
746 !infos[file.path][n].gotIgnorePreUpgradeWarning
747 ) {
748 mixedSuites = true;
749 }
e455399a 750 }
e71fc6e4 751 }
24313a9d
FE
752 gridData.push(repo);
753 }
754 });
755
24313a9d
FE
756 repoGrid.store.loadData(gridData);
757
758 me.updateStandardRepos(data['standard-repos']);
759 }
760
761 me.digest = digest;
762
5e0cecb7 763 vm.set('errors', errors);
f59d1076 764 vm.set('suitesWarning', suitesWarning);
e6ed4498 765 vm.set('mixedSuites', mixedSuites);
f59d1076 766 me.getController().updateState();
24313a9d
FE
767 });
768
769 me.check_subscription();
770 },
771
772 listeners: {
773 activate: function() {
774 let me = this;
775 me.reload();
776 },
777 },
778
779 initComponent: function() {
780 let me = this;
781
782 if (!me.nodename) {
783 throw "no node name specified";
784 }
785
786 let store = Ext.create('Ext.data.Store', {
787 proxy: {
788 type: 'proxmox',
789 url: `/api2/json/nodes/${me.nodename}/apt/repositories`,
790 },
791 });
792
793 Ext.apply(me, { store: store });
794
795 Proxmox.Utils.monStoreErrors(me, me.store, true);
796
797 me.callParent();
3fc020f4
TL
798
799 me.getViewModel().set('product', me.product);
24313a9d
FE
800 },
801});