mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-25 20:51:23 +03:00
12126 lines
803 KiB
Handlebars
12126 lines
803 KiB
Handlebars
<!DOCTYPE html>
|
|
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
|
|
<head>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
|
<meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
<meta name="format-detection" content="telephone=no" />
|
|
<link rel="shortcut icon" type="image/x-icon" href="{{{domainurl}}}favicon.ico" />
|
|
<link keeplink=1 type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
|
|
<link type="text/css" href="styles/ol.css" media="screen" rel="stylesheet" title="CSS" />
|
|
<link type="text/css" href="styles/ol3-contextmenu.min.css" media="screen" rel="stylesheet" title="CSS" />
|
|
<link type="text/css" href="styles/xterm.css" media="screen" rel="stylesheet" title="CSS" />
|
|
<script type="text/javascript" src="scripts/common-0.0.1.js"></script>
|
|
<script type="text/javascript" src="scripts/meshcentral.js"></script>
|
|
<script type="text/javascript" src="scripts/amt-0.2.0.js"></script>
|
|
<script type="text/javascript" src="scripts/amt-wsman-0.2.0.js"></script>
|
|
<script type="text/javascript" src="scripts/amt-desktop-0.0.2.js"></script>
|
|
<script type="text/javascript" src="scripts/amt-terminal-0.0.2.js"></script>
|
|
<script type="text/javascript" src="scripts/zlib.js"></script>
|
|
<script type="text/javascript" src="scripts/zlib-inflate.js"></script>
|
|
<script type="text/javascript" src="scripts/zlib-adler32.js"></script>
|
|
<script type="text/javascript" src="scripts/zlib-crc32.js"></script>
|
|
<script type="text/javascript" src="scripts/amt-redir-ws-0.1.0.js"></script>
|
|
<script type="text/javascript" src="scripts/amt-wsman-ws-0.2.0.js"></script>
|
|
<script type="text/javascript" src="scripts/agent-redir-ws-0.1.1.js"></script>
|
|
<script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0.js"></script>
|
|
<script type="text/javascript" src="scripts/agent-desktop-0.0.2.js"></script>
|
|
<script type="text/javascript" src="scripts/qrcode.min.js"></script>
|
|
<script type="text/javascript" src="scripts/xterm.js"></script>
|
|
<script type="text/javascript" src="scripts/xterm-addon-fit.js"></script>
|
|
<script keeplink=1 type="text/javascript" src="scripts/u2f-api.js"></script>
|
|
<script keeplink=1 type="text/javascript" src="scripts/charts.js"></script>
|
|
<script keeplink=1 type="text/javascript" src="scripts/filesaver.js"></script>
|
|
{{{StartGeoLocation}}}
|
|
<script keeplink=1 type="text/javascript" src="scripts/ol.js"></script>
|
|
<script keeplink=1 type="text/javascript" src="scripts/ol3-contextmenu.js"></script>
|
|
{{{EndGeoLocation}}}
|
|
<title>{{{title}}}</title>
|
|
</head>
|
|
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" oncontextmenu="handleContextMenu(event)" style="display:none;min-width:495px">
|
|
<!-- right click menu -->
|
|
<div id="contextMenu" class="contextMenu noselect" style="display:none">
|
|
<div id="cxinfo" class="cmtext" onclick="cmaction(1,event)"><b>Information</b></div>
|
|
<div id="cxdesktop" class="cmtext" onclick="cmaction(3,event)">Desktop</div>
|
|
<div id="cxterminal" class="cmtext" onclick="cmaction(2,event)">Terminal</div>
|
|
<div id="cxfiles" class="cmtext" onclick="cmaction(4,event)">Files</div>
|
|
<div id="cxevents" class="cmtext" onclick="cmaction(5,event)">Events</div>
|
|
<div id="cxconsole" class="cmtext" onclick="cmaction(6,event)">Console</div>
|
|
<hr id="cxmgroupsplit" />
|
|
<div id="cxmdesktop" class="cmtext" onclick="cmaction(7,event)" style="display:none">Multi-Desktop</div>
|
|
</div>
|
|
<div id="meshContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
|
|
<div id="cxselectall" class="cmtext" onclick="cmmeshaction(1,event)">Select All</div>
|
|
<div id="cxselectnone" class="cmtext" onclick="cmmeshaction(2,event)">Select None</div>
|
|
<!--
|
|
<hr id="cxmgroupsplit2" style="display:none" />
|
|
<div id="cxmmdesktop" class="cmtext" style="display:none" onclick="cmmeshaction(3,event)">Multi-Desktop</div>
|
|
-->
|
|
</div>
|
|
<div id="termShellContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
|
|
<div id="cxtermnorm" class="cmtext" onclick="cmtermaction(1,event)"><b>Admin Shell</b></div>
|
|
<div id="cxtermps" class="cmtext" onclick="cmtermaction(6,event)">Admin PowerShell</div>
|
|
<div id="cxtermunorm" class="cmtext" onclick="cmtermaction(8,event)">User Shell</div>
|
|
<div id="cxtermups" class="cmtext" onclick="cmtermaction(9,event)">User PowerShell</div>
|
|
</div>
|
|
<div id="termShellContextMenuLinux" class="contextMenu noselect" style="display:none;min-width:0px">
|
|
<div id="cxtermnorm" class="cmtext" onclick="cmtermaction(1,event)"><b>Root Shell</b></div>
|
|
<div id="cxtermps" class="cmtext" onclick="cmtermaction(8,event)">User Shell</div>
|
|
<div id="cxtermps" class="cmtext" onclick="cmtermaction(100,event)">Login Shell</div>
|
|
</div>
|
|
<div id="deskConnectContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
|
|
<div id="cxdeskuc" class="cmtext" onclick="cmdeskaction(1,event)"><b>Ask Consent + Bar</b></div>
|
|
<div id="cxdeskuc" class="cmtext" onclick="cmdeskaction(1,event)"><b>Ask Consent</b></div>
|
|
</div>
|
|
<div id="altPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
|
|
<div id="cxaltport" class="cmtext" onclick="cmaltportaction(1,event)"><b>Alternate Port</b></div>
|
|
</div>
|
|
<div id="filesContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
|
|
<div id="cxfilerename" class="cmtext" onclick="cmfilesaction(1,event)"><b>Rename</b></div>
|
|
<div id="cxfileedit" class="cmtext" onclick="cmfilesaction(2,event)"><b>Edit</b></div>
|
|
<div id="cxfiledelete" class="cmtext" onclick="cmfilesaction(3,event)"><b>Delete</b></div>
|
|
</div>
|
|
<!--
|
|
<div id="pluginTabContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
|
|
<div id="cxclose" class="cmtext" onclick="pluginTabClose(event)">Close Tab</div>
|
|
</div>
|
|
-->
|
|
<!-- main page -->
|
|
<div id=container>
|
|
<div id="notifiyBox" class="notifiyBox" style="display:none"></div>
|
|
<div id=masthead class=noselect>
|
|
<div style="float:left">{{{titlehtml}}}</div>
|
|
<div class="title">{{{title1}}}</div>
|
|
<div class="title2">{{{title2}}}</div>
|
|
<div style="float:right">
|
|
<div id=notificationCount onclick="clickNotificationIcon()" class="unselectable" style="display: none;" title="Click to view current notifications">0</div>
|
|
</div>
|
|
<p id="logoutControl"><span id=logoutControlSpan style="color:white"></span><span id=idleTimeoutNotify style="color:yellow"></span></p>
|
|
</div>
|
|
<div id="page_leftbar">
|
|
<div style="height:16px"></div>
|
|
<div id=LeftMenuMyDevices tabindex=0 class="lbbutton lbbuttonsel" title="My Devices" onclick=go(1,event) onkeypress="if (event.key=='Enter') { go(1); }">
|
|
<div class="lb2"></div>
|
|
</div>
|
|
<div id=LeftMenuMyAccount tabindex=0 class="lbbutton" title="My Account" onclick=go(2,event) onkeypress="if (event.key=='Enter') { go(2); }">
|
|
<div class="lb1"></div>
|
|
</div>
|
|
<div id=LeftMenuMyEvents tabindex=0 class="lbbutton" title="My Events" onclick=go(3,event) onkeypress="if (event.key=='Enter') { go(3); }">
|
|
<div class="lb3"></div>
|
|
</div>
|
|
<div id=LeftMenuMyFiles tabindex=0 class="lbbutton" style="display:none" title="My Files" onclick=go(5,event) onkeypress="if (event.key=='Enter') { go(5); }">
|
|
<div class="lb4"></div>
|
|
</div>
|
|
<div id=LeftMenuMyUsers tabindex=0 class="lbbutton" style="display:none" title="My Users" onclick=go(4,event) onkeypress="if (event.key=='Enter') { go(4); }">
|
|
<div class="lb5"></div>
|
|
</div>
|
|
<div id=LeftMenuMyServer tabindex=0 class="lbbutton" style="display:none" title="My Server" onclick=go(6,event) onkeypress="if (event.key=='Enter') { go(6); }">
|
|
<div class="lb6"></div>
|
|
</div>
|
|
</div>
|
|
<div id=topbar class=noselect>
|
|
<div>
|
|
<div style="position:relative">
|
|
<span id=logoutControlSpan2 style="color:white"></span>
|
|
<div tabindex=0 id=uiMenuButton title="User interface selection" onclick="showUserInterfaceSelectMenu()" onkeypress="if (event.key == 'Enter') showUserInterfaceSelectMenu()">
|
|
♦
|
|
<div id=uiMenu style="display:none">
|
|
<div tabindex=0 id=uiViewButton1 class=uiSelector onclick=userInterfaceSelectMenu(1) title="Left bar interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(1)"><div class="uiSelector1"></div></div>
|
|
<div tabindex=0 id=uiViewButton2 class=uiSelector onclick=userInterfaceSelectMenu(2) title="Top bar interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(2)"><div class="uiSelector2"></div></div>
|
|
<div tabindex=0 id=uiViewButton3 class=uiSelector onclick=userInterfaceSelectMenu(3) title="Fixed width interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(3)"><div class="uiSelector3"></div></div>
|
|
<div tabindex=0 id=uiViewButton4 class=uiSelector onclick=toggleNightMode() title="Toggle night mode" onkeypress="if (event.key == 'Enter') toggleNightMode()"><div class="uiSelector4"></div></div>
|
|
<div tabindex=0 id=uiViewButton5 class=uiSelector onclick=toggleFooterBarMode() title="Toggle footer bar" onkeypress="if (event.key == 'Enter') toggleFooterBarMode()"><div class="uiSelector5"></div></div>
|
|
</div>
|
|
</div>
|
|
<table id=MainMenuSpan cellpadding=0 cellspacing=0 class=style1>
|
|
<tr>
|
|
<td tabindex=0 id=MainMenuMyDevices class="topbar_td style3x" onclick=go(1,event) onkeypress="if (event.key == 'Enter') go(1)">My Devices</td>
|
|
<td tabindex=0 id=MainMenuMyAccount class="topbar_td style3x" onclick=go(2,event) onkeypress="if (event.key == 'Enter') go(2)">My Account</td>
|
|
<td tabindex=0 id=MainMenuMyEvents class="topbar_td style3x" onclick=go(3,event) onkeypress="if (event.key == 'Enter') go(3)">My Events</td>
|
|
<td tabindex=0 id=MainMenuMyFiles class="topbar_td style3x" onclick=go(5,event) onkeypress="if (event.key == 'Enter') go(5)">My Files</td>
|
|
<td tabindex=0 id=MainMenuMyUsers class="topbar_td style3x" onclick=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">My Users</td>
|
|
<td tabindex=0 id=MainMenuMyServer class="topbar_td style3x" onclick=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">My Server</td>
|
|
<td class="topbar_td_end style3"> </td>
|
|
</tr>
|
|
</table>
|
|
<div id=MainSubMenuSpan style="display:none">
|
|
<table id=MainSubMenu cellpadding=0 cellspacing=0 class=style1>
|
|
<tr>
|
|
<td tabindex=0 id=MainDev class="topbar_td style3x" onclick=go(10,event) onkeypress="if (event.key == 'Enter') go(10)">General</td>
|
|
<td tabindex=0 id=MainDevDesktop class="topbar_td style3x" onclick=go(11,event) onkeypress="if (event.key == 'Enter') go(11)">Desktop</td>
|
|
<td tabindex=0 id=MainDevTerminal class="topbar_td style3x" onclick=go(12,event) onkeypress="if (event.key == 'Enter') go(12)">Terminal</td>
|
|
<td tabindex=0 id=MainDevFiles class="topbar_td style3x" onclick=go(13,event) onkeypress="if (event.key == 'Enter') go(13)">Files</td>
|
|
<td tabindex=0 id=MainDevEvents class="topbar_td style3x" onclick=go(16,event) onkeypress="if (event.key == 'Enter') go(16)">Events</td>
|
|
<td tabindex=0 id=MainDevInfo class="topbar_td style3x" onclick=go(17,event) onkeypress="if (event.key == 'Enter') go(17)">Details</td>
|
|
<td tabindex=0 id=MainDevAmt class="topbar_td style3x" onclick=go(14,event) onkeypress="if (event.key == 'Enter') go(14)">Intel®AMT</td>
|
|
<td tabindex=0 id=MainDevConsole class="topbar_td style3x" onclick=go(15,event) onkeypress="if (event.key == 'Enter') go(15)">Console</td>
|
|
<td tabindex=0 id=MainDevPlugins class="topbar_td style3x" onclick=go(19,event) onkeypress="if (event.key == 'Enter') go(19)">Plugins</td>
|
|
<td class="topbar_td_end style3"> </td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div id=MeshSubMenuSpan style="display:none">
|
|
<table id=MeshSubMenu cellpadding=0 cellspacing=0 class=style1>
|
|
<tr>
|
|
<td tabindex=0 id=MeshGeneral class="topbar_td style3x" onclick=go(20,event) onkeypress="if (event.key == 'Enter') go(20)">General</td>
|
|
<td tabindex=0 id=MeshSummary class="topbar_td style3x" onclick=go(21,event) onkeypress="if (event.key == 'Enter') go(21)">Summary</td>
|
|
<td class="topbar_td_end style3"> </td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div id=UserSubMenuSpan style="display:none">
|
|
<table id=UserSubMenu cellpadding=0 cellspacing=0 class=style1>
|
|
<tr>
|
|
<td tabindex=0 id=UserGeneral class="topbar_td style3x" onclick=go(30,event) onkeypress="if (event.key == 'Enter') go(30)">General</td>
|
|
<td tabindex=0 id=UserEvents class="topbar_td style3x" onclick=go(31,event) onkeypress="if (event.key == 'Enter') go(31)">Events</td>
|
|
<td class="topbar_td_end style3"> </td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div id=UsersSubMenuSpan style="display:none">
|
|
<table id=UsersSubMenu cellpadding=0 cellspacing=0 class=style1>
|
|
<tr>
|
|
<td tabindex=0 id=UsersGeneral class="topbar_td style3x" onclick=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">Users</td>
|
|
<td tabindex=0 id=UsersGroups class="topbar_td style3x" onclick=go(50,event) onkeypress="if (event.key == 'Enter') go(50)">Groups</td>
|
|
<td class="topbar_td_end style3"> </td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div id=ServerSubMenuSpan style="display:none">
|
|
<table id=ServerSubMenu cellpadding=0 cellspacing=0 class=style1>
|
|
<tr>
|
|
<td tabindex=0 id=ServerGeneral class="topbar_td style3x" onclick=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">General</td>
|
|
<td tabindex=0 id=ServerStats class="topbar_td style3x" onclick=go(40,event) onkeypress="if (event.key == 'Enter') go(40)">Stats</td>
|
|
<td tabindex=0 id=ServerConsole class="topbar_td style3x" onclick=go(115,event) onkeypress="if (event.key == 'Enter') go(115)">Console</td>
|
|
<td tabindex=0 id=ServerTrace class="topbar_td style3x" onclick=go(41,event) onkeypress="if (event.key == 'Enter') go(41)">Trace</td>
|
|
<td tabindex=0 id=ServerPlugins class="topbar_td style3x" onclick=go(42,event) onkeypress="if (event.key == 'Enter') go(42)">Plugins</td>
|
|
<td class="topbar_td_end style3"> </td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div id=UserDummyMenuSpan>
|
|
<table id=UserDummyMenu cellpadding=0 cellspacing=0 class=style1>
|
|
<tr><td class=style3 style=""> </td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="column_l">
|
|
<div id=p0 style="display:none">
|
|
<div id=p0message><span id=p0span>Server disconnected</span>, <href onclick=reload() style=cursor:pointer><u>click to reconnect</u></href>.</div>
|
|
</div>
|
|
<div id=p1 style="display:none">
|
|
<div id="p1title">
|
|
<div style="display:none" id="devListToolbarViewIcons">
|
|
<div tabindex=0 id=devViewButton1 class=viewSelector onclick=onDeviceViewChange(1) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(1); }" title="Columns"><div class="viewSelector2"></div></div>
|
|
<div tabindex=0 id=devViewButton2 class=viewSelector onclick=onDeviceViewChange(2) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(2); }" title="List"><div class="viewSelector1"></div></div>
|
|
<div tabindex=0 id=devViewButton3 class=viewSelector onclick=onDeviceViewChange(3) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(3); }" title="Desktops"><div class="viewSelector3"></div></div>
|
|
<div tabindex=0 id=devViewButton4 class=viewSelector onclick=onDeviceViewChange(4) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(4); }" title="Map" style="display:none"><div class="viewSelector4"></div></div>
|
|
</div><div><h1>My Devices</h1></div>
|
|
</div>
|
|
<table id="devListToolbarSpan" class="noselect">
|
|
<tr>
|
|
<td class=h1></td>
|
|
<td id=devListToolbar class=style14 style="display:none">
|
|
<input type="button" id="SelectAllButton" onclick="selectallButtonFunction();" value="Select All" />
|
|
<input type=button id=GroupActionButton disabled="disabled" value="Group Action" onclick=groupActionFunction() />
|
|
<input id=SearchInput type=text placeholder=Filter onchange=masterUpdate(5) onkeyup=masterUpdate(5) autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) />
|
|
<label><input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() /><span title="Show devices operating system name">OS Name</span></label>
|
|
</td>
|
|
<td id=kvmListToolbar class=style14 style="display:none">
|
|
<input type="button" onclick="connectAllKvmFunction()" value="Connect All" />
|
|
<input type="button" onclick="disconnectAllKvmFunction()" value="Disconnect All" />
|
|
<label><input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" title="Automatic connect" />Auto </label>
|
|
<input type="button" onclick="showMultiDesktopSettings()" value="Settings" />
|
|
</td>
|
|
<td id=devMapToolbar class=style14 style="display:none">
|
|
<input type=text id=mapSearchLocation placeholder="Search Location" onfocus=onMapSearchFocus(1) onblur=onMapSearchFocus(0) />
|
|
<input type=button value=Search title="Search for location" onclick=getSearchLocation() />
|
|
<input type=button id=refreshmap title="Reset map view" value=Reset onclick=refreshMap(false,true) />
|
|
</td>
|
|
<td class="auto-style1" style=height:100%>
|
|
<div style="display:none" id=devListToolbarView>
|
|
View
|
|
<select id=viewselect onchange=onDeviceViewChange()>
|
|
<option value=1>Columns</option>
|
|
<option value=2>List</option>
|
|
<option value=3>Desktops</option>
|
|
<option id=viewselectmapoption value=4 style="display:none">Map</option>
|
|
</select>
|
|
</div>
|
|
<div style="display:none" id=devListToolbarSort>
|
|
Sort
|
|
<select id=sortselect onchange=masterUpdate(6)>
|
|
<option>Group</option>
|
|
<option>Power</option>
|
|
<option>Device</option>
|
|
<option>Tags</option>
|
|
</select>
|
|
|
|
</div>
|
|
<div style="display:none" id=devListToolbarSize>
|
|
Size
|
|
<select id=sizeselect onchange=onDeviceViewChange()>
|
|
<option value=0>Small</option>
|
|
<option value=1>Medium</option>
|
|
<option value=2>Large</option>
|
|
</select>
|
|
|
|
</div>
|
|
</td>
|
|
<td class=h2></td>
|
|
</tr>
|
|
</table>
|
|
<div id=NoMeshesPanel style="display:none">
|
|
<table>
|
|
<tr>
|
|
<td valign="top" style="width:50px">
|
|
<img src="images/info.png" />
|
|
</td>
|
|
<td>
|
|
<div id="getStarted1">To get started, <a href=# onclick="return account_createMesh()"><strong>click here to create a device group</strong></a>.</div>
|
|
<div id="getStarted2">No device groups.</div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div id="xdevices" class="noselect" style="display:none"></div>
|
|
<div id="xdevicesmap" style="display:none">
|
|
<div id=xmapSearchResultsDlg style="display:none">
|
|
<div id=xmapSearchResultsBck>
|
|
<div id=xmapSearchClose onclick=mapCloseSearchWindow()><b>X</b></div>
|
|
<div style=padding:5px>Location Results</div>
|
|
<div style=width:100%;margin:6px></div>
|
|
</div>
|
|
<div id=xmapSearchResults style=margin:6px></div>
|
|
</div>
|
|
</div>
|
|
<div id="xmap-info-window"></div>
|
|
</div>
|
|
<div id=p2 style="display:none">
|
|
<div id="p2title"><h1>My Account</h1></div>
|
|
<div id="p2info" style="overflow-y:auto">
|
|
<img id="p2AccountImage" alt="" src="images/clipboard-128.png" />
|
|
<div id="p2AccountSecurity" style="display:none">
|
|
<p><strong>Account security</strong></p>
|
|
<div style="margin-left:25px">
|
|
<div id="managePhoneNumber1"><div class="p2AccountActions"><span id="authPhoneNumberCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_managePhone()">Manage phone number</a><br /></span></div>
|
|
<div id="manageEmail2FA"><div class="p2AccountActions"><span id="authEmailSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageAuthEmail()">Manage email authentication</a><br /></span></div>
|
|
<div id="manageAuthApp"><div class="p2AccountActions"><span id="authAppSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageAuthApp()">Manage authenticator app</a><br /></span></div>
|
|
<div id="manageHardwareOtp"><div class="p2AccountActions"><span id="authKeySetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageHardwareOtp(0)">Manage security keys</a><br /></span></div>
|
|
<div id="manageOtp"><div class="p2AccountActions"><span id="authCodesSetupCheck"><strong>✓</strong></span></div><span><a href=# onclick="return account_manageOtp(0)">Manage backup codes</a><br /></span></div>
|
|
</div>
|
|
</div>
|
|
<div id="p2AccountActions">
|
|
<p><strong>Account actions</strong></p>
|
|
<p class="mL">
|
|
<span id="managePhoneNumber2" style="display:none"><a href=# onclick="return account_managePhone()">Manage phone number</a><br /></span>
|
|
<span id="verifyEmailId" style="display:none"><a href=# onclick="return account_showVerifyEmail()">Verify email</a><br /></span>
|
|
<span id="accountEnableNotificationsSpan" style="display:none"><a href=# onclick="return account_enableNotifications()">Enable web notifications</a><br /></span>
|
|
<a href=# onclick="return account_showLocalizationSettings()">Localization Settings</a><br />
|
|
<a href=# onclick="return account_showAccountNotifySettings()">Notification Settings</a><br />
|
|
<span id="p2AccountPassActions">
|
|
<span id="accountChangeEmailAddressSpan" style="display:none"><a href=# onclick="return account_showChangeEmail()">Change email address</a><br /></span>
|
|
<a href=# onclick="return account_showChangePassword()">Change password</a><span id="p2nextPasswordUpdateTime"></span><br />
|
|
<a href=# onclick="return account_showDeleteAccount()">Delete account</a><br />
|
|
</span>
|
|
</p>
|
|
<br style=clear:both />
|
|
</div>
|
|
<strong>Device Groups</strong>
|
|
<span id="p2createMeshLink1">( <a href=# onclick="return account_createMesh()" class="newMeshBtn"> New</a> )</span>
|
|
<br /><br />
|
|
<div id=p2meshes></div>
|
|
<div id=p2noMeshFound style="display:none">No device groups.<span id="p2createMeshLink2"> <a href=# onclick="return account_createMesh()"><strong>Get started here!</strong></a></span></div>
|
|
<br style=clear:both />
|
|
</div>
|
|
</div>
|
|
<div id=p3 style="display:none">
|
|
<div id="p3title"><h1>My Events</h1></div>
|
|
<table class="pTable">
|
|
<tr>
|
|
<td class="h1"></td>
|
|
<td class="auto-style1">
|
|
Show
|
|
<select id=p3limitdropdown onchange=refreshEvents()>
|
|
<option value=60>Last 60</option>
|
|
<option value=120>Last 120</option>
|
|
<option value=250>Last 250</option>
|
|
<option value=500>Last 500</option>
|
|
<option value=1000>Last 1000</option>
|
|
</select>
|
|
<a href=# onclick=p3showDownloadEventsDialog(2)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>
|
|
</td>
|
|
<td class="h2"></td>
|
|
</tr>
|
|
</table>
|
|
<div id=p3events style=""></div>
|
|
</div>
|
|
<div id=p4 style="display:none">
|
|
<div id="p4title"><h1>My Users</h1></div>
|
|
<table class="pTable">
|
|
<tr>
|
|
<td class="h1"></td>
|
|
<td class="style14">
|
|
<div style="float:right">
|
|
<input type=button onclick=showUserBroadcastDialog() style=margin-right:6px value="Broadcast" />
|
|
<a href=# onclick=p4downloadUserInfo()><img style="cursor:pointer" title="Download user information" src="images/link4.png" /></a>
|
|
<a href=# onclick=p4batchAccountCreate()><img id=p4UserBatchCreate style="cursor:pointer;display:none" title="Batch create many user accounts" src="images/link6.png" /></a>
|
|
</div>
|
|
<div>
|
|
<input type=button id=UsersSelectAllButton onclick="p3usersSelectallButtonFunction()" value="Select All" />
|
|
<input type=button id=UsersGroupActionButton disabled="disabled" value="Group Action" onclick=p3usersGroupActionFunction() />
|
|
<input id=UserNewAccountButton type=button onclick=showCreateNewAccountDialog() value="New Account..." />
|
|
<input id=UserSearchInput type=text style=width:120px;margin-left:6px placeholder=Filter onchange=onUserSearchInputChanged() onkeyup=onUserSearchInputChanged() autocomplete=off onfocus=onUserSearchFocus(1) onblur=onUserSearchFocus(0) />
|
|
</div>
|
|
</td>
|
|
<td class="h2"></td>
|
|
</tr>
|
|
</table>
|
|
<div id="p3users"></div>
|
|
</div>
|
|
<div id=p5 style="display:none">
|
|
<div id="p5title"><h1>My Files</h1></div>
|
|
<table id="p5toolbar" cellpadding="0" cellspacing="0">
|
|
<tr>
|
|
<td id="p5filehead" valign=bottom>
|
|
<div id="p5rightOfButtons"></div>
|
|
<div>
|
|
<input type=button id=p5FolderUp disabled="disabled" onclick="return p5folderup();" value="Up" />
|
|
<input type=button id=p5SelectAllButton disabled="disabled" onclick="p5selectallfile();" value="Select All" />
|
|
<input type=button id=p5RenameFileButton disabled="disabled" value="Rename" onclick="p5renamefile();" />
|
|
<input type=button id=p5DeleteFileButton disabled="disabled" value="Delete" onclick="p5deletefile();" />
|
|
<!--<input type=button id=p5ViewFileButton disabled="disabled" value="View" onclick="p5viewfile()" /> -->
|
|
<input type=button id=p5NewFolderButton disabled="disabled" value="New Folder" onclick="p5createfolder();" />
|
|
<input type=button id=p5UploadButton disabled="disabled" value="Upload" onclick="p5uploadFile()" />
|
|
<input type=button id=p5CutButton disabled="disabled" value="Cut" onclick="p5copyFile(1)" />
|
|
<input type=button id=p5CopyButton disabled="disabled" value="Copy" onclick="p5copyFile(0)" />
|
|
<input type=button id=p5PasteButton disabled="disabled" value="Paste" onclick="p5pasteFile()" />
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td id="p5filesubhead">
|
|
<div style=float:right>
|
|
<select id=p5sortdropdown onchange=updateFiles()>
|
|
<option value="1" selected="selected">Sort by name</option>
|
|
<option value="2">Sort by size</option>
|
|
<option value="3">Sort by date</option>
|
|
<option value="4">Descend by name</option>
|
|
<option value="5">Descend by size</option>
|
|
<option value="6">Descend by date</option>
|
|
</select>
|
|
</div>
|
|
<div> <span id="p5currentpath"></span></div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<div id="p5filetable">
|
|
<!--
|
|
<form id=p5fileCatchAll method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame>
|
|
<input type=file id=p5fileCatchAllInput name=files style="position:absolute;left:0;width:100%;top:0;bottom:0;opacity:0;display:none" onchange="p5fileCatchAllInputChanged(event)" />
|
|
<input id=p5fileDragLink2 name="link" style="display:none" />
|
|
<input type=submit id=p5fileCatchAllSubmit style="display:none" />
|
|
</form>
|
|
-->
|
|
<div id="p5PublicShare" style=""><div>These files are shared publicly, click "link" to get public url.</div></div>
|
|
<div id="bigok" style="display:none"><b>✓</b></div>
|
|
<div id="bigfail" style="display:none"><b>✗</b></div>
|
|
<span id="p5files"></span>
|
|
</div>
|
|
<table id="p5toolbarBottom" style=width:100% cellpadding=0 cellspacing=0>
|
|
<tr><td class=style6> <span id="p5bottomstatus"></span></td></tr>
|
|
</table>
|
|
</div>
|
|
<div id=p6 style="display:none">
|
|
<div id="p6info" style="overflow-y:auto">
|
|
<div id="p6title">
|
|
<img id=MainMeshImage src="serverpic.ashx">
|
|
<h1>My Server</h1>
|
|
</div>
|
|
<div id="p2ServerActions">
|
|
<p><strong>Server actions</strong></p>
|
|
<div class="mL">
|
|
<div id="p2ServerActionsBackup"><a href="{{{domainurl}}}backup.zip" rel="noreferrer noopener" target="_blank">Download server backup</a></div>
|
|
<div id="p2ServerActionsRestore"><a href=# onclick="return server_showRestoreDlg()">Restore server with backup</a></div>
|
|
<div id="p2ServerActionsVersion"><a href=# onclick="return server_showVersionDlg()">Check server version</a></div>
|
|
<div id="p2ServerActionsErrors"><a href=# onclick="return server_showErrorsDlg()">Show server error log</a></div>
|
|
</div>
|
|
</div>
|
|
<br /><strong>Server Statistics</strong><br /><br />
|
|
<div id="serverStats">
|
|
<div id="serverCpuChartView" style="display:none">
|
|
<div class="chartViewCanvas"><canvas id="serverCpuChart"></canvas></div>
|
|
<div class="chartViewText" id="serverCpuChartText"></div>
|
|
</div>
|
|
<div id="serverMemoryChartView" style="display:none">
|
|
<div class="chartViewCanvas"><canvas id="serverMemoryChart"></canvas></div>
|
|
<div class="chartViewText" id="serverMemoryChartText"></div>
|
|
</div><br /><br />
|
|
<div id="serverStatsTable"></div>
|
|
</div>
|
|
<div id="serverWarningsDiv" style="display:none">
|
|
<br /><strong>Server Warnings</strong><br /><br />
|
|
<div id="serverWarnings"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id=p10 style="display:none">
|
|
<div id=p10title>
|
|
<div id="p10BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>General - <span id=p10deviceName></span></h1>
|
|
</div>
|
|
<div id=p10info style="overflow-y:auto">
|
|
<table style="width:100%" cellpadding="0" cellspacing="0">
|
|
<tr>
|
|
<td style=width:auto valign=top>
|
|
<div id=p10html></div>
|
|
</td>
|
|
<td style=width:20px></td>
|
|
<td style=width:200px;vertical-align:top valign=top>
|
|
<a href=# onclick=p10showiconselector()><img id=MainComputerImage></a>
|
|
<div id=MainComputerState></div>
|
|
</td>
|
|
</tr>
|
|
</table><br />
|
|
<div id=p10html2></div>
|
|
<div id=p10html3></div>
|
|
<div id=p10html4></div>
|
|
</div>
|
|
</div>
|
|
<div id=p11 class="noselect" style="display:none">
|
|
<div id="p11title">
|
|
<div id=p11deviceNameHeader>
|
|
<div id="p11BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<div id="devListToolbarViewIcons"><div class="viewSelector" onclick=deskToggleFull(event) title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div>
|
|
<h1>Desktop - <span id=p11deviceName></span></h1>
|
|
</div>
|
|
</div>
|
|
<div id="p11warning" onclick="showFeaturesDlg()">
|
|
<div class="icon2"></div>
|
|
<div class="warningbox">Intel® AMT Redirection port or KVM feature is disabled<span id="p11warninga">, click here to enable it.</span></div>
|
|
</div>
|
|
<div id="p11warning2" onclick="showPowerActionDlg()">
|
|
<div class="icon2"></div>
|
|
<div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
|
|
</div>
|
|
<div id=deskarea0 cellpadding=0 cellspacing=0>
|
|
<div id=deskarea1 class="areaHead">
|
|
<div class="toright2">
|
|
<span id="p11power"></span>
|
|
<div class='deskareaicon' title="Toggle View Mode" onclick="toggleAspectRatio(1)">⇲</div>
|
|
<div class='deskareaicon' title="Rotate Left" onclick="drotate(-1)">↺</div>
|
|
<div class='deskareaicon' title="Rotate Right" onclick="drotate(1)">↻</div>
|
|
<div id="deskRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px"></div>
|
|
<input id="deskFocusBtn" type="button" title="Toggle focus mode, when active only the region around the mouse is updated" onkeypress="return false" onkeydown="return false" value="Focus All" onclick="deskToggleFocus()" style="margin-right:3px;display:none" />
|
|
<input id="deskSaveBtn" type="button" title="Save a screenshot of the remote desktop" onkeypress="return false" onkeydown="return false" value="Save..." onclick=deskSaveImage() class="mR" />
|
|
<input id="deskActionsBtn" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() class="mR" />
|
|
<input id="deskActionsSettings" type="button" value="Settings..." title="Edit remote desktop settings" onkeypress="return false" onkeydown="return false" onclick="showDesktopSettings()" class="mR" />
|
|
<input type="button" title="Change the power state of the remote machine" onkeypress="return false" onkeydown="return false" value="Power Actions..." onclick="showPowerActionDlg()" style="display:none" />
|
|
</div>
|
|
<div>
|
|
<div id="idx_deskFullBtn2" onclick=deskToggleFull(event)> ✖</div>
|
|
<input type="button" id="autoconnectbutton1" value="AutoConnect" onclick=autoConnectDesktop(event) onkeypress="return false" onkeydown="return false" style="display:none" />
|
|
<span id=connectbutton1span><input type=button id=connectbutton1 cmenu="deskConnectButton" value="Connect" onclick=connectDesktop(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
|
|
<span id=connectbutton1hspan> <input type=button id=connectbutton1h value="HW Connect" title="Connect using Intel AMT hardware KVM" onclick=connectDesktop(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
|
|
<span id=disconnectbutton1span> <input type=button id=disconnectbutton1 value="Disconnect" onclick=connectDesktop(event,0) onkeypress="return false" onkeydown="return false" /></span>
|
|
<span id="deskstatus">Disconnected</span>
|
|
</div>
|
|
</div>
|
|
<div id=deskarea2 style="">
|
|
<div class="areaProgress"><div id="progressbar" style=""></div></div>
|
|
</div>
|
|
<div id=deskarea3x>
|
|
<div id=DeskFocus oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></div>
|
|
<div id=DeskParent>
|
|
<canvas id=Desk width=640 height=480 oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event) onmousewheel=dmousewheel(event)></canvas>
|
|
</div>
|
|
<div id=DeskTools>
|
|
<div id=deskToolsAreaTop>
|
|
<a id=DeskToolsRefreshButton style="right:2px" onclick="refreshDeskTools()">Refresh</a>
|
|
<div id=deskToolsTopTabProcess class="deskToolsTopTab" onclick="changeDeskToolTab(0)" style="left:0px;bottom:0px">Processes</div>
|
|
<div id=deskToolsTopTabService class="deskToolsTopTab" onclick="changeDeskToolTab(1)" style="display:none;left:90px;color:gray">Services</div>
|
|
</div>
|
|
<div id=deskToolsArea>
|
|
<div id="DeskToolsProcessTab">
|
|
<div id=deskToolsHeader>
|
|
<a class="colmn1" title="Sort by process id" onclick=sortProcess(0)>PID</a>
|
|
<a class="colmn2" title="Sort by name" onclick=sortProcess(1)>Name</a>
|
|
</div>
|
|
<div id="DeskToolsProcesses" style=""></div>
|
|
</div>
|
|
<div id="DeskToolsServiceTab" style="display:none">
|
|
<div id=deskToolsServiceHeader>
|
|
<a class="colmn1" style="width:70px" title="Sort by state" onclick=sortService(0)>State</a>
|
|
<a class="colmn2" title="Sort by name" onclick=sortService(1)>Name</a>
|
|
</div>
|
|
<div id="DeskToolsServices" style=""></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id=p11DeskConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px;text-align:left" onclick=p11clearConsoleMsg()></div>
|
|
<div id=p11DeskSessionSelector style="display:none;position:absolute;left:30px;top:17px;right:30px;bottom:17px;overflow-y:auto"></div>
|
|
</div>
|
|
<div id=deskarea4 class="areaFoot">
|
|
<div class="toright2">
|
|
<span id="DeskLatency" title="Desktop Session Latency"></span>
|
|
<span id="DeskTimer" title="Session time"></span>
|
|
<select id=termdisplays style="display:none" onchange=deskSetDisplay(event) onkeypress="return false" onkeydown="return false"></select>
|
|
<input id=DeskToolsButton type=button value=Tools title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()" />
|
|
<span id=DeskChatButton class="deskarea" title="Open chat window to this computer"><img src='images/icon-chat.png' onclick=deviceChat(event) height=16 width=16 style=padding-top:2px /></span>
|
|
<span id=DeskNotifyButton title="Display a notification on the remote computer"><img src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:2px /></span>
|
|
<span id=DeskOpenWebButton title="Open a web address on the remote computer"><img src='images/icon-url2.png' onclick=deviceUrlFunction() height=16 width=16 style=padding-top:2px /></span>
|
|
<span id=DeskBackgroundButton title="Toggle remote desktop background"><img src='images/icon-background.png' onclick=deviceToggleBackground(event) height=16 width=16 style=padding-top:2px /></span>
|
|
</div>
|
|
<div>
|
|
<select id="deskkeys">
|
|
<option value=10>Ctrl+Alt+Del</option>
|
|
<option value=5>Win</option>
|
|
<option value=0>Win+Down</option>
|
|
<option value=1>Win+Up</option>
|
|
<option value=2>Win+L</option>
|
|
<option value=3>Win+M</option>
|
|
<option value=4>Shift+Win+M</option>
|
|
<option value=6>Win+R</option>
|
|
<option value=7>Alt-F4</option>
|
|
<option value=8>Ctrl-W</option>
|
|
<option value=9>Alt-Tab</option>
|
|
<option value=11>Win+Left</option>
|
|
<option value=12>Win+Right</option>
|
|
</select>
|
|
<input id="DeskWD" type=button value="Send" onkeypress="return false" onkeydown="return false" onclick="deskSendKeys()" />
|
|
<input id="DeskClip" style="" type="button" value="Clipboard" onkeypress="return false" onkeydown="return false" onclick="showDeskClip()" />
|
|
<input id="DeskType" style="" type="button" value="Type" onkeypress="return false" onkeydown="return false" onclick="showDeskType()" />
|
|
<label><span id="DeskControlSpan" title="Toggle mouse and keyboard input"><input id="DeskControl" type="checkbox" onkeypress="return false" onkeydown="return false" onclick="toggleKvmControl()" />Input</span></label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id=p12 style="display:none">
|
|
<div id="p12title">
|
|
<div id="p12BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<div id="devListToolbarViewIcons2" style="float:right"><div class="viewSelector" onclick=deskToggleFull(event) title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div>
|
|
<h1>Terminal - <span id=p12deviceName></span></h1>
|
|
</div>
|
|
<div id="p12warning" onclick=showFeaturesDlg()>
|
|
<div class="icon2"></div>
|
|
<div class="warningbox">Intel® AMT Redirection port or KVM feature is disabled<span id="p12warninga">, click here to enable it.</span></div>
|
|
</div>
|
|
<div id="p12warning2" onclick=showPowerActionDlg()>
|
|
<div class="icon2"></div>
|
|
<div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
|
|
</div>
|
|
<div id=termTable style="position:relative">
|
|
<table style="width:100%" cellpadding=0 cellspacing=0>
|
|
<tr>
|
|
<td class="areaHead">
|
|
<div class="toright2">
|
|
<div id="termRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px;margin-left:5px"></div>
|
|
<input id="termActionsBtn" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() />
|
|
</div>
|
|
<div>
|
|
<div id="idx_termFullBtn2" onclick=deskToggleFull(event)> ✖</div>
|
|
<input type="button" id="autoconnectbutton2" value="AutoConnect" onclick=autoConnectTerminal(event) onkeypress="return false" onkeydown="return false" style="display:none" />
|
|
<span id="connectbutton2span"><input type="button" id="connectbutton2" cmenu="termConnectButton" value="Connect" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
|
|
<span id="connectbutton2hspan"> <input type="button" id="connectbutton2h" value="HW Connect" title="Connect using Intel AMT hardware KVM" onclick=connectTerminal(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
|
|
<span id="disconnectbutton2span"> <input type="button" id="disconnectbutton2" value="Disconnect" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span>
|
|
<span id="termstatus">Disconnected</span><span id="termtitle"></span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<div class="areaProgress"><div id="termprogressbar" style=""></div></div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td id="termarea3x">
|
|
<div id="termarea3xdiv" style="width:100%;height:100%"></div>
|
|
<pre id="Term"></pre>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="areaFoot">
|
|
<div class="toright2">
|
|
<span id="TermTimer" title="Session time"></span>
|
|
<span id="terminalSettingsButtons" style="display:none">
|
|
<input id="id_tcrbutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="CR+LF" title="Toggle what the return key will send" onclick="termToggleCr()" />
|
|
<input id="id_tfxkeysbutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Intel (F10 = ESC+[OM)" title="Toggle F1 to F10 keys emulation type" onclick="termToggleFx()" />
|
|
<input id="id_ttypebutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Extended Ascii" title="Toggle terminal emulation type" onclick="termToggleType()" />
|
|
</span>
|
|
<span id="terminalSizeDropDown" style="display:none">
|
|
<select id="termSizeList" onkeypress="return false"><option value="1">80x25</option><option value="2">100x30</option></select>
|
|
</span>
|
|
<span id="specialKeyDropDown">
|
|
<select id="specialkeylist" onkeypress="return false"></select>
|
|
<input id="specialkeylistinput" type="button" onkeypress="return false" class="bottombutton" value="Send" title="Send the selected special key" onclick="sendSpecialKey()" />
|
|
</span>
|
|
</div>
|
|
<div>
|
|
|
|
<input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlcbutton" value="Ctl-C" onclick="termSendKey(3,'ctrlcbutton')" />
|
|
<input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlxbutton" value="Ctl-X" onclick="termSendKey(24,'ctrlxbutton')" />
|
|
<input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="escbutton" value="ESC" onclick="termSendKey(27,'escbutton')" />
|
|
<input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="bsbutton" value="Backspace" onclick="termSendKey(8,'bsbutton')" style="display:none" />
|
|
<input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="pastebutton" value="Paste" title="Paste text into the terminal" onclick="showTermPasteDialog()" style="display:none" />
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<div id=p12TermConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p12clearConsoleMsg()></div>
|
|
</div>
|
|
</div>
|
|
<div id=p13 style="display:none">
|
|
<div id="p13title">
|
|
<div id="p13BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>Files - <span id=p13deviceName></span></h1>
|
|
</div>
|
|
<table id="p13toolbar" cellpadding="0" cellspacing="0">
|
|
<tr>
|
|
<td class="areaHead">
|
|
<div class="toright2">
|
|
<input id="filesActionsBtn" type=button title="Perform power actions on the device" value=Actions onclick=deviceActionFunction() />
|
|
<div id="filesRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px;margin-left:5px"></div>
|
|
</div>
|
|
<div>
|
|
<input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) type="button" style="display:none" />
|
|
<input id=p13Connect value="Connect" onclick=connectFiles(event) type="button" />
|
|
<span id=p13Status>Disconnected</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="areaHead2" valign=bottom>
|
|
<div id="p13rightOfButtons" class="toright2"></div>
|
|
<div>
|
|
<input type=button id=p13FolderUp disabled="disabled" onclick="p13folderup()" value="Up" />
|
|
<input type=button id=p13SelectAllButton disabled="disabled" onclick="p13selectallfile()" value="Select All" />
|
|
<input type=button id=p13RenameFileButton disabled="disabled" value="Rename" onclick="p13renamefile()" />
|
|
<input type=button id=p13DeleteFileButton disabled="disabled" value="Delete" onclick="p13deletefile()" />
|
|
<input type=button id=p13ViewFileButton disabled="disabled" value="Edit" onclick="p13viewfile()" />
|
|
<input type=button id=p13NewFolderButton disabled="disabled" value="New Folder" onclick="p13createfolder()" />
|
|
<input type=button id=p13UploadButton disabled="disabled" value="Upload" onclick="p13uploadFile()" />
|
|
<input type=button id=p13CutButton disabled="disabled" value="Cut" onclick="p13copyFile(1)" />
|
|
<input type=button id=p13CopyButton disabled="disabled" value="Copy" onclick="p13copyFile(0)" />
|
|
<input type=button id=p13PasteButton disabled="disabled" value="Paste" onclick="p13pasteFile()" />
|
|
<input type=button id=p13RefreshButton disabled="disabled" value="Refresh" onclick="p13folderup(9999)" />
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="areaHead3">
|
|
<div class="toright2">
|
|
<select id=p13sortdropdown onchange=p13updateFiles()>
|
|
<option value=1 selected="selected">Sort by name</option>
|
|
<option value=2>Sort by size</option>
|
|
<option value=3>Sort by date</option>
|
|
<option value=4>Descend by name</option>
|
|
<option value=5>Descend by size</option>
|
|
<option value=6>Descend by date</option>
|
|
</select>
|
|
</div>
|
|
<div> <span id="p13currentpath"></span></div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<div id=p13FilesConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p13clearConsoleMsg()></div>
|
|
<div id="p13filetable" style="">
|
|
<div id="p13bigok" style="display:none"><b>✓</b></div>
|
|
<div id="p13bigfail" style="display:none"><b>✗</b></div>
|
|
<span id="p13files"></span>
|
|
</div>
|
|
<table id="p13toolbarBottom" cellpadding=0 cellspacing=0>
|
|
<tr><td class=style6> <span id="p13bottomstatus"></span></td></tr>
|
|
</table>
|
|
</div>
|
|
<div id=p14 style="display:none">
|
|
<div id="p14title">
|
|
<div id="p14BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<div id="devListToolbarViewIcons"><div class="viewSelector" onclick=deskToggleFull(event) title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div>
|
|
<h1>Intel® AMT - <span id=p14deviceName></span></h1>
|
|
</div>
|
|
<iframe id=p14iframe src="{{{domainurl}}}commander.htm"></iframe>
|
|
</div>
|
|
<div id=p15 style="display:none">
|
|
<div id="p15title">
|
|
<div id="p15BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1><span id=p15deviceName></span></h1>
|
|
</div>
|
|
<table id="consoleTable" cellpadding=0 cellspacing=0>
|
|
<tr>
|
|
<td class="areaHead">
|
|
<div class="toright2">
|
|
<div id=p15coreName title="Information about current core running on this agent"></div>
|
|
<input type=button id=p15uploadCore value="Agent Action" onclick=p15uploadCore(event) title="Change the agent Java Script code module" />
|
|
<img onclick=p15downloadConsoleText() style="cursor:pointer;margin-top:6px" title="Download console text" src="images/link4.png" />
|
|
</div>
|
|
<div id="p15statetext"></div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<div class="areaProgress"><div id="consoleprogressbar" style=""></div></div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td id=p15agentConsole>
|
|
<pre id=p15agentConsoleText></pre>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="areaFoot">
|
|
<table style="width:100%">
|
|
<tr>
|
|
<td style="width:99%">
|
|
<input id=p15consoleText style=width:100% onkeyup=p15consoleSend(event) onfocus=onConsoleFocus(1) onblur=onConsoleFocus(0) />
|
|
</td>
|
|
<td> </td>
|
|
<td id="p15outputselecttd">
|
|
<select id=p15outputselect>
|
|
<option value=1>Agent</option>
|
|
<option value=2>MQTT</option>
|
|
</select>
|
|
</td>
|
|
<td style="width:1%"><input id="id_p15consoleClear" type="button" class="bottombutton" value="Clear" onclick="p15consoleClear()" /></td>
|
|
</tr>
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div id=p16 style="display:none">
|
|
<div id="p16title">
|
|
<div id="p16BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>Events - <span id=p16deviceName></span></h1>
|
|
</div>
|
|
<table class="pTable">
|
|
<tr>
|
|
<td class="h1"></td>
|
|
<!--<td> <input type=button onclick=refreshDeviceEvents() value="Refresh" /></td>-->
|
|
<td class="auto-style1">
|
|
Show
|
|
<select id=p16limitdropdown onchange=refreshDeviceEvents()>
|
|
<option value=60>Last 60</option>
|
|
<option value=120>Last 120</option>
|
|
<option value=250>Last 250</option>
|
|
<option value=500>Last 500</option>
|
|
<option value=1000>Last 1000</option>
|
|
</select>
|
|
<a href=# onclick=p3showDownloadEventsDialog(1)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>
|
|
</td>
|
|
<td class="h2"></td>
|
|
</tr>
|
|
</table>
|
|
<div id=p16events></div>
|
|
</div>
|
|
<div id=p17 style="display:none;margin-left:-18px">
|
|
<div id="p17title" style="margin-left:18px">
|
|
<div id="p17BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>Details - <span id=p17deviceName></span></h1>
|
|
</div>
|
|
<div id=p17info style="overflow-y:auto"></div>
|
|
</div>
|
|
<div id=p19 style="display:none">
|
|
<div id="p19title">
|
|
<div id="p19BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>Plugins - <span id=p19deviceName></span></h1>
|
|
</div>
|
|
<div id="p19headers"></div>
|
|
<div id=p19pages></div>
|
|
</div>
|
|
<div id=p20 style="display:none">
|
|
<div id="p20title">
|
|
<picture id=MainMeshImage style=border-width:0px;height:200px;width:200px;float:right>
|
|
<source type="image/webp" width=200 height=200 srcset="images/webp/mesh-256.webp" />
|
|
<img alt="" width=200 height=200 src=images/mesh-256.png />
|
|
</picture>
|
|
<div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>General - <span id=p20meshName></span></h1>
|
|
</div>
|
|
<p id=p20info></p>
|
|
</div>
|
|
<div id=p21 style="display:none">
|
|
<div id="p21title">
|
|
<div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>Summary - <span id=p21meshName></span></h1>
|
|
</div>
|
|
<div style="width:100%">
|
|
<div style="display:table;width:93%">
|
|
<div id="meshPowerChartDiv" style="width:31%;display:inline-block;text-align:center;max-width:250px">
|
|
<div style="margin:10px;font-size:16px">Power States</div>
|
|
<canvas id="meshPowerChart" style="width:250px;height:250px"></canvas>
|
|
</div>
|
|
<div id="meshOsChartDiv" style="width:31%;display:inline-block;text-align:center;max-width:250px">
|
|
<div style="margin:10px;font-size:16px">Agent Types</div>
|
|
<canvas id="meshOsChart" style="width:250px;height:250px"></canvas>
|
|
</div>
|
|
<div id="meshConnChartDiv" style="width:31%;display:inline-block;text-align:center;max-width:250px">
|
|
<div style="margin:10px;font-size:16px">Connectivity</div>
|
|
<canvas id="meshConnChart" style="width:250px;height:250px"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p id=p21info></p>
|
|
</div>
|
|
<div id=p30 style="display:none">
|
|
<div id="p30title">
|
|
<div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>General - <span id=p30userName></span></h1>
|
|
</div>
|
|
<div id="p30info" style="overflow-y:auto">
|
|
<table style="width:100%" cellpadding="0" cellspacing="0">
|
|
<tr>
|
|
<td style=width:auto valign=top>
|
|
<div id=p30html></div>
|
|
</td>
|
|
<td style=width:20px></td>
|
|
<td style=width:200px valign=top>
|
|
<picture id=MainUserImage style=border-width:0px;height:200px;width:200px;float:right>
|
|
<source type="image/webp" width=200 height=200 srcset="images/webp/user-256.webp" />
|
|
<img alt="" width=200 height=200 src=images/user-256.png />
|
|
</picture>
|
|
<div style="width:100%;text-align:center"><strong><span id=MainUserState></span></strong></div>
|
|
</td>
|
|
</tr>
|
|
</table><br />
|
|
<div id=p30html2></div>
|
|
<div id=p30html3></div>
|
|
</div>
|
|
</div>
|
|
<div id=p31 style="display:none">
|
|
<div id="p31title">
|
|
<div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>Events - <span id=p31userName></span></h1>
|
|
</div>
|
|
<table class="pTable">
|
|
<tr>
|
|
<td class="h1"></td>
|
|
<!--<td> <input type=button onclick=refreshUsersEvents() value="Refresh" /></td>-->
|
|
<td class="auto-style1">
|
|
Show
|
|
<select id=p31limitdropdown onchange=refreshUsersEvents()>
|
|
<option value=60>Last 60</option>
|
|
<option value=120>Last 120</option>
|
|
<option value=250>Last 250</option>
|
|
<option value=500>Last 500</option>
|
|
<option value=1000>Last 1000</option>
|
|
</select>
|
|
<a href=# onclick=p3showDownloadEventsDialog(3)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>
|
|
</td>
|
|
<td class="h2"></td>
|
|
</tr>
|
|
</table>
|
|
<div id=p31events style=""></div>
|
|
</div>
|
|
<div id=p40 style="display:none">
|
|
<div id="p40title"><h1>My Server Stats</h1></div>
|
|
<div class="areaHead">
|
|
<div class="toright2">
|
|
<select id=p40type onchange=updateServerTimelineStats()>
|
|
<option value=0>Connections</option>
|
|
<option value=1>Memory</option>
|
|
</select>
|
|
<select id=p40time onchange=updateServerTimelineHours()>
|
|
<option value=3>Last 3 hours</option>
|
|
<option value=8>Last 8 hours</option>
|
|
<option value=24>Last day</option>
|
|
<option value=168>Last week</option>
|
|
<option value=720>Last 30 days</option>
|
|
</select>
|
|
<img src=images/link4.png height=10 width=10 title="Download data points (.csv)" style=cursor:pointer onclick=p40downloadEvents()>
|
|
</div>
|
|
<div>
|
|
<input value="Refresh" type="button" onclick="refreshServerTimelineStats()" />
|
|
<label><input id=p40log type="checkbox" onclick="updateServerTimelineHours()" />Log-X</label>
|
|
</div>
|
|
</div>
|
|
<canvas id=serverMainStats style=""></canvas>
|
|
</div>
|
|
<div id=p41 style="display:none">
|
|
<div id="p41title"><h1>My Server Tracing</h1></div>
|
|
<div class="areaHead">
|
|
<div class="toright2">
|
|
Show
|
|
<select id=p41limitdropdown onchange=displayServerTrace()>
|
|
<option value=100>Last 100</option>
|
|
<option value=250>Last 250</option>
|
|
<option value=500>Last 500</option>
|
|
<option value=1000>Last 1000</option>
|
|
</select>
|
|
<input value="Clear" type="button" onclick="clearServerTracing()" />
|
|
<img src=images/link4.png height=10 width=10 title="Download trace (.csv)" style=cursor:pointer onclick=p41downloadServerTrace()>
|
|
</div>
|
|
<div>
|
|
<input value="Tracing" type="button" onclick="setServerTracing()" />
|
|
<span id="p41traceStatus">None</span>
|
|
</div>
|
|
</div>
|
|
<div id=p41events style=""></div>
|
|
</div>
|
|
<div id=p42 style="display:none">
|
|
<h1>My Server Plugins</h1>
|
|
<div class="areaHead">
|
|
<div class="toright2">
|
|
</div>
|
|
<div>
|
|
<input value="Download Plugin" type="button" onclick="return pluginHandler.addPluginDlg();" />
|
|
</div>
|
|
</div>
|
|
<div id="pluginRestartNotice" class="areaHead" style="background-color:gold;display:none">
|
|
<div class="toright2">
|
|
<input value="Refresh Agent Cores" type="button" onclick="distributeCore();return false" />
|
|
</div>
|
|
<div style="padding:2px">
|
|
<div style="padding:2px"><b>Notice:</b> Plugins have been altered, this may require agent core update.</div>
|
|
</div>
|
|
</div>
|
|
<table id="p42tbl">
|
|
<tr class="DevSt"><th style="width:26px"></th><th style="width:10px"></th><th class="chName">Name</th><th class="chDescription">Description</th><th class="chSite" style="text-align:center">Link</th><th class="chVersion" style="text-align:center">Version</th><th class="chUpgradeAvail" style="text-align:center">Latest</th><th class="chStatus" style="text-align:center">Status</th><th class="chAction" style="text-align:center">Action</th><th style="width:10px"></th></tr>
|
|
</table>
|
|
<div id="pluginNoneNotice" style="width:100%;text-align:center;padding-top:10px;display:none"><i>No plugins on server.</i></div>
|
|
</div>
|
|
<div id=p43 style="display:none">
|
|
<div id="p43BackButton"><div class="backButton" tabindex=0 onclick=go(42) title="Back" onkeypress="if (event.key == 'Enter') go(42)"><div class="backButtonEx"></div></div></div>
|
|
<h1>My Server Plugins - <span id="p43title"></span></h1>
|
|
<iframe id="p43iframe" frameBorder=0 style="width:100%;height:calc(100vh - 245px);max-height:calc(100vh - 245px)"></iframe>
|
|
</div>
|
|
<div id=p50 style="display:none">
|
|
<div id="p50title"><h1>My User Groups</h1></div>
|
|
<table class="pTable">
|
|
<tr>
|
|
<td class="h1"></td>
|
|
<td class="style14">
|
|
<div style="float:right">
|
|
</div>
|
|
<div>
|
|
<input type=button id=UsersGroupsSelectAllButton onclick="p50usersSelectallButtonFunction()" value="Select All" />
|
|
<input type=button id=UsersGroupsGroupActionButton disabled="disabled" value="Group Action" onclick=p50usersGroupActionFunction() />
|
|
<input id=NewUserGroupButton type=button onclick=showCreateUserGroupDialog(1) value="New Group..." />
|
|
<input id=DuplicateUserGroupButton type=button style=display:none onclick=showCreateUserGroupDialog(2) value="Duplicate Group..." />
|
|
</div>
|
|
</td>
|
|
<td class="h2"></td>
|
|
</tr>
|
|
</table>
|
|
<div id="p50groups"></div>
|
|
</div>
|
|
<div id=p51 style="display:none">
|
|
<div id="p30title">
|
|
<div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
|
|
<h1>User Group - <span id=p51groupName></span></h1>
|
|
</div>
|
|
<div id="p51info" style="overflow-y:auto">
|
|
<table style="width:100%" cellpadding="0" cellspacing="0">
|
|
<tr>
|
|
<td style=width:auto valign=top>
|
|
<div id=p51group></div>
|
|
</td>
|
|
<td style=width:20px></td>
|
|
<td style=width:200px valign=top>
|
|
<picture id=MainUserImage style=border-width:0px;height:200px;width:200px;float:right>
|
|
<source type="image/webp" width=200 height=200 srcset="images/webp/group-256.webp" />
|
|
<img alt="" width=200 height=200 src=images/group-256.png />
|
|
</picture>
|
|
<div style="width:100%;text-align:center"><strong><span id=MainUserState></span></strong></div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<div id=p51group2></div>
|
|
<br />
|
|
</div>
|
|
</div>
|
|
<br id="column_l_bottomgap" />
|
|
</div>
|
|
<div id="footer">
|
|
<div class="footer1">{{{footer}}}</div>
|
|
<div class="footer2">
|
|
<a id="verifyEmailId2" style="display:none" href=# onclick="account_showVerifyEmail()">Verify Email</a>
|
|
<a id="termsLinkFooter" href=terms>Terms & Privacy</a>
|
|
</div>
|
|
</div>
|
|
<div id=dialog class="noselect" style="display:none">
|
|
<div id=dialogHeader>
|
|
<div tabindex=0 id=id_dialogclose onclick=setDialogMode() onkeypress="if (event.key == 'Enter') setDialogMode()">✖</div>
|
|
<div id=id_dialogtitle></div>
|
|
</div>
|
|
<div id=dialogBody>
|
|
<div id=dialog1>
|
|
<div id=id_dialogMessage style=""></div>
|
|
</div>
|
|
<div id=dialog2 style="">
|
|
<div id=id_dialogOptions></div>
|
|
</div>
|
|
<div id=dialog3 style="">
|
|
<div id=d3upload>
|
|
<div>File Selection</div>
|
|
<select id=d3uploadMode onchange=d3modechange()>
|
|
<option value=1>Local file upload</option>
|
|
<option value=2>Server file selection</option>
|
|
</select>
|
|
</div>
|
|
<div id=d3localmode style="display:none">
|
|
<div>Upload File</div>
|
|
<form id=d3localmodeform method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame>
|
|
<input type=text id=d3auth name=auth style="display:none" />
|
|
<input type=text id=d3attrib name=attrib style="display:none" />
|
|
<input type=file id=d3localFile name=files onchange=d3setActions() />
|
|
<input type=submit id=d3submit style="display:none" />
|
|
</form>
|
|
</div>
|
|
<div id=d3servermode>
|
|
<div id=d3serveraction valign=bottom>
|
|
<input type=button id=p3FolderUp disabled="disabled" onclick=d3folderup() value="Up" />
|
|
</div>
|
|
<div id=d3serverfiles></div>
|
|
</div>
|
|
</div>
|
|
<div id=dialog4 style="">
|
|
<input id="d4WrapButton" type="button" value="Wrap On" onclick="d4ToggleWrap()" />
|
|
<input id="d4SizeButton" type="button" value="Small" onclick="d4ToggleSize()" />
|
|
<textarea id=d4editorarea autocomplete="off" style="height:calc(100vh - 286px);width:100%;overflow:scroll;resize:none;white-space:pre"></textarea>
|
|
</div>
|
|
<div id=dialog7 style="">
|
|
<div id="d7meshkvm">
|
|
<h4>Agent Remote Desktop</h4>
|
|
<div>
|
|
<div>Quality</div>
|
|
<select id="d7bitmapquality" dir="rtl"></select>
|
|
</div>
|
|
<div>
|
|
<div>Scaling</div>
|
|
<select id="d7bitmapscaling" style="" dir="rtl">
|
|
<option selected=selected value=1024>100%</option>
|
|
<option value=896>87.5%</option>
|
|
<option value=768>75%</option>
|
|
<option value=640>62.5%</option>
|
|
<option value=512>50%</option>
|
|
<option value=384>37.5%</option>
|
|
<option value=256>25%</option>
|
|
<option value=128>12.5%</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<div>Frame rate</div>
|
|
<select id="d7framelimiter" dir="rtl">
|
|
<option selected=selected value=50>Fast</option>
|
|
<option value=100>Medium</option>
|
|
<option value=400>Slow</option>
|
|
<option value=1000>Very slow</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div id="d7amtkvm">
|
|
<h4>Intel® AMT Hardware KVM</h4>
|
|
<div>
|
|
<div>Image Encoding</div>
|
|
<select id="d7desktopmode">
|
|
<option value="1">RLE8, Fastest</option>
|
|
<option value="2">RLE16, Recommended</option>
|
|
<option value="3">RAW8, Slow</option>
|
|
<option value="4">RAW16, Very Slow</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<div>Other Settings</div>
|
|
<div id="d7otherset" style="display:block">
|
|
<label style="display:block"><input type="checkbox" id="d7showfocus" />Show Focus Tool</label>
|
|
<label style="display:block"><input type="checkbox" id="d7showcursor" />Show Local Mouse Cursor</label>
|
|
<label style="display:block"><input type="checkbox" id="d7localKeyMap" />Local Keyboard Map</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="idx_dlgButtonBar">
|
|
<input id="idx_dlgCancelButton" type="button" value="Cancel" style="" onclick="dialogclose(0)" />
|
|
<input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)" />
|
|
<div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)" /></div>
|
|
</div>
|
|
</div>
|
|
<iframe name="fileUploadFrame" style="display:none"></iframe>
|
|
<form style="display:none" method=post action=uploadfile.ashx enctype=multipart/form-data target=fileUploadFrame><input id=p5fileDragName name="name" /><input id=p5fileDragAuthCookie name="auth" /><input id=p5fileDragSize name="size" /><input id=p5fileDragType name="type" /><input id=p5fileDragData name="data" /><input id=p5fileDragLink name="link" /><input type=submit id=p5loginSubmit2 style="display:none" /></form>
|
|
<form style="display:none" method=post action=uploadnodefile.ashx enctype=multipart/form-data target=fileUploadFrame><input id=p13fileDragName name="name" /><input id=p13fileDragSize name="size" /><input id=p13fileDragType name="type" /><input id=p13fileDragData name="data" /><input id=p13fileDragLink name="link" /><input type=submit id=p13loginSubmit2 style="display:none" /></form>
|
|
<audio id="chimes"><source src="sounds/chimes.mp3" type="audio/mp3" /></audio>
|
|
</div>
|
|
<script type="text/javascript" nonce="abc">
|
|
'use strict';
|
|
|
|
// Process server-side web state
|
|
var webState = '{{{webstate}}}';
|
|
if (webState != '') { webState = JSON.parse(decodeURIComponent(webState)); }
|
|
for (var i in webState) { if (i != 'desktopsettings') { localStorage.setItem(i, webState[i]); } }
|
|
if (!webState.loctag) { try { delete localStorage.removeItem('loctag'); } catch (ex) { } }
|
|
|
|
var args, urlargs;
|
|
var autoReconnect = true;
|
|
var powerStatetable = ['', "Powered", "Sleep", "Sleep", "Sleep", "Hibernating", "Power off", "Present"];
|
|
var StatusStrs = ["Disconnected", "Connecting...", "Setup...", "Connected", "Intel® AMT Connected"];
|
|
var agentsStr = ["Unknown", "Windows 32bit console", "Windows 64bit console", "Windows 32bit service", "Windows 64bit service", "Linux 32bit", "Linux 64bit", "MIPS", "XENx86", "Android ARM", "Linux ARM", "MacOS 32bit", "Android x86", "PogoPlug ARM", "Android APK", "Linux Poky x86-32bit", "MacOS 64bit", "ChromeOS", "Linux Poky x86-64bit", "Linux NoKVM x86-32bit", "Linux NoKVM x86-64bit", "Windows MinCore console", "Windows MinCore service", "NodeJS", "ARM-Linaro", "ARMv6l / ARMv7l", "ARMv8 64bit", "ARMv6l / ARMv7l / NoKVM", "Unknown", "Unknown", "FreeBSD x86-64"];
|
|
var sort = 0;
|
|
var searchFocus = 0;
|
|
var mapSearchFocus = 0;
|
|
var userSearchFocus = 0;
|
|
var consoleFocus = 0;
|
|
var showRealNames = false;
|
|
var meshserver = null;
|
|
var meshes = {};
|
|
var meshcount = 0;
|
|
var nodes = null;
|
|
var usergroups = null;
|
|
var filetree = {};
|
|
var userinfo = null;
|
|
var serverinfo = null;
|
|
var events = [];
|
|
var users = null;
|
|
var wssessions = null;
|
|
var nodeShortIdent = 0;
|
|
var desktop;
|
|
var desktopsettings = { encoding: 2, showfocus: false, showmouse: true, showcad: true, quality: 40, scaling: 1024, framerate: 50, localkeymap: false };
|
|
var multidesktopsettings = { quality: 20, scaling: 128, framerate: 1000 };
|
|
var terminal;
|
|
var files;
|
|
var debugLevel = parseInt('{{{debuglevel}}}');
|
|
var features = parseInt('{{{features}}}');
|
|
var sessionTime = parseInt('{{{sessiontime}}}');
|
|
var domain = '{{{domain}}}';
|
|
var domainUrl = '{{{domainurl}}}';
|
|
var authCookie = '{{{authCookie}}}';
|
|
var authRelayCookie = '{{{authRelayCookie}}}';
|
|
var logoutControls = {{{logoutControls}}};
|
|
var authCookieRenewTimer = null;
|
|
var multiDesktop = {};
|
|
var multiDesktopFilter = null;
|
|
var serverPublicNamePort = '{{{serverDnsName}}}:{{{serverPublicPort}}}';
|
|
var amtScanResults = null;
|
|
var debugmode = 0;
|
|
var clickOnce = (((features & 256) != 0) && detectClickOnce());
|
|
var attemptWebRTC = ((features & 128) != 0);
|
|
var passRequirements = '{{{passRequirements}}}';
|
|
if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
|
|
var deskAspectRatio = 0;
|
|
try { deskAspectRatio = parseInt(getstore('deskAspectRatio', '0')); } catch (ex) { }
|
|
var uiMode = parseInt(getstore('uiMode', 1));
|
|
var webPageStackMenu = false;
|
|
var webPageFullScreen = true;
|
|
var nightMode = (getstore('nightMode', '0') == '1');
|
|
var footerBar = (getstore('footerBar', '1') == '1');
|
|
var sessionActivity = Date.now();
|
|
var updateSessionTimer = null;
|
|
var pluginHandlerBuilder = {{{pluginHandler}}};
|
|
var pluginHandler = null;
|
|
if (pluginHandlerBuilder != null) { pluginHandler = new pluginHandlerBuilder(); }
|
|
var installedPluginList = null;
|
|
var goBackStack = [];
|
|
var CollapsedGroups = {};
|
|
try { CollapsedGroups = JSON.parse(getstore('_collapse', '{}')); } catch(ex) {}
|
|
var xterm = null;
|
|
var xtermfit = null;
|
|
var xtermResizeTimer = null;
|
|
|
|
// Console Message Display Timers
|
|
var p11DeskConsoleMsgTimer = null;
|
|
var p12TermConsoleMsgTimer = null;
|
|
var p13FilesConsoleMsgTimer = null;
|
|
|
|
function startup() {
|
|
if ((features & 32) == 0) {
|
|
// Guard against other site's top frames (web bugs).
|
|
var loc = null;
|
|
try { loc = top.location.toString().toLowerCase(); } catch (e) { }
|
|
if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; }
|
|
}
|
|
|
|
// Fetch URL arguments
|
|
urlargs = parseUriArgs();
|
|
delete urlargs.viewmode;
|
|
delete urlargs.gotonode;
|
|
delete urlargs.gotomesh;
|
|
delete urlargs.gotouser;
|
|
delete urlargs.gotougrp;
|
|
|
|
// Fix links if a loginKey is used
|
|
if (urlargs.key) {
|
|
Q('termsLinkFooter').href += '?key=' + urlargs.key;
|
|
}
|
|
|
|
// Check if we are in debug mode
|
|
args = parseUriArgs();
|
|
if (!args.locale) { var x = getstore('loctag', 0); if ((x != null) && (x != '*')) { args.locale = x; } }
|
|
debugmode = args.debug;
|
|
if (args.webrtc != null) { attemptWebRTC = (args.webrtc == 1); }
|
|
QV('p13AutoConnect', debugmode); // Files
|
|
QV('autoconnectbutton2', debugmode); // Terminal
|
|
QV('autoconnectbutton1', debugmode); // Desktop
|
|
//QV('DeskClip', debugmode); // Clipboard feature, not completed so show in in debug mode only.
|
|
|
|
if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; }
|
|
toggleFullScreen();
|
|
|
|
// Setup page visuals
|
|
var hide = 0;
|
|
var globalHide = parseInt('{{{hide}}}');
|
|
if (globalHide || args.hide) {
|
|
if (args.hide) { hide = parseInt(args.hide); }
|
|
if (globalHide) { hide = (hide | globalHide); }
|
|
}
|
|
args.hide = hide;
|
|
QV('uiViewButton5', !(args.hide & 4)); // Hide the footer toggle button if footer is hidden anyway.
|
|
adjustPanels();
|
|
|
|
// Setup logout control
|
|
var logoutControl = '';
|
|
if (logoutControls)
|
|
if (logoutControls.name != null) { logoutControl = format("Welcome {0}.", logoutControls.name); }
|
|
if (logoutControls.logoutUrl != null) { logoutControl += format(' <a href=\"' + logoutControls.logoutUrl + '\" style="color:white">' + "Logout" + '</a>'); }
|
|
if (args.hide & 1) { QH('logoutControlSpan2', logoutControl); } else { QH('logoutControlSpan', logoutControl); }
|
|
|
|
// Setup the context menu
|
|
document.onclick = function (e) { hideContextMenu(); }
|
|
document.onkeypress = ondockeypress;
|
|
document.onkeydown = ondockeydown;
|
|
document.onkeyup = ondockeyup;
|
|
//window.addEventListener('focus', ondocfocus, false);
|
|
window.addEventListener('blur', ondocblur, false);
|
|
window.onresize = function () {
|
|
hideContextMenu();
|
|
masterUpdate(512);
|
|
if (xtermfit != null) { xtermfit.fit(); }
|
|
}
|
|
setTimeout(function() { masterUpdate(512); }, 200);
|
|
|
|
// Connect to the mesh server
|
|
meshserver = MeshServerCreateControl(domainUrl, authCookie);
|
|
meshserver.onStateChanged = onStateChanged;
|
|
meshserver.onMessage = onMessage;
|
|
meshserver.trace = (args.trace == 1);
|
|
meshserver.Start();
|
|
|
|
// Setup page controls
|
|
Q('sortselect').selectedIndex = sort = getstore('sort', 0);
|
|
Q('sizeselect').selectedIndex = getstore('viewsize', 1);
|
|
Q('SearchInput').value = getstore('_search', '');
|
|
showRealNames = (getstore('showRealNames', 0) == 1);
|
|
Q('RealNameCheckBox').checked = showRealNames;
|
|
Q('viewselect').value = getstore('deviceView', 1);
|
|
Q('DeskControl').checked = (getstore('DeskControl', 1) == 1);
|
|
QV('accountChangeEmailAddressSpan', (features & 0x200000) == 0);
|
|
|
|
// Display the page devices
|
|
masterUpdate(3)
|
|
for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
|
|
Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
|
|
|
|
// Setup upload drag & drop
|
|
Q('p5filetable').addEventListener('drop', p5fileDragDrop, false);
|
|
Q('p5filetable').addEventListener('dragover', p5fileDragOver, false);
|
|
Q('p5filetable').addEventListener('dragleave', p5fileDragLeave, false);
|
|
//Q('p5fileCatchAllInput').addEventListener('drop', p5fileDragDrop, false);
|
|
//Q('p5fileCatchAllInput').addEventListener('dragover', p5fileDragOver, false);
|
|
//Q('p5fileCatchAllInput').addEventListener('dragleave', p5fileDragLeave, false);
|
|
|
|
// Setup upload drag & drop
|
|
Q('p13filetable').addEventListener('drop', p13fileDragDrop, false);
|
|
Q('p13filetable').addEventListener('dragover', p13fileDragOver, false);
|
|
Q('p13filetable').addEventListener('dragleave', p13fileDragLeave, false);
|
|
|
|
// Timeline update interval
|
|
setInterval(updateDeviceTimeline, 120000); // Check every 2 minutes
|
|
|
|
// Load desktop settings
|
|
var t = null;
|
|
try { t = localStorage.getItem('desktopsettings'); } catch (ex) {}
|
|
if (t != null) { desktopsettings = JSON.parse(t); }
|
|
t = null;
|
|
try { t = localStorage.getItem('multidesktopsettings'); } catch (ex) {}
|
|
if (t != null) { multidesktopsettings = JSON.parse(t); }
|
|
applyDesktopSettings();
|
|
|
|
// Terminal special keys
|
|
var x = '';
|
|
for (var c = 1; c < 27; c++) x += '<option value=\'' + c + '\'>' + "Ctrl" + '-' + String.fromCharCode(64 + c) + ' (' + c + ')</option>';
|
|
QH('specialkeylist', x);
|
|
|
|
// Setup server stats panels
|
|
setupGeneralServerStats();
|
|
setupServerTimelineStats();
|
|
|
|
// Setup the user interface in the right mode
|
|
userInterfaceSelectMenu();
|
|
|
|
// Setup Mesh summary panel
|
|
setupMeshSummaryStats();
|
|
|
|
// If SSPI or LDAP authentication not used, allow batch account creation.
|
|
QV('p4UserBatchCreate', (features & 0x00080000) == 0);
|
|
|
|
// Set the file editor
|
|
d4EditWrapVal = getstore('editorWrap', 0);
|
|
d4EditSizeVal = getstore('editorSize', 0);
|
|
d4ToggleWrap(true);
|
|
d4ToggleSize(true);
|
|
if (pluginHandler != null) pluginHandler.callHook('onWebUIStartupEnd');
|
|
|
|
// Deleted non-english style and fix all topbar titles
|
|
if (('{{{lang}}}' != 'en') && ('{{{lang}}}' != '')) { QC('body').add('nonenglish'); }
|
|
var elements = document.getElementsByClassName('topbar_td');
|
|
for (var i in elements) { if (elements[i].innerHTML) { elements[i].innerHTML = elements[i].innerHTML.split(' ').join(' '); } }
|
|
}
|
|
|
|
function adjustPanels() {
|
|
var hide = args.hide;
|
|
if (footerBar == false) { hide |= 4; }
|
|
QV('masthead', !(hide & 1));
|
|
QV('topbar', !(hide & 2));
|
|
QV('footer', !(hide & 4));
|
|
QV('p1title', !(hide & 8));
|
|
QV('p2title', !(hide & 8));
|
|
QV('p3title', !(hide & 8));
|
|
QV('p4title', !(hide & 8));
|
|
QV('p5title', !(hide & 8));
|
|
QV('p6title', !(hide & 8));
|
|
QV('p10title', !(hide & 8));
|
|
QV('p11title', !(hide & 8));
|
|
QV('p12title', !(hide & 8));
|
|
QV('p13title', !(hide & 8));
|
|
QV('p14title', !(hide & 8));
|
|
QV('p15title', !(hide & 8));
|
|
QV('p16title', !(hide & 8));
|
|
QV('p17title', !(hide & 8));
|
|
QV('p20title', !(hide & 8));
|
|
QV('p21title', !(hide & 8));
|
|
QV('p30title', !(hide & 8));
|
|
QV('p31title', !(hide & 8));
|
|
QV('p40title', !(hide & 8));
|
|
QV('p41title', !(hide & 8));
|
|
//if (hide & 16) { QV('page_leftbar', false); QS('page_content').left = '0px'; }
|
|
if (!footerBar) { QC('body').add('nofooter'); } else { QC('body').remove('nofooter'); }
|
|
|
|
if (args.hide != 0) {
|
|
// Fix the main grid to zero-height elements we want to hide.
|
|
if (uiMode == 2) {
|
|
QS('container')['grid-template-rows'] = ((hide & 1) ? '0' : '66') + 'px fit-content(48px) auto ' + ((hide & 4) ? '0' : '45') + 'px';
|
|
QS('container')['-ms-grid-rows'] = ((hide & 1) ? '0' : '66') + 'px fit-content(48px) auto ' + ((hide & 4) ? '0' : '45') + 'px';
|
|
} else {
|
|
QS('container')['grid-template-rows'] = ((hide & 1) ? '0' : '66') + 'px ' + ((hide & 2) ? '0' : '24') + 'px auto ' + ((hide & 4) ? '0' : '45') + 'px';
|
|
QS('container')['-ms-grid-rows'] = ((hide & 1) ? '0' : '66') + 'px ' + ((hide & 2) ? '0' : '24') + 'px auto ' + ((hide & 4) ? '0' : '45') + 'px';
|
|
}
|
|
}
|
|
|
|
// Adjust height of remote desktop, files and Intel AMT
|
|
// 1 = Top bar, 2 = Tool Bar, 4 = Bottom Bar, 8 = Tab Title
|
|
var xh = (((hide & 1) ? 0 : 66) + ((hide & 2) ? 0 : 24) + ((hide & 4) ? 0 : 45) + ((hide & 8) ? 0 : 60)); // 0 to 195
|
|
var xh2 = (uiMode > 1)?24:0;
|
|
QS('p2info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p2info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p3users')['max-height'] = 'calc(100vh - ' + (50 + xh) + 'px)'; // 124
|
|
QS('p3events')['height'] = 'calc(100vh - ' + (50 + xh) + 'px)'; // 124
|
|
QS('p5filetable')['height'] = 'calc(100vh - ' + (99 + xh) + 'px)'; // 160
|
|
QS('p13filetable')['height'] = 'calc(100vh - ' + (127 + xh + xh2) + 'px)'; // 124
|
|
QS('serverMainStats')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)'; // 110
|
|
QS('serverMainStats')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)'; // 110
|
|
QS('xdevices')['max-height'] = 'calc(100vh - ' + (46 + xh) + 'px)'; // 124
|
|
QS('xdevicesmap')['max-height'] = 'calc(100vh - ' + (46 + xh) + 'px)'; // 124
|
|
QS('p15agentConsole')['height'] = 'calc(100vh - ' + (84 + xh + xh2) + 'px)';
|
|
QS('p15agentConsole')['max-height'] = 'calc(100vh - ' + (84 + xh + xh2) + 'px)';
|
|
QS('p15agentConsoleText')['height'] = 'calc(100vh - ' + (81 + xh + xh2) + 'px)';
|
|
QS('p15agentConsoleText')['max-height'] = 'calc(100vh - ' + (81 + xh + xh2) + 'px)';
|
|
var xtermActive = !((args.xterm === 0) || ((terminal != null) && (xterm == null)));
|
|
if (fullscreen) {
|
|
QS('deskarea3x')['height'] = null;
|
|
QS('deskarea3x')['max-height'] = null;
|
|
QS('p14iframe')['height'] = null;
|
|
QS('p14iframe')['max-height'] = null;
|
|
if (xtermActive) {
|
|
QS('termarea3x')['height'] = 'calc(100vh - 55px)';
|
|
QS('termarea3xdiv')['height'] = 'calc(100vh - 55px)';
|
|
QS('termarea3x')['max-width'] = 'calc(1px)';
|
|
}
|
|
} else {
|
|
QS('deskarea3x')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
|
|
QS('deskarea3x')['max-height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
|
|
QS('p14iframe')['height'] = 'calc(100vh - ' + (23 + xh + xh2) + 'px)';
|
|
QS('p14iframe')['max-height'] = 'calc(100vh - ' + (23 + xh + xh2) + 'px)';
|
|
if (xtermActive) {
|
|
QS('termarea3x')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
|
|
QS('termarea3xdiv')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
|
|
QS('termarea3x')['max-width'] = 'calc(1px)';
|
|
}
|
|
}
|
|
QS('p43iframe')['height'] = 'calc(100vh - ' + (84 + xh) + 'px)';
|
|
QS('p43iframe')['max-height'] = 'calc(100vh - ' + (84 + xh) + 'px)';
|
|
QS('p6info')['height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
|
|
QS('p6info')['max-height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
|
|
QS('p10info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p10info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p17info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p17info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p30info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p30info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p51info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p51info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
|
|
QS('p16events')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
|
|
QS('p16events')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
|
|
QS('p31events')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
|
|
QS('p31events')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
|
|
QS('p41events')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
|
|
QS('p41events')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
|
|
|
|
// We are looking at a single device, remove all the back buttons
|
|
if ('{{currentNode}}' != '') {
|
|
QV('p10BackButton', false);
|
|
QV('p11BackButton', false);
|
|
QV('p12BackButton', false);
|
|
QV('p13BackButton', false);
|
|
QV('p14BackButton', false);
|
|
QV('p15BackButton', false);
|
|
QV('p16BackButton', false);
|
|
}
|
|
p1updateInfo();
|
|
}
|
|
|
|
// Toggle the web page to full screen
|
|
function toggleAspectRatio(toggle) {
|
|
if (toggle === 1) { deskAspectRatio = ((deskAspectRatio + 1) % 3); putstore('deskAspectRatio', deskAspectRatio); }
|
|
deskAdjust();
|
|
}
|
|
|
|
// If FullScreen, toggle menu to be horizontal or vertical
|
|
function toggleStackMenu(toggle) {
|
|
if (webPageFullScreen == true) {
|
|
if (toggle === 1) {
|
|
webPageStackMenu = !webPageStackMenu;
|
|
putstore('webPageStackMenu', webPageStackMenu);
|
|
}
|
|
if (webPageStackMenu == false) {
|
|
QC('body').remove('menu_stack');
|
|
} else {
|
|
QC('body').add('menu_stack');
|
|
if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
|
|
}
|
|
deskAdjust();
|
|
}
|
|
}
|
|
|
|
// Toggle user interface menu
|
|
function showUserInterfaceSelectMenu() {
|
|
Q('uiViewButton1').classList.remove('uiSelectorSel');
|
|
Q('uiViewButton2').classList.remove('uiSelectorSel');
|
|
Q('uiViewButton3').classList.remove('uiSelectorSel');
|
|
Q('uiViewButton4').classList.remove('uiSelectorSel');
|
|
Q('uiViewButton5').classList.remove('uiSelectorSel');
|
|
try { Q('uiViewButton' + uiMode).classList.add('uiSelectorSel'); } catch (ex) { }
|
|
QV('uiMenu', (QS('uiMenu').display == 'none'));
|
|
//Q('uiViewButton1').focus();
|
|
if (nightMode) { Q('uiViewButton4').classList.add('uiSelectorSel'); }
|
|
if (footerBar) { Q('uiViewButton5').classList.add('uiSelectorSel'); }
|
|
}
|
|
|
|
function userInterfaceSelectMenu(s) {
|
|
if (s) { uiMode = s; putstore('uiMode', uiMode); }
|
|
webPageFullScreen = (uiMode < 3);
|
|
webPageStackMenu = (uiMode > 1);
|
|
toggleFullScreen(0);
|
|
toggleStackMenu(0);
|
|
if (webPageStackMenu && (xxcurrentView >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
|
|
adjustPanels();
|
|
}
|
|
|
|
function toggleNightMode() {
|
|
nightMode = !nightMode;
|
|
if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; } else { QC('body').remove('night'); QS('body')['background-color'] = '#d3d9d6'; }
|
|
putstore('nightMode', nightMode?'1':'0');
|
|
}
|
|
|
|
function toggleFooterBarMode() {
|
|
footerBar = !footerBar;
|
|
putstore('footerBar', footerBar?'1':'0');
|
|
QS('container')['grid-template-rows'] = null;
|
|
QS('container')['-ms-grid-rows'] = null;
|
|
adjustPanels();
|
|
}
|
|
|
|
// Toggle the web page to full screen
|
|
function toggleFullScreen(toggle) {
|
|
if (toggle === 1) { webPageFullScreen = !webPageFullScreen; putstore('webPageFullScreen', webPageFullScreen); }
|
|
if (webPageFullScreen == false) {
|
|
QC('body').remove('menu_stack');
|
|
QC('body').remove('fullscreen');
|
|
QC('body').remove('arg_hide');
|
|
if (xxcurrentView >= 10) QC('column_l').add('room4submenu');
|
|
QV('UserDummyMenuSpan', false);
|
|
//QV('page_leftbar', false);
|
|
} else {
|
|
QC('body').add('fullscreen');
|
|
if (args.hide & 16) QC('body').add('arg_hide'); // This is replacement for QV('page_leftbar', !(args.hide & 16));
|
|
QV('page_leftbar', !(args.hide & 16));
|
|
QV('MainMenuSpan', !(args.hide & 16));
|
|
if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
|
|
QV('UserDummyMenuSpan', (xxcurrentView < 10) && webPageFullScreen);
|
|
}
|
|
masterUpdate(512);
|
|
QV('body', true);
|
|
}
|
|
|
|
function getNodeFromId(id) { if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } } return null; }
|
|
function reload() {
|
|
var x = window.location.href;
|
|
if (x.endsWith('/#')) { x = x.substring(0, x.length - 2); }
|
|
window.location.href = x;
|
|
}
|
|
|
|
function onStateChanged(server, state, prevState, errorCode) {
|
|
if (state == 0) {
|
|
// Control web socket disconnected
|
|
setDialogMode(0); // Close any dialog boxes if present
|
|
go(0); // Go to disconnection panel
|
|
|
|
// Clean up
|
|
powerTimeline = null;
|
|
powerTimelineReq = null;
|
|
powerTimelineNode = null;
|
|
powerTimelineUpdate = null;
|
|
deleteAllNotifications(); // Close and clear notifications if present
|
|
hideContextMenu(); // Hide the context menu if present
|
|
QV('verifyEmailId2', false);
|
|
QV('logoutControl', false);
|
|
if (errorCode == 'noauth') { QH('p0span', "Unable to perform authentication"); return; }
|
|
if (prevState == 2) { if (autoReconnect) { setTimeout(serverPoll, 5000); } } else { QH('p0span', "Unable to connect web socket"); }
|
|
if (authCookieRenewTimer != null) { clearInterval(authCookieRenewTimer); authCookieRenewTimer = null; }
|
|
} else if (state == 2) {
|
|
// Fetch list of meshes, nodes, files
|
|
meshserver.send({ action: 'usergroups' });
|
|
meshserver.send({ action: 'meshes' });
|
|
meshserver.send({ action: 'nodes', id: '{{currentNode}}' });
|
|
if (pluginHandler != null) { meshserver.send({ action: 'plugins' }); }
|
|
if ('{{currentNode}}' == '') { meshserver.send({ action: 'files' }); }
|
|
if ('{{viewmode}}' == '') { go(1); }
|
|
authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
|
|
}
|
|
}
|
|
|
|
// Poll the server, if it responds, refresh the page.
|
|
function serverPoll() {
|
|
var xdr = null;
|
|
try { xdr = new XDomainRequest(); } catch (e) { }
|
|
if (!xdr) xdr = new XMLHttpRequest();
|
|
xdr.open('HEAD', window.location.href);
|
|
xdr.timeout = 15000;
|
|
xdr.onload = function () { reload(); };
|
|
xdr.onerror = xdr.ontimeout = function () { setTimeout(serverPoll, 10000); };
|
|
xdr.send();
|
|
}
|
|
|
|
// Return true if this browser supports clickonce
|
|
function detectClickOnce() {
|
|
for (var i in window.navigator.mimeTypes) { if (window.navigator.mimeTypes[i].type == 'application/x-ms-application') { return true; } }
|
|
var userAgent = window.navigator.userAgent.toUpperCase();
|
|
return (userAgent.indexOf('.NET CLR 3.5') >= 0) || (userAgent.indexOf('(WINDOWS NT ') >= 0);
|
|
}
|
|
|
|
function updateSiteAdmin() {
|
|
var noServerBackup = '{{{noServerBackup}}}';
|
|
var siteRights = userinfo.siteadmin;
|
|
if (noServerBackup == 1) { siteRights &= 0xFFFFFFFA; } // If not server backups allowed, remove server backup and restore permissions
|
|
|
|
// Update account actions
|
|
QV('p2AccountSecurity', ((features & 4) == 0) && (serverinfo.domainauth == false) && ((features & 4096) != 0)); // Hide Account Security if in single user mode, domain authentication to 2 factor auth not supported.
|
|
QV('managePhoneNumber1', (features & 0x02000000) && (features & 0x04000000));
|
|
QV('managePhoneNumber2', (features & 0x02000000) && !(features & 0x04000000));
|
|
QV('manageEmail2FA', features & 0x00800000);
|
|
QV('p2AccountPassActions', ((features & 4) == 0) && (serverinfo.domainauth == false)); // Hide Account Actions if in single user mode or domain authentication
|
|
//QV('p2AccountImage', ((features & 4) == 0) && (serverinfo.domainauth == false)); // If account actions are not visible, also remove the image on that panel
|
|
QV('p2ServerActions', siteRights & 21);
|
|
QV('LeftMenuMyServer', siteRights & 21); // 16 + 4 + 1
|
|
QV('MainMenuMyServer', siteRights & 21);
|
|
QV('p2ServerActionsBackup', siteRights & 1);
|
|
QV('p2ServerActionsRestore', siteRights & 4);
|
|
QV('p2ServerActionsVersion', siteRights & 16);
|
|
QV('MainMenuMyFiles', siteRights & 8);
|
|
QV('LeftMenuMyFiles', siteRights & 8);
|
|
if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
|
|
if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); }
|
|
|
|
// Update user management state
|
|
if ((userinfo.siteadmin & 2) != 0)
|
|
{
|
|
// We are user administrator
|
|
if (users == null) { meshserver.send({ action: 'users' }); }
|
|
if (wssessions == null) { meshserver.send({ action: 'wssessioncount' }); }
|
|
} else {
|
|
// We are not user administrator
|
|
users = null;
|
|
wssessions = null;
|
|
masterUpdate(16384);
|
|
if (xxcurrentView == 4 || ((xxcurrentView >= 30) && (xxcurrentView < 40))) { setDialogMode(0); go(1); currentUser = null; }
|
|
}
|
|
meshserver.send({ action: 'events', limit: parseInt(p3limitdropdown.value) });
|
|
QV('ServerConsole', userinfo.siteadmin === 0xFFFFFFFF);
|
|
QV('ServerTrace', userinfo.siteadmin === 0xFFFFFFFF);
|
|
if ((xxcurrentView == 115) && (userinfo.siteadmin != 0xFFFFFFFF)) { go(6); }
|
|
if ((xxcurrentView == 6) && ((userinfo.siteadmin & 21) == 0)) { go(1); }
|
|
|
|
// If we are site administrator, register to get server statistics
|
|
if ((siteRights & 21) != 0) { meshserver.send({ action: 'serverstats', interval: 10000 }); }
|
|
}
|
|
|
|
// To boost the speed of the web page when even floods occur, this method perform a delayed update on the web page.
|
|
var updateNaggleTimer = null;
|
|
var updateNaggleFlags = 0;
|
|
function masterUpdate(flags) {
|
|
updateNaggleFlags |= flags;
|
|
if (updateNaggleTimer == null) {
|
|
updateNaggleTimer = setTimeout(function () {
|
|
if (updateNaggleFlags & 512) { center(); }
|
|
if (updateNaggleFlags & 1) { onSearchInputChanged(); }
|
|
if (updateNaggleFlags & 2) { onSortSelectChange(false); }
|
|
if (updateNaggleFlags & 128) { updateMeshes(); }
|
|
if (updateNaggleFlags & 4) { updateDevices(); updateDeviceDetails(); if (xxcurrentView == 21) { p21updateMesh(); } }
|
|
if (updateNaggleFlags & 8) { drawNotifications(); }
|
|
{{{StartGeoLocationJS}}}if (updateNaggleFlags & 16) { updateMapMarkers(); }{{{EndGeoLocationJS}}}
|
|
if (updateNaggleFlags & 32) { eventsUpdate(); }
|
|
{{{StartGeoLocationJS}}}if (updateNaggleFlags & 64) { refreshMap(false, true); }{{{EndGeoLocationJS}}}
|
|
if (updateNaggleFlags & 256) { drawDeviceTimeline(); }
|
|
if (updateNaggleFlags & 1024) { deviceEventsUpdate(); }
|
|
if (updateNaggleFlags & 2048) { userEventsUpdate(); }
|
|
if (updateNaggleFlags & 4096) { p20updateMesh(); }
|
|
if (updateNaggleFlags & 8192) { updateUserGroups(); }
|
|
if (updateNaggleFlags & 16384) { updateUsers(); }
|
|
updateNaggleTimer = null;
|
|
updateNaggleFlags = 0;
|
|
gotoStartViewPage();
|
|
}, 150);
|
|
}
|
|
}
|
|
|
|
var backupCodesWarningDone = false;
|
|
function updateSelf() {
|
|
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
|
QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
|
QV('manageOtp', (userinfo.otpsecret == 1) || (userinfo.otphkeys > 0));
|
|
QV('authPhoneNumberCheck', (userinfo.phone != null));
|
|
QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
|
|
QV('authAppSetupCheck', userinfo.otpsecret == 1);
|
|
QV('authKeySetupCheck', userinfo.otphkeys > 0);
|
|
QV('authCodesSetupCheck', userinfo.otpkeys > 0);
|
|
masterUpdate(4 + 128 + 4096);
|
|
|
|
// Check if none or at least 2 factors are enabled.
|
|
var authFactorCount = 0;
|
|
if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount += 1; }
|
|
if ((features & 0x02000000) && (features & 0x04000000) && (userinfo.phone != null)) { authFactorCount += 1; }
|
|
if (userinfo.otpkeys == 1) { authFactorCount += 1; }
|
|
if (userinfo.otpsecret == 1) { authFactorCount += 1; }
|
|
if (userinfo.otphkeys != null) { authFactorCount += userinfo.otphkeys; }
|
|
if ((backupCodesWarningDone == false) && (authFactorCount == 1)) {
|
|
var n = { text: "Please add two-factor backup codes. If the current factor is lost, there is not way to recover this account.", title: "Two factor authentication" };
|
|
addNotification(n);
|
|
backupCodesWarningDone = true;
|
|
}
|
|
|
|
// If we can't create new groups, hide all links that can do that.
|
|
var newGroupsAllowed = ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0));
|
|
QV('p2createMeshLink1', newGroupsAllowed);
|
|
QV('p2createMeshLink2', newGroupsAllowed);
|
|
QV('getStarted1', newGroupsAllowed);
|
|
QV('getStarted2', !newGroupsAllowed);
|
|
|
|
if (typeof userinfo.passchange == 'number') {
|
|
if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); }
|
|
else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
|
|
var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000);
|
|
if (seconds < 0) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); }
|
|
else if (seconds < 3600) { var secs = Math.floor(seconds / 60); QH('p2nextPasswordUpdateTime',format((secs == 1)?" - Reset in 1 minute.":" - Reset in {0} minutes.", secs)); }
|
|
else if (seconds < 86400) { var hours = Math.floor(seconds / 3600); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 hour." : " - Reset in {0} hours.", hours)); }
|
|
else { var days = Math.floor(seconds / 86400); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 day." : " - Reset in {0} days.", days)); }
|
|
}
|
|
}
|
|
}
|
|
|
|
function setSessionActivity() { sessionActivity = Date.now(); QH('idleTimeoutNotify', ''); }
|
|
function checkIdleSessionTimeout() {
|
|
var delta = (Date.now() - sessionActivity);
|
|
if (delta > serverinfo.timeout) { window.location.href = 'logout'; } else {
|
|
var ds = Math.round((serverinfo.timeout - delta) / 1000);
|
|
if (ds <= 60) {
|
|
QH('idleTimeoutNotify', '<br />' + format((ds == 1)?"1 second until disconnect":"{0} seconds until disconnect", ds));
|
|
} else {
|
|
ds = Math.round(ds / 60);
|
|
if (ds <= 5) { QH('idleTimeoutNotify', '<br />' + format((ds == 1)?"1 minute until disconnect":"{0} minutes until disconnect", ds)); }
|
|
}
|
|
}
|
|
}
|
|
|
|
function onMessage(server, message) {
|
|
switch (message.action) {
|
|
case 'trace': {
|
|
serverTrace.unshift(message);
|
|
displayServerTrace();
|
|
break;
|
|
}
|
|
case 'intersession' : {
|
|
if (message.subaction == 'removeNotify') { notificationDelete(message.id); }
|
|
break;
|
|
}
|
|
case 'traceinfo': {
|
|
if (typeof message.traceSources == 'object') {
|
|
if ((message.traceSources != null) && (message.traceSources.length > 0)) {
|
|
serverTraceSources = message.traceSources;
|
|
QH('p41traceStatus', EscapeHtml(message.traceSources.join(', ')));
|
|
} else {
|
|
serverTraceSources = [];
|
|
QH('p41traceStatus', "None");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'serverstats': {
|
|
updateGeneralServerStats(message);
|
|
break;
|
|
}
|
|
case 'serverwarnings': {
|
|
if ((message.warnings != null) && (message.warnings.length > 0)) {
|
|
var x = '';
|
|
for (var i in message.warnings) { x += '<div style=color:red;padding-bottom:6px><b>' + "WARNING: " + message.warnings[i] + '</b></div>'; }
|
|
QH('serverWarnings', x);
|
|
QV('serverWarningsDiv', true);
|
|
}
|
|
break;
|
|
}
|
|
case 'servertimelinestats': {
|
|
setServerTimelineStats(message.events);
|
|
break;
|
|
}
|
|
case 'authcookie': {
|
|
// Got an authentication cookie refresh
|
|
authCookie = message.cookie;
|
|
authRelayCookie = message.rcookie;
|
|
break;
|
|
}
|
|
case 'serverinfo': {
|
|
serverinfo = message.serverinfo;
|
|
if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); }
|
|
if (debugmode == 1) { console.log('Server time: ', printDateTime(new Date(serverinfo.serverTime))); }
|
|
break;
|
|
}
|
|
case 'userinfo': {
|
|
userinfo = message.userinfo;
|
|
updateSiteAdmin();
|
|
updateSelf();
|
|
break;
|
|
}
|
|
case 'users': {
|
|
users = {};
|
|
for (var m in message.users) { users[message.users[m]._id] = message.users[m]; }
|
|
if (currentUser != null) { currentUser = users[currentUser._id]; }
|
|
masterUpdate(16384);
|
|
break;
|
|
}
|
|
case 'wssessioncount': {
|
|
wssessions = message.wssessions;
|
|
masterUpdate(16384);
|
|
break;
|
|
}
|
|
case 'meshes': {
|
|
meshes = {};
|
|
for (var m in message.meshes) { meshes[message.meshes[m]._id] = message.meshes[m]; }
|
|
if (currentMesh != null) { currentMesh = meshes[currentMesh._id]; }
|
|
masterUpdate(4 + 128);
|
|
break;
|
|
}
|
|
case 'usergroups': {
|
|
var groupCount = 0;
|
|
if (Array.isArray(message.ugroups)) {
|
|
usergroups = {};
|
|
for (var i in message.ugroups) { groupCount++; usergroups[message.ugroups[i]._id] = message.ugroups[i]; }
|
|
if (groupCount == 0) { usergroups = null; }
|
|
} else {
|
|
usergroups = message.ugroups;
|
|
for (var i in message.ugroups) { groupCount++; message.ugroups[i]._id = i; }
|
|
if (groupCount == 0) { usergroups = null; }
|
|
}
|
|
masterUpdate(8192);
|
|
break;
|
|
}
|
|
case 'files': {
|
|
filetree = setupBackPointers(message.filetree);
|
|
updateFiles();
|
|
d3updatefiles();
|
|
break;
|
|
}
|
|
case 'nodes': {
|
|
nodes = [];
|
|
for (var m in message.nodes) {
|
|
for (var n in message.nodes[m]) {
|
|
if (message.nodes[m][n]._id == null) { console.log('Invalid node (' + n + '): ' + JSON.stringify(message.nodes)); continue; }
|
|
message.nodes[m][n].namel = message.nodes[m][n].name.toLowerCase();
|
|
if (message.nodes[m][n].rname) { message.nodes[m][n].rnamel = message.nodes[m][n].rname.toLowerCase(); } else { message.nodes[m][n].rnamel = message.nodes[m][n].namel; }
|
|
message.nodes[m][n].meshnamel = meshes[m]?meshes[m].name.toLowerCase():'*';
|
|
message.nodes[m][n].meshid = m;
|
|
message.nodes[m][n].state = (message.nodes[m][n].state)?(message.nodes[m][n].state):0;
|
|
if (!message.nodes[m][n].icon) message.nodes[m][n].icon = 1;
|
|
message.nodes[m][n].ident = ++nodeShortIdent;
|
|
nodes.push(message.nodes[m][n]);
|
|
}
|
|
}
|
|
|
|
// If we are currently looking at a node this is now gone, change the view.
|
|
if ((currentNode != null) && (IsNodeViewable(currentNode) == false)) { currentNode = null; go(1); }
|
|
|
|
// Change the reference to the current node
|
|
if (currentNode != null) { currentNode = getNodeFromId(currentNode._id); if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); } else { go(1); } }
|
|
|
|
masterUpdate(1 | 2 | 4 | 64);
|
|
break;
|
|
}
|
|
case 'powertimeline': {
|
|
if (message.nodeid != powerTimelineReq) break;
|
|
powerTimelineNode = message.nodeid;
|
|
powerTimeline = message.timeline;
|
|
powerTimelineUpdate = Date.now() + 300000; // Update every 5 minutes
|
|
for (var i in powerTimeline) { if (i % 2 == 1) { powerTimeline[i] = powerTimeline[i] * 1000; } } // Decompress time
|
|
if (currentNode._id == message.nodeid) { masterUpdate(256); }
|
|
break;
|
|
}
|
|
case 'getsysinfo': {
|
|
if (message.nodeid != powerTimelineReq) break;
|
|
//console.log('getsysinfo', message); // ***********************
|
|
if (message.noinfo === true) {
|
|
updateDeviceDetails(getNodeFromId(message.nodeid));
|
|
} else {
|
|
updateDeviceDetails(getNodeFromId(message.nodeid), message.hardware);
|
|
}
|
|
break;
|
|
}
|
|
case 'lastconnect': {
|
|
var node = getNodeFromId(message.nodeid);
|
|
if (node != null) {
|
|
node.lastconnect = message.time;
|
|
node.lastaddr = message.addr;
|
|
if ((currentNode._id == node._id) && (Q('MainComputerState').innerHTML == '')) {
|
|
QH('MainComputerState', '<span>' + "Last seen:" + '<br />' + printDateTime(new Date(node.lastconnect)) + '</span>');
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'msg': {
|
|
// Check if this is a message from a node
|
|
if (message.nodeid != null) {
|
|
var index = -1;
|
|
if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } } }
|
|
if (index != -1) {
|
|
// Node was found, dispatch the message
|
|
if (message.type == 'console') { p15consoleReceive(nodes[index], message.value, message.source); } // This is a console message.
|
|
else if (message.type == 'notify') { // This is a notification message.
|
|
var n = getstore('notifications', 0);
|
|
if (((n & 8) == 0) && (message.amtMessage != null)) { break; } // Intel AMT desktop & terminal messages should be ignored.
|
|
var n = { text: message.value, title: message.title, icon: message.icon };
|
|
if (message.id != null) { n.id = message.id; }
|
|
if (message.nodeid != null) { n.nodeid = message.nodeid; }
|
|
if (message.tag != null) { n.tag = message.tag; }
|
|
if (message.username != null) { n.username = message.username; }
|
|
addNotification(n);
|
|
} else if (message.type == 'ps') {
|
|
showDeskToolsProcesses(message);
|
|
} else if (message.type == 'services') {
|
|
showDeskToolsServices(message);
|
|
} else if ((message.type == 'getclip') && (xxdialogTag == 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
|
|
Q('d2clipText').value = message.data;
|
|
} else if ((message.type == 'setclip') && (xxdialogTag == 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
|
|
// Display success/fail on the clipboard dialog box.
|
|
QH('dlgClipStatus', message.success ? '<span style=color:green>' + "Success" + '</span>' : '<span style=color:red>' + "Failed" + '</span>')
|
|
setTimeout(function () { try { QH('dlgClipStatus', ''); } catch (ex) { } }, 2000);
|
|
} else if ((message.type == 'userSessions') && (currentNode != null) && (currentNode._id == message.nodeid) && (desktop == null)) {
|
|
// Got list of user sessions
|
|
var userSessions = [];
|
|
if (message.data != null) { for (var i in message.data) { if ((message.data[i].State == 'Active') || (message.data[i].StationName == 'Console') || (debugmode == 3)) { userSessions.push(message.data[i]); } } }
|
|
if (userSessions.length == 0) { connectDesktop(null, 1, null, message.tag); } // No active sessions, do a normal connection.
|
|
else if (userSessions.length == 1) { connectDesktop(null, 1, userSessions[0].SessionId, message.tag); } // One active session, connect to it
|
|
else {
|
|
var x = '';
|
|
for (var i in userSessions) {
|
|
x += '<div style="text-align:left;cursor:pointer;background-color:gray;margin:5px;padding:5px;border-radius:5px" onclick=connectDesktop(event,1,' + userSessions[i].SessionId + ',' + message.tag + ')>' + userSessions[i].State + ', ' + userSessions[i].StationName;
|
|
if (userSessions[i].Username) { if (userSessions[i].Domain) { x += ' - ' + userSessions[i].Domain + '/' + userSessions[i].Username; } else { x += ' - ' + userSessions[i].Username; } }
|
|
x += '</div>';
|
|
}
|
|
QH('p11DeskSessionSelector', x);
|
|
QV('p11DeskSessionSelector', true);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (message.type == 'notify') { // This is a notification message.
|
|
var n = { text: message.value, title: message.title, icon: message.icon };
|
|
if (message.id != null) { n.id = message.id; }
|
|
if (message.tag != null) { n.tag = message.tag; }
|
|
if (message.username != null) { n.username = message.username; }
|
|
addNotification(n);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'getnetworkinfo': {
|
|
if (currentNode._id != message.nodeid) return;
|
|
updateDeviceDetails(getNodeFromId(message.nodeid), null, message);
|
|
if ((xxdialogMode == 2) && (xxdialogTag == 'if' + message.nodeid)) {
|
|
if (message.netif == null) {
|
|
QH('d2netinfo', "No network interface information available for this device.");
|
|
} else {
|
|
var x = '<div class=dialogText>';
|
|
|
|
if (currentNode.lastconnect) { x += addHtmlValue2("Last agent connection", printDateTime(new Date(currentNode.lastconnect))); }
|
|
if (currentNode.lastaddr) {
|
|
var splitip = currentNode.lastaddr.split(':');
|
|
if (splitip.length > 2) {
|
|
// IPv6
|
|
x += addHtmlValue2("Last agent address", currentNode.lastaddr + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(currentNode.lastaddr) + '\") width=10 height=10>');
|
|
} else {
|
|
// IPv4
|
|
if (isPrivateIP(currentNode.lastaddr)) {
|
|
x += addHtmlValue2("Last agent address", splitip[0] + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(splitip[0]) + '\") width=10 height=10>');
|
|
} else {
|
|
x += addHtmlValue2("Last agent address", '<a href="https://iplocation.com/?ip=' + splitip[0] + '" rel="noreferrer noopener" target="MeshIPLoopup">' + splitip[0] + '</a> <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(splitip[0]) + '\") width=10 height=10>');
|
|
}
|
|
}
|
|
}
|
|
|
|
x += addHtmlValue2("Last interfaces update", printDateTime(new Date(message.updateTime)));
|
|
for (var i in message.netif) {
|
|
var net = message.netif[i];
|
|
x += '<hr />'
|
|
if (net.name) { x += addHtmlValue2("Name", '<b>' + EscapeHtml(net.name) + '</b>'); }
|
|
if (net.desc) { x += addHtmlValue2("Description", EscapeHtml(net.desc).replace('(R)', '®').replace('(r)', '®')); }
|
|
if (net.dnssuffix) { x += addHtmlValue2("DNS suffix", EscapeHtml(net.dnssuffix) + ' <img src="images/link4.png" title="' + "Copy name to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(net.dnssuffix) + '\") width=10 height=10>'); }
|
|
if (net.mac) { x += addHtmlValue2("MAC address", '<a href="https://dnslytics.com/mac-address-lookup/' + net.mac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.mac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(net.mac.toLowerCase()) + '\") width=10 height=10>'); }
|
|
if (net.v4addr) { x += addHtmlValue2("IPv4 address", EscapeHtml(net.v4addr) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(net.v4addr) + '\") width=10 height=10>'); }
|
|
if (net.v4mask) { x += addHtmlValue2("IPv4 mask", EscapeHtml(net.v4mask) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(net.v4mask) + '\") width=10 height=10>'); }
|
|
if (net.v4gateway) { x += addHtmlValue2("IPv4 gateway", EscapeHtml(net.v4gateway) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(net.v4gateway) + '\") width=10 height=10>'); }
|
|
if (net.gatewaymac) { x += addHtmlValue2("Gateway MAC", '<a href="https://dnslytics.com/mac-address-lookup/' + net.gatewaymac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.gatewaymac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(net.gatewaymac.toLowerCase()) + '\") width=10 height=10>'); }
|
|
}
|
|
x += '</div>';
|
|
QH('d2netinfo', x);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'serverversion': {
|
|
if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerUpdate')) {
|
|
var x = '<div class=dialogText>';
|
|
if (!message.tags.current) { message.tags.current = "Unknown"; }
|
|
if (!message.tags.stable) { message.tags.stable = "Unknown"; }
|
|
if (!message.tags.latest) { message.tags.latest = "Unknown"; }
|
|
x += addHtmlValue2("Current Version", '<b>' + EscapeHtml(message.tags.current) + '</b>');
|
|
x += '<hr />';
|
|
x += addHtmlValue2('<label><input id=d2updateCheck1 type=checkbox onclick=server_showVersionDlgUpdate()' + (((message.tags.stable == "Unknown") || (message.tags.current == message.tags.stable))?' disabled':'') + ' /> ' + "Stable Version" + '</label>', '<b>' + EscapeHtml(message.tags.stable) + '</b>');
|
|
x += addHtmlValue2('<label><input id=d2updateCheck2 type=checkbox onclick=server_showVersionDlgUpdate()' + (((message.tags.latest == "Unknown") || (message.tags.current == message.tags.latest))?' disabled':'') + ' /> ' + "Latest Version" + '</label>', '<b>' + EscapeHtml(message.tags.latest) + '</b>');
|
|
x += '</div>';
|
|
if (((message.tags.current == message.tags.latest) && (message.tags.current == message.tags.stable)) || ((features & 2048) == 0)) {
|
|
setDialogMode(2, "MeshCentral Version", 1, null, x);
|
|
} else {
|
|
x += '<hr />' + "Check and click OK to start server self-update.";
|
|
setDialogMode(2, "MeshCentral Version", 3, server_showVersionDlgEx, x, message.tags);
|
|
server_showVersionDlgUpdate();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'servererrors': {
|
|
if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerErrors')) {
|
|
if (message.data == null) {
|
|
setDialogMode(2, "MeshCentral Server Errors", 1, null, "Server has no error log.");
|
|
} else {
|
|
var x = '<div class="dialogText dialogTextLog"><pre id=d2ServerErrorsLogPre>' + EscapeHtml(message.data) + '</pre></div>';
|
|
x += '<br /><div style=float:right><img src=images/link4.png height=10 width=10 title="' + "Download error log" + '" style=cursor:pointer onclick=d2CopyServerErrorsToClip()></div>';
|
|
x += '<div><label><input id=d2clearErrorsCheck type=checkbox onclick=server_showErrorsDlgUpdate() /> ' + "Check and click OK to clear error log." + '</label></div>';
|
|
setDialogMode(2, "MeshCentral Server Errors", 3, server_showErrorsDlgEx, x);
|
|
server_showErrorsDlgUpdate();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'serverconsole': {
|
|
p15consoleReceive('serverconsole', message.value);
|
|
break;
|
|
}
|
|
case 'events': {
|
|
if ((message.nodeid != null) && (currentNode != null) && (message.nodeid == currentNode._id)) {
|
|
currentDeviceEvents = message.events;
|
|
masterUpdate(1024);
|
|
} else if ((message.user != null) && (message.user == currentUser.name)) {
|
|
currentUserEvents = message.events;
|
|
masterUpdate(2048);
|
|
} else {
|
|
events = message.events;
|
|
masterUpdate(32);
|
|
}
|
|
break;
|
|
}
|
|
case 'getcookie': {
|
|
if (message.tag == 'clickonce') {
|
|
if (message.trustedCert == true) {
|
|
// Trusted certificate, use HTTPS port.
|
|
var rdpurl = window.location.origin + domainUrl + 'clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F' + window.location.hostname + '%2Fmeshrelay.ashx%3Fauth=' + message.cookie + '&CH={{{webcerthash}}}&AP=' + message.protocol + ((debugmode == 1) ? '' : '&HOL=1');
|
|
var newWindow = window.open(rdpurl, '_blank');
|
|
newWindow.opener = null;
|
|
} else {
|
|
// Not a trusted certificate, use HTTP port.
|
|
var basicPort = '{{{serverRedirPort}}}' == '' ? '{{{serverPublicPort}}}' : '{{{serverRedirPort}}}';
|
|
var rdpurl = 'http://' + window.location.hostname + ':' + basicPort + domainUrl + 'clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F' + window.location.hostname + '%2Fmeshrelay.ashx%3Fauth=' + message.cookie + '&CH={{{webcerthash}}}&AP=' + message.protocol + ((debugmode == 1) ? '' : '&HOL=1');
|
|
var newWindow = window.open(rdpurl, '_blank');
|
|
newWindow.opener = null;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'getNotes': {
|
|
var n = Q('d2devNotes');
|
|
if (n && (message.id == decodeURIComponent(n.attributes['noteid'].value))) {
|
|
if (message.notes) { QH('d2devNotes', decodeURIComponent(message.notes)); } else { QH('d2devNotes', ''); }
|
|
var ro = (n.attributes['ro'].value == 'true');
|
|
if (ro == false) { // If we have permissions, set read/write on this note.
|
|
n.removeAttribute('readonly');
|
|
QE('idx_dlgOkButton', true);
|
|
QV('idx_dlgOkButton', true);
|
|
focusTextBox('d2devNotes');
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'otpauth-request': {
|
|
if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-request')) {
|
|
var secret = message.secret;
|
|
if (secret.length == 52) { secret = secret.split(/(.............)/).filter(Boolean).join(' '); }
|
|
else if (secret.length == 32) { secret = secret.split(/(....)/).filter(Boolean).join(' '); secret = secret.substring(0, 20) + '<br/>' + secret.substring(20) }
|
|
QH('d2optinfo', '<table style=width:380px><tr><td style=vertical-align:top>' + format("Install <a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2\" rel=\"noreferrer noopener\" target=_blank>Google Authenticator</a> or a compatible application and scan the barcode, use <a href=\"{0}\" rel=\"noreferrer noopener\" target=_blank>this link</a> or enter the secret. Then, enter the current 6 digit token below to activate 2-Step login.", message.url) + '<br /><br />Secret<br /><tt id=d2optsecret secret=\"' + message.secret + '\" style=font-size:12px>' + secret + '</tt><br /><br /></td><td style=width:1px;vertical-align:top><a href=\"' + message.url + '\" rel=\"noreferrer noopener\" target=_blank><div id="qrcode"></div></a></td><tr><td colspan=2 style="text-align:center;border-top:1px solid black"><br />' + "Enter the token here for 2-step login:" + ' <input type=text autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" onkeypress=\"return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)\" onkeyup=account_addOtpCheck(event) onkeydown=account_addOtpCheck() maxlength=6 id=d2otpauthinput type=text></td></table>');
|
|
new QRCode(Q('qrcode'), { text: message.url, width: 128, height: 128, colorDark: '#000000', colorLight: '#EEE', correctLevel: QRCode.CorrectLevel.H });
|
|
QV('idx_dlgOkButton', true);
|
|
QE('idx_dlgOkButton', false);
|
|
Q('d2otpauthinput').focus();
|
|
}
|
|
break;
|
|
}
|
|
case 'otpauth-setup': {
|
|
if (xxdialogMode) return;
|
|
setDialogMode(2, "Authenticator App", 1, null, message.success ? ('<b style=color:green>' + "Authenticator app activation successful." + '</b> ' + "You will now need a valid token to login again.") : ('<b style=color:red>' + "2-step login activation failed." + '</b> ' + "Clear the secret from the application and try again. You only have a few minutes to enter the proper code."));
|
|
break;
|
|
}
|
|
case 'otpauth-clear': {
|
|
if (xxdialogMode) return;
|
|
setDialogMode(2, "Authenticator App", 1, null, message.success ? ('<b>' + "Authenticator application removed." + '</b> ' + "You can reactivate this feature at any time.") : ('<b style=color:red>' + "2-step login activation removal failed." + '</b> ' + "Try again."));
|
|
break;
|
|
}
|
|
case 'otpauth-getpasswords': {
|
|
if (xxdialogMode) return;
|
|
var x = "One time tokens can be used as secondary authentication. Generate a set, print them and keep them in a safe place.";
|
|
x += '<div style="border-radius:6px;border: 2px dashed #888;width:100%;margin-top:8px"><div style="padding:8px;font-family:Arial, Helvetica, sans-serif;font-size:20px;font-weight:bold"><table class=selecttext style=width:100%;text-align:center>';
|
|
if (message.passwords) {
|
|
var j = 0, clipb = '';
|
|
for (var i in message.passwords) {
|
|
if (++j % 2) { x += '<tr>'; }
|
|
var p = '' + message.passwords[i].p;
|
|
while (p.length < 8) { p = '0' + p; }
|
|
if (message.passwords[i].u === true) {
|
|
x += '<td>' + p.substring(0, 4) + ' ' + p.substring(4);
|
|
if (clipb != '') { clipb += ' '; }
|
|
clipb += p;
|
|
} else {
|
|
x += '<td><strike style=color:#BBB>' + p.substring(0, 4) + ' ' + p.substring(4); + '</strike>';
|
|
}
|
|
}
|
|
} else {
|
|
x += '<tr><td>' + "No Active Tokens";
|
|
}
|
|
x += '</table></div></div><br />';
|
|
x += '<div><input type=button value=' + "Close" + ' onclick=setDialogMode(0) style=float:right></input>';
|
|
x += '<input type=button value="' + "Generate New Tokens" + '" onclick="account_manageOtp(1);"></input>';
|
|
if (message.passwords != null) {
|
|
x += '<input type=button value="' + "Clear Tokens" + '" onclick="account_manageOtp(2);"></input>';
|
|
x += ' <img src=images/link4.png height=10 width=10 title="' + "Copy valid codes to clipboard" + '" style=cursor:pointer onclick=copyTextToClip2("' + encodeURIComponent(clipb) + '")>';
|
|
}
|
|
x += '</div><br />';
|
|
setDialogMode(2, "Manage Backup Codes", 8, null, x, 'otpauth-manage');
|
|
break;
|
|
}
|
|
case 'otp-hkey-get': {
|
|
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
|
var start = '<div style="border-radius:6px;border:2px solid #CCC;background-color:#BBB;width:100%;box-sizing:border-box;margin-bottom:6px"><div style="margin:3px;font-family:Arial, Helvetica, sans-serif;font-size:16px;font-weight:bold"><table style=width:100%;text-align:left>';
|
|
var end = '</table></div></div>';
|
|
var x = "<a href=\"https://www.yubico.com/\" rel=\"noreferrer noopener\" target=\"_blank\">Hardware keys</a> are used as secondary login authentication.";
|
|
x += '<div style="max-height:150px;overflow-y:auto;overflow-x:hidden;margin-top:6px;margin-bottom:6px">';
|
|
if (message.keys && message.keys.length > 0) {
|
|
for (var i in message.keys) {
|
|
var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn';
|
|
x += start + '<tr style=margin:5px><td style=width:30px><img width=24 height=18 src="images/hardware-key-' + type + '-24.png" style=margin-top:4px><td style=width:250px>' + key.name + '<td><input type=button value="' + "Remove" + '" onclick=account_removehkey(' + key.i + ')></input>' + end;
|
|
}
|
|
} else {
|
|
x += start + '<tr style=text-align:center><td>' + "No Keys Configured" + end;
|
|
}
|
|
x += '</div>';
|
|
x += '<div><input type=button value="' + "Close" + '" onclick=setDialogMode(0) style=float:right></input>';
|
|
if ((features & 0x00020000) != 0) { x += '<input id=d2addkey3 type=button value="' + "Add Key" + '" onclick="account_addhkey(3);"></input>'; }
|
|
if ((features & 0x00004000) != 0) { x += '<input id=d2addkey2 type=button value="' + "Add YubiKey® OTP" + '" onclick="account_addhkey(2);"></input>'; }
|
|
x += '</div><br />';
|
|
setDialogMode(2, "Manage Security Keys", 8, null, x, 'otpauth-hardware-manage');
|
|
if (u2fSupported() == false) { QE('d2addkey1', false); }
|
|
break;
|
|
}
|
|
case 'otp-hkey-yubikey-add': {
|
|
if (message.result) {
|
|
meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
|
|
} else {
|
|
setDialogMode(2, "Add Security Key", 1, null, '<br />' + "Error, Unable to add key." + '<br /><br />');
|
|
}
|
|
break;
|
|
}
|
|
case 'otp-hkey-setup-response': {
|
|
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
|
if (message.result == true) {
|
|
meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
|
|
} else {
|
|
setDialogMode(2, "Add Security Key", 1, null, '<br />' + "ERROR: Unable to add key." + '<br /><br />', 'otpauth-hardware-manage');
|
|
}
|
|
break;
|
|
}
|
|
case 'webauthn-startregister': {
|
|
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
|
var x = "Press the key button now." + '<br /><br /><div style=width:100%;text-align:center><img width=120 height=117 src="images/hardware-keypress-120.png" /></div><input id=dp1keyname style=display:none value=' + message.name + ' />';
|
|
setDialogMode(2, "Add Security Key", 2, null, x);
|
|
|
|
var publicKey = message.request;
|
|
message.request.challenge = Uint8Array.from(atob(message.request.challenge), function (c) { return c.charCodeAt(0) })
|
|
message.request.user.id = Uint8Array.from(atob(message.request.user.id), function (c) { return c.charCodeAt(0) })
|
|
setTimeout(function() {
|
|
navigator.credentials.create({ publicKey: publicKey })
|
|
.then(function(newCredentialInfo) {
|
|
// Public key credential
|
|
var r = { rawId: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.rawId))), response: { attestationObject: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.attestationObject))), clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.clientDataJSON))) }, type: newCredentialInfo.type };
|
|
meshserver.send({ action: 'webauthn-endregister', response: r });
|
|
setDialogMode(0);
|
|
}, function(error) {
|
|
// Error
|
|
setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error);
|
|
});
|
|
}, 100);
|
|
break;
|
|
}
|
|
case 'verifyPhone': {
|
|
if (xxdialogMode && (xxdialogTag != 'verifyPhone')) return;
|
|
var x = '<table><tr><td><img src="images/phone80.png" style=padding:8px>';
|
|
x += '<td>Check your phone and enter the verification code.';
|
|
x += '<br /><br /><div style=width:100%;text-align:center>' + "Verification code:" + ' <input type=tel pattern="[0-9]" inputmode="number" maxlength=6 id=d2phoneCodeInput onKeyUp=account_managePhoneCodeValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneCodeValidate(1)"></div></table>';
|
|
setDialogMode(2, "Phone Notifications", 3, account_managePhoneConfirm, x, message.cookie);
|
|
Q('d2phoneCodeInput').focus();
|
|
account_managePhoneCodeValidate();
|
|
break;
|
|
}
|
|
case 'event': {
|
|
if (!message.event.nolog) {
|
|
if (currentNode && (message.event.nodeid == currentNode._id)) {
|
|
// If this event has a nodeid and we are looking at this node, update the log in real time.
|
|
currentDeviceEvents.unshift(message.event);
|
|
var eventLimit = parseInt(p16limitdropdown.value);
|
|
while (currentDeviceEvents.length > eventLimit) { currentDeviceEvents.pop(); } // Remove element(s) at the end
|
|
masterUpdate(1024);
|
|
}
|
|
|
|
if (currentUser && (message.event.userid == currentUser._id)) {
|
|
// If this event has a userid and we are looking at this user, update the log in real time.
|
|
currentUserEvents.unshift(message.event);
|
|
var eventLimit = parseInt(p31limitdropdown.value);
|
|
while (currentUserEvents.length > eventLimit) { currentUserEvents.pop(); } // Remove element(s) at the end
|
|
masterUpdate(2048);
|
|
}
|
|
|
|
// Add this event to the master events log.
|
|
events.unshift(message.event);
|
|
var eventLimit = parseInt(p3limitdropdown.value);
|
|
while (events.length > eventLimit) { events.pop(); } // Remove element(s) at the end
|
|
masterUpdate(32);
|
|
}
|
|
if (message.event.noact) break; // Take no action on this event
|
|
switch (message.event.action) {
|
|
case 'userWebState': {
|
|
// New user web state, update the web page as needed
|
|
try {
|
|
if (localStorage != null) {
|
|
var oldShowRealNames = localStorage.getItem('showRealNames');
|
|
var oldUiMode = localStorage.getItem('uiMode');
|
|
var oldSort = localStorage.getItem('sort');
|
|
var oldLoctag = localStorage.getItem('loctag');
|
|
var oldNightMode = localStorage.getItem('nightMode');
|
|
var oldFooterBar = localStorage.getItem('footerBar');
|
|
|
|
var webstate = JSON.parse(message.event.state);
|
|
for (var i in webstate) { localStorage.setItem(i, webstate[i]); }
|
|
|
|
// Update the web page
|
|
if ((webstate.deskAspectRatio != null) && (webstate.deskAspectRatio != deskAspectRatio)) { deskAspectRatio = webstate.deskAspectRatio; deskAdjust(); }
|
|
if ((webstate.showRealNames != null) && (webstate.showRealNames != oldShowRealNames)) { showRealNames = Q('RealNameCheckBox').checked = (webstate.showRealNames == '1'); masterUpdate(6); }
|
|
if ((webstate.uiMode != null) && (webstate.uiMode != oldUiMode)) { userInterfaceSelectMenu(parseInt(webstate.uiMode)); }
|
|
if ((webstate.sort != null) && (webstate.sort != oldSort)) { document.getElementById('sortselect').selectedIndex = sort = parseInt(webstate.sort); masterUpdate(6); }
|
|
if ((webstate.loctag != null) && (webstate.loctag != oldLoctag)) { if (webstate.loctag != null) { args.locale = webstate.loctag; } else { delete args.locale; } masterUpdate(0xFFFFFFFF); }
|
|
if ((webstate.nightMode != null) && (webstate.nightMode != oldNightMode)) { nightMode = (webstate.nightMode == '1'); if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; } else { QC('body').remove('night'); QS('body')['background-color'] = '#d3d9d6'; } }
|
|
if ((webstate.footerBar != null) && (webstate.footerBar != oldFooterBar)) { footerBar = (webstate.footerBar == '1'); QS('container')['grid-template-rows'] = null; QS('container')['-ms-grid-rows'] = null; adjustPanels(); }
|
|
}
|
|
} catch (ex) {}
|
|
break;
|
|
}
|
|
case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; }
|
|
case 'accountcreate':
|
|
case 'accountchange': {
|
|
// An account was created or changed
|
|
if (userinfo.name == message.event.account.name) {
|
|
var newsiteadmin = message.event.account.siteadmin?message.event.account.siteadmin:0;
|
|
var oldsiteadmin = userinfo.siteadmin?userinfo.siteadmin:0;
|
|
if ((message.event.account.quota != userinfo.quota) || (((userinfo.siteadmin & 8) == 0) && ((message.event.account.siteadmin & 8) != 0))) { meshserver.send({ action: 'files' }); }
|
|
var oldgroups = userinfo.groups;
|
|
userinfo = message.event.account;
|
|
if (oldsiteadmin != newsiteadmin) updateSiteAdmin();
|
|
updateSelf();
|
|
|
|
if ((userinfo.siteadmin & 2) != 0) {
|
|
// Compare our groups
|
|
var og = oldgroups ? oldgroups : [];
|
|
var ng = userinfo.groups ? userinfo.groups : [];
|
|
if (og.join(',') != ng.join(',')) {
|
|
// Our groups have changed, re-ask for a list of users.
|
|
users = wssessions = null;
|
|
meshserver.send({ action: 'users' });
|
|
meshserver.send({ action: 'wssessioncount' });
|
|
}
|
|
}
|
|
|
|
// If our list of nodes may have changes, request the new list now.
|
|
if (message.event.nodeListChange == userinfo._id) { meshserver.send({ action: 'nodes' }); }
|
|
}
|
|
if (users == null) break;
|
|
|
|
// Check if the account is part of our user group
|
|
if ((userinfo.groups == null) || (userinfo.groups.length == 0) || (findOne(message.event.account.groups, userinfo.groups) == true)) {
|
|
users[message.event.account._id] = message.event.account; // Part of our groups, update this user.
|
|
} else {
|
|
delete users[message.event.account._id]; // No longer part of our groups, remove this user.
|
|
}
|
|
|
|
masterUpdate(4 | 16384);
|
|
break;
|
|
}
|
|
case 'accountremove': {
|
|
// An account was removed
|
|
if (users == null) break;
|
|
delete users['user/' + domain + '/' + message.event.username.toLowerCase()];
|
|
masterUpdate(16384);
|
|
break;
|
|
}
|
|
case 'createusergroup':
|
|
case 'usergroupchange': {
|
|
// User group changed
|
|
if (usergroups == null) { usergroups = {}; }
|
|
var ugroup = usergroups[message.event.ugrpid];
|
|
if (ugroup == null) {
|
|
// This is a new user group for us
|
|
usergroups[message.event.ugrpid] = { _id: message.event.ugrpid, name: message.event.name, desc: message.event.desc, domain: message.event.domain, links: message.event.links };
|
|
} else {
|
|
// This is an existing user group
|
|
ugroup.name = message.event.name;
|
|
if (message.event.desc) { ugroup.desc = message.event.desc; } else { delete ugroup.desc; }
|
|
if (message.event.links) { ugroup.links = message.event.links; } else { delete ugroup.links; }
|
|
}
|
|
masterUpdate(4096 + 8192 + 16384);
|
|
|
|
// Group update, refresh all our device groups and nodes. TODO: Optimize this to only do this when needed.
|
|
meshserver.send({ action: 'meshes' });
|
|
meshserver.send({ action: 'nodes' });
|
|
break;
|
|
}
|
|
case 'deleteusergroup': {
|
|
// User group removed
|
|
if ((usergroups != null) && (usergroups[message.event.ugrpid] != null)) {
|
|
delete usergroups[message.event.ugrpid];
|
|
var c = 0;
|
|
for (var i in usergroups) { c++; }
|
|
if (c == 0) { usergroups = null; } // If user groups is empty, set it to null.
|
|
masterUpdate(8192 + 16384);
|
|
}
|
|
break;
|
|
}
|
|
case 'createmesh': {
|
|
// A new mesh was created
|
|
if ((meshes[message.event.meshid] == null) && ((userinfo.manageAllDeviceGroups) || (message.event.links[userinfo._id] != null))) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
|
|
meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links };
|
|
masterUpdate(4 + 128 + 8192 + 16384);
|
|
meshserver.send({ action: 'files' });
|
|
}
|
|
break;
|
|
}
|
|
case 'meshchange': {
|
|
// Update mesh information
|
|
if (meshes[message.event.meshid] == null) {
|
|
// Check if we have any access to this device group
|
|
var add = false;
|
|
if (message.event.links[userinfo._id] != null) { add = true; }
|
|
if (userinfo.links[message.event.meshid] != null) { add = true; }
|
|
for (var i in userinfo.links) { if ((i.startsWith('ugrp/')) && (message.event.links[i] != null)) { add = true; } }
|
|
|
|
// This is a new mesh for us
|
|
if (add) {
|
|
meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links };
|
|
meshserver.send({ action: 'nodes' }); // Request a refresh of all nodes (TODO: We could optimize this to only request nodes for the new mesh).
|
|
}
|
|
} else {
|
|
// This is an existing mesh
|
|
if (message.event.name != null) {
|
|
meshes[message.event.meshid].name = message.event.name;
|
|
for (var i in nodes) { if (nodes[i].meshid == message.event.meshid) { nodes[i].meshnamel = message.event.name.toLowerCase(); } }
|
|
}
|
|
if (message.event.desc != null) { meshes[message.event.meshid].desc = message.event.desc; }
|
|
if (message.event.flags != null) { meshes[message.event.meshid].flags = message.event.flags; }
|
|
if (message.event.consent != null) { meshes[message.event.meshid].consent = message.event.consent; }
|
|
if (message.event.links) { meshes[message.event.meshid].links = message.event.links; }
|
|
if (message.event.amt) { meshes[message.event.meshid].amt = message.event.amt; }
|
|
if (message.event.invite != null) { meshes[message.event.meshid].invite = message.event.invite; } else { delete meshes[message.event.meshid].invite; }
|
|
|
|
// Check if we lost rights to this mesh in this change.
|
|
if (IsMeshViewable(message.event.meshid) == false) {
|
|
if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
|
|
delete meshes[message.event.meshid];
|
|
|
|
// Delete all nodes in that mesh, except ones with direct links
|
|
var newnodes = [];
|
|
for (var i in nodes) { if ((nodes[i].meshid != message.event.meshid) || ((userinfo.links != null) && (userinfo.links[nodes[i]._id] != null))) { newnodes.push(nodes[i]); } }
|
|
nodes = newnodes;
|
|
|
|
// If we are looking at a node that is no longer visible, move back to "My Devices"
|
|
if ((xxcurrentView >= 10) && (xxcurrentView < 20) && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(1); }
|
|
}
|
|
}
|
|
masterUpdate(4 + 128 + 8192 + 16384);
|
|
if (currentNode && !IsNodeViewable(currentNode)) { currentNode = null; if ((xxcurrentView >= 10) && (xxcurrentView < 20)) { go(1); } }
|
|
//meshserver.send({ action: 'files' }); // TODO: Why do we need to do this??
|
|
|
|
// If we are looking at a mesh that is now deleted, move back to "My Account"
|
|
if (xxcurrentView == 20 && currentMesh._id == message.event.meshid) { masterUpdate(4096); }
|
|
break;
|
|
}
|
|
case 'deletemesh': {
|
|
// Delete the mesh
|
|
if (meshes[message.event.meshid]) {
|
|
delete meshes[message.event.meshid];
|
|
masterUpdate(128);
|
|
meshserver.send({ action: 'files' });
|
|
}
|
|
|
|
// Delete all nodes in that mesh
|
|
var newnodes = [];
|
|
if (nodes != null) { for (var i in nodes) { if (nodes[i].meshid != message.event.meshid) { newnodes.push(nodes[i]); } } }
|
|
nodes = newnodes;
|
|
masterUpdate(4 + 8192 + 16384);
|
|
|
|
// If we are looking at a mesh that is now deleted, move back to "My Account"
|
|
if (xxcurrentView >= 20 && xxcurrentView < 30 && currentMesh._id == message.event.meshid) { setDialogMode(0); go(2); }
|
|
// If we are looking at a node in the deleted mesh, move back to "My Devices"
|
|
if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(1); }
|
|
break;
|
|
}
|
|
case 'addnode': {
|
|
var node = message.event.node;
|
|
if (!meshes[node.meshid]) break; // This is a node for a mesh we don't know. Happens when we are site administrator, we get all messages.
|
|
if (getNodeFromId(node._id) != null) break; // This node is already known.
|
|
node.namel = node.name.toLowerCase();
|
|
if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
|
|
node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*';
|
|
node.state = 0;
|
|
if (!node.icon) node.icon = 1;
|
|
node.ident = ++nodeShortIdent;
|
|
if (nodes == null) { }
|
|
nodes.push(node);
|
|
|
|
// Web page update
|
|
masterUpdate(1 | 2 | 4 | 16);
|
|
|
|
break;
|
|
}
|
|
case 'removenode': {
|
|
var index = -1;
|
|
for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
|
|
if (index != -1) {
|
|
var node = nodes[index];
|
|
if (currentNode == node) {
|
|
if (xxcurrentView >= 10 && xxcurrentView < 20) { setDialogMode(0); go(1); }
|
|
currentNode = null;
|
|
// TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
|
|
}
|
|
nodes.splice(index, 1);
|
|
|
|
// Web page update
|
|
masterUpdate(4 | 16);
|
|
}
|
|
break;
|
|
}
|
|
case 'changenode': {
|
|
var index = -1;
|
|
for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
|
|
if (index != -1) {
|
|
var node = nodes[index];
|
|
|
|
// Change the node
|
|
node.name = message.event.node.name;
|
|
node.rname = message.event.node.rname;
|
|
node.users = message.event.node.users;
|
|
node.host = message.event.node.host;
|
|
node.desc = message.event.node.desc;
|
|
node.ip = message.event.node.ip;
|
|
node.osdesc = message.event.node.osdesc;
|
|
node.publicip = message.event.node.publicip;
|
|
node.iploc = message.event.node.iploc;
|
|
node.wifiloc = message.event.node.wifiloc;
|
|
node.gpsloc = message.event.node.gpsloc;
|
|
node.tags = message.event.node.tags;
|
|
node.userloc = message.event.node.userloc;
|
|
node.rdpport = message.event.node.rdpport;
|
|
node.consent = message.event.node.consent;
|
|
if (message.event.node.links != null) { node.links = message.event.node.links; } else { delete node.links; }
|
|
if (message.event.node.agent != null) {
|
|
if (node.agent == null) node.agent = {};
|
|
if (message.event.node.agent.ver != null) { node.agent.ver = message.event.node.agent.ver; }
|
|
if (message.event.node.agent.id != null) { node.agent.id = message.event.node.agent.id; }
|
|
if (message.event.node.agent.caps != null) { node.agent.caps = message.event.node.agent.caps; }
|
|
if (message.event.node.agent.core != null) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } }
|
|
node.agent.tag = message.event.node.agent.tag;
|
|
}
|
|
if (message.event.node.intelamt != null) {
|
|
if (node.intelamt == null) node.intelamt = {};
|
|
if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
|
|
if (message.event.node.intelamt.host != null) { node.intelamt.user = message.event.node.intelamt.host; }
|
|
if (message.event.node.intelamt.user != null) { node.intelamt.user = message.event.node.intelamt.user; }
|
|
if (message.event.node.intelamt.tls != null) { node.intelamt.tls = message.event.node.intelamt.tls; }
|
|
if (message.event.node.intelamt.ver != null) { node.intelamt.ver = message.event.node.intelamt.ver; }
|
|
if (message.event.node.intelamt.tag != null) { node.intelamt.tag = message.event.node.intelamt.tag; }
|
|
if (message.event.node.intelamt.uuid != null) { node.intelamt.uuid = message.event.node.intelamt.uuid; }
|
|
if (message.event.node.intelamt.realm != null) { node.intelamt.realm = message.event.node.intelamt.realm; }
|
|
}
|
|
if (message.event.node.av != null) { node.av = message.event.node.av; }
|
|
node.namel = node.name.toLowerCase();
|
|
if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
|
|
if (message.event.node.icon) { node.icon = message.event.node.icon; }
|
|
|
|
// Web page update
|
|
masterUpdate(2 | 4 | 8 | 16);
|
|
refreshDevice(node._id);
|
|
|
|
if ((currentNode == node) && (xxdialogMode != null) && (xxdialogTag == '@xxmap')) { p10showNodeLocationDialog(); }
|
|
}
|
|
break;
|
|
}
|
|
case 'nodemeshchange': {
|
|
var index = -1;
|
|
for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
|
|
if (index != -1) {
|
|
var node = nodes[index];
|
|
if ((meshes[message.event.newMeshId] == null) && ((userinfo.links == null) || (userinfo.links[node._id] == null))) {
|
|
// We don't see the new mesh, remove this device
|
|
|
|
// TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
|
|
if ((xxcurrentView >= 10) && (xxcurrentView < 20) && !IsNodeViewable(currentNode)) { currentNode = null; setDialogMode(0); go(1); }
|
|
nodes.splice(index, 1);
|
|
masterUpdate(4 | 16);
|
|
} else {
|
|
// We see the new mesh, move this device
|
|
node.meshid = message.event.newMeshId;
|
|
node.meshnamel = meshes[message.event.newMeshId]?meshes[message.event.newMeshId].name.toLowerCase():'*';
|
|
masterUpdate(1 | 2 | 4);
|
|
}
|
|
refreshDevice(message.event.nodeid);
|
|
} else {
|
|
// This is a new device, add it.
|
|
var node = message.event.node;
|
|
if (!meshes[node.meshid]) break; // This is a node for a mesh we don't know. Happens when we are site administrator, we get all messages.
|
|
node.namel = node.name.toLowerCase();
|
|
if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
|
|
node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*';
|
|
node.state = 0;
|
|
if (!node.icon) node.icon = 1;
|
|
node.ident = ++nodeShortIdent;
|
|
if (nodes == null) { }
|
|
nodes.push(node);
|
|
|
|
// Web page update
|
|
masterUpdate(1 | 2 | 4 | 16);
|
|
}
|
|
break;
|
|
}
|
|
case 'nodeconnect': {
|
|
// Indicated a node has changed connectivity state
|
|
var index = -1;
|
|
for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
|
|
if (index != -1) {
|
|
var node = nodes[index];
|
|
|
|
// Event the connection change if needed
|
|
var n = getstore('notifications', 0); // Account notification settings
|
|
|
|
// Per-group notification settings
|
|
if (message.event.meshid && userinfo.links && userinfo.links[message.event.meshid] && userinfo.links[message.event.meshid].notify) {
|
|
n &= userinfo.links[message.event.meshid].notify;
|
|
} else {
|
|
n = 0;
|
|
}
|
|
|
|
// Show the notification
|
|
if (n & 2) {
|
|
if (((node.conn & 1) == 0) && ((message.event.conn & 1) != 0)) { addNotification({ text: "Agent connected", title: node.name, icon: node.icon, nodeid: node._id }); }
|
|
if (((node.conn & 2) == 0) && ((message.event.conn & 2) != 0)) { addNotification({ text: "Intel AMT detected", title: node.name, icon: node.icon, nodeid: node._id }); }
|
|
if (((node.conn & 4) == 0) && ((message.event.conn & 4) != 0)) { addNotification({ text: "Intel AMT CIRA connected", title: node.name, icon: node.icon, nodeid: node._id }); }
|
|
if (((node.conn & 16) == 0) && ((message.event.conn & 16) != 0)) { addNotification({ text: "MQTT connected", title: node.name, icon: node.icon, nodeid: node._id }); }
|
|
}
|
|
if (n & 4) {
|
|
if (((node.conn & 1) != 0) && ((message.event.conn & 1) == 0)) { addNotification({ text: "Agent disconnected", title: node.name, icon: node.icon, nodeid: node._id }); }
|
|
if (((node.conn & 2) != 0) && ((message.event.conn & 2) == 0)) { addNotification({ text: "Intel AMT not detected", title: node.name, icon: node.icon, nodeid: node._id }); }
|
|
if (((node.conn & 4) != 0) && ((message.event.conn & 4) == 0)) { addNotification({ text: "Intel AMT CIRA disconnected", title: node.name, icon: node.icon, nodeid: node._id }); }
|
|
if (((node.conn & 16) != 0) && ((message.event.conn & 16) == 0)) { addNotification({ text: "MQTT disconnected", title: node.name, icon: node.icon, nodeid: node._id }); }
|
|
}
|
|
|
|
// Change the node connection state
|
|
node.conn = message.event.conn;
|
|
node.pwr = message.event.pwr;
|
|
|
|
// Web page update
|
|
masterUpdate(4 | 16);
|
|
refreshDevice(node._id);
|
|
}
|
|
break;
|
|
}
|
|
case 'wssessioncount': {
|
|
// Update the active web socket session count for a user
|
|
if (wssessions != null) {
|
|
if (message.event.count == 0 && wssessions['user/' + domain + '/' + message.event.username.toLowerCase()]) {
|
|
delete wssessions['user/' + domain + '/' + message.event.username.toLowerCase()];
|
|
} else {
|
|
wssessions['user/' + domain + '/' + message.event.username.toLowerCase()] = message.event.count;
|
|
}
|
|
masterUpdate(16384);
|
|
}
|
|
break;
|
|
}
|
|
case 'login': {
|
|
// Update the last login time
|
|
if (users != null && users['user/' + domain + '/' + message.event.username.toLowerCase()]) {
|
|
users['user/' + domain + '/' + message.event.username.toLowerCase()].login = Math.floor(new Date(message.event.time).getTime() / 1000);
|
|
}
|
|
break;
|
|
}
|
|
case 'scanamtdevice': {
|
|
// Populate the Intel AMT scan dialog box with the result of the RMCP scan
|
|
if ((xxdialogMode == null) || (!Q('dp1range')) || (Q('dp1range').value != message.event.range)) return;
|
|
var x = '';
|
|
if (message.event.results == null) {
|
|
// The scan could not occur because of an error. Likely the user range was invalid.
|
|
x = '<div style=width:100%;text-align:center;margin-top:12px>' + "Unable to scan this address range." + '</div><div style=width:100%;text-align:center;margin-top:12px;color:gray;line-height:1.5>' + "Sample IP range values" + '<br />192.168.0.100<br />192.168.1.0/24<br />192.167.0.1-192.168.0.100</div>';
|
|
} else {
|
|
// Go thru all the results and populate the dialog box
|
|
amtScanResults = message.event.results;
|
|
for (var i in message.event.results) {
|
|
var r = message.event.results[i], shortname = r.hostname;
|
|
if (r.hosttype == 'host') { shortname = capitalizeFirstLetter(shortname.split('.')[0]); }
|
|
if (shortname.length > 20) { shortname = shortname.substring(0, 20) + '...'; }
|
|
var str = '<b title="' + EscapeHtml(r.hostname) + '">' + EscapeHtml(shortname) + '</b> - v' + r.ver;
|
|
if (r.state == 2) { if (r.tls == 1) { str += " with TLS."; } else { str += " without TLS."; } } else { str += ' not activated.'; }
|
|
x += '<div style=width:100%;margin-bottom:2px;background-color:lightgray><div style=padding:4px><div style=display:inline-block;margin-right:5px><input class=DevScanCheckbox name=dp1checkbox tag="' + EscapeHtml(i) + '" type=checkbox onclick=addAmtScanToMeshCheckbox() /></div><div class=j1 style=display:inline-block></div><div style=display:inline-block;margin-left:5px;overflow-x:auto;white-space:nowrap>' + str + '</div></div></div>';
|
|
}
|
|
// If no results where found, display a nice message
|
|
if (x == '') { x = '<div style=width:100%;text-align:center;margin-top:12px>' + "Scan returned no results." + '</div><div style=width:100%;text-align:center;margin-top:12px;color:gray;line-height:1.5>' + "Sample IP range values" + '<br />192.168.0.100<br />192.168.1.0/24<br />192.167.0.1-192.168.0.100</div>'; }
|
|
}
|
|
// Set the html in the dialog box and re-enable the scan button
|
|
QH('dp1results', x);
|
|
QE('dp1range', true);
|
|
QE('dp1rangebutton', true);
|
|
break;
|
|
}
|
|
case 'notify': {
|
|
var n = { text: message.event.value, title: message.event.title, icon: message.event.icon };
|
|
if (message.id != null) { n.id = message.id; }
|
|
if (message.event.tag != null) { n.tag = message.event.tag; }
|
|
addNotification(n);
|
|
break;
|
|
}
|
|
case 'traceinfo': {
|
|
if (typeof message.event.traceSources == 'object') {
|
|
if ((message.event.traceSources != null) && (message.event.traceSources.length > 0)) {
|
|
serverTraceSources = message.event.traceSources;
|
|
QH('p41traceStatus', EscapeHtml(message.event.traceSources.join(', ')));
|
|
} else {
|
|
serverTraceSources = [];
|
|
QH('p41traceStatus', "None");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'sysinfohash': {
|
|
// If the sysinfo document has changed and we are looking at it, request an update.
|
|
if ((currentNode != null) && (message.event.nodeid == powerTimelineReq)) {
|
|
meshserver.send({ action: 'getsysinfo', nodeid: message.event.nodeid });
|
|
}
|
|
break;
|
|
}
|
|
case 'stopped': { // Server is stopping.
|
|
// Disconnect
|
|
//console.log(message.msg);
|
|
break;
|
|
}
|
|
case 'updatePluginList': {
|
|
installedPluginList = message.event.list;
|
|
updatePluginList();
|
|
break;
|
|
}
|
|
case 'pluginStateChange': {
|
|
if (pluginHandler == null) break;
|
|
pluginHandler.refreshPluginHandler();
|
|
break;
|
|
}
|
|
case 'plugin': {
|
|
if (pluginHandler == null) break;
|
|
try { pluginHandler[message.event.plugin][message.event.pluginaction](message); } catch (e) { console.log("PluginHandler could not event message: ", e); }
|
|
break;
|
|
}
|
|
default:
|
|
//console.log('Unknown message.event.action', message.event.action);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 'createInviteLink': { // Agent installation invitation link
|
|
if (xxdialogTag != message.meshid) break;
|
|
var servername = serverinfo.name;
|
|
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
|
var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
|
|
var url;
|
|
if (serverinfo.https == true) {
|
|
var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
|
|
url = 'https://' + servername + portStr + domainUrl + 'agentinvite?c=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
|
|
} else {
|
|
var portStr = (serverinfo.port == 80) ? '' : (':' + serverinfo.port);
|
|
url = 'http://' + servername + portStr + domainUrl + 'agentinvite?c=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
|
|
}
|
|
Q('agentInvitationLink').href = url;
|
|
var t = format((message.expire == 1)?"1 hour":"{0} hours", message.expire);
|
|
if (message.expire == 24) { t = "1 day"; }
|
|
if (message.expire == 168) { t = "1 week"; }
|
|
if (message.expire == 5040) { t = "1 month"; }
|
|
if (message.expire == 0) { t = "Unlimited"; }
|
|
QH('agentInvitationLink', format("Invitation Link ({0})", t));
|
|
QV('agentInvitationLinkDiv', true);
|
|
break;
|
|
}
|
|
case 'getmqttlogin': {
|
|
if ((currentNode == null) || (currentNode._id != message.nodeid) || (xxdialogMode != null)) return;
|
|
var x = "These settings can be used to connect MQTT for this device." + '<br /><br />';
|
|
delete message.action;
|
|
delete message.nodeid;
|
|
x += '<textarea readonly=readonly style=width:100%;resize:none;height:100px;overflow:auto;font-size:12px readonly>' + JSON.stringify(message) + '</textarea>';
|
|
/*
|
|
x += addHtmlValue('Username', '<input style=width:230px readonly value="' + message.user + '" />');
|
|
x += addHtmlValue('Password', '<input style=width:230px readonly value="' + message.pass + '" />');
|
|
x += addHtmlValue('WS URL', '<input style=width:230px readonly value="' + message.wsUrl + '" />');
|
|
if (message.mpsUrl && message.mpsCertHash) {
|
|
x += addHtmlValue('MPS URL', '<input style=width:230px readonly value="' + message.mpsUrl + '" />');
|
|
x += addHtmlValue('MPS Cert Hash', '<input style=width:230px readonly value="' + message.mpsCertHash + '" />');
|
|
}
|
|
*/
|
|
setDialogMode(2, "MQTT Credentials", 1, null, x);
|
|
break;
|
|
}
|
|
case 'stopped': { // Server is stopping.
|
|
// Disconnect
|
|
autoReconnect = false;
|
|
QH('p0span', message.msg);
|
|
break;
|
|
}
|
|
case 'updatePluginList': {
|
|
installedPluginList = message.list;
|
|
updatePluginList();
|
|
break;
|
|
}
|
|
case 'pluginVersionsAvailable': {
|
|
if (pluginHandler == null) break;
|
|
updatePluginList(message.list);
|
|
break;
|
|
}
|
|
case 'downgradePluginVersions': {
|
|
var vSelect = '<select id="lastPluginVersion">';
|
|
message.info.versionList.forEach(function(v) { vSelect += '<option value="' + v.zipball_url + '">' + v.name + '</option>'; });
|
|
vSelect += '</select>';
|
|
setDialogMode(2, "Plugin Action", 3, pluginActionEx, format('Select the version to downgrade the plugin: {0}', message.info.name) + '<hr />' + vSelect + '<hr />' + "Please be aware that downgrading is not recommended. Please only do so in the event that a recent upgrade has broken something." + + '<input id="lastPluginAct" type="hidden" value="downgrade" /><input id="lastPluginId" type="hidden" value="' + message.info.id + '" />');
|
|
break;
|
|
}
|
|
case 'pluginError': {
|
|
setDialogMode(2, "Plugin Error", 1, null, message.msg);
|
|
break;
|
|
}
|
|
case 'plugin': {
|
|
if ((pluginHandler == null) || (typeof message.plugin != 'string')) break;
|
|
try { pluginHandler[message.plugin][message.method](server, message); } catch (e) { console.log('Error loading plugin handler ('+ e + ')'); }
|
|
break;
|
|
}
|
|
default:
|
|
//console.log('Unknown message.action', message.action);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go to the correct starting view page
|
|
function gotoStartViewPage() {
|
|
if (xxcurrentView != -1) return;
|
|
if ('{{currentNode}}' != '') {
|
|
if (getNodeFromId('{{currentNode}}') == null) return; // This node is not loaded yet
|
|
gotoDevice('{{currentNode}}', parseInt('{{viewmode}}'));
|
|
} else if (args.gotonode != null) {
|
|
if (getNodeFromId('node/' + domain + '/' + args.gotonode) == null) return; // This node is not loaded yet
|
|
gotoDevice('node/' + domain + '/' + args.gotonode, parseInt('{{viewmode}}'));
|
|
goBackStack.push(1);
|
|
} else if (args.gotomesh != null) {
|
|
if (meshes['mesh/' + domain + '/' + args.gotomesh] == null) return; // This device group is not loaded yet
|
|
gotoMesh('mesh/' + domain + '/' + args.gotomesh);
|
|
go(parseInt('{{viewmode}}'));
|
|
goBackStack.push(2);
|
|
} else if (args.gotouser != null) {
|
|
if ((users == null) || (users['user/' + domain + '/' + args.gotouser] == null)) return; // This user is not loaded yet
|
|
gotoUser('user/' + domain + '/' + args.gotouser);
|
|
go(parseInt('{{viewmode}}'));
|
|
goBackStack.push(4);
|
|
} else if (args.gotougrp != null) {
|
|
if ((usergroups == null) || usergroups['ugrp/' + domain + '/' + args.gotougrp] == null) return; // This user group is not loaded yet
|
|
gotoUserGroup('ugrp/' + domain + '/' + args.gotougrp);
|
|
go(parseInt('{{viewmode}}'));
|
|
goBackStack.push(50);
|
|
} else if ('{{viewmode}}' != '') {
|
|
go(parseInt('{{viewmode}}'));
|
|
} else {
|
|
setDialogMode(0);
|
|
go(1);
|
|
}
|
|
delete args.gotonode;
|
|
delete args.gotomesh;
|
|
delete args.gotouser;
|
|
delete args.gotougrp;
|
|
}
|
|
|
|
//
|
|
// MY DEVICES
|
|
//
|
|
|
|
function onRealNameCheckBox() {
|
|
showRealNames = Q('RealNameCheckBox').checked;
|
|
putstore('showRealNames', showRealNames ? 1 : 0);
|
|
masterUpdate(6);
|
|
return;
|
|
}
|
|
|
|
function onDeviceViewChange(i) {
|
|
if (i != null) { Q('viewselect').value = i; }
|
|
for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
|
|
Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
|
|
putstore('deviceView', Q('viewselect').value);
|
|
putstore('viewsize', Q('sizeselect').value);
|
|
masterUpdate(4);
|
|
setTimeout(function () { masterUpdate(512); }, 200);
|
|
}
|
|
|
|
function ondockeypress(e) {
|
|
setSessionActivity();
|
|
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
|
|
// Check what keys we are allows to send
|
|
if (currentNode != null) {
|
|
var meshrights = GetNodeRights(currentNode);
|
|
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
|
|
if (inputAllowed == false) return false;
|
|
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
|
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
|
|
}
|
|
return desktop.m.handleKeys(e);
|
|
}
|
|
if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3 && xterm == null) { return terminal.m.TermHandleKeys(e); }
|
|
if (!xxdialogMode && ((xxcurrentView == 15) || (xxcurrentView == 115))) return agentConsoleHandleKeys(e);
|
|
if (!xxdialogMode && xxcurrentView == 4) {
|
|
if (e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
|
|
var processed = 0;
|
|
if (e.key) {
|
|
if (e.key.length === 1 && userSearchFocus == 0) { Q('UserSearchInput').value = ((Q('UserSearchInput').value + e.key)); processed = 1; }
|
|
if (e.keyCode == 8 && userSearchFocus == 0) { var x = Q('UserSearchInput').value; Q('UserSearchInput').value = x.substring(0, x.length - 1); processed = 1; }
|
|
if (e.keyCode == 27) { Q('UserSearchInput').value = ''; processed = 1; }
|
|
} else {
|
|
if (e.charCode != 0 && userSearchFocus == 0) { Q('UserSearchInput').value = ((Q('UserSearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
|
|
}
|
|
if (processed > 0) { if (processed == 1) { onUserSearchInputChanged(); } return haltEvent(e); }
|
|
}
|
|
if (xxdialogMode || xxcurrentView != 1) return;
|
|
if (e.ctrlKey == true && e.charCode == 96) {
|
|
showRealNames = !showRealNames;
|
|
Q('RealNameCheckBox').value = showRealNames;
|
|
putstore('showRealNames', showRealNames ? 1 : 0);
|
|
masterUpdate(6)
|
|
return;
|
|
}
|
|
if (e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
|
|
if (Q('viewselect').value < 3) {
|
|
var processed = 0;
|
|
if (e.key) {
|
|
if (e.key.length === 1 && searchFocus == 0) { Q('SearchInput').value = ((Q('SearchInput').value + e.key)); processed = 1; }
|
|
if (e.keyCode == 8 && searchFocus == 0) { var x = Q('SearchInput').value; Q('SearchInput').value = x.substring(0, x.length - 1); processed = 1; }
|
|
if (e.keyCode == 27) { Q('SearchInput').value = ''; processed = 1; }
|
|
} else {
|
|
if (e.charCode != 0 && searchFocus == 0) { Q('SearchInput').value = ((Q('SearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
|
|
}
|
|
if (processed > 0) { if (processed == 1) { masterUpdate(5); } return haltEvent(e); }
|
|
}
|
|
if (Q('viewselect').value == 3) {
|
|
if (e.key) {
|
|
if (e.key.length === 1 && mapSearchFocus == 0) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + e.key)); processed = 1; }
|
|
//if (e.keyCode == 8 && mapSearchFocus == 0) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = x.substring(0, x.length - 1); processed = 1; }
|
|
if (e.keyCode == 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
|
|
if (e.keyCode == 13) { getSearchLocation(); }
|
|
} else {
|
|
if (e.charCode != 0 && mapSearchFocus == 0) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + String.fromCharCode(e.charCode))); processed = 1; }
|
|
}
|
|
}
|
|
}
|
|
|
|
function ondockeydown(e) {
|
|
setSessionActivity();
|
|
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
|
|
// Check what keys we are allows to send
|
|
if (currentNode != null) {
|
|
var meshrights = GetNodeRights(currentNode);
|
|
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
|
|
if (inputAllowed == false) return false;
|
|
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
|
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
|
|
}
|
|
return desktop.m.handleKeyDown(e);
|
|
}
|
|
if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3 && xterm == null) { terminal.m.TermHandleKeyDown(e); if ((e.keyCode >= 37) && (e.keyCode <= 40)) { haltEvent(e); } }
|
|
if (!xxdialogMode && xxcurrentView == 13 && e.keyCode == 116 && p13filetree != null) { haltEvent(e); return false; } // F5 Refresh on files
|
|
if (!xxdialogMode && ((xxcurrentView == 15) || (xxcurrentView == 115))) { return agentConsoleHandleKeys(e); }
|
|
if (!xxdialogMode && xxcurrentView == 4) {
|
|
if (e.keyCode === 8 && userSearchFocus == 0) { var x = Q('UserSearchInput').value; Q('UserSearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
|
|
if (e.keyCode === 27) { Q('UserSearchInput').value = ''; processed = 1; }
|
|
if (processed > 0) { if (processed == 1) { masterUpdate(5); } return haltEvent(e); }
|
|
}
|
|
if (xxdialogMode || xxcurrentView != 1 || e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
|
|
var processed = 0;
|
|
if (Q('viewselect').value < 3) {
|
|
if (e.keyCode === 8 && searchFocus == 0) { var x = Q('SearchInput').value; Q('SearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
|
|
if (e.keyCode === 27) { Q('SearchInput').value = ''; processed = 1; }
|
|
if (processed > 0) { if (processed == 1) { masterUpdate(5); } return haltEvent(e); }
|
|
}
|
|
if (Q('viewselect').value == 3) {
|
|
if (e.keyCode === 8 && mapSearchFocus == 0) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = (x.substring(0, x.length - 1)); processed = 1; }
|
|
if (e.keyCode === 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
|
|
}
|
|
}
|
|
|
|
function ondockeyup(e) {
|
|
setSessionActivity();
|
|
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked) {
|
|
// Check what keys we are allows to send
|
|
if (currentNode != null) {
|
|
var meshrights = GetNodeRights(currentNode);
|
|
var inputAllowed = ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
|
|
if (inputAllowed == false) return false;
|
|
var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
|
|
if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
|
|
}
|
|
return desktop.m.handleKeyUp(e);
|
|
}
|
|
if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3 && xterm == null) { return terminal.m.TermHandleKeyUp(e); }
|
|
if (!xxdialogMode && xxcurrentView == 13 && e.keyCode == 116 && p13filetree != null) { p13folderup(9999); haltEvent(e); return false; } // F5 Refresh on files
|
|
if (!xxdialogMode && xxcurrentView == 4) { if ((e.keyCode === 8 && searchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } }
|
|
if (xxdialogMode && e.keyCode == 27) { dialogclose(0); }
|
|
if (xxdialogMode || xxcurrentView != 0 || e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
|
|
if (Q('viewselect').value < 3) { if ((e.keyCode === 8 && searchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } }
|
|
if (Q('viewselect').value == 3) { if ((e.keyCode === 8 && mapSearchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } }
|
|
}
|
|
|
|
//function ondocfocus() { }
|
|
// TODO: Add handleReleaseKeys() for Intel AMT.
|
|
function ondocblur() { if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked && desktop.m.handleReleaseKeys) { return desktop.m.handleReleaseKeys(); } }
|
|
|
|
// Highlights the device being hovered
|
|
function devMouseHover(element, over) {
|
|
setSessionActivity();
|
|
var view = Q('viewselect').value;
|
|
if (view == 1) {
|
|
var e = element.children[1].children[1];
|
|
e.children[0].classList.remove('g1s');
|
|
e.children[1].classList.remove('e2s');
|
|
e.children[2].classList.remove('g2s');
|
|
if (over == 1) {
|
|
e.children[0].classList.add('g1s');
|
|
e.children[1].classList.add('e2s');
|
|
e.children[2].classList.add('g2s');
|
|
}
|
|
} else if (view == 2) {
|
|
var e = element;
|
|
e.children[2].classList.remove('g1s');
|
|
e.children[4].classList.remove('e2s');
|
|
e.children[3].classList.remove('g2s');
|
|
if (over == 1) {
|
|
e.children[2].classList.add('g1s');
|
|
e.children[4].classList.add('e2s');
|
|
e.children[3].classList.add('g2s');
|
|
}
|
|
}
|
|
}
|
|
|
|
var deviceHeaderId = 0;
|
|
var deviceHeaderTotal = 0;
|
|
var deviceHeadersTitles = {};
|
|
var deviceHeaderCount;
|
|
var deviceHeaders = {};
|
|
var oldviewmode = 0;
|
|
function updateDevices() {
|
|
if (nodes == null) { return; }
|
|
var r = '', c = 0, current = null, count = 0, scount = 0, displayedMeshes = {}, view = Q('viewselect').value, groups = {}, groupCount = {};
|
|
QV('xdevices', view < 4);
|
|
QV('xdevicesmap', view == 4);
|
|
QV('devListToolbar', view < 3);
|
|
QV('kvmListToolbar', view == 3);
|
|
QV('devMapToolbar', view == 4);
|
|
QV('devListToolbarSize', view == 3);
|
|
QV('NoMeshesPanel', (nodes.length == 0) && (meshcount == 0));
|
|
//QV('devListToolbarView', (meshcount != 0) && (nodes.length > 0));
|
|
QV('devListToolbarViewIcons', nodes.length > 0);
|
|
QV('devListToolbarSort', (nodes.length > 0) && (view < 4));
|
|
if (nodes.length == 0) { view = 1; sort = 0; }
|
|
if (view == 4) {
|
|
setTimeout( function() { if (xxmap.map != null) { xxmap.map.updateSize(); } }, 200);
|
|
// TODO
|
|
} else {
|
|
// 3 wide, list view or desktop view
|
|
deviceHeaderId = 0;
|
|
deviceHeaderCount = {};
|
|
deviceHeaderTotal = 0;
|
|
deviceHeaders = {};
|
|
deviceHeadersTitles = {};
|
|
var kvmDivs = [];
|
|
|
|
// Perform node sort
|
|
if (sort == 0) { nodes.sort(meshSort); }
|
|
else if (sort == 1) { nodes.sort(powerSort); }
|
|
else if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } }
|
|
|
|
// Save the list of currently checked nodeid's
|
|
var checkedNodeids = [], elements = document.getElementsByClassName('DeviceCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked) { checkedNodeids.push(elements[i].value); } }
|
|
if ((oldviewmode < 3) && (view == 3)) { multiDesktopFilter = checkedNodeids; }
|
|
else if ((oldviewmode == 3) && (view < 3)) { checkedNodeids = multiDesktopFilter; }
|
|
|
|
// Compute the width of the device view.
|
|
var totalDeviceViewWidth = Q('column_l').clientWidth - 60;
|
|
var deviceBoxWidth = Math.floor(totalDeviceViewWidth / 301);
|
|
deviceBoxWidth = 301 + Math.floor((totalDeviceViewWidth - (deviceBoxWidth * 301)) / deviceBoxWidth);
|
|
|
|
// Go thru the list of nodes and display them
|
|
for (var i in nodes) {
|
|
var node = nodes[i];
|
|
if (node.v == false) continue;
|
|
var mesh2 = meshes[node.meshid];
|
|
//if ((view == 3) && (mesh2.mtype == 1)) continue;
|
|
var meshrights = GetNodeRights(node);
|
|
if (sort == 0) {
|
|
// Mesh header
|
|
if (((meshes[node.meshid]?node.meshid:'*') != current)) {
|
|
if (((view == 1) || (view == 3)) && (current != null)) { r += '</div>'; } // Close collapse div
|
|
deviceHeaderSet();
|
|
var extra = '';
|
|
if (view == 2) { r += '<tr><td colspan=5>'; }
|
|
if (meshes[node.meshid] && (meshes[node.meshid].mtype == 1)) { extra = '<span class=devHeaderx>' + ", Intel® AMT only" + '</span>'; }
|
|
if ((view == 1) && (current != null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
|
|
if (view == 2) { r += '<div>'; }
|
|
r += '<div class=DevSt style=width:100%;padding-top:4px><span style=float:right>';
|
|
r += '<span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx></span>' + extra;
|
|
r += '</span>';
|
|
if ((view == 1) || (view == 2) || (view == 3)) {
|
|
var collapsed = CollapsedGroups[node.meshid];
|
|
r += '<img class=collapseImage id=\"DevxColImg' + deviceHeaderId + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + deviceHeaderId + '\",\"' + node.meshid + '\",' + view + ')></img>'; // Collapse action
|
|
}
|
|
if (meshes[node.meshid]) {
|
|
r += '<span id=MxMESH cmenu=meshContextMenu tabindex=0 style=cursor:pointer onclick=gotoMesh("' + node.meshid + '") onkeypress="if (event.key==\'Enter\') gotoMesh(\'' + node.meshid + '\')">' + EscapeHtml(meshes[node.meshid].name) + '</span>' + getMeshActions(mesh2, meshrights) + '</div>';
|
|
current = node.meshid;
|
|
} else {
|
|
r += '<span id=MxMESH><i>' + "Individual Devices" + '</i></span></div>';
|
|
current = '*';
|
|
}
|
|
if (view == 2) { r += '</div>'; }
|
|
displayedMeshes[current] = 1;
|
|
c = 0;
|
|
if ((view == 1) || (view == 3)) { r += '<div id=DevxCol' + deviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>'; } // Open collapse div
|
|
}
|
|
} else if (sort == 1) {
|
|
// Power header
|
|
var pwr = node.pwr?node.pwr:0;
|
|
if (pwr !== current) {
|
|
if (((view == 1) || (view == 3)) && (current != null)) { r += '</div>'; } // Close collapse div
|
|
deviceHeaderSet();
|
|
if ((view == 1) && (current !== null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
|
|
|
|
if (view == 2) { r += '<tr><td>'; }
|
|
r += '<div class=DevSt style=width:100%;padding-top:4px><span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx style=float:right></span>';
|
|
if ((view == 1) || (view == 2) || (view == 3)) {
|
|
var collapsed = CollapsedGroups['pwr:' + pwr];
|
|
r += '<img class=collapseImage id=\"DevxColImg' + deviceHeaderId + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + deviceHeaderId + '\",\"pwr:' + pwr + '\",' + view + ')></img>'; // Collapse action
|
|
}
|
|
r += '<span>' + PowerStateStr2(node.pwr) + '</span></div>';
|
|
|
|
current = pwr;
|
|
c = 0;
|
|
if ((view == 1) || (view == 3)) { r += '<div id=DevxCol' + deviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>'; } // Open collapse div
|
|
}
|
|
} else if (sort == 2) {
|
|
// Device header
|
|
if (current == null) { current = '1'; }
|
|
}
|
|
|
|
count++;
|
|
var title = EscapeHtml(node.name);
|
|
if (title.length == 0) { title = '<i>' + "None" + '</i>'; }
|
|
if ((node.rname != null) && (node.rname.length > 0)) { title += ' / ' + EscapeHtml(node.rname); }
|
|
var name = EscapeHtml(node.name);
|
|
if (showRealNames == true && node.rname != null) name = EscapeHtml(node.rname);
|
|
if (name.length == 0) { name = '<i>' + "None" + '</i>'; }
|
|
|
|
// Node
|
|
var icon = node.icon;
|
|
if ((!node.conn) || (node.conn == 0)) { icon += ' gray'; }
|
|
if (view == 1) {
|
|
r += '<div id=devs cmenu=devsContentMenu onmouseover=devMouseHover(this,1) onmouseout=devMouseHover(this,0) style=display:inline-block;width:' + deviceBoxWidth + 'px;height:50px;padding-top:1px;padding-bottom:1px><div style=width:22px;height:50%;float:left;padding-top:12px><input class="' + node.meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + node._id + ' type=checkbox></div><div style=height:100%;cursor:pointer tabindex=0 onclick=gotoDevice(\'' + node._id + '\',null,null,event) onkeypress="if (event.key==\'Enter\') gotoDevice(\'' + node._id + '\',null,null,event)"><div class="i' + icon + '" style=width:50px;float:left></div><div style=height:100%><div class=g1></div><div class=e2><div class=e1 style=width:' + (deviceBoxWidth - 100) + 'px title="' + title + '">' + name + '</div><div>' + NodeStateStr(node) + '</div></div><div class=g2></div></div></div></div>';
|
|
} else if (view == 2) {
|
|
var states = [];
|
|
if (node.conn) {
|
|
if ((node.conn & 1) != 0) { states.push('<span title=\"' + "Mesh agent is connected and ready for use." + '\">' + "Agent" + '</span>'); }
|
|
if ((node.conn & 2) != 0) { states.push('<span title=\"' + "Intel® AMT CIRA is connected and ready for use." + '\">' + "CIRA" + '</span>'); }
|
|
else if ((node.conn & 4) != 0) { states.push('<span title=\"' + "Intel® AMT is routable." + '\">' + "AMT" + '</span>'); }
|
|
if ((node.conn & 8) != 0) { states.push('<span title=\"' + "Mesh agent is reachable using another agent as relay." + '\">' + "Relay" + '</span>'); }
|
|
if ((node.conn & 16) != 0) { states.push('<span title=\"' + "MQTT connection to the device is active." + '\">' + "MQTT" + '</span>'); }
|
|
}
|
|
|
|
var collapseName = node.meshid;
|
|
if (sort == 1) { collapseName = ('pwr:' + (node.pwr?node.pwr:0)); }
|
|
else if (sort == 3) { collapseName = 'tag:**xx**xx*TaG*xx**xx**'; }
|
|
var collapsed = (sort != 3) & CollapsedGroups[collapseName];
|
|
r += '<tr name=DevxCol' + collapseName + (collapsed?' style=display:none':'') + '><td><div id=devs cmenu=devsContentMenu class=bar18 tabindex=0 onmouseover=devMouseHover(this,1) onmouseout=devMouseHover(this,0) style=height:18px;width:100%;font-size:medium onkeypress="if (event.key==\'Enter\') gotoDevice(\'' + node._id + '\',null,null,event)">';
|
|
r += '<div class=deviceBarCheckbox><input class="' + node.meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + node._id + ' type=checkbox></div>';
|
|
r += '<div class=deviceBarIcon onclick=gotoDevice(\'' + node._id + '\',null,null,event)><div class=\"j' + icon + '\" style=width:16px;margin-top:1px;margin-left:2px;height:16px></div></div>';
|
|
r += '<div class=g1 style=height:18px;float:left></div><div class=g2 style=height:18px;float:right></div>';
|
|
r += '<div class=style10 style=cursor:pointer;font-size:14px title="' + title + '" onclick=gotoDevice(\'' + node._id + '\',null,null,event)><span style=width:300px>' + name + '</span></div></div></td>';
|
|
r += '<td style=text-align:center>' + getUserShortStr(node);
|
|
r += '<td style=text-align:center>' + (node.ip != null ? node.ip : '');
|
|
r += '<td style=text-align:center>' + states.join(' + ');
|
|
//r += '<td style=text-align:center>' + (node.pwr != null ? powerStateStrings[node.pwr] : '');
|
|
r += '</tr>';
|
|
} else if ((view == 3) && (node.conn & 1) && (((meshrights & 8) || (meshrights & 256)) != 0) && ((node.agent.caps & 1) != 0)) { // Check if we have rights and agent is capable of KVM.
|
|
if ((multiDesktopFilter) && ((multiDesktopFilter.length == 0) || (multiDesktopFilter.indexOf('devid_' + node._id) >= 0))) {
|
|
r += '<div id=devs cmenu=devsContentMenu style=display:inline-block;margin:1px;background-color:lightgray;border-radius:5px;position:relative><div tabindex=0 style=padding:3px;cursor:pointer onclick=gotoDevice(\'' + node._id + '\',11,null,event) onkeypress="if (event.key==\'Enter\') gotoDevice(\'' + node._id + '\',11,null,event)">';
|
|
//r += '<input class="' + node.meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + node._id + ' type=checkbox style=float:left>';
|
|
r += '<div class="j' + icon + '" style=width:16px;float:left></div> ' + name + '</div>';
|
|
r += '<span onclick=gotoDevice(\'' + node._id + '\',null,null,event)></span><div id=xkvmid_' + node._id.split('/')[2] + '><div id=skvmid_' + node._id.split('/')[2] + ' tabindex=0 style="position:absolute;color:white;left:5px;top:27px;text-shadow:0px 0px 5px #000;z-index:1000;cursor:default" onclick=toggleKvmDevice(\'' + node._id + '\') onkeypress="if (event.key==\'Enter\') toggleKvmDevice(\'' + node._id + '\')">' + "Disconnected" + '</div></div>';
|
|
r += '</div>';
|
|
kvmDivs.push(node._id);
|
|
}
|
|
}
|
|
|
|
// If we are displaying devices by tags, put the device in the right tag group.
|
|
if ((sort == 3) && (r != '')) {
|
|
if (node.tags) {
|
|
for (var j in node.tags) {
|
|
var tag = node.tags[j];
|
|
var collapsed = CollapsedGroups['tag:' + tag];
|
|
var r2 = r.replace('**xx**xx*TaG*xx**xx**', encodeURIComponent(tag) + (collapsed?' style=display:none':''));
|
|
if (groups[tag] == null) { groups[tag] = r2; groupCount[tag] = 1; } else { groups[tag] += r2; groupCount[tag] += 1; }
|
|
if (view == 3) break;
|
|
}
|
|
}
|
|
r = '';
|
|
}
|
|
|
|
deviceHeaderTotal++;
|
|
if (typeof deviceHeaderCount[node.state] == 'undefined') { deviceHeaderCount[node.state] = 1; } else { deviceHeaderCount[node.state]++; }
|
|
}
|
|
|
|
if ((view == 1) && (current != null)) { r += '</div>'; } // Close collapse div
|
|
|
|
// Above 32 devices, gray out the auto connect feature.
|
|
if (kvmDivs.length >= 32) { Q('autoConnectDesktopCheckbox').checked = false; }
|
|
QE('autoConnectDesktopCheckbox', kvmDivs.length < 32);
|
|
|
|
// If displaying devices by groups, sort the group names and display the devices.
|
|
if (sort == 3) {
|
|
var groupNames = [], tagDeviceHeaderId = 0;
|
|
for (var i in groups) { groupNames.push(i); }
|
|
groupNames.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); });
|
|
for (var j in groupNames) {
|
|
var i = groupNames[j];
|
|
if (view == 2) {
|
|
r += '<tr><td colspan=4><div class=DevSt style=width:100%;padding-top:4px>';
|
|
var collapsed = CollapsedGroups['tag:' + encodeURIComponent(i)];
|
|
r += '<img class=collapseImage id=\"DevxColImg' + tagDeviceHeaderId + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + tagDeviceHeaderId + '\",\"tag:' + encodeURIComponent(i) + '\",2)></img>'; // Collapse action
|
|
r += '<span class=devHeaderx style=float:right>' + groupCount[i] + ' node' + ((groupCount[i] > 1) ? 's' : '') + '</span><span>' + i + '</span></div>' + groups[i];
|
|
} else {
|
|
r += '<div class=DevSt style=width:100%;padding-top:4px><span class=devHeaderx style=float:right>' + groupCount[i] + ' node' + ((groupCount[i] > 1) ? 's' : '') + '</span>';
|
|
var collapsed = CollapsedGroups['tag:' + encodeURIComponent(i)];
|
|
r += '<img class=collapseImage id=\"DevxColImg' + tagDeviceHeaderId + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + tagDeviceHeaderId + '\",\"tag:' + encodeURIComponent(i) + '\")></img>'; // Collapse action
|
|
r += '<span>' + i + '</span></div>';
|
|
r += '<div id=DevxCol' + tagDeviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
|
|
r += groups[i];
|
|
r += '</div>';
|
|
}
|
|
tagDeviceHeaderId++;
|
|
}
|
|
}
|
|
|
|
// If there is nothing to display, explain the problem
|
|
var viewNothing = false;
|
|
if ((r == '') && (nodes.length > 0) && (Q('SearchInput').value != '')) {
|
|
viewNothing = true;
|
|
if (sort == 3) {
|
|
r = '<div style="margin:30px">' + "No devices are included in any groups, click on a device\'s \"Groups\" to add to a group." + '</div>';
|
|
} else {
|
|
r = '<div style="margin:30px">' + "No devices matching this search." + '</div>';
|
|
}
|
|
}
|
|
|
|
if ((view == 1) && (c == 2)) r += '<td><div style=width:301px></div></td>'; // Adds device padding
|
|
|
|
// Display all empty device groups, we need to do this because users can add devices to these at any time.
|
|
if ((sort == 0) && (Q('SearchInput').value == '') && (view < 3)) {
|
|
var deviceHeaderId2 = deviceHeaderId, sortedMeshes = [];
|
|
for (var i in meshes) { sortedMeshes.push(meshes[i]); }
|
|
sortedMeshes.sort(nameSort);
|
|
for (var i in sortedMeshes) {
|
|
var mesh = sortedMeshes[i], meshrights = GetMeshRights(mesh);
|
|
if (displayedMeshes[mesh._id] == null) {
|
|
if ((current != '') && (r != '')) { r += '</tr></table>'; }
|
|
r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt>';
|
|
|
|
// Collapsing header & start collapsing area
|
|
deviceHeaderId2++;
|
|
var collapsed = CollapsedGroups[mesh._id];
|
|
r += '<img class=collapseImage id=\"DevxColImg' + deviceHeaderId2 + '\" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup(\"' + deviceHeaderId2 + '\",\"' + mesh._id + '\")></img>'; // Collapse action
|
|
|
|
r += '<span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span><span>';
|
|
r += getMeshActions(mesh, meshrights);
|
|
r += '</span></td></tr><tr>';
|
|
if (mesh.mtype == 1) {
|
|
r += '<td><div style=padding:10px><i>' + "No Intel® AMT devices in this mesh";
|
|
if ((meshrights & 4) != 0) { r += ', <a href=# style=cursor:pointer onclick=\'return addDeviceToMesh(\"' + mesh._id + '\")\'>' + "add one" + '</a>'; }
|
|
} else if (mesh.mtype == 2) {
|
|
r += '<td>';
|
|
r += '<div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
|
|
r += '<div style=padding:10px><i>' + "No devices in this group";
|
|
if ((meshrights & 4) != 0) { r += ', <a href=# style=cursor:pointer onclick=\'return addAgentToMesh(\"' + mesh._id + '\")\'>' + "add one" + '</a>'; }
|
|
}
|
|
r += '.</i></div></td>';
|
|
r += '</div>'; // End collapsing area
|
|
|
|
current = mesh._id;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((r != '') && (viewNothing == false)) {
|
|
r += '</table>';
|
|
if (view == 2) {
|
|
// This height of 1 div at the end to fix a problem in Linux firefox browsers
|
|
r = '<table style=width:100%;margin-top:4px cellpadding=0 cellspacing=0><th style=color:gray><th style=color:gray;width:120px>' + "User" + '<th style=color:gray;width:120px>' + "Address" + '<th style=color:gray;width:100px>' + "Connectivity" + r + '</tr></table><div style=height:1px></div>'; //<th style=color:gray;width:100px>State';
|
|
}
|
|
} else {
|
|
r += '</table>';
|
|
if (sort == 3) { r = '<div style="margin:10px"><i>' + "No devices with tags found." + '</i></div>'; }
|
|
}
|
|
|
|
// Add a "Add Device Group" option
|
|
r += '<div style=border-top-style:solid;border-top-width:1px;border-top-color:#DDDDDD;cursor:pointer;font-size:10px>';
|
|
if ((view < 3) && (sort == 0) && (nodes.length > 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0))) {
|
|
r += '<a href=# onclick="return account_createMesh()" title=\"' + "Create a new group of devices." + '\" style=cursor:pointer>' + "Add Device Group" + '</a> ';
|
|
}
|
|
if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) {
|
|
r += '<a href=# onclick=\'return p10showMeshCmdDialog(0)\' style=cursor:pointer title=\"' + "Download MeshCmd, a command line tool that performs many functions." + '\">' + "MeshCmd" + '</a> ';
|
|
if (navigator.platform.toLowerCase() == 'win32') { r += '<a href=# onclick=\'return p10showMeshRouterDialog()\' style=cursor:pointer title=\"' + "Download MeshCentral Router, a TCP port mapping tool." + '\">' + "Router" + '</a> '; }
|
|
}
|
|
r += '</div><br/>';
|
|
|
|
QH('xdevices', r);
|
|
deviceHeaderSet();
|
|
|
|
// Re-check nodeid's
|
|
var elements = document.getElementsByClassName('DeviceCheckbox'), checkcount = 0;
|
|
if (checkedNodeids) { for (var i=0;i<elements.length;i++) { elements[i].checked = (checkedNodeids.indexOf(elements[i].value) >= 0); } }
|
|
|
|
for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); }
|
|
for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; }
|
|
p1updateInfo();
|
|
|
|
// Take care of KVM surfaces in desktop view mode
|
|
if (view == 3) {
|
|
// Figure out and adjust the size to fill the width of the div
|
|
var vsize = [{ x: 180, y: 101 }, { x: 302, y: 169 }, { x: 454, y: 255 }][Q('sizeselect').selectedIndex];
|
|
//var realw = vsize.x + 2, tw = Q('xdevices').clientWidth - 30, xw = Math.floor(tw / realw);
|
|
var realw = vsize.x + 2, tw = totalDeviceViewWidth - 5, xw = Math.floor(tw / realw);
|
|
xw = realw + Math.floor((tw - (xw * realw)) / xw);
|
|
vsize.y = vsize.y * (xw / vsize.x);
|
|
vsize.x = xw;
|
|
|
|
for (var i in multiDesktop) { multiDesktop[i].xxdelete = true; }
|
|
for (var i in kvmDivs) {
|
|
var id = kvmDivs[i], shortid = id.split('/')[2], desk = multiDesktop[id];
|
|
if (desk != null) {
|
|
// This device already has a canvas, use it.
|
|
desk.m.CanvasId.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
|
|
Q('xkvmid_' + shortid).appendChild(desk.m.CanvasId);
|
|
delete desk.xxdelete;
|
|
QH('skvmid_' + shortid, ["Disconnected", "Connecting...", "Setup...", '', ''][((desk.m.State == null)?desk.m.state:desk.m.State)]);
|
|
} else {
|
|
var node = getNodeFromId(id);
|
|
if ((desktopNode == node) && (desktop != null)) { // Check if the main desktop is this device, if it is, use that.
|
|
// This device already has a canvas, use it.
|
|
var c = desktop.m.CanvasId;
|
|
c.setAttribute('id', 'kvmid_' + shortid);
|
|
c.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
|
|
c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
|
|
c.removeAttribute('onmousedown');
|
|
c.removeAttribute('onmouseup');
|
|
c.removeAttribute('onmousemove');
|
|
Q('xkvmid_' + shortid).appendChild(c);
|
|
QH('skvmid_' + shortid, ["Disconnected", "Connecting...", "Setup...", '', ''][((desktop.m.State == null)?desktop.m.state:desktop.m.State)]);
|
|
if (desktop.m.SendCompressionLevel) { desktop.m.SendCompressionLevel(1, multidesktopsettings.quality, multidesktopsettings.scaling, multidesktopsettings.framerate); }
|
|
desktop.shortid = shortid;
|
|
desktop.onStateChanged = onMultiDesktopStateChange;
|
|
multiDesktop[id] = desktop;
|
|
desktop = desktopNode = currentNode = null;
|
|
// Setup a replacement desktop
|
|
QH('DeskParent', '<canvas id="Desk" oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></canvas>');
|
|
} else {
|
|
// This is a new device, create a canvas for it.
|
|
var c = document.createElement('canvas');
|
|
c.setAttribute('id', 'kvmid_' + shortid);
|
|
c.setAttribute('width', 640);
|
|
c.setAttribute('height', 480);
|
|
c.setAttribute('oncontextmenu', 'return false');
|
|
c.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
|
|
c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
|
|
try { Q('xkvmid_' + shortid).appendChild(c); } catch (ex) {}
|
|
// Check if we need to auto-connect
|
|
if (Q('autoConnectDesktopCheckbox').checked == true) { setTimeout(function() { connectMultiDesktop(node, 1); }, 100); }
|
|
}
|
|
}
|
|
}
|
|
for (var i in multiDesktop) {
|
|
// If a device is no longer viewed, disconnect it.
|
|
if (multiDesktop[i].xxdelete == true) { multiDesktop[i].Stop(); delete multiDesktop[i]; }
|
|
else if (debugmode && multiDesktop[i].m && multiDesktop[i].m.onScreenSizeChange) {
|
|
mdeskAdjust(multiDesktop[i].m, multiDesktop[i].m.ScreenWidth, multiDesktop[i].m.ScreenHeight, multiDesktop[i].m.CanvasId); // Adjust screen size change
|
|
}
|
|
}
|
|
deskAdjust();
|
|
} else {
|
|
disconnectAllKvmFunction();
|
|
Q('autoConnectDesktopCheckbox').checked = false;
|
|
}
|
|
}
|
|
oldviewmode = view;
|
|
}
|
|
|
|
function toggleCollapseGroup(id, id2, type) {
|
|
var x;
|
|
if (type == 2) {
|
|
// Table rows collapse
|
|
var rows = document.getElementsByName('DevxCol' + id2);
|
|
if (rows.length == 0) return;
|
|
x = (rows[0].style['display'] == 'none');
|
|
if (x) { delete CollapsedGroups[id2]; } else { CollapsedGroups[id2] = true; }
|
|
for (var i = 0; i < rows.length; i++) { rows[i].style['display'] = (x ? '' : 'none'); }
|
|
} else {
|
|
// Simple DIV collapse
|
|
x = (QS('DevxCol' + id)['display'] == 'none');
|
|
if (x) { delete CollapsedGroups[id2]; } else { CollapsedGroups[id2] = true; }
|
|
QV('DevxCol' + id, x);
|
|
}
|
|
Q('DevxColImg' + id).src = x?'images/c2.png':'images/c1.png';
|
|
putstore('_collapse', JSON.stringify(CollapsedGroups));
|
|
}
|
|
|
|
function toggleKvmDevice(node) {
|
|
if (typeof node == 'string') { node = getNodeFromId(node); } // Convert nodeid to node if needed
|
|
var rights = GetNodeRights(node);
|
|
if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights
|
|
//var conn = 0;
|
|
//if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT)
|
|
if (node.conn & 1) { connectMultiDesktop(node, 1); }
|
|
}
|
|
}
|
|
|
|
function getUserShortStr(node) {
|
|
if (node == null || node.users == null || node.users.length == 0) return '';
|
|
if (node.users.length > 1) { return '<span title="' + EscapeHtml(node.users.join(', ')) + '">' + nobreak(format("{0} users", node.users.length)) + '</span>'; }
|
|
var u = node.users[0], su = u, i = u.indexOf('\\');
|
|
if (i > 0) { su = u.substring(i + 1); }
|
|
su = EscapeHtml(su);
|
|
if (su.length > 15) { su = su.substring(0, 14) + '…'; }
|
|
return '<span title="' + EscapeHtml(u) + '">' + su + '</span>';
|
|
}
|
|
|
|
function autoConnectDesktops() { if (Q('autoConnectDesktopCheckbox').checked == true) { connectAllKvmFunction(); } }
|
|
function connectAllKvmFunction(force) {
|
|
if (xxdialogMode) return false;
|
|
if (force !== true) { // We need to count how many devices will need to be connected, if it's a lot, prompt first.
|
|
var count = 0;
|
|
for (var i in nodes) {
|
|
var node = nodes[i], nodeid = nodes[i]._id;
|
|
if ((multiDesktop[nodeid] == null) && ((multiDesktopFilter.length == 0) || (multiDesktopFilter.indexOf('devid_' + nodeid) >= 0))) {
|
|
var rights = GetNodeRights(node);
|
|
if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights
|
|
//var conn = 0;
|
|
//if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT)
|
|
if ((node.conn & 1) && (node.v == true)) { count++; }
|
|
}
|
|
}
|
|
}
|
|
if (count > 8) { setDialogMode(2, "Connect All", 3, function() { connectAllKvmFunction(true); }, format("Are you sure you want to connect to {0} devices?", count)); return; }
|
|
}
|
|
|
|
// Perform connect all
|
|
for (var i in nodes) {
|
|
if ((nodes[i].v == true) && (multiDesktop[nodes[i]._id] == null) && ((multiDesktopFilter.length == 0) || (multiDesktopFilter.indexOf('devid_' + nodes[i]._id) >= 0))) {
|
|
toggleKvmDevice(nodes[i]._id);
|
|
}
|
|
}
|
|
}
|
|
function disconnectAllKvmFunction() { if (xxdialogMode) return false; for (var nodeid in multiDesktop) { multiDesktop[nodeid].Stop(); } multiDesktop = {}; }
|
|
function onMultiDesktopStateChange(desk, state) { try { QH('skvmid_' + desk.shortid, ["Disconnected", "Connecting...", "Setup...", '', ''][state]); } catch (ex) {} }
|
|
|
|
function showMultiDesktopSettings() {
|
|
QV('d7amtkvm', false);
|
|
QV('d7meshkvm', true);
|
|
d7bitmapquality.value = multidesktopsettings.quality;
|
|
d7bitmapscaling.value = multidesktopsettings.scaling;
|
|
if (multidesktopsettings.framerate) { d7framelimiter.value = multidesktopsettings.framerate; } else { d7framelimiter.value = 100; }
|
|
setDialogMode(7, "Remote Desktop Settings", 3, showMultiDesktopSettingsChanged);
|
|
}
|
|
|
|
function showMultiDesktopSettingsChanged() {
|
|
multidesktopsettings.quality = d7bitmapquality.value;
|
|
multidesktopsettings.scaling = d7bitmapscaling.value;
|
|
multidesktopsettings.framerate = d7framelimiter.value;
|
|
localStorage.setItem('multidesktopsettings', JSON.stringify(multidesktopsettings));
|
|
// Make changes to all current connections
|
|
for (var i in multiDesktop) { multiDesktop[i].m.SendCompressionLevel(1, multidesktopsettings.quality, multidesktopsettings.scaling, multidesktopsettings.framerate); }
|
|
}
|
|
|
|
function connectMultiDesktop(node, contype) {
|
|
var nodeid = node._id, shortid = nodeid.split('/')[2];
|
|
var desk = multiDesktop[nodeid];
|
|
if (desk == null) {
|
|
if (Q('kvmid_' + shortid) == null) return; // Check if this device is being displayed, if not, exit now.
|
|
if (contype == 2) {
|
|
// Setup the Intel AMT remote desktop
|
|
if ((node.intelamt.user == null) || (node.intelamt.user == '')) { return; }
|
|
desk = CreateAmtRedirect(CreateAmtRemoteDesktop('kvmid_' + shortid), authCookie);
|
|
desk.shortid = shortid;
|
|
//desk.debugmode = debugmode;
|
|
desk.onStateChanged = onMultiDesktopStateChange;
|
|
desk.m.bpp = 1;
|
|
desk.m.useZRLE = true;
|
|
desk.m.showmouse = true;
|
|
desk.m.onKvmData = function (data) { console.log('KVM Data received in multi-desktop mode, this is not supported.'); }; // KVM Data Channel not supported in multi-desktop right now.
|
|
//desk.m.onScreenSizeChange = deskAdjust;
|
|
if (debugmode > 0) { desk.m.onScreenSizeChange = mdeskAdjust; } // Multi-Desktop Adjust
|
|
desk.Start(nodeid, 16994, '*', '*', 0);
|
|
desk.contype = 2;
|
|
multiDesktop[nodeid] = desk;
|
|
} else if (contype == 1) {
|
|
// Setup the Mesh Agent remote desktop
|
|
desk = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('kvmid_' + shortid), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
|
desk.shortid = shortid;
|
|
desk.attemptWebRTC = attemptWebRTC;
|
|
desk.onStateChanged = onMultiDesktopStateChange;
|
|
//desk.onConsoleMessageChange = function () { console.log('CONSOLEMSG:', desk.consoleMessage); }
|
|
desk.m.CompressionLevel = multidesktopsettings.quality;
|
|
desk.m.ScalingLevel = multidesktopsettings.scaling;
|
|
if (multidesktopsettings.framerate) { desk.m.FrameRateTimer = multidesktopsettings.framerate; }
|
|
//desk.m.onDisplayinfo = deskDisplayInfo;
|
|
//desk.m.onScreenSizeChange = deskAdjust;
|
|
if (debugmode > 0) { desk.m.onScreenSizeChange = mdeskAdjust; } // Multi-Desktop Adjust
|
|
desk.Start(nodeid);
|
|
desk.contype = 1;
|
|
multiDesktop[nodeid] = desk;
|
|
}
|
|
} else {
|
|
// Disconnect and clean up the remote desktop
|
|
desk.Stop();
|
|
delete multiDesktop[nodeid];
|
|
}
|
|
}
|
|
|
|
function getMeshActions(mesh, meshrights) {
|
|
if ((meshrights & 4) == 0) return '';
|
|
var r = '';
|
|
if ((features & 1024) == 0) { // If CIRA is allowed
|
|
r += ' <a href=# style=cursor:pointer;font-size:10px title=\"' + "Add a new Intel® AMT computer that is located on the internet." + '\" onclick=\'return addCiraDeviceToMesh(\"' + mesh._id + '\")\'>' + "Add CIRA" + '</a>';
|
|
}
|
|
if (mesh.mtype == 1) {
|
|
if ((features & 1) == 0) { // If not WAN-Only
|
|
r += ' <a href=# style=cursor:pointer;font-size:10px title=\"' + "Add a new Intel® AMT computer that is located on the local network." + '\" onclick=\'return addDeviceToMesh(\"' + mesh._id + '\")\'>' + "Add Local" + '</a>';
|
|
r += ' <a href=# style=cursor:pointer;font-size:10px title=\"' + "Add a new Intel® AMT computer by scanning the local network." + '\" onclick=\'return addAmtScanToMesh(\"' + mesh._id + '\")\'>' + "Scan Network" + '</a>';
|
|
}
|
|
if (mesh.amt && (mesh.amt.type == 2)) { // CCM activation
|
|
r += ' <a href=# style=cursor:pointer;font-size:10px title=\"' + "Perform Intel AMT client control mode (CCM) activation." + '\" onclick=\'return showCcmActivation(\"' + mesh._id + '\")\'>' + "Activation" + '</a>';
|
|
} else if (mesh.amt && (mesh.amt.type == 3) && ((features & 0x00100000) != 0)) { // ACM activation
|
|
r += ' <a href=# style=cursor:pointer;font-size:10px title=\"' + "Perform Intel AMT admin control mode (ACM) activation." + '\" onclick=\'return showAcmActivation(\"' + mesh._id + '\")\'>' + "Activation" + '</a>';
|
|
}
|
|
}
|
|
if (mesh.mtype == 2) {
|
|
r += ' <a href=# style=cursor:pointer;font-size:10px title=\"' + "Add a new computer to this device group by installing the mesh agent." + '\" onclick=\'return addAgentToMesh(\"' + mesh._id + '\")\'>' + "Add Agent" + '</a>';
|
|
if ((features & 2) == 0) { r += ' <a href=# style=cursor:pointer;font-size:10px title=\"' + "Invite someone to install the mesh agent on this device group." + '\" onclick=\'return inviteAgentToMesh(\"' + mesh._id + '\")\'>' + "Invite" + '</a>'; }
|
|
}
|
|
return r;
|
|
}
|
|
|
|
function addDeviceToMesh(meshid) {
|
|
if (xxdialogMode) return false;
|
|
var mesh = meshes[meshid];
|
|
var x = format("Add a new Intel® AMT device to device group \"{0}\".", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
x += addHtmlValue("Device Name", '<input id=dp1devicename style=width:230px maxlength=32 autocomplete=off onchange=validateDeviceToMesh() onkeyup=validateDeviceToMesh() />');
|
|
x += addHtmlValue("Hostname", '<input id=dp1hostname style=width:230px maxlength=32 autocomplete=off placeholder=\"' + "Same as device name" + '\" onchange=validateDeviceToMesh() onkeyup=validateDeviceToMesh() />');
|
|
x += addHtmlValue("Username", '<input id=dp1username style=width:230px maxlength=32 autocomplete=off placeholder=\"' + "admin" + '\" onchange=validateDeviceToMesh() onkeyup=validateDeviceToMesh() />');
|
|
x += addHtmlValue("Password", '<input id=dp1password type=password style=width:230px autocomplete=off maxlength=32 onchange=validateDeviceToMesh() onkeyup=validateDeviceToMesh() />');
|
|
x += addHtmlValue("Security", '<select id=dp1tls style=width:236px><option value=0>' + "No TLS security" + '</option><option value=1>' + "TLS security required" + '</option></select>');
|
|
setDialogMode(2, "Add Intel® AMT device", 3, addDeviceToMeshEx, x, meshid);
|
|
validateDeviceToMesh();
|
|
Q('dp1devicename').focus();
|
|
return false;
|
|
}
|
|
|
|
// Intel AMT CCM Activation
|
|
function showCcmActivation(meshid) {
|
|
if (xxdialogMode) return false;
|
|
var servername = serverinfo.name, mesh = meshes[meshid];
|
|
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
|
var url, domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
|
|
if (serverinfo.https == true) {
|
|
var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
|
|
url = 'wss://' + servername + portStr + domainUrl;
|
|
} else {
|
|
var portStr = (serverinfo.port == 80) ? '' : (':' + serverinfo.port);
|
|
url = 'ws://' + servername + portStr + domainUrl;
|
|
}
|
|
var x = format("Perform Intel AMT client control mode (CCM) activation to group \"{0}\" by downloading the MeshCMD tool and running it like this:", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
x += '<textarea readonly=readonly style=width:100%;resize:none;height:100px;overflow:auto;font-size:12px readonly>meshcmd amtccm --url ' + url + 'amtactivate?id=' + meshid.split('/')[2] + ' --serverhttpshash ' + serverinfo.tlshash + '</textarea>';
|
|
setDialogMode(2, "Intel® AMT activation", 9, null, x);
|
|
Q('idx_dlgOkButton').focus();
|
|
return false;
|
|
}
|
|
|
|
// Intel AMT ACM Activation
|
|
function showAcmActivation(meshid) {
|
|
if (xxdialogMode) return false;
|
|
var servername = serverinfo.name, mesh = meshes[meshid];
|
|
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
|
var url, domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
|
|
if (serverinfo.https == true) {
|
|
var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
|
|
url = 'wss://' + servername + portStr + domainUrl;
|
|
} else {
|
|
var portStr = (serverinfo.port == 80) ? '' : (':' + serverinfo.port);
|
|
url = 'ws://' + servername + portStr + domainUrl;
|
|
}
|
|
var x = format("Perform Intel AMT admin control mode (ACM) activation to group \"{0}\" by downloading the MeshCMD tool and running it like this:", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
x += '<textarea readonly=readonly style=width:100%;resize:none;height:100px;overflow:auto;font-size:12px readonly>meshcmd amtacm --url ' + url + 'amtactivate?id=' + meshid.split('/')[2] + ' --serverhttpshash ' + serverinfo.tlshash + '</textarea>';
|
|
if (serverinfo.amtAcmFqdn != null) {
|
|
x += ('<div style=margin-top:8px>' + "Intel AMT will need to be set with a Trusted FQDN in MEBx or have a wired LAN on the network:" + ' <b>' + serverinfo.amtAcmFqdn.join(', ') + '</b></div>');
|
|
}
|
|
setDialogMode(2, "Intel® AMT activation", 9, null, x);
|
|
Q('idx_dlgOkButton').focus();
|
|
return false;
|
|
}
|
|
|
|
// Display the Intel AMT scanning dialog box
|
|
function addAmtScanToMesh(meshid) {
|
|
if (xxdialogMode) return false;
|
|
var x = "Enter a range of IP addresses to scan for Intel AMT devices." + '<br /><br />';
|
|
x += addHtmlValue("IP Range", '<input id=dp1range style=width:184px value="192.168.1.0/24" onkeyup=addAmtScanToMeshKeyUp(event) /><input id=dp1rangebutton type=button value=\"' + "Scan" + '\" onclick=addAmtScanToMeshButton()></input>');
|
|
x += '<div id=dp1results style="width:100%;height:200px;background-color:white;border:1px gray solid;overflow-y:scroll"></div>';
|
|
setDialogMode(2, "Scan for Intel® AMT devices", 3, addAmtScanToMeshEx, x, meshid);
|
|
QE('idx_dlgOkButton', false);
|
|
QH('dp1results', '<div style=width:100%;text-align:center;margin-top:12px;color:gray;line-height:1.5>Sample IP range values<br />192.168.0.100<br />192.168.1.0/24<br />192.167.0.1-192.168.0.100</div>');
|
|
focusTextBox('dp1range');
|
|
return false;
|
|
}
|
|
|
|
function addAmtScanToMeshKeyUp(e) {
|
|
if (e.keyCode == 13) { haltEvent(e); addAmtScanToMeshButton(); }
|
|
}
|
|
|
|
// Called when OK is pressed on the Intel AMT scanning box
|
|
function addAmtScanToMeshEx(button, meshid) {
|
|
var elements = document.getElementsByClassName('DevScanCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) {
|
|
if (elements[i].checked) {
|
|
var ipaddr = elements[i].getAttribute('tag');
|
|
var amtinfo = amtScanResults[ipaddr];
|
|
var name = amtinfo.hostname;
|
|
if (amtinfo.hosttype == 'host') { name = capitalizeFirstLetter(name.split('.')[0]); }
|
|
meshserver.send({ action: 'addamtdevice', meshid: meshid, devicename: name, hostname: amtinfo.hostname, amtusername: '', amtpassword: '', amttls: amtinfo.tls });
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the user presses the "Scan" button on the Intel AMT scanning dialog box, start a scan.
|
|
function addAmtScanToMeshButton() {
|
|
QE('dp1range', false);
|
|
QE('dp1rangebutton', false);
|
|
QH('dp1results', '<div style=width:100%;text-align:center;margin-top:12px>' + "Scanning..." + '</div>');
|
|
meshserver.send({ action: 'scanamtdevice', range: Q('dp1range').value });
|
|
}
|
|
|
|
// Called when a scanned computer is checked or unchecked.
|
|
function addAmtScanToMeshCheckbox() {
|
|
var elements = document.getElementsByClassName('DevScanCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked) checkcount++; }
|
|
QE('idx_dlgOkButton', checkcount > 0);
|
|
}
|
|
|
|
function addCiraDeviceToMesh(meshid) {
|
|
if (xxdialogMode) return false;
|
|
var mesh = meshes[meshid];
|
|
|
|
// Replace non alphabetic characters (@ and $) with 'X' because MPS username cannot accept it.
|
|
var meshidx = meshid.split('/')[2].replace(/\@/g, 'X').replace(/\$/g, 'X');
|
|
|
|
var y = '<select id=dlgAddCiraSel onclick=dlgAddCiraSelClick() style=width:230px><option value=0>' + "MeshCommander Script" + '</option><option value=1>' + "Manual Username/Password" + '</option>';
|
|
if ((features & 16) == 0) { y += ('<option value=2>' + "Manual Certificate" + '</option></select>'); } // Only display this option if Intel AMT CIRA with Mutual-Auth is allowed.
|
|
|
|
var x = '';
|
|
x += addHtmlValue("Setup Method", y);
|
|
x += '<hr>';
|
|
|
|
// Setup CIRA using a MeshCommander script (Pretty Simple)
|
|
x += '<div id=dlgAddCira0>' + format("To add a new Intel® AMT device to device group \"{0}\" with CIRA, download the following script files and use <a href='http://meshcommander.com' rel='noreferrer noopener' target='_blank'>MeshCommander</a> to run the script to configure computers.", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
//x += addHtmlValue('Setup CIRA', '<a href="mescript.ashx?type=1' + (urlargs.key?('&key=' + urlargs.key):'') + '&meshid=' + meshidx.substring(0, 16) + '" download>cira_setup.mescript</a>');
|
|
x += addHtmlValue("Setup CIRA", '<a href="mescript.ashx?type=1' + (urlargs.key?('&key=' + urlargs.key):'') + '&meshid=' + meshid + '" download>cira_setup.mescript</a>');
|
|
x += addHtmlValue("Cleanup CIRA", '<a href="mescript.ashx?type=2' + (urlargs.key?('&key=' + urlargs.key):'') + '" download>cira_clean.mescript</a>');
|
|
x += '</div>';
|
|
|
|
// Setup CIRA with user/pass authentication (Somewhat difficult)
|
|
x += '<div id=dlgAddCira1 style=display:none>' + format("To add a new Intel® AMT device to device group \"{0}\" with CIRA, load the following certificate as trusted root within Intel AMT", EscapeHtml(mesh.name));
|
|
if (serverinfo.mpspass) { x += (" and authenticate to the server using this username and password." + '<br /><br />'); } else { x += (" and authenticate to the server using this username and any password." + '<br /><br />'); }
|
|
x += addHtmlValue("Root Certificate", '<a href=\"' + "MeshServerRootCert.cer" + (urlargs.key?('?key=' + urlargs.key):'') + '\" download>' + "Root Certificate File" + '</a>');
|
|
x += addHtmlValue("Username", '<input style=width:230px readonly value="' + meshidx.substring(0, 16) + '" />');
|
|
if (serverinfo.mpspass) { x += addHtmlValue("Password", '<input style=width:230px readonly value="' + EscapeHtml(serverinfo.mpspass) + '" />'); }
|
|
if (serverinfo != null) { x += addHtmlValue("MPS Server", '<input style=width:230px readonly value="' + EscapeHtml(serverinfo.mpsname) + ':' + serverinfo.mpsport + '" />'); }
|
|
x += '</div>';
|
|
|
|
// Setup CIRA with certificate authentication (Really difficult, only if TLS offload is not used)
|
|
if ((features & 16) == 0) {
|
|
x += '<div id=dlgAddCira2 style=display:none>' + format("To add a new Intel® AMT device to device group \"{0}\" with CIRA, load the following certificate as trusted root within Intel AMT, authenticate using a client certificate with the following common name and connect to the following server.", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
x += addHtmlValue("Root Certificate", '<a href="' + "MeshServerRootCert.cer" + (urlargs.key?('?key=' + urlargs.key):'') + '" download>' + "Root Certificate File" + '</a>');
|
|
x += addHtmlValue("Organization", '<input style=width:230px readonly value="' + meshidx + '" />');
|
|
if (serverinfo != null) { x += addHtmlValue("MPS Server", '<input style=width:230px readonly value="' + EscapeHtml(serverinfo.mpsname) + ':' + serverinfo.mpsport + '" />'); }
|
|
x += '</div>';
|
|
}
|
|
|
|
setDialogMode(2, "Add Intel® AMT CIRA device", 2, null, x, 'fileDownload');
|
|
Q('dlgAddCiraSel').focus();
|
|
return false;
|
|
}
|
|
|
|
function dlgAddCiraSelClick() {
|
|
var val = Q('dlgAddCiraSel').value;
|
|
QV('dlgAddCira0', val == 0);
|
|
QV('dlgAddCira1', val == 1);
|
|
QV('dlgAddCira2', val == 2);
|
|
}
|
|
|
|
// Return true is the input string looks like an email address
|
|
function checkEmail(str) {
|
|
var x = str.split('@');
|
|
var ok = ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2));
|
|
if (ok == true) { var y = x[1].split('.'); for (var i in y) { if (y[i].length == 0) { ok = false; } } }
|
|
return ok;
|
|
}
|
|
|
|
function inviteAgentToMesh(meshid) {
|
|
if (xxdialogMode) return false;
|
|
var x = '', mesh = meshes[meshid];
|
|
if (features & 64) {
|
|
x += addHtmlValue("Invitation Type", '<select id=d2InviteType onchange=d2ChangedInviteType() style=width:236px><option value=0>' + "Link invitation" + '</option><option value=1>' + "Email invitation" + '</option></select>') + '<hr />';
|
|
x += '<div id=emailInviteDiv style=display:none>' + format("Invite someone to install the mesh agent. An email with be sent with the link to the mesh agent installation for the \"{0}\" device group.", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
x += addHtmlValue("Name (optional)", '<input id=agentInviteName value="" style=width:230px maxlength=64 />');
|
|
x += addHtmlValue("Email", '<input id=agentInviteEmail style=width:230px placeholder=\"' + "example@email.com" + '\" onkeyup=validateAgentInvite()></input>');
|
|
x += addHtmlValue("Operating System", '<select id=agentInviteNameOs onchange=d2ChangedInviteType() style=width:236px><option value=4>' + "Send installation link" + '</option><option value=0 selected>' + "Any supported" + '</option><option value=1>' + "Windows only" + '</option><option value=3>' + "Apple MacOS only" + '</option><option value=2>' + "Linux only" + '</option></select>');
|
|
x += '<div id=d2agentexpirediv>';
|
|
x += addHtmlValue("Link Expiration", '<select id=agentInviteExpire style=width:236px><option value=1>' + "1 hour" + '</option><option value=8>' + "8 hours" + '</option><option value=24>' + "1 day" + '</option><option value=168>' + "1 week" + '</option><option value=5040>' + "1 month" + '</option><option value=0>' + "Unlimited" + '</option></select>');
|
|
x += '</div>';
|
|
x += addHtmlValue("Installation Type", '<select id=agentInviteType style=width:236px><option value=0>' + "Background and interactive" + '</option><option value=2>' + "Background only" + '</option><option value=1>' + "Interactive only" + '</option></select>');
|
|
x += addHtmlValue("Message" + '<br />' + "(optional)", '<textarea id=agentInviteMessage value="" style=width:230px;height:100px;resize:none maxlength=1024 /></textarea>');
|
|
x += '</div>';
|
|
}
|
|
x += '<div id=urlInviteDiv>' + format("Invite someone to install the mesh agent by sharing an invitation link. This link points the user to installation instructions for the \"{0}\" device group. The link is public and no account for this server is needed.", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
x += addHtmlValue("Link Expiration", '<select id=d2inviteExpire style=width:236px onchange=d2RequestInvitationLink()><option value=1>' + "1 hour" + '</option><option value=8>' + "8 hours" + '</option><option value=24>' + "1 day" + '</option><option value=168>' + "1 week" + '</option><option value=5040>' + "1 month" + '</option><option value=0>' + "Unlimited" + '</option></select>');
|
|
x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px;display:none"><a href=# id=agentInvitationLink target="_blank" style=cursor:pointer></a> <img src=images/link4.png height=10 width=10 title=\"' + "Copy link to clipboard" + '\" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
|
|
setDialogMode(2, "Invite", 3, performAgentInvite, x, meshid);
|
|
if (features & 64) { Q('d2InviteType').focus(); d2ChangedInviteType(); } else { Q('d2inviteExpire').focus(); validateAgentInvite(); }
|
|
d2RequestInvitationLink();
|
|
return false;
|
|
}
|
|
|
|
function d2RequestInvitationLink() {
|
|
meshserver.send({ action: 'createInviteLink', meshid: xxdialogTag, expire: parseInt(Q('d2inviteExpire').value), flags: 0 });
|
|
}
|
|
|
|
function d2ChangedInviteType() {
|
|
QV('urlInviteDiv', Q('d2InviteType').value == 0);
|
|
QV('d2agentexpirediv', Q('agentInviteNameOs').value == 4);
|
|
QV('emailInviteDiv', Q('d2InviteType').value == 1);
|
|
validateAgentInvite();
|
|
}
|
|
|
|
function d2CopyInviteToClip() { copyTextToClip(Q('agentInvitationLink').href); }
|
|
|
|
function validateAgentInvite() {
|
|
if ((features & 64) && (Q('d2InviteType').value == 1)) {
|
|
QE('idx_dlgOkButton', checkEmail(Q('agentInviteEmail').value));
|
|
QV('idx_dlgCancelButton', true);
|
|
} else {
|
|
QE('idx_dlgOkButton', true);
|
|
QV('idx_dlgCancelButton', false);
|
|
}
|
|
}
|
|
|
|
function performAgentInvite(button, meshid) {
|
|
if ((features & 64) && (Q('d2InviteType').value == 1)) {
|
|
meshserver.send({ action: 'inviteAgent', meshid: meshid, email: Q('agentInviteEmail').value, name: Q('agentInviteName').value, os: Q('agentInviteNameOs').value, flags: Q('agentInviteType').value, msg: Q('agentInviteMessage').value, expire: parseInt(Q('agentInviteExpire').value) });
|
|
}
|
|
}
|
|
|
|
function addAgentToMesh(meshid) {
|
|
if (xxdialogMode) return false;
|
|
var mesh = meshes[meshid], x = '', installType = 0;
|
|
x += addHtmlValue("Operating System", '<select id=aginsSelect onchange=addAgentToMeshClick() style=width:236px><option value=0>' + "Windows" + '</option><option value=1>' + "Linux / BSD" + '</option><option value=2>' + "Apple MacOS" + '</option><option value=3>' + "Windows (UnInstall)" + '</option><option value=4>' + "Linux / BSD (UnInstall)" + '</option></select>');
|
|
x += '<div id=aginsTypeDiv>';
|
|
x += addHtmlValue("Installation Type", '<select id=aginsType onchange=addAgentToMeshClick() style=width:236px><option value=0>' + "Background & interactive" + '</option><option value=2>' + "Background only" + '</option><option value=1>' + "Interactive only" + '</option></select>');
|
|
x += '</div><hr>';
|
|
|
|
// \/:*?"<>|
|
|
var meshfilename = mesh.name
|
|
meshfilename = meshfilename.split('\\').join('').split('/').join('').split(':').join('').split('*').join('').split('?').join('').split('"').join('').split('<').join('').split('>').join('').split('|').join('').split(' ').join('').split('\'').join('');
|
|
|
|
// Windows agent install
|
|
//x += "<div id=agins_windows>To add a new computer to device group \"" + EscapeHtml(mesh.name) + "\", download the mesh agent and configuration file and install the agent on the computer to manage.<br /><br />";
|
|
x += '<div id=agins_windows>' + format("To add a new computer to device group \"{0}\", download the mesh agent and install it the computer to manage. This agent has server and device group information embedded within it.", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
x += addHtmlValue("Mesh Agent", '<a id=aginsw32lnk href="meshagents?id=3&meshid=' + meshid.split('/')[2] + '&installflags=0' + (urlargs.key?('&key=' + urlargs.key):'') + '" download onclick="setDialogMode(0)" title=\"' + "32bit version of the MeshAgent" + '\">' + "Windows (.exe)" + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy Windows 32bit agent URL to clipboard" + '" style=cursor:pointer onclick=copyAgentUrl("meshagents?id=3&meshid=' + meshid.split('/')[2] + '&installflags=",1)>');
|
|
x += addHtmlValue("Mesh Agent", '<a id=aginsw64lnk href="meshagents?id=4&meshid=' + meshid.split('/')[2] + '&installflags=0' + (urlargs.key?('&key=' + urlargs.key):'') + '" download onclick="setDialogMode(0)" title=\"' + "64bit version of the MeshAgent" + '\">' + "Windows x64 (.exe)" + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy Windows 64bit agent URL to clipboard" + '" style=cursor:pointer onclick=copyAgentUrl("meshagents?id=4&meshid=' + meshid.split('/')[2] + '&installflags=",1)>');
|
|
if (debugmode > 0) { x += addHtmlValue("Settings File", '<a id=aginswmshlnk href="meshsettings?id=' + meshid.split('/')[2] + '&installflags=0' + (urlargs.key?('&key=' + urlargs.key):'') + '" rel="noreferrer noopener" target="_blank">' + format("{0} settings (.msh)", EscapeHtml(mesh.name)) + '</a>'); }
|
|
x += '</div>';
|
|
|
|
// Linux agent install
|
|
x += '<div id=agins_linux style=display:none>' + format("To add a computer to {0} run the following command. Root credentials will be needed.", EscapeHtml(mesh.name)) + '<br />';
|
|
x += '<textarea id=agins_linux_area rows=2 cols=20 readonly=readonly style=width:100%;resize:none;height:120px;overflow:scroll;font-size:12px readonly></textarea>';
|
|
x += '<div style=\'font-size:x-small\'>' + "* For BSD, run \"pkg install wget sudo bash\" first." + '</div></div>';
|
|
|
|
// MacOS agent install
|
|
x += '<div id=agins_osx style=display:none>' + format("To add a new computer to device group \"{0}\", download the mesh agent and install it the computer to manage. This agent installer has server and device group information embedded within it.", EscapeHtml(mesh.name)) + '<br /><br />';
|
|
x += addHtmlValue("Mesh Agent", '<a href="meshosxagent?id=16&meshid=' + meshid.split('/')[2] + (urlargs.key?('&key=' + urlargs.key):'') + '" download rel="noreferrer noopener" target="_blank" title="' + "64bit version of MacOS Mesh Agent" + '">MacOS Agent (64bit)</a> <img src=images/link4.png height=10 width=10 title="' + "Copy MacOS agent URL to clipboard" + '" style=cursor:pointer onclick=copyAgentUrl("meshosxagent?id=16&meshid=' + meshid.split('/')[2] + '",0)>');
|
|
x += '</div>';
|
|
|
|
// Windows agent uninstall
|
|
x += '<div id=agins_windows_un style=display:none>' + "To remove a mesh agent, download the file below, run it and click \"uninstall\"." + '<br /><br />';
|
|
x += addHtmlValue("Mesh Agent", '<a href="meshagents?id=3' + (urlargs.key?('&key=' + urlargs.key):'') + '" download onclick="setDialogMode(0)" title="' + "32bit version of the MeshAgent" + '">' + "Windows (.exe)" + '</a>');
|
|
x += addHtmlValue("Mesh Agent", '<a href="meshagents?id=4' + (urlargs.key?('&key=' + urlargs.key):'') + '" download onclick="setDialogMode(0)" title="' + "64bit version of the MeshAgent" + '">' + "Windows x64 (.exe)" + '</a>');
|
|
x += '</div>';
|
|
|
|
// Linux agent uninstall
|
|
x += '<div id=agins_linux_un style=display:none>' + "To remove a mesh agent, run the following command. Root credentials will be needed." + '<br />';
|
|
x += '<textarea id=agins_linux_area_un rows=2 cols=20 readonly=readonly style=width:100%;resize:none;height:120px;overflow:scroll;font-size:12px readonly></textarea>';
|
|
x += '</div>';
|
|
|
|
setDialogMode(2, "Add Mesh Agent", 2, null, x, 'fileDownload');
|
|
var servername = serverinfo.name;
|
|
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
|
var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
|
|
|
|
if (serverinfo.https == true)
|
|
{
|
|
var portStr = (serverinfo.port == 443)?'':(':' + serverinfo.port);
|
|
if ((features & 0x2000) == 0)
|
|
{
|
|
Q('agins_linux_area').value = '(wget "https://' + servername + portStr + domainUrl + 'meshagents?script=1' + (urlargs.key?('&key=' + urlargs.key):'') + '" --no-check-certificate -O ./meshinstall.sh || wget "https://' + servername + portStr + domainUrl + 'meshagents?script=1" --no-proxy --no-check-certificate -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo -E ./meshinstall.sh https://' + servername + portStr + domainUrlNoSlash + ' \'' + meshid.split('/')[2] + '\' || ./meshinstall.sh https://' + servername + portStr + domainUrlNoSlash + ' \'' + meshid.split('/')[2] + '\'\r\n';
|
|
Q('agins_linux_area_un').value = '(wget "https://' + servername + portStr + domainUrl + 'meshagents?script=1' + (urlargs.key?('&key=' + urlargs.key):'') + '" --no-check-certificate -O ./meshinstall.sh || wget "https://' + servername + portStr + domainUrl + 'meshagents?script=1" --no-proxy --no-check-certificate -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n';
|
|
}
|
|
else
|
|
{
|
|
// Server asked that agent be installed to preferably not use a HTTP proxy.
|
|
Q('agins_linux_area').value = 'wget "https://' + servername + portStr + domainUrl + 'meshagents?script=1' + (urlargs.key?('&key=' + urlargs.key):'') + '" --no-proxy --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://' + servername + portStr + domainUrlNoSlash + ' \'' + meshid.split('/')[2] + '\' || ./meshinstall.sh https://' + servername + portStr + domainUrlNoSlash + ' \'' + meshid.split('/')[2] + '\'\r\n';
|
|
Q('agins_linux_area_un').value = 'wget "https://' + servername + portStr + domainUrl + 'meshagents?script=1' + (urlargs.key?('&key=' + urlargs.key):'') + '" --no-proxy --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var portStr = (serverinfo.port == 80) ? '' : (':' + serverinfo.port);
|
|
if ((features & 0x2000) == 0)
|
|
{
|
|
Q('agins_linux_area').value = '(wget "http://' + servername + portStr + domainUrl + 'meshagents?script=1' + (urlargs.key?('&key=' + urlargs.key):'') + '" -O ./meshinstall.sh || wget "http://' + servername + portStr + domainUrl + 'meshagents?script=1" --no-proxy -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo -E ./meshinstall.sh http://' + servername + portStr + domainUrlNoSlash + ' \'' + meshid.split('/')[2] + '\'\r\n';
|
|
Q('agins_linux_area_un').value = '(wget "http://' + servername + portStr + domainUrl + 'meshagents?script=1' + (urlargs.key?('&key=' + urlargs.key):'') + '" -O ./meshinstall.sh || wget "http://' + servername + portStr + domainUrl + 'meshagents?script=1" --no-proxy -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n';
|
|
}
|
|
else
|
|
{
|
|
// Server asked that agent be installed to preferably not use a HTTP proxy.
|
|
Q('agins_linux_area').value = 'wget "http://' + servername + portStr + domainUrl + 'meshagents?script=1' + (urlargs.key?('&key=' + urlargs.key):'') + '" --no-proxy -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://' + servername + portStr + domainUrlNoSlash + ' \'' + meshid.split('/')[2] + '\'\r\n';
|
|
Q('agins_linux_area_un').value = 'wget "http://' + servername + portStr + domainUrl + 'meshagents?script=1' + (urlargs.key?('&key=' + urlargs.key):'') + '" --no-proxy -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n';
|
|
}
|
|
}
|
|
Q('aginsSelect').focus();
|
|
addAgentToMeshClick();
|
|
return false;
|
|
}
|
|
|
|
function copyAgentUrl(url,addflag) {
|
|
var servername = serverinfo.name;
|
|
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
|
var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
|
|
var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
|
|
var c = 'https://' + servername + portStr + domainUrl + url;
|
|
if (addflag == 1) c += Q('aginsType').value;
|
|
c += (urlargs.key?('&key=' + urlargs.key):'');
|
|
copyTextToClip(c);
|
|
}
|
|
|
|
function addAgentToMeshClick() {
|
|
var v = Q('aginsSelect').value;
|
|
QV('agins_windows', v == 0);
|
|
QV('agins_linux', v == 1);
|
|
QV('agins_osx', v == 2);
|
|
QV('agins_windows_un', v == 3);
|
|
QV('agins_linux_un', v == 4);
|
|
QV('aginsTypeDiv', v == 0);
|
|
|
|
// Fix the links if needed
|
|
Q('aginsw32lnk').href = (Q('aginsw32lnk').href.split('installflags=')[0]) + 'installflags=' + Q('aginsType').value + (urlargs.key?('&key=' + urlargs.key):'');
|
|
Q('aginsw64lnk').href = (Q('aginsw64lnk').href.split('installflags=')[0]) + 'installflags=' + Q('aginsType').value + (urlargs.key?('&key=' + urlargs.key):'');
|
|
if (debugmode > 0) { Q('aginswmshlnk').href = (Q('aginswmshlnk').href.split('installflags=')[0]) + 'installflags=' + Q('aginsType').value + (urlargs.key?('&key=' + urlargs.key):''); }
|
|
}
|
|
|
|
function validateDeviceToMesh() {
|
|
QE('idx_dlgOkButton', (Q('dp1devicename').value.length > 0) && (passwordcheck(Q('dp1password').value)));
|
|
}
|
|
|
|
function addDeviceToMeshEx(button, meshid) {
|
|
var amtuser = Q('dp1username').value;
|
|
if (amtuser == '') amtuser = 'admin';
|
|
var host = Q('dp1hostname').value;
|
|
if (host == '') host = Q('dp1devicename').value;
|
|
meshserver.send({ action: 'addamtdevice', meshid: meshid, devicename: Q('dp1devicename').value, hostname: host, amtusername: amtuser, amtpassword: Q('dp1password').value, amttls: Q('dp1tls').value });
|
|
}
|
|
|
|
function deviceHeaderSet() {
|
|
if (deviceHeaderId == 0) { deviceHeaderId = 1; return; }
|
|
deviceHeaders['DevxHeader' + deviceHeaderId] = ((deviceHeaderTotal == 1) ? "1 node" : format("{0} nodes", deviceHeaderTotal));
|
|
//var title = '';
|
|
//for (x in deviceHeaderCount) { if (title.length > 0) title += ', '; title += deviceHeaderCount[x] + ' ' + PowerStateStr2(x); }
|
|
//deviceHeadersTitles["DevxHeader" + deviceHeaderId] = title;
|
|
deviceHeaderId++;
|
|
deviceHeaderCount = {};
|
|
deviceHeaderTotal = 0;
|
|
}
|
|
|
|
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>'];
|
|
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"];
|
|
var powerColorTable = ['pwsTransparent', 'pwsBlack', 'pwsBlue', 'pwsBlue2', 'pwsLightblue', 'pwsBlueviolet', 'pwsDarkgreen', 'pwsLightseagreen', 'pwsLightseagreen2'];
|
|
function NodeStateStr(node) {
|
|
var states = [];
|
|
if (node.state > 0 && node.state < powerStatetable.length) state.push(powerStatetable[node.state]);
|
|
if (node.conn) {
|
|
if ((node.conn & 1) != 0) { states.push('<span title=\"' + "Mesh agent is connected and ready for use." + '\">' + "Agent" + '</span>'); }
|
|
if ((node.conn & 2) != 0) { states.push('<span title=\"' + "Intel® AMT CIRA is connected and ready for use." + '\">' + "CIRA" + '</span>'); }
|
|
else if ((node.conn & 4) != 0) { states.push('<span title=\"' + "Intel® AMT is routable." + '\">' + "AMT" + '</span>'); }
|
|
if ((node.conn & 8) != 0) { states.push('<span title=\"' + "Mesh agent is reachable using another agent as relay." + '\">' + "Relay" + '</span>'); }
|
|
if ((node.conn & 16) != 0) { states.push('<span title=\"' + "MQTT connection to the device is active." + '\">' + "MQTT" + '</span>'); }
|
|
}
|
|
if ((node.pwr != null) && (node.pwr != 0)) { states.push(powerStateStrings[node.pwr]); }
|
|
return states.join(', ');
|
|
}
|
|
|
|
function PowerStateStr(x) {
|
|
if (x < powerStatetable.length) return powerStatetable[x];
|
|
return '';
|
|
}
|
|
|
|
function PowerStateStr2(x) {
|
|
if ((x != 0) && (x < powerStatetable.length)) return powerStatetable[x];
|
|
return "Unknown";
|
|
}
|
|
|
|
function selectallButtonFunction() {
|
|
var elements = document.getElementsByClassName('DeviceCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) checkcount++; }
|
|
for (var i=0;i<elements.length;i++) { elements[i].checked = (checkcount == 0); }
|
|
p1updateInfo();
|
|
}
|
|
|
|
function p1updateInfo() {
|
|
var elements = document.getElementsByClassName('DeviceCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) { checkcount++; } }
|
|
if (checkcount > 0) {
|
|
QE('GroupActionButton', true);
|
|
Q('SelectAllButton').value = "Select None";
|
|
QV('cxmgroupsplit', true);
|
|
QV('cxmdesktop', true);
|
|
} else {
|
|
QE('GroupActionButton', false);
|
|
Q('SelectAllButton').value = "Select All";
|
|
QV('cxmgroupsplit', false);
|
|
QV('cxmdesktop', false);
|
|
}
|
|
}
|
|
|
|
function groupActionFunction() {
|
|
var addedOptions = '', nodeids = getCheckedDevices();
|
|
|
|
// Check if any of the selected devices have a MQTT connection active
|
|
if (features & 0x00400000) {
|
|
for (var i in nodeids) { if ((getNodeFromId(nodeids[i]).conn & 16) != 0) { addedOptions += '<option value=103>' + "Send MQTT Message" + '</option>'; break; } }
|
|
}
|
|
|
|
// Display the "Uninstall Agent" option if allowed and we selected connected devices.
|
|
for (var i in nodeids) {
|
|
var node = getNodeFromId(nodeids[i]);
|
|
var rights = GetNodeRights(node);
|
|
if (((node.conn & 1) != 0) && ((rights & 32768) != 0)) { addedOptions += '<option value=104>' + "Uninstall Agent" + '</option>'; break; }
|
|
}
|
|
|
|
var x = "Select an operation to perform on all selected devices. Actions will be performed only with proper rights." + '<br /><br />';
|
|
x += addHtmlValue("Operation", '<select id=d2groupop><option value=100>' + "Wake-up devices" + '</option><option value=4>' + "Sleep devices" + '</option><option value=3>' + "Reset devices" + '</option><option value=2>' + "Power off devices" + '</option><option value=105>' + "Export device information" + '</option><option value=102>' + "Move to device group" + '</option>' + addedOptions + '<option value=101>' + "Delete devices" + '</option></select>');
|
|
setDialogMode(2, "Group Action", 3, groupActionFunctionEx, x);
|
|
}
|
|
|
|
// Get the list of checked devices, removes any duplicates.
|
|
function getCheckedDevices() {
|
|
var nodeids = [], elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked) { if (elements[i].value) { var nid = elements[i].value.substring(6); if (nodeids.indexOf(nid) == -1) { nodeids.push(nid); } } } }
|
|
return nodeids;
|
|
}
|
|
|
|
function groupActionFunctionEx() {
|
|
var op = Q('d2groupop').value;
|
|
if (op == 100) {
|
|
// Group wake
|
|
meshserver.send({ action: 'wakedevices', nodeids: getCheckedDevices() });
|
|
} else if (op == 101) {
|
|
// Group delete, ask for confirmation
|
|
var x = "Confirm delete selected devices(s)?" + '<br /><br />';
|
|
x += '<label><input id=d2check type=checkbox onchange=d2groupActionFunctionDelCheck() />' + "Confirm" + '</label>';
|
|
setDialogMode(2, "Delete Nodes", 3, d2groupActionFunctionDelExec, x);
|
|
QE('idx_dlgOkButton', false);
|
|
} else if (op == 102) {
|
|
// Move computers to a different group
|
|
p10showChangeGroupDialog(getCheckedDevices());
|
|
} else if (op == 103) {
|
|
// Send MQTT Message
|
|
p10showSendMqttMsgDialog(getCheckedDevices());
|
|
} else if (op == 104) {
|
|
// Uninstall agent
|
|
p10showSendUninstallAgentDialog(getCheckedDevices());
|
|
} else if (op == 105) {
|
|
// Export device information
|
|
p2downloadDeviceInfo();
|
|
} else {
|
|
// Power operation
|
|
meshserver.send({ action: 'poweraction', nodeids: getCheckedDevices(), actiontype: parseInt(op) });
|
|
}
|
|
}
|
|
|
|
function p2downloadDeviceInfo() {
|
|
if (xxdialogMode) return;
|
|
var x = "Download the list of devices with one of the file formats below." + '<br /><br />';
|
|
x += addHtmlValue("CSV Format", '<a href=# style=cursor:pointer onclick=\'return p2downloadDeviceInfoCSV()\'>' + "devicelist.csv" + '</a>');
|
|
x += addHtmlValue("JSON Format", '<a href=# style=cursor:pointer onclick=\'return p2downloadDeviceInfoJSON()\'>' + "devicelist.json" + '</a>');
|
|
setDialogMode(2, "Device Information Export", 1, null, x);
|
|
}
|
|
|
|
function p2downloadDeviceInfoCSV() {
|
|
var csv = "id, name, rname, host, icon, ip, osdesc, state, groupname, conn, pwr" + '\r\n', chkNodeIds = getCheckedDevices(), r = [];
|
|
for (var i in chkNodeIds) {
|
|
var n = getNodeFromId(chkNodeIds[i]);
|
|
csv += '\"' + n._id.split(',').join('') + '\",\"' + n.name.split(',').join('') + '\",\"' + (n.rname?(n.rname.split(',').join('')):'') + '\",\"' + (n.host?(n.host.split(',').join('')):'') + '\",\"' + n.icon + '\",\"' + (n.ip?n.ip:'') + '\",\"' + (n.osdesc?(n.osdesc.split(',').join('')):'') + '\",\"' + n.state + '\",\"' + meshes[n.meshid].name.split(',').join('') + '\",\"' + (n.conn?n.conn:'') + '\",\"' + (n.pwr?n.pwr:'') + '\"\r\n';
|
|
}
|
|
saveAs(new Blob([csv], { type: 'application/octet-stream' }), "devicelist.csv");
|
|
return false;
|
|
}
|
|
|
|
function p2downloadDeviceInfoJSON() {
|
|
var chkNodeIds = getCheckedDevices(), r = [];
|
|
for (var i in chkNodeIds) { r.push(getNodeFromId(chkNodeIds[i])); }
|
|
saveAs(new Blob([JSON.stringify(r, null, 2)], { type: 'application/octet-stream' }), "devicelist.json");
|
|
return false;
|
|
}
|
|
|
|
function d2groupActionFunctionDelCheck() { QE('idx_dlgOkButton', Q('d2check').checked); }
|
|
function d2groupActionFunctionDelExec() { meshserver.send({ action: 'removedevices', nodeids: getCheckedDevices() }); }
|
|
|
|
function onSortSelectChange(skipsave) {
|
|
sort = document.getElementById('sortselect').selectedIndex;
|
|
if (!skipsave) { putstore('sort', sort); }
|
|
}
|
|
|
|
function nameSort(a, b) { var aa = a.name.toLowerCase(), bb = b.name.toLowerCase(); if (aa > bb) return 1; if (aa < bb) return -1; return 0; }
|
|
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; }
|
|
function powerSort(a, b) { var ap = a.pwr?a.pwr:0; var bp = b.pwr?b.pwr:0; if (ap > bp) return -1; if (ap < bp) return 1; 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; } } return 0; }
|
|
function deviceSort(a, b) { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; }
|
|
function deviceHostSort(a, b) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; }
|
|
function onSearchFocus(x) { searchFocus = x; }
|
|
function onMapSearchFocus(x) { mapSearchFocus = x; }
|
|
function onUserSearchFocus(x) { userSearchFocus = x; }
|
|
function onConsoleFocus(x) { consoleFocus = x; }
|
|
|
|
function onSearchInputChanged() {
|
|
var x = Q('SearchInput').value.toLowerCase().trim(); putstore('_search', Q('SearchInput').value);
|
|
var userSearch = null, ipSearch = null, groupSearch = null, tagSearch = null;
|
|
if (x.startsWith("user:".toLowerCase())) { userSearch = x.substring("user:".length); }
|
|
else if (x.startsWith("u:".toLowerCase())) { userSearch = x.substring("u:".length); }
|
|
else if (x.startsWith("ip:".toLowerCase())) { ipSearch = x.substring("ip:".length); }
|
|
else if (x.startsWith("group:".toLowerCase())) { groupSearch = x.substring("group:".length); }
|
|
else if (x.startsWith("g:".toLowerCase())) { groupSearch = x.substring("g:".length); }
|
|
else if (x.startsWith("tag:".toLowerCase())) { tagSearch = Q('SearchInput').value.trim().substring("tag:".length); }
|
|
else if (x.startsWith("t:".toLowerCase())) { tagSearch = Q('SearchInput').value.trim().substring("t:".length); }
|
|
|
|
if (x == '') {
|
|
// No search
|
|
for (var d in nodes) { nodes[d].v = true; }
|
|
} else if (ipSearch != null) {
|
|
// IP address search
|
|
for (var d in nodes) { nodes[d].v = ((nodes[d].ip != null) && (nodes[d].ip.indexOf(ipSearch) >= 0)); }
|
|
} else if (groupSearch != null) {
|
|
// Group filter
|
|
for (var d in nodes) { nodes[d].v = (meshes[nodes[d].meshid].name.toLowerCase().indexOf(groupSearch) >= 0); }
|
|
} else if (tagSearch != null) {
|
|
// Tag filter
|
|
for (var d in nodes) {
|
|
nodes[d].v = ((nodes[d].tags == null) && (tagSearch == '')) || ((nodes[d].tags != null) && (nodes[d].tags.indexOf(tagSearch) >= 0));
|
|
}
|
|
} else if (userSearch != null) {
|
|
// User search
|
|
for (var d in nodes) {
|
|
nodes[d].v = false;
|
|
if (nodes[d].users && nodes[d].users.length > 0) { for (var i in nodes[d].users) { if (nodes[d].users[i].toLowerCase().indexOf(userSearch) >= 0) { nodes[d].v = true; } } }
|
|
}
|
|
} else {
|
|
// Device name search
|
|
try {
|
|
var rs = x.split(/\s+/).join('|'), rx = new RegExp(rs); // In some cases (like +), this can throw an exception.
|
|
for (var d in nodes) {
|
|
nodes[d].v = (rx.test(nodes[d].name.toLowerCase())) || (nodes[d].rnamel != null && rx.test(nodes[d].rnamel.toLowerCase()));
|
|
if ((nodes[d].v == false) && nodes[d].tags) {
|
|
for (var s in nodes[d].tags) {
|
|
if (rx.test(nodes[d].tags[s].toLowerCase())) {
|
|
nodes[d].v = true;
|
|
break;
|
|
} else {
|
|
nodes[d].v = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (ex) { for (var d in nodes) { nodes[d].v = true; } }
|
|
}
|
|
}
|
|
|
|
var contextelement = null;
|
|
function handleContextMenu(event) {
|
|
// When called, we look for elements with "cmenu=xxx" and show the right context menu for that element.
|
|
hideContextMenu();
|
|
if (xxdialogMode) return;
|
|
var scrollLeft = (window.pageXOffset !== null) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
|
|
var scrollTop = (window.pageYOffset !== null) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
|
|
var elem = document.elementFromPoint(event.pageX - scrollLeft, event.pageY - scrollTop);
|
|
while (elem && elem != null && elem.attributes.cmenu == null) { elem = elem.parentElement; } // Go up until element with context menu or root is reached.
|
|
if (elem == null) return true; // No "cmenu=xxx" found at the element that was clicked.
|
|
var cmenu = elem.attributes.cmenu.value;
|
|
|
|
switch (cmenu) {
|
|
case 'termConnectButton': {
|
|
// Terminal connect button context menu
|
|
if ((currentNode == null) || (currentNode.agent == null)) return true;
|
|
contextelement = elem;
|
|
var contextmenudiv = document.getElementById('termShellContextMenu'); // Windows options (Power Shell)
|
|
if (currentNode.agent.id > 4) { contextmenudiv = document.getElementById('termShellContextMenuLinux'); } // Non-Windows options
|
|
showContextMenuDiv(contextmenudiv, event.pageX, event.pageY);
|
|
break;
|
|
}
|
|
case 'deskConnectButton': {
|
|
// Desktop connect button context menu
|
|
if ((currentNode == null) || (currentNode.agent == null)) return true;
|
|
contextelement = elem;
|
|
showContextMenuDiv(document.getElementById('deskConnectContextMenu'), event.pageX, event.pageY);
|
|
break;
|
|
}
|
|
case 'devsContentMenu': {
|
|
// Device content menu
|
|
contextelement = elem;
|
|
var contextmenudiv = document.getElementById('contextMenu');
|
|
showContextMenuDiv(contextmenudiv, event.pageX, event.pageY);
|
|
|
|
// Get the node and set the menu options
|
|
var nodeid = contextelement.children[1].attributes.onclick.value;
|
|
var node = getNodeFromId(nodeid.substring(12, nodeid.length - 18));
|
|
var mesh = meshes[node.meshid];
|
|
var rights = GetNodeRights(node);
|
|
var consoleRights = ((rights & 16) != 0);
|
|
|
|
// Check if we have desktop, terminal and file access
|
|
var desktopAccess = ((rights == 0xFFFFFFFF) || ((rights & 65536) == 0));
|
|
var terminalAccess = ((rights == 0xFFFFFFFF) || ((rights & 512) == 0));
|
|
var fileAccess = ((rights == 0xFFFFFFFF) || ((rights & 1024) == 0));
|
|
|
|
QV('cxdesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((rights & 8) || (rights & 256)) && desktopAccess);
|
|
QV('cxterminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (rights & 8) && terminalAccess);
|
|
QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (rights & 8) && fileAccess);
|
|
QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (rights & 8));
|
|
QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (rights & 8));
|
|
break;
|
|
}
|
|
default: {
|
|
// Basic context menu
|
|
if ((contextmenudiv = document.getElementById(cmenu)) == null) return true;
|
|
contextelement = elem;
|
|
showContextMenuDiv(contextmenudiv, event.pageX, event.pageY);
|
|
break;
|
|
}
|
|
}
|
|
return haltEvent(event);
|
|
}
|
|
|
|
function showContextMenuDiv(element, x, y) {
|
|
var clientRect = document.documentElement.getBoundingClientRect();
|
|
var docHeight = clientRect.height;
|
|
var docWidth = clientRect.width;
|
|
element.style.left = element.style.right = element.style.top = element.style.bottom = null;
|
|
if (x > (docWidth / 2)) { element.style.right = (docWidth - event.pageX) + 'px'; } else { element.style.left = event.pageX + 'px'; }
|
|
if (y > (docHeight / 2)) { element.style.bottom = (docHeight - event.pageY) + 'px'; } else { element.style.top = event.pageY + 'px'; }
|
|
element.style.display = 'block';
|
|
}
|
|
|
|
function cmaction(action,event) {
|
|
var nodeid = contextelement.children[1].attributes.onclick.value;
|
|
nodeid = nodeid.substring(12, nodeid.length - 18);
|
|
if (action == 7) { Q('viewselect').value = 3; Q('viewselect').onchange(); Q('autoConnectDesktopCheckbox').checked = true; Q('autoConnectDesktopCheckbox').onclick(); } // Multi-Desktop
|
|
if ((action > 0) && (action < 7)) {
|
|
var panel = [0, 10, 12, 11, 13, 16, 15][action]; // (invalid), General, Desktop, Terminal, Files, Events, Console
|
|
if (event && (event.shiftKey == true)) {
|
|
// Open the device in a different tab
|
|
window.open(window.location.origin + '?node=' + nodeid.split('/')[2] + '&viewmode=' + panel + '&hide=16', 'meshcentral:' + nodeid);
|
|
} else {
|
|
// Go to the right panel
|
|
gotoDevice(nodeid, panel);
|
|
|
|
// If possible, connect...
|
|
var mesh = meshes[currentNode.meshid];
|
|
if ((currentNode.conn & 1) && (mesh.mtype == 2)) {
|
|
if ((panel == 11) && (desktop == null) && (currentNode.agent.caps & 1)) { connectDesktop(null, 3); } // Desktop
|
|
if ((panel == 12) && (terminal == null) && (currentNode.agent.caps & 2)) { connectTerminal(null, 1); } // Terminal
|
|
if ((panel == 13) && (files == null)) { connectFiles(null); } // files
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function cmmeshaction(action) {
|
|
var meshid = contextelement.attributes.onclick.value.substring(10, contextelement.attributes.onclick.value.length - 2);
|
|
var elements = document.getElementsByClassName('DeviceCheckbox');
|
|
if ((action == 1) || (action == 2)) {
|
|
for (var i = 0; i < elements.length; i++) {
|
|
if ((elements[i].attributes) && (elements[i].attributes['class']['value'].split(' ')[0] == meshid)) { elements[i].checked = (action == 1); }
|
|
}
|
|
}
|
|
//if (action == 3) { window.location = "multidesktop.aspx?mesh=" + meshid + "&auto=1"; }
|
|
p1updateInfo();
|
|
}
|
|
|
|
function cmtermaction(action) {
|
|
if (action < 100) {
|
|
connectTerminal(null, 1, { protocol: action });
|
|
} else if (action == 100) {
|
|
connectTerminal(null, 1, { protocol: 1, requireLogin: true });
|
|
}
|
|
}
|
|
|
|
function cmdeskaction(action) {
|
|
if (action == 1) { connectDesktop(null, 3, null, 0x0008 + 0x0040); } // Do remote desktop connection using Consent Prompt + Tool bar
|
|
if (action == 2) { connectDesktop(null, 3, null, 0x0008); } // Do remote desktop connection using Consent Prompt
|
|
}
|
|
|
|
function cmaltportaction(action) {
|
|
if (xxdialogMode) return;
|
|
var x = "RDP remote connection port:" + '<br /><br /><input type=text placeholder="3389" inputmode="numeric" pattern="[0-9]*" onkeypress=\"return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)\" maxlength=5 id=d10rdpport type=text>';
|
|
setDialogMode(2, "RDP Connection", 3, function() {
|
|
setDialogMode(0);
|
|
// Save the new RDP port to the server
|
|
var rdpport = ((Q('d10rdpport').value.length > 0) ? parseInt(Q('d10rdpport').value) : 3389);
|
|
meshserver.send({ action: 'changedevice', nodeid: currentNode._id, rdpport: rdpport });
|
|
if (currentNode != null) { p10clickOnce(currentNode._id, 'RDP2', rdpport); }
|
|
}, x, currentNode);
|
|
Q('d10rdpport').focus();
|
|
if (currentNode.rdpport != null) { Q('d10rdpport').value = currentNode.rdpport; }
|
|
}
|
|
|
|
function cmfilesaction(action) {
|
|
if (xxdialogMode) return;
|
|
var filetreexx = p13sort_files(p13filetree.dir);
|
|
var file = filetreexx[parseInt(contextelement.attributes.fileindex.nodeValue)];
|
|
if (action == 1) { // Rename the file
|
|
setDialogMode(2, "Rename", 3, p13renamefileEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% value="' + file.n + '" />', { action: 'rename', path: p13filetreelocation.join('/'), oldname: file.n});
|
|
focusTextBox('p13renameinput');
|
|
p13fileNameCheck();
|
|
} else if (action == 2) { // Edit the file
|
|
if (file.s <= 204800) {
|
|
p13downloadfile(encodeURIComponent(p13filetreelocation.join('/') + '/' + file.n), encodeURIComponent(file.n), file.s, 'viewer');
|
|
} else { messagebox("File Editor", "Only files less than 200k can be edited."); }
|
|
} else if (action == 3) { // Delete the file
|
|
setDialogMode(2, "Delete", 3, p13deletefileCm, "Delete item?", file);
|
|
}
|
|
}
|
|
|
|
function p13deletefileCm(b, file) {
|
|
files.sendText({ action: 'rm', reqid: 1, path: p13filetreelocation.join('/'), delfiles: [ file.n ], rec: false });
|
|
p13folderup(999);
|
|
}
|
|
|
|
/*
|
|
function pluginTabClose() {
|
|
var pluginTab = contextelement;
|
|
var pname = pluginTab.getAttribute('x-data-plugin-sname');
|
|
var pdiv = Q('plugin-'+pname);
|
|
pdiv.parentNode.removeChild(pdiv);
|
|
pluginTab.parentNode.removeChild(pluginTab);
|
|
QV('p42', true);
|
|
goPlugin(-1);
|
|
}
|
|
*/
|
|
|
|
function hideContextMenu() {
|
|
QV('contextMenu', false);
|
|
QV('meshContextMenu', false);
|
|
QV('termShellContextMenu', false);
|
|
QV('termShellContextMenuLinux', false);
|
|
QV('deskConnectContextMenu', false);
|
|
QV('altPortContextMenu', false);
|
|
QV('filesContextMenu', false);
|
|
//QV('pluginTabContextMenu', false);
|
|
contextelement = null;
|
|
}
|
|
|
|
//
|
|
// DEVICES MAP
|
|
//
|
|
|
|
{{{StartGeoLocationJS}}}
|
|
|
|
// Maps code starts from here. Initialize all the variables
|
|
var xxmap = {
|
|
map: null,
|
|
contextmenu: null,
|
|
activeInteractions: [], // Save Modified features in this list
|
|
showindex: 0,
|
|
markersSource: null, // Initialize a Source Vector
|
|
markersLayer: null,
|
|
mapLayer: null, // Create a tile and use OSM source
|
|
mapView: null, // Sets the initial view
|
|
}
|
|
|
|
// Add a feature for every Node and change style if connection status changes
|
|
function updateMapMarkers(selectedMesh) {
|
|
if ((xxmap != null) && (xxmap.map == null)) { try { loadmap(); } catch (ex) { console.error('loadmap() exception', ex); } }
|
|
if (xxmap == null) return;
|
|
var boundingBox = null;
|
|
for (var i in nodes) {
|
|
try {
|
|
var loc = map_parseNodeLoc(nodes[i]), feature = xxmap.markersSource.getFeatureById(nodes[i]._id);
|
|
if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations
|
|
var lat = loc[0], lon = loc[1], type = loc[2];
|
|
if (boundingBox == null) { boundingBox = [ lat, lon, lat, lon, 0 ]; } else { if (lat < boundingBox[0]) { boundingBox[0] = lat; } if (lon < boundingBox[1]) { boundingBox[1] = lon; } if (lat > boundingBox[2]) { boundingBox[2] = lat; } if (lon > boundingBox[3]) { boundingBox[3] = lon; } }
|
|
if (feature == null) { addFeature(nodes[i]); boundingBox[4] = 1; } else { updateFeature(nodes[i], feature); feature.setStyle(markerStyle(nodes[i], loc[2])); } // Update Feature
|
|
} else {
|
|
if (feature) { xxmap.markersSource.removeFeature(feature); }
|
|
}
|
|
} catch (ex) { console.error('updateMapMarkers() exception', ex, JSON.stringify(nodes[i])); }
|
|
}
|
|
return boundingBox;
|
|
}
|
|
|
|
// Show node details on hovering over a feature
|
|
var map_cm_popup = new ol.Overlay({ element: Q('xmap-info-window'), positioning: 'bottom-center', stopEvent: false });
|
|
|
|
// Edit Marker item
|
|
var map_cm_editMarker = { text: "Modify node location", callback: function (obj) { modifyMarkerloc(obj.data); } };
|
|
|
|
// Clear Marker item
|
|
var map_cm_clearMarker = { text: "Remove node location", callback: function (obj) {
|
|
meshserver.send({ action: 'changedevice', nodeid: obj.data.a, userloc: [] }); // Clear the user position marker
|
|
}};
|
|
|
|
// Save Marker item
|
|
var map_cm_saveMarker = { text: "Save node location", callback: function (obj) { saveMarkerloc(obj.data); } };
|
|
|
|
// Build a context menu for a feature
|
|
var map_cm_nodemenu_items = [
|
|
{ text: "General information", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 10); } } },
|
|
{ text: "Desktop", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 11); } } },
|
|
{ text: "Terminal", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 12); } } },
|
|
{ text: "Intel® AMT", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 14); } } },
|
|
'-',
|
|
{ text: "Zoom-in to extent", callback: function(obj) { var coords = obj.data.getGeometry().getCoordinates(); zoomToLocation(coords, 19); } },
|
|
{ text: "Zoom-out to extent", callback: function(obj) { var coords = obj.data.getGeometry().getCoordinates(); zoomToLocation(coords, 2); } }
|
|
];
|
|
|
|
// Context menu for clicks other than on feature
|
|
var contextmenu_items = [
|
|
{ text: "Refresh", callback: function () { refreshMap(true, true); } },
|
|
{ text: "Zoom to fit extent", callback: function () { zoomToFitExtent(); } },
|
|
{ text: "Center map here", callback: function(obj) { xxmap.mapView.animate({ center: obj.coordinate } ); } },
|
|
{ text: "Place node here", callback: function(obj) { placeNode(obj.coordinate); } }
|
|
];
|
|
|
|
function stringToIntHash(str) {
|
|
var hash = 0, i;
|
|
for (i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; }
|
|
return hash;
|
|
};
|
|
|
|
// Get the lat/lon from a node
|
|
function map_parseNodeLoc(node) {
|
|
var loc = null, t = 0;
|
|
if (node.iploc) { loc = node.iploc; t = 1; }
|
|
if (node.wifiloc) { loc = node.wifiloc; t = 2; }
|
|
if (node.gpsloc) { loc = node.gpsloc; t = 3; }
|
|
if (node.userloc) { loc = node.userloc; t = 4; }
|
|
if ((loc == null) || (typeof loc != 'string')) return null;
|
|
loc = loc.split(',');
|
|
if (t == 1) {
|
|
// If this is IP location, randomize the position a little.
|
|
return [ parseFloat(loc[0]) + (stringToIntHash(node._id.substring(0, 20)) / 100000000000), parseFloat(loc[1]) + (stringToIntHash(node._id.substring(20)) / 100000000000), t ];
|
|
} else {
|
|
// Return the real position
|
|
return [ parseFloat(loc[0]), parseFloat(loc[1]), t ];
|
|
}
|
|
}
|
|
|
|
// Load the entire map
|
|
function loadmap() {
|
|
if (xxmap == null) return;
|
|
if ((features & 0x8000) == 0) { xxmap = null; return; } // Geolocation not supported
|
|
QV('viewselectmapoption', true);
|
|
QV('devViewButton4', true);
|
|
try {
|
|
// Initialize a Source Vector
|
|
xxmap.markersSource = new ol.source.Vector();
|
|
|
|
xxmap.markersLayer = new ol.layer.Vector({
|
|
source: xxmap.markersSource
|
|
});
|
|
|
|
// Create a tile and use OSM source
|
|
xxmap.mapLayer = new ol.layer.Tile({ source: new ol.source.OSM() });
|
|
|
|
xxmap.mapView = new ol.View({ // Set the initial view
|
|
center: ol.proj.transform([0, 0], 'EPSG:4326', 'EPSG:3857'),
|
|
zoom: 2,
|
|
minZoom: 2,
|
|
maxZoom: 20,
|
|
extent: ol.proj.transformExtent([-100000, -69.55, 100000, 69.55], 'EPSG:4326', 'EPSG:3857')
|
|
});
|
|
|
|
xxmap.map = new ol.Map({
|
|
target: 'xdevicesmap',
|
|
layers: [xxmap.mapLayer, xxmap.markersLayer],
|
|
view: xxmap.mapView
|
|
});
|
|
|
|
xxmap.map.addOverlay(map_cm_popup);
|
|
|
|
// Goto information tab if a user clicks on a feature
|
|
xxmap.map.on('click', function(evt) {
|
|
var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(feat, layer) { return feat; });
|
|
if (feature) {
|
|
var nodeid = feature.getId();
|
|
if (nodeid != null) { gotoDevice(nodeid, 10); } // Goto general info tab
|
|
else { // For pointer
|
|
var nodeFeatgoto = getCorrespondingFeature(feature); gotoDevice(nodeFeatgoto.getId(), 10);
|
|
}
|
|
}
|
|
});
|
|
|
|
// On hover feature show the name of the node. Also add pointer style
|
|
xxmap.map.on('pointermove', function(evt) {
|
|
var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(feat, layer) { return feat; });
|
|
if (feature) {
|
|
xxmap.map.getTargetElement().style.cursor = 'pointer';
|
|
var coord = feature.getGeometry().getCoordinates();
|
|
// map_cm_popup.setPosition(evt.coordinate);
|
|
map_cm_popup.setPosition(coord);
|
|
var featid = feature.getId();
|
|
if (featid) {
|
|
QH('xmap-info-window', feature.get('name'));
|
|
} else {
|
|
var nodeFeat = getCorrespondingFeature(feature); // Return the node feature associated to pointer.
|
|
QH('xmap-info-window', nodeFeat.get('name'));
|
|
}
|
|
} else {
|
|
xxmap.map.getTargetElement().style.cursor = '';
|
|
QH('xmap-info-window', '');
|
|
}
|
|
});
|
|
|
|
// Initialize context menu for openlayers
|
|
var contextmenu = new ContextMenu({
|
|
width: 160,
|
|
defaultItems: false, // defaultItems are Zoom In/Zoom Out
|
|
items: contextmenu_items
|
|
});
|
|
|
|
// On right click open the context menu
|
|
contextmenu.on('open', function (evt) {
|
|
var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(ft, l){ return ft; });
|
|
xxmap.contextmenu.clear(); //Clear the context menu
|
|
if (feature) {
|
|
var featId = feature.getId();
|
|
if (featId) { addContextMenuItems(feature); } // Node feature will have an id
|
|
else { // If the feature is a pointer, Get its corresponding Node feature
|
|
var nodeFeature = getCorrespondingFeature(feature); //return the node feature associated to pointer.
|
|
if (nodeFeature) { addContextMenuItems(nodeFeature); }
|
|
else{ xxmap.contextmenu.extend(contextmenu_items); }
|
|
}
|
|
}
|
|
else { xxmap.contextmenu.extend(contextmenu_items); }
|
|
});
|
|
if (xxmap.contextmenu == null) { xxmap.contextmenu = contextmenu; }
|
|
xxmap.map.addControl(xxmap.contextmenu);
|
|
//addMeshOptions(); // Adds Mesh names to mesh dropdown
|
|
} catch (ex) {
|
|
console.log(ex);
|
|
QV('viewselectmapoption', false);
|
|
QV('devViewButton4', false);
|
|
xxmap = null;
|
|
}
|
|
}
|
|
|
|
// Add feature on to Map for a Node
|
|
function addFeature(node, lat, lon) {
|
|
var existingfeature = getModifiedFeature(node._id); // Check if Corresponding feature was Modified ( Modifed feature are in active interactions list)
|
|
if (existingfeature) { xxmap.markersSource.addFeature(existingfeature); } // Add that existing feature
|
|
else { // Add new feature for this node
|
|
if (!lat && !lon) { var loc = map_parseNodeLoc(node); lat = loc[0]; lon = loc[1]; }
|
|
|
|
// Fix the longiture and send an event to patch the db to correct coordinate format. It will cause second unnecessary updateFeature on this node to the map.
|
|
if (lon > 180) { lon = 180 - lon; meshserver.send({ action: 'changedevice', nodeid: node._id, userloc: [ lat, lon ] }); }
|
|
|
|
if ((lat < 90) && (lat > -90) && (lon < 180) && (lon > -180)) { // Check valid lat/lon
|
|
var feature = new ol.Feature({ geometry: new ol.geom.Point(ol.proj.transform([lon, lat], 'EPSG:4326','EPSG:3857')), name: node.name, status: node.conn, lat: lat, lon: lon });
|
|
feature.setId(node._id); // Set id for the device as nodeid
|
|
feature.setStyle(markerStyle(node));
|
|
xxmap.markersSource.addFeature(feature); // Add the feature to Marker Source
|
|
}
|
|
}
|
|
}
|
|
|
|
// Removing any feature from map
|
|
function removeFeature(node) {
|
|
var feature = xxmap.markersSource.getFeatureById(node._id);
|
|
if (feature) { xxmap.markersSource.removeFeature(feature); }
|
|
}
|
|
|
|
// Update feature
|
|
function updateFeature(node, feature) {
|
|
if (node.conn != feature.get('status') ) { // Update status if changed
|
|
feature.set('status',node.conn)
|
|
feature.setStyle(markerStyle(node));
|
|
}
|
|
|
|
// Since this is IP address location, add some fixed randomness to the location. Avoid pin pile-up.
|
|
var loc = map_parseNodeLoc(node);
|
|
if (loc != null) {
|
|
var lat = loc[0], lon = loc[1];
|
|
if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed
|
|
feature.set('lat', lat); feature.set('lon', lon);
|
|
var modifiedCoordinates = ol.proj.transform([parseFloat(lon), parseFloat(lat)], 'EPSG:4326', 'EPSG:3857');
|
|
feature.getGeometry().setCoordinates(modifiedCoordinates);
|
|
}
|
|
}
|
|
|
|
if (node.name != feature.get('name') ) { feature.set('name', node.name); } // Update name
|
|
}
|
|
|
|
// Enable dragging of a marker after edit option is clicked in context menu
|
|
function modifyMarkerloc(ft){
|
|
var featid = ft.getId();
|
|
if (featid) {
|
|
ft.setStyle(markerStyle(getNodeFromId(ft.a), 4)); // Switch to a user marker
|
|
if ( !getActiveInteractions(ft)) {
|
|
var dragInteration = new ol.interaction.Modify({
|
|
features: new ol.Collection([ft]),
|
|
pixelTolerance: 10
|
|
});
|
|
xxmap.activeInteractions.push({ featureid: featid, feature:ft, interaction: dragInteration }); // Also keep track of Interactions
|
|
xxmap.map.addInteraction(dragInteration);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This will be called when save location option is clicked in context menu
|
|
function saveMarkerloc(ft){
|
|
var featid = ft.getId()
|
|
if (featid) {
|
|
var actInteraction = getActiveInteractions(ft);
|
|
if (actInteraction) { // Check if the interaction exists
|
|
xxmap.map.removeInteraction(actInteraction); //Clear Interaction for that node
|
|
removeInteraction(featid);
|
|
var coord = ft.getGeometry().getCoordinates();
|
|
var v = ol.proj.transform(coord, 'EPSG:3857', 'EPSG:4326');
|
|
if (v[0] > 180) { v[0] = 180 - v[0]; }
|
|
var vx = [ v[1], v[0] ]; // Flip the coordinates around, lat/long
|
|
meshserver.send({ action: 'changedevice', nodeid: featid, userloc: vx }); // Send them to server to save changes
|
|
}
|
|
}
|
|
}
|
|
|
|
// Style the Markers
|
|
function markerStyle(node, type) {
|
|
if (type == null) {
|
|
type = 0;
|
|
if (node.iploc) { type = 1; }
|
|
if (node.wifiloc) { type = 2; }
|
|
if (node.gpsloc) { type = 3; }
|
|
if (node.userloc) { type = 4; }
|
|
}
|
|
var types = ['', '-ip','-wifi','-gps','-user'];
|
|
var color = connStateColor(node);
|
|
var style = new ol.style.Style({
|
|
image: new ol.style.Icon({ color: color, anchor: [0.5, 1], src: 'images/mapmarker' + types[type] + '.png' })
|
|
//stroke: new ol.style.Stroke({ color: '#000', width: 20 })
|
|
//text: new ol.style.Text({ text: 'bob!', textAlign: 'right', offsetX: -10, fill: new ol.style.Fill({ color: '#000' }), stroke: new ol.style.Stroke({ color: '#fff', width: 2 }) })
|
|
});
|
|
|
|
//deviceMark.setStyle(new ol.style.Style({
|
|
// text: new ol.style.Text({
|
|
// //font: '12px helvetica,sans-serif',
|
|
// text: currentNode.name,
|
|
// textAlign: 'right',
|
|
// offsetX: -10,
|
|
// fill: new ol.style.Fill({ color: '#000' }),
|
|
// stroke: new ol.style.Stroke({ color: '#fff', width: 2 })
|
|
// }),
|
|
// image: new ol.style.Icon(({ color: [113, 140, 0], src: 'images/dot.png' })) }));
|
|
|
|
return [ style ];
|
|
}
|
|
|
|
// TODO: Add more connection status types. Currently we only change color if connection status changes
|
|
function connStateColor(nodeConn){
|
|
if (nodeConn.conn == 1 || nodeConn.conn == 3 || nodeConn.conn == 5) { return '#00ffdd'; } // Green for connected devices
|
|
return '#C70039'; // Red if the Agent is not connected
|
|
}
|
|
|
|
// Add save/edit option to context menu
|
|
function addContextMenuItems(feature) {
|
|
if (getActiveInteractions(feature)) { // If this feature is modified then display save option in contextmenu
|
|
map_cm_saveMarker.data = feature;
|
|
xxmap.contextmenu.push(map_cm_saveMarker);
|
|
} else {
|
|
map_cm_editMarker.data = feature;
|
|
xxmap.contextmenu.push(map_cm_editMarker);
|
|
var node = getNodeFromId(feature.a);
|
|
if (node.userloc) {
|
|
map_cm_clearMarker.data = feature;
|
|
xxmap.contextmenu.push(map_cm_clearMarker);
|
|
}
|
|
}
|
|
map_cm_nodemenu_items.forEach(function (item){
|
|
if (item.text == "Zoom-in to extent" || item.text == "Zoom-out to extent") { item.data = feature; }
|
|
else { if (item != '-') { item.data = feature.getId(); } }
|
|
});
|
|
xxmap.contextmenu.extend(map_cm_nodemenu_items);
|
|
}
|
|
|
|
// Return a active Interaction if it exists in activeInteractions list
|
|
function getActiveInteractions(feature) {
|
|
var featid = feature.getId();
|
|
for (var i = 0; i < xxmap.activeInteractions.length; i++) {
|
|
if (xxmap.activeInteractions[i].featureid == featid) { return xxmap.activeInteractions[i].interaction; }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Return Modified feature based on Id
|
|
function getModifiedFeature(featid) {
|
|
if (featid) {
|
|
for (var i = 0; i < xxmap.activeInteractions.length; i++) {
|
|
if (xxmap.activeInteractions[i].featureid == featid) { return xxmap.activeInteractions[i].feature; }
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Remove Interaction
|
|
function removeInteraction(ftid) {
|
|
var index = -1;
|
|
for (var i = 0; i < xxmap.activeInteractions.length; i++) {
|
|
if (xxmap.activeInteractions[i].featureid === ftid) { index = i; break; }
|
|
}
|
|
if (index >= 0) { xxmap.activeInteractions.splice(index, 1); }
|
|
}
|
|
|
|
// Check if pointer coordinates are equal to features and return node feature
|
|
function getCorrespondingFeature(pointerFeat) {
|
|
var pointerCoord = pointerFeat.getGeometry().getCoordinates();
|
|
for (var i = 0; i < xxmap.activeInteractions.length ; i++) {
|
|
var modifiedFeatures = xxmap.activeInteractions[i].feature;
|
|
var fearCoord = modifiedFeatures.getGeometry().getCoordinates();
|
|
if (fearCoord[0].toFixed(5) == pointerCoord[0].toFixed(5) && fearCoord[1].toFixed(5) == pointerCoord[1].toFixed(5) ) { return modifiedFeatures; }
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Refresh the map and clear list
|
|
function refreshMap(reset, rebound){
|
|
if (reset) {
|
|
xxmap.map.setTarget(null);
|
|
xxmap.map = null;
|
|
xxmap.markersSource = null;
|
|
xxmap.mapView = null;
|
|
xxmap.mapLayer = null;
|
|
xxmap.activeInteractions = []; // Clear Active Interaction list
|
|
}
|
|
//clearMeshOptions();
|
|
//onSelectMeshChange();
|
|
var box = updateMapMarkers();
|
|
if ((box != null) && (rebound || (box[4] == 1))) {
|
|
var clat = (box[0] + box[2]) / 2;
|
|
var clon = (box[1] + box[3]) / 2;
|
|
var cscale = Math.max(Math.abs(box[0] - box[2]), Math.abs(box[1] - box[3]));
|
|
var view = xxmap.map.getView();
|
|
view.setCenter(ol.proj.transform([clon, clat], 'EPSG:4326', 'EPSG:3857'));
|
|
var i = 360, j = -2;
|
|
while (i > cscale) { j++; i = i / 2; }
|
|
view.setZoom(j);
|
|
}
|
|
}
|
|
|
|
// Called When Place a node option is clicked from context menu
|
|
function placeNode(coords) {
|
|
if (xxdialogMode) return;
|
|
var x = '<div style=margin-bottom:6px><label for=selectnode-search>' + "Search" + '</label>  <input type=text placeholder="' + "Device name" + '" id="selectnode-search" onchange=onPlaceNodeInputChange() onkeyup=onPlaceNodeInputChange() autocomplete=off style=width:120px></div><div id=placenode style="height:254px;overflow-y:auto;width:100%;margin:12px 1px 4px 1px;"><div id=noNodesMapPlace style=text-align:center;width:100%;display:none>' + "No devices found." + '</div>';
|
|
for (var i in nodes) {
|
|
x += '<div class=noselect id=' + nodes[i]._id + '-rowid onclick=selectNodeToPlace(event,\''+ nodes[i]._id +'\') style=background-color:lightgray;margin-bottom:4px;border-radius:2px><input name=PlaceMapDeviceCheckbox id=' + nodes[i]._id + '-checkid type=checkbox style=width:16px;display:inline />';
|
|
x += '<div class=j' + nodes[i].icon + ' style=width:16px;height:16px;margin-top:2px;margin-right:4px;display:inline-block></div><div style=width:16px;display:inline>' + nodes[i].name + '</div></div>';
|
|
}
|
|
setDialogMode(2, "Select a node to place", 3, placeNodeEx, x + '</div>', coords);
|
|
onPlaceNodeInputChange();
|
|
}
|
|
|
|
function placeNodeEx(button, coords) {
|
|
var elements = document.getElementsByName('PlaceMapDeviceCheckbox');
|
|
for (var i in elements) {
|
|
if (elements[i].checked) {
|
|
var node = getNodeFromId(elements[i].id.substring(0, elements[i].id.length - 8));
|
|
if (node) {
|
|
var feature = xxmap.markersSource.getFeatureById(i);
|
|
var v = ol.proj.transform(coords, 'EPSG:3857', 'EPSG:4326');
|
|
var vx = [ v[1], v[0] ]; // Flip the coordinates around, lat/long
|
|
if (feature) {
|
|
feature.getGeometry().setCoordinates(coords);
|
|
var activeInteraction = getActiveInteractions(feature);
|
|
if (activeInteraction) {
|
|
saveMarkerloc(feature);
|
|
} else { // If this feature is not saved after its location is changed, then send updated coords to server.
|
|
meshserver.send({ action: 'changedevice', nodeid: node._id, userloc: vx }); // Send them to server to save changes
|
|
}
|
|
} else {
|
|
meshserver.send({ action: 'changedevice', nodeid: node._id, userloc: vx }); // This Node is not yet added to maps.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called when the user changes the search box
|
|
function onPlaceNodeInputChange() {
|
|
updatePlaceNodeTable(Q('selectnode-search').value.trim().toLowerCase());
|
|
}
|
|
|
|
// Update the list of devices in the "place on map" table
|
|
function updatePlaceNodeTable(inputSearch) {
|
|
var elements = document.getElementsByName('PlaceMapDeviceCheckbox'), count = 0;
|
|
for (var i in nodes) {
|
|
var visible = ((nodes[i].namel.indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].rnamel != null && nodes[i].rnamel.indexOf(inputSearch) >= 0));
|
|
if (visible) { count++; }
|
|
QV(nodes[i]._id + '-rowid', visible);
|
|
}
|
|
QV('noNodesMapPlace', count == 0);
|
|
//console.log(selected);
|
|
//for (var i in nodes) {
|
|
// if ((nodes[i].name.toLowerCase().indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].rnamel != null && nodes[i].rnamel.toLowerCase().indexOf(inputSearch) >= 0)) {
|
|
// console.log(selected.indexOf(nodes[i]._id));
|
|
// x += '<div class=noselect id=' + nodes[i]._id + '-rowid onclick=selectNodeToPlace(event,\''+ nodes[i]._id +'\') style=background-color:lightgray;margin-bottom:4px;border-radius:2px><input name=PlaceMapDeviceCheckbox id=' + nodes[i]._id + '-checkid type=checkbox style=width:16px;display:inline ' + ((selected.indexOf(nodes[i]._id) >= 0)?'checked':'') + ' />';
|
|
// x += '<div class=j' + nodes[i].icon + ' style=width:16px;height:16px;margin-top:2px;margin-right:4px;display:inline-block></div><div style=width:16px;display:inline>' + nodes[i].name + '</div></div>';
|
|
// }
|
|
//}
|
|
//if (x == '') { x = '<div style=text-align:center;width:100%>No devices found.</div>'; }
|
|
//QH('placenode', '');
|
|
}
|
|
|
|
// Called when a user clicks on a device to toggle selection for placement on map.
|
|
function selectNodeToPlace(e, id) {
|
|
// Toggle checkbox if needed
|
|
if (e.target.name != 'PlaceMapDeviceCheckbox') { var inputElement = Q(id + '-checkid'); inputElement.checked = !inputElement.checked; }
|
|
|
|
// Check button state
|
|
var elements = document.getElementsByName('PlaceMapDeviceCheckbox'), checkcount = 0;
|
|
for (var i in elements) { if (elements[i].checked) checkcount++; }
|
|
QE('idx_dlgOkButton', checkcount > 0);
|
|
}
|
|
|
|
// Add option for available meshes in mesh Dropdown
|
|
function addMeshOptions(addMeshid, meshName) {
|
|
//var meshOptions = Q('select-mesh');
|
|
//if (addMeshid && meshName) {
|
|
// var option = document.createElement('option');
|
|
// option.value =addMeshid;
|
|
// option.text = meshName;
|
|
// meshOptions.add(option); // Add specific option
|
|
//}
|
|
//else {
|
|
// for (var i in meshes) { // Add all options
|
|
// var option = document.createElement('option');
|
|
// option.value = i;
|
|
// option.text = meshes[i].name;
|
|
// meshOptions.add(option);
|
|
// }
|
|
//}
|
|
}
|
|
|
|
// Remove/Modify options in Mesh dropdown (if modMeshname is defined then Modify else Remove)
|
|
function meshOptionRmvMod(delMeshid, modMeshname){
|
|
//var meshOptions = Q('select-mesh');
|
|
//if (delMeshid) {
|
|
// var index=-1;
|
|
// for (var i = 1; i < meshOptions.options.length; i++) {
|
|
// if (meshOptions[i].value === delMeshid) { index=i; }
|
|
// }
|
|
// if (index > 0) {
|
|
// if (modMeshname) {
|
|
// meshOptions[index].innerHTML=modMeshname; // If Mesh name is Modified
|
|
// }
|
|
// else { meshOptions.remove(index); }
|
|
// }
|
|
//}
|
|
}
|
|
|
|
//Check if there is any mesh created
|
|
function meshExists() {
|
|
for (var i in meshes) { if (meshes[i]) { return true; } }
|
|
return false;
|
|
}
|
|
|
|
// Reset Mesh dropdown option to 'All' when a current view mesh is deleted.
|
|
function setMeshView(emeshid) {
|
|
var selectMeshElement=Q('select-mesh');
|
|
var selectedIndex = selectMeshElement.selectedIndex;
|
|
if (selectMeshElement[selectedIndex].value == emeshid) { selectMeshElement[0].selected = true; onSelectMeshChange(); }
|
|
}
|
|
|
|
// Clear all mesh options except 'All'
|
|
function clearMeshOptions() {
|
|
//var meshOptions=Q('select-mesh');
|
|
//for(var i = meshOptions.options.length - 1 ; i > 0 ; i--) { meshOptions.remove(i); }
|
|
}
|
|
|
|
// Make a http get call- Replace this with AJAX get if jquery is used
|
|
function getSearchLocation() {
|
|
try {
|
|
var searchdata = Q('mapSearchLocation').value.trim();
|
|
if (searchdata.length > 0) {
|
|
var xmlhttp = new XMLHttpRequest(); // Compatible with Chrome, Opera, Safari, IE7+, Firefox.
|
|
xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { formatSearchData(xmlhttp.responseText); } }
|
|
xmlhttp.open('GET', 'https://nominatim.openstreetmap.org/search?q=' + searchdata + '&format=json', true); // Get request
|
|
xmlhttp.send();
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
// Format data recieved from nominatim API and display it on content window
|
|
function formatSearchData(data) {
|
|
try {
|
|
QH('xmapSearchResults','');
|
|
var dataInfo = JSON.parse(data), count = 0, x = '<div class="xmapItem">';
|
|
for (var i = 0; i < dataInfo.length; i++) {
|
|
if (dataInfo[i].display_name && dataInfo[i].boundingbox[0] && dataInfo[i].boundingbox[1] && dataInfo[i].boundingbox[2] && dataInfo[i].boundingbox[3]) {
|
|
count++;
|
|
var itemclass = (i % 2 == 0)?'xmapItemSel1':'xmapItemSel1';
|
|
x += '<div class="' + itemclass + '" onclick=mapGotoSelectedLocation(this)><div>' + dataInfo[i].display_name + '</div><div style=display:none>' + dataInfo[i].boundingbox[0] + '!#!' + dataInfo[i].boundingbox[1] + '!#!' + dataInfo[i].boundingbox[2] + '!#!' + dataInfo[i].boundingbox[3] + '</div></div>';
|
|
}
|
|
}
|
|
x += '</div>';
|
|
if (count == 1) {
|
|
// If only one result is returned then zoom to that location
|
|
var extent = [ parseFloat(dataInfo[0].boundingbox[2]), parseFloat(dataInfo[0].boundingbox[0]), parseFloat(dataInfo[0].boundingbox[3]), parseFloat(dataInfo[0].boundingbox[1]) ];
|
|
zoomToExtent(extent);
|
|
} else {
|
|
if (count == 0) { x = '<div style=width:200px>' + "No location found." + '<div>'; }
|
|
QV('xmapSearchResultsDlg', true);
|
|
}
|
|
QH('xmapSearchResults', x);
|
|
}
|
|
catch (e) {}
|
|
}
|
|
|
|
// Zoom into the bounding box
|
|
function mapGotoSelectedLocation(obj) {
|
|
var objchildren = obj.children;
|
|
var boundingBox = objchildren[1].innerHTML.split('!#!');
|
|
var extent = [parseFloat(boundingBox[2]), parseFloat(boundingBox[0]), parseFloat(boundingBox[3]), parseFloat(boundingBox[1])];
|
|
//Q('search-location').value = objchildren[0].innerHTML;
|
|
zoomToExtent(extent);
|
|
mapCloseSearchWindow();
|
|
}
|
|
|
|
// Close the search window
|
|
function mapCloseSearchWindow() {
|
|
QH('xmapSearchResults', '');
|
|
QV('xmapSearchResultsDlg', false);
|
|
}
|
|
|
|
// Zoom to specific cordinates
|
|
function zoomToLocation(coordinates, zoomVal) {
|
|
var view = xxmap.map.getView();
|
|
view.setCenter(coordinates);
|
|
view.setZoom(zoomVal);
|
|
}
|
|
|
|
function zoomToFitExtent() {
|
|
var features = xxmap.markersSource.getFeatures();
|
|
if (features.length > 0) {
|
|
var extent = xxmap.markersSource.getExtent();
|
|
xxmap.map.getView().fit(extent, xxmap.map.getSize());
|
|
}
|
|
}
|
|
|
|
function zoomToExtent(extent){
|
|
var boundingExtent = ol.proj.transformExtent(extent, ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857'));
|
|
xxmap.map.getView().fit(boundingExtent, xxmap.map.getSize());
|
|
}
|
|
|
|
{{{EndGeoLocationJS}}}
|
|
|
|
//
|
|
// MY DEVICE
|
|
//
|
|
function refreshDevice(nodeid) {
|
|
if (!currentNode || currentNode._id != nodeid) return;
|
|
gotoDevice(nodeid, xxcurrentView, true);
|
|
}
|
|
|
|
var currentNode;
|
|
var powerTimelineNode = null;
|
|
var powerTimelineReq = null;
|
|
var powerTimelineUpdate = null;
|
|
var powerTimeline = null;
|
|
function getCurrentNode() { return currentNode; };
|
|
function gotoDevice(nodeid, panel, refresh, event) {
|
|
// Remind the user to verify the email address
|
|
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
|
|
|
|
// Remind the user to add two factor authentication
|
|
if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" tab and look at the \"Account Security\" section."); return; }
|
|
|
|
if (event && (event.shiftKey == true)) {
|
|
// Open the device in a different tab
|
|
window.open(window.location.origin + '?node=' + nodeid.split('/')[2] + '&viewmode=10&hide=16', 'meshcentral:' + nodeid);
|
|
return;
|
|
}
|
|
|
|
//disconnectAllKvmFunction();
|
|
var node = getNodeFromId(nodeid);
|
|
if (node == null) return;
|
|
var mesh = meshes[node.meshid];
|
|
var meshrights = GetNodeRights(node);
|
|
if (!currentNode || currentNode._id != node._id || refresh == true) {
|
|
currentNode = node;
|
|
|
|
// Add node name
|
|
var nname = EscapeHtml(node.name);
|
|
if (nname.length == 0) { nname = '<i>' + "None" + '</i>'; }
|
|
if (((meshrights & 4) != 0) && ((!mesh.flags) || ((mesh.flags & 2) == 0))) { nname = '<span tabindex=0 title=\"' + "Click here to edit the server-side device name" + '\" onclick=showEditNodeValueDialog(0) onkeyup="if (event.key == \'Enter\') showEditNodeValueDialog(0)" style=cursor:pointer>' + nname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
|
|
if (mesh) { nname += '<span style=color:#AAA;font-size:small> - ' + EscapeHtml(mesh.name) + '</span>'; }
|
|
QH('p10deviceName', nname);
|
|
QH('p11deviceName', nname);
|
|
QH('p12deviceName', nname);
|
|
QH('p13deviceName', nname);
|
|
QH('p14deviceName', nname);
|
|
QH('p15deviceName', "Console - " + nname);
|
|
QH('p16deviceName', nname);
|
|
QH('p17deviceName', nname);
|
|
QH('p19deviceName', nname);
|
|
|
|
// Node attributes
|
|
var x = '<table style=width:100%>';
|
|
|
|
// Attribute: Mesh
|
|
if (mesh) { x += addDeviceAttribute('<span title=\"' + "The name of the device group this computer belong to." + '\">' + "Group" + '</span>', '<a href=# title=\"' + "The name of the device group this computer belong to" + '\" onclick=gotoMesh("' + node.meshid + '") style=cursor:pointer>' + EscapeHtml(meshes[node.meshid].name) + '</a>'); }
|
|
|
|
// Attribute: Name
|
|
if ((node.rname != null) && (node.name != node.rname)) { 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>'); }
|
|
|
|
// Attribute: Host
|
|
if ((features & 1) == 0) { // If not WAN-only, local hostname is in use
|
|
x += addDeviceAttribute("Hostname", addLinkConditional('<span onclick=showEditNodeValueDialog(1) style=cursor:pointer>' + (node.host?EscapeHtml(node.host):('<i>' + "None" + '</i>')) + '</span>', 'showEditNodeValueDialog(1)', meshrights & 4));
|
|
}
|
|
|
|
// Attribute: Description
|
|
var description = node.desc?EscapeHtml(node.desc):('<i>' + "None" + '</i>');
|
|
if ((meshrights & 4) != 0) {
|
|
x += addDeviceAttribute("Description", '<span onclick=showEditNodeValueDialog(2) style=cursor:pointer>' + description + ' <img class=hoverButton src="images/link5.png" /></span>');
|
|
} else {
|
|
x += addDeviceAttribute("Description", description);
|
|
}
|
|
|
|
// Attribute: Mesh Agent
|
|
if ((node.agent != null) && (node.agent.id != null) && (node.agent.ver != null)) {
|
|
var str = '';
|
|
if (node.agent.id <= agentsStr.length) { str = agentsStr[node.agent.id]; } else { str = agentsStr[0]; }
|
|
if (node.agent.ver != 0) { str += ' v' + node.agent.ver; }
|
|
x += addDeviceAttribute("Mesh Agent", str);
|
|
}
|
|
|
|
// Attribute: Intel AMT
|
|
if (node.intelamt != null) {
|
|
var str = '';
|
|
var provisioningStates = { 0: nobreak("Not Activated (Pre)"), 1: nobreak("Not Activated (In)"), 2: nobreak("Activated") };
|
|
if (node.intelamt.ver != null && node.intelamt.state == null) { str += '<i>' + "Unknown State" + '</i>, v' + node.intelamt.ver; } else
|
|
|
|
if ((node.intelamt.ver == null) && (node.intelamt.state == 2)) { str += '<i>' + "Activated" + '</i>'; }
|
|
else if ((node.intelamt.ver == null) || (node.intelamt.state == null)) { str += '<i>' + "Unknown Version & State" + '</i>'; }
|
|
else {
|
|
str += provisioningStates[node.intelamt.state];
|
|
if ((node.intelamt.state == 2) && 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>'; } }
|
|
str += (', v' + node.intelamt.ver);
|
|
}
|
|
|
|
if (node.intelamt.tls == 1) { str += ', <span title=\"' + "Intel AMT is setup with TLS network security" + '\">' + "TLS" + '</span>'; }
|
|
if (node.intelamt.state == 2) {
|
|
if (node.intelamt.user == null || node.intelamt.user == '') {
|
|
if ((meshrights & 4) != 0) {
|
|
str += ', <i style=color:#FF0000;cursor:pointer title=\"' + "Edit Intel® AMT credentials" + '\" onclick=editDeviceAmtSettings("' + node._id + '")>' + "No Credentials" + '</i>';
|
|
} else {
|
|
str += ', <i style=color:#FF0000>' + "No Credentials" + '</i>';
|
|
}
|
|
}
|
|
str += ' ';
|
|
if ((meshrights & 4) != 0) {
|
|
str += '<img src=images/link4.png height=10 width=10 title=\"' + "Edit Intel® AMT credentials" + '\" style=cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>';
|
|
}
|
|
}
|
|
|
|
var meName = '<span title=\"Intel® Manageability Engine\">' + "Intel® ME" + '<span>';
|
|
if (typeof node.intelamt.sku == 'number') {
|
|
if ((node.intelamt.sku & 8) != 0) { meName = '<span title=\"' + "Intel® Active Management Technology" + '\">' + "Intel® AMT" + '<span>'; }
|
|
else if ((node.intelamt.sku & 16) != 0) { meName = '<span title=\"' + "Intel® Standard Manageability" + '\">' + "Intel® SM" + '<span>'; }
|
|
}
|
|
x += addDeviceAttribute(meName, str);
|
|
}
|
|
|
|
if ((node.agent != null) && (node.agent.tag != null)) {
|
|
// Attribute: Mesh Agent Tag
|
|
var tag = EscapeHtml(node.agent.tag);
|
|
if (tag.startsWith('mailto:')) { tag = '<a href="' + tag + '">' + tag.substring(7) + '</a>'; }
|
|
x += addDeviceAttribute("Agent Tag", tag);
|
|
} else if ((node.intelamt != null) && (node.intelamt.tag != null)) {
|
|
// Attribute: Intel AMT Tag
|
|
var tag = EscapeHtml(node.intelamt.tag);
|
|
if (tag.startsWith('mailto:')) { tag = '<a href="' + tag + '">' + tag.substring(7) + '</a>'; }
|
|
x += addDeviceAttribute("Intel® AMT Tag", tag);
|
|
}
|
|
|
|
// Attribute: Intel AMT
|
|
//if (node.intelamt && node.intelamt.user) { x += addDeviceAttribute('Intel® AMT', node.intelamt.user); }
|
|
|
|
// Operating system description
|
|
if (node.osdesc) { x += addDeviceAttribute("Operating System", node.osdesc); }
|
|
|
|
// Antivirus
|
|
if (node.av && node.av.length > 0) {
|
|
var y = [];
|
|
for (var i in node.av) {
|
|
if (node.av[i].product) {
|
|
var avx = EscapeHtml(node.av[i].product);
|
|
if (node.av[i].enabled !== true) { avx += ' - <span style=color:red>' + "Disabled" + '</span>'; }
|
|
if (node.av[i].updated !== true) { avx += ' - <span style=color:red>' + "Out of date" + '</span>'; }
|
|
if ((node.av[i].enabled == true) && (node.av[i].updated == true)) { avx += ' - <span style=color:green>' + "OK" + '</span>'; }
|
|
y.push(avx);
|
|
}
|
|
}
|
|
x += addDeviceAttribute("Antivirus", y.join('<br />'));
|
|
}
|
|
|
|
// Active Users
|
|
if (node.users && node.conn && (node.users.length > 0) && (node.conn & 1)) { x += addDeviceAttribute(format("Active User{0}", ((node.users.length > 1)?'s':'')), node.users.join(', ')); }
|
|
|
|
// Display device user consent
|
|
if (node.agent != null) {
|
|
var meshFeatures = [];
|
|
var consent = 0;
|
|
if (node.consent) { consent = node.consent; }
|
|
if (serverinfo.consent) { consent |= serverinfo.consent; }
|
|
if ((consent & 0x0040) && (consent & 0x0008)) { meshFeatures.push("Desktop Prompt+Toolbar"); } else if (consent & 0x0040) { meshFeatures.push("Desktop Toolbar"); } else if (consent & 0x0008) { meshFeatures.push("Desktop Prompt"); } else { if (consent & 0x0001) { meshFeatures.push("Desktop Notify"); } }
|
|
if (consent & 0x0010) { meshFeatures.push("Terminal Prompt"); } else { if (consent & 0x0002) { meshFeatures.push("Terminal Notify"); } }
|
|
if (consent & 0x0020) { meshFeatures.push("Files Prompt"); } else { if (consent & 0x0004) { meshFeatures.push("Files Notify"); } }
|
|
if (consent == 7) { meshFeatures = ["Always Notify"]; }
|
|
if ((consent & 56) == 56) { meshFeatures = ["Always Prompt"]; }
|
|
|
|
meshFeatures = meshFeatures.join(', ');
|
|
if (meshFeatures == '') { meshFeatures = '<i>' + "None" + '</i>'; }
|
|
x += addDeviceAttribute("User Consent", addLinkConditional(meshFeatures, 'p20editmeshconsent(3)', meshrights & 1));
|
|
}
|
|
|
|
// Attribute: Connectivity (Only show this if more than just the agent is connected).
|
|
var connectivity = node.conn;
|
|
if (connectivity && connectivity > 1) {
|
|
var cstate = [];
|
|
if ((node.conn & 1) != 0) cstate.push('<span title=\"' + "Mesh agent is connected and ready for use." + '\">' + "Mesh Agent" + '</span>');
|
|
if ((node.conn & 2) != 0) cstate.push('<span title=\"' + "Intel® AMT CIRA is connected and ready for use." + '\">' + "Intel® AMT CIRA" + '</span>');
|
|
else if ((node.conn & 4) != 0) cstate.push('<span title=\"' + "Intel® AMT is routable and ready for use." + '\">' + "Intel® AMT" + '</span>');
|
|
if ((node.conn & 8) != 0) cstate.push('<span title=\"' + "Mesh agent is reachable using another agent as relay." + '\">' + "Mesh Relay" + '</span>');
|
|
if ((node.conn & 16) != 0) { cstate.push('<span title=\"' + "MQTT connection to the device is active." + '\">' + "MQTT" + '</span>'); }
|
|
x += addDeviceAttribute("Connectivity", cstate.join(', '));
|
|
}
|
|
|
|
// Node grouping tags
|
|
var groupingTags = '<i>' + "None" + '</i>';
|
|
if (node.tags != null) { groupingTags = ''; for (var i in node.tags) { groupingTags += '<span class="tagSpan">' + node.tags[i] + '</span>'; } }
|
|
if ((meshrights & 4) != 0) {
|
|
x += addDeviceAttribute('Tags', '<span onclick=showEditNodeValueDialog(3) style=cursor:pointer>' + groupingTags + ' <img class=hoverButton src="images/link5.png" /></span>');
|
|
} else {
|
|
x += addDeviceAttribute('Tags', groupingTags);
|
|
}
|
|
|
|
x += '</table><br />';
|
|
// Show action button, only show if we have permissions 4, 8, 64
|
|
if ((meshrights & 76) != 0) { x += '<input type=button value=\"' + "Actions" + '\" title=\"' + "Perform power actions on the device" + '\" onclick=deviceActionFunction() />'; }
|
|
x += '<input type=button value=\"' + "Notes" + '\" title=\"' + "View notes about this device" + '\" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
|
|
x += '<input type=button value=\"' + "Log Event" + '\" title=\"' + "Write an event for this device" + '\" onclick=writeDeviceEvent("' + encodeURIComponent(node._id) + '") />';
|
|
//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() />'; }
|
|
QH('p10html', x);
|
|
|
|
// Show node last 7 days timeline
|
|
masterUpdate(256);
|
|
|
|
// Check if we have terminal and file access
|
|
var desktopAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 65536) == 0));
|
|
var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
|
|
var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
|
|
var amtAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 2048) == 0));
|
|
|
|
// Show bottom buttons
|
|
x = '<div class="p10html3right">';
|
|
if ((meshrights & 4) != 0) {
|
|
// TODO: Show change group only if there is another mesh of the same type.
|
|
x += ' <a href=# onclick=p10showChangeGroupDialog(["' + node._id + '"]) title=\"' + "Move this device to a different device group" + '\">' + "Change Group" + '</a>';
|
|
x += ' <a href=# onclick=p10showDeleteNodeDialog("' + node._id + '") title=\"' + "Remove this device" + '\">' + "Delete Device" + '</a>';
|
|
}
|
|
x += '</div><div class="p10html3left">';
|
|
if (node.agent) x += '<a href=# onclick=p10showNodeNetInfoDialog("' + node._id + '") title=\"' + "Show device network interface information" + '\">' + "Interfaces" + '</a> ';
|
|
{{{StartGeoLocationJS}}}
|
|
if (xxmap != null) x += '<a href=# onclick=p10showNodeLocationDialog("' + node._id + '") title=\"' + "Show device locations information" + '\">' + "Location" + '</a> ';
|
|
{{{EndGeoLocationJS}}}
|
|
if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) { // Check if we should view tools
|
|
if ((terminalAccess) && ((meshrights & 8) != 0) && (node.agent != null)) x += '<a href=# onclick=p10showMeshCmdDialog(1,"' + node._id + '") title=\"' + "Traffic router used to connect to a device thru this server" + '.\">' + "MeshCmd" + '</a> ';
|
|
}
|
|
if ((args.xterm === 0) && (node.agent) && ((node.agent.caps & 2) != 0) && ((meshrights & 8) != 0) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0))) { x += '<a href=# onclick=p10openxterm(event,"' + node._id + '") title=\"' + "Open XTerm terminal" + '\">' + "XTerm" + '</a> '; }
|
|
|
|
// RDP link, show this link only of the remote machine is Windows.
|
|
if (((connectivity & 1) != 0) && (clickOnce == true) && (node.agent) && ((meshrights & 8) != 0)) {
|
|
if ((node.agent.id > 0) && (node.agent.id < 5)) { x += '<a href=# cmenu=altPortContextMenu id=rdpClickOnceLink onclick=p10clickOnce("' + node._id + '","RDP2") title=\"' + "Requires Microsoft ClickOnce support in your browser" + '.\">' + "RDP" + '</a> '; }
|
|
if (node.agent.id > 4) {
|
|
x += '<a href=# onclick=p10clickOnce("' + node._id + '","PSSH",22) title=\"' + "Requires Microsoft ClickOnce support in your browser." + '\">' + "Putty" + '</a> ';
|
|
x += '<a href=# onclick=p10clickOnce("' + node._id + '","WSCP",22) title=\"' + "Requires Microsoft ClickOnce support in your browser." + '\">' + "WinSCP" + '</a> ';
|
|
}
|
|
}
|
|
|
|
// MQTT options
|
|
if ((meshrights == 0xFFFFFFFF) && (features & 0x00400000)) { x += '<a href=# onclick=p10showMqttLoginDialog("' + node._id + '") title=\"' + "Get MQTT login credentials for this device." + '\">' + "MQTT Login" + '</a> '; }
|
|
x += '</div><br>'
|
|
|
|
QH('p10html3', x);
|
|
|
|
// Set the node power state
|
|
var powerstate = PowerStateStr(node.state);
|
|
//if (node.state == 0) { powerstate = 'Unknown State'; }
|
|
if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title=\"' + "Agent connected" + '\">' + "Agent connected" + '</span>'; }
|
|
if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title=\"' + "Intel® AMT connected" + '\">' + "Intel® AMT connected" + '</span>'; }
|
|
else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title=\"' + "Intel® AMT detected" + '\">' + "Intel® AMT detected" + '</span>'; }
|
|
if ((connectivity & 16) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title=\"' + "MQTT connected" + '\">' + "MQTT channel connected" + '</span>'; }
|
|
if ((powerstate == '') && node.lastconnect) { powerstate = '<span style=font-size:12px>' + "Last seen:" + '<br />' + printDateTime(new Date(node.lastconnect)) + '</span>'; }
|
|
QH('MainComputerState', powerstate);
|
|
|
|
// Set the node icon
|
|
Q('MainComputerImage').setAttribute('src', 'images/icons256-' + node.icon + '-1.png');
|
|
Q('MainComputerImage').className = ((!node.conn) || (node.conn == 0)?'gray':'');
|
|
|
|
// Setup/Refresh the desktop tab
|
|
if (terminalAccess) { setupTerminal(); }
|
|
if (fileAccess) { setupFiles(); }
|
|
var consoleRights = ((meshrights & 16) != 0);
|
|
if (consoleRights) { setupConsole(); } else { if (panel == 15) { panel = 10; } }
|
|
|
|
// Show or hide the tabs
|
|
// mesh.mtype: 1 = Intel AMT only, 2 = Mesh Agent
|
|
// node.agent.caps (bitmask): 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console
|
|
QV('MainDevDesktop', desktopAccess && ((((node.agent == null) && ((typeof node.intelamt.sku !== 'number') || ((node.intelamt.sku & 8) != 0)))
|
|
|| ((node.agent != null) && ((node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2)))))
|
|
&& ((meshrights & 8) || (meshrights & 256)))
|
|
);
|
|
QV('MainDevTerminal', (((node.agent == null) && (node.intelamt != null)) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (meshrights & 8) && terminalAccess);
|
|
QV('MainDevFiles', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 4) != 0) && (meshrights & 8) && fileAccess);
|
|
QV('MainDevAmt', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8) && amtAccess);
|
|
QV('MainDevConsole', (consoleRights && ((node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 8) != 0))) && (meshrights & 8));
|
|
QV('MainDevPlugins', false);
|
|
QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0));
|
|
QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
|
|
|
|
// Setup/Refresh Intel AMT tab
|
|
var amtFrameNode = Q('p14iframe').contentWindow.getCurrentMeshNode();
|
|
if ((amtFrameNode != null) && (amtFrameNode._id != currentNode._id)) { Q('p14iframe').contentWindow.disconnect(); }
|
|
var online = ((node.conn & 6) != 0)?true:false; // If CIRA (2) or AMT (4) connected, enable Commander
|
|
Q('p14iframe').contentWindow.setConnectionState(online);
|
|
Q('p14iframe').contentWindow.setFrameHeight('650px');
|
|
Q('p14iframe').contentWindow.setAuthCallback(updateAmtCredentials);
|
|
|
|
// Display "action" button on desktop/terminal/files
|
|
QV('deskActionsBtn', (meshrights & 72) != 0); // 72 = Wake-up + Remote Control permissions
|
|
QV('termActionsBtn', (meshrights & 72) != 0);
|
|
QV('filesActionsBtn', (meshrights & 72) != 0);
|
|
|
|
// Request the power timeline
|
|
if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) {
|
|
QH('p10html2', '');
|
|
powerTimelineReq = currentNode._id;
|
|
meshserver.send({ action: 'powertimeline', nodeid: currentNode._id });
|
|
meshserver.send({ action: 'lastconnect', nodeid: currentNode._id });
|
|
meshserver.send({ action: 'getsysinfo', nodeid: currentNode._id });
|
|
meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id });
|
|
QH('p17info', '');
|
|
}
|
|
|
|
// Reset the desktop tools
|
|
QV('DeskTools', false);
|
|
showDeskToolsProcesses();
|
|
|
|
// Ask for device events
|
|
refreshDeviceEvents();
|
|
|
|
// Update the web page title
|
|
if ((currentNode) && (xxcurrentView >= 10) && (xxcurrentView < 20)) {
|
|
document.title = currentNode.name + (mesh?(' - ' + mesh.name):'') + ' - ' + decodeURIComponent('{{{extitle}}}');
|
|
} else {
|
|
document.title = decodeURIComponent('{{{extitle}}}');
|
|
}
|
|
|
|
// Clear user consent status if present
|
|
p11clearConsoleMsg();
|
|
p12clearConsoleMsg();
|
|
p13clearConsoleMsg();
|
|
|
|
// Device refresh plugin handler
|
|
if (pluginHandler != null) {
|
|
QH('p19headers', ''); QH('p19pages', '');
|
|
pluginHandler.callHook('onDeviceRefreshEnd', nodeid, panel, refresh, event);
|
|
var lastTab = getstore('_curPluginPage', null);
|
|
if (lastTab != null && Q('p19ph-' + lastTab) != null) pluginHandler.callPluginPage(lastTab, Q('p19ph-' + lastTab));
|
|
}
|
|
|
|
// Show user device permissions
|
|
x = '';
|
|
if (meshrights & 7) {
|
|
x += '<a href=# onclick="return p20showAddMeshUserDialog(5)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add User" + '</a>';
|
|
if (usergroups != null) {
|
|
var userGroupCount = 0, newUserGroup = false;
|
|
for (var i in usergroups) { userGroupCount++; if ((currentNode.links == null) || (currentNode.links[i] == null)) { newUserGroup = true; } }
|
|
if ((userGroupCount > 0) && (newUserGroup)) { x += '<a href=# onclick="return p20showAddMeshUserDialog(6)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add User Group" + '</a>'; }
|
|
}
|
|
}
|
|
|
|
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>';
|
|
var count = 1;
|
|
if (currentNode.links != null) {
|
|
// Sort the list of users to display
|
|
var useridlist = [];
|
|
for (var i in currentNode.links) { if (i.startsWith('user/') || i.startsWith('ugrp/')) { useridlist.push(i); } }
|
|
useridlist.sort();
|
|
for (var i in useridlist) {
|
|
var trash = '', rights = '', userid = useridlist[i], srights = currentNode.links[userid].rights, username = EscapeHtml(userid.split('/')[2]), rights = makeUserDeviceRightsString(srights), ugroup = false;
|
|
if ((users != null) && (users[userid] != null)) { username = EscapeHtml(users[userid].name); }
|
|
if ((usergroups != null) && (usergroups[userid] != null)) { username = EscapeHtml(usergroups[userid].name); ugroup = true; }
|
|
if ((meshrights & 2) != 0) {
|
|
if (ugroup) {
|
|
trash = '<a href=# onclick=\'return p30removeUserFromNode(event,"' + encodeURIComponent(userid) + '")\' title=\"' + "Remove user group rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
|
rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(6,\"' + encodeURIComponent(userid) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
|
|
} else {
|
|
trash = '<a href=# onclick=\'return p30removeUserFromNode(event,"' + encodeURIComponent(userid) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
|
rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(5,\"' + encodeURIComponent(userid) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
|
|
}
|
|
}
|
|
if (users != null) {
|
|
if (ugroup) {
|
|
username = '<a href=# onclick=\'gotoUserGroup("' + encodeURIComponent(userid) + '");haltEvent(event);\'>' + username + '</a>';
|
|
} else {
|
|
username = '<a href=# onclick=\'gotoUser("' + encodeURIComponent(userid) + '");haltEvent(event);\'>' + username + '</a>';
|
|
}
|
|
}
|
|
|
|
x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + (ugroup?"User Group":"User") + '\" class=m' + (ugroup?4:2) + '></div><div> ' + username + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
|
|
}
|
|
}
|
|
if (count == 1) { x += '<tr><td><div style=padding:6px> <i>' + "No users with special device permissions" + '</i><div></div></div></td><td></td></tr>'; }
|
|
x += '</tbody></table>';
|
|
QH('p10html4', x);
|
|
|
|
// Change the URL
|
|
var urlviewmode = '';
|
|
if ((xxcurrentView >= 10) && (xxcurrentView <= 19) && (currentNode != null)) {
|
|
urlviewmode = '?viewmode=' + xxcurrentView + '&gotonode=' + currentNode._id.split('/')[2];
|
|
for (var i in urlargs) { urlviewmode += ('&' + i + '=' + urlargs[i]); }
|
|
try { window.history.replaceState({}, document.title, window.location.pathname + urlviewmode); } catch (ex) { }
|
|
}
|
|
}
|
|
setupDesktop(); // Always refresh the desktop, even if we are on the same device, we need to do some canvas switching.
|
|
if (!panel) panel = 10;
|
|
go(panel);
|
|
}
|
|
|
|
function makeUserDeviceRightsString(rights) {
|
|
if (rights == 57592) { return "Full Device Rights"; }
|
|
var str = [];
|
|
if (rights & 8) {
|
|
var str1 = [];
|
|
if (rights & 256) str1.push("No Input");
|
|
if (rights & 512) str1.push("No Terminal");
|
|
if (rights & 1024) str1.push("No Files");
|
|
if (rights & 2048) str1.push("No AMT");
|
|
if (rights & 4096) str1.push("Limited Input");
|
|
if (rights & 65536) str1.push("No Desktop");
|
|
if (str1.length > 0) { str.push('Control (' + str1.join(', ') + ')'); } else { str.push("Control"); }
|
|
}
|
|
if (rights & 16) str.push("Console");
|
|
if (rights & 32) str.push("Server Files");
|
|
if (rights & 64) str.push("Wake");
|
|
if (rights & 128) str.push("Notes");
|
|
if (rights & 8192) str.push("Limit Events");
|
|
if (rights & 16384) str.push("Chat");
|
|
if (rights & 32768) str.push("Uninstall");
|
|
if (str.length == 0) return "No Rights";
|
|
return str.join(', ');
|
|
}
|
|
|
|
function makeDeviceGroupRightsString(rights) {
|
|
if (rights == 0xFFFFFFFF) { return "Full Rights"; }
|
|
var str = [];
|
|
if (rights & 1) str.push("Edit Group");
|
|
if (rights & 2) str.push("Manage Users");
|
|
if (rights & 4) str.push("Manage Devices");
|
|
if (rights & 8) {
|
|
var str1 = [];
|
|
if (rights & 256) str1.push("No Input");
|
|
if (rights & 512) str1.push("No Terminal");
|
|
if (rights & 1024) str1.push("No Files");
|
|
if (rights & 2048) str1.push("No AMT");
|
|
if (rights & 4096) str1.push("Limited Input");
|
|
if (rights & 65536) str1.push("No Desktop");
|
|
if (str1.length > 0) { str.push('Control (' + str1.join(', ') + ')'); } else { str.push("Control"); }
|
|
}
|
|
if (rights & 16) str.push("Console");
|
|
if (rights & 32) str.push("Server Files");
|
|
if (rights & 64) str.push("Wake");
|
|
if (rights & 128) str.push("Notes");
|
|
if (rights & 8192) str.push("Limit Events");
|
|
if (rights & 16384) str.push("Chat");
|
|
if (rights & 32768) str.push("Uninstall");
|
|
if (str.length == 0) return "No Rights";
|
|
return str.join(', ');
|
|
}
|
|
|
|
function writeDeviceEvent(nodeid) {
|
|
if (xxdialogMode) return;
|
|
setDialogMode(2, "Add Device Event", 3, writeDeviceEventEx, '<textarea id=d2devEvent style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea><span style=font-size:10px>' + "This will add an entry to this device\'s event log." + '<span>', nodeid);
|
|
Q('d2devEvent').focus();
|
|
}
|
|
|
|
function writeDeviceEventEx(buttons, tag) { meshserver.send({ action: 'setDeviceEvent', nodeid: decodeURIComponent(tag), msg: encodeURIComponent(Q('d2devEvent').value) }); }
|
|
|
|
function showNotes(readonly, noteid) {
|
|
if (xxdialogMode) return;
|
|
setDialogMode(2, "Notes", 2, showNotesEx, '<textarea id=d2devNotes ro=' + readonly + ' noteid=' + noteid + ' readonly style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea><span style=font-size:10px>' + "Device group notes can be viewed and changed by other device group administrators." + '<span>', noteid);
|
|
meshserver.send({ action: 'getNotes', id: decodeURIComponent(noteid) });
|
|
}
|
|
|
|
function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponent(Q('d2devNotes').value) }); }
|
|
|
|
function deviceChat(e) {
|
|
if (xxdialogMode) return;
|
|
var url = '/messenger?id=meshmessenger/' + encodeURIComponent(currentNode._id) + '/' + encodeURIComponent(userinfo._id) + '&title=' + currentNode.name;
|
|
if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
|
|
if (e && (e.shiftKey == true)) {
|
|
window.open(url, 'meshmessenger:' + currentNode._id);
|
|
} else {
|
|
window.open(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560');
|
|
}
|
|
meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) });
|
|
}
|
|
|
|
function deviceToggleBackground() {
|
|
if (xxdialogMode) return;
|
|
meshserver.send({ action: 'msg', type: 'deskBackground', nodeid: currentNode._id, op: 1 }); // Toggle desktop background image
|
|
}
|
|
|
|
function deviceUrlFunction() {
|
|
if (xxdialogMode) return;
|
|
setDialogMode(2, "Open Page on Device", 3, deviceUrlFunctionEx, '<input id=d2devurl placeholder="http://server.com" style=width:100%;overflow-y:scroll></input>');
|
|
Q('d2devurl').focus();
|
|
}
|
|
|
|
function deviceUrlFunctionEx() {
|
|
meshserver.send({ action: 'msg', type: 'openUrl', nodeid: currentNode._id, url: Q('d2devurl').value });
|
|
}
|
|
|
|
function deviceToastFunction() {
|
|
if (xxdialogMode) return;
|
|
setDialogMode(2, "Device Notification", 3, deviceToastFunctionEx, '<textarea id=d2devToast style=width:100%;height:80px;resize:none;overflow-y:scroll></textarea>');
|
|
Q('d2devToast').focus();
|
|
}
|
|
|
|
function deviceToastFunctionEx() {
|
|
meshserver.send({ action: 'toast', nodeids: [ currentNode._id ], title: 'MeshCentral', msg: Q('d2devToast').value });
|
|
}
|
|
|
|
function deviceActionFunction() {
|
|
if (xxdialogMode) return;
|
|
var rights = GetNodeRights(currentNode);
|
|
var x = "Select an operation to perform on this device." + '<br /><br />';
|
|
var y = '<select id=d2deviceop style=float:right;width:250px>';
|
|
if ((rights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
|
|
if ((rights & 8) != 0) { y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } // Remote control permission
|
|
if ((currentNode.conn & 16) != 0) { y += '<option value=103>' + "Send MQTT Message" + '</option>'; }
|
|
if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { y += '<option value=104>' + "Uninstall Agent" + '</option>'; }
|
|
y += '</select>';
|
|
x += addHtmlValue("Operation", y);
|
|
setDialogMode(2, "Device Action", 3, deviceActionFunctionEx, x);
|
|
}
|
|
|
|
function deviceActionFunctionEx() {
|
|
var op = Q('d2deviceop').value;
|
|
if (op == 100) {
|
|
// Device wake
|
|
meshserver.send({ action: 'wakedevices', nodeids: [currentNode._id] });
|
|
} else if (op == 103) {
|
|
// Send MQTT Message
|
|
p10showSendMqttMsgDialog([currentNode._id]);
|
|
} else if (op == 104) {
|
|
// Uninstall agent
|
|
p10showSendUninstallAgentDialog([currentNode._id]);
|
|
} else {
|
|
// Power operation
|
|
meshserver.send({ action: 'poweraction', nodeids: [ currentNode._id ], actiontype: parseInt(op) });
|
|
}
|
|
}
|
|
|
|
// Called when MeshCommander needs new credentials or updated credentials.
|
|
function updateAmtCredentials(forceDialog) {
|
|
var node = getNodeFromId(currentNode._id);
|
|
if ((forceDialog == true) || (node.intelamt.user == null) || (node.intelamt.user == '')) {
|
|
editDeviceAmtSettings(currentNode._id, updateAmtCredentialsEx);
|
|
} else {
|
|
Q('p14iframe').contentWindow.connectButtonfunctionEx();
|
|
}
|
|
}
|
|
|
|
function updateAmtCredentialsEx(button, tag) {
|
|
Q('p14iframe').contentWindow.connectButtonfunctionEx();
|
|
}
|
|
|
|
// Look to see if we need to update the device timeline
|
|
function updateDeviceTimeline() {
|
|
if ((meshserver.State != 2) || (powerTimelineNode == null) || (powerTimelineUpdate == null) || (currentNode == null)) return;
|
|
if ((powerTimelineNode == powerTimelineReq) && (currentNode._id == powerTimelineNode) && (powerTimelineUpdate < Date.now())) {
|
|
powerTimelineUpdate = null;
|
|
meshserver.send({ action: 'powertimeline', nodeid: currentNode._id });
|
|
meshserver.send({ action: 'lastconnect', nodeid: currentNode._id });
|
|
}
|
|
}
|
|
|
|
// Draw device power bars. The bars are 766px wide.
|
|
function drawDeviceTimeline() {
|
|
if ((currentNode == null) || (xxcurrentView < 10) || (xxcurrentView > 19)) return;
|
|
var timeline = null, now = Date.now();
|
|
if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; }
|
|
|
|
// Calculate when the timeline starts
|
|
var d = new Date();
|
|
d.setHours(0, 0, 0, 0);
|
|
d = new Date(d.getTime() - (1000 * 60 * 60 * 24 * 6));
|
|
var timelineStart = d.getTime();
|
|
|
|
// De-compact the timeline
|
|
var timeline2 = [];
|
|
if (timeline != null && timeline.length > 1) {
|
|
timeline2.push([ 0, timeline[1], timeline[0] ]); // Start, End, Power
|
|
var ct = timeline[1];
|
|
for (var i = 2; i < timeline.length; i += 2) {
|
|
var power = timeline[i], dt = now;
|
|
if (timeline.length > (i + 1)) { dt = timeline[i + 1]; }
|
|
timeline2.push([ ct, ct + dt, power ]); // Start, End, Power
|
|
ct = ct + dt;
|
|
}
|
|
}
|
|
|
|
// Draw the timeline
|
|
var x = '', count = 1, date = new Date();
|
|
var totalWidth = Q('masthead').offsetWidth - (160 + 9 + 9 + 14); // Compute the total width of the power bar
|
|
date.setHours(0, 0, 0, 0);
|
|
for (var i = 0; i < 7; i++) {
|
|
var datavalue = '', start = date.getTime(), end = start + (1000 * 60 * 60 * 24);
|
|
for (var j in timeline2) {
|
|
var block = timeline2[j];
|
|
if (isTimeBlockInside(start, end, block[0], block[1]) == true) {
|
|
var ts = Math.max(start, block[0]);
|
|
var te = Math.min(Math.min(end, block[1]), now);
|
|
var width = Math.round(((te - ts) * totalWidth) / 86400000);
|
|
if (width > 0) {
|
|
var title = format('{0} from {1} to {2}.', powerStateStrings2[block[2]], printTime(new Date(ts)), printTime(new Date(te)));
|
|
datavalue += '<div class="pwState ' + powerColor(block[2]) + '" title="' + title + '" style="width:' + width + 'px;"></div>';
|
|
}
|
|
}
|
|
}
|
|
x += '<tr class=' + (((count % 2) == 0)?'altBack':'') + '><td><div> ' + printDate(date) + '<div></div></div></td><td><div>' + datavalue + '</div></td></tr>';
|
|
++count;
|
|
date = new Date(date.getTime() - (1000 * 60 * 60 * 24)); // Substract one day
|
|
}
|
|
|
|
QH('p10html2', '<table cellpadding=2 cellspacing=0><thead><tr style=><th scope=col style=text-align:center;width:150px>' + "Day" + '</th><th scope=col style=text-align:center><a download href="devicepowerevents.ashx?id=' + currentNode._id + (urlargs.key?('&key=' + urlargs.key):'') + '" onclick="setDialogMode(0)"><img title=\"' + "Download power events" + '\" src="images/link4.png" /></a>' + "7 Day Power State" + '</th></tr></thead><tbody>' + x + '</tbody></table>');
|
|
}
|
|
|
|
// Return a color for the given power state
|
|
function powerColor(x) { if (x < powerColorTable.length) { return powerColorTable[x]; } return 'pwsYellow'; }
|
|
|
|
// Return true if the time block is visible within the start/end period
|
|
function isTimeBlockInside(start, end, blockStart, blockEnd) {
|
|
if ((blockStart < start) && (blockEnd > end)) return true; // Block is wider than timespan
|
|
if ((blockStart > start) && (blockStart < end)) return true;
|
|
if ((blockEnd > start) && (blockEnd < end)) return true;
|
|
return false;
|
|
}
|
|
|
|
function addDeviceAttribute(name, value) { return '<tr><td class=style7>' + name + '</td><td class=style9>' + value + '</td></tr>'; }
|
|
|
|
function editDeviceAmtSettings(nodeid, func, arg) {
|
|
if (xxdialogMode) return;
|
|
var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = GetNodeRights(node);
|
|
if ((meshrights & 4) == 0) return;
|
|
x += addHtmlValue("Username", '<input id=dp10username style=width:230px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
|
|
x += addHtmlValue("Password", '<input id=dp10password type=password style=width:230px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
|
|
x += addHtmlValue("Security", '<select id=dp10tls style=width:236px><option value=0>' + "No TLS security" + '</option><option value=1>' + "TLS security required" + '</option></select>');
|
|
if ((node.intelamt.user != null) && (node.intelamt.user != '')) { buttons = 7; }
|
|
setDialogMode(2, "Edit Intel® AMT credentials", buttons, editDeviceAmtSettingsEx, x, { node: node, func: func, arg: arg });
|
|
if ((node.intelamt.user != null) && (node.intelamt.user != '')) { Q('dp10username').value = node.intelamt.user; } else { Q('dp10username').value = 'admin'; }
|
|
Q('dp10tls').value = node.intelamt.tls;
|
|
validateDeviceAmtSettings();
|
|
}
|
|
|
|
function validateDeviceAmtSettings() {
|
|
QE('idx_dlgOkButton', passwordcheck(Q('dp10password').value));
|
|
}
|
|
|
|
function editDeviceAmtSettingsEx(button, tag) {
|
|
if (button == 2) {
|
|
// Delete button pressed, remove credentials
|
|
meshserver.send({ action: 'changedevice', nodeid: tag.node._id, intelamt: { user: '', pass: '' } });
|
|
} else {
|
|
// Change Intel AMT credentials
|
|
var amtuser = Q('dp10username').value;
|
|
if (amtuser == '') amtuser = 'admin';
|
|
var amtpass = Q('dp10password').value;
|
|
if (amtpass == '') amtuser = '';
|
|
meshserver.send({ action: 'changedevice', nodeid: tag.node._id, intelamt: { user: amtuser, pass: amtpass, tls: parseInt(Q('dp10tls').value) } });
|
|
tag.node.intelamt.user = amtuser;
|
|
tag.node.intelamt.tls = parseInt(Q('dp10tls').value);
|
|
if (tag.func) { setTimeout(function () { tag.func(null, tag.arg); }, 300); }
|
|
}
|
|
}
|
|
|
|
function p10showSendMqttMsgDialog(nodeids) {
|
|
if (xxdialogMode) return false;
|
|
var x = addHtmlValue("Topic", '<input id=dp2topic style=width:230px maxlength=64 onchange=p10validateSendMqttMsgDialog() onkeyup=p10validateSendMqttMsgDialog(event,1) />');
|
|
x += addHtmlValue("Message", '<div style=width:230px;margin:0;padding:0><textarea id=dp2msg maxlength=4096 style=width:100%;height:150px;resize:none onchange=p10validateSendMqttMsgDialog() onkeyup=p10validateSendMqttMsgDialog(event,1)></textarea></div>');
|
|
setDialogMode(2, "Send MQTT message", 3, p10showSendMqttMsgDialogEx, x, nodeids);
|
|
p10validateSendMqttMsgDialog();
|
|
Q('dp2topic').focus();
|
|
return false;
|
|
}
|
|
|
|
function p10validateSendMqttMsgDialog() {
|
|
QE('idx_dlgOkButton', (Q('dp2topic').value.length > 0) && (Q('dp2msg').value.length > 0));
|
|
}
|
|
|
|
function p10showSendMqttMsgDialogEx(b, nodeids) {
|
|
meshserver.send({ action: 'sendmqttmsg', nodeids: nodeids, topic: Q('dp2topic').value, msg: Q('dp2msg').value });
|
|
}
|
|
|
|
function p10showSendUninstallAgentDialog(nodeids) {
|
|
if (xxdialogMode) return false;
|
|
var x = '';
|
|
if (nodeids.length > 1) { x = format("Are you sure you want to uninstall the selected {0} agents?", nodeids.length); } else { x = "Are you sure you want to uninstall selected agent?"; }
|
|
x += '<br /><br />';
|
|
if (nodeids.length > 1) { x += "This will not remove the devices from the server, but the devices will not longer be able to connect to the server. All remote access to the devices will be lost. The devices must be connected for this command to work."; } else { x += "This will not remove this device from the server, but the device will not longer be able to connect to the server. All remote access to the device will be lost. The device must be connect for this command to work."; }
|
|
x += '<br /><br /><label style=color:red><input id=p10check type=checkbox onchange=p10validateDeleteNodeDialog() />' + "Confirm" + '</label>';
|
|
setDialogMode(2, "Uninstall agent", 3, p10showSendUninstallAgentDialogEx, x, nodeids);
|
|
p10validateSendUninstallAgentDialog();
|
|
return false;
|
|
}
|
|
|
|
function p10validateSendUninstallAgentDialog() { QE('idx_dlgOkButton', Q('p10check').checked); }
|
|
function p10showSendUninstallAgentDialogEx(b, nodeids) { meshserver.send({ action: 'uninstallagent', nodeids: nodeids }); }
|
|
|
|
function p10showChangeGroupDialog(nodeids) {
|
|
if (xxdialogMode) return false;
|
|
var targetMeshId = null;
|
|
if (nodeids.length == 1) { try { targetMeshId = meshes[getNodeFromId(nodeids[0])]._id; } catch (ex) { } }
|
|
|
|
// List all available alternative groups
|
|
var y = '<select id=p10newGroup style=width:236px>', count = 0;
|
|
for (var i in meshes) {
|
|
var meshrights = GetMeshRights(i);
|
|
if ((meshes[i]._id != targetMeshId) && (meshrights & 4)) { count++; y += '<option value=\'' + meshes[i]._id + '\'>' + meshes[i].name + '</option>'; }
|
|
}
|
|
y += '</select>';
|
|
|
|
if (count > 0) {
|
|
var x = (nodeids.length == 1) ? ("Select a new group for this device" + '<br /><br />') : ("Select a new group for selected devices" + '<br /><br />');
|
|
x += addHtmlValue("New Device Group", y);
|
|
setDialogMode(2, "Change Group", 3, p10showChangeGroupDialogEx, x, nodeids);
|
|
} else {
|
|
setDialogMode(2, "Change Group", 1, null, "No other device group of same type exists.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function p10showChangeGroupDialogEx(b, nodeids) {
|
|
meshserver.send({ action: 'changeDeviceMesh', nodeids: nodeids, meshid: Q('p10newGroup').value });
|
|
}
|
|
|
|
function p10showDeleteNodeDialog(nodeid) {
|
|
if (xxdialogMode) return false;
|
|
var x = format("Are you sure you want to delete node {0}?", EscapeHtml(currentNode.name)) + '<br /><br /><label><input id=p10check type=checkbox onchange=p10validateDeleteNodeDialog() />' + "Confirm" + '</label>';
|
|
setDialogMode(2, "Delete Node", 3, p10showDeleteNodeDialogEx, x, nodeid);
|
|
p10validateDeleteNodeDialog();
|
|
return false;
|
|
}
|
|
|
|
function p10validateDeleteNodeDialog() {
|
|
QE('idx_dlgOkButton', Q('p10check').checked);
|
|
}
|
|
|
|
function p10showDeleteNodeDialogEx(buttons, nodeid) {
|
|
meshserver.send({ action: 'removedevices', nodeids: [ nodeid ] });
|
|
}
|
|
|
|
function p10clickOnce(nodeid, protocol, port) {
|
|
if ((protocol == 'RDP2') && (port == null)) { if (currentNode.rdpport != null) { port = currentNode.rdpport; } else { port = 3389; } }
|
|
meshserver.send({ action: 'getcookie', nodeid: nodeid, tcpport: port, tag: 'clickonce', protocol: protocol });
|
|
return false;
|
|
}
|
|
|
|
// Show current location
|
|
var d2map = null;
|
|
function p10showNodeLocationDialog() {
|
|
if ((xxdialogMode != null) && (xxdialogTag == '@xxmap')) { setDialogMode(0); } else { if (xxdialogMode) return false; }
|
|
var markers = [], types = ['iploc', 'wifiloc', 'gpsloc', 'userloc'], boundingBox = null;
|
|
|
|
for (var loctype in types) {
|
|
if (currentNode[types[loctype]] != null) {
|
|
var loc = currentNode[types[loctype]].split(','), lat = parseFloat(loc[0]), lon = parseFloat(loc[1]);
|
|
if ((lat < 90) && (lat > -90) && (lon < 180) && (lon > -180)) { // Check valid lat/lon
|
|
var deviceMark = new ol.Feature({ geometry: new ol.geom.Point(ol.proj.fromLonLat([lon, lat])) });
|
|
deviceMark.setStyle(markerStyle(currentNode, parseInt(loctype) + 1));
|
|
markers.push(deviceMark);
|
|
|
|
if (boundingBox == null) { boundingBox = [ lat, lon, lat, lon, 0 ]; } else { if (lat < boundingBox[0]) { boundingBox[0] = lat; } if (lon < boundingBox[1]) { boundingBox[1] = lon; } if (lat > boundingBox[2]) { boundingBox[2] = lat; } if (lon > boundingBox[3]) { boundingBox[3] = lon; } }
|
|
}
|
|
}
|
|
}
|
|
|
|
// Setup the device mark layer
|
|
var vectorSource = new ol.source.Vector({ features: markers });
|
|
var vectorLayer = new ol.layer.Vector({ source: vectorSource });
|
|
|
|
//var x = '<div><a href="https://www.google.com/maps/preview/@' + lat + ',' + lng + ',12z" rel="noreferrer noopener" target=_blank>Open in Google maps</a></div>';
|
|
var x = '<div id=d2map style=width:100%;height:300px></div>';
|
|
setDialogMode(2, "Device Location", 1, null, x, '@xxmap');
|
|
|
|
var clng = 0, clat = 0, zoom = 8;
|
|
if (boundingBox != null) {
|
|
var clat = (boundingBox[0] + boundingBox[2]) / 2;
|
|
var clng = (boundingBox[1] + boundingBox[3]) / 2;
|
|
var cscale = Math.max(Math.abs(boundingBox[0] - boundingBox[2]), Math.abs(boundingBox[1] - boundingBox[3]));
|
|
var i = 360, zoom = -2;
|
|
while (i > cscale) { zoom++; i = i / 2; }
|
|
}
|
|
|
|
if (markers.length == 1) { zoom = 8; }
|
|
|
|
// Setup the map
|
|
d2map = new ol.Map({
|
|
target: 'd2map',
|
|
interactions: ol.interaction.defaults({dragPan:false, mouseWheelZoom:false}),
|
|
layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }), vectorLayer ],
|
|
view: new ol.View({ center: ol.proj.fromLonLat([clng, clat]), zoom: zoom })
|
|
});
|
|
return false;
|
|
}
|
|
|
|
// Show network interfaces
|
|
function p10showNodeNetInfoDialog() {
|
|
if (xxdialogMode) return false;
|
|
setDialogMode(2, "Network Interfaces", 1, null, '<div id=d2netinfo>' + "Loading..." + '</div>', 'if' + currentNode._id );
|
|
meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id });
|
|
return false;
|
|
}
|
|
|
|
// Show MeshCentral Router dialog
|
|
function p10showMeshRouterDialog() {
|
|
if (xxdialogMode) return;
|
|
var x = '<div>' + "MeshCentral Router is a Windows tool for TCP port mapping. You can, for example, RDP into a remote device thru this server." + '</div><br />';
|
|
x += addHtmlValue("Win32 Executable", '<a style=cursor:pointer download href="meshagents?meshaction=winrouter' + (urlargs.key?('&key=' + urlargs.key):'') + '" onclick="setDialogMode(0)">MeshCentralRouter.exe</a>');
|
|
setDialogMode(2, "MeshCentral Router", 1, null, x, 'fileDownload');
|
|
}
|
|
|
|
// Request MQTT login credentials
|
|
function p10showMqttLoginDialog(nodeid) { meshserver.send({ action: 'getmqttlogin', nodeid: nodeid }); }
|
|
|
|
// Open XTerm
|
|
function p10openxterm(e, nodeid) {
|
|
haltEvent(e);
|
|
var url = '/xterm?nodeid=' + encodeURIComponent(nodeid) + '&auto=1';
|
|
var node = getNodeFromId(nodeid);
|
|
if (node == null) return;
|
|
if ([1, 2, 3, 4, 21, 22].indexOf(node.agent.id) >= 0) { url += '&os=win'; } else { url += '&os=linux'; }
|
|
window.open(url, 'xterm:' + nodeid);
|
|
return false;
|
|
}
|
|
|
|
// Show MeshCmd dialog
|
|
function p10showMeshCmdDialog(mode, nodeid) {
|
|
if (xxdialogMode) return;
|
|
var y = '<select id=aginsSelect onclick=meshCmdOsClick() style=width:236px>';
|
|
y += '<option value=3>' + "Windows (32bit)" + '</option>';
|
|
y += '<option value=4>' + "Windows (64bit)" + '</option>';
|
|
y += '<option value=5>' + "Linux x86 (32bit)" + '</option>';
|
|
y += '<option value=6>' + "Linux x86 (64bit)" + '</option>';
|
|
y += '<option value=16>' + "MacOS (64bit)" + '</option>';
|
|
y += '<option value=25>' + "Linux ARM, Raspberry Pi (32bit)" + '</option>';
|
|
y += '</select>';
|
|
|
|
var x = '';
|
|
if (mode == 0) { x += '<div>' + "MeshCmd is a command line tool that performs lots of different operations. The action file can optionally be downloaded and edited to provide server information and credentials." + '<br /><br />'; }
|
|
if (mode == 1) { x += '<div>' + "Download \"meshcmd\" with an action file to route traffic thru this server to this device. Make sure to edit meshaction.txt and add your account password or make any changes needed." + '<br /><br />'; }
|
|
x += addHtmlValue("Operating System", y);
|
|
x += addHtmlValue("MeshCmd", '<a id=meshcmddownloadid href="meshagents?meshcmd=3' + (urlargs.key?('&key=' + urlargs.key):'') + '" download></a>');
|
|
if (mode == 0) { x += addHtmlValue("Action File", '<a href="meshagents?meshaction=generic' + (urlargs.key?('&key=' + urlargs.key):'') + '" download>' + "MeshAction (.txt)" + '</a>'); }
|
|
if (mode == 1) { x += addHtmlValue("Action File", '<a href="meshagents?meshaction=route&nodeid=' + nodeid + (urlargs.key?('&key=' + urlargs.key):'') + '" download>' + "MeshAction (.txt)" + '</a>'); }
|
|
x += '</div>';
|
|
setDialogMode(2, "Download MeshCmd", 9, null, x, 'fileDownload');
|
|
meshCmdOsClick();
|
|
}
|
|
|
|
function meshCmdOsClick() {
|
|
var os = Q('aginsSelect').value, osn = '', osurl = '';
|
|
//Q('meshcmddownloadid').href = 'meshagents?meshcmd=' + os + (urlargs.key?('&key=' + urlargs.key):'');
|
|
if (os == 3) { osn = "MeshCmd (Win32 executable)"; }
|
|
if (os == 4) { osn = "MeshCmd (Win64 executable)"; }
|
|
if (os == 5) { osn = "MeshCmd (Linux x86, 32bit)"; }
|
|
if (os == 6) { osn = "MeshCmd (Linux x86, 64bit)"; }
|
|
if (os == 16) { osn = "MeshCmd (MacOS, 64bit)"; }
|
|
if (os == 25) { osn = "MeshCmd (Linux ARM, 32bit)"; }
|
|
QH('meshcmddownloadid', osn);
|
|
Q('meshcmddownloadid').setAttribute('href', 'meshagents?meshcmd=' + os + (urlargs.key?('&key=' + urlargs.key):''));
|
|
}
|
|
|
|
function p10showiconselector() {
|
|
if (xxdialogMode) return;
|
|
if ((GetNodeRights(currentNode) & 4) == 0) return;
|
|
|
|
var x = '<div style=text-align:center><br />';
|
|
x += '<div tabindex=0 style="display:inline-block;margin:0 10px 0 10px" class=i1 onclick=p10setIcon(1) onkeypress="if (event.key==\'Enter\') p10setIcon(1)"></div>';
|
|
x += '<div tabindex=0 style="display:inline-block;margin:0 10px 0 10px" class=i2 onclick=p10setIcon(2) onkeypress="if (event.key==\'Enter\') p10setIcon(2)"></div>';
|
|
x += '<div tabindex=0 style="display:inline-block;margin:0 10px 0 10px" class=i3 onclick=p10setIcon(3) onkeypress="if (event.key==\'Enter\') p10setIcon(3)"></div>';
|
|
x += '<div tabindex=0 style="display:inline-block;margin:0 10px 0 10px" class=i4 onclick=p10setIcon(4) onkeypress="if (event.key==\'Enter\') p10setIcon(4)"></div>';
|
|
x += '<br />';
|
|
x += '<div tabindex=0 style="display:inline-block;margin:0 10px 0 10px" class=i5 onclick=p10setIcon(5) onkeypress="if (event.key==\'Enter\') p10setIcon(5)"></div>';
|
|
x += '<div tabindex=0 style="display:inline-block;margin:0 10px 0 10px" class=i6 onclick=p10setIcon(6) onkeypress="if (event.key==\'Enter\') p10setIcon(6)"></div>';
|
|
x += '<div tabindex=0 style="display:inline-block;margin:0 10px 0 10px" class=i7 onclick=p10setIcon(7) onkeypress="if (event.key==\'Enter\') p10setIcon(7)"></div>';
|
|
x += '<div tabindex=0 style="display:inline-block;margin:0 10px 0 10px" class=i8 onclick=p10setIcon(8) onkeypress="if (event.key==\'Enter\') p10setIcon(8)"></div>';
|
|
x += '<br /><br /></div>';
|
|
setDialogMode(2, "Icon Selection", 0, null, x);
|
|
QV('id_dialogclose', true);
|
|
}
|
|
|
|
function p10setIcon(icon) {
|
|
setDialogMode(0);
|
|
meshserver.send({ action: 'changedevice', nodeid: currentNode._id, icon: icon });
|
|
}
|
|
|
|
var showEditNodeValueDialog_modes = ["Device Name", "Hostname", "Description", "Tags"];
|
|
var showEditNodeValueDialog_modes2 = ['name', 'host', 'desc', 'tags'];
|
|
var showEditNodeValueDialog_modes3 = ['', '', '', "Tag1, Tag2, Tag3"];
|
|
function showEditNodeValueDialog(mode) {
|
|
if (xxdialogMode) return;
|
|
var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue maxlength=64 placeholder="' + showEditNodeValueDialog_modes3[mode] + '" onchange=p10editdevicevalueValidate(' + mode + ',event) onkeyup=p10editdevicevalueValidate(' + mode + ',event) />');
|
|
setDialogMode(2, "Edit Device", 3, showEditNodeValueDialogEx, x, mode);
|
|
var v = currentNode[showEditNodeValueDialog_modes2[mode]];
|
|
if (v == null) v = '';
|
|
if (Array.isArray(v)) { v = v.join(', '); }
|
|
Q('dp10devicevalue').value = v;
|
|
p10editdevicevalueValidate();
|
|
Q('dp10devicevalue').focus();
|
|
}
|
|
|
|
function showEditNodeValueDialogEx(button, mode) {
|
|
var x = { action: 'changedevice', nodeid: currentNode._id };
|
|
x[showEditNodeValueDialog_modes2[mode]] = Q('dp10devicevalue').value;
|
|
meshserver.send(x);
|
|
}
|
|
|
|
function p10editdevicevalueValidate(mode, e) {
|
|
var x = ((mode > 1) || (Q('dp10devicevalue').value.length > 0));
|
|
QE('idx_dlgOkButton', x);
|
|
if ((e != null) && (x == true) && (e.keyCode == 13)) { dialogclose(1); }
|
|
}
|
|
|
|
//
|
|
// DESKTOP
|
|
//
|
|
|
|
var desktopNode;
|
|
function setupDesktop() {
|
|
// Setup the remote desktop
|
|
if ((desktopNode != currentNode) && (desktop != null)) { desktop.Stop(); desktopNode = null; desktop = null; }
|
|
|
|
// If the device desktop is already connected in multi-desktop, use that.
|
|
if ((desktopNode != currentNode) || (desktop == null)) {
|
|
var xdesk = multiDesktop[currentNode._id];
|
|
if (xdesk != null) {
|
|
// This device already has a canvas, use it.
|
|
QH('DeskParent', '');
|
|
var c = xdesk.m.CanvasId;
|
|
c.setAttribute('id', 'Desk');
|
|
c.setAttribute('onmousedown', 'dmousedown(event)');
|
|
c.setAttribute('onmouseup', 'dmouseup(event)');
|
|
c.setAttribute('onmousemove', 'dmousemove(event)');
|
|
c.removeAttribute('onclick');
|
|
Q('DeskParent').appendChild(c);
|
|
desktop = xdesk;
|
|
if (desktop.m.SendCompressionLevel) { desktop.m.SendCompressionLevel(1, desktopsettings.quality, desktopsettings.scaling, desktopsettings.framerate); }
|
|
desktop.onStateChanged = onDesktopStateChange;
|
|
desktopNode = currentNode;
|
|
onDesktopStateChange(desktop, desktop.State);
|
|
delete multiDesktop[currentNode._id];
|
|
} else {
|
|
// Device is not already connected, just setup a blank canvas
|
|
QH('DeskParent', '<canvas id=Desk oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></canvas>');
|
|
desktopNode = currentNode;
|
|
}
|
|
// Setup the mouse wheel
|
|
Q('Desk').addEventListener('DOMMouseScroll', function (e) { return dmousewheel(e); });
|
|
Q('Desk').addEventListener('mousewheel', function (e) { return dmousewheel(e); });
|
|
}
|
|
desktopNode = currentNode;
|
|
updateDesktopButtons();
|
|
deskAdjust();
|
|
|
|
// On some browsers like IE, we can't save screen shots. Hide the scheenshot/capture buttons.
|
|
if (!Q('Desk')['toBlob']) { QV('deskSaveBtn', false); }
|
|
}
|
|
|
|
// Show and enable the right buttons
|
|
function updateDesktopButtons() {
|
|
var mesh = meshes[currentNode.meshid];
|
|
var deskState = 0;
|
|
if (desktop != null) { deskState = desktop.State; }
|
|
var rights = GetNodeRights(currentNode);
|
|
|
|
// Show the right buttons
|
|
QV('disconnectbutton1span', (deskState != 0));
|
|
QV('connectbutton1span', (deskState == 0) && ((rights & 8) || (rights & 256)) && (currentNode.agent != null) && (currentNode.agent.caps & 1));
|
|
QV('connectbutton1hspan',
|
|
(deskState == 0) &&
|
|
(rights & 8) &&
|
|
(
|
|
((currentNode.intelamt != null) &&
|
|
(currentNode.intelamt.state == 2) &&
|
|
(currentNode.intelamt.ver != null) &&
|
|
((currentNode.intelamt.sku == null) ||
|
|
((typeof currentNode.intelamt.sku == 'number') &&
|
|
((currentNode.intelamt.sku & 8) != 0))))
|
|
)
|
|
);
|
|
|
|
// Show the right settings
|
|
QV('d7amtkvm', (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (currentNode.agent == null))) && ((deskState == 0) || (desktop.contype == 2)));
|
|
QV('d7meshkvm', (webRtcDesktop) || ((currentNode.agent != null) && (currentNode.agent.caps & 1) && ((deskState == false) || (desktop.contype == 1))));
|
|
|
|
// Enable buttons
|
|
var inputAllowed = (rights == 0xFFFFFFFF) || (((rights & 8) != 0) && ((rights & 256) == 0) && ((rights & 4096) == 0));
|
|
var online = ((currentNode.conn & 1) != 0); // If Agent (1) connected, enable remote desktop
|
|
QE('connectbutton1', online);
|
|
var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
|
|
QE('connectbutton1h', hwonline);
|
|
QE('deskSaveBtn', deskState == 3);
|
|
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (deskState != 0) && (desktopsettings.showfocus));
|
|
QV('DeskClip', (currentNode.agent) && (currentNode.agent.id != 11) && (currentNode.agent.id != 16) && ((desktop == null) || (desktop.contype != 2))); // Clipboard not supported on MacOS
|
|
QE('DeskClip', deskState == 3);
|
|
QE('DeskType', deskState == 3);
|
|
QV('DeskWD', inputAllowed);
|
|
QE('DeskWD', deskState == 3);
|
|
QV('deskkeys', inputAllowed);
|
|
QE('deskkeys', deskState == 3);
|
|
|
|
// Display this only if we have Chat & Notify permissions
|
|
QV('DeskChatButton', ((rights & 16384) != 0) && (browserfullscreen == false) && (inputAllowed) && (currentNode.agent) && online);
|
|
QV('DeskNotifyButton', ((rights & 16384) != 0) && (browserfullscreen == false) && (currentNode.agent) && (currentNode.agent.id < 5) && (inputAllowed) && (currentNode.agent) && online);
|
|
|
|
QV('DeskToolsButton', (inputAllowed) && (currentNode.agent) && online);
|
|
QV('DeskOpenWebButton', (browserfullscreen == false) && (inputAllowed) && (currentNode.agent) && online);
|
|
QV('DeskBackgroundButton', (deskState == 3) && (desktop.contype == 1) && (currentNode.agent) && (currentNode.agent.id != 11) && (currentNode.agent.id != 16) && online);
|
|
QV('DeskControlSpan', inputAllowed)
|
|
QV('deskActionsBtn', (browserfullscreen == false));
|
|
QV('deskActionsSettings', (browserfullscreen == false));
|
|
if (rights & 8) { Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); } else { Q('DeskControl').checked = false; }
|
|
QS('DeskControlSpan').color = Q('DeskControl').checked?null:'red';
|
|
|
|
if (online == false) QV('DeskTools', false);
|
|
}
|
|
|
|
// Debug
|
|
var autoConnectDesktopTimer = null;
|
|
function autoConnectDesktop(e) { if (autoConnectDesktopTimer == null) { autoConnectDesktopTimer = setInterval(function() { connectDesktop(null, 1) }, 1000); } else { clearInterval(autoConnectDesktopTimer); autoConnectDesktopTimer = null; } }
|
|
|
|
function connectDesktop(e, contype, tsid, consent) {
|
|
if (xxdialogMode) return;
|
|
if ((e != null) && (e.shiftKey != false) && (contype == 3)) { contype = 1; } // If the shift key is not pressed, don't try to ask for session list.
|
|
QV('p11DeskSessionSelector', false);
|
|
p11clearConsoleMsg();
|
|
if (desktop == null) {
|
|
desktopNode = currentNode;
|
|
if (contype == 2) {
|
|
// Setup the Intel AMT remote desktop
|
|
if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop, 2); return; }
|
|
desktop = CreateAmtRedirect(CreateAmtRemoteDesktop('Desk'), authCookie);
|
|
desktop.debugmode = debugmode;
|
|
desktop.onStateChanged = onDesktopStateChange;
|
|
desktop.m.bpp = (desktopsettings.encoding == 1 || desktopsettings.encoding == 3) ? 1 : 2;
|
|
desktop.m.useZRLE = (desktopsettings.encoding < 3);
|
|
desktop.m.localKeyMap = desktopsettings.localkeymap;
|
|
desktop.m.showmouse = desktopsettings.showmouse;
|
|
desktop.m.onScreenSizeChange = deskAdjust;
|
|
desktop.m.onKvmData = function (x) {
|
|
//console.log('onKvmData (' + x.length + '): ' + x);
|
|
// Send the presense probe only once if needed.
|
|
if (x.length == 0) { if (!desktop.m._sentPresence) { desktop.m._sentPresence = true; desktop.m.sendKvmData(JSON.stringify({ action: 'present', ver: 1 })); } return; }
|
|
var data = null;
|
|
try { data = JSON.parse(x); } catch (e) { }
|
|
if ((data != null) && (data.action != null)) {
|
|
if (data.action == 'restart') {
|
|
// Clear WebRTC channel
|
|
webRtcDesktopReset();
|
|
desktop.m.sendKvmData(JSON.stringify({ action: 'present', ver: 1 }));
|
|
} else if ((data.action == 'present') && (webRtcDesktop == null)) {
|
|
// Setup WebRTC channel
|
|
webRtcDesktop = { platform: data.platform };
|
|
var configuration = null; //{ "iceServers": [ { 'urls': 'stun:stun.services.mozilla.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ] };
|
|
if (typeof RTCPeerConnection !== 'undefined') { webRtcDesktop.webrtc = new RTCPeerConnection(configuration); }
|
|
else if (typeof webkitRTCPeerConnection !== 'undefined') { webRtcDesktop.webrtc = new webkitRTCPeerConnection(configuration); }
|
|
|
|
webRtcDesktop.webchannel = webRtcDesktop.webrtc.createDataChannel("DataChannel", {}); // { ordered: false, maxRetransmits: 2 }
|
|
webRtcDesktop.webchannel.onopen = function () {
|
|
// Switch to software KVM
|
|
//if (urlvars && urlvars['kvmdatatrace']) { console.log('WebRTC Data Channel Open'); }
|
|
console.log('WebRTC Data Channel Open');
|
|
Q('deskstatus').textContent = StatusStrs[desktop.State] + ", Soft-KVM";
|
|
desktop.m.hold(true);
|
|
webRtcDesktop.webRtcActive = true;
|
|
webRtcDesktop.softdesktop = CreateKvmDataChannel(webRtcDesktop.webchannel, CreateAgentRemoteDesktop('Desk', Q('id_mainarea')), desktop.m);
|
|
webRtcDesktop.softdesktop.m.setRotation(desktop.m.rotation);
|
|
webRtcDesktop.softdesktop.m.onScreenSizeChange = deskAdjust;
|
|
if (desktopsettings.quality) { webRtcDesktop.softdesktop.m.CompressionLevel = desktopsettings.quality; } // Number from 1 to 100. 50 or less is best.
|
|
if (desktopsettings.scaling) { webRtcDesktop.softdesktop.m.ScalingLevel = desktopsettings.scaling; }
|
|
webRtcDesktop.softdesktop.Start();
|
|
|
|
// Check if we can get remote file access
|
|
// ###BEGIN###{DesktopInbandFiles}
|
|
/*
|
|
QV('go24', true); // Files
|
|
downloadFile = null;
|
|
p24files = webRtcDesktop.softdesktop;
|
|
p24targetpath = '';
|
|
webRtcDesktop.softdesktop.onControlMsg = onFilesControlData;
|
|
webRtcDesktop.softdesktop.sendCtrlMsg(JSON.stringify({ action: 'ls', reqid: 1, path: '' })); // Ask for the root folder
|
|
*/
|
|
// ###END###{DesktopInbandFiles}
|
|
}
|
|
webRtcDesktop.webchannel.onclose = function (event) {
|
|
//if (urlvars['kvmdatatrace']) { console.log('WebRTC Data Channel Closed'); }
|
|
console.log('WebRTC Data Channel Closed');
|
|
webRtcDesktopReset();
|
|
}
|
|
webRtcDesktop.webrtc.onicecandidate = function (e) {
|
|
if (e.candidate == null) {
|
|
desktop.m.sendKvmData(JSON.stringify({ action: 'offer', ver: 1, sdp: webRtcDesktop.webrtcoffer.sdp }));
|
|
} else {
|
|
webRtcDesktop.webrtcoffer.sdp += ('a=' + e.candidate.candidate + '\r\n'); // New candidate, add it to the SDP
|
|
}
|
|
}
|
|
webRtcDesktop.webrtc.oniceconnectionstatechange = function () {
|
|
if ((webRtcDesktop != null) && (webRtcDesktop.webrtc != null) && ((webRtcDesktop.webrtc.iceConnectionState == 'disconnected') || (webRtcDesktop.webrtc.iceConnectionState == 'failed'))) { /*console.log('WebRTC ICE Failed');*/ webRtcDesktopReset(); }
|
|
}
|
|
webRtcDesktop.webrtc.createOffer(function (offer) {
|
|
// Got the offer
|
|
webRtcDesktop.webrtcoffer = offer;
|
|
webRtcDesktop.webrtc.setLocalDescription(offer, function () { }, webRtcDesktopReset);
|
|
}, webRtcDesktopReset, { mandatory: { OfferToReceiveAudio: false, OfferToReceiveVideo: false } });
|
|
} else if ((data.action == 'answer') && (webRtcDesktop != null)) {
|
|
// Complete the WebRTC channel
|
|
webRtcDesktop.webrtc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: data.sdp }), function () { }, webRtcDesktopReset);
|
|
}
|
|
}
|
|
};
|
|
desktop.Start(desktopNode._id, 16994, '*', '*', 0);
|
|
desktop.contype = 2;
|
|
} else if ((contype == null) || (contype == 1) || ((contype == 3) && (currentNode.agent.id > 4))) {
|
|
// Setup the Mesh Agent remote desktop
|
|
desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
|
desktop.debugmode = debugmode;
|
|
desktop.m.debugmode = debugmode;
|
|
desktop.attemptWebRTC = attemptWebRTC;
|
|
desktop.options = {};
|
|
if (tsid != null) { desktop.options.tsid = tsid; }
|
|
if (consent != null) { desktop.options.consent = consent; }
|
|
desktop.onStateChanged = onDesktopStateChange;
|
|
desktop.onConsoleMessageChange = function () {
|
|
if (desktop.consoleMessage) {
|
|
Q('p11DeskConsoleMsg').innerHTML += EscapeHtml(desktop.consoleMessage).split('\n').join('<br />') + '<br /><br />';
|
|
QV('p11DeskConsoleMsg', true);
|
|
if (p11DeskConsoleMsgTimer != null) { clearTimeout(p11DeskConsoleMsgTimer); }
|
|
p11DeskConsoleMsgTimer = setTimeout(p11clearConsoleMsg, 8000);
|
|
} else {
|
|
p11clearConsoleMsg();
|
|
}
|
|
}
|
|
desktop.m.CompressionLevel = desktopsettings.quality; // Number from 1 to 100. 50 or less is best.
|
|
desktop.m.ScalingLevel = desktopsettings.scaling;
|
|
if (desktopsettings.framerate) { desktop.m.FrameRateTimer = desktopsettings.framerate; }
|
|
desktop.m.onDisplayinfo = deskDisplayInfo;
|
|
desktop.m.onScreenSizeChange = deskAdjust;
|
|
desktop.Start(desktopNode._id);
|
|
desktop.latency.callback = function(ms) { console.log('latency', ms); updateSessionTime(); };
|
|
desktop.contype = 1;
|
|
} else if (contype == 3) {
|
|
// Ask for user sessions
|
|
meshserver.send({ action: 'msg', type: 'userSessions', nodeid: currentNode._id, tag: consent });
|
|
}
|
|
} else {
|
|
// Disconnect and clean up the remote desktop
|
|
desktop.Stop();
|
|
webRtcDesktopReset();
|
|
desktopNode = desktop = null;
|
|
if (pluginHandler != null) { pluginHandler.callHook('onDesktopDisconnect'); }
|
|
}
|
|
}
|
|
|
|
function p11clearConsoleMsg() { QH('p11DeskConsoleMsg', ''); QV('p11DeskConsoleMsg', false); if (p11DeskConsoleMsgTimer) { clearTimeout(p11DeskConsoleMsgTimer); p11DeskConsoleMsgTimer = null; } }
|
|
function p12clearConsoleMsg() { QH('p12TermConsoleMsg', ''); QV('p12TermConsoleMsg', false); if (p12TermConsoleMsgTimer) { clearTimeout(p12TermConsoleMsgTimer); p12TermConsoleMsgTimer = null; } }
|
|
function p13clearConsoleMsg() { QH('p13FilesConsoleMsg', ''); QV('p13FilesConsoleMsg', false); if (p13FilesConsoleMsgTimer) { clearTimeout(p13FilesConsoleMsgTimer); p13FilesConsoleMsgTimer = null; } }
|
|
|
|
var webRtcDesktop = null;
|
|
function webRtcDesktopReset() {
|
|
if (webRtcDesktop == null) return;
|
|
if (webRtcDesktop.softdesktop != null) { webRtcDesktop.softdesktop.Stop(); webRtcDesktop.softdesktop = null; }
|
|
if (webRtcDesktop.webchannel != null) { try { webRtcDesktop.webchannel.close(); } catch (e) { } webRtcDesktop.webchannel = null; }
|
|
if (webRtcDesktop.webrtc != null) { try { webRtcDesktop.webrtc.close(); } catch (e) { } webRtcDesktop.webrtc = null; }
|
|
webRtcDesktop = null;
|
|
// Switch back to hardware KVM
|
|
if (desktop && desktop.m) {
|
|
desktop.m.hold(false);
|
|
Q('deskstatus').textContent = StatusStrs[desktop.State];
|
|
}
|
|
// ###BEGIN###{DesktopInbandFiles}
|
|
/*
|
|
p24files = null;
|
|
p24downloadFileCancel() // If any downloads are in process, cancel them.
|
|
p24uploadFileCancel(); // If any uploads are in process, cancel them.
|
|
QV('go24', false); // Files
|
|
if (currentView == 24) { go(14); }
|
|
*/
|
|
// ###END###{DesktopInbandFiles}
|
|
}
|
|
|
|
function onDesktopStateChange(xdesktop, state) {
|
|
var xstate = state;
|
|
if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
|
|
var str = StatusStrs[xstate];
|
|
if ((desktop != null) && (desktop.webRtcActive == true)) { str += ", WebRTC"; }
|
|
//if (desktop.m.stopInput == true) { str += ', Loopback'; }
|
|
QH('deskstatus', str);
|
|
switch (state) {
|
|
case 0:
|
|
// Disconnect and clean up the remote desktop
|
|
desktop.Stop();
|
|
desktopNode = desktop = null;
|
|
QV('DeskFocus', false);
|
|
QV('termdisplays', false);
|
|
QV('deskRecordIcon', false);
|
|
deskFocusBtn.value = "All Focus";
|
|
if (fullscreen == true) { deskToggleFull(); }
|
|
webRtcDesktopReset();
|
|
deskPreferedStickyDisplay = 0;
|
|
break;
|
|
case 2:
|
|
break;
|
|
case 3:
|
|
if (desktop && (desktop.serverIsRecording == true)) { QV('deskRecordIcon', true); }
|
|
desktop.startTime = new Date();
|
|
if (updateSessionTimer == null) { updateSessionTimer = setInterval(updateSessionTime, 1000); }
|
|
break;
|
|
default:
|
|
//console.log('Unknown onDesktopStateChange state', state);
|
|
break;
|
|
}
|
|
updateDesktopButtons();
|
|
deskAdjust();
|
|
setTimeout(deskAdjust, 50);
|
|
}
|
|
|
|
function updateSessionTime() {
|
|
// Desktop
|
|
var latencyStr = '', seconds = 0;
|
|
if (desktop && desktop.startTime) {
|
|
if (desktop.latency && (desktop.latency.current >= 0)) { latencyStr = format('{0} ms, ', desktop.latency.current); }
|
|
seconds = Math.floor((new Date() - desktop.startTime) / 1000);
|
|
QH('DeskTimer', latencyStr + zeroPad(Math.floor(seconds / 3600), 2) + ':' + zeroPad((Math.floor(seconds / 60) % 60), 2) + ':' + zeroPad((seconds % 60), 2));
|
|
} else {
|
|
QH('DeskTimer', '');
|
|
}
|
|
|
|
// Terminal
|
|
seconds = 0;
|
|
if (terminal && terminal.startTime) {
|
|
if (terminal.latency && (terminal.latency.current >= 0)) { latencyStr = format('{0} ms, ', terminal.latency.current); }
|
|
seconds = Math.floor((new Date() - terminal.startTime) / 1000);
|
|
QH('TermTimer', latencyStr + zeroPad(Math.floor(seconds / 3600), 2) + ':' + zeroPad((Math.floor(seconds / 60) % 60), 2) + ':' + zeroPad((seconds % 60), 2));
|
|
} else {
|
|
QH('TermTimer', '');
|
|
}
|
|
|
|
if ((desktop == null) && (terminal == null)) { clearInterval(updateSessionTimer); updateSessionTimer = null; }
|
|
}
|
|
|
|
function showDesktopSettings() {
|
|
if (xxdialogMode) return;
|
|
applyDesktopSettings();
|
|
updateDesktopButtons();
|
|
setDialogMode(7, "Remote Desktop Settings", 3, showDesktopSettingsChanged);
|
|
}
|
|
|
|
function showDesktopSettingsChanged() {
|
|
desktopsettings.encoding = d7desktopmode.value;
|
|
desktopsettings.showfocus = d7showfocus.checked;
|
|
desktopsettings.showmouse = d7showcursor.checked;
|
|
desktopsettings.quality = d7bitmapquality.value;
|
|
desktopsettings.scaling = d7bitmapscaling.value;
|
|
desktopsettings.framerate = d7framelimiter.value;
|
|
desktopsettings.localkeymap = d7localKeyMap.checked;
|
|
localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings));
|
|
applyDesktopSettings();
|
|
if (desktop) {
|
|
if (desktop.contype == 1) {
|
|
if (desktop.State != 0) {
|
|
desktop.m.SendCompressionLevel(1, desktopsettings.quality, desktopsettings.scaling, desktopsettings.framerate);
|
|
}
|
|
}
|
|
if (desktop.contype == 2) {
|
|
if (desktopsettings.showfocus == false) { desktop.m.focusmode = 0; deskFocusBtn.value = "All Focus"; }
|
|
if (desktop.State != 0) { desktop.Stop(); setTimeout(function () { connectDesktop(null, 2); }, 50); }
|
|
}
|
|
}
|
|
}
|
|
|
|
function applyDesktopSettings() {
|
|
var r = '', ops = (features & 512)?[90,80,70,60,50,40,30,20,10,5,1]:[60,50,40,30,20,10,5,1];
|
|
for (var i in ops) { r += '<option value=' + ops[i] + '>' + ops[i] + '%</option>'; }
|
|
QH('d7bitmapquality', r);
|
|
d7desktopmode.value = desktopsettings.encoding;
|
|
d7showfocus.checked = desktopsettings.showfocus;
|
|
d7showcursor.checked = desktopsettings.showmouse;
|
|
d7bitmapquality.value = 40; // Default value
|
|
if (ops.indexOf(parseInt(desktopsettings.quality)) >= 0) { d7bitmapquality.value = desktopsettings.quality; }
|
|
d7bitmapscaling.value = desktopsettings.scaling;
|
|
if (desktopsettings.framerate) { d7framelimiter.value = desktopsettings.framerate; } else { d7framelimiter.value = 100; }
|
|
if (desktopsettings.localkeymap) { d7localKeyMap.checked = desktopsettings.localkeymap; }
|
|
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (desktop.state != 0) && (desktopsettings.showfocus));
|
|
}
|
|
|
|
// Enter browser fullscreen
|
|
function enterBrowserFullscreen(elem) {
|
|
if (elem.requestFullscreen) { elem.requestFullscreen(); }
|
|
else if (elem.msRequestFullscreen) { elem.msRequestFullscreen(); }
|
|
else if (elem.mozRequestFullScreen) { elem.mozRequestFullScreen(); }
|
|
else if (elem.webkitRequestFullscreen) { elem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); }
|
|
}
|
|
|
|
// Exit browser fullscreen
|
|
function exitBrowserFullscreen() {
|
|
if (document.exitFullscreen) { document.exitFullscreen(); }
|
|
else if (document.msExitFullscreen) { document.msExitFullscreen(); }
|
|
else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); }
|
|
else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); }
|
|
}
|
|
|
|
// Return true if the browser is fullscreen. This is a delayed method that will return true/false late. Not very useful.
|
|
function isBrowserFullscreen() {
|
|
if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) { return false; } else { return true; }
|
|
}
|
|
|
|
var fullscreen = false;
|
|
var browserfullscreen = false;
|
|
function deskToggleFull(e) {
|
|
var xtermActive = !((args.xterm === 0) || ((terminal != null) && (xterm == null)));
|
|
fullscreen = !fullscreen;
|
|
if (fullscreen) {
|
|
QC('body').add('fulldesk');
|
|
QS('deskarea3x')['height'] = '100%';
|
|
QS('deskarea3x')['max-height'] = '100%';
|
|
if (xtermActive) {
|
|
// XTerm terminal
|
|
QS('termTable')['position'] = 'absolute';
|
|
QS('termTable')['top'] = QS('termTable')['bottom'] = QS('termTable')['left'] = QS('termTable')['right'] = '0';
|
|
} else {
|
|
// Legacy terminal
|
|
QS('termTable')['height'] = '100%';
|
|
QS('termTable')['max-height'] = '100%';
|
|
}
|
|
|
|
// If shift is pressed, enter browser full screen.
|
|
if (e.shiftKey == true) {
|
|
enterBrowserFullscreen(Q('container'));
|
|
browserfullscreen = true;
|
|
}
|
|
} else {
|
|
QC('body').remove('fulldesk');
|
|
var hide = args.hide;
|
|
if (footerBar == false) { hide |= 4; }
|
|
var xh = (((hide & 1) ? 0 : 66) + ((hide & 2) ? 0 : 24) + ((hide & 4) ? 0 : 45) + ((hide & 8) ? 0 : 60)); // 0 to 195
|
|
QS('deskarea3x')['height'] = 'calc(100vh - ' + (75 + xh) + 'px)';
|
|
QS('deskarea3x')['max-height'] = 'calc(100vh - ' + (75 + xh) + 'px)';
|
|
if (xtermActive) {
|
|
// XTerm terminal
|
|
QS('termTable')['position'] = null;
|
|
QS('termTable')['top'] = QS('termTable')['bottom'] = QS('termTable')['left'] = QS('termTable')['right'] = null;
|
|
} else {
|
|
// Legacy terminal
|
|
QS('termTable')['height'] = 'calc(100vh - ' + (75 + xh) + 'px)';
|
|
QS('termTable')['max-height'] = 'calc(100vh - ' + (75 + xh) + 'px)';
|
|
}
|
|
if (browserfullscreen == true) { exitBrowserFullscreen(); browserfullscreen = false; }
|
|
}
|
|
deskAdjust();
|
|
updateDesktopButtons();
|
|
adjustPanels();
|
|
//setTimeout(adjustPanels, 10);
|
|
//setTimeout(function() { xtermfit.fit(); }, 10);
|
|
if (xterm != null) { xtermfit.fit(); if (xxcurrentView == 12) { xterm.focus(); } }
|
|
}
|
|
|
|
function deskToggleFocus() {
|
|
desktop.m.focusmode = (desktop.m.focusmode + 64) % 192;
|
|
Q('deskFocusBtn').value = ["All Focus", "Small Focus", "Large Focus"][desktop.m.focusmode / 64];
|
|
}
|
|
|
|
function deskAdjust() {
|
|
var parentH = Q('DeskParent').clientHeight, parentW = Q('DeskParent').clientWidth;
|
|
var deskH = Q('Desk').height, deskW = Q('Desk').width;
|
|
|
|
if (deskAspectRatio == 2) {
|
|
// Scale mode
|
|
QS('Desk')['margin-top'] = null;
|
|
QS('Desk').height = '100%';
|
|
QS('Desk').width = '100%';
|
|
//QS('deskarea3x').height = null;
|
|
QS('DeskParent').overflow = 'hidden';
|
|
} else if (deskAspectRatio == 1) {
|
|
// Zoomed mode
|
|
QS('Desk')['margin-top'] = '0px';
|
|
QS('Desk').height = deskH + 'px';
|
|
QS('Desk').width = deskW + 'px';
|
|
QS('DeskParent').overflow = 'scroll';
|
|
} else {
|
|
// Fixed aspect ratio
|
|
if ((parentH / parentW) > (deskH / deskW)) {
|
|
var hNew = ((deskH * parentW) / deskW) + 'px';
|
|
//if (webPageFullScreen || fullscreen) {
|
|
//QS('deskarea3x').height = null;
|
|
//} else {
|
|
// QS('deskarea3x').height = hNew;
|
|
//QS('deskarea3x').height = null;
|
|
//}
|
|
QS('Desk').height = hNew;
|
|
QS('Desk').width = '100%';
|
|
} else {
|
|
var wNew = ((deskW * parentH) / deskH) + 'px';
|
|
if (webPageFullScreen || fullscreen) {
|
|
QS('Desk').height = null;
|
|
} else {
|
|
QS('Desk').height = '100%';
|
|
}
|
|
QS('Desk').width = wNew;
|
|
}
|
|
QS('Desk')['margin-top'] = null;
|
|
QS('DeskParent').overflow = 'hidden';
|
|
}
|
|
}
|
|
|
|
function mdeskAdjust(mod, sw, sh, cv) {
|
|
if (!mod || !sw || !sh || !cv) return;
|
|
|
|
// Check if we are in single desktop mode
|
|
if (cv.id == 'Desk') { deskAdjust(); return; }
|
|
|
|
// Figure out and adjust the size to fill the width of the div
|
|
var vsize = [{ x: 180, y: 101 }, { x: 302, y: 169 }, { x: 454, y: 255 }][Q('sizeselect').selectedIndex];
|
|
var realw = vsize.x + 2, tw = Q('xdevices').clientWidth - 30, xw = Math.floor(tw / realw);
|
|
xw = realw + Math.floor((tw - (xw * realw)) / xw);
|
|
vsize.y = vsize.y * (xw / vsize.x);
|
|
vsize.x = xw;
|
|
var mh = vsize.y, mw = vsize.x;
|
|
if (mod.State != 0) { mh = vsize.y; mw = (sw / sh) * vsize.y; }
|
|
QS(cv.id)['max-height'] = mh + 'px';
|
|
QS(cv.id)['max-width'] = mw + 'px';
|
|
QS(cv.id)['margin-top'] = '0';
|
|
QS(cv.id)['margin-bottom'] = '0';
|
|
}
|
|
|
|
// Remote desktop special key combos for Windows
|
|
function deskSendKeys() {
|
|
if (xxdialogMode || desktop == null || desktop.State != 3) return;
|
|
var ks = Q('deskkeys').value;
|
|
if (ks == 0) { // WIN+Down arrow
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe7,1],[0xff54,1],[0xff54,0],[0xffe7,0]]); // Intel AMT: Meta-left down, Down arrow press, Down arrow release, Meta-left release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,40],[desktop.m.KeyAction.UP,40],[desktop.m.KeyAction.EXUP,0x5B]]); // Agent: L-Winkey press, Down arrow press, Down arrow release, L-Winkey release
|
|
}
|
|
} else if (ks == 1) { // WIN+Up arrow
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe7,1],[0xff52,1],[0xff52,0],[0xffe7,0]]); // Intel AMT: Meta-left down, Up arrow press, Up arrow release, Meta-left release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,38],[desktop.m.KeyAction.UP,38],[desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, Up arrow press, Up arrow release, L-Winkey release
|
|
}
|
|
} else if (ks == 2) { // WIN+L arrow
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe7,1],[0x6c,1],[0x6c,0],[0xffe7,0]]); // Intel AMT: Meta-left down, 'l' press, 'l' release, Meta-left release
|
|
} else {
|
|
desktop.sendCtrlMsg('{"action":"lock"}');
|
|
//desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,76],[desktop.m.KeyAction.UP,76],[desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, 'L' press, 'L' release, L-Winkey release
|
|
//desktop.m.SendKeyMsgKC(desktop.m.KeyAction.EXDOWN, 0x5B);
|
|
//desktop.m.SendKeyMsgKC(desktop.m.KeyAction.DOWN, 76);
|
|
//desktop.m.SendKeyMsgKC(desktop.m.KeyAction.UP, 76);
|
|
//desktop.m.SendKeyMsgKC(desktop.m.KeyAction.EXUP, 0x5B);
|
|
}
|
|
} else if (ks == 3) { // WIN+M arrow
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe7,1],[0x6d,1],[0x6d,0],[0xffe7,0]]); // Intel AMT: Meta-left down, 'm' press, 'm' release, Meta-left release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,77],[desktop.m.KeyAction.UP,77],[desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, 'M' press, 'M' release, L-Winkey release
|
|
}
|
|
} else if (ks == 4) { // Shift+WIN+M arrow
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe1,1],[0xffe7,1],[0x6d,1],[0x6d,0],[0xffe7,0],[0xffe1,0]]); // Intel AMT: Shift-left down, Meta-left down, 'm' press, 'm' release, Meta-left release, Shift-left release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.DOWN,16],[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,77],[desktop.m.KeyAction.UP,77],[desktop.m.KeyAction.EXUP,0x5B],[desktop.m.KeyAction.UP, 16]]); // MeshAgent: L-shift press, L-Winkey press, 'M' press, 'M' release, L-Winkey release, L-shift release
|
|
}
|
|
} else if (ks == 5) { // WIN
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe7,1],[0xffe7,0]]); // Intel AMT: Meta-left down, Meta-left release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B], [desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, L-Winkey release
|
|
}
|
|
} else if (ks == 6) { // WIN+R
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe7,1],[0x72,1],[0x72,0],[0xffe7,0]]); // Intel AMT: Meta-left down, 'r' press, 'r' release, Meta-left release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 82], [desktop.m.KeyAction.UP, 82], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, 'R' press, 'R' release, L-Winkey release
|
|
}
|
|
} else if (ks == 7) { // ALT-F4
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe9,1],[0xffc1,1],[0xffc1,0],[0xffe9,0]]); // Intel AMT: Alt down, 'F4' press, 'F4' release, Alt release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 18], [desktop.m.KeyAction.DOWN, 115], [desktop.m.KeyAction.UP, 115], [desktop.m.KeyAction.EXUP, 18]]); // MeshAgent: Alt press, 'F4' press, 'F4' release, Alt release
|
|
}
|
|
} else if (ks == 8) { // CTRL-W
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe3,1],[0x77,1],[0x77,0],[0xffe3,0]]); // Intel AMT: Ctrl down, 'w' press, 'w' release, Ctrl release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 17], [desktop.m.KeyAction.DOWN, 87], [desktop.m.KeyAction.UP, 87], [desktop.m.KeyAction.EXUP, 17]]); // MeshAgent: Ctrl press, 'W' press, 'W' release, Ctrl release
|
|
}
|
|
} else if (ks == 9) { // ALT-TAB
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe9, 1], [0xff09, 1], [0xff09, 0], [0xffe9, 0]]); // Intel AMT: Alt down, 'TAB' press, 'TAB' release, Alt release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 18], [desktop.m.KeyAction.DOWN, 9], [desktop.m.KeyAction.UP, 9], [desktop.m.KeyAction.EXUP, 18]]); // MeshAgent: Alt press, 'TAB' press, 'TAB' release, Alt release
|
|
}
|
|
} else if (ks == 10) { // CTRL-ALT-DEL
|
|
desktop.m.sendcad();
|
|
} else if (ks == 11) { // WIN-LEFT
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe7, 1], [0xff51, 1], [0xff51, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, Left arrow press, Left arrow release, Meta-left release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 37], [desktop.m.KeyAction.UP, 37], [desktop.m.KeyAction.EXUP, 0x5B]]);
|
|
}
|
|
} else if (ks == 12) { // WIN-RIGHT
|
|
if (desktop.contype == 2) {
|
|
desktop.m.sendkey([[0xffe7, 1], [0xff53, 1], [0xff53, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, Right arrow press, Right arrow release, Meta-left release
|
|
} else {
|
|
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 39], [desktop.m.KeyAction.UP, 39], [desktop.m.KeyAction.EXUP, 0x5B]]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remote desktop typing
|
|
function showDeskType() {
|
|
if (xxdialogMode || desktop == null || desktop.State != 3) return;
|
|
Q('DeskType').blur();
|
|
var x = '<div>' + "Enter text and click OK to remotely type it using a US english keyboard. Make sure to place the remote cursor at the correct position before proceeding." + '<div>';
|
|
x += '<textarea id=d2typeText style="margin-top:5px;width:100%;height:184px;resize:none" maxlength=2000></textarea>';
|
|
setDialogMode(2, "Remote Keyboard Entry", 3, showDeskTypeEx, x);
|
|
Q('d2typeText').focus();
|
|
}
|
|
|
|
var AmtDeskTypeTimer = null;
|
|
var AmtDeskTypeContent = null;
|
|
var DeskTypeTranslate = { 39: 222, 42: 106, 43: 107, 44: 188, 45: 189, 46: 190, 47: 191, 59: 186, 61: 187, 91: 219, 92: 220, 93: 221, 96: 192, 191: 111 };
|
|
var DeskTypeShiftTranslate = { 33: 49, 34: 222, 35: 51, 36: 52, 37: 53, 38: 55, 40: 57, 41: 48, 58: 186, 60: 188, 62: 190, 63: 191, 64: 50, 94: 54, 95: 189, 106: 56, 107: 187, 123: 219, 124: 220, 125: 221, 126: 192 };
|
|
function showDeskTypeEx() {
|
|
var txt = Q('d2typeText').value, ltxt = Q('d2typeText').value.toUpperCase(), x = [], shift = false;
|
|
if (desktop.contype == 2) {
|
|
// Intel AMT
|
|
for (var i in txt) { var a = txt.charCodeAt(i); x.push([a, 1], [a, 0]); }
|
|
AmtDeskTypeContent = x;
|
|
AmtDeskTypeTimer = setInterval(function () {
|
|
var key = AmtDeskTypeContent.shift();
|
|
if (desktop) { desktop.m.sendkey(key[0], key[1]); }
|
|
if ((desktop == null) || (AmtDeskTypeContent.length == 0)) { clearInterval(AmtDeskTypeTimer); AmtDeskTypeContent = null; }
|
|
}, 10);
|
|
} else {
|
|
// MeshAgent
|
|
for (var i in txt) {
|
|
var a = txt.charCodeAt(i), b = ltxt.charCodeAt(i);
|
|
if (((a >= 65) && (a <= 90)) || ((a >= 97) && (a <= 122))) {
|
|
if ((a == b) && (shift == false)) { x.push([desktop.m.KeyAction.DOWN, 16]); shift = true; } // LShift down
|
|
if ((a != b) && (shift == true)) { x.push([desktop.m.KeyAction.UP, 16]); shift = false; } // LShift up
|
|
} else if ((a >= 48) && (a <= 57)) {
|
|
if (shift == true) { x.push([desktop.m.KeyAction.UP, 16]); shift = false; } // Shift up
|
|
} else if (DeskTypeTranslate[a]) {
|
|
if (shift == true) { x.push([desktop.m.KeyAction.UP, 16]); shift = false; } // Shift up
|
|
b = DeskTypeTranslate[a];
|
|
} else if (DeskTypeShiftTranslate[a]) {
|
|
if (shift == false) { x.push([desktop.m.KeyAction.DOWN, 16]); shift = true; } // LShift down
|
|
b = DeskTypeShiftTranslate[a];
|
|
}
|
|
x.push([desktop.m.KeyAction.DOWN, b], [desktop.m.KeyAction.UP, b]);
|
|
}
|
|
if (shift == true) { x.push([desktop.m.KeyAction.UP, 16]); shift = false; } // Shift up
|
|
desktop.m.SendKeyMsgKC(x);
|
|
}
|
|
}
|
|
|
|
// Show clipboard dialog
|
|
function showDeskClip() {
|
|
if (xxdialogMode || desktop == null || desktop.State != 3) return;
|
|
Q('DeskClip').blur();
|
|
var x = '';
|
|
x += '<input id=dlgClipGet type=button value="Get Clipboard" style=width:120px onclick=showDeskClipGet()>';
|
|
x += '<input id=dlgClipSet type=button value="Set Clipboard" style=width:120px onclick=showDeskClipSet()>';
|
|
x += '<div id=dlgClipStatus style="display:inline-block;margin-left:8px" ></div>';
|
|
x += '<textarea id=d2clipText style="width:100%;height:184px;resize:none" maxlength=65535></textarea>';
|
|
x += '<input type=button value="Close" style=width:80px;float:right onclick=dialogclose(0)><div style=height:26px;margin-top:3px><span id=linuxClipWarn style=display:none>' + "Remote clipboard is valid for 60 seconds." + '</span> </div><div></div>';
|
|
setDialogMode(2, "Remote Clipboard", 8, null, x, 'clipboard');
|
|
Q('d2clipText').focus();
|
|
}
|
|
|
|
function showDeskClipGet() {
|
|
if (desktop == null || desktop.State != 3) return;
|
|
meshserver.send({ action: 'msg', type: 'getclip', nodeid: currentNode._id });
|
|
}
|
|
|
|
function showDeskClipSet() {
|
|
if (desktop == null || desktop.State != 3) return;
|
|
meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: Q('d2clipText').value });
|
|
QV('linuxClipWarn', currentNode && currentNode.agent && (currentNode.agent.id > 4) && (currentNode.agent.id != 21) && (currentNode.agent.id != 22));
|
|
}
|
|
|
|
// Send CTRL-ALT-DEL
|
|
function sendCAD() {
|
|
if (xxdialogMode || desktop == null || desktop.State != 3) return;
|
|
desktop.m.sendcad();
|
|
}
|
|
|
|
// Show process dialogs
|
|
function toggleDeskTools() {
|
|
if (xxdialogMode) return;
|
|
if (QS('DeskTools').display == 'none') {
|
|
QV('DeskTools', true);
|
|
Q('DeskTools').nodeid = currentNode._id;
|
|
QH('DeskToolsProcesses', '');
|
|
QH('DeskToolsServices', '');
|
|
QV('deskToolsTopTabService', false);
|
|
changeDeskToolTab(0)
|
|
refreshDeskTools(0);
|
|
refreshDeskTools(1);
|
|
} else {
|
|
QV('DeskTools', false);
|
|
}
|
|
}
|
|
|
|
var deskToolTabSelection = 0;
|
|
function changeDeskToolTab(tabnum) {
|
|
deskToolTabSelection = tabnum;
|
|
QV('DeskToolsProcessTab', tabnum == 0);
|
|
QV('DeskToolsServiceTab', tabnum == 1);
|
|
QS('deskToolsTopTabProcess')['bottom'] = (tabnum == 0) ? '0px' : '3px';
|
|
QS('deskToolsTopTabService')['bottom'] = (tabnum == 1) ? '0px' : '3px';
|
|
QS('deskToolsTopTabProcess')['color'] = (tabnum == 0) ? 'black' : 'gray';
|
|
QS('deskToolsTopTabService')['color'] = (tabnum == 1) ? 'black' : 'gray';
|
|
}
|
|
|
|
// Refresh all of the desktop tool panels
|
|
function refreshDeskTools(x) {
|
|
var sel = (x == null) ? deskToolTabSelection : x;
|
|
QV('DeskToolsRefreshButton', false);
|
|
setTimeout(refreshDeskToolsEx, 500);
|
|
if (sel == 0) meshserver.send({ action: 'msg', type: 'ps', nodeid: currentNode._id });
|
|
if (sel == 1) meshserver.send({ action: 'msg', type: 'services', nodeid: currentNode._id });
|
|
}
|
|
function refreshDeskToolsEx() { QV('DeskToolsRefreshButton', true); }
|
|
var deskTools = { sort: 1, ssort: 1, msg: null, smsg: null };
|
|
function sortProcess(sort) { deskTools.sort = sort; showDeskToolsProcesses(deskTools.msg); }
|
|
function sortService(sort) { deskTools.ssort = sort; showDeskToolsServices(deskTools.smsg); }
|
|
function sortProcessPid(a, b) { if (a.p > b.p) return 1; if (a.p < b.p) return (-1); return sortProcessName(a, b); }
|
|
function sortProcessName(a, b) { if (a.d > b.d) return 1; if (a.d < b.d) return (-1); return 0; }
|
|
function showDeskToolsProcesses(message) {
|
|
deskTools.msg = message;
|
|
if (message == null) { QH('DeskToolsProcesses', ''); return; }
|
|
if (Q('DeskTools').nodeid != message.nodeid) return;
|
|
var p = [], processes = null;
|
|
try { processes = JSON.parse(message.value); } catch (e) { }
|
|
if (processes != null) {
|
|
for (var pid in processes) { p.push( { p:parseInt(pid), c:processes[pid].cmd, d:processes[pid].cmd.toLowerCase(), u: processes[pid].user } ); }
|
|
if (deskTools.sort == 0) { p.sort(sortProcessPid); } else if (deskTools.sort == 1) { p.sort(sortProcessName); }
|
|
var x = '';
|
|
for (var i in p) {
|
|
if (p[i].p != 0) {
|
|
var c = p[i].c;
|
|
if (c.length > 30) { c = '<span title="' + c + '">' + c.substring(0,30) + '...</span>' }
|
|
x += '<div class=deskToolsBar><div style=width:50px;float:left;text-align:right;padding-right:5px>' + p[i].p + '</div><a href=# style=float:right;padding-right:5px;cursor:pointer title="' + "Stop process" + '" onclick=\'return stopProcess(' + p[i].p + ',"' + p[i].c + '")\'><img width=10 height=10 src="images/trash.png"></a><div style=float:right;padding-right:5px>' + (p[i].u ? p[i].u : '') + '</div><div>' + c + '</div></div>';
|
|
}
|
|
}
|
|
QH('DeskToolsProcesses', x);
|
|
}
|
|
}
|
|
function showDeskToolsServices(message) {
|
|
deskTools.smsg = message;
|
|
if (message == null) { QH('DeskToolsProcesses', ''); return; }
|
|
if (Q('DeskTools').nodeid != message.nodeid) return;
|
|
QV('deskToolsTopTabService', true);
|
|
var s = [], services = null;
|
|
try { services = JSON.parse(message.value); } catch (e) { }
|
|
deskTools.services = services;
|
|
if (services != null) {
|
|
for (var i in services) {
|
|
if (services[i].status) {
|
|
// Windows
|
|
s.push({ p: capitalizeFirstLetter(services[i].status.state.toLowerCase()), d: services[i].displayName, i: i });
|
|
} else if (services[i].serviceType) {
|
|
// Linux (TODO: This the service status is not displayed, not sure start/stop/restart will work).
|
|
s.push({ p: services[i].serviceType, d: services[i].name, i: i });
|
|
}
|
|
}
|
|
if (deskTools.ssort == 0) { s.sort(sortProcessPid); } else if (deskTools.ssort == 1) { s.sort(sortProcessName); }
|
|
var x = '';
|
|
for (var i in s) {
|
|
if (s[i].p != 0) {
|
|
var c = s[i].d;
|
|
if (c.length > 30) { c = '<span title="' + c + '">' + c.substring(0, 30) + '...</span>' }
|
|
x += '<div onclick=showServiceDetailsDialog(' + s[i].i + ') class=deskToolsBar><div style=width:70px;float:left;padding-right:5px>' + s[i].p + '</div><div>' + c + '</div></div>';
|
|
}
|
|
}
|
|
QH('DeskToolsServices', x);
|
|
}
|
|
}
|
|
|
|
function showServiceDetailsDialog(index) {
|
|
if (xxdialogMode) return;
|
|
var service = deskTools.services[index];
|
|
if (service != null) {
|
|
var x = '';
|
|
if (service.name) { x += addHtmlValue("Name", service.name); }
|
|
if (service.displayName) { x += addHtmlValue("Display name", service.displayName); }
|
|
if (service.status) {
|
|
if (service.status.state) { x += addHtmlValue("State", capitalizeFirstLetter(service.status.state.toLowerCase())); }
|
|
if (service.status.pid) { x += addHtmlValue("PID", service.status.pid); }
|
|
var serviceTypes = [];
|
|
if (service.status.isFileSystemDriver === true) { serviceTypes.push("FileSystemDriver"); }
|
|
if (service.status.isInteractive === true) { serviceTypes.push("Interactive"); }
|
|
if (service.status.isKernelDriver === true) { serviceTypes.push("KernelDriver"); }
|
|
if (service.status.isOwnProcess === true) { serviceTypes.push("OwnProcess"); }
|
|
if (service.status.isSharedProcess === true) { serviceTypes.push("SharedProcess"); }
|
|
if (serviceTypes.length > 0) { x += addHtmlValue("Type", serviceTypes.join(', ')); }
|
|
}
|
|
x += '<br/><div style=float:right;margin-bottom:12px><input type=button value=\"' + "Close" + '\" onclick=showServiceDetailsDialogEx(0,' + index + ')></div><div style=margin-bottom:12px><input type=button value=\"' + "Start" + '\" onclick=showServiceDetailsDialogEx(1,' + index + ')><input type=button value=\"' + "Stop" + '\" onclick=showServiceDetailsDialogEx(2,' + index + ')><input type=button value=\"' + "Restart" + '\" onclick=showServiceDetailsDialogEx(3,' + index + ')></div>';
|
|
setDialogMode(2, "Service Details", 8, null, x, name);
|
|
}
|
|
}
|
|
|
|
function showServiceDetailsDialogEx(action, index) {
|
|
setDialogMode(0);
|
|
if (action == 0) return;
|
|
var service = deskTools.services[index];
|
|
if (service != null) {
|
|
if (action == 1) { meshserver.send({ action: 'msg', type: 'serviceStart', nodeid: currentNode._id, serviceName: service.name }); }
|
|
if (action == 2) { meshserver.send({ action: 'msg', type: 'serviceStop', nodeid: currentNode._id, serviceName: service.name }); }
|
|
if (action == 3) { meshserver.send({ action: 'msg', type: 'serviceRestart', nodeid: currentNode._id, serviceName: service.name }); }
|
|
setTimeout(function () { refreshDeskTools(1) }, 1000);
|
|
}
|
|
}
|
|
|
|
// Toggle mouse and keyboard input
|
|
function toggleKvmControl() { putstore('DeskControl', (Q('DeskControl').checked?1:0)); QS('DeskControlSpan').color = Q('DeskControl').checked?null:'red'; }
|
|
|
|
// Save the desktop image to file
|
|
function deskSaveImage() {
|
|
if (xxdialogMode || desktop == null || desktop.State != 3) return;
|
|
var d = new Date(), n = 'Desktop-' + currentNode.name + '-' + d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2) + '-' + ('0' + d.getHours()).slice(-2) + '-' + ('0' + d.getMinutes()).slice(-2);
|
|
Q('Desk')['toBlob'](function (blob) { saveAs(blob, n + '.jpg'); });
|
|
}
|
|
|
|
function deskDisplayInfo(sender, displays, selDisplay) {
|
|
var displayCount = 0, displaySelector = '';
|
|
for (var i in displays) {
|
|
displayCount++;
|
|
displaySelector += '<option' + ((selDisplay == i) ? ' selected' : '') + ' value=' + i + '>' + displays[i] + '</option>';
|
|
if ((deskPreferedStickyDisplay == i) && (selDisplay != deskPreferedStickyDisplay)) { desktop.m.SetDisplay(i); }
|
|
deskPreferedStickyDisplay = -1;
|
|
}
|
|
QH('termdisplays', displaySelector);
|
|
QV('termdisplays', displayCount > 1);
|
|
}
|
|
|
|
function deskGetDisplayNumbers(e) { desktop.m.GetDisplayNumbers(); }
|
|
var deskPreferedStickyDisplay = -1;
|
|
function deskSetDisplay(e) { desktop.m.SetDisplay(deskPreferedStickyDisplay = parseInt(Q('termdisplays').value)); Q('termdisplays').blur(); }
|
|
|
|
// Double click detection. This is important for MacOS.
|
|
var dblClickDetectArgs = { t:0, x:0, y:0 };
|
|
function dblClickDetect(e) {
|
|
if (e.buttons != 1) return;
|
|
var t = Date.now();
|
|
if (((t - dblClickDetectArgs.t) < 250) && (Math.abs(e.clientX - dblClickDetectArgs.x) < 2) && (Math.abs(e.clientY - dblClickDetectArgs.y) < 2)) {
|
|
if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousedblclick(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousedblclick(e); } }
|
|
}
|
|
dblClickDetectArgs.t = t;
|
|
dblClickDetectArgs.x = e.clientX;
|
|
dblClickDetectArgs.y = e.clientY;
|
|
}
|
|
|
|
function dmousedown(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousedown(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousedown(e); } } dblClickDetect(e); }
|
|
function dmouseup(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && Q('DeskControl').checked) if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mouseup(e); desktop.m.sendKeepAlive(); } else { desktop.m.mouseup(e); } }
|
|
function dmousemove(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousemove(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousemove(e); } } }
|
|
function dmousewheel(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousewheel(e); desktop.m.sendKeepAlive(); } else { if (desktop.m.mousewheel) { desktop.m.mousewheel(e); } } haltEvent(e); return true; } return false; }
|
|
function drotate(x) { if (!xxdialogMode && desktop != null) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); deskAdjust(); } }
|
|
function stopProcess(id, name) { setDialogMode(2, "Process Control", 3, stopProcessEx, format("Stop process #{0} \"{1}\"?", id, name), id); return false; }
|
|
function stopProcessEx(buttons, tag) { meshserver.send({ action: 'msg', type: 'pskill', nodeid: currentNode._id, value: tag }); setTimeout(refreshDeskTools, 300); }
|
|
|
|
//
|
|
// TERMINAL
|
|
//
|
|
|
|
var terminalNode;
|
|
function setupTerminal() {
|
|
// Setup the terminal
|
|
if ((terminalNode != currentNode) && (terminal != null)) { terminal.Stop(); terminal = null; }
|
|
terminalNode = currentNode;
|
|
updateTerminalButtons();
|
|
}
|
|
|
|
// Show and enable the right buttons
|
|
function updateTerminalButtons() {
|
|
var mesh = meshes[terminalNode.meshid];
|
|
var termState = ((terminal != null) && (terminal.state != 0));
|
|
|
|
// Show the right buttons
|
|
QV('disconnectbutton2span', (termState == true));
|
|
QV('connectbutton2span', (termState == false) && (currentNode.agent != null) && (currentNode.agent.caps & 2));
|
|
QV('connectbutton2hspan', (termState == false) && (terminalNode.intelamt != null) && (terminalNode.intelamt.state == 2) && (terminalNode.intelamt.ver != null));
|
|
QV('terminalSizeDropDown', (termState == false) && (terminalNode.intelamt != null) && (terminalNode.intelamt.state == 2) && (terminalNode.intelamt.ver != null));
|
|
|
|
// Enable buttons
|
|
var online = ((terminalNode.conn & 1) != 0); // If Agent (1) connected, enable Terminal
|
|
QE('connectbutton2', online);
|
|
var hwonline = ((terminalNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
|
|
QE('connectbutton2h', hwonline);
|
|
|
|
// Key buttons
|
|
QE('ctrlcbutton', termState);
|
|
QE('ctrlxbutton', termState);
|
|
QE('escbutton', termState);
|
|
QE('bsbutton', termState);
|
|
QE('pastebutton', termState);
|
|
QE('specialkeylist', termState);
|
|
QE('specialkeylistinput', termState);
|
|
|
|
// Terminal settings
|
|
QV('terminalSettingsButtons', (terminal) && (terminal.contype == 2));
|
|
if (terminal) {
|
|
Q('id_ttypebutton').value = terminalEmulations[terminal.m.terminalEmulation];
|
|
Q('id_tfxkeysbutton').value = fxEmulations[terminal.m.fxEmulation];
|
|
Q('id_tcrbutton').value = (terminal.m.lineFeed == '\r\n')?"CR+LF":"LF";
|
|
}
|
|
|
|
// Display extra buttons on legacy terminal
|
|
var xtermActive = !((args.xterm === 0) || ((terminal != null) && (xterm == null)));
|
|
QV('termarea3xdiv', xtermActive);
|
|
QV('Term', !xtermActive);
|
|
QV('bsbutton', !xtermActive);
|
|
QV('pastebutton', !xtermActive);
|
|
QV('devListToolbarViewIcons2', xtermActive);
|
|
QE('termSizeList', terminal == null);
|
|
}
|
|
|
|
// Called when the terminal state changes
|
|
function onTerminalStateChange(xterminal, state) {
|
|
var xstate = state;
|
|
if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; }
|
|
var str = StatusStrs[xstate];
|
|
if (terminal.webRtcActive == true) { str += ", WebRTC"; }
|
|
QH('termstatus', str);
|
|
switch (state) {
|
|
case 0:
|
|
// Disconnected, clear the terminal
|
|
QH('termtitle', '');
|
|
QV('termRecordIcon', false);
|
|
if (xterm == null) {
|
|
xterminal.m.TermResetScreen();
|
|
xterminal.m.TermDraw();
|
|
} else {
|
|
xterm.dispose();
|
|
xterm = xtermfit = null;
|
|
}
|
|
if (terminal != null) { terminal.Stop(); terminal = null; }
|
|
break;
|
|
case 3:
|
|
if (xterminal && (xterminal.serverIsRecording == true)) { QV('termRecordIcon', true); }
|
|
terminal.startTime = new Date();
|
|
if (updateSessionTimer == null) { updateSessionTimer = setInterval(updateSessionTime, 1000); }
|
|
if (xterm != null) { xterm.focus(); }
|
|
break;
|
|
default:
|
|
//console.log('Unhandled onTerminalStateChange state', state);
|
|
break;
|
|
}
|
|
updateTerminalButtons();
|
|
}
|
|
|
|
// DEBUG
|
|
var autoConnectTerminalTimer = null;
|
|
function autoConnectTerminal(e) { if (autoConnectTerminalTimer == null) { autoConnectTerminalTimer = setInterval(connectTerminal, 100); } else { clearInterval(autoConnectTerminalTimer); autoConnectTerminalTimer = null; } }
|
|
|
|
// Handles a tunnel to a remote shell
|
|
function CreateRemoteTunnel(onTunnelUpdate, options) {
|
|
var obj = { protocol: 1 };
|
|
if ((options != null) && (typeof options.protocol == 'number')) { obj.protocol = options.protocol; }
|
|
obj.onTunnelUpdate = onTunnelUpdate;
|
|
obj.xxStateChange = function (state) { }
|
|
obj.ProcessBinaryData = function (data) { obj.onTunnelUpdate(data); }
|
|
obj.ProcessData = function (data) { obj.onTunnelUpdate(data); }
|
|
obj.terminalEmulation = 1;
|
|
obj.fxEmulation = 0;
|
|
obj.lineFeed = '\r\n';
|
|
return obj;
|
|
}
|
|
|
|
function tunnelUpdate(data) { if (typeof data == 'string') { xterm.writeUtf8(data); } else { xterm.writeUtf8(new Uint8Array(data)); } }
|
|
|
|
// Send the new terminal size to the agent
|
|
function xTermSendResize() {
|
|
xtermResizeTimer = null;
|
|
if ((xterm != null) && (terminal != null) && (terminal.sendCtrlMsg != null)) { terminal.sendCtrlMsg(JSON.stringify({ ctrlChannel: '102938', type: 'termsize', cols: xterm.cols, rows: xterm.rows })); }
|
|
}
|
|
|
|
function connectTerminal(e, contype, options) {
|
|
p12clearConsoleMsg();
|
|
if (!terminal) {
|
|
if (contype == 2) {
|
|
// Setup the Intel AMT terminal
|
|
if ((terminalNode.intelamt.user == null) || (terminalNode.intelamt.user == '')) { editDeviceAmtSettings(terminalNode._id, connectTerminal, 2); return; }
|
|
var termoptions = {};
|
|
if (Q('termSizeList').value == 1) { termoptions.cols = 80; termoptions.rows = 25; }
|
|
else if (Q('termSizeList').value == 2) { termoptions.cols = 100; termoptions.rows = 30; }
|
|
|
|
// Get out of full screen
|
|
if (fullscreen) { deskToggleFull(); }
|
|
|
|
// Setup legacy terminal
|
|
QV('termarea3xdiv', false);
|
|
QV('Term', true);
|
|
terminal = CreateAmtRedirect(CreateAmtRemoteTerminal('Term', termoptions), authCookie);
|
|
terminal.debugmode = debugmode;
|
|
terminal.m.debugmode = debugmode;
|
|
terminal.m.onTitleChange = function (sender, title) { QH('termtitle', ' - ' + EscapeHtml(title)); }
|
|
terminal.onStateChanged = onTerminalStateChange;
|
|
terminal.Start(terminalNode._id, 16994, '*', '*', 0);
|
|
terminal.contype = 2;
|
|
Q('id_ttypebutton').value = terminalEmulations[terminal.m.terminalEmulation];
|
|
} else {
|
|
// Terminal setup
|
|
var termoptions = { protocol: ((options != null) && (typeof options.protocol == 'number'))?options.protocol:1 };
|
|
if (options && options.requireLogin) { termoptions.requireLogin = true; }
|
|
if ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) == -1) {
|
|
if (Q('termSizeList').value == 1) { termoptions.cols = 80; termoptions.rows = 25; termoptions.xterm = true; }
|
|
else if (Q('termSizeList').value == 2) { termoptions.cols = 100; termoptions.rows = 30; termoptions.xterm = true; }
|
|
else if (Q('termSizeList').value == 3) {
|
|
// TODO: Try to improve terminal auto-size.
|
|
termoptions.cols = Math.floor((Q('column_l').clientWidth - 60) / 10);
|
|
termoptions.rows = Math.floor((Q('column_l').clientHeight - 120) / 20);
|
|
termoptions.xterm = true;
|
|
}
|
|
}
|
|
|
|
// If shift is pressed
|
|
if ((e && (e.shiftKey == true))) {
|
|
if (currentNode.agent.id > 4) {
|
|
if (termoptions.protocol == 1) { termoptions.protocol = 7; } // Switch to user shell
|
|
} else {
|
|
if (termoptions.protocol == 1) { termoptions.protocol = 6; } // Switch to Powershell
|
|
}
|
|
}
|
|
|
|
if (args.xterm !== 0) {
|
|
// Setup a mesh agent xterm terminal
|
|
QV('termarea3xdiv', true);
|
|
QV('Term', false);
|
|
|
|
// Setup the terminal with auto-fit
|
|
if (xterm != null) { xterm.dispose(); }
|
|
xtermfit = new FitAddon.FitAddon();
|
|
xterm = new Terminal();
|
|
if (xtermfit) { xterm.loadAddon(xtermfit); }
|
|
xterm.open(Q('termarea3xdiv')); // termarea3x
|
|
xterm.onData(function (data) { if (terminal != null) { terminal.sendText(data); } })
|
|
if (xtermfit) { xtermfit.fit(); }
|
|
xterm.onTitleChange(function (title) { QH('termtitle', ' - ' + EscapeHtml(title)); });
|
|
xterm.onResize(function (size) {
|
|
// Despam resize
|
|
if (xtermResizeTimer) clearTimeout(xtermResizeTimer);
|
|
xtermResizeTimer = setTimeout(xTermSendResize, 200);
|
|
});
|
|
|
|
// Setup a terminal tunnel to the agent
|
|
terminal = CreateAgentRedirect(meshserver, CreateRemoteTunnel(tunnelUpdate, options), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
|
terminal.debugmode = debugmode;
|
|
terminal.m.debugmode = debugmode;
|
|
terminal.options = { cols: xterm.cols, rows: xterm.rows };
|
|
if (options && options.requireLogin) { terminal.options.requireLogin = true; }
|
|
terminal.Start(terminalNode._id);
|
|
terminal.onStateChanged = onTerminalStateChange;
|
|
terminal.contype = 1;
|
|
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
|
terminal.onConsoleMessageChange = function (server, msg) {
|
|
if (terminal.consoleMessage) {
|
|
Q('p12TermConsoleMsg').innerHTML += EscapeHtml(terminal.consoleMessage).split('\n').join('<br />') + '<br /><br />';
|
|
QV('p12TermConsoleMsg', true);
|
|
if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
|
|
p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, 8000);
|
|
}
|
|
};
|
|
} else {
|
|
QV('termarea3xdiv', false);
|
|
QV('Term', true);
|
|
|
|
// Setup a mesh agent legacy terminal
|
|
terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term', termoptions), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
|
terminal.options = termoptions;
|
|
terminal.debugmode = debugmode;
|
|
terminal.m.debugmode = debugmode;
|
|
terminal.m.onTitleChange = function (sender, title) { QH('termtitle', ' - ' + EscapeHtml(title)); }
|
|
terminal.m.lineFeed = ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) >= 0) ? '\r\n' : '\r'; // On windows, send \r\n, on Linux only \r
|
|
terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
|
|
terminal.onStateChanged = onTerminalStateChange;
|
|
terminal.onConsoleMessageChange = function () {
|
|
if (terminal.consoleMessage) {
|
|
Q('p12TermConsoleMsg').innerHTML += EscapeHtml(terminal.consoleMessage).split('\n').join('<br />') + '<br /><br />';
|
|
QV('p12TermConsoleMsg', true);
|
|
if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
|
|
p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, 8000);
|
|
}
|
|
}
|
|
terminal.Start(terminalNode._id);
|
|
terminal.contype = 1;
|
|
terminal.m.terminalEmulation = 0;
|
|
terminal.m.fxEmulation = 0;
|
|
Q('id_ttypebutton').value = terminalEmulations[0];
|
|
}
|
|
}
|
|
} else {
|
|
//QH('Term', '');
|
|
terminal.Stop();
|
|
terminal = null;
|
|
if (fullscreen) { deskToggleFull(); }
|
|
}
|
|
Q('connectbutton2').blur(); // Deselect the connect button so the button does not get key presses.
|
|
}
|
|
|
|
var terminalEmulations = ["UTF8 Terminal", "Extended ASCII", "Intel ASCII"];
|
|
function termToggleType() {
|
|
if (!terminal || xxdialogMode) return;
|
|
terminal.m.terminalEmulation = (terminal.m.terminalEmulation + 1) % 3;
|
|
Q('id_ttypebutton').value = terminalEmulations[terminal.m.terminalEmulation];
|
|
Q('id_ttypebutton').blur(); // Deselect the connect button so the button does not get key presses.
|
|
}
|
|
|
|
var fxEmulations = ["Intel (F10 = ESC+[OM)", "Alternate (F10 = ESC+0)", "VT100+ (F10 = ESC+[OY)"];
|
|
function termToggleFx() {
|
|
if (!terminal || xxdialogMode) return;
|
|
terminal.m.fxEmulation = (terminal.m.fxEmulation + 1) % 3;
|
|
Q('id_tfxkeysbutton').value = fxEmulations[terminal.m.fxEmulation];
|
|
Q('id_tfxkeysbutton').blur(); // Deselect the connect button so the button does not get key presses.
|
|
}
|
|
|
|
function termToggleCr() {
|
|
if (!terminal || xxdialogMode) return;
|
|
if (terminal.m.lineFeed == '\n') { terminal.m.lineFeed = '\r\n'; } else { terminal.m.lineFeed = '\n'; }
|
|
Q('id_tcrbutton').value = (terminal.m.lineFeed == '\r\n') ? "CR+LF" : "LF";
|
|
}
|
|
|
|
function termSendKey(key, id) {
|
|
if (!terminal || xxdialogMode) return;
|
|
if (xterm != null) {
|
|
if (terminal.sendText) {
|
|
// MeshAgent
|
|
terminal.sendText(String.fromCharCode(key));
|
|
} else {
|
|
// CIRA
|
|
terminal.send(String.fromCharCode(key));
|
|
}
|
|
xterm.focus();
|
|
} else if (terminal != null) {
|
|
terminal.m.TermSendKey(key);
|
|
Q(id).blur(); // Deselect the connect button so the button does not get key presses.
|
|
}
|
|
}
|
|
|
|
function showTermPasteDialog() {
|
|
if (!terminal || xxdialogMode) return;
|
|
Q('pastebutton').blur();
|
|
setDialogMode(2, "Paste", 3, showTermPasteDialogEx, '<textarea id=d2pasteText style="width:100%;height:184px;resize:none"></textarea>');
|
|
Q('d2pasteText').focus();
|
|
}
|
|
|
|
function showTermPasteDialogEx() {
|
|
if (!terminal) return;
|
|
terminal.m.TermSendKeys(Q('d2pasteText').value);
|
|
}
|
|
|
|
// Send special key
|
|
function sendSpecialKey() {
|
|
if (xterm != null) {
|
|
terminal.sendText(String.fromCharCode(Q('specialkeylist').value));
|
|
xterm.focus();
|
|
} else if (terminal != null) {
|
|
terminal.m.TermSendKey(Q('specialkeylist').value);
|
|
Q('specialkeylist').blur();
|
|
Q('specialkeylistinput').blur();
|
|
}
|
|
}
|
|
|
|
//
|
|
// FILES
|
|
//
|
|
|
|
var filesNode;
|
|
function setupFiles() {
|
|
// Setup the files tab
|
|
var samenode = (filesNode == currentNode);
|
|
filesNode = currentNode;
|
|
var online = ((filesNode.conn & 1) != 0)?true:false; // If Agent (1) connected, enable Terminal
|
|
QE('p13Connect', online);
|
|
if (((samenode == false) || (online == false)) && files) { files.Stop(); files = null; }
|
|
}
|
|
|
|
function onFilesStateChange(xfiles, state) {
|
|
p13Connect.value = (state == 0) ? "Connect" : "Disconnect";
|
|
var str = StatusStrs[state];
|
|
if (files.webRtcActive == true) { str += ", WebRTC"; }
|
|
Q('p13Status').textContent = str;
|
|
switch (state) {
|
|
case 0:
|
|
// Disconnected, clear the files
|
|
QH('p13files', '');
|
|
p13filetree = null;
|
|
p13filetreelocation = [];
|
|
QH('p13currentpath', '');
|
|
QE('p13FolderUp', false);
|
|
QV('filesRecordIcon', false);
|
|
p13setActions();
|
|
if (files != null) { files.Stop(); files = null; }
|
|
break;
|
|
case 3:
|
|
p13targetpath = '';
|
|
if (files) {
|
|
files.sendText({ action: 'ls', reqid: 1, path: '' });
|
|
if (files.serverIsRecording == true) { QV('filesRecordIcon', true); }
|
|
}
|
|
break;
|
|
default:
|
|
//console.log('Unknown onFilesStateChange state', state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function CreateRemoteFiles(onFileUpdate) {
|
|
var obj = { protocol: 5 };
|
|
obj.onFileUpdate = onFileUpdate;
|
|
obj.xxStateChange = function(state) { }
|
|
obj.ProcessData = function(data) { obj.onFileUpdate(data); }
|
|
return obj;
|
|
}
|
|
|
|
// Debug Only
|
|
var autoConnectFilesTimer = null;
|
|
function autoConnectFiles(e) { if (autoConnectFilesTimer == null) { autoConnectFilesTimer = setInterval(connectFiles, 100); } else { clearInterval(autoConnectFilesTimer); autoConnectFilesTimer = null; } }
|
|
|
|
function connectFiles(e) {
|
|
p13clearConsoleMsg();
|
|
if (!files) {
|
|
// Setup a mesh agent files
|
|
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
|
files.attemptWebRTC = attemptWebRTC;
|
|
files.onStateChanged = onFilesStateChange;
|
|
files.onConsoleMessageChange = function () {
|
|
if (files.consoleMessage) {
|
|
Q('p13FilesConsoleMsg').innerHTML += EscapeHtml(files.consoleMessage).split('\n').join('<br />') + '<br /><br />';
|
|
QV('p13FilesConsoleMsg', true);
|
|
if (p13FilesConsoleMsgTimer != null) { clearTimeout(p13FilesConsoleMsgTimer); }
|
|
p13FilesConsoleMsgTimer = setTimeout(p13clearConsoleMsg, 8000);
|
|
}
|
|
}
|
|
files.Start(filesNode._id);
|
|
} else {
|
|
//QH('Term', '');
|
|
files.Stop();
|
|
files = null;
|
|
}
|
|
p13clipboard = p13clipboardFolder = null;
|
|
p13clipboardCut = 0;
|
|
p13updateClipview();
|
|
}
|
|
|
|
var p13filetree = null;
|
|
var p13targetpath = null;
|
|
var p13filetreelocation = [];
|
|
|
|
function p13gotFiles(data) {
|
|
if ((data.length > 0) && (data.charCodeAt(0) != 123)) { p13gotDownloadBinaryData(data); return; } // This is ok because 4 first bytes is a control value.
|
|
//console.log('p13gotFiles', data);
|
|
data = JSON.parse(decode_utf8(data));
|
|
if (data.action == 'download') { p13gotDownloadCommand(data); return; }
|
|
data.path = data.path.replace(/\//g, '\\');
|
|
if ((p13filetree != null) && (data.path == p13filetree.path)) {
|
|
// This is an update to the same folder
|
|
var checkedNames = p13getCheckedNames();
|
|
p13filetree = data;
|
|
p13updateFiles(checkedNames);
|
|
} else {
|
|
// Make both paths use the same seperator not start with /
|
|
var x1 = data.path.replace(/\//g, '\\'), x2 = p13targetpath.replace(/\//g, '\\');
|
|
while ((x1.length > 0) && (x1[0] == '\\')) { x1 = x1.substring(1); }
|
|
while ((x2.length > 0) && (x2[0] == '\\')) { x2 = x2.substring(1); }
|
|
if ((x1 == x2) || ((data.path == '\\') && (p13targetpath == ''))) {
|
|
// This is a different folder
|
|
p13filetree = data;
|
|
p13updateFiles();
|
|
}
|
|
}
|
|
}
|
|
|
|
function p13getCheckedNames() {
|
|
// Save all existing checked boxes
|
|
var checkedNames = [], checkboxes = document.getElementsByName('fd');
|
|
for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedNames.push(p13filetree.dir[checkboxes[i].value].n) }; }
|
|
return checkedNames;
|
|
}
|
|
|
|
function p13updateFiles(checkedNames) {
|
|
var html1 = '', html2 = '', displayPath = '<a href=# style=cursor:pointer onclick="return p13folderup(0)">' + "Root" + '</a>', fullPath = 'Root';
|
|
|
|
// Work on parsing the file path
|
|
var x = p13filetree.path.split('\\');
|
|
p13filetreelocation = [];
|
|
for (var i in x) { if (x[i] != '') { p13filetreelocation.push(x[i]); } } // Remove empty spaces
|
|
for (var i in p13filetreelocation) { displayPath += ' / <a href=# style=cursor:pointer onclick="return p13folderup(' + (parseInt(i) + 1) + ')">' + p13filetreelocation[i] + '</a>' } // Setup the path we display
|
|
var newlinkpath = p13filetreelocation.join('/');
|
|
|
|
// Sort the files
|
|
var filetreexx = p13sort_files(p13filetree.dir);
|
|
|
|
// Display all files and folders at this location
|
|
for (var i in filetreexx) {
|
|
// Figure out the name and shortname
|
|
var f = filetreexx[i], name = f.n, shortname;
|
|
shortname = name;
|
|
if (name.length > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + ("..." + '</span>'); } else { shortname = EscapeHtml(name); }
|
|
name = EscapeHtml(name);
|
|
|
|
// Figure out the date
|
|
var fdatestr = '';
|
|
if (f.d != null) { var fdate = new Date(f.d), fdatestr = printDateTime(fdate) + ' '; }
|
|
|
|
// Figure out the size
|
|
var fsize = '';
|
|
if (f.s != null) { fsize = getFileSizeStr(f.s); }
|
|
|
|
var h = '';
|
|
if (f.t < 3) {
|
|
var right = '', title = '';
|
|
h = '<div class=filelist file=999><input file=999 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'> <span style=float:right title=\"' + title + '\">' + right + '</span><span><div class=fileIcon' + f.t + ' onclick=p13folderset(\"' + encodeURIComponent(f.nx) + '\")></div><a href=# style=cursor:pointer onclick=\'return p13folderset(\"' + encodeURIComponent(f.nx) + '\")\'>' + shortname + '</a></span></div>';
|
|
} else {
|
|
var link = shortname;
|
|
if (f.s > 0) { link = '<a hrf=# rel=\"noreferrer noopener\" target=\"_blank\" style=cursor:pointer onclick=\"return p13downloadfile(\'' + encodeURIComponent(newlinkpath + '/' + name) + '\',\'' + encodeURIComponent(name) + '\',' + f.s + ')\">' + shortname + '</a>'; }
|
|
h = '<div id=fileEntry cmenu=filesContextMenu fileIndex=' + i + ' class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'> <span class=fsize>' + fdatestr + '</span><span style=float:right>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
|
|
}
|
|
|
|
if (f.t < 3) { html1 += h; } else { html2 += h; }
|
|
}
|
|
|
|
// Display the files and path
|
|
QH('p13files', html1 + html2);
|
|
QH('p13currentpath', displayPath);
|
|
QE('p13FolderUp', p13filetreelocation.length != 0);
|
|
|
|
// Re-check all boxes if needed using names
|
|
if (checkedNames != null) { var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkedNames.indexOf(p13filetree.dir[checkboxes[i].value].n) >= 0) { checkboxes[i].checked = true; } } }
|
|
|
|
// Update the actions buttons
|
|
p13setActions();
|
|
}
|
|
|
|
function p13folderset(x) {
|
|
p13targetpath = joinPaths(p13filetree.path, p13filetree.dir[x].n).split('\\').join('/');
|
|
files.sendText({ action: 'ls', reqid: 1, path: p13targetpath });
|
|
}
|
|
|
|
function p13folderup(x) {
|
|
if (x == null) { p13filetreelocation.pop(); } else { while (p13filetreelocation.length > x) { p13filetreelocation.pop(); } }
|
|
p13targetpath = p13filetreelocation.join('/');
|
|
files.sendText({ action: 'ls', reqid: 1, path: p13targetpath });
|
|
return false;
|
|
}
|
|
|
|
var p13sortorder;
|
|
function p13sort_filename(a, b) { if (a.ln > b.ln) return (1 * p13sortorder); if (a.ln < b.ln) return (-1 * p13sortorder); return 0; }
|
|
function p13sort_timestamp(a, b) { if (a.d > b.d) return (1 * p13sortorder); if (a.d < b.d) return (-1 * p13sortorder); return 0; }
|
|
function p13sort_bysize(a, b) { if (a.s == b.s) return p13sort_filename(a, b); return (((a.s - b.s)) * p13sortorder); }
|
|
|
|
function p13sort_files(files) {
|
|
var r = [], sortselection = Q('p13sortdropdown').value;
|
|
for (var i in files) { files[i].nx = i; if (files[i].s == null) { files[i].s = 0; } if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
|
|
p13sortorder = 1;
|
|
if (sortselection > 3) { p13sortorder = -1; sortselection -= 3; }
|
|
if (sortselection == 1) { r.sort(p13sort_filename); }
|
|
else if (sortselection == 2) { r.sort(p13sort_bysize); }
|
|
else if (sortselection == 3) { r.sort(p13sort_timestamp); }
|
|
return r;
|
|
}
|
|
|
|
function p13setActions() {
|
|
if (p13filetree == null) {
|
|
QE('p13DeleteFileButton', false);
|
|
QE('p13NewFolderButton', false);
|
|
QE('p13UploadButton', false);
|
|
QE('p13RenameFileButton', false);
|
|
QE('p13ViewFileButton', false);
|
|
QE('p13SelectAllButton', false);
|
|
Q('p13SelectAllButton').value = "Select All";
|
|
QE('p13RefreshButton', false);
|
|
QE('p13CutButton', false);
|
|
QE('p13CopyButton', false);
|
|
QE('p13PasteButton', false);
|
|
} else {
|
|
var cc = p13getFileSelCount(), tc = p13getFileCount(), sfc = p13getFileSelCount(false); // In order: number of entires selected, number of total entries, number of selected entires that are files (not folders)
|
|
var winAgent = ((currentNode.agent.id > 0) && (currentNode.agent.id < 5));
|
|
QE('p13DeleteFileButton', (cc > 0) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
|
QE('p13NewFolderButton', ((p13filetreelocation.length > 0) || (winAgent == false)));
|
|
QE('p13UploadButton', ((p13filetreelocation.length > 0) || (winAgent == false)));
|
|
QE('p13RenameFileButton', (cc == 1) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
|
QE('p13ViewFileButton', (cc == 1) && (sfc == 1) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
|
QE('p13SelectAllButton', tc > 0);
|
|
Q('p13SelectAllButton').value = (cc > 0 ? "Select None" : "Select All");
|
|
QE('p13RefreshButton', true);
|
|
QE('p13CutButton', (cc > 0) && (cc == sfc) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
|
QE('p13CopyButton', (cc > 0) && (cc == sfc) && ((p13filetreelocation.length > 0) || (winAgent == false)));
|
|
QE('p13PasteButton', ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0)));
|
|
}
|
|
}
|
|
|
|
function p13getFileSelCount(includeDirs) { var cc = 0; var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == '3'))) cc++; } return cc; }
|
|
function p13getFileSelDirCount() { var cc = 0, checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '999')) cc++; } return cc; }
|
|
function p13getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fd'); return checkboxes.length; }
|
|
function p13selectallfile() { var nv = (p13getFileSelCount() == 0), checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p13setActions(); }
|
|
function p13createfolder() { setDialogMode(2, "New Folder", 3, p13createfolderEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% />'); focusTextBox('p13renameinput'); p13fileNameCheck(); }
|
|
function p13createfolderEx() { files.sendText({ action: 'mkdir', reqid: 1, path: p13filetreelocation.join('/') + '/' + Q('p13renameinput').value }); p13folderup(999); }
|
|
function p13deletefile() { var cc = p13getFileSelCount(), rec = (p13getFileSelDirCount() > 0) ? '<br /><br /><label><input type=checkbox id=p13recdeleteinput>' + "Recursive delete" + '</label><br>' : '<input type=checkbox id=p13recdeleteinput style=\'display:none\'>'; setDialogMode(2, "Delete", 3, p13deletefileEx, (cc > 1) ? (format("Delete {0} selected items?", cc) + rec) : ("Delete selected item?" + rec)); }
|
|
function p13deletefileEx() { var delfiles = [], checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { delfiles.push(p13filetree.dir[checkboxes[i].value].n); } } files.sendText({ action: 'rm', reqid: 1, path: p13filetreelocation.join('/'), delfiles: delfiles, rec: Q('p13recdeleteinput').checked }); p13folderup(999); }
|
|
function p13renamefile() { var renamefile, checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { renamefile = p13filetree.dir[checkboxes[i].value].n; } } setDialogMode(2, "Rename", 3, p13renamefileEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% value="' + renamefile + '" />', { action: 'rename', path: p13filetreelocation.join('/'), oldname: renamefile}); focusTextBox('p13renameinput'); p13fileNameCheck(); }
|
|
function p13renamefileEx(b, t) { t.newname = Q('p13renameinput').value; files.sendText(t); p13folderup(999); }
|
|
function p13fileNameCheck(e) { var x = isFilenameValid(Q('p13renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); } }
|
|
function p13uploadFile() { setDialogMode(2, "Upload File", 3, p13uploadFileEx, '<input type=file name=files id=p13uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p13uploadinput\')" />'); updateUploadDialogOk('p13uploadinput'); }
|
|
function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q('p13uploadinput').files.length > 0); }
|
|
function p13uploadFileEx() { p13doUploadFiles(Q('p13uploadinput').files); }
|
|
function p13viewfile() {
|
|
var checkboxes = document.getElementsByName('fd');
|
|
for (var i = 0; i < checkboxes.length; i++) {
|
|
if (checkboxes[i].checked) {
|
|
if (p13filetree.dir[checkboxes[i].value].s <= 204800) {
|
|
p13downloadfile(encodeURIComponent(p13filetreelocation.join('/') + '/' + p13filetree.dir[checkboxes[i].value].n), encodeURIComponent(p13filetree.dir[checkboxes[i].value].n), p13filetree.dir[checkboxes[i].value].s, 'viewer');
|
|
} else { messagebox("File Editor", "Only files less than 200k can be edited."); }
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
var p13clipboard = null, p13clipboardFolder = null, p13clipboardCut = 0;
|
|
function p13copyFile(cut) { var checkboxes = document.getElementsByName('fd'); p13clipboard = []; p13clipboardCut = cut, p13clipboardFolder = p13targetpath; for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p13clipboard.push(p13filetree.dir[checkboxes[i].value].n); } } p13updateClipview(); }
|
|
function p13pasteFile() {
|
|
var x = '';
|
|
if ((p13clipboard != null) && (p13clipboard.length > 0)) {
|
|
if (p13clipboardCut == 0) {
|
|
if (p13clipboard.length > 1) { x = format("Confirm copy of {0} entries's to this location?", p13clipboard.length); } else { x = format("Confirm copy of 1 entrie to this location?"); }
|
|
} else {
|
|
if (p13clipboard.length > 1) { x = format("Confirm move of {0} entries's to this location?", p13clipboard.length); } else { x = format("Confirm move of 1 entrie to this location?"); }
|
|
}
|
|
}
|
|
setDialogMode(2, "Paste", 3, p13pasteFileEx, x);
|
|
}
|
|
function p13pasteFileEx() { files.sendText({ action: (p13clipboardCut == 0?'copy':'move'), reqid: 1, scpath: p13clipboardFolder, dspath: p13targetpath, names: p13clipboard }); p13folderup(999); if (p13clipboardCut == 1) { p13clipboard = null, p13clipboardFolder = null, p13clipboardCut = 0; p13updateClipview(); } }
|
|
function p13updateClipview() {
|
|
var x = '';
|
|
if ((p13clipboard != null) && (p13clipboard.length > 0)) {
|
|
if (p13clipboardCut == 0) {
|
|
if (p13clipboard.length > 1) {
|
|
x = format("Holding {0} entries for copy" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.', p13clipboard.length);
|
|
} else {
|
|
x = format("Holding 1 entrie for copy" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.');
|
|
}
|
|
} else {
|
|
if (p13clipboard.length > 1) {
|
|
x = format("Holding {0} entries for move" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.', p13clipboard.length);
|
|
} else {
|
|
x = format("Holding 1 entrie for move" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.');
|
|
}
|
|
}
|
|
}
|
|
QH('p13bottomstatus', x);
|
|
p13setActions();
|
|
}
|
|
function p13clearClip() { p13clipboard = null; p13clipboardFolder = null; p13clipboardCut = 0; p13updateClipview(); return false; }
|
|
|
|
function p13fileDragDrop(e) {
|
|
haltEvent(e);
|
|
QV('p13bigfail', false);
|
|
QV('p13bigok', false);
|
|
if (e.dataTransfer == null || e.dataTransfer.files.length == 0 || p13filetree == null) return;
|
|
|
|
// Check if these are files we can upload, remove all folders.
|
|
var files = [];
|
|
for (var i in e.dataTransfer.files) { if ((e.dataTransfer.files[i].size != null) && (e.dataTransfer.files[i].size != 0)) { files.push(e.dataTransfer.files[i]); } }
|
|
if (files.length == 0) return;
|
|
|
|
p13doUploadFiles(files);
|
|
}
|
|
|
|
var p13dragtimer = null;
|
|
function p13fileDragOver(e) {
|
|
haltEvent(e);
|
|
if (p13dragtimer != null) { clearTimeout(p13dragtimer); p13dragtimer = null; }
|
|
var ac = (p13filetree != null); // Set to true if we can accept the file
|
|
QV('p13bigok', ac);
|
|
QV('p13bigfail', !ac);
|
|
}
|
|
|
|
function p13fileDragLeave(e) {
|
|
haltEvent(e);
|
|
if (e.target.id != 'p13filetable') {
|
|
QV('p13bigfail', false);
|
|
QV('p13bigok', false);
|
|
} else {
|
|
p13dragtimer = setTimeout(function () { QV('p13bigfail',false); QV('p13bigok',false); p13dragtimer=null; }, 10);
|
|
}
|
|
}
|
|
|
|
//
|
|
// FILES DOWNLOAD
|
|
//
|
|
|
|
var downloadFile; // Global state for file download
|
|
|
|
// Called by the html page to start a download, arguments are: path, file name and file size.
|
|
function p13downloadfile(x, y, z, tag) {
|
|
if (xxdialogMode || downloadFile || !files) return;
|
|
downloadFile = { path: decodeURIComponent(x), file: decodeURIComponent(y), size: z, tsize: 0, data: '', state: 0, id: Math.random(), tag: tag }
|
|
//console.log('p13downloadFileCancel', downloadFile);
|
|
files.sendText({ action: 'download', sub: 'start', id: downloadFile.id, path: downloadFile.path });
|
|
setDialogMode(2, "Download File", 10, p13downloadFileCancel, '<div>' + downloadFile.file + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=' + z + ' />');
|
|
}
|
|
|
|
// Called by the html page to cancel the download
|
|
function p13downloadFileCancel() { setDialogMode(0); files.sendText({ action: 'download', sub: 'cancel', id: downloadFile.id }); downloadFile = null; }
|
|
|
|
// Called by the transport when download control command is received
|
|
function p13gotDownloadCommand(cmd) {
|
|
//console.log('p13gotDownloadCommand', cmd);
|
|
if ((downloadFile == null) || (cmd.id != downloadFile.id)) return;
|
|
if (cmd.sub == 'start') { downloadFile.state = 1; files.sendText({ action: 'download', sub: 'startack', id: downloadFile.id }); }
|
|
else if (cmd.sub == 'cancel') { downloadFile = null; setDialogMode(0); }
|
|
}
|
|
|
|
// Called by the transport when binary data is received
|
|
function p13gotDownloadBinaryData(data) {
|
|
if (!downloadFile || downloadFile.state == 0) return;
|
|
if (data.length > 4) {
|
|
downloadFile.tsize += (data.length - 4); // Add to the total bytes received
|
|
downloadFile.data += data.substring(4); // Append the data
|
|
Q('d2progressBar').value = downloadFile.tsize; // Change the progress bar
|
|
}
|
|
if ((ReadInt(data, 0) & 1) != 0) { // Check end flag
|
|
if (downloadFile.tag == 'viewer') {
|
|
// View the file in the dialog box
|
|
setDialogMode(4, EscapeHtml(downloadFile.file), 3, p13editSaveBack, null, downloadFile.file);
|
|
QS('dialog').width = 'auto';
|
|
QS('dialog').bottom = '80px';
|
|
QS('dialog').top = QS('dialog').left = QS('dialog').right = '100px';
|
|
Q('d4editorarea').value = downloadFile.data;
|
|
downloadFile = null;
|
|
} else {
|
|
// Save the file to disk
|
|
saveAs(data2blob(downloadFile.data), downloadFile.file); downloadFile = null; setDialogMode(0); // Save the file
|
|
}
|
|
} else {
|
|
files.sendText({ action: 'download', sub: 'ack', id: downloadFile.id }); // Send the ACK
|
|
}
|
|
}
|
|
|
|
var d4EditWrapVal = 0;
|
|
var d4EditSizeVal = 0;
|
|
function d4ToggleWrap(update) {
|
|
if (!update) { d4EditWrapVal = ++d4EditWrapVal % 2; }
|
|
Q('d4WrapButton').value = ["Wrap: ON","Wrap: OFF"][d4EditWrapVal];
|
|
QS('d4editorarea').overflow = (d4EditWrapVal == 0)?'auto':'scroll';
|
|
QS('d4editorarea')['white-space'] = (d4EditWrapVal == 0)?null:'pre';
|
|
putstore('editorWrap', d4EditWrapVal);
|
|
}
|
|
|
|
function d4ToggleSize(update) {
|
|
if (!update) { d4EditSizeVal = ++d4EditSizeVal % 4; }
|
|
QS('d4editorarea')['font-size'] = ['100%','125%','150%','200%'][d4EditSizeVal];
|
|
Q('d4SizeButton').value = ["Size: 100%","Size: 125%","Size: 150%","Size: 200%"][d4EditSizeVal];
|
|
putstore('editorSize', d4EditSizeVal);
|
|
}
|
|
|
|
function p13editSaveBack(b, tag) {
|
|
var data = new TextEncoder().encode(Q('d4editorarea').value);
|
|
p13uploadFileContinue(1, [{ name: tag, size: data.byteLength, type: 'text/plain', xdata: data }]);
|
|
}
|
|
|
|
/*
|
|
var downloadFile; // Global state for file download
|
|
|
|
// Called by the html page to start a download, arguments are: path, file name and file size.
|
|
function p13downloadfile(x, y, z) {
|
|
if (xxdialogMode) return;
|
|
downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); // Create our websocket file transport
|
|
downloadFile.ctrlMsgAllowed = false;
|
|
downloadFile.onStateChanged = onFileDownloadStateChange;
|
|
downloadFile.xpath = decodeURIComponent(x);
|
|
downloadFile.xfile = decodeURIComponent(y);
|
|
downloadFile.xsize = z;
|
|
downloadFile.xtsize = 0;
|
|
downloadFile.xstate = 0;
|
|
downloadFile.Start(filesNode._id);
|
|
setDialogMode(2, "Download File", 10, p13downloadFileCancel, '<div>' + downloadFile.xfile + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=' + z + ' />');
|
|
}
|
|
|
|
// Called by the html page to cancel the download
|
|
function p13downloadFileCancel(button, tag) {
|
|
//console.log('p13downloadFileCancel');
|
|
downloadFile.Stop();
|
|
delete downloadFile;
|
|
downloadFile = null;
|
|
}
|
|
|
|
// Called by the file transport to indicate when the transport connection state has changed
|
|
function onFileDownloadStateChange(xdownloadFile, state) {
|
|
switch (state) {
|
|
case 0: // Transport as disconnected. If this is not part of an abort, we need to save the file
|
|
setDialogMode(0); // Close any dialog boxes if present
|
|
if ((downloadFile != null) && (downloadFile.xstate == 1)) { saveAs(data2blob(downloadFile.xdata), downloadFile.xfile); } // Save the file
|
|
break;
|
|
case 3: // Transport as connected, send a command to indicate we want to start a file download
|
|
downloadFile.send(JSON.stringify({ action: 'download', reqid: 1, path: downloadFile.xpath }));
|
|
break;
|
|
default:
|
|
console.log('Unknown onFileDownloadStateChange state', state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Called by the transport when data is received
|
|
function p13gotDownloadData(data) {
|
|
if (downloadFile.xstate == 0) { // If state is 0, this is a command confirming if the file will be transfered.
|
|
var cmd = JSON.parse(data);
|
|
if (cmd.action == 'downloadstart') { // Yes, the file is about to start
|
|
downloadFile.xstate = 1; // Switch to state 1, we will start receiving the file data
|
|
downloadFile.xdata = ''; // Start with empty data
|
|
downloadFile.send('a'); // Send the first ACK
|
|
} else if (cmd.action == 'downloaderror') { // Problem opening this file, cancel
|
|
p13downloadFileCancel();
|
|
}
|
|
} else { // We are in the process of receiving the file
|
|
downloadFile.xtsize += (data.length); // Add to the total bytes received
|
|
downloadFile.xdata += data; // Append the data
|
|
Q('d2progressBar').value = downloadFile.xtsize; // Change the progress bar
|
|
downloadFile.send('a'); // Send the ACK
|
|
}
|
|
}
|
|
*/
|
|
|
|
//
|
|
// FILES UPLOAD
|
|
//
|
|
|
|
var uploadFile;
|
|
function p13doUploadFiles(files) {
|
|
if (xxdialogMode) return;
|
|
|
|
// Check if we are going to overwrite any files
|
|
var winAgent = ((currentNode.agent.id > 0) && (currentNode.agent.id < 5));
|
|
var targetFiles = [], overWriteCount = 0;
|
|
for (var i in p13filetree.dir) { if (winAgent) { targetFiles.push(p13filetree.dir[i].n.toLowerCase()); } else { targetFiles.push(p13filetree.dir[i].n); } }
|
|
for (var i = 0; i < files.length; i++) {
|
|
if (winAgent) {
|
|
if (targetFiles.indexOf(files[i].name.toLowerCase()) >= 0) { overWriteCount++; }
|
|
} else {
|
|
if (targetFiles.indexOf(files[i].name) >= 0) { overWriteCount++; }
|
|
}
|
|
}
|
|
|
|
if (overWriteCount == 0) {
|
|
// If no overwrite, go ahead with upload
|
|
p13uploadFileContinue(1, files);
|
|
} else {
|
|
// Otherwise, prompt for confirmation
|
|
setDialogMode(2, "Upload File", 3, p13uploadFileContinue, format((overWriteCount == 1)?"Upload will overwrite 1 file. Continue?":"Upload will overwrite {0} files. Continue?", overWriteCount), files);
|
|
}
|
|
}
|
|
|
|
function p13uploadFileContinue(b, files) {
|
|
uploadFile = {};
|
|
uploadFile.xpath = p13filetreelocation.join('/');
|
|
uploadFile.xfiles = files;
|
|
uploadFile.xfilePtr = -1;
|
|
setDialogMode(2, "Upload File", 10, p13uploadFileCancel, '<div id=p13dfileName>' + "Connecting..." + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=0 />');
|
|
p13uploadReconnect();
|
|
}
|
|
|
|
function onFileUploadStateChange(xdownloadFile, state) {
|
|
switch (state) {
|
|
case 0:
|
|
setTimeout(function () { p13folderup(9999); }, 200); // Delay the file refresh
|
|
break;
|
|
case 3:
|
|
p13uploadNextFile();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Connect again
|
|
function p13uploadReconnect() {
|
|
uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
|
|
uploadFile.ws.attemptWebRTC = false;
|
|
uploadFile.ws.ctrlMsgAllowed = false;
|
|
uploadFile.ws.onStateChanged = onFileUploadStateChange;
|
|
uploadFile.ws.Start(filesNode._id);
|
|
}
|
|
|
|
// Push the next file
|
|
function p13uploadNextFile() {
|
|
uploadFile.xfilePtr++;
|
|
if (uploadFile.xfiles.length > uploadFile.xfilePtr) {
|
|
uploadFile.xptr = 0;
|
|
var file = uploadFile.xfiles[uploadFile.xfilePtr];
|
|
QH('p13dfileName', file.name);
|
|
Q('d2progressBar').max = file.size;
|
|
Q('d2progressBar').value = 0;
|
|
|
|
if (file.xdata == null) {
|
|
// Load the data
|
|
uploadFile.xreader = new FileReader();
|
|
uploadFile.xreader.onload = function () {
|
|
uploadFile.xdata = uploadFile.xreader.result;
|
|
uploadFile.ws.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength }));
|
|
};
|
|
uploadFile.xreader.readAsArrayBuffer(file);
|
|
} else {
|
|
// Data already loaded
|
|
uploadFile.xdata = file.xdata;
|
|
uploadFile.ws.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength }));
|
|
}
|
|
} else {
|
|
p13uploadFileCancel();
|
|
}
|
|
}
|
|
|
|
// Used to cancel the entire transfer.
|
|
function p13uploadFileCancel(button, tag) {
|
|
if (uploadFile != null) {
|
|
if (uploadFile.ws != null) {
|
|
uploadFile.ws.Stop();
|
|
uploadFile.ws = null;
|
|
}
|
|
uploadFile = null;
|
|
}
|
|
setDialogMode(0); // Close any dialog boxes if present
|
|
}
|
|
|
|
// Receive upload ack from the mesh agent, use this to keep sending more data
|
|
function p13gotUploadData(data) {
|
|
var cmd = JSON.parse(data);
|
|
if ((uploadFile == null) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
|
|
|
|
if (cmd.action == 'uploadstart') {
|
|
p13uploadNextPart(false);
|
|
for (var i = 0; i < 8; i++) { p13uploadNextPart(true); } // Send 8 more blocks of 4 k to full the websocket.
|
|
} else if (cmd.action == 'uploadack') {
|
|
p13uploadNextPart(false);
|
|
} else if (cmd.action == 'uploaderror') {
|
|
p13uploadFileCancel();
|
|
}
|
|
}
|
|
|
|
// Push the next part of the file into the websocket. If dataPriming is true, push more data only if it's not the last block of the file.
|
|
function p13uploadNextPart(dataPriming) {
|
|
var data = uploadFile.xdata;
|
|
var start = uploadFile.xptr;
|
|
var end = uploadFile.xptr + 4096;
|
|
if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; }
|
|
if (start == data.byteLength) {
|
|
if (uploadFile.ws != null) { uploadFile.ws.Stop(); uploadFile.ws = null; }
|
|
if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadReconnect(); } else { p13uploadFileCancel(); }
|
|
} else {
|
|
var datapart = data.slice(start, end);
|
|
uploadFile.ws.send(datapart);
|
|
uploadFile.xptr = end;
|
|
Q('d2progressBar').value = end;
|
|
}
|
|
}
|
|
|
|
//
|
|
// DEVICE EVENTS
|
|
//
|
|
|
|
var currentDeviceEvents = null;
|
|
function deviceEventsUpdate() {
|
|
var x = '', dateHeader = null;
|
|
for (var i in currentDeviceEvents) {
|
|
var event = currentDeviceEvents[i], time = new Date(event.time);
|
|
if (event.msg) {
|
|
if (event.h == null) { event.h = Math.random(); }
|
|
if (printDate(time) != dateHeader) {
|
|
if (dateHeader != null) x += '</table>';
|
|
dateHeader = printDate(time);
|
|
x += '<table class=p3eventsTable cellpadding=0 cellspacing=0><tr><td colspan=4 class=DevSt>' + dateHeader + '</td></tr>';
|
|
}
|
|
var icon = 'si3';
|
|
if (event.etype == 'user') icon = 'm2';
|
|
if (event.etype == 'server') icon = 'si3';
|
|
|
|
var msg = EscapeHtml(event.msg).split('(R)').join('®');
|
|
if (event.username) {
|
|
if ((userinfo.siteadmin & 2) && (event.userid)) {
|
|
msg = '<a href=# onclick=\'gotoUser("' + encodeURIComponent(event.userid) + '");haltEvent(event);\'>' + EscapeHtml(event.username) + '</a> → ' + msg;
|
|
} else {
|
|
msg = EscapeHtml(event.username) + ' → ' + msg;
|
|
}
|
|
}
|
|
if (event.etype == 'relay' || event.action == 'relaylog') icon = 'relayIcon16';
|
|
x += '<tr onclick=showEventDetails(' + event.h + ',1) onmouseover=eventMouseHover(this,1) onmouseout=eventMouseHover(this,0) style=cursor:pointer><td style=width:18px><div class=' + icon + '></div></td><td class=g1> </td><td class=style10>' + printTime(time) + ' - ' + msg + '</td><td class=g2> </td></tr><tr style=height:2px></tr>';
|
|
}
|
|
}
|
|
if (dateHeader != null) x += '</table>';
|
|
if (x == '') x = '<br><i>' + "No Events Found" + '</i><br><br>';
|
|
QH('p16events', x);
|
|
}
|
|
|
|
function refreshDeviceEvents() {
|
|
meshserver.send({ action: 'events', nodeid: currentNode._id, limit: parseInt(p16limitdropdown.value) });
|
|
}
|
|
|
|
function showEventDetails(h, mode) {
|
|
var eventList, xevent;
|
|
if (mode == 1) { eventList = currentDeviceEvents; }
|
|
if (mode == 2) { eventList = events; }
|
|
if (mode == 3) { eventList = currentUserEvents; }
|
|
for (var i in eventList) { if (eventList[i].h == h) { xevent = eventList[i]; break; } }
|
|
if (xevent) {
|
|
if (xxdialogMode) return false;
|
|
var x = '<div style=overflow-y:auto>';
|
|
for (var i in xevent) {
|
|
if ((i == 'h') || (i == '_id') || (i == 'ids') || (i == 'domain') || (xevent[i] == null) || (typeof xevent[i] == 'object')) continue;
|
|
x += addHtmlValue3(EscapeHtml(i), EscapeHtml(xevent[i]));
|
|
}
|
|
x += '</div>';
|
|
setDialogMode(2, "Event Details", 9, null, x);
|
|
}
|
|
}
|
|
|
|
//
|
|
// DEVICE DETAILS
|
|
//
|
|
|
|
var DeviceDetailsHardware = null;
|
|
var DeviceDetailsNetwork = null;
|
|
var DeviceDetailsNodeId = null;
|
|
function updateDeviceDetails(node, hardware, network) {
|
|
if (currentNode == null) return;
|
|
if (node == null) { node = currentNode; }
|
|
if (currentNode._id != node._id) return;
|
|
if (DeviceDetailsNodeId != node._id) { DeviceDetailsHardware = null; DeviceDetailsNetwork = null; DeviceDetailsNodeId = node._id; }
|
|
if (hardware != null) { DeviceDetailsHardware = hardware; }
|
|
if (network != null) { DeviceDetailsNetwork = network; }
|
|
hardware = DeviceDetailsHardware;
|
|
network = DeviceDetailsNetwork;
|
|
if (hardware == null) { hardware = {}; }
|
|
if (network == null) { network = {}; }
|
|
var sections = [], s = {};
|
|
|
|
// Operating System
|
|
if ((hardware.windows && hardware.windows.osinfo) || node.osdesc) {
|
|
var x = '';
|
|
if (node.rname) { x += addDetailItem("Name", EscapeHtml(node.rname), s); }
|
|
if (node.osdesc) { x += addDetailItem("Version", EscapeHtml(node.osdesc), s); }
|
|
if (hardware.windows && hardware.windows.osinfo) {
|
|
var m = hardware.windows.osinfo;
|
|
if (m.OSArchitecture) { x += addDetailItem("Architecture", EscapeHtml(m.OSArchitecture), s); }
|
|
}
|
|
if (x != '') { sections.push({ name: "Operating System", html: x, img: 'software64.png'}); }
|
|
}
|
|
|
|
// MeshAgent
|
|
if (node.agent) {
|
|
var x = '';
|
|
if ((node.agent != null) && (node.agent.id != null) && (node.agent.ver != null)) {
|
|
var str = '';
|
|
if (node.agent.id <= agentsStr.length) { str = agentsStr[node.agent.id]; } else { str = agentsStr[0]; }
|
|
if (node.agent.ver != 0) { str += ' v' + node.agent.ver; }
|
|
x += addDetailItem("Mesh Agent", str);
|
|
}
|
|
if ((node.conn & 1) != 0) {
|
|
x += addDetailItem("Last agent connection", "Connected now");
|
|
} else {
|
|
if (node.lastconnect) { x += addDetailItem("Last agent connection", printDateTime(new Date(node.lastconnect))); }
|
|
}
|
|
if (node.lastaddr) {
|
|
var splitip = node.lastaddr.split(':');
|
|
if (splitip.length > 2) {
|
|
// IPv6
|
|
x += addDetailItem("Last agent address", node.lastaddr);
|
|
} else {
|
|
// IPv4
|
|
if (isPrivateIP(node.lastaddr)) {
|
|
x += addDetailItem("Last agent address", splitip[0]);
|
|
} else {
|
|
x += addDetailItem("Last agent address", '<a href="https://iplocation.com/?ip=' + splitip[0] + '" rel="noreferrer noopener" target="MeshIPLoopup">' + splitip[0] + '</a>');
|
|
}
|
|
}
|
|
}
|
|
if (x != '') { sections.push({ name: "Mesh Agent", html: x, img: 'meshagent64.png'}); }
|
|
}
|
|
|
|
// Networking
|
|
if (network.netif != null) {
|
|
var x = '';
|
|
x += '<table style=width:100%>';
|
|
for (var i in network.netif) {
|
|
var m = network.netif[i];
|
|
x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
|
|
x += '<div style=margin-bottom:3px><b>' + EscapeHtml(m.name + (m.dnssuffix?(', ' + m.dnssuffix):'')) + '</b></div>';
|
|
if (m.desc) { x += addDetailItem("Description", EscapeHtml(m.desc).split('(R)').join('®')); }
|
|
//if (m.dnssuffix) { x += addDetailItem("DNS Suffix", m.dnssuffix); }
|
|
if (m.mac) {
|
|
if (m.gatewaymac) {
|
|
x += addDetailItem("MAC Layer", format("MAC: {0}, Gateway: {1}", EscapeHtml(m.mac), EscapeHtml(m.gatewaymac)));
|
|
} else {
|
|
x += addDetailItem("MAC Layer", format("MAC: {0}", m.mac));
|
|
}
|
|
}
|
|
if (m.v4addr && (m.v4addr != '0.0.0.0')) {
|
|
if (m.v4gateway && m.v4mask) {
|
|
x += addDetailItem("IPv4 Layer", format("IP: {0}, Mask: {1}, Gateway: {2}", EscapeHtml(m.v4addr), EscapeHtml(m.v4mask), EscapeHtml(m.v4gateway)));
|
|
} else {
|
|
x += addDetailItem("IPv4 Layer", format("IP: {0}", EscapeHtml(m.v4addr)));
|
|
}
|
|
}
|
|
x += '</div>';
|
|
}
|
|
x += '</table>';
|
|
if (x != '') { sections.push({ name: "Networking", html: x, img: 'networking64.png'}); }
|
|
}
|
|
|
|
// Attribute: Intel AMT
|
|
if (node.intelamt != null) {
|
|
var x = '';
|
|
x += addDetailItem("Version", (node.intelamt.ver)?('v' + node.intelamt.ver):('<i>' + "Unknown" + '</i>'), s);
|
|
var provisioningStates = { 0: nobreak("Not Activated (Pre)"), 1: nobreak("Not Activated (In)"), 2: nobreak("Activated") };
|
|
var provisioningMode = '';
|
|
if ((node.intelamt.state == 2) && node.intelamt.flags) { if (node.intelamt.flags & 2) { provisioningMode = (', ' + "Client Control Mode (CCM)"); } else if (node.intelamt.flags & 4) { provisioningMode = (', ' + "Admin Control Mode (ACM)"); } }
|
|
x += addDetailItem("Provisioning State", ((node.intelamt.state)?(provisioningStates[node.intelamt.state]):('<i>' + "Unknown" + '</i>')) + provisioningMode, s);
|
|
x += addDetailItem("Security", (node.intelamt.tls == 1)?"Secured using TLS":"TLS is not setup", s);
|
|
x += addDetailItem("Admin Credentials", (node.intelamt.user == null || node.intelamt.user == '')?"Not Known":"Known", s);
|
|
if (x != '') { sections.push({ name: "Intel® Active Management Technology (Intel® AMT)", html: x, img: 'amt64.png' }); }
|
|
}
|
|
|
|
if (hardware.identifiers) {
|
|
var x = '', ident = hardware.identifiers;
|
|
// BIOS
|
|
if (ident.bios_vendor) { x += addDetailItem("Vendor", EscapeHtml(ident.bios_vendor), s); }
|
|
if (ident.bios_version) { x += addDetailItem("Version", EscapeHtml(ident.bios_version), s); }
|
|
if (x != '') { sections.push({ name: "BIOS", html: x, img: 'chip64.png' }); }
|
|
|
|
// Motherboard
|
|
x = '';
|
|
if (ident.board_vendor) { x += addDetailItem("Vendor", EscapeHtml(ident.board_vendor), s); }
|
|
if (ident.board_name) { x += addDetailItem("Name", EscapeHtml(ident.board_name), s); }
|
|
if (ident.board_serial && (ident.board_serial != '')) { x += addDetailItem("Serial", EscapeHtml(ident.board_serial), s); }
|
|
if (ident.board_version) { x += addDetailItem("Version", EscapeHtml(ident.board_version), s); }
|
|
if (ident.product_uuid) { x += addDetailItem("Identifier", EscapeHtml(ident.product_uuid), s); }
|
|
if (ident.cpu_name) { x += addDetailItem("CPU", EscapeHtml(ident.cpu_name).split('(TM)').join('™').split('(R)').join('®'), s); }
|
|
if (ident.gpu_name) { for (var i in ident.gpu_name) { x += addDetailItem("GPU", EscapeHtml(ident.gpu_name[i]).split('(TM)').join('™').split('(R)').join('®'), s); } }
|
|
if (x != '') { sections.push({ name: "Motherboard", html: x, img: 'motherboard64.png'}); }
|
|
}
|
|
|
|
if (hardware.windows) {
|
|
if (hardware.windows.memory) {
|
|
var x = '';
|
|
// Sort Memory
|
|
function memorySort(a, b) { if (a.BankLabel > b.BankLabel) return 1; if (a.BankLabel < b.BankLabel) return -1; return 0; }
|
|
hardware.windows.memory.sort(memorySort);
|
|
|
|
x += '<table style=width:100%>';
|
|
for (var i in hardware.windows.memory) {
|
|
var m = hardware.windows.memory[i];
|
|
x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
|
|
x += '<div style=margin-bottom:3px><b>' + EscapeHtml(m.BankLabel) + '</b></div>';
|
|
if (m.Capacity) { x += addDetailItem("Capacity / Speed", format("{0} Mb, {1} Mhz", (m.Capacity / 1024 / 1024), m.Speed), s); }
|
|
if (m.PartNumber) { x += addDetailItem("Part Number", EscapeHtml((m.Manufacturer && m.Manufacturer != 'Undefined')?(m.Manufacturer + ', '):'') + EscapeHtml(m.PartNumber), s); }
|
|
x += '</div>';
|
|
}
|
|
x += '</table>';
|
|
|
|
if (x != '') { sections.push({ name: "Memory", html: x, img: 'ram64.png'}); }
|
|
}
|
|
}
|
|
|
|
// Storage
|
|
if (hardware.identifiers && ident.storage_devices) {
|
|
var x = '';
|
|
// Sort Memory
|
|
function storageSort(a, b) { if (a.Caption > b.Caption) return 1; if (a.Caption < b.Caption) return -1; return 0; }
|
|
ident.storage_devices.sort(storageSort);
|
|
|
|
x += '<table style=width:100%>';
|
|
for (var i in ident.storage_devices) {
|
|
var m = ident.storage_devices[i];
|
|
if (m.Size) {
|
|
x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
|
|
x += '<div style=margin-bottom:3px><b>' + EscapeHtml(m.Caption) + '</b></div>';
|
|
if (m.Model && (m.Model != m.Caption)) { x += addDetailItem("Model", EscapeHtml(m.Model), s); }
|
|
if (m.Size) {
|
|
if ((typeof m.Size == 'string') && (parseInt(m.Size) == m.Size)) { m.Size = parseInt(m.Size); }
|
|
if (typeof m.Size == 'number') { x += addDetailItem("Capacity", format("{0} Mb", Math.floor(m.Size / 1024 / 1024)), s); }
|
|
if (typeof m.Size == 'string') { x += addDetailItem("Capacity", EscapeHtml(m.Size), s); }
|
|
}
|
|
x += '</div>';
|
|
}
|
|
}
|
|
x += '</table>';
|
|
|
|
if (x != '') { sections.push({ name: "Storage", html: x, img: 'storage64.png'}); }
|
|
}
|
|
|
|
// Render the sections
|
|
var x = '';
|
|
for (var i in sections) {
|
|
if (sections[i].img == null) {
|
|
x += '<div class=DevSt style=margin-bottom:3px;margin-left:16px><b>' + sections[i].name + '</b></div><div style=margin-bottom:10px;margin-left:16px>' + sections[i].html + '</div>';
|
|
} else {
|
|
x += '<table style=width:100%><tr>';
|
|
x += '<td style=width:64px;vertical-align:top><img src=images/details/' + sections[i].img + ' border=0 width=64 /></td>'; // height=12
|
|
x += '<td><div class=DevSt style=margin-bottom:3px;margin-left:16px><b>' + sections[i].name + '</b></div><div style=margin-bottom:10px;margin-left:16px>' + sections[i].html + '</div></td>';
|
|
x += '</tr></table>';
|
|
}
|
|
}
|
|
|
|
if (x == '') {
|
|
QH('p17info', "No information for this device.");
|
|
} else {
|
|
QH('p17info', x);
|
|
}
|
|
}
|
|
|
|
//
|
|
// CONSOLE
|
|
//
|
|
|
|
function agentConsoleHandleKeys(e) {
|
|
if ((e.ctrlKey) || (e.altKey)) { return true; }
|
|
var processed = 0, box = Q('p15consoleText');
|
|
if (e.key) {
|
|
if (e.keyCode == 13 && consoleFocus == 0) { p15consoleSend(e); processed = 1; }
|
|
else if (e.keyCode == 8 && consoleFocus == 0) { var x = box.value; box.value = x.substring(0, x.length - 1); processed = 1; }
|
|
else if (e.keyCode == 27) { box.value = ''; processed = 1; }
|
|
else if ((e.keyCode == 38) || (e.keyCode == 40)) { // Arrow up || Arrow down
|
|
var hindex = consoleHistory.indexOf(box.value);
|
|
//console.log(hindex, consoleHistory);
|
|
if ((e.keyCode == 38) && ((consoleHistory.length - 1) > hindex)) { box.value = consoleHistory[hindex + 1]; }
|
|
else if ((e.keyCode == 40) && (hindex > 0)) { box.value = consoleHistory[hindex - 1]; }
|
|
else if ((e.keyCode == 40) && (hindex == 0)) { box.value = ''; }
|
|
processed = 1;
|
|
}
|
|
else if (e.key.length === 1) {
|
|
//box.value = ((box.value + e.key));
|
|
insertTextAtCursor(box, e.key);
|
|
processed = 1;
|
|
}
|
|
} else {
|
|
if (e.charCode != 0 && consoleFocus == 0) { box.value = ((box.value + String.fromCharCode(e.charCode))); processed = 1; }
|
|
}
|
|
if (processed > 0) { return haltEvent(e); }
|
|
}
|
|
|
|
// Insert text at the cursor location on the
|
|
function insertTextAtCursor(ctrl, val) {
|
|
if (document.selection) { ctrl.focus(); sel = document.selection.createRange(); sel.text = val; }
|
|
else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
|
|
var start = ctrl.selectionStart, end = ctrl.selectionEnd;
|
|
ctrl.value = ctrl.value.substring(0, start) + val + ctrl.value.substring(end, ctrl.value.length);
|
|
ctrl.setSelectionRange(end + 1, end + 1);
|
|
} else { ctrl.value += myValue; }
|
|
}
|
|
|
|
var consoleNode;
|
|
var consoleServerText = '';
|
|
function setupConsole() {
|
|
if (xxcurrentView == 115) {
|
|
// Setup server console
|
|
var samenode = (consoleNode == 'server');
|
|
consoleNode = 'server';
|
|
|
|
QH('p15deviceName', "My Server Console");
|
|
QE('p15consoleText', true);
|
|
QH('p15statetext', '');
|
|
QH('p15coreName', '');
|
|
QV('p15outputselecttd', false);
|
|
|
|
if (samenode == false) {
|
|
QH('p15agentConsoleText', consoleServerText);
|
|
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
|
}
|
|
} else {
|
|
// Setup the console
|
|
var samenode = (consoleNode == currentNode);
|
|
consoleNode = currentNode;
|
|
|
|
var mesh = meshes[consoleNode.meshid];
|
|
var rights = GetNodeRights(currentNode);
|
|
if ((rights & 16) != 0) {
|
|
if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
|
|
if (samenode == false) {
|
|
QH('p15agentConsoleText', consoleNode.consoleText);
|
|
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
|
}
|
|
var online = (((consoleNode.conn & 1) != 0) || ((consoleNode.conn & 16) != 0)) ? true : false;
|
|
var onlineText = ((consoleNode.conn & 1) != 0) ? "Agent is online" : "Agent is offline"
|
|
if ((consoleNode.conn & 16) != 0) { onlineText += ", MQTT is online" }
|
|
QH('p15statetext', onlineText);
|
|
QE('p15consoleText', online);
|
|
QE('p15uploadCore', ((consoleNode.conn & 1) != 0));
|
|
QV('p15outputselecttd', (consoleNode.conn & 17) == 17);
|
|
} else {
|
|
QH('p15statetext', "Access Denied");
|
|
QE('p15consoleText', false);
|
|
QE('p15uploadCore', false);
|
|
QV('p15outputselecttd', false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the console for this node
|
|
function p15consoleClear() {
|
|
QH('p15agentConsoleText', '');
|
|
Q('id_p15consoleClear').blur();
|
|
if (xxcurrentView == 115) {
|
|
consoleServerText = '';
|
|
} else {
|
|
consoleNode.consoleText = '';
|
|
}
|
|
}
|
|
|
|
// Send a command to the agent
|
|
var consoleHistory = [];
|
|
function p15consoleSend(e) {
|
|
if (e && e.keyCode != 13) return;
|
|
var v = Q('p15consoleText').value, t = '<div style=color:green>> ' + EscapeHtml(v) + '<br/></div>';
|
|
|
|
if (xxcurrentView == 115) {
|
|
// Send the command to the server - TODO: In the future, we may support multiple servers.
|
|
consoleServerText += t;
|
|
meshserver.send({ action: 'serverconsole', value: v });
|
|
} else {
|
|
if (((consoleNode.conn & 16) != 0) && ((Q('p15outputselect').value == 2) || ((consoleNode.conn & 1) == 0))) {
|
|
// Send the command to MQTT
|
|
t = '<div style=color:orange>' + "MQTT" + '> ' + EscapeHtml(v) + '<br/></div>';
|
|
consoleNode.consoleText += t;
|
|
meshserver.send({ action: 'sendmqttmsg', topic: 'console', nodeids: [ consoleNode._id ], msg: v });
|
|
} else {
|
|
// Send the command to the mesh agent
|
|
consoleNode.consoleText += t;
|
|
meshserver.send({ action: 'msg', type: 'console', nodeid: consoleNode._id, value: v });
|
|
}
|
|
}
|
|
|
|
Q('p15agentConsoleText').innerHTML += t;
|
|
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
|
Q('p15consoleText').value = '';
|
|
|
|
// Add command to history list
|
|
if (v.length > 0) {
|
|
// Move this command to the top if it already exists
|
|
var j = consoleHistory.indexOf(v);
|
|
if (j >= 0) { consoleHistory.splice(j, 1); }
|
|
consoleHistory.unshift(v);
|
|
consoleHistory.splice(10);
|
|
}
|
|
}
|
|
|
|
// Handle Mesh Agent console data
|
|
function p15consoleReceive(node, data, source) {
|
|
if (node === 'serverconsole') {
|
|
// Server console data
|
|
data = '<div>' + EscapeHtml(data) + '</div>'
|
|
consoleServerText += data;
|
|
if (consoleNode == 'server') {
|
|
Q('p15agentConsoleText').innerHTML += data;
|
|
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
|
}
|
|
} else {
|
|
// Agent console data
|
|
if (source == 'MQTT') { data = '<div style=color:red>' + "MQTT" + '> ' + EscapeHtml(data) + '<br/></div>'; } else { data = '<div>' + EscapeHtml(data) + '</div>' }
|
|
if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; }
|
|
if (consoleNode == node) {
|
|
Q('p15agentConsoleText').innerHTML += data;
|
|
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save console text to file
|
|
function p15downloadConsoleText() {
|
|
saveAs(new Blob([Q('p15agentConsoleText').innerText], { type: 'application/octet-stream' }), "console.txt");
|
|
}
|
|
|
|
// Called then user presses the "Change Core" button
|
|
function p15uploadCore(e) {
|
|
if (xxdialogMode) return;
|
|
if (e.shiftKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' }); } // Upload default core
|
|
else if (e.altKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' }); } // Clear the core
|
|
else if (e.ctrlKey == true) { p15uploadCore2(); } // Upload the core from a file
|
|
else { setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue("Action", '<select id=d3coreMode style=width:230px><option value=1>' + "Upload default server core" + '</option><option value=2>' + "Clear the core" + '</option><option value=6>' + "Upload recovery core" + '</option><option value=3>' + "Upload a core file" + '</option><option value=4>' + "Soft disconnect agent" + '</option><option value=5>' + "Hard disconnect agent" + '</option></select>')); }
|
|
}
|
|
|
|
function p15uploadCoreEx() {
|
|
if (Q('d3coreMode').value == 1) {
|
|
// Upload default core
|
|
meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' });
|
|
} else if (Q('d3coreMode').value == 2) {
|
|
// Clear the core
|
|
meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' });
|
|
} else if (Q('d3coreMode').value == 3) {
|
|
// Upload file as core
|
|
p15uploadCore2();
|
|
} else if (Q('d3coreMode').value == 4) {
|
|
// Soft disconnect the mesh agent
|
|
meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 1 });
|
|
} else if (Q('d3coreMode').value == 5) {
|
|
// Hard disconnect the mesh agent
|
|
meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 2 });
|
|
} else if (Q('d3coreMode').value == 6) {
|
|
// Upload a recovery core
|
|
meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type:'recovery' });
|
|
}
|
|
}
|
|
|
|
// Called then user opts to upload a file as core
|
|
function p15uploadCore2() {
|
|
if (xxdialogMode) return;
|
|
Q('d3localmodeform').action = 'uploadmeshcorefile.ashx';
|
|
Q('d3auth').value = authCookie;
|
|
Q('d3attrib').value = currentNode._id;
|
|
setDialogMode(3, "Upload Mesh Agent Core", 3, p15uploadCoreEx2);
|
|
d3init();
|
|
}
|
|
|
|
function p15uploadCoreEx2() {
|
|
var mode = Q('d3uploadMode').value;
|
|
if (mode == 1) {
|
|
// Upload local mesh agent core
|
|
Q('d3submit').click();
|
|
} else {
|
|
// Upload server mesh agent code
|
|
var files = d3getFileSel();
|
|
if (files.length == 1) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'custom', path: d3filetreelocation.join('/') + '/' + files[0] }); }
|
|
}
|
|
}
|
|
|
|
//
|
|
// MY ACCOUNT
|
|
//
|
|
|
|
function account_managePhone() {
|
|
if (xxdialogMode || ((features & 0x02000000) == 0)) return;
|
|
var x;
|
|
if (userinfo.phone != null) {
|
|
x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>';
|
|
x += '<td style=text-align:center><div style=padding:6px>' + "Verified phone number" + '</div><div style=font-size:20px>' + userinfo.phone + '</div>';
|
|
x += '<div style=margin:10px><label><input id=d2delPhone type=checkbox onclick=account_managePhoneRemoveValidate() />' + "Remove phone number" + '</label></div>';
|
|
setDialogMode(2, "Phone Notifications", 3, account_managePhoneRemove, x);
|
|
account_managePhoneRemoveValidate();
|
|
} else {
|
|
x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>';
|
|
x += '<td>Enter your SMS capable phone number. Once verified, the number may be used for login verification and other notifications.';
|
|
x += '<br /><br /><div style=width:100%;text-align:center>' + "Phone number:" + ' <input type=tel pattern="[0-9]" autocomplete="tel" inputmode="tel" maxlength=18 id=d2phoneinput onKeyUp=account_managePhoneValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneValidate(1)"></div></table>';
|
|
setDialogMode(2, "Phone Notifications", 3, account_managePhoneAdd, x, 'verifyPhone');
|
|
Q('d2phoneinput').focus();
|
|
account_managePhoneValidate();
|
|
}
|
|
}
|
|
|
|
function isPhoneNumber(x) { return x.match(/^\(?([0-9]{3,4})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/) }
|
|
function account_managePhoneValidate(x) { var ok = isPhoneNumber(Q('d2phoneinput').value); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } }
|
|
function account_managePhoneCodeValidate(x) { var ok = (Q('d2phoneCodeInput').value.length == 6) && Q('d2phoneCodeInput').value.match(/[0-9]/); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } }
|
|
function account_managePhoneConfirm(b, tag) { meshserver.send({ action: 'confirmPhone', code: Q('d2phoneCodeInput').value, cookie: tag }); }
|
|
function account_managePhoneAdd() { if (isPhoneNumber(Q('d2phoneinput').value) == false) return; QE('d2phoneinput', false); meshserver.send({ action: 'verifyPhone', phone: Q('d2phoneinput').value }); }
|
|
function account_managePhoneRemove() { if (Q('d2delPhone').checked) { meshserver.send({ action: 'removePhone' }); } }
|
|
function account_managePhoneRemoveValidate() { QE('idx_dlgOkButton', Q('d2delPhone').checked); }
|
|
|
|
function account_manageAuthEmail() {
|
|
if (xxdialogMode || ((features & 0x00800000) == 0)) return;
|
|
var emailU2Fenabled = ((userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
|
|
setDialogMode(2, "Email Authentication", 1, function () {
|
|
if (emailU2Fenabled != Q('email2facheck').checked) { meshserver.send({ action: 'otpemail', enabled: Q('email2facheck').checked }); }
|
|
}, "When enabled, on each login, you will be given the option to receive a login token to you email account for added security." + '<br /><br /><label><input id=email2facheck type=checkbox ' + (emailU2Fenabled?'checked':'') + '/>' + "Enable email two-factor authenticaiton." + '</label>');
|
|
}
|
|
|
|
function account_manageAuthApp() {
|
|
if (xxdialogMode || ((features & 4096) == 0)) return;
|
|
if (userinfo.otpsecret == 1) { account_removeOtp(); } else { account_addOtp(); }
|
|
return false;
|
|
}
|
|
|
|
function account_addOtp() {
|
|
if (xxdialogMode || (userinfo.otpsecret == 1) || ((features & 4096) == 0)) return;
|
|
setDialogMode(2, "Authenticator App", 2, function () { meshserver.send({ action: 'otpauth-setup', secret: Q('d2optsecret').attributes.secret.value, token: Q('d2otpauthinput').value }); }, ('<div id=d2optinfo>' + "Loading..." + '</div>'), 'otpauth-request');
|
|
meshserver.send({ action: 'otpauth-request' });
|
|
}
|
|
|
|
function account_addOtpCheck(e) {
|
|
var tokenIsValid = (Q('d2otpauthinput').value.length == 6);
|
|
QE('idx_dlgOkButton', tokenIsValid);
|
|
if (e && (e.keyCode == 13) && tokenIsValid) { dialogclose(1); }
|
|
}
|
|
|
|
function account_removeOtp() {
|
|
if (xxdialogMode || (userinfo.otpsecret != 1) || ((features & 4096) == 0)) return;
|
|
setDialogMode(2, "Authenticator App", 3, function () { meshserver.send({ action: 'otpauth-clear' }); }, "Confirm removal of authenticator application 2-step login?");
|
|
}
|
|
|
|
function account_manageOtp(action) {
|
|
if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-manage')) { dialogclose(0); }
|
|
if (xxdialogMode || ((features & 4096) == 0)) return false;
|
|
if ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)) { meshserver.send({ action: 'otpauth-getpasswords', subaction: action }); }
|
|
return false;
|
|
}
|
|
|
|
function account_manageHardwareOtp() {
|
|
if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-hardware-manage')) { dialogclose(0); }
|
|
if (xxdialogMode || ((features & 4096) == 0)) return false;
|
|
meshserver.send({ action: 'otp-hkey-get' });
|
|
return false;
|
|
}
|
|
|
|
function account_addhkey(type) {
|
|
if (type == 3) {
|
|
var x = "Type in the name of the key to add." + '<br /><br />';
|
|
x += addHtmlValue("Key Name", '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off placeholder="' + "MyKey" + '" onkeyup=account_addhkeyValidate(event,2) />');
|
|
} else if (type == 2) {
|
|
var x = "Type in a key name, select the OTP box and press the button on the YubiKey™." + '<br /><br />';
|
|
x += addHtmlValue("Key Name", '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off placeholder="' + "MyKey" + '" onkeyup=account_addhkeyValidate(event,1) />');
|
|
x += addHtmlValue("YubiKey™ OTP", '<input id=dp1key style=width:230px autocomplete=off onkeyup=account_addhkeyValidate(event,2) />');
|
|
}
|
|
setDialogMode(2, "Add Security Key", 3, account_addhkeyEx, x, type);
|
|
Q('dp1keyname').focus();
|
|
}
|
|
|
|
function account_addhkeyValidate(e,action) {
|
|
if ((e != null) && (e.keyCode == 13)) { if (action == 2) { dialogclose(1); } else { Q('dp1key').focus(); } }
|
|
}
|
|
|
|
function account_addhkeyEx(button, type) {
|
|
var name = Q('dp1keyname').value;
|
|
if (name == '') { name = 'MyKey'; }
|
|
if (type == 2) {
|
|
meshserver.send({ action: 'otp-hkey-yubikey-add', name: name, otp: Q('dp1key').value });
|
|
setDialogMode(2, "Add Security Key", 0, null, '<br />' + "Checking..." + '<br /><br /><br />', 'otpauth-hardware-manage');
|
|
} else if (type == 3) {
|
|
meshserver.send({ action: 'webauthn-startregister', name: name });
|
|
}
|
|
}
|
|
|
|
function account_removehkey(index) {
|
|
meshserver.send({ action: 'otp-hkey-remove', index: index });
|
|
meshserver.send({ action: 'otp-hkey-get' });
|
|
}
|
|
|
|
var loclist = { 'af': "Afrikaans", 'sq': "Albanian", 'ar': "Arabic (Standard)", 'ar-dz': "Arabic (Algeria)", 'ar-bh': "Arabic (Bahrain)", 'ar-eg': "Arabic (Egypt)", 'ar-iq': "Arabic (Iraq)", 'ar-jo': "Arabic (Jordan)", 'ar-kw': "Arabic (Kuwait)", 'ar-lb': "Arabic (Lebanon)", 'ar-ly': "Arabic (Libya)", 'ar-ma': "Arabic (Morocco)", 'ar-om': "Arabic (Oman)", 'ar-qa': "Arabic (Qatar)", 'ar-sa': "Arabic (Saudi Arabia)", 'ar-sy': "Arabic (Syria)", 'ar-tn': "Arabic (Tunisia)", 'ar-ae': "Arabic (U.A.E.)", 'ar-ye': "Arabic (Yemen)", 'an': "Aragonese", 'hy': "Armenian", 'as': "Assamese", 'ast': "Asturian", 'az': "Azerbaijani", 'eu': "Basque", 'bg': "Bulgarian", 'be': "Belarusian", 'bn': "Bengali", 'bs': "Bosnian", 'br': "Breton", 'my': "Burmese", 'ca': "Catalan", 'ch': "Chamorro", 'ce': "Chechen", 'zh': "Chinese", 'zh-hk': "Chinese (Hong Kong)", 'zh-cn': "Chinese (PRC)", 'zh-sg': "Chinese (Singapore)", 'zh-tw': "Chinese (Taiwan)", 'cv': "Chuvash", 'co': "Corsican", 'cr': "Cree", 'hr': "Croatian", 'cs': "Czech", 'da': "Danish", 'nl': "Dutch (Standard)", 'nl-be': "Dutch (Belgian)", 'en': "English", 'en-au': "English (Australia)", 'en-bz': "English (Belize)", 'en-ca': "English (Canada)", 'en-ie': "English (Ireland)", 'en-jm': "English (Jamaica)", 'en-nz': "English (New Zealand)", 'en-ph': "English (Philippines)", 'en-za': "English (South Africa)", 'en-tt': "English (Trinidad & Tobago)", 'en-gb': "English (United Kingdom)", 'en-us': "English (United States)", 'en-zw': "English (Zimbabwe)", 'eo': "Esperanto", 'et': "Estonian", 'fo': "Faeroese", 'fa': "Farsi (Persian)", 'fj': "Fijian", 'fi': "Finnish", 'fr': "French (Standard)", 'fr-be': "French (Belgium)", 'fr-ca': "French (Canada)", 'fr-fr': "French (France)", 'fr-lu': "French (Luxembourg)", 'fr-mc': "French (Monaco)", 'fr-ch': "French (Switzerland)", 'fy': "Frisian", 'fur': "Friulian", 'gd': "Gaelic (Scots)", 'gd-ie': "Gaelic (Irish)", 'gl': "Galacian", 'ka': "Georgian", 'de': "German (Standard)", 'de-at': "German (Austria)", 'de-de': "German (Germany)", 'de-li': "German (Liechtenstein)", 'de-lu': "German (Luxembourg)", 'de-ch': "German (Switzerland)", 'el': "Greek", 'gu': "Gujurati", 'ht': "Haitian", 'he': "Hebrew", 'hi': "Hindi", 'hu': "Hungarian", 'is': "Icelandic", 'id': "Indonesian", 'iu': "Inuktitut", 'ga': "Irish", 'it': "Italian (Standard)", 'it-ch': "Italian (Switzerland)", 'ja': "Japanese", 'kn': "Kannada", 'ks': "Kashmiri", 'kk': "Kazakh", 'km': "Khmer", 'ky': "Kirghiz", 'tlh': "Klingon", 'ko': "Korean", 'ko-kp': "Korean (North Korea)", 'ko-kr': "Korean (South Korea)", 'la': "Latin", 'lv': "Latvian", 'lt': "Lithuanian", 'lb': "Luxembourgish", 'mk': "FYRO Macedonian", 'ms': "Malay", 'ml': "Malayalam", 'mt': "Maltese", 'mi': "Maori", 'mr': "Marathi", 'mo': "Moldavian", 'nv': "Navajo", 'ng': "Ndonga", 'ne': "Nepali", 'no': "Norwegian", 'nb': "Norwegian (Bokmal)", 'nn': "Norwegian (Nynorsk)", 'oc': "Occitan", 'or': "Oriya", 'om': "Oromo", 'fa-ir': "Persian/Iran", 'pl': "Polish", 'pt': "Portuguese", 'pt-br': "Portuguese (Brazil)", 'pa': "Punjabi", 'pa-in': "Punjabi (India)", 'pa-pk': "Punjabi (Pakistan)", 'qu': "Quechua", 'rm': "Rhaeto-Romanic", 'ro': "Romanian", 'ro-mo': "Romanian (Moldavia)", 'ru': "Russian", 'ru-mo': "Russian (Moldavia)", 'sz': "Sami (Lappish)", 'sg': "Sango", 'sa': "Sanskrit", 'sc': "Sardinian", 'sd': "Sindhi", 'si': "Singhalese", 'sr': "Serbian", 'sk': "Slovak", 'sl': "Slovenian", 'so': "Somani", 'sb': "Sorbian", 'es': "Spanish", 'es-ar': "Spanish (Argentina)", 'es-bo': "Spanish (Bolivia)", 'es-cl': "Spanish (Chile)", 'es-co': "Spanish (Colombia)", 'es-cr': "Spanish (Costa Rica)", 'es-do': "Spanish (Dominican Republic)", 'es-ec': "Spanish (Ecuador)", 'es-sv': "Spanish (El Salvador)", 'es-gt': "Spanish (Guatemala)", 'es-hn': "Spanish (Honduras)", 'es-mx': "Spanish (Mexico)", 'es-ni': "Spanish (Nicaragua)", 'es-pa': "Spanish (Panama)", 'es-py': "Spanish (Paraguay)", 'es-pe': "Spanish (Peru)", 'es-pr': "Spanish (Puerto Rico)", 'es-es': "Spanish (Spain)", 'es-uy': "Spanish (Uruguay)", 'es-ve': "Spanish (Venezuela)", 'sx': "Sutu", 'sw': "Swahili", 'sv': "Swedish", 'sv-fi': "Swedish (Finland)", 'sv-sv': "Swedish (Sweden)", 'ta': "Tamil", 'tt': "Tatar", 'te': "Teluga", 'th': "Thai", 'tig': "Tigre", 'ts': "Tsonga", 'tn': "Tswana", 'tr': "Turkish", 'tk': "Turkmen", 'uk': "Ukrainian", 'hsb': "Upper Sorbian", 'ur': "Urdu", 've': "Venda", 'vi': "Vietnamese", 'vo': "Volapuk", 'wa': "Walloon", 'cy': "Welsh", 'xh': "Xhosa", 'ji': "Yiddish", 'zu': "Zulu" };
|
|
var loclistex = { 'zh-chs': "Chinese (Simplified)" };
|
|
function account_showLocalizationSettings() {
|
|
if (xxdialogMode) return false;
|
|
var n = getstore('loctag', 0), y = '';
|
|
var x = '<select id=d2locselect style=width:240px><option value=\"*\">' + "User browser value" + '</option>';
|
|
for (var i in loclist) { x += '<option value="' + i + '"' + ((n == i)?' selected':'') + '>' + i + ' - ' + loclist[i] + '</option>'; }
|
|
x += '</select>';
|
|
if (serverinfo.languages && serverinfo.languages.length > 0) {
|
|
y += "Changing the language will require a refresh of the page." + '<br /><br />';
|
|
var z = '<select id=d2langselect style=width:240px><option value=\"*\">' + "User browser value" + '</option>';
|
|
for (var i in serverinfo.languages) {
|
|
var lang = serverinfo.languages[i];
|
|
z += '<option value="' + lang + '"' + ((userinfo.lang == lang)?' selected':'') + '>' + lang + ' - ' + (loclist[lang]?loclist[lang]:loclistex[lang]) + '</option>';
|
|
}
|
|
z += '</select>';
|
|
y += addHtmlValue("Language", z);
|
|
}
|
|
y += addHtmlValue("Dates & Time", x);
|
|
|
|
if ((userinfo.siteadmin == 0xFFFFFFFF) && (domain == '')) {
|
|
y += '<br /><a rel="noreferrer noopener" target="_blank" href="translator.htm">' + "Help translate MeshCentral" + '</a>';
|
|
}
|
|
|
|
setDialogMode(2, "Localization Settings", 3, account_showLocalizationSettingsEx, y);
|
|
return false;
|
|
}
|
|
|
|
function account_showLocalizationSettingsEx() {
|
|
// Set user language
|
|
var lang = Q('d2langselect').value;
|
|
if ((lang == '*') && (userinfo.lang == null)) { lang = userinfo.lang; }
|
|
if (lang != userinfo.lang) { meshserver.send({ action: 'changelang', lang: lang }); }
|
|
|
|
// Set date localization
|
|
var n = getstore('loctag', 0);
|
|
var m = Q('d2locselect').value;
|
|
if (n != m) {
|
|
if (m != '*') { args.locale = m; } else { delete args.locale; }
|
|
putstore('loctag', args.locale);
|
|
masterUpdate(0xFFFFFFFF); // Refresh everything.
|
|
}
|
|
}
|
|
|
|
function account_enableNotifications() {
|
|
if (Notification) { Notification.requestPermission().then(function (permission) { QV('accountEnableNotificationsSpan', permission != 'granted'); }); }
|
|
return false;
|
|
}
|
|
|
|
function account_showAccountNotifySettings() {
|
|
if (xxdialogMode) return false;
|
|
var x = '';
|
|
x += '<div><label><input id=p2notifyPlayNotifySound type=checkbox />' + "Notification sound." + '</label></div>';
|
|
x += '<div><label><input id=p2notifyGroupName type=checkbox />' + "Display device group name" + '</label></div>';
|
|
x += '<div><label><input id=p2notifyIntelDeviceConnect type=checkbox />' + "Device connections." + '</label></div>';
|
|
x += '<div><label><input id=p2notifyIntelDeviceDisconnect type=checkbox />' + "Device disconnections." + '</label></div>';
|
|
x += '<div><label><input id=p2notifyIntelAmtKvmActions type=checkbox />' + "Intel® AMT desktop and serial events." + '</label></div>';
|
|
setDialogMode(2, "Notification Settings", 3, account_showAccountNotifySettingsEx, x);
|
|
var n = getstore('notifications', 0);
|
|
Q('p2notifyPlayNotifySound').checked = (n & 1);
|
|
Q('p2notifyIntelDeviceConnect').checked = (n & 2);
|
|
Q('p2notifyIntelDeviceDisconnect').checked = (n & 4);
|
|
Q('p2notifyIntelAmtKvmActions').checked = (n & 8);
|
|
Q('p2notifyGroupName').checked = (n & 16);
|
|
return false;
|
|
}
|
|
|
|
function account_showAccountNotifySettingsEx() {
|
|
var n = 0;
|
|
n += Q('p2notifyPlayNotifySound').checked ? 1 : 0;
|
|
n += Q('p2notifyIntelDeviceConnect').checked ? 2 : 0;
|
|
n += Q('p2notifyIntelDeviceDisconnect').checked ? 4 : 0;
|
|
n += Q('p2notifyIntelAmtKvmActions').checked ? 8 : 0;
|
|
n += Q('p2notifyGroupName').checked ? 16 : 0;
|
|
putstore('notifications', n);
|
|
}
|
|
|
|
function account_showVerifyEmail() {
|
|
if (xxdialogMode || (userinfo.emailVerified == true) || (serverinfo.emailcheck != true)) return false;
|
|
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.";
|
|
setDialogMode(2, "Email Verification", 3, account_showVerifyEmailEx, x);
|
|
return false;
|
|
}
|
|
|
|
function account_showVerifyEmailEx() {
|
|
meshserver.send({ action: 'verifyemail', email: userinfo.email });
|
|
}
|
|
|
|
function account_showChangeEmail() {
|
|
if (xxdialogMode) return false;
|
|
var x = "Change your account email address here." + '<br /><br />';
|
|
x += addHtmlValue('Email', '<input id=dp2email style=width:230px maxlength=256 onchange=account_validateEmail() onkeyup=account_validateEmail(event) />');
|
|
setDialogMode(2, "Email Address Change", 3, account_changeEmail, x);
|
|
if (userinfo.email != null) { Q('dp2email').value = userinfo.email; }
|
|
account_validateEmail();
|
|
Q('dp2email').focus();
|
|
return false;
|
|
}
|
|
|
|
function account_validateEmail(e, email) {
|
|
QE('idx_dlgOkButton', validateEmail(Q('dp2email').value) && (Q('dp2email').value != userinfo.email));
|
|
if ((e != null) && (e.keyCode == 13)) { dialogclose(1); }
|
|
}
|
|
|
|
function account_changeEmail() {
|
|
meshserver.send({ action: 'changeemail', email: Q('dp2email').value });
|
|
}
|
|
|
|
function account_showDeleteAccount() {
|
|
if (xxdialogMode) return false;
|
|
var x = "To delete this account, type in the account password in both boxes below and hit ok." + '<br /><br />';
|
|
x += '<form method=post><input type=hidden name=action value=deleteaccount /><input type=hidden name=authcookie value=' + authCookie + ' /><table style=margin-left:80px><tr>';
|
|
x += '<td align=right>' + "Password:" + '</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>';
|
|
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>';
|
|
x += '</tr></table><br /><div style=padding:10px;margin-bottom:4px>';
|
|
x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
|
|
x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>';
|
|
x += '</div><br /></form>';
|
|
setDialogMode(2, "Delete Account", 0, null, x);
|
|
account_validateDeleteAccount();
|
|
Q('apassword1').focus();
|
|
return false;
|
|
}
|
|
|
|
function account_showChangePassword() {
|
|
if (xxdialogMode) return false;
|
|
var x = "Change your account password by entering the old password and new password twice in the boxes below.";
|
|
if (features & 0x00010000) { " Password hint can be used but is not recommanded."; }
|
|
x += '<br /><br />';
|
|
//x += "<form action='" + domainUrl + "changepassword' method=post>";
|
|
x += '<table style=margin-left:60px>';
|
|
x += '<tr><td align=right>' + nobreak("Old password:") + '</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b></b></td></tr>';
|
|
x += '<tr><td align=right>' + nobreak("New password:") + '</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td></tr>';
|
|
x += '<tr><td align=right>' + nobreak("New password:") + '</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>';
|
|
if (features & 0x00010000) { x += '<tr><td align=right>' + "Password hint:" + '</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>'; }
|
|
x += '</table>'
|
|
if (passRequirements) {
|
|
var r = [], rc = 0;
|
|
for (var i in passRequirements) { if ((i != 'reset') && (i != 'hint')) { r.push(i + ':' + passRequirements[i]); rc++; } }
|
|
if (rc > 0) { x += '<br /><span style=font-size:x-small>' + "Requirements: " + r.join(', ') + '.</span>'; }
|
|
}
|
|
x += '<br />';
|
|
//x += '<br /><div style=padding:10px;margin-bottom:4px>';
|
|
//x += '<input id=account_dlgCancelButton type=button value=Cancel style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
|
|
//x += '<input id=account_dlgOkButton type=submit value=OK style="float:right;width:80px" onclick=dialogclose(1)>';
|
|
//x += '</div><br /></form>';
|
|
setDialogMode(2, "Change Password", 3, account_showChangePasswordEx, x);
|
|
Q('apassword0').focus();
|
|
account_validateNewPassword();
|
|
return false;
|
|
}
|
|
|
|
function account_showChangePasswordEx() {
|
|
if (Q('apassword1').value == Q('apassword2').value) {
|
|
var r = { action: 'changepassword', oldpass: Q('apassword0').value, newpass: Q('apassword1').value };
|
|
if (features & 0x00010000) { r.hint = Q('apasswordhint').value; }
|
|
meshserver.send(r);
|
|
}
|
|
}
|
|
|
|
function account_createMesh() {
|
|
if (xxdialogMode) return false;
|
|
|
|
// Check if we are disallowed from creating a device group
|
|
if ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 64) != 0)) { setDialogMode(2, "New Device Group", 1, null, "This account does not have the rights to create a new device group."); return false; }
|
|
|
|
// Remind the user to verify the email address
|
|
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return false; }
|
|
|
|
// Remind the user to add two factor authentication
|
|
if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || ((features & 0x02000000) && (features & 0x04000000) && (userinfo.phone != null)) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" tab and look at the \"Account Security\" section."); return false; }
|
|
|
|
// We are allowed, let's prompt to information
|
|
var x = "Create a new device group using the options below." + '<br /><br />';
|
|
x += addHtmlValue("Name", '<input id=dp2meshname style=width:230px maxlength=64 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate(event,1) />');
|
|
x += addHtmlValue("Type", '<div style=width:230px;margin:0;padding:0><select id=dp2meshtype style=width:100% onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate(event,2) ><option value=2>' + "Manage using a software agent" + '</option><option value=1>' + "Intel® AMT only, no agent" + '</option></select></div>');
|
|
x += addHtmlValue("Description", '<div style=width:230px;margin:0;padding:0><textarea id=dp2meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
|
|
setDialogMode(2, "New Device Group", 3, account_createMeshEx, x);
|
|
account_validateMeshCreate();
|
|
Q('dp2meshname').focus();
|
|
return false;
|
|
}
|
|
|
|
function account_validateMeshCreate(e, x) {
|
|
if ((x == 1) && (e != null) && (e.key == "Enter") && (Q('dp2meshname').value.length > 0)) { Q('dp2meshtype').focus(); }
|
|
if ((x == 2) && (e != null) && (e.key == "Enter")) { Q('dp2meshdesc').focus(); }
|
|
QE('idx_dlgOkButton', Q('dp2meshname').value.length > 0);
|
|
}
|
|
|
|
function account_createMeshEx(button, tag) {
|
|
meshserver.send({ action: 'createmesh', meshname: Q('dp2meshname').value, meshtype: Q('dp2meshtype').value, desc: Q('dp2meshdesc').value });
|
|
}
|
|
|
|
function account_validateDeleteAccount() {
|
|
QE('account_dlgOkButton', (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value));
|
|
}
|
|
|
|
function account_validateNewPassword() {
|
|
var r = '', ok = (Q('apassword0').value.length > 0) && (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value) && (Q('apassword0').value != Q('apassword1').value);
|
|
if ((features & 0x00010000) && (Q('apasswordhint').value == Q('apassword1').value)) { ok = false; }
|
|
if (Q('apassword1').value != '') {
|
|
if (passRequirements == null || passRequirements == '') {
|
|
// No password requirements, display password strength
|
|
var passStrength = checkPasswordStrength(Q('apassword1').value);
|
|
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>'; }
|
|
} else {
|
|
// Password requirements provided, use that
|
|
var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
|
|
if (passReq == false) { ok = false; r = '<span style=color:red>' + "Policy" + '<span>' }
|
|
}
|
|
}
|
|
QH('dxPassWarn', r);
|
|
//QE('account_dlgOkButton', ok);
|
|
QE('idx_dlgOkButton', ok);
|
|
}
|
|
|
|
// Return a password strength score
|
|
function checkPasswordStrength(password) {
|
|
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) }
|
|
if (!password) return 0;
|
|
for (var i = 0; i< password.length; i++) { letters[password[i]] = (letters[password[i]] || 0) + 1; r += 5.0 / letters[password[i]]; }
|
|
for (var c in variations) { varCount += (variations[c] == true) ? 1 : 0; }
|
|
return parseInt(r + (varCount - 1) * 10);
|
|
}
|
|
|
|
// Check password requirements
|
|
function checkPasswordRequirements(password, requirements) {
|
|
if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true;
|
|
if (requirements.min) { if (password.length < requirements.min) return false; }
|
|
if (requirements.max) { if (password.length > requirements.max) return false; }
|
|
var num = 0, lower = 0, upper = 0, nonalpha = 0;
|
|
for (var i = 0; i < password.length; i++) {
|
|
if (/\d/.test(password[i])) { num++; }
|
|
if (/[a-z]/.test(password[i])) { lower++; }
|
|
if (/[A-Z]/.test(password[i])) { upper++; }
|
|
if (/\W/.test(password[i])) { nonalpha++; }
|
|
}
|
|
if (requirements.num && (num < requirements.num)) return false;
|
|
if (requirements.lower && (lower < requirements.lower)) return false;
|
|
if (requirements.upper && (upper < requirements.upper)) return false;
|
|
if (requirements.nonalpha && (nonalpha < requirements.nonalpha)) return false;
|
|
return true;
|
|
}
|
|
|
|
function updateMeshes() {
|
|
var r = '';
|
|
var c = 0, count = 0;
|
|
for (i in meshes) {
|
|
// Mesh positioning
|
|
if (c > 1) { r += '</tr><tr>'; c = 0; }
|
|
c++;
|
|
count++;
|
|
|
|
// Mesh rights
|
|
var meshrights = GetMeshRights(meshes[i]);
|
|
var rights = "Partial Rights";
|
|
if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights";
|
|
|
|
// Print the mesh information
|
|
r += '<div onmouseover=devMouseHover(this,1) onmouseout=devMouseHover(this,0) style=display:inline-block;width:431px;height:50px;padding-top:1px;padding-bottom:1px;float:left><div style=float:left;width:30px;height:100%></div><div tabindex=0 style=height:100%;cursor:pointer onclick=gotoMesh(\'' + i + '\') onkeypress="if (event.key==\'Enter\') gotoMesh(\'' + i + '\')"><div class=mi style=float:left;width:50px;height:50px></div><div style=height:100%><div class=g1></div><div class=e2 style=width:300px><div class=e1>' + EscapeHtml(meshes[i].name) + '</div><div>' + rights + '</div></div><div class=g2 style=float:left></div></div></div></div>';
|
|
}
|
|
|
|
meshcount = count;
|
|
QH('p2meshes', r);
|
|
QV('p2noMeshFound', count == 0);
|
|
}
|
|
|
|
function gotoMesh(meshid) {
|
|
currentMesh = meshes[meshid];
|
|
p20updateMesh();
|
|
go(20);
|
|
return false;
|
|
}
|
|
|
|
function server_showRestoreDlg() {
|
|
if (xxdialogMode) return false;
|
|
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 />';
|
|
x += '<form action="/restoreserver.ashx" enctype="multipart/form-data" method="post"><div>';
|
|
x += '<input type=hidden name=auth value=' + authCookie + '>';
|
|
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()>';
|
|
x += '<input id=account_dlgCancelButton type=button value=' + "Cancel" + ' style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
|
|
x += '<input id=account_dlgOkButton type=submit value=' + "OK" + ' style=float:right;width:80px onclick=dialogclose(1)>';
|
|
x += '</div><br /><br /></form>';
|
|
setDialogMode(2, "Restore Server", 0, null, x);
|
|
account_validateServerRestore();
|
|
return false;
|
|
}
|
|
|
|
function account_validateServerRestore() {
|
|
QE('account_dlgOkButton', Q('account_dlgFileInput').files.length == 1);
|
|
}
|
|
|
|
function server_showVersionDlg() {
|
|
if (xxdialogMode) return false;
|
|
setDialogMode(2, "MeshCentral Version", 1, null, "Loading...", 'MeshCentralServerUpdate');
|
|
meshserver.send({ action: 'serverversion' });
|
|
return false;
|
|
}
|
|
|
|
function server_showVersionDlgUpdate() {
|
|
var stableCheck = Q('d2updateCheck1').checked;
|
|
var latestCheck = Q('d2updateCheck2').checked;
|
|
QE('idx_dlgOkButton', ((stableCheck) && (!latestCheck)) || ((!stableCheck) && (latestCheck)));
|
|
}
|
|
|
|
function server_showVersionDlgEx(b, tags) {
|
|
if (Q('d2updateCheck1').checked) { meshserver.send({ action: 'serverupdate', tag: 'stable', version: tags.stable }); }
|
|
if (Q('d2updateCheck2').checked) { meshserver.send({ action: 'serverupdate' }); } // , tag: 'latest', version: tags.latest
|
|
}
|
|
|
|
function server_showErrorsDlg() {
|
|
if (xxdialogMode) return false;
|
|
setDialogMode(2, "MeshCentral Errors", 1, null, "Loading...", 'MeshCentralServerErrors');
|
|
meshserver.send({ action: 'servererrors' });
|
|
return false;
|
|
}
|
|
function server_showErrorsDlgUpdate() { QE('idx_dlgOkButton', Q('d2clearErrorsCheck').checked); }
|
|
function server_showErrorsDlgEx() { meshserver.send({ action: 'serverclearerrorlog' }); }
|
|
function d2CopyServerErrorsToClip() { saveAs(new Blob([Q('d2ServerErrorsLogPre').innerText], { type: 'application/octet-stream' }), "servererrors.txt"); }
|
|
|
|
//
|
|
// MY MESHS
|
|
//
|
|
|
|
var currentMesh;
|
|
function p20updateMesh() {
|
|
if (currentMesh == null) return;
|
|
|
|
// Add device group name
|
|
var meshrights = GetMeshRights(currentMesh), mname = EscapeHtml(currentMesh.name);
|
|
if (mname.length == 0) { mname = '<i>' + "None" + '</i>'; }
|
|
if ((meshrights & 1) != 0) { mname = '<span tabindex=0 title=\"' + "Click here to edit the device group name" + '\" onclick=p20editmesh(1) onkeyup="if (event.key == \'Enter\') p20editmesh(1)" style=cursor:pointer>' + mname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
|
|
QH('p20meshName', mname);
|
|
|
|
var meshtype = format("Unknown #{0}", currentMesh.mtype);
|
|
if (currentMesh.mtype == 1) meshtype = "Intel® AMT only, no agent";
|
|
if (currentMesh.mtype == 2) meshtype = "Managed using a software agent";
|
|
|
|
var x = addHtmlValue("Description", addLinkConditional(((currentMesh.desc && currentMesh.desc != '')?EscapeHtml(currentMesh.desc):('<i>' + "None" + '</i>')), 'p20editmesh(2)', (meshrights & 1) != 0));
|
|
|
|
// Display group type
|
|
x += addHtmlValue("Type", meshtype);
|
|
//x += addHtmlValue('Identifier', currentMesh._id.split('/')[2]);
|
|
|
|
// Display features
|
|
if (currentMesh.mtype == 2) {
|
|
var meshFeatures = [];
|
|
if (currentMesh.flags) {
|
|
if (currentMesh.flags & 1) { meshFeatures.push("Auto-Remove"); }
|
|
if (currentMesh.flags & 2) { meshFeatures.push("Hostname Sync"); }
|
|
}
|
|
meshFeatures = meshFeatures.join(', ');
|
|
if (meshFeatures == '') { meshFeatures = '<i>' + "None" + '</i>'; }
|
|
x += addHtmlValue("Features", addLinkConditional(meshFeatures, 'p20editmeshfeatures()', meshrights & 1));
|
|
}
|
|
|
|
// Display device group user consent
|
|
if (currentMesh.mtype == 2) {
|
|
var meshFeatures = [];
|
|
var consent = 0;
|
|
if (currentMesh.consent) { consent = currentMesh.consent; }
|
|
if (serverinfo.consent) { consent |= serverinfo.consent; }
|
|
if ((consent & 0x0040) && (consent & 0x0008)) { meshFeatures.push("Desktop Prompt+Toolbar"); } else if (consent & 0x0040) { meshFeatures.push("Desktop Toolbar"); } else if (consent & 0x0008) { meshFeatures.push("Desktop Prompt"); } else { if (consent & 0x0001) { meshFeatures.push("Desktop Notify"); } }
|
|
if (consent & 0x0010) { meshFeatures.push("Terminal Prompt"); } else { if (consent & 0x0002) { meshFeatures.push("Terminal Notify"); } }
|
|
if (consent & 0x0020) { meshFeatures.push("Files Prompt"); } else { if (consent & 0x0004) { meshFeatures.push("Files Notify"); } }
|
|
if (consent == 7) { meshFeatures = ["Always Notify"]; }
|
|
if ((consent & 56) == 56) { meshFeatures = ["Always Prompt"]; }
|
|
|
|
meshFeatures = meshFeatures.join(', ');
|
|
if (meshFeatures == '') { meshFeatures = '<i>' + "None" + '</i>'; }
|
|
x += addHtmlValue("User Consent", addLinkConditional(meshFeatures, 'p20editmeshconsent(1)', meshrights & 1));
|
|
}
|
|
|
|
// Display user notification
|
|
var meshNotify = 0, meshNotifyStr = [];
|
|
if (userinfo.links && userinfo.links[currentMesh._id] && userinfo.links[currentMesh._id].notify) { meshNotify = userinfo.links[currentMesh._id].notify; }
|
|
if (meshNotify & 2) { meshNotifyStr.push("Connect"); }
|
|
if (meshNotify & 4) { meshNotifyStr.push("Disconnect"); }
|
|
if (meshNotify & 8) { meshNotifyStr.push("Intel® AMT"); }
|
|
if (meshNotifyStr.length == 0) { meshNotifyStr.push('<i>' + "None" + '</i>'); }
|
|
x += addHtmlValue("Notifications", addLink(meshNotifyStr.join(', '), 'p20editMeshNotify()'));
|
|
|
|
// Display invitation codes
|
|
if ((features & 0x01000000) && (currentMesh.mtype == 2)) {
|
|
var inviteCodeStr = '<i>' + "None" + '</i>', icodes = false;
|
|
if (currentMesh.invite != null) { icodes = true; inviteCodeStr = currentMesh.invite.codes.join(', '); /* + ', ' + currentMesh.invite.flags;*/ }
|
|
x += addHtmlValue("Invite Codes", addLinkConditional(inviteCodeStr, 'p20editmeshInviteCode()', (meshrights & 1) || (icodes)));
|
|
}
|
|
|
|
// Intel AMT setup
|
|
var intelAmtPolicy = "No Policy";
|
|
if (currentMesh.amt) {
|
|
if (currentMesh.amt.type == 1) { intelAmtPolicy = 'Deactivate Client Control Mode (CCM)'; }
|
|
else if (currentMesh.amt.type == 2) {
|
|
intelAmtPolicy = "Simple Client Control Mode (CCM)";
|
|
if (currentMesh.amt.cirasetup == 2) { intelAmtPolicy += " + CIRA"; }
|
|
} else if (currentMesh.amt.type == 3) {
|
|
intelAmtPolicy = "Simple Admin Control Mode (ACM)";
|
|
if (currentMesh.amt.cirasetup == 2) { intelAmtPolicy += " + CIRA"; }
|
|
}
|
|
}
|
|
x += addHtmlValue("Intel® AMT", addLinkConditional(intelAmtPolicy, 'p20editMeshAmt()', meshrights & 1));
|
|
|
|
// Display group note support
|
|
if (meshrights & 1) { x += '<br><input type=button value=' + "Notes" + ' title=\"' + "View notes about this device group" + '\" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />'; }
|
|
|
|
x += '<br style=clear:both><br>';
|
|
if (meshrights & 2) {
|
|
x += '<a href=# onclick="return p20showAddMeshUserDialog()" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Users" + '</a>';
|
|
if (usergroups != null) {
|
|
var userGroupCount = 0, newUserGroup = false;
|
|
for (var i in usergroups) { userGroupCount++; if ((currentMesh.links == null) || (currentMesh.links[i] == null)) { newUserGroup = true; } }
|
|
if ((userGroupCount > 0) && (newUserGroup)) { x += '<a href=# onclick="return p20showAddMeshUserDialog(2)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add User Group" + '</a>'; }
|
|
}
|
|
}
|
|
|
|
if (meshrights & 4) {
|
|
if (currentMesh.mtype == 1) {
|
|
x += '<a href=# onclick=\'return addCiraDeviceToMesh(\"' + currentMesh._id + '\")\' style=cursor:pointer;margin-right:10px title=\"' + "Add a new Intel® AMT computer that is located on the internet." + '\"><img src=images/icon-installmesh.png border=0 height=12 width=12> ' + "Install CIRA" + '</a>';
|
|
x += '<a href=# onclick=\'return addDeviceToMesh(\"' + currentMesh._id + '\")\' style=cursor:pointer;margin-right:10px title=\"' + "Add a new Intel® AMT computer that is located on the local network." + '\"><img src=images/icon-installmesh.png border=0 height=12 width=12> ' + "Install local" + '</a>';
|
|
if (currentMesh.amt && (currentMesh.amt.type == 2)) { // CCM activation
|
|
x += '<a href=# onclick=\'return showCcmActivation(\"' + currentMesh._id + '\")\' style=cursor:pointer;margin-right:10px title=\"' + "Perform Intel AMT client control mode (CCM) activation." + '\"><img src=images/icon-installmesh.png border=0 height=12 width=12> ' + "Activation" + '</a>';
|
|
} else if (currentMesh.amt && (currentMesh.amt.type == 3) && ((features & 0x00100000) != 0)) { // ACM activation
|
|
x += '<a href=# onclick=\'return showAcmActivation(\"' + currentMesh._id + '\")\' style=cursor:pointer;margin-right:10px title=\"' + "Perform Intel AMT admin control mode (ACM) activation." + '\"><img src=images/icon-installmesh.png border=0 height=12 width=12> ' + "Activation" + '</a>';
|
|
}
|
|
}
|
|
if (currentMesh.mtype == 2) {
|
|
x += '<a href=# onclick=\'return addAgentToMesh(\"' + currentMesh._id + '\")\' style=cursor:pointer;margin-right:10px title=\"' + "Add a new computer to this device group by installing the mesh agent." + '\"><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Agent" + '</a>';
|
|
x += '<a href=# onclick=\'return inviteAgentToMesh(\"' + currentMesh._id + '\")\' style=cursor:pointer;margin-right:10px title=\"' + "Invite someone to install the mesh agent on this device group." + '\"><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Invite" + '</a>';
|
|
}
|
|
}
|
|
|
|
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>';
|
|
|
|
// Sort the users for this mesh
|
|
var count = 1, sortedusers = [];
|
|
for (var i in currentMesh.links) {
|
|
var uname = i.split('/')[2];
|
|
if (currentMesh.links[i].name) { uname = currentMesh.links[i].name; }
|
|
if (i == userinfo._id) { uname = userinfo.name; }
|
|
if ((usergroups != null) && (usergroups[i] != null)) { uname = usergroups[i].name; }
|
|
sortedusers.push({ id: i, name: uname, rights: currentMesh.links[i].rights });
|
|
}
|
|
sortedusers.sort(function(a, b) { if (a.name > b.name) return 1; if (a.name < b.name) return -1; return 0; });
|
|
|
|
// Display all users for this device group
|
|
for (var i in sortedusers) {
|
|
var trash = '', r = sortedusers[i].rights, rights = makeDeviceGroupRightsString(r), icon = 2;
|
|
if ((sortedusers[i].id != userinfo._id) && (meshrights == 0xFFFFFFFF || (((meshrights & 2) != 0)))) {
|
|
if ((meshrights == 0xFFFFFFFF) || (currentMesh.links[sortedusers[i].id].rights != 0xFFFFFFFF)) {
|
|
trash = '<a href=# onclick=\'return p20deleteUser(event,"' + encodeURIComponent(sortedusers[i].id) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
|
}
|
|
rights = '<span tabindex=0 style=cursor:pointer onclick=p20viewuser("' + encodeURIComponent(sortedusers[i].id) + '") onkeypress="if (event.key==\'Enter\') p20viewuser(\'' + encodeURIComponent(sortedusers[i].id) + '\')">' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
|
|
}
|
|
if (sortedusers[i].id.startsWith('ugrp/')) { icon = 4; }
|
|
var username = EscapeHtml(decodeURIComponent(sortedusers[i].name));
|
|
if ((usergroups != null) && sortedusers[i].id.startsWith('ugrp/')) { username = '<a tabindex=0 href=# onclick=\'gotoUserGroup("' + encodeURIComponent(sortedusers[i].id) + '");haltEvent(event);\'>' + username + '</a>'; }
|
|
if ((users != null) && sortedusers[i].id.startsWith('user/')) { username = '<a tabindex=0 href=# onclick=\'gotoUser("' + encodeURIComponent(sortedusers[i].id) + '");haltEvent(event);\'>' + username + '</a>'; }
|
|
x += '<tr style=' + (((count % 2) == 0) ? ';background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "User" + '\" class=m' + icon + '></div><div> ' + username + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
|
|
++count;
|
|
}
|
|
|
|
x += '</tbody></table>';
|
|
|
|
// If we are full administrator on this mesh, allow deletion of the mesh
|
|
if (meshrights == 0xFFFFFFFF) { x += '<div style=font-size:x-small;text-align:right><span><a href=# onclick=p20showDeleteMeshDialog() style=cursor:pointer>' + "Delete Group" + '</a></span></div>'; }
|
|
|
|
QH('p20info', x);
|
|
|
|
// Change the URL
|
|
var urlviewmode = '';
|
|
if ((xxcurrentView >= 20) && (xxcurrentView <= 29) && (currentMesh != null)) {
|
|
urlviewmode = '?viewmode=' + xxcurrentView + '&gotomesh=' + currentMesh._id.split('/')[2];
|
|
for (var i in urlargs) { urlviewmode += ('&' + i + '=' + urlargs[i]); }
|
|
try { window.history.replaceState({}, document.title, window.location.pathname + urlviewmode); } catch (ex) { }
|
|
}
|
|
}
|
|
|
|
function p20editMeshAmt() {
|
|
if (xxdialogMode) return;
|
|
var x = '', acmoption = '';
|
|
if ((features & 0x100000) != 0) { acmoption = '<option value=3>' + "Simple Admin Control Mode (ACM)" + '</option>'; }
|
|
if (currentMesh.mtype == 1) {
|
|
x += addHtmlValue("Type", '<select id=dp20amtpolicy style=width:230px onchange=p20editMeshAmtChange()><option value=0>' + "No Policy" + '</option><option value=2>' + "Simple Client Control Mode (CCM)" + '</option>' + acmoption + '</select>');
|
|
} else {
|
|
x += addHtmlValue("Type", '<select id=dp20amtpolicy style=width:230px onchange=p20editMeshAmtChange()><option value=0>' + "No Policy" + '</option><option value=1>' + "Deactivate Client Control Mode (CCM)" + '</option><option value=2>' + "Simple Client Control Mode (CCM)" + '</option>' + acmoption + '</select>');
|
|
}
|
|
x += '<div id=dp20amtpolicydiv></div>';
|
|
setDialogMode(2, "Intel® AMT Policy", 3, p20editMeshAmtEx, x);
|
|
if (currentMesh.amt) { Q('dp20amtpolicy').value = currentMesh.amt.type; }
|
|
p20editMeshAmtChange();
|
|
|
|
// Set the current Intel AMT policy
|
|
if (currentMesh.amt && (currentMesh.amt.type == 2) || (currentMesh.amt.type == 3)) {
|
|
Q('dp20amtpolicypass').value = currentMesh.amt.password;
|
|
if ((currentMesh.amt.type == 2) && (currentMesh.amt.badpass != null)) { Q('dp20amtbadpass').value = currentMesh.amt.badpass; }
|
|
if ((features & 0x400) == 0) { Q('dp20amtcira').value = currentMesh.amt.cirasetup; }
|
|
}
|
|
|
|
dp20amtValidatePolicy();
|
|
}
|
|
|
|
function p20editMeshAmtChange() {
|
|
var ptype = Q('dp20amtpolicy').value, x = '';
|
|
if (ptype >= 2) {
|
|
x = addHtmlValue("Password*", '<input id=dp20amtpolicypass type=password style=width:230px maxlength=32 onchange=dp20amtValidatePolicy() onkeyup=dp20amtValidatePolicy() autocomplete=off />')
|
|
x += addHtmlValue("Password*", '<input id=dp20amtpolicypass2 type=password style=width:230px maxlength=32 onchange=dp20amtValidatePolicy() onkeyup=dp20amtValidatePolicy() autocomplete=off />')
|
|
if ((ptype == 2) && (currentMesh.mtype == 2)) { x += addHtmlValue("Password mismatch", '<select id=dp20amtbadpass style=width:230px><option value=0>' + "Do nothing" + '</option><option value=1>' + "Reactivate Intel® AMT" + '</option></select>'); }
|
|
if ((features & 0x400) == 0) {
|
|
if (ptype == 2) {
|
|
x += addHtmlValue('<span title="' + "Client Initiated Remote Access" + '">' + "CIRA" + '</span>', '<select id=dp20amtcira style=width:230px><option value=0>' + "Don\'t configure" + '</option><option value=1>' + "Don\'t connect to server" + '</option><option value=2>' + "Connect to server" + '</option></select>');
|
|
} else {
|
|
x += addHtmlValue('<span title="' + "Client Initiated Remote Access" + '">' + "CIRA" + '</span>', '<select id=dp20amtcira style=width:230px><option value=0>' + "Don\'t configure" + '</option><option value=2>' + "Connect to server" + '</option></select>');
|
|
}
|
|
}
|
|
x += '<br/><span style="font-size:10px">' + "* Leave blank to assign a random password to each device." + '</span><br/>';
|
|
if (currentMesh.mtype == 2) {
|
|
if (ptype == 2) {
|
|
x += '<span style="font-size:10px">' + "This policy will not impact devices with Intel® AMT in ACM mode." + '</span><br/>';
|
|
x += '<span style="font-size:10px">' + "This is not a secure policy as agents will be performing activation." + '</span>';
|
|
} else {
|
|
x += '<span style="font-size:10px">' + "During activation, the agent will have access to admin password infomation." + '</span>';
|
|
}
|
|
}
|
|
}
|
|
QH('dp20amtpolicydiv', x);
|
|
setTimeout(dp20amtValidatePolicy, 1);
|
|
}
|
|
|
|
function dp20amtValidatePolicy() {
|
|
var ok = true, ptype = Q('dp20amtpolicy').value;
|
|
if ((ptype == 2) || (ptype == 3)) {
|
|
var pass = Q('dp20amtpolicypass').value, pass2 = Q('dp20amtpolicypass2').value;
|
|
ok = ((pass === pass2) && ((pass === '') ? true : passwordcheck(pass)));
|
|
}
|
|
QE('idx_dlgOkButton', ok);
|
|
}
|
|
|
|
function p20editMeshAmtEx() {
|
|
var ptype = parseInt(Q('dp20amtpolicy').value), amtpolicy = { type: ptype };
|
|
if (ptype == 2) {
|
|
amtpolicy = { type: ptype, password: Q('dp20amtpolicypass').value };
|
|
if (currentMesh.mtype == 2) { amtpolicy.badpass = parseInt(Q('dp20amtbadpass').value); }
|
|
if ((features & 0x400) == 0) { amtpolicy.cirasetup = parseInt(Q('dp20amtcira').value); } else { amtpolicy.cirasetup = 1; }
|
|
} else if (ptype == 3) {
|
|
amtpolicy = { type: ptype, password: Q('dp20amtpolicypass').value };
|
|
if ((features & 0x400) == 0) { amtpolicy.cirasetup = parseInt(Q('dp20amtcira').value); } else { amtpolicy.cirasetup = 1; }
|
|
}
|
|
meshserver.send({ action: 'meshamtpolicy', meshid: currentMesh._id, amtpolicy: amtpolicy });
|
|
}
|
|
|
|
function p20showDeleteMeshDialog() {
|
|
if (xxdialogMode) return false;
|
|
var x = format("Are you sure you want to delete group {0}? Deleting the device group will also delete all information about devices within this group.", EscapeHtml(currentMesh.name)) + '<br /><br />';
|
|
x += '<label><input id=p20check type=checkbox onchange=p20validateDeleteMeshDialog() />' + "Confirm" + '</label>';
|
|
setDialogMode(2, "Delete Group", 3, p20showDeleteMeshDialogEx, x);
|
|
p20validateDeleteMeshDialog();
|
|
return false;
|
|
}
|
|
|
|
function p20validateDeleteMeshDialog() {
|
|
QE('idx_dlgOkButton', Q('p20check').checked);
|
|
}
|
|
|
|
function p20showDeleteMeshDialogEx(buttons, tag) {
|
|
meshserver.send({ action: 'deletemesh', meshid: currentMesh._id, meshname: currentMesh.name });
|
|
}
|
|
|
|
function p20editmesh(focus) {
|
|
if (xxdialogMode) return;
|
|
var x = addHtmlValue("Name", '<input id=dp20meshname style=width:230px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate(event) />');
|
|
x += addHtmlValue("Description", '<div style=width:230px;margin:0;padding:0><textarea id=dp20meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
|
|
setDialogMode(2, "Edit Device Group", 3, p20editmeshEx, x);
|
|
Q('dp20meshname').value = currentMesh.name;
|
|
if (currentMesh.desc) Q('dp20meshdesc').value = currentMesh.desc;
|
|
p20editmeshValidate();
|
|
if (focus == 2) { Q('dp20meshdesc').focus(); } else { Q('dp20meshname').focus(); }
|
|
}
|
|
|
|
function p20editmeshEx() {
|
|
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, meshname: Q('dp20meshname').value, desc: Q('dp20meshdesc').value });
|
|
}
|
|
|
|
function p20editmeshValidate(e) {
|
|
QE('idx_dlgOkButton', Q('dp20meshname').value.length > 0);
|
|
if (e && e.key == 'Enter') { Q('dp20meshdesc').focus(); }
|
|
}
|
|
|
|
// editType: 1 = currentMesh, 2 = currentUser, 3 = currentDevice
|
|
function p20editmeshconsent(editType) {
|
|
if (xxdialogMode) return;
|
|
var x = '', consent = 0, title = '';
|
|
if (editType == 1) { consent = (currentMesh.consent) ? currentMesh.consent : 0; title = "Edit Device Group User Consent"; }
|
|
if (editType == 2) { consent = (currentUser.consent) ? currentUser.consent : 0; title = "Edit User Consent"; }
|
|
if (editType == 3) { consent = (currentNode.consent) ? currentNode.consent : 0; title = "Edit Device User Consent"; }
|
|
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px"><b>' + "Desktop" + '</b></div>';
|
|
x += '<div><label><input type=checkbox id=d20flag1 ' + ((consent & 0x0001) ? 'checked' : '') + '>' + "Notify user" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=d20flag2 ' + ((consent & 0x0008) ? 'checked' : '') + '>' + "Prompt for user consent" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=d20flag7 ' + ((consent & 0x0040) ? 'checked' : '') + '>' + "Show connection toolbar" + '</label></div>';
|
|
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:8px"><b>' + "Terminal" + '</b></div>';
|
|
x += '<div><label><input type=checkbox id=d20flag3 ' + ((consent & 0x0002) ? 'checked' : '') + '>' + "Notify user" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=d20flag4 ' + ((consent & 0x0010) ? 'checked' : '') + '>' + "Prompt for user consent" + '</label></div>';
|
|
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:8px"><b>' + "Files" + '</b></div>';
|
|
x += '<div><label><input type=checkbox id=d20flag5 ' + ((consent & 0x0004) ? 'checked' : '') + '>' + "Notify user" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=d20flag6 ' + ((consent & 0x0020) ? 'checked' : '') + '>' + "Prompt for user consent" + '</label></div>';
|
|
setDialogMode(2, title, 3, p20editmeshconsentEx, x, editType);
|
|
if (serverinfo.consent) {
|
|
if (serverinfo.consent & 0x0001) { Q('d20flag1').checked = true; }
|
|
if (serverinfo.consent & 0x0008) { Q('d20flag2').checked = true; }
|
|
if (serverinfo.consent & 0x0002) { Q('d20flag3').checked = true; }
|
|
if (serverinfo.consent & 0x0010) { Q('d20flag4').checked = true; }
|
|
if (serverinfo.consent & 0x0004) { Q('d20flag5').checked = true; }
|
|
if (serverinfo.consent & 0x0020) { Q('d20flag6').checked = true; }
|
|
if (serverinfo.consent & 0x0040) { Q('d20flag7').checked = true; }
|
|
QE('d20flag1', !(serverinfo.consent & 0x0001));
|
|
QE('d20flag2', !(serverinfo.consent & 0x0008));
|
|
QE('d20flag3', !(serverinfo.consent & 0x0002));
|
|
QE('d20flag4', !(serverinfo.consent & 0x0010));
|
|
QE('d20flag5', !(serverinfo.consent & 0x0004));
|
|
QE('d20flag6', !(serverinfo.consent & 0x0020));
|
|
QE('d20flag7', !(serverinfo.consent & 0x0040));
|
|
}
|
|
}
|
|
|
|
function p20editmeshconsentEx(b, editType) {
|
|
var consent = 0;
|
|
if (Q('d20flag1').checked) { consent += 0x0001; }
|
|
if (Q('d20flag2').checked) { consent += 0x0008; }
|
|
if (Q('d20flag3').checked) { consent += 0x0002; }
|
|
if (Q('d20flag4').checked) { consent += 0x0010; }
|
|
if (Q('d20flag5').checked) { consent += 0x0004; }
|
|
if (Q('d20flag6').checked) { consent += 0x0020; }
|
|
if (Q('d20flag7').checked) { consent += 0x0040; }
|
|
if (editType == 1) { meshserver.send({ action: 'editmesh', meshid: currentMesh._id, consent: consent }); }
|
|
if (editType == 2) { meshserver.send({ action: 'edituser', id: currentUser._id, consent: consent }); }
|
|
if (editType == 3) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, consent: consent }); }
|
|
}
|
|
|
|
function p20editmeshfeatures() {
|
|
if (xxdialogMode) return;
|
|
var flags = (currentMesh.flags)?currentMesh.flags:0;
|
|
var x = '<div><label><input type=checkbox id=d20flag1 ' + ((flags & 1) ? 'checked' : '') + '>' + "Remove device on disconnect" + '</label><br></div>';
|
|
x += '<div><label><input type=checkbox id=d20flag2 ' + ((flags & 2) ? 'checked' : '') + '>' + "Sync server device name to hostname" + '</label><br></div>';
|
|
setDialogMode(2, "Edit Device Group Features", 3, p20editmeshfeaturesEx, x);
|
|
}
|
|
|
|
function p20editmeshfeaturesEx() {
|
|
var flags = 0;
|
|
if (Q('d20flag1').checked) { flags += 1; }
|
|
if (Q('d20flag2').checked) { flags += 2; }
|
|
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, flags: flags });
|
|
}
|
|
|
|
function p20showAddMeshUserDialog(userid, selected) {
|
|
if (xxdialogMode) return false;
|
|
var x = '';
|
|
if ((userid == null) || (userid == 5)) {
|
|
if (selected == null) {
|
|
if (userid == null) { x += "Allow users to manage this device group and devices in this group."; } else { x += "Allow users to manage this device."; }
|
|
if (features & 0x00080000) { x += " Users need to login to this server once before they can be added to a device group." }
|
|
x += '<br /><br />';
|
|
}
|
|
x += '<div style=\'position:relative\'>';
|
|
x += addHtmlValue("User Names", '<input id=dp20username style=width:230px maxlength=32 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() placeholder="user1, user2, user3" />');
|
|
x += '<div id=dp20usersuggest class=suggestionBox style=\'top:30px;left:130px;display:none\'></div>';
|
|
x += '</div><br>';
|
|
} else if (userid == 1) {
|
|
var y = '';
|
|
if (selected == null) {
|
|
var omeshs = getOrderedList(meshes, 'name');
|
|
for (var i in omeshs) { if ((currentUser.links == null) || (currentUser.links[omeshs[i]._id] == null)) { y += '<option value=' + encodeURIComponent(omeshs[i]._id) + '>' + EscapeHtml(omeshs[i].name) + '</option>'; } }
|
|
} else {
|
|
y += '<option value=' + selected + '>' + EscapeHtml(meshes[decodeURIComponent(selected)].name) + '</option>';
|
|
}
|
|
x += addHtmlValue("Device Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog() id=dp2groupid style=width:100%' + (selected?' disabled':'') + '>' + y + '</select></div>');
|
|
} else if (userid == 2) {
|
|
if (usergroups == null) return;
|
|
var y = '';
|
|
var ousergroups = getOrderedList(usergroups, 'name');
|
|
for (var i in ousergroups) { if ((currentMesh.links == null) || (currentMesh.links[ousergroups[i]._id] == null)) { y += '<option value=' + encodeURIComponent(ousergroups[i]._id) + '>' + EscapeHtml(ousergroups[i].name) + '</option>'; } }
|
|
x += addHtmlValue("User Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog() id=dp2groupid style=width:100%>' + y + '</select></div>');
|
|
} else if (userid == 6) {
|
|
if (usergroups == null) return;
|
|
var y = '';
|
|
if (selected == null) {
|
|
var ousergroups = getOrderedList(usergroups, 'name');
|
|
for (var i in ousergroups) { if ((currentNode.links == null) || (currentNode.links[ousergroups[i]._id] == null)) { y += '<option value=' + encodeURIComponent(ousergroups[i]._id) + '>' + EscapeHtml(ousergroups[i].name) + '</option>'; } }
|
|
} else {
|
|
y += '<option value=' + selected + '>' + EscapeHtml(usergroups[decodeURIComponent(selected)].name) + '</option>';
|
|
}
|
|
x += addHtmlValue("User Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog() id=dp2groupid style=width:100%' + (selected?' disabled':'') + '>' + y + '</select></div>');
|
|
} else if (userid == 3) {
|
|
var y = '';
|
|
if (selected) { selected = decodeURIComponent(selected); }
|
|
var omeshs = getOrderedList(meshes, 'name');
|
|
for (var i in omeshs) { if ((selected != null) || (currentUserGroup.links == null) || (currentUserGroup.links[omeshs[i]._id] == null)) { y += '<option value=' + encodeURIComponent(omeshs[i]._id) + ((selected == omeshs[i]._id)?' selected':' ') + '>' + EscapeHtml(omeshs[i].name) + '</option>'; } }
|
|
x += addHtmlValue("Device Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog(' + userid + ') id=dp2groupid style=width:100%>' + y + '</select></div>');
|
|
} else if ((userid == 4) || (userid == 7)) {
|
|
var y = '', selectedMeshId = null, selectedNode = null;
|
|
if (selected != null) { selectedNode = getNodeFromId(decodeURIComponent(selected)); if (selectedNode != null) { selectedMeshId = selectedNode.meshid; } }
|
|
var omeshs = getOrderedList(meshes, 'name');
|
|
for (var i in omeshs) {
|
|
if ((omeshs[i].links[userinfo._id] != null) && (omeshs[i].links[userinfo._id].rights & 7)) { // Only show device groups that we have user administrator for.
|
|
if (selectedMeshId == null) { selectedMeshId = omeshs[i]._id; } y += '<option value=' + encodeURIComponent(omeshs[i]._id) + ((selectedMeshId == omeshs[i]._id)?' selected':' ') + '>' + EscapeHtml(omeshs[i].name) + '</option>';
|
|
}
|
|
}
|
|
x += addHtmlValue("Device Group", '<div style=width:230px;margin:0;padding:0><select onchange=p20changeMeshAddMeshUserDialog(' + userid + ') id=dp2meshid style=width:100%>' + y + '</select></div>');
|
|
y = '';
|
|
var onodes = getOrderedList(nodes, 'name');
|
|
for (var i in onodes) { if (onodes[i].meshid == selectedMeshId) { y += '<option value=' + encodeURIComponent(onodes[i]._id) + ((selectedNode == onodes[i])?' selected':' ') + '>' + EscapeHtml(onodes[i].name) + '</option>'; } }
|
|
x += addHtmlValue("Device", '<div style=width:230px;margin:0;padding:0><select onchange=p20validateAddMeshUserDialog(' + userid + ') id=dp2nodeid style=width:100%>' + y + '</select></div>');
|
|
} else {
|
|
userid = decodeURIComponent(userid);
|
|
var uname = userid.split('/')[2];
|
|
if (users && users[userid]) { uname = users[userid].name; }
|
|
if (usergroups && usergroups[userid]) { uname = usergroups[userid].name; }
|
|
if (userinfo._id == userid) { uname = userinfo.name; }
|
|
if (userid.startsWith('ugrp/')) {
|
|
x += format("Group permissions for {0}.", uname) + '<br /><br />';
|
|
} else {
|
|
x += format("Group permissions for user {0}.", uname) + '<br /><br />';
|
|
}
|
|
}
|
|
|
|
var urights = -1, meshRightsActive = ((userid != 4) && (userid != 5) && (userid != 6) && (userid != 7));
|
|
|
|
x += '<div style="height:120px;overflow-y:scroll;border:1px solid gray">';
|
|
if (meshRightsActive) {
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20fulladmin>' + "Full Administrator" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editmesh>' + "Edit Device Group" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20manageusers>' + "Manage Device Group Users" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20managecomputers>' + "Manage Device Group Computers" + '</label><br>';
|
|
}
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotecontrol>' + "Remote Control" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remoteview style=margin-left:12px>' + "Remote View Only" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotelimitedinput style=margin-left:12px>' + "Limited Input Only" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20nodesktop style=margin-left:12px>' + "No Desktop Access" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20noterminal style=margin-left:12px>' + "No Terminal Access" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20nofiles style=margin-left:12px>' + "No File Access" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20noamt style=margin-left:12px>' + "No Intel® AMT" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshagentconsole>' + "Mesh Agent Console" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshserverfiles>' + "Server Files" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20wakedevices>' + "Wake Devices" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editnotes>' + "Edit Device Notes" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20limitevents>' + "Show Only Own Events" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20chatnotify>' + "Chat & Notify" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20uninstall>' + "Uninstall Agent" + '</label><br>';
|
|
x += '</div>';
|
|
|
|
if (userid == null) {
|
|
setDialogMode(2, "Add Users to Device Group", 3, p20showAddMeshUserDialogEx, x);
|
|
QE('p20fulladmin', GetMeshRights(currentMesh) == 0xFFFFFFFF);
|
|
Q('dp20username').focus();
|
|
} else if (userid === 1) {
|
|
setDialogMode(2, (selected == null)?"Add Device Group Permissions":"Edit Device Group Permissions", selected?7:3, p20showAddMeshUserDialogEx, x, userid);
|
|
if (selected != null) { urights = meshes[decodeURIComponent(selected)].links[currentUser._id].rights; }
|
|
if (urights == 0xFFFFFFFF) { Q('p20fulladmin').checked = true; urights = -1; }
|
|
} else if (userid === 2) {
|
|
setDialogMode(2, "Add User Group", 3, p20showAddMeshUserDialogEx, x, userid);
|
|
QE('p20fulladmin', GetMeshRights(currentMesh) == 0xFFFFFFFF);
|
|
} else if (userid === 3) {
|
|
setDialogMode(2, (selected == null)?"Add Device Group":"Edit Device Group", selected?7:3, p20showAddMeshUserDialogEx, x, userid);
|
|
QE('dp2groupid', selected == null);
|
|
if (selected != null) { urights = currentUserGroup.links[decodeURIComponent(selected)].rights; }
|
|
if (urights == 0xFFFFFFFF) { Q('p20fulladmin').checked = true; urights = -1; }
|
|
} else if (userid === 4) {
|
|
setDialogMode(2, (selected == null)?"Add Device Permissions":"Edit Device Permissions", selected?7:3, p20showAddMeshUserDialogEx, x, userid);
|
|
QE('dp2meshid', selected == null);
|
|
QE('dp2nodeid', selected == null);
|
|
} else if (userid === 7) {
|
|
setDialogMode(2, (selected == null)?"Add Device Permissions":"Edit Device Permissions", selected?7:3, p20showAddMeshUserDialogEx, x, userid);
|
|
QE('dp2meshid', selected == null);
|
|
QE('dp2nodeid', selected == null);
|
|
if (selected != null) { urights = currentUserGroup.links[decodeURIComponent(selected)].rights; QE('dp20username', false); }
|
|
} else if (userid === 5) {
|
|
setDialogMode(2, selected?"Edit User Device Permissions":"Add User Device Permissions", selected?7:3, p20showAddMeshUserDialogEx, x, userid);
|
|
if (selected != null) {
|
|
selected = decodeURIComponent(selected);
|
|
if ((users != null) && (users[selected] != null)) { Q('dp20username').value = users[selected].name; } else { Q('dp20username').value = selected.split('/')[2]; }
|
|
urights = currentNode.links[selected].rights;
|
|
QE('dp20username', false);
|
|
}
|
|
Q('dp20username').focus();
|
|
} else if (userid === 6) {
|
|
setDialogMode(2, selected?"Edit User Group Device Permissions":"Add User Group Device Permissions", selected?7:3, p20showAddMeshUserDialogEx, x, userid);
|
|
if (selected != null) { urights = currentNode.links[decodeURIComponent(selected)].rights; }
|
|
} else {
|
|
if (userid.startsWith('ugrp/')) {
|
|
setDialogMode(2, "Edit Device Group Permissions", 7, p20showAddMeshUserDialogEx, x, userid);
|
|
} else {
|
|
setDialogMode(2, "Edit User Device Group Permissions", 7, p20showAddMeshUserDialogEx, x, userid);
|
|
}
|
|
var cmeshrights = GetMeshRights(currentMesh), urights = GetMeshRights(currentMesh, userid);
|
|
if (urights == 0xFFFFFFFF) { Q('p20fulladmin').checked = true; urights = -1; }
|
|
QE('p20fulladmin', GetMeshRights(currentMesh) == 0xFFFFFFFF);
|
|
}
|
|
|
|
if (urights != -1) {
|
|
if (meshRightsActive) {
|
|
if (urights & 1) { Q('p20editmesh').checked = true; }
|
|
if (urights & 2) { Q('p20manageusers').checked = true; }
|
|
if (urights & 4) { Q('p20managecomputers').checked = true; }
|
|
}
|
|
if (urights & 8) {
|
|
Q('p20remotecontrol').checked = true;
|
|
if (urights & 65536) { Q('p20nodesktop').checked = true; }
|
|
if (urights & 256) { Q('p20remoteview').checked = true; }
|
|
if (urights & 512) { Q('p20noterminal').checked = true; }
|
|
if (urights & 1024) { Q('p20nofiles').checked = true; }
|
|
if (urights & 2048) { Q('p20noamt').checked = true; }
|
|
if (urights & 4096) { Q('p20remotelimitedinput').checked = true; }
|
|
}
|
|
if (urights & 16) { Q('p20meshagentconsole').checked = true; }
|
|
if (urights & 32) { Q('p20meshserverfiles').checked = true; }
|
|
if (urights & 64) { Q('p20wakedevices').checked = true; }
|
|
if (urights & 128) { Q('p20editnotes').checked = true; }
|
|
if (urights & 8192) { Q('p20limitevents').checked = true; }
|
|
if (urights & 16384) { Q('p20chatnotify').checked = true; }
|
|
if (urights & 32768) { Q('p20uninstall').checked = true; }
|
|
}
|
|
|
|
p20validateAddMeshUserDialog(userid);
|
|
return false;
|
|
}
|
|
|
|
function p20changeMeshAddMeshUserDialog(userid) {
|
|
var y = '', meshid = decodeURIComponent(Q('dp2meshid').value);
|
|
for (var i in nodes) { if (nodes[i].meshid == meshid) { y += '<option value=' + encodeURIComponent(nodes[i]._id) + '>' + EscapeHtml(nodes[i].name) + '</option>'; } }
|
|
QH('dp2nodeid', y);
|
|
p20validateAddMeshUserDialog(userid);
|
|
}
|
|
|
|
function p20setname(name) {
|
|
name = decodeURIComponent(name);
|
|
var xusers = Q('dp20username').value.split(',');
|
|
for (var i in xusers) { xusers[i] = xusers[i].trim(); }
|
|
xusers[xusers.length - 1] = name;
|
|
Q('dp20username').value = xusers.join(', ');
|
|
p20validateAddMeshUserDialog();
|
|
return false;
|
|
}
|
|
|
|
function p20validateAddMeshUserDialog(updateId) {
|
|
var ok = true;
|
|
|
|
if (updateId === 4) {
|
|
// Update user device rights
|
|
var devrights = 0, nodeid = decodeURIComponent(Q('dp2nodeid').value);
|
|
if ((nodeid != '') && (currentUser.links != null) && (currentUser.links[nodeid] != null)) { devrights = currentUser.links[nodeid].rights; }
|
|
Q('p20remotecontrol').checked = ((devrights & 8) != 0);
|
|
Q('p20meshagentconsole').checked = ((devrights & 16) != 0);
|
|
Q('p20meshserverfiles').checked = ((devrights & 32) != 0);
|
|
Q('p20wakedevices').checked = ((devrights & 64) != 0);
|
|
Q('p20editnotes').checked = ((devrights & 128) != 0);
|
|
Q('p20remoteview').checked = ((devrights & 256) != 0);
|
|
Q('p20noterminal').checked = ((devrights & 512) != 0);
|
|
Q('p20nofiles').checked = ((devrights & 1024) != 0);
|
|
Q('p20noamt').checked = ((devrights & 2048) != 0);
|
|
Q('p20remotelimitedinput').checked = ((devrights & 4096) != 0);
|
|
Q('p20limitevents').checked = ((devrights & 8192) != 0);
|
|
Q('p20chatnotify').checked = ((devrights & 16384) != 0);
|
|
Q('p20uninstall').checked = ((devrights & 32768) != 0);
|
|
Q('p20nodesktop').checked = ((devrights & 65536) != 0);
|
|
ok = (nodeid != '');
|
|
}
|
|
|
|
/*
|
|
var meshrights = null;
|
|
if ((xxdialogTag === 1) || (xxdialogTag === 3)) {
|
|
meshrights = meshes[decodeURIComponent(Q('dp2groupid').value)].links[userinfo._id].rights;
|
|
//meshrights = GetMeshRights(decodeURIComponent(Q('dp2groupid').value));
|
|
} else {
|
|
meshrights = currentMesh.links[userinfo._id].rights;
|
|
//meshrights = GetMeshRights(currentMesh);
|
|
}
|
|
*/
|
|
if (Q('dp20username')) {
|
|
var xusers = Q('dp20username').value.split(',');
|
|
for (var i in xusers) {
|
|
var xuser = xusers[i] = xusers[i].trim();
|
|
if (xuser.length == 0) { ok = false; } else if (xuser.indexOf('"') >= 0) { ok = false; }
|
|
}
|
|
|
|
// Fill the suggestion box
|
|
var showsuggestbox = false, exactMatch = false;
|
|
if (users != null) {
|
|
var lastuser = xusers[xusers.length - 1].trim(), lastuserl = lastuser.toLowerCase(), matchingUsers = [];
|
|
if (lastuser.length > 0) {
|
|
for (var i in users) {
|
|
if (users[i].name === lastuser) { exactMatch = true; break; }
|
|
if (users[i].name.toLowerCase().indexOf(lastuserl) >= 0) { matchingUsers.push(users[i].name); if (matchingUsers.length >= 8) break; }
|
|
}
|
|
if ((exactMatch == false) && (matchingUsers.length > 0)) {
|
|
var x = '';
|
|
for (var i in matchingUsers) { x += '<a href=# onclick=\'p20setname("' + encodeURIComponent(matchingUsers[i]) + '")\'>' + matchingUsers[i] + '</a><br />'; }
|
|
QH('dp20usersuggest', x);
|
|
showsuggestbox = true;
|
|
}
|
|
}
|
|
}
|
|
QV('dp20usersuggest', showsuggestbox);
|
|
}
|
|
QE('idx_dlgOkButton', ok);
|
|
|
|
var nc;
|
|
if (Q('p20fulladmin') != null) {
|
|
nc = !Q('p20fulladmin').checked;
|
|
//QE('p20fulladmin', meshrights == 0xFFFFFFFF);
|
|
//QE('p20editmesh', nc && (meshrights == 0xFFFFFFFF));
|
|
//QE('p20fulladmin', nc);
|
|
QE('p20editmesh', nc);
|
|
QE('p20manageusers', nc);
|
|
QE('p20managecomputers', nc);
|
|
} else {
|
|
nc = (nodeid != '');
|
|
}
|
|
QE('p20remotecontrol', nc);
|
|
QE('p20meshagentconsole', nc);
|
|
QE('p20meshserverfiles', nc);
|
|
QE('p20wakedevices', nc);
|
|
QE('p20editnotes', nc);
|
|
QE('p20limitevents', nc);
|
|
QE('p20remoteview', nc && Q('p20remotecontrol').checked);
|
|
QE('p20remotelimitedinput', nc && Q('p20remotecontrol').checked && !Q('p20remoteview').checked);
|
|
QE('p20nodesktop', nc && Q('p20remotecontrol').checked);
|
|
QE('p20noterminal', nc && Q('p20remotecontrol').checked);
|
|
QE('p20nofiles', nc && Q('p20remotecontrol').checked);
|
|
QE('p20noamt', nc && Q('p20remotecontrol').checked);
|
|
QE('p20chatnotify', nc);
|
|
QE('p20uninstall', nc);
|
|
}
|
|
|
|
function p20showAddMeshUserDialogEx(b, t) {
|
|
// Get the currently selected rights
|
|
var meshadmin = 0;
|
|
if ((Q('p20fulladmin') != null) && (Q('p20fulladmin').checked == true)) { meshadmin = 0xFFFFFFFF; } else {
|
|
if (Q('p20fulladmin') != null) {
|
|
if (Q('p20editmesh').checked == true) meshadmin += 1;
|
|
if (Q('p20manageusers').checked == true) meshadmin += 2;
|
|
if (Q('p20managecomputers').checked == true) meshadmin += 4;
|
|
}
|
|
if (Q('p20remotecontrol').checked == true) meshadmin += 8;
|
|
if (Q('p20meshagentconsole').checked == true) meshadmin += 16;
|
|
if (Q('p20meshserverfiles').checked == true) meshadmin += 32;
|
|
if (Q('p20wakedevices').checked == true) meshadmin += 64;
|
|
if (Q('p20editnotes').checked == true) meshadmin += 128;
|
|
if (Q('p20remoteview').checked == true) meshadmin += 256;
|
|
if (Q('p20nodesktop').checked == true) meshadmin += 65536;
|
|
if (Q('p20noterminal').checked == true) meshadmin += 512;
|
|
if (Q('p20nofiles').checked == true) meshadmin += 1024;
|
|
if (Q('p20noamt').checked == true) meshadmin += 2048;
|
|
if (Q('p20remotelimitedinput').checked == true) meshadmin += 4096;
|
|
if (Q('p20limitevents').checked == true) meshadmin += 8192;
|
|
if (Q('p20chatnotify').checked == true) meshadmin += 16384;
|
|
if (Q('p20uninstall').checked == true) meshadmin += 32768;
|
|
}
|
|
|
|
// Clean up incorrect rights. If Remote Control is not selected, remove flags that don't make sense.
|
|
if ((meshadmin & 8) == 0) {
|
|
// Remove 256, 512, 1024, 2048, 4096, 65536
|
|
if (meshadmin & 256) { meshadmin -= 256; }
|
|
if (meshadmin & 512) { meshadmin -= 512; }
|
|
if (meshadmin & 1024) { meshadmin -= 1024; }
|
|
if (meshadmin & 2048) { meshadmin -= 2048; }
|
|
if (meshadmin & 4096) { meshadmin -= 4096; }
|
|
if (meshadmin & 65536) { meshadmin -= 65536; }
|
|
}
|
|
|
|
// Send the action to the server
|
|
if (t === 1) {
|
|
// Add current user to device group
|
|
var meshid = decodeURIComponent(Q('dp2groupid').value), mesh = meshes[meshid];
|
|
if (mesh != null) { meshserver.send({ action: 'addmeshuser', meshid: meshid, meshname: mesh.name, userids: [ currentUser._id ], meshadmin: meshadmin, remove: (b == 2) }); }
|
|
} else if (t === 2) {
|
|
// Add user group to device group
|
|
var ugrpid = decodeURIComponent(Q('dp2groupid').value), mesh = meshes[currentMesh._id];
|
|
if (mesh != null) { meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, userid: ugrpid, meshadmin: meshadmin, remove: (b == 2) }); }
|
|
} else if (t === 3) {
|
|
// Add device group to current user group
|
|
var meshid = decodeURIComponent(Q('dp2groupid').value), mesh = meshes[meshid];
|
|
if (mesh != null) { meshserver.send({ action: 'addmeshuser', meshid: meshid, meshname: mesh.name, userids: [ currentUserGroup._id ], meshadmin: meshadmin, remove: (b == 2) }); }
|
|
} else if (t === 4) {
|
|
// Add current user to device
|
|
var nodeid = decodeURIComponent(Q('dp2nodeid').value), node = getNodeFromId(nodeid);
|
|
if (node != null) { meshserver.send({ action: 'adddeviceuser', nodeid: nodeid, nodename: node.name, userids: [ currentUser._id ], rights: meshadmin, remove: (b == 2) }); }
|
|
} else if (t === 5) {
|
|
// Add users to device
|
|
var users = Q('dp20username').value.split(','), users2 = [];
|
|
for (var i in users) { users2.push(users[i].trim()); }
|
|
meshserver.send({ action: 'adddeviceuser', nodeid: currentNode._id, nodename: currentNode.name, usernames: users2, rights: meshadmin, remove: (b == 2) });
|
|
} else if (t === 6) {
|
|
// Add user group to device
|
|
var ugrpid = decodeURIComponent(Q('dp2groupid').value);
|
|
if (currentNode != null) { meshserver.send({ action: 'adddeviceuser', nodeid: currentNode._id, nodename: currentNode.name, userids: [ ugrpid ], rights: meshadmin, remove: (b == 2) }); }
|
|
} else if (t === 7) {
|
|
// Add current user group to device
|
|
var nodeid = decodeURIComponent(Q('dp2nodeid').value), node = getNodeFromId(nodeid);
|
|
if (node != null) { meshserver.send({ action: 'adddeviceuser', nodeid: nodeid, nodename: node.name, userids: [ currentUserGroup._id ], rights: meshadmin, remove: (b == 2) }); }
|
|
} else {
|
|
// Add user to device group
|
|
if (t == null) {
|
|
var users = Q('dp20username').value.split(','), users2 = [];
|
|
for (var i in users) { users2.push(users[i].trim()); }
|
|
meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, usernames: users2, meshadmin: meshadmin, remove: (b == 2) });
|
|
} else {
|
|
meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, userids: [ t ], meshadmin: meshadmin, remove: (b == 2) });
|
|
}
|
|
}
|
|
}
|
|
|
|
function p20viewuser(userid) {
|
|
if (xxdialogMode) return;
|
|
var xuserid = decodeURIComponent(userid);
|
|
var cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, xuserid);
|
|
if (((userinfo._id) != xuserid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) {
|
|
p20showAddMeshUserDialog(userid);
|
|
} else {
|
|
var r = [];
|
|
if (meshrights == 0xFFFFFFFF) r.push("Full Administrator (all rights)"); else {
|
|
if ((meshrights & 1) != 0) r.push("Edit Device Group");
|
|
if ((meshrights & 2) != 0) r.push("Manage Device Group Users");
|
|
if ((meshrights & 4) != 0) r.push("Manage Device Group Computers");
|
|
if ((meshrights & 8) != 0) r.push("Remote Control");
|
|
if ((meshrights & 16) != 0) r.push("Agent Console");
|
|
if ((meshrights & 32) != 0) r.push("Server Files");
|
|
if ((meshrights & 64) != 0) r.push("Wake Devices");
|
|
if ((meshrights & 128) != 0) r.push("Edit Notes");
|
|
if (((meshrights & 8) != 0) && (meshrights & 256) != 0) r.push("Remote View Only");
|
|
if (((meshrights & 8) != 0) && (meshrights & 65536) != 0) r.push("No Desktop");
|
|
if (((meshrights & 8) != 0) && (meshrights & 512) != 0) r.push("No Terminal");
|
|
if (((meshrights & 8) != 0) && (meshrights & 1024) != 0) r.push("No Files");
|
|
if (((meshrights & 8) != 0) && (meshrights & 2048) != 0) r.push("No Intel® AMT");
|
|
if (((meshrights & 8) != 0) && ((meshrights & 4096) != 0) && ((meshrights & 256) == 0)) r.push("Limited Input");
|
|
if ((meshrights & 8192) != 0) r.push("Self Events Only");
|
|
if ((meshrights & 16384) != 0) r.push("Chat & Notify");
|
|
if ((meshrights & 32768) != 0) r.push("Uninstall");
|
|
}
|
|
if (r.length == 0) { r.push("No Rights"); }
|
|
var uname = xuserid.split('/')[2];
|
|
if (users && users[xuserid]) { uname = users[xuserid].name; }
|
|
if (userinfo._id == xuserid) { uname = userinfo.name; }
|
|
var buttons = 1, x = addHtmlValue("User Name", EscapeHtml(decodeURIComponent(uname)));
|
|
if (xuserid.split('/')[2] != uname) { x += addHtmlValue("User Identifier", EscapeHtml(xuserid.split('/')[2])); }
|
|
|
|
x += addHtmlValue("Permissions", r.join(", "));
|
|
if (((userinfo._id) != xuserid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4;
|
|
setDialogMode(2, "Device Group User", buttons, p20viewuserEx, x, xuserid);
|
|
}
|
|
}
|
|
|
|
function p20viewuserEx(button, userid) {
|
|
if (button != 2) return;
|
|
var uname = userid.split('/')[2];
|
|
if (users && users[userid]) { uname = users[userid].name; }
|
|
if (usergroups && usergroups[userid]) { uname = usergroups[userid].name; }
|
|
if (userinfo._id == userid) { uname = userinfo.name; }
|
|
if (userid.startsWith('user/')) { setDialogMode(2, "Remove User Permissions", 3, p20viewuserEx2, format("Confirm removal of rights for user \"{0}\"?", EscapeHtml(decodeURIComponent(uname))), userid); }
|
|
if (userid.startsWith('ugrp/')) { setDialogMode(2, "Remove User Group Permissions", 3, p20viewuserEx2, format("Confirm removal of rights for user group \"{0}\"?", EscapeHtml(decodeURIComponent(uname))), userid); }
|
|
}
|
|
function p20deleteUser(e, userid) { haltEvent(e); p20viewuserEx(2, decodeURIComponent(userid)); return false; }
|
|
function p20viewuserEx2(button, userid) { meshserver.send({ action: 'removemeshuser', meshid: currentMesh._id, meshname: currentMesh.name, userid: userid }); }
|
|
|
|
function p20editmeshInviteCode() {
|
|
if (xxdialogMode) return false;
|
|
var meshrights = GetMeshRights(currentMesh);
|
|
|
|
var servername = serverinfo.name;
|
|
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
|
var url;
|
|
if (serverinfo.https == true) {
|
|
var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
|
|
url = 'https://' + servername + portStr + domainUrl + 'invite';
|
|
} else {
|
|
var portStr = (serverinfo.port == 80) ? '' : (':' + serverinfo.port);
|
|
url = 'http://' + servername + portStr + domainUrl + 'invite';
|
|
}
|
|
|
|
if (meshrights & 1) {
|
|
// We can edit the mesh invite codes
|
|
var x = "When enabled, invitation codes can be used by anyone to join devices to this device group using the following public link:" + '<br /><br />';
|
|
x += '<div style=width:100%;text-align:center><a target=_blank href="' + url + '">' + url + '</a></div><br />';
|
|
x += '<div style=margin-bottom:5px><label><input id=agentJoinCheck type=checkbox onclick=p20editmeshInviteCodeValidate() />' + "Enable Invite Codes" + '</label></div>';
|
|
x += addHtmlValue("Invite Codes", '<input id=agentInviteCode style=width:236px onkeyup=p20editmeshInviteCodeValidate() placeholder="code1, code2, code3" />');
|
|
x += addHtmlValue("Installation Type", '<select id=agentInviteType style=width:236px><option value=0>' + "Background and interactive" + '</option><option value=2>' + "Background only" + '</option><option value=1>' + "Interactive only" + '</option></select>');
|
|
setDialogMode(2, "Invite Codes", 3, p20editmeshInviteCodeEx, x);
|
|
if (currentMesh.invite != null) {
|
|
Q('agentJoinCheck').checked = true;
|
|
Q('agentInviteCode').value = currentMesh.invite.codes.join(', ');
|
|
Q('agentInviteType').value = (currentMesh.invite.flags & 3);
|
|
}
|
|
p20editmeshInviteCodeValidate();
|
|
} else {
|
|
// View codes only
|
|
var x = "Invitation codes can be used by anyone to join devices to this device group using the following public link:" + '<br /><br />';
|
|
x += '<div style=width:100%;text-align:center><a target=_blank href="' + url + '">' + url + '</a></div><br />';
|
|
x += addHtmlValue("Invite Codes", currentMesh.invite.codes.join(', '));
|
|
x += addHtmlValue("Installation Type", ["Background and interactive", "Background only", "Interactive only"][currentMesh.invite.flags & 3]);
|
|
setDialogMode(2, "Invite Codes", 1, null, x);
|
|
}
|
|
}
|
|
|
|
function p20editmeshInviteCodeValidate() {
|
|
var ok = true, codes = Q('agentInviteCode').value.split(',');
|
|
for (var i in codes) { codes[i] = codes[i].trim(); if (codes[i] == '') { ok = false; } }
|
|
QE('agentInviteCode', Q('agentJoinCheck').checked);
|
|
QE('agentInviteType', Q('agentJoinCheck').checked);
|
|
QE('idx_dlgOkButton', (Q('agentJoinCheck').checked == false) || (ok));
|
|
}
|
|
|
|
function p20editmeshInviteCodeEx() {
|
|
if (Q('agentJoinCheck').checked == true) {
|
|
var codes = Q('agentInviteCode').value.split(',');
|
|
for (var i in codes) { codes[i] = codes[i].trim(); }
|
|
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, invite: { codes: codes, flags: parseInt(Q('agentInviteType').value) } });
|
|
} else {
|
|
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, invite: '*' });
|
|
}
|
|
}
|
|
|
|
function p20editMeshNotify() {
|
|
if (xxdialogMode) return false;
|
|
var meshNotify = 0;
|
|
if (userinfo.links && userinfo.links[currentMesh._id] && userinfo.links[currentMesh._id].notify) { meshNotify = userinfo.links[currentMesh._id].notify; }
|
|
var x = "Notification settings must also be turned on in account settings." + '<br /><br />';
|
|
x += '<div><label><input id=p20notifyIntelDeviceConnect type=checkbox />' + "Device connections." + '</label></div>';
|
|
x += '<div><label><input id=p20notifyIntelDeviceDisconnect type=checkbox />' + "Device disconnections." + '</label></div>';
|
|
x += '<div><label><input id=p20notifyIntelAmtKvmActions type=checkbox />' + "Intel® AMT desktop and serial events." + '</label></div>';
|
|
setDialogMode(2, "Notification Settings", 3, p20editMeshNotifyEx, x);
|
|
Q('p20notifyIntelDeviceConnect').checked = (meshNotify & 2);
|
|
Q('p20notifyIntelDeviceDisconnect').checked = (meshNotify & 4);
|
|
Q('p20notifyIntelAmtKvmActions').checked = (meshNotify & 8);
|
|
return false;
|
|
}
|
|
|
|
function p20editMeshNotifyEx() {
|
|
var meshNotify = 0;
|
|
meshNotify += Q('p20notifyIntelDeviceConnect').checked ? 2 : 0;
|
|
meshNotify += Q('p20notifyIntelDeviceDisconnect').checked ? 4 : 0;
|
|
meshNotify += Q('p20notifyIntelAmtKvmActions').checked ? 8 : 0;
|
|
meshserver.send({ action: 'changemeshnotify', meshid: currentMesh._id, notify: meshNotify });
|
|
}
|
|
|
|
//
|
|
// Mesh Summary
|
|
//
|
|
|
|
function setupMeshSummaryStats() {
|
|
var draw = Chart.controllers.doughnut.prototype.draw;
|
|
Chart.controllers.doughnut = Chart.controllers.doughnut.extend({
|
|
draw: function() {
|
|
draw.apply(this, arguments);
|
|
let ctx = this.chart.chart.ctx;
|
|
let _fill = ctx.fill;
|
|
ctx.fill = function() {
|
|
ctx.save();
|
|
ctx.shadowColor = 'blue';
|
|
ctx.shadowBlur = 10;
|
|
ctx.shadowOffsetX = 2;
|
|
ctx.shadowOffsetY = 2;
|
|
_fill.apply(this, arguments)
|
|
ctx.restore();
|
|
}
|
|
}
|
|
});
|
|
|
|
window.meshPowerChart = new Chart(document.getElementById('meshPowerChart').getContext('2d'), {
|
|
type: 'doughnut',
|
|
data: { datasets: [{ data: [0, 0], backgroundColor: ['#20F', '#40D', '#60B', '#809', '#A07', '#C05'] }] },
|
|
options: { responsive: true, legend: { position: 'none' }, animation: { animateScale: true, animateRotate: true }, layout: { padding: { left: 10, right: 10, top: 10, bottom: 10 } } }
|
|
});
|
|
window.meshOsChart = new Chart(document.getElementById('meshOsChart').getContext('2d'), {
|
|
type: 'doughnut',
|
|
data: { datasets: [{ data: [0, 0], backgroundColor: ['#20F', '#40D', '#60B', '#809', '#A07', '#C05'] }] },
|
|
options: { responsive: true, legend: { position: 'none' }, animation: { animateScale: true, animateRotate: true }, layout: { padding: { left: 10, right: 10, top: 10, bottom: 10 } } }
|
|
});
|
|
window.meshConnChart = new Chart(document.getElementById('meshConnChart').getContext('2d'), {
|
|
type: 'doughnut',
|
|
data: { datasets: [{ data: [0, 0], backgroundColor: ['#20F', '#40D', '#60B', '#809', '#A07', '#C05'] }], labels: ["Not Connected", "Agent", "Intel AMT", "Agent + Intel AMT"] },
|
|
options: { responsive: true, legend: { position: 'none' }, animation: { animateScale: true, animateRotate: true }, layout: { padding: { left: 10, right: 10, top: 10, bottom: 10 } } }
|
|
});
|
|
}
|
|
|
|
function p21updateMesh() {
|
|
if (currentMesh == null) return;
|
|
|
|
// Add device group name
|
|
var meshrights = GetMeshRights(currentMesh), mname = EscapeHtml(currentMesh.name);
|
|
if (mname.length == 0) { mname = '<i>' + "None" + '</i>'; }
|
|
if ((meshrights & 1) != 0) { mname = '<span tabindex=0 title=\"' + "Click here to edit the device group name" + '\" onclick=p20editmesh(1) onkeyup="if (event.key == \'Enter\') p20editmesh(1)" style=cursor:pointer>' + mname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
|
|
QH('p21meshName', mname);
|
|
|
|
// Update charts
|
|
var power = {};
|
|
var conn = {};
|
|
var agentTypes = {};
|
|
var powerStates = {};
|
|
var connectivityStates = [ 0, 0, 0, 0 ]; // None, Agent, AMT, Agent + AMT
|
|
var showAgents = false;
|
|
var showPower = false;
|
|
var showConn = false;
|
|
for (var i in nodes) {
|
|
if (nodes[i].meshid == currentMesh._id) {
|
|
if (nodes[i].agent) { showAgents = true; if (agentTypes[nodes[i].agent.id] == null) { agentTypes[nodes[i].agent.id] = 1; } else { agentTypes[nodes[i].agent.id]++; } }
|
|
if (nodes[i].pwr) { showPower = true; if (powerStates[nodes[i].pwr] == null) { powerStates[nodes[i].pwr] = 1; } else { powerStates[nodes[i].pwr]++; } }
|
|
if (nodes[i].conn == 0) { showConn = true; connectivityStates[0]++; }
|
|
else if ((nodes[i].conn & 6) != 0) { showConn = true; if ((nodes[i].conn & 1) != 0) { connectivityStates[3]++; } else { connectivityStates[2]++; } }
|
|
else if ((nodes[i].conn & 1) != 0) { showConn = true; connectivityStates[1]++; }
|
|
}
|
|
}
|
|
|
|
var agentsData = [], agentsLabels = [], powerData = [], powerLabels = [];
|
|
for (var i in agentTypes) { agentsData.push(agentTypes[i]); agentsLabels.push(agentsStr[i]); }
|
|
for (var i in powerStates) { powerData.push(powerStates[i]); powerLabels.push(powerStatetable[i]); }
|
|
window.meshPowerChart.config.data.datasets[0].data = powerData;
|
|
window.meshPowerChart.config.data.labels = powerLabels;
|
|
window.meshPowerChart.update();
|
|
if (currentMesh.mtype == 2) {
|
|
window.meshOsChart.config.data.datasets[0].data = agentsData;
|
|
window.meshOsChart.config.data.labels = agentsLabels;
|
|
window.meshOsChart.update();
|
|
}
|
|
window.meshConnChart.config.data.datasets[0].data = connectivityStates;
|
|
window.meshConnChart.update();
|
|
|
|
// Update tables
|
|
var x = '', count = 0;
|
|
if (powerData.length > 0) {
|
|
var xpowerStates = [];
|
|
for (var i in powerStates) { xpowerStates.push([powerStatetable[i], powerStates[i]]); }
|
|
xpowerStates.sort(function(a, b){ return -(a[1]-b[1]) });
|
|
x += '<table style="margin-top:10px;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>' + "Power States" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
for (var i in xpowerStates) { x += '<tr style=' + (((++count % 2) == 0) ? 'background-color:#DDD' : '') + '><td><divclass=m2></div><div> ' + xpowerStates[i][0] + '<div></div></div></td><td><div style=float:right>' + xpowerStates[i][1] + ' </div><div></div></td></tr>'; }
|
|
x += '</tbody></table>';
|
|
}
|
|
|
|
if ((agentsData.length > 0) && (currentMesh.mtype == 2)) {
|
|
count = 0;
|
|
var xagentTypes = [];
|
|
for (var i in agentTypes) { xagentTypes.push([agentsStr[i], agentTypes[i]]); }
|
|
xagentTypes.sort(function(a, b){ return -(a[1]-b[1]) });
|
|
x += '<table style="margin-top:10px;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>' + "Agent Types" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
for (var i in xagentTypes) { x += '<tr style=' + (((++count % 2) == 0) ? 'background-color:#DDD' : '') + '><td><divclass=m2></div><div> ' + xagentTypes[i][0] + '<div></div></div></td><td><div style=float:right>' + xagentTypes[i][1] + ' </div><div></div></td></tr>'; }
|
|
x += '</tbody></table>';
|
|
}
|
|
|
|
if (showConn) {
|
|
count = 0;
|
|
var xconnectivityStates = [];
|
|
for (var i = 0; i < 4; i++) { xconnectivityStates.push([["Not Connected", "Agent", "Intel AMT", "Agent + Intel AMT"][i], connectivityStates[i]]); }
|
|
xconnectivityStates.sort(function(a, b){ return -(a[1]-b[1]) });
|
|
x += '<table style="margin-top:10px;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>' + "Connectivity" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
for (var i = 0; i < 4; i++) { if (xconnectivityStates[i][1] > 0) { x += '<tr style=' + (((++count % 2) == 0) ? 'background-color:#DDD' : '') + '><td><divclass=m2></div><div> ' + xconnectivityStates[i][0] + '<div></div></div></td><td><div style=float:right>' + xconnectivityStates[i][1] + ' </div><div></div></td></tr>'; } }
|
|
x += '</tbody></table>';
|
|
}
|
|
|
|
if (x == '') { x = '<i>' + "No devices in this device group." + '</i>'; }
|
|
|
|
QH('p21info', x);
|
|
|
|
// Only show the OS chart if the mesh is agent type.
|
|
QS('meshPowerChartDiv')['display'] = (showPower)?'inline-block':'none';
|
|
QS('meshOsChartDiv')['display'] = ((currentMesh.mtype == 2) && showAgents)?'inline-block':'none';
|
|
QS('meshConnChartDiv')['display'] = (showConn)?'inline-block':'none';
|
|
}
|
|
|
|
//
|
|
// MY FILES
|
|
//
|
|
|
|
var filetreelinkpath;
|
|
var filetreelocation = [];
|
|
|
|
function updateFiles() {
|
|
QV('MainMenuMyFiles', ((features & 8) == 0));
|
|
if ((features & 8) != 0) return; // If running on a server without files, exit now.
|
|
var html1 = '', html2 = '', displayPath = '<a href=# style=cursor:pointer onclick="return p5folderup(0)">' + "Root" + '</a>', fullPath = 'Root', publicPath, filetreex = filetree, folderdepth = 1;
|
|
|
|
// Navigate to path location, build the paths at the same time
|
|
var filetreelocation2 = [], oldlinkpath = filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc');
|
|
for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedBoxes.push(checkboxes[i].value) }; } // Save all existing checked boxes
|
|
|
|
filetreelinkpath = '';
|
|
for (var i in filetreelocation) {
|
|
if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) {
|
|
filetreelocation2.push(filetreelocation[i]);
|
|
fullPath += ' / ' + filetreelocation[i];
|
|
if ((folderdepth == 1)) {
|
|
var sp = filetreelocation[i].split('/');
|
|
publicPath = (("" + window.location).split('?')[0]) + sp[0] + 'files/' + sp[2];
|
|
//if (filetreelocation[i] === userinfo._id) { filetreelinkpath += 'self'; } else { filetreelinkpath += (sp[0] + '/' + sp[2]); }
|
|
filetreelinkpath += filetreelocation[i];
|
|
} else {
|
|
if (filetreelinkpath != '') { filetreelinkpath += '/' + filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + filetreelocation[i]; } }
|
|
}
|
|
filetreex = filetreex.f[filetreelocation[i]];
|
|
displayPath += ' / <a href=# style=cursor:pointer onclick="return p5folderup(' + folderdepth + ')">' + (filetreex.n != null?filetreex.n:filetreelocation[i]) + '</a>';
|
|
folderdepth++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
filetreelocation = filetreelocation2; // In case we could not go down the full path, we set the new path location here.
|
|
var publicfolder = fullPath.toLowerCase().startsWith('root / ' + userinfo._id + ' / public');
|
|
|
|
// Sort the files
|
|
var filetreexx = p5sort_files(filetreex.f);
|
|
|
|
// Display all files and folders at this location
|
|
for (var i in filetreexx) {
|
|
// Figure out the name and shortname
|
|
var f = filetreexx[i], name = f.n, shortname;
|
|
shortname = name;
|
|
if (name.length > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + "..." + '</span>'; } else { shortname = EscapeHtml(name); }
|
|
name = EscapeHtml(name);
|
|
|
|
// Figure out the date
|
|
var fdatestr = '';
|
|
if (f.d != null) { var fdate = new Date(f.d), fdatestr = printDateTime(fdate) + ' '; }
|
|
|
|
// Figure out the size
|
|
var fsize = '';
|
|
if (f.s != null) { fsize = getFileSizeStr(f.s); }
|
|
|
|
var h = '';
|
|
if (f.t < 3 || f.t == 4) {
|
|
var right = (f.t == 1 || f.t == 4)?p5getQuotabar(f):'', title = '';
|
|
h = '<div class=filelist file=999><input file=999 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value="' + name + '"> <span style=float:right title=\"' + title + '\">' + right + '</span><span><div class=fileIcon' + f.t + ' onclick=p5folderset(\"' + encodeURIComponent(f.nx) + '\")></div><a href=# style=cursor:pointer onclick=\'return p5folderset(\"' + encodeURIComponent(f.nx) + '\")\'>' + shortname + '</a></span></div>';
|
|
} else {
|
|
var link = shortname, publiclink = '';
|
|
if (publicfolder) { publiclink = '<img src="images/link2.png" style=cursor:pointer title="' + "Display public link" + '" onclick=\'return p5showPublicLink("' + (publicPath + '/' + encodeURIComponent(f.nx)) + '?download=1' + '")\' width=10 height=10 /> <img src="images/link4.png" title="' + "Copy link to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(publicPath + '/' + encodeURIComponent(f.nx) + '?download=1') + '\") width=10 height=10>'; }
|
|
if (f.s > 0) { link = publiclink + ' <a rel="noreferrer noopener" target="_blank" download href="downloadfile.ashx?link=' + encodeURIComponent(filetreelinkpath + '/' + f.nx) + '">' + shortname + '</a>'; }
|
|
h = '<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value="' + f.nx + '"> <span class=fsize>' + fdatestr + '</span><span style=float:right>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
|
|
}
|
|
|
|
if (f.t < 3) { html1 += h; } else { html2 += h; }
|
|
}
|
|
|
|
//if (f.parent == null) { }
|
|
QH('p5rightOfButtons', p5getQuotabar(filetreex));
|
|
|
|
QH('p5files', html1 + html2);
|
|
QH('p5currentpath', displayPath);
|
|
QE('p5FolderUp', filetreelocation.length != 0);
|
|
QV('p5PublicShare', publicfolder);
|
|
|
|
// Re-check all boxes if needed
|
|
if (oldlinkpath == filetreelinkpath) {
|
|
checkboxes = document.getElementsByName('fc');
|
|
for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = (checkedBoxes.indexOf(checkboxes[i].value) >= 0); }
|
|
}
|
|
|
|
p5setActions();
|
|
}
|
|
|
|
function getNiceSize(bytes) {
|
|
if (bytes <= 0) return "Storage limit exceed";
|
|
if (bytes < 2048) return format("{0} bytes remaining", bytes);
|
|
if (bytes < 2097152) return format("{0} kilobytes remaining", Math.round(bytes / 1024));
|
|
if (bytes < 2147483648) return format("{0} megabytes remaining", Math.round(bytes / 1024 / 1024));
|
|
return format("{0} gigabytes remaining", Math.round(bytes / 1024 / 1024 / 1024));
|
|
}
|
|
|
|
function getNiceSize2(bytes) {
|
|
if (bytes <= 0) return "None";
|
|
if (bytes < 2048) return format("{0} b", bytes);
|
|
if (bytes < 2097152) return format("{0} Kb", Math.round(bytes / 1024));
|
|
if (bytes < 2147483648) return format("{0} Mb", Math.round(bytes / 1024 / 1024));
|
|
return format("{0} Gb", Math.round(bytes / 1024 / 1024 / 1024));
|
|
}
|
|
|
|
function p5getQuotabar(f) {
|
|
while (f.t > 1 && f.t != 4) { f = f.parent; }
|
|
if ((f.t != 1 && f.t != 4) || (f.maxbytes == null)) return '';
|
|
var tf = Math.floor(f.s / 1024), tq = (f.maxbytes - f.s);
|
|
var title;
|
|
if (f.c > 1) { title = format("{0}k in {1} files. {2}k maximum", tf, f.c, (Math.floor(f.maxbytes / 1024 / 1024))); } else { title = format("{0}k in 1 file. {1}k maximum", tf, (Math.floor(f.maxbytes / 1024 / 1024))); }
|
|
return '<span title="' + title + '">' + getNiceSize(tq) + ' <progress style=height:10px;width:100px value=' + f.s + ' max=' + f.maxbytes + ' /></span>';
|
|
}
|
|
|
|
function p5showPublicLink(u) {
|
|
setDialogMode(2, "Public Link", 1, null, '<input type=text style=width:350px value="' + u + '" readonly /> <img src="images/link4.png" title="' + "Copy link to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2(\"' + encodeURIComponent(u) + '\") width=10 height=10>');
|
|
return false;
|
|
}
|
|
|
|
var sortorder;
|
|
function p5sort_filename(a, b) { if (a.ln > b.ln) return (1 * sortorder); if (a.ln < b.ln) return (-1 * sortorder); return 0; }
|
|
function p5sort_timestamp(a, b) { if (a.d > b.d) return (1 * sortorder); if (a.d < b.d) return (-1 * sortorder); return 0; }
|
|
function p5sort_bysize(a, b) { if (a.s == b.s) return p5sort_filename(a, b); return (((a.s - b.s)) * sortorder); }
|
|
|
|
function p5sort_files(files) {
|
|
var r = [], sortselection = Q('p5sortdropdown').value;
|
|
for (var i in files) { files[i].nx = i; if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
|
|
sortorder = 1;
|
|
if (sortselection > 3) { sortorder = -1; sortselection -= 3; }
|
|
if (sortselection == 1) { r.sort(p5sort_filename); }
|
|
else if (sortselection == 2) { r.sort(p5sort_bysize); }
|
|
else if (sortselection == 3) { r.sort(p5sort_timestamp); }
|
|
return r;
|
|
}
|
|
|
|
function p5setActions() {
|
|
var cc = getFileSelCount(), tc = getFileCount(), sfc = getFileSelCount(false); // In order: number of entires selected, number of total entries, number of selected entires that are files (not folders)
|
|
QE('p5DeleteFileButton', (cc > 0) && (filetreelocation.length > 0));
|
|
QE('p5NewFolderButton', filetreelocation.length > 0);
|
|
QE('p5UploadButton', filetreelocation.length > 0);
|
|
QE('p5RenameFileButton', (cc == 1) && (filetreelocation.length > 0));
|
|
//QE('p5ViewFileButton', (cc == 1) && (sfc == 1) && (filetreelocation.length > 0));
|
|
QE('p5SelectAllButton', tc > 0);
|
|
Q('p5SelectAllButton').value = (cc > 0 ? "Select None" : "Select All");
|
|
QE('p5CutButton', (sfc > 0) && (cc == sfc));
|
|
QE('p5CopyButton', (sfc > 0) && (cc == sfc));
|
|
QE('p5PasteButton', (p5clipboard != null) && (p5clipboard.length > 0) && (filetreelocation.length > 0));
|
|
}
|
|
|
|
function getFileSelCount(includeDirs) { var cc = 0, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == '3'))) cc++; } return cc; }
|
|
function getFileSelDirCount() { var cc = 0, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '999')) cc++; } return cc; }
|
|
function getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fc'); return checkboxes.length; }
|
|
function p5selectallfile() { var nv = (getFileSelCount() == 0), checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p5setActions(); }
|
|
function setupBackPointers(x) { if (x.f != null) { var fs = 0, fc = 0; for (var i in x.f) { setupBackPointers(x.f[i]); x.f[i].parent = x; if (x.f[i].s) { fs += x.f[i].s; } if (x.f[i].c) { fc += x.f[i].c; } if (x.f[i].t == 3) { fc++; } } x.s = fs; x.c = fc; } return x; }
|
|
function getFileSizeStr(size) { if (size == 1) return "1 byte"; return format("{0} bytes", size); }
|
|
function p5folderup(x) { if (x == null) { filetreelocation.pop(); } else { while (filetreelocation.length > x) { filetreelocation.pop(); } } updateFiles(); return false; }
|
|
function p5folderset(x) { filetreelocation.push(decodeURIComponent(x)); updateFiles(); return false; }
|
|
function p5createfolder() { setDialogMode(2, "New Folder", 3, p5createfolderEx, '<input type=text id=p5renameinput maxlength=64 onkeyup=p5fileNameCheck(event) style=width:100% />'); focusTextBox('p5renameinput'); p5fileNameCheck(); }
|
|
function p5createfolderEx() { meshserver.send({ action: 'fileoperation', fileop: 'createfolder', path: filetreelocation, newfolder: Q('p5renameinput').value}); }
|
|
function p5deletefile() { var cc = getFileSelCount(), rec = (getFileSelDirCount() > 0) ? '<br /><br /><label><input type=checkbox id=p5recdeleteinput>' + "Recursive delete" + '</label><br>' : '<input type=checkbox id=p5recdeleteinput style=\'display:none\'>'; setDialogMode(2, "Delete", 3, p5deletefileEx, (cc > 1) ? (format("Delete {0} selected items?", cc) + rec) : ("Delete selected item?" + rec)); }
|
|
function p5deletefileEx() { var delfiles = [], checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { delfiles.push(checkboxes[i].value); } } meshserver.send({ action: 'fileoperation', fileop: 'delete', path: filetreelocation, delfiles: delfiles, rec: Q('p5recdeleteinput').checked }); }
|
|
function p5renamefile() { var renamefile, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { renamefile = checkboxes[i].value; } } setDialogMode(2, "Rename", 3, p5renamefileEx, '<input type=text id=p5renameinput maxlength=64 onkeyup=p5fileNameCheck(event) style=width:100% value="' + renamefile + '" />', { action: 'fileoperation', fileop: 'rename', path: filetreelocation, oldname: renamefile}); focusTextBox('p5renameinput'); p5fileNameCheck(); }
|
|
function p5renamefileEx(b, t) { t.newname = Q('p5renameinput').value; meshserver.send(t); }
|
|
function p5fileNameCheck(e) { var x = isFilenameValid(Q('p5renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e && e.keyCode == 13)) { dialogclose(1); } }
|
|
var isFilenameValid = (function(){ var x1=/^[^\\/:\*\?"<>\|]+$/, x2=/^\./, x3=/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname){ return x1.test(fname)&&!x2.test(fname)&&!x3.test(fname)&&(fname[0] != '.'); } })();
|
|
function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '<form method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame><input type=text name=link style=display:none id=p5uploadpath value=\"' + encodeURIComponent(filetreelinkpath) + '\" /><input type=file name=files id=p5uploadinput style=width:100% multiple=multiple onchange="p5updateUploadDialogOk(\'p5uploadinput\')" /><input type=hidden name=authCookie value=' + authCookie + ' /><input type=submit id=p5loginSubmit style=display:none /><span id=p5confirmOverwriteSpan style=display:none><br /><label><input type=checkbox id=p5confirmOverwrite onchange="p5updateUploadDialogOk(\'p5uploadinput\')" />' + "Confirm overwrite?" + '</label></span></form>'); p5updateUploadDialogOk('p5uploadinput'); }
|
|
function p5uploadFileEx() { Q('p5loginSubmit').click(); }
|
|
function p5updateUploadDialogOk() {
|
|
// Check if these are files we can upload, remove all folders.
|
|
var xallfiles = Q('p5uploadinput').files, files = [];
|
|
for (var i in xallfiles) { if ((xallfiles[i].size != null) && (xallfiles[i].size != 0)) { files.push(xallfiles[i]); } }
|
|
|
|
// Check if these files are duplicates of existing files.
|
|
var filetreex = filetree, allfiles = [], overWriteCount = 0;
|
|
for (var i in filetreelocation) {
|
|
if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) { filetreex = filetreex.f[filetreelocation[i]]; }
|
|
}
|
|
QE('idx_dlgOkButton', xallfiles.length > 0);
|
|
if (xallfiles.length > 0) {
|
|
if (filetreex.f != null) {
|
|
for (var i in filetreex.f) { allfiles.push(i); }
|
|
for (var i = 0; i < xallfiles.length; i++) {
|
|
if (allfiles.indexOf(xallfiles[i].name) >= 0) { overWriteCount++; } // TODO: If the server is Windows, we need to lowercase both names.
|
|
}
|
|
}
|
|
QV('p5confirmOverwriteSpan', overWriteCount > 0);
|
|
if (overWriteCount > 0) {
|
|
QE('idx_dlgOkButton', Q('p5confirmOverwrite').checked);
|
|
} else {
|
|
Q('p5confirmOverwrite').checked = false;
|
|
QE('idx_dlgOkButton', true);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
function p5viewfile() {
|
|
var checkboxes = document.getElementsByName('fc');
|
|
for (var i = 0; i < checkboxes.length; i++) {
|
|
if (checkboxes[i].checked) {
|
|
console.log(filetreelocation.join('/') + '/' + checkboxes[i].value); // TODO: Download and show this file
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
var p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0;
|
|
function p5copyFile(cut) {
|
|
var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation);
|
|
for (var i = 0; i < checkboxes.length; i++) {
|
|
if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) {
|
|
console.log('yy', checkboxes[i].value);
|
|
p5clipboard.push(checkboxes[i].value);
|
|
}
|
|
}
|
|
p5updateClipview();
|
|
}
|
|
function p5pasteFile() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Confim {0} of {1} entrie{2} to this location?", (p5clipboardCut == 0?'copy':'move'), p5clipboard.length, ((p5clipboard.length > 1)?'s':'')) } setDialogMode(2, "Paste", 3, p5pasteFileEx, x); }
|
|
function p5pasteFileEx() { meshserver.send({ action: 'fileoperation', fileop: (p5clipboardCut == 0?'copy':'move'), scpath: p5clipboardFolder, path: filetreelocation, names: p5clipboard }); p5folderup(999); if (p5clipboardCut == 1) { p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; p5updateClipview(); } }
|
|
function p5updateClipview() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Holding {0} entrie{1} for {2}", p5clipboard.length, ((p5clipboard.length > 1)?'s':''), (p5clipboardCut == 0?"copy":"move")) + ', <a href=# onclick="return p5clearClip()" style=cursor:pointer>' + "Clear" + '</a>.' } QH('p5bottomstatus', x); p5setActions(); }
|
|
function p5clearClip() { p5clipboard = null; p5clipboardFolder = null; p5clipboardCut = 0; p5updateClipview(); return false; }
|
|
|
|
function p5fileDragDrop(e) {
|
|
if (xxdialogMode) return;
|
|
haltEvent(e);
|
|
QV('bigfail', false);
|
|
QV('bigok', false);
|
|
//QV('p5fileCatchAllInput', false);
|
|
|
|
// Check if these are files we can upload, remove all folders.
|
|
if (e.dataTransfer == null) return;
|
|
var files = [];
|
|
for (var i in e.dataTransfer.files) { if ((e.dataTransfer.files[i].size != null) && (e.dataTransfer.files[i].size != 0)) { files.push(e.dataTransfer.files[i]); } }
|
|
if (files.length == 0) return;
|
|
|
|
// Check if these files are duplicates of existing files.
|
|
var filetreex = filetree, allfiles = [], overWriteCount = 0;
|
|
for (var i in filetreelocation) {
|
|
if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) { filetreex = filetreex.f[filetreelocation[i]]; }
|
|
}
|
|
if (filetreex.f != null) {
|
|
for (var i in filetreex.f) { allfiles.push(i); }
|
|
for (var i = 0; i < e.dataTransfer.files.length; i++) {
|
|
if (allfiles.indexOf(e.dataTransfer.files[i].name) >= 0) { overWriteCount++; } // TODO: If the server is Windows, we need to lowercase both names.
|
|
}
|
|
}
|
|
|
|
if (overWriteCount == 0) {
|
|
// If no overwrite, go ahead with upload
|
|
p5PerformUpload(1, files);
|
|
} else {
|
|
// Otherwise, prompt for confirmation
|
|
setDialogMode(2, "Upload File", 3, p5PerformUpload, format((overWriteCount == 1)?"Upload will overwrite 1 file. Continue?":"Upload will overwrite {0} files. Continue?", overWriteCount), files);
|
|
}
|
|
}
|
|
|
|
function p5PerformUpload(b, files) {
|
|
// For Chrome & Firefox
|
|
var error = 0;
|
|
p5uploadFile(); // Display the the dialog box
|
|
try { Q('p5uploadinput').files = files; } catch (ex) { error = 1; } // Set the files in the dialog box
|
|
if (error == 0) { p5uploadFileEx(); } // Press the submit button
|
|
setDialogMode(0); // Close the dialog box
|
|
|
|
// For IE browser - This will not work with very large files
|
|
if (error == 1) {
|
|
if (filetreelocation.length == 0) return;
|
|
var names = [], sizes = [], types = [], datas = [], readercount = files.length, totalSize = 0;
|
|
for (var i = 0; i < files.length; i++) { totalSize += files[i].size; }
|
|
if (totalSize > 1300000) { p5uploadFile(); return; } // File is too large, not sure what the real maximum is.
|
|
for (var i = 0; i < files.length; i++) {
|
|
var reader = new FileReader(), file = files[i];
|
|
names.push(file.name);
|
|
sizes.push(file.size);
|
|
types.push(file.type);
|
|
reader.onload = function (event) {
|
|
datas.push(event.target.result);
|
|
if (--readercount == 0) {
|
|
Q('p5fileDragName').value = names.join('*');
|
|
Q('p5fileDragSize').value = sizes.join('*');
|
|
Q('p5fileDragType').value = types.join('*');
|
|
Q('p5fileDragData').value = datas.join('*'); // This will not work for large files, there is a limit on the data size in a field.
|
|
Q('p5fileDragLink').value = encodeURIComponent(filetreelinkpath);
|
|
Q('p5fileDragAuthCookie').value = authCookie;
|
|
Q('p5loginSubmit2').click();
|
|
}
|
|
}
|
|
reader.readAsDataURL(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
var p5dragtimer = null;
|
|
function p5fileDragOver(e) {
|
|
if (xxdialogMode) return;
|
|
haltEvent(e);
|
|
if (p5dragtimer != null) { clearTimeout(p5dragtimer); p5dragtimer = null; }
|
|
var ac = true; // TODO: Set to true if we can accept the file
|
|
if (filetreelocation.length == 0) { ac = false; }
|
|
QV('bigok', ac);
|
|
QV('bigfail', !ac);
|
|
//QV('p5fileCatchAllInput', ac);
|
|
}
|
|
|
|
function p5fileDragLeave(e) {
|
|
if (xxdialogMode) return;
|
|
haltEvent(e);
|
|
if (e.target.id != 'p5filetable') {
|
|
QV('bigfail', false);
|
|
QV('bigok', false);
|
|
//QV('p5fileCatchAllInput', false);
|
|
} else {
|
|
p5dragtimer = setTimeout(function () { QV('bigfail',false); QV('bigok',false); p5dragtimer=null; }, 10);
|
|
}
|
|
}
|
|
|
|
/*
|
|
function p5fileCatchAllInputChanged(e) {
|
|
p5fileDragLeave(e);
|
|
Q('p5fileDragLink2').value = encodeURIComponent(filetreelinkpath);
|
|
Q('p5fileCatchAllSubmit').click();
|
|
}
|
|
*/
|
|
|
|
//
|
|
// MY EVENTS
|
|
//
|
|
|
|
// Highlights the device being hovered
|
|
function eventMouseHover(e, over) {
|
|
e.children[1].classList.remove('g1s');
|
|
e.children[2].classList.remove('style10s');
|
|
//e.children[2].style['background-color'] = ((over == 0) ? '#c9c9c9' : '#b9b9b9');
|
|
e.children[3].classList.remove('g2s');
|
|
if (over == 1) { e.children[1].classList.add('g1s'); e.children[2].classList.add('style10s'); e.children[3].classList.add('g2s'); }
|
|
}
|
|
|
|
function eventsUpdate() {
|
|
var x = '', dateHeader = null;
|
|
for (var i in events) {
|
|
var event = events[i], time = new Date(event.time);
|
|
if (event.msg) {
|
|
if (event.h == null) { event.h = Math.random(); }
|
|
if (printDate(time) != dateHeader) {
|
|
if (dateHeader != null) x += '</table>';
|
|
dateHeader = printDate(time);
|
|
x += '<table class=p3eventsTable cellpadding=0 cellspacing=0><tr><td colspan=4 class=DevSt>' + dateHeader + '</td></tr>';
|
|
}
|
|
var icon = 'si3';
|
|
if (event.etype == 'ugrp') icon = 'm4';
|
|
if (event.etype == 'user') icon = 'm2';
|
|
if (event.etype == 'server') icon = 'si3';
|
|
|
|
var msg = EscapeHtml(event.msg).split('(R)').join('®');
|
|
if (event.nodeid) {
|
|
var node = getNodeFromId(event.nodeid);
|
|
if (node != null) {
|
|
icon = 'si' + node.icon;
|
|
msg = '<a href=# onclick=\'gotoDevice("' + event.nodeid + '",10);haltEvent(event);\'>' + EscapeHtml(node.name) + '</a> → ' + msg;
|
|
}
|
|
}
|
|
if (event.username) {
|
|
if ((userinfo.siteadmin & 2) && (event.userid)) {
|
|
msg = '<a href=# onclick=\'gotoUser("' + encodeURIComponent(event.userid) + '");haltEvent(event);\'>' + EscapeHtml(event.username) + '</a> → ' + msg;
|
|
} else {
|
|
msg = EscapeHtml(event.username) + ' → ' + msg;
|
|
}
|
|
}
|
|
if (event.etype == 'relay' || event.action == 'relaylog') icon = 'relayIcon16';
|
|
x += '<tr onclick=showEventDetails(' + event.h + ',2) onmouseover=eventMouseHover(this,1) onmouseout=eventMouseHover(this,0) style=cursor:pointer><td style=width:18px><div class=' + icon + '></div></td><td class=g1> </td><td class=style10>' + printTime(time) + ' - ' + msg + '</td><td class=g2> </td></tr><tr style=height:2px></tr>';
|
|
}
|
|
}
|
|
if (dateHeader != null) x += '</table>';
|
|
if (x == '') x = '<br><i>' + "No Events Found" + '</i><br><br>';
|
|
QH('p3events', x);
|
|
}
|
|
|
|
function refreshEvents() {
|
|
meshserver.send({ action: 'events', limit: parseInt(p3limitdropdown.value) });
|
|
}
|
|
|
|
function p3showDownloadEventsDialog(mode) {
|
|
if (xxdialogMode) return;
|
|
var x = "Download the list of events with one of the file formats below." + '<br /><br />';
|
|
x += addHtmlValue("CSV Format", '<a href=# style=cursor:pointer onclick="return p3downloadEventsDialogCSV(' + mode + ')">' + "eventslist.csv" + '</a>');
|
|
x += addHtmlValue("JSON Format", '<a href=# style=cursor:pointer onclick="return p3downloadEventsDialogJSON(' + mode + ')">' + "eventslist.json" + '</a>');
|
|
setDialogMode(2, "Event List Export", 1, null, x, mode);
|
|
}
|
|
|
|
function p3downloadEventsDialogCSV(mode) {
|
|
var csv, eventList;
|
|
if (mode == 1) { eventList = currentDeviceEvents; }
|
|
if (mode == 2) { eventList = events; }
|
|
if (mode == 3) { eventList = currentUserEvents; }
|
|
csv = "utc, time, type, action, user, device, message" + '\r\n';
|
|
for (var i in eventList) {
|
|
var nodename = '';
|
|
if (eventList[i].nodeid) { var node = getNodeFromId(eventList[i].nodeid); if (node && node.name) { nodename = node.name; } }
|
|
csv += '\"' + eventList[i].time + '\",\"' + printDateTime(new Date(eventList[i].time)) + '\",\"' + eventList[i].etype + '\",\"' + ((eventList[i].action != null) ? eventList[i].action : '') + '\",\"' + ((eventList[i].username != null) ? eventList[i].username : '') + '\",\"' + EscapeHtml(nodename) + '\",\"' + ((eventList[i].msg != null) ? eventList[i].msg : '').split(',').join(' -') + '\"\r\n';
|
|
}
|
|
saveAs(new Blob([csv], { type: 'application/octet-stream' }), "eventslist.csv");
|
|
return false;
|
|
}
|
|
|
|
function p3downloadEventsDialogJSON(mode) {
|
|
var r = [], eventList;
|
|
if (mode == 1) { eventList = currentDeviceEvents; }
|
|
if (mode == 2) { eventList = events; }
|
|
if (mode == 3) { eventList = currentUserEvents; }
|
|
for (var i in eventList) { r.push(events[i]); }
|
|
saveAs(new Blob([JSON.stringify(r, null, 2)], { type: 'application/octet-stream' }), "eventslist.json");
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// MY USERS
|
|
//
|
|
|
|
function updateUsers() {
|
|
QV('MainMenuMyUsers', (users != null) && ((features & 4) == 0));
|
|
QV('LeftMenuMyUsers', (users != null) && ((features & 4) == 0));
|
|
QV('UserNewAccountButton', ((features & 4) == 0) && (serverinfo.domainauth == false));
|
|
if ((users == null) || ((features & 4) != 0)) { QH('p3users', ''); return; }
|
|
|
|
// Sort the list of user id's
|
|
var sortedUserIds = [], maxUsers = 100, hiddenUsers = 0;
|
|
for (var i in users) { sortedUserIds.push(i); }
|
|
sortedUserIds.sort();
|
|
|
|
// Get search
|
|
var userSearch = Q('UserSearchInput').value.toLowerCase();
|
|
var emailSearch = userSearch;
|
|
if (userSearch.startsWith('email:')) { userSearch = null; emailSearch = emailSearch.substring(6); }
|
|
else if (userSearch.startsWith('name:')) { emailSearch = null; userSearch = userSearch.substring(5); }
|
|
else if (userSearch.startsWith('e:')) { userSearch = null; emailSearch = emailSearch.substring(2); }
|
|
else if (userSearch.startsWith('n:')) { emailSearch = null; userSearch = userSearch.substring(2); }
|
|
|
|
// Display the users using the sorted list
|
|
var x = '<table class=p3usersTable cellpadding=0 cellspacing=0>', addHeader = true;
|
|
x += '<th>' + "Name" + '<th style=width:80px>' + "Device Groups" + '<th style=width:120px>' + nobreak("Last Access") + '<th style=width:120px>' + "Permissions";
|
|
|
|
// Save the list of currently checked users
|
|
var checkedUserids = [], elements = document.getElementsByClassName('UserCheckbox');
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked) { checkedUserids.push(elements[i].value); } }
|
|
|
|
// Online users
|
|
for (var i in sortedUserIds) {
|
|
var user = users[sortedUserIds[i]], sessions = null;
|
|
if (wssessions != null) { sessions = wssessions[user._id]; }
|
|
if ((sessions != null) &&
|
|
((userSearch != null) && ((userSearch == '') || (user.name.toLowerCase().indexOf(userSearch) >= 0)) ||
|
|
((emailSearch != null) && ((user.email != null) && (user.email.toLowerCase().indexOf(emailSearch) >= 0))))
|
|
) {
|
|
if (maxUsers > 0) {
|
|
if (addHeader) { x += '<tr><td class=userTableHeader colspan=4>' + "Online Users"; addHeader = false; }
|
|
x += addUserHtml(user, sessions);
|
|
maxUsers--;
|
|
} else {
|
|
hiddenUsers++;
|
|
}
|
|
}
|
|
}
|
|
addHeader = true;
|
|
// Offline users
|
|
for (var i in sortedUserIds) {
|
|
var user = users[sortedUserIds[i]], sessions = null;
|
|
if (wssessions != null) { sessions = wssessions[user._id]; }
|
|
if ((sessions == null) &&
|
|
((userSearch != null) && ((userSearch == '') || (user.name.toLowerCase().indexOf(userSearch) >= 0)) ||
|
|
((emailSearch != null) && ((user.email != null) && (user.email.toLowerCase().indexOf(emailSearch) >= 0))))
|
|
) {
|
|
if (maxUsers > 0) {
|
|
if (addHeader) { x += '<tr><td class=userTableHeader colspan=4>' + "Offline Users"; addHeader = false; }
|
|
x += addUserHtml(user, sessions);
|
|
maxUsers--;
|
|
} else {
|
|
hiddenUsers++;
|
|
}
|
|
}
|
|
}
|
|
x += '</table>';
|
|
if (hiddenUsers == 1) { x += '<br />' + "1 more user not shown, use search box to look for users..." + '<br />'; }
|
|
else if (hiddenUsers > 1) { x += '<br />' + format("{0} more users not shown, use search box to look for users...", hiddenUsers) + '<br />'; }
|
|
if (maxUsers == 100) { x += '<br />' + "No users found." + '<br />'; }
|
|
QH('p3users', x);
|
|
|
|
// Re-check userid's
|
|
elements = document.getElementsByClassName('UserCheckbox');
|
|
var eself = encodeURIComponent(userinfo._id);
|
|
for (var i=0;i<elements.length;i++) { elements[i].checked = ((checkedUserids.indexOf(elements[i].value) >= 0) && (elements[i].value != eself)); }
|
|
p3updateInfo();
|
|
|
|
// Update current user panel if needed
|
|
if ((currentUser != null) && (xxcurrentView == 30)) { gotoUser(encodeURIComponent(currentUser._id),true); }
|
|
}
|
|
|
|
function addUserHtml(user, sessions) {
|
|
var x = '', gray = ' gray', icon = 'm2', msg = '', self = (user.name != userinfo.name), lastAccess = '', permissions = '';
|
|
if (sessions != null) {
|
|
gray = '';
|
|
if (self) {
|
|
msg = '<span style=float:right;margin-top:1px;margin-right:4px title=' + "Chat" + '><a href=# onclick=userChat(event,\"' + encodeURIComponent(user._id) + '\",\"' + encodeURIComponent(user.name) + '\")><img src=\'images/icon-chat.png\' height=16 width=16 style=padding-top:2px /></a></span>';
|
|
msg += '<span style=float:right;margin-top:1px;margin-left:4px;margin-right:4px title=Notify><a href=# onclick=\'return showUserAlertDialog(event,\"' + encodeURIComponent(user._id) + '\")\'><img src=\'images/icon-notify.png\' height=16 width=16 style=padding-top:2px /></a></span>';
|
|
}
|
|
if (sessions == 1) { lastAccess += nobreak("1 session"); } else { lastAccess += nobreak(format("{0} sessions", sessions)); }
|
|
} else {
|
|
if (user.login) { lastAccess += '<span title=\"' + format("Last login: {0}", printDateTime(new Date(user.login * 1000))) + '\">' + printDate(new Date(user.login * 1000)) + '</span>'; }
|
|
}
|
|
if (self) { permissions += '<a href=# style=cursor:pointer onclick=\'return showUserAdminDialog(event,\"' + encodeURIComponent(user._id) + '\")\'>'; }
|
|
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { permissions += "Locked" + ', '; }
|
|
permissions += '<span title=\'' + "Server Permissions" + '\'>';
|
|
|
|
var urights = user.siteadmin & (0xFFFFFFFF - 224);
|
|
if ((user.siteadmin == null) || (urights == 0)) {
|
|
permissions += "User";
|
|
} else if (urights == 8) {
|
|
permissions += "User + Files";
|
|
} else if (user.siteadmin == 0xFFFFFFFF) {
|
|
permissions += "Administrator";
|
|
} else if ((urights & 2) != 0) {
|
|
permissions += "Manager";
|
|
} else {
|
|
permissions += "Partial";
|
|
}
|
|
if ((user.siteadmin != null) && (user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & (64 + 128)) != 0)) { permissions += '*'; }
|
|
permissions += '</span>';
|
|
//if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
|
|
if (self) { permissions += '</a>'; }
|
|
|
|
var groups = 0
|
|
if (user.links) { for (var i in user.links) { if (i.startsWith('mesh/')) { groups++; } } }
|
|
|
|
var username = EscapeHtml(user.name), emailVerified = '';
|
|
if (serverinfo.emailcheck == true) { emailVerified = ((user.emailVerified != true) ? ' <b style=color:red title="' + "Email is not verified" + '">✗</b>' : ' <b style=color:green title="' + "Email is verified" + '">✓</b>'); }
|
|
if (user.email != null) {
|
|
if (((features & 0x200000) == 0) || (user.email.toLowerCase() != user.name.toLowerCase())) {
|
|
// Username & email are different
|
|
username += ', <a href=# onclick=\'return doemail(event,\"' + user.email + '\")\'>' + user.email + '</a>' + emailVerified;
|
|
} else {
|
|
// Username & email are the same
|
|
username += ' <a href=# onclick=\'return doemail(event,\"' + user.email + '\")\'><img src="images/mail12.png" height=9 width=12 title="' + "Send email to user" + '" style="margin-top:2px" /></a>' + emailVerified;
|
|
}
|
|
}
|
|
|
|
if ((user.otpsecret > 0) || (user.otphkeys > 0) || ((user.otpekey == 1) && (features & 0x00800000)) || ((user.phone != null) && (features & 0x04000000))) { username += ' <img src="images/key12.png" height=12 width=11 title="' + "2nd factor authentication enabled" + '" style="margin-top:2px" />'; }
|
|
if (user.phone != null) { username += ' <img src="images/phone12.png" height=12 width=7 title="' + "Verified phone number" + '" style="margin-top:2px" />'; }
|
|
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { username += ' <img src="images/padlock12.png" height=12 width=8 title="' + "Account is locked" + '" style="margin-top:2px" />'; }
|
|
x += '<tr tabindex=0 onmouseover=userMouseHover(this,1) onmouseout=userMouseHover(this,0) onkeypress="if (event.key==\'Enter\') gotoUser(\'' + encodeURIComponent(user._id) + '\')"><td>';
|
|
x += '<div class=bar>';
|
|
x += '<div class=baricon><input class=UserCheckbox value=' + encodeURIComponent(user._id) + ' onclick=p3updateInfo() type=checkbox' + ((user._id == userinfo._id)?' disabled':'') + '></div><div style=cursor:pointer onclick=gotoUser(\"' + encodeURIComponent(user._id) + '\")>';
|
|
x += '<div class=baricon><div class="' + icon + gray + '"></div></div>';
|
|
x += '<div class=g1></div><div class=g2></div><div>';
|
|
x += '<div><span>' + username + '</span>' + msg + '</div></div><td style=text-align:center>' + groups + '<td style=text-align:center>' + lastAccess + '<td style=text-align:center>' + permissions;
|
|
return x;
|
|
}
|
|
|
|
// Called when a user checkbox is clicked
|
|
function p3updateInfo() {
|
|
var elements = document.getElementsByClassName('UserCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) checkcount++; }
|
|
QE('UsersGroupActionButton', checkcount > 0);
|
|
Q('UsersSelectAllButton').value = (checkcount > 0)?"Select None":"Select All";
|
|
}
|
|
|
|
// Called to select all or unselect all users
|
|
function p3usersSelectallButtonFunction() {
|
|
var eself = encodeURIComponent(userinfo._id);
|
|
var elements = document.getElementsByClassName('UserCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) checkcount++; }
|
|
for (var i=0;i<elements.length;i++) { elements[i].checked = (checkcount == 0) && (elements[i].value != eself); }
|
|
p3updateInfo();
|
|
}
|
|
|
|
// Called to perform a group action on many users
|
|
function p3usersGroupActionFunction() {
|
|
var elements = document.getElementsByClassName('UserCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) checkcount++; }
|
|
if (checkcount == 0) return;
|
|
|
|
var x = "Select an operation to perform on all selected users." + '<br /><br />';
|
|
x += addHtmlValue("Operation", '<select style=width:240px id=d3groupop><option value=1>' + "Lock account" + '</option><option value=2>' + "Unlock account" + '</option><option value=3>' + "Delete account" + '</option></select>');
|
|
setDialogMode(2, "Group Action", 3, p3usersGroupActionFunctionEx, x);
|
|
}
|
|
|
|
function p3usersGroupActionFunctionEx() {
|
|
var elements = document.getElementsByClassName('UserCheckbox'), userids = [];
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) { userids.push(decodeURIComponent(elements[i].value)); } }
|
|
var op = Q('d3groupop').value;
|
|
if (op == 1) {
|
|
// Lock accounts
|
|
for (var i in userids) {
|
|
var user = users[userids[i]], siteadmin = (user.siteadmin == null)?0:user.siteadmin;
|
|
if ((siteadmin & 32) == 0) { siteadmin += 32; meshserver.send({ action: 'edituser', id: user._id, siteadmin: siteadmin }); }
|
|
}
|
|
} else if (op == 2) {
|
|
// Unlock accounts
|
|
for (var i in userids) {
|
|
var user = users[userids[i]], siteadmin = (user.siteadmin == null)?0:user.siteadmin;
|
|
if ((siteadmin & 32) != 0) { siteadmin -= 32; meshserver.send({ action: 'edituser', id: user._id, siteadmin: siteadmin }); }
|
|
}
|
|
} else if (op == 3) {
|
|
// Delete accounts, ask for confirmation
|
|
var x = "Confirm delete selected account(s)?" + '<br /><br />';
|
|
x += '<label><input id=d3check type=checkbox onchange=p3usersGroupActionFunctionDelCheck() />' + "Confirm" + '</label>';
|
|
setDialogMode(2, "Delete Accounts", 3, p3groupActionFunctionDelExec, x);
|
|
QE('idx_dlgOkButton', false);
|
|
}
|
|
}
|
|
|
|
function p3usersGroupActionFunctionDelCheck() { QE('idx_dlgOkButton', Q('d3check').checked); }
|
|
|
|
// Delete a batch of user accounts
|
|
function p3groupActionFunctionDelExec(b) {
|
|
var elements = document.getElementsByClassName('UserCheckbox'), userids = [];
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) { userids.push(decodeURIComponent(elements[i].value)); } }
|
|
for (var i in userids) { var user = users[userids[i]]; meshserver.send({ action: 'deleteuser', userid: user._id, username: user.name }); }
|
|
}
|
|
|
|
// Highlights the user being hovered
|
|
function userMouseHover(element, over) {
|
|
var e = element.children[0].children[0].children[1];
|
|
e.children[1].classList.remove('g1s');
|
|
e.children[2].classList.remove('g2s');
|
|
element.children[0].children[0].classList.remove('sbar');
|
|
if (over == 1) {
|
|
e.children[1].classList.add('g1s');
|
|
e.children[2].classList.add('g2s');
|
|
element.children[0].children[0].classList.add('sbar');
|
|
}
|
|
}
|
|
|
|
// Highlights the user being hovered
|
|
function userMouseHover2(element, over) {
|
|
var e = element.children[0].children[0];
|
|
e.children[2].classList.remove('g1s');
|
|
e.children[3].classList.remove('g2s');
|
|
element.children[0].children[0].classList.remove('sbar');
|
|
if (over == 1) {
|
|
e.children[2].classList.add('g1s');
|
|
e.children[3].classList.add('g2s');
|
|
element.children[0].children[0].classList.add('sbar');
|
|
}
|
|
}
|
|
|
|
function userChat(e, userid, name) {
|
|
haltEvent(e);
|
|
var url = '/messenger?id=meshmessenger/' + userid + '/' + encodeURIComponent(userinfo._id) + '&title=' + name;
|
|
if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
|
|
window.open(url, 'meshmessenger:' + userid);
|
|
meshserver.send({ action: 'meshmessenger', userid: decodeURIComponent(userid) });
|
|
return false;
|
|
}
|
|
|
|
function showSendSMS(userid) {
|
|
if (xxdialogMode) return;
|
|
setDialogMode(2, "Send SMS", 3, showSendSMSEx, '<textarea id=d2smsText maxlength=160 style=background-color:#fcf3cf;width:100%;height:100px;resize:none onKeyUp=showSendSMSValidate()></textarea><span style=font-size:10px><span>', userid);
|
|
Q('d2smsText').focus();
|
|
showSendSMSValidate();
|
|
}
|
|
|
|
function showSendSMSValidate() { QE('idx_dlgOkButton', Q('d2smsText').value.length > 0); }
|
|
function showSendSMSEx(b, tag) { if (Q('d2smsText').value.length > 0) { meshserver.send({ action: 'smsuser', userid: tag, msg: Q('d2smsText').value }); } }
|
|
|
|
function showUserAlertDialog(e, userid) {
|
|
if (xxdialogMode) return;
|
|
haltEvent(e);
|
|
setDialogMode(2, format("Notify {0}", EscapeHtml(users[decodeURIComponent(userid)].name)), 3, showUserAlertDialogEx, "Send a text notification to this user." + '<textarea id=d2notifyText maxlength=2048 style="width:100%;height:184px;resize:none"></textarea>', userid);
|
|
Q('d2notifyText').focus();
|
|
return false;
|
|
}
|
|
|
|
function showUserAlertDialogEx(button, userid) { meshserver.send({ action: 'notifyuser', userid: decodeURIComponent(userid), msg: Q('d2notifyText').value }); }
|
|
|
|
function doemail(e, addr) {
|
|
if (xxdialogMode) return false;
|
|
haltEvent(e);
|
|
window.open('mailto:' + addr);
|
|
return false;
|
|
}
|
|
|
|
function p4batchAccountCreate() {
|
|
if (xxdialogMode) return;
|
|
var x = "Create many accounts at once by importing a JSON file with the following format:" + '<br /><pre>[\r\n {"user":"x1","pass":"x","email":"x1@x"},\r\n {"user":"x2","pass":"x","resetNextLogin":true}\r\n]</pre><input style=width:370px type=file id=d4importFile accept=".json" onchange=p4batchAccountCreateValidate() />';
|
|
setDialogMode(2, "User Account Import", 3, p4batchAccountCreateEx, x);
|
|
QE('idx_dlgOkButton', false);
|
|
}
|
|
|
|
function p4batchAccountCreateValidate() {
|
|
QE('idx_dlgOkButton', Q('d4importFile').value != null);
|
|
}
|
|
|
|
function p4batchAccountCreateEx() {
|
|
var fr = new FileReader();
|
|
fr.onload = function (r) {
|
|
var j = null;
|
|
try { j = JSON.parse(r.target.result); } catch (ex) { setDialogMode(2, "User Account Import", 1, null, format("Invalid JSON file: {0}.", ex)); return; }
|
|
if ((j != null) && (Array.isArray(j))) {
|
|
var ok = true;
|
|
for (var i in j) {
|
|
if ((typeof j[i].user != 'string') || (j[i].user.length < 1) || (j[i].user.length > 64)) { ok = false; }
|
|
if ((typeof j[i].pass != 'string') || (j[i].pass.length < 1) || (j[i].pass.length > 256)) { ok = false; }
|
|
if (checkPasswordRequirements(j[i].pass, passRequirements) == false) { ok = false; }
|
|
if ((j[i].email != null) && ((typeof j[i].email != 'string') || (j[i].email.length < 1) || (j[i].email.length > 128))) { ok = false; }
|
|
}
|
|
if (ok == false) { setDialogMode(2, "User Account Import", 1, null, "Invalid JSON file format."); } else { meshserver.send({ action: 'adduserbatch', users: j }); }
|
|
} else { setDialogMode(2, "User Account Import", 1, null, "Invalid JSON file format."); }
|
|
};
|
|
fr.readAsText(Q('d4importFile').files[0]);
|
|
}
|
|
|
|
function p4downloadUserInfo() {
|
|
if (xxdialogMode) return;
|
|
var x = "Download the list of users with one of the file formats below." + '<br /><br />';
|
|
x += addHtmlValue("CSV Format", '<a href=# style=cursor:pointer onclick=\'return p4downloadUserInfoCSV()\'>' + "userlist.csv" + '</a>');
|
|
x += addHtmlValue("JSON Format", '<a href=# style=cursor:pointer onclick=\'return p4downloadUserInfoJSON()\'>' + "userlist.json" + '</a>');
|
|
setDialogMode(2, "User List Export", 1, null, x);
|
|
}
|
|
|
|
function p4downloadUserInfoCSV() {
|
|
var csv = "id, name, email, creation, lastlogin, groups, authfactors" + '\r\n';
|
|
for (var i in users) {
|
|
var multiFactor = false, factors = [];
|
|
if ((users[i].otpsecret > 0) || (users[i].otphkeys > 0)) {
|
|
multiFactor = true;
|
|
if (users[i].otpsecret > 0) { factors.push('AuthApp'); }
|
|
if (users[i].otphkeys > 0) { factors.push('SecurityKey'); }
|
|
if (users[i].otpkeys > 0) { factors.push('BackupCodes'); }
|
|
}
|
|
csv += '\"' + users[i]._id + '\",\"' + users[i].name + '\",\"' + (users[i].email ? users[i].email : '') + '\",\"' + (users[i].creation ? new Date(users[i].creation * 1000) : '') + '\",\"' + (users[i].login ? new Date(users[i].login * 1000) : '') + '\",\"' + (users[i].groups ? users[i].groups.join(',') : '') + '\",\"' + (multiFactor ? factors.join(',') : '') + '\"\r\n';
|
|
}
|
|
saveAs(new Blob([csv], { type: 'application/octet-stream' }), "userlist.csv");
|
|
return false;
|
|
}
|
|
|
|
function p4downloadUserInfoJSON() {
|
|
var r = []
|
|
for (var i in users) { r.push(users[i]); }
|
|
saveAs(new Blob([JSON.stringify(r, null, 2)], { type: 'application/octet-stream' }), "userlist.json");
|
|
return false;
|
|
}
|
|
|
|
function showUserBroadcastDialog(targetid) {
|
|
if (xxdialogMode) return;
|
|
var x = "Broadcast a message to all connected users." + '<textarea id=broadcastMessage value="" maxlength="256"/></textarea>';
|
|
setDialogMode(2, "Broadcast Message", 3, showUserBroadcastDialogEx, x, targetid?decodeURIComponent(targetid):null);
|
|
Q('broadcastMessage').focus();
|
|
}
|
|
|
|
function showUserBroadcastDialogEx(b, targetid) {
|
|
meshserver.send({ action: 'userbroadcast', msg: Q('broadcastMessage').value, target: targetid });
|
|
}
|
|
|
|
function showCreateNewAccountDialog() {
|
|
if (xxdialogMode) return;
|
|
var x = '';
|
|
if ((features & 0x200000) == 0) { x += addHtmlValue('<span id=p4hname>' + "Username" + '</span>', '<input id=p4name maxlength=64 autocomplete=username onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />'); }
|
|
x += addHtmlValue('<span id=p4hemail>' + "Email" + '</span>', '<input id=p4email maxlength=256 autocomplete="email" inputmode="email" onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
|
x += addHtmlValue('<span id=p4hp1>' + "Password" + '</span>', '<input id=p4pass1 type=password maxlength=256 autocomplete="new-password" onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
|
x += addHtmlValue('<span id=p4hp2>' + "Password" + '</span>', '<input id=p4pass2 type=password maxlength=256 autocomplete="new-password" onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
|
x += '<div><label><input id=p4randomPassword onchange=showCreateNewAccountDialogValidate() type=checkbox />' + "Randomize the password." + '</label></div>';
|
|
x += '<div><label><input id=p4resetNextLogin onchange=showCreateNewAccountDialogValidate() type=checkbox />' + "Force password reset on next login." + '</label></div>';
|
|
x += '<div><label><input id=p4removeEvents onchange=showCreateNewAccountDialogValidate() type=checkbox />' + "Remove all previous events for this userid." + '</label></div>';
|
|
if (serverinfo.emailcheck) {
|
|
x += '<div><label><input id=p4verifiedEmail onchange=showCreateNewAccountDialogValidate() type=checkbox />' + "Email is verified." + '</label></div>';
|
|
x += '<div><label><input id=p4invitationEmail type=checkbox />' + "Send invitation email." + '</label></div>';
|
|
}
|
|
|
|
if (passRequirements) {
|
|
var r = [], rc = 0;
|
|
for (var i in passRequirements) { if ((i != 'reset') && (i != 'hint')) { r.push(i + ':' + passRequirements[i]); rc++; } }
|
|
if (rc > 0) { x += '<div style=font-size:x-small;padding:6px>' + format("Requirements: {0}.", r.join(', ')) + '</div>'; }
|
|
}
|
|
|
|
setDialogMode(2, "Create Account", 3, showCreateNewAccountDialogEx, x);
|
|
showCreateNewAccountDialogValidate();
|
|
if ((features & 0x200000) == 0) { Q('p4name').focus(); } else { Q('p4email').focus(); }
|
|
}
|
|
|
|
function showCreateNewAccountDialogValidate() {
|
|
var emailok = validateEmail(Q('p4email').value);
|
|
var nameok = true;
|
|
var passok = (Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements));
|
|
|
|
if ((features & 0x200000) == 0) {
|
|
nameok = (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1)));
|
|
QS('p4hname').color = nameok?'black':'#7b241c';
|
|
}
|
|
QS('p4hemail').color = emailok?'black':'#7b241c';
|
|
|
|
if (serverinfo.emailcheck) {
|
|
QE('p4verifiedEmail', ve);
|
|
QE('p4invitationEmail', ve && Q('p4resetNextLogin').checked && Q('p4verifiedEmail').checked);
|
|
if (emailok == false) { Q('p4verifiedEmail').checked = false; }
|
|
if ((Q('p4resetNextLogin').checked == false) || (Q('p4verifiedEmail').checked == false)) { Q('p4invitationEmail').checked = false; }
|
|
}
|
|
QE('p4pass1', !Q('p4randomPassword').checked);
|
|
QE('p4pass2', !Q('p4randomPassword').checked);
|
|
QS('p4hp1').color = (passok || Q('p4randomPassword').checked)?'black':'#7b241c';
|
|
QS('p4hp2').color = (passok || Q('p4randomPassword').checked)?'black':'#7b241c';
|
|
|
|
var ok = nameok & emailok;
|
|
if (Q('p4randomPassword').checked == false) { ok &= passok; }
|
|
QE('idx_dlgOkButton', ok);
|
|
}
|
|
|
|
function showCreateNewAccountDialogEx() {
|
|
var username = ((features & 0x200000) == 0) ? Q('p4name').value : Q('p4email').value; // Username is email address
|
|
var x = { action: 'adduser', username: username, email: Q('p4email').value, pass: Q('p4pass1').value, resetNextLogin: Q('p4resetNextLogin').checked, randomPassword: Q('p4randomPassword').checked, removeEvents: Q('p4removeEvents').checked };
|
|
if (serverinfo.emailcheck) {
|
|
x.emailVerified = Q('p4verifiedEmail').checked;
|
|
x.emailInvitation = Q('p4invitationEmail').checked;
|
|
}
|
|
meshserver.send(x);
|
|
}
|
|
|
|
function showUserGroupDialog(e, userid) {
|
|
if (xxdialogMode) return;
|
|
haltEvent(e);
|
|
userid = decodeURIComponent(userid);
|
|
var user = users[userid.toLowerCase()], groups = "";
|
|
if (user.groups != null) { groups = user.groups.join(', ') }
|
|
var x = "Enter a comma seperate list of administrative realms names." + '<br /><br />';
|
|
x += addHtmlValue("Realms", '<input id=dp4usergroups style=width:230px value="' + groups + '" placeholder=\"' + "Name1, Name2, Name3" + '\" maxlength=256 onchange=p4validateUserGroups() onkeyup=p4validateUserGroups() />');
|
|
setDialogMode(2, "Administrative Realms", 3, showUserGroupDialogEx, x, user);
|
|
focusTextBox('dp4usergroups');
|
|
p4validateUserGroups();
|
|
return false;
|
|
}
|
|
|
|
function p4validateUserGroups() {
|
|
var groups = Q('dp4usergroups').value;
|
|
var k = 0, i = groups.indexOf('\"') + groups.indexOf('/') + groups.indexOf('>') + groups.indexOf('<') + groups.indexOf('\'');
|
|
var g = groups.split(',');
|
|
for (var j in g) { if (g[j].trim().length == 0) k++; }
|
|
QE('idx_dlgOkButton', (groups == '') || ((i == -5) && (k < 1)));
|
|
}
|
|
|
|
function showUserGroupDialogEx(event, user) {
|
|
var groups = Q('dp4usergroups').value, g = groups.split(','), g2 = [];
|
|
for (var j in g) { var x = g[j].trim(); if (x.length > 0) { g2.push(x); } }
|
|
meshserver.send({ action: 'edituser', id: user._id, groups: g2 });
|
|
}
|
|
|
|
function showUserAdminDialog(e, userid) {
|
|
if (xxdialogMode) return;
|
|
haltEvent(e);
|
|
userid = decodeURIComponent(userid);
|
|
var user = users[userid.toLowerCase()];
|
|
if (user == null) return;
|
|
var uself = (userinfo._id == user._id);
|
|
var x = '<div><div id=d2AdminPermissions>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_fileaccess>' + "Server Files" + '</label>, <input type=number onchange=showUserAdminDialogValidate() maxlength=10 id=ua_fileaccessquota>' + "k max, blank for default" + '<br><hr/>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_fulladmin>' + "Full Administrator" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_serverbackup>' + "Server Backup" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_serverrestore>' + "Server Restore" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_serverupdate>' + "Server Updates" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_manageusers>' + "Manage Users" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_manageusergroups>' + "Manage User Groups" + '</label><br>';
|
|
x += '<hr/></div><label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_lockedaccount>' + "Lock Account" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_nonewgroups>' + "No New Device Groups" + '</label><br>';
|
|
x += '<label><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_nomeshcmd>' + "No Tools (MeshCmd/Router)" + '</label><br>';
|
|
x += '</div>';
|
|
setDialogMode(2, "Server Permissions", 2 + (uself?0:1), showUserAdminDialogEx, x, user);
|
|
if (user.siteadmin && user.siteadmin != 0) {
|
|
Q('ua_fulladmin').checked = (user.siteadmin == 0xFFFFFFFF);
|
|
Q('ua_serverbackup').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1) != 0)); // Server Backup
|
|
Q('ua_manageusers').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 2) != 0)); // Manage Users
|
|
Q('ua_serverrestore').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 4) != 0)); // Server Restore
|
|
Q('ua_fileaccess').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 8) != 0)); // Server Files
|
|
Q('ua_serverupdate').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 16) != 0)); // Server Update
|
|
Q('ua_lockedaccount').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 32) != 0)); // Account locked
|
|
Q('ua_nonewgroups').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 64) != 0)); // No New Groups
|
|
Q('ua_nomeshcmd').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 128) != 0)); // No Tools (MeshCMD / Router)
|
|
Q('ua_manageusergroups').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 256) != 0)); // Manage User Groups
|
|
}
|
|
QE('ua_fulladmin', !uself && (userinfo.siteadmin == 0xFFFFFFFF));
|
|
QE('ua_serverbackup', !uself && (userinfo.siteadmin == 0xFFFFFFFF));
|
|
QE('ua_manageusers', !uself && (userinfo.siteadmin == 0xFFFFFFFF));
|
|
QE('ua_manageusergroups', !uself && (userinfo.siteadmin == 0xFFFFFFFF));
|
|
QE('ua_serverrestore', !uself && (userinfo.siteadmin == 0xFFFFFFFF));
|
|
QE('ua_fileaccess', !uself && (userinfo.siteadmin == 0xFFFFFFFF));
|
|
QE('ua_fileaccessquota', !uself && (userinfo.siteadmin == 0xFFFFFFFF));
|
|
QE('ua_serverupdate', !uself && (userinfo.siteadmin == 0xFFFFFFFF));
|
|
QV('d2AdminPermissions', userinfo.siteadmin == 0xFFFFFFFF)
|
|
QE('ua_lockedaccount', !uself && (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id));
|
|
QE('ua_nonewgroups', !uself && (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id));
|
|
QE('ua_nomeshcmd', !uself && (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id));
|
|
Q('ua_fileaccessquota').value = (user.quota != null)?(user.quota / 1024):'';
|
|
showUserAdminDialogValidate();
|
|
return false;
|
|
}
|
|
|
|
function showUserAdminDialogValidate() {
|
|
if (userinfo.siteadmin == 0xFFFFFFFF) {
|
|
QE('ua_serverbackup', !Q('ua_fulladmin').checked);
|
|
QE('ua_manageusers', !Q('ua_fulladmin').checked);
|
|
QE('ua_serverrestore', !Q('ua_fulladmin').checked);
|
|
QE('ua_fileaccess', !Q('ua_fulladmin').checked);
|
|
QE('ua_serverupdate', !Q('ua_fulladmin').checked);
|
|
QE('ua_lockedaccount', !Q('ua_fulladmin').checked);
|
|
QE('ua_nonewgroups', !Q('ua_fulladmin').checked);
|
|
QE('ua_nomeshcmd', !Q('ua_fulladmin').checked);
|
|
QE('ua_manageusergroups', !Q('ua_fulladmin').checked && Q('ua_manageusers').checked);
|
|
QE('ua_fileaccessquota', Q('ua_fileaccess').checked && !Q('ua_fulladmin').checked);
|
|
}
|
|
}
|
|
|
|
function showUserAdminDialogEx(button, user) {
|
|
var siteadmin = 0, quota = parseInt(Q('ua_fileaccessquota').value);
|
|
if (Q('ua_fulladmin').checked == true) { siteadmin = 0xFFFFFFFF; } else {
|
|
if (Q('ua_serverbackup').checked == true) siteadmin += 1;
|
|
if (Q('ua_manageusers').checked == true) siteadmin += 2;
|
|
if (Q('ua_serverrestore').checked == true) siteadmin += 4;
|
|
if (Q('ua_fileaccess').checked == true) siteadmin += 8;
|
|
if (Q('ua_serverupdate').checked == true) siteadmin += 16;
|
|
if (Q('ua_lockedaccount').checked == true) siteadmin += 32;
|
|
if (Q('ua_nonewgroups').checked == true) siteadmin += 64;
|
|
if (Q('ua_nomeshcmd').checked == true) siteadmin += 128;
|
|
if (Q('ua_manageusergroups').checked == true) siteadmin += 256;
|
|
}
|
|
var x = { action: 'edituser', id: user._id, siteadmin: siteadmin };
|
|
if (isNaN(quota) == false) { x.quota = (quota * 1024); }
|
|
meshserver.send(x);
|
|
}
|
|
|
|
function onUserSearchInputChanged() { masterUpdate(16384); }
|
|
|
|
|
|
//
|
|
// MY USER GROUPS
|
|
//
|
|
|
|
function updateUserGroups() {
|
|
// Sort the list of group names
|
|
var sortedGroups = [], x = '';
|
|
if (usergroups) { for (var i in usergroups) { sortedGroups.push(usergroups[i]); } }
|
|
sortedGroups.sort(nameSort);
|
|
|
|
// Save the list of currently checked users
|
|
var checkedUserGroupids = [], elements = document.getElementsByClassName('UserGroupCheckbox');
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked) { checkedUserGroupids.push(elements[i].value); } }
|
|
|
|
if (sortedGroups.length == 0) {
|
|
x += '<br />' + "No groups found." + '<br />';
|
|
QV('DuplicateUserGroupButton', false);
|
|
} else {
|
|
// Display the groups using the sorted list
|
|
x += '<table class=p3usersTable cellpadding=0 cellspacing=0>';
|
|
x += '<th>' + "Name" + '<th style=width:80px>' + "Users" + '<th style=width:80px>' + "Device Groups" + '<th style=width:80px>' + "Devices";
|
|
for (var i in sortedGroups) { x += addUserGroupHtml(sortedGroups[i]); }
|
|
x += '</table>';
|
|
QV('DuplicateUserGroupButton', true);
|
|
}
|
|
QH('p50groups', x);
|
|
|
|
// Re-check userid's
|
|
elements = document.getElementsByClassName('UserGroupCheckbox');
|
|
for (var i=0;i<elements.length;i++) { elements[i].checked = ((checkedUserGroupids.indexOf(elements[i].value) >= 0)); }
|
|
p50updateInfo();
|
|
|
|
// Update current user panel if needed
|
|
if ((currentUserGroup != null) && (xxcurrentView == 51)) { gotoUserGroup(encodeURIComponent(currentUserGroup._id), true); }
|
|
}
|
|
|
|
function addUserGroupHtml(group) {
|
|
var usercount = 0, meshcount = 0, devicecount = 0;
|
|
if (group.links) { for (var i in group.links) { if (i.startsWith('user/')) { usercount++; } if (i.startsWith('mesh/')) { meshcount++; } if (i.startsWith('node/')) { devicecount++; } } }
|
|
var x = '<tr tabindex=0 onmouseover=userMouseHover2(this,1) onmouseout=userMouseHover2(this,0) onkeypress="if (event.key==\'Enter\') gotoUserGroup(\'' + encodeURIComponent(group._id) + '\')"><td style=cursor:pointer>';
|
|
x += '<div class=bar style=width:100%>';
|
|
x += '<div class=baricon><input class=UserGroupCheckbox value=' + encodeURIComponent(group._id) + ' onclick=p50updateInfo() type=checkbox></div>';
|
|
x += '<div class=baricon onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")><div class=m4></div></div>';
|
|
x += '<div class=g1 onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")></div><div class=g2 onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")></div>';
|
|
x += '<div onclick=gotoUserGroup(\"' + encodeURIComponent(group._id) + '\")><span style=font-size:16px>' + group.name + '</span></div></div><td style=text-align:center>' + usercount + '<td style=text-align:center>' + meshcount + '<td style=text-align:center>' + devicecount;
|
|
return x;
|
|
}
|
|
|
|
function p50updateInfo() {
|
|
var elements = document.getElementsByClassName('UserGroupCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) checkcount++; }
|
|
QE('UsersGroupsGroupActionButton', checkcount > 0);
|
|
Q('UsersGroupsSelectAllButton').value = (checkcount > 0)?"Select None":"Select All";
|
|
}
|
|
|
|
// Called to select all or unselect all users
|
|
function p50usersSelectallButtonFunction() {
|
|
var eself = encodeURIComponent(userinfo._id);
|
|
var elements = document.getElementsByClassName('UserGroupCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) checkcount++; }
|
|
for (var i=0;i<elements.length;i++) { elements[i].checked = (checkcount == 0) && (elements[i].value != eself); }
|
|
p50updateInfo();
|
|
}
|
|
|
|
// Called to perform a group action on many users
|
|
function p50usersGroupActionFunction() {
|
|
var elements = document.getElementsByClassName('UserGroupCheckbox'), checkcount = 0;
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) checkcount++; }
|
|
if (checkcount == 0) return;
|
|
|
|
var x = "Select an operation to perform on all selected users." + '<br /><br />';
|
|
x += addHtmlValue("Operation", '<select style=width:240px id=d50groupop><option value=1>' + "Delete group" + '</option></select>');
|
|
setDialogMode(2, "Group Action", 3, p50usersGroupActionFunctionEx, x);
|
|
}
|
|
|
|
function p50usersGroupActionFunctionEx() {
|
|
var elements = document.getElementsByClassName('UserGroupCheckbox'), userids = [];
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) { userids.push(decodeURIComponent(elements[i].value)); } }
|
|
var op = Q('d50groupop').value;
|
|
if (op == 1) {
|
|
// Delete user groups, ask for confirmation
|
|
var x = "Confirm delete selected user groups(s)?" + '<br /><br />';
|
|
x += '<label><input id=d3check type=checkbox onchange=p50usersGroupActionFunctionDelCheck() />' + "Confirm" + '</label>';
|
|
setDialogMode(2, "Delete User Groups", 3, p50groupActionFunctionDelExec, x);
|
|
QE('idx_dlgOkButton', false);
|
|
}
|
|
}
|
|
|
|
function p50usersGroupActionFunctionDelCheck() { QE('idx_dlgOkButton', Q('d3check').checked); }
|
|
|
|
// Delete a batch of user accounts
|
|
function p50groupActionFunctionDelExec(b) {
|
|
var elements = document.getElementsByClassName('UserGroupCheckbox');
|
|
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) { meshserver.send({ action: 'deleteusergroup', ugrpid: decodeURIComponent(elements[i].value) }); } }
|
|
}
|
|
|
|
function showCreateUserGroupDialog(mode) {
|
|
if (xxdialogMode) return;
|
|
var x = '', y = '';
|
|
if (mode == 2) {
|
|
if (usergroups) { for (var i in usergroups) { y += '<option value=' + encodeURIComponent(i) + '>' + EscapeHtml(usergroups[i].name) + '</option>'; } }
|
|
x += addHtmlValue("User Group", '<div style=width:230px;margin:0;padding:0><select id=dp4groupid style=width:100%>' + y + '</select></div>');
|
|
}
|
|
x += addHtmlValue("Name", '<input id=p4name maxlength=64 onchange=showCreateUserGroupDialogValidate() onkeyup=showCreateUserGroupDialogValidate() />');
|
|
x += addHtmlValue("Description", '<textarea id=p4desc value="" style=width:230px;height:60px;resize:none maxlength=1024 /></textarea>');
|
|
setDialogMode(2, (mode == 1)?"Create User Group":"Duplicate User Group", 3, showCreateUserGroupDialogEx, x, mode);
|
|
showCreateUserGroupDialogValidate();
|
|
Q('p4name').focus();
|
|
}
|
|
|
|
function showCreateUserGroupDialogValidate() { QE('idx_dlgOkButton', Q('p4name').value.length > 0); }
|
|
|
|
function showCreateUserGroupDialogEx(b, mode) {
|
|
var x = { action: 'createusergroup', name: Q('p4name').value, desc: Q('p4desc').value };
|
|
if (mode == 2) { x.clone = decodeURIComponent(Q('dp4groupid').value); }
|
|
meshserver.send(x);
|
|
}
|
|
|
|
//
|
|
// MY USER GROUP
|
|
//
|
|
|
|
var currentUserGroup = null;
|
|
function gotoUserGroup(groupid, force) {
|
|
if (xxdialogMode && !force) return;
|
|
var group = currentUserGroup = usergroups?usergroups[decodeURIComponent(groupid)]:null;
|
|
if (group == null) { if (xxcurrentView == 51) { setDialogMode(0); go(50); } return; }
|
|
|
|
// Add user group name
|
|
var gname = EscapeHtml(group.name);
|
|
if (gname.length == 0) { gname = '<i>' + "None" + '</i>'; }
|
|
if ((userinfo.siteadmin & 256) != 0) { gname = '<span tabindex=0 title=\"' + "Click here to edit the user group name" + '\" onclick=p51editgroup(1) onkeyup="if (event.key == \'Enter\') p51editgroup(1)" style=cursor:pointer>' + gname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
|
|
QH('p51groupName', gname);
|
|
|
|
var usercount = 0, meshcount = 0, devicecount = 0;
|
|
if (group.links) {
|
|
for (var i in group.links) {
|
|
if (i.startsWith('user/')) { usercount++; }
|
|
if (i.startsWith('mesh/')) { meshcount++; }
|
|
if (i.startsWith('node/')) { devicecount++; }
|
|
}
|
|
}
|
|
var desc = group.desc;
|
|
if ((desc == null) || (desc == '')) { desc = '<i>' + "None" + '<i>'; } else { desc = EscapeHtml(desc); }
|
|
|
|
var x = '<div style=min-height:80px><table style=width:100%>';
|
|
if ((userinfo.siteadmin & 256) != 0) {
|
|
x += addDeviceAttribute("Description", '<span onclick=p51editgroup(2) style=cursor:pointer>' + desc + ' <img class=hoverButton src="images/link5.png" /></span>');
|
|
} else {
|
|
x += addDeviceAttribute("Description", desc);
|
|
}
|
|
x += addDeviceAttribute("Users", usercount);
|
|
x += addDeviceAttribute("Device Groups", meshcount);
|
|
x += addDeviceAttribute("Devices", devicecount);
|
|
|
|
x += '</table></div><br />';
|
|
|
|
if ((userinfo.siteadmin & 256) != 0) {
|
|
x += '<input type=button value=\"' + "Broadcast" + '\" title=\"' + "Send a notice to all users in this group." + '\" onclick=showUserBroadcastDialog("' + encodeURIComponent(group._id) + '") />';
|
|
}
|
|
|
|
// Setup the panel
|
|
QH('p51group', x);
|
|
|
|
x = '<br />';
|
|
if ((userinfo.siteadmin & 256) != 0) {
|
|
x += '<a href=# onclick="return p51showAddUserDialog()" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Users" + '</a>';
|
|
}
|
|
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>' + "Group Members" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
|
|
// Sort the users for this mesh
|
|
var count = 1, sortedusers = [];
|
|
for (var i in currentUserGroup.links) {
|
|
if (i.startsWith('user/') == false) continue;
|
|
var uname = i.split('/')[2];
|
|
if (currentUserGroup.links[i].name) { uname = currentUserGroup.links[i].name; }
|
|
if (i == userinfo._id) { uname = userinfo.name; }
|
|
sortedusers.push({ id: i, name: uname, rights: currentUserGroup.links[i].rights });
|
|
}
|
|
sortedusers.sort(function(a, b) { if (a.name > b.name) return 1; if (a.name < b.name) return -1; return 0; });
|
|
|
|
// Display all users for this user group
|
|
for (var i in sortedusers) {
|
|
var trash = '<a href=# onclick=\'return p51deleteUser(event,"' + encodeURIComponent(sortedusers[i].id) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
|
var username = EscapeHtml(decodeURIComponent(sortedusers[i].name));
|
|
if (users != null) { username = '<a href=# onclick=\'gotoUser("' + encodeURIComponent(sortedusers[i].id) + '");haltEvent(event);\'>' + username + '</a>'; }
|
|
x += '<tr ' + (((count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td><div title=\"' + "User" + '\" class=m2></div><div> ' + username + '<div></div></div></td><td><div style=float:right>' + trash + '</div></td></tr>';
|
|
++count;
|
|
}
|
|
|
|
if (count == 1) { x += '<tr><td><div style=padding:6px> <i>' + "No Members" + '</i><div></div></div></td><td></td></tr>'; }
|
|
|
|
x += '</tbody></table><br />';
|
|
|
|
// Display all device groups for this user group
|
|
count = 1;
|
|
var deviceGroupCount = 0, newDeviceGroup = false;
|
|
for (var i in meshes) { deviceGroupCount++; if ((currentUserGroup.links == null) || (currentUserGroup.links[i] == null)) { newDeviceGroup = true; } }
|
|
if ((deviceGroupCount > 0) && (newDeviceGroup)) { x += '<a href=# onclick="return p20showAddMeshUserDialog(3)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Device Group" + '</a>'; }
|
|
x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Common Device Groups" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
if (currentUserGroup.links) {
|
|
var omeshes = [];
|
|
for (var i in currentUserGroup.links) { if (i.startsWith('mesh/')) { if (meshes[i] != null) { omeshes.push(meshes[i]); } } }
|
|
omeshes = getOrderedList(omeshes, 'name');
|
|
for (var i in omeshes) {
|
|
var cr = 0, mesh = omeshes[i], r = currentUserGroup.links[mesh._id].rights, trash = '', rights = makeDeviceGroupRightsString(r);
|
|
if ((userinfo.links) && (userinfo.links[mesh._id] != null) && (userinfo.links[mesh._id].rights != null)) { cr = userinfo.links[mesh._id].rights; }
|
|
var meshname = '<i>' + "Unknown Device Group" + '</i>';
|
|
if (mesh) { meshname = '<a href=# onclick=\'gotoMesh("' + mesh._id + '");haltEvent(event);\'>' + mesh.name + '</a>'; } else {}
|
|
if ((cr & 2) != 0) {
|
|
trash = '<a href=# onclick=\'return p51removeMeshFromUserGroup(event,"' + encodeURIComponent(mesh._id) + '")\' title=\"' + "Remove user group rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
|
rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(3,\"' + encodeURIComponent(mesh._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
|
|
}
|
|
x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div> ' + meshname + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
|
|
}
|
|
}
|
|
if (count == 1) { x += '<tr><td><div style=padding:6px> <i>' + "No device groups in common" + '</i><div></div></div></td><td></td></tr>'; }
|
|
x += '</tbody></table>';
|
|
|
|
// Display all devices for this user group
|
|
count = 1;
|
|
x += '<br /><a href=# onclick="return p20showAddMeshUserDialog(7)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Device" + '</a>';
|
|
x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Common Devices" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
if (currentUserGroup.links) {
|
|
var onodes = [];
|
|
for (var i in currentUserGroup.links) { if (i.startsWith('node/')) { var node = getNodeFromId(i); if (node != null) { onodes.push(node); } } }
|
|
onodes = getOrderedList(onodes, 'name');
|
|
for (var i in onodes) {
|
|
var node = onodes[i], r = currentUserGroup.links[node._id].rights, trash = '', rights = makeUserDeviceRightsString(r), cr = GetNodeRights(node);
|
|
var nodename = '<i>' + "Unknown Device" + '</i>';
|
|
if (node) { nodename = '<a href=# onclick=\'gotoDevice("' + node._id + '");haltEvent(event);\'>' + node.name + '</a>'; } else {}
|
|
if ((cr & 2) != 0) {
|
|
trash = '<a href=# onclick=\'return p51removeDeviceFromUserGroup(event,"' + encodeURIComponent(node._id) + '")\' title=\"' + "Remove user group rights to this device" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
|
rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(7,\"' + encodeURIComponent(node._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
|
|
}
|
|
x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div> ' + nodename + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
|
|
}
|
|
}
|
|
if (count == 1) { x += '<tr><td><div style=padding:6px> <i>' + "No devices in common" + '</i><div></div></div></td><td></td></tr>'; }
|
|
x += '</tbody></table>';
|
|
|
|
if ((userinfo.siteadmin & 256) != 0) {
|
|
x += '<div style=font-size:x-small;text-align:right><span><a href=# onclick=p51showDeleteUserGroupDialog() style=cursor:pointer>' + "Delete User Group" + '</a></span></div>';
|
|
}
|
|
|
|
QH('p51group2', x);
|
|
go(51);
|
|
|
|
// Change the URL
|
|
var urlviewmode = '';
|
|
if ((xxcurrentView >= 51) && (xxcurrentView <= 59) && (currentUserGroup != null)) {
|
|
urlviewmode = '?viewmode=' + xxcurrentView + '&gotougrp=' + currentUserGroup._id.split('/')[2];
|
|
for (var i in urlargs) { urlviewmode += ('&' + i + '=' + urlargs[i]); }
|
|
try { window.history.replaceState({}, document.title, window.location.pathname + urlviewmode); } catch (ex) { }
|
|
}
|
|
}
|
|
|
|
function p51removeDeviceFromUserGroup(e, nodeid) {
|
|
if (xxdialogMode) return;
|
|
var node = getNodeFromId(decodeURIComponent(nodeid));
|
|
if (node == null) return;
|
|
setDialogMode(2, "Remove Device Permissions", 3, p51removeDeviceFromUserGroupEx, format("Confirm removal of access rights for device \"{0}\"?", node.name), node._id);
|
|
}
|
|
|
|
function p51removeDeviceFromUserGroupEx(b, nodeid) {
|
|
meshserver.send({ action: 'adddeviceuser', nodeid: nodeid, userids: [ currentUserGroup._id ], rights: 0, remove: true });
|
|
}
|
|
|
|
function p51removeMeshFromUserGroup(e, meshid) {
|
|
if (xxdialogMode) return;
|
|
var mesh = meshes[decodeURIComponent(meshid)];
|
|
if (mesh == null) return;
|
|
setDialogMode(2, "Remove Device Group Permissions", 3, p51removeMeshFromUserGroupEx, format("Confirm removal of access rights for device group \"{0}\"?", mesh.name), mesh._id);
|
|
}
|
|
|
|
function p51removeMeshFromUserGroupEx(b, meshid) {
|
|
meshserver.send({ action: 'removemeshuser', meshid: meshid, userid: currentUserGroup._id });
|
|
}
|
|
|
|
function p51editgroup(focus) {
|
|
if (xxdialogMode) return;
|
|
var x = addHtmlValue("Name", '<input id=dp51name style=width:230px maxlength=32 onchange=p51editgroupValidate() onkeyup=p51editgroupValidate(event) />');
|
|
x += addHtmlValue("Description", '<div style=width:230px;margin:0;padding:0><textarea id=dp51desc maxlength=1024 style=width:100%;resize:none></textarea></div>');
|
|
setDialogMode(2, "Edit User Group", 3, p51editgroupEx, x);
|
|
Q('dp51name').value = currentUserGroup.name;
|
|
if (currentUserGroup.desc) Q('dp51desc').value = currentUserGroup.desc;
|
|
p51editgroupValidate();
|
|
if (focus == 2) { Q('dp51desc').focus(); } else { Q('dp51name').focus(); }
|
|
}
|
|
|
|
function p51editgroupEx() {
|
|
meshserver.send({ action: 'editusergroup', ugrpid: currentUserGroup._id, name: Q('dp51name').value, desc: Q('dp51desc').value });
|
|
}
|
|
|
|
function p51editgroupValidate(e) {
|
|
QE('idx_dlgOkButton', Q('dp51name').value.length > 0); if (e && e.key == 'Enter') { Q('dp51desc').focus(); }
|
|
}
|
|
|
|
function p51showDeleteUserGroupDialog() {
|
|
if (xxdialogMode) return false;
|
|
var x = format("Delete user group {0}?", EscapeHtml(currentUserGroup.name)) + '<br /><br />';
|
|
x += '<label><input id=p51check type=checkbox onchange=p51validateDeleteGroupDialog() />' + "Confirm" + '</label>';
|
|
setDialogMode(2, "Delete User Group", 3, p51showDeleteUserGroupDialogEx, x);
|
|
p51validateDeleteGroupDialog();
|
|
return false;
|
|
}
|
|
|
|
function p51validateDeleteGroupDialog() {
|
|
QE('idx_dlgOkButton', Q('p51check').checked);
|
|
}
|
|
|
|
function p51showDeleteUserGroupDialogEx(buttons, tag) {
|
|
meshserver.send({ action: 'deleteusergroup', ugrpid: currentUserGroup._id });
|
|
}
|
|
|
|
function p51deleteUser(e, id) {
|
|
haltEvent(e);
|
|
p51viewuserEx(2, decodeURIComponent(id));
|
|
return false;
|
|
}
|
|
|
|
function p51viewuserEx(button, userid) {
|
|
if (button != 2) return;
|
|
var uname = userid.split('/')[2];
|
|
if (users && users[userid]) { uname = users[userid].name; }
|
|
if (userinfo._id == userid) { uname = userinfo.name; }
|
|
setDialogMode(2, "Remove User Membership", 3, p51viewuserEx2, format("Confirm membership removal of user \"{0}\"?", EscapeHtml(decodeURIComponent(uname))), userid);
|
|
}
|
|
function p51viewuserEx2(button, userid) { meshserver.send({ action: 'removeuserfromusergroup', ugrpid: currentUserGroup._id, userid: userid }); }
|
|
|
|
function p51showAddUserDialog() {
|
|
if (xxdialogMode) return false;
|
|
var x = "Allow users to manage this device group and devices in this group.";
|
|
if (features & 0x00080000) { x += " Users need to login to this server once before they can be added to a device group." }
|
|
x += '<br /><br /><div style=\'position:relative\'>';
|
|
x += addHtmlValue("User Names", '<input id=dp51username style=width:230px maxlength=32 onchange=p51validateAddUserDialog() onkeyup=p51validateAddUserDialog() placeholder="user1, user2, user3" />');
|
|
x += '<div id=dp51usersuggest class=suggestionBox style=\'top:30px;left:130px;display:none\'></div>';
|
|
x += '</div><br>';
|
|
setDialogMode(2, "Add Users to User Group", 3, p51showAddUserDialogEx, x);
|
|
Q('dp51username').focus();
|
|
p51validateAddUserDialog();
|
|
return false;
|
|
}
|
|
|
|
function p51setname(name) {
|
|
name = decodeURIComponent(name);
|
|
var xusers = Q('dp51username').value.split(',');
|
|
for (var i in xusers) { xusers[i] = xusers[i].trim(); }
|
|
xusers[xusers.length - 1] = name;
|
|
Q('dp51username').value = xusers.join(', ');
|
|
p51validateAddUserDialog();
|
|
return false;
|
|
}
|
|
|
|
function p51validateAddUserDialog() {
|
|
var meshrights = GetMeshRights(currentMesh);
|
|
var ok = true;
|
|
if (Q('dp51username')) {
|
|
var xusers = Q('dp51username').value.split(',');
|
|
for (var i in xusers) {
|
|
var xuser = xusers[i] = xusers[i].trim();
|
|
if (xuser.length == 0) { ok = false; } else if (xuser.indexOf('"') >= 0) { ok = false; }
|
|
}
|
|
|
|
// Fill the suggestion box
|
|
var showsuggestbox = false, exactMatch = false;
|
|
if (users != null) {
|
|
var lastuser = xusers[xusers.length - 1].trim(), lastuserl = lastuser.toLowerCase(), matchingUsers = [];
|
|
if (lastuser.length > 0) {
|
|
for (var i in users) {
|
|
if (users[i].name === lastuser) { exactMatch = true; break; }
|
|
if (users[i].name.toLowerCase().indexOf(lastuserl) >= 0) { matchingUsers.push(users[i].name); if (matchingUsers.length >= 8) break; }
|
|
}
|
|
if ((exactMatch == false) && (matchingUsers.length > 0)) {
|
|
var x = '';
|
|
for (var i in matchingUsers) { x += '<a href=# onclick=\'p51setname("' + encodeURIComponent(matchingUsers[i]) + '")\'>' + matchingUsers[i] + '</a><br />'; }
|
|
QH('dp51usersuggest', x);
|
|
showsuggestbox = true;
|
|
}
|
|
}
|
|
}
|
|
QV('dp51usersuggest', showsuggestbox);
|
|
}
|
|
QE('idx_dlgOkButton', ok);
|
|
}
|
|
|
|
function p51showAddUserDialogEx(b, t) {
|
|
if (t == null) {
|
|
var users = Q('dp51username').value.split(','), users2 = [];
|
|
for (var i in users) { users2.push(users[i].trim()); }
|
|
meshserver.send({ action: 'addusertousergroup', ugrpid: currentUserGroup._id, usernames: users2 });
|
|
} else {
|
|
meshserver.send({ action: 'addusertousergroup', ugrpid: currentUserGroup._id, usernames: [ t.split('/')[2] ] });
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// MY USER GENERAL
|
|
//
|
|
|
|
var currentUser = null;
|
|
function gotoUser(userid, force) {
|
|
if (xxdialogMode && !force) return;
|
|
var user = currentUser = users[decodeURIComponent(userid)];
|
|
if (user == null) { setDialogMode(0); go(4); return; }
|
|
QH('p30userName', user.name);
|
|
QH('p31userName', user.name);
|
|
var self = (user.name == userinfo.name), activeSessions = 0;
|
|
if (wssessions != null && wssessions[user._id]) { activeSessions = wssessions[user._id]; }
|
|
|
|
// Change user grayscale
|
|
Q('MainUserImage').classList.remove('gray');
|
|
if (activeSessions == 0) { Q('MainUserImage').classList.add('gray'); }
|
|
|
|
// Server permissions
|
|
var msg = [], premsg = '';
|
|
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = '<img src="images/padlock12.png" height=12 width=8 title="' + "Account is locked" + '" style="margin-top:2px" /> '; msg.push("Locked account"); }
|
|
if ((user.siteadmin == null) || ((user.siteadmin & (0xFFFFFFFF - 224)) == 0)) { msg.push("No server rights"); } else if (user.siteadmin == 8) { msg.push("Access to server files"); } else if (user.siteadmin == 0xFFFFFFFF) { msg.push("Full administrator"); } else { msg.push("Partial rights"); }
|
|
if ((user.siteadmin != null) && (user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & (64 + 128)) != 0)) { msg.push("Restrictions"); }
|
|
|
|
// Show user attributes
|
|
var x = '<div style=min-height:80px><table style=width:100%>';
|
|
var email = user.email?EscapeHtml(user.email):'<i>' + "Not set" + '</i>', everify = '';
|
|
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true) ? '<b style=color:green;cursor:pointer title=\"' + "Email is verified" + '\">✓</b> ' : '<b style=color:red;cursor:pointer title=\"' + "Email not verified" + '\">✗</b> '); }
|
|
if (user.name.toLowerCase() != user._id.split('/')[2]) { x += addDeviceAttribute("User Identifier", user._id.split('/')[2]); }
|
|
if (((features & 0x200000) == 0) && ((user.siteadmin != 0xFFFFFFFF) || (userinfo.siteadmin == 0xFFFFFFFF))) { // If we are not site admin, we can't change a admin email.
|
|
x += addDeviceAttribute("Email", everify + '<a href=# style=cursor:pointer onclick=p30showUserEmailChangeDialog(event,\"' + userid + '\")>' + email + '</a> <a href=# style=cursor:pointer onclick=\'return doemail(event,\"' + user.email + '\")\'><img class=hoverButton src="images/link1.png" /></a>');
|
|
} else {
|
|
x += addDeviceAttribute("Email", everify + email + ' <a href=# style=cursor:pointer onclick=\'return doemail(event,\"' + user.email + '\")\'><img class=hoverButton src="images/link1.png" /></a>');
|
|
}
|
|
if (user.phone != null) { x += addDeviceAttribute("Phone Number", user.phone); }
|
|
x += addDeviceAttribute("Server Rights", premsg + '<a href=# style=cursor:pointer onclick=\'return showUserAdminDialog(event,\"' + userid + '\")\'>' + msg.join(', ') + '</a>');
|
|
if (user.quota) x += addDeviceAttribute("Server Quota", EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
|
x += addDeviceAttribute("Creation", printDateTime(new Date(user.creation * 1000)));
|
|
if (user.login) x += addDeviceAttribute("Last Login", printDateTime(new Date(user.login * 1000)));
|
|
if (user.passchange == -1) { x += addDeviceAttribute("Password", "Will be changed on next login."); }
|
|
else if (user.passchange) { x += addDeviceAttribute("Password", format("Last changed: {0}", printDateTime(new Date(user.passchange * 1000)))); }
|
|
|
|
// Device Groups
|
|
var linkCount = 0, linkCountStr = '<i>' + "None" + '<i>';
|
|
if (user.links) {
|
|
for (var i in user.links) { if (i.startsWith('mesh/')) { linkCount++; } }
|
|
if (linkCount == 1) { linkCountStr = "1 group"; } else if (linkCount > 1) { linkCountStr = format("{0} groups", linkCount); }
|
|
}
|
|
x += addDeviceAttribute("Device Groups", linkCountStr);
|
|
|
|
// Administrative Realms
|
|
if ((userinfo.siteadmin == 0xFFFFFFFF) || (userinfo.siteadmin & 2)) {
|
|
var xuserGroups = '<i>' + "None" + '</i>';
|
|
if (user.groups) { xuserGroups = ''; for (var i in user.groups) { xuserGroups += '<span class="tagSpan">' + user.groups[i] + '</span>'; } }
|
|
x += addDeviceAttribute("Admin Realms", addLinkConditional(xuserGroups, 'showUserGroupDialog(event,\"' + userid + '\")', (userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.groups == null) && (userinfo._id != user._id) && (user.siteadmin != 0xFFFFFFFF))));
|
|
}
|
|
|
|
// Display device user consent
|
|
{
|
|
var meshFeatures = [], consent = 0;
|
|
if (user.consent) { consent = user.consent; }
|
|
if (serverinfo.consent) { consent |= serverinfo.consent; }
|
|
if ((consent & 0x0040) && (consent & 0x0008)) { meshFeatures.push("Desktop Prompt+Toolbar"); } else if (consent & 0x0040) { meshFeatures.push("Desktop Toolbar"); } else if (consent & 0x0008) { meshFeatures.push("Desktop Prompt"); } else { if (consent & 0x0001) { meshFeatures.push("Desktop Notify"); } }
|
|
if (consent & 0x0010) { meshFeatures.push("Terminal Prompt"); } else { if (consent & 0x0002) { meshFeatures.push("Terminal Notify"); } }
|
|
if (consent & 0x0020) { meshFeatures.push("Files Prompt"); } else { if (consent & 0x0004) { meshFeatures.push("Files Notify"); } }
|
|
if (consent == 7) { meshFeatures = ["Always Notify"]; }
|
|
if ((consent & 56) == 56) { meshFeatures = ["Always Prompt"]; }
|
|
|
|
meshFeatures = meshFeatures.join(', ');
|
|
if (meshFeatures == '') { meshFeatures = '<i>' + "None" + '</i>'; }
|
|
x += addDeviceAttribute("User Consent", addLinkConditional(meshFeatures, 'p20editmeshconsent(2)', true));
|
|
}
|
|
|
|
var multiFactor = 0;
|
|
if ((user.otpsecret > 0) || (user.otphkeys > 0)) {
|
|
multiFactor = 1;
|
|
var factors = [];
|
|
if (user.otpsecret > 0) { factors.push("Authentication App"); }
|
|
if (user.otphkeys > 0) { factors.push("Security Key"); }
|
|
if (user.otpkeys > 0) { factors.push("Backup Codes"); }
|
|
if ((user.phone != null) && (features & 0x04000000)) { factors.push("SMS"); }
|
|
x += addDeviceAttribute("Security", '<img src="images/key12.png" height=12 width=11 title=\"' + "2nd factor authentication enabled" + '\" style="margin-top:2px" /> ' + factors.join(', '));
|
|
}
|
|
|
|
x += '</table></div><br />';
|
|
|
|
// Add action buttons
|
|
x += '<input type=button value=\"' + "Notes" + '\" title=\"' + "View notes about this user" + '\" onclick=showNotes(false,"' + userid + '") />';
|
|
if (user.phone && (features & 0x02000000)) { x += '<input type=button value=\"' + "SMS" + '\" title=\"' + "Send a SMS message to this user" + '\" onclick=showSendSMS("' + userid + '") />'; }
|
|
if (!self && (activeSessions > 0)) { x += '<input type=button value=\"' + "Notify" + '\" title=\"' + "Send user notification" + '\" onclick=showUserAlertDialog(event,"' + userid + '") />'; }
|
|
|
|
// Setup the panel
|
|
QH('p30html', x);
|
|
|
|
// Draw the user timeline
|
|
drawUserPermissions();
|
|
|
|
// Check if we can change password / delete this user
|
|
var userAdminRights = (((userinfo.siteadmin != null) && (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF)) || (userinfo.siteadmin == 0xFFFFFFFF));
|
|
|
|
// Show bottom buttons
|
|
x = '<div style=float:right;font-size:x-small>';
|
|
if (userAdminRights) { x += '<a href=# style=cursor:pointer onclick=\'return p30showDeleteUserDialog()\' title="' + "Remove this user" + '">' + "Delete User" + '</a>'; }
|
|
x += '</div><div style=font-size:x-small>';
|
|
if (userAdminRights) { x += '<a href=# style=cursor:pointer onclick=\'return p30showUserChangePassDialog(' + multiFactor + ')\' title="' + "Change the password for this user" + '">' + "Change Password" + '</a>'; }
|
|
x += '</div><br>'
|
|
QH('p30html3', x);
|
|
|
|
// Update user's connection state
|
|
x = '';
|
|
if (activeSessions == 1) { x = "1 active session"; } else if (activeSessions > 1) { x = format("{0} active sessions", activeSessions); }
|
|
QH('MainUserState', x);
|
|
|
|
go(30);
|
|
|
|
// Update user events (TODO: do this only if we change users)
|
|
QH('p31events', '');
|
|
refreshUsersEvents();
|
|
|
|
// Change the URL
|
|
var urlviewmode = '';
|
|
if ((xxcurrentView >= 30) && (xxcurrentView <= 39) && (currentUser != null)) {
|
|
urlviewmode = '?viewmode=' + xxcurrentView + '&gotouser=' + currentUser._id.split('/')[2];
|
|
for (var i in urlargs) { urlviewmode += ('&' + i + '=' + urlargs[i]); }
|
|
try { window.history.replaceState({}, document.title, window.location.pathname + urlviewmode); } catch (ex) { }
|
|
}
|
|
}
|
|
|
|
// Display the user's email change dialog box
|
|
function p30showUserEmailChangeDialog(event) {
|
|
if (xxdialogMode) return false;
|
|
var x = '';
|
|
x += addHtmlValue("Email", '<input id=dp30email style=width:230px maxlength=32 onchange=p30validateEmail() onkeyup=p30validateEmail() />');
|
|
if (serverinfo.emailcheck) { x += addHtmlValue("Status", '<select id=dp30verified style=width:230px onchange=p30validateEmail()><option value=0>' + "Not verified" + '</option><option value=1>' + "Verified" + '</option></select>'); }
|
|
setDialogMode(2, format("Change Email for {0}", EscapeHtml(currentUser.name)), 3, p30showUserEmailChangeDialogEx, x);
|
|
Q('dp30email').focus();
|
|
Q('dp30email').value = (currentUser.email?currentUser.email:'');
|
|
if (serverinfo.emailcheck) { Q('dp30verified').value = currentUser.emailVerified?1:0; }
|
|
p30validateEmail();
|
|
return false;
|
|
}
|
|
|
|
// Perform validation on the user's email change dialog box
|
|
function p30validateEmail() {
|
|
var v = Q('dp30email').value, x = v.split('@');
|
|
x = (x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2) && (v.length < 1024) && ((v != userinfo.email) || ((serverinfo.emailcheck == true) && (Q('dp30verified').value != (userinfo.emailVerified?1:0))));
|
|
QE('idx_dlgOkButton', x);
|
|
}
|
|
|
|
// Send to the server the new user's email address and validation status
|
|
function p30showUserEmailChangeDialogEx() {
|
|
var x = { action: 'edituser', id: currentUser._id, email: Q('dp30email').value };
|
|
if (serverinfo.emailcheck) { x.emailVerified = (Q('dp30verified').value == 1); }
|
|
meshserver.send(x);
|
|
}
|
|
|
|
// Display the user's password change dialog box
|
|
function p30showUserChangePassDialog(multiFactor) {
|
|
if (xxdialogMode) return;
|
|
var x = '';
|
|
x += addHtmlValue("Password", '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=p30showUserChangePassDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
|
|
x += addHtmlValue("Password", '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=p30showUserChangePassDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
|
|
if (features & 0x00010000) { x += addHtmlValue("Password hint", '<input id=p4hint type=text style=width:230px maxlength=256></input>'); }
|
|
|
|
if (passRequirements) {
|
|
var r = [], rc = 0;
|
|
for (var i in passRequirements) { if ((i != 'reset') && (i != 'hint')) { r.push(i + ':' + passRequirements[i]); rc++; } }
|
|
if (rc > 0) { x += '<div style=font-size:x-small;padding:6px>' + format("Requirements: {0}.", r.join(', ')) + '</div>'; }
|
|
}
|
|
|
|
x += '<div><label><input id=p4resetNextLogin type=checkbox />' + "Force password reset on next login." + '</label></div>';
|
|
if (multiFactor == 1) { x += '<div><label><input id=p4twoFactorRemove type=checkbox />' + "Remove all 2nd factor authentication." + '</label></div>'; }
|
|
setDialogMode(2, format("Change Password for {0}", EscapeHtml(currentUser.name)), 3, p30showUserChangePassDialogEx, x, multiFactor);
|
|
p30showUserChangePassDialogValidate();
|
|
Q('p4pass1').focus();
|
|
if (currentUser.passchange == -1) { Q('p4resetNextLogin').checked = true; }
|
|
}
|
|
|
|
function p30showUserChangePassDialogValidate() {
|
|
var ok = true;
|
|
if ((Q('p4pass1').value != '') || (Q('p4pass2').value != '')) {
|
|
if (Q('p4pass1').value != Q('p4pass2').value) { ok = false; } else {
|
|
if (passRequirements) { if (checkPasswordRequirements(Q('p4pass1').value, passRequirements) == false) { ok = false; } }
|
|
}
|
|
}
|
|
QE('idx_dlgOkButton', ok);
|
|
}
|
|
|
|
function p30showUserChangePassDialogEx(b, tag) {
|
|
var removeMultiFactor = false;
|
|
if ((tag == 1) && (Q('p4twoFactorRemove').checked == true)) { removeMultiFactor = true; }
|
|
if (Q('p4pass1').value == Q('p4pass2').value) {
|
|
var r = { action: 'changeuserpass', userid: currentUser._id, pass: Q('p4pass1').value, removeMultiFactor: removeMultiFactor, resetNextLogin: Q('p4resetNextLogin').checked };
|
|
if (features & 0x00010000) { r.hint = Q('p4hint').value; }
|
|
meshserver.send(r);
|
|
}
|
|
}
|
|
|
|
function p30showDeleteUserDialog() {
|
|
if (xxdialogMode) return;
|
|
setDialogMode(2, format("Delete User {0}", EscapeHtml(currentUser.name)), 3, p30showDeleteUserDialogEx, format("Confirm deletion of user {0}?", EscapeHtml(currentUser.name)) + '<br /><br /><label><input id=p10check type=checkbox onchange=p10validateDeleteNodeDialog() />' + "Confirm" + '</label>');
|
|
p10validateDeleteNodeDialog();
|
|
}
|
|
|
|
function p30showDeleteUserDialogEx() {
|
|
meshserver.send({ action: 'deleteuser', userid: currentUser._id, username: currentUser.name });
|
|
}
|
|
|
|
// Draw device power bars. The bars are 766px wide.
|
|
function drawUserPermissions() {
|
|
var count = 1, x = '';
|
|
|
|
// Display common device groups
|
|
var deviceGroupCount = 0, newDeviceGroup = false;
|
|
for (var i in meshes) { deviceGroupCount++; if ((currentUser.links == null) || (currentUser.links[i] == null)) { newDeviceGroup = true; } }
|
|
if ((deviceGroupCount > 0) && (newDeviceGroup)) { x += '<a href=# onclick="return p20showAddMeshUserDialog(1)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Device Group" + '</a>'; }
|
|
x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Common Device Groups" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
if (currentUser.links) {
|
|
var omeshes = [];
|
|
for (var i in currentUser.links) { if (i.startsWith('mesh/')) { if (meshes[i] != null) { omeshes.push(meshes[i]); } } }
|
|
omeshes = getOrderedList(omeshes, 'name');
|
|
for (var i in omeshes) {
|
|
var cr = 0, mesh = omeshes[i], r = currentUser.links[mesh._id].rights, trash = '', rights = makeDeviceGroupRightsString(r);
|
|
if (mesh == null) { continue; }
|
|
if ((userinfo.links) && (userinfo.links[mesh._id] != null) && (userinfo.links[mesh._id].rights != null)) { cr = userinfo.links[mesh._id].rights; }
|
|
var meshname = '<i>' + "Unknown Device Group" + '</i>';
|
|
if (mesh) { meshname = '<a href=# onclick=\'gotoMesh("' + mesh._id + '");haltEvent(event);\'>' + EscapeHtml(mesh.name) + '</a>'; }
|
|
if ((currentUser._id != userinfo._id) && ((cr & 2) != 0)) { // 2 = MESHRIGHT_MANAGEUSERS
|
|
trash = '<a href=# onclick=\'return p30removeMeshFromUser(event,"' + encodeURIComponent(mesh._id) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
|
rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(1,\"' + encodeURIComponent(mesh._id) + '\")>' + rights + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
|
|
}
|
|
x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device Group" + '\" class=m99></div><div> ' + meshname + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
|
|
}
|
|
}
|
|
if (count == 1) { x += '<tr><td><div style=padding:6px> <i>' + "No device groups in common" + '</i><div></div></div></td><td></td></tr>'; }
|
|
x += '</tbody></table>';
|
|
|
|
// Display user groups
|
|
if (usergroups != null) {
|
|
count = 1;
|
|
x += '<br />';
|
|
if ((userinfo.siteadmin & 256) != 0) {
|
|
var userGroupCount = 0, newUserGroup = false;
|
|
for (var i in usergroups) { userGroupCount++; if ((currentUser.links == null) || (currentUser.links[i] == null)) { newUserGroup = true; } }
|
|
if ((userGroupCount > 0) && (newUserGroup)) { x += '<a href=# onclick="return p30showAddUserGroupDialog()" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add User Group" + '</a>'; }
|
|
}
|
|
x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "User Group Memberships" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
if (currentUser.links) {
|
|
var ougroups = [];
|
|
for (var i in currentUser.links) { if (i.startsWith('ugrp/')) { if (usergroups[i] != null) { ougroups.push(usergroups[i]); } } }
|
|
ougroups = getOrderedList(ougroups, 'name');
|
|
for (var i in ougroups) {
|
|
var group = ougroups[i], r = currentUser.links[ougroups[i]._id].rights, trash = '';
|
|
var groupname = '<i>' + "Unknown User Group" + '</i>';
|
|
if (group != null) {
|
|
groupname = EscapeHtml(group.name);
|
|
if (usergroups != null) { groupname = '<a href=# onclick=\'gotoUserGroup("' + encodeURIComponent(ougroups[i]._id) + '");haltEvent(event);\'>' + groupname + '</a>'; }
|
|
}
|
|
if ((userinfo.siteadmin & 256) != 0) { trash = '<a href=# onclick=\'return p30RemoveUserGroup(event,"' + encodeURIComponent(ougroups[i]._id) + '")\' title=\"' + "Remove user group membership" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
|
|
x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td><div title=\"' + "User Group" + '\" class=m4></div><div> ' + groupname + '<div></div></div></td><td><div style=float:right>' + trash + '</div></td></tr>';
|
|
}
|
|
}
|
|
if (count == 1) { x += '<tr><td><div style=padding:6px> <i>' + "No user group memberships" + '</i><div></div></div></td><td></td></tr>'; }
|
|
x += '</tbody></table>';
|
|
}
|
|
|
|
// Display common devices
|
|
count = 1;
|
|
x += '<br /><a href=# onclick="return p20showAddMeshUserDialog(4)" style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add Device" + '</a>';
|
|
x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "Common Devices" + '</th><th scope=col style=text-align:left></th></tr>';
|
|
if (currentUser.links) {
|
|
// Sort the list of devices to display
|
|
var nodelist = [];
|
|
for (var i in currentUser.links) { if (i.startsWith('node/')) { var node = getNodeFromId(i); if (node != null) { nodelist.push(node); } } }
|
|
nodelist.sort(nameSort);
|
|
for (var i in nodelist) {
|
|
var node = nodelist[i], r = currentUser.links[node._id].rights, trash = '', cr = GetNodeRights(node);
|
|
if ((userinfo.links) && (userinfo.links[i] != null) && (userinfo.links[i].rights != null)) { cr = userinfo.links[i].rights; }
|
|
var nodename = node?EscapeHtml(node.name):('<i>' + "Unknown Device" + '</i>');
|
|
if ((cr & 2) != 0) {
|
|
trash = '<a href=# onclick=\'return p30removeNodeFromUser(event,"' + encodeURIComponent(node._id) + '")\' title=\"' + "Remove user rights to this device group" + '\" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>';
|
|
rights = '<span style=cursor:pointer onclick=p20showAddMeshUserDialog(4,\"' + encodeURIComponent(node._id) + '\")>' + makeUserDeviceRightsString(r) + ' <img class=hoverButton style=cursor:pointer src=images/link5.png></span>';
|
|
}
|
|
nodename = '<a href=# onclick=\'gotoDevice("' + node._id + '",10);haltEvent(event);\'>' + nodename + '</a>';
|
|
x += '<tr ' + (((++count % 2) == 0) ? 'style=background-color:#DDD' : '') + '><td style=width:30%><div title=\"' + "Device" + '\" class=si' + node.icon + '></div><div> ' + nodename + '<div></div></div></td><td style=width:70%><div style=float:right>' + trash + '</div><div>' + rights + '</div></td></tr>';
|
|
}
|
|
}
|
|
if (count == 1) { x += '<tr><td><div style=padding:6px> <i>' + "No devices in common" + '</i><div></div></div></td><td></td></tr>'; }
|
|
x += '</tbody></table>';
|
|
|
|
QH('p30html2', x);
|
|
}
|
|
|
|
function p30removeNodeFromUser(event, nodeid) {
|
|
if (xxdialogMode) return;
|
|
var node = getNodeFromId(decodeURIComponent(nodeid));
|
|
setDialogMode(2, "Remove Device Permissions", 3, function(b, node) { meshserver.send({ action: 'adddeviceuser', nodeid: node._id, nodename: node.name, userids: [ currentUser._id ], rights: 0, remove: true }); }, format("Confirm removal of access rights for device \"{0}\"?", node.name), node);
|
|
}
|
|
|
|
function p30removeUserFromNode(event, userid) {
|
|
if (xxdialogMode) return;
|
|
var user = null, name = '';
|
|
userid = decodeURIComponent(userid);
|
|
if (userid.startsWith('user/')) {
|
|
if (users) { user = users[userid]; if (user != null) { name = user.name; } }
|
|
setDialogMode(2, "Remove User Permissions", 3, function(b, user) { meshserver.send({ action: 'adddeviceuser', nodeid: currentNode._id, nodename: currentNode.name, userids: [ userid ], rights: 0, remove: true }); }, name?format("Confirm removal of access rights for user \"{0}\"?", name):"Confirm removal of access rights?", user);
|
|
} else if (userid.startsWith('ugrp/')) {
|
|
if (usergroups) { user = usergroups[userid]; if (user != null) { name = user.name; } }
|
|
setDialogMode(2, "Remove User Group Permissions", 3, function(b, user) { meshserver.send({ action: 'adddeviceuser', nodeid: currentNode._id, nodename: currentNode.name, userids: [ userid ], rights: 0, remove: true }); }, name?format("Confirm removal of access rights for user group \"{0}\"?", name):"Confirm removal of access rights?", user);
|
|
}
|
|
}
|
|
|
|
function p30RemoveUserGroup(button, ugrpid) {
|
|
if (xxdialogMode || (usergroups == null)) return;
|
|
var groupid = decodeURIComponent(ugrpid), group = usergroups[groupid];
|
|
var name = (group != null)?EscapeHtml(group.name):('<i>' + "Unknown" + '</i>');
|
|
setDialogMode(2, "Remove User Group Membership", 3, p30RemoveUserGroupEx, format("Confirm membership removal of user group \"{0}\"?", name), groupid);
|
|
}
|
|
|
|
function p30RemoveUserGroupEx(b, groupid) {
|
|
meshserver.send({ action: 'removeuserfromusergroup', ugrpid: groupid, userid: currentUser._id });
|
|
}
|
|
|
|
function p30showAddUserGroupDialog() {
|
|
if (xxdialogMode || (usergroups == null)) return;
|
|
var y = '';
|
|
for (var i in usergroups) { if ((currentUser.links == null) || (currentUser.links[i] == null)) { y += '<option value=' + encodeURIComponent(i) + '>' + EscapeHtml(usergroups[i].name) + '</option>'; } }
|
|
var x = addHtmlValue("User Group", '<div style=width:230px;margin:0;padding:0><select id=dp2groupid style=width:100%>' + y + '</select></div>');
|
|
setDialogMode(2, "Add Membership", 3, p30showAddUserGroupDialogEx, x);
|
|
Q('dp2groupid').focus();
|
|
return false;
|
|
}
|
|
|
|
function p30showAddUserGroupDialogEx() {
|
|
meshserver.send({ action: 'addusertousergroup', ugrpid: decodeURIComponent(Q('dp2groupid').value), usernames: [ currentUser._id.split('/')[2] ] });
|
|
}
|
|
|
|
function p30removeMeshFromUser(e, meshid) {
|
|
if (xxdialogMode) return;
|
|
var mesh = meshes[decodeURIComponent(meshid)];
|
|
if (mesh == null) return;
|
|
setDialogMode(2, "Remove Device Group Permissions", 3, p30removeMeshFromUserEx, format("Confirm removal of access rights for device group \"{0}\"?", mesh.name), mesh._id);
|
|
}
|
|
|
|
function p30removeMeshFromUserEx(b, meshid) {
|
|
meshserver.send({ action: 'removemeshuser', meshid: meshid, userid: currentUser._id });
|
|
}
|
|
|
|
//
|
|
// MY USER EVENTS
|
|
//
|
|
|
|
var currentUserEvents = null;
|
|
function userEventsUpdate() {
|
|
if (currentUser == null) return;
|
|
var x = '', dateHeader = null;
|
|
for (var i in currentUserEvents) {
|
|
var event = currentUserEvents[i], time = new Date(event.time);
|
|
if (event.msg) {
|
|
if (event.h == null) { event.h = Math.random(); }
|
|
if (printDate(time) != dateHeader) {
|
|
if (dateHeader != null) x += '</table>';
|
|
dateHeader = printDate(time);
|
|
x += '<table class=p3eventsTable cellpadding=0 cellspacing=0><tr><td colspan=4 class=DevSt>' + dateHeader + '</td></tr>';
|
|
}
|
|
var icon = 'si3';
|
|
if (event.etype == 'user') icon = 'm2';
|
|
if (event.etype == 'server') icon = 'si3';
|
|
|
|
var msg = EscapeHtml(event.msg).split('(R)').join('®');
|
|
if (event.nodeid) {
|
|
var node = getNodeFromId(event.nodeid);
|
|
if (node != null) {
|
|
icon = 'si' + node.icon;
|
|
msg = '<a href=# onclick=\'gotoDevice("' + event.nodeid + '",10);haltEvent(event);\'>' + EscapeHtml(node.name) + '</a> → ' + msg;
|
|
}
|
|
}
|
|
if (event.username && (event.username != currentUser.name)) {
|
|
if ((userinfo.siteadmin & 2) && (event.userid)) {
|
|
msg = '<a href=# onclick=\'gotoUser("' + encodeURIComponent(event.userid) + '");haltEvent(event);\'>' + EscapeHtml(event.username) + '</a> → ' + msg;
|
|
} else {
|
|
msg = EscapeHtml(event.username) + ' → ' + msg;
|
|
}
|
|
}
|
|
if (event.etype == 'relay' || event.action == 'relaylog') icon = 'relayIcon16';
|
|
x += '<tr onclick=showEventDetails(' + event.h + ',3) onmouseover=eventMouseHover(this,1) onmouseout=eventMouseHover(this,0) style=cursor:pointer><td style=width:18px><div class=' + icon + '></div></td><td class=g1> </td><td class=style10>' + printTime(time) + ' - ' + msg + '</td><td class=g2> </td></tr><tr style=height:2px></tr>';
|
|
}
|
|
}
|
|
if (dateHeader != null) x += '</table>';
|
|
if (x == '') x = '<br><i>' + "No Events Found" + '</i><br><br>';
|
|
QH('p31events', x);
|
|
}
|
|
|
|
function refreshUsersEvents() {
|
|
meshserver.send({ action: 'events', limit: parseInt(p31limitdropdown.value), user: currentUser.name });
|
|
}
|
|
|
|
//
|
|
// FILE SELECTOR, DIALOG 3
|
|
//
|
|
|
|
function d3init() {
|
|
Q('d3localFile').value = '';
|
|
d3modechange();
|
|
}
|
|
|
|
function d3modechange() {
|
|
var mode = Q('d3uploadMode').value;
|
|
QV('d3localmode', mode == 1);
|
|
QV('d3servermode', mode == 2);
|
|
if (mode == 1) { d3setActions(); } else { d3updatefiles(); }
|
|
}
|
|
|
|
var d3filetreelinkpath;
|
|
var d3filetreelocation = [];
|
|
|
|
function d3updatefiles() {
|
|
if (Q('d3uploadMode').value == 1) return;
|
|
var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1;
|
|
|
|
// Navigate to path location, build the paths at the same time
|
|
var d3filetreelocation2 = [], oldlinkpath = d3filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc');
|
|
for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedBoxes.push(checkboxes[i].value) }; } // Save all existing checked boxes
|
|
|
|
d3filetreelinkpath = '';
|
|
for (var i in d3filetreelocation) {
|
|
if ((filetreex.f != null) && (filetreex.f[d3filetreelocation[i]] != null)) {
|
|
d3filetreelocation2.push(d3filetreelocation[i]);
|
|
if ((folderdepth == 1)) {
|
|
var sp = d3filetreelocation[i].split('/');
|
|
publicPath = window.location + sp[0] + 'files/' + sp[2];
|
|
if (d3filetreelocation[i] === userinfo._id) { d3filetreelinkpath += 'self'; } else { d3filetreelinkpath += (sp[0] + '/' + sp[2]); }
|
|
} else {
|
|
if (d3filetreelinkpath != '') { d3filetreelinkpath += '/' + d3filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + d3filetreelocation[i]; } }
|
|
}
|
|
filetreex = filetreex.f[d3filetreelocation[i]];
|
|
folderdepth++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
d3filetreelocation = d3filetreelocation2; // In case we could not go down the full path, we set the new path location here.
|
|
|
|
// Sort the files
|
|
var filetreexx = p5sort_files(filetreex.f);
|
|
|
|
// Display all files and folders at this location
|
|
for (var i in filetreexx) {
|
|
// Figure out the name and shortname
|
|
var f = filetreexx[i], name = f.n, shortname;
|
|
shortname = name;
|
|
if (name.length > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + ("..." + '</span>'); } else { shortname = EscapeHtml(name); }
|
|
name = EscapeHtml(name);
|
|
|
|
// Figure out the size
|
|
var fsize = '';
|
|
if (f.s != null) { fsize = getFileSizeStr(f.s); }
|
|
|
|
var h = '';
|
|
if (f.t < 3) {
|
|
var title = '';
|
|
h = '<div class=filelist file=999><span style=float:right title=\"' + title + '\"></span><span><div class=fileIcon' + f.t + ' onclick=d3folderset(\"' + encodeURIComponent(f.nx) + '\")></div> <a href=# style=cursor:pointer onclick=\'return d3folderset(\"' + encodeURIComponent(f.nx) + '\")\'>' + shortname + '</a></span></div>';
|
|
} else {
|
|
var link = shortname;
|
|
//if (f.s > 0) { link = "<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"downloadfile.ashx?link=" + encodeURIComponent(filetreelinkpath + '/' + f.nx) + "\">" + shortname + "</a>"; }
|
|
h = '<div class=filelist file=3><input style=float:left name=fcx class=fcb type=checkbox onchange=d3setActions() value="' + f.nx + '"> <span style=float:right>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
|
|
}
|
|
|
|
if (f.t < 3) { html1 += h; } else { html2 += h; }
|
|
}
|
|
|
|
QH('d3serverfiles', html1 + html2);
|
|
QE('p3FolderUp', d3filetreelocation.length > 0);
|
|
d3setActions();
|
|
}
|
|
|
|
function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); return false; }
|
|
function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
|
|
function d3getFileSel() { var cc = []; var checkboxes = document.getElementsByName('fcx'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { cc.push(checkboxes[i].value) } } return cc; }
|
|
function d3setActions() {
|
|
var mode = Q('d3uploadMode').value;
|
|
if (mode == 1) {
|
|
QE('idx_dlgOkButton', Q('d3localFile').value.length > 0);
|
|
} else {
|
|
QE('idx_dlgOkButton', d3getFileSel().length == 1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// NOTIFICATIONS
|
|
//
|
|
|
|
var notifications = [];
|
|
|
|
// Toggle showing notifications
|
|
function clickNotificationIcon(show) {
|
|
//addNotification({ icon:0, text:'test' });
|
|
if (show == true) { QV('notifiyBox', true); } else if (show == false) { QV('notifiyBox', false); } else { QV('notifiyBox', QS('notifiyBox')['display'] == 'none'); }
|
|
drawNotifications();
|
|
}
|
|
|
|
// Set the notification count on the upper right oft he screen
|
|
function setNotificationCount(c) {
|
|
if (parseInt(Q('notificationCount').innerHTML) == c) return; // If the count did not change, exit now.
|
|
QH('notificationCount', c);
|
|
QS('notificationCount')['background-color'] = (c == 0)?'lightblue':'orange';
|
|
QV('notificationCount', c > 0);
|
|
}
|
|
|
|
// Refresh the notification box
|
|
function drawNotifications() {
|
|
var notifySettings = getstore('notifications', 0);
|
|
var r = '';
|
|
if (notifications.length == 0) {
|
|
r = '<div style=margin:5px>' + "There are currently no notifications" + '</div>';
|
|
} else {
|
|
for (var i in notifications) {
|
|
var n = notifications[i], t = '', d = new Date(n.time), icon = 0;
|
|
if (n.title != null) { t = '<b>' + n.title + '</b>: ' }
|
|
if (n.nodeid != null) {
|
|
var node = getNodeFromId(n.nodeid);
|
|
if (node != null) {
|
|
icon = node.icon;
|
|
if (notifySettings & 16) { t = '<b>' + meshes[node.meshid].name + ' / ' + node.name + '</b>: '; } else { t = '<b>' + node.name + '</b>: '; } // Display with or without group name
|
|
}
|
|
}
|
|
|
|
r += '<div title="' + format("Occured at {0}", printDateTime(d)) + '" id="notifyx' + n.id + '" class=notification style="cursor:pointer;border-top:1px solid ' + ((r == '') ? 'transparent' : 'orange') + '">';
|
|
if (icon) { r += '<div class=j' + icon + ' onclick="notificationSelected(' + n.id + ')" style=margin:5px;float:left></div>'; }
|
|
r += '<div onclick="notificationDelete(' + n.id + ')" class=unselectable title="' + "Clear this notification" + '" style=margin:5px;float:right;color:orange><b>X</b></div><div onclick="notificationSelected(' + n.id + ')" style=margin:5px>' + t + n.text + '</div></div>';
|
|
}
|
|
}
|
|
var deleteall = '';
|
|
if (notifications.length > 1) { deleteall = '<div id="notifyRemoveAll" onclick="deleteAllNotifications()" style="cursor:pointer;border-top:1px solid orange;margin:5px;color:orange;text-align:right;padding-right:3px">' + "Clear all" + '</div>'; }
|
|
QH('notifiyBox', '<div class=customScroll style="max-height:170px;overflow-y:auto;margin:5px">' + r + '</div>' + deleteall );
|
|
}
|
|
|
|
// A notification was selected
|
|
function notificationSelected(id, del) {
|
|
var j = -1;
|
|
for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
|
|
if (j != -1) {
|
|
notificationSelectedEx(notifications[j], id);
|
|
if (del && notifications[j]) {
|
|
if (notifications[j].notification) { notifications[j].notification.close(); delete notifications[j].notification; }
|
|
notificationDelete(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
function notificationSelectedEx(n, id) {
|
|
if (n.nodeid != null) {
|
|
if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop
|
|
else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal
|
|
else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files
|
|
else if (n.tag == 'intelamt') gotoDevice(n.nodeid, 14); // Intel AMT
|
|
else if (n.tag == 'console') gotoDevice(n.nodeid, 15); // Files
|
|
else gotoDevice(n.nodeid, 10); // General
|
|
} else {
|
|
if ((n.tag != null) && n.tag.startsWith('meshmessenger/')) {
|
|
window.open('/messenger?id=' + n.tag + '&title=' + encodeURIComponent(n.username), n.tag.split('/')[2]);
|
|
notificationDelete(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove one notification
|
|
function notificationDelete(id) {
|
|
var j = -1, e = Q('notifyx' + id);
|
|
if (e != null) {
|
|
for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
|
|
if (j != -1) {
|
|
meshserver.send({ action: 'intersession', subaction: 'removeNotify', id: id }); // Remove the notification in other sessions of the same user.
|
|
if (notifications[j].notification) { notifications[j].notification.close(); delete notifications[j].notification; }
|
|
notifications.splice(j, 1);
|
|
e.parentNode.removeChild(e);
|
|
setNotificationCount(notifications.length);
|
|
if (notifications.length == 0) { QV('notifiyBox', false); }
|
|
if (notifications.length == 1) { QV('notifyRemoveAll', false); }
|
|
if ((notifications.length > 0) && (j == 0)) {
|
|
var n = notifications[0];
|
|
QS('notifyx' + n.id)['border-top'] = '1px solid transparent';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add a new notification and play the notification sound
|
|
function addNotification(n) {
|
|
// Show notification within the web page.
|
|
if (n.time == null) { n.time = Date.now(); }
|
|
if (n.id == null) { n.id = Math.random(); }
|
|
notifications.unshift(n);
|
|
setNotificationCount(notifications.length);
|
|
clickNotificationIcon(true);
|
|
var notifySettings = getstore('notifications', 0);
|
|
if (notifySettings & 1) { Q('chimes').play(); }
|
|
|
|
// If web notifications are granted, use it.
|
|
var notification = null;
|
|
if (Notification && (Notification.permission == 'granted')) {
|
|
var text = n.text.split('®').join('').split('<b>').join('').split('</b>').join('').split('<br />').join('\r\n'); // Clean up any HTML codes
|
|
if (n.nodeid) {
|
|
var node = getNodeFromId(n.nodeid);
|
|
if (node) {
|
|
if (notifySettings & 16) { // Notify with group name
|
|
notification = new Notification('{{{title}}} - ' + meshes[node.meshid].name + ' - ' + node.name, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + node.icon + '.png' });
|
|
} else {
|
|
notification = new Notification('{{{title}}} - ' + node.name, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + node.icon + '.png' });
|
|
}
|
|
}
|
|
} else {
|
|
if (n.icon == null) { n.icon = 0; }
|
|
var title = n.title;
|
|
if (title == null) { title = ''; } else { title = ' - ' + n.title; }
|
|
notification = new Notification('{{{title}}}' + title, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + n.icon + '.png' });
|
|
}
|
|
notification.id = n.id;
|
|
notification.xtag = n.tag;
|
|
notification.nodeid = n.nodeid;
|
|
notification.username = n.username;
|
|
notification.onclick = function (e) { notificationSelected(e.target.id, true); }
|
|
n.notification = notification;
|
|
}
|
|
}
|
|
|
|
// Remove all notifications
|
|
function deleteAllNotifications() {
|
|
notifications = [];
|
|
setNotificationCount(0);
|
|
drawNotifications();
|
|
QV('notifiyBox', false);
|
|
}
|
|
|
|
//
|
|
// MyServer General
|
|
//
|
|
|
|
function setupGeneralServerStats() {
|
|
window.serverStatCpu = new Chart(document.getElementById('serverCpuChart').getContext('2d'), {
|
|
type: 'doughnut',
|
|
data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ["Used", "Free"] },
|
|
options: { events: [], responsive: true, legend: { position: 'none', }, animation: { animateScale: true, animateRotate: true }, width: '60px' }
|
|
});
|
|
window.serverStatMemory = new Chart(document.getElementById('serverMemoryChart').getContext('2d'), {
|
|
type: 'doughnut',
|
|
data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ["Used", "Free"] },
|
|
options: { events: [], responsive: true, legend: { position: 'none', }, animation: { animateScale: true, animateRotate: true }, width: '60px' }
|
|
});
|
|
}
|
|
|
|
var serverStatsStrings = {
|
|
ServerState: "Server State",
|
|
AgentErrorCounters: "Agent Error Counters",
|
|
UnknownGroup: "Unknown Group",
|
|
InvalidPKCSsignature: "Invalid PKCS signature",
|
|
InvalidRSAsiguature: "Invalid RSA siguature",
|
|
InvalidJSON: "Invalid JSON",
|
|
UnknownAction: "Unknown Action",
|
|
BadWebCertificate: "Bad Web Certificate",
|
|
BadSignature: "Bad Signature",
|
|
MaxSessionsReached: "Max Sessions Reached",
|
|
UnknownDeviceGroup: "Unknown Device Group",
|
|
InvalidDeviceGroupType: "Invalid Device Group Type",
|
|
DuplicateAgent: "Duplicate Agent",
|
|
ConnectedIntelAMT: "Connected Intel® AMT",
|
|
RelayErrors: "Relay Errors",
|
|
UserAccounts: "User Accounts",
|
|
DeviceGroups: "Device Groups",
|
|
AgentSessions: "Agent Sessions",
|
|
ConnectedUsers: "Connected Users",
|
|
UsersSessions: "Users Sessions",
|
|
RelaySessions: "Relay Sessions",
|
|
RelayCount: "Relay Count"
|
|
};
|
|
|
|
var lastServerStats = null;
|
|
function updateGeneralServerStats(message) {
|
|
if (message != null) { lastServerStats = message; } else { message = lastServerStats; }
|
|
if (message == null) return;
|
|
|
|
// Paint the pie graphs
|
|
if (typeof message.cpuavg == 'object') {
|
|
var m = Math.min(message.cpuavg[0], 1);
|
|
window.serverStatCpu.config.data.datasets[0].data = [m, 1 - m];
|
|
QH('serverCpuChartText', '<div style=margin-bottom:5px>' + "CPU Load" + '</div><div><b title=\"' + "CPU load in the last minute" + '\">' + (Math.round(message.cpuavg[0] * 100.0) / 100.0) + '</b>, <b title=\"' + "CPU load in the last 5 minutes" + '\">' + (Math.round(message.cpuavg[1] * 100.0) / 100.0) + '</b>, <b title=\"' + "CPU load in the last 15 minutes" + '\">' + (Math.round(message.cpuavg[2] * 100.0) / 100.0) + '</b></div>');
|
|
QS('serverCpuChartView')['display'] = 'inline-block';
|
|
window.serverStatCpu.update();
|
|
}
|
|
if ((typeof message.totalmem == 'number') && (typeof message.freemem == 'number')) {
|
|
window.serverStatMemory.config.data.datasets[0].data = [message.totalmem - message.freemem, message.freemem];
|
|
QH('serverMemoryChartText', '<div style=margin-bottom:5px>' + "Memory" + '</div><div><b>' + getNiceSize2(message.freemem) + '</b> ' + "free" + ', <b>' + getNiceSize2(message.totalmem) + '</b> ' + "total" + '</div>');
|
|
QS('serverMemoryChartView')['display'] = 'inline-block';
|
|
window.serverStatMemory.update();
|
|
}
|
|
|
|
// Display all of the server values
|
|
// serverStatsStrings
|
|
var x = '<div style=width:100% cellpadding=0 cellspacing=0>';
|
|
if (typeof message.values == 'object') {
|
|
for (var i in message.values) {
|
|
x += '<div class=userTableHeader style=margin-bottom:4px;width:200px>' + (serverStatsStrings[i]?serverStatsStrings[i]:i) + '</div>';
|
|
for (var j in message.values[i]) {
|
|
x += '<div style=display:inline-block><table class=serverStateTableCell><tr><td class=h1></td><td><span>' + (serverStatsStrings[j]?serverStatsStrings[j]:j) + '</span><span style=float:right>' + message.values[i][j] + '</span></td><td class=h2></td></tr></table></div>';
|
|
}
|
|
}
|
|
}
|
|
x += '</div>';
|
|
|
|
QH('serverStatsTable', x);
|
|
}
|
|
|
|
//
|
|
// MyServer Stats
|
|
//
|
|
|
|
var serverTimelineStats = null;
|
|
var serverTimelineConfig = {
|
|
type: 'line',
|
|
data: { labels: [], datasets: [{ label: '', backgroundColor: 'rgba(255, 99, 132, .5)', borderColor: 'rgb(255, 99, 132)', data: [], fill: true }] },
|
|
options: {
|
|
animation: false,
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
elements: { line: { cubicInterpolationMode: 'monotone' } },
|
|
scales: {
|
|
xAxes: [{ type: 'time', time: { tooltipFormat: 'll HH:mm' }, display: true, scaleLabel: { display: false, labelString: '' } }],
|
|
yAxes: [{ type: 'linear', display: true, scaleLabel: { display: true, labelString: '' } }]
|
|
}
|
|
}
|
|
};
|
|
|
|
function refreshServerTimelineStats(stats) { meshserver.send({ action: 'servertimelinestats', hours: 24 * 30 }); }
|
|
function pastDate(hours) { var t = new Date(); t.setTime(t.getTime() - (60 * 60 * 1000 * hours)); return t; }
|
|
function setServerTimelineStats(stats) { serverTimelineStats = stats; updateServerTimelineStats(); }
|
|
function addServerTimelineStats(stats) {
|
|
if (serverTimelineStats == null) return;
|
|
serverTimelineStats.push(stats);
|
|
var chartType = Q('p40type').value;
|
|
if (chartType == 0) {
|
|
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.conn.ca });
|
|
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.conn.cu });
|
|
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.conn.us });
|
|
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.conn.rs });
|
|
if (stats.conn.am != null) { serverTimelineConfig.data.datasets[4].data.push({ x: stats.time, y: stats.conn.am }); }
|
|
} else if (chartType == 1) {
|
|
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.mem.external / (1024 * 1024) });
|
|
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.mem.heapUsed / (1024 * 1024) });
|
|
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.mem.heapTotal / (1024 * 1024) });
|
|
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.mem.rss / (1024 * 1024) });
|
|
} /* else if (chartType == 2) {
|
|
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.db.meshes });
|
|
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.db.nodes });
|
|
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.db.users });
|
|
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.db.total });
|
|
} */
|
|
updateServerTimelineHours();
|
|
}
|
|
function updateServerTimelineHours() {
|
|
serverTimelineConfig.options.scales.yAxes[0].type = (Q('p40log').checked ? 'logarithmic' : 'linear');
|
|
serverTimelineConfig.options.scales.xAxes[0].time = { min: pastDate(Q('p40time').value) };
|
|
window.serverMainStats.update();
|
|
}
|
|
function setupServerTimelineStats() { window.serverMainStats = new Chart(document.getElementById('serverMainStats').getContext('2d'), serverTimelineConfig); }
|
|
|
|
function updateServerTimelineStats() {
|
|
var data, chartType = Q('p40type').value, timeAfter = pastDate(Q('p40time').value);
|
|
serverTimelineConfig.options.scales.xAxes[0].time = { min: timeAfter };
|
|
if (chartType == 0) { // Connections
|
|
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = "Connection Count";
|
|
data = {
|
|
labels: [pastDate(0), timeAfter],
|
|
datasets: [
|
|
{ label: "Agents", data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
|
{ label: "Users", data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
|
{ label: "User Sessions", data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
|
{ label: "Relay Sessions", data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true },
|
|
{ label: "Intel AMT", data: [], backgroundColor: 'rgba(134, 16, 158, .1)', borderColor: 'rgb(134, 16, 158)', fill: true }
|
|
]
|
|
};
|
|
for (var i = 0; i < serverTimelineStats.length; i++) {
|
|
var t = new Date(serverTimelineStats[i].time);
|
|
if (serverTimelineStats[i].first == true) {
|
|
data.datasets[0].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
data.datasets[1].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
data.datasets[2].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
data.datasets[3].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
data.datasets[4].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
}
|
|
if (serverTimelineStats[i].conn) {
|
|
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.ca });
|
|
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.cu });
|
|
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.us });
|
|
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.rs });
|
|
if (serverTimelineStats[i].conn.am != null) { data.datasets[4].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.am }); }
|
|
}
|
|
}
|
|
} else if (chartType == 1) { // Memory
|
|
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = "Megabytes";
|
|
data = {
|
|
labels: [pastDate(0), timeAfter],
|
|
datasets: [
|
|
{ label: "External", data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
|
{ label: "Heap Used", data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
|
{ label: "Heap Total", data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
|
{ label: "RSS", data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true }
|
|
]
|
|
};
|
|
for (var i = 0; i < serverTimelineStats.length; i++) {
|
|
if (serverTimelineStats[i].first == true) {
|
|
data.datasets[0].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
data.datasets[1].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
data.datasets[2].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
data.datasets[3].data.push({ x: serverTimelineStats[i].time - 1, y: NaN });
|
|
}
|
|
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.external / (1024 * 1024) });
|
|
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.heapUsed / (1024 * 1024) });
|
|
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.heapTotal / (1024 * 1024) });
|
|
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.rss / (1024 * 1024) });
|
|
}
|
|
} /*else if (chartType == 2) { // Database
|
|
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Records';
|
|
data = {
|
|
labels: [pastDate(0), timeAfter],
|
|
datasets: [
|
|
{ label: 'Groups', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
|
{ label: 'Devices', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
|
{ label: 'Users', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
|
{ label: 'Records', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true }
|
|
]
|
|
};
|
|
for (var i = 0; i < serverTimelineStats.length; i++) {
|
|
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.meshes });
|
|
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.nodes });
|
|
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.users });
|
|
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.total });
|
|
}
|
|
}*/
|
|
serverTimelineConfig.data = data;
|
|
window.serverMainStats.update();
|
|
}
|
|
|
|
function p40downloadEvents() {
|
|
var csv = "time, conn.agent, conn.users, conn.usersessions, conn.relaysession, conn.intelamt, mem.external, mem.heapused, mem.heaptotal, mem.rss" + '\r\n';
|
|
for (var i = 0; i < serverTimelineStats.length; i++) {
|
|
if (serverTimelineStats[i].conn && serverTimelineStats[i].mem) {
|
|
csv += new Date(serverTimelineStats[i].time) + ', ' + serverTimelineStats[i].conn.ca + ', ' + serverTimelineStats[i].conn.cu + ', ' + serverTimelineStats[i].conn.us + ', ' + serverTimelineStats[i].conn.rs + ', ' + (serverTimelineStats[i].conn.am ? serverTimelineStats[i].conn.am : '') + ', ' + serverTimelineStats[i].mem.external + ', ' + serverTimelineStats[i].mem.heapUsed + ', ' + serverTimelineStats[i].mem.heapTotal + ', ' + serverTimelineStats[i].mem.rss + '\r\n';
|
|
}
|
|
}
|
|
saveAs(new Blob([csv], { type: 'application/octet-stream' }), "ServerStats.csv");
|
|
}
|
|
|
|
//
|
|
// My Server Tracing
|
|
//
|
|
|
|
var serverTrace = [];
|
|
var serverTraceSources = [];
|
|
|
|
function displayServerTrace() {
|
|
var x = '', max = parseInt(Q('p41limitdropdown').value);
|
|
if (serverTrace.length > max) { serverTrace.splice(max); }
|
|
for (var i in serverTrace) { x += '<div class=traceEvent>' + EscapeHtml(new Date(serverTrace[i].time).toLocaleTimeString()) + ' - <b>' + EscapeHtml(serverTrace[i].source.toUpperCase()) + '</b>: ' + EscapeHtml(serverTrace[i].args.join(', ')) + '</div>' }
|
|
QH('p41events', x);
|
|
}
|
|
|
|
function clearServerTracing() { serverTrace = []; displayServerTrace(); }
|
|
|
|
function setServerTracing() {
|
|
var x = '';
|
|
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:5px"><b>' + "Core Server" + '</b></div>';
|
|
x += '<div><label><input type=checkbox id=p41c1 ' + ((serverTraceSources.indexOf('cookie') >= 0) ? 'checked' : '') + '>' + "Cookie encoder" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c2 ' + ((serverTraceSources.indexOf('dispatch') >= 0) ? 'checked' : '') + '>' + "Message Dispatcher" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c3 ' + ((serverTraceSources.indexOf('main') >= 0) ? 'checked' : '') + '>' + "Main Server Messages" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c4 ' + ((serverTraceSources.indexOf('peer') >= 0) ? 'checked' : '') + '>' + "MeshCentral Server Peering" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c15 ' + ((serverTraceSources.indexOf('agent') >= 0) ? 'checked' : '') + '>' + "MeshAgent traffic" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c14 ' + ((serverTraceSources.indexOf('agentupdate') >= 0) ? 'checked' : '') + '>' + "MeshAgent update" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c16 ' + ((serverTraceSources.indexOf('cert') >= 0) ? 'checked' : '') + '>' + "Server Certificate" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c17 ' + ((serverTraceSources.indexOf('db') >= 0) ? 'checked' : '') + '>' + "Server Database" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c18 ' + ((serverTraceSources.indexOf('email') >= 0) ? 'checked' : '') + '>' + "Email/SMS Traffic" + '</label></div>';
|
|
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:5px"><b>' + "Web Server" + '</b></div>';
|
|
x += '<div><label><input type=checkbox id=p41c5 ' + ((serverTraceSources.indexOf('web') >= 0) ? 'checked' : '') + '>' + "Web Server" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c6 ' + ((serverTraceSources.indexOf('webrequest') >= 0) ? 'checked' : '') + '>' + "Web Server Requests" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c7 ' + ((serverTraceSources.indexOf('relay') >= 0) ? 'checked' : '') + '>' + "Web Socket Relay" + '</label></div>';
|
|
//x += '<div><label><input type=checkbox id=p41c8 ' + ((serverTraceSources.indexOf('webrelaydata') >= 0) ? 'checked' : '') + '>' + "Traffic Relay 2 Data" + '</label></div>';
|
|
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:5px"><b>' + "Intel AMT" + '</b></div>';
|
|
x += '<div><label><input type=checkbox id=p41c9 ' + ((serverTraceSources.indexOf('webrelay') >= 0) ? 'checked' : '') + '>' + "Connection Relay" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c10 ' + ((serverTraceSources.indexOf('mps') >= 0) ? 'checked' : '') + '>' + "CIRA Server" + '</label></div>';
|
|
x += '<div><label><input type=checkbox id=p41c11 ' + ((serverTraceSources.indexOf('mpscmd') >= 0) ? 'checked' : '') + '>' + "CIRA Server Commands" + '</label></div>';
|
|
//x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:5px"><b>Legacy</b></div>';
|
|
//x += '<div><label><input type=checkbox id=p41c12 ' + ((serverTraceSources.indexOf('swarm') >= 0) ? 'checked' : '') + ">' + "Legacy Swarm Server" + '</label></div>";
|
|
//x += '<div><label><input type=checkbox id=p41c13 ' + ((serverTraceSources.indexOf('swarmcmd') >= 0) ? 'checked' : '') + ">' + "Legacy Swarm Server Commands" + '</label></div>";
|
|
setDialogMode(2, "Server Tracing", 7, setServerTracingEx, x);
|
|
}
|
|
|
|
function setServerTracingEx(b) {
|
|
var sources = [], allsources = ['cookie', 'dispatch', 'main', 'peer', 'web', 'webrequest', 'relay', 'webrelaydata', 'webrelay', 'mps', 'mpscmd', 'swarm', 'swarmcmd', 'agentupdate', 'agent', 'cert', 'db', 'email'];
|
|
if (b == 1) { for (var i = 1; i < 19; i++) { try { if (Q('p41c' + i).checked) { sources.push(allsources[i - 1]); } } catch (ex) { } } }
|
|
meshserver.send({ action: 'traceinfo', traceSources: sources });
|
|
}
|
|
|
|
function p41downloadServerTrace() {
|
|
var csv = "time, source, message" + '\r\n';
|
|
for (var i in serverTrace) { csv += '\"' + new Date(serverTrace[i].time).toLocaleTimeString() + '\",\"' + serverTrace[i].source + '\",\"' + serverTrace[i].args.join(', ') + '\"\r\n'; }
|
|
saveAs(new Blob([csv], { type: 'application/octet-stream' }), "servertrace.csv");
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// POPUP DIALOG
|
|
//
|
|
|
|
// null = Hidden, 1 = Generic Message
|
|
var xxdialogMode;
|
|
var xxdialogFunc;
|
|
var xxdialogButtons;
|
|
var xxdialogTag;
|
|
var xxcurrentView = -1;
|
|
|
|
// Display a dialog box
|
|
// Parameters: Dialog Mode (0 = none), Dialog Title, Buttons (1 = OK, 2 = Cancel, 3 = OK & Cancel), Call back function(0 = Cancel, 1 = OK), Dialog Content (Mode 2 only)
|
|
function setDialogMode(x, y, b, f, c, tag) {
|
|
setSessionActivity();
|
|
QV('uiMenu', false);
|
|
xxdialogMode = x;
|
|
xxdialogFunc = f;
|
|
xxdialogButtons = b;
|
|
xxdialogTag = tag;
|
|
|
|
// Reset dialog size
|
|
QS('dialog').width = QS('dialog').top = QS('dialog').left = QS('dialog').right = QS('dialog').bottom = null;
|
|
|
|
QE('idx_dlgOkButton', true);
|
|
QV('idx_dlgOkButton', b & 1);
|
|
QV('idx_dlgCancelButton', b & 2);
|
|
QV('id_dialogclose', (b & 2) || (b & 8));
|
|
QV('idx_dlgDeleteButton', b & 4);
|
|
QV('idx_dlgButtonBar', b & 7);
|
|
if (y) QH('id_dialogtitle', y);
|
|
for (var i = 1; i < 24; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added
|
|
QV('dialog', x);
|
|
if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } }
|
|
}
|
|
|
|
function dialogclose(x) {
|
|
setSessionActivity();
|
|
var f = xxdialogFunc, b = xxdialogButtons, t = xxdialogTag;
|
|
setDialogMode();
|
|
if (((b & 8) || x) && f) f(x, t);
|
|
}
|
|
|
|
function center() {
|
|
setSessionActivity();
|
|
if (xxcurrentView == 11) { deskAdjust(); }
|
|
else if (xxcurrentView == 10) { masterUpdate(256); }
|
|
else if (xxcurrentView == 1) { masterUpdate(4); }
|
|
}
|
|
function messagebox(t, m) { setSessionActivity(); QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
|
|
function statusbox(t, m) { setSessionActivity(); QH('id_dialogMessage', m); setDialogMode(1, t); }
|
|
|
|
function goBack() {
|
|
setSessionActivity();
|
|
if (xxdialogMode || (goBackStack.length == 0)) return;
|
|
if (fullscreen) { deskToggleFull(); }
|
|
go(goBackStack.pop());
|
|
goBackStack.pop();
|
|
}
|
|
|
|
function go(x, event) {
|
|
if (pluginHandler != null) pluginHandler.callHook('goPageStart', x, event);
|
|
setSessionActivity();
|
|
if (xxdialogMode) return;
|
|
QV('uiMenu', false);
|
|
|
|
// If "shift" is pressed, open a new tab.
|
|
if (event && (event.shiftKey == true) && (x != 15) && ('{{currentNode}}' == '')) {
|
|
// Open the device in a different tab
|
|
if ((x >= 10) && (x <= 19)) {
|
|
if (currentNode) { window.open(window.location.origin + '?gotonode=' + currentNode._id.split('/')[2] + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentNode._id); }
|
|
} else if ((x >= 20) && (x <= 29)) {
|
|
if (currentMesh) { window.open(window.location.origin + '?gotomesh=' + currentMesh._id.split('/')[2] + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentMesh._id); }
|
|
} else if ((x >= 30) && (x <= 39)) {
|
|
if (currentUser) { window.open(window.location.origin + '?gotouser=' + currentUser._id.split('/')[2] + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentUser._id); }
|
|
} else if ((x >= 50) && (x <= 59)) {
|
|
if (currentUserGroup) { window.open(window.location.origin + '?gotougrp=' + currentUserGroup._id.split('/')[2] + '&viewmode=' + x + '&hide=16', 'meshcentral:' + currentUserGroup._id); }
|
|
} else { // if (x < 10))
|
|
window.open(window.location.origin + '?viewmode=' + x + '&hide=0', 'meshcentral:' + x);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If we are going to the same place, do nothing.
|
|
if (xxcurrentView == x) return;
|
|
|
|
// Set the goback stack, if going to top-level view, clear the stack.
|
|
if ((xxcurrentView < 0) || (x < 10)) { goBackStack = []; } else {
|
|
// Do not push into the back stack if we are changing tabs at the same level.
|
|
if ((xxcurrentView == 50) || (Math.floor(xxcurrentView / 10) != Math.floor(x / 10))) { goBackStack.push(xxcurrentView); }
|
|
}
|
|
|
|
// Edit this line when adding a new screen
|
|
for (var i = 0; i < 52; i++) { QV('p' + i, i == x); }
|
|
xxcurrentView = x;
|
|
|
|
// Get out of fullscreen if needed
|
|
if (fullscreen) { deskToggleFull(); }
|
|
|
|
// Change the URL
|
|
if (xxcurrentView > 0) {
|
|
var urlviewmode = '';
|
|
if ((xxcurrentView >= 10) && (xxcurrentView <= 19)) { // Device Link
|
|
if (currentNode != null) { urlviewmode = '?viewmode=' + xxcurrentView + '&gotonode=' + currentNode._id.split('/')[2]; }
|
|
} else if ((xxcurrentView >= 20) && (xxcurrentView <= 29)) { // Device Group Link
|
|
if (currentMesh != null) { urlviewmode = '?viewmode=' + xxcurrentView + '&gotomesh=' + currentMesh._id.split('/')[2]; }
|
|
} else if ((xxcurrentView >= 30) && (xxcurrentView <= 39)) { // User Link
|
|
if (currentUser != null) { urlviewmode = '?viewmode=' + xxcurrentView + '&gotouser=' + currentUser._id.split('/')[2]; }
|
|
} else if ((xxcurrentView >= 51) && (xxcurrentView <= 59)) { // User Group Link
|
|
if ((currentUserGroup != null) && (currentUserGroup._id != null)) { urlviewmode = '?viewmode=' + xxcurrentView + '&gotougrp=' + currentUserGroup._id.split('/')[2]; }
|
|
} else if (xxcurrentView > 1) { urlviewmode = '?viewmode=' + xxcurrentView; }
|
|
for (var i in urlargs) { urlviewmode += (((urlviewmode == '')?'?':'&') + i + '=' + urlargs[i]); }
|
|
try { window.history.replaceState({}, document.title, window.location.pathname + urlviewmode); } catch (ex) { }
|
|
}
|
|
|
|
// Remove top bar selection
|
|
var mainBarItems = ['MainMenuMyDevices', 'MainMenuMyAccount', 'MainMenuMyEvents', 'MainMenuMyFiles', 'MainMenuMyUsers', 'MainMenuMyServer'];
|
|
for (var i in mainBarItems) {
|
|
QC(mainBarItems[i]).remove('fullselect');
|
|
QC(mainBarItems[i]).remove('semiselect');
|
|
}
|
|
|
|
// Remove left bar selection
|
|
var leftBarItems = ['LeftMenuMyDevices', 'LeftMenuMyAccount', 'LeftMenuMyEvents', 'LeftMenuMyFiles', 'LeftMenuMyUsers', 'LeftMenuMyServer'];
|
|
for (var i in leftBarItems) {
|
|
QC(leftBarItems[i]).remove('lbbuttonsel');
|
|
QC(leftBarItems[i]).remove('lbbuttonsel2');
|
|
}
|
|
|
|
// Define class for Menu(s) as fully or semi active.
|
|
var mainMenuActiveClass = (x < 9 ? 'fullselect' : 'semiselect');
|
|
var leftMenuActiveClass = (((x < 9) || (x == 115) || (x == 40) || (x == 41) || (x == 42)) ? 'lbbuttonsel2' : 'lbbuttonsel');
|
|
|
|
var backView = 0;
|
|
if (goBackStack.length > 0) { backView = goBackStack[goBackStack.length - 1]; }
|
|
|
|
// My Devices
|
|
if (x == 1 || (backView == 1) || ((backView == 0) && (x >= 10 && x < 20))) {
|
|
QC('MainMenuMyDevices').add(mainMenuActiveClass);
|
|
QC('LeftMenuMyDevices').add(leftMenuActiveClass);
|
|
} else if (x == 2 || (backView == 2) || ((backView == 0) && (x >= 20 && x < 30))) {
|
|
// My Account
|
|
QC('MainMenuMyAccount').add(mainMenuActiveClass);
|
|
QC('LeftMenuMyAccount').add(leftMenuActiveClass);
|
|
} else if (x == 3) {
|
|
// My Events
|
|
QC('MainMenuMyEvents').add(mainMenuActiveClass);
|
|
QC('LeftMenuMyEvents').add(leftMenuActiveClass);
|
|
} else if (x == 4 || (x >= 30 && x < 40) || (x == 50) || (x == 51)) {
|
|
// My Users
|
|
QC('MainMenuMyUsers').add(mainMenuActiveClass);
|
|
QC('LeftMenuMyUsers').add(leftMenuActiveClass);
|
|
} else if (x == 5) {
|
|
// My Files
|
|
QC('MainMenuMyFiles').add(mainMenuActiveClass);
|
|
QC('LeftMenuMyFiles').add(leftMenuActiveClass);
|
|
} else if ((x == 6) || (x == 115) || (x >= 40 && x < 50)) {
|
|
// My Server
|
|
QC('MainMenuMyServer').add(mainMenuActiveClass);
|
|
QC('LeftMenuMyServer').add(leftMenuActiveClass);
|
|
}
|
|
QV('ServerPlugins', pluginHandler != null);
|
|
|
|
// column_l max-height
|
|
if (webPageStackMenu && (x >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
|
|
|
|
// If we are going to panel 0 in "full screen mode", hide the left bar.
|
|
QV('topbar', x != 0);
|
|
if ((x == 0) && (webPageFullScreen)) { QC('body').add('arg_hide'); }
|
|
|
|
QV('MainSubMenuSpan', (x >= 10) && (x < 20));
|
|
QV('UserDummyMenuSpan', (x == 51) || ((x < 10) && (x != 6) && webPageFullScreen));
|
|
QV('MeshSubMenuSpan', (x >= 20) && (x < 30));
|
|
QV('UserSubMenuSpan', (x >= 30) && (x < 40));
|
|
QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40 || x == 41 || x == 42 || x == 43);
|
|
QV('UsersSubMenuSpan', x == 4 || x == 50);
|
|
var panels = { 4: 'UsersGeneral', 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 17: 'MainDevInfo', 19: 'MainDevPlugins', 20: 'MeshGeneral', 21: 'MeshSummary', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 41: 'ServerTrace', 42: 'ServerPlugins', 50: 'UsersGroups', 115: 'ServerConsole' };
|
|
for (var i in panels) {
|
|
QC(panels[i]).remove('style3x');
|
|
QC(panels[i]).remove('style3sel');
|
|
QC(panels[i]).add((x == i) ? 'style3sel' : 'style3x');
|
|
}
|
|
|
|
// If going to the remote desktop tab, adjust the tab.
|
|
if (x == 11) { deskAdjust(); }
|
|
|
|
// Panel 115 is weird, it's panel 15 for device console but used as a server console.
|
|
if (x == 115) { QV('p15', true); }
|
|
QV('p15uploadCore', x != 115);
|
|
QV('p15BackButton', x != 115);
|
|
if ((x == 15) || (x == 115)) { setupConsole(); }
|
|
|
|
if (x == 1) masterUpdate(4);
|
|
|
|
// Setup web notifications
|
|
if ((x == 2) && Notification) { QV('accountEnableNotificationsSpan', Notification.permission != 'granted'); }
|
|
|
|
// Fetch the server timeline stats if needed
|
|
if ((x == 40) && (serverTimelineStats == null)) { refreshServerTimelineStats(); }
|
|
|
|
// MyServer Plugins
|
|
if (x == 42) { refreshPluginLatest(); }
|
|
|
|
// Update Mesh Summary
|
|
if (x == 21) { p21updateMesh(); }
|
|
|
|
// Update the web page title
|
|
if ((currentNode) && (x >= 10) && (x < 20)) {
|
|
document.title = currentNode.name + ((meshes[currentNode.meshid])?(' - ' + meshes[currentNode.meshid].name):'') + ' - ' + decodeURIComponent('{{{extitle}}}');
|
|
} else {
|
|
document.title = decodeURIComponent('{{{extitle}}}');
|
|
}
|
|
if (pluginHandler != null) pluginHandler.callHook('goPageEnd', x, event);
|
|
|
|
// Some panels must not have scroll bars
|
|
QS('column_l')['overflow'] = ([11,12].indexOf(x)>= 0)?'hidden':null;
|
|
}
|
|
|
|
//
|
|
// Plugin Management
|
|
//
|
|
|
|
function updatePluginList(versInfo) {
|
|
if (pluginHandler == null) return;
|
|
if (Array.isArray(versInfo)) { versInfo.forEach(function(v) { updatePluginList(v); }); }
|
|
QV('pluginNoneNotice', installedPluginList.length == 0);
|
|
if (installedPluginList.length) {
|
|
if (versInfo != null) {
|
|
if (installedPluginList['version_info'] == null) installedPluginList['version_info'] = [];
|
|
installedPluginList['version_info'][versInfo.id] = versInfo;
|
|
}
|
|
var tr = Q('p42tbl').querySelectorAll('.p42tblRow');
|
|
if (tr.length) {
|
|
for (var i in Object.values(tr)) {
|
|
tr[i].parentNode.removeChild(tr[i]);
|
|
}
|
|
}
|
|
var statusMap = {
|
|
0: {
|
|
'text': 'Disabled',
|
|
'color': '858483'
|
|
},
|
|
1: {
|
|
'text': 'Installed',
|
|
'color': '00aa00'
|
|
}
|
|
};
|
|
var statusAvailability = {
|
|
0: {
|
|
'install': 'Install',
|
|
'delete': 'Delete'
|
|
},
|
|
1: {
|
|
'disable': 'Disable',
|
|
'upgrade': 'Upgrade',
|
|
// 'downgrade': 'Downgrade' // disabling until plugins have prior versions available for better testing
|
|
}
|
|
};
|
|
var vers_not_compat = ' [ <span onclick="return setDialogMode(2, \'Compatibility Issue\', 1, null, \'This plugin version is not compatible with your MeshCentral installation, please upgrade MeshCentral first.\');" title="' + "Version incompatible, please upgrade your MeshCentral installation first" + '" style="cursor: pointer; color:red;"> ! </span> ]';
|
|
|
|
var tbl = Q('p42tbl');
|
|
installedPluginList.forEach(function(p){
|
|
var cant_action = [];
|
|
if (p.hasAdminPanel == true && p.status) {
|
|
p.nameHtml = '<a onclick="return goPlugin(\'' + p.shortName + '\', \'' + p.name.replace(/'/g, "\\'") + '\');">' + p.name + '</a>';
|
|
} else {
|
|
p.nameHtml = p.name;
|
|
}
|
|
p.statusText = statusMap[p.status].text;
|
|
p.statusColor = statusMap[p.status].color;
|
|
|
|
if (p.versionHistoryUrl == null) { cant_action.push('downgrade'); }
|
|
if (!p.status) { p.version = ' - '; } // It isn't technically installed, so no version number
|
|
p.upgradeAvail = "Checking...";
|
|
if (installedPluginList['version_info'] != null && installedPluginList['version_info'][p._id] != null) {
|
|
var vin = installedPluginList['version_info'][p._id];
|
|
if (vin.hasUpdate) {
|
|
p.upgradeAvail = '<a title="' + "View Changelog" + '" target="_blank" href="' + vin.changelogUrl + '">' + vin.version + '</a>';
|
|
} else {
|
|
cant_action.push('upgrade');
|
|
if (p.status) p.upgradeAvail = "Up to date";
|
|
else p.upgradeAvail = '<a title="' + "View Changelog" + '" target="_blank" href="' + vin.changelogUrl + '">' + vin.version + '</a>';
|
|
}
|
|
if (!vin.meshCentralCompat) {
|
|
p.upgradeAvail += vers_not_compat;
|
|
cant_action.push('install');
|
|
cant_action.push('upgrade');
|
|
}
|
|
}
|
|
|
|
p.actions = '<select onchange="return pluginAction(this,\'' + p._id + '\');"><option value=""> --</option>';
|
|
var entries = Object.entries(statusAvailability[p.status]);
|
|
for (var k in entries) {
|
|
if (cant_action.indexOf(entries[k][0]) === -1) {
|
|
p.actions += '<option value="' + entries[k][0] + '">' + entries[k][1] + '</option>';
|
|
}
|
|
}
|
|
p.actions += '</select>';
|
|
|
|
let tpl = '<td><img style=margin-top:3px src=images/plugin24.png></td><td class=gradTable1> </td><td class=gradTable2>' + p.nameHtml + '</td><td class=gradTable2>' + p.description + '</td><td class=gradTable2 style=text-align:center><a href="' + p.homepage + '" target="_blank">Home</a></td><td class=gradTable2 style=text-align:center>' + p.version + '</td><td style=text-align:center class="pluginUpgradeAvailable gradTable2">' + p.upgradeAvail + '</td><td class=gradTable2 style="text-align:center;color:#' + p.statusColor + '">' + p.statusText + '</td><td class="pluginAction gradTable2" style=text-align:center>' + p.actions + '</td><td class=gradTable3> </td>';
|
|
let tr = tbl.insertRow(-1);
|
|
tr.innerHTML = tpl;
|
|
tr.classList.add('p42tblRow');
|
|
tr.setAttribute('data-id', p._id);
|
|
tr.setAttribute('id', 'pluginRow-' + p._id);
|
|
});
|
|
} else {
|
|
var tr = Q('p42tbl').querySelectorAll('.p42tblRow');
|
|
for (var i in Object.values(tr)) { tr[i].parentNode.removeChild(tr[i]); }
|
|
}
|
|
if (versInfo == null) refreshPluginLatest();
|
|
}
|
|
|
|
function refreshPluginLatest() {
|
|
if (pluginHandler == null) return;
|
|
meshserver.send({ action: 'pluginLatestCheck' });
|
|
}
|
|
|
|
function distributeCore() {
|
|
if (pluginHandler == null) return;
|
|
meshserver.send({ action: 'distributeCore', nodes: nodes }); // All nodes the user has access to
|
|
QV('pluginRestartNotice', false);
|
|
}
|
|
|
|
function pluginActionEx() {
|
|
if (pluginHandler == null) return;
|
|
var act = Q('lastPluginAct').value, id = Q('lastPluginId').value, pVersUrl = Q('lastPluginVersion').value;
|
|
|
|
switch(act) {
|
|
case 'upgrade':
|
|
case 'install':
|
|
meshserver.send({ 'action': 'installplugin', 'id': id, 'version_only': false });
|
|
break;
|
|
case 'downgrade':
|
|
Q('lastPluginVersion').querySelectorAll('option').forEach(function(opt) {
|
|
if (opt.value == pVersUrl) pVers = opt.text;
|
|
});
|
|
meshserver.send({ 'action': 'installplugin', 'id': id, 'version_only': { 'name': pVers, 'url': pVersUrl }});
|
|
break;
|
|
case 'delete':
|
|
meshserver.send({ 'action': 'removeplugin', 'id': id });
|
|
break;
|
|
case 'disable':
|
|
meshserver.send({ 'action': 'disableplugin', 'id': id });
|
|
break;
|
|
}
|
|
QV('pluginRestartNotice', true);
|
|
}
|
|
|
|
function pluginAction(elem, id) {
|
|
if (pluginHandler == null) return;
|
|
if (elem.value == 'downgrade') {
|
|
meshserver.send({ 'action': 'getpluginversions', 'id': id });
|
|
} else {
|
|
var plugin = null;
|
|
for (var i in installedPluginList) { if (installedPluginList[i]._id == id) { plugin = installedPluginList[i]; } }
|
|
setDialogMode(2, "Plugin Action", 3, pluginActionEx, format("Are you sure you want to {0} the plugin: {1}", elem.value, plugin.name) + '<input id="lastPluginAct" type="hidden" value="' + elem.value + '" /><input id="lastPluginId" type="hidden" value="' + id + '" /><input id="lastPluginVersion" type="hidden" value="" />');
|
|
}
|
|
elem.value = '';
|
|
}
|
|
|
|
function goPlugin(pname, title) {
|
|
if (pluginHandler == null) return;
|
|
if (pname == null) { Q('p43iframe').src = ''; } else { QH('p43title', title); Q('p43iframe').src = '/pluginadmin.ashx?pin=' + pname; go(43); }
|
|
}
|
|
|
|
//
|
|
// Access Control Functions
|
|
// These must match server
|
|
//
|
|
|
|
// Get the right of a user on a given device group
|
|
function GetMeshRights(mesh, userid) {
|
|
if (mesh == null) { return 0; }
|
|
if (userid == null) { userid = userinfo._id; }
|
|
if (typeof mesh == 'string') { mesh = meshes[mesh] }
|
|
if ((mesh == null) || (mesh.links == null)) { return 0; }
|
|
|
|
// Check if super user
|
|
if (userinfo.manageAllDeviceGroups) return 0xFFFFFFFF;
|
|
|
|
// Check device group link permission
|
|
var rights = 0, r = mesh.links[userid];
|
|
if (r != null) {
|
|
if (r.rights == 0xFFFFFFFF) { return 0xFFFFFFFF; } // User has full rights thru a device group link, stop here.
|
|
rights = r.rights;
|
|
}
|
|
|
|
// Check permissions thru user groups
|
|
var user = null;
|
|
if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } }
|
|
if (user != null) {
|
|
for (var i in user.links) {
|
|
if (i.startsWith('ugrp/')) {
|
|
r = mesh.links[i];
|
|
if (r != null) {
|
|
if (r.rights == 0xFFFFFFFF) { return 0xFFFFFFFF; } // User has full rights thru a user group, stop here.
|
|
rights |= r.rights; // TODO: Deal with reverse permissions
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rights;
|
|
}
|
|
|
|
// Returns true if the user can view the given device group
|
|
function IsMeshViewable(mesh, userid) {
|
|
if (mesh == null) { return false; }
|
|
if (userid == null) { userid = userinfo._id; }
|
|
if (typeof mesh == 'string') { mesh = meshes[mesh] }
|
|
if ((mesh == null) || (mesh.links == null)) { return false; }
|
|
if (mesh.links[userid] != null) { return true; } // User has visilibity thru a direct link
|
|
|
|
// Check if user user
|
|
if (userinfo.manageAllDeviceGroups) return true;
|
|
|
|
// Check permissions thru user groups
|
|
var user = null;
|
|
if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } }
|
|
if (user != null) {
|
|
for (var i in user.links) {
|
|
if ((i.startsWith('ugrp/')) && (mesh.links[i] != null)) { return true; } // User has visilibity thru a user group
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Return the user rights for a given node
|
|
function GetNodeRights(node, userid) {
|
|
if (node == null) { return 0; }
|
|
if (userid == null) { userid = userinfo._id; }
|
|
if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } }
|
|
var r = GetMeshRights(node.meshid, userid);
|
|
if (r == 0xFFFFFFFF) return r;
|
|
|
|
// Check direct device rights using device data
|
|
if ((node.links != null) && (node.links[userid] != null)) { r |= node.links[userid].rights; } // TODO: Deal with reverse permissions
|
|
|
|
// Check direct device rights thru user groups
|
|
if ((node.links != null) && (userinfo.links != null)) {
|
|
for (var i in node.links) {
|
|
if (i.startsWith('ugrp/') && (userinfo.links[i] != null) && (node.links[i].rights != null)) { r |= node.links[i].rights; }
|
|
}
|
|
}
|
|
|
|
// Check direct device rights using user data
|
|
/*
|
|
var user = null;
|
|
if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } }
|
|
if ((user != null) && (user.links != null)) {
|
|
var r2 = user.links[node._id];
|
|
if (r2 != null) {
|
|
if (r2.rights == 0xFFFFFFFF) { return 0xFFFFFFFF; } // User has full rights thru a device link, stop here.
|
|
r |= r2.rights; // TODO: Deal with reverse permissions
|
|
}
|
|
}
|
|
*/
|
|
return r;
|
|
}
|
|
|
|
// Return true if the device is visible to the user
|
|
function IsNodeViewable(node, userid) {
|
|
if (node == null) { return false; }
|
|
if (userid == null) { userid = userinfo._id; }
|
|
if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return false; } }
|
|
if (IsMeshViewable(node.meshid, userid)) return true;
|
|
|
|
// Check direct device visibility using device data
|
|
if ((node.links != null) && (node.links[userid] != null)) { return true; }
|
|
|
|
// Check direct device visibility thru user groups
|
|
if ((node.links != null) && (userinfo.links != null)) {
|
|
for (var i in node.links) { if (i.startsWith('ugrp/') && (userinfo.links[i] != null) && (node.links[i].rights != null)) { return true; } }
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Generic methods
|
|
//
|
|
|
|
// Converts string to UTF8 byte array, polyfill for IE.
|
|
// Following method is code from Mozilla: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
|
|
if (typeof TextEncoder === 'undefined') {
|
|
window.TextEncoder=function TextEncoder(){};
|
|
TextEncoder.prototype.encode = function encode(str) {
|
|
'use strict';
|
|
var Len = str.length, resPos = -1;
|
|
var resArr = typeof Uint8Array === 'undefined' ? new Array(Len * 1.5) : new Uint8Array(Len * 3);
|
|
for (var point=0, nextcode=0, i = 0; i !== Len; ) {
|
|
point = str.charCodeAt(i), i += 1;
|
|
if (point >= 0xD800 && point <= 0xDBFF) {
|
|
if (i === Len) { resArr[resPos += 1] = 0xef; resArr[resPos += 1] = 0xbf; resArr[resPos += 1] = 0xbd; break; }
|
|
nextcode = str.charCodeAt(i);
|
|
if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
|
|
point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
|
|
i += 1;
|
|
if (point > 0xffff) { resArr[resPos += 1] = (0x1e<<3) | (point>>>18); resArr[resPos += 1] = (0x2<<6) | ((point>>>12)&0x3f); resArr[resPos += 1] = (0x2<<6) | ((point>>>6)&0x3f); resArr[resPos += 1] = (0x2<<6) | (point&0x3f); continue; }
|
|
} else { resArr[resPos += 1] = 0xef; resArr[resPos += 1] = 0xbf; resArr[resPos += 1] = 0xbd; continue; }
|
|
}
|
|
if (point <= 0x007f) {
|
|
resArr[resPos += 1] = (0x0<<7) | point;
|
|
} else if (point <= 0x07ff) {
|
|
resArr[resPos += 1] = (0x6<<5) | (point>>>6); resArr[resPos += 1] = (0x2<<6) | (point&0x3f);
|
|
} else {
|
|
resArr[resPos += 1] = (0xe<<4) | (point>>>12); resArr[resPos += 1] = (0x2<<6) | ((point>>>6)&0x3f); resArr[resPos += 1] = (0x2<<6) | (point&0x3f);
|
|
}
|
|
}
|
|
if (typeof Uint8Array !== 'undefined') return resArr.subarray(0, resPos + 1);
|
|
resArr.length = resPos + 1;
|
|
return resArr;
|
|
};
|
|
TextEncoder.prototype.toString = function(){return '[object TextEncoder]'};
|
|
try {
|
|
Object.defineProperty(TextEncoder.prototype,'encoding',{
|
|
get:function(){ if(TextEncoder.prototype.isPrototypeOf(this)) return'utf-8'; else throw TypeError('Illegal invocation'); }
|
|
});
|
|
} catch(e) { TextEncoder.prototype.encoding = 'utf-8'; }
|
|
if (typeof Symbol!=='undefined')TextEncoder.prototype[Symbol.toStringTag]='TextEncoder';
|
|
}
|
|
|
|
function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); }
|
|
function putstore(name, val) {
|
|
try {
|
|
if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return;
|
|
if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); }
|
|
} catch (e) { }
|
|
if (name[0] != '_') {
|
|
var s = {};
|
|
try {
|
|
for (var i = 0, len = localStorage.length; i < len; ++i) {
|
|
var k = localStorage.key(i);
|
|
if (k[0] != '_') {
|
|
s[k] = localStorage.getItem(k);
|
|
if ((k != 'desktopsettings') && (typeof s[k] == 'string') && (s[k].length > 64)) { delete s[k]; }
|
|
}
|
|
}
|
|
} catch (ex) {}
|
|
meshserver.send({ action: 'userWebState', state: JSON.stringify(s) });
|
|
}
|
|
}
|
|
function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
|
|
function addLink(x, f) { return '<span tabindex=0 style=cursor:pointer;text-decoration:none onclick=\'' + f + '\' onkeypress=\"if (event.key==\'Enter\') {' + f + '} \">' + x + ' <img class=hoverButton src=images/link5.png></span>'; }
|
|
function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
|
|
function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
|
function addOption(q, t, i) { var option = document.createElement('option'); option.text = t; option.value = i; Q(q).add(option); }
|
|
function passwordcheck(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); }
|
|
function methodcheck(r) { if (r && r != null && r.Body && r.Body.ReturnValueStr != 'SUCCESS') { messagebox("Call Error", r.Header.Method + ': ' + r.Body.ReturnValueStr.replace('_', ' ')); return true; } return false; }
|
|
function TableStart() { return '<table cellpadding=0 cellspacing=0 style=width:100%;border-radius:8px><tr><td width=200px><p><td>'; }
|
|
function TableStart2() { return '<table cellpadding=0 cellspacing=0 style=width:100%;border-radius:8px><tr><td><p><td>'; }
|
|
function TableEntry(n, v) { return '<tr><td><p>' + n + '<td>' + v; }
|
|
function FullTable(x, e) { var r = TableStart(); for (i in x) { if (i && x[i]) r += TableEntry(i, x[i]); } return r + TableEnd(e); }
|
|
function TableEnd(n) { return '<tr><td colspan=2><p>' + (n?n:'') + '</table>'; }
|
|
function AddButton(v, f) { return '<input type=button value="' + v + '" onclick="' + f + '" style=margin:4px>'; }
|
|
function AddButton2(v, f) { return '<input type=button value="' + v + '" onclick="' + f + '">'; }
|
|
function AddRefreshButton(f) { return '<input type=button name=refreshbtn value=Refresh onclick="refreshButtons(false);' + f + '" style=margin:4px ' + (refreshButtonsState==false?'disabled':'') + '>'; }
|
|
function MoreStart() { return '<a href=# style=cursor:pointer;color:blue id=morexxx1 onclick=QV(\"morexxx1\",false);QV(\"morexxx2\",true)>▼ ' + "More" + '</a><div id=morexxx2 style=display:none><br><hr>'; };
|
|
function MoreEnd() { return '<a href=# style=cursor:pointer;color:blue onclick=QV(\"morexxx2\",false);QV(\"morexxx1\",true)>▲ ' + "Less" + '</a></div>'; };
|
|
function getSelectedOptions(sel) { var opts = [], opt; for (var i = 0, len = sel.options.length; i < len; i++) { opt = sel.options[i]; if (opt.selected) { opts.push(opt.value); } } return opts; }
|
|
function getInstance(x, y) { for (var i in x) { if (x[i]['InstanceID'] == y) return x[i]; } return null; }
|
|
function getItem(x, y, z) { for (var i in x) { if (x[i][y] == z) return x[i]; } return null; }
|
|
function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + '-' + g.substring(10, 12) + g.substring(8, 10) + '-' + g.substring(14, 16) + g.substring(12, 14) + '-' + g.substring(16, 20) + '-' + g.substring(20); }
|
|
function getUrlVars() { var j, hash, vars = [], hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); for (var i = 0; i < hashes.length; i++) { j = hashes[i].indexOf('='); if (j > 0) { vars[hashes[i].substring(0, j)] = hashes[i].substring(j + 1, hashes[i].length); } } return vars; }
|
|
//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; }
|
|
//function addHtmlValue(t, v) { return '<div style=height:20px><div style=float:right;width:220px><b>' + v + '</b></div><div>' + t + '</div></div>'; }
|
|
function addHtmlValue(t, v) { return '<table><td style=width:120px>' + t + '<td><b>' + v + '</b></table>'; }
|
|
function addHtmlValue2(t, v) { return '<div><div style=display:inline-block;float:right>' + v + '</div><div style=display:inline-block>' + t + '</div></div>'; }
|
|
function addHtmlValue3(t, v) { return '<div><b>' + t + '</b></div><div style=margin-left:16px>' + v + '</div>'; }
|
|
function parseUriArgs() { var href = window.document.location.href; if (href.endsWith('#')) { href = href.substring(0, href.length - 1); } var name, r = {}, parsedUri = href.split(/[\?&|\=]/); parsedUri.splice(0, 1); for (x in parsedUri) { switch (x % 2) { case 0: { name = decodeURIComponent(parsedUri[x]); break; } case 1: { r[name] = decodeURIComponent(parsedUri[x]); var x = parseInt(r[name]); if (x == r[name]) { r[name] = x; } break; } default: { break; } } } return r; }
|
|
function focusTextBox(x) { setTimeout(function(){ Q(x).selectionStart = Q(x).selectionEnd = 65535; Q(x).focus(); }, 0); }
|
|
function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return emailReg.test(v); } // New version
|
|
function isPrivateIP(a) { return (a.startsWith('10.') || a.startsWith('172.16.') || a.startsWith('192.168.')); }
|
|
function u2fSupported() { return (window.u2f && ((navigator.userAgent.indexOf('Chrome/') > 0) || (navigator.userAgent.indexOf('Firefox/') > 0) || (navigator.userAgent.indexOf('Opera/') > 0) || (navigator.userAgent.indexOf('Safari/') > 0))); }
|
|
function findOne(arr1, arr2) { if ((arr1 == null) || (arr2 == null)) return false; return arr2.some(function (v) { return arr1.indexOf(v) >= 0; }); };
|
|
function copyTextToClip(txt) { function selectElementText(e) { if (document.selection) { var range = document.body.createTextRange(); range.moveToElementText(e); range.select(); } else if (window.getSelection) { var range = document.createRange(); range.selectNode(e); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); } } var e = document.createElement('DIV'); e.textContent = txt; document.body.appendChild(e); selectElementText(e); document.execCommand('copy'); e.remove(); }
|
|
function copyTextToClip2(txt) { function selectElementText(e) { if (document.selection) { var range = document.body.createTextRange(); range.moveToElementText(e); range.select(); } else if (window.getSelection) { var range = document.createRange(); range.selectNode(e); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); } } var e = document.createElement('DIV'); e.textContent = decodeURIComponent(txt); document.body.appendChild(e); selectElementText(e); document.execCommand('copy'); e.remove(); }
|
|
function capitalizeFirstLetter(x) { return x.charAt(0).toUpperCase() + x.slice(1); }
|
|
function printDate(d) { return d.toLocaleDateString(args.locale); }
|
|
function printTime(d) { return d.toLocaleTimeString(args.locale); }
|
|
function printDateTime(d) { return d.toLocaleString(args.locale); }
|
|
function addDetailItem(title, value, state) { return '<table style=width:100%><td>' + nobreak(title) + '<td style=text-align:right>' + value + '</table>'; }
|
|
function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
|
|
function addTextLink(subtext, text, link) { var i = text.toLowerCase().indexOf(subtext.toLowerCase()); if (i == -1) { return text; } return text.substring(0, i) + '<a href=\"' + link + '\">' + subtext + '</a>' + text.substring(i + subtext.length); }
|
|
function getOrderedList(objList, oname) { var r = []; for (var i in objList) { r.push(objList[i]); } r.sort(function(a, b) { var aa = a[oname].toLowerCase(), bb = b[oname].toLowerCase(); if (aa > bb) return 1; if (aa < bb) return -1; return 0; }); return r; }
|
|
function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); }
|
|
function nobreak(x) { return x.split(' ').join(' '); }
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|