]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/Utils.js
7b78eeb12a5ecdc97f7367820148328264a17433
[proxmox-widget-toolkit.git] / src / Utils.js
1 Ext.ns('Proxmox');
2 Ext.ns('Proxmox.Setup');
3
4 if (!Ext.isDefined(Proxmox.Setup.auth_cookie_name)) {
5 throw "Proxmox library not initialized";
6 }
7
8 // avoid errors when running without development tools
9 if (!Ext.isDefined(Ext.global.console)) {
10 let console = {
11 dir: function() {
12 // do nothing
13 },
14 log: function() {
15 // do nothing
16 },
17 warn: function() {
18 // do nothing
19 },
20 };
21 Ext.global.console = console;
22 }
23
24 Ext.Ajax.defaultHeaders = {
25 'Accept': 'application/json',
26 };
27
28 Ext.Ajax.on('beforerequest', function(conn, options) {
29 if (Proxmox.CSRFPreventionToken) {
30 if (!options.headers) {
31 options.headers = {};
32 }
33 options.headers.CSRFPreventionToken = Proxmox.CSRFPreventionToken;
34 }
35 let storedAuth = Proxmox.Utils.getStoredAuth();
36 if (storedAuth.token) {
37 options.headers.Authorization = storedAuth.token;
38 }
39 });
40
41 Ext.define('Proxmox.Utils', { // a singleton
42 utilities: {
43
44 yesText: gettext('Yes'),
45 noText: gettext('No'),
46 enabledText: gettext('Enabled'),
47 disabledText: gettext('Disabled'),
48 noneText: gettext('none'),
49 NoneText: gettext('None'),
50 errorText: gettext('Error'),
51 unknownText: gettext('Unknown'),
52 defaultText: gettext('Default'),
53 daysText: gettext('days'),
54 dayText: gettext('day'),
55 runningText: gettext('running'),
56 stoppedText: gettext('stopped'),
57 neverText: gettext('never'),
58 totalText: gettext('Total'),
59 usedText: gettext('Used'),
60 directoryText: gettext('Directory'),
61 stateText: gettext('State'),
62 groupText: gettext('Group'),
63
64 language_map: {
65 ar: 'Arabic',
66 ca: 'Catalan',
67 zh_CN: 'Chinese (Simplified)',
68 zh_TW: 'Chinese (Traditional)',
69 da: 'Danish',
70 nl: 'Dutch',
71 en: 'English',
72 eu: 'Euskera (Basque)',
73 fr: 'French',
74 de: 'German',
75 he: 'Hebrew',
76 it: 'Italian',
77 ja: 'Japanese',
78 kr: 'Korean',
79 nb: 'Norwegian (Bokmal)',
80 nn: 'Norwegian (Nynorsk)',
81 fa: 'Persian (Farsi)',
82 pl: 'Polish',
83 pt_BR: 'Portuguese (Brazil)',
84 ru: 'Russian',
85 sl: 'Slovenian',
86 es: 'Spanish',
87 sv: 'Swedish',
88 tr: 'Turkish',
89 },
90
91 render_language: function(value) {
92 if (!value) {
93 return Proxmox.Utils.defaultText + ' (English)';
94 }
95 let text = Proxmox.Utils.language_map[value];
96 if (text) {
97 return text + ' (' + value + ')';
98 }
99 return value;
100 },
101
102 language_array: function() {
103 let data = [['__default__', Proxmox.Utils.render_language('')]];
104 Ext.Object.each(Proxmox.Utils.language_map, function(key, value) {
105 data.push([key, Proxmox.Utils.render_language(value)]);
106 });
107
108 return data;
109 },
110
111 bond_mode_gettext_map: {
112 '802.3ad': 'LACP (802.3ad)',
113 'lacp-balance-slb': 'LACP (balance-slb)',
114 'lacp-balance-tcp': 'LACP (balance-tcp)',
115 },
116
117 render_bond_mode: value => Proxmox.Utils.bond_mode_gettext_map[value] || value || '',
118
119 bond_mode_array: function(modes) {
120 return modes.map(mode => [mode, Proxmox.Utils.render_bond_mode(mode)]);
121 },
122
123 getNoSubKeyHtml: function(url) {
124 // url http://www.proxmox.com/products/proxmox-ve/subscription-service-plans
125 return Ext.String.format('You do not have a valid subscription for this server. Please visit <a target="_blank" href="{0}">www.proxmox.com</a> to get a list of available options.', url || 'https://www.proxmox.com');
126 },
127
128 format_boolean_with_default: function(value) {
129 if (Ext.isDefined(value) && value !== '__default__') {
130 return value ? Proxmox.Utils.yesText : Proxmox.Utils.noText;
131 }
132 return Proxmox.Utils.defaultText;
133 },
134
135 format_boolean: function(value) {
136 return value ? Proxmox.Utils.yesText : Proxmox.Utils.noText;
137 },
138
139 format_neg_boolean: function(value) {
140 return !value ? Proxmox.Utils.yesText : Proxmox.Utils.noText;
141 },
142
143 format_enabled_toggle: function(value) {
144 return value ? Proxmox.Utils.enabledText : Proxmox.Utils.disabledText;
145 },
146
147 format_expire: function(date) {
148 if (!date) {
149 return Proxmox.Utils.neverText;
150 }
151 return Ext.Date.format(date, "Y-m-d");
152 },
153
154 // somewhat like a human would tell durations, omit zero values and do not
155 // give seconds precision if we talk days already
156 format_duration_human: function(ut) {
157 let seconds = 0, minutes = 0, hours = 0, days = 0;
158
159 if (ut <= 0.1) {
160 return '<0.1s';
161 }
162
163 let remaining = ut;
164 seconds = Number((remaining % 60).toFixed(1));
165 remaining = Math.trunc(remaining / 60);
166 if (remaining > 0) {
167 minutes = remaining % 60;
168 remaining = Math.trunc(remaining / 60);
169 if (remaining > 0) {
170 hours = remaining % 24;
171 remaining = Math.trunc(remaining / 24);
172 if (remaining > 0) {
173 days = remaining;
174 }
175 }
176 }
177
178 let res = [];
179 let add = (t, unit) => {
180 if (t > 0) res.push(t + unit);
181 return t > 0;
182 };
183
184 let addSeconds = !add(days, 'd');
185 add(hours, 'h');
186 add(minutes, 'm');
187 if (addSeconds) {
188 add(seconds, 's');
189 }
190 return res.join(' ');
191 },
192
193 format_duration_long: function(ut) {
194 let days = Math.floor(ut / 86400);
195 ut -= days*86400;
196 let hours = Math.floor(ut / 3600);
197 ut -= hours*3600;
198 let mins = Math.floor(ut / 60);
199 ut -= mins*60;
200
201 let hours_str = '00' + hours.toString();
202 hours_str = hours_str.substr(hours_str.length - 2);
203 let mins_str = "00" + mins.toString();
204 mins_str = mins_str.substr(mins_str.length - 2);
205 let ut_str = "00" + ut.toString();
206 ut_str = ut_str.substr(ut_str.length - 2);
207
208 if (days) {
209 let ds = days > 1 ? Proxmox.Utils.daysText : Proxmox.Utils.dayText;
210 return days.toString() + ' ' + ds + ' ' +
211 hours_str + ':' + mins_str + ':' + ut_str;
212 } else {
213 return hours_str + ':' + mins_str + ':' + ut_str;
214 }
215 },
216
217 format_subscription_level: function(level) {
218 if (level === 'c') {
219 return 'Community';
220 } else if (level === 'b') {
221 return 'Basic';
222 } else if (level === 's') {
223 return 'Standard';
224 } else if (level === 'p') {
225 return 'Premium';
226 } else {
227 return Proxmox.Utils.noneText;
228 }
229 },
230
231 compute_min_label_width: function(text, width) {
232 if (width === undefined) { width = 100; }
233
234 let tm = new Ext.util.TextMetrics();
235 let min = tm.getWidth(text + ':');
236
237 return min < width ? width : min;
238 },
239
240 getStoredAuth: function() {
241 let storedAuth = JSON.parse(window.localStorage.getItem('ProxmoxUser'));
242 return storedAuth || {};
243 },
244
245 setAuthData: function(data) {
246 Proxmox.UserName = data.username;
247 Proxmox.LoggedOut = data.LoggedOut;
248 // creates a session cookie (expire = null)
249 // that way the cookie gets deleted after the browser window is closed
250 if (data.ticket) {
251 Proxmox.CSRFPreventionToken = data.CSRFPreventionToken;
252 Ext.util.Cookies.set(Proxmox.Setup.auth_cookie_name, data.ticket, null, '/', null, true);
253 }
254
255 if (data.token) {
256 window.localStorage.setItem('ProxmoxUser', JSON.stringify(data));
257 }
258 },
259
260 authOK: function() {
261 if (Proxmox.LoggedOut) {
262 return undefined;
263 }
264 let storedAuth = Proxmox.Utils.getStoredAuth();
265 let cookie = Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name);
266 if ((Proxmox.UserName !== '' && cookie && !cookie.startsWith("PVE:tfa!")) || storedAuth.token) {
267 return cookie || storedAuth.token;
268 } else {
269 return false;
270 }
271 },
272
273 authClear: function() {
274 if (Proxmox.LoggedOut) {
275 return;
276 }
277 Ext.util.Cookies.clear(Proxmox.Setup.auth_cookie_name);
278 window.localStorage.removeItem("ProxmoxUser");
279 },
280
281 // comp.setLoading() is buggy in ExtJS 4.0.7, so we
282 // use el.mask() instead
283 setErrorMask: function(comp, msg) {
284 let el = comp.el;
285 if (!el) {
286 return;
287 }
288 if (!msg) {
289 el.unmask();
290 } else if (msg === true) {
291 el.mask(gettext("Loading..."));
292 } else {
293 el.mask(msg);
294 }
295 },
296
297 getResponseErrorMessage: (err) => {
298 if (!err.statusText) {
299 return gettext('Connection error');
300 }
301 let msg = [`${err.statusText} (${err.status})`];
302 if (err.response && err.response.responseText) {
303 let txt = err.response.responseText;
304 try {
305 let res = JSON.parse(txt);
306 if (res.errors && typeof res.errors === 'object') {
307 for (let [key, value] of Object.entries(res.errors)) {
308 msg.push(Ext.String.htmlEncode(`${key}: ${value}`));
309 }
310 }
311 } catch (e) {
312 // fallback to string
313 msg.push(Ext.String.htmlEncode(txt));
314 }
315 }
316 return msg.join('<br>');
317 },
318
319 monStoreErrors: function(component, store, clearMaskBeforeLoad) {
320 if (clearMaskBeforeLoad) {
321 component.mon(store, 'beforeload', function(s, operation, eOpts) {
322 Proxmox.Utils.setErrorMask(component, false);
323 });
324 } else {
325 component.mon(store, 'beforeload', function(s, operation, eOpts) {
326 if (!component.loadCount) {
327 component.loadCount = 0; // make sure it is nucomponent.ic
328 Proxmox.Utils.setErrorMask(component, true);
329 }
330 });
331 }
332
333 // only works with 'proxmox' proxy
334 component.mon(store.proxy, 'afterload', function(proxy, request, success) {
335 component.loadCount++;
336
337 if (success) {
338 Proxmox.Utils.setErrorMask(component, false);
339 return;
340 }
341
342 let error = request._operation.getError();
343 let msg = Proxmox.Utils.getResponseErrorMessage(error);
344 Proxmox.Utils.setErrorMask(component, msg);
345 });
346 },
347
348 extractRequestError: function(result, verbose) {
349 let msg = gettext('Successful');
350
351 if (!result.success) {
352 msg = gettext("Unknown error");
353 if (result.message) {
354 msg = result.message;
355 if (result.status) {
356 msg += ' (' + result.status + ')';
357 }
358 }
359 if (verbose && Ext.isObject(result.errors)) {
360 msg += "<br>";
361 Ext.Object.each(result.errors, function(prop, desc) {
362 msg += "<br><b>" + Ext.htmlEncode(prop) + "</b>: " +
363 Ext.htmlEncode(desc);
364 });
365 }
366 }
367
368 return msg;
369 },
370
371 // Ext.Ajax.request
372 API2Request: function(reqOpts) {
373 let newopts = Ext.apply({
374 waitMsg: gettext('Please wait...'),
375 }, reqOpts);
376
377 if (!newopts.url.match(/^\/api2/)) {
378 newopts.url = '/api2/extjs' + newopts.url;
379 }
380 delete newopts.callback;
381
382 let createWrapper = function(successFn, callbackFn, failureFn) {
383 Ext.apply(newopts, {
384 success: function(response, options) {
385 if (options.waitMsgTarget) {
386 if (Proxmox.Utils.toolkit === 'touch') {
387 options.waitMsgTarget.setMasked(false);
388 } else {
389 options.waitMsgTarget.setLoading(false);
390 }
391 }
392 let result = Ext.decode(response.responseText);
393 response.result = result;
394 if (!result.success) {
395 response.htmlStatus = Proxmox.Utils.extractRequestError(result, true);
396 Ext.callback(callbackFn, options.scope, [options, false, response]);
397 Ext.callback(failureFn, options.scope, [response, options]);
398 return;
399 }
400 Ext.callback(callbackFn, options.scope, [options, true, response]);
401 Ext.callback(successFn, options.scope, [response, options]);
402 },
403 failure: function(response, options) {
404 if (options.waitMsgTarget) {
405 if (Proxmox.Utils.toolkit === 'touch') {
406 options.waitMsgTarget.setMasked(false);
407 } else {
408 options.waitMsgTarget.setLoading(false);
409 }
410 }
411 response.result = {};
412 try {
413 response.result = Ext.decode(response.responseText);
414 } catch (e) {
415 // ignore
416 }
417 let msg = gettext('Connection error') + ' - server offline?';
418 if (response.aborted) {
419 msg = gettext('Connection error') + ' - aborted.';
420 } else if (response.timedout) {
421 msg = gettext('Connection error') + ' - Timeout.';
422 } else if (response.status && response.statusText) {
423 msg = gettext('Connection error') + ' ' + response.status + ': ' + response.statusText;
424 }
425 response.htmlStatus = msg;
426 Ext.callback(callbackFn, options.scope, [options, false, response]);
427 Ext.callback(failureFn, options.scope, [response, options]);
428 },
429 });
430 };
431
432 createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure);
433
434 let target = newopts.waitMsgTarget;
435 if (target) {
436 if (Proxmox.Utils.toolkit === 'touch') {
437 target.setMasked({ xtype: 'loadmask', message: newopts.waitMsg });
438 } else {
439 // Note: ExtJS bug - this does not work when component is not rendered
440 target.setLoading(newopts.waitMsg);
441 }
442 }
443 Ext.Ajax.request(newopts);
444 },
445
446 checked_command: function(orig_cmd) {
447 Proxmox.Utils.API2Request(
448 {
449 url: '/nodes/localhost/subscription',
450 method: 'GET',
451 failure: function(response, opts) {
452 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
453 },
454 success: function(response, opts) {
455 let res = response.result;
456 if (res === null || res === undefined || !res || res
457 .data.status.toLowerCase() !== 'active') {
458 Ext.Msg.show({
459 title: gettext('No valid subscription'),
460 icon: Ext.Msg.WARNING,
461 message: Proxmox.Utils.getNoSubKeyHtml(res.data.url),
462 buttons: Ext.Msg.OK,
463 callback: function(btn) {
464 if (btn !== 'ok') {
465 return;
466 }
467 orig_cmd();
468 },
469 });
470 } else {
471 orig_cmd();
472 }
473 },
474 },
475 );
476 },
477
478 assemble_field_data: function(values, data) {
479 if (!Ext.isObject(data)) {
480 return;
481 }
482 Ext.Object.each(data, function(name, val) {
483 if (Object.prototype.hasOwnProperty.call(values, name)) {
484 let bucket = values[name];
485 if (!Ext.isArray(bucket)) {
486 bucket = values[name] = [bucket];
487 }
488 if (Ext.isArray(val)) {
489 values[name] = bucket.concat(val);
490 } else {
491 bucket.push(val);
492 }
493 } else {
494 values[name] = val;
495 }
496 });
497 },
498
499 updateColumnWidth: function(container) {
500 let mode = Ext.state.Manager.get('summarycolumns') || 'auto';
501 let factor;
502 if (mode !== 'auto') {
503 factor = parseInt(mode, 10);
504 if (Number.isNaN(factor)) {
505 factor = 1;
506 }
507 } else {
508 factor = container.getSize().width < 1600 ? 1 : 2;
509 }
510
511 if (container.oldFactor === factor) {
512 return;
513 }
514
515 let items = container.query('>'); // direct childs
516 factor = Math.min(factor, items.length);
517 container.oldFactor = factor;
518
519 items.forEach((item) => {
520 item.columnWidth = 1 / factor;
521 });
522
523 // we have to update the layout twice, since the first layout change
524 // can trigger the scrollbar which reduces the amount of space left
525 container.updateLayout();
526 container.updateLayout();
527 },
528
529 dialog_title: function(subject, create, isAdd) {
530 if (create) {
531 if (isAdd) {
532 return gettext('Add') + ': ' + subject;
533 } else {
534 return gettext('Create') + ': ' + subject;
535 }
536 } else {
537 return gettext('Edit') + ': ' + subject;
538 }
539 },
540
541 network_iface_types: {
542 eth: gettext("Network Device"),
543 bridge: 'Linux Bridge',
544 bond: 'Linux Bond',
545 vlan: 'Linux VLAN',
546 OVSBridge: 'OVS Bridge',
547 OVSBond: 'OVS Bond',
548 OVSPort: 'OVS Port',
549 OVSIntPort: 'OVS IntPort',
550 },
551
552 render_network_iface_type: function(value) {
553 return Proxmox.Utils.network_iface_types[value] ||
554 Proxmox.Utils.unknownText;
555 },
556
557 task_desc_table: {
558 aptupdate: ['', gettext('Update package database')],
559 diskinit: ['Disk', gettext('Initialize Disk with GPT')],
560 spiceshell: ['', gettext('Shell') + ' (Spice)'],
561 srvreload: ['SRV', gettext('Reload')],
562 srvrestart: ['SRV', gettext('Restart')],
563 srvstart: ['SRV', gettext('Start')],
564 srvstop: ['SRV', gettext('Stop')],
565 termproxy: ['', gettext('Console') + ' (xterm.js)'],
566 vncshell: ['', gettext('Shell')],
567 },
568
569 // to add or change existing for product specific ones
570 override_task_descriptions: function(extra) {
571 for (const [key, value] of Object.entries(extra)) {
572 Proxmox.Utils.task_desc_table[key] = value;
573 }
574 },
575
576 format_task_description: function(type, id) {
577 let farray = Proxmox.Utils.task_desc_table[type];
578 let text;
579 if (!farray) {
580 text = type;
581 if (id) {
582 type += ' ' + id;
583 }
584 return text;
585 } else if (Ext.isFunction(farray)) {
586 return farray(type, id);
587 }
588 let prefix = farray[0];
589 text = farray[1];
590 if (prefix && id !== undefined) {
591 return prefix + ' ' + id + ' - ' + text;
592 }
593 return text;
594 },
595
596 format_size: function(size) {
597 let units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
598 let num = 0;
599 while (size >= 1024 && num++ <= units.length) {
600 size = size / 1024;
601 }
602
603 return size.toFixed(num > 0?2:0) + " " + units[num] + "B";
604 },
605
606 render_upid: function(value, metaData, record) {
607 let task = record.data;
608 let type = task.type || task.worker_type;
609 let id = task.id || task.worker_id;
610
611 return Proxmox.Utils.format_task_description(type, id);
612 },
613
614 render_uptime: function(value) {
615 let uptime = value;
616
617 if (uptime === undefined) {
618 return '';
619 }
620
621 if (uptime <= 0) {
622 return '-';
623 }
624
625 return Proxmox.Utils.format_duration_long(uptime);
626 },
627
628 systemd_unescape: function(string_value) {
629 const charcode_0 = '0'.charCodeAt(0);
630 const charcode_9 = '9'.charCodeAt(0);
631 const charcode_A = 'A'.charCodeAt(0);
632 const charcode_F = 'F'.charCodeAt(0);
633 const charcode_a = 'a'.charCodeAt(0);
634 const charcode_f = 'f'.charCodeAt(0);
635 const charcode_x = 'x'.charCodeAt(0);
636 const charcode_minus = '-'.charCodeAt(0);
637 const charcode_slash = '/'.charCodeAt(0);
638 const charcode_backslash = '\\'.charCodeAt(0);
639
640 let parse_hex_digit = function(d) {
641 if (d >= charcode_0 && d <= charcode_9) {
642 return d - charcode_0;
643 }
644 if (d >= charcode_A && d <= charcode_F) {
645 return d - charcode_A + 10;
646 }
647 if (d >= charcode_a && d <= charcode_f) {
648 return d - charcode_a + 10;
649 }
650 throw "got invalid hex digit";
651 };
652
653 let value = new TextEncoder().encode(string_value);
654 let result = new Uint8Array(value.length);
655
656 let i = 0;
657 let result_len = 0;
658
659 while (i < value.length) {
660 let c0 = value[i];
661 if (c0 === charcode_minus) {
662 result.set([charcode_slash], result_len);
663 result_len += 1;
664 i += 1;
665 continue;
666 }
667 if ((i + 4) < value.length) {
668 let c1 = value[i+1];
669 if (c0 === charcode_backslash && c1 === charcode_x) {
670 let h1 = parse_hex_digit(value[i+2]);
671 let h0 = parse_hex_digit(value[i+3]);
672 let ord = h1*16+h0;
673 result.set([ord], result_len);
674 result_len += 1;
675 i += 4;
676 continue;
677 }
678 }
679 result.set([c0], result_len);
680 result_len += 1;
681 i += 1;
682 }
683
684 return new TextDecoder().decode(result.slice(0, result.len));
685 },
686
687 parse_task_upid: function(upid) {
688 let task = {};
689
690 let res = upid.match(/^UPID:([^\s:]+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):(([0-9A-Fa-f]{8,16}):)?([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
691 if (!res) {
692 throw "unable to parse upid '" + upid + "'";
693 }
694 task.node = res[1];
695 task.pid = parseInt(res[2], 16);
696 task.pstart = parseInt(res[3], 16);
697 if (res[5] !== undefined) {
698 task.task_id = parseInt(res[5], 16);
699 }
700 task.starttime = parseInt(res[6], 16);
701 task.type = res[7];
702 task.id = Proxmox.Utils.systemd_unescape(res[8]);
703 task.user = res[9];
704
705 task.desc = Proxmox.Utils.format_task_description(task.type, task.id);
706
707 return task;
708 },
709
710 parse_task_status: function(status) {
711 if (status === 'OK') {
712 return 'ok';
713 }
714
715 if (status === 'unknown') {
716 return 'unknown';
717 }
718
719 let match = status.match(/^WARNINGS: (.*)$/);
720 if (match) {
721 return 'warning';
722 }
723
724 return 'error';
725 },
726
727 render_duration: function(value) {
728 if (value === undefined) {
729 return '-';
730 }
731 return Proxmox.Utils.format_duration_human(value);
732 },
733
734 render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) {
735 let servertime = new Date(value * 1000);
736 return Ext.Date.format(servertime, 'Y-m-d H:i:s');
737 },
738
739 render_zfs_health: function(value) {
740 if (typeof value === 'undefined') {
741 return "";
742 }
743 var iconCls = 'question-circle';
744 switch (value) {
745 case 'AVAIL':
746 case 'ONLINE':
747 iconCls = 'check-circle good';
748 break;
749 case 'REMOVED':
750 case 'DEGRADED':
751 iconCls = 'exclamation-circle warning';
752 break;
753 case 'UNAVAIL':
754 case 'FAULTED':
755 case 'OFFLINE':
756 iconCls = 'times-circle critical';
757 break;
758 default: //unknown
759 }
760
761 return '<i class="fa fa-' + iconCls + '"></i> ' + value;
762 },
763
764 get_help_info: function(section) {
765 let helpMap;
766 if (typeof proxmoxOnlineHelpInfo !== 'undefined') {
767 helpMap = proxmoxOnlineHelpInfo; // eslint-disable-line no-undef
768 } else if (typeof pveOnlineHelpInfo !== 'undefined') {
769 // be backward compatible with older pve-doc-generators
770 helpMap = pveOnlineHelpInfo; // eslint-disable-line no-undef
771 } else {
772 throw "no global OnlineHelpInfo map declared";
773 }
774
775 if (helpMap[section]) {
776 return helpMap[section];
777 }
778 // try to normalize - and _ separators, to support asciidoc and sphinx
779 // references at the same time.
780 let section_minus_normalized = section.replace(/_/g, '-');
781 if (helpMap[section_minus_normalized]) {
782 return helpMap[section_minus_normalized];
783 }
784 let section_underscore_normalized = section.replace(/-/g, '_');
785 return helpMap[section_underscore_normalized];
786 },
787
788 get_help_link: function(section) {
789 let info = Proxmox.Utils.get_help_info(section);
790 if (!info) {
791 return undefined;
792 }
793 return window.location.origin + info.link;
794 },
795
796 openXtermJsViewer: function(vmtype, vmid, nodename, vmname, cmd) {
797 let url = Ext.Object.toQueryString({
798 console: vmtype, // kvm, lxc, upgrade or shell
799 xtermjs: 1,
800 vmid: vmid,
801 vmname: vmname,
802 node: nodename,
803 cmd: cmd,
804
805 });
806 let nw = window.open("?" + url, '_blank', 'toolbar=no,location=no,status=no,menubar=no,resizable=yes,width=800,height=420');
807 if (nw) {
808 nw.focus();
809 }
810 },
811
812 },
813
814 singleton: true,
815 constructor: function() {
816 let me = this;
817 Ext.apply(me, me.utilities);
818
819 let IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])";
820 let IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")";
821 let IPV6_H16 = "(?:[0-9a-fA-F]{1,4})";
822 let IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")";
823 let IPV4_CIDR_MASK = "([0-9]{1,2})";
824 let IPV6_CIDR_MASK = "([0-9]{1,3})";
825
826
827 me.IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$");
828 me.IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")/" + IPV4_CIDR_MASK + "$");
829
830 /* eslint-disable no-useless-concat,no-multi-spaces */
831 let IPV6_REGEXP = "(?:" +
832 "(?:(?:" + "(?:" + IPV6_H16 + ":){6})" + IPV6_LS32 + ")|" +
833 "(?:(?:" + "::" + "(?:" + IPV6_H16 + ":){5})" + IPV6_LS32 + ")|" +
834 "(?:(?:(?:" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){4})" + IPV6_LS32 + ")|" +
835 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,1}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){3})" + IPV6_LS32 + ")|" +
836 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,2}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){2})" + IPV6_LS32 + ")|" +
837 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,3}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){1})" + IPV6_LS32 + ")|" +
838 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,4}" + IPV6_H16 + ")?::" + ")" + IPV6_LS32 + ")|" +
839 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,5}" + IPV6_H16 + ")?::" + ")" + IPV6_H16 + ")|" +
840 "(?:(?:(?:(?:" + IPV6_H16 + ":){0,7}" + IPV6_H16 + ")?::" + ")" + ")" +
841 ")";
842 /* eslint-enable no-useless-concat,no-multi-spaces */
843
844 me.IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$");
845 me.IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")/" + IPV6_CIDR_MASK + "$");
846 me.IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]");
847
848 me.IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$");
849 me.IP64_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + "/" + IPV6_CIDR_MASK + ")|(?:" + IPV4_REGEXP + "/" + IPV4_CIDR_MASK + ")$");
850
851 let 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])?))";
852 me.DnsName_match = new RegExp("^" + DnsName_REGEXP + "$");
853
854 me.HostPort_match = new RegExp("^(" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")(?::(\\d+))?$");
855 me.HostPortBrackets_match = new RegExp("^\\[(" + IPV6_REGEXP + "|" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")\\](?::(\\d+))?$");
856 me.IP6_dotnotation_match = new RegExp("^(" + IPV6_REGEXP + ")(?:\\.(\\d+))?$");
857 me.Vlan_match = /^vlan(\\d+)/;
858 me.VlanInterface_match = /(\\w+)\\.(\\d+)/;
859 },
860 });