More web application UI improvements.

Ylian Saint-Hilaire committed Apr 15, 2020 at 19:42 UTC e58838dfe868d619c9c2be71e6ab040b639792c3
5 files changed +119 -95
meshcentral.js
+1 -7
@@ -2458,13 +2458,7 @@ function mainStart() {
2458 if (require('os').platform() == 'win32') { modules.push('node-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
2459 if (ldap == true) { modules.push('ldapauth-fork'); }
2460 if (recordingIndex == true) { modules.push('image-size'); } // Need to get the remote desktop JPEG sizes to index the recodring file.
2461 - if (config.letsencrypt != null) {
2462 - if (config.letsencrypt.lib == 'greenlock') {
2463 - if ((nodeVersion < 10) || (require('crypto').generateKeyPair == null)) { addServerWarning("Let's Encrypt support requires Node v10.12 or higher.", !args.launch); } else { modules.push('greenlock@4.0.4'); }
2464 - } else {
2465 - if (nodeVersion < 8) { addServerWarning("Let's Encrypt support requires Node v8.x or higher.", !args.launch); } else { modules.push('acme-client'); }
2466 - }
2467 - } // Add Greenlock Module or acme-client module
2461 + if (config.letsencrypt != null) { if (nodeVersion < 8) { addServerWarning("Let's Encrypt support requires Node v8.x or higher.", !args.launch); } else { modules.push('acme-client'); } } // Add acme-client module
2462 if (config.settings.mqtt != null) { modules.push('aedes'); } // Add MQTT Modules
2463 if (config.settings.mysql != null) { modules.push('mysql'); } // Add MySQL, official driver.
2464 if (config.settings.mongodb != null) { modules.push('mongodb'); } // Add MongoDB, official driver.
meshuser.js
+6 -1
@@ -3081,7 +3081,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
3081 if ((typeof command.icon == 'number') && (command.icon != node.icon)) { change = 1; node.icon = command.icon; changes.push('icon'); }
3082 if ((typeof command.name == 'string') && (command.name != node.name)) { change = 1; node.name = command.name; changes.push('name'); }
3083 if ((typeof command.host == 'string') && (command.host != node.host)) { change = 1; node.host = command.host; changes.push('host'); }
3084 - if (typeof command.consent == 'number') { if (((command.consent != 0) && ((node.consent == null) || (node.consent == 0))) || ((command.consent == 0) && (node.consent != null) && (node.consent != 0))) { change = 1; if (command.consent == 0) { delete node.consent; } else { node.consent = command.consent; } changes.push('consent'); } }
3084 + if (typeof command.consent == 'number') {
3085 + var oldConsent = node.consent;
3086 + if (command.consent != node.consent) { node.consent = command.consent; }
3087 + if (command.consent == 0) { delete node.consent; }
3088 + if (oldConsent != node.consent) { change = 1; changes.push('consent'); }
3089 + }
3090
3091 if ((typeof command.rdpport == 'number') && (command.rdpport > 0) && (command.rdpport < 65536)) {
3092 if ((command.rdpport == 3389) && (node.rdpport != null)) {
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.5.1-v",
3 + "version": "0.5.1-w",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
translate/translate.json
+27 -16
@@ -3924,7 +3924,12 @@
3924 },
3925 {
3926 "en": "Ask Consent",
3927 - "en": "Vraag toestemming",
3927 + "xloc": [
3928 + "default.handlebars->deskConnectContextMenu->cxdeskuc->0"
3929 + ]
3930 + },
3931 + {
3932 + "en": "Ask Consent + Bar",
3933 "xloc": [
3934 "default.handlebars->deskConnectContextMenu->cxdeskuc->0"
3935 ]
@@ -8207,7 +8212,7 @@
8212 "en": "Devices",
8213 "nl": "Apparaten",
8214 "xloc": [
8210 - "default.handlebars->29->1441",
8215 + "default.handlebars->29->1443",
8216 "default.handlebars->29->1465"
8217 ]
8218 },
@@ -8793,18 +8798,7 @@
8798 ]
8799 },
8800 {
8796 - "cs": "Duplikovat agenta",
8797 - "de": "Agent-Duplikat",
8798 - "en": "Duplicate agent",
8799 - "es": "Agente duplicado",
8800 - "fr": "Cloner l'agent",
8801 - "hi": "डुप्लीकेट एजेंट",
8802 - "ja": "重複エージェント",
8803 - "ko": "중복 에이전트",
8804 - "nl": "Duplicaat Agent",
8805 - "pt": "Agente duplicado",
8806 - "ru": "Скопировать агент",
8807 - "zh-chs": "代理重複",
8801 + "en": "Duplicate Agent",
8802 "xloc": [
8803 "default.handlebars->29->1623"
8804 ]
@@ -8843,6 +8837,20 @@
8837 "default.handlebars->29->1457"
8838 ]
8839 },
8840 + {
8841 + "cs": "Duplikovat agenta",
8842 + "de": "Agent-Duplikat",
8843 + "en": "Duplicate agent",
8844 + "es": "Agente duplicado",
8845 + "fr": "Cloner l'agent",
8846 + "hi": "डुप्लीकेट एजेंट",
8847 + "ja": "重複エージェント",
8848 + "ko": "중복 에이전트",
8849 + "nl": "Duplicaat Agent",
8850 + "pt": "Agente duplicado",
8851 + "ru": "Скопировать агент",
8852 + "zh-chs": "代理重複"
8853 + },
8854 {
8855 "cs": "Délka trvání",
8856 "de": "Dauer",
@@ -26413,7 +26421,7 @@
26421 "ru": "Пользователи",
26422 "zh-chs": "用戶數",
26423 "xloc": [
26416 - "default.handlebars->29->1443",
26424 + "default.handlebars->29->1441",
26425 "default.handlebars->29->1463",
26426 "default.handlebars->29->1642",
26427 "default.handlebars->container->topbar->1->1->UsersSubMenuSpan->UsersSubMenu->1->0->UsersGeneral"
@@ -26710,6 +26718,9 @@
26718 "default.handlebars->29->1028"
26719 ]
26720 },
26721 + {
26722 + "en": "Vraag toestemming"
26723 + },
26724 {
26725 "cs": "VAROVÁNÍ: ",
26726 "de": "WARNUNG: ",
@@ -29515,4 +29526,4 @@
29526 ]
29527 }
29528 ]
29518 -}
29529 +}
\ No newline at end of file
views/default.handlebars
+84 -70
@@ -70,6 +70,7 @@
70 <div id="cxtermps" class="cmtext" onclick="cmtermaction(100,event)">Login Shell</div>
71 </div>
72 <div id="deskConnectContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
73 + <div id="cxdeskuc" class="cmtext" onclick="cmdeskaction(1,event)"><b>Ask Consent + Bar</b></div>
74 <div id="cxdeskuc" class="cmtext" onclick="cmdeskaction(1,event)"><b>Ask Consent</b></div>
75 </div>
76 <div id="altPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
@@ -4105,7 +4106,8 @@
4106 }
4107
4108 function cmdeskaction(action) {
4108 - if (action == 1) { connectDesktop(null, 3, null, 0x0008); } // Do remote desktop connection using consent prompt
4109 + if (action == 1) { connectDesktop(null, 3, null, 0x0008 + 0x0040); } // Do remote desktop connection using Consent Prompt + Tool bar
4110 + if (action == 2) { connectDesktop(null, 3, null, 0x0008); } // Do remote desktop connection using Consent Prompt
4111 }
4112
4113 function cmaltportaction(action) {
@@ -4989,7 +4991,9 @@
4991 {{{StartGeoLocationJS}}}
4992 if (xxmap != null) x += '<a href=# onclick=p10showNodeLocationDialog("' + node._id + '") title=\"' + "Show device locations information" + '\">' + "Location" + '</a>&nbsp;';
4993 {{{EndGeoLocationJS}}}
4992 - if ((terminalAccess) && ((meshrights & 8) != 0) && (node.agent != null)) x += '<a href=# onclick=p10showMeshCmdDialog(1,"' + node._id + '") title=\"' + "Traffic router used to connect to a device thru this server" + '.\">' + "MeshCmd" + '</a>&nbsp;';
4994 + if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) { // Check if we should view tools
4995 + if ((terminalAccess) && ((meshrights & 8) != 0) && (node.agent != null)) x += '<a href=# onclick=p10showMeshCmdDialog(1,"' + node._id + '") title=\"' + "Traffic router used to connect to a device thru this server" + '.\">' + "MeshCmd" + '</a>&nbsp;';
4996 + }
4997 if ((args.xterm === 0) && (node.agent) && ((node.agent.caps & 2) != 0) && ((meshrights & 8) != 0) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0))) { x += '<a href=# onclick=p10openxterm(event,"' + node._id + '") title=\"' + "Open XTerm terminal" + '\">' + "XTerm" + '</a>&nbsp;'; }
4998
4999 // RDP link, show this link only of the remote machine is Windows.
@@ -5118,10 +5122,10 @@
5122 if ((meshrights & 2) != 0) {
5123 if (ugroup) {
5124 trash = '<a href=# onclick=\'return p30removeUserFromNode(event,"' + encodeURIComponent(userid) + '")\' title=\"' + "Remove user group rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
5121 - rights = '<div style=cursor:pointer onclick=p20showAddMeshUserDialog(6,\"' + encodeURIComponent(userid) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></div>';
5125 + rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(6,\"' + encodeURIComponent(userid) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
5126 } else {
5127 trash = '<a href=# onclick=\'return p30removeUserFromNode(event,"' + encodeURIComponent(userid) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
5124 - rights = '<div style=cursor:pointer onclick=p20showAddMeshUserDialog(5,\"' + encodeURIComponent(userid) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></div>';
5128 + rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(5,\"' + encodeURIComponent(userid) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
5129 }
5130 }
5131 if (users != null) {
@@ -8363,7 +8367,7 @@
8367 var trash = '', r = sortedusers[i].rights, rights = makeDeviceGroupRightsString(r), icon = 2;
8368 if ((sortedusers[i].id != userinfo._id) && (meshrights == 0xFFFFFFFF || (((meshrights & 2) != 0)))) {
8369 trash = '<a href=# onclick=\'return p20deleteUser(event,"' + encodeURIComponent(sortedusers[i].id) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
8366 - rights = '<div tabindex=0 style=cursor:pointer onclick=p20viewuser("' + encodeURIComponent(sortedusers[i].id) + '") onkeypress="if (event.key==\'Enter\') p20viewuser(\'' + encodeURIComponent(sortedusers[i].id) + '\')">' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></div>';
8370 + rights = '<span tabindex=0 style=cursor:pointer onclick=p20viewuser("' + encodeURIComponent(sortedusers[i].id) + '") onkeypress="if (event.key==\'Enter\') p20viewuser(\'' + encodeURIComponent(sortedusers[i].id) + '\')">' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
8371 }
8372 if (sortedusers[i].id.startsWith('ugrp/')) { icon = 4; }
8373 var username = EscapeHtml(decodeURIComponent(sortedusers[i].name));
@@ -8576,44 +8580,50 @@
8580 x += addHtmlValue("User Names", '<input id=dp20username style=width:230px maxlength=32 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() placeholder="user1, user2, user3" />');
8581 x += '<div id=dp20usersuggest class=suggestionBox style=\'top:30px;left:130px;display:none\'></div>';
8582 x += '</div><br>';
8579 - } else if (userid === 1) {
8583 + } else if (userid == 1) {
8584 var y = '';
8585 if (selected == null) {
8582 - for (var i in meshes) { if ((currentUser.links == null) || (currentUser.links[i] == null)) { y += '<option value=' + encodeURIComponent(i) + '>' + EscapeHtml(meshes[i].name) + '</option>'; } }
8586 + var omeshs = getOrderedList(meshes, 'name');
8587 + for (var i in omeshs) { if ((currentUser.links == null) || (currentUser.links[omeshs[i]._id] == null)) { y += '<option value=' + encodeURIComponent(omeshs[i]._id) + '>' + EscapeHtml(omeshs[i].name) + '</option>'; } }
8588 } else {
8589 y += '<option value=' + selected + '>' + EscapeHtml(meshes[decodeURIComponent(selected)].name) + '</option>';
8590 }
8591 x += addHtmlValue("Device Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog() id=dp2groupid style=width:100%' + (selected?' disabled':'') + '>' + y + '</select></div>');
8587 - } else if (userid === 2) {
8592 + } else if (userid == 2) {
8593 if (usergroups == null) return;
8594 var y = '';
8590 - for (var i in usergroups) { if ((currentMesh.links == null) || (currentMesh.links[i] == null)) { y += '<option value=' + encodeURIComponent(i) + '>' + EscapeHtml(usergroups[i].name) + '</option>'; } }
8595 + var ousergroups = getOrderedList(usergroups, 'name');
8596 + for (var i in ousergroups) { if ((currentMesh.links == null) || (currentMesh.links[ousergroups[i]._id] == null)) { y += '<option value=' + encodeURIComponent(ousergroups[i]._id) + '>' + EscapeHtml(ousergroups[i].name) + '</option>'; } }
8597 x += addHtmlValue("User Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog() id=dp2groupid style=width:100%>' + y + '</select></div>');
8592 - } else if (userid === 6) {
8598 + } else if (userid == 6) {
8599 if (usergroups == null) return;
8600 var y = '';
8601 if (selected == null) {
8596 - for (var i in usergroups) { if ((currentNode.links == null) || (currentNode.links[i] == null)) { y += '<option value=' + encodeURIComponent(i) + '>' + EscapeHtml(usergroups[i].name) + '</option>'; } }
8602 + var ousergroups = getOrderedList(usergroups, 'name');
8603 + for (var i in ousergroups) { if ((currentNode.links == null) || (currentNode.links[ousergroups[i]._id] == null)) { y += '<option value=' + encodeURIComponent(ousergroups[i]._id) + '>' + EscapeHtml(ousergroups[i].name) + '</option>'; } }
8604 } else {
8605 y += '<option value=' + selected + '>' + EscapeHtml(usergroups[decodeURIComponent(selected)].name) + '</option>';
8606 }
8607 x += addHtmlValue("User Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog() id=dp2groupid style=width:100%' + (selected?' disabled':'') + '>' + y + '</select></div>');
8601 - } else if (userid === 3) {
8608 + } else if (userid == 3) {
8609 var y = '';
8610 if (selected) { selected = decodeURIComponent(selected); }
8604 - for (var i in meshes) { if ((selected != null) || (currentUserGroup.links == null) || (currentUserGroup.links[i] == null)) { y += '<option value=' + encodeURIComponent(i) + ((selected == i)?' selected':' ') + '>' + EscapeHtml(meshes[i].name) + '</option>'; } }
8605 - x += addHtmlValue("Device Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog() id=dp2groupid style=width:100%>' + y + '</select></div>');
8606 - } else if ((userid === 4) || (userid == 7)) {
8611 + var omeshs = getOrderedList(meshes, 'name');
8612 + for (var i in omeshs) { if ((selected != null) || (currentUserGroup.links == null) || (currentUserGroup.links[i] == null)) { y += '<option value=' + encodeURIComponent(omeshs[i]._id) + ((selected == omeshs[i]._id)?' selected':' ') + '>' + EscapeHtml(omeshs[i].name) + '</option>'; } }
8613 + x += addHtmlValue("Device Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog(' + userid + ') id=dp2groupid style=width:100%>' + y + '</select></div>');
8614 + } else if ((userid == 4) || (userid == 7)) {
8615 var y = '', selectedMeshId = null, selectedNode = null;
8616 if (selected != null) { selectedNode = getNodeFromId(decodeURIComponent(selected)); if (selectedNode != null) { selectedMeshId = selectedNode.meshid; } }
8609 - for (var i in meshes) {
8610 - if ((meshes[i].links[userinfo._id] != null) && (meshes[i].links[userinfo._id].rights & 7)) { // Only show device groups that we have user administrator for.
8611 - if (selectedMeshId == null) { selectedMeshId = meshes[i]._id; } y += '<option value=' + encodeURIComponent(meshes[i]._id) + ((selectedMeshId == meshes[i]._id)?' selected':' ') + '>' + EscapeHtml(meshes[i].name) + '</option>';
8617 + var omeshs = getOrderedList(meshes, 'name');
8618 + for (var i in omeshs) {
8619 + if ((omeshs[i].links[userinfo._id] != null) && (omeshs[i].links[userinfo._id].rights & 7)) { // Only show device groups that we have user administrator for.
8620 + if (selectedMeshId == null) { selectedMeshId = omeshs[i]._id; } y += '<option value=' + encodeURIComponent(omeshs[i]._id) + ((selectedMeshId == omeshs[i]._id)?' selected':' ') + '>' + EscapeHtml(omeshs[i].name) + '</option>';
8621 }
8622 }
8623 x += addHtmlValue("Device Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20changeMeshAddMeshUserDialog(' + userid + ') id=dp2meshid style=width:100%>' + y + '</select></div>');
8624 y = '';
8616 - for (var i in nodes) { if (nodes[i].meshid == selectedMeshId) { y += '<option value=' + encodeURIComponent(nodes[i]._id) + ((selectedNode == nodes[i])?' selected':' ') + '>' + EscapeHtml(nodes[i].name) + '</option>'; } }
8625 + var onodes = getOrderedList(nodes, 'name');
8626 + for (var i in onodes) { if (onodes[i].meshid == selectedMeshId) { y += '<option value=' + encodeURIComponent(onodes[i]._id) + ((selectedNode == onodes[i])?' selected':' ') + '>' + EscapeHtml(onodes[i].name) + '</option>'; } }
8627 x += addHtmlValue("Device", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog(' + userid + ') id=dp2nodeid style=width:100%>' + y + '</select></div>');
8628 } else {
8629 userid = decodeURIComponent(userid);
@@ -8726,11 +8736,11 @@
8736 return false;
8737 }
8738
8729 - function p20changeMeshAddMeshUserDialog() {
8739 + function p20changeMeshAddMeshUserDialog(userid) {
8740 var y = '', meshid = decodeURIComponent(Q('dp2meshid').value);
8741 for (var i in nodes) { if (nodes[i].meshid == meshid) { y += '<option value=' + encodeURIComponent(nodes[i]._id) + '>' + EscapeHtml(nodes[i].name) + '</option>'; } }
8742 QH('dp2nodeid', y);
8733 - p20validateAddMeshUserDialog(4);
8743 + p20validateAddMeshUserDialog(userid);
8744 }
8745
8746 function p20setname(name) {
@@ -8749,6 +8759,7 @@
8759 if (updateId === 4) {
8760 // Update user device rights
8761 var devrights = 0, nodeid = decodeURIComponent(Q('dp2nodeid').value);
8762 + console.log('nodeid', nodeid);
8763 if ((nodeid != '') && (currentUser.links != null) && (currentUser.links[nodeid] != null)) { devrights = currentUser.links[nodeid].rights; }
8764 Q('p20remotecontrol').checked = ((devrights & 8) != 0);
8765 Q('p20meshagentconsole').checked = ((devrights & 16) != 0);
@@ -10103,7 +10114,7 @@
10114 } else {
10115 // Display the groups using the sorted list
10116 x += '<table class=p3usersTable cellpadding=0 cellspacing=0>';
10106 - x += '<th>' + "Name" + '<th style=width:80px>' + "Devices" + '<th style=width:80px>' + "Device Groups" + '<th style=width:80px>' + "Users";
10117 + x += '<th>' + "Name" + '<th style=width:80px>' + "Users" + '<th style=width:80px>' + "Device Groups" + '<th style=width:80px>' + "Devices";
10118 for (var i in sortedGroups) { x += addUserGroupHtml(sortedGroups[i]); }
10119 x += '</table>';
10120 QV('DuplicateUserGroupButton', true);
@@ -10127,7 +10138,7 @@
10138 x += '<div class=baricon><input class=UserGroupCheckbox value=' + encodeURIComponent(group._id) + ' onclick=p50updateInfo() type=checkbox></div>';
10139 x += '<div class=baricon onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")><div class=m4></div></div>';
10140 x += '<div class=g1 onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")></div><div class=g2 onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")></div>';
10130 - x += '<div onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")><span style=font-size:16px>' + group.name + '</span></div></div><td style=text-align:center>' + devicecount + '<td style=text-align:center>' + meshcount + '<td style=text-align:center>' + usercount;
10141 + x += '<div onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")><span style=font-size:16px>' + group.name + '</span></div></div><td style=text-align:center>' + usercount + '<td style=text-align:center>' + meshcount + '<td style=text-align:center>' + devicecount;
10142 return x;
10143 }
10144
@@ -10282,19 +10293,19 @@
10293 if ((deviceGroupCount > 0) && (newDeviceGroup)) { x += '<a href=# onclick="return p20showAddMeshUserDialog(3)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Device Group" + '</a>'; }
10294 x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Common Device Groups" + '</th><th scope=col style=text-align:left></th></tr>';
10295 if (currentUserGroup.links) {
10285 - for (var i in currentUserGroup.links) {
10286 - if (i.startsWith('mesh/')) {
10287 - var cr = 0, r = currentUserGroup.links[i].rights, mesh = meshes[i], trash = '', rights = makeDeviceGroupRightsString(r);
10288 - if (mesh == null) { continue; }
10289 - if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
10290 - var meshname = '<i>' + "Unknown Device Group" + '</i>';
10291 - if (mesh) { meshname = '<a href=# onclick=\'gotoMesh("' + mesh._id + '");haltEvent(event);\'>' + mesh.name + '</a>'; } else {}
10292 - if ((cr & 2) != 0) {
10293 - trash = '<a href=# onclick=\'return p51removeMeshFromUserGroup(event,"' + encodeURIComponent(mesh._id) + '")\' title=\"' + "Remove user group rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
10294 - rights = '<div style=cursor:pointer onclick=p20showAddMeshUserDialog(3,\"' + encodeURIComponent(mesh._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></div>';
10295 - }
10296 - x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div>&nbsp;' + meshname + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
10296 + var omeshes = [];
10297 + for (var i in currentUserGroup.links) { if (i.startsWith('mesh/')) { if (meshes[i] != null) { omeshes.push(meshes[i]); } } }
10298 + omeshes = getOrderedList(omeshes, 'name');
10299 + for (var i in omeshes) {
10300 + var cr = 0, mesh = omeshes[i], r = currentUserGroup.links[mesh._id].rights, trash = '', rights = makeDeviceGroupRightsString(r);
10301 + if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
10302 + var meshname = '<i>' + "Unknown Device Group" + '</i>';
10303 + if (mesh) { meshname = '<a href=# onclick=\'gotoMesh("' + mesh._id + '");haltEvent(event);\'>' + mesh.name + '</a>'; } else {}
10304 + if ((cr & 2) != 0) {
10305 + trash = '<a href=# onclick=\'return p51removeMeshFromUserGroup(event,"' + encodeURIComponent(mesh._id) + '")\' title=\"' + "Remove user group rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
10306 + rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(3,\"' + encodeURIComponent(mesh._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
10307 }
10308 + x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div>&nbsp;' + meshname + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
10309 }
10310 }
10311 if (count == 1) { x += '<tr><td><div style=padding:6px>&nbsp;<i>' + "No device groups in common" + '</i><div></div></div></td><td></td></tr>'; }
@@ -10305,18 +10316,18 @@
10316 x += '<br /><a href=# onclick="return p20showAddMeshUserDialog(7)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Device" + '</a>';
10317 x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Common Devices" + '</th><th scope=col style=text-align:left></th></tr>';
10318 if (currentUserGroup.links) {
10308 - for (var i in currentUserGroup.links) {
10309 - if (i.startsWith('node/')) {
10310 - var r = currentUserGroup.links[i].rights, node = getNodeFromId(i), trash = '', rights = makeUserDeviceRightsString(r), cr = GetNodeRights(node);
10311 - if (node == null) { continue; }
10312 - var nodename = '<i>' + "Unknown Device" + '</i>';
10313 - if (node) { nodename = '<a href=# onclick=\'gotoDevice("' + node._id + '");haltEvent(event);\'>' + node.name + '</a>'; } else {}
10314 - if ((cr & 2) != 0) {
10315 - trash = '<a href=# onclick=\'return p51removeDeviceFromUserGroup(event,"' + encodeURIComponent(node._id) + '")\' title=\"' + "Remove user group rights to this device" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
10316 - rights = '<div style=cursor:pointer onclick=p20showAddMeshUserDialog(7,\"' + encodeURIComponent(node._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></div>';
10317 - }
10318 - x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div>&nbsp;' + nodename + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
10319 + var onodes = [];
10320 + for (var i in currentUserGroup.links) { if (i.startsWith('node/')) { var node = getNodeFromId(i); if (node != null) { onodes.push(node); } } }
10321 + onodes = getOrderedList(onodes, 'name');
10322 + for (var i in onodes) {
10323 + var node = onodes[i], r = currentUserGroup.links[node._id].rights, trash = '', rights = makeUserDeviceRightsString(r), cr = GetNodeRights(node);
10324 + var nodename = '<i>' + "Unknown Device" + '</i>';
10325 + if (node) { nodename = '<a href=# onclick=\'gotoDevice("' + node._id + '");haltEvent(event);\'>' + node.name + '</a>'; } else {}
10326 + if ((cr & 2) != 0) {
10327 + trash = '<a href=# onclick=\'return p51removeDeviceFromUserGroup(event,"' + encodeURIComponent(node._id) + '")\' title=\"' + "Remove user group rights to this device" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
10328 + rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(7,\"' + encodeURIComponent(node._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
10329 }
10330 + x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div>&nbsp;' + nodename + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
10331 }
10332 }
10333 if (count == 1) { x += '<tr><td><div style=padding:6px>&nbsp;<i>' + "No devices in common" + '</i><div></div></div></td><td></td></tr>'; }
@@ -10690,25 +10701,25 @@
10701 var count = 1, x = '';
10702
10703 // Display common device groups
10693 - count = 1;
10704 var deviceGroupCount = 0, newDeviceGroup = false;
10705 for (var i in meshes) { deviceGroupCount++; if ((currentUser.links == null) || (currentUser.links[i] == null)) { newDeviceGroup = true; } }
10706 if ((deviceGroupCount > 0) && (newDeviceGroup)) { x += '<a href=# onclick="return p20showAddMeshUserDialog(1)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Device Group" + '</a>'; }
10707 x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Common Device Groups" + '</th><th scope=col style=text-align:left></th></tr>';
10708 if (currentUser.links) {
10699 - for (var i in currentUser.links) {
10700 - if (i.startsWith('mesh/')) {
10701 - var cr = 0, r = currentUser.links[i].rights, mesh = meshes[i], trash = '', rights = makeDeviceGroupRightsString(r);
10702 - if (mesh == null) { continue; }
10703 - if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
10704 - var meshname = '<i>' + "Unknown Device Group" + '</i>';
10705 - if (mesh) { meshname = '<a href=# onclick=\'gotoMesh("' + mesh._id + '");haltEvent(event);\'>' + EscapeHtml(mesh.name) + '</a>'; } else {}
10706 - if ((currentUser._id != userinfo._id) && ((cr & 2) != 0)) {
10707 - rights = '<div style=cursor:pointer onclick=p20showAddMeshUserDialog(1,\"' + encodeURIComponent(mesh._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></div>';
10708 - trash = '<a href=# onclick=\'return p30removeMeshFromUser(event,"' + encodeURIComponent(mesh._id) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
10709 - }
10710 - x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div>&nbsp;' + meshname + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
10709 + var omeshes = [];
10710 + for (var i in currentUser.links) { if (i.startsWith('mesh/')) { if (meshes[i] != null) { omeshes.push(meshes[i]); } } }
10711 + omeshes = getOrderedList(omeshes, 'name');
10712 + for (var i in omeshes) {
10713 + var cr = 0, mesh = omeshes[i], r = currentUser.links[mesh._id].rights, trash = '', rights = makeDeviceGroupRightsString(r);
10714 + if (mesh == null) { continue; }
10715 + if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
10716 + var meshname = '<i>' + "Unknown Device Group" + '</i>';
10717 + if (mesh) { meshname = '<a href=# onclick=\'gotoMesh("' + mesh._id + '");haltEvent(event);\'>' + EscapeHtml(mesh.name) + '</a>'; } else {}
10718 + if ((currentUser._id != userinfo._id) && ((cr & 2) != 0)) {
10719 + trash = '<a href=# onclick=\'return p30removeMeshFromUser(event,"' + encodeURIComponent(mesh._id) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
10720 + rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(1,\"' + encodeURIComponent(mesh._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
10721 }
10722 + x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div>&nbsp;' + meshname + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
10723 }
10724 }
10725 if (count == 1) { x += '<tr><td><div style=padding:6px>&nbsp;<i>' + "No device groups in common" + '</i><div></div></div></td><td></td></tr>'; }
@@ -10725,17 +10736,18 @@
10736 }
10737 x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "User Group Memberships" + '</th><th scope=col style=text-align:left></th></tr>';
10738 if (currentUser.links) {
10728 - for (var i in currentUser.links) {
10729 - if (i.startsWith('ugrp/')) {
10730 - var r = currentUser.links[i].rights, group = usergroups[i], trash = '';
10731 - var groupname = '<i>' + "Unknown User Group" + '</i>';
10732 - if (group != null) {
10733 - groupname = EscapeHtml(group.name);
10734 - if (usergroups != null) { groupname = '<a href=# onclick=\'gotoUserGroup("' + encodeURIComponent(i) + '");haltEvent(event);\'>' + groupname + '</a>'; }
10735 - }
10736 - if ((userinfo.siteadmin & 256) != 0) { trash = '<a href=# onclick=\'return p30RemoveUserGroup(event,"' + encodeURIComponent(i) + '")\' title=\"' + "Remove user group membership" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
10737 - x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td><div title=\"' + "User Group" + '\" class=m4></div><div>&nbsp;' + groupname + '<div></div></div></td><td><div style=float:right>' + trash + '</div></td></tr>';
10739 + var ougroups = [];
10740 + for (var i in currentUser.links) { if (i.startsWith('ugrp/')) { if (usergroups[i] != null) { ougroups.push(usergroups[i]); } } }
10741 + ougroups = getOrderedList(ougroups, 'name');
10742 + for (var i in ougroups) {
10743 + var group = usergroups[i], r = currentUser.links[ougroups[i]._id].rights, trash = '';
10744 + var groupname = '<i>' + "Unknown User Group" + '</i>';
10745 + if (group != null) {
10746 + groupname = EscapeHtml(group.name);
10747 + if (usergroups != null) { groupname = '<a href=# onclick=\'gotoUserGroup("' + encodeURIComponent(i) + '");haltEvent(event);\'>' + groupname + '</a>'; }
10748 }
10749 + if ((userinfo.siteadmin & 256) != 0) { trash = '<a href=# onclick=\'return p30RemoveUserGroup(event,"' + encodeURIComponent(i) + '")\' title=\"' + "Remove user group membership" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
10750 + x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td><div title=\"' + "User Group" + '\" class=m4></div><div>&nbsp;' + groupname + '<div></div></div></td><td><div style=float:right>' + trash + '</div></td></tr>';
10751 }
10752 }
10753 if (count == 1) { x += '<tr><td><div style=padding:6px>&nbsp;<i>' + "No user group memberships" + '</i><div></div></div></td><td></td></tr>'; }
@@ -10743,6 +10755,7 @@
10755 }
10756
10757 // Display common devices
10758 + count = 1;
10759 x += '<br /><a href=# onclick="return p20showAddMeshUserDialog(4)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Device" + '</a>';
10760 x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Common Devices" + '</th><th scope=col style=text-align:left></th></tr>';
10761 if (currentUser.links) {
@@ -10755,8 +10768,8 @@
10768 if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
10769 var nodename = node?EscapeHtml(node.name):('<i>' + "Unknown Device" + '</i>');
10770 if ((cr & 2) != 0) {
10758 - rights = '<div style=cursor:pointer onclick=p20showAddMeshUserDialog(4,\"' + encodeURIComponent(node._id) + '\")>' + makeUserDeviceRightsString(r) + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></div>';
10771 trash = '<a href=# onclick=\'return p30removeNodeFromUser(event,"' + encodeURIComponent(node._id) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
10772 + rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(4,\"' + encodeURIComponent(node._id) + '\")>' + makeUserDeviceRightsString(r) + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
10773 }
10774 nodename = '<a href=# onclick=\'gotoDevice("' + node._id + '",10);haltEvent(event);\'>' + nodename + '</a>';
10775 x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device" + '\" class=si' + node.icon + '></div><div>&nbsp;' + nodename + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
@@ -11960,6 +11973,7 @@
11973 function addDetailItem(title, value, state) { return '<table style=width:100%><td>' + nobreak(title) + '<td style=text-align:right>' + value + '</table>'; }
11974 function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
11975 function addTextLink(subtext, text, link) { var i = text.toLowerCase().indexOf(subtext.toLowerCase()); if (i == -1) { return text; } return text.substring(0, i) + '<a href=\"' + link + '\">' + subtext + '</a>' + text.substring(i + subtext.length); }
11976 + function getOrderedList(objList, oname) { var r = []; for (var i in objList) { r.push(objList[i]); } r.sort(function(a, b) { var aa = a[oname].toLowerCase(), bb = b[oname].toLowerCase(); if (aa > bb) return 1; if (aa < bb) return -1; return 0; }); return r; }
11977 function nobreak(x) { return x.split(' ').join('&nbsp;'); }
11978
11979 </script>