From abc07b53a78d1a894083da48e5b99795390bf84a Mon Sep 17 00:00:00 2001 From: Eugene Venter Date: Thu, 31 Mar 2016 17:47:38 +1300 Subject: [PATCH] MDL-55491 badges: Add cohort as badge criteria commissioned by NZ Customs Service --- badges/classes/observer.php | 61 +++++++ badges/criteria/award_criteria.php | 7 + badges/criteria/award_criteria_cohort.php | 205 ++++++++++++++++++++++ lang/en/badges.php | 15 ++ lib/badgeslib.php | 1 + lib/db/events.php | 4 + 6 files changed, 293 insertions(+) create mode 100644 badges/criteria/award_criteria_cohort.php diff --git a/badges/classes/observer.php b/badges/classes/observer.php index 31be94ed618..5224d125337 100644 --- a/badges/classes/observer.php +++ b/badges/classes/observer.php @@ -168,4 +168,65 @@ class core_badges_observer { } } } + + /** + * Triggered when an event happens that updates cohort membership. Seeing as there + * are more than one event that can trigger this, we're accepting a generic event object + * as param. + * + * @param \core\event\base $event generated event + */ + public static function cohort_criteria_review(\core\event\base $event) { + global $DB, $CFG; + + if (!empty($CFG->enablebadges)) { + require_once($CFG->dirroot.'/lib/badgeslib.php'); + $cohortid = $event->objectid; + + // 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; + } + + // Get the users that should be issued badges. + $usersql = "SELECT userid + FROM {cohort_members} cm + WHERE cohortid = ? + AND userid NOT IN ( + SELECT userid + FROM {badge_issued} bi + WHERE badgeid IN ( + {$badgesql} + ) + )"; + $users = $DB->get_records_sql($usersql, array($cohortid, BADGE_CRITERIA_TYPE_COHORT, "cohort_{$cohortid}")); + + foreach ($badges as $b) { + $badge = new badge($b->badgeid); + if (!$badge->is_active()) { + continue; + } + foreach ($users as $u) { + if ($badge->is_issued($u->userid)) { + continue; + } + + if ($badge->criteria[BADGE_CRITERIA_TYPE_COHORT]->review($u->userid)) { + $badge->criteria[BADGE_CRITERIA_TYPE_COHORT]->mark_complete($u->userid); + + if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($u->userid)) { + $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($u->userid); + $badge->issue($u->userid); + } + } + } + } + } + } } diff --git a/badges/criteria/award_criteria.php b/badges/criteria/award_criteria.php index 54277830528..002899e1641 100644 --- a/badges/criteria/award_criteria.php +++ b/badges/criteria/award_criteria.php @@ -74,6 +74,12 @@ define('BADGE_CRITERIA_TYPE_PROFILE', 6); */ define('BADGE_CRITERIA_TYPE_BADGE', 7); +/* + * Cohort criteria type + * Criteria type constant, primarily for storing criteria type in the database. + */ +define('BADGE_CRITERIA_TYPE_COHORT', 98); + /* * Criteria type constant to class name mapping */ @@ -87,6 +93,7 @@ $BADGE_CRITERIA_TYPES = array( BADGE_CRITERIA_TYPE_COURSESET => 'courseset', BADGE_CRITERIA_TYPE_PROFILE => 'profile', BADGE_CRITERIA_TYPE_BADGE => 'badge', + BADGE_CRITERIA_TYPE_COHORT => 'cohort', ); /** diff --git a/badges/criteria/award_criteria_cohort.php b/badges/criteria/award_criteria_cohort.php new file mode 100644 index 00000000000..7493bad5760 --- /dev/null +++ b/badges/criteria/award_criteria_cohort.php @@ -0,0 +1,205 @@ +. + +/** + * This file contains the cohort membership badge award criteria type class + * + * @package core + * @subpackage badges + * @copyright 2016 onwards Catalyst IT {@link https://www.catalyst.net.nz/} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @author Eugene Venter + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot.'/cohort/lib.php'); + +/** + * Badge award criteria -- award on cohort membership + * + */ +class award_criteria_cohort extends award_criteria { + + /* @var int Criteria [BADGE_CRITERIA_TYPE_COHORT] */ + public $criteriatype = BADGE_CRITERIA_TYPE_COHORT; + + public $required_param = 'cohort'; + public $optional_params = array(); + + /** + * Get criteria details for displaying to users + * + * @return string + */ + public function get_details($short = '') { + global $DB, $OUTPUT; + $output = array(); + foreach ($this->params as $p) { + $cohortname = $DB->get_field('cohort', 'name', array('id' => $p['cohort'])); + if (!$cohortname) { + $str = $OUTPUT->error_text(get_string('error:nosuchcohort', 'badges')); + } else { + $str = html_writer::tag('b', '"' . $cohortname . '"'); + } + $output[] = $str; + } + + if ($short) { + return implode(', ', $output); + } else { + return html_writer::alist($output, array(), 'ul'); + } + } + + + /** + * Add appropriate new criteria options to the form + * + */ + public function get_options(&$mform) { + global $DB; + $none = false; + + $mform->addElement('header', 'first_header', $this->get_title()); + $mform->addHelpButton('first_header', 'criteria_' . $this->criteriatype, 'badges'); + + // Get cohorts + $cohorts = $DB->get_records_menu('cohort', array(), 'name ASC', 'id, name'); + if (!empty($cohorts)) { + $select = array(); + $selected = array(); + foreach ($cohorts as $cid => $cohortname) { + $select[$cid] = format_string($cohortname, true); + } + + if ($this->id !== 0) { + $selected = array_keys($this->params); + } + $settings = array('multiple' => 'multiple', 'size' => 20, 'class' => 'selectcohort'); + $mform->addElement('select', 'cohort_cohorts', get_string('addcohort', 'badges'), $select, $settings); + $mform->addRule('cohort_cohorts', get_string('requiredcohort', 'badges'), 'required'); + $mform->addHelpButton('cohort_cohorts', 'addcohort', 'badges'); + + if ($this->id !== 0) { + $mform->setDefault('cohort_cohorts', $selected); + } + } else { + $mform->addElement('static', 'nocohorts', '', get_string('error:nocohorts', 'badges')); + $none = true; + } + + // Add aggregation. + if (!$none) { + $mform->addElement('header', 'aggregation', get_string('method', 'badges')); + $agg = array(); + $agg[] =& $mform->createElement('radio', 'agg', '', get_string('allmethodcohort', 'badges'), 1); + $agg[] =& $mform->createElement('radio', 'agg', '', get_string('anymethodcohort', 'badges'), 2); + $mform->addGroup($agg, 'methodgr', '', array('
'), false); + if ($this->id !== 0) { + $mform->setDefault('agg', $this->method); + } else { + $mform->setDefault('agg', BADGE_CRITERIA_AGGREGATION_ANY); + } + } + + return array($none, get_string('noparamstoadd', 'badges')); + } + + /** + * Save criteria records + * + * @param $params criteria params + */ + public function save($params = array()) { + $cohorts = $params['cohort_cohorts']; + unset($params['cohort_cohorts']); + foreach ($cohorts as $cohortid) { + $params["cohort_{$cohortid}"] = $cohortid; + } + + parent::save($params); + } + + /** + * Review this criteria and decide if it has been completed + * + * @return bool Whether criteria is complete + */ + public function review($userid, $filtered = false) { + global $DB; + $overall = false; + + foreach ($this->params as $param) { + $cohort = $DB->get_record('cohort', array('id' => $param['cohort'])); + + // Extra check in case a cohort was deleted while badge is still active. + if (!$cohort) { + if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) { + return false; + } else { + continue; + } + } + + if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) { + if (cohort_is_member($cohort->id, $userid)) { + $overall = true; + continue; + } else { + return false; + } + } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) { + if (cohort_is_member($cohort->id, $userid)) { + return true; + } else { + $overall = false; + continue; + } + } + } + + return $overall; + } + + /** + * Checks criteria for any major problems. + * + * @return array A list containing status and an error message (if any). + */ + public function validate() { + global $DB; + $params = array_keys($this->params); + $method = ($this->method == BADGE_CRITERIA_AGGREGATION_ALL); + $singleparam = (count($params) == 1); + + foreach ($params as $param) { + // Perform check if there only one parameter with any type of aggregation, + // Or there are more than one parameter with aggregation ALL. + if (($singleparam || $method) && !$DB->record_exists('cohort', array('id' => $param))) { + return array(false, get_string('error:invalidparamcohort', 'badges')); + } + } + + return array(true, ''); + } + + public function get_completed_criteria_sql() { +// TODO; + + return array($join, $where, $params); + } +} diff --git a/lang/en/badges.php b/lang/en/badges.php index 309f29c2715..27e93c91235 100644 --- a/lang/en/badges.php +++ b/lang/en/badges.php @@ -32,6 +32,8 @@ $string['addbadge_help'] = 'Select all badges that should be added to this badge $string['addbadgecriteria'] = 'Add badge criteria'; $string['addcriteria'] = 'Add criteria'; $string['addcriteriatext'] = 'To start adding criteria, please select one of the options from the drop-down menu.'; +$string['addcohort'] = 'Add cohort'; +$string['addcohort_help'] = 'Select all cohorts that should be added to this badge requirement. Hold CTRL key to select multiple items.'; $string['addcourse'] = 'Add courses'; $string['addcourse_help'] = 'Select all courses that should be added to this badge requirement. Hold CTRL key to select multiple items.'; $string['addtobackpack'] = 'Add to backpack'; @@ -42,6 +44,7 @@ $string['all'] = 'All'; $string['allmethod'] = 'All of the selected conditions are met'; $string['allmethodactivity'] = 'All of the selected activities are complete'; $string['allmethodbadges'] = 'All of the selected badges have been earned'; +$string['allmethodcohort'] = 'Membership in all the selected cohorts'; $string['allmethodcourseset'] = 'All of the selected courses are complete'; $string['allmethodmanual'] = 'All of the selected roles award the badge'; $string['allmethodprofile'] = 'All of the selected profile fields have been completed'; @@ -55,6 +58,7 @@ $string['any'] = 'Any'; $string['anymethod'] = 'Any of the selected conditions is met'; $string['anymethodactivity'] = 'Any of the selected activities is complete'; $string['anymethodbadges'] = 'Any of the selected badges have been earned'; +$string['anymethodcohort'] = 'Membership in any of the selected cohorts'; $string['anymethodcourseset'] = 'Any of the selected courses is complete'; $string['anymethodmanual'] = 'Any of the selected roles awards the badge'; $string['anymethodprofile'] = 'Any of the selected profile fields has been completed'; @@ -181,18 +185,21 @@ $string['criteria_descr_short4'] = 'Complete the course '; $string['criteria_descr_short5'] = 'Complete {$a} of: '; $string['criteria_descr_short6'] = 'Complete {$a} of: '; $string['criteria_descr_short7'] = 'Complete {$a} of: '; +$string['criteria_descr_short98'] = 'Cohort membership in {$a} of: '; $string['criteria_descr_single_short1'] = 'Complete: '; $string['criteria_descr_single_short2'] = 'Awarded by: '; $string['criteria_descr_single_short4'] = 'Complete the course '; $string['criteria_descr_single_short5'] = 'Complete: '; $string['criteria_descr_single_short6'] = 'Complete: '; $string['criteria_descr_single_short7'] = 'Complete: '; +$string['criteria_descr_single_short98'] = 'Membership in: '; $string['criteria_descr_single_1'] = 'The following activity has to be completed:'; $string['criteria_descr_single_2'] = 'This badge has to be awarded by a user with the following role:'; $string['criteria_descr_single_4'] = 'Users must complete the course'; $string['criteria_descr_single_5'] = 'The following course has to be completed:'; $string['criteria_descr_single_6'] = 'The following user profile field has to be completed:'; $string['criteria_descr_single_7'] = 'The following badge has to be earned:'; +$string['criteria_descr_single_98'] = 'Membership in the following cohort is required:'; $string['criteria_descr_0'] = 'Users are awarded this badge when they complete {$a} of the listed requirements.'; $string['criteria_descr_1'] = '{$a} of the following activities are completed:'; $string['criteria_descr_2'] = 'This badge has to be awarded by the users with {$a} of the following roles:'; @@ -200,6 +207,7 @@ $string['criteria_descr_4'] = 'Users must complete the course'; $string['criteria_descr_5'] = '{$a} of the following courses have to be completed:'; $string['criteria_descr_6'] = '{$a} of the following user profile fields have to be completed:'; $string['criteria_descr_7'] = '{$a} of the following badges have to be earned:'; +$string['criteria_descr_98'] = 'Membership in {$a} of the following cohorts is required:'; $string['criteria_0'] = 'This badge is awarded when...'; $string['criteria_1'] = 'Activity completion'; $string['criteria_1_help'] = 'Allows a badge to be awarded to users based on the completion of a set of activities within a course.'; @@ -215,6 +223,8 @@ $string['criteria_6'] = 'Profile completion'; $string['criteria_6_help'] = 'Allows a badge to be awarded to users for completing certain fields in their profile. You can select from default and custom profile fields that are available to users. '; $string['criteria_7'] = 'Awarded badges'; $string['criteria_7_help'] = 'Allows a badge to be awarded to users based on the other badges thay have earned.'; +$string['criteria_98'] = 'Cohort membership'; +$string['criteria_98_help'] = 'Allows a badge to be awarded to users for becoming a member of certian cohorts.'; $string['criterror'] = 'Current parameters issues'; $string['criterror_help'] = 'This fieldset shows all parameters that were initially added to this badge requirement but are no longer available. It is recommended that you un-check such parameters to make sure that users can earn this badge in the future.'; $string['currentimage'] = 'Current image'; @@ -261,11 +271,15 @@ $string['error:invalidexpireperiod'] = 'Expiry period cannot be negative or equa $string['error:invalidparambadge'] = 'Badge does not exist. '; $string['error:noactivities'] = 'There are no activities with completion criteria enabled in this course.'; $string['error:nobadges'] = 'There are no course or site badges with access enabled to be added as criteria.'; +$string['error:invalidparamcohort'] = 'Cohort does not exist. '; +$string['error:noactivities'] = 'There are no activities with completion criteria enabled in this course.'; +$string['error:nocohorts'] = 'No cohorts'; $string['error:nocourses'] = 'Course completion is not enabled for any of the courses in this site, so none can be displayed. Course completion may be enabled in the course settings.'; $string['error:nogroups'] = '

There are no public collections of badges available in your backpack.

Only public collections are shown, visit your backpack to create some public collections.

'; $string['error:nopermissiontoview'] = 'You have no permissions to view badge recipients'; $string['error:nosuchbadge'] = 'Badge with id {$a} does not exist.'; +$string['error:nosuchcohort'] = 'Warning: This cohort is no longer available.'; $string['error:nosuchcourse'] = 'Warning: This course is no longer available.'; $string['error:nosuchfield'] = 'Warning: This user profile field is no longer available.'; $string['error:nosuchmod'] = 'Warning: This activity is no longer available.'; @@ -380,6 +394,7 @@ $string['recipientidentificationproblem'] = 'Cannot find a recipient of this bad $string['recipientvalidationproblem'] = 'Current user cannot be verified as a recipient of this badge.'; $string['relative'] = 'Relative date'; $string['revoke'] = 'Revoke badge'; +$string['requiredcohort'] = 'At least one cohort should be added to the cohort criterion.'; $string['requiredcourse'] = 'At least one course should be added to the courseset criterion.'; $string['requiredbadge'] = 'At least one badge should be added to the badge criterion.'; $string['reviewbadge'] = 'Changes in badge access'; diff --git a/lib/badgeslib.php b/lib/badgeslib.php index aa5efbb4a3b..ddbdbfb1b62 100644 --- a/lib/badgeslib.php +++ b/lib/badgeslib.php @@ -201,6 +201,7 @@ class badge { BADGE_CRITERIA_TYPE_COURSESET, BADGE_CRITERIA_TYPE_BADGE, BADGE_CRITERIA_TYPE_PROFILE, + BADGE_CRITERIA_TYPE_COHORT, ); } diff --git a/lib/db/events.php b/lib/db/events.php index de2be5165cc..0e1e3e17b1c 100644 --- a/lib/db/events.php +++ b/lib/db/events.php @@ -58,6 +58,10 @@ $observers = array( 'eventname' => '\core\event\user_updated', 'callback' => 'core_badges_observer::profile_criteria_review', ), + array( + 'eventname' => '\core\event\cohort_member_added', + 'callback' => 'core_badges_observer::cohort_criteria_review', + ), // Competencies. array(