Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // 1.0-19
- Ext.ns('Proxmox');
- Ext.ns('Proxmox.Setup');
- 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)) {
- var console = {
- dir: function() {},
- log: function() {}
- };
- }
- Ext.Ajax.defaultHeaders = {
- 'Accept': 'application/json'
- };
- Ext.Ajax.on('beforerequest', function(conn, options) {
- if (Proxmox.CSRFPreventionToken) {
- if (!options.headers) {
- options.headers = {};
- }
- options.headers.CSRFPreventionToken = Proxmox.CSRFPreventionToken;
- }
- });
- Ext.define('Proxmox.Utils', { utilities: {
- // this singleton contains miscellaneous utilities
- yesText: gettext('Yes'),
- noText: gettext('No'),
- enabledText: gettext('Enabled'),
- disabledText: gettext('Disabled'),
- noneText: gettext('none'),
- errorText: gettext('Error'),
- unknownText: gettext('Unknown'),
- defaultText: gettext('Default'),
- daysText: gettext('days'),
- dayText: gettext('day'),
- runningText: gettext('running'),
- stoppedText: gettext('stopped'),
- neverText: gettext('never'),
- totalText: gettext('Total'),
- usedText: gettext('Used'),
- directoryText: gettext('Directory'),
- stateText: gettext('State'),
- groupText: gettext('Group'),
- language_map: {
- zh_CN: 'Chinese (Simplified)',
- zh_TW: 'Chinese (Traditional)',
- ca: 'Catalan',
- da: 'Danish',
- en: 'English',
- eu: 'Euskera (Basque)',
- fr: 'French',
- de: 'German',
- it: 'Italian',
- es: 'Spanish',
- ja: 'Japanese',
- nb: 'Norwegian (Bokmal)',
- nn: 'Norwegian (Nynorsk)',
- fa: 'Persian (Farsi)',
- pl: 'Polish',
- pt_BR: 'Portuguese (Brazil)',
- ru: 'Russian',
- sl: 'Slovenian',
- sv: 'Swedish',
- tr: 'Turkish'
- },
- render_language: function (value) {
- if (!value) {
- return Proxmox.Utils.defaultText + ' (English)';
- }
- var text = Proxmox.Utils.language_map[value];
- if (text) {
- return text + ' (' + value + ')';
- }
- return value;
- },
- language_array: function() {
- var data = [['__default__', Proxmox.Utils.render_language('')]];
- Ext.Object.each(Proxmox.Utils.language_map, function(key, value) {
- data.push([key, Proxmox.Utils.render_language(value)]);
- });
- return data;
- },
- getNoSubKeyHtml: function(url) {
- return Ext.String.format('This server is the property of Anjolen Inc. All un-authorized access is strictly prohibited. All access attempts are logged. All activity on this server is logged and monitored.');
- },
- format_boolean_with_default: function(value) {
- if (Ext.isDefined(value) && value !== '__default__') {
- return value ? Proxmox.Utils.yesText : Proxmox.Utils.noText;
- }
- return Proxmox.Utils.defaultText;
- },
- format_boolean: function(value) {
- return value ? Proxmox.Utils.yesText : Proxmox.Utils.noText;
- },
- format_neg_boolean: function(value) {
- return !value ? Proxmox.Utils.yesText : Proxmox.Utils.noText;
- },
- format_enabled_toggle: function(value) {
- return value ? Proxmox.Utils.enabledText : Proxmox.Utils.disabledText;
- },
- format_expire: function(date) {
- if (!date) {
- return Proxmox.Utils.neverText;
- }
- return Ext.Date.format(date, "Y-m-d");
- },
- format_duration_long: function(ut) {
- var days = Math.floor(ut / 86400);
- ut -= days*86400;
- var hours = Math.floor(ut / 3600);
- ut -= hours*3600;
- var mins = Math.floor(ut / 60);
- ut -= mins*60;
- var hours_str = '00' + hours.toString();
- hours_str = hours_str.substr(hours_str.length - 2);
- var mins_str = "00" + mins.toString();
- mins_str = mins_str.substr(mins_str.length - 2);
- var ut_str = "00" + ut.toString();
- ut_str = ut_str.substr(ut_str.length - 2);
- if (days) {
- var ds = days > 1 ? Proxmox.Utils.daysText : Proxmox.Utils.dayText;
- return days.toString() + ' ' + ds + ' ' +
- hours_str + ':' + mins_str + ':' + ut_str;
- } else {
- return hours_str + ':' + mins_str + ':' + ut_str;
- }
- },
- format_subscription_level: function(level) {
- if (level === 'c') {
- return 'Community';
- } else if (level === 'b') {
- return 'Basic';
- } else if (level === 's') {
- return 'Standard';
- } else if (level === 'p') {
- return 'Premium';
- } else {
- return Proxmox.Utils.noneText;
- }
- },
- compute_min_label_width: function(text, width) {
- if (width === undefined) { width = 100; }
- var tm = new Ext.util.TextMetrics();
- var min = tm.getWidth(text + ':');
- return min < width ? width : min;
- },
- authOK: function() {
- return (Proxmox.UserName !== '') && Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name);
- },
- authClear: function() {
- Ext.util.Cookies.clear(Proxmox.Setup.auth_cookie_name);
- },
- // comp.setLoading() is buggy in ExtJS 4.0.7, so we
- // use el.mask() instead
- setErrorMask: function(comp, msg) {
- var el = comp.el;
- if (!el) {
- return;
- }
- if (!msg) {
- el.unmask();
- } else {
- if (msg === true) {
- el.mask(gettext("Loading..."));
- } else {
- el.mask(msg);
- }
- }
- },
- monStoreErrors: function(me, store, clearMaskBeforeLoad) {
- if (clearMaskBeforeLoad) {
- me.mon(store, 'beforeload', function(s, operation, eOpts) {
- Proxmox.Utils.setErrorMask(me, false);
- });
- } else {
- me.mon(store, 'beforeload', function(s, operation, eOpts) {
- if (!me.loadCount) {
- me.loadCount = 0; // make sure it is numeric
- Proxmox.Utils.setErrorMask(me, true);
- }
- });
- }
- // only works with 'proxmox' proxy
- me.mon(store.proxy, 'afterload', function(proxy, request, success) {
- me.loadCount++;
- if (success) {
- Proxmox.Utils.setErrorMask(me, false);
- return;
- }
- var msg;
- /*jslint nomen: true */
- var operation = request._operation;
- var error = operation.getError();
- if (error.statusText) {
- msg = error.statusText + ' (' + error.status + ')';
- } else {
- msg = gettext('Connection error');
- }
- Proxmox.Utils.setErrorMask(me, msg);
- });
- },
- extractRequestError: function(result, verbose) {
- var msg = gettext('Successful');
- if (!result.success) {
- msg = gettext("Unknown error");
- if (result.message) {
- msg = result.message;
- if (result.status) {
- msg += ' (' + result.status + ')';
- }
- }
- if (verbose && Ext.isObject(result.errors)) {
- msg += "<br>";
- Ext.Object.each(result.errors, function(prop, desc) {
- msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
- Ext.htmlEncode(desc);
- });
- }
- }
- return msg;
- },
- // Ext.Ajax.request
- API2Request: function(reqOpts) {
- var newopts = Ext.apply({
- waitMsg: gettext('Please wait...')
- }, reqOpts);
- if (!newopts.url.match(/^\/api2/)) {
- newopts.url = '/api2/extjs' + newopts.url;
- }
- delete newopts.callback;
- var createWrapper = function(successFn, callbackFn, failureFn) {
- Ext.apply(newopts, {
- success: function(response, options) {
- if (options.waitMsgTarget) {
- if (Proxmox.Utils.toolkit === 'touch') {
- options.waitMsgTarget.setMasked(false);
- } else {
- options.waitMsgTarget.setLoading(false);
- }
- }
- var result = Ext.decode(response.responseText);
- response.result = result;
- if (!result.success) {
- response.htmlStatus = Proxmox.Utils.extractRequestError(result, true);
- Ext.callback(callbackFn, options.scope, [options, false, response]);
- Ext.callback(failureFn, options.scope, [response, options]);
- return;
- }
- Ext.callback(callbackFn, options.scope, [options, true, response]);
- Ext.callback(successFn, options.scope, [response, options]);
- },
- failure: function(response, options) {
- if (options.waitMsgTarget) {
- if (Proxmox.Utils.toolkit === 'touch') {
- options.waitMsgTarget.setMasked(false);
- } else {
- options.waitMsgTarget.setLoading(false);
- }
- }
- response.result = {};
- try {
- response.result = Ext.decode(response.responseText);
- } catch(e) {}
- var msg = gettext('Connection error') + ' - server offline?';
- if (response.aborted) {
- msg = gettext('Connection error') + ' - aborted.';
- } else if (response.timedout) {
- msg = gettext('Connection error') + ' - Timeout.';
- } else if (response.status && response.statusText) {
- msg = gettext('Connection error') + ' ' + response.status + ': ' + response.statusText;
- }
- response.htmlStatus = msg;
- Ext.callback(callbackFn, options.scope, [options, false, response]);
- Ext.callback(failureFn, options.scope, [response, options]);
- }
- });
- };
- createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure);
- var target = newopts.waitMsgTarget;
- if (target) {
- if (Proxmox.Utils.toolkit === 'touch') {
- target.setMasked({ xtype: 'loadmask', message: newopts.waitMsg} );
- } else {
- // Note: ExtJS bug - this does not work when component is not rendered
- target.setLoading(newopts.waitMsg);
- }
- }
- Ext.Ajax.request(newopts);
- },
- checked_command: function(orig_cmd) {
- Proxmox.Utils.API2Request({
- url: '/nodes/localhost/subscription',
- method: 'GET',
- //waitMsgTarget: me,
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, opts) {
- var data = response.result.data;
- if (data.status !== 'Active') {
- Ext.Msg.show({
- title: gettext('Legal Notice'),
- icon: Ext.Msg.WARNING,
- msg: Proxmox.Utils.getNoSubKeyHtml(data.url),
- buttons: Ext.Msg.OK,
- callback: function(btn) {
- if (btn !== 'ok') {
- return;
- }
- orig_cmd();
- }
- });
- } else {
- orig_cmd();
- }
- }
- });
- },
- assemble_field_data: function(values, data) {
- if (Ext.isObject(data)) {
- Ext.Object.each(data, function(name, val) {
- if (values.hasOwnProperty(name)) {
- var bucket = values[name];
- if (!Ext.isArray(bucket)) {
- bucket = values[name] = [bucket];
- }
- if (Ext.isArray(val)) {
- values[name] = bucket.concat(val);
- } else {
- bucket.push(val);
- }
- } else {
- values[name] = val;
- }
- });
- }
- },
- dialog_title: function(subject, create, isAdd) {
- if (create) {
- if (isAdd) {
- return gettext('Add') + ': ' + subject;
- } else {
- return gettext('Create') + ': ' + subject;
- }
- } else {
- return gettext('Edit') + ': ' + subject;
- }
- },
- network_iface_types: {
- eth: gettext("Network Device"),
- bridge: 'Linux Bridge',
- bond: 'Linux Bond',
- OVSBridge: 'OVS Bridge',
- OVSBond: 'OVS Bond',
- OVSPort: 'OVS Port',
- OVSIntPort: 'OVS IntPort'
- },
- render_network_iface_type: function(value) {
- return Proxmox.Utils.network_iface_types[value] ||
- Proxmox.Utils.unknownText;
- },
- task_desc_table: {
- acmenewcert: [ 'SRV', gettext('Order Certificate') ],
- acmeregister: [ 'ACME Account', gettext('Register') ],
- acmedeactivate: [ 'ACME Account', gettext('Deactivate') ],
- acmeupdate: [ 'ACME Account', gettext('Update') ],
- acmerefresh: [ 'ACME Account', gettext('Refresh') ],
- acmerenew: [ 'SRV', gettext('Renew Certificate') ],
- acmerevoke: [ 'SRV', gettext('Revoke Certificate') ],
- 'move_volume': [ 'CT', gettext('Move Volume') ],
- clustercreate: [ '', gettext('Create Cluster') ],
- clusterjoin: [ '', gettext('Join Cluster') ],
- diskinit: [ 'Disk', gettext('Initialize Disk with GPT') ],
- vncproxy: [ 'VM/CT', gettext('Console') ],
- spiceproxy: [ 'VM/CT', gettext('Console') + ' (Spice)' ],
- vncshell: [ '', gettext('Shell') ],
- spiceshell: [ '', gettext('Shell') + ' (Spice)' ],
- qmsnapshot: [ 'VM', gettext('Snapshot') ],
- qmrollback: [ 'VM', gettext('Rollback') ],
- qmdelsnapshot: [ 'VM', gettext('Delete Snapshot') ],
- qmcreate: [ 'VM', gettext('Create') ],
- qmrestore: [ 'VM', gettext('Restore') ],
- qmdestroy: [ 'VM', gettext('Destroy') ],
- qmigrate: [ 'VM', gettext('Migrate') ],
- qmclone: [ 'VM', gettext('Clone') ],
- qmmove: [ 'VM', gettext('Move disk') ],
- qmtemplate: [ 'VM', gettext('Convert to template') ],
- qmstart: [ 'VM', gettext('Start') ],
- qmstop: [ 'VM', gettext('Stop') ],
- qmreset: [ 'VM', gettext('Reset') ],
- qmshutdown: [ 'VM', gettext('Shutdown') ],
- qmsuspend: [ 'VM', gettext('Suspend') ],
- qmresume: [ 'VM', gettext('Resume') ],
- qmconfig: [ 'VM', gettext('Configure') ],
- vzsnapshot: [ 'CT', gettext('Snapshot') ],
- vzrollback: [ 'CT', gettext('Rollback') ],
- vzdelsnapshot: [ 'CT', gettext('Delete Snapshot') ],
- vzcreate: ['CT', gettext('Create') ],
- vzrestore: ['CT', gettext('Restore') ],
- vzdestroy: ['CT', gettext('Destroy') ],
- vzmigrate: [ 'CT', gettext('Migrate') ],
- vzclone: [ 'CT', gettext('Clone') ],
- vztemplate: [ 'CT', gettext('Convert to template') ],
- vzstart: ['CT', gettext('Start') ],
- vzstop: ['CT', gettext('Stop') ],
- vzmount: ['CT', gettext('Mount') ],
- vzumount: ['CT', gettext('Unmount') ],
- vzshutdown: ['CT', gettext('Shutdown') ],
- vzsuspend: [ 'CT', gettext('Suspend') ],
- vzresume: [ 'CT', gettext('Resume') ],
- hamigrate: [ 'HA', gettext('Migrate') ],
- hastart: [ 'HA', gettext('Start') ],
- hastop: [ 'HA', gettext('Stop') ],
- srvstart: ['SRV', gettext('Start') ],
- srvstop: ['SRV', gettext('Stop') ],
- srvrestart: ['SRV', gettext('Restart') ],
- srvreload: ['SRV', gettext('Reload') ],
- cephcreatemgr: ['Ceph Manager', gettext('Create') ],
- cephdestroymgr: ['Ceph Manager', gettext('Destroy') ],
- cephcreatemon: ['Ceph Monitor', gettext('Create') ],
- cephdestroymon: ['Ceph Monitor', gettext('Destroy') ],
- cephcreateosd: ['Ceph OSD', gettext('Create') ],
- cephdestroyosd: ['Ceph OSD', gettext('Destroy') ],
- cephcreatepool: ['Ceph Pool', gettext('Create') ],
- cephdestroypool: ['Ceph Pool', gettext('Destroy') ],
- imgcopy: ['', gettext('Copy data') ],
- imgdel: ['', gettext('Erase data') ],
- download: ['', gettext('Download') ],
- vzdump: ['', gettext('Backup') ],
- aptupdate: ['', gettext('Update package database') ],
- startall: [ '', gettext('Start all VMs and Containers') ],
- stopall: [ '', gettext('Stop all VMs and Containers') ],
- migrateall: [ '', gettext('Migrate all VMs and Containers') ]
- },
- format_task_description: function(type, id) {
- var farray = Proxmox.Utils.task_desc_table[type];
- if (!farray) {
- var text = type;
- if (id) {
- type += ' ' + id;
- }
- return text;
- }
- var prefix = farray[0];
- var text = farray[1];
- if (prefix) {
- return prefix + ' ' + id + ' - ' + text;
- }
- return text;
- },
- format_size: function(size) {
- /*jslint confusion: true */
- var units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
- var num = 0;
- while (size >= 1024 && ((num++)+1) < units.length) {
- size = size / 1024;
- }
- return size.toFixed((num > 0)?2:0) + " " + units[num] + "B";
- },
- render_upid: function(value, metaData, record) {
- var type = record.data.type;
- var id = record.data.id;
- return Proxmox.Utils.format_task_description(type, id);
- },
- render_uptime: function(value) {
- var uptime = value;
- if (uptime === undefined) {
- return '';
- }
- if (uptime <= 0) {
- return '-';
- }
- return Proxmox.Utils.format_duration_long(uptime);
- },
- parse_task_upid: function(upid) {
- var task = {};
- var res = upid.match(/^UPID:(\S+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
- if (!res) {
- throw "unable to parse upid '" + upid + "'";
- }
- task.node = res[1];
- task.pid = parseInt(res[2], 16);
- task.pstart = parseInt(res[3], 16);
- task.starttime = parseInt(res[4], 16);
- task.type = res[5];
- task.id = res[6];
- task.user = res[7];
- task.desc = Proxmox.Utils.format_task_description(task.type, task.id);
- return task;
- },
- render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) {
- var servertime = new Date(value * 1000);
- return Ext.Date.format(servertime, 'Y-m-d H:i:s');
- },
- openXtermJsViewer: function(vmtype, vmid, nodename, vmname) {
- var url = Ext.urlEncode({
- console: vmtype, // kvm, lxc, upgrade or shell
- xtermjs: 1,
- vmid: vmid,
- vmname: vmname,
- node: nodename
- });
- var nw = window.open("?" + url, '_blank', 'toolbar=no,location=no,status=no,menubar=no,resizable=yes,width=800,height=420');
- nw.focus();
- }
- },
- singleton: true,
- constructor: function() {
- var me = this;
- Ext.apply(me, me.utilities);
- var IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])";
- var IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")";
- var IPV6_H16 = "(?:[0-9a-fA-F]{1,4})";
- var IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")";
- me.IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$");
- me.IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")\/([0-9]{1,2})$");
- var IPV6_REGEXP = "(?:" +
- "(?:(?:" + "(?:" + IPV6_H16 + ":){6})" + IPV6_LS32 + ")|" +
- "(?:(?:" + "::" + "(?:" + IPV6_H16 + ":){5})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){4})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,1}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){3})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,2}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){2})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,3}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){1})" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,4}" + IPV6_H16 + ")?::" + ")" + IPV6_LS32 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,5}" + IPV6_H16 + ")?::" + ")" + IPV6_H16 + ")|" +
- "(?:(?:(?:(?:" + IPV6_H16 + ":){0,7}" + IPV6_H16 + ")?::" + ")" + ")" +
- ")";
- me.IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$");
- me.IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")\/([0-9]{1,3})$");
- me.IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]");
- me.IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$");
- var DnsName_REGEXP = "(?:(([a-zA-Z0-9]([a-zA-Z0-9\\-]*[a-zA-Z0-9])?)\\.)*([A-Za-z0-9]([A-Za-z0-9\\-]*[A-Za-z0-9])?))";
- me.DnsName_match = new RegExp("^" + DnsName_REGEXP + "$");
- 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+)?$");
- }
- });
- // ExtJS related things
- // do not send '_dc' parameter
- Ext.Ajax.disableCaching = false;
- // custom Vtypes
- Ext.apply(Ext.form.field.VTypes, {
- IPAddress: function(v) {
- return Proxmox.Utils.IP4_match.test(v);
- },
- IPAddressText: gettext('Example') + ': 192.168.1.1',
- IPAddressMask: /[\d\.]/i,
- IPCIDRAddress: function(v) {
- var result = Proxmox.Utils.IP4_cidr_match.exec(v);
- // limits according to JSON Schema see
- // pve-common/src/PVE/JSONSchema.pm
- return (result !== null && result[1] >= 8 && result[1] <= 32);
- },
- IPCIDRAddressText: gettext('Example') + ': 192.168.1.1/24' + "<br>" + gettext('Valid CIDR Range') + ': 8-32',
- IPCIDRAddressMask: /[\d\.\/]/i,
- IP6Address: function(v) {
- return Proxmox.Utils.IP6_match.test(v);
- },
- IP6AddressText: gettext('Example') + ': 2001:DB8::42',
- IP6AddressMask: /[A-Fa-f0-9:]/,
- IP6CIDRAddress: function(v) {
- var result = Proxmox.Utils.IP6_cidr_match.exec(v);
- // limits according to JSON Schema see
- // pve-common/src/PVE/JSONSchema.pm
- return (result !== null && result[1] >= 8 && result[1] <= 120);
- },
- IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "<br>" + gettext('Valid CIDR Range') + ': 8-120',
- IP6CIDRAddressMask: /[A-Fa-f0-9:\/]/,
- IP6PrefixLength: function(v) {
- return v >= 0 && v <= 128;
- },
- IP6PrefixLengthText: gettext('Example') + ': X, where 0 <= X <= 128',
- IP6PrefixLengthMask: /[0-9]/,
- IP64Address: function(v) {
- return Proxmox.Utils.IP64_match.test(v);
- },
- IP64AddressText: gettext('Example') + ': 192.168.1.1 2001:DB8::42',
- IP64AddressMask: /[A-Fa-f0-9\.:]/,
- MacAddress: function(v) {
- return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v);
- },
- MacAddressMask: /[a-fA-F0-9:]/,
- MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab',
- MacPrefix: function(v) {
- return (/^[a-f0-9]{2}(?::[a-f0-9]{2}){0,2}:?$/i).test(v);
- },
- MacPrefixMask: /[a-fA-F0-9:]/,
- MacPrefixText: gettext('Example') + ': 02:8f',
- BridgeName: function(v) {
- return (/^vmbr\d{1,4}$/).test(v);
- },
- BridgeNameText: gettext('Format') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
- BondName: function(v) {
- return (/^bond\d{1,4}$/).test(v);
- },
- BondNameText: gettext('Format') + ': bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
- InterfaceName: function(v) {
- return (/^[a-z][a-z0-9_]{1,20}$/).test(v);
- },
- InterfaceNameText: gettext("Allowed characters") + ": 'a-z', '0-9', '_'" + "<br />" +
- gettext("Minimum characters") + ": 2" + "<br />" +
- gettext("Maximum characters") + ": 21" + "<br />" +
- gettext("Must start with") + ": 'a-z'",
- StorageId: function(v) {
- return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
- },
- StorageIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '-', '_', '.'" + "<br />" +
- gettext("Minimum characters") + ": 2" + "<br />" +
- gettext("Must start with") + ": 'A-Z', 'a-z'<br />" +
- gettext("Must end with") + ": 'A-Z', 'a-z', '0-9'<br />",
- ConfigId: function(v) {
- return (/^[a-z][a-z0-9\_]+$/i).test(v);
- },
- ConfigIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '_'" + "<br />" +
- gettext("Minimum characters") + ": 2" + "<br />" +
- gettext("Must start with") + ": " + gettext("letter"),
- HttpProxy: function(v) {
- return (/^http:\/\/.*$/).test(v);
- },
- HttpProxyText: gettext('Example') + ": http://username:password@host:port/",
- DnsName: function(v) {
- return Proxmox.Utils.DnsName_match.test(v);
- },
- DnsNameText: gettext('This is not a valid DNS name'),
- // workaround for https://www.sencha.com/forum/showthread.php?302150
- proxmoxMail: function(v) {
- return (/^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,63}$/).test(v);
- },
- proxmoxMailText: gettext('Example') + ": user@example.com",
- HostList: function(v) {
- var list = v.split(/[\ \,\;]+/);
- var i;
- for (i = 0; i < list.length; i++) {
- if (list[i] == "") {
- continue;
- }
- if (!Proxmox.Utils.HostPort_match.test(list[i]) &&
- !Proxmox.Utils.HostPortBrackets_match.test(list[i]) &&
- !Proxmox.Utils.IP6_dotnotation_match.test(list[i])) {
- return false;
- }
- }
- return true;
- },
- HostListText: gettext('Not a valid list of hosts'),
- password: function(val, field) {
- if (field.initialPassField) {
- var pwd = field.up('form').down(
- '[name=' + field.initialPassField + ']');
- return (val == pwd.getValue());
- }
- return true;
- },
- passwordText: gettext('Passwords do not match')
- });
- // Firefox 52+ Touchscreen bug
- // see https://www.sencha.com/forum/showthread.php?336762-Examples-don-t-work-in-Firefox-52-touchscreen/page2
- // and https://bugzilla.proxmox.com/show_bug.cgi?id=1223
- Ext.define('EXTJS_23846.Element', {
- override: 'Ext.dom.Element'
- }, function(Element) {
- var supports = Ext.supports,
- proto = Element.prototype,
- eventMap = proto.eventMap,
- additiveEvents = proto.additiveEvents;
- if (Ext.os.is.Desktop && supports.TouchEvents && !supports.PointerEvents) {
- eventMap.touchstart = 'mousedown';
- eventMap.touchmove = 'mousemove';
- eventMap.touchend = 'mouseup';
- eventMap.touchcancel = 'mouseup';
- additiveEvents.mousedown = 'mousedown';
- additiveEvents.mousemove = 'mousemove';
- additiveEvents.mouseup = 'mouseup';
- additiveEvents.touchstart = 'touchstart';
- additiveEvents.touchmove = 'touchmove';
- additiveEvents.touchend = 'touchend';
- additiveEvents.touchcancel = 'touchcancel';
- additiveEvents.pointerdown = 'mousedown';
- additiveEvents.pointermove = 'mousemove';
- additiveEvents.pointerup = 'mouseup';
- additiveEvents.pointercancel = 'mouseup';
- }
- });
- Ext.define('EXTJS_23846.Gesture', {
- override: 'Ext.event.publisher.Gesture'
- }, function(Gesture) {
- var me = Gesture.instance;
- if (Ext.supports.TouchEvents && !Ext.isWebKit && Ext.os.is.Desktop) {
- me.handledDomEvents.push('mousedown', 'mousemove', 'mouseup');
- me.registerEvents();
- }
- });
- // we always want the number in x.y format and never in, e.g., x,y
- Ext.define('PVE.form.field.Number', {
- override: 'Ext.form.field.Number',
- submitLocaleSeparator: false
- });
- // ExtJs 5-6 has an issue with caching
- // see https://www.sencha.com/forum/showthread.php?308989
- Ext.define('Proxmox.UnderlayPool', {
- override: 'Ext.dom.UnderlayPool',
- checkOut: function () {
- var cache = this.cache,
- len = cache.length,
- el;
- // do cleanup because some of the objects might have been destroyed
- while (len--) {
- if (cache[len].destroyed) {
- cache.splice(len, 1);
- }
- }
- // end do cleanup
- el = cache.shift();
- if (!el) {
- el = Ext.Element.create(this.elementConfig);
- el.setVisibilityMode(2);
- //<debug>
- // tell the spec runner to ignore this element when checking if the dom is clean
- el.dom.setAttribute('data-sticky', true);
- //</debug>
- }
- return el;
- }
- });
- // 'Enter' in Textareas and aria multiline fields should not activate the
- // defaultbutton, fixed in extjs 6.0.2
- Ext.define('PVE.panel.Panel', {
- override: 'Ext.panel.Panel',
- fireDefaultButton: function(e) {
- if (e.target.getAttribute('aria-multiline') === 'true' ||
- e.target.tagName === "TEXTAREA") {
- return true;
- }
- return this.callParent(arguments);
- }
- });
- // if the order of the values are not the same in originalValue and value
- // extjs will not overwrite value, but marks the field dirty and thus
- // the reset button will be enabled (but clicking it changes nothing)
- // so if the arrays are not the same after resetting, we
- // clear and set it
- Ext.define('Proxmox.form.ComboBox', {
- override: 'Ext.form.field.ComboBox',
- reset: function() {
- // copied from combobox
- var me = this;
- me.callParent();
- // clear and set when not the same
- var value = me.getValue();
- if (Ext.isArray(me.originalValue) && Ext.isArray(value) && !Ext.Array.equals(value, me.originalValue)) {
- me.clearValue();
- me.setValue(me.originalValue);
- }
- }
- });
- // when refreshing a grid/tree view, restoring the focus moves the view back to
- // the previously focused item. Save scroll position before refocusing.
- Ext.define(null, {
- override: 'Ext.view.Table',
- jumpToFocus: false,
- saveFocusState: function() {
- var me = this,
- store = me.dataSource,
- actionableMode = me.actionableMode,
- navModel = me.getNavigationModel(),
- focusPosition = actionableMode ? me.actionPosition : navModel.getPosition(true),
- refocusRow, refocusCol;
- if (focusPosition) {
- // Separate this from the instance that the nav model is using.
- focusPosition = focusPosition.clone();
- // Exit actionable mode.
- // We must inform any Actionables that they must relinquish control.
- // Tabbability must be reset.
- if (actionableMode) {
- me.ownerGrid.setActionableMode(false);
- }
- // Blur the focused descendant, but do not trigger focusLeave.
- me.el.dom.focus();
- // Exiting actionable mode navigates to the owning cell, so in either focus mode we must
- // clear the navigation position
- navModel.setPosition();
- // The following function will attempt to refocus back in the same mode to the same cell
- // as it was at before based upon the previous record (if it's still inthe store), or the row index.
- return function() {
- // If we still have data, attempt to refocus in the same mode.
- if (store.getCount()) {
- // Adjust expectations of where we are able to refocus according to what kind of destruction
- // might have been wrought on this view's DOM during focus save.
- refocusRow = Math.min(focusPosition.rowIdx, me.all.getCount() - 1);
- refocusCol = Math.min(focusPosition.colIdx, me.getVisibleColumnManager().getColumns().length - 1);
- focusPosition = new Ext.grid.CellContext(me).setPosition(
- store.contains(focusPosition.record) ? focusPosition.record : refocusRow, refocusCol);
- if (actionableMode) {
- me.ownerGrid.setActionableMode(true, focusPosition);
- } else {
- me.cellFocused = true;
- // we sometimes want to scroll back to where we were
- var x = me.getScrollX();
- var y = me.getScrollY();
- // Pass "preventNavigation" as true so that that does not cause selection.
- navModel.setPosition(focusPosition, null, null, null, true);
- if (!me.jumpToFocus) {
- me.scrollTo(x,y);
- }
- }
- }
- // No rows - focus associated column header
- else {
- focusPosition.column.focus();
- }
- };
- }
- return Ext.emptyFn;
- }
- });
- // should be fixed with ExtJS 6.0.2, see:
- // https://www.sencha.com/forum/showthread.php?307244-Bug-with-datefield-in-window-with-scroll
- Ext.define('Proxmox.Datepicker', {
- override: 'Ext.picker.Date',
- hideMode: 'visibility'
- });
- // ExtJS 6.0.1 has no setSubmitValue() (although you find it in the docs).
- // Note: this.submitValue is a boolean flag, whereas getSubmitValue() returns
- // data to be submitted.
- Ext.define('Proxmox.form.field.Text', {
- override: 'Ext.form.field.Text',
- setSubmitValue: function(v) {
- this.submitValue = v;
- },
- });
- // this should be fixed with ExtJS 6.0.2
- // make mousescrolling work in firefox in the containers overflowhandler
- Ext.define(null, {
- override: 'Ext.layout.container.boxOverflow.Scroller',
- createWheelListener: function() {
- var me = this;
- if (Ext.isFirefox) {
- me.wheelListener = me.layout.innerCt.on('wheel', me.onMouseWheelFirefox, me, {destroyable: true});
- } else {
- me.wheelListener = me.layout.innerCt.on('mousewheel', me.onMouseWheel, me, {destroyable: true});
- }
- },
- // special wheel handler for firefox. differs from the default onMouseWheel
- // handler by using deltaY instead of wheelDeltaY and no normalizing,
- // because it is already
- onMouseWheelFirefox: function(e) {
- e.stopEvent();
- var delta = e.browserEvent.deltaY || 0;
- this.scrollBy(delta * this.wheelIncrement, false);
- }
- });
- // force alert boxes to be rendered with an Error Icon
- // since Ext.Msg is an object and not a prototype, we need to override it
- // after the framework has been initiated
- Ext.onReady(function() {
- /*jslint confusion: true */
- Ext.override(Ext.Msg, {
- alert: function(title, message, fn, scope) {
- if (Ext.isString(title)) {
- var config = {
- title: title,
- message: message,
- icon: this.ERROR,
- buttons: this.OK,
- fn: fn,
- scope : scope,
- minWidth: this.minWidth
- };
- return this.show(config);
- }
- }
- });
- /*jslint confusion: false */
- });
- Ext.define('Ext.ux.IFrame', {
- extend: 'Ext.Component',
- alias: 'widget.uxiframe',
- loadMask: 'Loading...',
- src: 'about:blank',
- renderTpl: [
- '<iframe src="{src}" id="{id}-iframeEl" data-ref="iframeEl" name="{frameName}" width="100%" height="100%" frameborder="0" allowfullscreen="true"></iframe>'
- ],
- childEls: ['iframeEl'],
- initComponent: function () {
- this.callParent();
- this.frameName = this.frameName || this.id + '-frame';
- },
- initEvents : function() {
- var me = this;
- me.callParent();
- me.iframeEl.on('load', me.onLoad, me);
- },
- initRenderData: function() {
- return Ext.apply(this.callParent(), {
- src: this.src,
- frameName: this.frameName
- });
- },
- getBody: function() {
- var doc = this.getDoc();
- return doc.body || doc.documentElement;
- },
- getDoc: function() {
- try {
- return this.getWin().document;
- } catch (ex) {
- return null;
- }
- },
- getWin: function() {
- var me = this,
- name = me.frameName,
- win = Ext.isIE
- ? me.iframeEl.dom.contentWindow
- : window.frames[name];
- return win;
- },
- getFrame: function() {
- var me = this;
- return me.iframeEl.dom;
- },
- beforeDestroy: function () {
- this.cleanupListeners(true);
- this.callParent();
- },
- cleanupListeners: function(destroying){
- var doc, prop;
- if (this.rendered) {
- try {
- doc = this.getDoc();
- if (doc) {
- /*jslint nomen: true*/
- Ext.get(doc).un(this._docListeners);
- /*jslint nomen: false*/
- if (destroying && doc.hasOwnProperty) {
- for (prop in doc) {
- if (doc.hasOwnProperty(prop)) {
- delete doc[prop];
- }
- }
- }
- }
- } catch(e) { }
- }
- },
- onLoad: function() {
- var me = this,
- doc = me.getDoc(),
- fn = me.onRelayedEvent;
- if (doc) {
- try {
- // These events need to be relayed from the inner document (where they stop
- // bubbling) up to the outer document. This has to be done at the DOM level so
- // the event reaches listeners on elements like the document body. The effected
- // mechanisms that depend on this bubbling behavior are listed to the right
- // of the event.
- /*jslint nomen: true*/
- Ext.get(doc).on(
- me._docListeners = {
- mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
- mousemove: fn, // window resize drag detection
- mouseup: fn, // window resize termination
- click: fn, // not sure, but just to be safe
- dblclick: fn, // not sure again
- scope: me
- }
- );
- /*jslint nomen: false*/
- } catch(e) {
- // cannot do this xss
- }
- // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
- Ext.get(this.getWin()).on('beforeunload', me.cleanupListeners, me);
- this.el.unmask();
- this.fireEvent('load', this);
- } else if (me.src) {
- this.el.unmask();
- this.fireEvent('error', this);
- }
- },
- onRelayedEvent: function (event) {
- // relay event from the iframe's document to the document that owns the iframe...
- var iframeEl = this.iframeEl,
- // Get the left-based iframe position
- iframeXY = iframeEl.getTrueXY(),
- originalEventXY = event.getXY(),
- // Get the left-based XY position.
- // This is because the consumer of the injected event will
- // perform its own RTL normalization.
- eventXY = event.getTrueXY();
- // the event from the inner document has XY relative to that document's origin,
- // so adjust it to use the origin of the iframe in the outer document:
- event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];
- event.injectEvent(iframeEl); // blame the iframe for the event...
- event.xy = originalEventXY; // restore the original XY (just for safety)
- },
- load: function (src) {
- var me = this,
- text = me.loadMask,
- frame = me.getFrame();
- if (me.fireEvent('beforeload', me, src) !== false) {
- if (text && me.el) {
- me.el.mask(text);
- }
- frame.src = me.src = (src || me.src);
- }
- }
- });
- Ext.define('Proxmox.Mixin.CBind', {
- extend: 'Ext.Mixin',
- mixinConfig: {
- before: {
- initComponent: 'cloneTemplates'
- }
- },
- cloneTemplates: function() {
- var me = this;
- if (typeof(me.cbindData) == "function") {
- me.cbindData = me.cbindData(me.initialConfig) || {};
- }
- var getConfigValue = function(cname) {
- if (cname in me.initialConfig) {
- return me.initialConfig[cname];
- }
- if (cname in me.cbindData) {
- return me.cbindData[cname];
- }
- if (cname in me) {
- return me[cname];
- }
- throw "unable to get cbind data for '" + cname + "'";
- };
- var applyCBind = function(obj) {
- var cbind = obj.cbind, prop, cdata, cvalue, match, found;
- if (!cbind) return;
- for (prop in cbind) {
- cdata = cbind[prop];
- found = false;
- if (match = /^\{(!)?([a-z_][a-z0-9_]*)\}$/i.exec(cdata)) {
- var cvalue = getConfigValue(match[2]);
- if (match[1]) cvalue = !cvalue;
- obj[prop] = cvalue;
- found = true;
- } else if (match = /^\{(!)?([a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)+)\}$/i.exec(cdata)) {
- var keys = match[2].split('.');
- var cvalue = getConfigValue(keys.shift());
- keys.forEach(function(k) {
- if (k in cvalue) {
- cvalue = cvalue[k];
- } else {
- throw "unable to get cbind data for '" + match[2] + "'";
- }
- });
- if (match[1]) cvalue = !cvalue;
- obj[prop] = cvalue;
- found = true;
- } else {
- obj[prop] = cdata.replace(/{([a-z_][a-z0-9_]*)\}/ig, function(match, cname) {
- var cvalue = getConfigValue(cname);
- found = true;
- return cvalue;
- });
- }
- if (!found) {
- throw "unable to parse cbind template '" + cdata + "'";
- }
- }
- };
- if (me.cbind) {
- applyCBind(me);
- }
- var cloneTemplateArray = function(org) {
- var copy, i, found, el, elcopy, arrayLength;
- arrayLength = org.length;
- found = false;
- for (i = 0; i < arrayLength; i++) {
- el = org[i];
- if (el.constructor == Object && el.xtype) {
- found = true;
- break;
- }
- }
- if (!found) return org; // no need to copy
- copy = [];
- for (i = 0; i < arrayLength; i++) {
- el = org[i];
- if (el.constructor == Object && el.xtype) {
- elcopy = cloneTemplateObject(el);
- if (elcopy.cbind) {
- applyCBind(elcopy);
- }
- copy.push(elcopy);
- } else if (el.constructor == Array) {
- elcopy = cloneTemplateArray(el);
- copy.push(elcopy);
- } else {
- copy.push(el);
- }
- }
- return copy;
- };
- var cloneTemplateObject = function(org) {
- var res = {}, prop, el, copy;
- for (prop in org) {
- el = org[prop];
- if (el.constructor == Object && el.xtype) {
- copy = cloneTemplateObject(el);
- if (copy.cbind) {
- applyCBind(copy);
- }
- res[prop] = copy;
- } else if (el.constructor == Array) {
- copy = cloneTemplateArray(el);
- res[prop] = copy;
- } else {
- res[prop] = el;
- }
- }
- return res;
- };
- var condCloneProperties = function() {
- var prop, el, i, tmp;
- for (prop in me) {
- el = me[prop];
- if (el === undefined || el === null) continue;
- if (typeof(el) === 'object' && el.constructor == Object) {
- if (el.xtype && prop != 'config') {
- me[prop] = cloneTemplateObject(el);
- }
- } else if (el.constructor == Array) {
- tmp = cloneTemplateArray(el);
- me[prop] = tmp;
- }
- }
- };
- condCloneProperties();
- }
- });
- /* A reader to store a single JSON Object (hash) into a storage.
- * Also accepts an array containing a single hash.
- *
- * So it can read:
- *
- * example1: {data1: "xyz", data2: "abc"}
- * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}]
- *
- * example2: [ {data1: "xyz", data2: "abc"} ]
- * returns [{key: "data1", value: "xyz"}, {key: "data2", value: "abc"}]
- *
- * If you set 'readArray', the reader expexts the object as array:
- *
- * example3: [ { key: "data1", value: "xyz", p2: "cde" }, { key: "data2", value: "abc", p2: "efg" }]
- * returns [{key: "data1", value: "xyz", p2: "cde}, {key: "data2", value: "abc", p2: "efg"}]
- *
- * Note: The records can contain additional properties (like 'p2' above) when you use 'readArray'
- *
- * Additional feature: specify allowed properties with default values with 'rows' object
- *
- * var rows = {
- * memory: {
- * required: true,
- * defaultValue: 512
- * }
- * }
- *
- */
- Ext.define('Proxmox.data.reader.JsonObject', {
- extend: 'Ext.data.reader.Json',
- alias : 'reader.jsonobject',
- readArray: false,
- rows: undefined,
- constructor: function(config) {
- var me = this;
- Ext.apply(me, config || {});
- me.callParent([config]);
- },
- getResponseData: function(response) {
- var me = this;
- var data = [];
- try {
- var result = Ext.decode(response.responseText);
- // get our data items inside the server response
- var root = result[me.getRootProperty()];
- if (me.readArray) {
- var rec_hash = {};
- Ext.Array.each(root, function(rec) {
- if (Ext.isDefined(rec.key)) {
- rec_hash[rec.key] = rec;
- }
- });
- if (me.rows) {
- Ext.Object.each(me.rows, function(key, rowdef) {
- var rec = rec_hash[key];
- if (Ext.isDefined(rec)) {
- if (!Ext.isDefined(rec.value)) {
- rec.value = rowdef.defaultValue;
- }
- data.push(rec);
- } else if (Ext.isDefined(rowdef.defaultValue)) {
- data.push({key: key, value: rowdef.defaultValue} );
- } else if (rowdef.required) {
- data.push({key: key, value: undefined });
- }
- });
- } else {
- Ext.Array.each(root, function(rec) {
- if (Ext.isDefined(rec.key)) {
- data.push(rec);
- }
- });
- }
- } else {
- var org_root = root;
- if (Ext.isArray(org_root)) {
- if (root.length == 1) {
- root = org_root[0];
- } else {
- root = {};
- }
- }
- if (me.rows) {
- Ext.Object.each(me.rows, function(key, rowdef) {
- if (Ext.isDefined(root[key])) {
- data.push({key: key, value: root[key]});
- } else if (Ext.isDefined(rowdef.defaultValue)) {
- data.push({key: key, value: rowdef.defaultValue});
- } else if (rowdef.required) {
- data.push({key: key, value: undefined});
- }
- });
- } else {
- Ext.Object.each(root, function(key, value) {
- data.push({key: key, value: value });
- });
- }
- }
- }
- catch (ex) {
- Ext.Error.raise({
- response: response,
- json: response.responseText,
- parseError: ex,
- msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
- });
- }
- return data;
- }
- });
- Ext.define('Proxmox.RestProxy', {
- extend: 'Ext.data.RestProxy',
- alias : 'proxy.proxmox',
- pageParam : null,
- startParam: null,
- limitParam: null,
- groupParam: null,
- sortParam: null,
- filterParam: null,
- noCache : false,
- afterRequest: function(request, success) {
- this.fireEvent('afterload', this, request, success);
- return;
- },
- constructor: function(config) {
- Ext.applyIf(config, {
- reader: {
- type: 'json',
- rootProperty: config.root || 'data'
- }
- });
- this.callParent([config]);
- }
- }, function() {
- Ext.define('KeyValue', {
- extend: "Ext.data.Model",
- fields: [ 'key', 'value' ],
- idProperty: 'key'
- });
- Ext.define('KeyValuePendingDelete', {
- extend: "Ext.data.Model",
- fields: [ 'key', 'value', 'pending', 'delete' ],
- idProperty: 'key'
- });
- Ext.define('proxmox-tasks', {
- extend: 'Ext.data.Model',
- fields: [
- { name: 'starttime', type : 'date', dateFormat: 'timestamp' },
- { name: 'endtime', type : 'date', dateFormat: 'timestamp' },
- { name: 'pid', type: 'int' },
- 'node', 'upid', 'user', 'status', 'type', 'id'
- ],
- idProperty: 'upid'
- });
- Ext.define('proxmox-cluster-log', {
- extend: 'Ext.data.Model',
- fields: [
- { name: 'uid' , type: 'int' },
- { name: 'time', type : 'date', dateFormat: 'timestamp' },
- { name: 'pri', type: 'int' },
- { name: 'pid', type: 'int' },
- 'node', 'user', 'tag', 'msg',
- {
- name: 'id',
- convert: function(value, record) {
- var info = record.data;
- var text;
- if (value) {
- return value;
- }
- // compute unique ID
- return info.uid + ':' + info.node;
- }
- }
- ],
- idProperty: 'id'
- });
- });
- /* Extends the Ext.data.Store type
- * with startUpdate() and stopUpdate() methods
- * to refresh the store data in the background
- * Components using this store directly will flicker
- * due to the redisplay of the element ater 'config.interval' ms
- *
- * Note that you have to call yourself startUpdate() for the background load
- * to begin
- */
- Ext.define('Proxmox.data.UpdateStore', {
- extend: 'Ext.data.Store',
- alias: 'store.update',
- isStopped: true,
- autoStart: false,
- destroy: function() {
- var me = this;
- me.stopUpdate();
- me.callParent();
- },
- constructor: function(config) {
- var me = this;
- config = config || {};
- if (!config.interval) {
- config.interval = 3000;
- }
- if (!config.storeid) {
- throw "no storeid specified";
- }
- var load_task = new Ext.util.DelayedTask();
- var run_load_task = function() {
- if (me.isStopped) {
- return;
- }
- if (Proxmox.Utils.authOK()) {
- var start = new Date();
- me.load(function() {
- var runtime = (new Date()) - start;
- var interval = config.interval + runtime*2;
- load_task.delay(interval, run_load_task);
- });
- } else {
- load_task.delay(200, run_load_task);
- }
- };
- Ext.apply(config, {
- startUpdate: function() {
- me.isStopped = false;
- // run_load_task(); this makes problems with chrome
- load_task.delay(1, run_load_task);
- },
- stopUpdate: function() {
- me.isStopped = true;
- load_task.cancel();
- }
- });
- me.callParent([config]);
- me.load_task = load_task;
- if (me.autoStart) {
- me.startUpdate();
- }
- }
- });
- /*
- * The DiffStore is a in-memory store acting as proxy between a real store
- * instance and a component.
- * Its purpose is to redisplay the component *only* if the data has been changed
- * inside the real store, to avoid the annoying visual flickering of using
- * the real store directly.
- *
- * Implementation:
- * The DiffStore monitors via mon() the 'load' events sent by the real store.
- * On each 'load' event, the DiffStore compares its own content with the target
- * store (call to cond_add_item()) and then fires a 'refresh' event.
- * The 'refresh' event will automatically trigger a view refresh on the component
- * who binds to this store.
- */
- /* Config properties:
- * rstore: the realstore which will autorefresh its content from the API
- * Only works if rstore has a model and use 'idProperty'
- * sortAfterUpdate: sort the diffstore before rendering the view
- */
- Ext.define('Proxmox.data.DiffStore', {
- extend: 'Ext.data.Store',
- alias: 'store.diff',
- sortAfterUpdate: false,
- constructor: function(config) {
- var me = this;
- config = config || {};
- if (!config.rstore) {
- throw "no rstore specified";
- }
- if (!config.rstore.model) {
- throw "no rstore model specified";
- }
- var rstore = config.rstore;
- Ext.apply(config, {
- model: rstore.model,
- proxy: { type: 'memory' }
- });
- me.callParent([config]);
- var first_load = true;
- var cond_add_item = function(data, id) {
- var olditem = me.getById(id);
- if (olditem) {
- olditem.beginEdit();
- Ext.Array.each(me.model.prototype.fields, function(field) {
- if (olditem.data[field.name] !== data[field.name]) {
- olditem.set(field.name, data[field.name]);
- }
- });
- olditem.endEdit(true);
- olditem.commit();
- } else {
- var newrec = Ext.create(me.model, data);
- var pos = (me.appendAtStart && !first_load) ? 0 : me.data.length;
- me.insert(pos, newrec);
- }
- };
- var loadFn = function(s, records, success) {
- if (!success) {
- return;
- }
- me.suspendEvents();
- // getSource returns null if data is not filtered
- // if it is filtered it returns all records
- var allItems = me.getData().getSource() || me.getData();
- // remove vanished items
- allItems.each(function(olditem) {
- var item = rstore.getById(olditem.getId());
- if (!item) {
- me.remove(olditem);
- }
- });
- rstore.each(function(item) {
- cond_add_item(item.data, item.getId());
- });
- me.filter();
- if (me.sortAfterUpdate) {
- me.sort();
- }
- first_load = false;
- me.resumeEvents();
- me.fireEvent('refresh', me);
- me.fireEvent('datachanged', me);
- };
- if (rstore.isLoaded()) {
- // if store is already loaded,
- // insert items instantly
- loadFn(rstore, [], true);
- }
- me.mon(rstore, 'load', loadFn);
- }
- });
- /* This store encapsulates data items which are organized as an Array of key-values Objects
- * ie data[0] contains something like {key: "keyboard", value: "da"}
- *
- * Designed to work with the KeyValue model and the JsonObject data reader
- */
- Ext.define('Proxmox.data.ObjectStore', {
- extend: 'Proxmox.data.UpdateStore',
- getRecord: function() {
- var me = this;
- var record = Ext.create('Ext.data.Model');
- me.getData().each(function(item) {
- record.set(item.data.key, item.data.value);
- });
- record.commit(true);
- return record;
- },
- constructor: function(config) {
- var me = this;
- config = config || {};
- if (!config.storeid) {
- config.storeid = 'proxmox-store-' + (++Ext.idSeed);
- }
- Ext.applyIf(config, {
- model: 'KeyValue',
- proxy: {
- type: 'proxmox',
- url: config.url,
- extraParams: config.extraParams,
- reader: {
- type: 'jsonobject',
- rows: config.rows,
- readArray: config.readArray,
- rootProperty: config.root || 'data'
- }
- }
- });
- me.callParent([config]);
- }
- });
- /* Extends the Proxmox.data.UpdateStore type
- *
- *
- */
- Ext.define('Proxmox.data.RRDStore', {
- extend: 'Proxmox.data.UpdateStore',
- alias: 'store.proxmoxRRDStore',
- setRRDUrl: function(timeframe, cf) {
- var me = this;
- if (!timeframe) {
- timeframe = me.timeframe;
- }
- if (!cf) {
- cf = me.cf;
- }
- me.proxy.url = me.rrdurl + "?timeframe=" + timeframe + "&cf=" + cf;
- },
- proxy: {
- type: 'proxmox'
- },
- timeframe: 'hour',
- cf: 'AVERAGE',
- constructor: function(config) {
- var me = this;
- config = config || {};
- // set default interval to 30seconds
- if (!config.interval) {
- config.interval = 30000;
- }
- // set a new storeid
- if (!config.storeid) {
- config.storeid = 'rrdstore-' + (++Ext.idSeed);
- }
- // rrdurl is required
- if (!config.rrdurl) {
- throw "no rrdurl specified";
- }
- var stateid = 'proxmoxRRDTypeSelection';
- var sp = Ext.state.Manager.getProvider();
- var stateinit = sp.get(stateid);
- if (stateinit) {
- if(stateinit.timeframe !== me.timeframe || stateinit.cf !== me.rrdcffn){
- me.timeframe = stateinit.timeframe;
- me.rrdcffn = stateinit.cf;
- }
- }
- me.callParent([config]);
- me.setRRDUrl();
- me.mon(sp, 'statechange', function(prov, key, state){
- if (key === stateid) {
- if (state && state.id) {
- if (state.timeframe !== me.timeframe || state.cf !== me.cf) {
- me.timeframe = state.timeframe;
- me.cf = state.cf;
- me.setRRDUrl();
- me.reload();
- }
- }
- }
- });
- }
- });
- Ext.define('Timezone', {
- extend: 'Ext.data.Model',
- fields: ['zone']
- });
- Ext.define('Proxmox.data.TimezoneStore', {
- extend: 'Ext.data.Store',
- model: 'Timezone',
- data: [
- ['Africa/Abidjan'],
- ['Africa/Accra'],
- ['Africa/Addis_Ababa'],
- ['Africa/Algiers'],
- ['Africa/Asmara'],
- ['Africa/Bamako'],
- ['Africa/Bangui'],
- ['Africa/Banjul'],
- ['Africa/Bissau'],
- ['Africa/Blantyre'],
- ['Africa/Brazzaville'],
- ['Africa/Bujumbura'],
- ['Africa/Cairo'],
- ['Africa/Casablanca'],
- ['Africa/Ceuta'],
- ['Africa/Conakry'],
- ['Africa/Dakar'],
- ['Africa/Dar_es_Salaam'],
- ['Africa/Djibouti'],
- ['Africa/Douala'],
- ['Africa/El_Aaiun'],
- ['Africa/Freetown'],
- ['Africa/Gaborone'],
- ['Africa/Harare'],
- ['Africa/Johannesburg'],
- ['Africa/Kampala'],
- ['Africa/Khartoum'],
- ['Africa/Kigali'],
- ['Africa/Kinshasa'],
- ['Africa/Lagos'],
- ['Africa/Libreville'],
- ['Africa/Lome'],
- ['Africa/Luanda'],
- ['Africa/Lubumbashi'],
- ['Africa/Lusaka'],
- ['Africa/Malabo'],
- ['Africa/Maputo'],
- ['Africa/Maseru'],
- ['Africa/Mbabane'],
- ['Africa/Mogadishu'],
- ['Africa/Monrovia'],
- ['Africa/Nairobi'],
- ['Africa/Ndjamena'],
- ['Africa/Niamey'],
- ['Africa/Nouakchott'],
- ['Africa/Ouagadougou'],
- ['Africa/Porto-Novo'],
- ['Africa/Sao_Tome'],
- ['Africa/Tripoli'],
- ['Africa/Tunis'],
- ['Africa/Windhoek'],
- ['America/Adak'],
- ['America/Anchorage'],
- ['America/Anguilla'],
- ['America/Antigua'],
- ['America/Araguaina'],
- ['America/Argentina/Buenos_Aires'],
- ['America/Argentina/Catamarca'],
- ['America/Argentina/Cordoba'],
- ['America/Argentina/Jujuy'],
- ['America/Argentina/La_Rioja'],
- ['America/Argentina/Mendoza'],
- ['America/Argentina/Rio_Gallegos'],
- ['America/Argentina/Salta'],
- ['America/Argentina/San_Juan'],
- ['America/Argentina/San_Luis'],
- ['America/Argentina/Tucuman'],
- ['America/Argentina/Ushuaia'],
- ['America/Aruba'],
- ['America/Asuncion'],
- ['America/Atikokan'],
- ['America/Bahia'],
- ['America/Bahia_Banderas'],
- ['America/Barbados'],
- ['America/Belem'],
- ['America/Belize'],
- ['America/Blanc-Sablon'],
- ['America/Boa_Vista'],
- ['America/Bogota'],
- ['America/Boise'],
- ['America/Cambridge_Bay'],
- ['America/Campo_Grande'],
- ['America/Cancun'],
- ['America/Caracas'],
- ['America/Cayenne'],
- ['America/Cayman'],
- ['America/Chicago'],
- ['America/Chihuahua'],
- ['America/Costa_Rica'],
- ['America/Cuiaba'],
- ['America/Curacao'],
- ['America/Danmarkshavn'],
- ['America/Dawson'],
- ['America/Dawson_Creek'],
- ['America/Denver'],
- ['America/Detroit'],
- ['America/Dominica'],
- ['America/Edmonton'],
- ['America/Eirunepe'],
- ['America/El_Salvador'],
- ['America/Fortaleza'],
- ['America/Glace_Bay'],
- ['America/Godthab'],
- ['America/Goose_Bay'],
- ['America/Grand_Turk'],
- ['America/Grenada'],
- ['America/Guadeloupe'],
- ['America/Guatemala'],
- ['America/Guayaquil'],
- ['America/Guyana'],
- ['America/Halifax'],
- ['America/Havana'],
- ['America/Hermosillo'],
- ['America/Indiana/Indianapolis'],
- ['America/Indiana/Knox'],
- ['America/Indiana/Marengo'],
- ['America/Indiana/Petersburg'],
- ['America/Indiana/Tell_City'],
- ['America/Indiana/Vevay'],
- ['America/Indiana/Vincennes'],
- ['America/Indiana/Winamac'],
- ['America/Inuvik'],
- ['America/Iqaluit'],
- ['America/Jamaica'],
- ['America/Juneau'],
- ['America/Kentucky/Louisville'],
- ['America/Kentucky/Monticello'],
- ['America/La_Paz'],
- ['America/Lima'],
- ['America/Los_Angeles'],
- ['America/Maceio'],
- ['America/Managua'],
- ['America/Manaus'],
- ['America/Marigot'],
- ['America/Martinique'],
- ['America/Matamoros'],
- ['America/Mazatlan'],
- ['America/Menominee'],
- ['America/Merida'],
- ['America/Mexico_City'],
- ['America/Miquelon'],
- ['America/Moncton'],
- ['America/Monterrey'],
- ['America/Montevideo'],
- ['America/Montreal'],
- ['America/Montserrat'],
- ['America/Nassau'],
- ['America/New_York'],
- ['America/Nipigon'],
- ['America/Nome'],
- ['America/Noronha'],
- ['America/North_Dakota/Center'],
- ['America/North_Dakota/New_Salem'],
- ['America/Ojinaga'],
- ['America/Panama'],
- ['America/Pangnirtung'],
- ['America/Paramaribo'],
- ['America/Phoenix'],
- ['America/Port-au-Prince'],
- ['America/Port_of_Spain'],
- ['America/Porto_Velho'],
- ['America/Puerto_Rico'],
- ['America/Rainy_River'],
- ['America/Rankin_Inlet'],
- ['America/Recife'],
- ['America/Regina'],
- ['America/Resolute'],
- ['America/Rio_Branco'],
- ['America/Santa_Isabel'],
- ['America/Santarem'],
- ['America/Santiago'],
- ['America/Santo_Domingo'],
- ['America/Sao_Paulo'],
- ['America/Scoresbysund'],
- ['America/Shiprock'],
- ['America/St_Barthelemy'],
- ['America/St_Johns'],
- ['America/St_Kitts'],
- ['America/St_Lucia'],
- ['America/St_Thomas'],
- ['America/St_Vincent'],
- ['America/Swift_Current'],
- ['America/Tegucigalpa'],
- ['America/Thule'],
- ['America/Thunder_Bay'],
- ['America/Tijuana'],
- ['America/Toronto'],
- ['America/Tortola'],
- ['America/Vancouver'],
- ['America/Whitehorse'],
- ['America/Winnipeg'],
- ['America/Yakutat'],
- ['America/Yellowknife'],
- ['Antarctica/Casey'],
- ['Antarctica/Davis'],
- ['Antarctica/DumontDUrville'],
- ['Antarctica/Macquarie'],
- ['Antarctica/Mawson'],
- ['Antarctica/McMurdo'],
- ['Antarctica/Palmer'],
- ['Antarctica/Rothera'],
- ['Antarctica/South_Pole'],
- ['Antarctica/Syowa'],
- ['Antarctica/Vostok'],
- ['Arctic/Longyearbyen'],
- ['Asia/Aden'],
- ['Asia/Almaty'],
- ['Asia/Amman'],
- ['Asia/Anadyr'],
- ['Asia/Aqtau'],
- ['Asia/Aqtobe'],
- ['Asia/Ashgabat'],
- ['Asia/Baghdad'],
- ['Asia/Bahrain'],
- ['Asia/Baku'],
- ['Asia/Bangkok'],
- ['Asia/Beirut'],
- ['Asia/Bishkek'],
- ['Asia/Brunei'],
- ['Asia/Choibalsan'],
- ['Asia/Chongqing'],
- ['Asia/Colombo'],
- ['Asia/Damascus'],
- ['Asia/Dhaka'],
- ['Asia/Dili'],
- ['Asia/Dubai'],
- ['Asia/Dushanbe'],
- ['Asia/Gaza'],
- ['Asia/Harbin'],
- ['Asia/Ho_Chi_Minh'],
- ['Asia/Hong_Kong'],
- ['Asia/Hovd'],
- ['Asia/Irkutsk'],
- ['Asia/Jakarta'],
- ['Asia/Jayapura'],
- ['Asia/Jerusalem'],
- ['Asia/Kabul'],
- ['Asia/Kamchatka'],
- ['Asia/Karachi'],
- ['Asia/Kashgar'],
- ['Asia/Kathmandu'],
- ['Asia/Kolkata'],
- ['Asia/Krasnoyarsk'],
- ['Asia/Kuala_Lumpur'],
- ['Asia/Kuching'],
- ['Asia/Kuwait'],
- ['Asia/Macau'],
- ['Asia/Magadan'],
- ['Asia/Makassar'],
- ['Asia/Manila'],
- ['Asia/Muscat'],
- ['Asia/Nicosia'],
- ['Asia/Novokuznetsk'],
- ['Asia/Novosibirsk'],
- ['Asia/Omsk'],
- ['Asia/Oral'],
- ['Asia/Phnom_Penh'],
- ['Asia/Pontianak'],
- ['Asia/Pyongyang'],
- ['Asia/Qatar'],
- ['Asia/Qyzylorda'],
- ['Asia/Rangoon'],
- ['Asia/Riyadh'],
- ['Asia/Sakhalin'],
- ['Asia/Samarkand'],
- ['Asia/Seoul'],
- ['Asia/Shanghai'],
- ['Asia/Singapore'],
- ['Asia/Taipei'],
- ['Asia/Tashkent'],
- ['Asia/Tbilisi'],
- ['Asia/Tehran'],
- ['Asia/Thimphu'],
- ['Asia/Tokyo'],
- ['Asia/Ulaanbaatar'],
- ['Asia/Urumqi'],
- ['Asia/Vientiane'],
- ['Asia/Vladivostok'],
- ['Asia/Yakutsk'],
- ['Asia/Yekaterinburg'],
- ['Asia/Yerevan'],
- ['Atlantic/Azores'],
- ['Atlantic/Bermuda'],
- ['Atlantic/Canary'],
- ['Atlantic/Cape_Verde'],
- ['Atlantic/Faroe'],
- ['Atlantic/Madeira'],
- ['Atlantic/Reykjavik'],
- ['Atlantic/South_Georgia'],
- ['Atlantic/St_Helena'],
- ['Atlantic/Stanley'],
- ['Australia/Adelaide'],
- ['Australia/Brisbane'],
- ['Australia/Broken_Hill'],
- ['Australia/Currie'],
- ['Australia/Darwin'],
- ['Australia/Eucla'],
- ['Australia/Hobart'],
- ['Australia/Lindeman'],
- ['Australia/Lord_Howe'],
- ['Australia/Melbourne'],
- ['Australia/Perth'],
- ['Australia/Sydney'],
- ['Europe/Amsterdam'],
- ['Europe/Andorra'],
- ['Europe/Athens'],
- ['Europe/Belgrade'],
- ['Europe/Berlin'],
- ['Europe/Bratislava'],
- ['Europe/Brussels'],
- ['Europe/Bucharest'],
- ['Europe/Budapest'],
- ['Europe/Chisinau'],
- ['Europe/Copenhagen'],
- ['Europe/Dublin'],
- ['Europe/Gibraltar'],
- ['Europe/Guernsey'],
- ['Europe/Helsinki'],
- ['Europe/Isle_of_Man'],
- ['Europe/Istanbul'],
- ['Europe/Jersey'],
- ['Europe/Kaliningrad'],
- ['Europe/Kiev'],
- ['Europe/Lisbon'],
- ['Europe/Ljubljana'],
- ['Europe/London'],
- ['Europe/Luxembourg'],
- ['Europe/Madrid'],
- ['Europe/Malta'],
- ['Europe/Mariehamn'],
- ['Europe/Minsk'],
- ['Europe/Monaco'],
- ['Europe/Moscow'],
- ['Europe/Oslo'],
- ['Europe/Paris'],
- ['Europe/Podgorica'],
- ['Europe/Prague'],
- ['Europe/Riga'],
- ['Europe/Rome'],
- ['Europe/Samara'],
- ['Europe/San_Marino'],
- ['Europe/Sarajevo'],
- ['Europe/Simferopol'],
- ['Europe/Skopje'],
- ['Europe/Sofia'],
- ['Europe/Stockholm'],
- ['Europe/Tallinn'],
- ['Europe/Tirane'],
- ['Europe/Uzhgorod'],
- ['Europe/Vaduz'],
- ['Europe/Vatican'],
- ['Europe/Vienna'],
- ['Europe/Vilnius'],
- ['Europe/Volgograd'],
- ['Europe/Warsaw'],
- ['Europe/Zagreb'],
- ['Europe/Zaporozhye'],
- ['Europe/Zurich'],
- ['Indian/Antananarivo'],
- ['Indian/Chagos'],
- ['Indian/Christmas'],
- ['Indian/Cocos'],
- ['Indian/Comoro'],
- ['Indian/Kerguelen'],
- ['Indian/Mahe'],
- ['Indian/Maldives'],
- ['Indian/Mauritius'],
- ['Indian/Mayotte'],
- ['Indian/Reunion'],
- ['Pacific/Apia'],
- ['Pacific/Auckland'],
- ['Pacific/Chatham'],
- ['Pacific/Chuuk'],
- ['Pacific/Easter'],
- ['Pacific/Efate'],
- ['Pacific/Enderbury'],
- ['Pacific/Fakaofo'],
- ['Pacific/Fiji'],
- ['Pacific/Funafuti'],
- ['Pacific/Galapagos'],
- ['Pacific/Gambier'],
- ['Pacific/Guadalcanal'],
- ['Pacific/Guam'],
- ['Pacific/Honolulu'],
- ['Pacific/Johnston'],
- ['Pacific/Kiritimati'],
- ['Pacific/Kosrae'],
- ['Pacific/Kwajalein'],
- ['Pacific/Majuro'],
- ['Pacific/Marquesas'],
- ['Pacific/Midway'],
- ['Pacific/Nauru'],
- ['Pacific/Niue'],
- ['Pacific/Norfolk'],
- ['Pacific/Noumea'],
- ['Pacific/Pago_Pago'],
- ['Pacific/Palau'],
- ['Pacific/Pitcairn'],
- ['Pacific/Pohnpei'],
- ['Pacific/Port_Moresby'],
- ['Pacific/Rarotonga'],
- ['Pacific/Saipan'],
- ['Pacific/Tahiti'],
- ['Pacific/Tarawa'],
- ['Pacific/Tongatapu'],
- ['Pacific/Wake'],
- ['Pacific/Wallis']
- ]
- });
- Ext.define('Proxmox.form.field.Integer',{
- extend: 'Ext.form.field.Number',
- alias: 'widget.proxmoxintegerfield',
- config: {
- deleteEmpty: false
- },
- allowDecimals: false,
- allowExponential: false,
- step: 1,
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue && !me.isFileUpload()) {
- val = me.getSubmitValue();
- if (val !== undefined && val !== null && val !== '') {
- data = {};
- data[me.getName()] = val;
- } else if (me.getDeleteEmpty()) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- }
- });
- Ext.define('Proxmox.form.field.Textfield', {
- extend: 'Ext.form.field.Text',
- alias: ['widget.proxmoxtextfield'],
- config: {
- skipEmptyText: true,
- deleteEmpty: false,
- },
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue && !me.isFileUpload()) {
- val = me.getSubmitValue();
- if (val !== null) {
- data = {};
- data[me.getName()] = val;
- } else if (me.getDeleteEmpty()) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- },
- getSubmitValue: function() {
- var me = this;
- var value = this.processRawValue(this.getRawValue());
- if (value !== '') {
- return value;
- }
- return me.getSkipEmptyText() ? null: value;
- }
- });
- Ext.define('Proxmox.DateTimeField', {
- extend: 'Ext.form.FieldContainer',
- xtype: 'promxoxDateTimeField',
- layout: 'hbox',
- referenceHolder: true,
- submitFormat: 'U',
- getValue: function() {
- var me = this;
- var d = me.lookupReference('dateentry').getValue();
- if (d === undefined || d === null) { return null; }
- var t = me.lookupReference('timeentry').getValue();
- if (t === undefined || t === null) { return null; }
- var offset = (t.getHours()*3600+t.getMinutes()*60)*1000;
- return new Date(d.getTime() + offset);
- },
- getSubmitValue: function() {
- var me = this;
- var format = me.submitFormat;
- var value = me.getValue();
- return value ? Ext.Date.format(value, format) : null;
- },
- items: [
- {
- xtype: 'datefield',
- editable: false,
- reference: 'dateentry',
- flex: 1,
- format: 'Y-m-d'
- },
- {
- xtype: 'timefield',
- reference: 'timeentry',
- format: 'H:i',
- width: 80,
- value: '00:00',
- increment: 60
- }
- ],
- initComponent: function() {
- var me = this;
- me.callParent();
- var value = me.value || new Date();
- me.lookupReference('dateentry').setValue(value);
- me.lookupReference('timeentry').setValue(value);
- me.relayEvents(me.lookupReference('dateentry'), ['change']);
- me.relayEvents(me.lookupReference('timeentry'), ['change']);
- }
- });
- Ext.define('Proxmox.form.Checkbox', {
- extend: 'Ext.form.field.Checkbox',
- alias: ['widget.proxmoxcheckbox'],
- config: {
- defaultValue: undefined,
- deleteDefaultValue: false,
- deleteEmpty: false
- },
- inputValue: '1',
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue) {
- val = me.getSubmitValue();
- if (val !== null) {
- data = {};
- if ((val == me.getDefaultValue()) && me.getDeleteDefaultValue()) {
- data['delete'] = me.getName();
- } else {
- data[me.getName()] = val;
- }
- } else if (me.getDeleteEmpty()) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- },
- // also accept integer 1 as true
- setRawValue: function(value) {
- var me = this;
- if (value === 1) {
- me.callParent([true]);
- } else {
- me.callParent([value]);
- }
- }
- });
- /* Key-Value ComboBox
- *
- * config properties:
- * comboItems: an array of Key - Value pairs
- * deleteEmpty: if set to true (default), an empty value received from the
- * comboBox will reset the property to its default value
- */
- Ext.define('Proxmox.form.KVComboBox', {
- extend: 'Ext.form.field.ComboBox',
- alias: 'widget.proxmoxKVComboBox',
- config: {
- deleteEmpty: true
- },
- comboItems: undefined,
- displayField: 'value',
- valueField: 'key',
- queryMode: 'local',
- // overide framework function to implement deleteEmpty behaviour
- getSubmitData: function() {
- var me = this,
- data = null,
- val;
- if (!me.disabled && me.submitValue) {
- val = me.getSubmitValue();
- if (val !== null && val !== '' && val !== '__default__') {
- data = {};
- data[me.getName()] = val;
- } else if (me.getDeleteEmpty()) {
- data = {};
- data['delete'] = me.getName();
- }
- }
- return data;
- },
- validator: function(val) {
- var me = this;
- if (me.editable || val === null || val === '') {
- return true;
- }
- if (me.store.getCount() > 0) {
- var values = me.multiSelect ? val.split(me.delimiter) : [val];
- var items = me.store.getData().collect('value', 'data');
- if (Ext.Array.every(values, function(value) {
- return Ext.Array.contains(items, value);
- })) {
- return true;
- }
- }
- // returns a boolean or string
- /*jslint confusion: true */
- return "value '" + val + "' not allowed!";
- },
- initComponent: function() {
- var me = this;
- me.store = Ext.create('Ext.data.ArrayStore', {
- model: 'KeyValue',
- data : me.comboItems
- });
- if (me.initialConfig.editable === undefined) {
- me.editable = false;
- }
- me.callParent();
- }
- });
- Ext.define('Proxmox.form.LanguageSelector', {
- extend: 'Proxmox.form.KVComboBox',
- xtype: 'proxmoxLanguageSelector',
- comboItems: Proxmox.Utils.language_array()
- });
- /*
- * ComboGrid component: a ComboBox where the dropdown menu (the
- * "Picker") is a Grid with Rows and Columns expects a listConfig
- * object with a columns property roughly based on the GridPicker from
- * https://www.sencha.com/forum/showthread.php?299909
- *
- */
- Ext.define('Proxmox.form.ComboGrid', {
- extend: 'Ext.form.field.ComboBox',
- alias: ['widget.proxmoxComboGrid'],
- // this value is used as default value after load()
- preferredValue: undefined,
- // hack: allow to select empty value
- // seems extjs does not allow that when 'editable == false'
- onKeyUp: function(e, t) {
- var me = this;
- var key = e.getKey();
- if (!me.editable && me.allowBlank && !me.multiSelect &&
- (key == e.BACKSPACE || key == e.DELETE)) {
- me.setValue('');
- }
- me.callParent(arguments);
- },
- // needed to trigger onKeyUp etc.
- enableKeyEvents: true,
- editable: false,
- // override ExtJS method
- // if the field has multiSelect enabled, the store is not loaded, and
- // the displayfield == valuefield, it saves the rawvalue as an array
- // but the getRawValue method is only defined in the textfield class
- // (which has not to deal with arrays) an returns the string in the
- // field (not an array)
- //
- // so if we have multiselect enabled, return the rawValue (which
- // should be an array) and else we do callParent so
- // it should not impact any other use of the class
- getRawValue: function() {
- var me = this;
- if (me.multiSelect) {
- return me.rawValue;
- } else {
- return me.callParent();
- }
- },
- // override ExtJS protected method
- onBindStore: function(store, initial) {
- var me = this,
- picker = me.picker,
- extraKeySpec,
- valueCollectionConfig;
- // We're being bound, not unbound...
- if (store) {
- // If store was created from a 2 dimensional array with generated field names 'field1' and 'field2'
- if (store.autoCreated) {
- me.queryMode = 'local';
- me.valueField = me.displayField = 'field1';
- if (!store.expanded) {
- me.displayField = 'field2';
- }
- // displayTpl config will need regenerating with the autogenerated displayField name 'field1'
- me.setDisplayTpl(null);
- }
- if (!Ext.isDefined(me.valueField)) {
- me.valueField = me.displayField;
- }
- // Add a byValue index to the store so that we can efficiently look up records by the value field
- // when setValue passes string value(s).
- // The two indices (Ext.util.CollectionKeys) are configured unique: false, so that if duplicate keys
- // are found, they are all returned by the get call.
- // This is so that findByText and findByValue are able to return the *FIRST* matching value. By default,
- // if unique is true, CollectionKey keeps the *last* matching value.
- extraKeySpec = {
- byValue: {
- rootProperty: 'data',
- unique: false
- }
- };
- extraKeySpec.byValue.property = me.valueField;
- store.setExtraKeys(extraKeySpec);
- if (me.displayField === me.valueField) {
- store.byText = store.byValue;
- } else {
- extraKeySpec.byText = {
- rootProperty: 'data',
- unique: false
- };
- extraKeySpec.byText.property = me.displayField;
- store.setExtraKeys(extraKeySpec);
- }
- // We hold a collection of the values which have been selected, keyed by this field's valueField.
- // This collection also functions as the selected items collection for the BoundList's selection model
- valueCollectionConfig = {
- rootProperty: 'data',
- extraKeys: {
- byInternalId: {
- property: 'internalId'
- },
- byValue: {
- property: me.valueField,
- rootProperty: 'data'
- }
- },
- // Whenever this collection is changed by anyone, whether by this field adding to it,
- // or the BoundList operating, we must refresh our value.
- listeners: {
- beginupdate: me.onValueCollectionBeginUpdate,
- endupdate: me.onValueCollectionEndUpdate,
- scope: me
- }
- };
- // This becomes our collection of selected records for the Field.
- me.valueCollection = new Ext.util.Collection(valueCollectionConfig);
- // We use the selected Collection as our value collection and the basis
- // for rendering the tag list.
- //proxmox override: since the picker is represented by a grid panel,
- // we changed here the selection to RowModel
- me.pickerSelectionModel = new Ext.selection.RowModel({
- mode: me.multiSelect ? 'SIMPLE' : 'SINGLE',
- // There are situations when a row is selected on mousedown but then the mouse is dragged to another row
- // and released. In these situations, the event target for the click event won't be the row where the mouse
- // was released but the boundview. The view will then determine that it should fire a container click, and
- // the DataViewModel will then deselect all prior selections. Setting `deselectOnContainerClick` here will
- // prevent the model from deselecting.
- deselectOnContainerClick: false,
- enableInitialSelection: false,
- pruneRemoved: false,
- selected: me.valueCollection,
- store: store,
- listeners: {
- scope: me,
- lastselectedchanged: me.updateBindSelection
- }
- });
- if (!initial) {
- me.resetToDefault();
- }
- if (picker) {
- picker.setSelectionModel(me.pickerSelectionModel);
- if (picker.getStore() !== store) {
- picker.bindStore(store);
- }
- }
- }
- },
- // copied from ComboBox
- createPicker: function() {
- var me = this;
- var picker;
- var pickerCfg = Ext.apply({
- // proxmox overrides: display a grid for selection
- xtype: 'gridpanel',
- id: me.pickerId,
- pickerField: me,
- floating: true,
- hidden: true,
- store: me.store,
- displayField: me.displayField,
- preserveScrollOnRefresh: true,
- pageSize: me.pageSize,
- tpl: me.tpl,
- selModel: me.pickerSelectionModel,
- focusOnToFront: false
- }, me.listConfig, me.defaultListConfig);
- picker = me.picker || Ext.widget(pickerCfg);
- if (picker.getStore() !== me.store) {
- picker.bindStore(me.store);
- }
- if (me.pageSize) {
- picker.pagingToolbar.on('beforechange', me.onPageChange, me);
- }
- // proxmox overrides: pass missing method in gridPanel to its view
- picker.refresh = function() {
- picker.getSelectionModel().select(me.valueCollection.getRange());
- picker.getView().refresh();
- };
- picker.getNodeByRecord = function() {
- picker.getView().getNodeByRecord(arguments);
- };
- // We limit the height of the picker to fit in the space above
- // or below this field unless the picker has its own ideas about that.
- if (!picker.initialConfig.maxHeight) {
- picker.on({
- beforeshow: me.onBeforePickerShow,
- scope: me
- });
- }
- picker.getSelectionModel().on({
- beforeselect: me.onBeforeSelect,
- beforedeselect: me.onBeforeDeselect,
- focuschange: me.onFocusChange,
- selectionChange: function (sm, selectedRecords) {
- var me = this;
- if (selectedRecords.length) {
- me.setValue(selectedRecords);
- me.fireEvent('select', me, selectedRecords);
- }
- },
- scope: me
- });
- // hack for extjs6
- // when the clicked item is the same as the previously selected,
- // it does not select the item
- // instead we hide the picker
- if (!me.multiSelect) {
- picker.on('itemclick', function (sm,record) {
- if (picker.getSelection()[0] === record) {
- picker.hide();
- }
- });
- }
- // when our store is not yet loaded, we increase
- // the height of the gridpanel, so that we can see
- // the loading mask
- //
- // we save the minheight to reset it after the load
- picker.on('show', function() {
- if (me.enableLoadMask) {
- me.savedMinHeight = picker.getMinHeight();
- picker.setMinHeight(100);
- }
- });
- picker.getNavigationModel().navigateOnSpace = false;
- return picker;
- },
- initComponent: function() {
- var me = this;
- Ext.apply(me, {
- queryMode: 'local',
- matchFieldWidth: false
- });
- Ext.applyIf(me, { value: ''}); // hack: avoid ExtJS validate() bug
- Ext.applyIf(me.listConfig, { width: 400 });
- me.callParent();
- // Create the picker at an early stage, so it is available to store the previous selection
- if (!me.picker) {
- me.createPicker();
- }
- if (me.editable) {
- // The trigger.picker causes first a focus event on the field then
- // toggles the selection picker. Thus skip expanding in this case,
- // else our focus listner expands and the picker.trigger then
- // collapses it directly afterwards.
- Ext.override(me.triggers.picker, {
- onMouseDown : function (e) {
- // copied "should we focus" check from Ext.form.trigger.Trigger
- if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) {
- me.skip_expand_on_focus = true;
- }
- this.callParent(arguments);
- }
- });
- me.on("focus", function(me) {
- if (!me.isExpanded && !me.skip_expand_on_focus) {
- me.expand();
- }
- me.skip_expand_on_focus = false;
- });
- }
- me.mon(me.store, 'beforeload', function() {
- if (!me.isDisabled()) {
- me.enableLoadMask = true;
- }
- });
- // hack: autoSelect does not work
- me.mon(me.store, 'load', function(store, r, success, o) {
- if (success) {
- me.clearInvalid();
- if (me.enableLoadMask) {
- delete me.enableLoadMask;
- // if the picker exists,
- // we reset its minheight to the saved var/0
- // we have to update the layout, otherwise the height
- // gets not recalculated
- if (me.picker) {
- me.picker.setMinHeight(me.savedMinHeight || 0);
- delete me.savedMinHeight;
- me.picker.updateLayout();
- }
- }
- var def = me.getValue() || me.preferredValue;
- if (def) {
- me.setValue(def, true); // sync with grid
- }
- var found = false;
- if (def) {
- if (Ext.isArray(def)) {
- Ext.Array.each(def, function(v) {
- if (store.findRecord(me.valueField, v)) {
- found = true;
- return false; // break
- }
- });
- } else {
- found = store.findRecord(me.valueField, def);
- }
- }
- if (!found) {
- var rec = me.store.first();
- if (me.autoSelect && rec && rec.data) {
- def = rec.data[me.valueField];
- me.setValue(def, true);
- } else {
- me.setValue(me.editable ? def : '', true);
- }
- }
- }
- });
- }
- });
- Ext.define('Proxmox.form.RRDTypeSelector', {
- extend: 'Ext.form.field.ComboBox',
- alias: ['widget.proxmoxRRDTypeSelector'],
- displayField: 'text',
- valueField: 'id',
- editable: false,
- queryMode: 'local',
- value: 'hour',
- stateEvents: [ 'select' ],
- stateful: true,
- stateId: 'proxmoxRRDTypeSelection',
- store: {
- type: 'array',
- fields: [ 'id', 'timeframe', 'cf', 'text' ],
- data : [
- [ 'hour', 'hour', 'AVERAGE',
- gettext('Hour') + ' (' + gettext('average') +')' ],
- [ 'hourmax', 'hour', 'MAX',
- gettext('Hour') + ' (' + gettext('maximum') + ')' ],
- [ 'day', 'day', 'AVERAGE',
- gettext('Day') + ' (' + gettext('average') + ')' ],
- [ 'daymax', 'day', 'MAX',
- gettext('Day') + ' (' + gettext('maximum') + ')' ],
- [ 'week', 'week', 'AVERAGE',
- gettext('Week') + ' (' + gettext('average') + ')' ],
- [ 'weekmax', 'week', 'MAX',
- gettext('Week') + ' (' + gettext('maximum') + ')' ],
- [ 'month', 'month', 'AVERAGE',
- gettext('Month') + ' (' + gettext('average') + ')' ],
- [ 'monthmax', 'month', 'MAX',
- gettext('Month') + ' (' + gettext('maximum') + ')' ],
- [ 'year', 'year', 'AVERAGE',
- gettext('Year') + ' (' + gettext('average') + ')' ],
- [ 'yearmax', 'year', 'MAX',
- gettext('Year') + ' (' + gettext('maximum') + ')' ]
- ]
- },
- // save current selection in the state Provider so RRDView can read it
- getState: function() {
- var ind = this.getStore().findExact('id', this.getValue());
- var rec = this.getStore().getAt(ind);
- if (!rec) {
- return;
- }
- return {
- id: rec.data.id,
- timeframe: rec.data.timeframe,
- cf: rec.data.cf
- };
- },
- // set selection based on last saved state
- applyState : function(state) {
- if (state && state.id) {
- this.setValue(state.id);
- }
- }
- });
- Ext.define('Proxmox.form.BondModeSelector', {
- extend: 'Proxmox.form.KVComboBox',
- alias: ['widget.bondModeSelector'],
- openvswitch: false,
- initComponent: function() {
- var me = this;
- if (me.openvswitch) {
- me.comboItems = [
- ['active-backup', 'active-backup'],
- ['balance-slb', 'balance-slb'],
- ['lacp-balance-slb', 'LACP (balance-slb)'],
- ['lacp-balance-tcp', 'LACP (balance-tcp)']
- ];
- } else {
- me.comboItems = [
- ['balance-rr', 'balance-rr'],
- ['active-backup', 'active-backup'],
- ['balance-xor', 'balance-xor'],
- ['broadcast', 'broadcast'],
- ['802.3ad', 'LACP (802.3ad)'],
- ['balance-tlb', 'balance-tlb'],
- ['balance-alb', 'balance-alb']
- ];
- }
- me.callParent();
- }
- });
- Ext.define('Proxmox.form.BondPolicySelector', {
- extend: 'Proxmox.form.KVComboBox',
- alias: ['widget.bondPolicySelector'],
- comboItems: [
- ['layer2', 'layer2'],
- ['layer2+3', 'layer2+3'],
- ['layer3+4', 'layer3+4']
- ]
- });
- /* Button features:
- * - observe selection changes to enable/disable the button using enableFn()
- * - pop up confirmation dialog using confirmMsg()
- */
- Ext.define('Proxmox.button.Button', {
- extend: 'Ext.button.Button',
- alias: 'widget.proxmoxButton',
- // the selection model to observe
- selModel: undefined,
- // if 'false' handler will not be called (button disabled)
- enableFn: function(record) { },
- // function(record) or text
- confirmMsg: false,
- // take special care in confirm box (select no as default).
- dangerous: false,
- initComponent: function() {
- /*jslint confusion: true */
- var me = this;
- if (me.handler) {
- // Note: me.realHandler may be a string (see named scopes)
- var realHandler = me.handler;
- me.handler = function(button, event) {
- var rec, msg;
- if (me.selModel) {
- rec = me.selModel.getSelection()[0];
- if (!rec || (me.enableFn(rec) === false)) {
- return;
- }
- }
- if (me.confirmMsg) {
- msg = me.confirmMsg;
- if (Ext.isFunction(me.confirmMsg)) {
- msg = me.confirmMsg(rec);
- }
- Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1;
- Ext.Msg.show({
- title: gettext('Confirm'),
- icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION,
- msg: msg,
- buttons: Ext.Msg.YESNO,
- defaultFocus: me.dangerous ? 'no' : 'yes',
- callback: function(btn) {
- if (btn !== 'yes') {
- return;
- }
- Ext.callback(realHandler, me.scope, [button, event, rec], 0, me);
- }
- });
- } else {
- Ext.callback(realHandler, me.scope, [button, event, rec], 0, me);
- }
- };
- }
- me.callParent();
- var grid;
- if (!me.selModel && me.selModel !== null) {
- grid = me.up('grid');
- if (grid && grid.selModel) {
- me.selModel = grid.selModel;
- }
- }
- if (me.waitMsgTarget === true) {
- grid = me.up('grid');
- if (grid) {
- me.waitMsgTarget = grid;
- } else {
- throw "unable to find waitMsgTarget";
- }
- }
- if (me.selModel) {
- me.mon(me.selModel, "selectionchange", function() {
- var rec = me.selModel.getSelection()[0];
- if (!rec || (me.enableFn(rec) === false)) {
- me.setDisabled(true);
- } else {
- me.setDisabled(false);
- }
- });
- }
- }
- });
- Ext.define('Proxmox.button.StdRemoveButton', {
- extend: 'Proxmox.button.Button',
- alias: 'widget.proxmoxStdRemoveButton',
- text: gettext('Remove'),
- disabled: true,
- config: {
- baseurl: undefined
- },
- getUrl: function(rec) {
- var me = this;
- return me.baseurl + '/' + rec.getId();
- },
- // also works with names scopes
- callback: function(options, success, response) {},
- getRecordName: function(rec) { return rec.getId() },
- confirmMsg: function (rec) {
- var me = this;
- var name = me.getRecordName(rec);
- return Ext.String.format(
- gettext('Are you sure you want to remove entry {0}'),
- "'" + name + "'");
- },
- handler: function(btn, event, rec) {
- var me = this;
- Proxmox.Utils.API2Request({
- url: me.getUrl(rec),
- method: 'DELETE',
- waitMsgTarget: me.waitMsgTarget,
- callback: function(options, success, response) {
- Ext.callback(me.callback, me.scope, [options, success, response], 0, me);
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
- /* help button pointing to an online documentation
- for components contained in a modal window
- */
- /*global
- proxmoxOnlineHelpInfo
- */
- Ext.define('Proxmox.button.Help', {
- extend: 'Ext.button.Button',
- xtype: 'proxmoxHelpButton',
- text: gettext('Help'),
- // make help button less flashy by styling it like toolbar buttons
- iconCls: ' x-btn-icon-el-default-toolbar-small fa fa-question-circle',
- cls: 'x-btn-default-toolbar-small proxmox-inline-button',
- hidden: true,
- listenToGlobalEvent: true,
- controller: {
- xclass: 'Ext.app.ViewController',
- listen: {
- global: {
- proxmoxShowHelp: 'onProxmoxShowHelp',
- proxmoxHideHelp: 'onProxmoxHideHelp'
- }
- },
- onProxmoxShowHelp: function(helpLink) {
- var me = this.getView();
- if (me.listenToGlobalEvent === true) {
- me.setOnlineHelp(helpLink);
- me.show();
- }
- },
- onProxmoxHideHelp: function() {
- var me = this.getView();
- if (me.listenToGlobalEvent === true) {
- me.hide();
- }
- }
- },
- getOnlineHelpInfo: function (ref) {
- var helpMap;
- if (typeof proxmoxOnlineHelpInfo !== 'undefined') {
- helpMap = proxmoxOnlineHelpInfo;
- } else if (typeof pveOnlineHelpInfo !== 'undefined') {
- // be backward compatible with older pve-doc-generators
- helpMap = pveOnlineHelpInfo;
- } else {
- throw "no global OnlineHelpInfo map declared";
- }
- return helpMap[ref];
- },
- // this sets the link and the tooltip text
- setOnlineHelp:function(blockid) {
- var me = this;
- var info = me.getOnlineHelpInfo(blockid);
- if (info) {
- me.onlineHelp = blockid;
- var title = info.title;
- if (info.subtitle) {
- title += ' - ' + info.subtitle;
- }
- me.setTooltip(title);
- }
- },
- // helper to set the onlineHelp via a config object
- setHelpConfig: function(config) {
- var me = this;
- me.setOnlineHelp(config.onlineHelp);
- },
- handler: function() {
- var me = this;
- var docsURI;
- if (me.onlineHelp) {
- var info = me.getOnlineHelpInfo(me.onlineHelp);
- if (info) {
- docsURI = window.location.origin + info.link;
- }
- }
- if (docsURI) {
- window.open(docsURI);
- } else {
- Ext.Msg.alert(gettext('Help'), gettext('No Help available'));
- }
- },
- initComponent: function() {
- /*jslint confusion: true */
- var me = this;
- me.callParent();
- if (me.onlineHelp) {
- me.setOnlineHelp(me.onlineHelp); // set tooltip
- }
- }
- });
- /* Renders a list of key values objets
- mandatory config parameters:
- rows: an object container where each propery is a key-value object we want to render
- var rows = {
- keyboard: {
- header: gettext('Keyboard Layout'),
- editor: 'Your.KeyboardEdit',
- required: true
- },
- optional:
- disabled: setting this parameter to true will disable selection and focus on the
- proxmoxObjectGrid as well as greying out input elements.
- Useful for a readonly tabular display
- */
- Ext.define('Proxmox.grid.ObjectGrid', {
- extend: 'Ext.grid.GridPanel',
- alias: ['widget.proxmoxObjectGrid'],
- disabled: false,
- hideHeaders: true,
- monStoreErrors: false,
- add_combobox_row: function(name, text, opts) {
- var me = this;
- opts = opts || {};
- me.rows = me.rows || {};
- me.rows[name] = {
- required: true,
- defaultValue: opts.defaultValue,
- header: text,
- renderer: opts.renderer,
- editor: {
- xtype: 'proxmoxWindowEdit',
- subject: text,
- fieldDefaults: {
- labelWidth: opts.labelWidth || 100
- },
- items: {
- xtype: 'proxmoxKVComboBox',
- name: name,
- comboItems: opts.comboItems,
- value: opts.defaultValue,
- deleteEmpty: opts.deleteEmpty ? true : false,
- emptyText: opts.defaultValue,
- labelWidth: Proxmox.Utils.compute_min_label_width(
- text, opts.labelWidth),
- fieldLabel: text
- }
- }
- };
- },
- add_text_row: function(name, text, opts) {
- var me = this;
- opts = opts || {};
- me.rows = me.rows || {};
- me.rows[name] = {
- required: true,
- defaultValue: opts.defaultValue,
- header: text,
- renderer: opts.renderer,
- editor: {
- xtype: 'proxmoxWindowEdit',
- subject: text,
- fieldDefaults: {
- labelWidth: opts.labelWidth || 100
- },
- items: {
- xtype: 'proxmoxtextfield',
- name: name,
- deleteEmpty: opts.deleteEmpty ? true : false,
- emptyText: opts.defaultValue,
- labelWidth: Proxmox.Utils.compute_min_label_width(
- text, opts.labelWidth),
- vtype: opts.vtype,
- fieldLabel: text
- }
- }
- };
- },
- add_boolean_row: function(name, text, opts) {
- var me = this;
- opts = opts || {};
- me.rows = me.rows || {};
- me.rows[name] = {
- required: true,
- defaultValue: opts.defaultValue || 0,
- header: text,
- renderer: opts.renderer || Proxmox.Utils.format_boolean,
- editor: {
- xtype: 'proxmoxWindowEdit',
- subject: text,
- fieldDefaults: {
- labelWidth: opts.labelWidth || 100
- },
- items: {
- xtype: 'proxmoxcheckbox',
- name: name,
- uncheckedValue: 0,
- defaultValue: opts.defaultValue || 0,
- checked: opts.defaultValue ? true : false,
- deleteDefaultValue: opts.deleteDefaultValue ? true : false,
- labelWidth: Proxmox.Utils.compute_min_label_width(
- text, opts.labelWidth),
- fieldLabel: text
- }
- }
- };
- },
- add_integer_row: function(name, text, opts) {
- var me = this;
- opts = opts || {}
- me.rows = me.rows || {};
- me.rows[name] = {
- required: true,
- defaultValue: opts.defaultValue,
- header: text,
- renderer: opts.renderer,
- editor: {
- xtype: 'proxmoxWindowEdit',
- subject: text,
- fieldDefaults: {
- labelWidth: opts.labelWidth || 100
- },
- items: {
- xtype: 'proxmoxintegerfield',
- name: name,
- minValue: opts.minValue,
- maxValue: opts.maxValue,
- emptyText: gettext('Default'),
- deleteEmpty: opts.deleteEmpty ? true : false,
- value: opts.defaultValue,
- labelWidth: Proxmox.Utils.compute_min_label_width(
- text, opts.labelWidth),
- fieldLabel: text
- }
- }
- };
- },
- editorConfig: {}, // default config passed to editor
- run_editor: function() {
- var me = this;
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var rows = me.rows;
- var rowdef = rows[rec.data.key];
- if (!rowdef.editor) {
- return;
- }
- var win;
- var config;
- if (Ext.isString(rowdef.editor)) {
- config = Ext.apply({
- confid: rec.data.key,
- }, me.editorConfig);
- win = Ext.create(rowdef.editor, config);
- } else {
- config = Ext.apply({
- confid: rec.data.key,
- }, me.editorConfig);
- Ext.apply(config, rowdef.editor);
- win = Ext.createWidget(rowdef.editor.xtype, config);
- win.load();
- }
- win.show();
- win.on('destroy', me.reload, me);
- },
- reload: function() {
- var me = this;
- me.rstore.load();
- },
- getObjectValue: function(key, defaultValue) {
- var me = this;
- var rec = me.store.getById(key);
- if (rec) {
- return rec.data.value;
- }
- return defaultValue;
- },
- renderKey: function(key, metaData, record, rowIndex, colIndex, store) {
- var me = this;
- var rows = me.rows;
- var rowdef = (rows && rows[key]) ? rows[key] : {};
- return rowdef.header || key;
- },
- renderValue: function(value, metaData, record, rowIndex, colIndex, store) {
- var me = this;
- var rows = me.rows;
- var key = record.data.key;
- var rowdef = (rows && rows[key]) ? rows[key] : {};
- var renderer = rowdef.renderer;
- if (renderer) {
- return renderer(value, metaData, record, rowIndex, colIndex, store);
- }
- return value;
- },
- listeners: {
- itemkeydown: function(view, record, item, index, e) {
- if (e.getKey() === e.ENTER) {
- this.pressedIndex = index;
- }
- },
- itemkeyup: function(view, record, item, index, e) {
- if (e.getKey() === e.ENTER && index == this.pressedIndex) {
- this.run_editor();
- }
- this.pressedIndex = undefined;
- }
- },
- initComponent : function() {
- var me = this;
- var rows = me.rows;
- if (!me.rstore) {
- if (!me.url) {
- throw "no url specified";
- }
- me.rstore = Ext.create('Proxmox.data.ObjectStore', {
- url: me.url,
- interval: me.interval,
- extraParams: me.extraParams,
- rows: me.rows
- });
- }
- var rstore = me.rstore;
- var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore,
- sorters: [],
- filters: []
- });
- if (rows) {
- Ext.Object.each(rows, function(key, rowdef) {
- if (Ext.isDefined(rowdef.defaultValue)) {
- store.add({ key: key, value: rowdef.defaultValue });
- } else if (rowdef.required) {
- store.add({ key: key, value: undefined });
- }
- });
- }
- if (me.sorterFn) {
- store.sorters.add(Ext.create('Ext.util.Sorter', {
- sorterFn: me.sorterFn
- }));
- }
- store.filters.add(Ext.create('Ext.util.Filter', {
- filterFn: function(item) {
- if (rows) {
- var rowdef = rows[item.data.key];
- if (!rowdef || (rowdef.visible === false)) {
- return false;
- }
- }
- return true;
- }
- }));
- Proxmox.Utils.monStoreErrors(me, rstore);
- Ext.applyIf(me, {
- store: store,
- stateful: false,
- columns: [
- {
- header: gettext('Name'),
- width: me.cwidth1 || 200,
- dataIndex: 'key',
- renderer: me.renderKey
- },
- {
- flex: 1,
- header: gettext('Value'),
- dataIndex: 'value',
- renderer: me.renderValue
- }
- ]
- });
- me.callParent();
- if (me.monStoreErrors) {
- Proxmox.Utils.monStoreErrors(me, me.store);
- }
- }
- });
- Ext.define('Proxmox.grid.PendingObjectGrid', {
- extend: 'Proxmox.grid.ObjectGrid',
- alias: ['widget.proxmoxPendingObjectGrid'],
- getObjectValue: function(key, defaultValue, pending) {
- var me = this;
- var rec = me.store.getById(key);
- if (rec) {
- var value = rec.data.value;
- if (pending) {
- if (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') {
- value = rec.data.pending;
- } else if (rec.data['delete'] === 1) {
- value = defaultValue;
- }
- }
- if (Ext.isDefined(value) && (value !== '')) {
- return value;
- } else {
- return defaultValue;
- }
- }
- return defaultValue;
- },
- hasPendingChanges: function(key) {
- var me = this;
- var rows = me.rows;
- var rowdef = (rows && rows[key]) ? rows[key] : {};
- var keys = rowdef.multiKey || [ key ];
- var pending = false;
- Ext.Array.each(keys, function(k) {
- var rec = me.store.getById(k);
- if (rec && rec.data && (
- (Ext.isDefined(rec.data.pending) && rec.data.pending !== '') ||
- rec.data['delete'] === 1
- )) {
- pending = true;
- return false; // break
- }
- });
- return pending;
- },
- renderValue: function(value, metaData, record, rowIndex, colIndex, store) {
- var me = this;
- var rows = me.rows;
- var key = record.data.key;
- var rowdef = (rows && rows[key]) ? rows[key] : {};
- var renderer = rowdef.renderer;
- var current = '';
- var pendingdelete = '';
- var pending = '';
- if (renderer) {
- current = renderer(value, metaData, record, rowIndex, colIndex, store, false);
- if (me.hasPendingChanges(key)) {
- pending = renderer(record.data.pending, metaData, record, rowIndex, colIndex, store, true);
- }
- if (pending == current) {
- pending = undefined;
- }
- } else {
- current = value || '';
- pending = record.data.pending;
- }
- if (record.data['delete']) {
- var delete_all = true;
- if (rowdef.multiKey) {
- Ext.Array.each(rowdef.multiKey, function(k) {
- var rec = me.store.getById(k);
- if (rec && rec.data && rec.data['delete'] !== 1) {
- delete_all = false;
- return false; // break
- }
- });
- }
- if (delete_all) {
- pending = '<div style="text-decoration: line-through;">'+ current +'</div>';
- }
- }
- if (pending) {
- return current + '<div style="color:red">' + pending + '</div>';
- } else {
- return current;
- }
- },
- initComponent : function() {
- var me = this;
- var rows = me.rows;
- if (!me.rstore) {
- if (!me.url) {
- throw "no url specified";
- }
- me.rstore = Ext.create('Proxmox.data.ObjectStore', {
- model: 'KeyValuePendingDelete',
- readArray: true,
- url: me.url,
- interval: me.interval,
- extraParams: me.extraParams,
- rows: me.rows
- });
- }
- me.callParent();
- }
- });
- Ext.define('Proxmox.panel.InputPanel', {
- extend: 'Ext.panel.Panel',
- alias: ['widget.inputpanel'],
- listeners: {
- activate: function() {
- // notify owning container that it should display a help button
- if (this.onlineHelp) {
- Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp);
- }
- },
- deactivate: function() {
- if (this.onlineHelp) {
- Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp);
- }
- }
- },
- border: false,
- // override this with an URL to a relevant chapter of the pve manual
- // setting this will display a help button in our parent panel
- onlineHelp: undefined,
- // will be set if the inputpanel has advanced items
- hasAdvanced: false,
- // if the panel has advanced items,
- // this will determine if they are shown by default
- showAdvanced: false,
- // overwrite this to modify submit data
- onGetValues: function(values) {
- return values;
- },
- getValues: function(dirtyOnly) {
- var me = this;
- if (Ext.isFunction(me.onGetValues)) {
- dirtyOnly = false;
- }
- var values = {};
- Ext.Array.each(me.query('[isFormField]'), function(field) {
- if (!dirtyOnly || field.isDirty()) {
- Proxmox.Utils.assemble_field_data(values, field.getSubmitData());
- }
- });
- return me.onGetValues(values);
- },
- setAdvancedVisible: function(visible) {
- var me = this;
- var advItems = me.getComponent('advancedContainer');
- if (advItems) {
- advItems.setVisible(visible);
- }
- },
- setValues: function(values) {
- var me = this;
- var form = me.up('form');
- Ext.iterate(values, function(fieldId, val) {
- var field = me.query('[isFormField][name=' + fieldId + ']')[0];
- if (field) {
- field.setValue(val);
- if (form.trackResetOnLoad) {
- field.resetOriginalValue();
- }
- }
- });
- },
- initComponent: function() {
- var me = this;
- var items;
- if (me.items) {
- me.columns = 1;
- items = [
- {
- columnWidth: 1,
- layout: 'anchor',
- items: me.items
- }
- ];
- me.items = undefined;
- } else if (me.column4) {
- me.columns = 4;
- items = [
- {
- columnWidth: 0.25,
- padding: '0 10 0 0',
- layout: 'anchor',
- items: me.column1
- },
- {
- columnWidth: 0.25,
- padding: '0 10 0 0',
- layout: 'anchor',
- items: me.column2
- },
- {
- columnWidth: 0.25,
- padding: '0 10 0 0',
- layout: 'anchor',
- items: me.column3
- },
- {
- columnWidth: 0.25,
- padding: '0 0 0 10',
- layout: 'anchor',
- items: me.column4
- }
- ];
- if (me.columnB) {
- items.push({
- columnWidth: 1,
- padding: '10 0 0 0',
- layout: 'anchor',
- items: me.columnB
- });
- }
- } else if (me.column1) {
- me.columns = 2;
- items = [
- {
- columnWidth: 0.5,
- padding: '0 10 0 0',
- layout: 'anchor',
- items: me.column1
- },
- {
- columnWidth: 0.5,
- padding: '0 0 0 10',
- layout: 'anchor',
- items: me.column2 || [] // allow empty column
- }
- ];
- if (me.columnB) {
- items.push({
- columnWidth: 1,
- padding: '10 0 0 0',
- layout: 'anchor',
- items: me.columnB
- });
- }
- } else {
- throw "unsupported config";
- }
- var advItems;
- if (me.advancedItems) {
- advItems = [
- {
- columnWidth: 1,
- layout: 'anchor',
- items: me.advancedItems
- }
- ];
- me.advancedItems = undefined;
- } else if (me.advancedColumn1) {
- advItems = [
- {
- columnWidth: 0.5,
- padding: '0 10 0 0',
- layout: 'anchor',
- items: me.advancedColumn1
- },
- {
- columnWidth: 0.5,
- padding: '0 0 0 10',
- layout: 'anchor',
- items: me.advancedColumn2 || [] // allow empty column
- }
- ];
- me.advancedColumn1 = undefined;
- me.advancedColumn2 = undefined;
- if (me.advancedColumnB) {
- advItems.push({
- columnWidth: 1,
- padding: '10 0 0 0',
- layout: 'anchor',
- items: me.advancedColumnB
- });
- me.advancedColumnB = undefined;
- }
- }
- if (advItems) {
- me.hasAdvanced = true;
- advItems.unshift({
- columnWidth: 1,
- xtype: 'box',
- hidden: false,
- border: true,
- autoEl: {
- tag: 'hr'
- }
- });
- items.push({
- columnWidth: 1,
- xtype: 'container',
- itemId: 'advancedContainer',
- hidden: !me.showAdvanced,
- layout: 'column',
- defaults: {
- border: false
- },
- items: advItems
- });
- }
- if (me.useFieldContainer) {
- Ext.apply(me, {
- layout: 'fit',
- items: Ext.apply(me.useFieldContainer, {
- layout: 'column',
- defaultType: 'container',
- items: items
- })
- });
- } else {
- Ext.apply(me, {
- layout: 'column',
- defaultType: 'container',
- items: items
- });
- }
- me.callParent();
- }
- });
- /*
- * Display log entries in a panel with scrollbar
- * The log entries are automatically refreshed via a background task,
- * with newest entries comming at the bottom
- */
- Ext.define('Proxmox.panel.LogView', {
- extend: 'Ext.panel.Panel',
- alias: ['widget.proxmoxLogView'],
- pageSize: 500,
- lineHeight: 16,
- viewInfo: undefined,
- scrollToEnd: true,
- autoScroll: true,
- layout: 'auto',
- bodyPadding: 5,
- getMaxDown: function(scrollToEnd) {
- var me = this;
- var target = me.getTargetEl();
- var dom = target.dom;
- if (scrollToEnd) {
- dom.scrollTop = dom.scrollHeight - dom.clientHeight;
- }
- var maxDown = dom.scrollHeight - dom.clientHeight -
- dom.scrollTop;
- return maxDown;
- },
- updateView: function(start, end, total, text) {
- var me = this;
- if (me.destroyed) { // return if element is not there anymore
- return;
- }
- var el = me.dataCmp.el;
- if (me.viewInfo && me.viewInfo.start === start &&
- me.viewInfo.end === end && me.viewInfo.total === total &&
- me.viewInfo.textLength === text.length) {
- return; // same content
- }
- var maxDown = me.getMaxDown();
- var scrollToEnd = (maxDown <= 0) && me.scrollToEnd;
- el.setStyle('padding-top', (start*me.lineHeight).toString() + 'px');
- el.update(text);
- me.dataCmp.setHeight(total*me.lineHeight);
- if (scrollToEnd) {
- me.getMaxDown(true);
- }
- me.viewInfo = {
- start: start,
- end: end,
- total: total,
- textLength: text.length
- };
- },
- doAttemptLoad: function(start) {
- var me = this;
- var req_params = {
- start: start,
- limit: me.pageSize
- };
- if (me.log_select_timespan) {
- // always show log until the end of the selected day
- req_params.until = Ext.Date.format(me.until_date, 'Y-m-d') + ' 23:59:59';
- req_params.since = Ext.Date.format(me.since_date, 'Y-m-d');
- }
- Proxmox.Utils.API2Request({
- url: me.url,
- params: req_params,
- method: 'GET',
- success: function(response) {
- Proxmox.Utils.setErrorMask(me, false);
- var list = response.result.data;
- var total = response.result.total;
- var first = 0, last = 0;
- var text = '';
- Ext.Array.each(list, function(item) {
- if (!first|| item.n < first) {
- first = item.n;
- }
- if (!last || item.n > last) {
- last = item.n;
- }
- text = text + Ext.htmlEncode(item.t) + "<br>";
- });
- if (first && last && total) {
- me.updateView(first -1 , last -1, total, text);
- } else {
- me.updateView(0, 0, 0, '');
- }
- },
- failure: function(response) {
- var msg = response.htmlStatus;
- Proxmox.Utils.setErrorMask(me, msg);
- }
- });
- },
- attemptLoad: function(start) {
- var me = this;
- if (!me.loadTask) {
- me.loadTask = Ext.create('Ext.util.DelayedTask', me.doAttemptLoad, me, []);
- }
- me.loadTask.delay(200, me.doAttemptLoad, me, [start]);
- },
- requestUpdate: function(top, force) {
- var me = this;
- if (top === undefined) {
- var target = me.getTargetEl();
- top = target.dom.scrollTop;
- }
- var viewStart = parseInt((top / me.lineHeight) - 1, 10);
- if (viewStart < 0) {
- viewStart = 0;
- }
- var viewEnd = parseInt(((top + me.getHeight())/ me.lineHeight) + 1, 10);
- var info = me.viewInfo;
- if (info && !force) {
- if (viewStart >= info.start && viewEnd <= info.end) {
- return;
- }
- }
- var line = parseInt((top / me.lineHeight) - (me.pageSize / 2) + 10, 10);
- if (line < 0) {
- line = 0;
- }
- me.attemptLoad(line);
- },
- afterRender: function() {
- var me = this;
- me.callParent(arguments);
- Ext.Function.defer(function() {
- var target = me.getTargetEl();
- target.on('scroll', function(e) {
- me.requestUpdate();
- });
- me.requestUpdate(0);
- }, 20);
- },
- initComponent : function() {
- /*jslint confusion: true */
- var me = this;
- if (!me.url) {
- throw "no url specified";
- }
- // show logs from today back to 3 days ago per default
- me.until_date = new Date();
- me.since_date = new Date();
- me.since_date.setDate(me.until_date.getDate() - 3);
- me.dataCmp = Ext.create('Ext.Component', {
- style: 'font:normal 11px tahoma, arial, verdana, sans-serif;' +
- 'line-height: ' + me.lineHeight.toString() + 'px; white-space: pre;'
- });
- me.task = Ext.TaskManager.start({
- run: function() {
- if (!me.isVisible() || !me.scrollToEnd || !me.viewInfo) {
- return;
- }
- var maxDown = me.getMaxDown();
- if (maxDown > 0) {
- return;
- }
- me.requestUpdate(undefined, true);
- },
- interval: 1000
- });
- Ext.apply(me, {
- items: me.dataCmp,
- listeners: {
- destroy: function() {
- Ext.TaskManager.stop(me.task);
- }
- }
- });
- if (me.log_select_timespan) {
- me.tbar = ['->','Since: ',
- {
- xtype: 'datefield',
- maxValue: me.until_date,
- value: me.since_date,
- name: 'since_date',
- format: 'Y-m-d',
- listeners: {
- select: function(field, date) {
- me.since_date_selected = date;
- var until_field = field.up().down('field[name=until_date]');
- if (date > until_field.getValue()) {
- until_field.setValue(date);
- }
- }
- }
- },
- 'Until: ',
- {
- xtype: 'datefield',
- maxValue: me.until_date,
- value: me.until_date,
- name: 'until_date',
- format: 'Y-m-d',
- listeners: {
- select: function(field, date) {
- var since_field = field.up().down('field[name=since_date]');
- if (date < since_field.getValue()) {
- since_field.setValue(date);
- }
- }
- }
- },
- {
- xtype: 'button',
- text: 'Update',
- handler: function() {
- var until_field = me.down('field[name=until_date]');
- var since_field = me.down('field[name=since_date]');
- if (until_field.getValue() < since_field.getValue()) {
- Ext.Msg.alert('Error',
- 'Since date must be less equal than Until date.');
- until_field.setValue(me.until_date);
- since_field.setValue(me.since_date);
- } else {
- me.until_date = until_field.getValue();
- me.since_date = since_field.getValue();
- me.requestUpdate();
- }
- }
- }
- ];
- }
- me.callParent();
- }
- });
- Ext.define('Proxmox.widget.RRDChart', {
- extend: 'Ext.chart.CartesianChart',
- alias: 'widget.proxmoxRRDChart',
- unit: undefined, // bytes, bytespersecond, percent
- controller: {
- xclass: 'Ext.app.ViewController',
- convertToUnits: function(value) {
- var units = ['', 'k','M','G','T', 'P'];
- var si = 0;
- while(value >= 1000 && si < (units.length -1)){
- value = value / 1000;
- si++;
- }
- // javascript floating point weirdness
- value = Ext.Number.correctFloat(value);
- // limit to 2 decimal points
- value = Ext.util.Format.number(value, "0.##");
- return value.toString() + " " + units[si];
- },
- leftAxisRenderer: function(axis, label, layoutContext) {
- var me = this;
- return me.convertToUnits(label);
- },
- onSeriesTooltipRender: function(tooltip, record, item) {
- var me = this.getView();
- var suffix = '';
- if (me.unit === 'percent') {
- suffix = '%';
- } else if (me.unit === 'bytes') {
- suffix = 'B';
- } else if (me.unit === 'bytespersecond') {
- suffix = 'B/s';
- }
- var prefix = item.field;
- if (me.fieldTitles && me.fieldTitles[me.fields.indexOf(item.field)]) {
- prefix = me.fieldTitles[me.fields.indexOf(item.field)];
- }
- tooltip.setHtml(prefix + ': ' + this.convertToUnits(record.get(item.field)) + suffix +
- '<br>' + new Date(record.get('time')));
- },
- onAfterAnimation: function(chart, eopts) {
- // if the undobuton is disabled,
- // disable our tool
- var ourUndoZoomButton = chart.tools[0];
- var undoButton = chart.interactions[0].getUndoButton();
- ourUndoZoomButton.setDisabled(undoButton.isDisabled());
- }
- },
- width: 770,
- height: 300,
- animation: false,
- interactions: [{
- type: 'crosszoom'
- }],
- axes: [{
- type: 'numeric',
- position: 'left',
- grid: true,
- renderer: 'leftAxisRenderer',
- //renderer: function(axis, label) { return label; },
- minimum: 0
- }, {
- type: 'time',
- position: 'bottom',
- grid: true,
- fields: ['time']
- }],
- legend: {
- docked: 'bottom'
- },
- listeners: {
- animationend: 'onAfterAnimation'
- },
- initComponent: function() {
- var me = this;
- var series = {};
- if (!me.store) {
- throw "cannot work without store";
- }
- if (!me.fields) {
- throw "cannot work without fields";
- }
- me.callParent();
- // add correct label for left axis
- var axisTitle = "";
- if (me.unit === 'percent') {
- axisTitle = "%";
- } else if (me.unit === 'bytes') {
- axisTitle = "Bytes";
- } else if (me.unit === 'bytespersecond') {
- axisTitle = "Bytes/s";
- } else if (me.fieldTitles && me.fieldTitles.length === 1) {
- axisTitle = me.fieldTitles[0];
- } else if (me.fields.length === 1) {
- axisTitle = me.fields[0];
- }
- me.axes[0].setTitle(axisTitle);
- if (!me.noTool) {
- me.addTool([{
- type: 'minus',
- disabled: true,
- tooltip: gettext('Undo Zoom'),
- handler: function(){
- var undoButton = me.interactions[0].getUndoButton();
- if (undoButton.handler) {
- undoButton.handler();
- }
- }
- },{
- type: 'restore',
- tooltip: gettext('Toggle Legend'),
- handler: function(){
- if (me.legend) {
- me.legend.setVisible(!me.legend.isVisible());
- }
- }
- }]);
- }
- // add a series for each field we get
- me.fields.forEach(function(item, index){
- var title = item;
- if (me.fieldTitles && me.fieldTitles[index]) {
- title = me.fieldTitles[index];
- }
- me.addSeries(Ext.apply(
- {
- type: 'line',
- xField: 'time',
- yField: item,
- title: title,
- fill: true,
- style: {
- lineWidth: 1.5,
- opacity: 0.60
- },
- marker: {
- opacity: 0,
- scaling: 0.01,
- fx: {
- duration: 200,
- easing: 'easeOut'
- }
- },
- highlightCfg: {
- opacity: 1,
- scaling: 1.5
- },
- tooltip: {
- trackMouse: true,
- renderer: 'onSeriesTooltipRender'
- }
- },
- me.seriesConfig
- ));
- });
- // enable animation after the store is loaded
- me.store.onAfter('load', function() {
- me.setAnimation(true);
- }, this, {single: true});
- }
- });
- Ext.define('Proxmox.panel.GaugeWidget', {
- extend: 'Ext.panel.Panel',
- alias: 'widget.proxmoxGauge',
- defaults: {
- style: {
- 'text-align':'center'
- }
- },
- items: [
- {
- xtype: 'box',
- itemId: 'title',
- data: {
- title: ''
- },
- tpl: '<h3>{title}</h3>'
- },
- {
- xtype: 'polar',
- height: 120,
- border: false,
- itemId: 'chart',
- series: [{
- type: 'gauge',
- value: 0,
- colors: ['#f5f5f5'],
- sectors: [0],
- donut: 90,
- needleLength: 100,
- totalAngle: Math.PI
- }],
- sprites: [{
- id: 'valueSprite',
- type: 'text',
- text: '',
- textAlign: 'center',
- textBaseline: 'bottom',
- x: 125,
- y: 110,
- fontSize: 30
- }]
- },
- {
- xtype: 'box',
- itemId: 'text'
- }
- ],
- header: false,
- border: false,
- warningThreshold: 0.6,
- criticalThreshold: 0.9,
- warningColor: '#fc0',
- criticalColor: '#FF6C59',
- defaultColor: '#c2ddf2',
- backgroundColor: '#f5f5f5',
- initialValue: 0,
- updateValue: function(value, text) {
- var me = this;
- var color = me.defaultColor;
- var attr = {};
- if (value >= me.criticalThreshold) {
- color = me.criticalColor;
- } else if (value >= me.warningThreshold) {
- color = me.warningColor;
- }
- me.chart.series[0].setColors([color, me.backgroundColor]);
- me.chart.series[0].setValue(value*100);
- me.valueSprite.setText(' '+(value*100).toFixed(0) + '%');
- attr.x = me.chart.getWidth()/2;
- attr.y = me.chart.getHeight()-20;
- if (me.spriteFontSize) {
- attr.fontSize = me.spriteFontSize;
- }
- me.valueSprite.setAttributes(attr, true);
- if (text !== undefined) {
- me.text.setHtml(text);
- }
- },
- initComponent: function() {
- var me = this;
- me.callParent();
- if (me.title) {
- me.getComponent('title').update({title: me.title});
- }
- me.text = me.getComponent('text');
- me.chart = me.getComponent('chart');
- me.valueSprite = me.chart.getSurface('chart').get('valueSprite');
- }
- });
- // fixme: how can we avoid those lint errors?
- /*jslint confusion: true */
- Ext.define('Proxmox.window.Edit', {
- extend: 'Ext.window.Window',
- alias: 'widget.proxmoxWindowEdit',
- // autoLoad trigger a load() after component creation
- autoLoad: false,
- resizable: false,
- // use this tio atimatically generate a title like
- // Create: <subject>
- subject: undefined,
- // set isCreate to true if you want a Create button (instead
- // OK and RESET)
- isCreate: false,
- // set to true if you want an Add button (instead of Create)
- isAdd: false,
- // set to true if you want an Remove button (instead of Create)
- isRemove: false,
- // custom submitText
- submitText: undefined,
- backgroundDelay: 0,
- // needed for finding the reference to submitbutton
- // because we do not have a controller
- referenceHolder: true,
- defaultButton: 'submitbutton',
- // finds the first form field
- defaultFocus: 'field[disabled=false][hidden=false]',
- showProgress: false,
- showTaskViewer: false,
- // gets called if we have a progress bar or taskview and it detected that
- // the task finished. function(success)
- taskDone: Ext.emptyFn,
- // gets called when the api call is finished, right at the beginning
- // function(success, response, options)
- apiCallDone: Ext.emptyFn,
- // assign a reference from docs, to add a help button docked to the
- // bottom of the window. If undefined we magically fall back to the
- // onlineHelp of our first item, if set.
- onlineHelp: undefined,
- isValid: function() {
- var me = this;
- var form = me.formPanel.getForm();
- return form.isValid();
- },
- getValues: function(dirtyOnly) {
- var me = this;
- var values = {};
- var form = me.formPanel.getForm();
- form.getFields().each(function(field) {
- if (!field.up('inputpanel') && (!dirtyOnly || field.isDirty())) {
- Proxmox.Utils.assemble_field_data(values, field.getSubmitData());
- }
- });
- Ext.Array.each(me.query('inputpanel'), function(panel) {
- Proxmox.Utils.assemble_field_data(values, panel.getValues(dirtyOnly));
- });
- return values;
- },
- setValues: function(values) {
- var me = this;
- var form = me.formPanel.getForm();
- Ext.iterate(values, function(fieldId, val) {
- var field = form.findField(fieldId);
- if (field && !field.up('inputpanel')) {
- field.setValue(val);
- if (form.trackResetOnLoad) {
- field.resetOriginalValue();
- }
- }
- });
- Ext.Array.each(me.query('inputpanel'), function(panel) {
- panel.setValues(values);
- });
- },
- submit: function() {
- var me = this;
- var form = me.formPanel.getForm();
- var values = me.getValues();
- Ext.Object.each(values, function(name, val) {
- if (values.hasOwnProperty(name)) {
- if (Ext.isArray(val) && !val.length) {
- values[name] = '';
- }
- }
- });
- if (me.digest) {
- values.digest = me.digest;
- }
- if (me.backgroundDelay) {
- values.background_delay = me.backgroundDelay;
- }
- var url = me.url;
- if (me.method === 'DELETE') {
- url = url + "?" + Ext.Object.toQueryString(values);
- values = undefined;
- }
- Proxmox.Utils.API2Request({
- url: url,
- waitMsgTarget: me,
- method: me.method || (me.backgroundDelay ? 'POST' : 'PUT'),
- params: values,
- failure: function(response, options) {
- me.apiCallDone(false, response, options);
- if (response.result && response.result.errors) {
- form.markInvalid(response.result.errors);
- }
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- var hasProgressBar = (me.backgroundDelay || me.showProgress || me.showTaskViewer) &&
- response.result.data ? true : false;
- me.apiCallDone(true, response, options);
- if (hasProgressBar) {
- // stay around so we can trigger our close events
- // when background action is completed
- me.hide();
- var upid = response.result.data;
- var viewerClass = me.showTaskViewer ? 'Viewer' : 'Progress';
- var win = Ext.create('Proxmox.window.Task' + viewerClass, {
- upid: upid,
- taskDone: me.taskDone,
- listeners: {
- destroy: function () {
- me.close();
- }
- }
- });
- win.show();
- } else {
- me.close();
- }
- }
- });
- },
- load: function(options) {
- var me = this;
- var form = me.formPanel.getForm();
- options = options || {};
- var newopts = Ext.apply({
- waitMsgTarget: me
- }, options);
- var createWrapper = function(successFn) {
- Ext.apply(newopts, {
- url: me.url,
- method: 'GET',
- success: function(response, opts) {
- form.clearInvalid();
- me.digest = response.result.data.digest;
- if (successFn) {
- successFn(response, opts);
- } else {
- me.setValues(response.result.data);
- }
- // hack: fix ExtJS bug
- Ext.Array.each(me.query('radiofield'), function(f) {
- f.resetOriginalValue();
- });
- },
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() {
- me.close();
- });
- }
- });
- };
- createWrapper(options.success);
- Proxmox.Utils.API2Request(newopts);
- },
- initComponent : function() {
- var me = this;
- if (!me.url) {
- throw "no url specified";
- }
- if (me.create) {throw "deprecated parameter, use isCreate";}
- var items = Ext.isArray(me.items) ? me.items : [ me.items ];
- me.items = undefined;
- me.formPanel = Ext.create('Ext.form.Panel', {
- url: me.url,
- method: me.method || 'PUT',
- trackResetOnLoad: true,
- bodyPadding: 10,
- border: false,
- defaults: Ext.apply({}, me.defaults, {
- border: false
- }),
- fieldDefaults: Ext.apply({}, me.fieldDefaults, {
- labelWidth: 100,
- anchor: '100%'
- }),
- items: items
- });
- var inputPanel = me.formPanel.down('inputpanel');
- var form = me.formPanel.getForm();
- var submitText;
- if (me.isCreate) {
- if (me.submitText) {
- submitText = me.submitText;
- } else if (me.isAdd) {
- submitText = gettext('Add');
- } else if (me.isRemove) {
- submitText = gettext('Remove');
- } else {
- submitText = gettext('Create');
- }
- } else {
- submitText = me.submitText || gettext('OK');
- }
- var submitBtn = Ext.create('Ext.Button', {
- reference: 'submitbutton',
- text: submitText,
- disabled: !me.isCreate,
- handler: function() {
- me.submit();
- }
- });
- var resetBtn = Ext.create('Ext.Button', {
- text: 'Reset',
- disabled: true,
- handler: function(){
- form.reset();
- }
- });
- var set_button_status = function() {
- var valid = form.isValid();
- var dirty = form.isDirty();
- submitBtn.setDisabled(!valid || !(dirty || me.isCreate));
- resetBtn.setDisabled(!dirty);
- if (inputPanel && inputPanel.hasAdvanced) {
- // we want to show the advanced options
- // as soon as some of it is not valid
- var advancedItems = me.down('#advancedContainer').query('field');
- var valid = true;
- advancedItems.forEach(function(field) {
- if (!field.isValid()) {
- valid = false;
- }
- });
- if (!valid) {
- inputPanel.setAdvancedVisible(true);
- me.down('#advancedcb').setValue(true);
- }
- }
- };
- form.on('dirtychange', set_button_status);
- form.on('validitychange', set_button_status);
- var colwidth = 300;
- if (me.fieldDefaults && me.fieldDefaults.labelWidth) {
- colwidth += me.fieldDefaults.labelWidth - 100;
- }
- var twoColumn = inputPanel &&
- (inputPanel.column1 || inputPanel.column2);
- if (me.subject && !me.title) {
- me.title = Proxmox.Utils.dialog_title(me.subject, me.isCreate, me.isAdd);
- }
- if (me.isCreate) {
- me.buttons = [ submitBtn ] ;
- } else {
- me.buttons = [ submitBtn, resetBtn ];
- }
- if (inputPanel && inputPanel.hasAdvanced) {
- var sp = Ext.state.Manager.getProvider();
- var advchecked = sp.get('proxmox-advanced-cb');
- inputPanel.setAdvancedVisible(advchecked);
- me.buttons.unshift(
- {
- xtype: 'proxmoxcheckbox',
- itemId: 'advancedcb',
- boxLabelAlign: 'before',
- boxLabel: gettext('Advanced'),
- stateId: 'proxmox-advanced-cb',
- value: advchecked,
- listeners: {
- change: function(cb, val) {
- inputPanel.setAdvancedVisible(val);
- sp.set('proxmox-advanced-cb', val);
- }
- }
- }
- );
- }
- var onlineHelp = me.onlineHelp;
- if (!onlineHelp && inputPanel && inputPanel.onlineHelp) {
- onlineHelp = inputPanel.onlineHelp;
- }
- if (onlineHelp) {
- var helpButton = Ext.create('Proxmox.button.Help');
- me.buttons.unshift(helpButton, '->');
- Ext.GlobalEvents.fireEvent('proxmoxShowHelp', onlineHelp);
- }
- Ext.applyIf(me, {
- modal: true,
- width: twoColumn ? colwidth*2 : colwidth,
- border: false,
- items: [ me.formPanel ]
- });
- me.callParent();
- // always mark invalid fields
- me.on('afterlayout', function() {
- // on touch devices, the isValid function
- // triggers a layout, which triggers an isValid
- // and so on
- // to prevent this we disable the layouting here
- // and enable it afterwards
- me.suspendLayout = true;
- me.isValid();
- me.suspendLayout = false;
- });
- if (me.autoLoad) {
- me.load();
- }
- }
- });
- Ext.define('Proxmox.window.PasswordEdit', {
- extend: 'Proxmox.window.Edit',
- alias: 'proxmoxWindowPasswordEdit',
- subject: gettext('Password'),
- url: '/api2/extjs/access/password',
- fieldDefaults: {
- labelWidth: 120
- },
- items: [
- {
- xtype: 'textfield',
- inputType: 'password',
- fieldLabel: gettext('Password'),
- minLength: 5,
- allowBlank: false,
- name: 'password',
- listeners: {
- change: function(field){
- field.next().validate();
- },
- blur: function(field){
- field.next().validate();
- }
- }
- },
- {
- xtype: 'textfield',
- inputType: 'password',
- fieldLabel: gettext('Confirm password'),
- name: 'verifypassword',
- allowBlank: false,
- vtype: 'password',
- initialPassField: 'password',
- submitValue: false
- },
- {
- xtype: 'hiddenfield',
- name: 'userid'
- }
- ],
- initComponent : function() {
- var me = this;
- if (!me.userid) {
- throw "no userid specified";
- }
- me.callParent();
- me.down('[name=userid]').setValue(me.userid);
- }
- });
- Ext.define('Proxmox.window.TaskProgress', {
- extend: 'Ext.window.Window',
- alias: 'widget.proxmoxTaskProgress',
- taskDone: Ext.emptyFn,
- initComponent: function() {
- var me = this;
- if (!me.upid) {
- throw "no task specified";
- }
- var task = Proxmox.Utils.parse_task_upid(me.upid);
- var statstore = Ext.create('Proxmox.data.ObjectStore', {
- url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status",
- interval: 1000,
- rows: {
- status: { defaultValue: 'unknown' },
- exitstatus: { defaultValue: 'unknown' }
- }
- });
- me.on('destroy', statstore.stopUpdate);
- var getObjectValue = function(key, defaultValue) {
- var rec = statstore.getById(key);
- if (rec) {
- return rec.data.value;
- }
- return defaultValue;
- };
- var pbar = Ext.create('Ext.ProgressBar', { text: 'running...' });
- me.mon(statstore, 'load', function() {
- var status = getObjectValue('status');
- if (status === 'stopped') {
- var exitstatus = getObjectValue('exitstatus');
- if (exitstatus == 'OK') {
- pbar.reset();
- pbar.updateText("Done!");
- Ext.Function.defer(me.close, 1000, me);
- } else {
- me.close();
- Ext.Msg.alert('Task failed', exitstatus);
- }
- me.taskDone(exitstatus == 'OK');
- }
- });
- var descr = Proxmox.Utils.format_task_description(task.type, task.id);
- Ext.apply(me, {
- title: gettext('Task') + ': ' + descr,
- width: 300,
- layout: 'auto',
- modal: true,
- bodyPadding: 5,
- items: pbar,
- buttons: [
- {
- text: gettext('Details'),
- handler: function() {
- var win = Ext.create('Proxmox.window.TaskViewer', {
- taskDone: me.taskDone,
- upid: me.upid
- });
- win.show();
- me.close();
- }
- }
- ]
- });
- me.callParent();
- statstore.startUpdate();
- pbar.wait();
- }
- });
- // fixme: how can we avoid those lint errors?
- /*jslint confusion: true */
- Ext.define('Proxmox.window.TaskViewer', {
- extend: 'Ext.window.Window',
- alias: 'widget.proxmoxTaskViewer',
- extraTitle: '', // string to prepend after the generic task title
- taskDone: Ext.emptyFn,
- initComponent: function() {
- var me = this;
- if (!me.upid) {
- throw "no task specified";
- }
- var task = Proxmox.Utils.parse_task_upid(me.upid);
- var statgrid;
- var rows = {
- status: {
- header: gettext('Status'),
- defaultValue: 'unknown',
- renderer: function(value) {
- if (value != 'stopped') {
- return value;
- }
- var es = statgrid.getObjectValue('exitstatus');
- if (es) {
- return value + ': ' + es;
- }
- }
- },
- exitstatus: {
- visible: false
- },
- type: {
- header: gettext('Task type'),
- required: true
- },
- user: {
- header: gettext('User name'),
- required: true
- },
- node: {
- header: gettext('Node'),
- required: true
- },
- pid: {
- header: gettext('Process ID'),
- required: true
- },
- starttime: {
- header: gettext('Start Time'),
- required: true,
- renderer: Proxmox.Utils.render_timestamp
- },
- upid: {
- header: gettext('Unique task ID')
- }
- };
- var statstore = Ext.create('Proxmox.data.ObjectStore', {
- url: "/api2/json/nodes/" + task.node + "/tasks/" + me.upid + "/status",
- interval: 1000,
- rows: rows
- });
- me.on('destroy', statstore.stopUpdate);
- var stop_task = function() {
- Proxmox.Utils.API2Request({
- url: "/nodes/" + task.node + "/tasks/" + me.upid,
- waitMsgTarget: me,
- method: 'DELETE',
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- };
- var stop_btn1 = new Ext.Button({
- text: gettext('Stop'),
- disabled: true,
- handler: stop_task
- });
- var stop_btn2 = new Ext.Button({
- text: gettext('Stop'),
- disabled: true,
- handler: stop_task
- });
- statgrid = Ext.create('Proxmox.grid.ObjectGrid', {
- title: gettext('Status'),
- layout: 'fit',
- tbar: [ stop_btn1 ],
- rstore: statstore,
- rows: rows,
- border: false
- });
- var logView = Ext.create('Proxmox.panel.LogView', {
- title: gettext('Output'),
- tbar: [ stop_btn2 ],
- border: false,
- url: "/api2/extjs/nodes/" + task.node + "/tasks/" + me.upid + "/log"
- });
- me.mon(statstore, 'load', function() {
- var status = statgrid.getObjectValue('status');
- if (status === 'stopped') {
- logView.requestUpdate(undefined, true);
- logView.scrollToEnd = false;
- statstore.stopUpdate();
- me.taskDone(statgrid.getObjectValue('exitstatus') == 'OK');
- }
- stop_btn1.setDisabled(status !== 'running');
- stop_btn2.setDisabled(status !== 'running');
- });
- statstore.startUpdate();
- Ext.apply(me, {
- title: "Task viewer: " + task.desc + me.extraTitle,
- width: 800,
- height: 400,
- layout: 'fit',
- modal: true,
- items: [{
- xtype: 'tabpanel',
- region: 'center',
- items: [ logView, statgrid ]
- }]
- });
- me.callParent();
- logView.fireEvent('show', logView);
- }
- });
- Ext.define('apt-pkglist', {
- extend: 'Ext.data.Model',
- fields: [ 'Package', 'Title', 'Description', 'Section', 'Arch',
- 'Priority', 'Version', 'OldVersion', 'ChangeLogUrl', 'Origin' ],
- idProperty: 'Package'
- });
- Ext.define('Proxmox.node.APT', {
- extend: 'Ext.grid.GridPanel',
- xtype: 'proxmoxNodeAPT',
- upgradeBtn: undefined,
- columns: [
- {
- header: gettext('Package'),
- width: 200,
- sortable: true,
- dataIndex: 'Package'
- },
- {
- text: gettext('Version'),
- columns: [
- {
- header: gettext('current'),
- width: 100,
- sortable: false,
- dataIndex: 'OldVersion'
- },
- {
- header: gettext('new'),
- width: 100,
- sortable: false,
- dataIndex: 'Version'
- }
- ]
- },
- {
- header: gettext('Description'),
- sortable: false,
- dataIndex: 'Title',
- flex: 1
- }
- ],
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- var store = Ext.create('Ext.data.Store', {
- model: 'apt-pkglist',
- groupField: 'Origin',
- proxy: {
- type: 'proxmox',
- url: "/api2/json/nodes/" + me.nodename + "/apt/update"
- },
- sorters: [
- {
- property : 'Package',
- direction: 'ASC'
- }
- ]
- });
- var groupingFeature = Ext.create('Ext.grid.feature.Grouping', {
- groupHeaderTpl: '{[ "Origin: " + values.name ]} ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})',
- enableGroupingMenu: false
- });
- var rowBodyFeature = Ext.create('Ext.grid.feature.RowBody', {
- getAdditionalData: function (data, rowIndex, record, orig) {
- var headerCt = this.view.headerCt;
- var colspan = headerCt.getColumnCount();
- // Usually you would style the my-body-class in CSS file
- return {
- rowBody: '<div style="padding: 1em">' +
- Ext.String.htmlEncode(data.Description) +
- '</div>',
- rowBodyColspan: colspan
- };
- }
- });
- var reload = function() {
- store.load();
- };
- Proxmox.Utils.monStoreErrors(me, store, true);
- var apt_command = function(cmd){
- Proxmox.Utils.API2Request({
- url: "/nodes/" + me.nodename + "/apt/" + cmd,
- method: 'POST',
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, opts) {
- var upid = response.result.data;
- var win = Ext.create('Proxmox.window.TaskViewer', {
- upid: upid
- });
- win.show();
- me.mon(win, 'close', reload);
- }
- });
- };
- var sm = Ext.create('Ext.selection.RowModel', {});
- var update_btn = new Ext.Button({
- text: gettext('Refresh'),
- handler: function(){
- Proxmox.Utils.checked_command(function() { apt_command('update'); });
- }
- });
- var show_changelog = function(rec) {
- if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) {
- return;
- }
- var view = Ext.createWidget('component', {
- autoScroll: true,
- style: {
- 'background-color': 'white',
- 'white-space': 'pre',
- 'font-family': 'monospace',
- padding: '5px'
- }
- });
- var win = Ext.create('Ext.window.Window', {
- title: gettext('Changelog') + ": " + rec.data.Package,
- width: 800,
- height: 400,
- layout: 'fit',
- modal: true,
- items: [ view ]
- });
- Proxmox.Utils.API2Request({
- waitMsgTarget: me,
- url: "/nodes/" + me.nodename + "/apt/changelog",
- params: {
- name: rec.data.Package,
- version: rec.data.Version
- },
- method: 'GET',
- failure: function(response, opts) {
- win.close();
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, opts) {
- win.show();
- view.update(Ext.htmlEncode(response.result.data));
- }
- });
- };
- var changelog_btn = new Proxmox.button.Button({
- text: gettext('Changelog'),
- selModel: sm,
- disabled: true,
- enableFn: function(rec) {
- if (!rec || !rec.data || !(rec.data.ChangeLogUrl && rec.data.Package)) {
- return false;
- }
- return true;
- },
- handler: function(b, e, rec) {
- show_changelog(rec);
- }
- });
- if (me.upgradeBtn) {
- me.tbar = [ update_btn, me.upgradeBtn, changelog_btn ];
- } else {
- me.tbar = [ update_btn, changelog_btn ];
- }
- Ext.apply(me, {
- store: store,
- stateful: true,
- stateId: 'grid-update',
- selModel: sm,
- viewConfig: {
- stripeRows: false,
- emptyText: '<div style="display:table; width:100%; height:100%;"><div style="display:table-cell; vertical-align: middle; text-align:center;"><b>' + gettext('No updates available.') + '</div></div>'
- },
- features: [ groupingFeature, rowBodyFeature ],
- listeners: {
- activate: reload,
- itemdblclick: function(v, rec) {
- show_changelog(rec);
- }
- }
- });
- me.callParent();
- }
- });
- Ext.define('Proxmox.node.NetworkEdit', {
- extend: 'Proxmox.window.Edit',
- alias: ['widget.proxmoxNodeNetworkEdit'],
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- if (!me.iftype) {
- throw "no network device type specified";
- }
- me.isCreate = !me.iface;
- var iface_vtype;
- if (me.iftype === 'bridge') {
- iface_vtype = 'BridgeName';
- } else if (me.iftype === 'bond') {
- iface_vtype = 'BondName';
- } else if (me.iftype === 'eth' && !me.isCreate) {
- iface_vtype = 'InterfaceName';
- } else if (me.iftype === 'vlan' && !me.isCreate) {
- iface_vtype = 'InterfaceName';
- } else if (me.iftype === 'OVSBridge') {
- iface_vtype = 'BridgeName';
- } else if (me.iftype === 'OVSBond') {
- iface_vtype = 'BondName';
- } else if (me.iftype === 'OVSIntPort') {
- iface_vtype = 'InterfaceName';
- } else if (me.iftype === 'OVSPort') {
- iface_vtype = 'InterfaceName';
- } else {
- console.log(me.iftype);
- throw "unknown network device type specified";
- }
- me.subject = Proxmox.Utils.render_network_iface_type(me.iftype);
- var column2 = [];
- if (!(me.iftype === 'OVSIntPort' || me.iftype === 'OVSPort' ||
- me.iftype === 'OVSBond')) {
- column2.push({
- xtype: 'proxmoxcheckbox',
- fieldLabel: gettext('Autostart'),
- name: 'autostart',
- uncheckedValue: 0,
- checked: me.isCreate ? true : undefined
- });
- }
- if (me.iftype === 'bridge') {
- column2.push({
- xtype: 'proxmoxcheckbox',
- fieldLabel: gettext('VLAN aware'),
- name: 'bridge_vlan_aware',
- deleteEmpty: !me.isCreate
- });
- column2.push({
- xtype: 'textfield',
- fieldLabel: gettext('Bridge ports'),
- name: 'bridge_ports'
- });
- } else if (me.iftype === 'OVSBridge') {
- column2.push({
- xtype: 'textfield',
- fieldLabel: gettext('Bridge ports'),
- name: 'ovs_ports'
- });
- column2.push({
- xtype: 'textfield',
- fieldLabel: gettext('OVS options'),
- name: 'ovs_options'
- });
- } else if (me.iftype === 'OVSPort' || me.iftype === 'OVSIntPort') {
- column2.push({
- xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield',
- fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'),
- allowBlank: false,
- nodename: me.nodename,
- bridgeType: 'OVSBridge',
- name: 'ovs_bridge'
- });
- column2.push({
- xtype: 'pveVlanField',
- deleteEmpty: !me.isCreate,
- name: 'ovs_tag',
- value: ''
- });
- column2.push({
- xtype: 'textfield',
- fieldLabel: gettext('OVS options'),
- name: 'ovs_options'
- });
- } else if (me.iftype === 'bond') {
- column2.push({
- xtype: 'textfield',
- fieldLabel: gettext('Slaves'),
- name: 'slaves'
- });
- var policySelector = Ext.createWidget('bondPolicySelector', {
- fieldLabel: gettext('Hash policy'),
- name: 'bond_xmit_hash_policy',
- deleteEmpty: !me.isCreate,
- disabled: true
- });
- column2.push({
- xtype: 'bondModeSelector',
- fieldLabel: gettext('Mode'),
- name: 'bond_mode',
- value: me.isCreate ? 'balance-rr' : undefined,
- listeners: {
- change: function(f, value) {
- if (value === 'balance-xor' ||
- value === '802.3ad') {
- policySelector.setDisabled(false);
- } else {
- policySelector.setDisabled(true);
- policySelector.setValue('');
- }
- }
- },
- allowBlank: false
- });
- column2.push(policySelector);
- } else if (me.iftype === 'OVSBond') {
- column2.push({
- xtype: me.isCreate ? 'PVE.form.BridgeSelector' : 'displayfield',
- fieldLabel: Proxmox.Utils.render_network_iface_type('OVSBridge'),
- allowBlank: false,
- nodename: me.nodename,
- bridgeType: 'OVSBridge',
- name: 'ovs_bridge'
- });
- column2.push({
- xtype: 'pveVlanField',
- deleteEmpty: !me.isCreate,
- name: 'ovs_tag',
- value: ''
- });
- column2.push({
- xtype: 'textfield',
- fieldLabel: gettext('OVS options'),
- name: 'ovs_options'
- });
- }
- column2.push({
- xtype: 'textfield',
- fieldLabel: gettext('Comment'),
- allowBlank: true,
- nodename: me.nodename,
- name: 'comments'
- });
- var url;
- var method;
- if (me.isCreate) {
- url = "/api2/extjs/nodes/" + me.nodename + "/network";
- method = 'POST';
- } else {
- url = "/api2/extjs/nodes/" + me.nodename + "/network/" + me.iface;
- method = 'PUT';
- }
- var column1 = [
- {
- xtype: 'hiddenfield',
- name: 'type',
- value: me.iftype
- },
- {
- xtype: me.isCreate ? 'textfield' : 'displayfield',
- fieldLabel: gettext('Name'),
- name: 'iface',
- value: me.iface,
- vtype: iface_vtype,
- allowBlank: false
- }
- ];
- if (me.iftype === 'OVSBond') {
- column1.push(
- {
- xtype: 'bondModeSelector',
- fieldLabel: gettext('Mode'),
- name: 'bond_mode',
- openvswitch: true,
- value: me.isCreate ? 'active-backup' : undefined,
- allowBlank: false
- },
- {
- xtype: 'textfield',
- fieldLabel: gettext('Slaves'),
- name: 'ovs_bonds'
- }
- );
- } else {
- column1.push(
- {
- xtype: 'proxmoxtextfield',
- deleteEmpty: !me.isCreate,
- fieldLabel: gettext('IP address'),
- vtype: 'IPAddress',
- name: 'address'
- },
- {
- xtype: 'proxmoxtextfield',
- deleteEmpty: !me.isCreate,
- fieldLabel: gettext('Subnet mask'),
- vtype: 'IPAddress',
- name: 'netmask',
- validator: function(value) {
- /*jslint confusion: true */
- if (!me.items) {
- return true;
- }
- var address = me.down('field[name=address]').getValue();
- if (value !== '') {
- if (address === '') {
- return "Subnet mask requires option 'IP address'";
- }
- } else {
- if (address !== '') {
- return "Option 'IP address' requires a subnet mask";
- }
- }
- return true;
- }
- },
- {
- xtype: 'proxmoxtextfield',
- deleteEmpty: !me.isCreate,
- fieldLabel: gettext('Gateway'),
- vtype: 'IPAddress',
- name: 'gateway'
- },
- {
- xtype: 'proxmoxtextfield',
- deleteEmpty: !me.isCreate,
- fieldLabel: gettext('IPv6 address'),
- vtype: 'IP6Address',
- name: 'address6'
- },
- {
- xtype: 'proxmoxtextfield',
- deleteEmpty: !me.isCreate,
- fieldLabel: gettext('Prefix length'),
- vtype: 'IP6PrefixLength',
- name: 'netmask6',
- value: '',
- allowBlank: true,
- validator: function(value) {
- /*jslint confusion: true */
- if (!me.items) {
- return true;
- }
- var address = me.down('field[name=address6]').getValue();
- if (value !== '') {
- if (address === '') {
- return "IPv6 prefix length requires option 'IPv6 address'";
- }
- } else {
- if (address !== '') {
- return "Option 'IPv6 address' requires an IPv6 prefix length";
- }
- }
- return true;
- }
- },
- {
- xtype: 'proxmoxtextfield',
- deleteEmpty: !me.isCreate,
- fieldLabel: gettext('Gateway'),
- vtype: 'IP6Address',
- name: 'gateway6'
- }
- );
- }
- Ext.applyIf(me, {
- url: url,
- method: method,
- items: {
- xtype: 'inputpanel',
- column1: column1,
- column2: column2
- }
- });
- me.callParent();
- if (me.isCreate) {
- me.down('field[name=iface]').setValue(me.iface_default);
- } else {
- me.load({
- success: function(response, options) {
- var data = response.result.data;
- if (data.type !== me.iftype) {
- var msg = "Got unexpected device type";
- Ext.Msg.alert(gettext('Error'), msg, function() {
- me.close();
- });
- return;
- }
- me.setValues(data);
- me.isValid(); // trigger validation
- }
- });
- }
- }
- });
- Ext.define('proxmox-networks', {
- extend: 'Ext.data.Model',
- fields: [
- 'iface', 'type', 'active', 'autostart',
- 'bridge_ports', 'slaves',
- 'address', 'netmask', 'gateway',
- 'address6', 'netmask6', 'gateway6',
- 'comments'
- ],
- idProperty: 'iface'
- });
- Ext.define('Proxmox.node.NetworkView', {
- extend: 'Ext.panel.Panel',
- alias: ['widget.proxmoxNodeNetworkView'],
- // defines what types of network devices we want to create
- // order is always the same
- types: ['bridge', 'bond', 'ovs'],
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- var baseUrl = '/nodes/' + me.nodename + '/network';
- var store = Ext.create('Ext.data.Store', {
- model: 'proxmox-networks',
- proxy: {
- type: 'proxmox',
- url: '/api2/json' + baseUrl
- },
- sorters: [
- {
- property : 'iface',
- direction: 'ASC'
- }
- ]
- });
- var reload = function() {
- var changeitem = me.down('#changes');
- Proxmox.Utils.API2Request({
- url: baseUrl,
- failure: function(response, opts) {
- store.loadData({});
- Proxmox.Utils.setErrorMask(me, response.htmlStatus);
- changeitem.update('');
- changeitem.setHidden(true);
- },
- success: function(response, opts) {
- var result = Ext.decode(response.responseText);
- store.loadData(result.data);
- var changes = result.changes;
- if (changes === undefined || changes === '') {
- changes = gettext("No changes");
- changeitem.setHidden(true);
- } else {
- changeitem.update("<pre>" + Ext.htmlEncode(changes) + "</pre>");
- changeitem.setHidden(false);
- }
- }
- });
- };
- var run_editor = function() {
- var grid = me.down('gridpanel');
- var sm = grid.getSelectionModel();
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var win = Ext.create('Proxmox.node.NetworkEdit', {
- nodename: me.nodename,
- iface: rec.data.iface,
- iftype: rec.data.type
- });
- win.show();
- win.on('destroy', reload);
- };
- var edit_btn = new Ext.Button({
- text: gettext('Edit'),
- disabled: true,
- handler: run_editor
- });
- var del_btn = new Ext.Button({
- text: gettext('Remove'),
- disabled: true,
- handler: function(){
- var grid = me.down('gridpanel');
- var sm = grid.getSelectionModel();
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var iface = rec.data.iface;
- Proxmox.Utils.API2Request({
- url: baseUrl + '/' + iface,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- });
- var set_button_status = function() {
- var grid = me.down('gridpanel');
- var sm = grid.getSelectionModel();
- var rec = sm.getSelection()[0];
- edit_btn.setDisabled(!rec);
- del_btn.setDisabled(!rec);
- };
- var render_ports = function(value, metaData, record) {
- if (value === 'bridge') {
- return record.data.bridge_ports;
- } else if (value === 'bond') {
- return record.data.slaves;
- } else if (value === 'OVSBridge') {
- return record.data.ovs_ports;
- } else if (value === 'OVSBond') {
- return record.data.ovs_bonds;
- }
- };
- var find_next_iface_id = function(prefix) {
- var next;
- for (next = 0; next <= 9999; next++) {
- if (!store.getById(prefix + next.toString())) {
- break;
- }
- }
- return prefix + next.toString();
- };
- var menu_items = [];
- if (me.types.indexOf('bridge') !== -1) {
- menu_items.push({
- text: Proxmox.Utils.render_network_iface_type('bridge'),
- handler: function() {
- var win = Ext.create('Proxmox.node.NetworkEdit', {
- nodename: me.nodename,
- iftype: 'bridge',
- iface_default: find_next_iface_id('vmbr')
- });
- win.on('destroy', reload);
- win.show();
- }
- });
- }
- if (me.types.indexOf('bond') !== -1) {
- menu_items.push({
- text: Proxmox.Utils.render_network_iface_type('bond'),
- handler: function() {
- var win = Ext.create('Proxmox.node.NetworkEdit', {
- nodename: me.nodename,
- iftype: 'bond',
- iface_default: find_next_iface_id('bond')
- });
- win.on('destroy', reload);
- win.show();
- }
- });
- }
- if (me.types.indexOf('ovs') !== -1) {
- if (menu_items.length > 0) {
- menu_items.push({ xtype: 'menuseparator' });
- }
- menu_items.push(
- {
- text: Proxmox.Utils.render_network_iface_type('OVSBridge'),
- handler: function() {
- var win = Ext.create('Proxmox.node.NetworkEdit', {
- nodename: me.nodename,
- iftype: 'OVSBridge',
- iface_default: find_next_iface_id('vmbr')
- });
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: Proxmox.Utils.render_network_iface_type('OVSBond'),
- handler: function() {
- var win = Ext.create('Proxmox.node.NetworkEdit', {
- nodename: me.nodename,
- iftype: 'OVSBond',
- iface_default: find_next_iface_id('bond')
- });
- win.on('destroy', reload);
- win.show();
- }
- },
- {
- text: Proxmox.Utils.render_network_iface_type('OVSIntPort'),
- handler: function() {
- var win = Ext.create('Proxmox.node.NetworkEdit', {
- nodename: me.nodename,
- iftype: 'OVSIntPort'
- });
- win.on('destroy', reload);
- win.show();
- }
- }
- );
- }
- Ext.apply(me, {
- layout: 'border',
- tbar: [
- {
- text: gettext('Create'),
- menu: {
- plain: true,
- items: menu_items
- }
- }, ' ',
- {
- text: gettext('Revert'),
- handler: function() {
- Proxmox.Utils.API2Request({
- url: baseUrl,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- reload();
- },
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- }
- });
- }
- },
- edit_btn,
- del_btn
- ],
- items: [
- {
- xtype: 'gridpanel',
- stateful: true,
- stateId: 'grid-node-network',
- store: store,
- region: 'center',
- border: false,
- columns: [
- {
- header: gettext('Name'),
- sortable: true,
- dataIndex: 'iface'
- },
- {
- header: gettext('Type'),
- sortable: true,
- width: 120,
- renderer: Proxmox.Utils.render_network_iface_type,
- dataIndex: 'type'
- },
- {
- xtype: 'booleancolumn',
- header: gettext('Active'),
- width: 80,
- sortable: true,
- dataIndex: 'active',
- trueText: Proxmox.Utils.yesText,
- falseText: Proxmox.Utils.noText,
- undefinedText: Proxmox.Utils.noText,
- },
- {
- xtype: 'booleancolumn',
- header: gettext('Autostart'),
- width: 80,
- sortable: true,
- dataIndex: 'autostart',
- trueText: Proxmox.Utils.yesText,
- falseText: Proxmox.Utils.noText,
- undefinedText: Proxmox.Utils.noText
- },
- {
- xtype: 'booleancolumn',
- header: gettext('VLAN aware'),
- width: 80,
- sortable: true,
- dataIndex: 'bridge_vlan_aware',
- trueText: Proxmox.Utils.yesText,
- falseText: Proxmox.Utils.noText,
- undefinedText: Proxmox.Utils.noText
- },
- {
- header: gettext('Ports/Slaves'),
- dataIndex: 'type',
- renderer: render_ports
- },
- {
- header: gettext('IP address'),
- sortable: true,
- width: 120,
- dataIndex: 'address',
- renderer: function(value, metaData, rec) {
- if (rec.data.address && rec.data.address6) {
- return rec.data.address + "<br>"
- + rec.data.address6 + '/' + rec.data.netmask6;
- } else if (rec.data.address6) {
- return rec.data.address6 + '/' + rec.data.netmask6;
- } else {
- return rec.data.address;
- }
- }
- },
- {
- header: gettext('Subnet mask'),
- width: 120,
- sortable: true,
- dataIndex: 'netmask'
- },
- {
- header: gettext('Gateway'),
- width: 120,
- sortable: true,
- dataIndex: 'gateway',
- renderer: function(value, metaData, rec) {
- if (rec.data.gateway && rec.data.gateway6) {
- return rec.data.gateway + "<br>" + rec.data.gateway6;
- } else if (rec.data.gateway6) {
- return rec.data.gateway6;
- } else {
- return rec.data.gateway;
- }
- }
- },
- {
- header: gettext('Comment'),
- dataIndex: 'comments',
- flex: 1,
- renderer: Ext.String.htmlEncode
- }
- ],
- listeners: {
- selectionchange: set_button_status,
- itemdblclick: run_editor
- }
- },
- {
- border: false,
- region: 'south',
- autoScroll: true,
- hidden: true,
- itemId: 'changes',
- tbar: [
- gettext('Pending changes') + ' (' +
- gettext('Please reboot to activate changes') + ')'
- ],
- split: true,
- bodyPadding: 5,
- flex: 0.6,
- html: gettext("No changes")
- }
- ],
- });
- me.callParent();
- reload();
- }
- });
- Ext.define('Proxmox.node.DNSEdit', {
- extend: 'Proxmox.window.Edit',
- alias: ['widget.proxmoxNodeDNSEdit'],
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- me.items = [
- {
- xtype: 'textfield',
- fieldLabel: gettext('Search domain'),
- name: 'search',
- allowBlank: false
- },
- {
- xtype: 'proxmoxtextfield',
- fieldLabel: gettext('DNS server') + " 1",
- vtype: 'IP64Address',
- skipEmptyText: true,
- name: 'dns1'
- },
- {
- xtype: 'proxmoxtextfield',
- fieldLabel: gettext('DNS server') + " 2",
- vtype: 'IP64Address',
- skipEmptyText: true,
- name: 'dns2'
- },
- {
- xtype: 'proxmoxtextfield',
- fieldLabel: gettext('DNS server') + " 3",
- vtype: 'IP64Address',
- skipEmptyText: true,
- name: 'dns3'
- }
- ];
- Ext.applyIf(me, {
- subject: gettext('DNS'),
- url: "/api2/extjs/nodes/" + me.nodename + "/dns",
- fieldDefaults: {
- labelWidth: 120
- }
- });
- me.callParent();
- me.load();
- }
- });
- Ext.define('Proxmox.node.DNSView', {
- extend: 'Proxmox.grid.ObjectGrid',
- alias: ['widget.proxmoxNodeDNSView'],
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- var run_editor = function() {
- var win = Ext.create('Proxmox.node.DNSEdit', {
- nodename: me.nodename
- });
- win.show();
- };
- Ext.apply(me, {
- url: "/api2/json/nodes/" + me.nodename + "/dns",
- cwidth1: 130,
- interval: 1000,
- run_editor: run_editor,
- rows: {
- search: { header: 'Search domain', required: true },
- dns1: { header: gettext('DNS server') + " 1", required: true },
- dns2: { header: gettext('DNS server') + " 2" },
- dns3: { header: gettext('DNS server') + " 3" }
- },
- tbar: [
- {
- text: gettext("Edit"),
- handler: run_editor
- }
- ],
- listeners: {
- itemdblclick: run_editor
- }
- });
- me.callParent();
- me.on('activate', me.rstore.startUpdate);
- me.on('deactivate', me.rstore.stopUpdate);
- me.on('destroy', me.rstore.stopUpdate);
- }
- });
- Ext.define('Proxmox.node.Tasks', {
- extend: 'Ext.grid.GridPanel',
- alias: ['widget.proxmoxNodeTasks'],
- stateful: true,
- stateId: 'grid-node-tasks',
- loadMask: true,
- sortableColumns: false,
- vmidFilter: 0,
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- var store = Ext.create('Ext.data.BufferedStore', {
- pageSize: 500,
- autoLoad: true,
- remoteFilter: true,
- model: 'proxmox-tasks',
- proxy: {
- type: 'proxmox',
- startParam: 'start',
- limitParam: 'limit',
- url: "/api2/json/nodes/" + me.nodename + "/tasks"
- }
- });
- var userfilter = '';
- var filter_errors = 0;
- var updateProxyParams = function() {
- var params = {
- errors: filter_errors
- };
- if (userfilter) {
- params.userfilter = userfilter;
- }
- if (me.vmidFilter) {
- params.vmid = me.vmidFilter;
- }
- store.proxy.extraParams = params;
- };
- updateProxyParams();
- var reload_task = Ext.create('Ext.util.DelayedTask',function() {
- updateProxyParams();
- store.reload();
- });
- var run_task_viewer = function() {
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var win = Ext.create('Proxmox.window.TaskViewer', {
- upid: rec.data.upid
- });
- win.show();
- };
- var view_btn = new Ext.Button({
- text: gettext('View'),
- disabled: true,
- handler: run_task_viewer
- });
- Proxmox.Utils.monStoreErrors(me, store, true);
- Ext.apply(me, {
- store: store,
- viewConfig: {
- trackOver: false,
- stripeRows: false, // does not work with getRowClass()
- getRowClass: function(record, index) {
- var status = record.get('status');
- if (status && status != 'OK') {
- return "proxmox-invalid-row";
- }
- }
- },
- tbar: [
- view_btn, '->', gettext('User name') +':', ' ',
- {
- xtype: 'textfield',
- width: 200,
- value: userfilter,
- enableKeyEvents: true,
- listeners: {
- keyup: function(field, e) {
- userfilter = field.getValue();
- reload_task.delay(500);
- }
- }
- }, ' ', gettext('Only Errors') + ':', ' ',
- {
- xtype: 'checkbox',
- hideLabel: true,
- checked: filter_errors,
- listeners: {
- change: function(field, checked) {
- filter_errors = checked ? 1 : 0;
- reload_task.delay(10);
- }
- }
- }, ' '
- ],
- columns: [
- {
- header: gettext("Start Time"),
- dataIndex: 'starttime',
- width: 100,
- renderer: function(value) {
- return Ext.Date.format(value, "M d H:i:s");
- }
- },
- {
- header: gettext("End Time"),
- dataIndex: 'endtime',
- width: 100,
- renderer: function(value, metaData, record) {
- return Ext.Date.format(value,"M d H:i:s");
- }
- },
- {
- header: gettext("Node"),
- dataIndex: 'node',
- width: 100
- },
- {
- header: gettext("User name"),
- dataIndex: 'user',
- width: 150
- },
- {
- header: gettext("Description"),
- dataIndex: 'upid',
- flex: 1,
- renderer: Proxmox.Utils.render_upid
- },
- {
- header: gettext("Status"),
- dataIndex: 'status',
- width: 200,
- renderer: function(value, metaData, record) {
- if (value == 'OK') {
- return 'OK';
- }
- // metaData.attr = 'style="color:red;"';
- return "ERROR: " + value;
- }
- }
- ],
- listeners: {
- itemdblclick: run_task_viewer,
- selectionchange: function(v, selections) {
- view_btn.setDisabled(!(selections && selections[0]));
- },
- show: function() { reload_task.delay(10); },
- destroy: function() { reload_task.cancel(); }
- }
- });
- me.callParent();
- }
- });
- Ext.define('proxmox-services', {
- extend: 'Ext.data.Model',
- fields: [ 'service', 'name', 'desc', 'state' ],
- idProperty: 'service'
- });
- Ext.define('Proxmox.node.ServiceView', {
- extend: 'Ext.grid.GridPanel',
- alias: ['widget.proxmoxNodeServiceView'],
- startOnlyServices: {},
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- var rstore = Ext.create('Proxmox.data.UpdateStore', {
- interval: 1000,
- storeid: 'proxmox-services' + me.nodename,
- model: 'proxmox-services',
- proxy: {
- type: 'proxmox',
- url: "/api2/json/nodes/" + me.nodename + "/services"
- }
- });
- var store = Ext.create('Proxmox.data.DiffStore', {
- rstore: rstore,
- sortAfterUpdate: true,
- sorters: [
- {
- property : 'name',
- direction: 'ASC'
- }
- ]
- });
- var view_service_log = function() {
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
- var win = Ext.create('Ext.window.Window', {
- title: gettext('Syslog') + ': ' + rec.data.service,
- modal: true,
- items: {
- xtype: 'proxmoxLogView',
- width: 800,
- height: 400,
- url: "/api2/extjs/nodes/" + me.nodename + "/syslog?service=" +
- rec.data.service,
- log_select_timespan: 1
- }
- });
- win.show();
- };
- var service_cmd = function(cmd) {
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
- Proxmox.Utils.API2Request({
- url: "/nodes/" + me.nodename + "/services/" + rec.data.service + "/" + cmd,
- method: 'POST',
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- me.loading = true;
- },
- success: function(response, opts) {
- rstore.startUpdate();
- var upid = response.result.data;
- var win = Ext.create('Proxmox.window.TaskProgress', {
- upid: upid
- });
- win.show();
- }
- });
- };
- var start_btn = new Ext.Button({
- text: gettext('Start'),
- disabled: true,
- handler: function(){
- service_cmd("start");
- }
- });
- var stop_btn = new Ext.Button({
- text: gettext('Stop'),
- disabled: true,
- handler: function(){
- service_cmd("stop");
- }
- });
- var restart_btn = new Ext.Button({
- text: gettext('Restart'),
- disabled: true,
- handler: function(){
- service_cmd("restart");
- }
- });
- var syslog_btn = new Ext.Button({
- text: gettext('Syslog'),
- disabled: true,
- handler: view_service_log
- });
- var set_button_status = function() {
- var sm = me.getSelectionModel();
- var rec = sm.getSelection()[0];
- if (!rec) {
- start_btn.disable();
- stop_btn.disable();
- restart_btn.disable();
- syslog_btn.disable();
- return;
- }
- var service = rec.data.service;
- var state = rec.data.state;
- syslog_btn.enable();
- if (me.startOnlyServices[service]) {
- if (state == 'running') {
- start_btn.disable();
- restart_btn.enable();
- } else {
- start_btn.enable();
- restart_btn.disable();
- }
- stop_btn.disable();
- } else {
- if (state == 'running') {
- start_btn.disable();
- restart_btn.enable();
- stop_btn.enable();
- } else {
- start_btn.enable();
- restart_btn.disable();
- stop_btn.disable();
- }
- }
- };
- me.mon(store, 'refresh', set_button_status);
- Proxmox.Utils.monStoreErrors(me, rstore);
- Ext.apply(me, {
- store: store,
- stateful: false,
- tbar: [ start_btn, stop_btn, restart_btn, syslog_btn ],
- columns: [
- {
- header: gettext('Name'),
- flex: 1,
- sortable: true,
- dataIndex: 'name'
- },
- {
- header: gettext('Status'),
- width: 100,
- sortable: true,
- dataIndex: 'state'
- },
- {
- header: gettext('Description'),
- renderer: Ext.String.htmlEncode,
- dataIndex: 'desc',
- flex: 2
- }
- ],
- listeners: {
- selectionchange: set_button_status,
- itemdblclick: view_service_log,
- activate: rstore.startUpdate,
- destroy: rstore.stopUpdate
- }
- });
- me.callParent();
- }
- });
- Ext.define('Proxmox.node.TimeEdit', {
- extend: 'Proxmox.window.Edit',
- alias: ['widget.proxmoxNodeTimeEdit'],
- subject: gettext('Time zone'),
- width: 400,
- autoLoad: true,
- fieldDefaults: {
- labelWidth: 70
- },
- items: {
- xtype: 'combo',
- fieldLabel: gettext('Time zone'),
- name: 'timezone',
- queryMode: 'local',
- store: Ext.create('Proxmox.data.TimezoneStore'),
- displayField: 'zone',
- forceSelection: true,
- editable: false,
- allowBlank: false
- },
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- me.url = "/api2/extjs/nodes/" + me.nodename + "/time";
- me.callParent();
- }
- });
- Ext.define('Proxmox.node.TimeView', {
- extend: 'Proxmox.grid.ObjectGrid',
- alias: ['widget.proxmoxNodeTimeView'],
- initComponent : function() {
- var me = this;
- if (!me.nodename) {
- throw "no node name specified";
- }
- var tzoffset = (new Date()).getTimezoneOffset()*60000;
- var renderlocaltime = function(value) {
- var servertime = new Date((value * 1000) + tzoffset);
- return Ext.Date.format(servertime, 'Y-m-d H:i:s');
- };
- var run_editor = function() {
- var win = Ext.create('Proxmox.node.TimeEdit', {
- nodename: me.nodename
- });
- win.show();
- };
- Ext.apply(me, {
- url: "/api2/json/nodes/" + me.nodename + "/time",
- cwidth1: 150,
- interval: 1000,
- run_editor: run_editor,
- rows: {
- timezone: {
- header: gettext('Time zone'),
- required: true
- },
- localtime: {
- header: gettext('Server time'),
- required: true,
- renderer: renderlocaltime
- }
- },
- tbar: [
- {
- text: gettext("Edit"),
- handler: run_editor
- }
- ],
- listeners: {
- itemdblclick: run_editor
- }
- });
- me.callParent();
- me.on('activate', me.rstore.startUpdate);
- me.on('deactivate', me.rstore.stopUpdate);
- me.on('destroy', me.rstore.stopUpdate);
- }
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement