Added mesh screen to mobile web page

Ylian Saint-Hilaire committed May 29, 2018 at 16:57 UTC 25b467b59212c4c773b4d87b58fa011880229f21
12 files changed +444 -160
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
meshcentral.js
+6 -2
@@ -99,7 +99,7 @@ function CreateMeshCentralServer(config, args) {
99 console.log(' --redirport [number] Creates an additional HTTP server to redirect users to the HTTPS server.');
100 console.log(' --exactports Server must run with correct ports or exit.');
101 console.log(' --noagentupdate Server will not update mesh agent native binaries.');
102 - console.log(' --cert [name], (country), (org) Create a web server certificate with [name]server name.');
102 + console.log(' --cert [name], (country), (org) Create a web server certificate with [name] server name.');
103 console.log(' country and organization can optionaly be set.');
104 return;
105 }
@@ -1148,9 +1148,13 @@ function mainStart(args) {
1148 var config = getConfig();
1149 if (config == null) { process.exit(); }
1150
1151 + // Check is Windows SSPI will be used
1152 + var sspi = false;
1153 + if (require('os').platform() == 'win32') { for (var i in config.domains) { if (config.domains[i].auth == 'sspi') { sspi = true; } } }
1154 +
1155 // Build the list of required modules
1156 var modules = ['ws', 'nedb', 'https', 'yauzl', 'xmldom', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'express-session', 'express-handlebars'];
1153 - if (require('os').platform() == 'win32') { modules.push('node-sspi'); modules.push('node-windows'); } // Add Windows modules
1157 + if (require('os').platform() == 'win32') { modules.push('node-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
1158 if (config.letsencrypt != null) { modules.push('greenlock'); modules.push('le-store-certbot'); modules.push('le-challenge-fs'); modules.push('le-acme-core'); } // Add Greenlock Modules
1159 if (config.settings.mongodb != null) { modules.push('mongojs'); } // Add MongoDB
1160 if (config.smtp != null) { modules.push('nodemailer'); } // Add SMTP support
meshuser.js
+1 -1
@@ -624,7 +624,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
624 if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
625
626 if ((obj.common.validateString(command.meshname, 1, 64) == true) && (command.meshname != mesh.name)) { change = 'Mesh name changed from "' + mesh.name + '" to "' + command.meshname + '"'; mesh.name = command.meshname; }
627 - if ((obj.common.validateString(command.desc, 1, 1024) == true) && (command.desc != mesh.desc)) { if (change != '') change += ' and description changed'; else change += 'Mesh "' + mesh.name + '" description changed'; mesh.desc = command.desc; }
627 + if ((obj.common.validateString(command.desc, 0, 1024) == true) && (command.desc != mesh.desc)) { if (change != '') change += ' and description changed'; else change += 'Mesh "' + mesh.name + '" description changed'; mesh.desc = command.desc; }
628 if (change != '') { obj.db.Set(mesh); obj.parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) }
629 }
630 break;
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.7-s",
3 + "version": "0.1.7-x",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
views/default-mobile.handlebars
+425 -145
@@ -20,58 +20,34 @@
20 text-decoration: underline;
21 }
22
23 - #footer a:hover {
24 - color: #fff;
25 - text-decoration: none;
26 - }
27 -
28 - .i1 {
29 - background: url(../images/icons50.png) 0px 0px;
30 - height: 50px;
31 - width: 50px;
32 - border: none;
33 - }
34 -
35 - .i2 {
36 - background: url(../images/icons50.png) -50px 0px;
37 - height: 50px;
38 - width: 50px;
39 - border: none;
40 - }
41 -
42 - .i3 {
43 - background: url(../images/icons50.png) -100px 0px;
44 - height: 50px;
45 - width: 50px;
46 - border: none;
47 - }
48 -
49 - .i4 {
50 - background: url(../images/icons50.png) -150px 0px;
51 - height: 50px;
52 - width: 50px;
53 - border: none;
54 - }
55 -
56 - .i5 {
57 - background: url(../images/icons50.png) -200px 0px;
58 - height: 50px;
59 - width: 50px;
60 - border: none;
23 + #footer a:hover {
24 + color: #fff;
25 + text-decoration: none;
26 }
27
63 - .i6 {
64 - background: url(../images/icons50.png) -250px 0px;
65 - height: 50px;
66 - width: 50px;
67 - border: none;
68 - }
28 + .i1 {background:url(../images/icons50.png) 0px 0px;height:50px;width:50px;border:none;}
29 + .i2 {background:url(../images/icons50.png) -50px 0px;height:50px;width:50px;border:none;}
30 + .i3 {background:url(../images/icons50.png) -100px 0px;height:50px;width:50px;border:none;}
31 + .i4 {background:url(../images/icons50.png) -150px 0px;height:50px;width:50px;border:none;}
32 + .i5 {background:url(../images/icons50.png) -200px 0px;height:50px;width:50px;border:none;}
33 + .i6 {background:url(../images/icons50.png) -250px 0px; height:50px;width:50px; border:none; }
34 + .m0 {background:url(../images/images16.png) -32px 0px; height:16px;width:16px; border:none;float:left }
35 + .m1 {background:url(../images/images16.png) -16px 0px; height:16px;width:16px; border:none;float:left }
36 + .m2 {background:url(../images/images16.png) -96px 0px; height:16px;width:16px; border:none;float:left }
37 + .m3 {background:url(../images/images16.png) -112px 0px; height:16px;width:16px; border:none;float:left }
38
39 .gray {
40 /*filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale");*/ /* Firefox 10+, Firefox on Android */
41 filter: gray; /* IE6-9 */
42 -webkit-filter: grayscale(100%) opacity(60%); /* Chrome 19+, Safari 6+, Safari 6+ iOS */
43 }
44 +
45 + .DevSt {
46 + padding-left:5px;
47 + border-bottom-style: solid;
48 + border-bottom-width: 1px;
49 + border-bottom-color: #DDDDDD;
50 + }
51 </style>
52 </head>
53 <body onload="if (typeof(startup) !== 'undefined') startup();" style="overflow-y:hidden;margin:0;padding:0;border:0;color:black;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif">
@@ -86,7 +62,7 @@
62 </div>
63 </div>
64 <div id=page_content style="overflow-y:scroll;position:absolute;bottom:32px;top:50px;width:100%">
89 - <div id=column_l style="width:100%;height:calc(100hv - 82px)">
65 + <div id=column_l style="width:100%;padding:0;position:absolute;bottom:0px;top:0px">
66 <div id=p0 style=display:none;width:100%;height:100%>
67 <div style="display:flex;align-items:center;width:100%;height:100%">
68 <div id=p0message style=text-align:center;width:100%>Server disconnected, <href onclick=reload() style=cursor:pointer><u>click to reconnect</u></href>.</div>
@@ -100,6 +76,51 @@
76 <div id=p2 style=display:none>
77 <div id=xdevices></div>
78 </div>
79 + <div id=p10 style=display:none;position:absolute;bottom:0;top:0;width:100%>
80 + <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;">
81 + <tr style=padding:0>
82 + <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
83 + <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
84 + <div style="background-color:white;width:10px;height:10px;border-radius:10px 0 0 0;border-right:1px solid white;border-bottom:1px solid white"></div>
85 + </div>
86 + <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">&#9664;</div>
87 + </td>
88 + <td>
89 + <a id=MainComputerImage style=cursor:pointer onclick=p10showiconselector()></a>
90 + </td>
91 + <td>
92 + <div style=margin-left:5px>
93 + <strong><span id=p10deviceName></span></strong><br />
94 + <span id=MainComputerState></span>
95 + </div>
96 + </td>
97 + </tr>
98 + </table>
99 + <div id=p10html style="margin-left:8px;margin-right:8px"></div>
100 + <div id=p10html2></div>
101 + <div id=p10html3></div>
102 + </div>
103 + <div id=p20 style=display:none;position:absolute;bottom:0;top:0;width:100%>
104 + <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;">
105 + <tr style=padding:0>
106 + <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
107 + <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
108 + <div style="background-color:white;width:10px;height:10px;border-radius:10px 0 0 0;border-right:1px solid white;border-bottom:1px solid white"></div>
109 + </div>
110 + <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">&#9664;</div>
111 + </td>
112 + <td onclick="p20editmesh(1)">
113 + <img src="/images/meshicon50.png" width=50 height=50 />
114 + </td>
115 + <td onclick="p20editmesh(1)">
116 + <div style=margin-left:5px>
117 + <strong style="font-size:large"><span id=p20meshName></span></strong><br />
118 + </div>
119 + </td>
120 + </tr>
121 + </table>
122 + <div id=p20info style="margin-left:8px;margin-right:8px"></div>
123 + </div>
124 </div>
125 </div>
126 <div id=footer style="height:32px;width:100%;text-align:center;background-color:#113962;position:absolute;bottom:0px">
@@ -111,7 +132,7 @@
132 </table>
133 </div>
134 </div>
114 - <div id=dialog style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:5px;position:fixed;top:180px;width:400px;display:none">
135 + <div id=dialog style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:5px;position:fixed;top:90px;width:300px;display:none">
136 <div style="width:100%;background-color:#003366;color:#FFF;border-radius:5px 5px 0 0">
137 <div id=id_dialogclose style=float:right;padding:5px;cursor:pointer onclick=setDialogMode()><b>X</b></div>
138 <div id=id_dialogtitle style=padding:5px></div>
@@ -225,9 +246,9 @@
246 break;
247 }
248 case 'files': {
228 - filetree = setupBackPointers(message.filetree);
229 - updateFiles();
230 - d3updatefiles();
249 + //filetree = setupBackPointers(message.filetree);
250 + //updateFiles();
251 + //d3updatefiles();
252 break;
253 }
254 case 'nodes': {
@@ -401,12 +422,12 @@
422 if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
423 }
424 node.namel = node.name.toLowerCase();
404 - if (node.host) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
425 + if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
426 if (message.event.node.icon) { node.icon = message.event.node.icon; }
427
428 //onSortSelectChange(true);
429 //drawNotifications();
409 - //refreshDevice(node._id);
430 + refreshDevice(node._id);
431 //updateMapMarkers();
432 updateDevices();
433
@@ -422,8 +443,8 @@
443 var node = nodes[index];
444
445 // Change the node connection state
425 - //node.conn = message.event.conn;
426 - //node.pwr = message.event.pwr;
446 + node.conn = message.event.conn;
447 + node.pwr = message.event.pwr;
448 updateDevices();
449 //updateMapMarkers();
450 //refreshDevice(node._id);
@@ -462,19 +483,27 @@
483 // MY DEVICES
484 //
485
486 + var backStack = [];
487 + function goBack() { if (backStack.length == 0) { goMyDevices(); } else { gotoDevice(backStack.pop()); } }
488 + function goMyDevices() { go(2); }
489 +
490 var sort = 0;
491 var deviceHeaderId = 0;
492 var deviceHeaderCount;
493 var deviceHeaders = {};
494 + var showRealNames = false;
495 + var deviceHeaderTotal = 0;
496 + var deviceHeaders = {};
497 + var deviceHeadersTitles = {};
498 function updateDevices() {
499 var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, groups = {}, groupCount = {};
500
501 // 3 wide, list view or desktop view
502 deviceHeaderId = 0;
503 deviceHeaderCount = {};
475 - var deviceHeaderTotal = 0;
476 - var deviceHeaders = {};
477 - var deviceHeadersTitles = {};
504 + deviceHeaderTotal = 0;
505 + deviceHeaders = {};
506 + deviceHeadersTitles = {};
507 var current;
508
509 // Go thru the list of nodes and display them
@@ -484,17 +513,17 @@
513 if (meshlinks == null) continue;
514 var meshrights = meshlinks.rights;
515
487 - /*
516 if (sort == 0) {
517 // Mesh header
518 + nodes.sort(meshSort);
519 if (nodes[i].meshid != current) {
491 - //deviceHeaderSet();
520 + deviceHeaderSet();
521 var extra = '';
493 - if (meshes[nodes[i].meshid].mtype == 1) { extra = '<span class=devHeaderx>, Intel&reg; AMT only</span>'; }
522 + if (meshes[nodes[i].meshid].mtype == 1) { extra = '<span style=color:lightgray>, Intel&reg; AMT only</span>'; }
523 if (current != null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
495 - r += '<div class=DevSt style=width:100%;padding-top:4px><span style=float:right>';
524 + r += '<div class=DevSt style=padding-top:4px><span style=float:right>';
525 //r += getMeshActions(mesh2, meshrights);
497 - r += '</span><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + nodes[i].meshid + '")>' + EscapeHtml(meshes[nodes[i].meshid].name) + '</span>' + extra + '<span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx></span></div>';
526 + r += '</span><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + nodes[i].meshid + '")>' + EscapeHtml(meshes[nodes[i].meshid].name) + '</span>' + extra + '<span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>';
527 current = nodes[i].meshid;
528 displayedMeshes[current] = 1;
529 c = 0;
@@ -502,9 +531,9 @@
531 } else if (sort == 1) {
532 // Power header
533 if (nodes[i].pwr !== current) {
505 - //deviceHeaderSet();
534 + deviceHeaderSet();
535 if (current !== null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
507 - r += '<div class=DevSt style=width:100%;padding-top:4px><span>' + PowerStateStr2(nodes[i].pwr) + '</span><span id=DevxHeader' + deviceHeaderId + ' class="devHeaderx"></span></div>';
536 + r += '<div class=DevSt style=width:100%;padding-top:4px><span>' + PowerStateStr2(nodes[i].pwr) + '</span><span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>';
537 current = nodes[i].pwr;
538 c = 0;
539 }
@@ -512,29 +541,23 @@
541 // Device header
542 if (current == null) { current = '1'; }
543 }
515 - */
516 -
544
545 count++;
546 var title = EscapeHtml(nodes[i].name);
547 if (title.length == 0) { title = '<i>None</i>'; }
548 if ((nodes[i].rname != null) && (nodes[i].rname.length > 0)) { title += " / " + EscapeHtml(nodes[i].rname); }
549 var name = EscapeHtml(nodes[i].name);
523 - //if (showRealNames == true && nodes[i].rname != null) name = EscapeHtml(nodes[i].rname);
550 + if (showRealNames == true && nodes[i].rname != null) name = EscapeHtml(nodes[i].rname);
551 if (name.length == 0) { name = '<i>None</i>'; }
552
553 // Node
527 - var icon = nodes[i].icon;
528 - var nodestate = NodeStateStr(nodes[i]);
554 + var icon = nodes[i].icon, nodestate = NodeStateStr(nodes[i]);
555 if ((!nodes[i].conn) || (nodes[i].conn == 0)) { icon += ' gray'; }
530 - //var title = '';
531 - //r += '<div id=devs style=display:inline-block;width:301px;height:50px;padding-top:1px;padding-bottom:1px><div style=width:22px;height:50%;float:left;padding-top:12px><input class="' + nodes[i].meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + nodes[i]._id + ' type=checkbox></div><div style=height:100%;cursor:pointer onclick=gotoDevice(\'' + nodes[i]._id + '\')><div class="i' + icon + '" style=width:50px;float:left></div><div style=height:100%><div class=g1></div><div class=e2><div class=e1 title="' + title + '">' + name + '</div><div>' + nodestate + '</div></div><div class=g2></div></div></div></div>';
532 - r += '<div>';
556 + r += '<div style=cursor:pointer onclick=gotoDevice(\'' + nodes[i]._id + '\')>';
557 r += '<div class="i' + icon + '" style="float:left;margin-left:4px"></div>';
534 - r += '<div style="width:auto;height:40px;background-color:lightgray;margin-top:5px;margin-bottom:5px;margin-left:60px;padding-top:5px;padding-bottom:5px;cursor:pointer;border-radius:8px 0px 0px 8px">';
558 + r += '<div style="width:auto;height:40px;background-color:lightgray;margin-top:5px;margin-bottom:5px;margin-left:60px;padding-top:5px;padding-bottom:5px;border-radius:8px 0px 0px 8px">';
559 r += '<div><div style=padding-left:12px;padding-top:2px><b>' + name + '</b></div><div style=padding-left:12px;padding-top:3px;color:gray>' + nodestate + '</div></div>';
536 - r += '</div>';
537 - r += '</div>';
560 + r += '</div></div>';
561
562 // If we are displaying devices by group, put the device in the right group.
563 /*
@@ -554,9 +577,39 @@
577 if (typeof deviceHeaderCount[nodes[i].state] == 'undefined') { deviceHeaderCount[nodes[i].state] = 1; } else { deviceHeaderCount[nodes[i].state]++; }
578 }
579
580 + // Display all empty meshes, we need to do this because users can add devices to these at any time.
581 + if (sort == 0) {
582 + for (var i in meshes) {
583 + var mesh = meshes[i], meshlink = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
584 + if (meshlink != null) {
585 + var meshrights = meshlink.rights;
586 + if (displayedMeshes[mesh._id] == null) {
587 + if ((current != '') && (r != '')) { r += '</tr></table>'; }
588 + r += '<div><div colspan=3 class=DevSt><span style=float:right>';
589 + //r += getMeshActions(mesh, meshrights);
590 + r += '</span><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span></div>';
591 + if (mesh.mtype == 1) { r += '<div style=padding:10px><i>No Intel&reg; AMT devices in this mesh'; }
592 + if (mesh.mtype == 2) { r += '<div style=padding:10px><i>No devices in this mesh'; }
593 + r += '.</i></div></div>';
594 + current = mesh._id;
595 + count++;
596 + }
597 + }
598 + }
599 + }
600 +
601 QH('xdevices', r);
602 + deviceHeaderSet();
603 + for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); }
604 + for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; }
605 + }
606 +
607 + function gotoMeshStack(meshid) {
608 + backStack.push(currentNode._id);
609 + gotoMesh(meshid);
610 }
611
612 + var powerStatetable = ['', 'Powered', 'Sleep', 'Sleep', 'Sleep', 'Hibernating', 'Power off', 'Present'];
613 var powerStateStrings = ['', '<span title="Device is powered on.">Powered</span>', '<span title="Device is in sleep state (S1).">Sleeping</span>', '<span title="Device is in sleep state (S2).">Sleeping</span>', '<span title="Device is in deep sleep state (S3).">Deep Sleep</span>', '<span title="Device is in hibernating state (S4).">Hibernating</span>', '<span title="Device is in powered off state (S5).">Soft-Off</span>', '<span title="Device is detected but power state could not be obtained.">Present</span>'];
614 var powerStateStrings2 = ['', 'Device is powered', 'Device is in sleep state (S1)', 'Device is in sleep state (S2)', 'Device is in deep sleep state (S3)', 'Device is hibernating (S4)', 'Device is in soft-off state (S5)', 'Device is present, but power state cannot be determined'];
615 var powerColorTable = ['#00000000', 'black', 'blue', 'blue', 'lightblue', 'blueviolet', 'darkgreen', 'lightseagreen', 'lightseagreen'];
@@ -583,6 +636,31 @@
636 return 'Unknown';
637 }
638
639 + function onSortSelectChange(skipsave) {
640 + sort = document.getElementById("sortselect").selectedIndex;
641 + if (!skipsave) { putstore("sort", sort); }
642 + if (sort == 0) { nodes.sort(meshSort); }
643 + if (sort == 1) { nodes.sort(powerSort); }
644 + if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } }
645 + updateDevices();
646 + }
647 +
648 + function deviceHeaderSet() {
649 + if (deviceHeaderId == 0) { deviceHeaderId = 1; return; }
650 + deviceHeaders["DevxHeader" + deviceHeaderId] = ', ' + deviceHeaderTotal + ((deviceHeaderTotal == 1) ? ' node' : ' nodes');
651 + var title = '';
652 + for (x in deviceHeaderCount) { if (title.length > 0) title += ', '; title += deviceHeaderCount[x] + ' ' + PowerStateStr2(x); }
653 + deviceHeadersTitles["DevxHeader" + deviceHeaderId] = title;
654 + deviceHeaderId++;
655 + deviceHeaderCount = {};
656 + deviceHeaderTotal = 0;
657 + }
658 +
659 + function meshSort(a, b) { if (a.meshnamel > b.meshnamel) return 1; if (a.meshnamel < b.meshnamel) return -1; if (a.meshid == b.meshid) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } return 0; }
660 + function powerSort(a, b) { var ap = a.pwr?a.pwr:0; var bp = b.pwr?b.pwr:0; if (ap == bp) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } if (ap > bp) return 1; if (ap < bp) return -1; return 0; }
661 + function deviceSort(a, b) { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; }
662 + function deviceHostSort(a, b) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; }
663 +
664 //
665 // MY DEVICE
666 //
@@ -604,9 +682,7 @@
682 var powerTimeline = null;
683 function getCurrentNode() { return currentNode; };
684 function gotoDevice(nodeid, panel, refresh) {
607 - //disconnectAllKvmFunction();
608 - var node = getNodeFromId(nodeid);
609 - var mesh = meshes[node.meshid];
685 + var node = getNodeFromId(nodeid), mesh = meshes[node.meshid];
686 var meshrights = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
687 if (!currentNode || currentNode._id != node._id || refresh == true) {
688 currentNode = node;
@@ -616,18 +692,20 @@
692 if (nname.length == 0) { nname = '<i>None</i>'; }
693 if ((meshrights & 4) != 0) { nname = '<span onclick=showEditNodeValueDialog(0) style=cursor:pointer>' + nname + '</span>'; }
694 QH('p10deviceName', nname);
695 + /*
696 QH('p11deviceName', nname);
697 QH('p12deviceName', nname);
698 QH('p13deviceName', nname);
699 QH('p14deviceName', nname);
700 QH('p15deviceName', nname);
701 QH('p16deviceName', nname);
702 + */
703
704 // Node attributes
705 var x = '<table style=width:100%>';
706
707 // Attribute: Mesh
630 - x += addDeviceAttribute('<span title="The name of the administrative group this computer belong to">Mesh</span>', '<a title="The name of the group this computer belong to" onclick=gotoMesh("' + node.meshid + '") style=cursor:pointer>' + EscapeHtml(meshes[node.meshid].name) + '</a>');
708 + x += addDeviceAttribute('<span title="The name of the administrative group this computer belong to">Mesh</span>', '<a title="The name of the group this computer belong to" onclick=gotoMeshStack("' + node.meshid + '") style=cursor:pointer>' + EscapeHtml(meshes[node.meshid].name) + '</a>');
709
710 // Attribute: Name
711 if (node.rname != null) { x += addDeviceAttribute('<span title="The name of this computer as set in the operating system">Name</span>', '<span title="The name of this computer as set in the operating system">' + EscapeHtml(node.rname) + '</span>'); }
@@ -676,12 +754,12 @@
754 if (node.intelamt.state == 2) {
755 if (node.intelamt.user == null || node.intelamt.user == '') {
756 if ((meshrights & 4) != 0) {
679 - str += ', <i style=color:#FF0000;cursor:pointer title="Edit Intel&reg; AMT credentials" onclick=editDeviceAmtSettings("' + node._id + '")>No Credentials</i>';
757 + str += ', <i style=color:#FF0000;cursor:pointer title="Edit Intel&reg; AMT credentials" onclick=editDeviceAmtSettings("' + node._id + '")>No&nbsp;Credentials</i>';
758 } else {
681 - str += ', <i style=color:#FF0000>No Credentials</i>';
759 + str += ', <i style=color:#FF0000>No&nbsp;Credentials</i>';
760 }
761 }
684 - str += ' ';
762 + str += '&nbsp;';
763 if ((meshrights & 4) != 0) {
764 str += '<img src=images/link4.png height=10 width=10 title="Edit Intel&reg; AMT credentials" style=cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>';
765 }
@@ -717,31 +795,20 @@
795
796 x += '</table><br />';
797 // Show action button, only show if we have permissions 4, 8, 64
720 - if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
721 - x += '<input type=button value=Notes title="View notes about this device" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
798 + //if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
799 + //x += '<input type=button value=Notes title="View notes about this device" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
800 //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast title="Display a text message of the remote device" onclick=deviceToastFunction() />'; }
801 QH('p10html', x);
802
803 // Show node last 7 days timeline
726 - drawDeviceTimeline();
804 + //drawDeviceTimeline();
805
806 // Show bottom buttons
807 x = '<div style=float:right;font-size:x-small>';
730 - if ((meshrights & 4) != 0) x += '<a style=cursor:pointer onclick=p10showDeleteNodeDialog("' + node._id + '") title="Remove this device">Delete Device</a>';
808 + //if ((meshrights & 4) != 0) x += '<a style=cursor:pointer onclick=p10showDeleteNodeDialog("' + node._id + '") title="Remove this device">Delete Device</a>';
809 x += '</div><div style=font-size:x-small>';
732 - if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a>&nbsp;';
733 - if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a>&nbsp;';
734 -
735 - if (((meshrights & 8) != 0) && (mesh.mtype == 2)) x += '<a style=cursor:pointer onclick=p10showMeshCmdDialog(1,"' + node._id + '") title="Traffic router used to connect to a device thru this server.">Router</a>&nbsp;';
736 -
737 - // RDP link, show this link only of the remote machine is Windows.
738 - if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
739 - if ((node.agent.id > 0) && (node.agent.id < 5)) { x += '<a style=cursor:pointer onclick=p10clickOnce("' + node._id + '","RDP2",3389) title="Requires Microsoft ClickOnce support in your browser.">RDP</a>&nbsp;'; }
740 - if (node.agent.id > 4) {
741 - x += '<a style=cursor:pointer onclick=p10clickOnce("' + node._id + '","PSSH",22) title="Requires Microsoft ClickOnce support in your browser.">Putty</a>&nbsp;';
742 - x += '<a style=cursor:pointer onclick=p10clickOnce("' + node._id + '","WSCP",22) title="Requires Microsoft ClickOnce support in your browser.">WinSCP</a>&nbsp;';
743 - }
744 - }
810 + //if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a>&nbsp;';
811 + //if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a>&nbsp;';
812 x += '</div><br>'
813
814 QH('p10html3', x);
@@ -749,60 +816,269 @@
816 // Set the node power state
817 powerstate = PowerStateStr(node.state);
818 //if (node.state == 0) { powerstate = 'Unknown State'; }
752 - if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title="Agent connected">Agent connected</span>'; }
753 - if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title="Intel&reg; AMT connected">Intel&reg; AMT connected</span>'; }
754 - if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title="Intel&reg; AMT detected">Intel&reg; AMT detected</span>'; }
819 + if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += '<span style=font-size:10px title="Agent connected">Mesh Agent</span>'; }
820 + if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += '<span style=font-size:10px title="Intel&reg; AMT connected">Intel&reg; AMT connected</span>'; } else
821 + if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += '<span style=font-size:10px title="Intel&reg; AMT detected">Intel&reg; AMT detected</span>'; }
822 QH('MainComputerState', powerstate);
823
824 // Set the node icon
758 - Q('MainComputerImage').setAttribute("src", "images/icons200-" + node.icon + "-1.png");
759 - Q('MainComputerImage').className = ((!node.conn) || (node.conn == 0)?'gray':'');
760 -
761 - // Setup/Refresh the desktop tab
762 - setupTerminal();
763 - setupFiles();
764 - var consoleRights = ((meshrights & 16) != 0);
765 - if (consoleRights) { setupConsole(); } else { if (panel == 15) { panel = 10; } }
766 -
767 - // Show or hide the tabs
768 - // mesh.mtype: 1 = Intel AMT only, 2 = Mesh Agent
769 - // node.agent.caps (bitmask): 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console
770 - QV('MainDevDesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0)) && (meshrights & 8));
771 - QV('MainDevTerminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0)) && (meshrights & 8));
772 - QV('MainDevFiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8));
773 - QV('MainDevAmt', (node.intelamt != null) && (node.intelamt.state == 2) && (meshrights & 8));
774 - QV('MainDevConsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
775 - QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0) && (userinfo.siteadmin == 0xFFFFFFFF));
776 - QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
777 -
778 - // Setup/Refresh Intel AMT tab
779 - var amtFrameNode = Q('p14iframe').contentWindow.getCurrentMeshNode();
780 - if ((amtFrameNode != null) && (amtFrameNode._id != currentNode._id)) { Q('p14iframe').contentWindow.disconnect(); }
781 - var online = ((node.conn & 6) != 0)?true:false; // If CIRA (2) or AMT (4) connected, enable Commander
782 - Q('p14iframe').contentWindow.setConnectionState(online);
783 - Q('p14iframe').contentWindow.setFrameHeight('650px');
784 - Q('p14iframe').contentWindow.setAuthCallback(updateAmtCredentials);
785 -
786 - // Display "action" button on desktop/terminal/files
787 - QV('deskActionsBtn', (meshrights & 72) != 0); // 72 = Wake-up + Remote Control permissions
788 - QV('termActionsBtn', (meshrights & 72) != 0);
789 - QV('filesActionsBtn', (meshrights & 72) != 0);
790 -
791 - // Request the power timeline
792 - if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) { powerTimelineReq = currentNode._id; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); }
793 -
794 - // Reset the desktop tools
795 - QV('DeskTools', false);
796 - showDeskToolsProcesses();
797 -
798 - // Ask for device events
799 - //refreshDeviceEvents();
825 + QH('MainComputerImage', '<div class="i' + node.icon + '"></div>');
826 }
801 - setupDesktop(); // Always refresh the desktop, even if we are on the same device, we need to do some canvas switching.
827 if (!panel) panel = 10;
828 go(panel);
829 }
830
831 + function addDeviceAttribute(name, value) {
832 + return '<tr><td style=width:100px;color:gray>' + name + '</td><td style=overflow:hidden>' + value + '</td></tr>';
833 + }
834 +
835 + function p10showiconselector() {
836 + if (xxdialogMode) return;
837 + var mesh = meshes[currentNode.meshid];
838 + var meshrights = mesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
839 + if ((meshrights & 4) == 0) return;
840 +
841 + var x = '<table align=center><td>';
842 + x += '<div style=display:inline-block class=i1 onclick=p10setIcon(1)></div>';
843 + x += '<div style=display:inline-block class=i2 onclick=p10setIcon(2)></div>';
844 + x += '<div style=display:inline-block class=i3 onclick=p10setIcon(3)></div><br>';
845 + x += '<div style=display:inline-block class=i4 onclick=p10setIcon(4)></div>';
846 + x += '<div style=display:inline-block class=i5 onclick=p10setIcon(5)></div>';
847 + x += '<div style=display:inline-block class=i6 onclick=p10setIcon(6)></div></table>';
848 + setDialogMode(2, "Icon Selection", 0, null, x);
849 + QV('id_dialogclose', true);
850 + }
851 +
852 + function p10setIcon(icon) {
853 + setDialogMode(0);
854 + meshserver.send({ action: 'changedevice', nodeid: currentNode._id, icon: icon });
855 + }
856 +
857 + var showEditNodeValueDialog_modes = ['Device Name', 'Hostname', 'Description', 'Groups'];
858 + var showEditNodeValueDialog_modes2 = ['name', 'host', 'desc', 'tags'];
859 + var showEditNodeValueDialog_modes3 = ['', '', '', 'Group1, Group2, Group3'];
860 + function showEditNodeValueDialog(mode) {
861 + if (xxdialogMode) return;
862 + var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue style=width:170px maxlength=64 placeholder="' + showEditNodeValueDialog_modes3[mode] + '" onchange=p10editdevicevalueValidate(' + mode + ',event) onkeyup=p10editdevicevalueValidate(' + mode + ',event) />');
863 + setDialogMode(2, "Edit Device", 3, showEditNodeValueDialogEx, x, mode);
864 + var v = currentNode[showEditNodeValueDialog_modes2[mode]];
865 + if (v == null) v = '';
866 + if (Array.isArray(v)) { v = v.join(', '); }
867 + Q('dp10devicevalue').value = v;
868 + p10editdevicevalueValidate();
869 + Q('dp10devicevalue').focus();
870 + }
871 +
872 + function showEditNodeValueDialogEx(button, mode) {
873 + var x = { action: 'changedevice', nodeid: currentNode._id };
874 + x[showEditNodeValueDialog_modes2[mode]] = Q('dp10devicevalue').value;
875 + meshserver.send(x);
876 + }
877 +
878 + function p10editdevicevalueValidate(mode, e) {
879 + var x = ((mode > 1) || (Q('dp10devicevalue').value.length > 0));
880 + QE('idx_dlgOkButton', x);
881 + if ((e != null) && (x == true) && (e.keyCode == 13)) { dialogclose(1); }
882 + }
883 +
884 + //
885 + // MY ACCOUNT
886 + //
887 +
888 + function gotoMesh(meshid) {
889 + currentMesh = meshes[meshid];
890 + p20updateMesh();
891 + go(20);
892 + }
893 +
894 +
895 + //
896 + // MY MESHS
897 + //
898 +
899 + var currentMesh;
900 + function p20updateMesh() {
901 + QH('p20meshName', EscapeHtml(currentMesh.name));
902 + var meshtype = 'Unknown #' + currentMesh.mtype;
903 + var meshrights = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
904 + if (currentMesh.mtype == 1) meshtype = 'Intel&reg; AMT group';
905 + if (currentMesh.mtype == 2) meshtype = 'Mesh agent group';
906 +
907 + var x = '';
908 + x += addHtmlValue('Name', addLinkConditional(EscapeHtml(currentMesh.name), 'p20editmesh(1)', (meshrights & 1) != 0));
909 + x += addHtmlValue('Description', addLinkConditional(((currentMesh.desc && currentMesh.desc != '')?EscapeHtml(currentMesh.desc):'<i>None</i>'), 'p20editmesh(2)', (meshrights & 1) != 0));
910 + x += addHtmlValue('Type', meshtype);
911 + //x += addHtmlValue('Identifier', currentMesh._id.split('/')[2]);
912 +
913 + //x += '<br><input type=button value=Notes title="View notes about this mesh" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />';
914 +
915 + x += '<br style=clear:both><br>';
916 + var currentMeshLinks = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
917 + if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += '<a onclick=p20showAddMeshUserDialog() style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> Add User</a>'; }
918 +
919 + /*
920 + if ((meshrights & 4) != 0) {
921 + if (currentMesh.mtype == 1) {
922 + x += '<a onclick=addCiraDeviceToMesh(\"' + currentMesh._id + '\") style=cursor:pointer;margin-right:10px title="Add a new Intel&reg; AMT computer that is located on the internet."><img src=images/icon-installmesh.png border=0 height=12 width=12> Install CIRA</a>';
923 + x += '<a onclick=addDeviceToMesh(\"' + currentMesh._id + '\") style=cursor:pointer;margin-right:10px title="Add a new Intel&reg; AMT computer that is located on the local network."><img src=images/icon-installmesh.png border=0 height=12 width=12> Install local</a>';
924 + }
925 + if (currentMesh.mtype == 2) {
926 + x += '<a onclick=addAgentToMesh(\"' + currentMesh._id + '\") style=cursor:pointer;margin-right:10px title="Add a new computer to this mesh by installing the mesh agent."><img src=images/icon-addnew.png border=0 height=12 width=12> Install</a>';
927 + }
928 + }
929 + */
930 +
931 + /*
932 + function getMeshActions(mesh, meshrights) {
933 + if ((meshrights & 4) == 0) return '';
934 + var r = '';
935 + if (mesh.mtype == 1) {
936 + r += ' <a style=cursor:pointer;font-size:10px title="Add a new Intel&reg; AMT computer that is located on the internet." onclick=addCiraDeviceToMesh(\"' + mesh._id + '\")>Add CIRA</a>';
937 + r += ' <a style=cursor:pointer;font-size:10px title="Add a new Intel&reg; AMT computer that is located on the local network." onclick=addDeviceToMesh(\"' + mesh._id + '\")>Add Local</a>';
938 + }
939 + if (mesh.mtype == 2) {
940 + r += ' <a style=cursor:pointer;font-size:10px title="Add a new computer to this mesh by installing the mesh agent." onclick=addAgentToMesh(\"' + mesh._id + '\")>Add Agent</a>';
941 + }
942 + return r;
943 + }
944 + */
945 +
946 + 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 Authorizations</th><th scope=col style=text-align:left></th></tr>';
947 +
948 + // Sort the users for this mesh
949 + var count = 1, sortedusers = [];
950 + for (var i in currentMesh.links) { sortedusers.push({ id: i, name: i.split('/')[2], rights: currentMesh.links[i].rights }); }
951 + sortedusers.sort(function(a, b) { if (a.name > b.name) return 1; if (a.name < b.name) return -1; return 0; });
952 +
953 + // Display all users for this mesh
954 + for (var i in sortedusers) {
955 + var trash = '', rights = 'Partial&nbsp;Rights', r = sortedusers[i].rights;
956 + if (r == 0xFFFFFFFF) rights = 'Full&nbsp;Administrator'; else if (r == 0) rights = 'No&nbsp;Rights';
957 + if ((i != userinfo._id) && (meshrights == 0xFFFFFFFF || (((meshrights & 2) != 0)))) { trash = '<a onclick=p20deleteUser(event,"' + encodeURIComponent(sortedusers[i].id) + '") title="Remote user rights to this mesh" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
958 + x += '<tr onclick=p20viewuser("' + encodeURIComponent(sortedusers[i].id) + '") style=height:32px;cursor:pointer' + (((count % 2) == 0)?';background-color:#DDD':'') + '><td>';
959 + x += '<div style=float:right>' + trash + '</div><div style=float:right;padding-right:4px>' + rights + '</div><div class=m2></div><div>&nbsp;' + sortedusers[i].name + '<div></div></div>';
960 + x += '</td></tr>';
961 + ++count;
962 + }
963 +
964 + x += '</tbody></table>';
965 +
966 + // If we are full administrator on this mesh, allow deletion of the mesh
967 + if (meshrights == 0xFFFFFFFF) { x += '<div style=font-size:x-small;text-align:right><span><a onclick=p20showDeleteMeshDialog() style=cursor:pointer>Delete Mesh</a></span></div>'; }
968 +
969 + QH('p20info', x);
970 + }
971 +
972 +
973 + function p20showDeleteMeshDialog() {
974 + if (xxdialogMode) return;
975 + var x = "Are you sure you want to delete mesh \"" + EscapeHtml(currentMesh.name) + "\"? Deleting the mesh will also delete all information about computers within this mesh.<br /><br />";
976 + x += "<input id=p20check type=checkbox onchange=p20validateDeleteMeshDialog() />Confirm";
977 + setDialogMode(2, "Delete Mesh", 3, p20showDeleteMeshDialogEx, x);
978 + p20validateDeleteMeshDialog();
979 + }
980 +
981 + function p20validateDeleteMeshDialog() {
982 + QE('idx_dlgOkButton', Q('p20check').checked);
983 + }
984 +
985 + function p20showDeleteMeshDialogEx(buttons, tag) {
986 + meshserver.send({ action: 'deletemesh', meshid: currentMesh._id, meshname: currentMesh.name });
987 + }
988 +
989 + function p20editmesh(focus) {
990 + if (xxdialogMode) return;
991 + var x = addHtmlValue('Name', '<input id=dp20meshname style=width:170px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate() />');
992 + x += addHtmlValue('Description', '<input id=dp20meshdesc style=width:170px maxlength=1024 onkeyup=p20editmeshValidate() />');
993 + setDialogMode(2, "Edit Mesh", 3, p20editmeshEx, x);
994 + Q('dp20meshname').value = currentMesh.name;
995 + if (currentMesh.desc) Q('dp20meshdesc').value = currentMesh.desc;
996 + p20editmeshValidate();
997 + if (focus == 2) { Q('dp20meshdesc').focus(); } else { Q('dp20meshname').focus(); }
998 + }
999 +
1000 + function p20editmeshEx() {
1001 + meshserver.send({ action: 'editmesh', meshid: currentMesh._id, meshname: Q('dp20meshname').value, desc: Q('dp20meshdesc').value });
1002 + }
1003 +
1004 + function p20editmeshValidate() {
1005 + QE('idx_dlgOkButton', Q('dp20meshname').value.length > 0);
1006 + }
1007 +
1008 + function p20showAddMeshUserDialog() {
1009 + if (xxdialogMode) return;
1010 + var x = addHtmlValue('User', '<input id=dp20username style=width:170px maxlength=32 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() />');
1011 + x += '<div style="border:2px groove gray;background-color:white;max-height:80px;overflow-y:scroll">';
1012 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20fulladmin>Full Administrator<br>';
1013 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editmesh>Edit Mesh<br>';
1014 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20manageusers>Manage Mesh Users<br>';
1015 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20managecomputers>Manage Mesh Computers<br>';
1016 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotecontrol>Remote Control<br>';
1017 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshagentconsole>Mesh Agent Console<br>';
1018 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshserverfiles>Server Files<br>';
1019 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20wakedevices>Wake Devices<br>';
1020 + x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editnotes>Edit Device Notes<br>';
1021 + x += '</div>';
1022 + setDialogMode(2, "Add User to Mesh", 3, p20showAddMeshUserDialogEx, x);
1023 + p20validateAddMeshUserDialog();
1024 + Q('dp20username').focus();
1025 + }
1026 +
1027 + function p20validateAddMeshUserDialog() {
1028 + var meshrights = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
1029 + QE('idx_dlgOkButton', (Q('dp20username').value.length > 0));
1030 + QE('p20fulladmin', meshrights == 0xFFFFFFFF);
1031 + QE('p20editmesh', (!Q('p20fulladmin').checked) && (meshrights == 0xFFFFFFFF));
1032 + QE('p20manageusers', !Q('p20fulladmin').checked);
1033 + QE('p20managecomputers', !Q('p20fulladmin').checked);
1034 + QE('p20remotecontrol', !Q('p20fulladmin').checked);
1035 + QE('p20meshagentconsole', !Q('p20fulladmin').checked);
1036 + QE('p20meshserverfiles', !Q('p20fulladmin').checked);
1037 + QE('p20wakedevices', !Q('p20fulladmin').checked);
1038 + QE('p20editnotes', !Q('p20fulladmin').checked);
1039 + }
1040 +
1041 + function p20showAddMeshUserDialogEx() {
1042 + var meshadmin = 0;
1043 + if (Q('p20fulladmin').checked == true) { meshadmin = 0xFFFFFFFF; } else {
1044 + if (Q('p20editmesh').checked == true) meshadmin += 1;
1045 + if (Q('p20manageusers').checked == true) meshadmin += 2;
1046 + if (Q('p20managecomputers').checked == true) meshadmin += 4;
1047 + if (Q('p20remotecontrol').checked == true) meshadmin += 8;
1048 + if (Q('p20meshagentconsole').checked == true) meshadmin += 16;
1049 + if (Q('p20meshserverfiles').checked == true) meshadmin += 32;
1050 + if (Q('p20wakedevices').checked == true) meshadmin += 64;
1051 + if (Q('p20editnotes').checked == true) meshadmin += 128;
1052 + }
1053 + meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, username: Q('dp20username').value , meshadmin: meshadmin});
1054 + }
1055 +
1056 + function p20viewuser(userid) {
1057 + if (xxdialogMode) return;
1058 + userid = decodeURIComponent(userid);
1059 + var r = '', cmeshrights = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights, meshrights = currentMesh.links[userid].rights;
1060 + if (meshrights == 0xFFFFFFFF) r = ', Full Administrator'; else {
1061 + if ((meshrights & 1) != 0) r += ', Edit Mesh';
1062 + if ((meshrights & 2) != 0) r += ', Manage Mesh Users';
1063 + if ((meshrights & 4) != 0) r += ', Manage Mesh Computers';
1064 + if ((meshrights & 8) != 0) r += ', Remote Control';
1065 + if ((meshrights & 16) != 0) r += ', Agent Console';
1066 + if ((meshrights & 32) != 0) r += ', Server Files';
1067 + if ((meshrights & 64) != 0) r += ', Wake Devices';
1068 + if ((meshrights & 128) != 0) r += ', Edit Notes';
1069 + }
1070 + r = r.substring(2);
1071 + if (r == '') { r = 'No Rights'; }
1072 + var buttons = 1, x = addHtmlValue('User', userid.split('/')[2]);
1073 + x += addHtmlValue('Permissions', r);
1074 + if ((('user/{{{domain}}}/' + userinfo.name.toLowerCase()) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4;
1075 + setDialogMode(2, "Mesh User", buttons, p20viewuserEx, x, userid);
1076 + }
1077 +
1078 + function p20viewuserEx(button, userid) { if (button != 2) return; setDialogMode(2, "Remote Mesh User", 3, p20viewuserEx2, "Confirm removal of user " + userid.split('/')[2] + "?", userid); }
1079 + function p20deleteUser(e, userid) { haltEvent(e); p20viewuserEx(2, decodeURIComponent(userid)); }
1080 + function p20viewuserEx2(button, userid) { meshserver.send({ action: 'removemeshuser', meshid: currentMesh._id, meshname: currentMesh.name, userid: userid}); }
1081 +
1082 //
1083 // PANELS
1084 //
@@ -852,7 +1128,7 @@
1128 if (((b & 8) || x) && f) f(x, t);
1129 }
1130
855 - function center() { QS('dialog').left = ((((getDocWidth() - 400) / 2)) + "px"); }
1131 + function center() { QS('dialog').left = ((((getDocWidth() - 300) / 2)) + "px"); }
1132 function messagebox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
1133 function statusbox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t); }
1134 function getDocWidth() { if (window.innerWidth) return window.innerWidth; if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientWidth != 0) return document.documentElement.clientWidth; return document.getElementsByTagName('body')[0].clientWidth; }
@@ -861,6 +1137,10 @@
1137 function validateEmail(v) { var emailReg = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; return emailReg.test(v); }
1138 function reload() { window.location.href = window.location.href; }
1139 function getNodeFromId(id) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } return null; }
1140 + function addHtmlValue(t, v) { return '<table><td style=width:120px>' + t + '<td><b>' + v + '</b></table>'; }
1141 + function addHtmlValue2(t, v) { return '<div><div style=display:inline-block;float:right>' + v + '</div><div style=display:inline-block>' + t + '</div></div>'; }
1142 + function addLink(x, f) { return "<a style=cursor:pointer;color:darkblue;text-decoration:none onclick='" + f + "'>&diams; " + x + "</a>"; }
1143 + function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
1144
1145 </script>
1146 </body>
views/default.handlebars
+3 -4
@@ -1304,7 +1304,7 @@
1304 if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
1305 }
1306 node.namel = node.name.toLowerCase();
1307 - if (node.host) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
1307 + if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
1308 if (message.event.node.icon) { node.icon = message.event.node.icon; }
1309
1310 onSortSelectChange(true);
@@ -4742,8 +4742,7 @@
4742
4743 function p20editmesh(focus) {
4744 if (xxdialogMode) return;
4745 - x = "Create a new mesh computer group using the options below.<br /><br />";
4746 - x += addHtmlValue('Mesh Name', '<input id=dp20meshname style=width:230px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate() />');
4745 + var x = addHtmlValue('Mesh Name', '<input id=dp20meshname style=width:230px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate() />');
4746 x += addHtmlValue('Description', '<div style=width:230px;margin:0;padding:0><textarea id=dp20meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
4747 setDialogMode(2, "Edit Mesh", 3, p20editmeshEx, x);
4748 Q('dp20meshname').value = currentMesh.name;
@@ -4762,7 +4761,7 @@
4761
4762 function p20showAddMeshUserDialog() {
4763 if (xxdialogMode) return;
4765 - x = "Allow a user to manage the mesh and computers on this mesh<br /><br />";
4764 + var x = "Allow a user to manage the mesh and computers on this mesh<br /><br />";
4765 x += addHtmlValue('User Name', '<input id=dp20username style=width:230px maxlength=32 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() />');
4766 x += '<br><div>';
4767 x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20fulladmin>Full Administrator<br>';
webserver.js
+8 -7
@@ -712,14 +712,14 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
712 // Send the master web application
713 if ((!obj.args.user) && (obj.args.nousers != true) && (nologout == false)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // If a default user is in use or no user mode, don't display the logout button
714 var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
715 - res.render(obj.path.join(__dirname, isModuleBrowser(req) ? 'views/default-mobile' : 'views/default'), { viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, mpspass: args.mpspass, webcerthash: obj.webCertificateHashBase64, footer: (domain.footer == null) ? '' : domain.footer });
715 + res.render(obj.path.join(__dirname, isMobileBrowser(req) ? 'views/default-mobile' : 'views/default'), { viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, mpspass: args.mpspass, webcerthash: obj.webCertificateHashBase64, footer: (domain.footer == null) ? '' : domain.footer });
716 } else {
717 // Send back the login application
718 var loginmode = req.session.loginmode, features = 0;
719 delete req.session.loginmode; // Clear this state, if the user hits refresh, we want to go back to the login page.
720 if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe
721 var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
722 - res.render(obj.path.join(__dirname, isModuleBrowser(req) ? 'views/login-mobile' : 'views/login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, footer: (domain.footer == null) ? '' : domain.footer });
722 + res.render(obj.path.join(__dirname, isMobileBrowser(req) ? 'views/login-mobile' : 'views/login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: domain.newaccounts, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, footer: (domain.footer == null) ? '' : domain.footer });
723 }
724 }
725
@@ -738,9 +738,9 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
738 if (req.session && req.session.userid) {
739 if (req.session.domainid != domain.id) { req.session.destroy(function () { res.redirect(domain.url); }); return; } // Check is the session is for the correct domain
740 var user = obj.users[req.session.userid];
741 - res.render(obj.path.join(__dirname, isModuleBrowser(req) ? 'views/terms-mobile' : 'views/terms'), { title: domain.title, title2: domain.title2, logoutControl: 'Welcome ' + user.name + '. <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>' });
741 + res.render(obj.path.join(__dirname, isMobileBrowser(req) ? 'views/terms-mobile' : 'views/terms'), { title: domain.title, title2: domain.title2, logoutControl: 'Welcome ' + user.name + '. <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>' });
742 } else {
743 - res.render(obj.path.join(__dirname, isModuleBrowser(req) ? 'views/terms-mobile' : 'views/terms'), { title: domain.title, title2: domain.title2 });
743 + res.render(obj.path.join(__dirname, isMobileBrowser(req) ? 'views/terms-mobile' : 'views/terms'), { title: domain.title, title2: domain.title2 });
744 }
745 }
746
@@ -1873,9 +1873,10 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1873
1874 // Return true if a mobile browser is detected.
1875 // This code comes from "http://detectmobilebrowsers.com/" and was modified, This is free and unencumbered software released into the public domain. For more information, please refer to the http://unlicense.org/
1876 - function isModuleBrowser(req) {
1877 - var ua = req.headers['user-agent'].toLowerCase();
1878 - return (/(android|bb\d+|meego).+mobile|mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(ua) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(ua.substr(0, 4)));
1876 + function isMobileBrowser(req) {
1877 + //var ua = req.headers['user-agent'].toLowerCase();
1878 + //return (/(android|bb\d+|meego).+mobile|mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(ua) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(ua.substr(0, 4)));
1879 + return (req.headers['user-agent'].toLowerCase().indexOf('mobile') >= 0);
1880 }
1881
1882 return obj;