From a5de02a54c866b90071070bfca36e47548904637 Mon Sep 17 00:00:00 2001 From: Jason Den Dulk Date: Mon, 4 Nov 2024 09:46:21 +1100 Subject: [PATCH] MDL-83479 backup: Add hooks to restore process. - A hook to enable plugins to add extra settings to the restore task. This would be placed inside restore_root_task::define_settings() - A hook to enable plugins to add extra fields to the copy course form. This would be done inside the copy_form class. - An adjustment to add the restore plan settings to the course restored event data. This would be done inside restore_plan::execute(). - A hook to enable extra work inside the asynchronous copy task. To be placed inside asynchronous_copy_task::execute(). --- .../hook/after_copy_form_definition.php | 44 ++++++++ .../after_restore_root_define_settings.php | 43 ++++++++ .../hook/before_copy_course_execute.php | 49 +++++++++ .../hook/copy_helper_process_formdata.php | 51 +++++++++ backup/moodle2/restore_root_task.class.php | 8 ++ .../fixtures/restore_task_hook_callbacks.php | 47 ++++++++ .../tests/fixtures/restore_task_hooks.php | 40 +++++++ .../moodle2/tests/restore_stepslib_test.php | 56 ++++++++++ backup/tests/hook/copy_course_hook_test.php | 104 ++++++++++++++++++ .../fixtures/copy_course_hook_callbacks.php | 40 +++++++ .../tests/hook/fixtures/copy_course_hooks.php | 40 +++++++ backup/util/helper/copy_helper.class.php | 11 +- backup/util/helper/tests/copy_helper_test.php | 42 +++++++ .../tests/fixtures/helper_hook_callbacks.php | 37 +++++++ .../helper/tests/fixtures/helper_hooks.php | 41 +++++++ backup/util/plan/base_task.class.php | 20 ++-- backup/util/plan/restore_plan.class.php | 3 + .../hook/after_copy_form_definition.php | 33 ++++++ .../after_restore_root_define_settings.php | 30 +++++ .../hook/before_copy_course_execute.php | 30 +++++ .../hook/copy_helper_process_formdata.php | 32 ++++++ backup/util/ui/classes/output/copy_form.php | 8 ++ backup/util/ui/tests/copy_form_hook_test.php | 68 ++++++++++++ .../fixtures/copy_form_hook_callbacks.php | 38 +++++++ .../ui/tests/fixtures/copy_form_hooks.php | 40 +++++++ lib/classes/task/asynchronous_copy_task.php | 7 ++ 26 files changed, 954 insertions(+), 8 deletions(-) create mode 100644 backup/classes/hook/after_copy_form_definition.php create mode 100644 backup/classes/hook/after_restore_root_define_settings.php create mode 100644 backup/classes/hook/before_copy_course_execute.php create mode 100644 backup/classes/hook/copy_helper_process_formdata.php create mode 100644 backup/moodle2/tests/fixtures/restore_task_hook_callbacks.php create mode 100644 backup/moodle2/tests/fixtures/restore_task_hooks.php create mode 100644 backup/tests/hook/copy_course_hook_test.php create mode 100644 backup/tests/hook/fixtures/copy_course_hook_callbacks.php create mode 100644 backup/tests/hook/fixtures/copy_course_hooks.php create mode 100644 backup/util/helper/tests/fixtures/helper_hook_callbacks.php create mode 100644 backup/util/helper/tests/fixtures/helper_hooks.php create mode 100644 backup/util/ui/classes/hook/after_copy_form_definition.php create mode 100644 backup/util/ui/classes/hook/after_restore_root_define_settings.php create mode 100644 backup/util/ui/classes/hook/before_copy_course_execute.php create mode 100644 backup/util/ui/classes/hook/copy_helper_process_formdata.php create mode 100644 backup/util/ui/tests/copy_form_hook_test.php create mode 100644 backup/util/ui/tests/fixtures/copy_form_hook_callbacks.php create mode 100644 backup/util/ui/tests/fixtures/copy_form_hooks.php diff --git a/backup/classes/hook/after_copy_form_definition.php b/backup/classes/hook/after_copy_form_definition.php new file mode 100644 index 00000000000..e2b57768acc --- /dev/null +++ b/backup/classes/hook/after_copy_form_definition.php @@ -0,0 +1,44 @@ +. + +namespace core_backup\hook; + +use MoodleQuickForm; + +/** + * Hook to allow adding extra fields to the copy course form. + * This should be used together with core_backup\hook\copy_helper_process_formdata + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +#[\core\attribute\label('Use to add extra elements to the copy course form.')] +#[\core\attribute\tags('backup')] +final class after_copy_form_definition { + + /** @var MoodleQuickForm */ + public readonly MoodleQuickForm $mform; + + /** + * Constructor. + * + * @param MoodleQuickForm $mform + */ + public function __construct(MoodleQuickForm $mform) { + $this->mform = $mform; + } +} diff --git a/backup/classes/hook/after_restore_root_define_settings.php b/backup/classes/hook/after_restore_root_define_settings.php new file mode 100644 index 00000000000..04b7c9ba522 --- /dev/null +++ b/backup/classes/hook/after_restore_root_define_settings.php @@ -0,0 +1,43 @@ +. + +namespace core_backup\hook; + +use restore_root_task; + +/** + * Hook to allow extra settings to be defined for the course restore process. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +#[\core\attribute\label('Use to add extra elements to the settings tab of the restore process.')] +#[\core\attribute\tags('backup')] +final class after_restore_root_define_settings { + + /** @var restore_root_task */ + public readonly restore_root_task $task; + + /** + * Constructor. + * + * @param restore_root_task $task + */ + public function __construct(restore_root_task $task) { + $this->task = $task; + } +} diff --git a/backup/classes/hook/before_copy_course_execute.php b/backup/classes/hook/before_copy_course_execute.php new file mode 100644 index 00000000000..9930351b4e6 --- /dev/null +++ b/backup/classes/hook/before_copy_course_execute.php @@ -0,0 +1,49 @@ +. + +namespace core_backup\hook; + +use stdClass; +use restore_plan; + +/** + * Hook used to allow interaction with the copy task, before the actual task execution takes place. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +#[\core\attribute\label('Use to interact with the copy course task before the actual execution takes place.')] +#[\core\attribute\tags('backup')] +final class before_copy_course_execute { + + /** @var restore_plan */ + public readonly restore_plan $plan; + + /** @var stdClass */ + public readonly stdClass $copyinfo; + + /** + * Hook constructor. + * + * @param restore_plan $plan + * @param stdClass $copyinfo + */ + public function __construct(restore_plan $plan, stdClass $copyinfo) { + $this->plan = $plan; + $this->copyinfo = $copyinfo; + } +} diff --git a/backup/classes/hook/copy_helper_process_formdata.php b/backup/classes/hook/copy_helper_process_formdata.php new file mode 100644 index 00000000000..84cec9f7ea0 --- /dev/null +++ b/backup/classes/hook/copy_helper_process_formdata.php @@ -0,0 +1,51 @@ +. + +namespace core_backup\hook; + +/** + * Hook used by copy_helper::process_formdata() to expand the list of required fields. + * This should be used together with core_backup\hook\after_copy_form_definition. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +#[\core\attribute\label('Able to add extra fields to the copy course.')] +#[\core\attribute\tags('backup')] +final class copy_helper_process_formdata { + + /** @var array List of extra fields to be added. */ + protected $extrafields = []; + + /** + * Add an extra field. + * + * @param string $extrafield + */ + public function add_extra_field(string $extrafield) { + $this->extrafields[] = $extrafield; + } + + /** + * Get the extra fields. + * + * @return array + */ + public function get_extrafields(): array { + return $this->extrafields; + } +} diff --git a/backup/moodle2/restore_root_task.class.php b/backup/moodle2/restore_root_task.class.php index 2a93bc031d6..b3c31cf246b 100644 --- a/backup/moodle2/restore_root_task.class.php +++ b/backup/moodle2/restore_root_task.class.php @@ -26,6 +26,10 @@ defined('MOODLE_INTERNAL') || die(); +use core\di; +use core\hook\manager; +use core_backup\hook\after_restore_root_define_settings; + /** * Start task that provides all the settings common to all restores and other initial steps * @@ -343,5 +347,9 @@ class restore_root_task extends restore_task { $legacyfiles->set_ui(new backup_setting_ui_checkbox($legacyfiles, get_string('rootsettinglegacyfiles', 'backup'))); $legacyfiles->get_ui()->set_changeable($changeable); $this->add_setting($legacyfiles); + + // Create and dispatch a hook to allow plugins to add other settings for the restore process. + $hook = new after_restore_root_define_settings($this); + di::get(manager::class)->dispatch($hook); } } diff --git a/backup/moodle2/tests/fixtures/restore_task_hook_callbacks.php b/backup/moodle2/tests/fixtures/restore_task_hook_callbacks.php new file mode 100644 index 00000000000..4b61576bf45 --- /dev/null +++ b/backup/moodle2/tests/fixtures/restore_task_hook_callbacks.php @@ -0,0 +1,47 @@ +. + +namespace core_backup\fixtures; + +use base_setting; +use restore_generic_setting; +use backup_setting_ui_checkbox; +use core_backup\hook\after_restore_root_define_settings; + +/** + * Callback class to test after_restore_root_define_settings hook. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class restore_task_hook_callbacks { + /** + * Tests use of after_restore_root_define_settings hook. + * + * @param after_restore_root_define_settings $hook + */ + public static function after_restore_root_define_settings(after_restore_root_define_settings $hook) { + $task = $hook->task; + $defaultvalue = true; + $changeable = true; + + $somebox = new restore_generic_setting('extra_test', base_setting::IS_BOOLEAN, $defaultvalue); + $somebox->set_ui(new backup_setting_ui_checkbox($somebox, 'Extra test')); + $somebox->get_ui()->set_changeable($changeable); + $task->add_setting($somebox); + } +} diff --git a/backup/moodle2/tests/fixtures/restore_task_hooks.php b/backup/moodle2/tests/fixtures/restore_task_hooks.php new file mode 100644 index 00000000000..ff062847916 --- /dev/null +++ b/backup/moodle2/tests/fixtures/restore_task_hooks.php @@ -0,0 +1,40 @@ +. + +/** + * Describes the hook callbacks used to test the restore settings definition. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use core_backup\fixtures\restore_task_hook_callbacks; +use core_backup\hook\after_restore_root_define_settings; + +require_once(__DIR__ . '/restore_task_hook_callbacks.php'); + +$callbacks = [ + [ + 'hook' => after_restore_root_define_settings::class, + 'callback' => [ + restore_task_hook_callbacks::class, + 'after_restore_root_define_settings', + ], + ], +]; diff --git a/backup/moodle2/tests/restore_stepslib_test.php b/backup/moodle2/tests/restore_stepslib_test.php index 953c34b3898..cc76ebf7917 100644 --- a/backup/moodle2/tests/restore_stepslib_test.php +++ b/backup/moodle2/tests/restore_stepslib_test.php @@ -17,6 +17,9 @@ namespace core_backup; use backup; +use core\di; +use core\hook\manager; +use restore_controller; /** * Tests for Moodle 2 restore steplib classes. @@ -144,4 +147,57 @@ final class restore_stepslib_test extends \advanced_testcase { $this->assertEquals($originalsections[1]->$field, $restoredsections[1]->$field); } } + + /** + * Tests the hooks for restore task settings definition. + * + * @covers \restore_root_task::define_settings + */ + public function test_restore_hook(): void { + // Load the callback classes. + require_once(__DIR__ . '/fixtures/restore_task_hooks.php'); + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Replace the version of the manager in the DI container with a phpunit one. + di::set( + manager::class, + manager::phpunit_get_instance([ + // Load a list of hooks for `test_plugin1` from the fixture file. + 'test_plugin1' => __DIR__ . + '/fixtures/restore_task_hooks.php', + ]), + ); + + global $CFG, $USER; + + // Create course to restore into, and a user to do the restore. + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + + $backupid = $this->backup_course($course); + + // Turn off file logging, otherwise it can't delete the file (Windows). + $CFG->backup_file_logger_level = backup::LOG_NONE; + + $course = $generator->create_course(); + + // Do restore to new course with default settings. + $rc = new restore_controller( + $backupid, + $course->id, + backup::INTERACTIVE_NO, + backup::MODE_GENERAL, + $USER->id, + backup::TARGET_EXISTING_DELETING + ); + + $precheck = $rc->execute_precheck(); + $this->assertTrue($precheck); + $setting = $rc->get_plan()->get_setting('extra_test'); + $this->assertNotEmpty($setting); + $rc->execute_plan(); + $rc->destroy(); + } } diff --git a/backup/tests/hook/copy_course_hook_test.php b/backup/tests/hook/copy_course_hook_test.php new file mode 100644 index 00000000000..8af997cb0e9 --- /dev/null +++ b/backup/tests/hook/copy_course_hook_test.php @@ -0,0 +1,104 @@ +. + +namespace core_backup\hook; + +use core\di; +use copy_helper; +use advanced_testcase; +use core\hook\manager; +use core\task\manager as taskmanager; +use core\event\course_restored; +use core_backup\hook\fixtures\copy_course_hook_callbacks; + +/** + * Class to test the hook inside asynchronous_copy_task. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class copy_course_hook_test extends advanced_testcase { + + /** + * Test the hook. + * + * @covers \core\task\asynchronous_copy_task::execute + * @covers \core_backup\hook\before_copy_course_execute + */ + public function test_copy_course_hook(): void { + // Load the callback classes. + require_once(__DIR__ . '/fixtures/copy_course_hooks.php'); + + // Replace the version of the manager in the DI container with a phpunit one. + di::set( + manager::class, + manager::phpunit_get_instance([ + // Load a list of hooks for `test_plugin1` from the fixture file. + 'test_plugin1' => __DIR__ . + '/fixtures/copy_course_hooks.php', + ]), + ); + + $this->resetAfterTest(true); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + + $copydata = (object) [ + 'courseid' => $course->id, + 'fullname' => 'Name', + 'shortname' => 'name', + 'category' => 1, + 'visible' => 1, + 'startdate' => '123456789', + 'enddate' => '123456789', + 'idnumber' => 'dnum', + 'userdata' => false, + ]; + + $processed = copy_helper::process_formdata($copydata); + copy_helper::create_copy($processed); + $sink = $this->redirectEvents(); + + // Capture mtrace output. + ob_start(); + + // Execute adhoc task. + $now = time(); + $task = taskmanager::get_next_adhoc_task($now); + $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task); + $task->execute(); + taskmanager::adhoc_task_complete($task); + + ob_get_clean(); + + $this->assertGreaterThan(0, copy_course_hook_callbacks::$count); + + // Check that the restore settings have been added to the event data. + $events = $sink->get_events(); + $count = 0; + foreach ($events as $event) { + if ($event instanceof course_restored) { + $count++; + $data = $event->get_data(); + $this->assertNotEmpty($data['other']['settings']); + } + } + $this->assertGreaterThan(0, $count); + } +} diff --git a/backup/tests/hook/fixtures/copy_course_hook_callbacks.php b/backup/tests/hook/fixtures/copy_course_hook_callbacks.php new file mode 100644 index 00000000000..8f30c6a066f --- /dev/null +++ b/backup/tests/hook/fixtures/copy_course_hook_callbacks.php @@ -0,0 +1,40 @@ +. + +namespace core_backup\hook\fixtures; + +use core_backup\hook\before_copy_course_execute; + +/** + * Callbacks used to test the hooks in the copy course task. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class copy_course_hook_callbacks { + /** @var int Used to keep track oh how many times the callback has been called. */ + public static $count = 0; + + /** + * Callback used to test the before_copy_course_execute hook. + * + * @param before_copy_course_execute $hook + */ + public static function before_copy_course_execute(before_copy_course_execute $hook) { + self::$count++; + } +} diff --git a/backup/tests/hook/fixtures/copy_course_hooks.php b/backup/tests/hook/fixtures/copy_course_hooks.php new file mode 100644 index 00000000000..cf9eb60a6b3 --- /dev/null +++ b/backup/tests/hook/fixtures/copy_course_hooks.php @@ -0,0 +1,40 @@ +. + +/** + * Describes hooks used for testing. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use core_backup\hook\before_copy_course_execute; +use core_backup\hook\fixtures\copy_course_hook_callbacks; + +require_once(__DIR__ . '/copy_course_hook_callbacks.php'); + +$callbacks = [ + [ + 'hook' => before_copy_course_execute::class, + 'callback' => [ + copy_course_hook_callbacks::class, + 'before_copy_course_execute', + ], + ], +]; diff --git a/backup/util/helper/copy_helper.class.php b/backup/util/helper/copy_helper.class.php index 99f01c3a842..ab8083833f9 100644 --- a/backup/util/helper/copy_helper.class.php +++ b/backup/util/helper/copy_helper.class.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +use core\di; +use core\hook\manager; +use core_backup\hook\copy_helper_process_formdata; + defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php'); @@ -39,13 +43,18 @@ final class copy_helper { 'fullname', // Fullname of the destination course. 'shortname', // Shortname of the destination course. 'category', // Category integer ID that contains the destination course. - 'visible', // Integer to detrmine of the copied course will be visible. + 'visible', // Integer to determine of the copied course will be visible. 'startdate', // Integer timestamp of the start of the destination course. 'enddate', // Integer timestamp of the end of the destination course. 'idnumber', // ID of the destination course. 'userdata', // Integer to determine if the copied course will contain user data. ]; + // Use hook to expand field list. + $hook = new copy_helper_process_formdata(); + di::get(manager::class)->dispatch($hook); + $requiredfields = array_merge($requiredfields, $hook->get_extrafields()); + $missingfields = array_diff($requiredfields, array_keys((array)$formdata)); if ($missingfields) { throw new \moodle_exception('copyfieldnotfound', 'backup', '', null, implode(", ", $missingfields)); diff --git a/backup/util/helper/tests/copy_helper_test.php b/backup/util/helper/tests/copy_helper_test.php index 3fd2f31ca93..8b735559a65 100644 --- a/backup/util/helper/tests/copy_helper_test.php +++ b/backup/util/helper/tests/copy_helper_test.php @@ -17,6 +17,9 @@ namespace core_backup; use backup; +use core\di; +use copy_helper; +use core\hook\manager; defined('MOODLE_INTERNAL') || die(); @@ -836,4 +839,43 @@ final class copy_helper_test extends \advanced_testcase { $copydata = \copy_helper::process_formdata($formdata); \copy_helper::create_copy($copydata); } + + /** + * Test copy_helper_process_formdata hook. + * + * @covers \core_backup\hook\copy_helper_process_formdata + */ + public function test_copy_helper_process_formdata_hook(): void { + // Load the callback classes. + require_once(__DIR__ . '/fixtures/helper_hooks.php'); + + // Replace the version of the manager in the DI container with a phpunit one. + di::set( + manager::class, + manager::phpunit_get_instance([ + // Load a list of hooks for `test_plugin1` from the fixture file. + 'test_plugin1' => __DIR__ . + '/fixtures/helper_hooks.php', + ]), + ); + + $formdata = (object) [ + 'courseid' => 4, + 'fullname' => 'Name', + 'shortname' => 'name', + 'category' => 12, + 'visible' => 1, + 'startdate' => '123456789', + 'enddate' => '123456789', + 'idnumber' => 'dnum', + 'userdata' => false, + 'extra' => 13, + ]; + + $processed = copy_helper::process_formdata($formdata); + + // Check that the extra fields are present. + $this->assertTrue(isset($processed->extra)); + $this->assertEquals(13, $processed->extra); + } } diff --git a/backup/util/helper/tests/fixtures/helper_hook_callbacks.php b/backup/util/helper/tests/fixtures/helper_hook_callbacks.php new file mode 100644 index 00000000000..1be4027f80c --- /dev/null +++ b/backup/util/helper/tests/fixtures/helper_hook_callbacks.php @@ -0,0 +1,37 @@ +. + +namespace core_backup\fixtures; + +use core_backup\hook\copy_helper_process_formdata; + +/** + * Callbacks used to test hook. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class helper_hook_callbacks { + /** + * Constructor. + * + * @param copy_helper_process_formdata $hook + */ + public static function copy_helper_process_formdata(copy_helper_process_formdata $hook) { + $hook->add_extra_field('extra'); + } +} diff --git a/backup/util/helper/tests/fixtures/helper_hooks.php b/backup/util/helper/tests/fixtures/helper_hooks.php new file mode 100644 index 00000000000..5f338dcc726 --- /dev/null +++ b/backup/util/helper/tests/fixtures/helper_hooks.php @@ -0,0 +1,41 @@ +. + +/** + * Hook definitions. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use core_backup\fixtures\helper_hook_callbacks; +use core_backup\hook\copy_helper_process_formdata; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once(__DIR__ . '/helper_hook_callbacks.php'); + +$callbacks = [ + [ + 'hook' => copy_helper_process_formdata::class, + 'callback' => [ + helper_hook_callbacks::class, + 'copy_helper_process_formdata', + ], + ], +]; diff --git a/backup/util/plan/base_task.class.php b/backup/util/plan/base_task.class.php index 148c0bbe883..9b32d953d7c 100644 --- a/backup/util/plan/base_task.class.php +++ b/backup/util/plan/base_task.class.php @@ -270,6 +270,19 @@ abstract class base_task implements checksumable, executable, loggable { } } + /** + * Add a setting to the task. + * + * @param base_setting $setting + * @return void + */ + public function add_setting($setting) { + if (! $setting instanceof base_setting) { + throw new base_setting_exception('wrong_base_setting_specified'); + } + $this->settings[] = $setting; + } + // Protected API starts here /** @@ -278,13 +291,6 @@ abstract class base_task implements checksumable, executable, loggable { * in the task. */ abstract protected function define_settings(); - - protected function add_setting($setting) { - if (! $setting instanceof base_setting) { - throw new base_setting_exception('wrong_base_setting_specified'); - } - $this->settings[] = $setting; - } } /* diff --git a/backup/util/plan/restore_plan.class.php b/backup/util/plan/restore_plan.class.php index 666e81d926a..71f2c7a59f7 100644 --- a/backup/util/plan/restore_plan.class.php +++ b/backup/util/plan/restore_plan.class.php @@ -185,6 +185,9 @@ class restore_plan extends base_plan implements loggable { $otherarray['originalcourseid'] = $this->controller->get_info()->original_course_id; } + // Add the settings to the event data. + $otherarray['settings'] = $this->get_settings(); + // Trigger a course restored event. $event = \core\event\course_restored::create(array( 'objectid' => $this->get_courseid(), diff --git a/backup/util/ui/classes/hook/after_copy_form_definition.php b/backup/util/ui/classes/hook/after_copy_form_definition.php new file mode 100644 index 00000000000..d7a2bf6be49 --- /dev/null +++ b/backup/util/ui/classes/hook/after_copy_form_definition.php @@ -0,0 +1,33 @@ +. + +/** + * Hook to allow adding extra fields to the copy course form. + * + * This should be used together with core_backup\hook\copy_helper_process_formdata + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_backup\hook; + +defined('MOODLE_INTERNAL') || die(); + +// This file is a placeholder. Remove when MDL-83618 is available. +require_once(__DIR__.'/../../../../classes/hook/after_copy_form_definition.php'); + diff --git a/backup/util/ui/classes/hook/after_restore_root_define_settings.php b/backup/util/ui/classes/hook/after_restore_root_define_settings.php new file mode 100644 index 00000000000..382b33495aa --- /dev/null +++ b/backup/util/ui/classes/hook/after_restore_root_define_settings.php @@ -0,0 +1,30 @@ +. + +/** + * Hook to allow extra settings to be defined for the course restore process. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_backup\hook; + +defined('MOODLE_INTERNAL') || die(); + +// This file is a placeholder. Remove when MDL-83618 is available. +require_once(__DIR__.'/../../../../classes/hook/after_restore_root_define_settings.php'); diff --git a/backup/util/ui/classes/hook/before_copy_course_execute.php b/backup/util/ui/classes/hook/before_copy_course_execute.php new file mode 100644 index 00000000000..5f90bc0e1c1 --- /dev/null +++ b/backup/util/ui/classes/hook/before_copy_course_execute.php @@ -0,0 +1,30 @@ +. + +/** + * Hook used to allow interaction with the copy task, before the actual task execution takes place. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_backup\hook; + +defined('MOODLE_INTERNAL') || die(); + +// This file is a placeholder. Remove when MDL-83618 is available. +require_once(__DIR__.'/../../../../classes/hook/before_copy_course_execute.php'); diff --git a/backup/util/ui/classes/hook/copy_helper_process_formdata.php b/backup/util/ui/classes/hook/copy_helper_process_formdata.php new file mode 100644 index 00000000000..fea46a992c5 --- /dev/null +++ b/backup/util/ui/classes/hook/copy_helper_process_formdata.php @@ -0,0 +1,32 @@ +. + +/** + * Hook used by copy_helper::process_formdata() to expand the list of required fields. + * + * This should be used together with core_backup\hook\after_copy_form_definition. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_backup\hook; + +defined('MOODLE_INTERNAL') || die(); + +// This file is a placeholder. Remove when MDL-83618 is available. +require_once(__DIR__.'/../../../../classes/hook/copy_helper_process_formdata.php'); diff --git a/backup/util/ui/classes/output/copy_form.php b/backup/util/ui/classes/output/copy_form.php index 9c86d210523..53fb40f984f 100644 --- a/backup/util/ui/classes/output/copy_form.php +++ b/backup/util/ui/classes/output/copy_form.php @@ -25,6 +25,10 @@ namespace core_backup\output; +use core\di; +use core\hook\manager; +use core_backup\hook\after_copy_form_definition; + defined('MOODLE_INTERNAL') || die(); require_once("$CFG->libdir/formslib.php"); @@ -197,6 +201,10 @@ class copy_form extends \moodleform { $this->add_checkbox_controller(2); } + // Dispatch hook to allow more elements to be added to the form. + $hook = new after_copy_form_definition($mform); + di::get(manager::class)->dispatch($hook); + $buttonarray = array(); $buttonarray[] = $mform->createElement('submit', 'submitreturn', get_string('copyreturn', 'backup')); $buttonarray[] = $mform->createElement('submit', 'submitdisplay', get_string('copyview', 'backup')); diff --git a/backup/util/ui/tests/copy_form_hook_test.php b/backup/util/ui/tests/copy_form_hook_test.php new file mode 100644 index 00000000000..cbd7fdb65fe --- /dev/null +++ b/backup/util/ui/tests/copy_form_hook_test.php @@ -0,0 +1,68 @@ +. + +namespace core_backup; + +use core\di; +use core\hook\manager; +use advanced_testcase; +use core_backup\output\copy_form; + +/** + * Tests the after_copy_form_definition hook. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class copy_form_hook_test extends advanced_testcase { + /** + * Test the after_copy_form_definition hook. + * + * @covers \core_backup\output\copy_form::definition + */ + public function test_copy_form_hook(): void { + // Load the callback classes. + require_once(__DIR__ . '/fixtures/copy_form_hooks.php'); + + // Replace the version of the manager in the DI container with a phpunit one. + di::set( + manager::class, + manager::phpunit_get_instance([ + // Load a list of hooks for `test_plugin1` from the fixture file. + 'test_plugin1' => __DIR__ . + '/fixtures/copy_form_hooks.php', + ]), + ); + + $this->resetAfterTest(true); + $this->setAdminUser(); + + $generator = $this->getDataGenerator(); + $course = $generator->create_course(); + $form = new copy_form( + null, + ['course' => $course, 'returnto' => null, 'returnurl' => null] + ); + ob_start(); + $form->display(); + $html = ob_get_clean(); + + // Check that the wierdtestname element is part of the form. + $pos = strpos($html, 'wierdtestname'); + $this->assertNotFalse($pos); + } +} diff --git a/backup/util/ui/tests/fixtures/copy_form_hook_callbacks.php b/backup/util/ui/tests/fixtures/copy_form_hook_callbacks.php new file mode 100644 index 00000000000..07d8c9e9d8c --- /dev/null +++ b/backup/util/ui/tests/fixtures/copy_form_hook_callbacks.php @@ -0,0 +1,38 @@ +. + +namespace core_backup\fixtures; + +use core_backup\hook\after_copy_form_definition; + +/** + * Callback for testing after_copy_form_definition hook. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class copy_form_hook_callbacks { + /** + * Callback for testing after_copy_form_definition hook. + * + * @param after_copy_form_definition $hook + */ + public static function after_copy_form_definition(after_copy_form_definition $hook) { + $mform = $hook->mform; + $mform->addElement('checkbox', 'wierdtestname', 'Wierd test'); + } +} diff --git a/backup/util/ui/tests/fixtures/copy_form_hooks.php b/backup/util/ui/tests/fixtures/copy_form_hooks.php new file mode 100644 index 00000000000..4e710226122 --- /dev/null +++ b/backup/util/ui/tests/fixtures/copy_form_hooks.php @@ -0,0 +1,40 @@ +. + +/** + * Hooks used in testing. + * + * @package core_backup + * @copyright 2024 Monash University (https://www.monash.edu) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use core_backup\hook\after_copy_form_definition; +use core_backup\fixtures\copy_form_hook_callbacks; + +require_once(__DIR__ . '/copy_form_hook_callbacks.php'); + +$callbacks = [ + [ + 'hook' => after_copy_form_definition::class, + 'callback' => [ + copy_form_hook_callbacks::class, + 'after_copy_form_definition', + ], + ], +]; diff --git a/lib/classes/task/asynchronous_copy_task.php b/lib/classes/task/asynchronous_copy_task.php index 323ee3e356d..300d63dee4c 100644 --- a/lib/classes/task/asynchronous_copy_task.php +++ b/lib/classes/task/asynchronous_copy_task.php @@ -25,8 +25,11 @@ namespace core\task; +use core\di; use async_helper; use cache_helper; +use core\hook\manager; +use core_backup\hook\before_copy_course_execute; defined('MOODLE_INTERNAL') || die(); @@ -125,6 +128,10 @@ class asynchronous_copy_task extends adhoc_task { $shortname = $plan->get_setting('course_shortname'); $shortname->set_value($copyinfo->shortname); + // Create and dispatch a hook to allow interaction with the task immediately prior to execution. + $hook = new before_copy_course_execute($plan, $copyinfo); + di::get(manager::class)->dispatch($hook); + // Do some preflight checks on the restore. $rc->execute_precheck(); $status = $rc->get_status();