Added PowerShell support

This commit is contained in:
Ylian Saint-Hilaire 2019-07-24 16:29:11 -07:00
parent 0031db0c14
commit 82e4415843
8 changed files with 8506 additions and 44 deletions

View File

@ -940,11 +940,11 @@ function createMeshCore(agent)
if (data == 'c') { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ } if (data == 'c') { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
} else { } else {
// Handle tunnel data // Handle tunnel data
if (this.httprequest.protocol == 0) { // 1 = Terminal, 2 = Desktop, 5 = Files if (this.httprequest.protocol == 0) { // 1 = Terminal, 2 = Desktop, 5 = Files, 6 = PowerShell
// Take a look at the protocol // Take a look at the protocol
this.httprequest.protocol = parseInt(data); this.httprequest.protocol = parseInt(data);
if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; } if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
if (this.httprequest.protocol == 1) { if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6)) {
// Check user access rights for terminal // Check user access rights for terminal
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) { if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
// Disengage this tunnel, user does not have the rights to do this!! // Disengage this tunnel, user does not have the rights to do this!!
@ -954,7 +954,6 @@ function createMeshCore(agent)
return; return;
} }
this.end = function () { this.end = function () {
if (process.platform == "win32") { if (process.platform == "win32") {
// Unpipe the web socket // Unpipe the web socket
@ -977,7 +976,11 @@ function createMeshCore(agent)
// Remote terminal using native pipes // Remote terminal using native pipes
if (process.platform == "win32") { if (process.platform == "win32") {
this.httprequest._term = require('win-terminal').Start(80, 25); if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true)) {
this.httprequest._term = require('win-terminal').StartPowerShell(80, 25);
} else {
this.httprequest._term = require('win-terminal').Start(80, 25);
}
this.httprequest._term.pipe(this, { dataTypeSkip: 1 }); this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false }); this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); }); this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); });
@ -1428,7 +1431,7 @@ function createMeshCore(agent)
} }
ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
} else if (obj.type == 'webrtc1') { } else if (obj.type == 'webrtc1') {
if (ws.httprequest.protocol == 1) { // Terminal if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
// Switch the user input from websocket to webrtc at this point. // Switch the user input from websocket to webrtc at this point.
if (process.platform == 'win32') if (process.platform == 'win32')
{ {
@ -1450,7 +1453,7 @@ function createMeshCore(agent)
ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}"); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point. ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}"); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
} else if (obj.type == 'webrtc2') { } else if (obj.type == 'webrtc2') {
// Other side received websocket end of data marker, start sending data on WebRTC channel // Other side received websocket end of data marker, start sending data on WebRTC channel
if (ws.httprequest.protocol == 1) { // Terminal if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
if (process.platform == 'win32') if (process.platform == 'win32')
{ {
ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
@ -1465,7 +1468,7 @@ function createMeshCore(agent)
} }
} else if (obj.type == 'offer') { } else if (obj.type == 'offer') {
// This is a WebRTC offer. // This is a WebRTC offer.
if (ws.httprequest.protocol == 1) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now. if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
ws.webrtc = rtc.createConnection(); ws.webrtc = rtc.createConnection();
ws.webrtc.websocket = ws; ws.webrtc.websocket = ws;
ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ }); ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });

View File

