diff --git a/.github/workflows/pipelines.yml b/.github/workflows/pipelines.yml new file mode 100644 index 0000000..e4a71f5 --- /dev/null +++ b/.github/workflows/pipelines.yml @@ -0,0 +1,74 @@ +name: Moodle plugin CI +on: [push, pull_request] + +jobs: + test: + runs-on: 'ubuntu-latest' + strategy: + fail-fast: false + matrix: + include: + - php: '8.0' + moodle-branch: 'MOODLE_401_STABLE' + database: 'pgsql' + - php: '8.0' + moodle-branch: 'MOODLE_401_STABLE' + database: 'mariadb' + + services: + postgres: + image: postgres:13 + env: + POSTGRES_USER: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 3 + ports: + - 5432:5432 + + mariadb: + image: mariadb:10 + env: + MYSQL_USER: 'root' + MYSQL_ALLOW_EMPTY_PASSWORD: "true" + MYSQL_CHARACTER_SET_SERVER: "utf8mb4" + MYSQL_COLLATION_SERVER: "utf8mb4_unicode_ci" + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3 + + steps: + - name: Check out out repository code + uses: actions/checkout@v3 + with: + path: plugin + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.extensions }} + ini-values: max_input_vars=5000 + coverage: none + + - name: Initialise moodle-plugin-ci + run: | + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 + echo $(cd ci/bin; pwd) >> $GITHUB_PATH + echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH + sudo locale-gen en_AU.UTF-8 + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV + + - name: Install moodle-plugin-ci + run: | + moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 + env: + DB: ${{ matrix.database }} + MOODLE_BRANCH: ${{ matrix.moodle-branch }} + + - name: PHPUnit tests + if: ${{ always() }} + run: moodle-plugin-ci phpunit --fail-on-warning \ No newline at end of file diff --git a/MLC-CHANGELOG.md b/MLC-CHANGELOG.md deleted file mode 100644 index 4fee10c..0000000 --- a/MLC-CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -# Changelog -All notable changes to this project by My Learning Consultants will be documented in this file. Commit hash is the original local_autogroup commit this code is based on. - -## Commit hash -6b2a10cc130dcd77a96714e5e4b8620f12e610da - -## [CRU-119 - 2021-03-12] -### Changed -- Added option for profile field to contain multiple groups -### Unit test -- The multiple_profile_values_test in local_autogroup_lib_testcase in local/autogroup/tests/lib_test.php -### Manual test instructions -- Create a profile text field -- On Site Admin -> Plugins -> Local Plugins -1. Check Enabled -2. Set Group by to be the new profile field -3. Under Event Triggers, enable user profiles -- Create a user -- Create a course -- Enroll the user in the course -- Add a value to the user's profile field that contains comma separated values (eg Test 1, Test 2, Test 3) -- Confirm the user is added to multiple groups based on the values in the field diff --git a/classes/domain/group.php b/classes/domain/group.php index e8d95a3..8949966 100644 --- a/classes/domain/group.php +++ b/classes/domain/group.php @@ -178,7 +178,7 @@ public function ensure_user_is_member($userid) { } // User was not found as a member so will now make member a user. - \groups_add_member($this->as_object(), $userid, 'local_autogroup'); + \groups_add_member($this->as_object()->id, $userid, 'local_autogroup'); return true; } diff --git a/classes/event_handler.php b/classes/event_handler.php index 2021fd7..0a1656a 100644 --- a/classes/event_handler.php +++ b/classes/event_handler.php @@ -30,6 +30,7 @@ use \core\event; use local_autogroup\domain\group; +use local_autogroup\task\process_event; /** * Class event_handler @@ -43,11 +44,23 @@ * @package local_autogroup */ class event_handler { + /** - * @param event\user_enrolment_created $event + * @var array + */ + const FUNCTION_MAPPING = [ + 'group_deleted' => 'group_change', + 'group_updated' => 'group_change', + 'role_assigned' => 'role_change', + 'role_unassigned' => 'role_change', + 'role_deleted' => 'role_deleted', + ]; + + /** + * @param object $event * @return mixed */ - public static function user_enrolment_created(event\user_enrolment_created $event) { + public static function user_enrolment_created(object $event) { $pluginconfig = get_config('local_autogroup'); if (!$pluginconfig->listenforrolechanges) { return false; @@ -63,12 +76,12 @@ public static function user_enrolment_created(event\user_enrolment_created $even } /** - * @param event\group_member_added $event + * @param object $event * @return bool * @throws \Exception * @throws \dml_exception */ - public static function group_member_added(event\group_member_added $event) { + public static function group_member_added(object $event) { if (self::triggered_by_autogroup($event)) { return false; } @@ -99,12 +112,12 @@ public static function group_member_added(event\group_member_added $event) { } /** - * @param event\group_member_removed $event + * @param object $event * @return bool * @throws \Exception * @throws \dml_exception */ - public static function group_member_removed(event\group_member_removed $event) { + public static function group_member_removed(object $event) { if (self::triggered_by_autogroup($event)) { return false; } @@ -135,10 +148,10 @@ public static function group_member_removed(event\group_member_removed $event) { } /** - * @param event\user_updated $event + * @param object $event * @return mixed */ - public static function user_updated(event\user_updated $event) { + public static function user_updated(object $event) { $pluginconfig = get_config('local_autogroup'); if (!$pluginconfig->listenforuserprofilechanges) { return false; @@ -153,12 +166,12 @@ public static function user_updated(event\user_updated $event) { } /** - * @param event\base $event + * @param object $event * @return bool * @throws \Exception * @throws \dml_exception */ - public static function group_created(event\base $event) { + public static function group_created(object $event) { if (self::triggered_by_autogroup($event)) { return false; } @@ -177,12 +190,12 @@ public static function group_created(event\base $event) { } /** - * @param event\base $event + * @param object $event * @return bool * @throws \Exception * @throws \dml_exception */ - public static function group_change(event\base $event) { + public static function group_change(object $event) { if (self::triggered_by_autogroup($event)) { return false; } @@ -212,10 +225,10 @@ public static function group_change(event\base $event) { } /** - * @param event\base $event + * @param object $event * @return mixed */ - public static function role_change(event\base $event) { + public static function role_change(object $event) { $pluginconfig = get_config('local_autogroup'); if (!$pluginconfig->listenforrolechanges) { return false; @@ -230,10 +243,10 @@ public static function role_change(event\base $event) { } /** - * @param event\role_deleted $event + * @param object $event * @return mixed */ - public static function role_deleted(event\role_deleted $event) { + public static function role_deleted(object $event) { global $DB; $DB->delete_records('local_autogroup_roles', ['roleid' => $event->objectid]); @@ -243,10 +256,10 @@ public static function role_deleted(event\role_deleted $event) { } /** - * @param event\course_created $event + * @param object $event * @return mixed */ - public static function course_created(event\course_created $event) { + public static function course_created(object $event) { $config = get_config('local_autogroup'); if (!$config->addtonewcourses) { return false; @@ -260,10 +273,10 @@ public static function course_created(event\course_created $event) { } /** - * @param event\course_restored $event + * @param object $event * @return mixed */ - public static function course_restored(event\course_restored $event) { + public static function course_restored(object $event) { $config = get_config('local_autogroup'); if (!$config->addtorestoredcourses) { return false; @@ -277,10 +290,10 @@ public static function course_restored(event\course_restored $event) { } /** - * @param \totara_core\event\position_updated $event + * @param object $event * @return bool */ - public static function position_updated(\totara_core\event\position_updated $event) { + public static function position_updated(object $event) { $pluginconfig = get_config('local_autogroup'); if (!$pluginconfig->listenforuserpositionchanges) { return false; @@ -298,21 +311,39 @@ public static function position_updated(\totara_core\event\position_updated $eve * Checks the data of an event to see whether it was initiated * by the local_autogroup component * - * @param event\base $event + * @param object $data * @return bool */ - private static function triggered_by_autogroup(\core\event\base $event) { - $data = $event->get_data(); - if ( - isset($data['other']) && - is_array($data['other']) && - isset($data['other']['component']) && - is_string($data['other']['component']) && - strstr($data['other']['component'], 'autogroup') - ) { - return true; + private static function triggered_by_autogroup(object $data) { + return !empty($data->other->component) && strstr($data->other->component, 'autogroup'); + } + + /** + * Create ad hoc task for event. + * + * @param event\base $event + * @return void + */ + public static function create_adhoc_task(\core\event\base $event) { + + $task = new process_event(); + $task->set_custom_data($event->get_data()); + $queryadhoc = get_config('local_autogroup', 'adhoceventhandler'); + if (!empty($queryadhoc)) { + \core\task\manager::queue_adhoc_task($task); + } else { + $task->execute(); } + } - return false; + /** + * @param object $event + * @return mixed + */ + public static function process_event(object $event) { + $explodename = explode('\\', $event->eventname); + $eventname = end($explodename); + $functionname = self::FUNCTION_MAPPING[$eventname] ?? $eventname; + self::$functionname($event); } } diff --git a/classes/sort_module/user_info_field.php b/classes/sort_module/user_info_field.php index 9cb81de..f6f7a63 100644 --- a/classes/sort_module/user_info_field.php +++ b/classes/sort_module/user_info_field.php @@ -96,23 +96,10 @@ public function eligible_groups_for_user(stdClass $user) { $field = $this->field; $data = $DB->get_record('user_info_data', ['fieldid' => $field, 'userid' => $user->id]); - if ($data && !empty($data->data)){ - // Check if the field has comma separated values. - if ($fieldvalues = explode(',', $data->data)) { - $fields = []; - foreach ($fieldvalues as $fieldvalue) { - if ($fieldvalue = trim($fieldvalue)) { - $fields[] = $fieldvalue; - } - } - return $fields; - } else { - return [$data->data]; - } - } - else { - return []; + if ($data && !empty($data->data)) { + return [$data->data]; } + return []; } /** diff --git a/classes/task/process_event.php b/classes/task/process_event.php new file mode 100644 index 0000000..88847a0 --- /dev/null +++ b/classes/task/process_event.php @@ -0,0 +1,48 @@ +. + +/** + * Task that processes an event. + * + * @package local_autogroup + * @author Oscar Nadjar + * @copyright Moodle US + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_autogroup\task; +use local_autogroup\event_handler; + +defined('MOODLE_INTERNAL') || die(); + +class process_event extends \core\task\adhoc_task { + + /** + * Get task name + */ + public function get_name() { + return get_string('process_event', 'local_autogroup'); + } + + /** + * Execute task + */ + public function execute() { + $event = (object) $this->get_custom_data(); + + event_handler::process_event($event); + } +} \ No newline at end of file diff --git a/classes/usecase/verify_course_group_membership.php b/classes/usecase/verify_course_group_membership.php index c5ceacd..4495356 100644 --- a/classes/usecase/verify_course_group_membership.php +++ b/classes/usecase/verify_course_group_membership.php @@ -67,7 +67,7 @@ public function invoke() { return false; } - set_time_limit(500); + !PHPUNIT_TEST ? set_time_limit(500) : null; return $this->course->verify_all_group_membership($this->db); } } diff --git a/db/events.php b/db/events.php index 4fa33cf..7f42390 100644 --- a/db/events.php +++ b/db/events.php @@ -32,7 +32,7 @@ [ 'eventname' => '\core\event\user_enrolment_created', - 'callback' => '\local_autogroup\event_handler::user_enrolment_created', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -40,7 +40,7 @@ [ 'eventname' => 'core\event\group_member_added', - 'callback' => '\local_autogroup\event_handler::group_member_added', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -48,7 +48,7 @@ [ 'eventname' => '\core\event\group_member_removed', - 'callback' => '\local_autogroup\event_handler::group_member_removed', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -56,7 +56,7 @@ [ 'eventname' => '\core\event\user_updated', - 'callback' => '\local_autogroup\event_handler::user_updated', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -64,7 +64,7 @@ [ 'eventname' => '\core\event\group_created', - 'callback' => '\local_autogroup\event_handler::group_created', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -72,7 +72,7 @@ [ 'eventname' => '\core\event\group_deleted', - 'callback' => '\local_autogroup\event_handler::group_change', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -80,7 +80,7 @@ [ 'eventname' => '\core\event\group_updated', - 'callback' => '\local_autogroup\event_handler::group_change', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -88,7 +88,7 @@ [ 'eventname' => '\core\event\role_assigned', - 'callback' => '\local_autogroup\event_handler::role_change', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -96,7 +96,7 @@ [ 'eventname' => '\core\event\role_unassigned', - 'callback' => '\local_autogroup\event_handler::role_change', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -104,7 +104,7 @@ [ 'eventname' => '\core\event\role_deleted', - 'callback' => '\local_autogroup\event_handler::role_deleted', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -112,7 +112,7 @@ [ 'eventname' => '\core\event\course_created', - 'callback' => '\local_autogroup\event_handler::course_created', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -120,7 +120,7 @@ [ 'eventname' => '\core\event\course_restored', - 'callback' => '\local_autogroup\event_handler::course_restored', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, @@ -128,7 +128,7 @@ [ 'eventname' => '\totara_core\event\position_updated', - 'callback' => '\local_autogroup\event_handler::position_updated', + 'callback' => '\local_autogroup\event_handler::create_adhoc_task', 'includefile' => 'local/autogroup/classes/event_handler.php', 'internal' => true, 'priority' => 0, diff --git a/lang/en/local_autogroup.php b/lang/en/local_autogroup.php index 07f9515..f8fef34 100644 --- a/lang/en/local_autogroup.php +++ b/lang/en/local_autogroup.php @@ -71,6 +71,7 @@ $string['listenforgroupchanges_help'] = 'Listen for modifications to groups on a course. This can prevent issues caused by manual changes but will also slow down AutoGroup as it double checks its own actions. Previously configured as "Strict Enforcement".'; $string['listenforgroupmembership'] = 'Group Membership'; $string['listenforgroupmembership_help'] = 'Listen for changes to group membership. This can prevent issues caused by manual changes but will also slow down AutoGroup as it double checks its own actions. Previously configured as "Strict Enforcement".'; +$string['adhoceventhandler'] = 'Execute events actions trough ad-hoc tasks'; // Capabilities. $string['autogroup:managecourse'] = 'Manage autogroup settings on course'; @@ -93,3 +94,6 @@ $string['privacy:metadata:local_autogroup_manual:id'] = 'Record ID'; $string['privacy:metadata:local_autogroup_manual:userid'] = 'The ID of the user'; $string['privacy:metadata:local_autogroup_manual:groupid'] = 'The ID that relates to the group'; + +// Event task. +$string['process_event'] = 'Process event'; diff --git a/settings.php b/settings.php index 7633254..ceb9534 100644 --- a/settings.php +++ b/settings.php @@ -82,6 +82,15 @@ ) ); + $settings->add( + new admin_setting_configcheckbox( + 'local_autogroup/adhoceventhandler', + get_string('adhoceventhandler', 'local_autogroup'), + '', + 0 + ) + ); + // Default settings. $settings->add( new admin_setting_heading( diff --git a/tests/lib_test.php b/tests/local_autogroup_lib_test.php similarity index 59% rename from tests/lib_test.php rename to tests/local_autogroup_lib_test.php index b7a980a..c659b37 100644 --- a/tests/lib_test.php +++ b/tests/local_autogroup_lib_test.php @@ -29,21 +29,27 @@ require_once($CFG->dirroot.'/user/profile/lib.php'); require_once($CFG->dirroot.'/user/profile/definelib.php'); -require_once($CFG->dirroot.'/user/lib.php'); -class local_autogroup_lib_testcase extends advanced_testcase { +/** + * Test class for local_autogroup_lib. + * + * @package local_autogroup + * @author Oscar Nadjar + * @copyright Moodle US + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class local_autogroup_lib_test extends advanced_testcase { /** * Test setup. */ - public function setUp() { + public function setUp(): void { $this->resetAfterTest(); } /** - * A completed course with no equivalents being complete and triggering recompletion notifications - * and also expiration.. + * Test that an user is assigned to a group based on a profile field. */ - public function test_multiple_profile_values() { + public function test_autogroup_assign() { global $DB; $this->resetAfterTest(); @@ -54,24 +60,61 @@ public function test_multiple_profile_values() { set_config('enabled', true, 'local_autogroup'); set_config('addtonewcourses', true, 'local_autogroup'); set_config('filter', $fieldid, 'local_autogroup'); + set_config('adhoceventhandler', false, 'local_autogroup'); $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); $user = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student'); + profile_save_custom_fields($user->id, ['test' => 'Test 1']); + user_update_user($user, false, true); + + $groups = groups_get_all_groups($course->id, $user->id); + $this->assertCount(1, $groups); + $count = 1; + foreach ($groups as $group) { + $this->assertEquals('Test ' . $count, $group->name); + $count++; + } + } + + /** + * Same as above but with adhoc event handler. + */ + public function test_autogroup_adhoc_assign() { + global $DB; + $this->resetAfterTest(); + $this->setAdminUser(); + + $fieldid = $this->create_profile_field(); + + set_config('enabled', true, 'local_autogroup'); + set_config('addtonewcourses', true, 'local_autogroup'); + set_config('filter', $fieldid, 'local_autogroup'); + set_config('adhoceventhandler', true, 'local_autogroup'); + + $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); + $user = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student'); - profile_save_custom_fields($user->id, array('test' => 'Test 1, Test 2, Test 3')); + profile_save_custom_fields($user->id, ['test' => 'Test 1']); user_update_user($user, false, true); + $this->execute_adhoc(); $groups = groups_get_all_groups($course->id, $user->id); - $this->assertCount(3, $groups); - - for ($i = 0; $i < count($groups); $i++) { - $this->assertEquals($groups[$i], 'Test ' . $i); + $this->assertCount(1, $groups); + $count = 1; + foreach ($groups as $group) { + $this->assertEquals('Test ' . $count, $group->name); + $count++; } - } + /** + * Create a profile field. + * + * @return int + */ private function create_profile_field() { global $CFG, $DB, $PAGE; @@ -119,4 +162,18 @@ private function create_profile_field() { return $field->id; } + + /** + * Execute adhoc tasks. + */ + public function execute_adhoc() { + global $DB; + $tasks = $DB->get_records('task_adhoc', ['component' => 'local_autogroup']); + foreach ($tasks as $taskinfo) { + $task = new \local_autogroup\task\process_event(); + $task->set_custom_data(json_decode($taskinfo->customdata)); + $task->execute(); + $DB->delete_records('task_adhoc', ['id' => $taskinfo->id]); + } + } } \ No newline at end of file