]> git.proxmox.com Git - proxmox-widget-toolkit.git/blobdiff - src/node/APTRepositories.js
node: repos: increase width of add-repo window + style cleanup
[proxmox-widget-toolkit.git] / src / node / APTRepositories.js
index 49e46b304f4fa47e329d1edad6376e9585e189de..99955506d1ea907e81af48999c7a9e3431d59733 100644 (file)
@@ -3,7 +3,7 @@ Ext.define('apt-repolist', {
     fields: [
        'Path',
        'Index',
-       'OfficialHost',
+       'Origin',
        'FileType',
        'Enabled',
        'Comment',
@@ -15,6 +15,91 @@ Ext.define('apt-repolist', {
     ],
 });
 
+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]),
+           isValid: function() {
+               const handle = this.value;
+
+               if (!handle) {
+                   return false;
+               }
+
+               const info = me.repoInfo.find(elem => elem.handle === handle);
+
+               if (!info) {
+                   return false;
+               }
+
+               // not yet configured
+               return info.status === undefined || info.status === null;
+           },
+           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',
 
@@ -24,6 +109,8 @@ Ext.define('Proxmox.node.APTRepositoriesErrors', {
 
     store: {},
 
+    border: false,
+
     viewConfig: {
        stripeRows: false,
        getRowClass: () => 'proxmox-invalid-row',
@@ -50,6 +137,10 @@ Ext.define('Proxmox.node.APTRepositoriesGrid', {
 
     title: gettext('APT Repositories'),
 
+    cls: 'proxmox-apt-repos', // to allow applying styling to general components with local effect
+
+    border: false,
+
     tbar: [
        {
            text: gettext('Reload'),
@@ -61,10 +152,31 @@ Ext.define('Proxmox.node.APTRepositoriesGrid', {
        },
        {
            text: gettext('Add'),
-           menu: {
-               plain: true,
-               itemId: "addMenu",
-               items: [],
+           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/json/nodes/${panel.nodename}/apt/repositories`,
+                       method: 'PUT',
+                       extraRequestParams: extraParams,
+                       listeners: {
+                           destroy: function() {
+                               panel.reload();
+                           },
+                       },
+                   }).show();
+               });
            },
        },
        '-',
@@ -75,6 +187,9 @@ Ext.define('Proxmox.node.APTRepositoriesGrid', {
            altText: gettext('Disable'),
            id: 'repoEnableButton',
            disabled: true,
+           bind: {
+               text: '{enableButtonText}',
+           },
            handler: function(button, event, record) {
                let me = this;
                let panel = me.up('proxmoxNodeAPTRepositories');
@@ -121,27 +236,12 @@ Ext.define('Proxmox.node.APTRepositoriesGrid', {
 
     columns: [
        {
-           header: gettext('Official'),
-           dataIndex: 'OfficialHost',
-           renderer: function(value, cell, record) {
-               let icon = (cls) => `<i class="fa fa-fw ${cls}"></i>`;
-
-               const enabled = record.data.Enabled;
-
-               if (value === undefined || value === null) {
-                   return icon('fa-question-circle-o');
-               }
-               if (!value) {
-                   return icon('fa-times ' + (enabled ? 'critical' : 'faded'));
-               }
-               return icon('fa-check ' + (enabled ? 'good' : 'faded'));
-           },
-           width: 70,
-       },
-       {
+           xtype: 'checkcolumn',
            header: gettext('Enabled'),
            dataIndex: 'Enabled',
-           renderer: Proxmox.Utils.format_enabled_toggle,
+           listeners: {
+               beforecheckchange: () => false, // veto, we don't want to allow inline change - to subtle
+           },
            width: 90,
        },
        {
@@ -163,8 +263,20 @@ Ext.define('Proxmox.node.APTRepositoriesGrid', {
        {
            header: gettext('Suites'),
            dataIndex: 'Suites',
-           renderer: function(suites, cell, record) {
-               return suites.join(' ');
+           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('<br>'))}"`;
+                   metaData.tdCls = 'proxmox-invalid-row';
+                   err = '<i class="fa fa-fw critical fa-exclamation-circle"></i> ';
+               }
+               return suites.join(' ') + err;
            },
            width: 130,
        },
@@ -203,6 +315,20 @@ Ext.define('Proxmox.node.APTRepositoriesGrid', {
            },
            flex: 1,
        },
+       {
+           header: gettext('Origin'),
+           dataIndex: 'Origin',
+           width: 120,
+           renderer: (value, meta, rec) => {
+               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 `<i class='${cls}'></i> ${value}`;
+           },
+       },
        {
            header: gettext('Comment'),
            dataIndex: 'Comment',
@@ -210,53 +336,6 @@ Ext.define('Proxmox.node.APTRepositoriesGrid', {
        },
     ],
 
-    addAdditionalInfos: function(gridData, infos) {
-       let me = this;
-
-       let warnings = {};
-       let officialHosts = {};
-
-       let addLine = function(obj, key, line) {
-           if (obj[key]) {
-               obj[key] += "\n";
-               obj[key] += line;
-           } else {
-               obj[key] = line;
-           }
-       };
-
-       for (const info of infos) {
-           const key = `${info.path}:${info.index}`;
-           if (info.kind === 'warning' ||
-               (info.kind === 'ignore-pre-upgrade-warning' && !me.majorUpgradeAllowed)
-           ) {
-               addLine(warnings, key, gettext('Warning') + ": " + info.message);
-           } else if (info.kind === 'badge' && info.message === 'official host name') {
-               officialHosts[key] = true;
-           }
-       }
-
-       gridData.forEach(function(record) {
-           const key = `${record.Path}:${record.Index}`;
-           record.OfficialHost = !!officialHosts[key];
-       });
-
-       me.rowBodyFeature.getAdditionalData = function(innerData, rowIndex, record, orig) {
-           let headerCt = this.view.headerCt;
-           let colspan = headerCt.getColumnCount();
-
-           const key = `${innerData.Path}:${innerData.Index}`;
-           const warning_text = warnings[key];
-
-           return {
-               rowBody: '<div style="color: red; white-space: pre-line">' +
-                   Ext.String.htmlEncode(warning_text) + '</div>',
-               rowBodyCls: warning_text ? '' : Ext.baseCSSPrefix + 'grid-row-body-hidden',
-               rowBodyColspan: colspan,
-           };
-       };
-    },
-
     initComponent: function() {
        let me = this;
 
@@ -275,38 +354,19 @@ Ext.define('Proxmox.node.APTRepositoriesGrid', {
            ],
        });
 
-       let rowBodyFeature = Ext.create('Ext.grid.feature.RowBody', {});
-
        let groupingFeature = Ext.create('Ext.grid.feature.Grouping', {
            groupHeaderTpl: '{[ "File: " + values.name ]} ({rows.length} ' +
                'repositor{[values.rows.length > 1 ? "ies" : "y"]})',
            enableGroupingMenu: false,
        });
 
-       let sm = Ext.create('Ext.selection.RowModel', {});
-
        Ext.apply(me, {
            store: store,
-           selModel: sm,
-           rowBodyFeature: rowBodyFeature,
-           features: [groupingFeature, rowBodyFeature],
+           features: [groupingFeature],
        });
 
        me.callParent();
     },
-
-    listeners: {
-       selectionchange: function() {
-           let me = this;
-
-           if (me.onSelectionChange) {
-               let sm = me.getSelectionModel();
-               let rec = sm.getSelection()[0];
-
-               me.onSelectionChange(rec, sm);
-           }
-       },
-    },
 });
 
 Ext.define('Proxmox.node.APTRepositories', {
@@ -316,15 +376,35 @@ Ext.define('Proxmox.node.APTRepositories', {
 
     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') === '' ||
@@ -332,21 +412,20 @@ Ext.define('Proxmox.node.APTRepositories', {
                    return '';
                }
 
-               let withStyle = (msg) => "<div style='color:red;'><i class='fa fa-fw " +
-                   "fa-exclamation-triangle'></i>" + gettext('Warning') + ': ' + msg + "</div>";
+               let icon = `<i class='fa fa-fw fa-exclamation-triangle critical'></i>`;
+               let fmt = (msg) => `<div class="black">${icon}${gettext('Warning')}: ${msg}</div>`;
 
                if (!get('subscriptionActive') && get('enterpriseRepo')) {
-                   return withStyle(gettext('The enterprise repository is ' +
-                       'enabled, but there is no active subscription!'));
+                   return fmt(gettext('The enterprise repository is enabled, but there is no active subscription!'));
                }
 
                if (get('noSubscriptionRepo')) {
-                   return withStyle(gettext('The no-subscription repository is ' +
-                       'not recommended for production use!'));
+                   return fmt(gettext('The no-subscription repository is not recommended for production use!'));
                }
 
                if (!get('enterpriseRepo') && !get('noSubscriptionRepo')) {
-                   return withStyle(gettext('No Proxmox repository is enabled!'));
+                   let msg = Ext.String.format(gettext('No {0} repository is enabled!'), get('product'));
+                   return fmt(msg);
                }
 
                return '';
@@ -354,20 +433,33 @@ Ext.define('Proxmox.node.APTRepositories', {
        },
     },
 
+    scrollable: true,
+    layout: {
+       type: 'vbox',
+       align: 'stretch',
+    },
+
     items: [
        {
-           title: gettext('Warning'),
-           name: 'repositoriesMainWarning',
-           xtype: 'panel',
+           xtype: 'header',
+           baseCls: 'x-panel-header',
            bind: {
+               hidden: '{!mainWarning}',
                title: '{mainWarning}',
+           },
+       },
+       {
+           xtype: 'box',
+           bind: {
                hidden: '{!mainWarning}',
            },
+           height: 5,
        },
        {
            xtype: 'proxmoxNodeAPTRepositoriesErrors',
            name: 'repositoriesErrors',
            hidden: true,
+           padding: '0 0 5 0',
            bind: {
                hidden: '{noErrors}',
            },
@@ -379,12 +471,8 @@ Ext.define('Proxmox.node.APTRepositories', {
                nodename: '{nodename}',
            },
            majorUpgradeAllowed: false, // TODO get release information from an API call?
-           onSelectionChange: function(rec, sm) {
-               let me = this;
-               if (rec) {
-                   let btn = me.up('proxmoxNodeAPTRepositories').down('#repoEnableButton');
-                   btn.setText(rec.get('Enabled') ? gettext('Disable') : gettext('Enable'));
-               }
+           listeners: {
+               selectionchange: 'selectionChange',
            },
        },
     ],
@@ -409,8 +497,8 @@ Ext.define('Proxmox.node.APTRepositories', {
        let me = this;
        let vm = me.getViewModel();
 
-       let menu = me.down('#addMenu');
-       menu.removeAll();
+       let addButton = me.down('#addButton');
+       addButton.repoInfo = [];
 
        for (const standardRepo of standardRepos) {
            const handle = standardRepo.handle;
@@ -422,43 +510,11 @@ Ext.define('Proxmox.node.APTRepositories', {
                vm.set('noSubscriptionRepo', status);
            }
 
-           let status_text = '';
-           if (status !== undefined && status !== null) {
-               status_text = Ext.String.format(
-                   ' ({0}, {1})',
-                   gettext('configured'),
-                   status ? gettext('enabled') : gettext('disabled'),
-               );
-           }
-
-           menu.add({
-               text: standardRepo.name + status_text,
-               disabled: status !== undefined && status !== null,
-               repoHandle: handle,
-               handler: function(menuItem) {
-                  let params = {
-                      handle: menuItem.repoHandle,
-                  };
-
-                  if (me.digest !== undefined) {
-                      params.digest = me.digest;
-                  }
-
-                   Proxmox.Utils.API2Request({
-                       url: `/nodes/${me.nodename}/apt/repositories`,
-                       method: 'PUT',
-                       params: params,
-                       failure: function(response, opts) {
-                           Ext.Msg.alert(gettext('Error'), response.htmlStatus);
-                           me.reload();
-                       },
-                       success: function(response, opts) {
-                           me.reload();
-                       },
-                   });
-               },
-           });
+           addButton.repoInfo.push(standardRepo);
+           addButton.digest = me.digest;
        }
+
+       addButton.setDisabled(false);
     },
 
     reload: function() {
@@ -478,16 +534,46 @@ Ext.define('Proxmox.node.APTRepositories', {
                errors = data.errors;
                digest = data.digest;
 
+               let infos = {};
+               for (const info of data.infos) {
+                   let path = info.path;
+                   let idx = info.index;
+
+                   if (!infos[path]) {
+                       infos[path] = {};
+                   }
+                   if (!infos[path][idx]) {
+                       infos[path][idx] = {
+                           origin: '',
+                           warnings: [],
+                       };
+                   }
+
+                   if (info.kind === 'origin') {
+                       infos[path][idx].origin = info.message;
+                   } else if (info.kind === 'warning' ||
+                       (info.kind === 'ignore-pre-upgrade-warning' && !repoGrid.majorUpgradeAllowed)
+                   ) {
+                       infos[path][idx].warnings.push(info);
+                   } else {
+                       throw 'unknown info';
+                   }
+               }
+
+
                files.forEach(function(file) {
                    for (let n = 0; n < file.repositories.length; n++) {
                        let repo = file.repositories[n];
                        repo.Path = file.path;
                        repo.Index = n;
+                       if (infos[file.path] && infos[file.path][n]) {
+                           repo.Origin = infos[file.path][n].origin || Proxmox.Utils.UnknownText;
+                           repo.warnings = infos[file.path][n].warnings || [];
+                       }
                        gridData.push(repo);
                    }
                });
 
-               repoGrid.addAdditionalInfos(gridData, data.infos);
                repoGrid.store.loadData(gridData);
 
                me.updateStandardRepos(data['standard-repos']);
@@ -528,5 +614,7 @@ Ext.define('Proxmox.node.APTRepositories', {
        Proxmox.Utils.monStoreErrors(me, me.store, true);
 
        me.callParent();
+
+       me.getViewModel().set('product', me.product);
     },
 });