Skip to content

Commit

Permalink
rewriting how tasker JS works
Browse files Browse the repository at this point in the history
  • Loading branch information
mtwebit committed Nov 3, 2020
1 parent f26cbb8 commit 5cd6304
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 146 deletions.
97 changes: 34 additions & 63 deletions TaskerAdmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,24 @@
*
* Performs API calls to query task status / execute command etc.
*
* Copyright 2017-2019 Tamas Meszaros <mt+git@webit.hu>
* Copyright 2017-2020 Tamas Meszaros <mt+git@webit.hu>
* This file licensed under Mozilla Public License v2.0 http://mozilla.org/MPL/2.0/
*/

/*
$(document).ready(function() {
if ( $("#tasker").length) tasker_client();
});
function tasker_client() {
tasker = $("#tasker");
// for each running task create and display a progressbar
tasker.children('div[run]').each(function() {
var progressbar = $(this).children('div.progress-bar').first(),
progressLabel = progressbar.children('div').first(),
taskId = $(this).attr('taskid');
if (!progressbar) return;
// create a progressbar for the task
progressbar.progressbar({
value: false,
change: function() {
progressLabel.text(progressbar.progressbar( "value" ) + "% done.");
},
});
// call the API with the specified command
progressLabel.text("Querying status...");
performApiCall(ProcessWire.config.tasker.apiUrl + '/?cmd=run&id=' + taskId, statusCallback, progressbar);
});
}
*/

// Execute a command on a given task using backend API calls
function TaskerAction(taskId, command) {
taskerAdminApiUrl = ProcessWire.config.tasker.apiUrl,
taskerAdminAPIUrl = ProcessWire.config.tasker.apiUrl,
taskdiv = $("div[taskid='" + taskId + "']"),
taskdiv.children("ul.actions").hide();
taskdiv.children("i.fa").hide();
taskdiv.children('span.TaskTitle').prepend('<b>Calling ' + command + ':</b> ');
performApiCall(taskerAdminApiUrl + '/?id=' + taskId + '&cmd=' + command, statusCallback);

performAPICall(taskerAdminAPIUrl + '/?id=' + taskId + '&cmd=' + command, statusCallback, taskdiv);
}

