]> git.proxmox.com Git - proxmox-widget-toolkit.git/blobdiff - src/Utils.js
Utils: refactor updateColumns from pve-manager
[proxmox-widget-toolkit.git] / src / Utils.js
index d80d7036336e031ca6bd6c87b22025682b1f3b8a..adff5f4886fe03f9a5510eac18319b91ed021946 100644 (file)
@@ -5,13 +5,6 @@ if (!Ext.isDefined(Proxmox.Setup.auth_cookie_name)) {
     throw "Proxmox library not initialized";
 }
 
-// avoid errors related to Accessible Rich Internet Applications
-// (access for people with disabilities)
-// TODO reenable after all components are upgraded
-Ext.enableAria = false;
-Ext.enableAriaButtons = false;
-Ext.enableAriaPanels = false;
-
 // avoid errors when running without development tools
 if (!Ext.isDefined(Ext.global.console)) {
     let console = {
@@ -55,6 +48,7 @@ utilities: {
     noneText: gettext('none'),
     NoneText: gettext('None'),
     errorText: gettext('Error'),
+    warningsText: gettext('Warnings'),
     unknownText: gettext('Unknown'),
     defaultText: gettext('Default'),
     daysText: gettext('days'),
@@ -450,6 +444,17 @@ utilities: {
        Ext.Ajax.request(newopts);
     },
 
+    // can be useful for catching displaying errors from the API, e.g.:
+    // Proxmox.Async.api2({
+    //     ...
+    // }).catch(Proxmox.Utils.alertResponseFailure);
+    alertResponseFailure: (response) => {
+       Ext.Msg.alert(
+           gettext('Error'),
+           response.htmlStatus || response.result.message,
+       );
+    },
+
     checked_command: function(orig_cmd) {
        Proxmox.Utils.API2Request(
            {
@@ -784,11 +789,11 @@ utilities: {
        }
        // try to normalize - and _ separators, to support asciidoc and sphinx
        // references at the same time.
-       let section_minus_normalized = section.replace(/_/, '-');
+       let section_minus_normalized = section.replace(/_/g, '-');
        if (helpMap[section_minus_normalized]) {
            return helpMap[section_minus_normalized];
        }
-       let section_underscore_normalized = section.replace(/-/, '_');
+       let section_underscore_normalized = section.replace(/-/g, '_');
        return helpMap[section_underscore_normalized];
     },
 
@@ -816,6 +821,275 @@ utilities: {
        }
     },
 
+    render_optional_url: function(value) {
+       if (value && value.match(/^https?:\/\//) !== null) {
+           return '<a target="_blank" href="' + value + '">' + value + '</a>';
+       }
+       return value;
+    },
+
+    render_san: function(value) {
+       var names = [];
+       if (Ext.isArray(value)) {
+           value.forEach(function(val) {
+               if (!Ext.isNumber(val)) {
+                   names.push(val);
+               }
+           });
+           return names.join('<br>');
+       }
+       return value;
+    },
+
+    render_usage: function(val) {
+       return (val*100).toFixed(2) + '%';
+    },
+
+    render_cpu_usage: function(val, max) {
+       return Ext.String.format(gettext('{0}% of {1}') +
+           ' ' + gettext('CPU(s)'), (val*100).toFixed(2), max);
+    },
+
+    render_size_usage: function(val, max) {
+       if (max === 0) {
+           return gettext('N/A');
+       }
+       return (val*100/max).toFixed(2) + '% (' +
+           Ext.String.format(gettext('{0} of {1}'),
+           Proxmox.Utils.render_size(val), Proxmox.Utils.render_size(max)) + ')';
+    },
+
+    render_cpu: function(value, metaData, record, rowIndex, colIndex, store) {
+       if (!(record.data.uptime && Ext.isNumeric(value))) {
+           return '';
+       }
+
+       var maxcpu = record.data.maxcpu || 1;
+
+       if (!Ext.isNumeric(maxcpu) && maxcpu >= 1) {
+           return '';
+       }
+
+       var per = value * 100;
+
+       return per.toFixed(1) + '% of ' + maxcpu.toString() + (maxcpu > 1 ? 'CPUs' : 'CPU');
+    },
+
+    render_size: function(value, metaData, record, rowIndex, colIndex, store) {
+       if (!Ext.isNumeric(value)) {
+           return '';
+       }
+
+       return Proxmox.Utils.format_size(value);
+    },
+
+    render_cpu_model: function(cpuinfo) {
+       return cpuinfo.cpus + " x " + cpuinfo.model + " (" +
+           cpuinfo.sockets.toString() + " " +
+           (cpuinfo.sockets > 1
+               ? gettext('Sockets')
+               : gettext('Socket')
+           ) + ")";
+    },
+
+    /* this is different for nodes */
+    render_node_cpu_usage: function(value, record) {
+       return Proxmox.Utils.render_cpu_usage(value, record.cpus);
+    },
+
+    render_node_size_usage: function(record) {
+       return Proxmox.Utils.render_size_usage(record.used, record.total);
+    },
+
+    loadTextFromFile: function(file, callback, maxBytes) {
+       let maxSize = maxBytes || 8192;
+       if (file.size > maxSize) {
+           Ext.Msg.alert(gettext('Error'), gettext("Invalid file size: ") + file.size);
+           return;
+       }
+       let reader = new FileReader();
+       reader.onload = evt => callback(evt.target.result);
+       reader.readAsText(file);
+    },
+
+    parsePropertyString: function(value, defaultKey) {
+       var res = {},
+           error;
+
+       if (typeof value !== 'string' || value === '') {
+           return res;
+       }
+
+       Ext.Array.each(value.split(','), function(p) {
+           var kv = p.split('=', 2);
+           if (Ext.isDefined(kv[1])) {
+               res[kv[0]] = kv[1];
+           } else if (Ext.isDefined(defaultKey)) {
+               if (Ext.isDefined(res[defaultKey])) {
+                   error = 'defaultKey may be only defined once in propertyString';
+                   return false; // break
+               }
+               res[defaultKey] = kv[0];
+           } else {
+               error = 'invalid propertyString, not a key=value pair and no defaultKey defined';
+               return false; // break
+           }
+           return true;
+       });
+
+       if (error !== undefined) {
+           console.error(error);
+           return undefined;
+       }
+
+       return res;
+    },
+
+    printPropertyString: function(data, defaultKey) {
+       var stringparts = [],
+           gotDefaultKeyVal = false,
+           defaultKeyVal;
+
+       Ext.Object.each(data, function(key, value) {
+           if (defaultKey !== undefined && key === defaultKey) {
+               gotDefaultKeyVal = true;
+               defaultKeyVal = value;
+           } else if (Ext.isArray(value)) {
+               stringparts.push(key + '=' + value.join(';'));
+           } else if (value !== '') {
+               stringparts.push(key + '=' + value);
+           }
+       });
+
+       stringparts = stringparts.sort();
+       if (gotDefaultKeyVal) {
+           stringparts.unshift(defaultKeyVal);
+       }
+
+       return stringparts.join(',');
+    },
+
+    acmedomain_count: 5,
+
+    parseACMEPluginData: function(data) {
+       let res = {};
+       let extradata = [];
+       data.split('\n').forEach((line) => {
+           // capture everything after the first = as value
+           let [key, value] = line.split('=');
+           if (value !== undefined) {
+               res[key] = value;
+           } else {
+               extradata.push(line);
+           }
+       });
+       return [res, extradata];
+    },
+
+    delete_if_default: function(values, fieldname, default_val, create) {
+       if (values[fieldname] === '' || values[fieldname] === default_val) {
+           if (!create) {
+               if (values.delete) {
+                   if (Ext.isArray(values.delete)) {
+                       values.delete.push(fieldname);
+                   } else {
+                       values.delete += ',' + fieldname;
+                   }
+               } else {
+                   values.delete = fieldname;
+               }
+           }
+
+           delete values[fieldname];
+       }
+    },
+
+    printACME: function(value) {
+       if (Ext.isArray(value.domains)) {
+           value.domains = value.domains.join(';');
+       }
+       return Proxmox.Utils.printPropertyString(value);
+    },
+
+    parseACME: function(value) {
+       if (!value) {
+           return {};
+       }
+
+       var res = {};
+       var error;
+
+       Ext.Array.each(value.split(','), function(p) {
+           var kv = p.split('=', 2);
+           if (Ext.isDefined(kv[1])) {
+               res[kv[0]] = kv[1];
+           } else {
+               error = 'Failed to parse key-value pair: '+p;
+               return false;
+           }
+           return true;
+       });
+
+       if (error !== undefined) {
+           console.error(error);
+           return undefined;
+       }
+
+       if (res.domains !== undefined) {
+           res.domains = res.domains.split(/;/);
+       }
+
+       return res;
+    },
+
+    add_domain_to_acme: function(acme, domain) {
+       if (acme.domains === undefined) {
+           acme.domains = [domain];
+       } else {
+           acme.domains.push(domain);
+           acme.domains = acme.domains.filter((value, index, self) => self.indexOf(value) === index);
+       }
+       return acme;
+    },
+
+    remove_domain_from_acme: function(acme, domain) {
+       if (acme.domains !== undefined) {
+           acme.domains = acme.domains.filter(
+               (value, index, self) => self.indexOf(value) === index && value !== domain,
+           );
+       }
+       return acme;
+    },
+
+    updateColumns: function(container) {
+       let mode = Ext.state.Manager.get('summarycolumns') || 'auto';
+       let factor;
+       if (mode !== 'auto') {
+           factor = parseInt(mode, 10);
+           if (Number.isNaN(factor)) {
+               factor = 1;
+           }
+       } else {
+           factor = container.getSize().width < 1400 ? 1 : 2;
+       }
+
+       if (container.oldFactor === factor) {
+           return;
+       }
+
+       let items = container.query('>'); // direct childs
+       factor = Math.min(factor, items.length);
+       container.oldFactor = factor;
+
+       items.forEach((item) => {
+           item.columnWidth = 1 / factor;
+       });
+
+       // we have to update the layout twice, since the first layout change
+       // can trigger the scrollbar which reduces the amount of space left
+       container.updateLayout();
+       container.updateLayout();
+    },
 },
 
     singleton: true,
@@ -861,7 +1135,27 @@ utilities: {
        me.HostPort_match = new RegExp("^(" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")(?::(\\d+))?$");
        me.HostPortBrackets_match = new RegExp("^\\[(" + IPV6_REGEXP + "|" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")\\](?::(\\d+))?$");
        me.IP6_dotnotation_match = new RegExp("^(" + IPV6_REGEXP + ")(?:\\.(\\d+))?$");
-       me.Vlan_match = /^vlan(\\d+)/;
-       me.VlanInterface_match = /(\\w+)\\.(\\d+)/;
+       me.Vlan_match = /^vlan(\d+)/;
+       me.VlanInterface_match = /(\w+)\.(\d+)/;
+    },
+});
+
+Ext.define('Proxmox.Async', {
+    singleton: true,
+
+    // Returns a Promise resolving to the result of an `API2Request` or rejecting to the error
+    // repsonse on failure
+    api2: function(reqOpts) {
+       return new Promise((resolve, reject) => {
+           delete reqOpts.callback; // not allowed in this api
+           reqOpts.success = response => resolve(response);
+           reqOpts.failure = response => reject(response);
+           Proxmox.Utils.API2Request(reqOpts);
+       });
+    },
+
+    // Delay for a number of milliseconds.
+    sleep: function(millis) {
+       return new Promise((resolve, _reject) => setTimeout(resolve, millis));
     },
 });