From 95eea308cd63ac312ef37d02053907ffbbd9bb41 Mon Sep 17 00:00:00 2001 From: Dmitrii Metelkin Date: Tue, 5 Dec 2023 11:07:22 +1100 Subject: [PATCH] MDL-73483 course: add callbacks to extend course form --- course/classes/hook/after_form_definition.php | 94 ++++++++++++ .../hook/after_form_definition_after_data.php | 94 ++++++++++++ course/classes/hook/after_form_submission.php | 94 ++++++++++++ course/classes/hook/after_form_validation.php | 137 ++++++++++++++++++ course/edit_form.php | 30 ++++ course/lib.php | 6 + course/upgrade.txt | 5 + version.php | 2 +- 8 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 course/classes/hook/after_form_definition.php create mode 100644 course/classes/hook/after_form_definition_after_data.php create mode 100644 course/classes/hook/after_form_submission.php create mode 100644 course/classes/hook/after_form_validation.php diff --git a/course/classes/hook/after_form_definition.php b/course/classes/hook/after_form_definition.php new file mode 100644 index 0000000000000..63599b7a71b93 --- /dev/null +++ b/course/classes/hook/after_form_definition.php @@ -0,0 +1,94 @@ +. + +namespace core_course\hook; + +use core\hook\described_hook; +use course_edit_form; +use MoodleQuickForm; + +/** + * Allows plugins to extend course form definition and add/remove/update form elements. + * + * @see course_edit_form::definition() + * + * @package core + * @copyright 2023 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class after_form_definition implements described_hook { + + /** + * Course form wrapper. + * + * @var course_edit_form + */ + protected $formwrapper; + + /** + * Form to be extended. + * + * @var \MoodleQuickForm + */ + protected $mform; + + /** + * Creates new hook. + * + * @param course_edit_form $formwrapper Course form wrapper. + * @param MoodleQuickForm $mform Form to be extended. + */ + public function __construct(course_edit_form $formwrapper, MoodleQuickForm $mform) { + $this->formwrapper = $formwrapper; + $this->mform = $mform; + } + + /** + * Returns form. + * + * @return MoodleQuickForm + */ + public function get_mform(): MoodleQuickForm { + return $this->mform; + } + + /** + * Returns form wrapper instance. + * + * @return course_edit_form + */ + public function get_formwrapper(): course_edit_form { + return $this->formwrapper; + } + + /** + * Describes the hook purpose. + * + * @return string + */ + public static function get_hook_description(): string { + return 'Allows plugins to extend course editing form'; + } + + /** + * List of tags that describe this hook. + * + * @return string[] + */ + public static function get_hook_tags(): array { + return ['course']; + } +} diff --git a/course/classes/hook/after_form_definition_after_data.php b/course/classes/hook/after_form_definition_after_data.php new file mode 100644 index 0000000000000..45f2216562832 --- /dev/null +++ b/course/classes/hook/after_form_definition_after_data.php @@ -0,0 +1,94 @@ +. + +namespace core_course\hook; + +use core\hook\described_hook; +use course_edit_form; +use MoodleQuickForm; + +/** + * Allows plugins to extend course form after data is set. + * + * @see course_edit_form::definition_after_data() + * + * @package core + * @copyright 2023 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class after_form_definition_after_data implements described_hook { + + /** + * Course form wrapper. + * + * @var course_edit_form + */ + protected $formwrapper; + + /** + * Form to be extended. + * + * @var \MoodleQuickForm + */ + protected $mform; + + /** + * Creates new hook. + * + * @param course_edit_form $formwrapper Course form wrapper.. + * @param MoodleQuickForm $mform Form to be extended. + */ + public function __construct(course_edit_form $formwrapper, MoodleQuickForm $mform) { + $this->formwrapper = $formwrapper; + $this->mform = $mform; + } + + /** + * Returns form. + * + * @return MoodleQuickForm + */ + public function get_mform(): MoodleQuickForm { + return $this->mform; + } + + /** + * Returns form wrapper instance. + * + * @return course_edit_form + */ + public function get_formwrapper(): course_edit_form { + return $this->formwrapper; + } + + /** + * Describes the hook purpose. + * + * @return string + */ + public static function get_hook_description(): string { + return 'Allows plugins to extend course editing form after data is set'; + } + + /** + * List of tags that describe this hook. + * + * @return string[] + */ + public static function get_hook_tags(): array { + return ['course']; + } +} diff --git a/course/classes/hook/after_form_submission.php b/course/classes/hook/after_form_submission.php new file mode 100644 index 0000000000000..e51e4b9563988 --- /dev/null +++ b/course/classes/hook/after_form_submission.php @@ -0,0 +1,94 @@ +. + +namespace core_course\hook; + +use core\hook\described_hook; +use stdClass; + +/** + * Allows plugins to extend course form submission. + * + * @see create_course() + * @see update_course() + * + * @package core + * @copyright 2023 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class after_form_submission implements described_hook { + + /** + * Submitted data. + * + * @var stdClass + */ + protected $data; + + /** + * Is it a new course ? + * + * @var bool + */ + protected $isnewcourse = false; + + /** + * Creates new hook. + * + * @param stdClass $data Submitted data. + * @param bool $isnewcourse Is it a new course? + */ + public function __construct(stdClass $data, bool $isnewcourse = false) { + $this->data = $data; + $this->isnewcourse = $isnewcourse; + } + + /** + * Returns submitted data. + * + * @return stdClass + */ + public function get_data(): stdClass { + return $this->data; + } + + /** + * Informs callbacks if a hook called for a new course. + * + * @return bool + */ + public function is_new_course(): bool { + return $this->isnewcourse; + } + + /** + * Describes the hook purpose. + * + * @return string + */ + public static function get_hook_description(): string { + return 'Allows plugins to extend saving of the course editing form'; + } + + /** + * List of tags that describe this hook. + * + * @return string[] + */ + public static function get_hook_tags(): array { + return ['course']; + } +} diff --git a/course/classes/hook/after_form_validation.php b/course/classes/hook/after_form_validation.php new file mode 100644 index 0000000000000..d6fae0286204c --- /dev/null +++ b/course/classes/hook/after_form_validation.php @@ -0,0 +1,137 @@ +. + +namespace core_course\hook; + +use core\hook\described_hook; +use course_edit_form; + +/** + * Allows plugins to extend course form validation. + * + * @see course_edit_form::validation() + * + * @package core + * @copyright 2023 Dmitrii Metelkin + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class after_form_validation implements described_hook { + + /** + * Course form wrapper. + * + * @var course_edit_form + */ + protected $formwrapper; + + /** + * Submitted data. + * + * @var array + */ + protected $data = []; + + /** + * Submitted files. + * + * @var array + */ + protected $files = []; + + /** + * Plugin errors. + * + * @var array + */ + protected $errors = []; + + /** + * Creates new hook. + * + * @param course_edit_form $formwrapper Course form wrapper.. + * @param array $data Submitted data. + * @param array $files Submitted files. + */ + public function __construct(course_edit_form $formwrapper, array $data, array $files = []) { + $this->formwrapper = $formwrapper; + $this->data = $data; + $this->files = $files; + } + + /** + * Returns form wrapper instance. + * + * @return course_edit_form + */ + public function get_formwrapper(): course_edit_form { + return $this->formwrapper; + } + + /** + * Returns submitted data. + * + * @return array + */ + public function get_data(): array { + return $this->data; + } + + /** + * Returns submitted files. + * + * @return array + */ + public function get_files(): array { + return $this->files; + } + + /** + * Return plugin generated errors. + * + * @return array + */ + public function get_errors(): array { + return $this->errors; + } + + /** + * Plugins implementing a callback can add validation errors. + * + * @param array $errors Validation errors generated by a plugin. + * @return void + */ + public function add_errors(array $errors): void { + $this->errors = array_merge($this->errors, $errors); + } + + /** + * Describes the hook purpose. + * + * @return string + */ + public static function get_hook_description(): string { + return 'Allows plugins to extend a validation of the course editing form'; + } + + /** + * List of tags that describe this hook. + * + * @return string[] + */ + public static function get_hook_tags(): array { + return ['course']; + } +} diff --git a/course/edit_form.php b/course/edit_form.php index 5769e4ff24dba..0c1879ad9bb53 100644 --- a/course/edit_form.php +++ b/course/edit_form.php @@ -418,6 +418,8 @@ function definition() { $handler->set_parent_context($categorycontext); // For course handler only. $handler->instance_form_definition($mform, empty($course->id) ? 0 : $course->id); + $hook = new \core_course\hook\after_form_definition($this, $mform); + \core\hook\manager::get_instance()->dispatch($hook); // When two elements we need a group. $buttonarray = array(); @@ -488,6 +490,9 @@ function definition_after_data() { // Tweak the form with values provided by custom fields in use. $handler = core_course\customfield\course_handler::create(); $handler->instance_form_definition_after_data($mform, empty($courseid) ? 0 : $courseid); + + $hook = new \core_course\hook\after_form_definition_after_data($this, $mform); + \core\hook\manager::get_instance()->dispatch($hook); } /** @@ -534,6 +539,31 @@ function validation($data, $files) { $handler = core_course\customfield\course_handler::create(); $errors = array_merge($errors, $handler->instance_form_validation($data, $files)); + $hook = new \core_course\hook\after_form_validation($this, $data, $files); + \core\hook\manager::get_instance()->dispatch($hook); + $pluginerrors = $hook->get_errors(); + if (!empty($pluginerrors)) { + $errors = array_merge($errors, $pluginerrors); + } + return $errors; } + + /** + * Returns course object. + * + * @return \stdClass + */ + public function get_course(): stdClass { + return $this->course; + } + + /** + * Returns context. + * + * @return \core\context + */ + public function get_context(): \core\context { + return $this->context; + } } diff --git a/course/lib.php b/course/lib.php index a8074c504733c..a39eeab4342af 100644 --- a/course/lib.php +++ b/course/lib.php @@ -2177,6 +2177,9 @@ function create_course($data, $editoroptions = NULL) { $data->id = $course->id; $handler->instance_form_save($data, true); + $hook = new \core_course\hook\after_form_submission($data, true); + \core\hook\manager::get_instance()->dispatch($hook); + return $course; } @@ -2425,6 +2428,9 @@ function update_course($data, $editoroptions = NULL) { $handler = core_course\customfield\course_handler::create(); $handler->instance_form_save($data); + $hook = new \core_course\hook\after_form_submission($data); + \core\hook\manager::get_instance()->dispatch($hook); + // Update with the new data $DB->update_record('course', $data); // make sure the modinfo cache is reset diff --git a/course/upgrade.txt b/course/upgrade.txt index 8b906375d1c39..f41a5d72a31a3 100644 --- a/course/upgrade.txt +++ b/course/upgrade.txt @@ -22,6 +22,11 @@ information provided here is intended especially for developers. * New format actions classes. Those classes will eventually replace all course/lib.php content editing functions. All new methods are distributed in three classes formats can extend. Method can be accessed using static methods (see doc block of core_courseformat\formatactions for more information). +* New hooks for extending course form: + - core_course\hook\after_form_definition + - core_course\hook\after_form_definition_after_data + - core_course\hook\after_form_validation + - core_course\hook\after_form_submission === 4.3 === * The `core_course_renderer::course_section_cm_completion` method has been removed, and can no longer be used diff --git a/version.php b/version.php index 2cf1b5955b793..aa8fa402aedd4 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2024021500.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2024021500.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.4dev (Build: 20240215)'; // Human-friendly version name