1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * This file contains the badge earned badge award criteria type class 19 * 20 * @package core 21 * @subpackage badges 22 * @copyright 2019 Damyon Wiese 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 /** 29 * Badge award criteria -- award on competency completion 30 * 31 * @package core 32 * @subpackage badges 33 * @copyright 2019 Damyon Wiese 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class award_criteria_competency extends award_criteria { 37 38 /* @var int The criteria type */ 39 public $criteriatype = BADGE_CRITERIA_TYPE_COMPETENCY; 40 /* @var string a required param */ 41 public $required_param = 'competency'; 42 /* @var array no optional params */ 43 public $optional_params = []; 44 45 46 /** 47 * Get criteria details for displaying to users 48 * @param string $short Print short version of criteria 49 * @return string 50 */ 51 public function get_details($short = '') { 52 $output = array(); 53 54 foreach ($this->params as $p) { 55 $competency = new \core_competency\competency($p['competency']); 56 if ($short) { 57 $competency->set('description', ''); 58 } 59 // Render the competency even if competencies are not currently enabled. 60 \core_competency\api::skip_enabled(); 61 if ($pluginsfunction = get_plugins_with_function('render_competency_summary')) { 62 foreach ($pluginsfunction as $plugintype => $plugins) { 63 foreach ($plugins as $pluginfunction) { 64 $output[] = $pluginfunction($competency, $competency->get_framework(), false, false, true); 65 } 66 } 67 } 68 \core_competency\api::check_enabled(); 69 } 70 71 return '<dl><dd class="p-3 mb-2 bg-light text-dark border">' . 72 implode('</dd><dd class="p-3 mb-2 bg-light text-dark border">', $output) . 73 '</dd></dl>'; 74 } 75 76 /** 77 * Add appropriate new criteria options to the form 78 * @param object $mform moodle form 79 * @return array First item is a boolean to indicate an error and the second is the error message. 80 */ 81 public function get_options(&$mform) { 82 global $DB; 83 $none = false; 84 $availablebadges = null; 85 86 $mform->addElement('header', 'first_header', $this->get_title()); 87 $mform->addHelpButton('first_header', 'criteria_' . $this->criteriatype, 'badges'); 88 89 // Determine if this badge is a course badge or a site badge. 90 $competencies = ''; 91 if (count($this->params)) { 92 $competencies = implode(',', array_keys($this->params)); 93 } 94 $badge = $DB->get_record('badge', array('id' => $this->badgeid)); 95 $context = null; 96 $courseid = 0; 97 98 if ($badge->type == BADGE_TYPE_SITE) { 99 $context = context_system::instance(); 100 $courseid = SITEID; 101 } else if ($badge->type == BADGE_TYPE_COURSE) { 102 $context = context_course::instance($badge->courseid); 103 $courseid = $badge->courseid; 104 } 105 if ($pluginsfunction = get_plugins_with_function('competency_picker')) { 106 foreach ($pluginsfunction as $plugintype => $plugins) { 107 foreach ($plugins as $pluginfunction) { 108 $output[] = $pluginfunction($mform, $courseid, $context, 'competency_competencies'); 109 } 110 } 111 } 112 $mform->getElement('competency_competencies')->setValue($competencies); 113 $mform->addRule('competency_competencies', get_string('requiredcompetency', 'badges'), 'required'); 114 115 // Add aggregation. 116 if (!$none) { 117 $mform->addElement('header', 'aggregation', get_string('method', 'badges')); 118 $agg = array(); 119 $agg[] =& $mform->createElement('radio', 'agg', '', get_string('allmethodcompetencies', 'badges'), 1); 120 $agg[] =& $mform->createElement('radio', 'agg', '', get_string('anymethodcompetencies', 'badges'), 2); 121 $mform->addGroup($agg, 'methodgr', '', array('<br/>'), false); 122 if ($this->id !== 0) { 123 $mform->setDefault('agg', $this->method); 124 } else { 125 $mform->setDefault('agg', BADGE_CRITERIA_AGGREGATION_ANY); 126 } 127 } 128 129 return array($none, get_string('noparamstoadd', 'badges')); 130 } 131 132 /** 133 * Save criteria records 134 * 135 * @param array $params Values from the form or any other array. 136 */ 137 public function save($params = array()) { 138 $competencies = $params['competency_competencies']; 139 unset($params['competency_competencies']); 140 if (is_string($competencies)) { 141 $competencies = explode(',', $competencies); 142 } 143 foreach ($competencies as $competencyid) { 144 $params["competency_{$competencyid}"] = $competencyid; 145 } 146 parent::save($params); 147 } 148 149 /** 150 * Review this criteria and decide if it has been completed 151 * 152 * @param int $userid User whose criteria completion needs to be reviewed. 153 * @param bool $filtered An additional parameter indicating that user list 154 * has been reduced and some expensive checks can be skipped. 155 * 156 * @return bool Whether criteria is complete. 157 */ 158 public function review($userid, $filtered = false) { 159 global $DB; 160 161 $overall = false; 162 $competencyids = []; 163 164 if (!self::is_enabled()) { 165 return false; 166 } 167 foreach ($this->params as $param) { 168 $competencyids[] = $param['competency']; 169 } 170 171 $existing = []; 172 $badge = $DB->get_record('badge', array('id' => $this->badgeid)); 173 if ($badge->type == BADGE_TYPE_SITE) { 174 $existing = \core_competency\user_competency::get_multiple($userid, $competencyids); 175 } else if ($badge->type == BADGE_TYPE_COURSE) { 176 $existing = \core_competency\user_competency_course::get_multiple($userid, $badge->courseid, $competencyids); 177 } 178 179 if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) { 180 // Any vs all conditions are reversed when no criteria let us finish early. 181 $overall = true; 182 } 183 184 foreach ($this->params as $param) { 185 $proficiency = false; 186 foreach ($existing as $usercompetency) { 187 if ($usercompetency->get('competencyid') == $param['competency']) { 188 $proficiency = $usercompetency->get('proficiency'); 189 } 190 } 191 192 if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) { 193 if (!$proficiency) { 194 return false; 195 } 196 } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) { 197 if ($proficiency) { 198 return true; 199 } 200 } 201 } 202 203 return $overall; 204 } 205 206 /** 207 * Returns array with sql code and parameters returning all ids 208 * of users who meet this particular criterion. 209 * 210 * @return array list($join, $where, $params) 211 */ 212 public function get_completed_criteria_sql() { 213 global $DB; 214 215 $join = ''; 216 $where = ''; 217 $params = []; 218 $competencyids = []; 219 220 $badge = $DB->get_record('badge', array('id' => $this->badgeid)); 221 222 if (!self::is_enabled()) { 223 return array($join, $where, $params); 224 } 225 226 if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) { 227 // User has received ANY of the required competencies (we can use an in or equals list). 228 foreach ($this->params as $param) { 229 $competencyids[] = $param['competency']; 230 } 231 232 $where = ' AND uc2.competencyid '; 233 list($sql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED, 'usercomp'); 234 $where .= $sql; 235 if ($badge->type == BADGE_TYPE_SITE) { 236 $join = ' JOIN {competency_usercomp} uc2 ON uc2.userid = u.id'; 237 } else if ($badge->type == BADGE_TYPE_COURSE) { 238 $join = ' JOIN {competency_usercompcourse} uc2 ON uc2.userid = u.id AND uc2.courseid = :competencycourseid '; 239 $params['competencycourseid'] = $badge->courseid; 240 } 241 $where .= ' AND uc2.proficiency = :isproficient '; 242 $params['isproficient'] = true; 243 } else { 244 245 // User has received ALL of the required competencies (we have to join on each one). 246 $joincount = 0; 247 foreach ($this->params as $param) { 248 $joincount++; 249 $join .= ' JOIN {competency_usercomp} uc' . $joincount . ' ON uc' . $joincount . '.userid = u.id'; 250 $where .= ' AND uc' . $joincount . '.competencyid = :competencyindex' . $joincount; 251 $params['competencyindex' . $joincount] = $param['competency']; 252 253 $where .= ' AND uc' . $joincount . '.userid = u.id'; 254 $where .= ' AND uc' . $joincount . '.proficiency = :isproficient' . $joincount; 255 $params['isproficient' . $joincount] = true; 256 } 257 258 } 259 return array($join, $where, $params); 260 } 261 262 /** 263 * Hide this criteria when competencies are disabled. 264 * 265 * @return boolean 266 */ 267 public static function is_enabled() { 268 return \core_competency\api::is_enabled(); 269 } 270 271 /** 272 * Check if any badge has records for competencies. 273 * 274 * @param array $competencyids Array of competencies ids. 275 * @return boolean Return true if competencies were found in any badge. 276 */ 277 public static function has_records_for_competencies($competencyids) { 278 global $DB; 279 list($insql, $params) = $DB->get_in_or_equal($competencyids, SQL_PARAMS_NAMED); 280 $sql = "SELECT DISTINCT bc.badgeid 281 FROM {badge_criteria} bc 282 JOIN {badge_criteria_param} bcp ON bc.id = bcp.critid 283 WHERE bc.criteriatype = :criteriatype AND value $insql"; 284 $params['criteriatype'] = BADGE_CRITERIA_TYPE_COMPETENCY; 285 286 return self::record_exists_sql($sql, $params); 287 } 288 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body