add run commands to mobile ui (#6044)
Signed-off-by: si458 <simonsmith5521@gmail.com>
Simon Smith committed
Apr 24, 2024 at 09:09 UTC
56d6527bf5570563357965339440501f901a9030
1 file changed
+217
-1
views/default-mobile.handlebars
+217
-1
@@ -1,4 +1,4 @@
1
-<!DOCTYPE html>
1
+<!DOCTYPE html>
2
<html lang="en" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
3
<head>
4
<meta charset="utf-8">
@@ -674,6 +674,23 @@
674
.night .tagSpan {
675
color: black;
676
}
677
+
678
+ #d3serveraction, #d2serveraction {
679
+ width: 100%;
680
+ background-color: #d3d9d6;
681
+ text-align: left;
682
+ padding: 3px;
683
+ }
684
+
685
+ #d3serverfiles, #d2serverfiles {
686
+ width: 100%;
687
+ height: 150px;
688
+ background-color: white;
689
+ padding: 2px;
690
+ border: 1px solid gray;
691
+ overflow-y: scroll;
692
+ }
693
+
694
</style>
695
</head>
696
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" style="overflow-y:hidden;margin:0;padding:0;border:0;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif">
@@ -927,6 +944,7 @@
944
<span id=DeskChatButton><img src='images/icon-chat.png' onclick=deviceChat(event) height=16 width=16 style=padding-top:5px;cursor:pointer /></span>
945
<span id=DeskToastButton><img src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:5px;cursor:pointer /></span>
946
<span id=DeskOpenWebButton><img src='images/icon-url2.png' onclick=deviceUrlFunction() height=16 width=16 style=padding-top:5px;cursor:pointer /></span>
947
+ <span id=DeskRunButton><img src='images/icon-play.png' onclick=runDeviceCmd() height=16 width=16 style=padding-top:2px;cursor:pointer /></span>
948
<!--<input id=DeskToolsButton type=button value=Tools onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()"> -->
949
</div>
950
<div>
@@ -2570,6 +2588,111 @@
2588
go(20);
2589
}
2590
2591
+ //
2592
+ // FILE SELECTOR, DIALOG 3
2593
+ //
2594
+
2595
+ function d3init() {
2596
+ d3fileoptions = { dialog: 1, filter: 'd3filter', files: 'd3serverfiles', folderup: 'p3FolderUp', currentFolder: 'p3CurrentFolder', func: d3setActions };
2597
+ Q('d3localFile').value = '';
2598
+ Q('d3localFile').accept = Q('d3filter').value;
2599
+ d3modechange();
2600
+ }
2601
+
2602
+ function d3modechange() {
2603
+ var mode = Q('d3uploadMode').value;
2604
+ QV('d3localmode', mode == 1);
2605
+ QV('d3servermode', mode == 2);
2606
+ if (mode == 1) { d3setActions(); } else { d3updatefiles(); }
2607
+ }
2608
+
2609
+ var d3filetreelinkpath;
2610
+ var d3filetreelocation = [];
2611
+ var d3fileoptions = null;
2612
+ function d3updatefiles() {
2613
+ if (d3fileoptions == null) return;
2614
+ if ((d3fileoptions.filter == 'd3filter') && (Q('d3uploadMode').value == 1)) return;
2615
+ var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1, publicPath = null, lastFolderName = '';
2616
+
2617
+ // Navigate to path location, build the paths at the same time
2618
+ var d3filetreelocation2 = [], oldlinkpath = d3filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc');
2619
+ for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedBoxes.push(checkboxes[i].value) }; } // Save all existing checked boxes
2620
+
2621
+ d3filetreelinkpath = '';
2622
+ for (var i in d3filetreelocation) {
2623
+ if ((filetreex.f != null) && (filetreex.f[d3filetreelocation[i]] != null)) {
2624
+ d3filetreelocation2.push(d3filetreelocation[i]);
2625
+ if ((folderdepth == 1)) {
2626
+ var sp = d3filetreelocation[i].split('/');
2627
+ publicPath = window.location + sp[0] + 'files/' + sp[2];
2628
+ if (d3filetreelocation[i] === userinfo._id) { d3filetreelinkpath += 'self'; } else { d3filetreelinkpath += (sp[0] + '/' + sp[2]); }
2629
+ } else {
2630
+ if (d3filetreelinkpath != '') { d3filetreelinkpath += '/' + d3filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + d3filetreelocation[i]; } }
2631
+ }
2632
+ filetreex = filetreex.f[d3filetreelocation[i]];
2633
+ lastFolderName = filetreex.n;
2634
+ folderdepth++;
2635
+ } else {
2636
+ break;
2637
+ }
2638
+ }
2639
+ d3filetreelocation = d3filetreelocation2; // In case we could not go down the full path, we set the new path location here.
2640
+
2641
+ // Sort the files
2642
+ var filetreexx = p5sort_files(filetreex.f);
2643
+
2644
+ // File filter
2645
+ var fileFilter = '';
2646
+ if (d3fileoptions.filter) { fileFilter = Q(d3fileoptions.filter).value };
2647
+
2648
+ // Display all files and folders at this location
2649
+ for (var i in filetreexx) {
2650
+ // Figure out the name and shortname
2651
+ var f = filetreexx[i], name = f.n, shortname;
2652
+
2653
+ // Filter out files
2654
+ if ((f.t == 3) && (fileFilter != '') && (f.nx.toLowerCase().endsWith(fileFilter) == false)) { continue; }
2655
+ if (name.length > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + ("..." + '</span>'); } else { shortname = EscapeHtml(name); }
2656
+
2657
+ // Figure out the size
2658
+ var fsize = '';
2659
+ if (f.s != null) { fsize = getFileSizeStr(f.s); }
2660
+
2661
+ var h = '';
2662
+ if (f.t != 3) {
2663
+ var title = '';
2664
+ h = '<div class=filelist file=999><span style=float:right title="' + title + '"></span><span><div class=fileIcon' + f.t + ' onclick=d3folderset("' + encodeURIComponentEx(f.nx) + '")></div> <a href=# style=cursor:pointer onclick=\'return d3folderset("' + encodeURIComponentEx(f.nx) + '")\'>' + shortname + '</a></span></div>';
2665
+ } else {
2666
+ var link = shortname;
2667
+ //if (f.s > 0) { link = "<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"downloadfile.ashx?link=" + encodeURIComponentEx(filetreelinkpath + '/' + f.nx) + "\">" + shortname + "</a>"; }
2668
+ 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>' + EscapeHtml(fsize) + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
2669
+ }
2670
+
2671
+ if (f.t < 3) { html1 += h; } else { html2 += h; }
2672
+ }
2673
+
2674
+ if (d3fileoptions.currentFolder) { QH(d3fileoptions.currentFolder, lastFolderName); }
2675
+ QH(d3fileoptions.files, html1 + html2);
2676
+ QE(d3fileoptions.folderup, d3filetreelocation.length > 0);
2677
+ if (d3fileoptions.func) { d3fileoptions.func(); }
2678
+ }
2679
+
2680
+ function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); return false; }
2681
+ function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
2682
+ 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; }
2683
+ function d3setActions() {
2684
+ if (d3fileoptions.dialog == 1) {
2685
+ var mode = Q('d3uploadMode').value;
2686
+ if (mode == 1) {
2687
+ QE('idx_dlgOkButton', Q('d3localFile').value.length > 0);
2688
+ } else {
2689
+ QE('idx_dlgOkButton', d3getFileSel().length == 1);
2690
+ }
2691
+ } else if (d3fileoptions.dialog == 2) {
2692
+ QE('idx_dlgOkButton', d3getFileSel().length == 1);
2693
+ }
2694
+ }
2695
+
2696
//
2697
// MY FILES
2698
//
@@ -3957,6 +4080,99 @@
4080
meshserver.send({ action: 'msg', type: 'openUrl', nodeid: currentNode._id, url: Q('d2devurl').value });
4081
}
4082
4083
+ function runDeviceCmd(nodeid) { if (xxdialogMode) return; d2runCommandDialog({ nodeids: [ nodeid ? decodeURIComponent(nodeid) : currentNode._id ] }); }
4084
+
4085
+ function d2runCommandDialog(options) {
4086
+ var wintype = false, linuxtype = false, agenttype = false;
4087
+ for (var i in options.nodeids) {
4088
+ var n = getNodeFromId(options.nodeids[i]);
4089
+ if (n.agent) { if ((GetNodeRights(n) & 24) == 24) { agenttype = true; }
4090
+ if ((n.agent.id > 0) && (n.agent.id < 5)) { wintype = true; } else { linuxtype = true; } }
4091
+ }
4092
+ if ((wintype == true) || (linuxtype == true) || (agenttype == true)) {
4093
+ // Fetch run options
4094
+ var runopt = { type:1, runAs:0, source:1, cmd:'' };
4095
+ try { runopt = JSON.parse(getstore('runopt', runopt)); } catch (ex) {}
4096
+
4097
+ if (options.selectedFile) {
4098
+ var filename = options.selectedFile.name.toLowerCase();
4099
+ console.log('filename', filename);
4100
+ if (filename.endsWith('.bat')) { runopt.type = 1; }
4101
+ if (filename.endsWith('.ps1')) { runopt.type = 2; }
4102
+ if (filename.endsWith('.sh')) { runopt.type = 3; }
4103
+ if (filename.endsWith('.agentconsole')) { runopt.type = 4; }
4104
+ }
4105
+
4106
+ var x = '';
4107
+ if (options.title) { x += options.title + '<br />'; }
4108
+ x += '<select id=d2cmdtype onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px;margin-top:4px>';
4109
+ if (wintype == true) { x += '<option value=1' + ((runopt.type == 1)?' selected':'') + '>' + "Windows Command Prompt" + '</option><option value=2' + ((runopt.type == 2)?' selected':'') + '>' + "Windows PowerShell" + '</option>'; }
4110
+ if (linuxtype == true) { x += '<option value=3' + ((runopt.type == 3)?' selected':'') + '>' + "Linux/BSD/macOS Command Shell" + '</option>'; }
4111
+ if (agenttype == true) { x += '<option value=4' + ((runopt.type == 4)?' selected':'') + '>' + "Agent Console" + '</option>'; } // MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
4112
+ x += '</select>';
4113
+ x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0' + ((runopt.runAs == 0)?' selected':'') + '>' + "Run as agent" + '</option><option value=1' + ((runopt.runAs == 1)?' selected':'') + '>' + "Run as user, agent if no user" + '</option><option value=2' + ((runopt.runAs == 2)?' selected':'') + '>' + "Must run as user" + '</option></select>';
4114
+ if (options.selectedFile == null) {
4115
+ x += '<select id=d2cmdsource onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px><option value=0' + ((runopt.source == 0)?' selected':'') + '>' + "Commands from text box" + '</option><option value=1' + ((runopt.source == 1)?' selected':'') + '>' + "Commands from file" + '</option>';
4116
+ if (userinfo.siteadmin & 8) { x += '<option value=2' + ((runopt.source == 2)?' selected':'') + '>' + "Commands from file on server" + '</option>'; }
4117
+ x += '</select><textarea id=d2runcmd onkeyup=d2runCommandValidate() style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll>' + (runopt.cmd ? EscapeHtml(decodeURIComponent(runopt.cmd)) : '') + '</textarea>';
4118
+ x += '<div id=d2runfile style=display:none><input id=d2runfileex type=file onchange=d2runCommandValidate() id=d2localFile name=files onchange=d2runCommandValidate() /></div>';
4119
+ if (userinfo.siteadmin & 8) { x += '<div id=d2runsfile style=display:none><div id=d2serveraction valign=bottom><input type=button id=p2FolderUp disabled="disabled" onclick=d3folderup() value="Up" /> <span id=p2CurrentFolder></span></div><div id=d2serverfiles></div></div>'; }
4120
+ }
4121
+ setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x, options);
4122
+ if (options.selectedFile == null) {
4123
+ Q('d2runcmd').focus();
4124
+ if (userinfo.siteadmin & 8) { d3fileoptions = { dialog: 2, files: 'd2serverfiles', folderup: 'p2FolderUp', currentFolder: 'p2CurrentFolder', func: null }; d3updatefiles(); } // Update the server files
4125
+ }
4126
+ d2runCommandValidate();
4127
+ }
4128
+ }
4129
+
4130
+ function d2runCommandValidate() {
4131
+ QV('d2cmduser', Q('d2cmdtype').value < 4);
4132
+ if (xxdialogTag.selectedFile == null) {
4133
+ QV('d2runcmd', Q('d2cmdsource').value == 0);
4134
+ QV('d2runfile', Q('d2cmdsource').value == 1);
4135
+ QV('d2runsfile', Q('d2cmdsource').value == 2);
4136
+ var ok = false;
4137
+ if (Q('d2cmdsource').value == 0) { if (Q('d2runcmd').value.length > 0) { ok = true; } } // From text box
4138
+ if (Q('d2cmdsource').value == 1) { if (Q('d2runfileex').files.length == 1) { ok = true; } } // From file
4139
+ if (Q('d2cmdsource').value == 2) { ok = false; } // From server file
4140
+ QE('idx_dlgOkButton', ok);
4141
+ } else {
4142
+ QE('idx_dlgOkButton', true);
4143
+ }
4144
+ }
4145
+
4146
+ function d2groupActionFunctionRunCommands(b, options) {
4147
+ var type = 3;
4148
+ try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { }
4149
+ if (options.selectedFile == null) { putstore('runopt', JSON.stringify({ type: type, runAs: parseInt(Q('d2cmduser').value), source: parseInt(Q('d2cmdsource').value), cmd: encodeURIComponent(Q('d2runcmd').value) })); } // Save run options
4150
+ var cmd = { action: 'runcommands', nodeids: options.nodeids, type: type, runAsUser: parseInt(Q('d2cmduser').value) };
4151
+ if (options.selectedFile) {
4152
+ // Drag & drop file
4153
+ var reader = new FileReader();
4154
+ reader.onload = function (e) { cmd.cmds = e.target.result; meshserver.send(cmd); if (options.func) { options.func(); } }
4155
+ reader.readAsText(options.selectedFile);
4156
+ } else if (Q('d2cmdsource').value == 0) {
4157
+ // From text box
4158
+ cmd.cmds = Q('d2runcmd').value;
4159
+ meshserver.send(cmd);
4160
+ if (options.func) { options.func(); }
4161
+ } else if (Q('d2cmdsource').value == 1) {
4162
+ // From file
4163
+ var reader = new FileReader();
4164
+ reader.onload = function (e) { cmd.cmds = e.target.result; meshserver.send(cmd); if (options.func) { options.func(); } }
4165
+ reader.readAsText(Q('d2runfileex').files[0]);
4166
+ } else if (Q('d2cmdsource').value == 2) {
4167
+ // From server file
4168
+ var files = d3getFileSel();
4169
+ if (files.length != 1) return;
4170
+ cmd.cmdpath = d3filetreelocation.join('/') + '/' + files[0];
4171
+ meshserver.send(cmd);
4172
+ if (options.func) { options.func(); }
4173
+ }
4174
+ }
4175
+
4176
// Look to see if we need to update the device timeline
4177
function updateDeviceTimeline() {
4178
if ((meshserver.State != 2) || (powerTimelineNode == null) || (powerTimelineUpdate == null) || (currentNode == null) || (currentNode.mtype == 3)) return;