From 881e9d0b3047cbd333d7c15c3a1176ef22d8c540 Mon Sep 17 00:00:00 2001 From: Dmitrii Metelkin Date: Fri, 27 Sep 2024 17:33:39 +1000 Subject: [PATCH] WIP --- classes/helper.php | 165 ++++++++++++++++++++++++---- classes/observer.php | 50 ++++----- classes/preset_form.php | 21 +--- classes/task/add_item.php | 18 ++- classes/task/remove_item.php | 1 + classes/task/update_preset_data.php | 27 ++--- tests/helper_test.php | 91 +++++++++++++++ tests/observer_test.php | 120 ++++++++++++++++++++ 8 files changed, 403 insertions(+), 90 deletions(-) diff --git a/classes/helper.php b/classes/helper.php index ba4227c..2e76598 100644 --- a/classes/helper.php +++ b/classes/helper.php @@ -89,10 +89,10 @@ class helper { * @param int $itemid Item ID number * @param string $itemtype Item type (tag, course, category). * @param string $itemname Item name. - * @param array $courses Course to set up enrolment method. If not set, the no enrolment method will be created. + * @param array $courseids Course to set up enrolment method. If not set, the no enrolment method will be created. * @return void */ - public static function add_item(int $itemid, string $itemtype, string $itemname, array $courses = []): void { + public static function add_item(int $itemid, string $itemtype, string $itemname, array $courseids = []): void { $cohort = self::get_cohort_by_item($itemid, $itemtype); if (empty($cohort)) { @@ -114,15 +114,8 @@ public static function add_item(int $itemid, string $itemtype, string $itemname, self::add_rule($cohort, $itemtype); // Add a tag to a custom profile field. self::add_profile_field_item($itemtype, $itemname); - - // Create enrolment method for the cohort for a given course. - if (!empty($courses)) { - foreach ($courses as $course) { - if (!empty($course)) { - self::add_enrolment_method($course, $cohort); - } - } - } + // Add enrolment method for given course ids. + self::add_enrolment_method($cohort, $courseids); } /** @@ -151,6 +144,8 @@ public static function remove_item(int $itemid, string $itemtype, string $itemna cohort_delete_cohort($cohort); // Delete tag value from custom profile field list. self::delete_profile_field_item($itemtype, $itemname); + // Clean up presets. + self::clean_up_presets($itemid, $itemtype); // Clean up user data? } } @@ -244,26 +239,34 @@ public static function get_cohort_by_item(int $itemid, string $itemtype): ?stdCl /** * Helper method to add enrolment method to a course. * - * @param stdClass $course Course. * @param stdClass $cohort Cohort. + * @param array $courseids A list of course ids. * * @return void */ - public static function add_enrolment_method(stdClass $course, stdClass $cohort): void { + public static function add_enrolment_method(stdClass $cohort, array $courseids): void { global $DB; $studentrole = self::get_student_role(); - $fields = [ - 'enrol' => 'cohort', - 'customint1' => $cohort->id, - 'roleid' => $studentrole->id, - 'courseid' => $course->id, - ]; + if (!empty($courseids)) { + list($sql, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); + $select = 'id ' . $sql; + $courses = $DB->get_records_select('course', $select, $params); - if (!$DB->record_exists('enrol', $fields)) { - $enrol = enrol_get_plugin('cohort'); - $enrol->add_instance($course, $fields); + foreach ($courses as $course) { + $fields = [ + 'enrol' => 'cohort', + 'customint1' => $cohort->id, + 'roleid' => $studentrole->id, + 'courseid' => $course->id, + ]; + + if (!$DB->record_exists('enrol', $fields)) { + $enrol = enrol_get_plugin('cohort'); + $enrol->add_instance($course, $fields); + } + } } } @@ -510,8 +513,7 @@ public static function update_course_category(int $courseid, int $newcategoryid) $cohort = self::get_cohort_by_item($newcategoryid, self::ITEM_TYPE_CATEGORY); if ($cohort) { - $course = get_course($courseid); - self::add_enrolment_method($course, $cohort); + self::add_enrolment_method($cohort, [$courseid]); } } @@ -610,4 +612,119 @@ public static function get_courses_by_tags(array $tagids): array { return $DB->get_records_sql($sql, $params); } + + /** + * A tiny helper method to convert list of items from string to array. + * + * @param string|null $data + * @return array + */ + public static function explode_data(?string $data): array { + return !empty($data) ? explode(',', $data) : []; + } + + /** + * Get list of presets by item type and ID. + * + * @param int $itemid Item ID. + * @param string $itemtype Itenm type. + * + * @return preset[] + */ + public static function get_presets_by_item(int $itemid, string $itemtype): array { + $results = []; + + if (in_array($itemtype, self::get_field_types())) { + foreach (preset::get_records() as $preset) { + if (in_array($itemid, self::explode_data($preset->get($itemtype)))) { + $results[$preset->get('id')] = $preset; + } + } + } + + return $results; + } + + /** + * Get a list of field types. + * + * @return string[] + */ + public static function get_field_types(): array { + return [self::ITEM_TYPE_CATEGORY, self::ITEM_TYPE_COURSE, self::ITEM_TYPE_TAG]; + } + + /** + * Get list of course IDs for a given preset. + * + * @param preset $preset Preset + * @return array + */ + public static function get_course_ids_from_preset(preset $preset): array { + $courseids = []; + + if (!empty($preset->get('category'))) { + $catcourses = array_keys( + self::get_courses_by_categories(explode(',', $preset->get('category'))) + ); + + $courseids = array_unique(array_merge($courseids, $catcourses)); + } + + if (!empty($preset->get('course'))) { + $courses = explode(',', $preset->get('course')); + $courseids = array_unique(array_merge($courseids, $courses)); + } + + if (!empty($preset->get('tag'))) { + $tagcourses = array_keys( + self::get_courses_by_tags(explode(',', $preset->get('tag'))) + ); + $courseids = array_values(array_unique(array_merge($courseids, $tagcourses))); + } + + return $courseids; + } + + public static function clean_up_presets(int $itemid, string $itemtype): void { + global $DB; + + $presets = helper::get_presets_by_item($itemid, $itemtype); + + foreach ($presets as $preset) { + $items = self::explode_data($preset->get($itemtype)); + if (($key = array_search($itemid, $items)) !== false) { + unset($items[$key]); + } + $preset->set($itemtype, implode(',', $items)); + $preset->save(); + } + + foreach ($presets as $preset) { + $cohort = self::get_cohort_by_item($preset->get('id'), self::ITEM_TYPE_PRESET); + if ($cohort) { + $presetcourses = self::get_course_ids_from_preset($preset); + $studentrole = self::get_student_role(); + + $fields = [ + 'enrol' => 'cohort', + 'customint1' => $cohort->id, + 'roleid' => $studentrole->id, + ]; + + $instances = $DB->get_records('enrol', $fields); + + if ($instances) { + $enrol = enrol_get_plugin('cohort'); + foreach ($instances as $instance) { + // If enrolment instance belongs to a course that is no longer part of preset, + // them delete this enrolment instance. + if (!in_array($instance->courseid, $presetcourses)) { + $enrol->delete_instance($instance); + } + } + } + } + } + } } diff --git a/classes/observer.php b/classes/observer.php index 6099148..0e7b1ab 100644 --- a/classes/observer.php +++ b/classes/observer.php @@ -90,6 +90,10 @@ public static function tag_removed(tag_removed $event): void { 'itemtype' => helper::ITEM_TYPE_TAG, 'courseid' => $event->other['itemid'], ]); + + // Find all presets based on item type and id. + // For each preset get cohort of that preset + // Remove that cohort to the course. } /** @@ -106,6 +110,11 @@ public static function tag_deleted(tag_deleted $event): void { 'itemtype' => helper::ITEM_TYPE_TAG, 'itemname' => $tagname, ]); + + + // Find all presets based on item type and id. + // For each preset get cohort of that preset + // Remove that cohort from the course. } /** @@ -169,6 +178,14 @@ public static function course_updated(course_updated $event): void { 'categoryid' => $event->other['updatedfields']['category'], ]); } + + // Find all presets based on item type (category) and id. + // For each preset get cohort of that preset + // Add that cohort from the course. + + // Find all presets based on item type (category) and old id. + // For each preset get cohort of that preset + // Remove that cohort from the course. } /** @@ -234,6 +251,10 @@ public static function course_category_deleted(course_category_deleted $event): 'itemtype' => helper::ITEM_TYPE_CATEGORY, 'itemname' => $categoryname, ]); + + // Find all presets based on item type and id. + // For each preset get cohort of that preset + // Remove that cohort from the all courses. } /** @@ -242,34 +263,13 @@ public static function course_category_deleted(course_category_deleted $event): * @param preset_created $event The event. */ public static function preset_created(preset_created $event): void { - $courseids = []; - - if (!empty($event->other['categories'])) { - $catcourses = array_keys( - helper::get_courses_by_categories(explode(',', $event->other['categories'])) - ); - - $courseids = array_unique(array_merge($courseids, $catcourses)); - } - - if (!empty($event->other['courses'])) { - $courses = explode(',', $event->other['courses']); - $courseids = array_unique(array_merge($courseids, $courses)); - - } - - if (!empty($event->other['tags'])) { - $tagcourses = array_keys( - helper::get_courses_by_tags(explode(',', $event->other['tags'])) - ); - $courseids = array_values(array_unique(array_merge($courseids, $tagcourses))); - } + $preset = preset::get_record(['id' => $event->other['presetid']]); self::queue_adhoc_task('add_item', [ - 'itemid' => $event->other['presetid'], + 'itemid' => $preset->get('id'), 'itemtype' => helper::ITEM_TYPE_PRESET, - 'itemname' => $event->other['presetname'], - 'courseids' => $courseids, + 'itemname' => $preset->get('name'), + 'courseids' => helper::get_course_ids_from_preset($preset), ]); } diff --git a/classes/preset_form.php b/classes/preset_form.php index 5bc651b..624f2f4 100644 --- a/classes/preset_form.php +++ b/classes/preset_form.php @@ -47,7 +47,7 @@ protected function definition() { $this->_form->addElement('static', 'error', ''); - foreach ($this->get_field_types() as $type) { + foreach (helper::get_field_types() as $type) { $methodname = 'get_' . $type . '_options'; $this->_form->addElement( 'autocomplete', @@ -59,15 +59,6 @@ protected function definition() { } } - /** - * Get a list of field types. - * - * @return string[] - */ - protected function get_field_types(): array { - return ['category', 'course', 'tag']; - } - /** * Gets categories options. * @@ -132,13 +123,13 @@ public function validation($data, $files) { $errors = parent::validation($data, $files); $empty = 0; - foreach ($this->get_field_types() as $type) { + foreach (helper::get_field_types() as $type) { if (empty($data[$type])) { $empty++; } } - if ($empty == count($this->get_field_types())) { + if ($empty == count(helper::get_field_types())) { $errors['error'] = get_string('mustselectentities', 'tool_enrolprofile'); } @@ -178,7 +169,7 @@ public function process_dynamic_submission(): \stdClass { $preset = new preset(); } - foreach ($this->get_field_types() as $type) { + foreach (helper::get_field_types() as $type) { if (!empty($data->$type)) { $data->$type = implode(',', $data->$type); } else { @@ -197,7 +188,7 @@ public function process_dynamic_submission(): \stdClass { 'presetname' => $preset->get('name'), ]; - foreach ($this->get_field_types() as $type) { + foreach (helper::get_field_types() as $type) { $other[$type] = $data->$type; $other['old' . $type] = $olddata->$type; } @@ -208,7 +199,7 @@ public function process_dynamic_submission(): \stdClass { 'other' => $other, ])->trigger(); } else { - foreach ($this->get_field_types() as $type) { + foreach (helper::get_field_types() as $type) { $other['old' . $type] = $olddata->$type; } preset_updated::create([ diff --git a/classes/task/add_item.php b/classes/task/add_item.php index 8c8f748..c3ffb7b 100644 --- a/classes/task/add_item.php +++ b/classes/task/add_item.php @@ -35,7 +35,6 @@ class add_item extends adhoc_task { public function execute() { global $DB; - $courses = []; $data = $this->get_custom_data(); helper::validate_task_custom_data($data); @@ -47,13 +46,20 @@ public function execute() { $data->courseids = [$data->courseid]; } - if (!empty($data->courseids) && is_array($data->courseids)) { - list($sql, $params) = $DB->get_in_or_equal($data->courseids, SQL_PARAMS_NAMED); - $select = 'id ' . $sql; - $courses = $DB->get_records_select('course', $select, $params); + if (empty($data->courseids)) { + $data->courseids = []; + } + + helper::add_item($data->itemid, $data->itemtype, $data->itemname, $data->courseids); + + $presets = helper::get_presets_by_item($data->itemid, $data->itemtype); + foreach ($presets as $preset) { + $cohort = helper::get_cohort_by_item($preset->get('id'), helper::ITEM_TYPE_PRESET); + if (!empty($cohort)) { + helper::add_enrolment_method($cohort, $data->courseids); + } } - helper::add_item($data->itemid, $data->itemtype, $data->itemname, $courses); $transaction->allow_commit(); } catch (Exception $exception) { $transaction->rollback($exception); diff --git a/classes/task/remove_item.php b/classes/task/remove_item.php index 304805d..06a7934 100644 --- a/classes/task/remove_item.php +++ b/classes/task/remove_item.php @@ -42,6 +42,7 @@ public function execute() { try { helper::remove_item($data->itemid, $data->itemtype, $data->itemname); + $transaction->allow_commit(); } catch (Exception $exception) { $transaction->rollback($exception); diff --git a/classes/task/update_preset_data.php b/classes/task/update_preset_data.php index ecfceb0..53198ee 100644 --- a/classes/task/update_preset_data.php +++ b/classes/task/update_preset_data.php @@ -102,8 +102,8 @@ private function validate_custom_data(stdClass $data): void { * @param stdClass $data Categories data. */ private function process_categories(stdClass $data): void { - $categories = $this->explode_data($data->categories); - $oldcategories = $this->explode_data($data->oldcategories); + $categories = helper::explode_data($data->categories); + $oldcategories = helper::explode_data($data->oldcategories); $this->update_course_lists($categories, $oldcategories, 'get_courses_by_categories'); } @@ -114,8 +114,8 @@ private function process_categories(stdClass $data): void { * @param stdClass $data Courses data. */ private function process_courses(stdClass $data): void { - $courses = $this->explode_data($data->courses); - $oldcourses = $this->explode_data($data->oldcourses); + $courses = helper::explode_data($data->courses); + $oldcourses = helper::explode_data($data->oldcourses); $this->update_course_lists($courses, $oldcourses); } @@ -126,22 +126,12 @@ private function process_courses(stdClass $data): void { * @param \stdClass $data Tags data. */ private function process_tags(stdClass $data): void { - $tags = $this->explode_data($data->tags); - $oldtags = $this->explode_data($data->oldtags); + $tags = helper::explode_data($data->tags); + $oldtags = helper::explode_data($data->oldtags); $this->update_course_lists($tags, $oldtags, 'get_courses_by_tags'); } - /** - * A tiny helper method to convert list of items from string to array. - * - * @param string $data - * @return array - */ - private function explode_data(string $data): array { - return !empty($data) ? explode(',', $data) : []; - } - /** * Update list of courses based on new and old items. * @@ -175,9 +165,6 @@ private function update_enrolments(int $itemid, string $itemtype): void { helper::remove_enrolment_method($itemid, $itemtype, $courseid); } - foreach ($this->coursesadd as $courseid) { - $course = get_course($courseid); - helper::add_enrolment_method($course, $this->cohort); - } + helper::add_enrolment_method($this->cohort, $this->coursesadd); } } diff --git a/tests/helper_test.php b/tests/helper_test.php index 89ff3b4..8417111 100644 --- a/tests/helper_test.php +++ b/tests/helper_test.php @@ -234,4 +234,95 @@ public function test_validate_task_custom_data(array $data, array $fields, strin helper::validate_task_custom_data((object)$data, $fields); } + + /** + * Testing getting presets by item + */ + public function test_get_presets_by_item() { + $this->resetAfterTest(); + + $category1 = $this->getDataGenerator()->create_category(); + $category2 = $this->getDataGenerator()->create_category(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + + $tag1 = $this->getDataGenerator()->create_tag(); + $tag2 = $this->getDataGenerator()->create_tag(); + + $preset1 = new preset(); + $preset1->set('name', 'Test preset 1'); + $preset1->set('category', implode(',', [$category1->id])); + $preset1->set('course', implode(',', [$course1->id])); + $preset1->set('tag', implode(',', [$tag1->id])); + $preset1->save(); + + $preset2 = new preset(); + $preset2->set('name', 'Test preset 2'); + $preset2->set('category', implode(',', [$category2->id])); + $preset2->set('course', implode(',', [$course2->id])); + $preset2->set('tag', implode(',', [$tag2->id])); + $preset2->save(); + + $preset3 = new preset(); + $preset3->set('name', 'Test preset 3'); + $preset3->set('category', implode(',', [$category1->id])); + $preset3->save(); + + $preset4 = new preset(); + $preset4->set('name', 'Test preset 4'); + $preset4->set('course', implode(',', [$course1->id])); + $preset4->save(); + + $preset5 = new preset(); + $preset5->set('name', 'Test preset 5'); + $preset5->set('tag', implode(',', [$tag2->id])); + $preset5->save(); + + $preset6 = new preset(); + $preset6->set('name', 'Test preset 6'); + $preset6->set('category', implode(',', [$category1->id, $category2->id])); + $preset6->set('course', implode(',', [$course1->id, $course2->id])); + $preset6->set('tag', implode(',', [$tag1->id, $tag2->id])); + $preset6->save(); + + $results = helper::get_presets_by_item($category1->id, helper::ITEM_TYPE_CATEGORY); + $this->assertCount(3, $results); + $this->assertArrayHasKey($preset1->get('id'), $results); + $this->assertArrayHasKey($preset3->get('id'), $results); + $this->assertArrayHasKey($preset6->get('id'), $results); + + $results = helper::get_presets_by_item($category2->id, helper::ITEM_TYPE_CATEGORY); + $this->assertCount(2, $results); + $this->assertArrayHasKey($preset2->get('id'), $results); + $this->assertArrayHasKey($preset6->get('id'), $results); + + $results = helper::get_presets_by_item($course1->id, helper::ITEM_TYPE_COURSE); + $this->assertCount(3, $results); + $this->assertArrayHasKey($preset1->get('id'), $results); + $this->assertArrayHasKey($preset4->get('id'), $results); + $this->assertArrayHasKey($preset6->get('id'), $results); + + $results = helper::get_presets_by_item($course2->id, helper::ITEM_TYPE_COURSE); + $this->assertCount(2, $results); + $this->assertArrayHasKey($preset2->get('id'), $results); + $this->assertArrayHasKey($preset6->get('id'), $results); + + $results = helper::get_presets_by_item($tag1->id, helper::ITEM_TYPE_TAG); + $this->assertCount(2, $results); + $this->assertArrayHasKey($preset1->get('id'), $results); + $this->assertArrayHasKey($preset6->get('id'), $results); + + $results = helper::get_presets_by_item($tag2->id, helper::ITEM_TYPE_TAG); + $this->assertCount(3, $results); + $this->assertArrayHasKey($preset2->get('id'), $results); + $this->assertArrayHasKey($preset5->get('id'), $results); + $this->assertArrayHasKey($preset6->get('id'), $results); + + $results = helper::get_presets_by_item(77777, helper::ITEM_TYPE_TAG); + $this->assertCount(0, $results); + + $results = helper::get_presets_by_item($preset1->get('id'), helper::ITEM_TYPE_PRESET); + $this->assertCount(0, $results); + } } diff --git a/tests/observer_test.php b/tests/observer_test.php index da26218..5cb2950 100644 --- a/tests/observer_test.php +++ b/tests/observer_test.php @@ -1273,4 +1273,124 @@ public function test_preset_logic() { $DB->get_record('enrol', ['courseid' => $course32->id, 'enrol' => 'cohort', 'customint1' => $presetcohortid]) ); } + + /** + * Test preset enrolment method added when tag added to a course. + */ + public function test_preset_enrolment_added_when_tag_added() { + global $DB; + + $category = $this->getDataGenerator()->create_category(); + $course = $this->getDataGenerator()->create_course(['category' => $category->id]); + $tag = $this->getDataGenerator()->create_tag(); + + $preset1name = 'Test preset 1'; + $preset1 = new preset(); + $preset1->set('name', $preset1name); + $preset1->set('tag', implode(',', [$tag->id])); + $preset1->save(); + + preset_created::create([ + 'context' => system::instance(), + 'other' => [ + 'presetid' => $preset1->get('id'), + 'presetname' => $preset1->get('name'), + 'categories' => $preset1->get('category'), + 'oldcategories' => null, + 'courses' => $preset1->get('course'), + 'oldcourses' => null, + 'tags' => $preset1->get('tag'), + 'oldtags' => null + ] + ])->trigger(); + + $preset2name = 'Test preset 2'; + $preset2 = new preset(); + $preset2->set('name', $preset2name); + $preset2->set('tag', implode(',', [$tag->id])); + $preset2->save(); + + preset_created::create([ + 'context' => system::instance(), + 'other' => [ + 'presetid' => $preset2->get('id'), + 'presetname' => $preset2->get('name'), + 'categories' => $preset2->get('category'), + 'oldcategories' => null, + 'courses' => $preset2->get('course'), + 'oldcourses' => null, + 'tags' => $preset2->get('tag'), + 'oldtags' => null + ] + ])->trigger(); + + core_tag_tag::set_item_tags('core', 'course', $course->id, course::instance($course->id), [$tag->rawname]); + $this->execute_tasks(); + + $preset1cohort = $DB->get_record('cohort', ['name' => $preset1name]); + $this->assertNotEmpty($preset1cohort); + + $preset2cohort = $DB->get_record('cohort', ['name' => $preset2name]); + $this->assertNotEmpty($preset2cohort); + + $this->assertNotEmpty( + $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'cohort', 'customint1' => $preset1cohort->id]) + ); + + $this->assertNotEmpty( + $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'cohort', 'customint1' => $preset2cohort->id]) + ); + } + + /** + * Test category preset enrolment method when course created. + */ + public function test_preset_enrolment_added_when_course_created() { + global $DB; + + $category = $this->getDataGenerator()->create_category(); + + $preset1name = 'Test preset 1'; + $preset1 = new preset(); + $preset1->set('name', $preset1name); + $preset1->set('category', implode(',', [$category->id])); + $preset1->save(); + + preset_created::create([ + 'context' => system::instance(), + 'other' => [ + 'presetid' => $preset1->get('id'), + 'presetname' => $preset1->get('name'), + 'categories' => $preset1->get('category'), + 'oldcategories' => null, + 'courses' => $preset1->get('course'), + 'oldcourses' => null, + 'tags' => $preset1->get('tag'), + 'oldtags' => null + ] + ])->trigger(); + + $this->execute_tasks(); + + $preset1cohort = $DB->get_record('cohort', ['name' => $preset1name]); + $this->assertNotEmpty($preset1cohort); + + $course1 = $this->getDataGenerator()->create_course(['category' => $category->id]); + $course2 = $this->getDataGenerator()->create_course(['category' => $category->id]); + $course3 = $this->getDataGenerator()->create_course(); + + $this->execute_tasks(); + + $this->assertNotEmpty( + $DB->get_record('enrol', ['courseid' => $course1->id, 'enrol' => 'cohort', 'customint1' => $preset1cohort->id]) + ); + + $this->assertNotEmpty( + $DB->get_record('enrol', ['courseid' => $course2->id, 'enrol' => 'cohort', 'customint1' => $preset1cohort->id]) + ); + + $this->assertEmpty( + $DB->get_record('enrol', ['courseid' => $course3->id, 'enrol' => 'cohort', 'customint1' => $preset1cohort->id]) + ); + } }