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>&nbsp;
945 <span id=DeskToastButton><img src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:5px;cursor:pointer /></span>&nbsp;
946 <span id=DeskOpenWebButton><img src='images/icon-url2.png' onclick=deviceUrlFunction() height=16 width=16 style=padding-top:5px;cursor:pointer /></span>&nbsp;
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()">&nbsp;-->
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>&nbsp;<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 + '">&nbsp;<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" />&nbsp;<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;