Added Windows Service control in remote desktop tools

This commit is contained in:
Ylian Saint-Hilaire 2019-07-31 16:49:23 -07:00
parent 4bacd57aa5
commit ebdf55844e
15 changed files with 256 additions and 38 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -672,7 +672,9 @@ function createMeshCore(agent)
case 'ps': {
// Return the list of running processes
if (data.sessionid) {
processManager.getProcesses(function (plist) { mesh.SendCommand({ "action": "msg", "type": "ps", "value": JSON.stringify(plist), "sessionid": data.sessionid }); });
processManager.getProcesses(function (plist) {
mesh.SendCommand({ "action": "msg", "type": "ps", "value": JSON.stringify(plist), "sessionid": data.sessionid });
});
}
break;
}
@ -684,6 +686,39 @@ function createMeshCore(agent)
}
break;
}
case 'services': {
// Return the list of installed services
var services = null;
try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
if (services != null) { mesh.SendCommand({ "action": "msg", "type": "services", "value": JSON.stringify(services), "sessionid": data.sessionid }); }
break;
}
case 'serviceStop': {
// Stop a service
try {
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.stop(); }
} catch (e) { }
break;
}
case 'serviceStart': {
// Start a service
try {
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.start(); }
} catch (e) { }
break;
}
/*
case 'serviceRestart': {
// Start a service
try {
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.stop(); service.start(); }
} catch (e) { }
break;
}
*/
case 'openUrl': {
// Open a local web browser and return success/fail
MeshServerLog('Opening: ' + data.url, data);
@ -2037,6 +2072,11 @@ function createMeshCore(agent)
response = JSON.stringify(addedModules);
break;
}
case 'listservices': {
var services = require('service-manager').manager.enumerateService();
response = JSON.stringify(services, null, 1);
break;
}
case 'getscript': {
if (args['_'].length != 1) {
response = 'Proper usage: getscript [scriptNumber].';

View File

@ -672,7 +672,9 @@ function createMeshCore(agent)
case 'ps': {
// Return the list of running processes
if (data.sessionid) {
processManager.getProcesses(function (plist) { mesh.SendCommand({ "action": "msg", "type": "ps", "value": JSON.stringify(plist), "sessionid": data.sessionid }); });
processManager.getProcesses(function (plist) {
mesh.SendCommand({ "action": "msg", "type": "ps", "value": JSON.stringify(plist), "sessionid": data.sessionid });
});
}
break;
}
@ -684,6 +686,39 @@ function createMeshCore(agent)
}
break;
}
case 'services': {
// Return the list of installed services
var services = null;
try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
if (services != null) { mesh.SendCommand({ "action": "msg", "type": "services", "value": JSON.stringify(services), "sessionid": data.sessionid }); }
break;
}
case 'serviceStop': {
// Stop a service
try {
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.stop(); }
} catch (e) { }
break;
}
case 'serviceStart': {
// Start a service
try {
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.start(); }
} catch (e) { }
break;
}
/*
case 'serviceRestart': {
// Start a service
try {
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.stop(); service.start(); }
} catch (e) { }
break;
}
*/
case 'openUrl': {
// Open a local web browser and return success/fail
MeshServerLog('Opening: ' + data.url, data);
@ -2037,6 +2072,11 @@ function createMeshCore(agent)
response = JSON.stringify(addedModules);
break;
}
case 'listservices': {
var services = require('service-manager').manager.enumerateService();
response = JSON.stringify(services, null, 1);
break;
}
case 'getscript': {
if (args['_'].length != 1) {
response = 'Proper usage: getscript [scriptNumber].';

18
db.js
View File

@ -531,12 +531,12 @@ module.exports.CreateDB = function (parent, func) {
}
};
obj.GetAll = function (func) { obj.file.find({}).toArray(func); };
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }, { type: 0 }).toArray(func); };
obj.GetAllTypeNoTypeField = function (type, domain, func) { obj.file.find({ type: type, domain: domain }).project({ type: 0 }).toArray(func); };
obj.GetAllTypeNoTypeFieldMeshFiltered = function (meshes, domain, type, id, func) { var x = { type: type, domain: domain, meshid: { $in: meshes } }; if (id) { x._id = id; } obj.file.find(x, { type: 0 }).toArray(func); };
obj.GetAllType = function (type, func) { obj.file.find({ type: type }).toArray(func); };
obj.GetAllIdsOfType = function (ids, domain, type, func) { obj.file.find({ type: type, domain: domain, _id: { $in: ids } }).toArray(func); };
obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }, { type: 0 }).toArray(func); };
obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }, { type: 0 }).toArray(func); };
obj.GetUserWithEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email }).project({ type: 0 }).toArray(func); };
obj.GetUserWithVerifiedEmail = function (domain, email, func) { obj.file.find({ type: 'user', domain: domain, email: email, emailVerified: true }).project({ type: 0 }).toArray(func); };
obj.Remove = function (id) { obj.file.deleteOne({ _id: id }); };
obj.RemoveAll = function (func) { obj.file.deleteMany({}, { multi: true }, func); };
obj.RemoveAllOfType = function (type, func) { obj.file.deleteMany({ type: type }, { multi: true }, func); };
@ -557,18 +557,18 @@ module.exports.CreateDB = function (parent, func) {
// Database actions on the events collection
obj.GetAllEvents = function (func) { obj.eventsfile.find({}).toArray(func); };
obj.StoreEvent = function (event) { obj.eventsfile.insertOne(event); };
obj.GetEvents = function (ids, domain, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }, { type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
obj.GetEventsWithLimit = function (ids, domain, limit, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }, { type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
obj.GetUserEvents = function (ids, domain, username, func) { obj.eventsfile.find({ domain: domain, $or: [{ ids: { $in: ids } }, { username: username }] }, { type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
obj.GetUserEventsWithLimit = function (ids, domain, username, limit, func) { obj.eventsfile.find({ domain: domain, $or: [{ ids: { $in: ids } }, { username: username }] }, { type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
obj.GetNodeEventsWithLimit = function (nodeid, domain, limit, func) { obj.eventsfile.find({ domain: domain, nodeid: nodeid }, { type: 0, etype: 0, _id: 0, domain: 0, ids: 0, node: 0, nodeid: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
obj.GetEvents = function (ids, domain, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
obj.GetEventsWithLimit = function (ids, domain, limit, func) { obj.eventsfile.find({ domain: domain, ids: { $in: ids } }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
obj.GetUserEvents = function (ids, domain, username, func) { obj.eventsfile.find({ domain: domain, $or: [{ ids: { $in: ids } }, { username: username }] }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).toArray(func); };
obj.GetUserEventsWithLimit = function (ids, domain, username, limit, func) { obj.eventsfile.find({ domain: domain, $or: [{ ids: { $in: ids } }, { username: username }] }).project({ type: 0, _id: 0, domain: 0, ids: 0, node: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
obj.GetNodeEventsWithLimit = function (nodeid, domain, limit, func) { obj.eventsfile.find({ domain: domain, nodeid: nodeid }).project({ type: 0, etype: 0, _id: 0, domain: 0, ids: 0, node: 0, nodeid: 0 }).sort({ time: -1 }).limit(limit).toArray(func); };
obj.RemoveAllEvents = function (domain) { obj.eventsfile.deleteMany({ domain: domain }, { multi: true }); };
obj.RemoveAllNodeEvents = function (domain, nodeid) { obj.eventsfile.deleteMany({ domain: domain, nodeid: nodeid }, { multi: true }); };
// Database actions on the power collection
obj.getAllPower = function (func) { obj.powerfile.find({}).toArray(func); };
obj.storePowerEvent = function (event, multiServer, func) { if (multiServer != null) { event.server = multiServer.serverid; } obj.powerfile.insertOne(event, func); };
obj.getPowerTimeline = function (nodeid, func) { obj.powerfile.find({ nodeid: { $in: ['*', nodeid] } }, { _id: 0, nodeid: 0, s: 0 }).sort({ time: 1 }).toArray(func); };
obj.getPowerTimeline = function (nodeid, func) { obj.powerfile.find({ nodeid: { $in: ['*', nodeid] } }).project({ _id: 0, nodeid: 0, s: 0 }).sort({ time: 1 }).toArray(func); };
obj.removeAllPowerEvents = function () { obj.powerfile.deleteMany({}, { multi: true }); };
obj.removeAllPowerEventsForNode = function (nodeid) { obj.powerfile.deleteMany({ nodeid: nodeid }, { multi: true }); };

View File

@ -1438,10 +1438,10 @@ function CreateMeshCentralServer(config, args) {
// List of possible mesh agents
obj.meshAgentsArchitectureNumbers = {
0: { id: 0, localname: 'Unknown', rname: 'meshconsole.exe', desc: 'Unknown agent', update: false, amt: true, platform: 'unknown', core: 'linux-noamt', rcore: 'linux-recovery', arcore: 'linux-agentrecovery' },
1: { id: 1, localname: 'MeshConsole.exe', rname: 'meshconsole.exe', desc: 'Windows x86-32 console', update: true, amt: true, platform: 'win32', core: 'windows-amt', rcore: 'windows-recovery', arcore: 'windows-agentrecovery' },
2: { id: 2, localname: 'MeshConsole64.exe', rname: 'meshconsole.exe', desc: 'Windows x86-64 console', update: true, amt: true, platform: 'win32', core: 'windows-amt', rcore: 'windows-recovery', arcore: 'windows-agentrecovery' },
3: { id: 3, localname: 'MeshService-signed.exe', rname: 'meshagent.exe', desc: 'Windows x86-32 service', update: true, amt: true, platform: 'win32', core: 'windows-amt', rcore: 'windows-recovery', arcore: 'windows-agentrecovery' },
4: { id: 4, localname: 'MeshService64-signed.exe', rname: 'meshagent.exe', desc: 'Windows x86-64 service', update: true, amt: true, platform: 'win32', core: 'windows-amt', rcore: 'windows-recovery', arcore: 'windows-agentrecovery' },
1: { id: 1, localname: 'MeshConsole.exe', rname: 'meshconsole32.exe', desc: 'Windows x86-32 console', update: true, amt: true, platform: 'win32', core: 'windows-amt', rcore: 'windows-recovery', arcore: 'windows-agentrecovery' },
2: { id: 2, localname: 'MeshConsole64.exe', rname: 'meshconsole64.exe', desc: 'Windows x86-64 console', update: true, amt: true, platform: 'win32', core: 'windows-amt', rcore: 'windows-recovery', arcore: 'windows-agentrecovery' },
3: { id: 3, localname: 'MeshService-signed.exe', rname: 'meshagent32.exe', desc: 'Windows x86-32 service', update: true, amt: true, platform: 'win32', core: 'windows-amt', rcore: 'windows-recovery', arcore: 'windows-agentrecovery' },
4: { id: 4, localname: 'MeshService64-signed.exe', rname: 'meshagent64.exe', desc: 'Windows x86-64 service', update: true, amt: true, platform: 'win32', core: 'windows-amt', rcore: 'windows-recovery', arcore: 'windows-agentrecovery' },
5: { id: 5, localname: 'meshagent_x86', rname: 'meshagent', desc: 'Linux x86-32', update: true, amt: true, platform: 'linux', core: 'linux-amt', rcore: 'linux-recovery', arcore: 'linux-agentrecovery' },
6: { id: 6, localname: 'meshagent_x86-64', rname: 'meshagent', desc: 'Linux x86-64', update: true, amt: true, platform: 'linux', core: 'linux-amt', rcore: 'linux-recovery', arcore: 'linux-agentrecovery' },
7: { id: 7, localname: 'meshagent_mips', rname: 'meshagent', desc: 'Linux MIPS', update: true, amt: false, platform: 'linux', core: 'linux-noamt', rcore: 'linux-recovery', arcore: 'linux-agentrecovery' },

View File

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

View File

@ -2056,15 +2056,34 @@ a {
margin: auto;
}
#deskToolsBar {
.deskToolsTopTab {
position:absolute;
background-color:lightgray;
padding:2px;
top:2px;
left:0px;
width:80px;
bottom:2px;
border-top-left-radius:4px;
border-top-right-radius:4px;
cursor:pointer;
}
#deskToolsAreaTop {
position: absolute;
/*
padding: 3px;
border-radius: 3px 3px 0px 0px;
top: 5px;
left: 4px;
bottom: 26px;
background-color: lightgray;
cursor: pointer;
*/
top: 0px;
left: 4px;
right: 4px;
height: 26px;
background-color: gray;
}
#deskToolsArea {
@ -2088,6 +2107,17 @@ a {
float: left;
}
#deskToolsServiceHeader {
border-bottom: 1px solid darkgray;
padding: 3px;
}
#deskToolsServiceHeader .colmn1 {
width: 50px;
padding-right: 5px;
float: left;
}
#DeskToolsProcesses {
overflow-y: scroll;
position: absolute;
@ -2097,6 +2127,15 @@ a {
color:black;
}
#DeskToolsServices {
overflow-y: scroll;
position: absolute;
top: 24px;
bottom: 0px;
width: 100%;
color:black;
}
.deskToolsBar {
padding: 3px;
}

File diff suppressed because one or more lines are too long

View File

@ -467,13 +467,26 @@
<canvas id=Desk width=640 height=480 oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event) onmousewheel=dmousewheel(event)></canvas>
</div>
<div id=DeskTools>
<a id=DeskToolsRefreshButton style="" onclick="refreshDeskTools()">Refresh</a>
<div id=DeskToolsBar>Processes</div>
<div id=deskToolsAreaTop>
<a id=DeskToolsRefreshButton style="right:2px" onclick="refreshDeskTools()">Refresh</a>
<div id=deskToolsTopTabProcess class="deskToolsTopTab" onclick="changeDeskToolTab(0)" style="left:0px;bottom:0px">Processes</div>
<div id=deskToolsTopTabService class="deskToolsTopTab" onclick="changeDeskToolTab(1)" style="display:none;left:90px;color:gray">Services</div>
</div>
<div id=deskToolsArea>
<div id=deskToolsHeader>
<a class="colmn1" title="Sort by process id" onclick=sortProcess(0)>PID</a>
<a class="colmn2" title="Sort by name" onclick=sortProcess(1)>Name</a></div>
<div id="DeskToolsProcesses" style=""></div>
<div id="DeskToolsProcessTab">
<div id=deskToolsHeader>
<a class="colmn1" title="Sort by process id" onclick=sortProcess(0)>PID</a>
<a class="colmn2" title="Sort by name" onclick=sortProcess(1)>Name</a>
</div>
<div id="DeskToolsProcesses" style=""></div>
</div>
<div id="DeskToolsServiceTab" style="display:none">
<div id=deskToolsServiceHeader>
<a class="colmn1" style="width:70px" title="Sort by state" onclick=sortService(0)>State</a>
<a class="colmn2" title="Sort by name" onclick=sortService(1)>Name</a>
</div>
<div id="DeskToolsServices" style=""></div>
</div>
</div>
</div>
<div id=p11DeskConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p11clearConsoleMsg()></div>
@ -1452,6 +1465,8 @@
addNotification(n);
} else if (message.type == 'ps') {
showDeskToolsProcesses(message);
} else if (message.type == 'services') {
showDeskToolsServices(message);
} else if ((message.type == 'getclip') && (xxdialogTag == 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
Q('d2clipText').value = message.data;
} else if ((message.type == 'setclip') && (xxdialogTag == 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
@ -5112,22 +5127,41 @@
if (QS('DeskTools').display == 'none') {
QV('DeskTools', true);
Q('DeskTools').nodeid = currentNode._id;
refreshDeskTools();
QH('DeskToolsProcesses', '');
QH('DeskToolsServices', '');
QV('deskToolsTopTabService', false);
changeDeskToolTab(0)
refreshDeskTools(0);
refreshDeskTools(1);
} else {
QV('DeskTools', false);
}
}
var deskToolTabSelection = 0;
function changeDeskToolTab(tabnum) {
deskToolTabSelection = tabnum;
QV('DeskToolsProcessTab', tabnum == 0);
QV('DeskToolsServiceTab', tabnum == 1);
QS('deskToolsTopTabProcess')['bottom'] = (tabnum == 0) ? '0px' : '3px';
QS('deskToolsTopTabService')['bottom'] = (tabnum == 1) ? '0px' : '3px';
QS('deskToolsTopTabProcess')['color'] = (tabnum == 0) ? 'black' : 'gray';
QS('deskToolsTopTabService')['color'] = (tabnum == 1) ? 'black' : 'gray';
}
// Refresh all of the desktop tool panels
function refreshDeskTools() {
function refreshDeskTools(x) {
var sel = (x == null) ? deskToolTabSelection : x;
QV('DeskToolsRefreshButton', false);
setTimeout(refreshDeskToolsEx, 500);
meshserver.send({ action: 'msg', type:'ps', nodeid: currentNode._id });
if (sel == 0) meshserver.send({ action: 'msg', type: 'ps', nodeid: currentNode._id });
if (sel == 1) meshserver.send({ action: 'msg', type: 'services', nodeid: currentNode._id });
}
function refreshDeskToolsEx() { QV('DeskToolsRefreshButton', true); }
var deskTools = { sort: 1, msg: null };
var deskTools = { sort: 1, ssort: 1, msg: null, smsg: null };
function sortProcess(sort) { deskTools.sort = sort; showDeskToolsProcesses(deskTools.msg); }
function sortProcessPid(a, b) { if (a.p > b.p) return 1; if (a.p < b.p) return (-1); return 0; }
function sortService(sort) { deskTools.ssort = sort; showDeskToolsServices(deskTools.smsg); }
function sortProcessPid(a, b) { if (a.p > b.p) return 1; if (a.p < b.p) return (-1); return sortProcessName(a, b); }
function sortProcessName(a, b) { if (a.d > b.d) return 1; if (a.d < b.d) return (-1); return 0; }
function showDeskToolsProcesses(message) {
deskTools.msg = message;
@ -5149,6 +5183,68 @@
QH('DeskToolsProcesses', x);
}
}
function showDeskToolsServices(message) {
deskTools.smsg = message;
if (message == null) { QH('DeskToolsProcesses', ''); return; }
if (Q('DeskTools').nodeid != message.nodeid) return;
QV('deskToolsTopTabService', true);
var s = [], services = null;
try { services = JSON.parse(message.value); } catch (e) { }
deskTools.services = services;
if (services != null) {
for (var i in services) {
if (services[i].status) {
s.push({ p: capitalizeFirstLetter(services[i].status.state.toLowerCase()), d: services[i].displayName, i: i });
}
}
if (deskTools.ssort == 0) { s.sort(sortProcessPid); } else if (deskTools.ssort == 1) { s.sort(sortProcessName); }
var x = '';
for (var i in s) {
if (s[i].p != 0) {
var c = s[i].d;
if (c.length > 30) { c = '<span title="' + c + '">' + c.substring(0, 30) + '...</span>' }
x += '<div onclick=showServiceDetailsDialog(' + s[i].i + ') class=deskToolsBar><div style=width:70px;float:left;padding-right:5px>' + s[i].p + '</div><div>' + c + '</div></div>';
}
}
QH('DeskToolsServices', x);
}
}
function showServiceDetailsDialog(index) {
if (xxdialogMode) return;
var service = deskTools.services[index];
if (service != null) {
var x = '';
if (service.name) { x += addHtmlValue('Name', service.name); }
if (service.displayName) { x += addHtmlValue('Display name', service.displayName); }
if (service.status) {
if (service.status.state) { x += addHtmlValue('State', capitalizeFirstLetter(service.status.state.toLowerCase())); }
if (service.status.pid) { x += addHtmlValue('PID', service.status.pid); }
var serviceTypes = [];
if (service.status.isFileSystemDriver === true) { serviceTypes.push('FileSystemDriver'); }
if (service.status.isInteractive === true) { serviceTypes.push('Interactive'); }
if (service.status.isKernelDriver === true) { serviceTypes.push('KernelDriver'); }
if (service.status.isOwnProcess === true) { serviceTypes.push('OwnProcess'); }
if (service.status.isSharedProcess === true) { serviceTypes.push('SharedProcess'); }
if (serviceTypes.length > 0) { x += addHtmlValue('Type', serviceTypes.join(', ')); }
}
x += '<br/><div style=float:right;margin-bottom:12px><input type=button value="Close" onclick=showServiceDetailsDialogEx(0,' + index + ')></div><div style=margin-bottom:12px><input type=button value="Start" onclick=showServiceDetailsDialogEx(1,' + index + ')><input type=button value="Stop" onclick=showServiceDetailsDialogEx(2,' + index + ')></div>';
// <input type=button value="Restart" onclick=showServiceDetailsDialogEx(3,' + index + ')>
setDialogMode(2, "Service Details", 8, null, x, name);
}
}
function showServiceDetailsDialogEx(action, index) {
setDialogMode(0);
if (action == 0) return;
var service = deskTools.services[index];
if (service != null) {
if (action == 1) { meshserver.send({ action: 'msg', type: 'serviceStart', nodeid: currentNode._id, serviceName: service.name }); }
if (action == 2) { meshserver.send({ action: 'msg', type: 'serviceStop', nodeid: currentNode._id, serviceName: service.name }); }
//if (action == 3) { meshserver.send({ action: 'msg', type: 'serviceRestart', nodeid: currentNode._id, serviceName: service.name }); }
setTimeout(function () { refreshDeskTools(1) }, 1000);
}
}
// Toggle mouse and keyboard input
function toggleKvmControl() { putstore('DeskControl', (Q("DeskControl").checked?1:0)); }
@ -7609,9 +7705,9 @@
userid = decodeURIComponent(userid);
var user = users[userid.toLowerCase()], groups = "";
if (user.groups != null) { groups = user.groups.join(', ') }
var x = 'Enter a comma seperate list of groups.<br /><br />';
x += addHtmlValue('Groups', '<input id=dp4usergroups style=width:230px value="' + groups + '" placeholder="Group1, Group2, Group3" maxlength=256 onchange=p4validateUserGroups() onkeyup=p4validateUserGroups() />');
setDialogMode(2, "User Groups", 3, showUserGroupDialogEx, x, user);
var x = 'Enter a comma seperate list of administrative realms names.<br /><br />';
x += addHtmlValue('Realms', '<input id=dp4usergroups style=width:230px value="' + groups + '" placeholder="Name1, Name2, Name3" maxlength=256 onchange=p4validateUserGroups() onkeyup=p4validateUserGroups() />');
setDialogMode(2, "Administrative Realms", 3, showUserGroupDialogEx, x, user);
focusTextBox('dp4usergroups');
p4validateUserGroups();
return false;
@ -7757,10 +7853,12 @@
}
x += addDeviceAttribute('Device Groups', linkCountStr);
// User Groups
var userGroups = '<i>None</i>';
if (user.groups) { userGroups = ''; for (var i in user.groups) { userGroups += '<span class="tagSpan">' + user.groups[i] + '</span>'; } }
x += addDeviceAttribute('User Groups', addLinkConditional(userGroups, 'showUserGroupDialog(event,\"' + userid + '\")', (userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.groups == null) && (userinfo.siteadmin & 2) && (userinfo._id != user._id) && (user._id != 0xFFFFFFFF))));
// Administrative Realms
if ((userinfo.siteadmin == 0xFFFFFFFF) || (userinfo.siteadmin & 2)) {
var userGroups = '<i>None</i>';
if (user.groups) { userGroups = ''; for (var i in user.groups) { userGroups += '<span class="tagSpan">' + user.groups[i] + '</span>'; } }
x += addDeviceAttribute('Admin Realms', addLinkConditional(userGroups, 'showUserGroupDialog(event,\"' + userid + '\")', (userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.groups == null) && (userinfo._id != user._id) && (user.siteadmin != 0xFFFFFFFF))));
}
var multiFactor = 0;
if ((user.otpsecret > 0) || (user.otphkeys > 0)) {
@ -8579,6 +8677,7 @@
function u2fSupported() { return (window.u2f && ((navigator.userAgent.indexOf('Chrome/') > 0) || (navigator.userAgent.indexOf('Firefox/') > 0) || (navigator.userAgent.indexOf('Opera/') > 0) || (navigator.userAgent.indexOf('Safari/') > 0))); }
function findOne(arr1, arr2) { if ((arr1 == null) || (arr2 == null)) return false; return arr2.some(function (v) { return arr1.indexOf(v) >= 0; }); };
function copyTextToClip(txt) { function selectElementText(e) { if (document.selection) { var range = document.body.createTextRange(); range.moveToElementText(e); range.select(); } else if (window.getSelection) { var range = document.createRange(); range.selectNode(e); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); } } var e = document.createElement('DIV'); e.textContent = txt; document.body.appendChild(e); selectElementText(e); document.execCommand('copy'); e.remove(); }
function capitalizeFirstLetter(x) { return x.charAt(0).toUpperCase() + x.slice(1); }
function printDate(d) { return d.toLocaleDateString(args.locale); }
function printTime(d) { return d.toLocaleTimeString(args.locale); }
function printDateTime(d) { return d.toLocaleString(args.locale); }

File diff suppressed because one or more lines are too long