From 273209e1459a767c69e1504159e5ec8e3ff9d4e6 Mon Sep 17 00:00:00 2001 From: Nathan Nguyen Date: Mon, 4 Sep 2023 10:13:03 +1000 Subject: [PATCH 1/2] issue#178 refactor subplugins calls --- classes/local/form/form_step_instance.php | 7 +- classes/local/form/form_trigger_instance.php | 7 +- classes/local/manager/lib_manager.php | 27 ++-- classes/local/manager/step_manager.php | 16 +++ classes/local/manager/trigger_manager.php | 15 +++ settings.php | 62 +++++++++- step/lib.php | 36 +++++- subplugins.php | 16 +-- .../classes/lifecycle/interaction.php | 33 +++++ .../samplestep/classes/lifecycle/step.php | 31 +++++ .../samplestep/classes/privacy/provider.php | 38 ++++++ .../samplestep/lang/en/tool_samplestep.php | 18 +++ .../fakeplugins/samplestep/version.php | 28 +++++ .../classes/lifecycle/trigger.php | 32 +++++ .../classes/privacy/provider.php | 38 ++++++ .../lang/en/tool_sampletrigger.php | 18 +++ .../fakeplugins/sampletrigger/version.php | 28 +++++ tests/subplugin_test.php | 116 ++++++++++++++++++ trigger/lib.php | 34 +++++ version.php | 2 +- 20 files changed, 578 insertions(+), 24 deletions(-) create mode 100644 tests/fixtures/fakeplugins/samplestep/classes/lifecycle/interaction.php create mode 100644 tests/fixtures/fakeplugins/samplestep/classes/lifecycle/step.php create mode 100644 tests/fixtures/fakeplugins/samplestep/classes/privacy/provider.php create mode 100644 tests/fixtures/fakeplugins/samplestep/lang/en/tool_samplestep.php create mode 100644 tests/fixtures/fakeplugins/samplestep/version.php create mode 100644 tests/fixtures/fakeplugins/sampletrigger/classes/lifecycle/trigger.php create mode 100644 tests/fixtures/fakeplugins/sampletrigger/classes/privacy/provider.php create mode 100644 tests/fixtures/fakeplugins/sampletrigger/lang/en/tool_sampletrigger.php create mode 100644 tests/fixtures/fakeplugins/sampletrigger/version.php create mode 100644 tests/subplugin_test.php diff --git a/classes/local/form/form_step_instance.php b/classes/local/form/form_step_instance.php index 0543b54f..85d36689 100644 --- a/classes/local/form/form_step_instance.php +++ b/classes/local/form/form_step_instance.php @@ -191,8 +191,11 @@ public function definition_after_data() { $mform->setDefault('id', ''); $subpluginname = $this->subpluginname; } - $mform->setDefault('subpluginnamestatic', - get_string('pluginname', 'lifecyclestep_' . $subpluginname)); + + if (isset($this->lib)) { + $mform->setDefault('subpluginnamestatic', $this->lib->get_plugin_description()); + } + $mform->setDefault('subpluginname', $subpluginname); // Setting the default values for the local step settings. diff --git a/classes/local/form/form_trigger_instance.php b/classes/local/form/form_trigger_instance.php index e3d075a3..d7c9834f 100644 --- a/classes/local/form/form_trigger_instance.php +++ b/classes/local/form/form_trigger_instance.php @@ -173,8 +173,11 @@ public function definition_after_data() { $mform->setDefault('id', $this->trigger->id); $mform->setDefault('instancename', $this->trigger->instancename); } - $mform->setDefault('subpluginnamestatic', - get_string('pluginname', 'lifecycletrigger_' . $this->subpluginname)); + + if (isset($this->lib)) { + $mform->setDefault('subpluginnamestatic', $this->lib->get_plugin_description()); + } + $mform->setDefault('subpluginname', $this->subpluginname); // Setting the default values for the local trigger settings. diff --git a/classes/local/manager/lib_manager.php b/classes/local/manager/lib_manager.php index afd381ee..22861672 100644 --- a/classes/local/manager/lib_manager.php +++ b/classes/local/manager/lib_manager.php @@ -102,18 +102,27 @@ public static function get_step_interactionlib($subpluginname) { * @return null|base|libbase */ private static function get_lib($subpluginname, $subplugintype, $libsubtype = '') { + // Plugins defined in subplugins.json file. $triggerlist = \core_component::get_plugin_list('lifecycle' . $subplugintype); - if (!array_key_exists($subpluginname, $triggerlist)) { - return null; - } - $filename = $triggerlist[$subpluginname].'/'.$libsubtype.'lib.php'; - if (file_exists($filename)) { - require_once($filename); - $extendedclass = "tool_lifecycle\\$subplugintype\\$libsubtype$subpluginname"; - if (class_exists($extendedclass)) { - return new $extendedclass(); + if (array_key_exists($subpluginname, $triggerlist)) { + $filename = $triggerlist[$subpluginname].'/'.$libsubtype.'lib.php'; + if (file_exists($filename)) { + require_once($filename); + $extendedclass = "tool_lifecycle\\$subplugintype\\$libsubtype$subpluginname"; + if (class_exists($extendedclass)) { + return new $extendedclass(); + } } } + + // Plugins defined under "lifecycle" name space. + // The base class has already been checked by get_trigger_types or get_steps_types. + $classname = !$libsubtype ? $subplugintype : $libsubtype; + $classname = "$subpluginname\\lifecycle\\$classname"; + if (class_exists($classname)) { + return new $classname(); + } + return null; } } diff --git a/classes/local/manager/step_manager.php b/classes/local/manager/step_manager.php index bfb67d3b..fa26279c 100644 --- a/classes/local/manager/step_manager.php +++ b/classes/local/manager/step_manager.php @@ -239,11 +239,27 @@ public static function get_step_instances_by_subpluginname($subpluginname) { * @throws \coding_exception */ public static function get_step_types() { + // Sub plugins in 'step' folder. $subplugins = \core_component::get_plugin_list('lifecyclestep'); $result = []; foreach (array_keys($subplugins) as $plugin) { $result[$plugin] = get_string('pluginname', 'lifecyclestep_' . $plugin); } + + // Additional sub plugins defined under "lifecycle" name space, ie "local_newstep\lifecycle". + // The class name must be step (step.php) and placed under "classes/lifecycle" folder. + // The name space must be "local_newstep\lifecycle" + // The "local_newstep\lifecycle\step" class must extend the step base classes. + foreach (array_keys(\core_component::get_plugin_types()) as $plugintype) { + $potentialsteps = \core_component::get_plugin_list_with_class($plugintype, 'lifecycle\\step'); + foreach ($potentialsteps as $plugin => $potentialstep) { + // Check if it implements the step base class. + if (is_a($potentialstep, \tool_lifecycle\step\libbase::class, true)) { + $result[$plugin] = get_string('pluginname', $plugin); + } + } + } + return $result; } diff --git a/classes/local/manager/trigger_manager.php b/classes/local/manager/trigger_manager.php index 79d57140..21b96df0 100644 --- a/classes/local/manager/trigger_manager.php +++ b/classes/local/manager/trigger_manager.php @@ -254,6 +254,21 @@ public static function get_trigger_types() { $result[$plugin] = get_string('pluginname', 'lifecycletrigger_' . $plugin); } } + + // Additional sub plugins defined under "lifecycle" name space, ie "local_newtrigger\lifecycle". + // The class name must be trigger (trigger.php) and placed under "classes/lifecycle" folder. + // The name space must be "local_newtrigger\lifecycle" + // The "local_newtrigger\lifecycle\trigger" class must extend the trigger base classes (base_automatic or base_manual). + foreach (array_keys(\core_component::get_plugin_types()) as $plugintype) { + $potentialtriggers = \core_component::get_plugin_list_with_class($plugintype, 'lifecycle\\trigger'); + foreach ($potentialtriggers as $plugin => $potentialtrigger) { + // Check if it implements the trigger base class. + if (is_a($potentialtrigger, \tool_lifecycle\trigger\base::class, true)) { + $result[$plugin] = get_string('pluginname', $plugin); + } + } + } + return $result; } diff --git a/settings.php b/settings.php index f5c3359a..078937d1 100644 --- a/settings.php +++ b/settings.php @@ -25,6 +25,9 @@ defined('MOODLE_INTERNAL') || die(); +use tool_lifecycle\local\manager\lib_manager; +use tool_lifecycle\local\manager\step_manager; +use tool_lifecycle\local\manager\trigger_manager; use tool_lifecycle\tabs; // Check for the moodle/site:config permission. @@ -89,5 +92,62 @@ get_string('config_forum_desc', 'tool_lifecycle'), null, PARAM_INT)); + $triggers = core_component::get_plugin_list('lifecycletrigger'); + if ($triggers) { + $settings->add(new admin_setting_heading('lifecycletriggerheader', + get_string('triggers_installed', 'tool_lifecycle'), '')); + foreach ($triggers as $trigger => $path) { + $triggername = html_writer::span(get_string('pluginname', 'lifecycletrigger_' . $trigger), + "font-weight-bold"); + $uninstall = ''; + if ($trigger == 'sitecourse' || $trigger == 'delayedcourses') { + $uninstall = html_writer::span(' Depracated. Will be removed with version 5.0.', 'text-danger'); + } + if ($trigger == 'customfieldsemester') { + $settings->add(new admin_setting_description('lifecycletriggersetting_'.$trigger, + $triggername, + get_string('customfieldsemesterdescription', 'tool_lifecycle'))); + } else { + try { + $plugindescription = get_string('plugindescription', 'lifecycletrigger_' . $trigger); + } catch (Exception $e) { + $plugindescription = ""; + } + $settings->add(new admin_setting_description('lifecycletriggersetting_'.$trigger, + $triggername, + $plugindescription.$uninstall)); + $lib = lib_manager::get_trigger_lib($trigger); + $lib->get_plugin_settings(); + } + } + } else { + $settings->add(new admin_setting_heading('adminsettings_notriggers', + get_string('adminsettings_notriggers', 'tool_lifecycle'), '')); + } + + $steps = core_component::get_plugin_list('lifecyclestep'); + if ($steps) { + $settings->add(new admin_setting_heading('lifecyclestepheader', + get_string('steps_installed', 'tool_lifecycle'), '')); + foreach ($steps as $step => $path) { + $stepname = html_writer::span(get_string('pluginname', 'lifecyclestep_' . $step), + "font-weight-bold"); + try { + $plugindescription = get_string('plugindescription', 'lifecyclestep_' . $step); + } catch (Exception $e) { + $plugindescription = ""; + } + $settings->add(new admin_setting_description('lifecyclestepsetting_'.$step, + $stepname, + $plugindescription)); + $lib = lib_manager::get_step_lib($step); + $lib->get_plugin_settings(); + } + } else { + $settings->add(new admin_setting_heading('adminsettings_nosteps', + get_string('adminsettings_nosteps', 'tool_lifecycle'), '')); + } + $settings->add(new admin_setting_description('spacer', "", " ")); + $ADMIN->add('tools', $settings); -} +} \ No newline at end of file diff --git a/step/lib.php b/step/lib.php index f8bc421a..bac65937 100644 --- a/step/lib.php +++ b/step/lib.php @@ -190,6 +190,40 @@ public function get_icon() { public function is_stoppable() { return false; } + + /** + * Define name of the step. + * Allow subplugins to have custom name. + * + * @return string name of the trigger. + */ + public function get_plugin_name() { + return get_string("pluginname", "lifecyclestep_" . $this->get_subpluginname()); + } + + /** + * Define description of the step. + * Allow subplugins to have custom description. + * + * @return string description of the trigger. + */ + public function get_plugin_description() { + return get_string("plugindescription", "lifecyclestep_" . $this->get_subpluginname()); + } + + /** + * Returns the settings of the step. + * + * @return void + */ + public function get_plugin_settings() { + $step = $this->get_subpluginname(); + $file = __DIR__ . "/$step/settings.php"; + + if (file_exists($file)) { + include($file); + } + } } /** @@ -222,4 +256,4 @@ public function __construct(string $name, string $paramtype, bool $editable = fa $this->editable = $editable; } -} +} \ No newline at end of file diff --git a/subplugins.php b/subplugins.php index aba509b8..0b31a8dd 100644 --- a/subplugins.php +++ b/subplugins.php @@ -24,6 +24,8 @@ */ use tool_lifecycle\local\manager\lib_manager; +use tool_lifecycle\local\manager\step_manager; +use tool_lifecycle\local\manager\trigger_manager; use tool_lifecycle\tabs; use tool_lifecycle\urls; @@ -50,17 +52,16 @@ echo html_writer::link('https://github.com/learnweb/moodle-tool_lifecycle/wiki/List-of-Installed-Subplugins', get_string('documentationlink', 'tool_lifecycle'), ['target' => '_blank']); -$triggers = core_component::get_plugin_list('lifecycletrigger'); +$triggers = trigger_manager::get_trigger_types(); if ($triggers) { echo html_writer::div(get_string('triggers_installed', 'tool_lifecycle'), 'h2 mt-2'); foreach ($triggers as $trigger => $path) { $lib = lib_manager::get_trigger_lib($trigger); $triggericon = $lib->get_icon(); echo $OUTPUT->pix_icon($triggericon, $trigger, 'moodle', ['class' => 'mr-1']); - echo html_writer::div(get_string('pluginname', 'lifecycletrigger_' . $trigger), - "font-weight-bold d-inline-block"); + echo html_writer::div($lib->get_plugin_name(), "font-weight-bold d-inline-block"); try { - $plugindescription = get_string('plugindescription', 'lifecycletrigger_' . $trigger); + $plugindescription = $lib->get_plugin_description(); } catch (Exception $e) { $plugindescription = ""; } @@ -80,17 +81,16 @@ echo html_writer::div(get_string('adminsettings_notriggers', 'tool_lifecycle')); } -$steps = core_component::get_plugin_list('lifecyclestep'); +$steps = step_manager::get_step_types(); if ($steps) { echo html_writer::div(get_string('steps_installed', 'tool_lifecycle'), 'h2 mt-2'); foreach ($steps as $step => $path) { $lib = lib_manager::get_step_lib($step); $stepicon = $lib->get_icon(); echo $OUTPUT->pix_icon($stepicon, $step, 'moodle', ['class' => 'mr-1']); - echo html_writer::div(get_string('pluginname', 'lifecyclestep_' . $step), - "font-weight-bold d-inline-block"); + echo html_writer::div($lib->get_plugin_name(), "font-weight-bold d-inline-block"); try { - $plugindescription = get_string('plugindescription', 'lifecyclestep_' . $step); + $plugindescription = $lib->get_plugin_description(); } catch (Exception $e) { $plugindescription = ""; } diff --git a/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/interaction.php b/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/interaction.php new file mode 100644 index 00000000..ac126624 --- /dev/null +++ b/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/interaction.php @@ -0,0 +1,33 @@ +dirroot . '/admin/tool/lifecycle/step/interactionlib.php'); + +use tool_lifecycle\step\interactionlibbase; + +defined('MOODLE_INTERNAL') || die(); + +class interaction extends interactionlibbase { + + public function get_relevant_capability() + { + } + + public function get_action_tools($process) + { + } + + public function get_status_message($process) + { + } + + public function get_action_string($action, $user) + { + } + + public function handle_interaction($process, $step, $action = 'default') + { + } +} diff --git a/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/step.php b/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/step.php new file mode 100644 index 00000000..96028f15 --- /dev/null +++ b/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/step.php @@ -0,0 +1,31 @@ +dirroot . '/admin/tool/lifecycle/step/lib.php'); + +use tool_lifecycle\step\libbase; + +defined('MOODLE_INTERNAL') || die(); + +class step extends libbase { + public function get_subpluginname() + { + return 'sample step'; + } + + public function get_plugin_name() { + return "Sample step"; + } + + public function get_plugin_description() { + return "Sample step plugin"; + } + + public function process_course($processid, $instanceid, $course) + { + return null; + } + +} diff --git a/tests/fixtures/fakeplugins/samplestep/classes/privacy/provider.php b/tests/fixtures/fakeplugins/samplestep/classes/privacy/provider.php new file mode 100644 index 00000000..de402f95 --- /dev/null +++ b/tests/fixtures/fakeplugins/samplestep/classes/privacy/provider.php @@ -0,0 +1,38 @@ +. + +namespace tool_samplestep\privacy; + +use core_privacy\local\metadata\null_provider; + +/** + * Privacy subsystem implementation for tool_samplestep. + * + * @package tool_samplestep + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements null_provider { + + /** + * Get the language string identifier with the component's language + * file to explain why this plugin stores no data. + * + * @return string the reason + */ + public static function get_reason() : string { + return 'privacy:metadata'; + } +} diff --git a/tests/fixtures/fakeplugins/samplestep/lang/en/tool_samplestep.php b/tests/fixtures/fakeplugins/samplestep/lang/en/tool_samplestep.php new file mode 100644 index 00000000..cbd7087c --- /dev/null +++ b/tests/fixtures/fakeplugins/samplestep/lang/en/tool_samplestep.php @@ -0,0 +1,18 @@ +. + +$string['pluginname'] = 'Sample step'; +$string['privacy:metadata'] = 'The plugin does not store any personal data.'; diff --git a/tests/fixtures/fakeplugins/samplestep/version.php b/tests/fixtures/fakeplugins/samplestep/version.php new file mode 100644 index 00000000..440d216e --- /dev/null +++ b/tests/fixtures/fakeplugins/samplestep/version.php @@ -0,0 +1,28 @@ +. + +/** + * Fake component for testing + * + * @package core + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2023100400; +$plugin->requires = 2022041200; +$plugin->component = 'tool_samplestep'; diff --git a/tests/fixtures/fakeplugins/sampletrigger/classes/lifecycle/trigger.php b/tests/fixtures/fakeplugins/sampletrigger/classes/lifecycle/trigger.php new file mode 100644 index 00000000..f613ff94 --- /dev/null +++ b/tests/fixtures/fakeplugins/sampletrigger/classes/lifecycle/trigger.php @@ -0,0 +1,32 @@ +dirroot . '/admin/tool/lifecycle/trigger/lib.php'); + +use tool_lifecycle\trigger\base_automatic; + +defined('MOODLE_INTERNAL') || die(); + +class trigger extends base_automatic { + + public function get_subpluginname() + { + return 'sample trigger'; + } + + public function get_plugin_name() { + return "Sample trigger"; + } + + public function get_plugin_description() { + return "Sample trigger"; + } + + public function check_course($course, $triggerid) + { + return null; + } + +} diff --git a/tests/fixtures/fakeplugins/sampletrigger/classes/privacy/provider.php b/tests/fixtures/fakeplugins/sampletrigger/classes/privacy/provider.php new file mode 100644 index 00000000..8ba4c99f --- /dev/null +++ b/tests/fixtures/fakeplugins/sampletrigger/classes/privacy/provider.php @@ -0,0 +1,38 @@ +. + +namespace tool_sampletrigger\privacy; + +use core_privacy\local\metadata\null_provider; + +/** + * Privacy subsystem implementation for tool_sampletrigger. + * + * @package tool_sampletrigger + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements null_provider { + + /** + * Get the language string identifier with the component's language + * file to explain why this plugin stores no data. + * + * @return string the reason + */ + public static function get_reason() : string { + return 'privacy:metadata'; + } +} diff --git a/tests/fixtures/fakeplugins/sampletrigger/lang/en/tool_sampletrigger.php b/tests/fixtures/fakeplugins/sampletrigger/lang/en/tool_sampletrigger.php new file mode 100644 index 00000000..ed3104d6 --- /dev/null +++ b/tests/fixtures/fakeplugins/sampletrigger/lang/en/tool_sampletrigger.php @@ -0,0 +1,18 @@ +. + +$string['pluginname'] = 'Sample trigger'; +$string['privacy:metadata'] = 'The plugin does not store any personal data.'; diff --git a/tests/fixtures/fakeplugins/sampletrigger/version.php b/tests/fixtures/fakeplugins/sampletrigger/version.php new file mode 100644 index 00000000..38a6277f --- /dev/null +++ b/tests/fixtures/fakeplugins/sampletrigger/version.php @@ -0,0 +1,28 @@ +. + +/** + * Fake component for testing + * + * @package core + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2023100400; +$plugin->requires = 2022041200; +$plugin->component = 'tool_sampletrigger'; diff --git a/tests/subplugin_test.php b/tests/subplugin_test.php new file mode 100644 index 00000000..338931b9 --- /dev/null +++ b/tests/subplugin_test.php @@ -0,0 +1,116 @@ +. + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir.'/upgradelib.php'); + +use tool_lifecycle\local\manager\lib_manager; +use tool_lifecycle\local\manager\step_manager; +use tool_lifecycle\local\manager\trigger_manager; + + +class subplugin_test extends \advanced_testcase { + + public function test_builtin_triggers() { + $this->resetAfterTest(); + $builtintriggers = \core_component::get_plugin_list('lifecycletrigger'); + $triggers = trigger_manager::get_trigger_types(); + + // All builtin triggers are also in the list of triggers. + $this->assertEmpty(array_diff_key($builtintriggers, $triggers)); + + // Trigger classes are loadable. + foreach ($builtintriggers as $triggername => $triggerpath) { + $this->assertNotEmpty(lib_manager::get_trigger_lib($triggername)); + } + } + + public function test_builtin_steps() { + $this->resetAfterTest(); + $builtinsteps = \core_component::get_plugin_list('lifecyclestep'); + $steps = step_manager::get_step_types(); + + // All builtin steps are also in the list of steps. + $this->assertEmpty(array_diff_key($builtinsteps, $steps)); + + // Step classes are loadable. + foreach ($builtinsteps as $stepname => $steppath) { + $this->assertNotEmpty(lib_manager::get_step_lib($stepname)); + } + } + + public function test_additional_triggers() { + $this->resetAfterTest(); + + // Add a fake tool plugin, which define a trigger. + $mockedcomponent = new ReflectionClass(\core_component::class); + $mockedplugins = $mockedcomponent->getProperty('plugins'); + $mockedplugins->setAccessible(true); + $plugins = $mockedplugins->getValue(); + $plugins['tool'] += ['sampletrigger' => __DIR__ . '/fixtures/fakeplugins/sampletrigger']; + $mockedplugins->setValue($plugins); + + // The 'fixture' is not autoloaded, so we need to require it. + require_once(__DIR__ . '/fixtures/fakeplugins/sampletrigger/classes//lifecycle/trigger.php'); + + // Check if the trigger is available. + $triggers = trigger_manager::get_trigger_types(); + $this->assertArrayHasKey('tool_sampletrigger', $triggers); + + // Lib is loadable. + $this->assertInstanceOf('tool_sampletrigger\lifecycle\trigger', + lib_manager::get_trigger_lib('tool_sampletrigger')); + + // Unset the fake plugin. + unset($plugins['tool']['sampletrigger']); + $mockedplugins->setValue($plugins); + } + + public function test_additional_steps() { + $this->resetAfterTest(); + + // Add a fake tool plugin, which define a step. + $mockedcomponent = new ReflectionClass(\core_component::class); + $mockedplugins = $mockedcomponent->getProperty('plugins'); + $mockedplugins->setAccessible(true); + $plugins = $mockedplugins->getValue(); + $plugins['tool'] += ['samplestep' => __DIR__ . '/fixtures/fakeplugins/samplestep']; + $mockedplugins->setValue($plugins); + + // The 'fixture' is not autoloaded, so we need to require it. + require_once(__DIR__ . '/fixtures/fakeplugins/samplestep/classes//lifecycle/step.php'); + require_once(__DIR__ . '/fixtures/fakeplugins/samplestep/classes//lifecycle/interaction.php'); + + // Check if the step is available. + $steps = step_manager::get_step_types(); + $this->assertArrayHasKey('tool_samplestep', $steps); + + // Lib is loadable. + $this->assertInstanceOf('tool_samplestep\lifecycle\step', + lib_manager::get_step_lib('tool_samplestep')); + + // Lib subtype is loadable. + $this->assertInstanceOf('tool_samplestep\lifecycle\interaction', + lib_manager::get_step_interactionlib('tool_samplestep')); + + // Unset the fake plugin. + unset($plugins['tool']['samplestep']); + $mockedplugins->setValue($plugins); + } + +} diff --git a/trigger/lib.php b/trigger/lib.php index 6f12a222..3f595366 100644 --- a/trigger/lib.php +++ b/trigger/lib.php @@ -131,6 +131,40 @@ public function ensure_validity(array $settings): array { public function get_icon() { return 'i/nosubcat'; } + + /** + * Defgine name of the trigger. + * Allow subplugins to have custom names. + * + * @return string name of the trigger. + */ + public function get_plugin_name() { + return get_string("pluginname", "lifecycletrigger_" . $this->get_subpluginname()); + } + + /** + * Define description of the trigger. + * Allow subplugins to have custom description. + * + * @return string description of the trigger. + */ + public function get_plugin_description() { + return get_string("pluginname", "lifecycletrigger_" . $this->get_subpluginname()); + } + + /** + * Returns the settings of the trigger. + * + * @return void + */ + public function get_plugin_settings() { + $trigger = $this->get_subpluginname(); + $file = __DIR__ . "/$trigger/settings.php"; + + if (file_exists($file)) { + include($file); + } + } } /** diff --git a/version.php b/version.php index b4ab3380..62583d9f 100644 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ $plugin->component = 'tool_lifecycle'; $plugin->maturity = MATURITY_STABLE; -$plugin->version = 2026012003; +$plugin->version = 2026012004; $plugin->requires = 2024100700; // Requires Moodle 4.5+. $plugin->supported = [405, 501]; $plugin->release = 'v5.1-r4'; From 3ef485162975adef07a6b998997a0a1efec4ed44 Mon Sep 17 00:00:00 2001 From: Scott Verbeek Date: Fri, 6 Mar 2026 14:42:33 +1000 Subject: [PATCH 2/2] issue#178 resolve phpcs issues --- settings.php | 2 +- step/lib.php | 2 +- .../classes/lifecycle/interaction.php | 68 +++++++++++++++---- .../samplestep/classes/lifecycle/step.php | 56 ++++++++++++--- .../samplestep/classes/privacy/provider.php | 5 +- .../samplestep/lang/en/tool_samplestep.php | 8 +++ .../fakeplugins/samplestep/version.php | 3 +- .../classes/lifecycle/trigger.php | 56 ++++++++++++--- .../classes/privacy/provider.php | 5 +- .../lang/en/tool_sampletrigger.php | 8 +++ .../fakeplugins/sampletrigger/version.php | 3 +- tests/subplugin_test.php | 63 ++++++++++++----- 12 files changed, 220 insertions(+), 59 deletions(-) diff --git a/settings.php b/settings.php index 078937d1..a8891dc5 100644 --- a/settings.php +++ b/settings.php @@ -150,4 +150,4 @@ $settings->add(new admin_setting_description('spacer', "", " ")); $ADMIN->add('tools', $settings); -} \ No newline at end of file +} diff --git a/step/lib.php b/step/lib.php index bac65937..92aaeb3d 100644 --- a/step/lib.php +++ b/step/lib.php @@ -256,4 +256,4 @@ public function __construct(string $name, string $paramtype, bool $editable = fa $this->editable = $editable; } -} \ No newline at end of file +} diff --git a/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/interaction.php b/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/interaction.php index ac126624..87ee6d87 100644 --- a/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/interaction.php +++ b/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/interaction.php @@ -1,33 +1,71 @@ . namespace tool_samplestep\lifecycle; -global $CFG; -require_once($CFG->dirroot . '/admin/tool/lifecycle/step/interactionlib.php'); - use tool_lifecycle\step\interactionlibbase; -defined('MOODLE_INTERNAL') || die(); - +/** + * Sample interaction class for lifecycle step plugin. + * + * @package tool_lifecycle + * @copyright 2026 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class interaction extends interactionlibbase { - public function get_relevant_capability() - { + /** + * Mock method to get the relevant capability for the interaction. + */ + public function get_relevant_capability() { } - public function get_action_tools($process) - { + /** + * Mock method to get the action tools for the interaction. + * + * @param \tool_lifecycle\local\entity\process $process + */ + public function get_action_tools($process) { } - public function get_status_message($process) - { + /** + * Mock method to get the status message for the interaction. + * + * @param \tool_lifecycle\local\entity\process $process + * @return string status message + */ + public function get_status_message($process) { } - public function get_action_string($action, $user) - { + /** + * Mock method to get the action string for the interaction. + * + * @param string $action The action being performed. + * @param string $user html-link with username as text that refers to the user profile. + */ + public function get_action_string($action, $user) { } - public function handle_interaction($process, $step, $action = 'default') - { + /** + * Mock method to handle the interaction. + * + * @param \tool_lifecycle\local\entity\process $process + * @param \tool_lifecycle\local\entity\step_subplugin $step The lifecycle step object. + * @param string $action The action being performed. + */ + public function handle_interaction($process, $step, $action = 'default') { } } diff --git a/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/step.php b/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/step.php index 96028f15..151cf127 100644 --- a/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/step.php +++ b/tests/fixtures/fakeplugins/samplestep/classes/lifecycle/step.php @@ -1,30 +1,68 @@ . namespace tool_samplestep\lifecycle; -global $CFG; -require_once($CFG->dirroot . '/admin/tool/lifecycle/step/lib.php'); - use tool_lifecycle\step\libbase; -defined('MOODLE_INTERNAL') || die(); +/** + * Sample step class for lifecycle step plugin. + * + * @package tool_lifecycle + * @copyright 2026 Scott Verbeek + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class step extends libbase { - public function get_subpluginname() - { - return 'sample step'; + /** + * Returns the subplugin name. + * + * @return string + */ + public function get_subpluginname() { + return 'tool_samplestep'; } + /** + * Returns the plugin name. + * + * @return string + */ public function get_plugin_name() { return "Sample step"; } + /** + * Returns the plugin description. + * + * @return string + */ public function get_plugin_description() { return "Sample step plugin"; } - public function process_course($processid, $instanceid, $course) - { + /** + * Processes the course for this step. + * + * @param int $processid The process ID. + * @param int $instanceid The instance ID. + * @param object $course The course object. + * @return null No action for this sample step. + */ + public function process_course($processid, $instanceid, $course) { return null; } diff --git a/tests/fixtures/fakeplugins/samplestep/classes/privacy/provider.php b/tests/fixtures/fakeplugins/samplestep/classes/privacy/provider.php index de402f95..b285fbec 100644 --- a/tests/fixtures/fakeplugins/samplestep/classes/privacy/provider.php +++ b/tests/fixtures/fakeplugins/samplestep/classes/privacy/provider.php @@ -21,7 +21,8 @@ /** * Privacy subsystem implementation for tool_samplestep. * - * @package tool_samplestep + * @package tool_lifecycle + * @copyright 2026 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements null_provider { @@ -32,7 +33,7 @@ class provider implements null_provider { * * @return string the reason */ - public static function get_reason() : string { + public static function get_reason(): string { return 'privacy:metadata'; } } diff --git a/tests/fixtures/fakeplugins/samplestep/lang/en/tool_samplestep.php b/tests/fixtures/fakeplugins/samplestep/lang/en/tool_samplestep.php index cbd7087c..f101c3fd 100644 --- a/tests/fixtures/fakeplugins/samplestep/lang/en/tool_samplestep.php +++ b/tests/fixtures/fakeplugins/samplestep/lang/en/tool_samplestep.php @@ -14,5 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Sample step language strings. + * + * @package tool_lifecycle + * @copyright 2026 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + $string['pluginname'] = 'Sample step'; $string['privacy:metadata'] = 'The plugin does not store any personal data.'; diff --git a/tests/fixtures/fakeplugins/samplestep/version.php b/tests/fixtures/fakeplugins/samplestep/version.php index 440d216e..6530c77d 100644 --- a/tests/fixtures/fakeplugins/samplestep/version.php +++ b/tests/fixtures/fakeplugins/samplestep/version.php @@ -17,7 +17,8 @@ /** * Fake component for testing * - * @package core + * @package tool_lifecycle + * @copyright 2026 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/tests/fixtures/fakeplugins/sampletrigger/classes/lifecycle/trigger.php b/tests/fixtures/fakeplugins/sampletrigger/classes/lifecycle/trigger.php index f613ff94..d955d80d 100644 --- a/tests/fixtures/fakeplugins/sampletrigger/classes/lifecycle/trigger.php +++ b/tests/fixtures/fakeplugins/sampletrigger/classes/lifecycle/trigger.php @@ -1,31 +1,67 @@ . namespace tool_sampletrigger\lifecycle; -global $CFG; -require_once($CFG->dirroot . '/admin/tool/lifecycle/trigger/lib.php'); - use tool_lifecycle\trigger\base_automatic; -defined('MOODLE_INTERNAL') || die(); - +/** + * Sample trigger class for lifecycle trigger plugin. + * + * @package tool_lifecycle + * @copyright 2026 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class trigger extends base_automatic { - public function get_subpluginname() - { - return 'sample trigger'; + /** + * Returns the subplugin name. + * + * @return string + */ + public function get_subpluginname() { + return 'tool_sampletrigger'; } + /** + * Returns the plugin name. + * + * @return string + */ public function get_plugin_name() { return "Sample trigger"; } + /** + * Returns the plugin description. + * + * @return string + */ public function get_plugin_description() { return "Sample trigger"; } - public function check_course($course, $triggerid) - { + /** + * Checks the course for this trigger. + * + * @param object $course The course object. + * @param int $triggerid The trigger ID. + * @return null No action for this sample trigger. + */ + public function check_course($course, $triggerid) { return null; } diff --git a/tests/fixtures/fakeplugins/sampletrigger/classes/privacy/provider.php b/tests/fixtures/fakeplugins/sampletrigger/classes/privacy/provider.php index 8ba4c99f..90260040 100644 --- a/tests/fixtures/fakeplugins/sampletrigger/classes/privacy/provider.php +++ b/tests/fixtures/fakeplugins/sampletrigger/classes/privacy/provider.php @@ -21,7 +21,8 @@ /** * Privacy subsystem implementation for tool_sampletrigger. * - * @package tool_sampletrigger + * @package tool_lifecycle + * @copyright 2026 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements null_provider { @@ -32,7 +33,7 @@ class provider implements null_provider { * * @return string the reason */ - public static function get_reason() : string { + public static function get_reason(): string { return 'privacy:metadata'; } } diff --git a/tests/fixtures/fakeplugins/sampletrigger/lang/en/tool_sampletrigger.php b/tests/fixtures/fakeplugins/sampletrigger/lang/en/tool_sampletrigger.php index ed3104d6..ac7956c2 100644 --- a/tests/fixtures/fakeplugins/sampletrigger/lang/en/tool_sampletrigger.php +++ b/tests/fixtures/fakeplugins/sampletrigger/lang/en/tool_sampletrigger.php @@ -14,5 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Sample trigger language strings. + * + * @package tool_lifecycle + * @copyright 2026 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + $string['pluginname'] = 'Sample trigger'; $string['privacy:metadata'] = 'The plugin does not store any personal data.'; diff --git a/tests/fixtures/fakeplugins/sampletrigger/version.php b/tests/fixtures/fakeplugins/sampletrigger/version.php index 38a6277f..bfe8816c 100644 --- a/tests/fixtures/fakeplugins/sampletrigger/version.php +++ b/tests/fixtures/fakeplugins/sampletrigger/version.php @@ -17,7 +17,8 @@ /** * Fake component for testing * - * @package core + * @package tool_lifecycle + * @copyright 2026 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/tests/subplugin_test.php b/tests/subplugin_test.php index 338931b9..b60bf88c 100644 --- a/tests/subplugin_test.php +++ b/tests/subplugin_test.php @@ -14,19 +14,29 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -defined('MOODLE_INTERNAL') || die(); - -global $CFG; -require_once($CFG->libdir.'/upgradelib.php'); +namespace tool_lifecycle; use tool_lifecycle\local\manager\lib_manager; use tool_lifecycle\local\manager\step_manager; use tool_lifecycle\local\manager\trigger_manager; - -class subplugin_test extends \advanced_testcase { - - public function test_builtin_triggers() { +/** + * Tests the subplugin handling. + * @package tool_lifecycle + * @category test + * @group tool_lifecycle + * @copyright 2026 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class subplugin_test extends \advanced_testcase { + + /** + * Tests that all builtin triggers are registered and loadable. + * + * @covers \tool_lifecycle\local\manager\trigger_manager::get_trigger_types + * @covers \tool_lifecycle\local\manager\lib_manager::get_trigger_lib + */ + public function test_builtin_triggers(): void { $this->resetAfterTest(); $builtintriggers = \core_component::get_plugin_list('lifecycletrigger'); $triggers = trigger_manager::get_trigger_types(); @@ -40,7 +50,13 @@ public function test_builtin_triggers() { } } - public function test_builtin_steps() { + /** + * Tests that all builtin steps are registered and loadable. + * + * @covers \tool_lifecycle\local\manager\step_manager::get_step_types + * @covers \tool_lifecycle\local\manager\lib_manager::get_step_lib + */ + public function test_builtin_steps(): void { $this->resetAfterTest(); $builtinsteps = \core_component::get_plugin_list('lifecyclestep'); $steps = step_manager::get_step_types(); @@ -54,16 +70,22 @@ public function test_builtin_steps() { } } - public function test_additional_triggers() { + /** + * Tests that additional external triggers can be registered and loaded. + * + * @covers \tool_lifecycle\local\manager\trigger_manager::get_trigger_types + * @covers \tool_lifecycle\local\manager\lib_manager::get_trigger_lib + */ + public function test_additional_triggers(): void { $this->resetAfterTest(); // Add a fake tool plugin, which define a trigger. - $mockedcomponent = new ReflectionClass(\core_component::class); + $mockedcomponent = new \ReflectionClass(\core_component::class); $mockedplugins = $mockedcomponent->getProperty('plugins'); $mockedplugins->setAccessible(true); $plugins = $mockedplugins->getValue(); $plugins['tool'] += ['sampletrigger' => __DIR__ . '/fixtures/fakeplugins/sampletrigger']; - $mockedplugins->setValue($plugins); + $mockedplugins->setValue(null, $plugins); // The 'fixture' is not autoloaded, so we need to require it. require_once(__DIR__ . '/fixtures/fakeplugins/sampletrigger/classes//lifecycle/trigger.php'); @@ -78,19 +100,26 @@ public function test_additional_triggers() { // Unset the fake plugin. unset($plugins['tool']['sampletrigger']); - $mockedplugins->setValue($plugins); + $mockedplugins->setValue(null, $plugins); } - public function test_additional_steps() { + /** + * Tests that additional external steps and their interaction libs can be registered and loaded. + * + * @covers \tool_lifecycle\local\manager\step_manager::get_step_types + * @covers \tool_lifecycle\local\manager\lib_manager::get_step_lib + * @covers \tool_lifecycle\local\manager\lib_manager::get_step_interactionlib + */ + public function test_additional_steps(): void { $this->resetAfterTest(); // Add a fake tool plugin, which define a step. - $mockedcomponent = new ReflectionClass(\core_component::class); + $mockedcomponent = new \ReflectionClass(\core_component::class); $mockedplugins = $mockedcomponent->getProperty('plugins'); $mockedplugins->setAccessible(true); $plugins = $mockedplugins->getValue(); $plugins['tool'] += ['samplestep' => __DIR__ . '/fixtures/fakeplugins/samplestep']; - $mockedplugins->setValue($plugins); + $mockedplugins->setValue(null, $plugins); // The 'fixture' is not autoloaded, so we need to require it. require_once(__DIR__ . '/fixtures/fakeplugins/samplestep/classes//lifecycle/step.php'); @@ -110,7 +139,7 @@ public function test_additional_steps() { // Unset the fake plugin. unset($plugins['tool']['samplestep']); - $mockedplugins->setValue($plugins); + $mockedplugins->setValue(null, $plugins); } }