]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/Toolkit.js
add HostList validator and check monhosts with it
[pve-manager.git] / www / manager6 / Toolkit.js
1 /*global IP4_match, IP4_cidr_match, IP6_match, IP6_cidr_match, IP64_match, DnsName_match, DnsName_REGEXP, IPV4_REGEXP, IPV6_REGEXP*/
2 /*global HostPort_match, HostPortBrackets_match, IP6_dotnotation_match*/
3 // ExtJS related things
4
5 PVE.Utils.toolkit = 'extjs';
6
7 // do not send '_dc' parameter
8 Ext.Ajax.disableCaching = false;
9
10 // custom Vtypes
11 Ext.apply(Ext.form.field.VTypes, {
12 IPAddress: function(v) {
13 return IP4_match.test(v);
14 },
15 IPAddressText: gettext('Example') + ': 192.168.1.1',
16 IPAddressMask: /[\d\.]/i,
17
18 IPCIDRAddress: function(v) {
19 var result = IP4_cidr_match.exec(v);
20 // limits according to JSON Schema see
21 // pve-common/src/PVE/JSONSchema.pm
22 return (result !== null && result[1] >= 8 && result[1] <= 32);
23 },
24 IPCIDRAddressText: gettext('Example') + ': 192.168.1.1/24' + "<br>" + gettext('Valid CIDR Range') + ': 8-32',
25 IPCIDRAddressMask: /[\d\.\/]/i,
26
27 IP6Address: function(v) {
28 return IP6_match.test(v);
29 },
30 IP6AddressText: gettext('Example') + ': 2001:DB8::42',
31 IP6AddressMask: /[A-Fa-f0-9:]/,
32
33 IP6CIDRAddress: function(v) {
34 var result = IP6_cidr_match.exec(v);
35 // limits according to JSON Schema see
36 // pve-common/src/PVE/JSONSchema.pm
37 return (result !== null && result[1] >= 8 && result[1] <= 120);
38 },
39 IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "<br>" + gettext('Valid CIDR Range') + ': 8-120',
40 IP6CIDRAddressMask: /[A-Fa-f0-9:\/]/,
41
42 IP6PrefixLength: function(v) {
43 return v >= 0 && v <= 128;
44 },
45 IP6PrefixLengthText: gettext('Example') + ': X, where 0 <= X <= 128',
46 IP6PrefixLengthMask: /[0-9]/,
47
48 IP64Address: function(v) {
49 return IP64_match.test(v);
50 },
51 IP64AddressText: gettext('Example') + ': 192.168.1.1 2001:DB8::42',
52 IP64AddressMask: /[A-Fa-f0-9\.:]/,
53
54 MacAddress: function(v) {
55 return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v);
56 },
57 MacAddressMask: /[a-fA-F0-9:]/,
58 MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab',
59
60 BridgeName: function(v) {
61 return (/^vmbr\d{1,4}$/).test(v);
62 },
63 BridgeNameText: gettext('Format') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
64
65 BondName: function(v) {
66 return (/^bond\d{1,4}$/).test(v);
67 },
68 BondNameText: gettext('Format') + ': bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
69
70 InterfaceName: function(v) {
71 return (/^[a-z][a-z0-9_]{1,20}$/).test(v);
72 },
73 InterfaceNameText: gettext('Format') + ': [a-z][a-z0-9_]{1,20}',
74
75
76 QemuStartDate: function(v) {
77 return (/^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/).test(v);
78 },
79 QemuStartDateText: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"',
80
81 StorageId: function(v) {
82 return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
83 },
84 StorageIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '-', '_', '.'",
85
86 ConfigId: function(v) {
87 return (/^[a-z][a-z0-9\_]+$/i).test(v);
88 },
89 ConfigIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '_'",
90
91 HttpProxy: function(v) {
92 return (/^http:\/\/.*$/).test(v);
93 },
94 HttpProxyText: gettext('Example') + ": http://username:password&#64;host:port/",
95
96 DnsName: function(v) {
97 return DnsName_match.test(v);
98 },
99 DnsNameText: gettext('This is not a valid DNS name'),
100
101 // workaround for https://www.sencha.com/forum/showthread.php?302150
102 pveMail: function(v) {
103 return (/^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,63}$/).test(v);
104 },
105 pveMailText: gettext('Example') + ": user@example.com",
106
107 HostList: function(v) {
108 var list = v.split(/[\ \,\;]+/);
109 var i;
110 for (i = 0; i < list.length; i++) {
111 if (list[i] == "") {
112 continue;
113 }
114
115 if (!HostPort_match.test(list[i]) &&
116 !HostPortBrackets_match.test(list[i]) &&
117 !IP6_dotnotation_match.test(list[i])) {
118 return false;
119 }
120 }
121
122 return true;
123 },
124 HostListText: gettext('Not a valid list of hosts')
125 });
126
127 // ExtJs 5-6 has an issue with caching
128 // see https://www.sencha.com/forum/showthread.php?308989
129 Ext.define('PVE.UnderlayPool', {
130 override: 'Ext.dom.UnderlayPool',
131
132 checkOut: function () {
133 var cache = this.cache,
134 len = cache.length,
135 el;
136
137 // do cleanup because some of the objects might have been destroyed
138 while (len--) {
139 if (cache[len].destroyed) {
140 cache.splice(len, 1);
141 }
142 }
143 // end do cleanup
144
145 el = cache.shift();
146
147 if (!el) {
148 el = Ext.Element.create(this.elementConfig);
149 el.setVisibilityMode(2);
150 //<debug>
151 // tell the spec runner to ignore this element when checking if the dom is clean
152 el.dom.setAttribute('data-sticky', true);
153 //</debug>
154 }
155
156 return el;
157 }
158 });
159
160 // if the order of the values are not the same in originalValue and value
161 // extjs will not overwrite value, but marks the field dirty and thus
162 // the reset button will be enabled (but clicking it changes nothing)
163 // so if the arrays are not the same after resetting, we
164 // clear and set it
165 Ext.define('PVE.form.ComboBox', {
166 override: 'Ext.form.field.ComboBox',
167
168 reset: function() {
169 // copied from combobox
170 var me = this;
171 me.callParent();
172 me.applyEmptyText();
173
174 // clear and set when not the same
175 if (Ext.isArray(me.originalValue) && !Ext.Array.equals(me.getValue(), me.originalValue)) {
176 me.clearValue();
177 me.setValue(me.originalValue);
178 }
179 }
180 });
181
182 // should be fixed with ExtJS 6.0.2, see:
183 // https://www.sencha.com/forum/showthread.php?307244-Bug-with-datefield-in-window-with-scroll
184 Ext.define('PVE.Datepicker', {
185 override: 'Ext.picker.Date',
186 hideMode: 'visibility'
187 });
188
189 // force alert boxes to be rendered with an Error Icon
190 // since Ext.Msg is an object and not a prototype, we need to override it
191 // after the framework has been initiated
192 Ext.onReady(function() {
193 /*jslint confusion: true */
194 Ext.override(Ext.Msg, {
195 alert: function(title, message, fn, scope) {
196 if (Ext.isString(title)) {
197 var config = {
198 title: title,
199 message: message,
200 icon: this.ERROR,
201 buttons: this.OK,
202 fn: fn,
203 scope : scope,
204 minWidth: this.minWidth
205 };
206 return this.show(config);
207 }
208 }
209 });
210 /*jslint confusion: false */
211 });
212 Ext.define('Ext.ux.IFrame', {
213 extend: 'Ext.Component',
214
215 alias: 'widget.uxiframe',
216
217 loadMask: 'Loading...',
218
219 src: 'about:blank',
220
221 renderTpl: [
222 '<iframe src="{src}" id="{id}-iframeEl" data-ref="iframeEl" name="{frameName}" width="100%" height="100%" frameborder="0" allowfullscreen="true"></iframe>'
223 ],
224 childEls: ['iframeEl'],
225
226 initComponent: function () {
227 this.callParent();
228
229 this.frameName = this.frameName || this.id + '-frame';
230 },
231
232 initEvents : function() {
233 var me = this;
234 me.callParent();
235 me.iframeEl.on('load', me.onLoad, me);
236 },
237
238 initRenderData: function() {
239 return Ext.apply(this.callParent(), {
240 src: this.src,
241 frameName: this.frameName
242 });
243 },
244
245 getBody: function() {
246 var doc = this.getDoc();
247 return doc.body || doc.documentElement;
248 },
249
250 getDoc: function() {
251 try {
252 return this.getWin().document;
253 } catch (ex) {
254 return null;
255 }
256 },
257
258 getWin: function() {
259 var me = this,
260 name = me.frameName,
261 win = Ext.isIE
262 ? me.iframeEl.dom.contentWindow
263 : window.frames[name];
264 return win;
265 },
266
267 getFrame: function() {
268 var me = this;
269 return me.iframeEl.dom;
270 },
271
272 beforeDestroy: function () {
273 this.cleanupListeners(true);
274 this.callParent();
275 },
276
277 cleanupListeners: function(destroying){
278 var doc, prop;
279
280 if (this.rendered) {
281 try {
282 doc = this.getDoc();
283 if (doc) {
284 /*jslint nomen: true*/
285 Ext.get(doc).un(this._docListeners);
286 /*jslint nomen: false*/
287 if (destroying && doc.hasOwnProperty) {
288 for (prop in doc) {
289 if (doc.hasOwnProperty(prop)) {
290 delete doc[prop];
291 }
292 }
293 }
294 }
295 } catch(e) { }
296 }
297 },
298
299 onLoad: function() {
300 var me = this,
301 doc = me.getDoc(),
302 fn = me.onRelayedEvent;
303
304 if (doc) {
305 try {
306 // These events need to be relayed from the inner document (where they stop
307 // bubbling) up to the outer document. This has to be done at the DOM level so
308 // the event reaches listeners on elements like the document body. The effected
309 // mechanisms that depend on this bubbling behavior are listed to the right
310 // of the event.
311 /*jslint nomen: true*/
312 Ext.get(doc).on(
313 me._docListeners = {
314 mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
315 mousemove: fn, // window resize drag detection
316 mouseup: fn, // window resize termination
317 click: fn, // not sure, but just to be safe
318 dblclick: fn, // not sure again
319 scope: me
320 }
321 );
322 /*jslint nomen: false*/
323 } catch(e) {
324 // cannot do this xss
325 }
326
327 // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
328 Ext.get(this.getWin()).on('beforeunload', me.cleanupListeners, me);
329
330 this.el.unmask();
331 this.fireEvent('load', this);
332
333 } else if (me.src) {
334
335 this.el.unmask();
336 this.fireEvent('error', this);
337 }
338
339
340 },
341
342 onRelayedEvent: function (event) {
343 // relay event from the iframe's document to the document that owns the iframe...
344
345 var iframeEl = this.iframeEl,
346
347 // Get the left-based iframe position
348 iframeXY = iframeEl.getTrueXY(),
349 originalEventXY = event.getXY(),
350
351 // Get the left-based XY position.
352 // This is because the consumer of the injected event will
353 // perform its own RTL normalization.
354 eventXY = event.getTrueXY();
355
356 // the event from the inner document has XY relative to that document's origin,
357 // so adjust it to use the origin of the iframe in the outer document:
358 event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];
359
360 event.injectEvent(iframeEl); // blame the iframe for the event...
361
362 event.xy = originalEventXY; // restore the original XY (just for safety)
363 },
364
365 load: function (src) {
366 var me = this,
367 text = me.loadMask,
368 frame = me.getFrame();
369
370 if (me.fireEvent('beforeload', me, src) !== false) {
371 if (text && me.el) {
372 me.el.mask(text);
373 }
374
375 frame.src = me.src = (src || me.src);
376 }
377 }
378 });