]>
git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/node/APTRepositories.js
5c832264cc7cfb67901c79905b4eb5ae24b793b2
1 Ext
.define('apt-repolist', {
2 extend
: 'Ext.data.Model',
18 Ext
.define('Proxmox.window.APTRepositoryAdd', {
19 extend
: 'Proxmox.window.Edit',
20 alias
: 'widget.pmxAPTRepositoryAdd',
25 subject
: gettext('Repository'),
27 initComponent: function() {
30 if (!me
.repoInfo
|| me
.repoInfo
.length
=== 0) {
31 throw "repository information not initialized";
34 let description
= Ext
.create('Ext.form.field.Display', {
35 fieldLabel
: gettext('Description'),
39 let status
= Ext
.create('Ext.form.field.Display', {
40 fieldLabel
: gettext('Status'),
42 renderer: function(value
) {
43 let statusText
= gettext('Not yet configured');
45 statusText
= Ext
.String
.format(
47 gettext('Configured'),
48 value
? gettext('enabled') : gettext('disabled'),
56 let repoSelector
= Ext
.create('Proxmox.form.KVComboBox', {
57 fieldLabel
: gettext('Repository'),
58 xtype
: 'proxmoxKVComboBox',
61 comboItems
: me
.repoInfo
.map(info
=> [info
.handle
, info
.name
]),
63 const handle
= this.value
;
69 const info
= me
.repoInfo
.find(elem
=> elem
.handle
=== handle
);
76 return info
.status
=== undefined || info
.status
=== null;
79 change: function(f
, value
) {
80 const info
= me
.repoInfo
.find(elem
=> elem
.handle
=== value
);
81 description
.setValue(info
.description
);
82 status
.setValue(info
.status
);
87 repoSelector
.setValue(me
.repoInfo
[0].handle
);
97 repoSelector
: repoSelector
,
104 Ext
.define('Proxmox.node.APTRepositoriesErrors', {
105 extend
: 'Ext.grid.GridPanel',
107 xtype
: 'proxmoxNodeAPTRepositoriesErrors',
109 title
: gettext('Errors'),
117 getRowClass
: () => 'proxmox-invalid-row',
122 header
: gettext('File'),
124 renderer
: value
=> `<i class='pve-grid-fa fa fa-fw fa-exclamation-triangle'></i>${value}`,
128 header
: gettext('Error'),
135 Ext
.define('Proxmox.node.APTRepositoriesGrid', {
136 extend
: 'Ext.grid.GridPanel',
137 xtype
: 'proxmoxNodeAPTRepositoriesGrid',
139 title
: gettext('APT Repositories'),
141 cls
: 'proxmox-apt-repos', // to allow applying styling to general components with local effect
147 text
: gettext('Reload'),
148 iconCls
: 'fa fa-refresh',
149 handler: function() {
151 me
.up('proxmoxNodeAPTRepositories').reload();
155 text
: gettext('Add'),
159 handler: function(button
, event
, record
) {
160 Proxmox
.Utils
.checked_command(() => {
162 let panel
= me
.up('proxmoxNodeAPTRepositories');
164 let extraParams
= {};
165 if (panel
.digest
!== undefined) {
166 extraParams
.digest
= panel
.digest
;
169 Ext
.create('Proxmox.window.APTRepositoryAdd', {
170 repoInfo
: me
.repoInfo
,
171 url
: `/api2/json/nodes/${panel.nodename}/apt/repositories`,
173 extraRequestParams
: extraParams
,
175 destroy: function() {
185 xtype
: 'proxmoxButton',
186 text
: gettext('Enable'),
187 defaultText
: gettext('Enable'),
188 altText
: gettext('Disable'),
189 id
: 'repoEnableButton',
191 handler: function(button
, event
, record
) {
193 let panel
= me
.up('proxmoxNodeAPTRepositories');
196 path
: record
.data
.Path
,
197 index
: record
.data
.Index
,
198 enabled
: record
.data
.Enabled
? 0 : 1, // invert
201 if (panel
.digest
!== undefined) {
202 params
.digest
= panel
.digest
;
205 Proxmox
.Utils
.API2Request({
206 url
: `/nodes/${panel.nodename}/apt/repositories`,
209 failure: function(response
, opts
) {
210 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
213 success: function(response
, opts
) {
219 render: function(btn
) {
220 // HACK: calculate the max button width on first render to avoid toolbar glitches
221 let defSize
= btn
.getSize().width
;
223 btn
.setText(btn
.altText
);
224 let altSize
= btn
.getSize().width
;
226 btn
.setText(btn
.defaultText
);
227 btn
.setSize({ width
: altSize
> defSize
? altSize
: defSize
});
233 sortableColumns
: false,
237 xtype
: 'checkcolumn',
238 header
: gettext('Enabled'),
239 dataIndex
: 'Enabled',
241 beforecheckchange
: () => false, // veto, we don't want to allow inline change - to subtle
246 header
: gettext('Types'),
248 renderer: function(types
, cell
, record
) {
249 return types
.join(' ');
254 header
: gettext('URIs'),
256 renderer: function(uris
, cell
, record
) {
257 return uris
.join(' ');
262 header
: gettext('Suites'),
264 renderer: function(suites
, cell
, record
) {
265 return suites
.join(' ');
270 header
: gettext('Components'),
271 dataIndex
: 'Components',
272 renderer: function(components
, cell
, record
) {
273 return components
.join(' ');
278 header
: gettext('Options'),
279 dataIndex
: 'Options',
280 renderer: function(options
, cell
, record
) {
285 let filetype
= record
.data
.FileType
;
288 options
.forEach(function(option
) {
289 let key
= option
.Key
;
290 if (filetype
=== 'list') {
291 let values
= option
.Values
.join(',');
292 text
+= `${key}=${values} `;
293 } else if (filetype
=== 'sources') {
294 let values
= option
.Values
.join(' ');
295 text
+= `${key}: ${values}<br>`;
297 throw "unkown file type";
305 header
: gettext('Origin'),
308 renderer
: (value
, meta
, rec
) => {
309 let cls
= 'fa fa-fw fa-question-circle-o';
310 if (value
.match(/^\s*Proxmox\s*$/i)) {
311 cls
= 'pmx-itype-icon pmx-itype-icon-proxmox-x';
312 } else if (value
.match(/^\s*Debian\s*$/i)) {
313 cls
= 'pmx-itype-icon pmx-itype-icon-debian-swirl';
315 return `<i class='${cls}'></i> ${value}`;
319 header
: gettext('Comment'),
320 dataIndex
: 'Comment',
325 addAdditionalInfos: function(gridData
, infos
) {
331 let addLine = function(obj
, key
, line
) {
340 for (const info
of infos
) {
341 const key
= `${info.path}:${info.index}`;
342 if (info
.kind
=== 'warning' ||
343 (info
.kind
=== 'ignore-pre-upgrade-warning' && !me
.majorUpgradeAllowed
)
345 addLine(warnings
, key
, gettext('Warning') + ": " + info
.message
);
346 } else if (info
.kind
=== 'origin') {
347 origins
[key
] = info
.message
;
351 gridData
.forEach(function(record
) {
352 const key
= `${record.Path}:${record.Index}`;
353 record
.Origin
= origins
[key
];
356 me
.rowBodyFeature
.getAdditionalData = function(innerData
, rowIndex
, record
, orig
) {
357 let headerCt
= this.view
.headerCt
;
358 let colspan
= headerCt
.getColumnCount();
360 const key
= `${innerData.Path}:${innerData.Index}`;
361 const warning_text
= warnings
[key
];
364 rowBody
: '<div style="color: red; white-space: pre-line">' +
365 Ext
.String
.htmlEncode(warning_text
) + '</div>',
366 rowBodyCls
: warning_text
? '' : Ext
.baseCSSPrefix
+ 'grid-row-body-hidden',
367 rowBodyColspan
: colspan
,
372 initComponent: function() {
376 throw "no node name specified";
379 let store
= Ext
.create('Ext.data.Store', {
380 model
: 'apt-repolist',
390 let rowBodyFeature
= Ext
.create('Ext.grid.feature.RowBody', {});
392 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
393 groupHeaderTpl
: '{[ "File: " + values.name ]} ({rows.length} ' +
394 'repositor{[values.rows.length > 1 ? "ies" : "y"]})',
395 enableGroupingMenu
: false,
398 let sm
= Ext
.create('Ext.selection.RowModel', {});
403 rowBodyFeature
: rowBodyFeature
,
404 features
: [groupingFeature
, rowBodyFeature
],
411 selectionchange: function() {
414 if (me
.onSelectionChange
) {
415 let sm
= me
.getSelectionModel();
416 let rec
= sm
.getSelection()[0];
418 me
.onSelectionChange(rec
, sm
);
424 Ext
.define('Proxmox.node.APTRepositories', {
425 extend
: 'Ext.panel.Panel',
426 xtype
: 'proxmoxNodeAPTRepositories',
427 mixins
: ['Proxmox.Mixin.CBind'],
431 product
: 'Proxmox VE', // default
435 product
: 'Proxmox VE', // default
437 subscriptionActive
: '',
438 noSubscriptionRepo
: '',
442 noErrors
: (get) => get('errorCount') === 0,
443 mainWarning: function(get) {
444 // Not yet initialized
445 if (get('subscriptionActive') === '' ||
446 get('enterpriseRepo') === '') {
450 let icon
= `<i class='fa fa-fw fa-exclamation-triangle critical'></i>`;
451 let fmt
= (msg
) => `<div class="black">${icon}${gettext('Warning')}: ${msg}</div>`;
453 if (!get('subscriptionActive') && get('enterpriseRepo')) {
454 return fmt(gettext('The enterprise repository is enabled, but there is no active subscription!'));
457 if (get('noSubscriptionRepo')) {
458 return fmt(gettext('The no-subscription repository is not recommended for production use!'));
461 if (!get('enterpriseRepo') && !get('noSubscriptionRepo')) {
462 let msg
= Ext
.String
.format(gettext('No {0} repository is enabled!'), get('product'));
480 baseCls
: 'x-panel-header',
482 hidden
: '{!mainWarning}',
483 title
: '{mainWarning}',
489 hidden
: '{!mainWarning}',
494 xtype
: 'proxmoxNodeAPTRepositoriesErrors',
495 name
: 'repositoriesErrors',
499 hidden
: '{noErrors}',
503 xtype
: 'proxmoxNodeAPTRepositoriesGrid',
504 name
: 'repositoriesGrid',
506 nodename
: '{nodename}',
508 majorUpgradeAllowed
: false, // TODO get release information from an API call?
509 onSelectionChange: function(rec
, sm
) {
512 let btn
= me
.up('proxmoxNodeAPTRepositories').down('#repoEnableButton');
513 btn
.setText(rec
.get('Enabled') ? gettext('Disable') : gettext('Enable'));
519 check_subscription: function() {
521 let vm
= me
.getViewModel();
523 Proxmox
.Utils
.API2Request({
524 url
: `/nodes/${me.nodename}/subscription`,
526 failure
: (response
, opts
) => Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
),
527 success: function(response
, opts
) {
528 const res
= response
.result
;
529 const subscription
= !(!res
|| !res
.data
|| res
.data
.status
.toLowerCase() !== 'active');
530 vm
.set('subscriptionActive', subscription
);
535 updateStandardRepos: function(standardRepos
) {
537 let vm
= me
.getViewModel();
539 let addButton
= me
.down('#addButton');
540 addButton
.repoInfo
= [];
542 for (const standardRepo
of standardRepos
) {
543 const handle
= standardRepo
.handle
;
544 const status
= standardRepo
.status
;
546 if (handle
=== "enterprise") {
547 vm
.set('enterpriseRepo', status
);
548 } else if (handle
=== "no-subscription") {
549 vm
.set('noSubscriptionRepo', status
);
552 addButton
.repoInfo
.push(standardRepo
);
553 addButton
.digest
= me
.digest
;
556 addButton
.setDisabled(false);
561 let vm
= me
.getViewModel();
562 let repoGrid
= me
.down('proxmoxNodeAPTRepositoriesGrid');
563 let errorGrid
= me
.down('proxmoxNodeAPTRepositoriesErrors');
565 me
.store
.load(function(records
, operation
, success
) {
570 if (success
&& records
.length
> 0) {
571 let data
= records
[0].data
;
572 let files
= data
.files
;
573 errors
= data
.errors
;
574 digest
= data
.digest
;
576 files
.forEach(function(file
) {
577 for (let n
= 0; n
< file
.repositories
.length
; n
++) {
578 let repo
= file
.repositories
[n
];
579 repo
.Path
= file
.path
;
585 repoGrid
.addAdditionalInfos(gridData
, data
.infos
);
586 repoGrid
.store
.loadData(gridData
);
588 me
.updateStandardRepos(data
['standard-repos']);
593 vm
.set('errorCount', errors
.length
);
594 errorGrid
.store
.loadData(errors
);
597 me
.check_subscription();
601 activate: function() {
607 initComponent: function() {
611 throw "no node name specified";
614 let store
= Ext
.create('Ext.data.Store', {
617 url
: `/api2/json/nodes/${me.nodename}/apt/repositories`,
621 Ext
.apply(me
, { store
: store
});
623 Proxmox
.Utils
.monStoreErrors(me
, me
.store
, true);
627 me
.getViewModel().set('product', me
.product
);