Skip to content

Implement Process error table #136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions amd/build/tablebulkactions.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions amd/build/tablebulkactions.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 70 additions & 0 deletions amd/src/tablebulkactions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Javascript controller for checkboxed table.
* @module tool_lifecycle/tablebulkactions
* @copyright 2021 Justus Dieckmann WWU
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

/**
* Helper function to redirect via POST
* @param {String} url redirect to
* @param {Array} data redirect with data
*/
function redirectPost(url, data) {
const form = document.createElement('form');
document.body.appendChild(form);
form.method = 'post';
form.action = url;
for (const pair of data) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = pair.k;
input.value = pair.v;
form.appendChild(input);
}
form.submit();
}

/**
* Init function
*/
export function init() {
const checkboxes = document.querySelectorAll('input[name="procerror-select"]');

const action = document.querySelectorAll('*[data-lifecycle-action]');
action.forEach((a) => {
a.onclick = (e) => {
e.preventDefault();
let data = [
{k: 'action', v: a.getAttribute('data-lifecycle-action')},
{k: 'sesskey', v: M.cfg.sesskey}
];
if (a.getAttribute('data-lifecycle-forall') === '1') {
data.push({k: 'all', v: '1'});
redirectPost(window.location, data);
} else {
checkboxes.forEach((c) => {
if (c.checked) {
data.push({k: 'id[]', v: c.value});
}
});
redirectPost(window.location, data);
}
};
});
}
66 changes: 66 additions & 0 deletions classes/local/manager/process_manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
namespace tool_lifecycle\local\manager;

use core\event\course_deleted;
use Exception;
use tool_lifecycle\local\entity\process;
use tool_lifecycle\event\process_proceeded;
use tool_lifecycle\event\process_rollback;
Expand Down Expand Up @@ -245,4 +246,69 @@ public static function abort_process($process) {
$steplib->abort_course($process);
self::remove_process($process);
}

/**
* Moves a process into the procerror table.
*
* @param process $process The process
* @param Exception $e The exception
* @return void
*/
public static function insert_process_error(process $process, Exception $e) {
global $DB;

$procerror = (object) clone $process;
$procerror->errormessage = get_class($e) . ': ' . $e->getMessage();
$procerror->errortrace = $e->getTraceAsString();
$procerror->errortime = time();
$m = '';
foreach ($e->getTrace() as $v) {
$m .= $v['file'] . ':' . $v['line'] . '::';
}
$procerror->errorhash = md5($m);
$procerror->waiting = intval($procerror->waiting);

$DB->insert_record_raw('tool_lifecycle_proc_error', $procerror, false, false, true);
$DB->delete_records('tool_lifecycle_process', ['id' => $process->id]);
}

/**
* Proceed process from procerror back into the process board.
* @param int $processid the processid
* @return void
*/
public static function proceed_process_after_error(int $processid) {
global $DB;
$process = $DB->get_record('tool_lifecycle_proc_error', ['id' => $processid]);
// Unset process error entries.
unset($process->errormessage);
unset($process->errortrace);
unset($process->errorhash);
unset($process->errortime);

$DB->insert_record_raw('tool_lifecycle_process', $process, false, false, true);
$DB->delete_records('tool_lifecycle_proc_error', ['id' => $process->id]);
}

/**
* Rolls back a process from procerror table
* @param int $processid the processid
* @return void
*/
public static function rollback_process_after_error(int $processid) {
global $DB;

$process = $DB->get_record('tool_lifecycle_proc_error', ['id' => $processid]);
// Unset process error entries.
unset($process->errormessage);
unset($process->errortrace);
unset($process->errorhash);
unset($process->errortime);

$DB->insert_record_raw('tool_lifecycle_process', $process, false, false, true);
$DB->delete_records('tool_lifecycle_proc_error', ['id' => $process->id]);

delayed_courses_manager::set_course_delayed_for_workflow($process->courseid, true, $process->workflowid);
self::rollback_process($process);
}
}
221 changes: 221 additions & 0 deletions classes/local/table/process_errors_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Table listing all process errors
*
* @package tool_lifecycle
* @copyright 2021 Justus Dieckmann WWU
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_lifecycle\local\table;

defined('MOODLE_INTERNAL') || die;

require_once($CFG->libdir . '/tablelib.php');

/**
* Table listing all process errors
*
* @package tool_lifecycle
* @copyright 2021 Justus Dieckmann WWU
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class process_errors_table extends \table_sql {

/**
* @var array "cached" lang strings
*/
private $strings;

