diff --git a/activeprocesses.php b/activeprocesses.php index 6e413928f06d33104a6c7580fbb23ba0751fc409..7efdd38d6464aa05a376527fa9baaed0b6c3f7ff 100644 --- a/activeprocesses.php +++ b/activeprocesses.php @@ -23,24 +23,49 @@ */ require_once(__DIR__ . '/../../../config.php'); require_once($CFG->libdir . '/adminlib.php'); +require_login(); +\tool_lifecycle\permission_and_navigation::setup_active(); -$PAGE->set_context(context_system::instance()); -require_login(null, false); -require_capability('moodle/site:config', context_system::instance()); +$PAGE->set_url(new \moodle_url(\tool_lifecycle\urls::ACTIVE_PROCESSES)); -admin_externalpage_setup('tool_lifecycle_activeprocesses'); +$title = get_string('find_course_list_header', 'tool_lifecycle'); +$PAGE->set_title($title); +$PAGE->set_heading($title); +$PAGE->navbar->add($title, $PAGE->url); -$PAGE->set_url(new \moodle_url('/admin/tool/lifecycle/activeprocesses.php')); +$renderer = $PAGE->get_renderer('tool_lifecycle'); +$mform = new \tool_lifecycle\local\form\form_courses_filter(); -$table = new tool_lifecycle\local\table\active_processes_table('tool_lifecycle_active_processes'); +// Cache handling. +$cachekey = 'activeprocesses_filter'; +$cache = cache::make('tool_lifecycle', 'mformdata'); +if ($search = optional_param('search', null, PARAM_RAW)) { + $obj = new stdClass(); + $obj->fullname = $search; + $obj->courseid = null; + $obj->shortname = null; + $cache->set($cachekey, $obj); + redirect($PAGE->url); +} -$PAGE->set_title(get_string('active_processes_list_header', 'tool_lifecycle')); -$PAGE->set_heading(get_string('active_processes_list_header', 'tool_lifecycle')); +if ($mform->is_cancelled()) { + $cache->delete($cachekey); + redirect($PAGE->url); +} else if ($data = $mform->get_data()) { + $cache->set($cachekey, $data); +} else { + $data = $cache->get($cachekey); + if ($data) { + $mform->set_data($data); + } +} -$renderer = $PAGE->get_renderer('tool_lifecycle'); +$table = new tool_lifecycle\local\table\active_processes_table('tool_lifecycle_active_processes', $data); echo $renderer->header(); +$mform->display(); + $table->out(50, false); echo $renderer->footer(); diff --git a/activeworkflows.php b/activeworkflows.php new file mode 100644 index 0000000000000000000000000000000000000000..9ce9c0a2dfcb9ede7fbdb301ab20741589008ad8 --- /dev/null +++ b/activeworkflows.php @@ -0,0 +1,82 @@ +<?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/>. + +/** + * Displays the tables of active workflow definitions. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_lifecycle\local\table\active_automatic_workflows_table; +use tool_lifecycle\local\table\active_manual_workflows_table; +use tool_lifecycle\urls; + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_login(); + +global $OUTPUT, $PAGE, $DB; + +\tool_lifecycle\permission_and_navigation::setup_active(); + +$PAGE->set_url(new \moodle_url(urls::ACTIVE_WORKFLOWS)); + +$action = optional_param('action', null, PARAM_TEXT); +if ($action) { + $wfid = required_param('workflowid', PARAM_INT); + \tool_lifecycle\local\manager\workflow_manager::handle_action($action, $wfid); + redirect($PAGE->url); +} + +$PAGE->set_title(get_string('active_workflows_header', 'tool_lifecycle')); +$PAGE->set_heading(get_string('active_workflows_header', 'tool_lifecycle')); + +$renderer = $PAGE->get_renderer('tool_lifecycle'); + +echo $renderer->header(' '); + +echo $renderer->render_from_template('tool_lifecycle/search_input', [ + 'action' => (new moodle_url(urls::ACTIVE_PROCESSES))->out(false), + 'uniqid' => 'tool_lifecycle-search-courses', + 'inputname' => 'search', + 'extraclasses' => 'mb-3', + 'inform' => false, + 'searchstring' => 'Search for courses' +]); + +echo $OUTPUT->heading(get_string('active_automatic_workflows_heading', 'tool_lifecycle')); + +$table = new active_automatic_workflows_table('tool_lifecycle_active_automatic_workflows'); +echo $OUTPUT->box_start("lifecycle-enable-overflow lifecycle-table"); +$table->out(10, false); +echo $OUTPUT->box_end(); + +echo $OUTPUT->heading(get_string('active_manual_workflows_heading', 'tool_lifecycle')); + +$table = new active_manual_workflows_table('tool_lifecycle_manual_workflows'); +echo $OUTPUT->box_start("lifecycle-enable-overflow lifecycle-table"); +$table->out(10, false); +echo $OUTPUT->box_end(); + +echo \html_writer::link(new \moodle_url(urls::WORKFLOW_DRAFTS), + get_string('workflow_drafts_list', 'tool_lifecycle')); +echo '<br>'; +echo \html_writer::link(new \moodle_url(urls::DEACTIVATED_WORKFLOWS), + get_string('deactivated_workflows_list', 'tool_lifecycle')); + +echo $renderer->footer(); diff --git a/adminlib.php b/adminlib.php deleted file mode 100644 index fa1aaaf6eef4395ef4bbe75e084c4ca890db5e29..0000000000000000000000000000000000000000 --- a/adminlib.php +++ /dev/null @@ -1,687 +0,0 @@ -<?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/>. - -/** - * Admin lib providing multiple classes for admin settings. - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -namespace tool_lifecycle; - -use tool_lifecycle\local\entity\trigger_subplugin; -use tool_lifecycle\local\form\form_workflow_instance; -use tool_lifecycle\local\form\form_upload_workflow; -use tool_lifecycle\local\form\form_step_instance; -use tool_lifecycle\local\form\form_trigger_instance; -use tool_lifecycle\local\backup\restore_lifecycle_workflow; -use tool_lifecycle\local\manager\step_manager; -use tool_lifecycle\local\manager\settings_manager; -use tool_lifecycle\local\manager\trigger_manager; -use tool_lifecycle\local\entity\workflow; -use tool_lifecycle\local\entity\step_subplugin; -use tool_lifecycle\local\manager\workflow_manager; -use tool_lifecycle\local\table\active_manual_workflows_table; -use tool_lifecycle\local\table\workflow_definition_table; -use tool_lifecycle\local\table\active_automatic_workflows_table; -use tool_lifecycle\local\table\step_table; - -defined('MOODLE_INTERNAL') || die; - -require_once($CFG->libdir . '/adminlib.php'); -require_once(__DIR__ . '/lib.php'); - -/** - * External Page for showing active lifecycle processes - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class admin_page_active_processes extends \admin_externalpage { - - /** - * The constructor - calls parent constructor - * - * @throws \moodle_exception - */ - public function __construct() { - $url = new \moodle_url('/admin/tool/lifecycle/activeprocesses.php'); - parent::__construct('tool_lifecycle_activeprocesses', - get_string('active_processes_list_header', 'tool_lifecycle'), - $url); - } -} - -/** - * External Page for showing deactivated lifecycle workflows - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class admin_page_deactivated_workflows extends \admin_externalpage { - /** - * admin_page_deactivated_workflows constructor. - * @throws \coding_exception - * @throws \moodle_exception - */ - public function __construct() { - $url = new \moodle_url('/admin/tool/lifecycle/deactivatedworkflows.php'); - parent::__construct('tool_lifecycle_deactivatedworkflows', - get_string('deactivated_workflows_list_header', 'tool_lifecycle'), - $url); - } -} - -/** - * External Page for showing course backups - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class admin_page_course_backups extends \admin_externalpage { - - /** - * The constructor - calls parent constructor - * - * @throws \moodle_exception - */ - public function __construct() { - $url = new \moodle_url('/admin/tool/lifecycle/coursebackups.php'); - parent::__construct('tool_lifecycle_coursebackups', - get_string('course_backups_list_header', 'tool_lifecycle'), - $url); - } -} - -/** - * External Page for defining settings for subplugins - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class admin_page_sublugins extends \admin_externalpage { - - /** - * The constructor - calls parent constructor - * - * @throws \moodle_exception - */ - public function __construct() { - $url = new \moodle_url('/admin/tool/lifecycle/adminsettings.php'); - parent::__construct('tool_lifecycle_adminsettings', - get_string('adminsettings_heading', 'tool_lifecycle'), - $url); - } -} - -/** - * Class that handles the display and configuration the settings. - * - * @package tool_lifecycle - * @copyright 2015 Tobias Reischmann - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class admin_settings { - - /** @var object the url of the subplugin settings page */ - private $pageurl; - - /** - * Constructor for this subplugin settings - * @throws \moodle_exception - */ - public function __construct() { - global $PAGE; - $this->pageurl = new \moodle_url('/admin/tool/lifecycle/adminsettings.php'); - $PAGE->set_title(get_string('adminsettings_heading', 'tool_lifecycle')); - $PAGE->set_url($this->pageurl); - } - - /** - * Write the HTML for the submission plugins table. - * @throws \moodle_exception - */ - private function view_plugins_table() { - global $OUTPUT, $PAGE; - - // Set up the table. - $this->view_header(); - - echo $OUTPUT->heading(get_string('active_automatic_workflows_heading', 'tool_lifecycle')); - - $table = new active_automatic_workflows_table('tool_lifecycle_active_automatic_workflows'); - echo $OUTPUT->box_start("lifecycle-enable-overflow lifecycle-table"); - $table->out(10, false); - echo $OUTPUT->box_end(); - - echo $OUTPUT->heading(get_string('active_manual_workflows_heading', 'tool_lifecycle')); - - $table = new active_manual_workflows_table('tool_lifecycle_manual_workflows'); - echo $OUTPUT->box_start("lifecycle-enable-overflow lifecycle-table"); - $table->out(10, false); - echo $OUTPUT->box_end(); - - echo $OUTPUT->heading(get_string('workflow_definition_heading', 'tool_lifecycle')); - - echo $OUTPUT->single_button(new \moodle_url($PAGE->url, - array('action' => action::WORKFLOW_INSTANCE_FROM, 'sesskey' => sesskey())), - get_string('add_workflow', 'tool_lifecycle')); - - echo $OUTPUT->single_button(new \moodle_url($PAGE->url, - array('action' => action::WORKFLOW_UPLOAD_FROM, 'sesskey' => sesskey())), - get_string('upload_workflow', 'tool_lifecycle')); - - $table = new workflow_definition_table('tool_lifecycle_workflow_definitions'); - echo $OUTPUT->box_start("lifecycle-enable-overflow lifecycle-table"); - $table->out(10, false); - echo $OUTPUT->box_end(); - - $surl = new \moodle_url('/admin/tool/lifecycle/deactivatedworkflows.php', - array('sesskey' => sesskey())); - echo \html_writer::link($surl, get_string('deactivated_workflows_list', 'tool_lifecycle')); - - $this->view_footer(); - } - - /** - * Write the HTML for the add workflow form. - * - * @param form_workflow_instance $form - * @throws \coding_exception - */ - private function view_workflow_instance_form($form) { - global $OUTPUT; - - // Set up the table. - $this->view_header(); - - echo $OUTPUT->heading(get_string('adminsettings_edit_workflow_definition_heading', 'tool_lifecycle')); - - echo $form->render(); - - $this->view_footer(); - } - - /** - * Redirect to workflow details page. - * - * @param int $workflowid Id of the workflow. - * @throws \moodle_exception - */ - private function view_workflow_details($workflowid) { - $url = new \moodle_url('/admin/tool/lifecycle/workflowsettings.php', - array('workflowid' => $workflowid, 'sesskey' => sesskey())); - redirect($url); - } - - /** - * Write the page header - */ - private function view_header() { - global $OUTPUT; - // Print the page heading. - echo $OUTPUT->header(); - } - - /** - * Write the page footer - */ - private function view_footer() { - global $OUTPUT; - echo $OUTPUT->footer(); - } - - /** - * Check this user has permission to edit the subplugin settings - * @throws \coding_exception - * @throws \dml_exception - * @throws \moodle_exception - * @throws \require_login_exception - * @throws \required_capability_exception - */ - private function check_permissions() { - // Check permissions. - require_login(); - $systemcontext = \context_system::instance(); - require_capability('moodle/site:config', $systemcontext); - } - - /** - * This is the entry point for this controller class. - * @param string $action Action string (see {@see action}). - * @param int $workflowid Id of the workflow. - * @throws \coding_exception - * @throws \dml_exception - * @throws \moodle_exception - * @throws \require_login_exception - * @throws \required_capability_exception - */ - public function execute($action, $workflowid) { - global $PAGE; - $this->check_permissions(); - /** @var \tool_lifecycle_renderer $renderer */ - $renderer = $PAGE->get_renderer('tool_lifecycle'); - - // Has to be called before moodleform is created! - admin_externalpage_setup('tool_lifecycle_adminsettings'); - - workflow_manager::handle_action($action, $workflowid); - - $instanceform = new form_workflow_instance($PAGE->url, workflow_manager::get_workflow($workflowid)); - $uploadform = new form_upload_workflow($PAGE->url); - $PAGE->set_title(get_string('adminsettings_edit_workflow_definition_heading', 'tool_lifecycle')); - - if ($action === action::WORKFLOW_INSTANCE_FROM) { - $this->view_workflow_instance_form($instanceform); - } else if ($action === action::WORKFLOW_UPLOAD_FROM) { - $renderer->render_workflow_upload_form($uploadform); - } else { - $this->process_instance_form($instanceform); - $this->process_upload_form($uploadform); - $this->view_plugins_table(); - } - } - - /** - * Processes the instance form. - * First it checks, if it was submitted. If so, it store the respective data. - * @param form_workflow_instance $instanceform - * @throws \moodle_exception - */ - private function process_instance_form($instanceform) { - if ($instanceform->is_submitted() && !$instanceform->is_cancelled() && $data = $instanceform->get_submitted_data()) { - if ($data->id) { - $workflow = workflow_manager::get_workflow($data->id); - $workflow->title = $data->title; - $workflow->displaytitle = $data->displaytitle; - $workflow->rollbackdelay = $data->rollbackdelay; - $workflow->finishdelay = $data->finishdelay; - $workflow->delayforallworkflows = property_exists($data, 'delayforallworkflows') ? $data->delayforallworkflows : 0; - $newworkflow = false; - } else { - $workflow = workflow::from_record($data); - $newworkflow = true; - } - workflow_manager::insert_or_update($workflow); - // If a new workflow was created, redirect to details page to directly create a trigger. - if ($newworkflow) { - $this->view_workflow_details($workflow->id); - } - } - } - - /** - * Processes the upload form. - * First it checks, if it was submitted. If so, it starts the restore process with the uploaded file. - * @param form_upload_workflow $uploadform - * @throws \coding_exception - * @throws \moodle_exception - */ - private function process_upload_form($uploadform) { - global $PAGE; - if ($uploadform->is_submitted() && !$uploadform->is_cancelled() && $data = $uploadform->get_submitted_data()) { - $xmldata = $uploadform->get_file_content('backupfile'); - $restore = new restore_lifecycle_workflow($xmldata); - $errors = $restore->execute(); - if (count($errors) != 0) { - /** @var \tool_lifecycle_renderer $renderer */ - $renderer = $PAGE->get_renderer('tool_lifecycle'); - $renderer->render_workflow_upload_form($uploadform, $errors); - return; - } - } - } -} - -/** - * Class that handles the display and configuration of a workflow. - * - * @package tool_lifecycle - * @copyright 2015 Tobias Reischmann - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class workflow_settings { - - /** @var object the url of the subplugin settings page */ - private $pageurl; - - /** @var int id of the workflow the settings should be displayed for (null for new workflow). - */ - private $workflowid; - - /** - * Constructor for this subplugin settings - * @param int $workflowid Id of the workflow. - * @throws \coding_exception - * @throws \moodle_exception - */ - public function __construct($workflowid) { - global $PAGE; - // Has to be called before moodleform is created! - admin_externalpage_setup('tool_lifecycle_adminsettings'); - $this->pageurl = new \moodle_url('/admin/tool/lifecycle/workflowsettings.php'); - $PAGE->set_title(get_string('adminsettings_workflow_definition_steps_heading', 'tool_lifecycle')); - $PAGE->set_url($this->pageurl); - $this->workflowid = $workflowid; - } - - /** - * Write the HTML for the submission plugins table. - * @throws \moodle_exception - */ - private function view_plugins_table() { - global $OUTPUT, $PAGE; - - // Set up the table. - $this->view_header(); - - echo $OUTPUT->heading(get_string('adminsettings_workflow_definition_steps_heading', 'tool_lifecycle')); - - if (workflow_manager::is_editable($this->workflowid)) { - $triggers = trigger_manager::get_chooseable_trigger_types(); - echo $OUTPUT->single_select(new \moodle_url($PAGE->url, - array('action' => action::TRIGGER_INSTANCE_FORM, 'sesskey' => sesskey(), 'workflowid' => $this->workflowid)), - 'triggername', $triggers, '', array('' => get_string('add_new_trigger_instance', 'tool_lifecycle'))); - } - - if (workflow_manager::is_editable($this->workflowid)) { - $steps = step_manager::get_step_types(); - echo '<span class="ml-1"></span>'; - echo $OUTPUT->single_select(new \moodle_url($PAGE->url, - array('action' => action::STEP_INSTANCE_FORM, 'sesskey' => sesskey(), - 'workflowid' => $this->workflowid, 'class' => 'ml-1')), - 'stepname', $steps, '', array('' => get_string('add_new_step_instance', 'tool_lifecycle'))); - } - - $url = new \moodle_url('/admin/tool/lifecycle/adminsettings.php'); - echo \html_writer::start_tag('div', array('class' => 'd-inline-block')); - echo \html_writer::start_tag('form', array('action' => $url, 'method' => 'post', 'class' => 'form-inline')); - echo \html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())); - echo \html_writer::tag('button', get_string('back'), array('class' => 'btn btn-secondary ml-1')); - echo \html_writer::end_tag('form'); - echo \html_writer::end_tag('div'); - - $table = new step_table('tool_lifecycle_workflows', $this->workflowid); - $table->out(50, false); - - $this->view_footer(); - } - - /** - * Write the HTML for the step instance form. - * - * @param \moodleform $form Form to be displayed. - * @throws \coding_exception - */ - private function view_step_instance_form($form) { - $workflow = workflow_manager::get_workflow($this->workflowid); - $this->view_instance_form($form, - get_string('adminsettings_edit_step_instance_heading', 'tool_lifecycle', - $workflow->title)); - } - - /** - * Write the HTML for the trigger instance form. - * - * @param \moodleform $form Form to be displayed. - * @throws \coding_exception - */ - private function view_trigger_instance_form($form) { - $workflow = workflow_manager::get_workflow($this->workflowid); - $this->view_instance_form($form, - get_string('adminsettings_edit_trigger_instance_heading', 'tool_lifecycle', - $workflow->title)); - } - - /** - * Write the HTML for subplugin instance form with specific header. - * - * @param \moodleform $form Form to be displayed. - * @param string $header Header of the form. - */ - private function view_instance_form($form, $header) { - global $OUTPUT; - - // Set up the table. - $this->view_header(); - - echo $OUTPUT->heading($header); - - echo $form->render(); - - $this->view_footer(); - } - - /** - * Write the page header - */ - private function view_header() { - global $OUTPUT; - // Print the page heading. - echo $OUTPUT->header(); - } - - /** - * Write the page footer - */ - private function view_footer() { - global $OUTPUT; - echo $OUTPUT->footer(); - } - - /** - * Check this user has permission to edit the subplugin settings - * @throws \coding_exception - * @throws \dml_exception - * @throws \moodle_exception - * @throws \require_login_exception - * @throws \required_capability_exception - */ - private function check_permissions() { - // Check permissions. - require_login(); - $systemcontext = \context_system::instance(); - require_capability('moodle/site:config', $systemcontext); - } - - /** - * This is the entry point for this controller class. - * @param string $action Action string to be executed. - * @param int $subpluginid Id of the subplugin associated. - * @param int $workflowid Id of the workflow associated. - * @throws \coding_exception - * @throws \moodle_exception - */ - public function execute($action, $subpluginid, $workflowid) { - $this->check_permissions(); - - // Handle other actions. - step_manager::handle_action($action, $subpluginid, $workflowid); - trigger_manager::handle_action($action, $subpluginid, $workflowid); - workflow_manager::handle_action($action, $workflowid); - - if ($action === action::TRIGGER_INSTANCE_FORM) { - if ($this->handle_trigger_instance_form()) { - return; - } - } - - if ($action === action::STEP_INSTANCE_FORM) { - if ($this->handle_step_instance_form()) { - return; - } - } - // If no action handler has printed any form yet, display the plugins tables. - $this->view_plugins_table(); - } - - /** - * Handles actions for the trigger instance form and causes related forms to be rendered. - * - * @return bool True, if no further action handling or output should be conducted. - * @throws \coding_exception - * @throws \moodle_exception - */ - private function handle_trigger_instance_form() { - global $PAGE; - $subpluginname = null; - $triggertomodify = null; - $triggersettings = null; - - if (!$this->retrieve_trigger_parameters($triggertomodify, $subpluginname, $triggersettings)) { - return false; - } - - $form = new form_trigger_instance($PAGE->url, $this->workflowid, $triggertomodify, $subpluginname, $triggersettings); - - // Skip this part and continue with requiring a trigger if still null. - if (!$form->is_cancelled()) { - if ($form->is_submitted() && $form->is_validated() && $data = $form->get_submitted_data()) { - // In case the workflow was active, we do not allow changes to the steps or trigger. - if (!workflow_manager::is_editable($this->workflowid)) { - \core\notification::add( - get_string('active_workflow_not_changeable', 'tool_lifecycle'), - \core\notification::WARNING); - } else { - if (!empty($data->id)) { - $triggertomodify = trigger_manager::get_instance($data->id); - $triggertomodify->subpluginname = $data->subpluginname; - $triggertomodify->instancename = $data->instancename; - } else { - $triggertomodify = trigger_subplugin::from_record($data); - } - trigger_manager::insert_or_update($triggertomodify); - // Save local subplugin settings. - settings_manager::save_settings($triggertomodify->id, settings_type::TRIGGER, $data->subpluginname, $data); - } - return false; - } else { - $this->view_trigger_instance_form($form); - return true; - } - } - return false; - } - - /** - * Retrieves the relevant parameters for the trigger instance form from the sent params. - * Thereby it store the data in the given parameters. - * @param int $triggertomodify Id of the trigger instance to be modified. - * @param string $subpluginname Name of the subplugin, the trigger instance belongs to. - * @param array $triggersettings Settings of the trigger instance. - * @return bool - * @throws \coding_exception - * @throws \dml_exception - */ - private function retrieve_trigger_parameters(&$triggertomodify, &$subpluginname, &$triggersettings) { - if ($triggerid = optional_param('subplugin', null, PARAM_INT)) { - $triggertomodify = trigger_manager::get_instance($triggerid); - // If step was removed! - if (!$triggertomodify) { - return false; - } - $triggersettings = settings_manager::get_settings($triggerid, settings_type::TRIGGER); - } else if ($name = optional_param('subpluginname', null, PARAM_ALPHA)) { - $subpluginname = $name; - } else if ($name = optional_param('triggername', null, PARAM_ALPHA)) { - $subpluginname = $name; - } else { - return false; - } - return true; - } - - /** - * Handles actions for the trigger instance form and causes related forms to be rendered. - * - * @return bool True, if no further action handling or output should be conducted. - * @throws \coding_exception - * @throws \moodle_exception - */ - private function handle_step_instance_form() { - global $PAGE; - $steptomodify = null; - $subpluginname = null; - $stepsettings = null; - - if (!$this->retrieve_step_parameters($steptomodify, $subpluginname, $stepsettings)) { - return false; - } - - $form = new form_step_instance($PAGE->url, $steptomodify, $this->workflowid, $subpluginname, $stepsettings); - - if ($form->is_cancelled()) { - return false; - } else if ($form->is_submitted() && $form->is_validated() && $data = $form->get_submitted_data()) { - // In case the workflow was active, we do not allow changes to the steps or trigger. - if (!workflow_manager::is_editable($this->workflowid)) { - \core\notification::add( - get_string('active_workflow_not_changeable', 'tool_lifecycle'), - \core\notification::WARNING); - } - if (!empty($data->id)) { - $step = step_manager::get_step_instance($data->id); - if (isset($data->instancename)) { - $step->instancename = $data->instancename; - } - } else { - $step = step_subplugin::from_record($data); - } - step_manager::insert_or_update($step); - // Save local subplugin settings. - settings_manager::save_settings($step->id, settings_type::STEP, $form->subpluginname, $data, true); - } else { - $this->view_step_instance_form($form); - return true; - } - return false; - } - - /** - * Retrieves the relevant parameters for the step instance form from the sent params. - * Thereby it store the data in the given parameters. - * @param int $steptomodify Id of the step instance to be modified. - * @param string $subpluginname Name of the subplugin, the step instance belongs to. - * @param array $stepsettings Settings of the step instance. - * @return bool - * @throws \coding_exception - * @throws \dml_exception - */ - private function retrieve_step_parameters(&$steptomodify, &$subpluginname, &$stepsettings) { - if ($stepid = optional_param('subplugin', null, PARAM_INT)) { - $steptomodify = step_manager::get_step_instance($stepid); - // If step was removed! - if (!$steptomodify) { - return false; - } - $stepsettings = settings_manager::get_settings($stepid, settings_type::STEP); - } else if ($name = optional_param('subpluginname', null, PARAM_ALPHA)) { - $subpluginname = $name; - } else if ($name = optional_param('stepname', null, PARAM_ALPHA)) { - $subpluginname = $name; - } else { - return false; - } - return true; - } - - -} diff --git a/adminsettings.php b/adminsettings.php deleted file mode 100644 index 3a5f89be20edb23423ac16f71855be670de52047..0000000000000000000000000000000000000000 --- a/adminsettings.php +++ /dev/null @@ -1,36 +0,0 @@ -<?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/>. - -/** - * Displays the tables of active and inactive workflow definitions and handles all action associated with it. - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -require_once(__DIR__ . '/../../../config.php'); -require_once(__DIR__ . '/adminlib.php'); - -$PAGE->set_context(context_system::instance()); -require_login(null, false); -require_capability('moodle/site:config', context_system::instance()); - -// Create the class for this controller. -$adminsettings = new tool_lifecycle\admin_settings(); - -// Execute the controller. -$adminsettings->execute(optional_param('action', null, PARAM_TEXT), - optional_param('workflowid', null, PARAM_INT)); diff --git a/classes/local/backup/restore_lifecycle_workflow.php b/classes/local/backup/restore_lifecycle_workflow.php index 8662fa2c184c9d647d85c5737e7d3d10c8918c27..1cb3ff1c37848728b044906a2c48518b9d825c25 100644 --- a/classes/local/backup/restore_lifecycle_workflow.php +++ b/classes/local/backup/restore_lifecycle_workflow.php @@ -213,4 +213,15 @@ class restore_lifecycle_workflow { } } } + + /** + * Returns the workflow in case there were no errors. + * @return workflow + */ + public function get_workflow() { + if (empty($this->errors)) { + return $this->workflow; + } + return null; + } } diff --git a/classes/local/form/form_backups_filter.php b/classes/local/form/form_courses_filter.php similarity index 69% rename from classes/local/form/form_backups_filter.php rename to classes/local/form/form_courses_filter.php index 9abbe6c04f5fbf7b20a6d1614941b62a95ec4aca..f589bcea85dfa3c577e8e61dc5d191ef9a62e34d 100644 --- a/classes/local/form/form_backups_filter.php +++ b/classes/local/form/form_courses_filter.php @@ -34,7 +34,7 @@ require_once($CFG->libdir . '/formslib.php'); * @copyright 2021 Justus Dieckmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class form_backups_filter extends \moodleform { +class form_courses_filter extends \moodleform { /** * Defines forms elements @@ -42,13 +42,23 @@ class form_backups_filter extends \moodleform { public function definition() { $mform = $this->_form; + $mform->addElement('text', 'courseid', get_string('courseid', 'tool_lifecycle')); + $mform->setType('courseid', PARAM_ALPHANUM); + $mform->addRule('courseid', null, 'numeric', null, 'client'); + $mform->addElement('text', 'shortname', get_string('shortname')); $mform->setType('shortname', PARAM_TEXT); $mform->addElement('text', 'fullname', get_string('fullname')); $mform->setType('fullname', PARAM_TEXT); - $this->add_action_buttons(true, get_string('apply', 'tool_lifecycle')); + // Edited from $this->add_action_buttons to allow custom cancel text. + $buttonarray = array(); + $buttonarray[] = &$mform->createElement('submit', 'submitbutton', + get_string('apply', 'tool_lifecycle')); + $buttonarray[] = &$mform->createElement('cancel', 'cancel', get_string('reset')); + $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false); + $mform->closeHeaderBefore('buttonar'); } } diff --git a/classes/local/form/form_step_instance.php b/classes/local/form/form_step_instance.php index 8e64abce2d06b6b776024e0cc6f3e6842904626a..f080413d6c97d4fafe83a39e1957339d2c04dec4 100644 --- a/classes/local/form/form_step_instance.php +++ b/classes/local/form/form_step_instance.php @@ -70,13 +70,13 @@ class form_step_instance extends \moodleform { * Constructor * * @param \moodle_url $url . + * @param int $workflowid id of the step's workflow * @param step_subplugin $step step entity. - * @param int $workflowid if of the step's workflow * @param string $subpluginname name of the subplugin. * @param array $stepsettings settings of the step. * @throws \moodle_exception if neither step nor subpluginname are set. */ - public function __construct($url, $step, $workflowid, $subpluginname = null, $stepsettings = null) { + public function __construct($url, $workflowid, $step, $subpluginname = null, $stepsettings = null) { $this->step = $step; $this->workflowid = $workflowid; if ($step) { diff --git a/classes/local/manager/delayed_courses_manager.php b/classes/local/manager/delayed_courses_manager.php index a9c4024574a42d82f4a96fab1decd7e10df173ad..b2520a9b1196a7537083373901f06a1ec1e779b0 100644 --- a/classes/local/manager/delayed_courses_manager.php +++ b/classes/local/manager/delayed_courses_manager.php @@ -24,6 +24,8 @@ */ namespace tool_lifecycle\local\manager; +use tool_lifecycle\local\entity\workflow; + /** * Manager for Delayed Courses. * @@ -38,7 +40,7 @@ class delayed_courses_manager { * Sets a delay for a course for specific workflow. * @param int $courseid Id of the course. * @param bool $becauserollback True, if the delay is caused by a rollback. - * @param int $workfloworid Id of the workflow. + * @param int|workflow $workfloworid Id of the workflow. * @throws \dml_exception */ public static function set_course_delayed_for_workflow($courseid, $becauserollback, $workfloworid) { @@ -136,6 +138,17 @@ class delayed_courses_manager { return array($where, $params); } + /** + * Get the globally delayed courses. + * @return array array of course ids. + * @throws \dml_exception + */ + public static function get_globally_delayed_courses() { + global $DB; + $sql = 'SELECT courseid FROM {tool_lifecycle_delayed} WHERE delayeduntil > :now'; + return $DB->get_fieldset_sql($sql, array('now' => time())); + } + /** * Deletes the delay entry for a course. * @param int $courseid id of the course diff --git a/classes/local/manager/workflow_manager.php b/classes/local/manager/workflow_manager.php index 7a2ce4f2577fe3e4d148d8eb02936099894083d3..9a107aa115da6df9e5890c9d4b5c432bcebc757f 100644 --- a/classes/local/manager/workflow_manager.php +++ b/classes/local/manager/workflow_manager.php @@ -290,10 +290,6 @@ class workflow_manager { // If no action has been called. Continue. return; } - if (!defined('PHPUNIT_TEST') || !PHPUNIT_TEST) { - // In case of a called action, redirect to mainview. - redirect(new \moodle_url('/admin/tool/lifecycle/adminsettings.php')); - } } /** diff --git a/classes/local/table/active_processes_table.php b/classes/local/table/active_processes_table.php index 1ca21699c893998261122b218f1800381f444840..2c2a3c708c03288544464b361d6009c815ce834b 100644 --- a/classes/local/table/active_processes_table.php +++ b/classes/local/table/active_processes_table.php @@ -23,6 +23,8 @@ */ namespace tool_lifecycle\local\table; +use tool_lifecycle\urls; + defined('MOODLE_INTERNAL') || die; require_once($CFG->libdir . '/tablelib.php'); @@ -39,35 +41,57 @@ class active_processes_table extends \table_sql { /** * Constructor for active_processes_table. * @param int $uniqueid Unique id of this table. + * @param \stdClass|null $filterdata */ - public function __construct($uniqueid) { + public function __construct($uniqueid, $filterdata) { parent::__construct($uniqueid); - global $PAGE; - $this->set_attribute('class', $this->attributes['class'] . ' ' . $uniqueid); + global $PAGE, $DB; + $this->set_attribute('class', $this->attributes['class'] . ' lifecycle-table ' . $uniqueid); + + $where = ['TRUE']; + $params = []; + + if ($filterdata) { + if ($filterdata->shortname) { + $where[] = $DB->sql_like('c.shortname', ':shortname', false, false); + $params['shortname'] = '%' . $DB->sql_like_escape($filterdata->shortname) . '%'; + } + + if ($filterdata->fullname) { + $where[] = $DB->sql_like('c.fullname', ':fullname', false, false); + $params['fullname'] = '%' . $DB->sql_like_escape($filterdata->fullname) . '%'; + } + + if ($filterdata->courseid) { + $where[] = 'c.courseid = :courseid'; + $params['courseid'] = $filterdata->courseid; + } + } + $this->set_sql('c.id as courseid, ' . 'c.fullname as coursefullname, ' . 'c.shortname as courseshortname, ' . - 'instancename as instancename ', - '{tool_lifecycle_process} p join ' . - '{course} c on p.courseid = c.id join ' . - '{tool_lifecycle_step} s '. - 'on p.workflowid = s.workflowid AND p.stepindex = s.sortindex', - "TRUE"); + 'instancename as instancename, ' . + 's.id as stepid, ' . + 'w.title as workflow, ' . + 'w.displaytitle as wfdisplaytitle, ' . + 'w.id as wfid ', + '{tool_lifecycle_process} p ' . + 'JOIN {course} c ON p.courseid = c.id ' . + 'JOIN {tool_lifecycle_step} s ON p.workflowid = s.workflowid AND p.stepindex = s.sortindex ' . + 'JOIN {tool_lifecycle_workflow} w ON p.workflowid = w.id', + join(' AND ', $where), $params); $this->define_baseurl($PAGE->url); - $this->init(); - } - - /** - * Initialize the table. - */ - public function init() { - $this->define_columns(['courseid', 'courseshortname', 'coursefullname', 'instancename']); + $this->define_columns(['courseid', 'courseshortname', 'coursefullname', 'workflow', 'instancename', 'tools']); $this->define_headers([ get_string('course'), get_string('shortnamecourse'), get_string('fullnamecourse'), - get_string('step', 'tool_lifecycle')]); - $this->setup(); + get_string('workflow', 'tool_lifecycle'), + get_string('step', 'tool_lifecycle'), + get_string('tools', 'tool_lifecycle')]); + + $this->column_nosort = ['tools']; } /** @@ -98,12 +122,22 @@ class active_processes_table extends \table_sql { } /** - * Render instancename column. + * Render workflow column. * @param object $row Row data. - * @return string pluginname of the instance + * @return string Workflow title */ - public function col_instancename($row) { + public function col_workflow($row) { + return $row->workflow . '<br><span class="workflow_displaytitle">' . $row->wfdisplaytitle . '</span>'; + } - return $row->instancename; + /** + * Render tools column. + * @param object $row Row data. + * @return string Tools. + */ + public function col_tools($row) { + return \html_writer::link(new \moodle_url(urls::WORKFLOW_DETAILS, + ['wf' => $row->wfid, 'courseid' => $row->courseid, 'step' => $row->stepid] + ), get_string('see_in_workflow', 'tool_lifecycle'), ['class' => 'btn btn-secondary']); } } diff --git a/classes/local/table/active_workflows_table.php b/classes/local/table/active_workflows_table.php index 9a85218459d05435a0bfd09577ba705bd2a4796f..c3142d0301ec9e320e4f47a2d6389ab931249b9a 100644 --- a/classes/local/table/active_workflows_table.php +++ b/classes/local/table/active_workflows_table.php @@ -24,10 +24,8 @@ namespace tool_lifecycle\local\table; use tool_lifecycle\action; -use tool_lifecycle\local\manager\lib_manager; -use tool_lifecycle\local\manager\step_manager; -use tool_lifecycle\local\manager\trigger_manager; use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\urls; defined('MOODLE_INTERNAL') || die; @@ -57,17 +55,12 @@ abstract class active_workflows_table extends workflow_table { $alt = get_string('viewsteps', 'tool_lifecycle'); $icon = 't/viewdetails'; - $url = new \moodle_url('/admin/tool/lifecycle/workflowsettings.php', - array('workflowid' => $row->id, 'sesskey' => sesskey())); - $output .= $OUTPUT->action_icon($url, new \pix_icon($icon, $alt, 'moodle', array('title' => $alt)), + $overviewurl = new \moodle_url(urls::WORKFLOW_DETAILS, + array('wf' => $row->id)); + $output .= $OUTPUT->action_icon($overviewurl, new \pix_icon($icon, $alt, 'moodle', array('title' => $alt)), null, array('title' => $alt)); if (workflow_manager::is_disableable($row->id)) { - $action = action::WORKFLOW_DUPLICATE; - $alt = get_string('duplicateworkflow', 'tool_lifecycle'); - $icon = 't/copy'; - $output .= $this->format_icon_link($action, $row->id, $icon, $alt); - $action = action::WORKFLOW_BACKUP; $alt = get_string('backupworkflow', 'tool_lifecycle'); $icon = 't/backup'; @@ -75,7 +68,7 @@ abstract class active_workflows_table extends workflow_table { $alt = get_string('disableworkflow', 'tool_lifecycle'); $icon = 't/disable'; - $url = new \moodle_url('/admin/tool/lifecycle/deactivatedworkflows.php', + $url = new \moodle_url(urls::DEACTIVATED_WORKFLOWS, array('workflowid' => $row->id, 'action' => action::WORKFLOW_DISABLE, 'sesskey' => sesskey())); $confirmaction = new \confirm_action(get_string('disableworkflow_confirm', 'tool_lifecycle')); $output .= $OUTPUT->action_icon($url, @@ -85,7 +78,7 @@ abstract class active_workflows_table extends workflow_table { $alt = get_string('abortdisableworkflow', 'tool_lifecycle'); $icon = 't/stop'; - $url = new \moodle_url('/admin/tool/lifecycle/deactivatedworkflows.php', + $url = new \moodle_url(urls::DEACTIVATED_WORKFLOWS, array('workflowid' => $row->id, 'action' => action::WORKFLOW_ABORTDISABLE, 'sesskey' => sesskey())); $confirmaction = new \confirm_action(get_string('abortdisableworkflow_confirm', 'tool_lifecycle')); $output .= $OUTPUT->action_icon($url, diff --git a/classes/local/table/course_backups_table.php b/classes/local/table/course_backups_table.php index 678e3d1f5c68dd305781b934353b82b01ba3d782..6444969a7602ea989af6949e91d52cfda161bb21 100644 --- a/classes/local/table/course_backups_table.php +++ b/classes/local/table/course_backups_table.php @@ -50,15 +50,20 @@ class course_backups_table extends \table_sql { $params = []; if ($filterdata) { - if ($filterdata && $filterdata->shortname) { + if ($filterdata->shortname) { $where[] = $DB->sql_like('b.shortname', ':shortname', false, false); $params['shortname'] = '%' . $DB->sql_like_escape($filterdata->shortname) . '%'; } - if ($filterdata && $filterdata->fullname) { + if ($filterdata->fullname) { $where[] = $DB->sql_like('b.fullname', ':fullname', false, false); $params['fullname'] = '%' . $DB->sql_like_escape($filterdata->fullname) . '%'; } + + if ($filterdata->courseid) { + $where[] = 'b.courseid = :courseid'; + $params['courseid'] = $filterdata->courseid; + } } $this->set_sql('b.id, b.courseid, b.shortname as courseshortname, b.fullname as coursefullname, b.backupcreated', @@ -76,7 +81,7 @@ class course_backups_table extends \table_sql { public function init() { $this->define_columns(['courseid', 'courseshortname', 'coursefullname', 'backupcreated', 'download', 'restore']); $this->define_headers([ - get_string('course'), + get_string('courseid', 'tool_lifecycle'), get_string('shortnamecourse'), get_string('fullnamecourse'), get_string('backupcreated', 'tool_lifecycle'), diff --git a/classes/local/table/courses_in_step_table.php b/classes/local/table/courses_in_step_table.php new file mode 100644 index 0000000000000000000000000000000000000000..f23d1ef02b1a5c32e68833a25942315fec2dd1a8 --- /dev/null +++ b/classes/local/table/courses_in_step_table.php @@ -0,0 +1,175 @@ +<?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 courses for a specific step. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_lifecycle\local\table; + +use tool_lifecycle\local\entity\step_subplugin; +use tool_lifecycle\local\manager\interaction_manager; +use tool_lifecycle\local\manager\lib_manager; +use tool_lifecycle\local\manager\step_manager; + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->libdir . '/tablelib.php'); + +/** + * Table listing all courses for a specific step. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class courses_in_step_table extends \table_sql { + + /** @var int|null if set, it is the courseid to focus on. */ + private $courseid; + + /** + * Constructor for courses_in_step_table. + * @param step_subplugin $step step to show courses of + * @param int|null $courseid if supplied, courseid to focus on + */ + public function __construct($step, $courseid) { + parent::__construct('tool_lifecycle-courses-in-step'); + global $PAGE; + + $this->courseid = $courseid; + + $this->define_baseurl($PAGE->url); + $this->define_columns(['courseid', 'coursefullname', 'startdate', 'tools']); + $this->define_headers([ + get_string('courseid', 'tool_lifecycle'), + get_string('coursename', 'tool_lifecycle'), + get_string('startdate'), + get_string('tools', 'tool_lifecycle'), + ]); + + $fields = "p.id as processid, c.id as courseid, c.fullname as coursefullname, c.startdate, " . + "c.shortname as courseshortname, s.id as stepinstanceid, s.instancename as stepinstancename, s.subpluginname"; + $from = "{tool_lifecycle_process} p join " . + "{course} c on p.courseid = c.id join " . + "{tool_lifecycle_step} s ". + "on p.workflowid = s.workflowid AND p.stepindex = s.sortindex "; + + $where = "p.stepindex = :stepindex AND p.workflowid = :wfid"; + + $this->column_nosort = array('status', 'tools'); + $this->set_sql($fields, $from, $where, ['stepindex' => $step->sortindex, 'wfid' => $step->workflowid]); + if ($courseid) { + $this->set_sortdata([]); + } + } + + /** + * Sets the page number to the page where the courseid is located. + * @param int $pagesize pagesize, items per page. + */ + public function jump_to_course($pagesize) { + global $DB; + $params = $this->sql->params; + $params['courseid'] = $this->courseid; + $count = $DB->count_records_sql( + "SELECT COUNT (*) FROM {$this->sql->from} WHERE {$this->sql->where} AND c.id < :courseid", + $params); + $this->set_page_number(intval(ceil(($count + 1) / $pagesize))); + } + + /** + * Query the db. Store results in the table object for use by build_table. + * + * @param int $pagesize size of page for paginated displayed table. + * @param bool $useinitialsbar do you want to use the initials bar. Bar + * will only be used if there is a fullname column defined for the table. + */ + public function query_db($pagesize, $useinitialsbar = true) { + if ($this->courseid) { + $this->jump_to_course($pagesize); + } + parent::query_db($pagesize, $useinitialsbar); + } + + /** + * Get any extra classes names to add to this row in the HTML. + * @param object $row the data for this row. + * @return string added to the class="" attribute of the tr. + */ + public function get_row_class($row) { + if ($row->courseid == $this->courseid) { + return 'table-primary'; + } + return ''; + } + + /** + * Render coursefullname column. + * @param object $row Row data. + * @return string course link + */ + public function col_coursefullname($row) { + $courselink = \html_writer::link(course_get_url($row->courseid), format_string($row->coursefullname)); + return $courselink . '<br><span class="secondary-info">' . $row->courseshortname . '</span>'; + } + + /** + * Render startdate column. + * @param object $row Row data. + * @return string human readable date + * @throws \coding_exception + */ + public function col_startdate($row) { + if ($row->startdate) { + $dateformat = get_string('strftimedate', 'langconfig'); + return userdate($row->startdate, $dateformat); + } else { + return "-"; + } + } + + /** + * Render tools column. + * @param object $row Row data. + * @return string pluginname of the subplugin + * @throws \coding_exception + * @throws \invalid_parameter_exception + */ + public function col_tools($row) { + global $OUTPUT, $PAGE; + $output = ''; + $output .= $OUTPUT->single_button(new \moodle_url($PAGE->url, + ['action' => 'rollback', 'processid' => $row->processid, 'sesskey' => sesskey()]), + get_string('rollback', 'tool_lifecycle') + ); + $output .= $OUTPUT->single_button(new \moodle_url($PAGE->url, + ['action' => 'proceed', 'processid' => $row->processid, 'sesskey' => sesskey()]), + get_string('proceed', 'tool_lifecycle') + ); + return $output; + } + + /** + * Prints a customized "nothing to display" message. + */ + public function print_nothing_to_display() { + echo \html_writer::tag('h4', 'There are no courses in the selected step!', ['class' => 'm-2']); + } +} diff --git a/classes/local/table/deactivated_workflows_table.php b/classes/local/table/deactivated_workflows_table.php index 54ee0ebb36ddd43992e9cfb428d0b2ad8c781f34..e6d3d182d9938b05189100ed748472c9182f1e9e 100644 --- a/classes/local/table/deactivated_workflows_table.php +++ b/classes/local/table/deactivated_workflows_table.php @@ -27,6 +27,7 @@ use tool_lifecycle\action; use tool_lifecycle\local\manager\lib_manager; use tool_lifecycle\local\manager\trigger_manager; use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\urls; defined('MOODLE_INTERNAL') || die; @@ -87,25 +88,15 @@ class deactivated_workflows_table extends workflow_table { $alt = get_string('viewsteps', 'tool_lifecycle'); $icon = 't/viewdetails'; - $url = new \moodle_url('/admin/tool/lifecycle/workflowsettings.php', - array('workflowid' => $row->id, 'sesskey' => sesskey())); + $url = new \moodle_url(urls::WORKFLOW_DETAILS, + array('wf' => $row->id)); $output .= $OUTPUT->action_icon($url, new \pix_icon($icon, $alt, 'moodle', array('title' => $alt)), null, array('title' => $alt)); - $action = action::WORKFLOW_DUPLICATE; - $alt = get_string('duplicateworkflow', 'tool_lifecycle'); - $icon = 't/copy'; - $output .= $this->format_icon_link($action, $row->id, $icon, $alt); - - $action = action::WORKFLOW_INSTANCE_FROM; - $alt = get_string('editworkflow', 'tool_lifecycle'); - $icon = 't/edit'; - $output .= $this->format_icon_link($action, $row->id, $icon, $alt); - if (workflow_manager::is_abortable($row->id)) { $alt = get_string('abortprocesses', 'tool_lifecycle'); $icon = 't/stop'; - $url = new \moodle_url('/admin/tool/lifecycle/deactivatedworkflows.php', + $url = new \moodle_url(urls::DEACTIVATED_WORKFLOWS, array('workflowid' => $row->id, 'action' => action::WORKFLOW_ABORT, 'sesskey' => sesskey())); $confirmaction = new \confirm_action(get_string('abortprocesses_confirm', 'tool_lifecycle')); $output .= $OUTPUT->action_icon($url, @@ -113,13 +104,12 @@ class deactivated_workflows_table extends workflow_table { $confirmaction, array('title' => $alt) ); - } if (workflow_manager::is_removable($row->id)) { $alt = get_string('deleteworkflow', 'tool_lifecycle'); $icon = 't/delete'; - $url = new \moodle_url('/admin/tool/lifecycle/deactivatedworkflows.php', + $url = new \moodle_url(urls::DEACTIVATED_WORKFLOWS, array('workflowid' => $row->id, 'action' => action::WORKFLOW_DELETE, 'sesskey' => sesskey())); $confirmaction = new \confirm_action(get_string('deleteworkflow_confirm', 'tool_lifecycle')); $output .= $OUTPUT->action_icon($url, diff --git a/classes/local/table/interaction_attention_table.php b/classes/local/table/interaction_attention_table.php index 7b7b43c460c8b6c0d4fdd5dc70874de303138228..6cf8c5041bed67e83e3acac02896b773ab9e5d7f 100644 --- a/classes/local/table/interaction_attention_table.php +++ b/classes/local/table/interaction_attention_table.php @@ -46,7 +46,7 @@ class interaction_attention_table extends interaction_table { * @param int[] $courseids List of ids for courses that require attention. * @param object $filterdata Object of filter criteria */ - public function __construct($uniqueid, $courseids, $filterdata) { + public function __construct($uniqueid, $courseids, $filterdata = null) { parent::__construct($uniqueid); global $PAGE, $DB; @@ -68,15 +68,20 @@ class interaction_attention_table extends interaction_table { $params = []; if ($filterdata) { - if ($filterdata && $filterdata->shortname) { + if ($filterdata->shortname) { $where[] = $DB->sql_like('c.shortname', ':shortname', false, false); $params['shortname'] = '%' . $DB->sql_like_escape($filterdata->shortname) . '%'; } - if ($filterdata && $filterdata->fullname) { + if ($filterdata->fullname) { $where[] = $DB->sql_like('c.fullname', ':fullname', false, false); $params['fullname'] = '%' . $DB->sql_like_escape($filterdata->fullname) . '%'; } + + if ($filterdata->courseid) { + $where[] = 'c.id = :courseid'; + $params['courseid'] = $filterdata->courseid; + } } $this->column_nosort = array('status', 'tools'); diff --git a/classes/local/table/select_workflow_table.php b/classes/local/table/select_workflow_table.php new file mode 100644 index 0000000000000000000000000000000000000000..4d0e3a43edeb60faff48202bc2b50c19b6b9b4f4 --- /dev/null +++ b/classes/local/table/select_workflow_table.php @@ -0,0 +1,165 @@ +<?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 workflows with a "copy from" button. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_lifecycle\local\table; + +use tool_lifecycle\action; +use tool_lifecycle\local\manager\process_manager; +use tool_lifecycle\local\manager\trigger_manager; +use tool_lifecycle\urls; + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->libdir . '/tablelib.php'); +require_once(__DIR__ . '/../../../lib.php'); + +/** + * Table listing all workflows with a "copy from" button. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class select_workflow_table extends \flexible_table { + + /** + * Constructor for workflow_table. + * @param int $uniqueid Unique id of this table. + */ + public function __construct($uniqueid) { + parent::__construct($uniqueid); + global $PAGE; + $this->set_attribute('class', 'lifecycle-table'); + $this->set_attribute('id', $uniqueid); + $this->define_baseurl($PAGE->url); + $this->define_columns(['title', 'status', 'since', 'trigger', 'processes', 'tools']); + $this->define_headers([ + get_string('workflow_title', 'tool_lifecycle'), + get_string('status'), + get_string('since'), + get_string('trigger', 'tool_lifecycle'), + get_string('workflow_processes', 'tool_lifecycle'), + get_string('workflow_tools', 'tool_lifecycle'), + ]); + $this->sortable(false); + $this->setup(); + } + + /** + * Method to display the table. + * @return void + */ + public function out() { + global $DB; + $records = $DB->get_records_sql('SELECT id, title, displaytitle, timeactive, timedeactive, sortindex ' . + 'FROM {tool_lifecycle_workflow} ORDER BY timedeactive IS NOT NULL, timeactive IS NOT NULL'); + $this->format_and_add_array_of_rows($records, true); + $this->finish_output(); + } + + /** + * Render title column. + * @param object $row Row data. + * @return string Rendered title. + */ + public function col_title($row) { + return $row->title . '<br><span class="workflow_displaytitle">' . $row->displaytitle . '</span>'; + } + + /** + * Render the trigger column. + * @param object $row Row data. + * @return string instancename of the trigger + */ + public function col_trigger($row) { + $triggers = trigger_manager::get_triggers_for_workflow($row->id); + $triggerstring = ''; + if ($triggers) { + $triggerstring = $triggers[0]->instancename; + for ($i = 1; $i < count($triggers); $i++) { + $triggerstring .= ', '; + $triggerstring .= $triggers[$i]->instancename; + } + } + return $triggerstring; + } + + /** + * Render the processes column. It shows the number of active processes for the workflow instance. + * @param object $row Row data. + * @return string Number of processes. + * @throws \dml_exception + */ + public function col_processes($row) { + if ($row->timeactive && !$row->timedeactive) { + return process_manager::count_processes_by_workflow($row->id); + } else { + return ''; + } + } + + /** + * Render status column. + * @param object $row Row data. + * @return string status column + */ + public function col_status($row) { + if ($row->timedeactive) { + return get_string('deactivated', 'tool_lifecycle'); + } else if ($row->timeactive) { + return get_string('active', 'tool_lifecycle'); + } else { + return get_string('draft', 'tool_lifecycle'); + } + } + + /** + * Render since column. + * @param object $row Row data. + * @return string since column + */ + public function col_since($row) { + if ($row->timedeactive) { + return userdate($row->timedeactive, get_string('strftimedatetime')); + } else if ($row->timeactive) { + return userdate($row->timeactive, get_string('strftimedatetime')); + } else { + return ''; + } + } + + /** + * Render tools column. + * @param object $row Row data. + * @return string action buttons for workflows + * @throws \coding_exception + * @throws \moodle_exception + */ + public function col_tools($row) { + global $PAGE; + $createcopy = get_string('create_copy', 'tool_lifecycle'); + return \html_writer::link(new \moodle_url($PAGE->url, ['wf' => $row->id]), + $createcopy, ['class' => 'btn btn-primary', 'title' => $createcopy]); + } + +} diff --git a/classes/local/table/step_table.php b/classes/local/table/step_table.php deleted file mode 100644 index d80f32a51b57a4d34c3601da457303aee7a3dc36..0000000000000000000000000000000000000000 --- a/classes/local/table/step_table.php +++ /dev/null @@ -1,292 +0,0 @@ -<?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 step instances - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -namespace tool_lifecycle\local\table; - -use tool_lifecycle\action; -use tool_lifecycle\local\entity\trigger_subplugin; -use tool_lifecycle\local\manager\step_manager; -use tool_lifecycle\local\manager\trigger_manager; -use tool_lifecycle\local\manager\workflow_manager; - -defined('MOODLE_INTERNAL') || die; - -require_once($CFG->libdir . '/tablelib.php'); -require_once(__DIR__ . '/../../../lib.php'); - -/** - * Table listing step instances - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class step_table extends \table_sql { - - /** @var int $workflowid If of the workflow. */ - private $workflowid; - - /** - * step_table constructor. - * @param string $uniqueid Unique id of the table. - * @param int $workflowid Id of the workflow instance. - * @throws \coding_exception - * @throws \dml_exception - * @throws \moodle_exception - */ - public function __construct($uniqueid, $workflowid) { - parent::__construct($uniqueid); - global $PAGE, $DB; - $this->set_attribute('class', $this->attributes['class'] . ' ' . $uniqueid); - $this->workflowid = $workflowid; - list($sqlwhere, $params) = $DB->get_in_or_equal($workflowid); - $this->set_sql("id, subpluginname, instancename, sortindex, sortindex as show_action, 'step' as type", - '{tool_lifecycle_step}', - "workflowid " . $sqlwhere, $params); - $this->define_baseurl(new \moodle_url($PAGE->url, array('workflowid' => $workflowid))); - $this->pageable(false); - $this->init(); - } - - /** - * Take the data returned from the db_query and go through all the rows - * processing each col using either col_{columnname} method or other_cols - * method or if other_cols returns NULL then put the data straight into the - * table. - * - * After calling this function, don't forget to call close_recordset. - */ - public function build_table() { - $triggers = trigger_manager::get_triggers_for_workflow($this->workflowid); - foreach ($triggers as $trigger) { - $this->format_and_add_array_of_rows(array( - array( - 'id' => $trigger->id, - 'type' => 'trigger', - 'subpluginname' => $trigger->subpluginname, - 'sortindex' => $trigger->sortindex, - 'show_action' => $trigger->sortindex, - 'instancename' => $trigger->instancename, - ) - ), false); - } - return parent::build_table(); - } - - /** - * Initialize the table. - */ - public function init() { - $columns = ['type', 'instancename', 'subpluginname']; - $headers = [ - get_string('step_type', 'tool_lifecycle'), - get_string('step_instancename', 'tool_lifecycle'), - get_string('step_subpluginname', 'tool_lifecycle'), - ]; - if (! workflow_manager::is_editable($this->workflowid)) { - $columns[] = 'show_action'; - $headers[] = get_string('step_show', 'tool_lifecycle'); - } else { - $columns[] = 'sortindex'; - $headers[] = get_string('step_sortindex', 'tool_lifecycle'); - $columns[] = 'edit'; - $headers[] = get_string('step_edit', 'tool_lifecycle'); - $columns[] = 'delete'; - $headers[] = get_string('step_delete', 'tool_lifecycle'); - } - $this->define_columns($columns); - $this->define_headers($headers); - - if (!workflow_manager::is_editable($this->workflowid)) { - $this->sortable(false, 'show_action'); - } else { - $this->sortable(false, 'sortindex'); - } - $this->setup(); - } - - /** - * Render the type column. This column displays Trigger or Step, depending of the type of the subplugin. - * @param object $row Row data. - * @return string type of the subplugin - * @throws \coding_exception - */ - public function col_type($row) { - if ($row->type == 'step') { - return get_string('step', 'tool_lifecycle'); - } - return get_string('trigger', 'tool_lifecycle'); - } - - /** - * Render subpluginname column. - * @param object $row Row data. - * @return string pluginname of the subplugin - * @throws \coding_exception - */ - public function col_subpluginname($row) { - - $subpluginname = $row->subpluginname; - if ($row->type == 'step') { - return get_string('pluginname', 'lifecyclestep_' . $subpluginname); - } else { - return get_string('pluginname', 'lifecycletrigger_' . $subpluginname); - } - } - - /** - * Render sortindex column. - * @param object $row Row data. - * @return string action buttons for changing sortorder of the subplugin - * @throws \coding_exception - * @throws \moodle_exception - */ - public function col_sortindex($row) { - global $OUTPUT; - $output = ''; - if ($row->type == 'step') { - if ($row->sortindex !== null) { - if ($row->sortindex > 1) { - $alt = 'up'; - $icon = 't/up'; - $action = action::UP_STEP; - $output .= $this->format_icon_link($action, $row->id, $icon, get_string($alt)); - } else { - $output .= $OUTPUT->spacer(); - } - if ($row->sortindex < step_manager::count_steps_of_workflow($this->workflowid)) { - $alt = 'down'; - $icon = 't/down'; - $action = action::DOWN_STEP; - $output .= $this->format_icon_link($action, $row->id, $icon, get_string($alt)); - } else { - $output .= $OUTPUT->spacer(); - } - } - } - if ($row->type == 'trigger') { - if ($row->sortindex !== null) { - if ($row->sortindex > 1) { - $alt = 'up'; - $icon = 't/up'; - $action = action::UP_TRIGGER; - $output .= $this->format_icon_link($action, $row->id, $icon, get_string($alt)); - } else { - $output .= $OUTPUT->spacer(); - } - if ($row->sortindex < trigger_manager::count_triggers_of_workflow($this->workflowid)) { - $alt = 'down'; - $icon = 't/down'; - $action = action::DOWN_TRIGGER; - $output .= $this->format_icon_link($action, $row->id, $icon, get_string($alt)); - } else { - $output .= $OUTPUT->spacer(); - } - } - } - - return $output; - } - - /** - * Render edit column. - * @param object $row Row data. - * @return string action button for editing of the subplugin - * @throws \coding_exception - * @throws \moodle_exception - */ - public function col_edit($row) { - - $alt = 'edit'; - $icon = 't/edit'; - if ($row->type == 'step') { - $action = action::STEP_INSTANCE_FORM; - } else { - $action = action::TRIGGER_INSTANCE_FORM; - } - - return $this->format_icon_link($action, $row->id, $icon, get_string($alt)); - } - - /** - * Render show column. - * @param object $row Row data. - * @return string action button for editing of the subplugin - * @throws \coding_exception - * @throws \moodle_exception - */ - public function col_show_action($row) { - - $alt = 'view'; - $icon = 't/viewdetails'; - if ($row->type == 'step') { - $action = action::STEP_INSTANCE_FORM; - } else { - $action = action::TRIGGER_INSTANCE_FORM; - } - - return $this->format_icon_link($action, $row->id, $icon, get_string($alt)); - } - - /** - * Render delete column. - * @param object $row Row data. - * @return string action button for deleting the subplugin - * @throws \coding_exception - * @throws \moodle_exception - */ - public function col_delete($row) { - - $alt = 'delete'; - $icon = 't/delete'; - if ($row->type == 'step') { - $action = action::STEP_INSTANCE_DELETE; - } else { - $action = action::TRIGGER_INSTANCE_DELETE; - } - return $this->format_icon_link($action, $row->id, $icon, get_string($alt)); - } - - /** - * Util function for writing an action icon link - * - * @param string $action URL parameter to include in the link - * @param string $subpluginid URL parameter to include in the link - * @param string $icon The key to the icon to use (e.g. 't/up') - * @param string $alt The string description of the link used as the title and alt text - * @return string The icon/link - * @throws \moodle_exception - */ - private function format_icon_link($action, $subpluginid, $icon, $alt) { - global $PAGE, $OUTPUT; - - return $OUTPUT->action_icon(new \moodle_url($PAGE->url, - array('action' => $action, - 'subplugin' => $subpluginid, - 'sesskey' => sesskey(), - 'workflowid' => $this->workflowid)), - new \pix_icon($icon, $alt, 'moodle', array('title' => $alt)), - null, array('title' => $alt)) . ' '; - } - -} diff --git a/classes/local/table/workflow_definition_table.php b/classes/local/table/workflow_definition_table.php index 8a13245fdf230f7b7da9ebe0a5b2a53377d90b76..b394d3bbed2045f6e4e573d7325dd2832a89357c 100644 --- a/classes/local/table/workflow_definition_table.php +++ b/classes/local/table/workflow_definition_table.php @@ -28,6 +28,7 @@ use tool_lifecycle\local\manager\lib_manager; use tool_lifecycle\local\manager\step_manager; use tool_lifecycle\local\manager\trigger_manager; use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\urls; defined('MOODLE_INTERNAL') || die; @@ -85,7 +86,7 @@ class workflow_definition_table extends workflow_table { return userdate($row->timeactive, get_string('strftimedatetime'), 0); } if (workflow_manager::is_valid($row->id)) { - return $OUTPUT->single_button(new \moodle_url($PAGE->url, + return $OUTPUT->single_button(new \moodle_url(urls::ACTIVE_WORKFLOWS, array('action' => action::WORKFLOW_ACTIVATE, 'sesskey' => sesskey(), 'workflowid' => $row->id)), @@ -109,8 +110,8 @@ class workflow_definition_table extends workflow_table { $alt = get_string('viewsteps', 'tool_lifecycle'); $icon = 't/viewdetails'; - $url = new \moodle_url('/admin/tool/lifecycle/workflowsettings.php', - array('workflowid' => $row->id, 'sesskey' => sesskey())); + $url = new \moodle_url(urls::WORKFLOW_DETAILS, + array('wf' => $row->id)); $output .= $OUTPUT->action_icon($url, new \pix_icon($icon, $alt, 'moodle', array('title' => $alt)), null, array('title' => $alt)); @@ -120,17 +121,6 @@ class workflow_definition_table extends workflow_table { } if (!isset($lib) || $lib->has_multiple_instances()) { - - $action = action::WORKFLOW_INSTANCE_FROM; - $alt = get_string('editworkflow', 'tool_lifecycle'); - $icon = 't/edit'; - $output .= $this->format_icon_link($action, $row->id, $icon, $alt); - - $action = action::WORKFLOW_DUPLICATE; - $alt = get_string('duplicateworkflow', 'tool_lifecycle'); - $icon = 't/copy'; - $output .= $this->format_icon_link($action, $row->id, $icon, $alt); - $action = action::WORKFLOW_BACKUP; $alt = get_string('backupworkflow', 'tool_lifecycle'); $icon = 't/backup'; diff --git a/classes/local/table/workflow_table.php b/classes/local/table/workflow_table.php index 7db075c1823d8e3ce954cadc1a658755877cf467..88e237d96ad5221878b89286bad268e3a6bb193c 100644 --- a/classes/local/table/workflow_table.php +++ b/classes/local/table/workflow_table.php @@ -25,9 +25,8 @@ namespace tool_lifecycle\local\table; use tool_lifecycle\action; use tool_lifecycle\local\manager\process_manager; -use tool_lifecycle\local\manager\step_manager; use tool_lifecycle\local\manager\trigger_manager; -use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\urls; defined('MOODLE_INTERNAL') || die; @@ -133,10 +132,10 @@ abstract class workflow_table extends \table_sql { $alt = get_string('viewsteps', 'tool_lifecycle'); $icon = 't/viewdetails'; - $url = new \moodle_url('/admin/tool/lifecycle/workflowsettings.php', - array('workflowid' => $row->id, 'sesskey' => sesskey())); + $url = new \moodle_url(urls::WORKFLOW_DETAILS, + array('wf' => $row->id)); $output .= $OUTPUT->action_icon($url, new \pix_icon($icon, $alt, 'moodle', array('title' => $alt)), - null , array('title' => $alt)); + null, array('title' => $alt)); return $output; } @@ -152,12 +151,12 @@ abstract class workflow_table extends \table_sql { * @throws \moodle_exception */ protected function format_icon_link($action, $workflowid, $icon, $alt) { - global $OUTPUT; + global $OUTPUT, $PAGE; - return $OUTPUT->action_icon(new \moodle_url( '/admin/tool/lifecycle/adminsettings.php', + return $OUTPUT->action_icon(new \moodle_url($PAGE->url, array('action' => $action, - 'sesskey' => sesskey(), - 'workflowid' => $workflowid)), + 'workflowid' => $workflowid, + 'sesskey' => sesskey())), new \pix_icon($icon, $alt, 'moodle', array('title' => $alt)), null , array('title' => $alt)) . ' '; } diff --git a/classes/permission_and_navigation.php b/classes/permission_and_navigation.php new file mode 100644 index 0000000000000000000000000000000000000000..38766c5b4ff2af87f245a9993789c4c662c1cf66 --- /dev/null +++ b/classes/permission_and_navigation.php @@ -0,0 +1,91 @@ +<?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/>. + +/** + * Checks for permission and handles breadcrumbs. + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_lifecycle; + +use moodle_url; +use navigation_node; +use tool_lifecycle\local\entity\workflow; + +/** + * Checks for permission and handles breadcrumbs. + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class permission_and_navigation { + + /** + * Check permission and setup breadcrumbs for the given workflow. + * @param workflow $workflow + * @param bool $includeworkflowname whether to add the title of the workflow to the navbar. Default true. + */ + public static function setup_workflow(workflow $workflow, bool $includeworkflowname = true) { + global $PAGE; + if (!$workflow) { + throw new \coding_exception('Workflow does not exists!'); + } + if ($workflow->timedeactive) { + self::setup_deactived();; + } else if ($workflow->timeactive) { + self::setup_active(); + } else { + self::setup_draft(); + } + + if ($includeworkflowname) { + $PAGE->navbar->add($workflow->title, new moodle_url(urls::WORKFLOW_DETAILS, ['wf' => $workflow->id])); + } + } + + /** + * Check permission and setup breadcrumbs for workflow drafts. + * @return void + */ + public static function setup_draft() { + navigation_node::override_active_url(new moodle_url(urls::WORKFLOW_DRAFTS)); + admin_externalpage_setup('tool_lifecycle_workflow_drafts'); + } + + /** + * Check permission and setup breadcrumbs for active workflows. + * @return void + */ + public static function setup_active() { + navigation_node::override_active_url(new moodle_url(urls::ACTIVE_WORKFLOWS)); + admin_externalpage_setup('tool_lifecycle_active_workflows'); + } + + /** + * Check permission and setup breadcrumbs for deactivated workflows. + * @return void + */ + public static function setup_deactived() { + global $PAGE; + + navigation_node::override_active_url(new moodle_url('/admin/category.php', ['category' => 'lifecycle_category'])); + admin_externalpage_setup('tool_lifecycle_active_workflows'); + $PAGE->navbar->add(get_string('deactivated_workflows_list_header', 'tool_lifecycle'), + new moodle_url(urls::DEACTIVATED_WORKFLOWS)); + } + +} diff --git a/classes/processor.php b/classes/processor.php index c1a185d9247afa4d361d0155954656f971d1c319..bf4f9ce415f81cbd597dd00bb5b2d660cb18a419 100644 --- a/classes/processor.php +++ b/classes/processor.php @@ -193,7 +193,7 @@ class processor { /** * Returns a record set with all relevant courses. * Relevant means that there is currently no lifecycle process running for this course. - * @param trigger[] $triggers List of triggers, which will be asked for additional where requirements. + * @param trigger_subplugin[] $triggers List of triggers, which will be asked for additional where requirements. * @param int[] $exclude List of course id, which should be excluded from execution. * @return \moodle_recordset with relevant courses. * @throws \coding_exception @@ -228,4 +228,77 @@ class processor { return $DB->get_recordset_sql($sql, $whereparams); } + /** + * Calculates triggered and excluded courses for every trigger of a workflow, and in total. + * @param int $workflowid + * @return array + */ + public function get_count_of_courses_to_trigger_for_workflow($workflowid) { + $countcourses = 0; + $counttriggered = 0; + $countexcluded = 0; + $triggers = trigger_manager::get_triggers_for_workflow($workflowid); + + // Exclude globally delayed courses, courses delayed for this workflow, and the site course. + $exclude = delayed_courses_manager::get_globally_delayed_courses(); + $exclude = array_merge($exclude, delayed_courses_manager::get_delayed_courses_for_workflow($workflowid)); + $exclude[] = SITEID; + + $amounts = []; + $autotriggers = []; + foreach ($triggers as $trigger) { + $obj = new \stdClass(); + if (lib_manager::get_trigger_lib($trigger->subpluginname)->is_manual_trigger()) { + $obj->automatic = false; + } else { + $obj->automatic = true; + $obj->triggered = 0; + $obj->excluded = 0; + $autotriggers[] = $trigger; + } + $amounts[$trigger->sortindex] = $obj; + } + + $recordset = $this->get_course_recordset($autotriggers, $exclude); + + while ($recordset->valid()) { + $course = $recordset->current(); + $countcourses++; + $action = false; + foreach ($autotriggers as $trigger) { + $lib = lib_manager::get_automatic_trigger_lib($trigger->subpluginname); + $response = $lib->check_course($course, $trigger->id); + if ($response == trigger_response::next()) { + if (!$action) { + $action = true; + } + continue; + } + if ($response == trigger_response::exclude()) { + if (!$action) { + $action = true; + $countexcluded++; + } + $amounts[$trigger->sortindex]->excluded++; + continue; + } + if ($response == trigger_response::trigger()) { + $amounts[$trigger->sortindex]->triggered++; + } + } + if (!$action) { + $counttriggered++; + } + $recordset->next(); + } + + $all = new \stdClass(); + $all->excluded = $countexcluded; + $all->triggered = $counttriggered; + $all->coursesetsize = $countcourses; + + $amounts['all'] = $all; + return $amounts; + } + } diff --git a/classes/urls.php b/classes/urls.php new file mode 100644 index 0000000000000000000000000000000000000000..1db1e51fe1e6c849d306909e9271c7813b4d595d --- /dev/null +++ b/classes/urls.php @@ -0,0 +1,52 @@ +<?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/>. + +/** + * Constants for urls used within this plugin. + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_lifecycle; + +/** + * Constants for urls used within this plugin. + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class urls { + + /** @var string Lists active workflows. */ + const ACTIVE_WORKFLOWS = '/admin/tool/lifecycle/activeworkflows.php'; + /** @var string Lists workflow drafts. */ + const WORKFLOW_DRAFTS = '/admin/tool/lifecycle/workflowdrafts.php'; + /** @var string Lists deactivated workflows. */ + const DEACTIVATED_WORKFLOWS = '/admin/tool/lifecycle/deactivatedworkflows.php'; + /** @var string Edits general settings of workflow. */ + const EDIT_WORKFLOW = '/admin/tool/lifecycle/editworkflow.php'; + /** @var string Lets the user upload a workflow definition. */ + const UPLOAD_WORKFLOW = '/admin/tool/lifecycle/uploadworkflow.php'; + /** @var string Displays a nice visual representation of the workflow. */ + const WORKFLOW_DETAILS = '/admin/tool/lifecycle/workflowoverview.php'; + /** @var string Edits settings of triggers and steps */ + const EDIT_ELEMENT = '/admin/tool/lifecycle/editelement.php'; + /** @var string Edits settings of triggers and steps */ + const CREATE_FROM_EXISTING = '/admin/tool/lifecycle/createworkflowfromexisting.php'; + /** @var string Lists active processes and finds courses */ + const ACTIVE_PROCESSES = '/admin/tool/lifecycle/activeprocesses.php'; + +} diff --git a/coursebackups.php b/coursebackups.php index b0a44a1c5fcba01b4931062bb193af9494b0152a..711b524d7bbcb759ac3338ce582d875cff5282f5 100644 --- a/coursebackups.php +++ b/coursebackups.php @@ -32,7 +32,7 @@ admin_externalpage_setup('tool_lifecycle_coursebackups'); $PAGE->set_url(new \moodle_url('/admin/tool/lifecycle/coursebackups.php')); -$mform = new \tool_lifecycle\local\form\form_backups_filter(); +$mform = new \tool_lifecycle\local\form\form_courses_filter(); // Cache handling. $cache = cache::make('tool_lifecycle', 'mformdata'); diff --git a/createworkflowfromexisting.php b/createworkflowfromexisting.php new file mode 100644 index 0000000000000000000000000000000000000000..3b019c7dea43e96e8c6f71979c9fd11b131dff8c --- /dev/null +++ b/createworkflowfromexisting.php @@ -0,0 +1,83 @@ +<?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/>. + +/** + * Displays form for copying a new workflow from a existing one. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_lifecycle\action; +use tool_lifecycle\local\entity\workflow; +use tool_lifecycle\local\form\form_workflow_instance; +use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\local\table\workflow_definition_table; +use tool_lifecycle\urls; + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_login(); + +global $OUTPUT, $PAGE, $DB; + +$workflowid = optional_param('wf', null, PARAM_INT); + +\tool_lifecycle\permission_and_navigation::setup_draft(); + +$title = get_string('create_workflow_from_existing', 'tool_lifecycle'); + +$PAGE->set_title($title); +$PAGE->set_heading($title); +$PAGE->navbar->add($title, $PAGE->url); + +$renderer = $PAGE->get_renderer('tool_lifecycle'); + +if ($workflowid) { + $workflow = workflow_manager::get_workflow($workflowid); + $PAGE->set_url(new \moodle_url(urls::CREATE_FROM_EXISTING), ['wf' => $workflowid]); + $workflow->title = get_string('workflow_duplicate_title', 'tool_lifecycle', $workflow->title); + $form = new form_workflow_instance($PAGE->url, $workflow); + if ($form->is_cancelled()) { + // Cancelled, redirect back to workflow drafts. + redirect(new moodle_url(urls::WORKFLOW_DRAFTS)); + } + if ($data = $form->get_data()) { + $newworkflow = workflow_manager::duplicate_workflow($workflow->id); + $newworkflow->title = $data->title; + $newworkflow->displaytitle = $data->displaytitle; + $newworkflow->rollbackdelay = $data->rollbackdelay; + $newworkflow->finishdelay = $data->finishdelay; + $newworkflow->delayforallworkflows = $data->delayforallworkflows ?? 0; + workflow_manager::insert_or_update($newworkflow); + + // Workflow created, redirect to workflow detail page. + redirect(new moodle_url(urls::WORKFLOW_DETAILS, ['wf' => $newworkflow->id])); + } + + echo $renderer->header(); + $form->display(); + echo $renderer->footer(); + +} else { + $PAGE->set_url(new \moodle_url(urls::CREATE_FROM_EXISTING)); + + $table = new \tool_lifecycle\local\table\select_workflow_table('tool_lifecycle-select-workflow'); + echo $renderer->header(); + $table->out(); + echo $renderer->footer(); +} diff --git a/deactivatedworkflows.php b/deactivatedworkflows.php index 96bde649b68c644bdfc9883f93b4f96cf51a027f..e325eefbb4dcb12ad65b8c3c87d41955e829723d 100644 --- a/deactivatedworkflows.php +++ b/deactivatedworkflows.php @@ -23,22 +23,20 @@ */ require_once(__DIR__ . '/../../../config.php'); -require_once(__DIR__ . '/adminlib.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_login(); use tool_lifecycle\local\table\deactivated_workflows_table; +use tool_lifecycle\urls; -$PAGE->set_context(context_system::instance()); -require_login(null, false); -require_capability('moodle/site:config', context_system::instance()); +global $PAGE, $OUTPUT; -$PAGE->set_url(new \moodle_url('/admin/tool/lifecycle/deactivatedworkflows.php')); +\tool_lifecycle\permission_and_navigation::setup_deactived(); +$PAGE->set_url(new \moodle_url(urls::DEACTIVATED_WORKFLOWS)); $PAGE->set_title(get_string('deactivated_workflows_list_header', 'tool_lifecycle')); $PAGE->set_heading(get_string('deactivated_workflows_list_header', 'tool_lifecycle')); -admin_externalpage_setup('tool_lifecycle_deactivatedworkflows'); - - $workflowid = optional_param('workflowid', null, PARAM_INT); $action = optional_param('action', null, PARAM_TEXT); if ($workflowid && $action) { @@ -51,10 +49,16 @@ $table = new deactivated_workflows_table('tool_lifecycle_deactivated_workflows') echo $renderer->header(); +echo $OUTPUT->box_start("lifecycle-enable-overflow lifecycle-table"); + $table->out(50, false); -$surl = new \moodle_url('/admin/tool/lifecycle/adminsettings.php', - array('sesskey' => sesskey())); -echo \html_writer::link($surl, get_string('active_workflows_list', 'tool_lifecycle')); +echo $OUTPUT->box_end(); + +echo \html_writer::link(new \moodle_url(urls::WORKFLOW_DRAFTS), + get_string('workflow_drafts_list', 'tool_lifecycle')); +echo '<br>'; +echo \html_writer::link(new \moodle_url(urls::ACTIVE_WORKFLOWS), + get_string('active_workflows_list', 'tool_lifecycle')); echo $renderer->footer(); diff --git a/editelement.php b/editelement.php new file mode 100644 index 0000000000000000000000000000000000000000..de3ac303368ee35ef73bed71138f8395ec988343 --- /dev/null +++ b/editelement.php @@ -0,0 +1,158 @@ +<?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/>. + +/** + * Displays form for creating or editing a new step or trigger. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_lifecycle\local\entity\step_subplugin; +use tool_lifecycle\local\entity\trigger_subplugin; +use tool_lifecycle\local\form\form_step_instance; +use tool_lifecycle\local\form\form_trigger_instance; +use tool_lifecycle\local\manager\settings_manager; +use tool_lifecycle\local\manager\step_manager; +use tool_lifecycle\local\manager\trigger_manager; +use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\settings_type; +use tool_lifecycle\urls; + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_login(); + +global $OUTPUT, $PAGE, $DB; + +$type = required_param('type', PARAM_ALPHA); +$elementid = optional_param('elementid', null, PARAM_INT); + +if ($type === settings_type::STEP) { + $isstep = true; +} else if ($type === settings_type::TRIGGER) { + $isstep = false; +} else { + throw new coding_exception('type has to be either "step" or "trigger"!'); +} + +if ($elementid) { + if ($isstep) { + $element = \tool_lifecycle\local\manager\step_manager::get_step_instance($elementid); + } else { + $element = \tool_lifecycle\local\manager\trigger_manager::get_instance($elementid); + } + if (!$element) { + throw new coding_exception('Element with that ID and type does not exist!'); + } + $workflowid = $element->workflowid; + $subplugin = $element->subpluginname; +} else { + $workflowid = required_param('wf', PARAM_INT); + $subplugin = required_param('subplugin', PARAM_ALPHANUMEXT); + $element = null; +} + +$workflow = workflow_manager::get_workflow($workflowid); +\tool_lifecycle\permission_and_navigation::setup_workflow($workflow); + +$params = [ + 'type' => $type +]; +if ($elementid) { + $params['elementid'] = $element->id; +} else { + $params['subplugin'] = $subplugin; + $params['wf'] = $workflow->id; +} + +$PAGE->set_url(new moodle_url(urls::EDIT_ELEMENT, $params)); + +if ($element) { + $settings = settings_manager::get_settings($element->id, $type); +} else { + $settings = null; +} + +if ($isstep) { + $form = new form_step_instance($PAGE->url, $workflow->id, $element, $subplugin, $settings); +} else { + $form = new form_trigger_instance($PAGE->url, $workflow->id, $element, $subplugin, $settings); +} + +$titlestrid = ($element ? 'edit' : 'create') . '_' . $type; +$title = get_string($titlestrid, 'tool_lifecycle'); + +// Return to drafts, or to deactivated workflows if workflow was deactivated. +$returnurl = new moodle_url(urls::WORKFLOW_DETAILS, ['wf' => $workflow->id]); + +$PAGE->set_title($title); +$PAGE->set_heading($title); +$PAGE->navbar->add($title, $PAGE->url); + +if ($form->is_cancelled()) { + // Cancelled, redirect back to workflow drafts. + redirect($returnurl); +} +if ($data = $form->get_data()) { + if ($isstep) { + if (!empty($data->id)) { + $element = step_manager::get_step_instance($data->id); + if (isset($data->instancename)) { + $element->instancename = $data->instancename; + } + } else { + $element = step_subplugin::from_record($data); + } + step_manager::insert_or_update($element); + } else { + if (!empty($data->id)) { + $element = trigger_manager::get_instance($data->id); + if (isset($data->instancename)) { + $element->instancename = $data->instancename; + } + } else { + $triggers = trigger_manager::get_triggers_for_workflow($workflow->id); + foreach ($triggers as $trigger) { + if ($trigger->subpluginname == $data->subpluginname) { + throw new coding_exception('Only one instance of each trigger type allowed!'); + } + } + $element = trigger_subplugin::from_record($data); + } + trigger_manager::insert_or_update($element); + } + // Save local subplugin settings. + settings_manager::save_settings($element->id, $type, $form->subpluginname, $data, true); + + // Workflow updated, redirect back to workflow drafts. + redirect($returnurl); +} + +if (!workflow_manager::is_editable($workflow->id)) { + \core\notification::add( + get_string('active_workflow_not_changeable', 'tool_lifecycle'), + \core\notification::WARNING); +} + +$renderer = $PAGE->get_renderer('tool_lifecycle'); + +echo $renderer->header(); + +$form->display(); + +echo $renderer->footer(); diff --git a/editworkflow.php b/editworkflow.php new file mode 100644 index 0000000000000000000000000000000000000000..ad29b353a5bb07cbc4d27591852f7f0d2ec78559 --- /dev/null +++ b/editworkflow.php @@ -0,0 +1,93 @@ +<?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/>. + +/** + * Displays form for creating a new or editing an existing workflow. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_lifecycle\action; +use tool_lifecycle\local\entity\workflow; +use tool_lifecycle\local\form\form_workflow_instance; +use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\local\table\workflow_definition_table; +use tool_lifecycle\urls; + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_login(); + +global $OUTPUT, $PAGE, $DB; + +$workflowid = optional_param('wf', null, PARAM_INT); + +if ($workflowid) { + $workflow = workflow_manager::get_workflow($workflowid); + \tool_lifecycle\permission_and_navigation::setup_workflow($workflow, false); + + $title = get_string('editworkflow', 'tool_lifecycle'); + $PAGE->set_url(new \moodle_url(urls::EDIT_WORKFLOW), ['wf' => $workflowid]); +} else { + \tool_lifecycle\permission_and_navigation::setup_draft(); + + $title = get_string('add_workflow', 'tool_lifecycle'); + $PAGE->set_url(new \moodle_url(urls::EDIT_WORKFLOW)); + $workflow = null; +} + +$PAGE->set_title($title); +$PAGE->set_heading($title); +$PAGE->navbar->add($title, $PAGE->url); + +$form = new form_workflow_instance($PAGE->url, $workflow); +if ($form->is_cancelled()) { + if ($workflowid) { + // Aborted updating workflow, redirect back to workflow details. + redirect(new moodle_url(urls::WORKFLOW_DETAILS, ['wf' => $workflow->id])); + } else { + // Aborted creating new workflow, redirect back to workflow drafts. + redirect(new moodle_url(urls::WORKFLOW_DRAFTS)); + } +} +if ($data = $form->get_data()) { + if ($data->id) { + $workflow = workflow_manager::get_workflow($data->id); + $workflow->title = $data->title; + $workflow->displaytitle = $data->displaytitle; + $workflow->rollbackdelay = $data->rollbackdelay; + $workflow->finishdelay = $data->finishdelay; + $workflow->delayforallworkflows = property_exists($data, 'delayforallworkflows') ? $data->delayforallworkflows : 0; + $newworkflow = false; + } else { + $workflow = workflow::from_record($data); + $newworkflow = true; + } + workflow_manager::insert_or_update($workflow); + + // New Workflow created, redirect to details page. + redirect(new moodle_url(urls::WORKFLOW_DETAILS, ['wf' => $workflow->id])); +} + +$renderer = $PAGE->get_renderer('tool_lifecycle'); + +echo $renderer->header(); + +$form->display(); + +echo $renderer->footer(); diff --git a/lang/de/tool_lifecycle.php b/lang/de/tool_lifecycle.php index fdafbd56c3354e57b11985613577d9efdc449c8e..986f7b4ba590671ac160a125de335be2b24b87ee 100644 --- a/lang/de/tool_lifecycle.php +++ b/lang/de/tool_lifecycle.php @@ -34,10 +34,11 @@ $string['config_delay_duration'] = 'Standardlänge eines Kursausschlusses'; $string['config_delay_duration_desc'] = 'Diese Einstellung definiert den Standardlänge einer Kursausschlusses in einem Workflow falls ein Prozess des Workflows zurückgesetzt oder beendigt wird. Die Länge des Kursausschlusses besagt, wie lange es dauert, bis der Kurs wieder vom Workflow bearbeitet wird.'; -$string['active_processes_list_header'] = 'Aktive Prozesse'; +$string['find_course_list_header'] = 'Kurse finden'; $string['adminsettings_heading'] = 'Workflow-Einstellungen'; $string['active_manual_workflows_heading'] = 'Aktive manuelle Workflows'; $string['active_automatic_workflows_heading'] = 'Aktive automatische Workflows'; +$string['see_in_workflow'] = 'In Workflow ansehen'; $string['workflow_definition_heading'] = 'Workflowdefinitionen'; $string['adminsettings_edit_workflow_definition_heading'] = 'Workflowdefinition'; $string['adminsettings_workflow_definition_steps_heading'] = 'Workflowschritte'; @@ -70,18 +71,21 @@ $string['trigger_sortindex'] = 'Hoch/Runter'; $string['trigger_workflow'] = 'Workflow'; $string['workflow'] = 'Workflow'; -$string['add_workflow'] = 'Workflow hinzufügen'; +$string['workflow_drafts_header'] = 'Workflow-Entwürfe'; +$string['active_workflows_header'] = 'Aktive Workflows'; +$string['add_workflow'] = 'Neuen Workflow hinzufügen'; $string['upload_workflow'] = 'Workflow hochladen'; +$string['create_workflow_from_existing'] = 'Kopie von bestehendem Workflow erstellen'; $string['workflow_title'] = 'Titel'; $string['workflow_title_help'] = 'Titel des Workflows (nur sichtbar für Admins).'; $string['workflow_displaytitle'] = 'Angezeigter Titel des Workflows'; $string['workflow_displaytitle_help'] = 'Dieser Titel wird Nutzern beim Verwalten ihrer Kurse angezeigt.'; $string['workflow_rollbackdelay'] = 'Kursauschluss beim Zurücksetzen'; $string['workflow_rollbackdelay_help'] = 'Dieser Wert beschreibt die Zeit, bis wieder ein Prozess für diesen Workflow und einen Kurs - gestarted werden kann, nachdem der Kurs innerhalb eines Prozesses dieses Workflows zurückgesetzt wurde.'; + gestartet werden kann, nachdem der Kurs innerhalb eines Prozesses dieses Workflows zurückgesetzt wurde.'; $string['workflow_finishdelay'] = 'Kursauschluss bei Beendigung'; $string['workflow_finishdelay_help'] = 'Dieser Wert beschreibt die Zeit, bis wieder ein Prozess für diesen Workflow und einen Kurs - gestarted werden kann, nachdem der Kurs einen Prozess dieses Workflows beendingt hat.'; + gestartet werden kann, nachdem der Kurs einen Prozess dieses Workflows beendingt hat.'; $string['workflow_delayforallworkflows'] = 'Ausschluss für alle Workflows?'; $string['workflow_delayforallworkflows_help'] = 'Falls ja, wird ein Kurs für die oben genannte Zeit nicht nur von diesem, sondern von allen Workflows ausgeschlossen. Das heißt, bis die Zeit abgelaufen ist, kann kein Prozess für den Kurs gestartet werden.'; @@ -109,7 +113,8 @@ $string['workflow_duplicate_title'] = '{$a} (Kopie)'; $string['deactivated_workflows_list'] = 'Zeige deaktivierte Workflows'; $string['deactivated_workflows_list_header'] = 'Deaktivierte Workflows'; $string['workflow_timedeactive'] = 'Deaktiviert seit'; -$string['active_workflows_list'] = 'Zeige aktive Workflows und Workflowdefinitionen'; +$string['active_workflows_list'] = 'Zeige aktive Workflows'; +$string['workflow_drafts_list'] = 'Zeige Workflow-Entwürfe'; $string['step_type'] = 'Typ'; $string['step_subpluginname'] = 'Subpluginname'; @@ -191,3 +196,37 @@ $string['errorbackuppath'] = "Ein Fehler ist aufgetreten beim Versuchen das Back Ihnen fehlen wahrscheinlich die Berechtigung dazu. Bitte überprüfen Sie den Pfad unter Seitenadministration/Plugins/Dienstprogramme/Kurs-Lebenszyklus/Allgemein & Subplugins."; $string['errornobackup'] = "Es wurde kein Backup in dem angegebenen Pfad erstellt."; + +// Workflowoverview. +$string['workflowoverview'] = 'Workflow ansehen'; +$string['workflowoverview_list_header'] = 'Details zu Workflows'; +$string['create_step'] = 'Step erstellen'; +$string['create_trigger'] = 'Trigger erstellen'; +$string['edit_step'] = 'Step bearbeiten'; +$string['edit_trigger'] = 'Trigger bearbeiten'; +$string['move_up'] = 'Nach oben bewegen'; +$string['move_down'] = 'Nach unten bewegen'; +$string['courses_triggered'] = 'Kurse insgesamt getriggered: {$a}'; +$string['courses_excluded'] = 'Kurse insgesamt ausgeschlossen: {$a}'; +$string['courses_size'] = 'Kurse insgesamt genauer betrachtet: {$a}'; +$string['details:displaytitle'] = 'Wird Lehrenden als <b>{$a}</b> angezeigt.'; +$string['details:rollbackdelay'] = 'Nachdem ein Kurs zurückgesetzt wird, wird er für <b>{$a}</b> verzögert.'; +$string['details:finishdelay'] = 'Nachdem ein Kurs einen Workflow beendet, wird er für <b>{$a}</b> verzögert.'; +$string['details:globaldelay_yes'] = 'Diese Verzögerungen gelten <b>für alle Workflows</b>.'; +$string['details:globaldelay_no'] = 'Diese Verzögerungen gelten <b>nur für diesen Workflow</b>.'; +$string['courses_will_be_triggered_total'] = '{$a} Kurse werden insgesamt getriggert'; +$string['courses_will_be_excluded_total'] = '{$a} Kurse werden insgesamt ausgeschlossen'; +$string['courses_will_be_triggered'] = '{$a} Kurse werden getriggert'; +$string['courses_will_be_excluded'] = '{$a} Kurse werden ausgeschlossen'; +$string['overview:trigger'] = 'Trigger'; +$string['overview:trigger_help'] = 'Ein Kurs fängt nur dann an, einen Workflow zu durchlaufen, wenn alle Trigger des Workflows dies übereinstimmend (UND-Verknüpfung) aussagen.<br><br> +In den hier genannten Zahlen werden Kurse, die verzögert werden oder sich bereits in anderen Workflows befinden, nicht mitgezählt.<br> +Trotzdem sind die Zahlen nur approximiert, da es sein könnte, dass die Kurse vor diesem einen anderen Workflow auslösen.'; +$string['overview:add_trigger'] = 'Trigger hinzufügen'; +$string['overview:add_trigger_help'] = 'Es kann nur eine Instanz jedes Triggertyps hinzugefügt werden.'; + +// Create copy from existing. +$string['create_copy'] = 'Kopie erstellen'; +$string['active'] = 'Aktiv'; +$string['deactivated'] = 'Deaktiviert'; +$string['draft'] = 'Entwurf'; diff --git a/lang/en/tool_lifecycle.php b/lang/en/tool_lifecycle.php index d8fd35fdb45a08366adb664e4cf1b1921cabc4eb..9d0bb75e7299fc18dbe5f3289044a45c0aaa734b 100644 --- a/lang/en/tool_lifecycle.php +++ b/lang/en/tool_lifecycle.php @@ -38,10 +38,11 @@ The delay duration determines how long a course will be excepted from being proc $string['config_backup_path'] = 'Path of the lifecycle backup folder'; $string['config_backup_path_desc'] = 'This settings defines the storage location of the backups created by the backup step. The path has to be specified as an absolute path on your server.'; -$string['active_processes_list_header'] = 'Active processes'; +$string['find_course_list_header'] = 'Find courses'; $string['adminsettings_heading'] = 'Workflow settings'; $string['active_manual_workflows_heading'] = 'Active manual workflows'; $string['active_automatic_workflows_heading'] = 'Active automatic workflows'; +$string['see_in_workflow'] = 'See in workflow'; $string['workflow_definition_heading'] = 'Workflow definitions'; $string['adminsettings_edit_workflow_definition_heading'] = 'Workflow definition'; $string['adminsettings_workflow_definition_steps_heading'] = 'Workflow steps'; @@ -75,8 +76,11 @@ $string['trigger_sortindex'] = 'Up/Down'; $string['trigger_workflow'] = 'Workflow'; $string['workflow'] = 'Workflow'; -$string['add_workflow'] = 'Add workflow'; +$string['workflow_drafts_header'] = 'Workflow drafts'; +$string['active_workflows_header'] = 'Active workflows'; +$string['add_workflow'] = 'Create new workflow'; $string['upload_workflow'] = 'Upload workflow'; +$string['create_workflow_from_existing'] = 'Copy new workflow from existing'; $string['workflow_title'] = 'Title'; $string['workflow_title_help'] = 'Workflow title (visible for admins only).'; $string['workflow_displaytitle'] = 'Displayed workflow title'; @@ -115,7 +119,8 @@ $string['workflow_duplicate_title'] = '{$a} (Copy)'; $string['deactivated_workflows_list'] = 'List deactivated workflows'; $string['deactivated_workflows_list_header'] = 'Deactivated workflows'; $string['workflow_timedeactive'] = 'Deactivated since'; -$string['active_workflows_list'] = 'List active workflows and workflow definitions'; +$string['active_workflows_list'] = 'List active workflows'; +$string['workflow_drafts_list'] = 'List workflow drafts'; $string['step_type'] = 'Type'; $string['step_subpluginname'] = 'Subplugin name'; @@ -227,3 +232,34 @@ $string['notifyerrorsemailcontenthtml'] = 'There are {$a->amount} new tool_lifec $string['errorbackuppath'] = "Error while trying to create the backup directory. You might be missing the permission to do so. Please check your path at Site administration/Plugins/Admin tools/Life Cycle/General & subplugins/backup_path."; $string['errornobackup'] = "No backup was created at the specified directory, reasons unknown."; + +// Workflowoverview. +$string['workflowoverview'] = 'View workflow'; +$string['workflowoverview_list_header'] = 'Details of Workflows'; +$string['create_step'] = 'Create step'; +$string['create_trigger'] = 'Create trigger'; +$string['edit_step'] = 'Edit step'; +$string['edit_trigger'] = 'Edit trigger'; +$string['move_up'] = 'Move up'; +$string['move_down'] = 'Move down'; +$string['details:displaytitle'] = 'Displayed to teachers as <b>{$a}</b>.'; +$string['details:rollbackdelay'] = 'When a course is rolled back, it will be delayed for <b>{$a}</b>.'; +$string['details:finishdelay'] = 'When a course has finished the workflow, it will be delayed for <b>{$a}</b>.'; +$string['details:globaldelay_yes'] = 'These delays apply <b>to all workflows</b>.'; +$string['details:globaldelay_no'] = 'These delays apply <b>only to this workflow</b>.'; +$string['courses_will_be_triggered_total'] = '{$a} courses will be triggered in total'; +$string['courses_will_be_excluded_total'] = '{$a} courses will be excluded in total'; +$string['courses_will_be_triggered'] = '{$a} Courses will be triggered'; +$string['courses_will_be_excluded'] = '{$a} Courses will be excluded'; +$string['overview:trigger'] = 'Trigger'; +$string['overview:trigger_help'] = 'A course will only trigger a workflow, if all triggers agree on it (AND operation).<br><br> +Courses which are delayed, or already in another workflow are not included in the displayed counts.<br> +Still, these numbers are only approximates, since it could be that a course is excluded by another workflow, or will trigger another workflow before this one.'; +$string['overview:add_trigger'] = 'Add trigger'; +$string['overview:add_trigger_help'] = 'You can only add one instance of each trigger type.'; + +// Create copy from existing workflow. +$string['create_copy'] = 'Create copy'; +$string['active'] = 'Active'; +$string['deactivated'] = 'Deactivated'; +$string['draft'] = 'Draft'; diff --git a/renderer.php b/renderer.php index d332cb3fb95b0676d4d53b78b746085d37069db7..f17197d3a4fc6dbc8f2aca62227c8ed0da8b9124 100644 --- a/renderer.php +++ b/renderer.php @@ -47,8 +47,6 @@ class tool_lifecycle_renderer extends plugin_renderer_base { echo $this->output->header(); if ($title) { echo $this->output->heading($title); - } else { - echo $this->output->heading($this->page->heading); } } diff --git a/settings.php b/settings.php index ce6ad36f9395ad5b095bd4093de8c40272881ed1..f1d37919e38eb93231b5da952a2fe5b51604b726 100644 --- a/settings.php +++ b/settings.php @@ -24,7 +24,6 @@ defined('MOODLE_INTERNAL') || die; if ($hassiteconfig) { - require_once(__DIR__ . '/adminlib.php'); $category = new admin_category('lifecycle_category', get_string('pluginname', 'tool_lifecycle')); @@ -43,13 +42,22 @@ if ($hassiteconfig) { get_string('config_backup_path_desc', 'tool_lifecycle'), $CFG->dataroot . DIRECTORY_SEPARATOR . 'lifecycle_backups')); - $ADMIN->add('lifecycle_category', new tool_lifecycle\admin_page_active_processes()); - $ADMIN->add('lifecycle_category', new tool_lifecycle\admin_page_course_backups()); - $ADMIN->add('lifecycle_category', new tool_lifecycle\admin_page_sublugins()); - $ADMIN->add('lifecycle_category', new tool_lifecycle\admin_page_deactivated_workflows()); + $ADMIN->add('lifecycle_category', new admin_externalpage('tool_lifecycle_workflow_drafts', + get_string('workflow_drafts_header', 'tool_lifecycle'), + new moodle_url(\tool_lifecycle\urls::WORKFLOW_DRAFTS))); + + $ADMIN->add('lifecycle_category', new admin_externalpage('tool_lifecycle_active_workflows', + get_string('active_workflows_header', 'tool_lifecycle'), + new moodle_url(\tool_lifecycle\urls::ACTIVE_WORKFLOWS))); + + $ADMIN->add('lifecycle_category', new admin_externalpage('tool_lifecycle_coursebackups', + get_string('course_backups_list_header', 'tool_lifecycle'), + new \moodle_url('/admin/tool/lifecycle/coursebackups.php'))); + $ADMIN->add('lifecycle_category', new admin_externalpage('tool_lifecycle_delayed_courses', - get_string('delayed_courses_header', 'tool_lifecycle'), - new moodle_url('/admin/tool/lifecycle/delayedcourses.php'))); + get_string('delayed_courses_header', 'tool_lifecycle'), + new moodle_url('/admin/tool/lifecycle/delayedcourses.php'))); + $ADMIN->add('lifecycle_category', new admin_externalpage('tool_lifecycle_process_errors', get_string('process_errors_header', 'tool_lifecycle'), new moodle_url('/admin/tool/lifecycle/errors.php'))); diff --git a/styles.css b/styles.css index b4dfbe878201cb8864d92e6d1152bc2d1ac4dbe9..8ecb11978bf316c3a004557c8ffcd4a78b027b64 100644 --- a/styles.css +++ b/styles.css @@ -2,13 +2,126 @@ overflow: initial; } +body.path-admin-tool-lifecycle .wf-secondary-info, .lifecycle-table span.workflow_displaytitle, .lifecycle-table span.secondary-info { - color: #888; + color: #777; font-size: .9em; padding: 0.5em; } +.lifecycle-highlighted { + background-color: rgba(0, 178, 255, 0.15); +} + span.tool_lifecycle-hint { text-decoration: underline dashed #444; +} + +/* Workflowoverview */ + +#lifecycle-workflow-details .wf-wrapper { + display: flex; + flex-direction: column; + align-items: center; + padding: 1em 0; +} + +#lifecycle-workflow-details .workflow-item, +#lifecycle-workflow-details .workflow-trigger { + text-align: center; + border: 1px solid #aaa; + border-radius: 8px; + min-width: 200px; +} + +#lifecycle-workflow-details .workflow-item { + box-shadow: 1px 1px 8px 0 rgba(0, 0, 0, 0.2); +} + +#lifecycle-workflow-details .workflow-trigger { + max-width: 300px; + margin: 8px; +} + +#lifecycle-workflow-details .wf-header { + border-bottom: 1px solid #aaa; + line-height: 1; + display: flex; + padding: 8px 4px 8px 16px; + align-items: center; +} + +#lifecycle-workflow-details .wf-header-text { + flex: auto 1 1; +} + +#lifecycle-workflow-details .wf-actionmenu { + margin-left: 16px; +} + +#lifecycle-workflow-details .wf-content { + padding: 8px 12px; +} + +#lifecycle-workflow-details .wf-arrow { + height: 32px; + width: 0; + border-left: 1px solid #777; +} + +#lifecycle-workflow-details .wf-arrow::after { + display: block; + height: 8px; + width: 8px; + content: ''; + border: 1px solid #777; + border-top: none; + border-right: none; + transform: translate(-4.5px, 23px) rotate(-45deg); +} + +#lifecycle-workflow-details .workflow.wf-trigger-wrapper { + display: inline-flex; + margin-top: -8px; + flex-wrap: wrap; + justify-content: center; +} + +#lifecycle-workflow-details .workflow-step.wf-selected { + border: 2px solid black; + background-color: rgba(0, 178, 255, 0.15); +} + +#lifecycle-workflow-details .wf-wrapper { + flex: 60% 1 1; +} + +#lifecycle-workflow-details .courses-table { + flex: 40% 1 1; + border: 1px solid #bbb; + border-right: none; +} + +#lifecycle-workflow-details .bar { + border: 1px solid #dee2e6; + border-right: none; +} + +#lifecycle-workflow-details .close-button { + cursor: pointer; + padding: 0 12px; + border-right: 1px solid #dee2e6; + display: inline-block; + font-size: 1.75em; +} + +#lifecycle-workflow-details .close-button:hover { + text-decoration: none; +} + +#lifecycle-workflow-details .edit-pen { + position: absolute; + top: 1em; + right: 1em; } \ No newline at end of file diff --git a/templates/search_input.mustache b/templates/search_input.mustache new file mode 100644 index 0000000000000000000000000000000000000000..a41e9afe36c4d570913a20c55c969218bfc4cadf --- /dev/null +++ b/templates/search_input.mustache @@ -0,0 +1,77 @@ +{{! + 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/>. +}} +{{! + @template tool_lifecycle/search_input + + Simple search input. + + Directly copied from core/search_input, because we have to provide it for Moodle 3.9. + + Example context (json): + { + "action": "https://moodle.local/admin/search.php", + "extraclasses": "my-2", + "inputname": "search", + "inform": false, + "searchstring": "Search settings", + "value": "policy", + "btnclass": "primary", + "query": "themedesigner", + "hiddenfields": [ + { + "name": "context", + "value": "11" + } + ] + } +}} +<div class="simplesearchform {{ extraclasses }}"> + {{^inform}} + <form autocomplete="off" action="{{ action }}" method="get" accept-charset="utf-8" class="mform form-inline simplesearchform"> + {{/inform}} + {{#hiddenfields}} + <input type="hidden" name="{{ name }}" value="{{ value }}"> + {{/hiddenfields}} + <div class="input-group"> + <label for="searchinput-{{uniqid}}"> + <span class="sr-only">{{{ searchstring }}}</span> + </label> + <input type="text" + id="searchinput-{{uniqid}}" + class="form-control" + placeholder="{{ searchstring }}" + aria-label="{{ searchstring }}" + name="{{ inputname }}" + data-region="input" + autocomplete="off" + value="{{ query }}" + > + <div class="input-group-append"> + <button type="submit" class="btn {{^btnclass}}btn-submit{{/btnclass}} {{ btnclass }} search-icon"> + {{#pix}} a/search, core {{/pix}} + <span class="sr-only">{{ searchstring }}</span> + </button> + </div> + + </div> + {{#otherfields}} + <div class="ml-2">{{{ otherfields }}}</div> + {{/otherfields}} +{{^inform}} + </form> +{{/inform}} +</div> \ No newline at end of file diff --git a/templates/warn_icon.mustache b/templates/warn_icon.mustache new file mode 100644 index 0000000000000000000000000000000000000000..5a2e6596dc402731a5dc6009ac69ca027b68f673 --- /dev/null +++ b/templates/warn_icon.mustache @@ -0,0 +1,25 @@ +{{! + @template core/warn_icon + + Help icon. + + Example context (json): + { + "title": "Help with something", + "url": "http://example.org/help", + "linktext": "", + "icon":{ + "attributes": [ + {"name": "class", "value": "iconhelp"}, + {"name": "src", "value": "../../../pix/help.svg"}, + {"name": "alt", "value": "Help icon"} + ] + } + } +}} +<a class="btn btn-link p-0" role="button" + data-container="body" data-toggle="popover" + data-placement="{{#ltr}}right{{/ltr}}{{^ltr}}left{{/ltr}}" data-content="{{text}} {{completedoclink}}" + data-html="true" tabindex="0" data-trigger="focus"> + {{#pix}}i/risk_xss, core, {{{alt}}}{{/pix}} +</a> diff --git a/templates/workflowoverview.mustache b/templates/workflowoverview.mustache new file mode 100644 index 0000000000000000000000000000000000000000..a3d6b7e2413295d81532fa2bf1598237ea8f6276 --- /dev/null +++ b/templates/workflowoverview.mustache @@ -0,0 +1,143 @@ +{{! + This file is part of Moodle - https://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/>. +}} +{{! + @template plugintype_pluginname/template_name + + Template purpose and description. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + * none + + Example context (json): + { + "triggerhelp": "<a></a>", + "editsettingslink": "http://localhost/moodle400/admin/tool/lifecycle/editworkflow.php?wf=8", + "title": "My Workflow Title", + "displaytitle": "My Workflow displaytitle", + "rollbackdelay": "183 days", + "finishdelay": "183 days", + "delayglobally": "1", + "trigger": [ + { + "id": "8", + "instancename": "Immediately!", + "workflowid": "8", + "subpluginname": "startdatedelay", + "sortindex": "1", + "actionmenu": "<div></div>", + "automatic": true, + "triggeredcourses": 0, + "excludedcourses": 0 + } + ], + "automatic": 1, + "coursestriggered": 0, + "coursesexcluded": 0, + "coursesetsize": 0, + "steps": [ + { + "id": "2", + "instancename": "First email step", + "workflowid": "8", + "subpluginname": "email", + "sortindex": "1", + "numberofcourses": 2, + "actionmenu": "<div></div>" + } + ], + "listofcourses": [], + "nosteplink": {}, + "table": null + } +}} +<div class="d-flex" id="lifecycle-workflow-details"> + <div class="wf-wrapper"> + <div class="bg-light d-inline-block p-3 position-relative" style="max-width: 800px;"> + <a class="edit-pen" href="{{{editsettingslink}}}">{{#pix}} i/edit, core, {{/pix}}</a> + <h4 class="pr-5">{{title}}</h4> + <span>{{#str}}details:displaytitle, tool_lifecycle, {{displaytitle}}{{/str}}</span><br> + <span>{{#str}}details:rollbackdelay, tool_lifecycle, {{rollbackdelay}}{{/str}}</span><br> + <span>{{#str}}details:finishdelay, tool_lifecycle, {{finishdelay}}{{/str}}</span><br> + <span>{{# delayglobally }}{{#str}}details:globaldelay_yes, tool_lifecycle{{/str}}{{/ delayglobally }} + {{^delayglobally}}{{#str}}details:globaldelay_no, tool_lifecycle{{/str}}{{/delayglobally}}</span> + </div> + <div class="mt-5 mb-3">{{{addinstance}}}</div> + <div class="wf-trigger-block workflow-item"> + <h5 class="my-2">{{#str}} trigger, tool_lifecycle{{/str}} {{{triggerhelp}}}</h5> + {{#automatic}} + <div class="mb-2 mx-2"> + {{#str}} courses_will_be_triggered_total, tool_lifecycle, {{coursestriggered}} {{/str}}<br> + {{#str}} courses_will_be_excluded_total, tool_lifecycle, {{coursesexcluded}} {{/str}}<br> + </div> + {{/automatic}} + <div class="workflow wf-trigger-wrapper"> + {{#trigger}} + <div class="workflow-trigger"> + <div class="wf-header"> + <div class="wf-header-text"> + <span>{{instancename}}</span><br> + <span class="text-muted" style="font-size: 0.9em">{{subpluginname}}</span> + </div> + <div class="wf-actionmenu"> + {{{ actionmenu }}} + </div> + </div> + <div class="wf-content"> + {{#automatic}} + <span>{{#str}} courses_will_be_triggered, tool_lifecycle, {{triggeredcourses}} {{/str}}</span><br> + <span>{{#str}} courses_will_be_excluded, tool_lifecycle, {{excludedcourses}} {{/str}}</span> + {{/automatic}} + </div> + </div> + {{/trigger}} + </div> + </div> + {{#steps}} + <div class="wf-arrow"></div> + <div class="workflow-step workflow-item {{#selected}}wf-selected{{/selected}}"> + <div class="wf-header"> + <div class="wf-header-text"> + <span>{{instancename}}</span><br> + <span class="text-muted" style="font-size: 0.9em">{{subpluginname}}</span> + </div> + <div class="wf-actionmenu"> + {{{ actionmenu }}} + </div> + </div> + <div class="wf-content"> + <a href="{{nosteplink}}&step={{id}}">{{icon}}{{name}} + {{#str}} courses {{/str}}: {{numberofcourses}} + </a> + </div> + </div> + {{/steps}} + </div> +{{# table }} + <div class="courses-table"> + <div class="bar"> + <a class="close-button" href="{{nosteplink}}">×</a> + </div> + {{{ table }}} + </div> +{{/table}} +</div> \ No newline at end of file diff --git a/tests/active_workflow_is_manual_test.php b/tests/active_workflow_is_manual_test.php index de502e5f63a756ba47ceeb5b36e83da980b3b633..2e004264a02cfe5ce33468d2eb9d15b7f639b557 100644 --- a/tests/active_workflow_is_manual_test.php +++ b/tests/active_workflow_is_manual_test.php @@ -40,7 +40,7 @@ use tool_lifecycle\local\entity\workflow; * @copyright 2018 WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class workflow_is_manual_test extends \advanced_testcase { +class active_workflow_is_manual_test extends \advanced_testcase { /** Icon of the trigger. */ const MANUAL_TRIGGER1_ICON = 't/up'; @@ -79,6 +79,11 @@ class workflow_is_manual_test extends \advanced_testcase { /** * Test to activate the manual workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager manual workflows + * @throws \coding_exception + * @throws \dml_exception + * @throws \dml_transaction_exception + * @throws \moodle_exception */ public function test_activate_manual() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->manualworkflow->id); @@ -89,6 +94,11 @@ class workflow_is_manual_test extends \advanced_testcase { /** * Test to activate the automatic workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager active workflows + * @throws \coding_exception + * @throws \dml_exception + * @throws \dml_transaction_exception + * @throws \moodle_exception */ public function test_activate_automatic() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->automaticworkflow->id); diff --git a/tests/active_workflow_sortindex_updown_test.php b/tests/active_workflow_sortindex_updown_test.php index 03ffeff11da0f1db94635578af0604c8d2f36ad6..e43eb80b1d49ca260723acc0c25da8439e7f383e 100644 --- a/tests/active_workflow_sortindex_updown_test.php +++ b/tests/active_workflow_sortindex_updown_test.php @@ -41,10 +41,11 @@ use tool_lifecycle\local\manager\workflow_manager; * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class workflow_sortindex_updown_test extends workflow_actions_testcase { +class active_workflow_sortindex_updown_test extends workflow_actions_testcase { /** * Test to put down the first workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager move actions down */ public function test_down_first() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); @@ -64,6 +65,7 @@ class workflow_sortindex_updown_test extends workflow_actions_testcase { /** * Test to put down the second workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager move actions down */ public function test_down_second() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); @@ -83,6 +85,7 @@ class workflow_sortindex_updown_test extends workflow_actions_testcase { /** * Test to put down the third workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager move actions down */ public function test_down_third() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); @@ -102,6 +105,7 @@ class workflow_sortindex_updown_test extends workflow_actions_testcase { /** * Test to put up the third workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager move actions up */ public function test_up_first() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); @@ -121,6 +125,7 @@ class workflow_sortindex_updown_test extends workflow_actions_testcase { /** * Test to put up the third workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager move actions up */ public function test_up_second() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); @@ -140,6 +145,7 @@ class workflow_sortindex_updown_test extends workflow_actions_testcase { /** * Test to put up the third workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager move actions up */ public function test_up_third() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); diff --git a/tests/backup_and_restore_workflow_test.php b/tests/backup_and_restore_workflow_test.php index 82c702d1a3e6eeb3cabb5cbf0d1391388218557d..2472092b2a68fd7cbb973922b16c60918cc35bde 100644 --- a/tests/backup_and_restore_workflow_test.php +++ b/tests/backup_and_restore_workflow_test.php @@ -67,6 +67,7 @@ class backup_and_restore_workflow_test extends \advanced_testcase { /** * Test to activate the manual workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager check if backup is created */ public function test_backup_workflow() { $backuptask = new backup_lifecycle_workflow($this->workflow->id); diff --git a/tests/backup_manager_test.php b/tests/backup_manager_test.php index 8934b0e4610e077cb0368e805ce7d3fd259de0fa..2ed7603274796cda590315a530336965d7fb9988 100644 --- a/tests/backup_manager_test.php +++ b/tests/backup_manager_test.php @@ -49,6 +49,7 @@ class backup_manager_test extends \advanced_testcase { /** * Test creating a backup for a course. + * @covers \tool_lifecycle\local\manager\backup_manager create backup */ public function test_backup_create() { global $DB; @@ -60,6 +61,7 @@ class backup_manager_test extends \advanced_testcase { /** * Test redirect without errors when starting to restore a backup. + * @covers \tool_lifecycle\local\manager\backup_manager restore backup */ public function test_backup_restore() { global $DB; diff --git a/tests/behat/activate_workflow.feature b/tests/behat/activate_workflow.feature index 3d1ba024befe61a917d057421e549e860ce24b02..e60ea3c548d56d35ab112c1c3881b6a48bdcc8b7 100644 --- a/tests/behat/activate_workflow.feature +++ b/tests/behat/activate_workflow.feature @@ -4,25 +4,25 @@ Feature: Add a workflow definition activate it Scenario: Add a new workflow definition with steps and rearrange Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | When I press "Save changes" - Then I should see "Workflow steps" - When I select "Start date delay trigger" from the "triggername" singleselect - Then I should see "Trigger for workflow 'My Workflow'" + Then I should see "Displayed to teachers as Teachers view on workflow" + When I select "Start date delay trigger" from the "tool_lifecycle-choose-trigger" singleselect + Then I should see "Create trigger" And I should see "Specific settings of the trigger type" When I set the following fields to these values: | instancename | delay trigger | | delay[number] | 2 | | delay[timeunit] | days | And I press "Save changes" - Then I should see "Workflow steps" + Then I should see "Displayed to teachers as Teachers view on workflow" And I should see "Add new step instance" - And I should see "Start date delay trigger" - When I select "Email step" from the "stepname" singleselect + And I should see "startdatedelay" + When I select "Email step" from the "tool_lifecycle-choose-step" singleselect And I set the following fields to these values: | Instance name | Email step | | responsetimeout[number] | 14 | @@ -31,29 +31,26 @@ Feature: Add a workflow definition activate it | Content plain text template | Content | | Content HTML Template | Content HTML | And I press "Save changes" - And I select "Create backup step" from the "stepname" singleselect + And I select "Create backup step" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Create backup step" And I press "Save changes" - And I select "Delete course step" from the "stepname" singleselect + And I select "Delete course step" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Delete Course 2" And I press "Save changes" - And I press "Back" + And I click on "Workflow drafts" "link" Then I should see the tool "View workflow steps" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table - And I should see the tool "Duplicate workflow" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table - And I should see the tool "Edit general settings" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table + And I should see the tool "Backup workflow" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table And I should see the tool "Delete workflow" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table - And I should not see the row "My Workflow" in the "tool_lifecycle_active_automatic_workflows" table - When I press "Activate" + When I click on "List active workflows" "link" + Then I should not see the row "My Workflow" in the "tool_lifecycle_active_automatic_workflows" table + When I click on "List workflow drafts" "link" + And I press "Activate" Then I should see the tool "View workflow steps" in the "My Workflow" row of the "tool_lifecycle_active_automatic_workflows" table + And I click on "List workflow drafts" "link" And I should not see the table "tool_lifecycle_workflow_definitions" - # And I should not see the tool "View Workflow Steps" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table # won't work because table doesnt exist when only workflow - # And I should see the tool "Duplicate workflow" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table - # And I should see the tool "Edit General Settings" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table - # And I should not see the tool "Delete workflow" in the "My Workflow" row of the "tool_lifecycle_workflow_definitions" table + And I click on "List active workflows" "link" When I click on the tool "View workflow steps" in the "My Workflow" row of the "tool_lifecycle_active_automatic_workflows" table - Then I should not see the tool "Edit" in any row of the "tool_lifecycle_workflows" table - And I should not see the tool "Delete" in any row of the "tool_lifecycle_workflows" table - And I should not see the tool "Up" in any row of the "tool_lifecycle_workflows" table - And I should not see the tool "Down" in any row of the "tool_lifecycle_workflows" table - And I should see the tool "View" in all rows of the "tool_lifecycle_workflows" table + Then I should see "Edit" + And I should not see "Move up" + And I should not see "Move down" And I should not see "Add new step instance" diff --git a/tests/behat/add_workflow.feature b/tests/behat/add_workflow.feature index 428e0bdf1085e9ae2253172b9ccd0bab702bd34d..ce5b70c4068dd78b8935d37983a9cca7f9f6eed2 100644 --- a/tests/behat/add_workflow.feature +++ b/tests/behat/add_workflow.feature @@ -5,15 +5,15 @@ Feature: Add a workflow definition Scenario: Add a new workflow definition without steps For displaying the additional trigger settings the "Save changes" button is used. Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | When I press "Save changes" - Then I should see "Workflow steps" - When I select "Manual trigger" from the "triggername" singleselect - Then I should see "Trigger for workflow 'My Workflow'" + Then I should see "Displayed to teachers as Teachers view on workflow" + When I select "Manual trigger" from the "tool_lifecycle-choose-trigger" singleselect + Then I should see "Create trigger" When I set the following fields to these values: | Instance name | My Trigger | And I press "Save changes" @@ -25,53 +25,53 @@ Feature: Add a workflow definition | Action name | Delete course | | Capability | moodle/course:manageactivities | And I press "Save changes" - Then I should see "Workflow steps" - And I should see "Manual trigger" - When I press "Back" + Then I should see "Displayed to teachers as Teachers view on workflow" + And I should see "manual" + When I click on "Workflow drafts" "link" Then I should see "My Workflow" Scenario: Add a new workflow definition with steps For displaying the additional trigger settings the "reload" button is used. Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | When I press "Save changes" - Then I should see "Workflow steps" - When I select "Start date delay trigger" from the "triggername" singleselect + Then I should see "Displayed to teachers as Teachers view on workflow" + When I select "Start date delay trigger" from the "tool_lifecycle-choose-trigger" singleselect And I set the following fields to these values: | Instance name | My Trigger | | delay[number] | 2 | | delay[timeunit] | days | And I press "Save changes" - Then I should see "Workflow steps" - And I should see "Start date delay trigger" - When I select "Delete course step" from the "stepname" singleselect + Then I should see "Displayed to teachers as Teachers view on workflow" + And I should see "startdatedelay" + When I select "Delete course step" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Delete Course" And I press "Save changes" - Then I should see "Workflow steps" - And I should see "Delete Course" + Then I should see "Displayed to teachers as Teachers view on workflow" + And I should see "deletecourse" Scenario: Add a new workflow definition and alter trigger Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | When I press "Save changes" - Then I should see "Workflow steps" - When I select "Start date delay trigger" from the "triggername" singleselect + Then I should see "Displayed to teachers as Teachers view on workflow" + When I select "Start date delay trigger" from the "tool_lifecycle-choose-trigger" singleselect And I set the following fields to these values: | Instance name | My Trigger | | delay[number] | 2 | | delay[timeunit] | days | And I press "Save changes" - Then I should see "Workflow steps" - And I should see "Start date delay trigger" - When I click on the tool "Edit" in the "Trigger" row of the "tool_lifecycle_workflows" table + Then I should see "Displayed to teachers as Teachers view on workflow" + And I should see "startdatedelay" + When I click on "Edit" in the trigger "Trigger" Then the following fields match these values: | Instance name | My Trigger | | delay[number] | 2 | @@ -81,7 +81,7 @@ Feature: Add a workflow definition | delay[number] | 4 | | delay[timeunit] | weeks | And I press "Save changes" - When I click on the tool "Edit" in the "Trigger" row of the "tool_lifecycle_workflows" table + When I click on "Edit" in the trigger "Trigger" Then the following fields match these values: | Instance name | Other Trigger | | delay[number] | 4 | @@ -89,38 +89,38 @@ Feature: Add a workflow definition Scenario: Add a new workflow definition with steps and rearange Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | When I press "Save changes" - Then I should see "Workflow steps" - When I select "Start date delay trigger" from the "triggername" singleselect + Then I should see "Displayed to teachers as Teachers view on workflow" + When I select "Start date delay trigger" from the "tool_lifecycle-choose-trigger" singleselect And I set the following fields to these values: | Instance name | My Trigger | | delay[number] | 2 | | delay[timeunit] | days | And I press "Save changes" - Then I should see "Workflow steps" - And I should see "Start date delay trigger" - When I select "Delete course step" from the "stepname" singleselect + Then I should see "Displayed to teachers as Teachers view on workflow" + And I should see "startdatedelay" + When I select "Delete course step" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Delete Course 1" And I press "Save changes" - And I select "Delete course step" from the "stepname" singleselect + And I select "deletecourse" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Delete Course 2" And I press "Save changes" - And I select "Create backup step" from the "stepname" singleselect + And I select "createbackup" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Create backup step" And I press "Save changes" Then the step "Delete Course 1" should be at the 1 position And the step "Delete Course 2" should be at the 2 position And the step "Create backup step" should be at the 3 position - And I click on the tool "Down" in the "Delete Course 1" row of the "tool_lifecycle_workflows" table + And I click on "Move down" in the step "Delete Course 1" Then the step "Delete Course 1" should be at the 2 position And the step "Delete Course 2" should be at the 1 position And the step "Create backup step" should be at the 3 position - And I click on the tool "Up" in the "Create backup step" row of the "tool_lifecycle_workflows" table + And I click on "Move up" in the step "Create backup step" Then the step "Delete Course 1" should be at the 3 position And the step "Delete Course 2" should be at the 1 position And the step "Create backup step" should be at the 2 position diff --git a/tests/behat/behat_tool_lifecycle.php b/tests/behat/behat_tool_lifecycle.php index b742d30c876939739e9712b6ed38bc3e6e3f6c17..b5f7d70792bfb10cdf1472801da2a5a53e5ccd4b 100644 --- a/tests/behat/behat_tool_lifecycle.php +++ b/tests/behat/behat_tool_lifecycle.php @@ -362,7 +362,9 @@ class behat_tool_lifecycle extends behat_base { * @throws ExpectationException */ public function the_step_should_be_at_the_position($stepname, $position) { - $xpathelement = "//table/tbody/tr[@id = 'tool_lifecycle_workflows_r$position']/td[contains(text(),'$stepname')]"; + $xpathelement = "(//*[@id='lifecycle-workflow-details']" . + "//*[contains(concat(' ', normalize-space(@class), ' '), ' workflow-step ')])[$position]" . + "//span[contains(text(),'$stepname')]"; try { $this->find('xpath', $xpathelement); @@ -372,6 +374,53 @@ class behat_tool_lifecycle extends behat_base { } } + /** + * Click on a link in a step card with a specific title. + * + * @When /^I click on "([^"]*)" in the step "([^"]*)"$/ + * + * @param string $linktext + * @param string $stepname + * @throws ElementNotFoundException + * @throws ExpectationException + */ + public function i_click_on_in_the_step($linktext, $stepname) { + $xpathelement = "//*[@id='lifecycle-workflow-details']" . + "//*[contains(concat(' ', normalize-space(@class), ' '), ' workflow-step ') and " . + "descendant::span[contains(text(),'$stepname')]]"; + try { + $this->find('xpath', $xpathelement); + } catch (ElementNotFoundException $e) { + throw new ExpectationException('"The step ' . $stepname . ' was not found.', $this->getSession()); + } + $xpathelement = $xpathelement . "//span[contains(text(), '$linktext')]/parent::a"; + $element = $this->find('xpath', $xpathelement); + $element->click(); + } + + /** + * Click on a link in a trigger card with a specific title. + * + * @When /^I click on "([^"]*)" in the trigger "([^"]*)"$/ + * + * @param string $linktext + * @param string $triggername + * @throws ElementNotFoundException + * @throws ExpectationException + */ + public function i_click_on_in_the_trigger($linktext, $triggername) { + $xpathelement = "//*[@id='lifecycle-workflow-details']" . + "//*[contains(concat(' ', normalize-space(@class), ' '), ' workflow-trigger ') and " . + "descendant::span[contains(text(),'$triggername')]]"; + try { + $this->find('xpath', $xpathelement); + } catch (ElementNotFoundException $e) { + throw new ExpectationException('"The trigger ' . $triggername . ' was not found.', $this->getSession()); + } + $xpathelement = $xpathelement . "//span[contains(text(), '$linktext')]/parent::a"; + $element = $this->find('xpath', $xpathelement); + $element->click(); + } /** * Opens Teacher's Courses Overview. diff --git a/tests/behat/disable_workflow.feature b/tests/behat/disable_workflow.feature index 34f574a8768ca0aaca295facf2aa273537d88e66..9c06f07d832946c181569eb1a5df5e4b05799d42 100644 --- a/tests/behat/disable_workflow.feature +++ b/tests/behat/disable_workflow.feature @@ -7,19 +7,19 @@ Feature: Disable a workflow | fullname | shortname | category | startdate | | Course 1 | C1 | 0 | ##4 days ago## | And I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: - | Title | My Workflow | - | Displayed workflow title | Teachers view on workflow | + | Title | My Workflow | + | Displayed workflow title | Teachers view on workflow | And I press "Save changes" - And I select "Start date delay trigger" from the "triggername" singleselect + And I select "Start date delay trigger" from the "tool_lifecycle-choose-trigger" singleselect And I set the following fields to these values: | Instance name | My Trigger | | delay[number] | 3 | | delay[timeunit] | days | And I press "Save changes" - And I select "Email step" from the "stepname" singleselect + And I select "Email step" from the "tool_lifecycle-choose-step" singleselect And I set the following fields to these values: | Instance name | Email step | | responsetimeout[number] | 42 | @@ -28,10 +28,10 @@ Feature: Disable a workflow | Content plain text template | Content | | Content HTML Template | Content HTML | And I press "Save changes" - And I select "Delete course step" from the "stepname" singleselect + And I select "Delete course step" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Delete Course 1" And I press "Save changes" - And I press "Back" + And I click on "Workflow drafts" "link" And I press "Activate" When I wait "10" seconds And I run the scheduled task "tool_lifecycle\task\lifecycle_task" @@ -39,7 +39,7 @@ Feature: Disable a workflow Scenario: Disable an workflow, keep processes running, then abort all processes and delete workflow Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration + And I navigate to "Plugins > Admin tools > Life Cycle > Active workflows" in site administration Then I should see the row "My Workflow" in the "tool_lifecycle_active_automatic_workflows" table And I should see the tool "Disable workflow (processes keep running)" in the "My Workflow" row of the "tool_lifecycle_active_automatic_workflows" table When I click on the tool "Disable workflow (processes keep running)" in the "My Workflow" row of the "tool_lifecycle_active_automatic_workflows" table @@ -56,7 +56,7 @@ Feature: Disable a workflow Scenario: Disable an workflow and kill processes (abort), then delete workflow Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration + And I navigate to "Plugins > Admin tools > Life Cycle > Active workflows" in site administration Then I should see the row "My Workflow" in the "tool_lifecycle_active_automatic_workflows" table And I should see the tool "Disable workflow (abort processes, maybe unsafe!)" in the "My Workflow" row of the "tool_lifecycle_active_automatic_workflows" table When I click on the tool "Disable workflow (abort processes, maybe unsafe!)" in the "My Workflow" row of the "tool_lifecycle_active_automatic_workflows" table @@ -68,13 +68,20 @@ Feature: Disable a workflow Scenario: Disable an workflow then create (duplicate) a new one with the same configuration Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration + And I navigate to "Plugins > Admin tools > Life Cycle > Active workflows" in site administration Then I should see the row "My Workflow" in the "tool_lifecycle_active_automatic_workflows" table - When I click on the tool "Disable workflow (processes keep running)" in the "My Workflow" row of the "tool_lifecycle_active_automatic_workflows" table - When I click on the tool "Duplicate workflow" in the "My Workflow" row of the "tool_lifecycle_deactivated_workflows" table + And I click on the tool "Disable workflow (processes keep running)" in the "My Workflow" row of the "tool_lifecycle_active_automatic_workflows" table + When I click on "List workflow drafts" "link" + And I click on "Copy new workflow from existing" "link" + And I click on the tool "Create copy" in the "My Workflow" row of the "tool_lifecycle-select-workflow" table + And I press "Save changes" + And I click on "Workflow drafts" "link" Then I should see the row "My Workflow" in the "tool_lifecycle_workflow_definitions" table + And I click on "List active workflows" "link" And I should not see the row "My Workflow" in the "tool_lifecycle_active_automatic_workflows" table + And I click on "List workflow drafts" "link" When I press "Activate" # Since no element is left, the table is not displayed anymore. - Then I should not see the table "tool_lifecycle_workflow_definitions" And I should see the row "My Workflow" in the "tool_lifecycle_active_automatic_workflows" table + And I click on "List workflow drafts" "link" + Then I should not see the table "tool_lifecycle_workflow_definitions" diff --git a/tests/behat/interaction.feature b/tests/behat/interaction.feature index 367dbbfde3d817d7283cb9b2fae95133d344063a..6b4885ac612c3d489033996908ae5a888e0bea38 100644 --- a/tests/behat/interaction.feature +++ b/tests/behat/interaction.feature @@ -16,19 +16,19 @@ Feature: Add a workflow with an email step and test the interaction as a teacher | teacher1 | C2 | editingteacher | | teacher1 | C3 | editingteacher | And I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | And I press "Save changes" - And I select "Start date delay trigger" from the "triggername" singleselect + And I select "Start date delay trigger" from the "tool_lifecycle-choose-trigger" singleselect And I set the following fields to these values: | Instance name | My Trigger | | delay[number] | 3 | | delay[timeunit] | days | And I press "Save changes" - And I select "Email step" from the "stepname" singleselect + And I select "Email step" from the "tool_lifecycle-choose-step" singleselect And I set the following fields to these values: | Instance name | Email step | | responsetimeout[number] | 8 | @@ -37,10 +37,10 @@ Feature: Add a workflow with an email step and test the interaction as a teacher | Content plain text template | Content | | Content HTML Template | Content HTML | And I press "Save changes" - And I select "Delete course step" from the "stepname" singleselect + And I select "Delete course step" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Delete Course 2" And I press "Save changes" - And I press "Back" + And I click on "Workflow drafts" "link" And I press "Activate" And I log out diff --git a/tests/behat/interaction_forms.feature b/tests/behat/interaction_forms.feature index a00df30a53d8f843b9e7dbe04c46fc572f7174fe..a8345da6bae4001e8b64acfe57725302cd3a083e 100644 --- a/tests/behat/interaction_forms.feature +++ b/tests/behat/interaction_forms.feature @@ -12,24 +12,24 @@ Feature: Add a workflow with a manual trigger and a duplicate step and test the | user | course | role | | teacher1 | C1 | editingteacher | And I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | And I press "Save changes" - And I select "Manual trigger" from the "triggername" singleselect + And I select "Manual trigger" from the "tool_lifecycle-choose-trigger" singleselect And I set the following fields to these values: | Instance name | My Trigger | | Icon | t/delete | | Action name | Duplicate course | | Capability | moodle/course:manageactivities | And I press "Save changes" - And I select "Duplicate step" from the "stepname" singleselect + And I select "Duplicate step" from the "tool_lifecycle-choose-step" singleselect And I set the following fields to these values: | Instance name | Duplicate step | And I press "Save changes" - And I press "Back" + And I click on "Workflow drafts" "link" And I press "Activate" And I log out diff --git a/tests/behat/manual_trigger.feature b/tests/behat/manual_trigger.feature index c2b92224b13efea9a5ba1d631a0e1030faa13e54..164d4088fc40cafe0cee15615ee807109c64dbf0 100644 --- a/tests/behat/manual_trigger.feature +++ b/tests/behat/manual_trigger.feature @@ -24,20 +24,20 @@ Feature: Add a manual trigger and test view and actions as a teacher And I set the following system permissions of "Non-editing teacher" role: | capability | permission | | tool/lifecycle:managecourses | Allow | - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | And I press "Save changes" - And I select "Manual trigger" from the "triggername" singleselect + And I select "Manual trigger" from the "tool_lifecycle-choose-trigger" singleselect And I set the following fields to these values: | Instance name | My Trigger | | Icon | t/delete | | Action name | Delete course | | Capability | moodle/course:manageactivities | And I press "Save changes" - And I press "Back" + And I click on "Workflow drafts" "link" And I press "Activate" And I log out And I log in as "teacher1" @@ -52,26 +52,26 @@ Feature: Add a manual trigger and test view and actions as a teacher @javascript Scenario: Manually trigger backup and course deletion Given I log in as "admin" - And I navigate to "Plugins > Admin tools > Life Cycle > Workflow settings" in site administration - And I press "Add workflow" + And I navigate to "Plugins > Admin tools > Life Cycle > Workflow drafts" in site administration + And I click on "Create new workflow" "link" And I set the following fields to these values: | Title | My Workflow | | Displayed workflow title | Teachers view on workflow | And I press "Save changes" - And I select "Manual trigger" from the "triggername" singleselect + And I select "Manual trigger" from the "tool_lifecycle-choose-trigger" singleselect And I set the following fields to these values: | Instance name | My Trigger | | Icon | t/delete | | Action name | Delete course | | Capability | moodle/course:manageactivities | And I press "Save changes" - And I select "Create backup step" from the "stepname" singleselect + And I select "Create backup step" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Create backup step" And I press "Save changes" - And I select "Delete course step" from the "stepname" singleselect + And I select "Delete course step" from the "tool_lifecycle-choose-step" singleselect And I set the field "Instance name" to "Delete Course 2" And I press "Save changes" - And I press "Back" + And I click on "Workflow drafts" "link" And I press "Activate" And I log out And I log in as "teacher1" diff --git a/tests/manual_trigger_tools_test.php b/tests/manual_trigger_tools_test.php index f6c36d0cfbfdd223e766ac06fd1a553cacaff190..db905e01f8e94b58f7349c554f86b1583c4f8a78 100644 --- a/tests/manual_trigger_tools_test.php +++ b/tests/manual_trigger_tools_test.php @@ -85,6 +85,7 @@ class manual_trigger_tools_test extends \advanced_testcase { /** * Test getting manual trigger tools of active workflows. + * @covers \tool_lifecycle\local\manager\workflow_manager get triggers for one wf */ public function test_get_manual_trigger_tools_for_one_active_workflow() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow2->id); @@ -99,6 +100,7 @@ class manual_trigger_tools_test extends \advanced_testcase { /** * Test getting manual trigger tools of active workflows. + * @covers \tool_lifecycle\local\manager\workflow_manager get triggers for multiple wf */ public function test_get_manual_trigger_tools_for_active_workflows() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow2->id); diff --git a/tests/manually_triggered_process_test.php b/tests/manually_triggered_process_test.php index e0ee7e81c1fba7fd99cedfbe2b1ed34741caf676..52597adc0145ab6965695b8561683abf991923d1 100644 --- a/tests/manually_triggered_process_test.php +++ b/tests/manually_triggered_process_test.php @@ -93,6 +93,7 @@ class manually_triggered_process_test extends \advanced_testcase { /** * Test to proceed a manually triggered process to step index 1. + * @covers \tool_lifecycle\local\manager\process_manager test if manual process started */ public function test_proceeding_of_manually_triggered_processes() { $process = process_manager::manually_trigger_process($this->course->id, $this->trigger->id); diff --git a/tests/persistence/persist_process_data_test.php b/tests/persistence/persist_process_data_test.php index 462d2d913029727b7e9cbf59b304fe12d1b282c6..b76f67be65f0f81e0aeedbfe988f9e486daa94e9 100644 --- a/tests/persistence/persist_process_data_test.php +++ b/tests/persistence/persist_process_data_test.php @@ -22,6 +22,7 @@ * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace tool_lifecycle; defined('MOODLE_INTERNAL') || die(); require_once(__DIR__ . '/../../lib.php'); @@ -39,7 +40,7 @@ use tool_lifecycle\local\manager\step_manager; * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class tool_lifecycle_persist_process_data_testcase extends \advanced_testcase { +class persist_process_data_test extends \advanced_testcase { /** @var process $process Instance of the process. */ private $process; @@ -67,6 +68,7 @@ class tool_lifecycle_persist_process_data_testcase extends \advanced_testcase { /** * Test the getting and setting of process data. + * @covers \tool_lifecycle\local\manager\process_data_manager */ public function test_get_set_process_data() { $step = step_manager::get_step_instance_by_workflow_index($this->process->workflowid, $this->process->stepindex); diff --git a/tests/persistence/persist_process_test.php b/tests/persistence/persist_process_test.php index cf985dd0066baeca3cb3515adb84d67dac7c18e0..2915ab552a2ba6c3673851f7351561320665dced 100644 --- a/tests/persistence/persist_process_test.php +++ b/tests/persistence/persist_process_test.php @@ -22,6 +22,7 @@ * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace tool_lifecycle; defined('MOODLE_INTERNAL') || die(); require_once(__DIR__ . '/../../lib.php'); @@ -38,7 +39,7 @@ use tool_lifecycle\local\manager\process_manager; * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class tool_lifecycle_persist_process_testcase extends \advanced_testcase { +class persist_process_test extends \advanced_testcase { /** @var workflow $workflow Instance of the workflow. */ private $workflow; @@ -60,6 +61,7 @@ class tool_lifecycle_persist_process_testcase extends \advanced_testcase { /** * Test the creation of a process. + * @covers \tool_lifecycle\local\manager\process_manager */ public function test_create() { $process = process_manager::create_process($this->course->id, $this->workflow->id); @@ -73,6 +75,7 @@ class tool_lifecycle_persist_process_testcase extends \advanced_testcase { /** * Tests setting a process on waiting. + * @covers \tool_lifecycle\local\manager\process_manager */ public function test_process_waiting() { $process = process_manager::create_process($this->course->id, $this->workflow->id); @@ -84,6 +87,7 @@ class tool_lifecycle_persist_process_testcase extends \advanced_testcase { /** * Tests deletion of a process when rolledback. + * @covers \tool_lifecycle\local\manager\process_manager */ public function test_process_rollback() { $process = process_manager::create_process($this->course->id, $this->workflow->id); @@ -95,6 +99,7 @@ class tool_lifecycle_persist_process_testcase extends \advanced_testcase { /** * Tests proceeding a process to the next step. + * @covers \tool_lifecycle\local\manager\process_manager */ public function test_process_proceed() { $process = process_manager::create_process($this->course->id, $this->workflow->id); diff --git a/tests/persistence/persist_step_test.php b/tests/persistence/persist_step_test.php index e87f466eaf2da5009af4718c337ad7e55a97840f..bc830a0a83ad672956fc520d57cb101adb7400d9 100644 --- a/tests/persistence/persist_step_test.php +++ b/tests/persistence/persist_step_test.php @@ -22,6 +22,7 @@ * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace tool_lifecycle; defined('MOODLE_INTERNAL') || die(); require_once(__DIR__ . '/../../lib.php'); @@ -38,7 +39,7 @@ use tool_lifecycle\local\manager\step_manager; * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class tool_lifecycle_persist_step_testcase extends \advanced_testcase { +class persist_step_test extends \advanced_testcase { /** @var workflow $workflow Instance of the workflow. */ private $workflow; @@ -60,6 +61,7 @@ class tool_lifecycle_persist_step_testcase extends \advanced_testcase { /** * Test that after an insert the id from the database is set within the step object. + * @covers \tool_lifecycle\local\manager\step_manager */ public function test_add_step() { $step = $this->generator->create_step( @@ -73,6 +75,7 @@ class tool_lifecycle_persist_step_testcase extends \advanced_testcase { /** * Test that sortindizes are created correclty when creating multiple steps. + * @covers \tool_lifecycle\local\manager\step_manager */ public function test_add_multiple_steps() { $step1 = $this->generator->create_step( @@ -94,6 +97,7 @@ class tool_lifecycle_persist_step_testcase extends \advanced_testcase { /** * Test that the step can be removed correctly. + * @covers \tool_lifecycle\local\manager\step_manager */ public function test_remove_step() { $step1 = $this->generator->create_step( @@ -126,6 +130,7 @@ class tool_lifecycle_persist_step_testcase extends \advanced_testcase { /** * Test that sortindizes are still created correctly, when some steps were already removed. + * @covers \tool_lifecycle\local\manager\step_manager */ public function test_add_after_remove_step() { $step1 = $this->generator->create_step( diff --git a/tests/persistence/persist_workflow_test.php b/tests/persistence/persist_workflow_test.php index a68aae36de1e845c2312a90c3f83eb704714b307..f4109c5574d4bfebe5b8a723ba0a5830e0e25d36 100644 --- a/tests/persistence/persist_workflow_test.php +++ b/tests/persistence/persist_workflow_test.php @@ -22,6 +22,7 @@ * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +namespace tool_lifecycle; defined('MOODLE_INTERNAL') || die(); require_once(__DIR__ . '/../../lib.php'); @@ -37,7 +38,7 @@ use tool_lifecycle\local\manager\workflow_manager; * @copyright 2017 Tobias Reischmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class tool_lifecycle_persist_workflow_testcase extends \advanced_testcase { +class persist_workflow_test extends \advanced_testcase { /** @var workflow $workflow Instance of the workflow. */ private $workflow; @@ -47,7 +48,7 @@ class tool_lifecycle_persist_workflow_testcase extends \advanced_testcase { */ public function setUp() : void { $this->resetAfterTest(true); - $record = new stdClass(); + $record = new \stdClass(); $record->id = null; $record->title = 'Title'; $this->workflow = workflow::from_record($record); @@ -55,6 +56,7 @@ class tool_lifecycle_persist_workflow_testcase extends \advanced_testcase { /** * Test the creation of a process. + * @covers \tool_lifecycle\local\manager\workflow_manager create a wf. */ public function test_create() { $this->assertNull($this->workflow->id); diff --git a/tests/privacy_test.php b/tests/privacy_test.php index dc047519c396c758c9984ba48ce2da2c84e57eee..461a5f37cf9b9d0b9f466915e31d887a9a05b8fe 100644 --- a/tests/privacy_test.php +++ b/tests/privacy_test.php @@ -22,7 +22,7 @@ * @copyright 2019 Justus Dieckmann WWU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -namespace tool_lifecycle\privacy; +namespace tool_lifecycle; defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -92,6 +92,14 @@ class privacy_test extends provider_testcase { $this->emailstep = $this->generator->create_step("instance2", "email", $this->workflow->id); } + /** + * Get all contextes in which users are effected. + * @covers \tool_lifecycle\privacy\provider contexts + * @throws \coding_exception + * @throws \dml_exception + * @throws \invalid_parameter_exception + * @throws \moodle_exception + */ public function test_get_contexts_for_userid() { $c1 = $this->getDataGenerator()->create_course(); $c2 = $this->getDataGenerator()->create_course(); @@ -114,6 +122,14 @@ class privacy_test extends provider_testcase { $this->assertTrue($contextlist->current() instanceof \context_system); } + /** + * Export all data for privacy provider + * @covers \tool_lifecycle\privacy\provider data + * @throws \coding_exception + * @throws \dml_exception + * @throws \invalid_parameter_exception + * @throws \moodle_exception + */ public function test_export_user_data() { $c1 = $this->getDataGenerator()->create_course(); $c2 = $this->getDataGenerator()->create_course(); @@ -145,6 +161,14 @@ class privacy_test extends provider_testcase { $this->assertEquals(self::ACTION_KEEP, $data2->action); } + /** + * delete data for context - privacy provider + * @covers \tool_lifecycle\privacy\provider data + * @throws \coding_exception + * @throws \dml_exception + * @throws \invalid_parameter_exception + * @throws \moodle_exception + */ public function test_delete_data_for_all_users_in_context() { global $DB; $c1 = $this->getDataGenerator()->create_course(); @@ -163,6 +187,14 @@ class privacy_test extends provider_testcase { $this->assertFalse($DB->record_exists_select('tool_lifecycle_action_log', 'userid != -1')); } + /** + * delete data for user - privacy provider + * @covers \tool_lifecycle\privacy\provider data + * @throws \coding_exception + * @throws \dml_exception + * @throws \invalid_parameter_exception + * @throws \moodle_exception + */ public function test_delete_data_for_user() { global $DB; $c1 = $this->getDataGenerator()->create_course(); @@ -189,6 +221,14 @@ class privacy_test extends provider_testcase { $this->assertEquals(1, $DB->count_records_select('tool_lifecycle_action_log', "userid = -1")); } + /** + * all users of context - privacy provider + * @covers \tool_lifecycle\privacy\provider user in context + * @throws \coding_exception + * @throws \dml_exception + * @throws \invalid_parameter_exception + * @throws \moodle_exception + */ public function test_get_users_in_context() { $c1 = $this->getDataGenerator()->create_course(); $c2 = $this->getDataGenerator()->create_course(); @@ -210,6 +250,14 @@ class privacy_test extends provider_testcase { $this->assertEquals($u1->id, $userlist->current()->id); } + /** + * delete data for *users* - privacy provider + * @covers \tool_lifecycle\privacy\provider data *users* + * @throws \coding_exception + * @throws \dml_exception + * @throws \invalid_parameter_exception + * @throws \moodle_exception + */ public function test_delete_data_for_users() { global $DB; $c1 = $this->getDataGenerator()->create_course(); diff --git a/tests/process_error_test.php b/tests/process_error_test.php index 3298e1007e8deb217572237e6084a323bf9bac49..2e601fe11820efaaaab7e2d36e842aed0616084e 100644 --- a/tests/process_error_test.php +++ b/tests/process_error_test.php @@ -90,6 +90,7 @@ class process_error_test extends \advanced_testcase { /** * Test if the correct process error was put into the table. + * @covers \tool_lifecycle\processor */ public function test_process_error_in_table() { global $DB; diff --git a/tests/process_status_message_test.php b/tests/process_status_message_test.php index b3b4c264d3eddc148b6772987b69edbb68f8f53e..dad40e119dfdc77fe66406d76e67b6d1dc2ebe16 100644 --- a/tests/process_status_message_test.php +++ b/tests/process_status_message_test.php @@ -74,6 +74,7 @@ class process_status_message_test extends \advanced_testcase { /** * Test getting status message for a process. + * @covers \tool_lifecycle\local\manager\interaction_manager */ public function test_get_status_message() { $process = $this->generator->create_process(2, $this->workflow->id); diff --git a/tests/settings_manager_test.php b/tests/settings_manager_test.php index d418e3a6f5587be9aa5a3d4044f72e4306d66dfa..e91623cbfcce14a2a37215269455d825ff3b04de 100644 --- a/tests/settings_manager_test.php +++ b/tests/settings_manager_test.php @@ -69,6 +69,7 @@ class settings_manager_test extends \advanced_testcase { /** * Test setting and getting settings data for steps. + * @covers \tool_lifecycle\local\manager\settings_manager */ public function test_set_get_step_settings() { $data = new \stdClass(); @@ -81,6 +82,7 @@ class settings_manager_test extends \advanced_testcase { /** * Test setting and getting settings data for triggers. + * @covers \tool_lifecycle\local\manager\settings_manager */ public function test_set_get_trigger_settings() { $data = new \stdClass(); @@ -93,6 +95,7 @@ class settings_manager_test extends \advanced_testcase { /** * Test correct removal of setting, if steps, triggers or workflows are deleted. + * @covers \tool_lifecycle\local\manager\settings_manager */ public function test_remove_workflow() { global $DB; diff --git a/tests/workflow_activate_disable_duplicate_test.php b/tests/workflow_activate_disable_duplicate_test.php index 6aac9fad4958c1ba24d2f287240ce21964bf44fb..e48b37c0dfcb9536cba672576538ad51867e3c19 100644 --- a/tests/workflow_activate_disable_duplicate_test.php +++ b/tests/workflow_activate_disable_duplicate_test.php @@ -46,6 +46,7 @@ class workflow_activate_disable_duplicate_test extends workflow_actions_testcase /** * Test to activate the first workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager */ public function test_activate_first() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); @@ -56,6 +57,7 @@ class workflow_activate_disable_duplicate_test extends workflow_actions_testcase /** * Test to activate the first and the second workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager */ public function test_activate_second() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); @@ -68,6 +70,7 @@ class workflow_activate_disable_duplicate_test extends workflow_actions_testcase /** * Test to activate all three workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager */ public function test_activate_third() { workflow_manager::handle_action(action::WORKFLOW_ACTIVATE, $this->workflow1->id); @@ -81,6 +84,7 @@ class workflow_activate_disable_duplicate_test extends workflow_actions_testcase /** * Test to deactivate the first workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager */ public function test_deactivate_first() { workflow_manager::handle_action(action::WORKFLOW_ABORTDISABLE, $this->workflow1->id); @@ -89,6 +93,7 @@ class workflow_activate_disable_duplicate_test extends workflow_actions_testcase /** * Test to duplicate the first workflow. + * @covers \tool_lifecycle\local\manager\workflow_manager */ public function test_duplicate_first() { workflow_manager::handle_action(action::WORKFLOW_DUPLICATE, $this->workflow1->id); diff --git a/trigger/categories/tests/trigger_test.php b/trigger/categories/tests/trigger_test.php index 077149e435340a2ff127c58ff7853154255f9ddd..c00bfda60f538d18347eb0d56a3c6bc79bec89b4 100644 --- a/trigger/categories/tests/trigger_test.php +++ b/trigger/categories/tests/trigger_test.php @@ -84,6 +84,7 @@ class trigger_test extends \advanced_testcase { /** * Tests if courses, which are in the category are correctly triggered. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\categories */ public function test_course_has_cat() { @@ -108,6 +109,7 @@ class trigger_test extends \advanced_testcase { /** * Tests if courses, which are in the category are correctly triggered. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\categories */ public function test_course_within_cat() { @@ -132,6 +134,7 @@ class trigger_test extends \advanced_testcase { /** * Tests if courses, which are not in the category are correctly triggered. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\categories */ public function test_course_not_within_cat() { $course = $this->getDataGenerator()->create_course(); diff --git a/trigger/delayedcourses/tests/trigger_test.php b/trigger/delayedcourses/tests/trigger_test.php index 89dd51d333de703c52b93909ac5ee19dc6c609c0..ed8dedf0b8e22733d442a989464895b2d335eb3f 100644 --- a/trigger/delayedcourses/tests/trigger_test.php +++ b/trigger/delayedcourses/tests/trigger_test.php @@ -69,6 +69,7 @@ class trigger_test extends \advanced_testcase { /** * Tests that a course is not excluded by this plugin, when there exists no dalayed entry, yet. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\delayedcourses */ public function test_course_not_delayed() { @@ -87,6 +88,7 @@ class trigger_test extends \advanced_testcase { /** * Tests that a course is excluded by this plugin, when there exists a dalayed entry. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\delayedcourses */ public function test_course_delayed() { @@ -107,6 +109,7 @@ class trigger_test extends \advanced_testcase { /** * Tests that a course is not excluded by this plugin, when there exists a dalayed entry, but it is expired. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\delayedcourses */ public function test_course_delay_expired() { $course = $this->getDataGenerator()->create_course(); @@ -124,6 +127,7 @@ class trigger_test extends \advanced_testcase { /** * Tests that a course is not excluded by this plugin, when it was delayed for a single workflow. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\delayedcourses */ public function test_course_delay_for_single_workflow() { $course = $this->getDataGenerator()->create_course(); @@ -141,6 +145,7 @@ class trigger_test extends \advanced_testcase { /** * Tests that a course is excluded by this plugin, when it was delayed for all workflows. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\delayedcourses */ public function test_course_delay_for_all_workflows() { $course = $this->getDataGenerator()->create_course(); diff --git a/trigger/sitecourse/tests/trigger_test.php b/trigger/sitecourse/tests/trigger_test.php index 655ccfa77261dd84b77f57cfbc47a55e399d67dc..2f48f48b53d778d6d806abfe43c4966e059420b8 100644 --- a/trigger/sitecourse/tests/trigger_test.php +++ b/trigger/sitecourse/tests/trigger_test.php @@ -59,6 +59,7 @@ class trigger_test extends \advanced_testcase { /** * Tests if the site course is excluded by this plugin. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\sitecourse */ public function test_sitecourse_course() { @@ -72,6 +73,7 @@ class trigger_test extends \advanced_testcase { /** * Tests if courses, which are older than the default of 190 days are triggered by this plugin. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\sitecourse */ public function test_normal_course() { diff --git a/trigger/startdatedelay/tests/trigger_test.php b/trigger/startdatedelay/tests/trigger_test.php index 6ca598fd0c48cea2baf916ff65ddea74502a1873..0175bf4ea68c78c5ecceb01f4ab192fcc1a85d5e 100644 --- a/trigger/startdatedelay/tests/trigger_test.php +++ b/trigger/startdatedelay/tests/trigger_test.php @@ -58,6 +58,7 @@ class trigger_test extends \advanced_testcase { /** * Tests if courses, which are newer than the default of 190 days are not triggered by this plugin. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\startdatedelay */ public function test_young_course() { @@ -76,6 +77,7 @@ class trigger_test extends \advanced_testcase { /** * Tests if courses, which are older than the default of 190 days are triggered by this plugin. + * @covers \tool_lifecycle\processor \tool_lifecycle\trigger\startdatedelay */ public function test_old_course() { diff --git a/uploadworkflow.php b/uploadworkflow.php new file mode 100644 index 0000000000000000000000000000000000000000..7fd08dfb57bf98c860634621d9a3c4d3218f3b60 --- /dev/null +++ b/uploadworkflow.php @@ -0,0 +1,70 @@ +<?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/>. + +/** + * Displays form for uploading a backed up workflow. + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_lifecycle\action; +use tool_lifecycle\local\backup\restore_lifecycle_workflow; +use tool_lifecycle\local\entity\workflow; +use tool_lifecycle\local\form\form_upload_workflow; +use tool_lifecycle\local\form\form_workflow_instance; +use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\local\table\workflow_definition_table; +use tool_lifecycle\urls; + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_login(); +global $OUTPUT, $PAGE, $DB; + +\tool_lifecycle\permission_and_navigation::setup_draft(); + +$PAGE->set_url(new \moodle_url(urls::UPLOAD_WORKFLOW)); +$title = get_string('upload_workflow', 'tool_lifecycle'); +$PAGE->set_title($title); +$PAGE->set_heading($title); +$PAGE->navbar->add($title, $PAGE->url); + +$form = new form_upload_workflow(); +if ($form->is_cancelled()) { + // Cancelled, redirect back to workflow drafts. + redirect(new moodle_url(urls::WORKFLOW_DRAFTS)); +} + +$renderer = $PAGE->get_renderer('tool_lifecycle'); + +if ($data = $form->get_data()) { + $xmldata = $form->get_file_content('backupfile'); + $restore = new restore_lifecycle_workflow($xmldata); + $errors = $restore->execute(); + if (count($errors) != 0) { + /** @var \tool_lifecycle_renderer $renderer */ + $renderer = $PAGE->get_renderer('tool_lifecycle'); + $renderer->render_workflow_upload_form($form, $errors); + die(); + } else { + // Redirect to workflow page. + redirect(urls::WORKFLOW_DETAILS, ['wf' => $restore->get_workflow()->id]); + } +} + +$renderer->render_workflow_upload_form($form); diff --git a/view.php b/view.php index 8ad1df50a3e7b09d55ff4950cae628ec0a9f2eb1..245c9a46800ccdfa4fa5152b0c7c97b00defa39e 100644 --- a/view.php +++ b/view.php @@ -23,10 +23,6 @@ */ require_once(__DIR__ . '/../../../config.php'); -use tool_lifecycle\local\manager\step_manager; -use tool_lifecycle\local\manager\interaction_manager; -use tool_lifecycle\local\table\interaction_attention_table; - require_login(null, false); $PAGE->set_context(context_system::instance()); @@ -63,17 +59,17 @@ $renderer = $PAGE->get_renderer('tool_lifecycle'); echo $renderer->header(); -$mform = new \tool_lifecycle\local\form\form_backups_filter(); +$mform = new \tool_lifecycle\local\form\form_courses_filter(); // Cache handling. $cache = cache::make('tool_lifecycle', 'mformdata'); if ($mform->is_cancelled()) { - $cache->delete('coursebackups_filter'); + $cache->delete('viewcourses_filter'); redirect($PAGE->url); } else if ($data = $mform->get_data()) { - $cache->set('coursebackups_filter', $data); + $cache->set('viewcourses_filter', $data); } else { - $data = $cache->get('coursebackups_filter'); + $data = $cache->get('viewcourses_filter'); if ($data) { $mform->set_data($data); } diff --git a/workflowdrafts.php b/workflowdrafts.php new file mode 100644 index 0000000000000000000000000000000000000000..67a7954c5f59e67d27c8fd114b3a9edb0cc9bf39 --- /dev/null +++ b/workflowdrafts.php @@ -0,0 +1,73 @@ +<?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/>. + +/** + * Displays the tables inactive workflow definitions (drafts). + * + * @package tool_lifecycle + * @copyright 2022 Justus Dieckmann WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use tool_lifecycle\action; +use tool_lifecycle\local\table\workflow_definition_table; +use tool_lifecycle\urls; + +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_login(); + +global $OUTPUT, $PAGE, $DB; + +\tool_lifecycle\permission_and_navigation::setup_draft(); + +$PAGE->set_url(new \moodle_url(urls::WORKFLOW_DRAFTS)); + +$action = optional_param('action', null, PARAM_TEXT); +if ($action) { + $wfid = required_param('workflowid', PARAM_INT); + \tool_lifecycle\local\manager\workflow_manager::handle_action($action, $wfid); + redirect($PAGE->url); +} + +$PAGE->set_title(get_string('workflow_drafts_header', 'tool_lifecycle')); +$PAGE->set_heading(get_string('workflow_drafts_header', 'tool_lifecycle')); + +$renderer = $PAGE->get_renderer('tool_lifecycle'); + +echo $renderer->header(); + +echo html_writer::link(new \moodle_url(urls::EDIT_WORKFLOW), + get_string('add_workflow', 'tool_lifecycle'), ['class' => 'btn btn-primary mx-1']); + +echo html_writer::link(new \moodle_url(urls::UPLOAD_WORKFLOW), + get_string('upload_workflow', 'tool_lifecycle'), ['class' => 'btn btn-secondary mx-1']); + +echo html_writer::link(new \moodle_url(urls::CREATE_FROM_EXISTING), + get_string('create_workflow_from_existing', 'tool_lifecycle'), ['class' => 'btn btn-secondary mx-1']); + +$table = new workflow_definition_table('tool_lifecycle_workflow_definitions'); +echo $OUTPUT->box_start("lifecycle-enable-overflow lifecycle-table"); +$table->out(10, false); +echo $OUTPUT->box_end(); + +echo \html_writer::link(new \moodle_url(urls::ACTIVE_WORKFLOWS), + get_string('active_workflows_list', 'tool_lifecycle')); +echo '<br>'; +echo \html_writer::link(new \moodle_url(urls::DEACTIVATED_WORKFLOWS), + get_string('deactivated_workflows_list', 'tool_lifecycle')); + +echo $renderer->footer(); diff --git a/workflowoverview.php b/workflowoverview.php new file mode 100644 index 0000000000000000000000000000000000000000..c4229929200c4f48887921294c3e55037d8064a1 --- /dev/null +++ b/workflowoverview.php @@ -0,0 +1,223 @@ +<?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/>. + +/** + * Displays a workflow in a nice visual form. + * + * @package tool_lifecycle + * @copyright 2021 Nina Herrmann and Justus Dieckmann, WWU + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +require_once(__DIR__ . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_login(); + +use tool_lifecycle\action; +use tool_lifecycle\local\manager\delayed_courses_manager; +use tool_lifecycle\local\manager\step_manager; +use tool_lifecycle\local\manager\trigger_manager; +use tool_lifecycle\local\manager\workflow_manager; +use tool_lifecycle\settings_type; +use tool_lifecycle\urls; + +global $OUTPUT, $PAGE, $DB; + +$workflowid = required_param('wf', PARAM_INT); + +$workflow = \tool_lifecycle\local\manager\workflow_manager::get_workflow($workflowid); +\tool_lifecycle\permission_and_navigation::setup_workflow($workflow); + +$iseditable = workflow_manager::is_editable($workflow->id); + +$stepid = optional_param('step', null, PARAM_INT); + +$params = ['wf' => $workflow->id]; +$nosteplink = new moodle_url(urls::WORKFLOW_DETAILS, $params); + +if ($stepid) { + $params['step'] = $stepid; +} +$PAGE->set_url(new \moodle_url(urls::WORKFLOW_DETAILS, $params)); +$PAGE->set_title($workflow->title); +$PAGE->set_heading($workflow->title); + +$action = optional_param('action', null, PARAM_TEXT); + +if ($action) { + step_manager::handle_action($action, optional_param('actionstep', null, PARAM_INT), $workflow->id); + trigger_manager::handle_action($action, optional_param('actiontrigger', null, PARAM_INT), $workflow->id); + $processid = optional_param('processid', null, PARAM_INT); + if ($processid) { + $process = \tool_lifecycle\local\manager\process_manager::get_process_by_id($processid); + if ($action === 'rollback') { + \tool_lifecycle\local\manager\process_manager::rollback_process($process); + delayed_courses_manager::set_course_delayed_for_workflow($process->courseid, true, $workflow); + } else if ($action === 'proceed') { + \tool_lifecycle\local\manager\process_manager::proceed_process($process); + delayed_courses_manager::set_course_delayed_for_workflow($process->courseid, false, $workflow); + } else { + throw new coding_exception('processid was specified but action was neither "rollback" nor "proceed"!'); + } + } + redirect($PAGE->url); +} + +$renderer = $PAGE->get_renderer('tool_lifecycle'); + +$steps = \tool_lifecycle\local\manager\step_manager::get_step_instances($workflow->id); +$triggers = \tool_lifecycle\local\manager\trigger_manager::get_triggers_for_workflow($workflow->id); + +$str = [ + 'edit' => get_string('edit'), + 'delete' => get_string('delete'), + 'move_up' => get_string('move_up', 'tool_lifecycle'), + 'move_down' => get_string('move_down', 'tool_lifecycle') +]; + +$amounts = (new \tool_lifecycle\processor())->get_count_of_courses_to_trigger_for_workflow($workflow->id); +$displaytotaltriggered = !empty($triggers); + +foreach ($triggers as $trigger) { + // The array from the DB Function uses ids as keys. + // Mustache cannot handle arrays which have other keys therefore a new array is build. + // FUTURE: Nice to have Icon for each subplugin. + + $actionmenu = new action_menu([ + new action_menu_link_secondary( + new moodle_url(urls::EDIT_ELEMENT, ['type' => settings_type::TRIGGER, 'elementid' => $trigger->id]), + new pix_icon('i/edit', $str['edit']), $str['edit']) + ]); + if ($iseditable) { + $actionmenu->add(new action_menu_link_secondary( + new moodle_url($PAGE->url, + ['action' => action::TRIGGER_INSTANCE_DELETE, 'sesskey' => sesskey(), 'actiontrigger' => $trigger->id]), + new pix_icon('t/delete', $str['delete']), $str['delete']) + ); + } + $trigger->actionmenu = $OUTPUT->render($actionmenu); + $trigger->automatic = $amounts[$trigger->sortindex]->automatic; + $displaytotaltriggered &= $trigger->automatic; + if ($trigger->automatic) { + $trigger->triggeredcourses = $amounts[$trigger->sortindex]->triggered; + $trigger->excludedcourses = $amounts[$trigger->sortindex]->excluded; + } +} + +foreach ($steps as $step) { + $ncourses = $DB->count_records('tool_lifecycle_process', + array('stepindex' => $step->sortindex, 'workflowid' => $workflowid)); + $step->numberofcourses = $ncourses; + if ($step->id == $stepid) { + $step->selected = true; + } + $actionmenu = new action_menu([ + new action_menu_link_secondary( + new moodle_url(urls::EDIT_ELEMENT, ['type' => settings_type::STEP, 'elementid' => $step->id]), + new pix_icon('i/edit', $str['edit']), $str['edit']) + ]); + if ($iseditable) { + $actionmenu->add(new action_menu_link_secondary( + new moodle_url($PAGE->url, + ['action' => action::STEP_INSTANCE_DELETE, 'sesskey' => sesskey(), 'actionstep' => $step->id]), + new pix_icon('t/delete', $str['delete']), $str['delete']) + ); + if ($step->sortindex > 1) { + $actionmenu->add(new action_menu_link_secondary( + new moodle_url($PAGE->url, + ['action' => action::UP_STEP, 'sesskey' => sesskey(), 'actionstep' => $step->id]), + new pix_icon('t/up', $str['move_up']), $str['move_up']) + ); + } + if ($step->sortindex < count($steps)) { + $actionmenu->add(new action_menu_link_secondary( + new moodle_url($PAGE->url, + ['action' => action::DOWN_STEP, 'sesskey' => sesskey(), 'actionstep' => $step->id]), + new pix_icon('t/down', $str['move_down']), $str['move_down']) + ); + } + } + $step->actionmenu = $OUTPUT->render($actionmenu); +} + +$arrayofcourses = array(); + +$url = new moodle_url(urls::WORKFLOW_DETAILS, array('wf' => $workflowid)); + +$out = null; +if ($stepid) { + $step = step_manager::get_step_instance($stepid); + $table = new \tool_lifecycle\local\table\courses_in_step_table($step, + optional_param('courseid', null, PARAM_INT)); + ob_start(); + $table->out(20, false); + $out = ob_get_contents(); + ob_end_clean(); +} + +$data = [ + 'triggerhelp' => $OUTPUT->help_icon('overview:trigger', 'tool_lifecycle', null), + 'editsettingslink' => (new moodle_url(urls::EDIT_WORKFLOW, ['wf' => $workflow->id]))->out(false), + 'title' => $workflow->title, + 'displaytitle' => $workflow->displaytitle, + 'rollbackdelay' => format_time($workflow->rollbackdelay), + 'finishdelay' => format_time($workflow->finishdelay), + 'delayglobally' => $workflow->delayforallworkflows, + 'trigger' => array_values($triggers), + 'automatic' => $displaytotaltriggered, + 'coursestriggered' => $amounts['all']->triggered, + 'coursesexcluded' => $amounts['all']->excluded, + 'coursesetsize' => $amounts['all']->coursesetsize, + 'steps' => array_values($steps), + 'listofcourses' => $arrayofcourses, + 'nosteplink' => $nosteplink, + 'table' => $out +]; + +echo $renderer->header(); + +if (workflow_manager::is_editable($workflow->id)) { + $addinstance = ''; + $triggertypes = trigger_manager::get_chooseable_trigger_types(); + $workflowtriggers = trigger_manager::get_triggers_for_workflow($workflow->id); + $selectabletriggers = []; + foreach ($triggertypes as $triggertype => $triggername) { + foreach ($workflowtriggers as $workflowtrigger) { + if ($triggertype == $workflowtrigger->subpluginname) { + continue 2; + } + } + $selectabletriggers[$triggertype] = $triggername; + } + $icondata = (new help_icon('overview:add_trigger', 'tool_lifecycle'))->export_for_template($OUTPUT); + $addinstance .= $OUTPUT->render_from_template('tool_lifecycle/warn_icon', $icondata); + + $addinstance .= $OUTPUT->single_select(new \moodle_url(urls::EDIT_ELEMENT, + array('type' => settings_type::TRIGGER, 'wf' => $workflow->id)), + 'subplugin', $selectabletriggers, '', array('' => get_string('add_new_trigger_instance', 'tool_lifecycle')), + null, ['id' => 'tool_lifecycle-choose-trigger']); + + $steps = step_manager::get_step_types(); + $addinstance .= '<span class="ml-1"></span>'; + $addinstance .= $OUTPUT->single_select(new \moodle_url(urls::EDIT_ELEMENT, + array('type' => settings_type::STEP, 'wf' => $workflow->id)), + 'subplugin', $steps, '', array('' => get_string('add_new_step_instance', 'tool_lifecycle')), + null, ['id' => 'tool_lifecycle-choose-step']); + $data['addinstance'] = $addinstance; +} + +echo $OUTPUT->render_from_template('tool_lifecycle/workflowoverview', $data); + +echo $renderer->footer(); diff --git a/workflowsettings.php b/workflowsettings.php deleted file mode 100644 index 60c09ed7770376f7d36efba0de347025831ed072..0000000000000000000000000000000000000000 --- a/workflowsettings.php +++ /dev/null @@ -1,45 +0,0 @@ -<?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/>. - -/** - * Displays the settings associated with one single workflow and handles action for it. - * - * @package tool_lifecycle - * @copyright 2017 Tobias Reischmann WWU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -require_once(__DIR__ . '/../../../config.php'); -require_once(__DIR__ . '/adminlib.php'); - -$PAGE->set_context(context_system::instance()); -require_login(null, false); -require_capability('moodle/site:config', context_system::instance()); - -$workflowid = required_param('workflowid', PARAM_INT); - -$workflow = tool_lifecycle\local\manager\workflow_manager::get_workflow($workflowid); - -if (!$workflow) { - throw new moodle_exception('workflownotfound', 'tool_lifecycle', - new \moodle_url('/admin/tool/lifecycle/adminsettings.php'), $workflowid); -} - -// Create the class for this controller. -$workflowsettings = new tool_lifecycle\workflow_settings($workflowid); - -// Execute the controller. -$subpluginid = optional_param('subplugin', null, PARAM_INT); -$workflowsettings->execute(optional_param('action', null, PARAM_TEXT), $subpluginid, $workflowid);