@ -940,11 +940,11 @@ function createMeshCore(agent)
if (data == 'c') { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ } if (data == 'c') { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
} else { } else {
// Handle tunnel data // Handle tunnel data
if (this.httprequest.protocol == 0) { // 1 = Terminal, 2 = Desktop, 5 = Files if (this.httprequest.protocol == 0) { // 1 = Terminal, 2 = Desktop, 5 = Files, 6 = PowerShell
// Take a look at the protocol // Take a look at the protocol
this.httprequest.protocol = parseInt(data); this.httprequest.protocol = parseInt(data);
if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; } if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
if (this.httprequest.protocol == 1) { if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6)) {
// Check user access rights for terminal // Check user access rights for terminal
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) { if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
// Disengage this tunnel, user does not have the rights to do this!! // Disengage this tunnel, user does not have the rights to do this!!
@ -954,7 +954,6 @@ function createMeshCore(agent)
return; return;
} }
this.end = function () { this.end = function () {
if (process.platform == "win32") { if (process.platform == "win32") {
// Unpipe the web socket // Unpipe the web socket
@ -977,7 +976,11 @@ function createMeshCore(agent)
// Remote terminal using native pipes // Remote terminal using native pipes
if (process.platform == "win32") { if (process.platform == "win32") {
this.httprequest._term = require('win-terminal').Start(80, 25); if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true)) {
this.httprequest._term = require('win-terminal').StartPowerShell(80, 25);
} else {
this.httprequest._term = require('win-terminal').Start(80, 25);
}
this.httprequest._term.pipe(this, { dataTypeSkip: 1 }); this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false }); this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); }); this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); });
@ -1428,7 +1431,7 @@ function createMeshCore(agent)
} }
ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
} else if (obj.type == 'webrtc1') { } else if (obj.type == 'webrtc1') {
if (ws.httprequest.protocol == 1) { // Terminal if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
// Switch the user input from websocket to webrtc at this point. // Switch the user input from websocket to webrtc at this point.
if (process.platform == 'win32') if (process.platform == 'win32')
{ {
@ -1450,7 +1453,7 @@ function createMeshCore(agent)
ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}"); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point. ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}"); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
} else if (obj.type == 'webrtc2') { } else if (obj.type == 'webrtc2') {
// Other side received websocket end of data marker, start sending data on WebRTC channel // Other side received websocket end of data marker, start sending data on WebRTC channel
if (ws.httprequest.protocol == 1) { // Terminal if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
if (process.platform == 'win32') if (process.platform == 'win32')
{ {
ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
@ -1465,7 +1468,7 @@ function createMeshCore(agent)
} }
} else if (obj.type == 'offer') { } else if (obj.type == 'offer') {
// This is a WebRTC offer. // This is a WebRTC offer.
if (ws.httprequest.protocol == 1) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now. if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
ws.webrtc = rtc.createConnection(); ws.webrtc = rtc.createConnection();
ws.webrtc.websocket = ws; ws.webrtc.websocket = ws;
ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ }); ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.3.8-l", "version": "0.3.8-n",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",

View File

@ -13,6 +13,7 @@ var CreateAmtRemoteTerminal = function (divid, options) {
obj.DivId = divid; obj.DivId = divid;
obj.DivElement = document.getElementById(divid); obj.DivElement = document.getElementById(divid);
obj.protocol = 1; // SOL obj.protocol = 1; // SOL
if (options.protocol) { obj.protocol = options.protocol; } // 1 = Normal, 6 = PowerShell
// ###BEGIN###{Terminal-Enumation-All} // ###BEGIN###{Terminal-Enumation-All}
obj.terminalEmulation = 1; obj.terminalEmulation = 1;
// ###END###{Terminal-Enumation-All} // ###END###{Terminal-Enumation-All}

File diff suppressed because one or more lines are too long

8433
views/default-old.handlebars Normal file

File diff suppressed because it is too large Load Diff

View File

@ -45,11 +45,17 @@
<hr id="cxmgroupsplit" /> <hr id="cxmgroupsplit" />
<div id="cxmdesktop" class="cmtext" onclick="cmaction(7,event)" style="display:none">Multi-Desktop</div> <div id="cxmdesktop" class="cmtext" onclick="cmaction(7,event)" style="display:none">Multi-Desktop</div>
</div> </div>
<div id="meshContextMenu" class="contextMenu,noselect" style="display: none; min-width: 0px"> <div id="meshContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
<div id="cxselectall" class="cmtext" onclick="cmmeshaction(1,event)">Select All</div> <div id="cxselectall" class="cmtext" onclick="cmmeshaction(1,event)">Select All</div>
<div id="cxselectnone" class="cmtext" onclick="cmmeshaction(2,event)">Select None</div> <div id="cxselectnone" class="cmtext" onclick="cmmeshaction(2,event)">Select None</div>
<!--
<hr id="cxmgroupsplit2" style="display:none" /> <hr id="cxmgroupsplit2" style="display:none" />
<div id="cxmmdesktop" class="cmtext" style="display:none" onclick="cmmeshaction(3,event)">Multi-Desktop</div> <div id="cxmmdesktop" class="cmtext" style="display:none" onclick="cmmeshaction(3,event)">Multi-Desktop</div>
-->
</div>
<div id="termShellContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
<div id="cxtermnorm" class="cmtext" onclick="cmtermaction(1,event)">Normal Connect</div>
<div id="cxtermps" class="cmtext" onclick="cmtermaction(2,event)">PowerShell Connect</div>
</div> </div>
<!-- main page --> <!-- main page -->
<div id=container> <div id=container>
@ -519,7 +525,7 @@
<tr> <tr>
<td class="areaHead"> <td class="areaHead">
<div class="toright2"> <div class="toright2">
<input id="termActionsBtn" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction()/> <input id="termActionsBtn" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() />
</div> </div>
<div> <div>
<input type="button" id="autoconnectbutton2" value="AutoConnect" onclick=autoConnectTerminal(event) onkeypress="return false" onkeydown="return false" style="display:none"> <input type="button" id="autoconnectbutton2" value="AutoConnect" onclick=autoConnectTerminal(event) onkeypress="return false" onkeydown="return false" style="display:none">
@ -3142,7 +3148,13 @@
var scrollLeft = (window.pageXOffset !== null) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft; var scrollLeft = (window.pageXOffset !== null) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
var scrollTop = (window.pageYOffset !== null) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; var scrollTop = (window.pageYOffset !== null) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
var elem = document.elementFromPoint(event.pageX - scrollLeft, event.pageY - scrollTop); var elem = document.elementFromPoint(event.pageX - scrollLeft, event.pageY - scrollTop);
if (elem && elem != null && elem.id == "MxMESH") { if (elem && elem != null && elem.id == "connectbutton2" && currentNode && currentNode.agent && (currentNode.agent.id > 0) && (currentNode.agent.id < 5)) {
contextelement = elem;
var contextmenudiv = document.getElementById("termShellContextMenu");
contextmenudiv.style.left = event.pageX + "px";
contextmenudiv.style.top = event.pageY + "px";
contextmenudiv.style.display = "block";
} else if (elem && elem != null && elem.id == "MxMESH") {
contextelement = elem; contextelement = elem;
var contextmenudiv = document.getElementById("meshContextMenu"); var contextmenudiv = document.getElementById("meshContextMenu");
contextmenudiv.style.left = event.pageX + "px"; contextmenudiv.style.left = event.pageX + "px";
@ -3156,26 +3168,26 @@
contextmenudiv.style.left = event.pageX + "px"; contextmenudiv.style.left = event.pageX + "px";
contextmenudiv.style.top = event.pageY + "px"; contextmenudiv.style.top = event.pageY + "px";
contextmenudiv.style.display = "block"; contextmenudiv.style.display = "block";
// Get the node and set the menu options
var nodeid = contextelement.children[1].attributes.onclick.value;
var node = getNodeFromId(nodeid.substring(12, nodeid.length - 18));
var mesh = meshes[node.meshid];
var meshlinks = mesh.links[userinfo._id];
var meshrights = meshlinks.rights;
var consoleRights = ((meshrights & 16) != 0);
// Check if we have terminal and file access
var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
QV('cxdesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((meshrights & 8) || (meshrights & 256)));
QV('cxterminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (meshrights & 8) && terminalAccess);
QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess);
QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8));
QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
} }
// Get the node and set the menu options
var nodeid = contextelement.children[1].attributes.onclick.value;
var node = getNodeFromId(nodeid.substring(12, nodeid.length - 18));
var mesh = meshes[node.meshid];
var meshlinks = mesh.links[userinfo._id];
var meshrights = meshlinks.rights;
var consoleRights = ((meshrights & 16) != 0);
// Check if we have terminal and file access
var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
QV('cxdesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((meshrights & 8) || (meshrights & 256)));
QV('cxterminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (meshrights & 8) && terminalAccess);
QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess);
QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8));
QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
return haltEvent(event); return haltEvent(event);
} }
@ -3204,17 +3216,25 @@
} }
function cmmeshaction(action) { function cmmeshaction(action) {
var meshid = contextelement.attributes.onclick.value.substring(32, (32 + 69)); var meshid = contextelement.attributes.onclick.value.substring(10, contextelement.attributes.onclick.value.length - 2);
var elements = document.getElementsByClassName("DeviceCheckbox"); var elements = document.getElementsByClassName("DeviceCheckbox");
if (action == 1) { for (var i = 0; i < elements.length; i++) { if ( (elements[i].attributes) && (elements[i].attributes['class']['value'].substring(0, 69) == meshid)) { elements[i].checked = true; } } } if ((action == 1) || (action == 2)) {
if (action == 2) { for (var i = 0; i < elements.length; i++) { if ( (elements[i].attributes) && (elements[i].attributes['class']['value'].substring(0, 69) == meshid)) { elements[i].checked = false; } } } for (var i = 0; i < elements.length; i++) {
if ((elements[i].attributes) && (elements[i].attributes['class']['value'].split(' ')[0] == meshid)) { elements[i].checked = (action == 1); }
}
}
//if (action == 3) { window.location = "multidesktop.aspx?mesh=" + meshid + "&auto=1"; } //if (action == 3) { window.location = "multidesktop.aspx?mesh=" + meshid + "&auto=1"; }
p1updateInfo(); p1updateInfo();
} }
function cmtermaction(action) {
connectTerminal(null, 1, { powershell: (action == 2) });
}
function hideContextMenu() { function hideContextMenu() {
QV('contextMenu', false); QV('contextMenu', false);
QV('meshContextMenu', false); QV('meshContextMenu', false);
QV('termShellContextMenu', false);
contextelement = null; contextelement = null;
} }
@ -5166,7 +5186,7 @@
var autoConnectTerminalTimer = null; var autoConnectTerminalTimer = null;
function autoConnectTerminal(e) { if (autoConnectTerminalTimer == null) { autoConnectTerminalTimer = setInterval(connectTerminal, 100); } else { clearInterval(autoConnectTerminalTimer); autoConnectTerminalTimer = null; } } function autoConnectTerminal(e) { if (autoConnectTerminalTimer == null) { autoConnectTerminalTimer = setInterval(connectTerminal, 100); } else { clearInterval(autoConnectTerminalTimer); autoConnectTerminalTimer = null; } }
function connectTerminal(e, contype) { function connectTerminal(e, contype, options) {
p12clearConsoleMsg(); p12clearConsoleMsg();
if (!terminal) { if (!terminal) {
if (contype == 2) { if (contype == 2) {
@ -5194,6 +5214,7 @@
termoptions.xterm = true; termoptions.xterm = true;
} }
} }
if ((e && (e.shiftKey == true)) || (options && (options.powershell))) { termoptions.protocol = 6; }
terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term', termoptions), serverPublicNamePort, authCookie, domainUrl); terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term', termoptions), serverPublicNamePort, authCookie, domainUrl);
terminal.debugmode = debugmode; terminal.debugmode = debugmode;
terminal.m.debugmode = debugmode; terminal.m.debugmode = debugmode;
@ -6554,7 +6575,7 @@
function p20editmesh(focus) { function p20editmesh(focus) {
if (xxdialogMode) return; if (xxdialogMode) return;
var x = addHtmlValue('Name', '<input id=dp20meshname style=width:230px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate() />'); var x = addHtmlValue('Name', '<input id=dp20meshname style=width:230px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate(event) />');
x += addHtmlValue('Description', '<div style=width:230px;margin:0;padding:0><textarea id=dp20meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>'); x += addHtmlValue('Description', '<div style=width:230px;margin:0;padding:0><textarea id=dp20meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
setDialogMode(2, "Edit Device Group", 3, p20editmeshEx, x); setDialogMode(2, "Edit Device Group", 3, p20editmeshEx, x);
Q('dp20meshname').value = currentMesh.name; Q('dp20meshname').value = currentMesh.name;
@ -6567,8 +6588,9 @@
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, meshname: Q('dp20meshname').value, desc: Q('dp20meshdesc').value }); meshserver.send({ action: 'editmesh', meshid: currentMesh._id, meshname: Q('dp20meshname').value, desc: Q('dp20meshdesc').value });
} }
function p20editmeshValidate() { function p20editmeshValidate(e) {
QE('idx_dlgOkButton', Q('dp20meshname').value.length > 0); QE('idx_dlgOkButton', Q('dp20meshname').value.length > 0);
if (e && e.key == 'Enter') { Q('dp20meshdesc').focus(); }
} }
function p20editmeshconsent() { function p20editmeshconsent() {