// perform a HTTP AJAX (JSON) request and handle errors if necessary
function performApiCall(url, callback, progressbar) {
var debuglog = $("#tasker").next("div.NoticeMessages"),
// perform a HTTP request to the TaskerAdmin API and display the results
function performAPICall(url, callback, taskdiv) {
var msgText = taskdiv.children("span.TaskTitle"),
timeout = ProcessWire.config.tasker.timeout + 500, // add some extra time
unloading = false;

Expand All @@ -64,15 +34,13 @@ function performApiCall(url, callback, progressbar) {
success: callback,
timeout: timeout,
error: function(jqXHR, status, errorThrown) {
var progressLabel = progressbar.children('div').first(),
taskdiv = progressbar.parent();
taskdiv.children("ul.actions").remove(); // remove the action buttons on error
if (status == 'timeout') {
progressLabel.text("Request timeout. Please check the backend for more info.");
msgText.append(" <b>ERROR:</b> Request timeout. Please check the backend for more info.");
} else if (unloading) {
progressLabel.text("Cancelling request...");
msgText.append(" Cancelling request...");
} else {
progressLabel.text("Error receiving response from the server: " + status);
msgText.append(" <b>ERROR:</b> Invalid response from the server: " + status);
}
}
});
Expand All @@ -81,53 +49,56 @@ function performApiCall(url, callback, progressbar) {
// callback for HTTP requests
function statusCallback(data) {
var taskId = data['taskid'],
debuglog = $("#tasker").next("div.NoticeMessages"),
taskdiv = $("div[taskid='" + taskId + "']"),
progressbar = $("div[taskid='" + taskId + "'] > div"),
progressLabel = progressbar.children('div').first(),
run = taskdiv.attr('run'),
repeatTime = taskdiv.attr('repeatTime');

if (!data['status']) { // return status is not OK
progressbar.progressbar("value", false);
progressLabel.text("Error: " + data["result"]);
if (debuglog.length) debuglog.append(data['log']);
taskdiv.children("span.TaskTitle").append(' <b>ERROR: Command failed</b>');
return;
}
msgText = taskdiv.children("span.TaskTitle"),
progressbar = taskdiv.children("div.progressbar");

if (data['status_html'] == '') {
// Task not found (e.g. trashed), remove it from the tasklist
taskdiv.remove();
return;
}

// replace the task div with the new html code
// replace the task div with the new HTML content
taskdiv.replaceWith(data['status_html']);

if (debuglog.length) {
debuglog.html(data['log']);
if (!data['status']) { // return status is not OK
if (progressbar.length) progressbar.remove();
return;
}

if (!progressbar) return;
// update the variables from the newly received content
taskdiv = $("div[taskid='" + taskId + "']");
progressbar = taskdiv.children("div.progressbar");

if (!progressbar.length) return;

// create a progressbar for the task
run = taskdiv.attr('run'),
repeatTime = taskdiv.attr('repeatTime');

progressLabel = progressbar.children('div.progress-label');

// initialize a progressbar for the task
progressbar.progressbar({
value: false,
change: function() {
progressLabel.text(progressbar.progressbar( "value" ) + "% done.");
},
});

progressLabel.text("Querying task's status...");

progressbar.progressbar("value", data['progress']);

if (run) { // we're executing the task
// TODO don't execute the task if it is stopped by another request

if (run) { // we're executing the task and it is still active
if (repeatTime > 0) {
setTimeout(function() {
performApiCall(ProcessWire.config.tasker.apiUrl + '/?cmd=run&id=' + taskId, statusCallback, progressbar);
performAPICall(ProcessWire.config.tasker.apiUrl + '/?cmd=run&id=' + taskId, statusCallback, taskdiv);
}, 1000 * Number(repeatTime));
} else {
performApiCall(ProcessWire.config.tasker.apiUrl + '/?cmd=run&id=' + taskId, statusCallback, progressbar);
performAPICall(ProcessWire.config.tasker.apiUrl + '/?cmd=run&id=' + taskId, statusCallback, taskdiv);
}
}
}
113 changes: 30 additions & 83 deletions TaskerAdmin.module.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,79 +63,24 @@ public function init() {
* Process module endpoints
*
* Module routing under <admin>/page/tasks
* admin page - loc: /[?id=$taskId&cmd=$command] - execute() - display tasks or execute a command or a task
* JSON API - loc: /api/?id=$taskId&cmd=$command - executeApi() - execute an api call or a task and return a JSON object
*
* More info about routing:
* https://processwire.com/talk/topic/7832-module-routing-executesomething-versus-this-input-urlsegment1-in-process-modules/
**********************************************************************/
/**
* Execute the main function for the admin menu interface
* Main function for the admin menu interface
*/
public function execute() {
list ($command, $taskId, $params) = $this->analyzeRequestURI($_SERVER['REQUEST_URI']);
if ($command !== false) {
// the run command will display it's own page
if ($command == 'run') return $this->runCommand($command, $taskId, $params);
$out = '<h2>Executing command '.$command.'</h2>';
$out .= '<p>'.$this->runCommand($command, $taskId, $params).'</p>';
} else $out = '';

$out .= '<h2>Task management</h2>';
$out = '<h2>Task management</h2>';
$out .= $this->renderTaskList();

$out .= '<p><a href="'.$this->page->url.'">Refresh this page.</a></p>';
$out .= '<p><a href="'.$this->page->url.'">Refresh this list.</a></p>';
return $out;
}


/**
* Execute a command
*
* @param $command string command
* @param $taskId int ID of the task Page
* @param $params assoc array of query arguments
*
*/
public function runCommand($command, $taskId, $params) {
$tasker = wire('modules')->get('Tasker');
$task = $tasker->getTaskById($taskId);
if ($task instanceof NullPage) {
$this->error('Task not found.');
return;
}

switch ($command) {
case 'activate': // activate the task (will be executed by Cron or LazyCron)
return ($tasker->activateTask($task) ? 'Starting task ' : 'Failed to start ').$task->title;
case 'suspend': // suspend the task but keep its progress
return ($tasker->stopTask($task) ? 'Suspending ' : 'Failed to suspend ').$task->title;
case 'reset': // reset progress, clear the logs but keep the task active (restart)
return ($tasker->stopTask($task, false, true) ? 'Resetting ' : 'Failed to reset ').$task->title;
case 'kill': // stop the task, but keep progress and log messages
return ($tasker->stopTask($task, true, false) ? 'Killed task ' : 'Failed to kill ').$task->title;
case 'trash': // delete the task
return ($tasker->trashTask($task, $params) ? 'Removed task ' : 'Failed to trash ').$task->title;
case 'run': // execute and monitor the task right now, see below
break;
default:
return 'Unknown command: '.$command;
}

// start the task (set status to Active)
$tasker->activateTask($task);

// render only this task and put a message low below it
$out = '<h2>Executing and monitoring task: '.$task->title.'</h2>';
$out .= $this->renderTaskList('id='.$task->id, '', '', 'run');

return $out;
// the task will be executed by Javascript functions
}


/**
* Public admin API functions over HTTP (/api)
* HTTP JSON API endpoint
* URI structure: .... api/?id=taskId&cmd=command[&arguments]
*/
public function executeApi() {
Expand Down Expand Up @@ -215,9 +160,9 @@ public function executeApi() {
$ret['status'] = true;
$ret['result'] = $task->title;
break;
case 'restart': // reset progress then start the task
case 'restart': // reset progress then activate the task
$tasker->resetProgress($task);
case 'activate': // activate the task, will be started/continued by Cron or LazyCron
case 'activate': // activate the task (will be run by Cron or LazyCron)
$ret['status'] = true;
$ret['result'] = $tasker->activateTask($task);
$ret['task_running'] = $task->task_running;
Expand All @@ -238,18 +183,19 @@ public function executeApi() {
exit;
}
$ret['status'] = true;
// change the command to 'run' to activate the JS backend executor
$ret['result'] = $tasker->activateTask($task);
// change the command to 'run' to activate the JS backend executor during tasklist rendering
$command = 'run';
break;
case 'reset': // reset progress and logs but keep it running
case 'reset': // reset progress and logs
$ret['status'] = true;
$tasker->stopTask($task, false, true);
break;
case 'suspend': // stop the task but keep progress
$ret['status'] = true;
$tasker->stopTask($task);
break;
case 'kill': // reset progress and stop the task
case 'kill': // stop the running task
$ret['status'] = true;
$tasker->stopTask($task, true, true);
break;
Expand All @@ -271,9 +217,15 @@ public function executeApi() {
$ret['task_state'] = $task->task_state;
$ret['task_state_info'] = $this->stateInfo[$task->task_state];
$ret['log'] .= "\n<ul class='NoticeMessages'>\n";
// return the PW notice messages in the debug log
foreach(wire('notices') as $msg) {
if (strlen(trim($msg))) $ret['log'] .= " <li>$msg</li>\n";
}
/* skip the full task log
foreach (explode("\n", $task->log_messages) as $msg) {
$ret['log'] .= " <li>$msg</li>\n";
if (strlen(trim($msg))) $ret['log'] .= " <li>$msg</li>\n";
}
*/
$ret['log'] .= "</ul>\n";
echo json_encode($ret);
exit; // don't output anything else
Expand Down Expand Up @@ -339,7 +291,7 @@ public function renderTaskStatus($task, $liClass='', $aClass='', $jsCommand='sta
$icon = 'fa-clock-o'; // TODO 'fa-hourglass' or 'fa-spinner' ?
$actions = array('activate' => 'Activate');
if ($this->enableWebRun) { // if Web-based task execution is enabled
$actions['run'] = 'Run now';
$actions['start'] = 'Run now';
}
if ($task->task_running) $taskState = 'preempting';
break;
Expand All @@ -350,32 +302,34 @@ public function renderTaskStatus($task, $liClass='', $aClass='', $jsCommand='sta
if (!$task->task_running && $this->enableWebRun) {
if ($jsCommand == 'run') {
$out .= ' run="1"'; // instruct the JS route to run the task
$taskState = 'running'; // display the running state
} else {
$actions['run'] = 'Execute via Web'; // add a run action
$actions['start'] = 'Run now'; // add a run action
}
}
if ($task->task_running) $taskState = 'running';
else $taskState = 'ready to run';
} else if ($task->task_running) $taskState = 'running';
break;
case Tasker::taskFinished:
$icon = 'fa-check';
$actions['reset'] = 'Reset';
if ($jsCommand == 'run') $jsCommand=''; // clear the run command if the task is finished
break;
case Tasker::taskKilled:
$icon = 'fa-hand-stop-o';
if ($task->task_running) $taskState = 'killing';
if ($jsCommand == 'run') $jsCommand=''; // clear the run command if the task is killed
break;
case Tasker::taskFailed:
$icon = 'fa-warning';
$actions = array('activate' => 'Try again');
if ($jsCommand == 'run') $jsCommand=''; // clear the run command if the task is failed
break;
default:
$icon = 'fa-question';
}

// Add a reset button if the task has made any progress
if ($task->progress > 0) {
$logSummary = ' ('.$tasker->getLogSummary($task).')';
$logSummary = ' ('.$tasker->getLogSummary($task, true, true).')';
$actions['reset'] = 'Reset';
}

Expand All @@ -393,15 +347,9 @@ public function renderTaskStatus($task, $liClass='', $aClass='', $jsCommand='sta
';

foreach ($actions as $cmd => $title) {
if ($jsCommand == $cmd) continue; // we're already executing the command, skip its menu element
if ($cmd == 'run') { // insert a link to the admin page to run the task using JS calls
$out .= '<li style="display: inline !important;"'.$liClass.'>
<a href="'.$this->adminUrl.'?id='.$task->id.'&cmd=run"'.$aClass.'>'.$title."</a>
</li>\n";
} else { // stay on the current page and execute these commands using background JS calls
$out .= '<li style="display: inline !important;"'.$liClass.'>
<span><a onclick="TaskerAction(\''.$task->id.'\', \''.$cmd.'\')"'.$aClass.'>'.$title."</a></span>\n</li>\n";
}
if ($jsCommand == $cmd) continue; // skip the active command
$out .= '<li style="display: inline !important;"'.$liClass.'>
<span><a onclick="TaskerAction(\''.$task->id.'\', \''.$cmd.'\')"'.$aClass.'>'.$title."</a></span>\n</li>\n";
}

// insert a link to the task's edit page to show its details
Expand All @@ -411,9 +359,8 @@ public function renderTaskStatus($task, $liClass='', $aClass='', $jsCommand='sta

$out .= "\n</ul>\n";

// add a progress bar to running tasks
if ($jsCommand == 'run' || $task->task_running) {
$out .= '<div class="progress-bar"><div class="progress-label"></div></div>';
if ($jsCommand == 'run') { // if the task is running
$out .= '<div class="progressbar"><div class="progress-label"></div></div>';
}

return $out . "\n</div>\n";
Expand Down

0 comments on commit 5cd6304

Please sign in to comment.