diff --git a/.upgradenotes/MDL-82168-2024062815382042.yml b/.upgradenotes/MDL-82168-2024062815382042.yml new file mode 100644 index 00000000000..43a78574eee --- /dev/null +++ b/.upgradenotes/MDL-82168-2024062815382042.yml @@ -0,0 +1,5 @@ +issueNumber: MDL-82168 +notes: + core_badges: + - message: New webservices enable_badges and disable_badges have been added. + type: improved diff --git a/badges/classes/external/disable_badges.php b/badges/classes/external/disable_badges.php new file mode 100644 index 00000000000..e3c2bb7f27b --- /dev/null +++ b/badges/classes/external/disable_badges.php @@ -0,0 +1,126 @@ +. + +namespace core_badges\external; + +use core_badges\badge; +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_multiple_structure; +use core_external\external_value; +use core_external\external_warnings; +use moodle_exception; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/badgeslib.php'); + +/** + * External service to disable badges. + * + * @package core_badges + * @category external + * @copyright 2024 Sara Arjona + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 4.5 + */ +class disable_badges extends external_api { + + /** + * Describes the parameters. + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'badgeids' => new external_multiple_structure( + new external_value(PARAM_TEXT, 'The badge identifiers to update', VALUE_REQUIRED), + ), + ]); + } + + /** + * Disable the given badges. + * + * @param array $badgeids List of badge identifiers to disable. + * @return array List of results and warnings. + */ + public static function execute(array $badgeids): array { + global $CFG, $DB; + + $warnings = []; + + [ + 'badgeids' => $badgeids, + ] = self::validate_parameters(self::execute_parameters(), [ + 'badgeids' => $badgeids, + ]); + + // Check if badges are enabled. + if (empty($CFG->enablebadges)) { + throw new moodle_exception('badgesdisabled', 'badges'); + } + + foreach ($badgeids as $badgeid) { + $badge = new badge($badgeid); + + // Check capabilities. + $context = $badge->get_context(); + self::validate_context($context); + if (!has_capability('moodle/badges:configurecriteria', $context)) { + $warnings[] = [ + 'item' => $badgeid, + 'warningcode' => 'nopermissions', + 'message' => get_string('nopermissions', 'error'), + ]; + continue; + } + + // Check if course badges are enabled. + if (empty($CFG->badges_allowcoursebadges) && ($badge->type == BADGE_TYPE_COURSE)) { + $warnings[] = [ + 'item' => $badgeid, + 'warningcode' => 'coursebadgesdisabled', + 'message' => get_string('coursebadgesdisabled', 'badges'), + ]; + continue; + } + + $status = ($badge->status == BADGE_STATUS_ACTIVE) ? BADGE_STATUS_INACTIVE : BADGE_STATUS_INACTIVE_LOCKED; + // Deactivate the badge. + $badge->set_status($status); + } + + return [ + 'result' => empty($warnings), + 'warnings' => $warnings, + ]; + } + + /** + * Describe the return structure of the external service. + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'result' => new external_value(PARAM_BOOL, 'The processing result'), + 'warnings' => new external_warnings(), + ]); + } +} diff --git a/badges/classes/external/enable_badges.php b/badges/classes/external/enable_badges.php new file mode 100644 index 00000000000..ddf9f9be3c2 --- /dev/null +++ b/badges/classes/external/enable_badges.php @@ -0,0 +1,183 @@ +. + +namespace core_badges\external; + +use core_badges\badge; +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_single_structure; +use core_external\external_multiple_structure; +use core_external\external_value; +use core_external\external_warnings; +use moodle_exception; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/badgeslib.php'); + +/** + * External service to enable badges. + * + * @package core_badges + * @category external + * @copyright 2024 Sara Arjona + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 4.5 + */ +class enable_badges extends external_api { + + /** + * Describes the parameters. + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'badgeids' => new external_multiple_structure( + new external_value(PARAM_TEXT, 'The badge identifiers to update', VALUE_REQUIRED), + ), + ]); + } + + /** + * Enable the given badges. + * + * @param array $badgeids List of badge identifiers to enable. + * @return array The number of awarded users for each badge or 'cron' when there are more than 1000 users. + */ + public static function execute(array $badgeids): array { + global $CFG, $DB; + + $result = []; + $warnings = []; + + [ + 'badgeids' => $badgeids, + ] = self::validate_parameters(self::execute_parameters(), [ + 'badgeids' => $badgeids, + ]); + + // Check if badges are enabled. + if (empty($CFG->enablebadges)) { + throw new moodle_exception('badgesdisabled', 'badges'); + } + + foreach ($badgeids as $badgeid) { + $badge = new badge($badgeid); + + // Check capabilities. + $context = $badge->get_context(); + self::validate_context($context); + if (!has_capability('moodle/badges:configurecriteria', $context)) { + $warnings[] = [ + 'item' => $badgeid, + 'warningcode' => 'nopermissions', + 'message' => get_string('nopermissions', 'error'), + ]; + continue; + } + + // Check if course badges are enabled. + if (empty($CFG->badges_allowcoursebadges) && ($badge->type == BADGE_TYPE_COURSE)) { + $warnings[] = [ + 'item' => $badgeid, + 'warningcode' => 'coursebadgesdisabled', + 'message' => get_string('coursebadgesdisabled', 'badges'), + ]; + continue; + } + + // Check if the badge has criteria. + if (!$badge->has_criteria()) { + $warnings[] = [ + 'item' => $badgeid, + 'warningcode' => 'nocriteria', + 'message' => get_string('nocriteria', 'badges'), + ]; + continue; + } + + // Activate the badge. + $status = ($badge->status == BADGE_STATUS_INACTIVE) ? BADGE_STATUS_ACTIVE : BADGE_STATUS_ACTIVE_LOCKED; + $badge->set_status($status); + $awards = self::review_criteria($badge); + + $result[] = [ + 'badgeid' => $badgeid, + 'awards' => $awards, + ]; + } + + return [ + 'result' => $result, + 'warnings' => $warnings, + ]; + } + + /** + * Describe the return structure of the external service. + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'result' => new external_multiple_structure( + new external_single_structure([ + 'badgeid' => new external_value(PARAM_INT, 'The badge identifier'), + 'awards' => new external_value(PARAM_ALPHANUM, 'The processing result'), + ]), + ), + 'warnings' => new external_warnings(), + ]); + } + + /** + * Review the criteria of the badge. + * + * @param \core_badges\badge $badge The badge to review the criteria. + * @return string The number of awarded users or 'cron' when there are more than 1000 users. + */ + private static function review_criteria(badge $badge): string { + global $CFG, $DB; + + if ($badge->type == BADGE_TYPE_SITE) { + // Review on cron if there are more than 1000 users who can earn a site-level badge. + $sql = 'SELECT COUNT(u.id) as num + FROM {user} u + LEFT JOIN {badge_issued} bi + ON u.id = bi.userid AND bi.badgeid = :badgeid + WHERE bi.badgeid IS NULL AND u.id != :guestid AND u.deleted = 0'; + $toearn = $DB->get_record_sql( + $sql, + [ + 'badgeid' => $badge->id, + 'guestid' => $CFG->siteguest, + ], + ); + if ($toearn->num < 1000) { + $awards = $badge->review_all_criteria(); + } else { + $awards = 'cron'; + } + } else { + $awards = $badge->review_all_criteria(); + } + + return $awards; + } +} diff --git a/badges/renderer.php b/badges/renderer.php index 8740f66f0f3..94154370c1c 100644 --- a/badges/renderer.php +++ b/badges/renderer.php @@ -573,17 +573,34 @@ class core_badges_renderer extends plugin_renderer_base { $message = $status . $action; } else { + $this->page->requires->js_call_amd('core_badges/actions', 'init'); + $status = get_string('statusmessage_' . $badge->status, 'badges'); if ($badge->is_active()) { - $action = $this->output->single_button(new moodle_url('/badges/action.php', - array('id' => $badge->id, 'lock' => 1, 'sesskey' => sesskey(), - 'return' => $this->page->url->out_as_local_url(false))), - get_string('deactivate', 'badges'), 'POST', array('class' => 'activatebadge')); + $action = $this->output->single_button( + new moodle_url('#'), + get_string('deactivate', 'badges'), + 'POST', + [ + 'class' => 'activatebadge', + 'data-action' => 'disablebadge', + 'data-badgeid' => $badge->id, + 'data-badgename' => $badge->name, + 'data-courseid' => $badge->courseid, + ], + ); } else { - $action = $this->output->single_button(new moodle_url('/badges/action.php', - array('id' => $badge->id, 'activate' => 1, 'sesskey' => sesskey(), - 'return' => $this->page->url->out_as_local_url(false))), - get_string('activate', 'badges'), 'POST', array('class' => 'activatebadge')); + $action = $this->output->single_button( + new moodle_url('#'), + get_string('activate', 'badges'), + 'POST', + [ + 'class' => 'activatebadge', + 'data-action' => 'enablebadge', + 'data-badgeid' => $badge->id, + 'data-badgename' => $badge->name, + 'data-courseid' => $badge->courseid, + ]); } $message = $status . $this->output->help_icon('status', 'badges') . $action; diff --git a/badges/tests/external/disable_badges_test.php b/badges/tests/external/disable_badges_test.php new file mode 100644 index 00000000000..397dc6f2a0c --- /dev/null +++ b/badges/tests/external/disable_badges_test.php @@ -0,0 +1,224 @@ +. + +namespace core_badges\external; + +use core_badges_generator; +use core_badges\badge; +use externallib_advanced_testcase; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); +require_once($CFG->libdir . '/badgeslib.php'); + +/** + * Tests for external function disable_badges. + * + * @package core_badges + * @copyright 2024 Sara Arjona + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 4.5 + * @covers \core_badges\external\disable_badges + */ +final class disable_badges_test extends externallib_advanced_testcase { + + /** + * Test execute method. + */ + public function test_execute(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $data = $this->prepare_test_data(); + + $this->assertTrue($data['sitebadge']->is_active()); + $this->assertTrue($data['coursebadge']->is_active()); + + $result = disable_badges::execute([ + $data['sitebadge']->id, + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(disable_badges::execute_returns(), $result); + $this->assertTrue($result['result']); + $this->assertEmpty($result['warnings']); + + $sitebadge = new badge($data['sitebadge']->id); + $coursebadge = new badge($data['coursebadge']->id); + $this->assertFalse($sitebadge->is_active()); + $this->assertFalse($coursebadge->is_active()); + } + + /** + * Test execute method when badges are disabled. + */ + public function test_execute_badgesdisabled(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Disable course badges. + set_config('enablebadges', 0); + + $data = $this->prepare_test_data(); + + $this->expectException(\moodle_exception::class); + $this->expectExceptionMessage(get_string('badgesdisabled', 'core_badges')); + $result = disable_badges::execute([ + $data['sitebadge']->id, + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(disable_badges::execute_returns(), $result); + } + + /** + * Test execute method when course badges are disabled. + */ + public function test_execute_coursebadgesdisabled(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Disable course badges. + set_config('badges_allowcoursebadges', 0); + + $data = $this->prepare_test_data(); + + $this->assertTrue($data['sitebadge']->is_active()); + $this->assertTrue($data['coursebadge']->is_active()); + + $result = disable_badges::execute([ + $data['sitebadge']->id, + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(disable_badges::execute_returns(), $result); + // Course badge can't be disabled because course badges are disabled. + $this->assertFalse($result['result']); + $this->assertNotEmpty($result['warnings']); + $this->assertEquals($data['coursebadge']->id, $result['warnings'][0]['item']); + $this->assertEquals('coursebadgesdisabled', $result['warnings'][0]['warningcode']); + + $sitebadge = new badge($data['sitebadge']->id); + $coursebadge = new badge($data['coursebadge']->id); + $this->assertFalse($sitebadge->is_active()); + $this->assertTrue($coursebadge->is_active()); + } + + /** + * Test execute method when the user doesn't have the capability to disable badges. + */ + public function test_execute_without_capability(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $data = $this->prepare_test_data(); + $this->setUser($data['teacher']); + + $this->assertTrue($data['sitebadge']->is_active()); + $this->assertTrue($data['coursebadge']->is_active()); + + $result = disable_badges::execute([ + $data['sitebadge']->id, + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(disable_badges::execute_returns(), $result); + // Teacher doesn't have capability to disable site badges. + $this->assertFalse($result['result']); + $this->assertNotEmpty($result['warnings']); + $this->assertEquals($data['sitebadge']->id, $result['warnings'][0]['item']); + $this->assertEquals('nopermissions', $result['warnings'][0]['warningcode']); + + $sitebadge = new badge($data['sitebadge']->id); + $coursebadge = new badge($data['coursebadge']->id); + $this->assertTrue($sitebadge->is_active()); + $this->assertFalse($coursebadge->is_active()); + } + + /** + * Test execute method when the badge is already disabled. + */ + public function test_execute_disabledisabledbadge(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $data = $this->prepare_test_data(); + + $this->assertTrue($data['coursebadge']->is_active()); + $data['coursebadge']->set_status(BADGE_STATUS_INACTIVE); + $this->assertFalse($data['coursebadge']->is_active()); + + $result = disable_badges::execute([ + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(disable_badges::execute_returns(), $result); + // Disabled badges can be disabled again. + $this->assertTrue($result['result']); + $this->assertEmpty($result['warnings']); + + $coursebadge = new badge($data['coursebadge']->id); + $this->assertFalse($coursebadge->is_active()); + } + + /** + * Prepare the test, creating a few users and badges. + * + * @return array Test data. + */ + private function prepare_test_data(): array { + global $DB; + + // Setup test data. + $course = $this->getDataGenerator()->create_course(); + + // Create users and enrolments. + $student1 = $this->getDataGenerator()->create_and_enrol($course); + $student2 = $this->getDataGenerator()->create_and_enrol($course); + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); + + /** @var core_badges_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); + $sitebadge = $generator->create_badge([ + 'name' => 'Site badge', + 'description' => 'Site badge', + 'status' => BADGE_STATUS_ACTIVE, + ]); + $coursebadge = $generator->create_badge([ + 'name' => 'Course badge', + 'description' => 'Course badge', + 'type' => BADGE_TYPE_COURSE, + 'courseid' => $course->id, + 'status' => BADGE_STATUS_ACTIVE, + ]); + + // Create criteria for manually awarding by role. + $managerrole = $DB->get_field('role', 'id', ['shortname' => 'manager']); + $generator->create_criteria(['badgeid' => $sitebadge->id, 'roleid' => $managerrole]); + $generator->create_criteria(['badgeid' => $coursebadge->id, 'roleid' => $managerrole]); + + // Issue badges to student1. + $sitebadge->issue($student1->id, true); + $coursebadge->issue($student1->id, true); + + return [ + 'course' => $course, + 'student1' => $student1, + 'student2' => $student2, + 'teacher' => $teacher, + 'sitebadge' => $sitebadge, + 'coursebadge' => $coursebadge, + ]; + } +} diff --git a/badges/tests/external/enable_badges_test.php b/badges/tests/external/enable_badges_test.php new file mode 100644 index 00000000000..891ac305bd6 --- /dev/null +++ b/badges/tests/external/enable_badges_test.php @@ -0,0 +1,312 @@ +. + +namespace core_badges\external; + +use core_badges_generator; +use core_badges\badge; +use externallib_advanced_testcase; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; + +require_once($CFG->dirroot . '/webservice/tests/helpers.php'); +require_once($CFG->libdir . '/badgeslib.php'); + +/** + * Tests for external function enable_badges. + * + * @package core_badges + * @copyright 2024 Sara Arjona + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since Moodle 4.5 + * @covers \core_badges\external\enable_badges + */ +final class enable_badges_test extends externallib_advanced_testcase { + + /** + * Test execute method. + */ + public function test_execute(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $data = $this->prepare_test_data(); + + $this->assertFalse($data['sitebadge']->is_active()); + $this->assertFalse($data['coursebadge']->is_active()); + + $result = enable_badges::execute([ + $data['sitebadge']->id, + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(enable_badges::execute_returns(), $result); + $this->assertEquals($data['sitebadge']->id, $result['result'][0]['badgeid']); + $this->assertEquals($data['coursebadge']->id, $result['result'][1]['badgeid']); + $this->assertEmpty($result['warnings']); + + $sitebadge = new badge($data['sitebadge']->id); + $coursebadge = new badge($data['coursebadge']->id); + $this->assertTrue($sitebadge->is_active()); + $this->assertTrue($coursebadge->is_active()); + } + + /** + * Test execute method when badges are disabled. + */ + public function test_execute_badgesdisabled(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Disable course badges. + set_config('enablebadges', 0); + + $data = $this->prepare_test_data(); + + $this->expectException(\moodle_exception::class); + $this->expectExceptionMessage(get_string('badgesdisabled', 'core_badges')); + $result = enable_badges::execute([ + $data['sitebadge']->id, + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(enable_badges::execute_returns(), $result); + } + + /** + * Test execute method when course badges are disabled. + */ + public function test_execute_coursebadgesdisabled(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Disable course badges. + set_config('badges_allowcoursebadges', 0); + + $data = $this->prepare_test_data(); + + $this->assertFalse($data['sitebadge']->is_active()); + $this->assertFalse($data['coursebadge']->is_active()); + + $result = enable_badges::execute([ + $data['sitebadge']->id, + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(enable_badges::execute_returns(), $result); + $this->assertEquals($data['sitebadge']->id, $result['result'][0]['badgeid']); + // Course badge can't be enabled because course badges are disabled. + $this->assertNotEmpty($result['warnings']); + $this->assertEquals($data['coursebadge']->id, $result['warnings'][0]['item']); + $this->assertEquals('coursebadgesdisabled', $result['warnings'][0]['warningcode']); + + $sitebadge = new badge($data['sitebadge']->id); + $coursebadge = new badge($data['coursebadge']->id); + $this->assertTrue($sitebadge->is_active()); + $this->assertFalse($coursebadge->is_active()); + } + + /** + * Test execute method when badge doesn't have criteria. + */ + public function test_execute_badgewithoutcriteria(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $data = $this->prepare_test_data(); + + $this->assertFalse($data['nocriteriabadge']->has_criteria()); + $this->assertFalse($data['nocriteriabadge']->is_active()); + + $result = enable_badges::execute([ + $data['nocriteriabadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(enable_badges::execute_returns(), $result); + $this->assertEmpty($result['result']); + // Badges without criteria can't be enabled. + $this->assertNotEmpty($result['warnings']); + $this->assertEquals($data['nocriteriabadge']->id, $result['warnings'][0]['item']); + $this->assertEquals('nocriteria', $result['warnings'][0]['warningcode']); + + $nocriteriabadge = new badge($data['nocriteriabadge']->id); + $this->assertFalse($nocriteriabadge->is_active()); + } + + /** + * Test execute method when the badge is already enabled. + */ + public function test_execute_enableenabledbadge(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $data = $this->prepare_test_data(); + + $this->assertFalse($data['coursebadge']->is_active()); + $data['coursebadge']->set_status(BADGE_STATUS_ACTIVE); + $this->assertTrue($data['coursebadge']->is_active()); + + $result = enable_badges::execute([ + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(enable_badges::execute_returns(), $result); + $this->assertEquals($data['coursebadge']->id, $result['result'][0]['badgeid']); + // Enabled badges can be enabled again. + $this->assertEmpty($result['warnings']); + + $coursebadge = new badge($data['coursebadge']->id); + $this->assertTrue($coursebadge->is_active()); + } + + /** + * Test execute method when badgeid doesn't exist. + */ + public function test_execute_wrongbadgeid(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $badgeid = 1234; + $this->expectException(\moodle_exception::class); + $this->expectExceptionMessage(get_string('error:nosuchbadge', 'core_badges', $badgeid)); + $result = enable_badges::execute([ + $badgeid, + ]); + $result = \core_external\external_api::clean_returnvalue(enable_badges::execute_returns(), $result); + } + + /** + * Test execute method when the user doesn't have the capability to enable badges. + */ + public function test_execute_without_capability(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $data = $this->prepare_test_data(); + $this->setUser($data['teacher']); + + $this->assertFalse($data['sitebadge']->is_active()); + $this->assertFalse($data['coursebadge']->is_active()); + + $result = enable_badges::execute([ + $data['sitebadge']->id, + $data['coursebadge']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(enable_badges::execute_returns(), $result); + $this->assertEquals($data['coursebadge']->id, $result['result'][0]['badgeid']); + // Teacher doesn't have capability to enable site badges. + $this->assertNotEmpty($result['warnings']); + $this->assertEquals($data['sitebadge']->id, $result['warnings'][0]['item']); + $this->assertEquals('nopermissions', $result['warnings'][0]['warningcode']); + + $sitebadge = new badge($data['sitebadge']->id); + $coursebadge = new badge($data['coursebadge']->id); + $this->assertFalse($sitebadge->is_active()); + $this->assertTrue($coursebadge->is_active()); + } + + /** + * Test execute method when the badge has the same name as another enabled badge. + */ + public function test_execute_duplicatedname(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + $data = $this->prepare_test_data(); + + $this->assertFalse($data['coursebadge']->is_active()); + $this->assertFalse($data['coursebadge2']->is_active()); + + $result = enable_badges::execute([ + $data['coursebadge']->id, + $data['coursebadge2']->id, + ]); + $result = \core_external\external_api::clean_returnvalue(enable_badges::execute_returns(), $result); + // Badge can be enabled because badges with the same name are now allowed. + $this->assertEquals($data['coursebadge']->id, $result['result'][0]['badgeid']); + $this->assertEquals($data['coursebadge2']->id, $result['result'][1]['badgeid']); + $this->assertEmpty($result['warnings']); + + $coursebadge = new badge($data['coursebadge']->id); + $coursebadge2 = new badge($data['coursebadge2']->id); + $this->assertTrue($coursebadge->is_active()); + $this->assertTrue($coursebadge2->is_active()); + } + + /** + * Prepare the test, creating a few users and badges. + * + * @return array Test data. + */ + private function prepare_test_data(): array { + global $DB; + + // Setup test data. + $course = $this->getDataGenerator()->create_course(); + + // Create users and enrolments. + $student1 = $this->getDataGenerator()->create_and_enrol($course); + $student2 = $this->getDataGenerator()->create_and_enrol($course); + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); + + /** @var core_badges_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_badges'); + $sitebadge = $generator->create_badge([ + 'name' => 'Site badge', + 'description' => 'Site badge', + 'status' => BADGE_STATUS_INACTIVE, + ]); + $coursebadge = $generator->create_badge([ + 'name' => 'Course badge', + 'description' => 'Course badge', + 'type' => BADGE_TYPE_COURSE, + 'courseid' => $course->id, + 'status' => BADGE_STATUS_INACTIVE, + ]); + $coursebadge2 = $generator->create_badge([ + 'name' => 'Course badge', + 'description' => 'Course badge', + 'type' => BADGE_TYPE_COURSE, + 'courseid' => $course->id, + 'status' => BADGE_STATUS_INACTIVE, + ]); + $nocriteriabadge = $generator->create_badge([ + 'name' => 'No criteria badge', + 'description' => 'No criteria badge', + 'type' => BADGE_TYPE_COURSE, + 'courseid' => $course->id, + 'status' => BADGE_STATUS_INACTIVE, + ]); + + // Create criteria for manually awarding by role. + $managerrole = $DB->get_field('role', 'id', ['shortname' => 'manager']); + $generator->create_criteria(['badgeid' => $sitebadge->id, 'roleid' => $managerrole]); + $generator->create_criteria(['badgeid' => $coursebadge->id, 'roleid' => $managerrole]); + $generator->create_criteria(['badgeid' => $coursebadge2->id, 'roleid' => $managerrole]); + + // Issue badges to student1. + $sitebadge->issue($student1->id, true); + $coursebadge->issue($student1->id, true); + + return [ + 'course' => $course, + 'student1' => $student1, + 'student2' => $student2, + 'teacher' => $teacher, + 'sitebadge' => $sitebadge, + 'coursebadge' => $coursebadge, + 'coursebadge2' => $coursebadge2, + 'nocriteriabadge' => $nocriteriabadge, + ]; + } +} diff --git a/lib/db/services.php b/lib/db/services.php index feb0ce346c4..27622ce9b9f 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -119,6 +119,18 @@ $functions = array( 'ajax' => true, 'loginrequired' => true, ), + 'core_badges_disable_badges' => [ + 'classname' => 'core_badges\external\disable_badges', + 'description' => 'Disable badges', + 'type' => 'write', + 'ajax' => true, + ], + 'core_badges_enable_badges' => [ + 'classname' => 'core_badges\external\enable_badges', + 'description' => 'Enable badges', + 'type' => 'write', + 'ajax' => true, + ], 'core_badges_get_user_badges' => array( 'classname' => 'core_badges_external', 'methodname' => 'get_user_badges', diff --git a/version.php b/version.php index 95c817a0cc3..544e8680d14 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2024070500.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2024070500.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.5dev (Build: 20240705)'; // Human-friendly version name