Ext.define('apt-repolist', {
extend: 'Ext.data.Model',
fields: [
'Path',
'Index',
'Origin',
'FileType',
'Enabled',
'Comment',
'Types',
'URIs',
'Suites',
'Components',
'Options',
],
});
Ext.define('Proxmox.window.APTRepositoryAdd', {
extend: 'Proxmox.window.Edit',
alias: 'widget.pmxAPTRepositoryAdd',
isCreate: true,
isAdd: true,
subject: gettext('Repository'),
width: 600,
initComponent: function() {
let me = this;
if (!me.repoInfo || me.repoInfo.length === 0) {
throw "repository information not initialized";
}
let description = Ext.create('Ext.form.field.Display', {
fieldLabel: gettext('Description'),
name: 'description',
});
let status = Ext.create('Ext.form.field.Display', {
fieldLabel: gettext('Status'),
name: 'status',
renderer: function(value) {
let statusText = gettext('Not yet configured');
if (value !== '') {
statusText = Ext.String.format(
'{0}: {1}',
gettext('Configured'),
value ? gettext('enabled') : gettext('disabled'),
);
}
return statusText;
},
});
let repoSelector = Ext.create('Proxmox.form.KVComboBox', {
fieldLabel: gettext('Repository'),
xtype: 'proxmoxKVComboBox',
name: 'handle',
allowBlank: false,
comboItems: me.repoInfo.map(info => [info.handle, info.name]),
validator: function(renderedValue) {
let handle = this.value;
// we cannot use this.callParent in instantiations
let valid = Proxmox.form.KVComboBox.prototype.validator.call(this, renderedValue);
if (!valid || !handle) {
return false;
}
const info = me.repoInfo.find(elem => elem.handle === handle);
if (!info) {
return false;
}
if (info.status) {
return Ext.String.format(gettext('{0} is already configured'), renderedValue);
}
return valid;
},
listeners: {
change: function(f, value) {
const info = me.repoInfo.find(elem => elem.handle === value);
description.setValue(info.description);
status.setValue(info.status);
},
},
});
repoSelector.setValue(me.repoInfo[0].handle);
Ext.apply(me, {
items: [
repoSelector,
description,
status,
],
repoSelector: repoSelector,
});
me.callParent();
},
});
Ext.define('Proxmox.node.APTRepositoriesErrors', {
extend: 'Ext.grid.GridPanel',
xtype: 'proxmoxNodeAPTRepositoriesErrors',
title: gettext('Errors'),
store: {},
border: false,
viewConfig: {
stripeRows: false,
getRowClass: () => 'proxmox-invalid-row',
},
columns: [
{
header: gettext('File'),
dataIndex: 'path',
renderer: value => `${value}`,
width: 350,
},
{
header: gettext('Error'),
dataIndex: 'error',
flex: 1,
},
],
});
Ext.define('Proxmox.node.APTRepositoriesGrid', {
extend: 'Ext.grid.GridPanel',
xtype: 'proxmoxNodeAPTRepositoriesGrid',
title: gettext('APT Repositories'),
cls: 'proxmox-apt-repos', // to allow applying styling to general components with local effect
border: false,
tbar: [
{
text: gettext('Reload'),
iconCls: 'fa fa-refresh',
handler: function() {
let me = this;
me.up('proxmoxNodeAPTRepositories').reload();
},
},
{
text: gettext('Add'),
id: 'addButton',
disabled: true,
repoInfo: undefined,
handler: function(button, event, record) {
Proxmox.Utils.checked_command(() => {
let me = this;
let panel = me.up('proxmoxNodeAPTRepositories');
let extraParams = {};
if (panel.digest !== undefined) {
extraParams.digest = panel.digest;
}
Ext.create('Proxmox.window.APTRepositoryAdd', {
repoInfo: me.repoInfo,
url: `/api2/extjs/nodes/${panel.nodename}/apt/repositories`,
method: 'PUT',
extraRequestParams: extraParams,
listeners: {
destroy: function() {
panel.reload();
},
},
}).show();
});
},
},
'-',
{
xtype: 'proxmoxButton',
text: gettext('Enable'),
defaultText: gettext('Enable'),
altText: gettext('Disable'),
id: 'repoEnableButton',
disabled: true,
bind: {
text: '{enableButtonText}',
},
handler: function(button, event, record) {
let me = this;
let panel = me.up('proxmoxNodeAPTRepositories');
let params = {
path: record.data.Path,
index: record.data.Index,
enabled: record.data.Enabled ? 0 : 1, // invert
};
if (panel.digest !== undefined) {
params.digest = panel.digest;
}
Proxmox.Utils.API2Request({
url: `/nodes/${panel.nodename}/apt/repositories`,
method: 'POST',
params: params,
failure: function(response, opts) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
panel.reload();
},
success: function(response, opts) {
panel.reload();
},
});
},
listeners: {
render: function(btn) {
// HACK: calculate the max button width on first render to avoid toolbar glitches
let defSize = btn.getSize().width;
btn.setText(btn.altText);
let altSize = btn.getSize().width;
btn.setText(btn.defaultText);
btn.setSize({ width: altSize > defSize ? altSize : defSize });
},
},
},
],
sortableColumns: false,
viewConfig: {
stripeRows: false,
getRowClass: (record, index) => record.get('Enabled') ? '' : 'proxmox-disabled-row',
},
columns: [
{
xtype: 'checkcolumn',
header: gettext('Enabled'),
dataIndex: 'Enabled',
listeners: {
beforecheckchange: () => false, // veto, we don't want to allow inline change - to subtle
},
width: 90,
},
{
header: gettext('Types'),
dataIndex: 'Types',
renderer: function(types, cell, record) {
return types.join(' ');
},
width: 100,
},
{
header: gettext('URIs'),
dataIndex: 'URIs',
renderer: function(uris, cell, record) {
return uris.join(' ');
},
width: 350,
},
{
header: gettext('Suites'),
dataIndex: 'Suites',
renderer: function(suites, metaData, record) {
let err = '';
if (record.data.warnings && record.data.warnings.length > 0) {
let txt = [gettext('Warning')];
record.data.warnings.forEach((warning) => {
if (warning.property === 'Suites') {
txt.push(warning.message);
}
});
metaData.tdAttr = `data-qtip="${Ext.htmlEncode(txt.join('
'))}"`;
if (record.data.Enabled) {
metaData.tdCls = 'proxmox-invalid-row';
err = ' ';
} else {
metaData.tdCls = 'proxmox-warning-row';
err = ' ';
}
}
return suites.join(' ') + err;
},
width: 130,
},
{
header: gettext('Components'),
dataIndex: 'Components',
renderer: function(components, cell, record) {
return components.join(' ');
},
width: 170,
},
{
header: gettext('Options'),
dataIndex: 'Options',
renderer: function(options, cell, record) {
if (!options) {
return '';
}
let filetype = record.data.FileType;
let text = '';
options.forEach(function(option) {
let key = option.Key;
if (filetype === 'list') {
let values = option.Values.join(',');
text += `${key}=${values} `;
} else if (filetype === 'sources') {
let values = option.Values.join(' ');
text += `${key}: ${values}
`;
} else {
throw "unkown file type";
}
});
return text;
},
flex: 1,
},
{
header: gettext('Origin'),
dataIndex: 'Origin',
width: 120,
renderer: (value, meta, rec) => {
if (typeof value !== 'string' || value.length === 0) {
value = gettext('Other');
}
let cls = 'fa fa-fw fa-question-circle-o';
if (value.match(/^\s*Proxmox\s*$/i)) {
cls = 'pmx-itype-icon pmx-itype-icon-proxmox-x';
} else if (value.match(/^\s*Debian\s*$/i)) {
cls = 'pmx-itype-icon pmx-itype-icon-debian-swirl';
}
return ` ${value}`;
},
},
{
header: gettext('Comment'),
dataIndex: 'Comment',
flex: 2,
},
],
initComponent: function() {
let me = this;
if (!me.nodename) {
throw "no node name specified";
}
let store = Ext.create('Ext.data.Store', {
model: 'apt-repolist',
groupField: 'Path',
sorters: [
{
property: 'Index',
direction: 'ASC',
},
],
});
let groupingFeature = Ext.create('Ext.grid.feature.Grouping', {
groupHeaderTpl: '{[ "File: " + values.name ]} ({rows.length} ' +
'repositor{[values.rows.length > 1 ? "ies" : "y"]})',
enableGroupingMenu: false,
});
Ext.apply(me, {
store: store,
features: [groupingFeature],
});
me.callParent();
},
});
Ext.define('Proxmox.node.APTRepositories', {
extend: 'Ext.panel.Panel',
xtype: 'proxmoxNodeAPTRepositories',
mixins: ['Proxmox.Mixin.CBind'],
digest: undefined,
product: 'Proxmox VE', // default
controller: {
xclass: 'Ext.app.ViewController',
selectionChange: function(grid, selection) {
let me = this;
if (!selection || selection.length < 1) {
return;
}
let rec = selection[0];
let vm = me.getViewModel();
vm.set('selectionenabled', rec.get('Enabled'));
},
},
viewModel: {
data: {
product: 'Proxmox VE', // default
errorCount: 0,
subscriptionActive: '',
noSubscriptionRepo: '',
enterpriseRepo: '',
selectionenabled: false,
},
formulas: {
noErrors: (get) => get('errorCount') === 0,
enableButtonText: (get) => get('selectionenabled')
? gettext('Disable') : gettext('Enable'),
mainWarning: function(get) {
// Not yet initialized
if (get('subscriptionActive') === '' ||
get('enterpriseRepo') === '') {
return '';
}
let icon = ``;
let fmt = (msg) => `