]>
git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/node/APTRepositories.js
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'),
115 getRowClass
: () => 'proxmox-invalid-row',
120 header
: gettext('File'),
122 renderer
: value
=> `<i class='pve-grid-fa fa fa-fw fa-exclamation-triangle'></i>${value}`,
126 header
: gettext('Error'),
133 Ext
.define('Proxmox.node.APTRepositoriesGrid', {
134 extend
: 'Ext.grid.GridPanel',
135 xtype
: 'proxmoxNodeAPTRepositoriesGrid',
137 title
: gettext('APT Repositories'),
139 cls
: 'proxmox-apt-repos', // to allow applying styling to general components with local effect
143 text
: gettext('Reload'),
144 iconCls
: 'fa fa-refresh',
145 handler: function() {
147 me
.up('proxmoxNodeAPTRepositories').reload();
151 text
: gettext('Add'),
155 handler: function(button
, event
, record
) {
156 Proxmox
.Utils
.checked_command(() => {
158 let panel
= me
.up('proxmoxNodeAPTRepositories');
160 let extraParams
= {};
161 if (panel
.digest
!== undefined) {
162 extraParams
.digest
= panel
.digest
;
165 Ext
.create('Proxmox.window.APTRepositoryAdd', {
166 repoInfo
: me
.repoInfo
,
167 url
: `/api2/json/nodes/${panel.nodename}/apt/repositories`,
169 extraRequestParams
: extraParams
,
171 destroy: function() {
181 xtype
: 'proxmoxButton',
182 text
: gettext('Enable'),
183 defaultText
: gettext('Enable'),
184 altText
: gettext('Disable'),
185 id
: 'repoEnableButton',
187 handler: function(button
, event
, record
) {
189 let panel
= me
.up('proxmoxNodeAPTRepositories');
192 path
: record
.data
.Path
,
193 index
: record
.data
.Index
,
194 enabled
: record
.data
.Enabled
? 0 : 1, // invert
197 if (panel
.digest
!== undefined) {
198 params
.digest
= panel
.digest
;
201 Proxmox
.Utils
.API2Request({
202 url
: `/nodes/${panel.nodename}/apt/repositories`,
205 failure: function(response
, opts
) {
206 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
209 success: function(response
, opts
) {
215 render: function(btn
) {
216 // HACK: calculate the max button width on first render to avoid toolbar glitches
217 let defSize
= btn
.getSize().width
;
219 btn
.setText(btn
.altText
);
220 let altSize
= btn
.getSize().width
;
222 btn
.setText(btn
.defaultText
);
223 btn
.setSize({ width
: altSize
> defSize
? altSize
: defSize
});
229 sortableColumns
: false,
233 xtype
: 'checkcolumn',
234 header
: gettext('Enabled'),
235 dataIndex
: 'Enabled',
237 beforecheckchange
: () => false, // veto, we don't want to allow inline change - to subtle
242 header
: gettext('Types'),
244 renderer: function(types
, cell
, record
) {
245 return types
.join(' ');
250 header
: gettext('URIs'),
252 renderer: function(uris
, cell
, record
) {
253 return uris
.join(' ');
258 header
: gettext('Suites'),
260 renderer: function(suites
, cell
, record
) {
261 return suites
.join(' ');
266 header
: gettext('Components'),
267 dataIndex
: 'Components',
268 renderer: function(components
, cell
, record
) {
269 return components
.join(' ');
274 header
: gettext('Options'),
275 dataIndex
: 'Options',
276 renderer: function(options
, cell
, record
) {
281 let filetype
= record
.data
.FileType
;
284 options
.forEach(function(option
) {
285 let key
= option
.Key
;
286 if (filetype
=== 'list') {
287 let values
= option
.Values
.join(',');
288 text
+= `${key}=${values} `;
289 } else if (filetype
=== 'sources') {
290 let values
= option
.Values
.join(' ');
291 text
+= `${key}: ${values}<br>`;
293 throw "unkown file type";
301 header
: gettext('Origin'),
306 header
: gettext('Comment'),
307 dataIndex
: 'Comment',
312 addAdditionalInfos: function(gridData
, infos
) {
318 let addLine = function(obj
, key
, line
) {
327 for (const info
of infos
) {
328 const key
= `${info.path}:${info.index}`;
329 if (info
.kind
=== 'warning' ||
330 (info
.kind
=== 'ignore-pre-upgrade-warning' && !me
.majorUpgradeAllowed
)
332 addLine(warnings
, key
, gettext('Warning') + ": " + info
.message
);
333 } else if (info
.kind
=== 'origin') {
334 origins
[key
] = info
.message
;
338 gridData
.forEach(function(record
) {
339 const key
= `${record.Path}:${record.Index}`;
340 record
.Origin
= origins
[key
];
343 me
.rowBodyFeature
.getAdditionalData = function(innerData
, rowIndex
, record
, orig
) {
344 let headerCt
= this.view
.headerCt
;
345 let colspan
= headerCt
.getColumnCount();
347 const key
= `${innerData.Path}:${innerData.Index}`;
348 const warning_text
= warnings
[key
];
351 rowBody
: '<div style="color: red; white-space: pre-line">' +
352 Ext
.String
.htmlEncode(warning_text
) + '</div>',
353 rowBodyCls
: warning_text
? '' : Ext
.baseCSSPrefix
+ 'grid-row-body-hidden',
354 rowBodyColspan
: colspan
,
359 initComponent: function() {
363 throw "no node name specified";
366 let store
= Ext
.create('Ext.data.Store', {
367 model
: 'apt-repolist',
377 let rowBodyFeature
= Ext
.create('Ext.grid.feature.RowBody', {});
379 let groupingFeature
= Ext
.create('Ext.grid.feature.Grouping', {
380 groupHeaderTpl
: '{[ "File: " + values.name ]} ({rows.length} ' +
381 'repositor{[values.rows.length > 1 ? "ies" : "y"]})',
382 enableGroupingMenu
: false,
385 let sm
= Ext
.create('Ext.selection.RowModel', {});
390 rowBodyFeature
: rowBodyFeature
,
391 features
: [groupingFeature
, rowBodyFeature
],
398 selectionchange: function() {
401 if (me
.onSelectionChange
) {
402 let sm
= me
.getSelectionModel();
403 let rec
= sm
.getSelection()[0];
405 me
.onSelectionChange(rec
, sm
);
411 Ext
.define('Proxmox.node.APTRepositories', {
412 extend
: 'Ext.panel.Panel',
413 xtype
: 'proxmoxNodeAPTRepositories',
414 mixins
: ['Proxmox.Mixin.CBind'],
418 product
: 'Proxmox VE', // default
422 product
: 'Proxmox VE', // default
424 subscriptionActive
: '',
425 noSubscriptionRepo
: '',
429 noErrors
: (get) => get('errorCount') === 0,
430 mainWarning: function(get) {
431 // Not yet initialized
432 if (get('subscriptionActive') === '' ||
433 get('enterpriseRepo') === '') {
437 let icon
= `<i class='fa fa-fw fa-exclamation-triangle critical'></i>`;
438 let fmt
= (msg
) => `<div class="black">${icon}${gettext('Warning')}: ${msg}</div>`;
440 if (!get('subscriptionActive') && get('enterpriseRepo')) {
441 return fmt(gettext('The enterprise repository is enabled, but there is no active subscription!'));
444 if (get('noSubscriptionRepo')) {
445 return fmt(gettext('The no-subscription repository is not recommended for production use!'));
448 if (!get('enterpriseRepo') && !get('noSubscriptionRepo')) {
449 let msg
= Ext
.String
.format(gettext('No {0} repository is enabled!'), get('product'));
460 title
: gettext('Warning'),
461 name
: 'repositoriesMainWarning',
464 title
: '{mainWarning}',
465 hidden
: '{!mainWarning}',
469 xtype
: 'proxmoxNodeAPTRepositoriesErrors',
470 name
: 'repositoriesErrors',
473 hidden
: '{noErrors}',
477 xtype
: 'proxmoxNodeAPTRepositoriesGrid',
478 name
: 'repositoriesGrid',
480 nodename
: '{nodename}',
482 majorUpgradeAllowed
: false, // TODO get release information from an API call?
483 onSelectionChange: function(rec
, sm
) {
486 let btn
= me
.up('proxmoxNodeAPTRepositories').down('#repoEnableButton');
487 btn
.setText(rec
.get('Enabled') ? gettext('Disable') : gettext('Enable'));
493 check_subscription: function() {
495 let vm
= me
.getViewModel();
497 Proxmox
.Utils
.API2Request({
498 url
: `/nodes/${me.nodename}/subscription`,
500 failure
: (response
, opts
) => Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
),
501 success: function(response
, opts
) {
502 const res
= response
.result
;
503 const subscription
= !(!res
|| !res
.data
|| res
.data
.status
.toLowerCase() !== 'active');
504 vm
.set('subscriptionActive', subscription
);
509 updateStandardRepos: function(standardRepos
) {
511 let vm
= me
.getViewModel();
513 let addButton
= me
.down('#addButton');
514 addButton
.repoInfo
= [];
516 for (const standardRepo
of standardRepos
) {
517 const handle
= standardRepo
.handle
;
518 const status
= standardRepo
.status
;
520 if (handle
=== "enterprise") {
521 vm
.set('enterpriseRepo', status
);
522 } else if (handle
=== "no-subscription") {
523 vm
.set('noSubscriptionRepo', status
);
526 addButton
.repoInfo
.push(standardRepo
);
527 addButton
.digest
= me
.digest
;
530 addButton
.setDisabled(false);
535 let vm
= me
.getViewModel();
536 let repoGrid
= me
.down('proxmoxNodeAPTRepositoriesGrid');
537 let errorGrid
= me
.down('proxmoxNodeAPTRepositoriesErrors');
539 me
.store
.load(function(records
, operation
, success
) {
544 if (success
&& records
.length
> 0) {
545 let data
= records
[0].data
;
546 let files
= data
.files
;
547 errors
= data
.errors
;
548 digest
= data
.digest
;
550 files
.forEach(function(file
) {
551 for (let n
= 0; n
< file
.repositories
.length
; n
++) {
552 let repo
= file
.repositories
[n
];
553 repo
.Path
= file
.path
;
559 repoGrid
.addAdditionalInfos(gridData
, data
.infos
);
560 repoGrid
.store
.loadData(gridData
);
562 me
.updateStandardRepos(data
['standard-repos']);
567 vm
.set('errorCount', errors
.length
);
568 errorGrid
.store
.loadData(errors
);
571 me
.check_subscription();
575 activate: function() {
581 initComponent: function() {
585 throw "no node name specified";
588 let store
= Ext
.create('Ext.data.Store', {
591 url
: `/api2/json/nodes/${me.nodename}/apt/repositories`,
595 Ext
.apply(me
, { store
: store
});
597 Proxmox
.Utils
.monStoreErrors(me
, me
.store
, true);
601 me
.getViewModel().set('product', me
.product
);