mirror of
				https://github.com/Ylianst/MeshCentral.git
				synced 2025-03-09 15:40:18 +00:00 
			
		
		
		
	Changed how stript-task will be integrated into MeshCentral, added run button to device general tab.
This commit is contained in:
		
							parent
							
								
									1b67b84369
								
							
						
					
					
						commit
						44af3a2408
					
				
					 10 changed files with 44 additions and 2550 deletions
				
			
		| 
						 | 
				
			
			@ -708,7 +708,6 @@ db = require('SimpleDataStore').Shared();
 | 
			
		|||
sha = require('SHA256Stream');
 | 
			
		||||
mesh = require('MeshAgent');
 | 
			
		||||
childProcess = require('child_process');
 | 
			
		||||
try { scriptTask = require('script-task').CreateScriptTask(mesh); } catch (ex) { }
 | 
			
		||||
 | 
			
		||||
if (mesh.hasKVM == 1) {   // if the agent is compiled with KVM support
 | 
			
		||||
    // Check if this computer supports a desktop
 | 
			
		||||
| 
						 | 
				
			
			@ -1556,10 +1555,6 @@ function handleServerCommand(data) {
 | 
			
		|||
                try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (ex) { throw ex; }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case 'task': {
 | 
			
		||||
                if (scriptTask) { scriptTask.consoleAction(data, data.rights, data.sessionid, false); }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case 'coredump':
 | 
			
		||||
                // Set the current agent coredump situation.s
 | 
			
		||||
                if (data.value === true) {
 | 
			
		||||
| 
						 | 
				
			
			@ -4565,11 +4560,6 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
 | 
			
		|||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case 'task': {
 | 
			
		||||
                if (!scriptTask) { response = "Tasks are not supported on this agent"; }
 | 
			
		||||
                else { response = scriptTask.consoleAction(args, rights, sessionid, true); }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case 'plugin': {
 | 
			
		||||
                if (typeof args['_'][0] == 'string') {
 | 
			
		||||
                    try {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,416 +0,0 @@
 | 
			
		|||
/** 
 | 
			
		||||
* @description MeshCentral Script-Task
 | 
			
		||||
* @author Ryan Blenis
 | 
			
		||||
* @copyright 
 | 
			
		||||
* @license Apache-2.0
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
'use strict';
 | 
			
		||||
function CreateScriptTask(parent) {
 | 
			
		||||
    var obj = {};
 | 
			
		||||
    var db = require('SimpleDataStore').Shared();
 | 
			
		||||
    var pendingDownload = [];
 | 
			
		||||
    var debugFlag = false;
 | 
			
		||||
    var runningJobs = [];
 | 
			
		||||
    var runningJobPIDs = {};
 | 
			
		||||
    
 | 
			
		||||
    function dbg(str) {
 | 
			
		||||
        if (debugFlag !== true) return;
 | 
			
		||||
        var fs = require('fs');
 | 
			
		||||
        var logStream = fs.createWriteStream('scripttask.txt', { 'flags': 'a' });
 | 
			
		||||
        // use {'flags': 'a'} to append and {'flags': 'w'} to erase and write a new file
 | 
			
		||||
        logStream.write('\n' + new Date().toLocaleString() + ': ' + str);
 | 
			
		||||
        logStream.end('\n');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function removeFromArray(arr, from, to) {
 | 
			
		||||
        var rest = arr.slice((to || from) + 1 || arr.length);
 | 
			
		||||
        arr.length = from < 0 ? arr.length + from : from;
 | 
			
		||||
        return arr.push.apply(arr, rest);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    obj.consoleAction = function(args, rights, sessionid, interactive) {
 | 
			
		||||
        //sendConsoleText('task: ' + JSON.stringify(args), sessionid); // Debug
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        if (typeof args['_'] == 'undefined') {
 | 
			
		||||
          args['_'] = [];
 | 
			
		||||
          args['_'][1] = args.pluginaction; // TODO
 | 
			
		||||
          args['_'][2] = null;
 | 
			
		||||
          args['_'][3] = null;
 | 
			
		||||
          args['_'][4] = null;
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        var fnname = args['_'][0];
 | 
			
		||||
        if (fnname == null) { return "Valid task commands are: trigger, cache, clear, clearCache, debug, list"; }
 | 
			
		||||
 | 
			
		||||
        switch (fnname.toLowerCase()) {
 | 
			
		||||
            case 'trigger': {
 | 
			
		||||
                var jObj = {
 | 
			
		||||
                    jobId: args.jobId,
 | 
			
		||||
                    scriptId: args.scriptId,
 | 
			
		||||
                    replaceVars: args.replaceVars,
 | 
			
		||||
                    scriptHash: args.scriptHash,
 | 
			
		||||
                    dispatchTime: args.dispatchTime
 | 
			
		||||
                };
 | 
			
		||||
                //dbg('jObj args is ' + JSON.stringify(jObj));
 | 
			
		||||
                var sObj = getScriptFromCache(jObj.scriptId);
 | 
			
		||||
                //dbg('sobj = ' + JSON.stringify(sObj) + ', shash = ' + jObj.scriptHash);
 | 
			
		||||
                if ((sObj == null) || (sObj.contentHash != jObj.scriptHash)) {
 | 
			
		||||
                    // get from the server, then run
 | 
			
		||||
                    //dbg('Getting and caching script '+ jObj.scriptId);
 | 
			
		||||
                    parent.SendCommand({ action: 'script-task', subaction: 'getScript', scriptId: jObj.scriptId, sessionid: sessionid, tag: 'console' });
 | 
			
		||||
                    pendingDownload.push(jObj);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // ready to run
 | 
			
		||||
                    runScript(sObj, jObj, sessionid);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case 'cache': {
 | 
			
		||||
                var sObj = args.script;
 | 
			
		||||
                cacheScript(sObj);
 | 
			
		||||
                var setRun = [];
 | 
			
		||||
                if (pendingDownload.length) {
 | 
			
		||||
                    pendingDownload.forEach(function (pd, k) {
 | 
			
		||||
                        if ((pd.scriptId == sObj._id) && (pd.scriptHash == sObj.contentHash)) {
 | 
			
		||||
                            if (setRun.indexOf(pd) === -1) { runScript(sObj, pd, sessionid); setRun.push(pd); }
 | 
			
		||||
                            removeFromArray(pendingDownload, k);
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case 'clear': {
 | 
			
		||||
                clearCache();
 | 
			
		||||
                parent.SendCommand({ action: 'script-task', subaction: 'clearAllPendingTasks', sessionid: sessionid, tag: 'console' });
 | 
			
		||||
                return "Cache cleared. All pending tasks cleared.";
 | 
			
		||||
            }
 | 
			
		||||
            case 'clearcache': {
 | 
			
		||||
                clearCache();
 | 
			
		||||
                return "The script cache has been cleared";
 | 
			
		||||
            }
 | 
			
		||||
            case 'debug': {
 | 
			
		||||
                debugFlag = (debugFlag) ? false : true;
 | 
			
		||||
                var str = (debugFlag) ? 'on' : 'off';
 | 
			
		||||
                return 'Debugging is now ' + str;
 | 
			
		||||
            }
 | 
			
		||||
            case 'list': {
 | 
			
		||||
                var ret = '';
 | 
			
		||||
                if (pendingDownload.length == 0) return "No tasks pending script download";
 | 
			
		||||
                pendingDownload.forEach(function (pd, k) { ret += 'Task ' + k + ': ' + 'TaskID: ' + pd.jobId + ' ScriptID: ' + pd.scriptId + '\r\n'; });
 | 
			
		||||
                return ret;
 | 
			
		||||
            }
 | 
			
		||||
            default: {
 | 
			
		||||
                dbg('Unknown action: ' + fnname + ' with data ' + JSON.stringify(args));
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function finalizeJob(job, retVal, errVal, sessionid) {
 | 
			
		||||
        if (errVal != null && errVal.stack != null) errVal = errVal.stack;
 | 
			
		||||
        removeFromArray(runningJobs, runningJobs.indexOf(job.jobId));
 | 
			
		||||
        if (typeof runningJobPIDs[job.jobId] != 'undefined') delete runningJobPIDs[job.jobId];
 | 
			
		||||
        parent.SendCommand({
 | 
			
		||||
            action: 'script-task',
 | 
			
		||||
            subaction: 'taskComplete',
 | 
			
		||||
            jobId: job.jobId,
 | 
			
		||||
            scriptId: job.scriptId,
 | 
			
		||||
            retVal: retVal,
 | 
			
		||||
            errVal: errVal,
 | 
			
		||||
            dispatchTime: job.dispatchTime, // include original run time (long running tasks could have tried a re-send)
 | 
			
		||||
            sessionid: sessionid,
 | 
			
		||||
            tag: 'console'
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //@TODO Test powershell on *nix devices with and without powershell installed
 | 
			
		||||
    function runPowerShell(sObj, jObj, sessionid) {
 | 
			
		||||
        if (process.platform != 'win32') return runPowerShellNonWin(sObj, jObj);
 | 
			
		||||
        const fs = require('fs');
 | 
			
		||||
        var rand = Math.random().toString(32).replace('0.', '');
 | 
			
		||||
 | 
			
		||||
        var oName = 'st' + rand + '.txt';
 | 
			
		||||
        var pName = 'st' + rand + '.ps1';
 | 
			
		||||
        var pwshout = '', pwsherr = '', cancontinue = false;
 | 
			
		||||
        try {
 | 
			
		||||
            fs.writeFileSync(pName, sObj.content);
 | 
			
		||||
            var outstr = '', errstr = '';
 | 
			
		||||
            var child = require('child_process').execFile(process.env['windir'] + '\\system32\\WindowsPowerShell\\v1.0\\powershell.exe', ['-NoLogo']);
 | 
			
		||||
            child.stderr.on('data', function (chunk) { errstr += chunk; });
 | 
			
		||||
            child.stdout.on('data', function (chunk) { });
 | 
			
		||||
            runningJobPIDs[jObj.jobId] = child.pid;
 | 
			
		||||
            child.stdin.write('.\\' + pName + ' | Out-File ' + oName + ' -Encoding UTF8\r\n');
 | 
			
		||||
            child.on('exit', function (procRetVal, procRetSignal) {
 | 
			
		||||
                dbg('Exiting with ' + procRetVal + ', Signal: ' + procRetSignal);
 | 
			
		||||
                if (errstr != '') {
 | 
			
		||||
                    finalizeJob(jObj, null, errstr, sessionid);
 | 
			
		||||
                    try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (procRetVal == 1) {
 | 
			
		||||
                    finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
 | 
			
		||||
                    try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                try { outstr = fs.readFileSync(oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
 | 
			
		||||
                if (outstr) {
 | 
			
		||||
                    //outstr = outstr.replace(/[^\x20-\x7E]/g, ''); 
 | 
			
		||||
                    try { outstr = outstr.trim(); } catch (ex) { }
 | 
			
		||||
                } else {
 | 
			
		||||
                    outstr = (procRetVal) ? 'Failure' : 'Success';
 | 
			
		||||
                }
 | 
			
		||||
                dbg('Output is: ' + outstr);
 | 
			
		||||
                finalizeJob(jObj, outstr, null, sessionid);
 | 
			
		||||
                try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { }
 | 
			
		||||
            });
 | 
			
		||||
            child.stdin.write('exit\r\n');
 | 
			
		||||
            //child.waitExit(); // this was causing the event loop to stall on long-running scripts, switched to '.on exit'
 | 
			
		||||
 | 
			
		||||
        } catch (ex) {
 | 
			
		||||
            dbg('Error block was (PowerShell): ' + ex);
 | 
			
		||||
            finalizeJob(jObj, null, ex, sessionid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function runPowerShellNonWin(sObj, jObj, sessionid) {
 | 
			
		||||
        const fs = require('fs');
 | 
			
		||||
        var rand = Math.random().toString(32).replace('0.', '');
 | 
			
		||||
 | 
			
		||||
        var path = '';
 | 
			
		||||
        var pathTests = ['/usr/local/mesh', '/tmp', '/usr/local/mesh_services/meshagent', '/var/tmp'];
 | 
			
		||||
        pathTests.forEach(function (p) { if (path == '' && fs.existsSync(p)) { path = p; } });
 | 
			
		||||
        dbg('Path chosen is: ' + path);
 | 
			
		||||
        path = path + '/';
 | 
			
		||||
 | 
			
		||||
        var oName = 'st' + rand + '.txt';
 | 
			
		||||
        var pName = 'st' + rand + '.ps1';
 | 
			
		||||
        var pwshout = '', pwsherr = '', cancontinue = false;
 | 
			
		||||
        try {
 | 
			
		||||
            var childp = require('child_process').execFile('/bin/sh', ['sh']);
 | 
			
		||||
            childp.stderr.on('data', function (chunk) { pwsherr += chunk; });
 | 
			
		||||
            childp.stdout.on('data', function (chunk) { pwshout += chunk; });
 | 
			
		||||
            childp.stdin.write('which pwsh' + '\n');
 | 
			
		||||
            childp.stdin.write('exit\n');
 | 
			
		||||
            childp.waitExit();
 | 
			
		||||
        } catch (ex) { finalizeJob(jObj, null, "Couldn't determine pwsh in env: " + ex, sessionid); }
 | 
			
		||||
        if (pwsherr != '') { finalizeJob(jObj, null, "PowerShell env determination error: " + pwsherr, sessionid); return; }
 | 
			
		||||
        if (pwshout.trim() != '') { cancontinue = true; }
 | 
			
		||||
        if (cancontinue === false) { finalizeJob(jObj, null, "PowerShell is not installed", sessionid); return; }
 | 
			
		||||
        try {
 | 
			
		||||
            fs.writeFileSync(path + pName, '#!' + pwshout + '\n' + sObj.content.split('\r\n').join('\n').split('\r').join('\n'));
 | 
			
		||||
            var outstr = '', errstr = '';
 | 
			
		||||
            var child = require('child_process').execFile('/bin/sh', ['sh']);
 | 
			
		||||
            child.stderr.on('data', function (chunk) { errstr += chunk; });
 | 
			
		||||
            child.stdout.on('data', function (chunk) { });
 | 
			
		||||
            runningJobPIDs[jObj.jobId] = child.pid;
 | 
			
		||||
 | 
			
		||||
            child.stdin.write('cd ' + path + '\n');
 | 
			
		||||
            child.stdin.write('chmod a+x ' + pName + '\n');
 | 
			
		||||
            child.stdin.write('./' + pName + ' > ' + oName + '\n');
 | 
			
		||||
            child.on('exit', function (procRetVal, procRetSignal) {
 | 
			
		||||
                if (errstr != '') {
 | 
			
		||||
                    finalizeJob(jObj, null, errstr, sessionid);
 | 
			
		||||
                    try {
 | 
			
		||||
                        fs.unlinkSync(path + oName);
 | 
			
		||||
                        fs.unlinkSync(path + pName);
 | 
			
		||||
                    } catch (ex) { dbg('Could not unlink files, error was: ' + ex + ' for path ' + path); }
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (procRetVal == 1) {
 | 
			
		||||
                    finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
 | 
			
		||||
                    try {
 | 
			
		||||
                        fs.unlinkSync(path + oName);
 | 
			
		||||
                        fs.unlinkSync(path + pName);
 | 
			
		||||
                    } catch (ex) { dbg('Could not unlink files1, error was: ' + ex + ' for path ' + path); }
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                try { outstr = fs.readFileSync(path + oName, 'utf8').toString(); } catch (es) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
 | 
			
		||||
                if (outstr) {
 | 
			
		||||
                    //outstr = outstr.replace(/[^\x20-\x7E]/g, ''); 
 | 
			
		||||
                    try { outstr = outstr.trim(); } catch (ex) { }
 | 
			
		||||
                } else {
 | 
			
		||||
                    outstr = (procRetVal) ? 'Failure' : 'Success';
 | 
			
		||||
                }
 | 
			
		||||
                dbg('Output is: ' + outstr);
 | 
			
		||||
                finalizeJob(jObj, outstr, null, sessionid);
 | 
			
		||||
                try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files2, error was: ' + ex + ' for path ' + path); }
 | 
			
		||||
            });
 | 
			
		||||
            child.stdin.write('exit\n');
 | 
			
		||||
        } catch (ex) {
 | 
			
		||||
            dbg('Error block was (PowerShellNonWin): ' + ex);
 | 
			
		||||
            finalizeJob(jObj, null, ex, sessionid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function runBat(sObj, jObj, sessionid) {
 | 
			
		||||
        if (process.platform != 'win32') { finalizeJob(jObj, null, "Platform not supported.", sessionid); return; }
 | 
			
		||||
        const fs = require('fs');
 | 
			
		||||
        var rand = Math.random().toString(32).replace('0.', '');
 | 
			
		||||
        var oName = 'st' + rand + '.txt';
 | 
			
		||||
        var pName = 'st' + rand + '.bat';
 | 
			
		||||
        try {
 | 
			
		||||
            fs.writeFileSync(pName, sObj.content);
 | 
			
		||||
            var outstr = '', errstr = '';
 | 
			
		||||
            var child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe');
 | 
			
		||||
            child.stderr.on('data', function (chunk) { errstr += chunk; });
 | 
			
		||||
            child.stdout.on('data', function (chunk) { });
 | 
			
		||||
            runningJobPIDs[jObj.jobId] = child.pid;
 | 
			
		||||
            child.stdin.write(pName + ' > ' + oName + '\r\n');
 | 
			
		||||
            child.stdin.write('exit\r\n');
 | 
			
		||||
 | 
			
		||||
            child.on('exit', function (procRetVal, procRetSignal) {
 | 
			
		||||
                if (errstr != '') {
 | 
			
		||||
                    try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
 | 
			
		||||
                    finalizeJob(jObj, null, errstr, sessionid);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (procRetVal == 1) {
 | 
			
		||||
                    try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
 | 
			
		||||
                    finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                try { outstr = fs.readFileSync(oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
 | 
			
		||||
                if (outstr) {
 | 
			
		||||
                    //outstr = outstr.replace(/[^\x20-\x7E]/g, ''); 
 | 
			
		||||
                    try { outstr = outstr.trim(); } catch (ex) { }
 | 
			
		||||
                } else {
 | 
			
		||||
                    outstr = (procRetVal) ? 'Failure' : 'Success';
 | 
			
		||||
                }
 | 
			
		||||
                dbg('Output is: ' + outstr);
 | 
			
		||||
                try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
 | 
			
		||||
                finalizeJob(jObj, outstr, null, sessionid);
 | 
			
		||||
            });
 | 
			
		||||
        } catch (ex) {
 | 
			
		||||
            dbg('Error block was (BAT): ' + ex);
 | 
			
		||||
            finalizeJob(jObj, null, ex, sessionid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function runBash(sObj, jObj, sessionid) {
 | 
			
		||||
        if (process.platform == 'win32') { finalizeJob(jObj, null, "Platform not supported.", sessionid); return; }
 | 
			
		||||
        //dbg('proc is ' + JSON.stringify(process));
 | 
			
		||||
        const fs = require('fs');
 | 
			
		||||
        var path = '';
 | 
			
		||||
        var pathTests = ['/usr/local/mesh', '/tmp', '/usr/local/mesh_services/meshagent', '/var/tmp'];
 | 
			
		||||
        pathTests.forEach(function (p) {
 | 
			
		||||
            if (path == '' && fs.existsSync(p)) { path = p; }
 | 
			
		||||
        });
 | 
			
		||||
        dbg('Path chosen is: ' + path);
 | 
			
		||||
        path = path + '/';
 | 
			
		||||
        //var child = require('child_process');
 | 
			
		||||
        //child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
 | 
			
		||||
 | 
			
		||||
        var rand = Math.random().toString(32).replace('0.', '');
 | 
			
		||||
        var oName = 'st' + rand + '.txt';
 | 
			
		||||
        var pName = 'st' + rand + '.sh';
 | 
			
		||||
        try {
 | 
			
		||||
            fs.writeFileSync(path + pName, sObj.content);
 | 
			
		||||
            var outstr = '', errstr = '';
 | 
			
		||||
            var child = require('child_process').execFile('/bin/sh', ['sh']);
 | 
			
		||||
            child.stderr.on('data', function (chunk) { errstr += chunk; });
 | 
			
		||||
            child.stdout.on('data', function (chunk) { });
 | 
			
		||||
            runningJobPIDs[jObj.jobId] = child.pid;
 | 
			
		||||
            child.stdin.write('cd ' + path + '\n');
 | 
			
		||||
            child.stdin.write('chmod a+x ' + pName + '\n');
 | 
			
		||||
            child.stdin.write('./' + pName + ' > ' + oName + '\n');
 | 
			
		||||
            child.stdin.write('exit\n');
 | 
			
		||||
 | 
			
		||||
            child.on('exit', function (procRetVal, procRetSignal) {
 | 
			
		||||
                if (errstr != '') {
 | 
			
		||||
                    try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex + ' for path ' + path); }
 | 
			
		||||
                    finalizeJob(jObj, null, errstr, sessionid);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (procRetVal == 1) {
 | 
			
		||||
                    try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files1, error was: ' + ex + ' for path ' + path); }
 | 
			
		||||
                    finalizeJob(jObj, null, "Process terminated unexpectedly.", sessionid);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                try { outstr = fs.readFileSync(path + oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
 | 
			
		||||
                if (outstr) {
 | 
			
		||||
                    //outstr = outstr.replace(/[^\x20-\x7E]/g, ''); 
 | 
			
		||||
                    try { outstr = outstr.trim(); } catch (ex) { }
 | 
			
		||||
                } else {
 | 
			
		||||
                    outstr = (procRetVal) ? 'Failure' : 'Success';
 | 
			
		||||
                }
 | 
			
		||||
                dbg('Output is: ' + outstr);
 | 
			
		||||
                try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files2, error was: ' + ex + ' for path ' + path); }
 | 
			
		||||
                finalizeJob(jObj, outstr, null, sessionid);
 | 
			
		||||
            });
 | 
			
		||||
        } catch (ex) {
 | 
			
		||||
            dbg('Error block was (bash): ' + ex);
 | 
			
		||||
            finalizeJob(jObj, null, ex, sessionid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function jobIsRunning(jObj) {
 | 
			
		||||
        if (runningJobs.indexOf(jObj.jobId) === -1) return false;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function runScript(sObj, jObj, sessionid) {
 | 
			
		||||
        // get current processes and clean running jobs if they are no longer running (computer fell asleep, user caused process to stop, etc.)
 | 
			
		||||
        if (process.platform != 'linux' && runningJobs.length) { // linux throws errors here in the meshagent for some reason
 | 
			
		||||
            require('process-manager').getProcesses(function (plist) {
 | 
			
		||||
                dbg('Got process list');
 | 
			
		||||
                dbg('There are currently ' + runningJobs.length + ' running jobs.');
 | 
			
		||||
                if (runningJobs.length) {
 | 
			
		||||
                    runningJobs.forEach(function (jobId, idx) {
 | 
			
		||||
                        dbg('Checking for running job: ' + jobId + ' with PID ' + runningJobPIDs[jobId]);
 | 
			
		||||
                        if (typeof plist[runningJobPIDs[jobId]] == 'undefined' || typeof plist[runningJobPIDs[jobId]].cmd != 'string') {
 | 
			
		||||
                            dbg('Found job with no process. Removing running status.');
 | 
			
		||||
                            delete runningJobPIDs[jobId];
 | 
			
		||||
                            removeFromArray(runningJobs, runningJobs.indexOf(idx));
 | 
			
		||||
                            //dbg('RunningJobs: ' + JSON.stringify(runningJobs));
 | 
			
		||||
                            //dbg('RunningJobsPIDs: ' + JSON.stringify(runningJobPIDs));
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if (jobIsRunning(jObj)) { dbg('Job already running job id [' + jObj.jobId + ']. Skipping.'); return; }
 | 
			
		||||
        if (jObj.replaceVars != null) {
 | 
			
		||||
            Object.getOwnPropertyNames(jObj.replaceVars).forEach(function (key) {
 | 
			
		||||
                var val = jObj.replaceVars[key];
 | 
			
		||||
                sObj.content = sObj.content.replace(new RegExp('#' + key + '#', 'g'), val);
 | 
			
		||||
                dbg('replacing var ' + key + ' with ' + val);
 | 
			
		||||
            });
 | 
			
		||||
            sObj.content = sObj.content.replace(new RegExp('#(.*?)#', 'g'), 'VAR_NOT_FOUND');
 | 
			
		||||
        }
 | 
			
		||||
        runningJobs.push(jObj.jobId);
 | 
			
		||||
        dbg('Running Script ' + sObj._id);
 | 
			
		||||
        switch (sObj.filetype) {
 | 
			
		||||
            case 'ps1': runPowerShell(sObj, jObj, sessionid); break;
 | 
			
		||||
            case 'bat': runBat(sObj, jObj, sessionid); break;
 | 
			
		||||
            case 'bash': runBash(sObj, jObj, sessionid); break;
 | 
			
		||||
            default: dbg('Unknown filetype: ' + sObj.filetype); break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getScriptFromCache(id) {
 | 
			
		||||
        var script = db.Get('scriptTask_script_' + id);
 | 
			
		||||
        if (script == '' || script == null) return null;
 | 
			
		||||
        try { script = JSON.parse(script); } catch (ex) { return null; }
 | 
			
		||||
        return script;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function cacheScript(sObj) {
 | 
			
		||||
        db.Put('scriptTask_script_' + sObj._id, sObj);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function clearCache() {
 | 
			
		||||
        db.Keys.forEach(function (k) { if (k.indexOf('scriptTask_script_') === 0) { db.Put(k, null); db.Delete(k); } });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sendConsoleText(text, sessionid) {
 | 
			
		||||
        if (typeof text == 'object') { text = JSON.stringify(text); }
 | 
			
		||||
        parent.SendCommand({ action: 'msg', type: 'console', value: 'XXX: ' + text, sessionid: sessionid });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = { CreateScriptTask: CreateScriptTask };
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								public/tail.datetime/tail.datetime.min.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/tail.datetime/tail.datetime.min.js
									
										
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										712
									
								
								taskmanager.js
									
										
									
									
									
								
							
							
						
						
									
										712
									
								
								taskmanager.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,714 +1,20 @@
 | 
			
		|||
/**
 | 
			
		||||
* @description MeshCentral ScriptTask
 | 
			
		||||
* @author Ryan Blenis
 | 
			
		||||
* @copyright 
 | 
			
		||||
* @description MeshCentral task manager
 | 
			
		||||
* @author Ylian Saint-Hilaire
 | 
			
		||||
* @copyright Intel Corporation 2018-2022
 | 
			
		||||
* @license Apache-2.0
 | 
			
		||||
* @version v0.0.1
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*jslint node: true */
 | 
			
		||||
/*jshint node: true */
 | 
			
		||||
/*jshint strict:false */
 | 
			
		||||
/*jshint -W097 */
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports.createTaskManager = function (parent) {
 | 
			
		||||
    var obj = {};
 | 
			
		||||
    obj.parent = parent.webserver;
 | 
			
		||||
    obj.meshServer = parent;
 | 
			
		||||
    obj.db = null;
 | 
			
		||||
    obj.intervalTimer = null;
 | 
			
		||||
    obj.debug = obj.meshServer.debug;
 | 
			
		||||
    obj.VIEWS = __dirname + '/views/';
 | 
			
		||||
    obj.exports = [      
 | 
			
		||||
        'onDeviceRefreshEnd',
 | 
			
		||||
        'resizeContent',
 | 
			
		||||
        'historyData',
 | 
			
		||||
        'variableData',
 | 
			
		||||
        'malix_triggerOption'
 | 
			
		||||
    ];
 | 
			
		||||
    
 | 
			
		||||
    obj.malix_triggerOption = function(selectElem) {
 | 
			
		||||
        selectElem.options.add(new Option("ScriptTask - Run Script", "scripttask_runscript"));
 | 
			
		||||
    }
 | 
			
		||||
    obj.malix_triggerFields_scripttask_runscript = function() {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
    obj.resetQueueTimer = function() {
 | 
			
		||||
        clearTimeout(obj.intervalTimer);
 | 
			
		||||
        obj.intervalTimer = setInterval(obj.queueRun, 1 * 60 * 1000); // every minute
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Start the task manager
 | 
			
		||||
    obj.server_startup = function() {
 | 
			
		||||
        obj.meshServer.pluginHandler.scripttask_db = require (__dirname + '/db.js').CreateDB(obj.meshServer);
 | 
			
		||||
        obj.db = obj.meshServer.pluginHandler.scripttask_db;
 | 
			
		||||
        obj.resetQueueTimer();
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.onDeviceRefreshEnd = function() {
 | 
			
		||||
        pluginHandler.registerPluginTab({ tabTitle: 'ScriptTask', tabId: 'pluginScriptTask' });
 | 
			
		||||
        QA('pluginScriptTask', '<iframe id="pluginIframeScriptTask" style="width: 100%; height: 800px;" scrolling="no" frameBorder=0 src="/pluginadmin.ashx?pin=scripttask&user=1" />');
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    // may not be needed, saving for later. Can be called to resize iFrame
 | 
			
		||||
    obj.resizeContent = function() {
 | 
			
		||||
        var iFrame = document.getElementById('pluginIframeScriptTask');
 | 
			
		||||
        var newHeight = 800;
 | 
			
		||||
        var sHeight = iFrame.contentWindow.document.body.scrollHeight;
 | 
			
		||||
        if (sHeight > newHeight) newHeight = sHeight;
 | 
			
		||||
        if (newHeight > 1600) newHeight = 1600;
 | 
			
		||||
        iFrame.style.height = newHeight + 'px';
 | 
			
		||||
    };
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    obj.queueRun = async function() {
 | 
			
		||||
        var onlineAgents = Object.keys(obj.meshServer.webserver.wsagents);
 | 
			
		||||
        //obj.debug('ScriptTask', 'Queue Running', Date().toLocaleString(), 'Online agents: ', onlineAgents);
 | 
			
		||||
 | 
			
		||||
        obj.db.getPendingJobs(onlineAgents)
 | 
			
		||||
        .then(function(jobs) {
 | 
			
		||||
            if (jobs.length == 0) return;
 | 
			
		||||
            //@TODO check for a large number and use taskLimiter to queue the jobs
 | 
			
		||||
            jobs.forEach(function(job) {
 | 
			
		||||
                obj.db.get(job.scriptId)
 | 
			
		||||
                .then(async function(script) {
 | 
			
		||||
                    script = script[0];
 | 
			
		||||
                    var foundVars = script.content.match(/#(.*?)#/g);
 | 
			
		||||
                    var replaceVars = {};
 | 
			
		||||
                    if (foundVars != null && foundVars.length > 0) {
 | 
			
		||||
                        var foundVarNames = [];
 | 
			
		||||
                        foundVars.forEach(function(fv) { foundVarNames.push(fv.replace(/^#+|#+$/g, '')); });
 | 
			
		||||
                        
 | 
			
		||||
                        var limiters = { 
 | 
			
		||||
                            scriptId: job.scriptId,
 | 
			
		||||
                            nodeId: job.node,
 | 
			
		||||
                            meshId: obj.meshServer.webserver.wsagents[job.node]['dbMeshKey'],
 | 
			
		||||
                            names: foundVarNames
 | 
			
		||||
                        };
 | 
			
		||||
                        var finvals = await obj.db.getVariables(limiters);
 | 
			
		||||
                        var ordering = { 'global': 0, 'script': 1, 'mesh': 2, 'node': 3 }
 | 
			
		||||
                        finvals.sort(function(a, b) { return (ordering[a.scope] - ordering[b.scope]) || a.name.localeCompare(b.name); });
 | 
			
		||||
                        finvals.forEach(function(fv) { replaceVars[fv.name] = fv.value; });
 | 
			
		||||
                        replaceVars['GBL:meshId'] = obj.meshServer.webserver.wsagents[job.node]['dbMeshKey'];
 | 
			
		||||
                        replaceVars['GBL:nodeId'] = job.node;
 | 
			
		||||
                        //console.log('FV IS', finvals);
 | 
			
		||||
                        //console.log('RV IS', replaceVars);
 | 
			
		||||
                    }
 | 
			
		||||
                    var dispatchTime = Math.floor(new Date() / 1000);
 | 
			
		||||
                    var jObj = { 
 | 
			
		||||
                        action: 'task', 
 | 
			
		||||
                        subaction: 'triggerJob',
 | 
			
		||||
                        jobId: job._id,
 | 
			
		||||
                        scriptId: job.scriptId,
 | 
			
		||||
                        replaceVars: replaceVars,
 | 
			
		||||
                        scriptHash: script.contentHash,
 | 
			
		||||
                        dispatchTime: dispatchTime
 | 
			
		||||
                    };
 | 
			
		||||
                    //obj.debug('ScriptTask', 'Sending job to agent');
 | 
			
		||||
                    try { 
 | 
			
		||||
                        obj.meshServer.webserver.wsagents[job.node].send(JSON.stringify(jObj));
 | 
			
		||||
                        obj.db.update(job._id, { dispatchTime: dispatchTime });
 | 
			
		||||
                    } catch (ex) { }
 | 
			
		||||
                })
 | 
			
		||||
                .catch(function (ex) { console.log('task: Could not dispatch job.', ex) });
 | 
			
		||||
            });
 | 
			
		||||
        })
 | 
			
		||||
        .then(function() {
 | 
			
		||||
            obj.makeJobsFromSchedules();
 | 
			
		||||
            obj.cleanHistory();
 | 
			
		||||
        })
 | 
			
		||||
        .catch(function(ex) { console.log('task: Queue Run Error: ', ex); });
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.cleanHistory = function() {
 | 
			
		||||
        if (Math.round(Math.random() * 100) == 99) {
 | 
			
		||||
            //obj.debug('Task', 'Running history cleanup');
 | 
			
		||||
            obj.db.deleteOldHistory();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.downloadFile = function(req, res, user) {
 | 
			
		||||
        var id = req.query.dl;
 | 
			
		||||
        obj.db.get(id)
 | 
			
		||||
        .then(function(found) {
 | 
			
		||||
          if (found.length != 1) { res.sendStatus(401); return; }
 | 
			
		||||
          var file = found[0];
 | 
			
		||||
          res.setHeader('Content-disposition', 'attachment; filename=' + file.name);
 | 
			
		||||
          res.setHeader('Content-type', 'text/plain');
 | 
			
		||||
          //var fs = require('fs');
 | 
			
		||||
          res.send(file.content);
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.updateFrontEnd = async function(ids){
 | 
			
		||||
        if (ids.scriptId != null) {
 | 
			
		||||
            var scriptHistory = null;
 | 
			
		||||
            obj.db.getJobScriptHistory(ids.scriptId)
 | 
			
		||||
            .then(function(sh) {
 | 
			
		||||
                scriptHistory = sh;
 | 
			
		||||
                return obj.db.getJobSchedulesForScript(ids.scriptId);
 | 
			
		||||
            })
 | 
			
		||||
            .then(function(scriptSchedule) {
 | 
			
		||||
                var targets = ['*', 'server-users'];
 | 
			
		||||
                obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'historyData', scriptId: ids.scriptId, nodeId: null, scriptHistory: scriptHistory, nodeHistory: null, scriptSchedule: scriptSchedule });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if (ids.nodeId != null) {
 | 
			
		||||
            var nodeHistory = null;
 | 
			
		||||
            obj.db.getJobNodeHistory(ids.nodeId)
 | 
			
		||||
            .then(function(nh) {
 | 
			
		||||
                nodeHistory = nh;
 | 
			
		||||
                return obj.db.getJobSchedulesForNode(ids.nodeId);
 | 
			
		||||
            })
 | 
			
		||||
            .then(function(nodeSchedule) {
 | 
			
		||||
                var targets = ['*', 'server-users'];
 | 
			
		||||
                obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'historyData', scriptId: null, nodeId: ids.nodeId, scriptHistory: null, nodeHistory: nodeHistory, nodeSchedule: nodeSchedule });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if (ids.tree === true) {
 | 
			
		||||
            obj.db.getScriptTree()
 | 
			
		||||
            .then(function(tree) {
 | 
			
		||||
                var targets = ['*', 'server-users'];
 | 
			
		||||
                obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'newScriptTree', tree: tree });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if (ids.variables === true) {
 | 
			
		||||
            obj.db.getVariables()
 | 
			
		||||
            .then(function(vars) {
 | 
			
		||||
                var targets = ['*', 'server-users'];
 | 
			
		||||
                obj.meshServer.DispatchEvent(targets, obj, { nolog: true, action: 'task', subaction: 'variableData', vars: vars });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.handleAdminReq = function(req, res, user) {
 | 
			
		||||
        if ((user.siteadmin & 0xFFFFFFFF) == 1 && req.query.admin == 1) 
 | 
			
		||||
        {
 | 
			
		||||
            // admin wants admin, grant
 | 
			
		||||
            var vars = {};
 | 
			
		||||
            res.render(obj.VIEWS + 'admin', vars);
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (req.query.admin == 1 && (user.siteadmin & 0xFFFFFFFF) == 0) {
 | 
			
		||||
            // regular user wants admin
 | 
			
		||||
            res.sendStatus(401); 
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (req.query.user == 1) { 
 | 
			
		||||
            // regular user wants regular access, grant
 | 
			
		||||
            if (req.query.dl != null) return obj.downloadFile(req, res, user);
 | 
			
		||||
            var vars = {};
 | 
			
		||||
            
 | 
			
		||||
            if (req.query.edit == 1) { // edit script
 | 
			
		||||
                if (req.query.id == null) return res.sendStatus(401); 
 | 
			
		||||
                obj.db.get(req.query.id)
 | 
			
		||||
                .then(function(scripts) {
 | 
			
		||||
                    if (scripts[0].filetype == 'proc') {
 | 
			
		||||
                        vars.procData = JSON.stringify(scripts[0]);
 | 
			
		||||
                        res.render(obj.VIEWS + 'procedit', vars);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        vars.scriptData = JSON.stringify(scripts[0]);
 | 
			
		||||
                        res.render(obj.VIEWS + 'scriptedit', vars);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                return;
 | 
			
		||||
            } else if (req.query.schedule == 1) {
 | 
			
		||||
                var vars = {};
 | 
			
		||||
                res.render(obj.VIEWS + 'schedule', vars);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // default user view (tree)
 | 
			
		||||
            vars.scriptTree = 'null';
 | 
			
		||||
            obj.db.getScriptTree()
 | 
			
		||||
            .then(function(tree) {
 | 
			
		||||
              vars.scriptTree = JSON.stringify(tree);
 | 
			
		||||
              res.render(obj.VIEWS + 'user', vars);
 | 
			
		||||
            });
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (req.query.include == 1) {
 | 
			
		||||
            switch (req.query.path.split('/').pop().split('.').pop()) {
 | 
			
		||||
                case 'css':     res.contentType('text/css'); break;
 | 
			
		||||
                case 'js':      res.contentType('text/javascript'); break;
 | 
			
		||||
            }
 | 
			
		||||
            res.sendFile(__dirname + '/includes/' + req.query.path); // don't freak out. Express covers any path issues.
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        res.sendStatus(401); 
 | 
			
		||||
        return;
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.historyData = function (message) {
 | 
			
		||||
        if (typeof pluginHandler.scripttask.loadHistory == 'function') pluginHandler.scripttask.loadHistory(message);
 | 
			
		||||
        if (typeof pluginHandler.scripttask.loadSchedule == 'function') pluginHandler.scripttask.loadSchedule(message);
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.variableData = function (message) {
 | 
			
		||||
        if (typeof pluginHandler.scripttask.loadVariables == 'function') pluginHandler.scripttask.loadVariables(message);
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.determineNextJobTime = function(s) {
 | 
			
		||||
        var nextTime = null;
 | 
			
		||||
        var nowTime = Math.floor(new Date() / 1000);
 | 
			
		||||
        
 | 
			
		||||
        // special case: we've reached the end of our run
 | 
			
		||||
        if (s.endAt !== null && s.endAt <= nowTime) {
 | 
			
		||||
            return nextTime;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (s.recur) {
 | 
			
		||||
            case 'once':
 | 
			
		||||
                if (s.nextRun == null) nextTime = s.startAt;
 | 
			
		||||
                else nextTime = null;
 | 
			
		||||
            break;
 | 
			
		||||
            case 'minutes':
 | 
			
		||||
                /*var lRun = s.nextRun || nowTime;
 | 
			
		||||
                if (lRun == null) lRun = nowTime;
 | 
			
		||||
                nextTime = lRun + (s.interval * 60);
 | 
			
		||||
                if (s.startAt > nextTime) nextTime = s.startAt;*/
 | 
			
		||||
                if (s.nextRun == null) { // hasn't run yet, set to start time
 | 
			
		||||
                    nextTime = s.startAt;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                nextTime = s.nextRun + (s.interval * 60);
 | 
			
		||||
                // this prevents "catch-up" tasks being scheduled if an endpoint is offline for a long period of time
 | 
			
		||||
                // e.g. always make sure the next scheduled time is relevant to the scheduled interval, but in the future
 | 
			
		||||
                if (nextTime < nowTime) {
 | 
			
		||||
                    // initially I was worried about this causing event loop lockups
 | 
			
		||||
                    // if there was a long enough time gap. Testing over 50 years of backlog for a 3 min interval
 | 
			
		||||
                    // still ran under a fraction of a second. Safe to say this approach is safe! (~8.5 million times)
 | 
			
		||||
                    while (nextTime < nowTime) {
 | 
			
		||||
                        nextTime = nextTime + (s.interval * 60);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (s.startAt > nextTime) nextTime = s.startAt;
 | 
			
		||||
            break;
 | 
			
		||||
            case 'hourly':
 | 
			
		||||
                if (s.nextRun == null) { // hasn't run yet, set to start time
 | 
			
		||||
                    nextTime = s.startAt;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                nextTime = s.nextRun + (s.interval * 60 * 60);
 | 
			
		||||
                if (nextTime < nowTime) {
 | 
			
		||||
                    while (nextTime < nowTime) {
 | 
			
		||||
                        nextTime = nextTime + (s.interval * 60 * 60);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (s.startAt > nextTime) nextTime = s.startAt;
 | 
			
		||||
            break;
 | 
			
		||||
            case 'daily':
 | 
			
		||||
                if (s.nextRun == null) { // hasn't run yet, set to start time
 | 
			
		||||
                    nextTime = s.startAt;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                nextTime = s.nextRun + (s.interval * 60 * 60 * 24);
 | 
			
		||||
                if (nextTime < nowTime) {
 | 
			
		||||
                    while (nextTime < nowTime) {
 | 
			
		||||
                        nextTime = nextTime + (s.interval * 60 * 60 * 24);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (s.startAt > nextTime) nextTime = s.startAt;
 | 
			
		||||
            break;
 | 
			
		||||
            case 'weekly':
 | 
			
		||||
                var tempDate = new Date();
 | 
			
		||||
                var nowDate = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate());
 | 
			
		||||
                
 | 
			
		||||
                if (s.daysOfWeek.length == 0) {
 | 
			
		||||
                    nextTime = null;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                s.daysOfWeek = s.daysOfWeek.map(function (el) { Number(el) });
 | 
			
		||||
                var baseTime = s.startAt;
 | 
			
		||||
                //console.log('dow is ', s.daysOfWeek);
 | 
			
		||||
                var lastDayOfWeek = Math.max(...s.daysOfWeek);
 | 
			
		||||
                var startX = 0;
 | 
			
		||||
                //console.log('ldow is ', lastDayOfWeek);
 | 
			
		||||
                if (s.nextRun != null) {
 | 
			
		||||
                    baseTime = s.nextRun;
 | 
			
		||||
                    //console.log('basetime 2: ', baseTime);
 | 
			
		||||
                    if (nowDate.getDay() == lastDayOfWeek) {
 | 
			
		||||
                        baseTime = baseTime + ( s.interval * 604800 ) - (lastDayOfWeek * 86400);
 | 
			
		||||
                        //console.log('basetime 3: ', baseTime);
 | 
			
		||||
                    }
 | 
			
		||||
                    startX = 0;
 | 
			
		||||
                } else if (s.startAt < nowTime) {
 | 
			
		||||
                    baseTime = Math.floor(nowDate.getTime() / 1000);
 | 
			
		||||
                    //console.log('basetime 4: ', baseTime);
 | 
			
		||||
                }
 | 
			
		||||
                //console.log('startX is: ', startX);
 | 
			
		||||
                //var secondsFromMidnight = nowTimeDate.getSeconds() + (nowTimeDate.getMinutes() * 60) + (nowTimeDate.getHours() * 60 * 60);
 | 
			
		||||
                //console.log('seconds from midnight: ', secondsFromMidnight);
 | 
			
		||||
                //var dBaseTime = new Date(0); dBaseTime.setUTCSeconds(baseTime);
 | 
			
		||||
                //var dMidnight = new Date(dBaseTime.getFullYear(), dBaseTime.getMonth(), dBaseTime.getDate());
 | 
			
		||||
                //baseTime = Math.floor(dMidnight.getTime() / 1000);
 | 
			
		||||
                for (var x = startX; x <= 7; x++){
 | 
			
		||||
                    var checkDate = baseTime + (86400 * x);
 | 
			
		||||
                    var d = new Date(0); d.setUTCSeconds(checkDate);
 | 
			
		||||
                    var dm = new Date(d.getFullYear(), d.getMonth(), d.getDate());
 | 
			
		||||
                    
 | 
			
		||||
                    console.log('testing date: ', dm.toLocaleString()); // dMidnight.toLocaleString());
 | 
			
		||||
                    //console.log('if break check :', (s.daysOfWeek.indexOf(d.getDay()) !== -1 && checkDate >= nowTime));
 | 
			
		||||
                    //console.log('checkDate vs nowTime: ', (checkDate - nowTime), ' if positive, nowTime is less than checkDate');
 | 
			
		||||
                    if (s.nextRun == null && s.daysOfWeek.indexOf(dm.getDay()) !== -1 && dm.getTime() >= nowDate.getTime()) break;
 | 
			
		||||
                    if (s.daysOfWeek.indexOf(dm.getDay()) !== -1 && dm.getTime() > nowDate.getTime()) break;
 | 
			
		||||
                    //if (s.daysOfWeek.indexOf(d.getDay()) !== -1 && Math.floor(d.getTime() / 1000) >= nowTime) break;
 | 
			
		||||
                }
 | 
			
		||||
                var sa = new Date(0); sa.setUTCSeconds(s.startAt);
 | 
			
		||||
                var sad = new Date(sa.getFullYear(), sa.getMonth(), sa.getDate());
 | 
			
		||||
                var diff = (sa.getTime() - sad.getTime()) / 1000;
 | 
			
		||||
                nextTime = Math.floor(dm.getTime() / 1000) + diff;
 | 
			
		||||
                //console.log('next schedule is ' + d.toLocaleString());
 | 
			
		||||
            break;
 | 
			
		||||
            default:
 | 
			
		||||
                nextTime = null;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (s.endAt != null && nextTime > s.endAt) nextTime = null; // if the next time reaches the bound of the endAt time, nullify
 | 
			
		||||
        
 | 
			
		||||
        return nextTime;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    obj.makeJobsFromSchedules = function(scheduleId) {
 | 
			
		||||
        //obj.debug('ScriptTask', 'makeJobsFromSchedules starting');
 | 
			
		||||
        return obj.db.getSchedulesDueForJob(scheduleId)
 | 
			
		||||
        .then(function(schedules) {
 | 
			
		||||
            //obj.debug('ScriptTask', 'Found ' + schedules.length + ' schedules to process. Current time is: ' + Math.floor(new Date() / 1000));
 | 
			
		||||
            if (schedules.length) {
 | 
			
		||||
                schedules.forEach(function(s) {
 | 
			
		||||
                    var nextJobTime = obj.determineNextJobTime(s);
 | 
			
		||||
                    var nextJobScheduled = false;
 | 
			
		||||
                    if (nextJobTime === null) {
 | 
			
		||||
                        //obj.debug('ScriptTask', 'Removing Job Schedule for', JSON.stringify(s));
 | 
			
		||||
                        obj.db.removeJobSchedule(s._id);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        //obj.debug('ScriptTask', 'Scheduling Job for', JSON.stringify(s));
 | 
			
		||||
                        obj.db.get(s.scriptId)
 | 
			
		||||
                        .then(function(scripts) {
 | 
			
		||||
                            // if a script is scheduled to run, but a previous run hasn't completed, 
 | 
			
		||||
                            // don't schedule another job for the same (device probably offline).
 | 
			
		||||
                            // results in the minimum jobs running once an agent comes back online.
 | 
			
		||||
                            return obj.db.getIncompleteJobsForSchedule(s._id)
 | 
			
		||||
                            .then(function(jobs) {
 | 
			
		||||
                                if (jobs.length > 0) { /* obj.debug('Task', 'Skipping job creation'); */ return Promise.resolve(); }
 | 
			
		||||
                                else { /* obj.debug('Task', 'Creating new job'); */ nextJobScheduled = true; return obj.db.addJob( { scriptId: s.scriptId, scriptName: scripts[0].name, node: s.node, runBy: s.scheduledBy, dontQueueUntil: nextJobTime, jobSchedule: s._id } ); }
 | 
			
		||||
                            });
 | 
			
		||||
                        })
 | 
			
		||||
                        .then(function() {
 | 
			
		||||
                            if (nextJobScheduled) { /* obj.debug('Plugin', 'ScriptTask', 'Updating nextRun time'); */ return obj.db.update(s._id, { nextRun: nextJobTime }); }
 | 
			
		||||
                            else { /* obj.debug('Plugin', 'ScriptTask', 'NOT updating nextRun time'); */ return Promise.resolve(); }
 | 
			
		||||
                        })
 | 
			
		||||
                        .then(function() {
 | 
			
		||||
                            obj.updateFrontEnd( { scriptId: s.scriptId, nodeId: s.node } );
 | 
			
		||||
                        })
 | 
			
		||||
                        .catch(function(ex) { console.log('Task: Error managing job schedules: ', ex); });
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    obj.deleteElement = function (command) {
 | 
			
		||||
        var delObj = null;
 | 
			
		||||
        obj.db.get(command.id)
 | 
			
		||||
        .then(function(found) {
 | 
			
		||||
          var file = found[0];
 | 
			
		||||
          delObj = {...{}, ...found[0]};
 | 
			
		||||
          return file;
 | 
			
		||||
        })
 | 
			
		||||
        .then(function(file) {
 | 
			
		||||
          if (file.type == 'folder') return obj.db.deleteByPath(file.path); //@TODO delete schedules for scripts within folders
 | 
			
		||||
          if (file.type == 'script') return obj.db.deleteSchedulesForScript(file._id);
 | 
			
		||||
          if (file.type == 'jobSchedule') return obj.db.deletePendingJobsForSchedule(file._id);
 | 
			
		||||
        })
 | 
			
		||||
        .then(function() {
 | 
			
		||||
          return obj.db.delete(command.id)
 | 
			
		||||
        })
 | 
			
		||||
        .then(function() {
 | 
			
		||||
          var updateObj = { tree: true };
 | 
			
		||||
          if (delObj.type == 'jobSchedule') {
 | 
			
		||||
              updateObj.scriptId = delObj.scriptId;
 | 
			
		||||
              updateObj.nodeId = delObj.node;
 | 
			
		||||
          }
 | 
			
		||||
          return obj.updateFrontEnd( updateObj );
 | 
			
		||||
        })
 | 
			
		||||
        .catch(function(ex) { console.log('Task: Error deleting ', ex.stack); });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Process 'task' commands received by an agent
 | 
			
		||||
    obj.agentAction = function (command, agent) {
 | 
			
		||||
        console.log('task-agentAction', command);
 | 
			
		||||
        switch (command.subaction) {
 | 
			
		||||
            case 'getScript':
 | 
			
		||||
                // TODO
 | 
			
		||||
                break;
 | 
			
		||||
            case 'clearAllPendingTasks':
 | 
			
		||||
                // TODO
 | 
			
		||||
                break;
 | 
			
		||||
            case 'taskComplete':
 | 
			
		||||
                // TODO
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    obj.serveraction = function(command, myparent, grandparent) {
 | 
			
		||||
        switch (command.subaction) {
 | 
			
		||||
            case 'addScript':
 | 
			
		||||
                obj.db.addScript(command.name, command.content, command.path, command.filetype)
 | 
			
		||||
                .then(function() { obj.updateFrontEnd( { tree: true } ); });            
 | 
			
		||||
            break;
 | 
			
		||||
            case 'new':
 | 
			
		||||
                var parent_path = '', new_path = '';
 | 
			
		||||
                obj.db.get(command.parent_id)
 | 
			
		||||
                .then(function(found) { if (found.length > 0) { var file = found[0]; parent_path = file.path; } else { parent_path = 'Shared'; } })
 | 
			
		||||
                .then(function () { obj.db.addScript(command.name, '', parent_path, command.filetype); })
 | 
			
		||||
                .then(function() { obj.updateFrontEnd( { tree: true } ); });
 | 
			
		||||
            break;
 | 
			
		||||
            case 'rename':
 | 
			
		||||
              obj.db.get(command.id)
 | 
			
		||||
              .then(function(docs) {
 | 
			
		||||
                  var doc = docs[0];
 | 
			
		||||
                  if (doc.type == 'folder') {
 | 
			
		||||
                      console.log('old', doc.path, 'new', doc.path.replace(doc.path, command.name));
 | 
			
		||||
                      return obj.db.update(command.id, { path: doc.path.replace(doc.name, command.name) })
 | 
			
		||||
                      .then(function() { // update sub-items
 | 
			
		||||
                          return obj.db.getByPath(doc.path)
 | 
			
		||||
                      })
 | 
			
		||||
                      .then(function(found) {
 | 
			
		||||
                          if (found.length > 0) {
 | 
			
		||||
                            var proms = [];
 | 
			
		||||
                            found.forEach(function(f) { proms.push(obj.db.update(f._id, { path: doc.path.replace(doc.name, command.name) } )); })
 | 
			
		||||
                            return Promise.all(proms);
 | 
			
		||||
                          }
 | 
			
		||||
                      })
 | 
			
		||||
                  } else {
 | 
			
		||||
                      return Promise.resolve();
 | 
			
		||||
                  }
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() {
 | 
			
		||||
                  obj.db.update(command.id, { name: command.name })
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() {
 | 
			
		||||
                  return obj.db.updateScriptJobName(command.id, command.name);
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() {
 | 
			
		||||
                  obj.updateFrontEnd( { scriptId: command.id, nodeId: command.currentNodeId, tree: true } );
 | 
			
		||||
              });
 | 
			
		||||
            break;
 | 
			
		||||
            case 'move':
 | 
			
		||||
              var toPath = null, fromPath = null, parentType = null;
 | 
			
		||||
              obj.db.get(command.to)
 | 
			
		||||
              .then(function(found) { // get target data
 | 
			
		||||
                  if (found.length > 0) {
 | 
			
		||||
                    var file = found[0];
 | 
			
		||||
                    toPath = file.path;
 | 
			
		||||
                  } else throw Error('Target destination not found');
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() { // get item to be moved
 | 
			
		||||
                return obj.db.get(command.id);
 | 
			
		||||
              })
 | 
			
		||||
              .then(function(found) { // set item to new location
 | 
			
		||||
                  var file = found[0];
 | 
			
		||||
                  if (file.type == 'folder') {
 | 
			
		||||
                    fromPath = file.path;
 | 
			
		||||
                    toPath += '/' + file.name;
 | 
			
		||||
                    parentType = 'folder';
 | 
			
		||||
                    if (file.name == 'Shared' && file.path == 'Shared') throw Error('Cannot move top level directory: Shared');
 | 
			
		||||
                  }
 | 
			
		||||
                  return obj.db.update(command.id, { path: toPath } );
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() { // update sub-items
 | 
			
		||||
                  return obj.db.getByPath(fromPath)
 | 
			
		||||
              })
 | 
			
		||||
              .then(function(found) {
 | 
			
		||||
                  if (found.length > 0) {
 | 
			
		||||
                    var proms = [];
 | 
			
		||||
                    found.forEach(function(f) {
 | 
			
		||||
                      proms.push(obj.db.update(f._id, { path: toPath } ));
 | 
			
		||||
                    })
 | 
			
		||||
                    return Promise.all(proms);
 | 
			
		||||
                  }
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() {
 | 
			
		||||
                return obj.updateFrontEnd( { tree: true } );
 | 
			
		||||
              })
 | 
			
		||||
              .catch(function(ex) { console.log('Task: Error moving ', ex.stack); });
 | 
			
		||||
            break;
 | 
			
		||||
            case 'newFolder':
 | 
			
		||||
              var parent_path = '';
 | 
			
		||||
              var new_path = '';
 | 
			
		||||
              
 | 
			
		||||
              obj.db.get(command.parent_id)
 | 
			
		||||
              .then(function(found) {
 | 
			
		||||
                if (found.length > 0) {
 | 
			
		||||
                    var file = found[0];
 | 
			
		||||
                    parent_path = file.path;
 | 
			
		||||
                } else {
 | 
			
		||||
                    parent_path = 'Shared';
 | 
			
		||||
                }
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() {
 | 
			
		||||
                new_path = parent_path + '/' + command.name;
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() {
 | 
			
		||||
                  return obj.db.addFolder(command.name, new_path);
 | 
			
		||||
              })
 | 
			
		||||
              .then(function () {
 | 
			
		||||
                return obj.updateFrontEnd( { tree: true } );
 | 
			
		||||
              })
 | 
			
		||||
              .catch(function(ex) { console.log('Task: Error creating new folder ', ex.stack); });
 | 
			
		||||
            break;
 | 
			
		||||
            case 'delete':
 | 
			
		||||
              obj.deleteElement(command);
 | 
			
		||||
            break;
 | 
			
		||||
            case 'addScheduledJob':
 | 
			
		||||
                /* { 
 | 
			
		||||
                    scriptId: scriptId, 
 | 
			
		||||
                    node: s, 
 | 
			
		||||
                    scheduledBy: myparent.user.name,
 | 
			
		||||
                    recur: command.recur, // [once, minutes, hourly, daily, weekly, monthly]
 | 
			
		||||
                    interval: x,
 | 
			
		||||
                    daysOfWeek: x, // only used for weekly recur val
 | 
			
		||||
                    // onTheXDay: x, // only used for monthly
 | 
			
		||||
                    startAt: x,
 | 
			
		||||
                    endAt: x,
 | 
			
		||||
                    runCountLimit: x,
 | 
			
		||||
                    lastRun: x,
 | 
			
		||||
                    nextRun: x,
 | 
			
		||||
                    type: "scheduledJob"
 | 
			
		||||
                } */
 | 
			
		||||
                var sj = command.schedule;
 | 
			
		||||
                
 | 
			
		||||
                var sched = {
 | 
			
		||||
                    scriptId: command.scriptId, 
 | 
			
		||||
                    node: null, 
 | 
			
		||||
                    scheduledBy: myparent.user.name,
 | 
			
		||||
                    recur: sj.recur,
 | 
			
		||||
                    interval: sj.interval,
 | 
			
		||||
                    daysOfWeek: sj.dayVals,
 | 
			
		||||
                    startAt: sj.startAt,
 | 
			
		||||
                    endAt: sj.endAt,
 | 
			
		||||
                    lastRun: null,
 | 
			
		||||
                    nextRun: null,
 | 
			
		||||
                    type: "jobSchedule"
 | 
			
		||||
                };
 | 
			
		||||
                var sel = command.nodes;
 | 
			
		||||
                var proms = [];
 | 
			
		||||
                if (Array.isArray(sel)) {
 | 
			
		||||
                  sel.forEach(function(s) {
 | 
			
		||||
                    var sObj = {...sched, ...{ node: s }};
 | 
			
		||||
                    proms.push(obj.db.addJobSchedule( sObj ));
 | 
			
		||||
                  });
 | 
			
		||||
              } else { test.push(sObj);
 | 
			
		||||
                  proms.push(obj.db.addJobSchedule( sObj ));
 | 
			
		||||
                }
 | 
			
		||||
                Promise.all(proms)
 | 
			
		||||
                .then(function() {
 | 
			
		||||
                    obj.makeJobsFromSchedules();
 | 
			
		||||
                    return Promise.resolve();
 | 
			
		||||
                })
 | 
			
		||||
                .catch(function(ex) { console.log('Task: Error adding schedules. The error was: ', ex); });
 | 
			
		||||
            break;
 | 
			
		||||
            case 'runScript':
 | 
			
		||||
              var scriptId = command.scriptId;
 | 
			
		||||
              var sel = command.nodes;
 | 
			
		||||
              var proms = [];
 | 
			
		||||
              if (Array.isArray(sel)) {
 | 
			
		||||
                sel.forEach(function(s) {
 | 
			
		||||
                  proms.push(obj.db.addJob( { scriptId: scriptId, node: s, runBy: myparent.user.name } ));
 | 
			
		||||
                });
 | 
			
		||||
              } else {
 | 
			
		||||
                proms.push(obj.db.addJob( { scriptId: scriptId, node: sel, runBy: myparent.user.name } ));
 | 
			
		||||
              }
 | 
			
		||||
              Promise.all(proms)
 | 
			
		||||
              .then(function() {
 | 
			
		||||
                  return obj.db.get(scriptId);
 | 
			
		||||
              })
 | 
			
		||||
              .then(function(scripts) {
 | 
			
		||||
                  return obj.db.updateScriptJobName(scriptId, scripts[0].name);
 | 
			
		||||
              })
 | 
			
		||||
              .then(function() {
 | 
			
		||||
                  obj.resetQueueTimer();
 | 
			
		||||
                  obj.queueRun();
 | 
			
		||||
                  obj.updateFrontEnd( { scriptId: scriptId, nodeId: command.currentNodeId } );
 | 
			
		||||
              });
 | 
			
		||||
            break;
 | 
			
		||||
            case 'getScript':
 | 
			
		||||
                //obj.debug('ScriptTask', 'getScript Triggered', JSON.stringify(command));
 | 
			
		||||
                obj.db.get(command.scriptId)
 | 
			
		||||
                .then(function(script) {
 | 
			
		||||
                    myparent.send(JSON.stringify({ 
 | 
			
		||||
                        action: 'task',
 | 
			
		||||
                        subaction: 'cacheScript',
 | 
			
		||||
                        nodeid: myparent.dbNodeKey,
 | 
			
		||||
                        rights: true,
 | 
			
		||||
                        sessionid: true,
 | 
			
		||||
                        script: script[0]
 | 
			
		||||
                    }));
 | 
			
		||||
                });
 | 
			
		||||
            break;
 | 
			
		||||
            case 'jobComplete':
 | 
			
		||||
                //obj.debug('ScriptTask', 'jobComplete Triggered', JSON.stringify(command));
 | 
			
		||||
                var jobNodeHistory = null, scriptHistory = null;
 | 
			
		||||
                var jobId = command.jobId, retVal = command.retVal, errVal = command.errVal, dispatchTime = command.dispatchTime;
 | 
			
		||||
                var completeTime = Math.floor(new Date() / 1000);
 | 
			
		||||
                obj.db.update(jobId, {
 | 
			
		||||
                    completeTime: completeTime,
 | 
			
		||||
                    returnVal: retVal,
 | 
			
		||||
                    errorVal: errVal,
 | 
			
		||||
                    dispatchTime: dispatchTime
 | 
			
		||||
                })
 | 
			
		||||
                .then(function() {
 | 
			
		||||
                    return obj.db.get(jobId)
 | 
			
		||||
                    .then(function(jobs) {
 | 
			
		||||
                        return Promise.resolve(jobs[0].jobSchedule);
 | 
			
		||||
                    })
 | 
			
		||||
                    .then(function(sId) {
 | 
			
		||||
                        if (sId == null) return Promise.resolve();
 | 
			
		||||
                        return obj.db.update(sId, { lastRun: completeTime } )
 | 
			
		||||
                        .then(function() {
 | 
			
		||||
                            obj.makeJobsFromSchedules(sId);
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                })
 | 
			
		||||
                .then(function() {
 | 
			
		||||
                    obj.updateFrontEnd( { scriptId: command.scriptId, nodeId: myparent.dbNodeKey } );
 | 
			
		||||
                })
 | 
			
		||||
                .catch(function(ex) { console.log('Task: Failed to complete job. ', ex); });
 | 
			
		||||
                // update front end by eventing
 | 
			
		||||
            break;
 | 
			
		||||
            case 'loadNodeHistory':
 | 
			
		||||
                obj.updateFrontEnd( { nodeId: command.nodeId } );
 | 
			
		||||
            break;
 | 
			
		||||
            case 'loadScriptHistory':
 | 
			
		||||
                obj.updateFrontEnd( { scriptId: command.scriptId } );
 | 
			
		||||
            break;
 | 
			
		||||
            case 'editScript':
 | 
			
		||||
                obj.db.update(command.scriptId, { type: command.scriptType, name: command.scriptName, content: command.scriptContent })
 | 
			
		||||
                .then(function() { obj.updateFrontEnd( { scriptId: command.scriptId, tree: true } ); });
 | 
			
		||||
            break;
 | 
			
		||||
            case 'clearAllPendingJobs':
 | 
			
		||||
                obj.db.deletePendingJobsForNode(myparent.dbNodeKey);
 | 
			
		||||
            break;
 | 
			
		||||
            case 'loadVariables':
 | 
			
		||||
                obj.updateFrontEnd( { variables: true } );
 | 
			
		||||
            break;
 | 
			
		||||
            case 'newVar':
 | 
			
		||||
                obj.db.addVariable(command.name, command.scope, command.scopeTarget, command.value)
 | 
			
		||||
                .then(function() { obj.updateFrontEnd( { variables: true } ); })
 | 
			
		||||
            break;
 | 
			
		||||
            case 'editVar':
 | 
			
		||||
                obj.db.update(command.id, { 
 | 
			
		||||
                    name: command.name, 
 | 
			
		||||
                    scope: command.scope, 
 | 
			
		||||
                    scopeTarget: command.scopeTarget,
 | 
			
		||||
                    value: command.value
 | 
			
		||||
                })
 | 
			
		||||
                .then(function() { obj.updateFrontEnd( { variables: true } ); })
 | 
			
		||||
            break;
 | 
			
		||||
            case 'deleteVar':
 | 
			
		||||
                obj.db.delete(command.id)
 | 
			
		||||
                .then(function() { obj.updateFrontEnd( { variables: true } ); })
 | 
			
		||||
            break;
 | 
			
		||||
            default:
 | 
			
		||||
                console.log('Task: unknown action');
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5537,25 +5537,7 @@
 | 
			
		|||
                p2downloadDeviceInfo();
 | 
			
		||||
            } else if (op == 106) {
 | 
			
		||||
                // Run commands
 | 
			
		||||
                var wintype = false, linuxtype = false, agenttype = false, chkNodeIds = getCheckedDevices();
 | 
			
		||||
                for (var i in chkNodeIds) {
 | 
			
		||||
                    var n = getNodeFromId(chkNodeIds[i]);
 | 
			
		||||
                    if (n.agent) { if ((GetNodeRights(n) & 24) == 24) { agenttype = true; } if ((n.agent.id > 0) && (n.agent.id < 5)) { wintype = true; } else { linuxtype = true; } }
 | 
			
		||||
                }
 | 
			
		||||
                if ((wintype == true) || (linuxtype == true) || (agenttype == true)) {
 | 
			
		||||
                    var x = "Run commands on selected devices." + '<br />';
 | 
			
		||||
                    x += '<select id=d2cmdtype onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px;margin-top:4px>';
 | 
			
		||||
                    if (wintype == true) { x += '<option value=1>' + "Windows Command Prompt" + '</option><option value=2>' + "Windows PowerShell" + '</option>'; }
 | 
			
		||||
                    if (linuxtype == true) { x += '<option value=3>' + "Linux/BSD/macOS Command Shell" + '</option>'; }
 | 
			
		||||
                    if (agenttype == true) { x += '<option value=4>' + "Agent Console" + '</option>'; } // MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
 | 
			
		||||
                    x += '</select>';
 | 
			
		||||
                    x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0>' + "Run as agent" + '</option><option value=1>' + "Run as user, agent if no user" + '</option><option value=2>' + "Must run as user" + '</option></select>';
 | 
			
		||||
                    x += '<textarea id=d2runcmd style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
 | 
			
		||||
                    setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x);
 | 
			
		||||
                    Q('d2runcmd').focus();
 | 
			
		||||
                    //QE('idx_dlgOkButton', true);
 | 
			
		||||
                    d2runCommandValidate();
 | 
			
		||||
                }
 | 
			
		||||
                d2runCommandDialog({ nodeids: getCheckedDevices(), title: "Run commands on selected devices.", func: uncheckAllDevices });
 | 
			
		||||
            } else if (op == 107) {
 | 
			
		||||
                // Edit tags
 | 
			
		||||
                var x = "Perform batch device tag operation" + '<br /><br />';
 | 
			
		||||
| 
						 | 
				
			
			@ -5611,7 +5593,6 @@
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function d2runCommandValidate() { QV('d2cmduser', Q('d2cmdtype').value < 4); }
 | 
			
		||||
        function d2batchUploadValidate() { QE('idx_dlgOkButton', (Q('d2uploadinput').files.length != 0) && ((Q('d2winuploadpath') == null) || (Q('d2winuploadpath').value != '')) && ((Q('d2linuxuploadpath') == null) || (Q('d2linuxuploadpath').value != ''))); }
 | 
			
		||||
        function d2batchUploadValidateOk() { Q('d2batchUploadSubmit').click(); }
 | 
			
		||||
        function d2groupActionFunctionAgentUpdateExec() { meshserver.send({ action: 'updateAgents', nodeids: getCheckedDevices() }); }
 | 
			
		||||
| 
						 | 
				
			
			@ -5728,10 +5709,34 @@
 | 
			
		|||
 | 
			
		||||
        function d2groupActionFunctionDelCheck() { QE('idx_dlgOkButton', Q('d2check').checked); }
 | 
			
		||||
        function d2groupActionFunctionDelExec() { meshserver.send({ action: 'removedevices', nodeids: getCheckedDevices() }); uncheckAllDevices(); }
 | 
			
		||||
        function d2groupActionFunctionRunCommands() {
 | 
			
		||||
 | 
			
		||||
        function d2runCommandDialog(options) {
 | 
			
		||||
            var wintype = false, linuxtype = false, agenttype = false;
 | 
			
		||||
            for (var i in options.nodeids) {
 | 
			
		||||
                var n = getNodeFromId(options.nodeids[i]);
 | 
			
		||||
                if (n.agent) { if ((GetNodeRights(n) & 24) == 24) { agenttype = true; } if ((n.agent.id > 0) && (n.agent.id < 5)) { wintype = true; } else { linuxtype = true; } }
 | 
			
		||||
            }
 | 
			
		||||
            if ((wintype == true) || (linuxtype == true) || (agenttype == true)) {
 | 
			
		||||
                var x = options.title + '<br />';
 | 
			
		||||
                x += '<select id=d2cmdtype onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px;margin-top:4px>';
 | 
			
		||||
                if (wintype == true) { x += '<option value=1>' + "Windows Command Prompt" + '</option><option value=2>' + "Windows PowerShell" + '</option>'; }
 | 
			
		||||
                if (linuxtype == true) { x += '<option value=3>' + "Linux/BSD/macOS Command Shell" + '</option>'; }
 | 
			
		||||
                if (agenttype == true) { x += '<option value=4>' + "Agent Console" + '</option>'; } // MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
 | 
			
		||||
                x += '</select>';
 | 
			
		||||
                x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0>' + "Run as agent" + '</option><option value=1>' + "Run as user, agent if no user" + '</option><option value=2>' + "Must run as user" + '</option></select>';
 | 
			
		||||
                x += '<textarea id=d2runcmd style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
 | 
			
		||||
                setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x, options);
 | 
			
		||||
                Q('d2runcmd').focus();
 | 
			
		||||
                //QE('idx_dlgOkButton', true);
 | 
			
		||||
                d2runCommandValidate();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        function d2runCommandValidate() { QV('d2cmduser', Q('d2cmdtype').value < 4); }
 | 
			
		||||
        function d2groupActionFunctionRunCommands(b, options) {
 | 
			
		||||
            var type = 3;
 | 
			
		||||
            try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { }
 | 
			
		||||
            meshserver.send({ action: 'runcommands', nodeids: getCheckedDevices(), type: type, cmds: Q('d2runcmd').value, runAsUser: parseInt(Q('d2cmduser').value) }); uncheckAllDevices();
 | 
			
		||||
            meshserver.send({ action: 'runcommands', nodeids: options.nodeids, type: type, cmds: Q('d2runcmd').value, runAsUser: parseInt(Q('d2cmduser').value) });
 | 
			
		||||
            if (options.func) { options.func(); }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function onSortSelectChange(skipsave) {
 | 
			
		||||
| 
						 | 
				
			
			@ -7174,6 +7179,7 @@
 | 
			
		|||
                if (((meshrights & (4 + 8 + 64 + 262144)) != 0) && (node.mtype < 3) && ((node.agent == null) || (node.agent.id != 34))) { 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) + ',"' + encodeURIComponentEx(node._id) + '") />';
 | 
			
		||||
                x += '<input type=button value="' + "Log Event" + '" title="' + "Write an event for this device" + '" onclick=writeDeviceEvent("' + encodeURIComponentEx(node._id) + '") />';
 | 
			
		||||
                if ((node.mtype == 2) && (connectivity & 1) && (meshrights == 0xFFFFFFFF)) { x += '<input type=button value="' + "Run" + '" title="' + "Run commands on this device" + '" onclick=runDeviceCmd("' + encodeURIComponentEx(node._id) + '") />'; }
 | 
			
		||||
                if (node.mtype != 4) {
 | 
			
		||||
                    if ((meshrights & 8) && ((connectivity & 1) || ((node.pmt == 1) && ((features2 & 2) != 0)))) { x += '<input type=button value="' + "Message" + '" title="' + "Display a text message on the remote device" + '" onclick=deviceMessageFunction() />';  }
 | 
			
		||||
                    //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() />';  }
 | 
			
		||||
| 
						 | 
				
			
			@ -7632,6 +7638,12 @@
 | 
			
		|||
            return str.join(', ');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Run commands on current device
 | 
			
		||||
        function runDeviceCmd(nodeid) {
 | 
			
		||||
            if (xxdialogMode) return;
 | 
			
		||||
            d2runCommandDialog({ nodeids: [ decodeURIComponent(nodeid) ], title: "Run commands on this device." });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,249 +0,0 @@
 | 
			
		|||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <script type="text/javascript" src="scripts/common-0.0.1.js"></script>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="/public/tail.DateTime/tail.datetime-default-blue.min.css" />
 | 
			
		||||
    <style>
 | 
			
		||||
        body {
 | 
			
		||||
            font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
 | 
			
		||||
            color: white;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #scriptContent {
 | 
			
		||||
            width: 80%;
 | 
			
		||||
            height: 80%;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #schedContentC {
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #controlBar button {
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #scriptNameC {
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #scriptName {
 | 
			
		||||
            width: 300px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #controlBar {
 | 
			
		||||
            padding: 5px;
 | 
			
		||||
            padding-left: 20px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #left {
 | 
			
		||||
            height: 100%;
 | 
			
		||||
            width: 25%;
 | 
			
		||||
            float: left;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #right {
 | 
			
		||||
            height: 100%;
 | 
			
		||||
            width: 75%;
 | 
			
		||||
            float: right;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        body {
 | 
			
		||||
            background-color: #036;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #intervalListC {
 | 
			
		||||
            list-style-type: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #daysListC {
 | 
			
		||||
            list-style-type: none;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .rOpt {
 | 
			
		||||
            height: 40px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #daysListC {
 | 
			
		||||
            display: inline-grid;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        li {
 | 
			
		||||
            padding: 2px;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body onload="doOnLoad();">
 | 
			
		||||
    <script type="text/javascript" src="/public/tail.DateTime/tail.datetime.min.js"></script>
 | 
			
		||||
    <div id="scriptTaskSchedule">
 | 
			
		||||
        <div id="controlBar">
 | 
			
		||||
            <button onclick="goSave();">Schedule</button>
 | 
			
		||||
            <button onclick="goCancel();">Cancel</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div id="schedContentC">
 | 
			
		||||
            <div id="left">
 | 
			
		||||
                <span class="oTitle">Recurrence</span>
 | 
			
		||||
                <ul id="intervalListC">
 | 
			
		||||
                    <li><label><input onclick="intervalSelected(this);" type="radio" checked name="recur" value="once">Once</label></li>
 | 
			
		||||
                    <li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="minutes">Minutes</label></li>
 | 
			
		||||
                    <li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="hourly">Hourly</label></li>
 | 
			
		||||
                    <li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="daily">Daily</label></li>
 | 
			
		||||
                    <li><label><input onclick="intervalSelected(this);" type="radio" name="recur" value="weekly">Weekly</label></li>
 | 
			
		||||
                    <!-- li><label><input type="radio" name="recur" value="monthly">Monthly</label></li -->
 | 
			
		||||
                </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div id="right">
 | 
			
		||||
                <div class="rOpt">
 | 
			
		||||
                    <span class="oTitle">Start: </span>
 | 
			
		||||
                    <input type="text" class="datePick" id="startDate" value="" />
 | 
			
		||||
                    <input type="text" class="timePick" id="startTime" value="" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="rOpt" id="intervalC" style="display: none;">
 | 
			
		||||
                    <span class="oTitle">Every: </span>
 | 
			
		||||
                    <input type="text" id="interval" value="1" /> <span id="hintText"></span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="rOpt" id="endC" style="display: none;">
 | 
			
		||||
                    <span class="oTitle">End: </span>
 | 
			
		||||
                    <input type="text" class="datePick" id="endDate" value="" />
 | 
			
		||||
                    <input type="text" class="timePick" id="endTime" value="" />
 | 
			
		||||
                    <label><input type="checkbox" id="endNever" checked onclick="checkEndNever(this);" /> Never</label>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="rOpt" id="daysC" style="display: none;">
 | 
			
		||||
                    <span class="oTitle">Days: </span>
 | 
			
		||||
                    <ul id="daysListC">
 | 
			
		||||
                        <li><label><input type="checkbox" name="days[]" value="0"> Sunday</label></li>
 | 
			
		||||
                        <li><label><input type="checkbox" name="days[]" value="1"> Monday</label></li>
 | 
			
		||||
                        <li><label><input type="checkbox" name="days[]" value="2"> Tuesday</label></li>
 | 
			
		||||
                        <li><label><input type="checkbox" name="days[]" value="3"> Wednesday</label></li>
 | 
			
		||||
                        <li><label><input type="checkbox" name="days[]" value="4"> Thursday</label></li>
 | 
			
		||||
                        <li><label><input type="checkbox" name="days[]" value="5"> Friday</label></li>
 | 
			
		||||
                        <li><label><input type="checkbox" name="days[]" value="6"> Saturday</label></li>
 | 
			
		||||
                    </ul>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <script type="text/javascript">
 | 
			
		||||
 | 
			
		||||
        function checkEndNever(el) {
 | 
			
		||||
            if (el.checked) {
 | 
			
		||||
                Q('endDate').value = '';
 | 
			
		||||
                Q('endTime').value = '';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        function setTimePick() {
 | 
			
		||||
            var d = new Date();
 | 
			
		||||
            document.getElementById("startDate").value = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate();
 | 
			
		||||
            document.getElementById("startTime").value = d.getHours() + ':' + d.getMinutes();
 | 
			
		||||
            tail.DateTime(".datePick", { position: "bottom", dateStart: Date(), timeFormat: false });
 | 
			
		||||
            tail.DateTime(".timePick", { position: "bottom", dateFormat: false, timeFormat: "HH:ii", timeStepMinutes: 15 });
 | 
			
		||||
            tail.datetime.inst[document.getElementById('endDate').getAttribute('data-tail-datetime')].on('change', function () {
 | 
			
		||||
                document.getElementById('endNever').checked = false;
 | 
			
		||||
            });
 | 
			
		||||
            tail.datetime.inst[document.getElementById('endTime').getAttribute('data-tail-datetime')].on('change', function () {
 | 
			
		||||
                document.getElementById('endNever').checked = false;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function doOnLoad() {
 | 
			
		||||
            try {
 | 
			
		||||
                if (scriptId == null) {
 | 
			
		||||
                    alert('Page reloaded and data lost. Please re-run scheduler from the main window.');
 | 
			
		||||
                    goCancel();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                alert('Page reloaded and data lost. Please re-run scheduler from the main window.');
 | 
			
		||||
                goCancel();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            setTimePick();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function intervalSelected(el) {
 | 
			
		||||
            var v = el.value;
 | 
			
		||||
            switch (v) {
 | 
			
		||||
                case 'once':
 | 
			
		||||
                    QV('intervalC', false);
 | 
			
		||||
                    QV('endC', false);
 | 
			
		||||
                    QV('daysC', false);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'minutes':
 | 
			
		||||
                    QV('intervalC', true);
 | 
			
		||||
                    QV('endC', true);
 | 
			
		||||
                    QV('daysC', false);
 | 
			
		||||
                    QH('hintText', 'minute(s)');
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'hourly':
 | 
			
		||||
                    QV('intervalC', true);
 | 
			
		||||
                    QV('endC', true);
 | 
			
		||||
                    QV('daysC', false);
 | 
			
		||||
                    QH('hintText', 'hour(s)');
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'daily':
 | 
			
		||||
                    QV('intervalC', true);
 | 
			
		||||
                    QV('endC', true);
 | 
			
		||||
                    QV('daysC', false);
 | 
			
		||||
                    QH('hintText', 'day(s)');
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'weekly':
 | 
			
		||||
                    QV('intervalC', true);
 | 
			
		||||
                    QV('endC', true);
 | 
			
		||||
                    QV('daysC', true);
 | 
			
		||||
                    QH('hintText', 'week(s)');
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        function goSave() {
 | 
			
		||||
            var o = {};
 | 
			
		||||
            var recurEls = document.getElementsByName("recur");
 | 
			
		||||
            recurEls.forEach(function (el) {
 | 
			
		||||
                if (el.checked) o.recur = el.value;
 | 
			
		||||
            });
 | 
			
		||||
            switch (o.recur) {
 | 
			
		||||
                case 'once':
 | 
			
		||||
                    o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
 | 
			
		||||
                    o.startAt = Math.floor(o.startAt / 1000);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'minutes':
 | 
			
		||||
                case 'hourly':
 | 
			
		||||
                case 'daily':
 | 
			
		||||
                    o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
 | 
			
		||||
                    o.startAt = Math.floor(o.startAt / 1000);
 | 
			
		||||
                    o.interval = Number(Q('interval').value);
 | 
			
		||||
                    if (Q('endNever').checked) o.endAt = null;
 | 
			
		||||
                    else {
 | 
			
		||||
                        o.endAt = Date.parse(Q('endDate').value + ' ' + Q('endTime').value);
 | 
			
		||||
                        o.endAt = Math.floor(o.endAt / 1000);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'weekly':
 | 
			
		||||
                    o.startAt = Date.parse(Q('startDate').value + ' ' + Q('startTime').value);
 | 
			
		||||
                    o.startAt = Math.floor(o.startAt / 1000);
 | 
			
		||||
                    o.interval = Number(Q('interval').value);
 | 
			
		||||
                    if (Q('endNever').checked) o.endAt = null;
 | 
			
		||||
                    else {
 | 
			
		||||
                        o.endAt = Date.parse(Q('endDate').value + ' ' + Q('endTime').value);
 | 
			
		||||
                        o.endAt = Math.floor(o.endAt / 1000);
 | 
			
		||||
                    }
 | 
			
		||||
                    var dayEls = document.getElementsByName("days[]");
 | 
			
		||||
                    o.dayVals = [];
 | 
			
		||||
                    if (dayEls.length) {
 | 
			
		||||
                        dayEls.forEach(function (de) {
 | 
			
		||||
                            if (de.checked) o.dayVals.push(de.value);
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            o.scriptId = scriptId;
 | 
			
		||||
            o.nodes = nodes;
 | 
			
		||||
 | 
			
		||||
            window.opener.schedCallback(o);
 | 
			
		||||
            window.close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function goCancel() {
 | 
			
		||||
            window.close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    </script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,73 +0,0 @@
 | 
			
		|||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <script type="text/javascript" src="scripts/common-0.0.1.js"></script>
 | 
			
		||||
    <style>
 | 
			
		||||
        body {
 | 
			
		||||
            font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
 | 
			
		||||
            color: white;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #scriptContent {
 | 
			
		||||
            width: 80%;
 | 
			
		||||
            height: 80%;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #scriptContentC {
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #controlBar button {
 | 
			
		||||
            cursor: pointer;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #scriptNameC {
 | 
			
		||||
            padding: 20px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #scriptName {
 | 
			
		||||
            width: 300px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #controlBar {
 | 
			
		||||
            padding: 5px;
 | 
			
		||||
            padding-left: 20px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        body {
 | 
			
		||||
            background-color: #036;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body onload="doOnLoad();">
 | 
			
		||||
    <div id="scriptTaskScriptEdit">
 | 
			
		||||
        <div id="scriptNameC">Script Name: <input type="text" value="" id="scriptName" /></div>
 | 
			
		||||
        <div id="controlBar">
 | 
			
		||||
            <button onclick="goSave();">Save</button>
 | 
			
		||||
            <button onclick="goClose();">Close</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div id="scriptContentC">
 | 
			
		||||
            <textarea id="scriptContent"></textarea>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <script type="text/javascript">
 | 
			
		||||
        var scriptData = {{{scriptData}}};
 | 
			
		||||
 | 
			
		||||
        function doOnLoad() {
 | 
			
		||||
            //QH('scriptContent', scriptData.content);
 | 
			
		||||
            Q('scriptContent').value = scriptData.content;
 | 
			
		||||
            Q('scriptName').value = scriptData.name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function goSave() {
 | 
			
		||||
            scriptData.content = Q('scriptContent').value;
 | 
			
		||||
            scriptData.name = Q('scriptName').value;
 | 
			
		||||
            window.opener.callback(scriptData);
 | 
			
		||||
            //goClose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function goClose() {
 | 
			
		||||
            window.close();
 | 
			
		||||
        }
 | 
			
		||||
    </script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue