diff --git a/.upgradenotes/MDL-83904-2025032008552774.yml b/.upgradenotes/MDL-83904-2025032008552774.yml new file mode 100644 index 00000000000..61f9a676d57 --- /dev/null +++ b/.upgradenotes/MDL-83904-2025032008552774.yml @@ -0,0 +1,10 @@ +issueNumber: MDL-83904 +notes: + core_badges: + - message: >- + The class core_badges_observer in badges/classes/observer.php has been moved to + core_badges\event\observer in badges/classes/event/observer.php. A compatibility + layer has been added to maintain backward compatibility, but direct use of the old + class name is now deprecated. If you've extended or directly used the old class, + you should update your code to use the new namespaced class. + type: improved diff --git a/badges/classes/event/observer.php b/badges/classes/event/observer.php new file mode 100644 index 00000000000..46fd04b1616 --- /dev/null +++ b/badges/classes/event/observer.php @@ -0,0 +1,268 @@ +. + +namespace core_badges\event; + +use core\event\badge_awarded; +use core\event\cohort_member_added; +use core\event\competency_evidence_created; +use core\event\course_completed; +use core\event\course_module_completion_updated; +use core\event\user_updated; +use core_badges\badge; + +/** + * Event observer for badges. + * + * @package core_badges + * @copyright 2013 Rajesh Taneja + * @author Rajesh Taneja + * @author Dai Nguyen Trong + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class observer { + /** + * Determines if badge processing is enabled. + * + * @return bool True if badges are enabled, false otherwise. + */ + private static function badges_enabled(): bool { + global $CFG; + return !empty($CFG->enablebadges); + } + + /** + * Common method to process badge issue after criteria is met. + * + * @param badge $badge The badge instance + * @param int $criteriatype The type of criteria being checked + * @param int $userid The user ID to check criteria against + */ + private static function process_criteria_completion(badge $badge, int $criteriatype, int $userid): void { + if (!$badge->is_active() || $badge->is_issued($userid)) { + return; + } + + if ($badge->criteria[$criteriatype]->review($userid)) { + $badge->criteria[$criteriatype]->mark_complete($userid); + + if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { + $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); + $badge->issue($userid); + } + } + } + + /** + * Triggered when 'course_module_completion_updated' event is triggered. + * + * @param course_module_completion_updated $event + */ + public static function course_module_criteria_review(course_module_completion_updated $event): void { + global $DB, $CFG; + + if (!self::badges_enabled()) { + return; + } + + require_once($CFG->dirroot.'/lib/badgeslib.php'); + + $eventdata = $event->get_record_snapshot('course_modules_completion', $event->objectid); + $userid = $event->relateduserid; + $mod = $event->contextinstanceid; + + // Only process completion states that indicate the module has been completed. + if (!in_array($eventdata->completionstate, [ + COMPLETION_COMPLETE, + COMPLETION_COMPLETE_PASS, + COMPLETION_COMPLETE_FAIL, + ])) { + return; + } + + // Find all badges that have this module in their criteria. + $params = [ + 'name' => 'module_' . $mod, + 'value' => $mod, + ]; + + if ($rs = $DB->get_records('badge_criteria_param', $params)) { + foreach ($rs as $r) { + $bid = $DB->get_field('badge_criteria', 'badgeid', ['id' => $r->critid], MUST_EXIST); + $badge = new badge($bid); + + self::process_criteria_completion($badge, BADGE_CRITERIA_TYPE_ACTIVITY, $userid); + } + } + } + + /** + * Triggered when '\core\event\competency_evidence_created' event is triggered. + * + * @param competency_evidence_created $event + */ + public static function competency_criteria_review(competency_evidence_created $event): void { + global $DB, $CFG; + + if (!self::badges_enabled()) { + return; + } + + require_once($CFG->dirroot.'/lib/badgeslib.php'); + + if (!get_config('core_competency', 'enabled')) { + return; + } + + $cid = $event->other['competencyid']; + $userid = $event->relateduserid; + $params = [ + 'name' => 'competency_' . $cid, + 'value' => $cid, + ]; + + if ($rs = $DB->get_records('badge_criteria_param', $params)) { + foreach ($rs as $r) { + $crit = $DB->get_record('badge_criteria', ['id' => $r->critid], 'badgeid, criteriatype', MUST_EXIST); + $badge = new badge($crit->badgeid); + + self::process_criteria_completion($badge, $crit->criteriatype, $userid); + } + } + } + + /** + * Triggered when 'course_completed' event is triggered. + * + * @param course_completed $event + */ + public static function course_criteria_review(course_completed $event): void { + global $DB, $CFG; + + if (!self::badges_enabled()) { + return; + } + + require_once($CFG->dirroot.'/lib/badgeslib.php'); + + $userid = $event->relateduserid; + $courseid = $event->courseid; + $params = [ + 'name' => 'course_' . $courseid, + 'value' => $courseid, + ]; + + if ($rs = $DB->get_records('badge_criteria_param', $params)) { + foreach ($rs as $r) { + $crit = $DB->get_record( + 'badge_criteria', + ['id' => $r->critid], + 'badgeid, criteriatype', + MUST_EXIST + ); + $badge = new badge($crit->badgeid); + + self::process_criteria_completion($badge, $crit->criteriatype, $userid); + } + } + } + + /** + * Triggered when 'badge_awarded' event happens. + * + * @param badge_awarded $event event generated when a badge is awarded. + */ + public static function badge_criteria_review(badge_awarded $event): void { + global $DB, $CFG; + + if (!self::badges_enabled()) { + return; + } + + require_once($CFG->dirroot.'/lib/badgeslib.php'); + + $userid = $event->relateduserid; + + if ($rs = $DB->get_records('badge_criteria', ['criteriatype' => BADGE_CRITERIA_TYPE_BADGE])) { + foreach ($rs as $r) { + $badge = new badge($r->badgeid); + self::process_criteria_completion($badge, BADGE_CRITERIA_TYPE_BADGE, $userid); + } + } + } + + /** + * Triggered when 'user_updated' event happens. + * + * @param user_updated $event event generated when user profile is updated. + */ + public static function profile_criteria_review(user_updated $event): void { + global $DB, $CFG; + + if (!self::badges_enabled()) { + return; + } + + require_once($CFG->dirroot . '/lib/badgeslib.php'); + $userid = $event->objectid; + + if ($rs = $DB->get_records('badge_criteria', ['criteriatype' => BADGE_CRITERIA_TYPE_PROFILE])) { + foreach ($rs as $r) { + $badge = new badge($r->badgeid); + self::process_criteria_completion($badge, BADGE_CRITERIA_TYPE_PROFILE, $userid); + } + } + } + + /** + * Triggered when the 'cohort_member_added' event happens. + * + * @param cohort_member_added $event generated when a user is added to a cohort + */ + public static function cohort_criteria_review(cohort_member_added $event): void { + global $DB, $CFG; + + if (!self::badges_enabled()) { + return; + } + + require_once($CFG->dirroot . '/lib/badgeslib.php'); + + $cohortid = $event->objectid; + $userid = $event->relateduserid; + + // Get relevant badges. + $badgesql = "SELECT badgeid + FROM {badge_criteria_param} cp + JOIN {badge_criteria} c ON cp.critid = c.id + WHERE c.criteriatype = ? + AND cp.name = ?"; + + $badges = $DB->get_records_sql( + $badgesql, + [BADGE_CRITERIA_TYPE_COHORT, "cohort_{$cohortid}"], + ); + + if (empty($badges)) { + return; + } + + foreach ($badges as $b) { + $badge = new badge($b->badgeid); + self::process_criteria_completion($badge, BADGE_CRITERIA_TYPE_COHORT, $userid); + } + } +} diff --git a/badges/classes/observer.php b/badges/classes/observer.php deleted file mode 100644 index 9817a65131f..00000000000 --- a/badges/classes/observer.php +++ /dev/null @@ -1,254 +0,0 @@ -. - -defined('MOODLE_INTERNAL') || die(); - -use \core_badges\badge; - -/** - * Event observer for badges. - * - * @package core_badges - * @copyright 2013 Rajesh Taneja - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class core_badges_observer { - /** - * Triggered when 'course_module_completion_updated' event is triggered. - * - * @param \core\event\course_module_completion_updated $event - */ - public static function course_module_criteria_review(\core\event\course_module_completion_updated $event) { - global $DB, $CFG; - - if (!empty($CFG->enablebadges)) { - require_once($CFG->dirroot.'/lib/badgeslib.php'); - - $eventdata = $event->get_record_snapshot('course_modules_completion', $event->objectid); - $userid = $event->relateduserid; - $mod = $event->contextinstanceid; - - if ($eventdata->completionstate == COMPLETION_COMPLETE - || $eventdata->completionstate == COMPLETION_COMPLETE_PASS - || $eventdata->completionstate == COMPLETION_COMPLETE_FAIL) { - // Need to take into account that there can be more than one badge with the same activity in its criteria. - if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'module_' . $mod, 'value' => $mod))) { - foreach ($rs as $r) { - $bid = $DB->get_field('badge_criteria', 'badgeid', array('id' => $r->critid), MUST_EXIST); - $badge = new badge($bid); - if (!$badge->is_active() || $badge->is_issued($userid)) { - continue; - } - - if ($badge->criteria[BADGE_CRITERIA_TYPE_ACTIVITY]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_ACTIVITY]->mark_complete($userid); - - if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); - $badge->issue($userid); - } - } - } - } - } - } - } - - /** - * Triggered when '\core\event\competency_evidence_created' event is triggered. - * - * @param \core\event\competency_evidence_created $event - */ - public static function competency_criteria_review(\core\event\competency_evidence_created $event) { - global $DB, $CFG; - - if (!empty($CFG->enablebadges)) { - require_once($CFG->dirroot.'/lib/badgeslib.php'); - - if (!get_config('core_competency', 'enabled')) { - return; - } - - $ucid = $event->other['usercompetencyid']; - $cid = $event->other['competencyid']; - $userid = $event->relateduserid; - - if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'competency_' . $cid, 'value' => $cid))) { - foreach ($rs as $r) { - $crit = $DB->get_record('badge_criteria', array('id' => $r->critid), 'badgeid, criteriatype', MUST_EXIST); - $badge = new badge($crit->badgeid); - // Only site badges are updated from site competencies. - if (!$badge->is_active() || $badge->is_issued($userid)) { - continue; - } - - if ($badge->criteria[$crit->criteriatype]->review($userid)) { - $badge->criteria[$crit->criteriatype]->mark_complete($userid); - - if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); - $badge->issue($userid); - } - } - } - } - } - } - - /** - * Triggered when 'course_completed' event is triggered. - * - * @param \core\event\course_completed $event - */ - public static function course_criteria_review(\core\event\course_completed $event) { - global $DB, $CFG; - - if (!empty($CFG->enablebadges)) { - require_once($CFG->dirroot.'/lib/badgeslib.php'); - - $userid = $event->relateduserid; - $courseid = $event->courseid; - - // Need to take into account that course can be a part of course_completion and courseset_completion criteria. - if ($rs = $DB->get_records('badge_criteria_param', array('name' => 'course_' . $courseid, 'value' => $courseid))) { - foreach ($rs as $r) { - $crit = $DB->get_record('badge_criteria', array('id' => $r->critid), 'badgeid, criteriatype', MUST_EXIST); - $badge = new badge($crit->badgeid); - if (!$badge->is_active() || $badge->is_issued($userid)) { - continue; - } - - if ($badge->criteria[$crit->criteriatype]->review($userid)) { - $badge->criteria[$crit->criteriatype]->mark_complete($userid); - - if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); - $badge->issue($userid); - } - } - } - } - } - } - - /** - * Triggered when 'badge_awarded' event happens. - * - * @param \core\event\badge_awarded $event event generated when a badge is awarded. - */ - public static function badge_criteria_review(\core\event\badge_awarded $event) { - global $DB, $CFG; - - if (!empty($CFG->enablebadges)) { - require_once($CFG->dirroot.'/lib/badgeslib.php'); - $userid = $event->relateduserid; - - if ($rs = $DB->get_records('badge_criteria', array('criteriatype' => BADGE_CRITERIA_TYPE_BADGE))) { - foreach ($rs as $r) { - $badge = new badge($r->badgeid); - if (!$badge->is_active() || $badge->is_issued($userid)) { - continue; - } - - if ($badge->criteria[BADGE_CRITERIA_TYPE_BADGE]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_BADGE]->mark_complete($userid); - - if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); - $badge->issue($userid); - } - } - } - } - } - } - /** - * Triggered when 'user_updated' event happens. - * - * @param \core\event\user_updated $event event generated when user profile is updated. - */ - public static function profile_criteria_review(\core\event\user_updated $event) { - global $DB, $CFG; - - if (!empty($CFG->enablebadges)) { - require_once($CFG->dirroot.'/lib/badgeslib.php'); - $userid = $event->objectid; - - if ($rs = $DB->get_records('badge_criteria', array('criteriatype' => BADGE_CRITERIA_TYPE_PROFILE))) { - foreach ($rs as $r) { - $badge = new badge($r->badgeid); - if (!$badge->is_active() || $badge->is_issued($userid)) { - continue; - } - - if ($badge->criteria[BADGE_CRITERIA_TYPE_PROFILE]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_PROFILE]->mark_complete($userid); - - if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); - $badge->issue($userid); - } - } - } - } - } - } - - /** - * Triggered when the 'cohort_member_added' event happens. - * - * @param \core\event\cohort_member_added $event generated when a user is added to a cohort - */ - public static function cohort_criteria_review(\core\event\cohort_member_added $event) { - global $DB, $CFG; - - if (!empty($CFG->enablebadges)) { - require_once($CFG->dirroot.'/lib/badgeslib.php'); - $cohortid = $event->objectid; - $userid = $event->relateduserid; - - // Get relevant badges. - $badgesql = "SELECT badgeid - FROM {badge_criteria_param} cp - JOIN {badge_criteria} c ON cp.critid = c.id - WHERE c.criteriatype = ? - AND cp.name = ?"; - $badges = $DB->get_records_sql($badgesql, array(BADGE_CRITERIA_TYPE_COHORT, "cohort_{$cohortid}")); - if (empty($badges)) { - return; - } - - foreach ($badges as $b) { - $badge = new badge($b->badgeid); - if (!$badge->is_active()) { - continue; - } - if ($badge->is_issued($userid)) { - continue; - } - - if ($badge->criteria[BADGE_CRITERIA_TYPE_COHORT]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_COHORT]->mark_complete($userid); - - if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) { - $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid); - $badge->issue($userid); - } - } - } - } - } -} diff --git a/badges/db/renamedclasses.php b/badges/db/renamedclasses.php new file mode 100644 index 00000000000..42333b9df27 --- /dev/null +++ b/badges/db/renamedclasses.php @@ -0,0 +1,37 @@ +. + +/** + * This file contains mappings for classes that have been renamed so that they meet the requirements of the autoloader. + * + * Renaming isn't always the recommended approach, but can provide benefit in situations where we've already got a + * close structure, OR where lots of classes get included and not necessarily used, or checked for often. + * + * When renaming a class delete the original class and add an entry to the db/renamedclasses.php directory for that + * component. + * This way we don't need to keep around old classes, instead creating aliases only when required. + * One big advantage to this method is that we provide consistent debugging for renamed classes when they are used. + * + * @package core_badges + * @copyright Dai Nguyen Trong + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$renamedclasses = [ + 'core_badges_observer' => 'core_badges\\event\\observer', +]; diff --git a/lib/db/events.php b/lib/db/events.php index 3f9b021ff9a..6cadc90b4f0 100644 --- a/lib/db/events.php +++ b/lib/db/events.php @@ -34,47 +34,46 @@ defined('MOODLE_INTERNAL') || die(); // List of legacy event handlers. -$handlers = array( +$handlers = [ // No more old events! -); +]; // List of events_2 observers. -$observers = array( - - array( +$observers = [ + [ 'eventname' => '\core\event\course_module_completion_updated', - 'callback' => 'core_badges_observer::course_module_criteria_review', - ), - array( + 'callback' => '\core_badges\event\observer::course_module_criteria_review', + ], + [ 'eventname' => '\core\event\badge_awarded', - 'callback' => 'core_badges_observer::badge_criteria_review', - ), - array( + 'callback' => '\core_badges\event\observer::badge_criteria_review', + ], + [ 'eventname' => '\core\event\course_completed', - 'callback' => 'core_badges_observer::course_criteria_review', - ), - array( + 'callback' => '\core_badges\event\observer::course_criteria_review', + ], + [ 'eventname' => '\core\event\user_updated', - 'callback' => 'core_badges_observer::profile_criteria_review', - ), - array( + 'callback' => '\core_badges\event\observer::profile_criteria_review', + ], + [ 'eventname' => '\core\event\cohort_member_added', - 'callback' => 'core_badges_observer::cohort_criteria_review', - ), - array( + 'callback' => '\core_badges\event\observer::cohort_criteria_review', + ], + [ 'eventname' => '\core\event\competency_evidence_created', - 'callback' => 'core_badges_observer::competency_criteria_review', - ), + 'callback' => '\core_badges\event\observer::competency_criteria_review', + ], // Competencies. - array( + [ 'eventname' => '\core\event\course_completed', 'callback' => 'core_competency\api::observe_course_completed', - ), - array( + ], + [ 'eventname' => '\core\event\course_module_completion_updated', 'callback' => 'core_competency\api::observe_course_module_completion_updated', - ), -); + ], +]; // List of all events triggered by Moodle can be found using Events list report.