diff --git a/agents/MeshCmd-signed.exe b/agents/MeshCmd-signed.exe index 480af74f..32c2eb02 100644 Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ diff --git a/agents/MeshCmd64-signed.exe b/agents/MeshCmd64-signed.exe index ebb4dea6..9949a757 100644 Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ diff --git a/agents/MeshService-signed.exe b/agents/MeshService-signed.exe index 2a1bdb4a..2477cda3 100644 Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ diff --git a/agents/MeshService.exe b/agents/MeshService.exe index 9a6fa53f..d5833e5e 100644 Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ diff --git a/agents/MeshService64-signed.exe b/agents/MeshService64-signed.exe index e6af1837..2158f8e5 100644 Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ diff --git a/agents/MeshService64.exe b/agents/MeshService64.exe index 8d841301..400584ef 100644 Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ diff --git a/agents/meshcore.js b/agents/meshcore.js index 68bb7435..791f7d34 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -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].'; diff --git a/agents/meshcore.min.js b/agents/meshcore.min.js index 68bb7435..791f7d34 100644 --- a/agents/meshcore.min.js +++ b/agents/meshcore.min.js @@ -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].'; diff --git a/db.js b/db.js index bea2ddd1..dfdb0b2a 100644 --- a/db.js +++ b/db.js @@ -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 }); }; diff --git a/meshcentral.js b/meshcentral.js index f83da4df..e9801b43 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -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' }, diff --git a/package.json b/package.json index 11615a11..b90ea903 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meshcentral", - "version": "0.3.8-x", + "version": "0.3.8-y", "keywords": [ "Remote Management", "Intel AMT", diff --git a/public/styles/style.css b/public/styles/style.css index e3dd8ec5..1a26113a 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -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; } diff --git a/views/default-min.handlebars b/views/default-min.handlebars index cbe90332..90785de7 100644 --- a/views/default-min.handlebars +++ b/views/default-min.handlebars @@ -1 +1 @@ - {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index bf1615cf..04f9cf85 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -467,13 +467,26 @@
- Refresh -
Processes
+
+ Refresh +
Processes
+ +
-
- PID - Name
-
+
+
+ PID + Name +
+
+
+
@@ -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 = '' + c.substring(0, 30) + '...' } + x += '
' + s[i].p + '
' + c + '
'; + } + } + 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 += '
'; + // + 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.

'; - x += addHtmlValue('Groups', ''); - setDialogMode(2, "User Groups", 3, showUserGroupDialogEx, x, user); + var x = 'Enter a comma seperate list of administrative realms names.

'; + x += addHtmlValue('Realms', ''); + 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 = 'None'; - if (user.groups) { userGroups = ''; for (var i in user.groups) { userGroups += '' + user.groups[i] + ''; } } - 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 = 'None'; + if (user.groups) { userGroups = ''; for (var i in user.groups) { userGroups += '' + user.groups[i] + ''; } } + 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); } diff --git a/views/messenger-min.handlebars b/views/messenger-min.handlebars index b8b80904..ded0098a 100644 --- a/views/messenger-min.handlebars +++ b/views/messenger-min.handlebars @@ -1 +1 @@ - MeshMessenger
MeshMessenger
\ No newline at end of file + MeshMessenger
MeshMessenger
\ No newline at end of file