/**
* Constructor for delayed_courses_table.
*
* @throws \coding_exception
*/
public function __construct() {
global $OUTPUT;

parent::__construct('tool_lifecycle-process_errors');

$this->strings = [
'proceed' => get_string('proceed', 'tool_lifecycle'),
'rollback' => get_string('rollback', 'tool_lifecycle')
];

$fields = 'c.fullname as course, w.title as workflow, s.instancename as step, pe.*';

$from = '{tool_lifecycle_proc_error} pe ' .
'JOIN {tool_lifecycle_workflow} w ON pe.workflowid = w.id ' .
'JOIN {tool_lifecycle_step} s ON pe.workflowid = s.workflowid AND pe.stepindex = s.sortindex ' .
'LEFT JOIN {course} c ON pe.courseid = c.id ';

$this->set_sql($fields, $from, 'TRUE');
$this->column_nosort = ['select', 'tools'];
$this->define_columns(['select', 'workflow', 'step', 'courseid', 'course', 'error', 'tools']);
$this->define_headers([
$OUTPUT->render(new \core\output\checkbox_toggleall('procerrors-table', true, [
'id' => 'select-all-procerrors',
'name' => 'select-all-procerrors',
'label' => get_string('selectall'),
'labelclasses' => 'sr-only',
'classes' => 'm-1',
'checked' => false,
])),
get_string('workflow', 'tool_lifecycle'),
get_string('step', 'tool_lifecycle'),
get_string('courseid', 'tool_lifecycle'),
get_string('course'),
get_string('error'),
get_string('tools', 'tool_lifecycle')
]);
}

/**
* Render error column.
*
* @param object $row Row data.
* @return string error cell
* @throws \coding_exception
* @throws \moodle_exception
*/
public function col_error($row) {
return "<details><summary>" .
nl2br(htmlentities($row->errormessage)) .
"</summary><code>" .
nl2br(htmlentities($row->errortrace)) .
"</code></details>";
}

/**
* Render tools column.
*
* @param object $row Row data.
* @return string pluginname of the subplugin
* @throws \coding_exception
* @throws \moodle_exception
*/
public function col_tools($row) {
global $OUTPUT;

$actionmenu = new \action_menu();
$actionmenu->add_primary_action(
new \action_menu_link_primary(
new \moodle_url('', ['action' => 'proceed', 'id[]' => $row->id, 'sesskey' => sesskey()]),
new \pix_icon('e/tick', $this->strings['proceed']),
$this->strings['proceed']
)
);
$actionmenu->add_primary_action(
new \action_menu_link_primary(
new \moodle_url('', ['action' => 'rollback', 'id[]' => $row->id, 'sesskey' => sesskey()]),
new \pix_icon('e/undo', $this->strings['rollback']),
$this->strings['rollback']
)
);
return $OUTPUT->render($actionmenu);
}

/**
* Generate the select column.
*
* @param \stdClass $data
* @return string
*/
public function col_select($data) {
global $OUTPUT;

$checkbox = new \core\output\checkbox_toggleall('procerrors-table', false, [
'classes' => 'usercheckbox m-1',
'id' => 'procerror' . $data->id,
'name' => 'procerror-select',
'value' => $data->id,
'checked' => false,
'label' => get_string('selectitem', 'moodle', $data->id),
'labelclasses' => 'accesshide',
]);

return $OUTPUT->render($checkbox);
}

/**
* Override the table show_hide_link to not show for select column.
*
* @param string $column the column name, index into various names.
* @param int $index numerical index of the column.
* @return string HTML fragment.
*/
protected function show_hide_link($column, $index) {
if ($index > 0) {
return parent::show_hide_link($column, $index);
}
return '';
}

/**
* Show custom nothing to display message.
* @return void
*/
public function print_nothing_to_display() {
global $OUTPUT;

// Render the dynamic table header.
echo $this->get_dynamic_table_html_start();

// Render button to allow user to reset table preferences.
echo $this->render_reset_button();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have already asked Aaron about this. Do we really need these buttons for an empty table? We have seen that in multiple plugins but why is it necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know, I just copied the print_nothing_to_display() function and edited the string

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wird schon seine Gründe haben


$this->print_initials_bar();

echo $OUTPUT->heading(get_string('noprocesserrors', 'tool_lifecycle'));

// Render the dynamic table footer.
echo $this->get_dynamic_table_html_end();
}

/**
* Hook that can be overridden in child classes to wrap a table in a form
* for example. Called only when there is data to display and not
* downloading.
*/
public function wrap_html_finish() {
global $OUTPUT;
parent::wrap_html_finish();
echo "<br>";

$actionmenu = new \action_menu();
$actionmenu->add_secondary_action(
new \action_menu_link_secondary(
new \moodle_url(''),
new \pix_icon('e/tick', $this->strings['proceed']),
$this->strings['proceed'],
['data-lifecycle-action' => 'proceed']
)
);

$actionmenu->add_secondary_action(
new \action_menu_link_secondary(
new \moodle_url(''),
new \pix_icon('e/undo', $this->strings['rollback']),
$this->strings['rollback'],
['data-lifecycle-action' => 'rollback']
)
);

$actionmenu->set_menu_trigger(get_string('forselected', 'tool_lifecycle'));
echo $OUTPUT->render_action_menu($actionmenu);
}
}
Loading