Improved mobile app
Ylian Saint-Hilaire committed
Jun 5, 2018 at 13:28 UTC
53c7eae4d28764f205b4a7295799fe9fb694a71f
7 files changed
+442
-65
meshagent.js
+1
-1
@@ -467,7 +467,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
467
var cmdstr = JSON.stringify(command);
468
for (var userid in obj.parent.wssessions) { // Find all connected users for this mesh and send the message
469
var user = obj.parent.users[userid];
470
- if (user) {
470
+ if ((user != null) && (user.links != null)) {
471
var rights = user.links[obj.dbMeshKey];
472
if (rights != null) { // TODO: Look at what rights are needed for message routing
473
var sessions = obj.parent.wssessions[userid];
mpsserver.js
+11
-7
@@ -135,7 +135,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
135
if (nodes.length == 0) {
136
if (mesh.mtype == 1) {
137
// Node is not in the database, add it. Credentials will be empty until added by the user.
138
- var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: domainid, intelamt: { user: '', pass: '', tls: 0 } };
138
+ var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: domainid, intelamt: { user: '', pass: '', tls: 0, state: 2 } };
139
obj.db.Set(device);
140
141
// Event the new node
@@ -152,7 +152,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
152
} else {
153
// Node is already present
154
var node = nodes[0];
155
- if (node.intelamt != undefined) { socket.tag.host = node.intelamt.host; }
155
+ if ((node.intelamt != undefined) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; }
156
}
157
158
// Add the connection to the MPS connection list
@@ -249,7 +249,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
249
if (nodes.length == 0) {
250
if (mesh.mtype == 1) {
251
// Node is not in the database, add it. Credentials will be empty until added by the user.
252
- var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0 } };
252
+ var device = { type: 'node', mtype: 1, _id: socket.tag.nodeid, meshid: socket.tag.meshid, name: socket.tag.name, host: null, domain: mesh.domain, intelamt: { user: '', pass: '', tls: 0, state: 2 } };
253
obj.db.Set(device);
254
255
// Event the new node
@@ -266,7 +266,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
266
} else {
267
// Node is already present
268
var node = nodes[0];
269
- if (node.intelamt != undefined) { socket.tag.host = node.intelamt.host; }
269
+ if ((node.intelamt != undefined) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; }
270
}
271
272
// Add the connection to the MPS connection list
@@ -286,7 +286,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
286
287
// Node is present
288
var node = nodes[0];
289
- if (node.intelamt != undefined) { socket.tag.host = node.intelamt.host; }
289
+ if ((node.intelamt != undefined) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; }
290
socket.tag.nodeid = node._id;
291
socket.tag.meshid = mesh._id;
292
socket.tag.connectTime = Date.now();
@@ -667,7 +667,10 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
667
obj.db.Get(socket.tag.nodeid, function (err, nodes) {
668
if (nodes.length != 1) return;
669
var node = nodes[0];
670
- if ((node.intelamt != undefined) && (node.intelamt.host == host)) return;
670
+
671
+ // See if any changes need to be made
672
+ console.log(node.intelamt);
673
+ if ((node.intelamt != undefined) && (node.intelamt.host == host) && (node.name != '') && (node.intelamt.state == 2)) return;
674
675
// Get the mesh for this device
676
obj.db.Get(node.meshid, function (err, meshes) {
@@ -681,9 +684,10 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
684
// Make the change & save
685
if (node.intelamt == undefined) node.intelamt = {};
686
node.intelamt.host = host;
687
+ node.intelamt.state = 2; // Set the state to activated, since this is pretty obvious, we have a CIRA connection.
688
if (node.name == '') { node.name = host.split('.')[0]; }
689
obj.db.Set(node);
686
-
690
+
691
// Event the node change
692
event.msg = 'CIRA changed device ' + node.name + ' from mesh ' + mesh.name + ': ' + changes.join(', ');
693
var node2 = common.Clone(node);
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.1.7-x",
3
+ "version": "0.1.8-a",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",
public/images/3bars-30.png
Binary files /dev/null and b/public/images/3bars-30.png differ
public/images/user-50.png
Binary files /dev/null and b/public/images/user-50.png differ
views/default-mobile.handlebars
+409
-44
@@ -48,18 +48,30 @@
48
border-bottom-width: 1px;
49
border-bottom-color: #DDDDDD;
50
}
51
+
52
+ .noselect {
53
+ -webkit-touch-callout: none;
54
+ -webkit-user-select: none;
55
+ -khtml-user-select: none;
56
+ -moz-user-select: none;
57
+ -ms-user-select: none;
58
+ user-select: none;
59
+ }
60
</style>
61
</head>
62
<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">
63
<div id=container>
64
<div id=mastheadx></div>
65
<div id=masthead style="background-color:#036;background-repeat:no-repeat;height:50px;width:100%;overflow:hidden">
57
- <div style="float:left;height:66px;color:#c8c8c8;padding-left:10px;padding-top:6px">
58
- <strong><font style="font-size:36px;font-family:Arial,Helvetica,sans-serif">{{{title}}}</font></strong>
59
- </div>
60
- <div style="float:left;height:66px;color:#c8c8c8;padding-left:5px;padding-top:10px">
61
- <strong><font style="font-size:12px;font-family:Arial,Helvetica,sans-serif">{{{title2}}}</font></strong>
66
+ <div style="width:calc(100% - 50px);overflow:hidden">
67
+ <div style="float:left;height:66px;color:#c8c8c8;padding-left:10px;padding-top:6px">
68
+ <strong><font style="font-size:36px;font-family:Arial,Helvetica,sans-serif">{{{title}}}</font></strong>
69
+ </div>
70
+ <div style="float:left;height:66px;color:#c8c8c8;padding-left:5px;padding-top:10px">
71
+ <strong><font style="font-size:12px;font-family:Arial,Helvetica,sans-serif">{{{title2}}}</font></strong>
72
+ </div>
73
</div>
74
+ <img class=noselect style="position:absolute;right:0;top:10px;bottom:50px;color:#c8c8c8;font-size:44px;margin-right:8px;cursor:pointer" onclick=topMenu() src="/images/3bars-30.png" width=30 height=30 />
75
</div>
76
<div id=page_content style="overflow-y:scroll;position:absolute;bottom:32px;top:50px;width:100%">
77
<div id=column_l style="width:100%;padding:0;position:absolute;bottom:0px;top:0px">
@@ -76,8 +88,48 @@
88
<div id=p2 style=display:none>
89
<div id=xdevices></div>
90
</div>
79
- <div id=p10 style=display:none;position:absolute;bottom:0;top:0;width:100%>
91
+ <div id=p3 style=display:none;position:absolute;bottom:0;top:0;width:100%>
92
<table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;">
93
+ <tr style=padding:0>
94
+ <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
95
+ <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
96
+ <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>
97
+ </div>
98
+ <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div>
99
+ </td>
100
+ <td>
101
+ <img src="/images/user-50.png" width=50 height=50 />
102
+ </td>
103
+ <td>
104
+ <div style=margin-left:5px>
105
+ <strong style="font-size:large"><span id=p3userName></span></strong><br />
106
+ </div>
107
+ </td>
108
+ </tr>
109
+ </table>
110
+ <div id=p3info style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%">
111
+ <div style="margin-left:8px">
112
+ <div id="p3AccountActions">
113
+ <p><strong>Account actions</strong></p>
114
+ <div style="margin-left:9px;margin-bottom:8px">
115
+ <div style="margin-top:5px"><span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a></span></div>
116
+ <div style="margin-top:5px"><a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a></div>
117
+ <div style="margin-top:5px"><a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a></div>
118
+ <div style="margin-top:5px"><a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a></div>
119
+ </div>
120
+ </div>
121
+ <br style=clear:both />
122
+ <strong>Meshes</strong>
123
+ ( <a onclick=account_createMesh() style=cursor:pointer><img height=12 src="images/icon-addnew.png" width=12 border=0 /> New</a> )
124
+ <br /><br />
125
+ <div id=p3meshes></div>
126
+ <div id=p3noMeshFound style=margin-left:9px;display:none>No meshes. <a onclick=account_createMesh() style=cursor:pointer><strong>Get started here!</strong></a></div>
127
+ <br style=clear:both />
128
+ </div>
129
+ </div>
130
+ </div>
131
+ <div id=p10 style=display:none;position:absolute;bottom:0;top:0;width:100%;overflow:hidden>
132
+ <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;position:absolute;top:0">
133
<tr style=padding:0>
134
<td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
135
<div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
@@ -96,9 +148,11 @@
148
</td>
149
</tr>
150
</table>
99
- <div id=p10html style="margin-left:8px;margin-right:8px"></div>
100
- <div id=p10html2></div>
101
- <div id=p10html3></div>
151
+ <div style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%">
152
+ <div id=p10html style="margin-left:8px;margin-right:8px"></div>
153
+ <div id=p10html2></div>
154
+ <div id=p10html3></div>
155
+ </div>
156
</div>
157
<div id=p20 style=display:none;position:absolute;bottom:0;top:0;width:100%>
158
<table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;">
@@ -151,6 +205,10 @@
205
<input id="idx_dlgOkButton" type="button" value="OK" style="float:right;width:80px" onclick="dialogclose(1)">
206
</div>
207
</div>
208
+ <div id=topMenu style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:0px 0px 5px 5px;position:fixed;top:50px;right:5px;width:170px;display:none">
209
+ <div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(1)>My Account</div>
210
+ <a href=/logout><div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer">Logout</div></a>
211
+ </div>
212
<script>
213
var debugLevel = {{{debuglevel}}};
214
var features = {{{features}}};
@@ -222,6 +280,7 @@
280
}
281
case 'userinfo': {
282
userinfo = message.userinfo;
283
+ QH('p3userName', userinfo.name);
284
//updateSiteAdmin();
285
//QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
286
//QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
@@ -397,6 +456,7 @@
456
457
// Change the node
458
node.name = message.event.node.name;
459
+ node.rname = message.event.node.rname;
460
node.host = message.event.node.host;
461
node.desc = message.event.node.desc;
462
node.publicip = message.event.node.publicip;
@@ -473,20 +533,194 @@
533
}
534
}
535
536
+ //
537
+ // Menu System
538
+ //
539
+
540
+ function topMenu(select) {
541
+ if ((xxdialogMode != null) && (xxdialogMode != 0) && (xxdialogMode != 999)) return;
542
+ if (select === undefined) {
543
+ var x = (QS('topMenu').display == 'none');
544
+ if (x == true) { if ((xxdialogMode == 0) || (xxdialogMode == null)) { QV('topMenu', true); xxdialogMode = 999; } } else { QV('topMenu', false); xxdialogMode = 0; }
545
+ } else {
546
+ QV('topMenu', false);
547
+ xxdialogMode = 0;
548
+ if ((select == 1) && (xxcurrentView != 3)) { goForward('account'); } // My Account
549
+ }
550
+ }
551
+
552
+ var backStack = [];
553
+ function goBack() { if (xxdialogMode) return; if (backStack.length > 0) { backStack.pop(); } goStack(); }
554
+ function goForward(id) { if (xxdialogMode) return; backStack.push(id); goStack(); }
555
+ function goStack() {
556
+ if (backStack.length == 0) { go(2); return; }
557
+ var id = backStack[backStack.length - 1], idtype = id.split('/')[0];
558
+ if (idtype == 'node') { gotoDevice(id); }
559
+ if (idtype == 'mesh') { gotoMesh(id); }
560
+ if (idtype == 'account') { go(3); }
561
+ if (idtype == 'devices') { go(2); }
562
+ }
563
+
564
//
565
// MY ACCOUNT
566
//
567
480
- function updateMeshes() { }
568
+ function account_showVerifyEmail() {
569
+ if (xxdialogMode || (userinfo.emailVerified == true) || (serverinfo.emailcheck != true)) return;
570
+ var x = "Click ok to send a verification mail to:<br /><div style=padding:8px><b>" + EscapeHtml(userinfo.email) + "</b></div>Please wait a few minute to receive the verification.";
571
+ setDialogMode(2, "Email Verification", 3, account_showVerifyEmailEx, x);
572
+ }
573
+
574
+ function account_showVerifyEmailEx() {
575
+ meshserver.send({ action: 'verifyemail', email: userinfo.email });
576
+ }
577
+
578
+ function account_showChangeEmail() {
579
+ if (xxdialogMode) return;
580
+ var x = addHtmlValue('Email', '<input id=dp3email style=width:170px maxlength=256 onchange=account_validateEmail() onkeyup=account_validateEmail(event) />');
581
+ setDialogMode(2, "Email Address Change", 3, account_changeEmail, x);
582
+ if (userinfo.email != null) { Q('dp3email').value = userinfo.email; }
583
+ account_validateEmail();
584
+ Q('dp3email').focus();
585
+ }
586
+
587
+ function account_validateEmail(e, email) {
588
+ QE('idx_dlgOkButton', validateEmail(Q('dp3email').value) && (Q('dp3email').value != userinfo.email));
589
+ if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); }
590
+ }
591
+
592
+ function account_changeEmail() {
593
+ meshserver.send({ action: 'changeemail', email: Q('dp3email').value });
594
+ }
595
+
596
+ function account_showDeleteAccount() {
597
+ if (xxdialogMode) return;
598
+ var x = "<form action='{{{domainurl}}}deleteaccount' method=post><table style=margin-left:10px><tr>";
599
+ x += "<td align=right>Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>";
600
+ x += "</tr><tr><td align=right>Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>";
601
+ x += '</tr></table><div style=padding:10px;margin-bottom:4px>';
602
+ x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
603
+ x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>';
604
+ x += '</div><br /></form>';
605
+ setDialogMode(2, "Delete Account", 0, null, x);
606
+ account_validateDeleteAccount();
607
+ Q('apassword1').focus();
608
+ }
609
+
610
+ function account_showChangePassword() {
611
+ if (xxdialogMode) return;
612
+ var x = "<form action='{{{domainurl}}}changepassword' method=post><table style=margin-left:10px><tr>";
613
+ x += "<td align=right>Password:</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td>";
614
+ x += "</tr><tr><td align=right>Password:</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() /></td>";
615
+ x += "</tr><tr><td align=right>Hint:</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off /></td>";
616
+ x += '</tr></table><div style=padding:10px;margin-bottom:4px>';
617
+ x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
618
+ x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>';
619
+ x += '</div><br /></form>';
620
+ setDialogMode(2, "Change Password", 0, null, x);
621
+ account_validateDeleteAccount();
622
+ Q('apassword1').focus();
623
+ }
624
+
625
+ function account_createMesh() {
626
+ if (xxdialogMode) return;
627
+ var x = addHtmlValue('Name', '<input id=dp3meshname style=width:170px maxlength=64 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate() />');
628
+ x += addHtmlValue('Type', '<div style=width:170px;margin:0;padding:0><select id=dp3meshtype style=width:100% onchange=account_validateMeshCreate() ><option value=2>Mesh Agent Policy</option><option value=1>Intel® AMT Agent-less Policy</option></select></div>');
629
+ x += addHtmlValue('Description', '<div style=width:170px;margin:0;padding:0><textarea id=dp3meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
630
+ setDialogMode(2, "Create Mesh", 3, account_createMeshEx, x);
631
+ account_validateMeshCreate();
632
+ Q('dp3meshname').focus();
633
+ }
634
+
635
+ function account_validateMeshCreate() {
636
+ QE('idx_dlgOkButton', Q('dp3meshname').value.length > 0);
637
+ }
638
+
639
+ function account_createMeshEx(button, tag) {
640
+ meshserver.send({ action: 'createmesh', meshname: Q('dp3meshname').value, meshtype: Q('dp3meshtype').value, desc: Q('dp3meshdesc').value });
641
+ }
642
+
643
+ function account_validateDeleteAccount() {
644
+ QE('account_dlgOkButton', (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value));
645
+ }
646
+
647
+ function account_validateNewPassword() {
648
+ QE('account_dlgOkButton', (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value));
649
+ var r = '';
650
+ if (Q('apassword1').value != '') {
651
+ var passStrength = checkPasswordStrength(Q('apassword1').value);
652
+ if (passStrength >= 80) { r = '<span style=color:green>Strong<span>'; } else if (passStrength >= 60) { r = '<span style=color:blue>Good<span>'; } else { r = '<span style=color:red>Weak<span>'; }
653
+ }
654
+ QH('dxPassWarn', r);
655
+ }
656
+
657
+ // Return a password strength score
658
+ function checkPasswordStrength(password) {
659
+ var r = 0, letters = {}, varCount = 0, variations = { digits: /\d/.test(password), lower: /[a-z]/.test(password), upper: /[A-Z]/.test(password), nonWords: /\W/.test(password) }
660
+ if (!password) return 0;
661
+ for (var i = 0; i< password.length; i++) { letters[password[i]] = (letters[password[i]] || 0) + 1; r += 5.0 / letters[password[i]]; }
662
+ for (var c in variations) { varCount += (variations[c] == true) ? 1 : 0; }
663
+ return parseInt(r + (varCount - 1) * 10);
664
+ }
665
+
666
+ function updateMeshes() {
667
+ var r = '', count = 0;
668
+ for (i in meshes) {
669
+ count++;
670
+
671
+ // Mesh rights
672
+ var meshrights = meshes[i].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
673
+ var rights = 'Partial Rights';
674
+ if (meshrights == 0xFFFFFFFF) rights = 'Full Administrator'; else if (meshrights == 0) rights = 'No Rights';
675
+
676
+ // Print the mesh information
677
+ r += '<div style=cursor:pointer onclick=goForward(\'' + i + '\')>';
678
+ r += '<div style="float:left;margin-left:4px"><img src="/images/meshicon50.png" width=50 height=50 /></div>';
679
+ 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">';
680
+ r += '<div><div style=padding-left:12px;padding-top:2px><b>' + EscapeHtml(meshes[i].name) + '</b></div><div style=padding-left:12px;padding-top:3px;color:gray>' + rights + '</div></div>';
681
+ r += '</div></div>';
682
+ }
683
+
684
+ meshcount = count;
685
+ QH('p3meshes', r);
686
+ QV('p3noMeshFound', count == 0);
687
+ }
688
+
689
+ function gotoMesh(meshid) {
690
+ currentMesh = meshes[meshid];
691
+ p20updateMesh();
692
+ go(20);
693
+ }
694
+
695
+ function server_showRestoreDlg() {
696
+ if (xxdialogMode) return;
697
+ var x = 'Restore the server using a backup, <span style=color:red>this will delete the existing server data</span>. Only do this if you know what you are doing.<br /><br />';
698
+ x += '<form action="/restoreserver.ashx" enctype="multipart/form-data" method="post"><div>';
699
+ x += '<input id=account_dlgFileInput type=file name=datafile style=width:100% accept=".zip,application/octet-stream,application/zip,application/x-zip,application/x-zip-compressed" onchange=account_validateServerRestore()>';
700
+ x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
701
+ x += '<input id=account_dlgOkButton type=submit value=OK style=float:right;width:80px onclick=dialogclose(1)>';
702
+ x += '</div><br /><br /></form>';
703
+ setDialogMode(2, "Restore Server", 0, null, x);
704
+ account_validateServerRestore();
705
+ }
706
+
707
+ function account_validateServerRestore() {
708
+ QE('account_dlgOkButton', Q('account_dlgFileInput').files.length == 1);
709
+ }
710
+
711
+ function server_showVersionDlg() {
712
+ if (xxdialogMode) return;
713
+ setDialogMode(2, "MeshCentral Version", 1, null, "Loading...", 'MeshCentralServerUpdate');
714
+ meshserver.send({ action: 'serverversion' });
715
+ }
716
+
717
+ function server_showVersionDlgUpdate() { QE('idx_dlgOkButton', Q('d2updateCheck').checked); }
718
+ function server_showVersionDlgEx() { meshserver.send({ action: 'serverupdate' }); }
719
720
//
721
// MY DEVICES
722
//
723
486
- var backStack = [];
487
- function goBack() { if (backStack.length == 0) { goMyDevices(); } else { gotoDevice(backStack.pop()); } }
488
- function goMyDevices() { go(2); }
489
-
724
var sort = 0;
725
var deviceHeaderId = 0;
726
var deviceHeaderCount;
@@ -523,7 +757,7 @@
757
if (current != null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
758
r += '<div class=DevSt style=padding-top:4px><span style=float:right>';
759
//r += getMeshActions(mesh2, meshrights);
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>';
760
+ r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + nodes[i].meshid + '")>' + EscapeHtml(meshes[nodes[i].meshid].name) + '</span>' + extra + '<span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>';
761
current = nodes[i].meshid;
762
displayedMeshes[current] = 1;
763
c = 0;
@@ -553,7 +787,7 @@
787
// Node
788
var icon = nodes[i].icon, nodestate = NodeStateStr(nodes[i]);
789
if ((!nodes[i].conn) || (nodes[i].conn == 0)) { icon += ' gray'; }
556
- r += '<div style=cursor:pointer onclick=gotoDevice(\'' + nodes[i]._id + '\')>';
790
+ r += '<div style=cursor:pointer onclick=goForward(\'' + nodes[i]._id + '\')>';
791
r += '<div class="i' + icon + '" style="float:left;margin-left:4px"></div>';
792
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">';
793
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>';
@@ -587,7 +821,7 @@
821
if ((current != '') && (r != '')) { r += '</tr></table>'; }
822
r += '<div><div colspan=3 class=DevSt><span style=float:right>';
823
//r += getMeshActions(mesh, meshrights);
590
- r += '</span><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span></div>';
824
+ r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span></div>';
825
if (mesh.mtype == 1) { r += '<div style=padding:10px><i>No Intel® AMT devices in this mesh'; }
826
if (mesh.mtype == 2) { r += '<div style=padding:10px><i>No devices in this mesh'; }
827
r += '.</i></div></div>';
@@ -604,11 +838,6 @@
838
for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; }
839
}
840
607
- function gotoMeshStack(meshid) {
608
- backStack.push(currentNode._id);
609
- gotoMesh(meshid);
610
- }
611
-
841
var powerStatetable = ['', 'Powered', 'Sleep', 'Sleep', 'Sleep', 'Hibernating', 'Power off', 'Present'];
842
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>'];
843
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'];
@@ -705,7 +934,7 @@
934
var x = '<table style=width:100%>';
935
936
// Attribute: Mesh
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>');
937
+ 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=goForward("' + node.meshid + '") style=cursor:pointer>' + EscapeHtml(meshes[node.meshid].name) + '</a>');
938
939
// Attribute: Name
940
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>'); }
@@ -743,23 +972,27 @@
972
// Attribute: Intel AMT
973
if (node.intelamt != null) {
974
var str = '';
746
- var provisioningStates = { 0: 'Not Activated (Pre)', 1: 'Not Activated (In)', 2: 'Activated' };
747
- if (node.intelamt.ver != null && node.intelamt.state == null) { str += '<i>Unknown State</i>, v' + node.intelamt.ver; } else
748
- if (node.intelamt.ver == null || node.intelamt.state == null) { str += '<i>Unknown Version & State</i>'; } else {
975
+ var provisioningStates = { 0: 'Not Activated (Pre)', 1: 'Not Activated (In)', 2: 'Activated' };
976
+ if (node.intelamt.ver != null && node.intelamt.state == null) { str += '<i>Unknown State</i>, v' + node.intelamt.ver; } else
977
+
978
+ if ((node.intelamt.ver == null) && (node.intelamt.state == 2)) { str += '<i>Activated</i>'; }
979
+ else if ((node.intelamt.ver == null) || (node.intelamt.state == null)) { str += '<i>Unknown Version & State</i>'; }
980
+ else {
981
str += provisioningStates[node.intelamt.state];
982
if (node.intelamt.flags) { if (node.intelamt.flags & 2) { str += ' <span title="Intel AMT is activated in Client Control Mode">CCM</span>'; } else if (node.intelamt.flags & 4) { str += ' <span title="Intel AMT is activated in Admin Control Mode">ACM</span>'; } }
983
str += (', v' + node.intelamt.ver);
984
}
985
+
986
if (node.intelamt.tls == 1) { str += ', <span title="Intel AMT is setup with TLS network security">TLS</span>'; }
987
if (node.intelamt.state == 2) {
988
if (node.intelamt.user == null || node.intelamt.user == '') {
989
if ((meshrights & 4) != 0) {
990
str += ', <i style=color:#FF0000;cursor:pointer title="Edit Intel® AMT credentials" onclick=editDeviceAmtSettings("' + node._id + '")>No Credentials</i>';
991
} else {
759
- str += ', <i style=color:#FF0000>No Credentials</i>';
992
+ str += ', <i style=color:#FF0000>No Credentials</i>';
993
}
994
}
762
- str += ' ';
995
+ str += ' ';
996
if ((meshrights & 4) != 0) {
997
str += '<img src=images/link4.png height=10 width=10 title="Edit Intel® AMT credentials" style=cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>';
998
}
@@ -795,7 +1028,7 @@
1028
1029
x += '</table><br />';
1030
// Show action button, only show if we have permissions 4, 8, 64
798
- //if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
1031
+ if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
1032
//x += '<input type=button value=Notes title="View notes about this device" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
1033
//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() />'; }
1034
QH('p10html', x);
@@ -804,8 +1037,8 @@
1037
//drawDeviceTimeline();
1038
1039
// Show bottom buttons
807
- x = '<div style=float:right;font-size:x-small>';
808
- //if ((meshrights & 4) != 0) x += '<a style=cursor:pointer onclick=p10showDeleteNodeDialog("' + node._id + '") title="Remove this device">Delete Device</a>';
1040
+ x = '<div style=float:right;font-size:x-small;margin-right:10px>';
1041
+ if ((meshrights & 4) != 0) x += '<a style=cursor:pointer onclick=p10showDeleteNodeDialog("' + node._id + '") title="Remove this device">Delete Device</a>';
1042
x += '</div><div style=font-size:x-small>';
1043
//if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a> ';
1044
//if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a> ';
@@ -823,15 +1056,156 @@
1056
1057
// Set the node icon
1058
QH('MainComputerImage', '<div class="i' + node.icon + '"></div>');
1059
+
1060
+ // Request the power timeline
1061
+ if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) { QH('p10html2', ''); powerTimelineReq = currentNode._id; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); }
1062
}
1063
if (!panel) panel = 10;
1064
go(panel);
1065
}
1066
1067
+ function deviceActionFunction() {
1068
+ if (xxdialogMode) return;
1069
+ var meshrights = meshes[currentNode.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
1070
+ var x = "Select an operation to perform on this device.<br /><br />";
1071
+ var y = '<select id=d2deviceop style=float:right;width:170px>';
1072
+ if ((meshrights & 64) != 0) { y += '<option value=100>Wake-up</option>'; } // Wake-up permission
1073
+ if ((meshrights & 8) != 0) { y += '<option value=4>Sleep</option><option value=3>Reset</option><option value=2>Power off</option>'; } // Remote control permission
1074
+ y += '</select>';
1075
+ x += addHtmlValue('Operation', y);
1076
+ setDialogMode(2, "Device Action", 3, deviceActionFunctionEx, x);
1077
+ }
1078
+
1079
+ function deviceActionFunctionEx() {
1080
+ var op = Q('d2deviceop').value;
1081
+ if (op == 100) {
1082
+ // Device wake
1083
+ meshserver.send({ action: 'wakedevices', nodeids: [ currentNode._id ] });
1084
+ } else {
1085
+ // Power operation
1086
+ meshserver.send({ action: 'poweraction', nodeids: [ currentNode._id ], actiontype: op });
1087
+ }
1088
+ }
1089
+
1090
+ // Look to see if we need to update the device timeline
1091
+ function updateDeviceTimeline() {
1092
+ if ((meshserver.State != 2) || (powerTimelineNode == null) || (powerTimelineUpdate == null) || (currentNode == null)) return;
1093
+ if ((powerTimelineNode == powerTimelineReq) && (currentNode._id == powerTimelineNode) && (powerTimelineUpdate < Date.now())) { powerTimelineUpdate = null; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); }
1094
+ }
1095
+
1096
+ // Draw device power bars. The bars are 766px wide.
1097
+ function drawDeviceTimeline() {
1098
+ var timeline = null, now = Date.now();
1099
+ if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; }
1100
+
1101
+ // Calculate when the timeline starts
1102
+ var d = new Date();
1103
+ d.setHours(0, 0, 0, 0);
1104
+ d = new Date(d.getTime() - (1000 * 60 * 60 * 24 * 6));
1105
+ var timelineStart = d.getTime();
1106
+
1107
+ // De-compact the timeline
1108
+ var timeline2 = [];
1109
+ if (timeline != null && timeline.length > 1) {
1110
+ timeline2.push([ 0, timeline[1], timeline[0] ]); // Start, End, Power
1111
+ var ct = timeline[1];
1112
+ for (var i = 2; i < timeline.length; i += 2) {
1113
+ var power = timeline[i], dt = now;
1114
+ if (timeline.length > (i + 1)) { dt = timeline[i + 1]; }
1115
+ timeline2.push([ ct, ct + dt, power ]); // Start, End, Power
1116
+ ct = ct + dt;
1117
+ }
1118
+ }
1119
+
1120
+ // Draw the timeline
1121
+ var x = '', count = 1, date = new Date();
1122
+ var totalWidth = Q('masthead').offsetWidth - (90 + 9 + 9 + 14); // Compute the total width of the power bar
1123
+ date.setHours(0, 0, 0, 0);
1124
+ for (var i = 0; i < 7; i++) {
1125
+ var datavalue = '', start = date.getTime(), end = start + (1000 * 60 * 60 * 24);
1126
+ for (var j in timeline2) {
1127
+ var block = timeline2[j];
1128
+ if (isTimeBlockInside(start, end, block[0], block[1]) == true) {
1129
+ var ts = Math.max(start, block[0]);
1130
+ var te = Math.min(Math.min(end, block[1]), now);
1131
+ var width = Math.round(((te - ts) * totalWidth) / 86400000);
1132
+ if (width > 0) {
1133
+ var title = powerStateStrings2[block[2]] + ' from ' + new Date(ts).toLocaleTimeString() + ' to ' + new Date(te).toLocaleTimeString() + '.';
1134
+ datavalue += '<div title="' + title + '" style=display:table-cell;width:' + width + 'px;background-color:' + powerColor(block[2]) + ';height:16px></div>';
1135
+ }
1136
+ }
1137
+ }
1138
+ x += '<tr style=' + (((count % 2) == 0)?'background-color:#DDD':'') + '><td><div> ' + date.toLocaleDateString() + '<div></div></div></td><td><div>' + datavalue + '</div></td></tr>';
1139
+ ++count;
1140
+ date = new Date(date.getTime() - (1000 * 60 * 60 * 24)); // Substract one day
1141
+ }
1142
+ QH('p10html2', '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse;width:calc(100% - 18px);margin:9px" border=0 cellpadding=2 cellspacing=0><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:center;width:90px>Day</th><th scope=col style=text-align:center>Power State</th></tr>' + x + '</tbody></table>');
1143
+ }
1144
+
1145
+ // Return a color for the given power state
1146
+ function powerColor(x) { if (x < powerColorTable.length) { return powerColorTable[x]; } return 'yellow'; }
1147
+
1148
+ // Return true if the time block is visible within the start/end period
1149
+ function isTimeBlockInside(start, end, blockStart, blockEnd) {
1150
+ if ((blockStart < start) && (blockEnd > end)) return true; // Block is wider than timespan
1151
+ if ((blockStart > start) && (blockStart < end)) return true;
1152
+ if ((blockEnd > start) && (blockEnd < end)) return true;
1153
+ return false;
1154
+ }
1155
+
1156
function addDeviceAttribute(name, value) {
1157
return '<tr><td style=width:100px;color:gray>' + name + '</td><td style=overflow:hidden>' + value + '</td></tr>';
1158
}
1159
1160
+ function editDeviceAmtSettings(nodeid, func) {
1161
+ if (xxdialogMode) return;
1162
+ var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = getNodeRights(nodeid);
1163
+ if ((meshrights & 4) == 0) return;
1164
+ x += addHtmlValue('Username', '<input id=dp10username style=width:170px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
1165
+ x += addHtmlValue('Password', '<input id=dp10password type=password style=width:170px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
1166
+ x += addHtmlValue('Security', '<select id=dp10tls style=width:176px><option value=0>No TLS security</option><option value=1>TLS security required</option></select>');
1167
+ if ((node.intelamt.user != null) && (node.intelamt.user != '')) { buttons = 7; }
1168
+ setDialogMode(2, "Edit Intel® AMT credentials", buttons, editDeviceAmtSettingsEx, x, { node: node, func: func });
1169
+ if ((node.intelamt.user != null) && (node.intelamt.user != '')) { Q('dp10username').value = node.intelamt.user; } else { Q('dp10username').value = 'admin'; }
1170
+ Q('dp10tls').value = node.intelamt.tls;
1171
+ validateDeviceAmtSettings();
1172
+ }
1173
+
1174
+ function validateDeviceAmtSettings() {
1175
+ QE('idx_dlgOkButton', passwordcheck(Q('dp10password').value));
1176
+ }
1177
+
1178
+ function editDeviceAmtSettingsEx(button, tag) {
1179
+ if (button == 2) {
1180
+ // Delete button pressed, remove credentials
1181
+ meshserver.send({ action: 'changedevice', nodeid: tag.node._id, intelamt: { user: '', pass: '' } });
1182
+ } else {
1183
+ // Change Intel AMT credentials
1184
+ var amtuser = Q('dp10username').value;
1185
+ if (amtuser == '') amtuser = 'admin';
1186
+ var amtpass = Q('dp10password').value;
1187
+ if (amtpass == '') amtuser = '';
1188
+ meshserver.send({ action: 'changedevice', nodeid: tag.node._id, intelamt: { user: amtuser, pass: amtpass, tls: Q('dp10tls').value } });
1189
+ tag.node.intelamt.user = amtuser;
1190
+ tag.node.intelamt.tls = Q('dp10tls').value;
1191
+ if (tag.func) { setTimeout(tag.func, 300); }
1192
+ }
1193
+ }
1194
+
1195
+ function p10showDeleteNodeDialog(nodeid) {
1196
+ if (xxdialogMode) return;
1197
+ setDialogMode(2, "Delete Node", 3, p10showDeleteNodeDialogEx, "Delete \"" + EscapeHtml(currentNode.name) + "\"?<br /><br /><input id=p10check type=checkbox onchange=p10validateDeleteNodeDialog() />Confirm", nodeid);
1198
+ p10validateDeleteNodeDialog();
1199
+ }
1200
+
1201
+ function p10validateDeleteNodeDialog() {
1202
+ QE('idx_dlgOkButton', Q('p10check').checked);
1203
+ }
1204
+
1205
+ function p10showDeleteNodeDialogEx(buttons, nodeid) {
1206
+ meshserver.send({ action: 'removedevices', nodeids: [ nodeid ] });
1207
+ }
1208
+
1209
function p10showiconselector() {
1210
if (xxdialogMode) return;
1211
var mesh = meshes[currentNode.meshid];
@@ -880,17 +1254,6 @@
1254
QE('idx_dlgOkButton', x);
1255
if ((e != null) && (x == true) && (e.keyCode == 13)) { dialogclose(1); }
1256
}
883
-
884
- //
885
- // MY ACCOUNT
886
- //
887
-
888
- function gotoMesh(meshid) {
889
- currentMesh = meshes[meshid];
890
- p20updateMesh();
891
- go(20);
892
- }
893
-
1257
1258
//
1259
// MY MESHS
@@ -898,6 +1261,7 @@
1261
1262
var currentMesh;
1263
function p20updateMesh() {
1264
+ if (currentMesh == null) return;
1265
QH('p20meshName', EscapeHtml(currentMesh.name));
1266
var meshtype = 'Unknown #' + currentMesh.mtype;
1267
var meshrights = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
@@ -914,7 +1278,7 @@
1278
1279
x += '<br style=clear:both><br>';
1280
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>'; }
1281
+ if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += '<div style=margin-bottom:6px><a onclick=p20showAddMeshUserDialog() style=cursor:pointer><img src=images/icon-addnew.png border=0 height=12 width=12> Add User</a></div>'; }
1282
1283
/*
1284
if ((meshrights & 4) != 0) {
@@ -964,7 +1328,7 @@
1328
x += '</tbody></table>';
1329
1330
// 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>'; }
1331
+ if (meshrights == 0xFFFFFFFF) { x += '<div style=font-size:small;text-align:right;margin-top:6px><span><a onclick=p20showDeleteMeshDialog() style=cursor:pointer>Delete Mesh</a></span></div>'; }
1332
1333
QH('p20info', x);
1334
}
@@ -1141,6 +1505,7 @@
1505
function addHtmlValue2(t, v) { return '<div><div style=display:inline-block;float:right>' + v + '</div><div style=display:inline-block>' + t + '</div></div>'; }
1506
function addLink(x, f) { return "<a style=cursor:pointer;color:darkblue;text-decoration:none onclick='" + f + "'>♦ " + x + "</a>"; }
1507
function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
1508
+ function passwordcheck(p) { var re = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()]).{8,}/; return re.test(p); }
1509
1510
</script>
1511
</body>
views/default.handlebars
+20
-12
@@ -886,13 +886,10 @@
886
//QS('column_l').padding = '0';
887
QS('column_l').width = 'calc(100% - 30px)';
888
}
889
+ drawDeviceTimeline();
890
}
891
891
- function getNodeFromId(id) {
892
- for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; }
893
- return null;
894
- }
895
-
892
+ function getNodeFromId(id) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } return null; }
893
function reload() { window.location.href = window.location.href; }
894
895
function onStateChanged(server, state) {
@@ -1279,6 +1276,7 @@
1276
1277
// Change the node
1278
node.name = message.event.node.name;
1279
+ node.rname = message.event.node.rname;
1280
node.host = message.event.node.host;
1281
node.desc = message.event.node.desc;
1282
node.publicip = message.event.node.publicip;
@@ -1600,8 +1598,8 @@
1598
}
1599
1600
// If there is nothing to display, explain the problem
1603
- if ((r == '') && (meshcount > 0)) {
1604
- if ((Q('SearchInput').value == '') && (sort == 3)) {
1601
+ if ((r == '') && (meshcount > 0) && (Q('SearchInput').value != '')) {
1602
+ if (sort == 3) {
1603
r = '<div style="margin:30px">No devices are included in any groups, click on a device\'s \"Groups\" to add to a group.</div>';
1604
} else {
1605
r = '<div style="margin:30px">No devices matching this search.</div>';
@@ -2874,11 +2872,15 @@
2872
var str = '';
2873
var provisioningStates = { 0: 'Not Activated (Pre)', 1: 'Not Activated (In)', 2: 'Activated' };
2874
if (node.intelamt.ver != null && node.intelamt.state == null) { str += '<i>Unknown State</i>, v' + node.intelamt.ver; } else
2877
- if (node.intelamt.ver == null || node.intelamt.state == null) { str += '<i>Unknown Version & State</i>'; } else {
2875
+
2876
+ if ((node.intelamt.ver == null) && (node.intelamt.state == 2)) { str += '<i>Activated</i>'; }
2877
+ else if ((node.intelamt.ver == null) || (node.intelamt.state == null)) { str += '<i>Unknown Version & State</i>'; }
2878
+ else {
2879
str += provisioningStates[node.intelamt.state];
2880
if (node.intelamt.flags) { if (node.intelamt.flags & 2) { str += ' <span title="Intel AMT is activated in Client Control Mode">CCM</span>'; } else if (node.intelamt.flags & 4) { str += ' <span title="Intel AMT is activated in Admin Control Mode">ACM</span>'; } }
2881
str += (', v' + node.intelamt.ver);
2882
}
2883
+
2884
if (node.intelamt.tls == 1) { str += ', <span title="Intel AMT is setup with TLS network security">TLS</span>'; }
2885
if (node.intelamt.state == 2) {
2886
if (node.intelamt.user == null || node.intelamt.user == '') {
@@ -2977,7 +2979,7 @@
2979
QV('MainDevDesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0)) && (meshrights & 8));
2980
QV('MainDevTerminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0)) && (meshrights & 8));
2981
QV('MainDevFiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8));
2980
- QV('MainDevAmt', (node.intelamt != null) && (node.intelamt.state == 2) && (meshrights & 8));
2982
+ QV('MainDevAmt', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8));
2983
QV('MainDevConsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
2984
QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0) && (userinfo.siteadmin == 0xFFFFFFFF));
2985
QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
@@ -2996,7 +2998,7 @@
2998
QV('filesActionsBtn', (meshrights & 72) != 0);
2999
3000
// Request the power timeline
2999
- if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) { powerTimelineReq = currentNode._id; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); }
3001
+ if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) { QH('p10html2', ''); powerTimelineReq = currentNode._id; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); }
3002
3003
// Reset the desktop tools
3004
QV('DeskTools', false);
@@ -3072,6 +3074,7 @@
3074
3075
// Draw device power bars. The bars are 766px wide.
3076
function drawDeviceTimeline() {
3077
+ if ((currentNode == null) || (xxcurrentView < 10) || (xxcurrentView > 19)) return;
3078
var timeline = null, now = Date.now();
3079
if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; }
3080
@@ -3096,6 +3099,7 @@
3099
3100
// Draw the timeline
3101
var x = '', count = 1, date = new Date();
3102
+ var totalWidth = Q('masthead').offsetWidth - (160 + 9 + 9 + 14); // Compute the total width of the power bar
3103
date.setHours(0, 0, 0, 0);
3104
for (var i = 0; i < 7; i++) {
3105
var datavalue = '', start = date.getTime(), end = start + (1000 * 60 * 60 * 24);
@@ -3104,7 +3108,7 @@
3108
if (isTimeBlockInside(start, end, block[0], block[1]) == true) {
3109
var ts = Math.max(start, block[0]);
3110
var te = Math.min(Math.min(end, block[1]), now);
3107
- var width = Math.round((te - ts) / 112794);
3111
+ var width = Math.round(((te - ts) * totalWidth) / 86400000);
3112
if (width > 0) {
3113
var title = powerStateStrings2[block[2]] + ' from ' + new Date(ts).toLocaleTimeString() + ' to ' + new Date(te).toLocaleTimeString() + '.';
3114
datavalue += '<div title="' + title + '" style=display:table-cell;width:' + width + 'px;background-color:' + powerColor(block[2]) + ';height:16px></div>';
@@ -4657,6 +4661,7 @@
4661
4662
var currentMesh;
4663
function p20updateMesh() {
4664
+ if (currentMesh == null) return;
4665
QH('p20meshName', EscapeHtml(currentMesh.name));
4666
var meshtype = 'Unknown #' + currentMesh.mtype;
4667
var meshrights = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
@@ -5710,7 +5715,10 @@
5715
if (((b & 8) || x) && f) f(x, t);
5716
}
5717
5713
- function center() { QS('dialog').left = ((((getDocWidth() - 400) / 2)) + "px"); deskAdjust(); }
5718
+ function center() {
5719
+ QS('dialog').left = ((((getDocWidth() - 400) / 2)) + "px"); deskAdjust();
5720
+ drawDeviceTimeline();
5721
+ }
5722
function messagebox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
5723
function statusbox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t); }
5724
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; }