See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
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 activity badge award criteria type class 19 * 20 * @package core 21 * @subpackage badges 22 * @copyright 2012 onwards Totara Learning Solutions Ltd {@link http://www.totaralms.com/} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 * @author Yuliya Bozhko <yuliya.bozhko@totaralms.com> 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 require_once($CFG->libdir . '/completionlib.php'); 29 30 /** 31 * Badge award criteria -- award on activity completion 32 * 33 */ 34 class award_criteria_activity extends award_criteria { 35 36 /* @var int Criteria [BADGE_CRITERIA_TYPE_ACTIVITY] */ 37 public $criteriatype = BADGE_CRITERIA_TYPE_ACTIVITY; 38 39 private $courseid; 40 private $course; 41 42 public $required_param = 'module'; 43 public $optional_params = array('bydate'); 44 45 public function __construct($record) { 46 global $DB; 47 parent::__construct($record); 48 49 $this->course = $DB->get_record_sql('SELECT c.id, c.enablecompletion, c.cacherev, c.startdate 50 FROM {badge} b LEFT JOIN {course} c ON b.courseid = c.id 51 WHERE b.id = :badgeid ', array('badgeid' => $this->badgeid), MUST_EXIST); 52 53 // If the course doesn't exist but we're sure the badge does (thanks to the LEFT JOIN), then use the site as the course. 54 if (empty($this->course->id)) { 55 $this->course = get_course(SITEID); 56 } 57 $this->courseid = $this->course->id; 58 } 59 60 /** 61 * Gets the module instance from the database and returns it. 62 * If no module instance exists this function returns false. 63 * 64 * @return stdClass|bool 65 */ 66 private function get_mod_instance($cmid) { 67 global $DB; 68 $rec = $DB->get_record_sql("SELECT md.name 69 FROM {course_modules} cm, 70 {modules} md 71 WHERE cm.id = ? AND 72 md.id = cm.module", array($cmid)); 73 74 if ($rec) { 75 return get_coursemodule_from_id($rec->name, $cmid); 76 } else { 77 return null; 78 } 79 } 80 81 /** 82 * Get criteria description for displaying to users 83 * 84 * @return string 85 */ 86 public function get_details($short = '') { 87 global $DB, $OUTPUT; 88 $output = array(); 89 foreach ($this->params as $p) { 90 $mod = self::get_mod_instance($p['module']); 91 if (!$mod) { 92 $str = $OUTPUT->error_text(get_string('error:nosuchmod', 'badges')); 93 } else { 94 $str = html_writer::tag('b', '"' . get_string('modulename', $mod->modname) . ' - ' . $mod->name . '"'); 95 if (isset($p['bydate'])) { 96 $str .= get_string('criteria_descr_bydate', 'badges', userdate($p['bydate'], get_string('strftimedate', 'core_langconfig'))); 97 } 98 } 99 $output[] = $str; 100 } 101 102 if ($short) { 103 return implode(', ', $output); 104 } else { 105 return html_writer::alist($output, array(), 'ul'); 106 } 107 } 108 109 /** 110 * Add appropriate new criteria options to the form 111 * 112 */ 113 public function get_options(&$mform) { 114 $none = true; 115 $existing = array(); 116 $missing = array(); 117 118 $course = $this->course; 119 $info = new completion_info($course); 120 $mods = $info->get_activities(); 121 $mids = array_keys($mods); 122 123 if ($this->id !== 0) { 124 $existing = array_keys($this->params); 125 $missing = array_diff($existing, $mids); 126 } 127 128 if (!empty($missing)) { 129 $mform->addElement('header', 'category_errors', get_string('criterror', 'badges')); 130 $mform->addHelpButton('category_errors', 'criterror', 'badges'); 131 foreach ($missing as $m) { 132 $this->config_options($mform, array('id' => $m, 'checked' => true, 133 'name' => get_string('error:nosuchmod', 'badges'), 'error' => true)); 134 $none = false; 135 } 136 } 137 138 if (!empty($mods)) { 139 $mform->addElement('header', 'first_header', $this->get_title()); 140 foreach ($mods as $mod) { 141 $checked = false; 142 if (in_array($mod->id, $existing)) { 143 $checked = true; 144 } 145 $param = array('id' => $mod->id, 146 'checked' => $checked, 147 'name' => get_string('modulename', $mod->modname) . ' - ' . $mod->name, 148 'error' => false 149 ); 150 151 if ($this->id !== 0 && isset($this->params[$mod->id]['bydate'])) { 152 $param['bydate'] = $this->params[$mod->id]['bydate']; 153 } 154 155 if ($this->id !== 0 && isset($this->params[$mod->id]['grade'])) { 156 $param['grade'] = $this->params[$mod->id]['grade']; 157 } 158 159 $this->config_options($mform, $param); 160 $none = false; 161 } 162 } 163 164 // Add aggregation. 165 if (!$none) { 166 $mform->addElement('header', 'aggregation', get_string('method', 'badges')); 167 $agg = array(); 168 $agg[] =& $mform->createElement('radio', 'agg', '', get_string('allmethodactivity', 'badges'), 1); 169 $agg[] =& $mform->createElement('radio', 'agg', '', get_string('anymethodactivity', 'badges'), 2); 170 $mform->addGroup($agg, 'methodgr', '', array('<br/>'), false); 171 if ($this->id !== 0) { 172 $mform->setDefault('agg', $this->method); 173 } else { 174 $mform->setDefault('agg', BADGE_CRITERIA_AGGREGATION_ANY); 175 } 176 } 177 178 return array($none, get_string('error:noactivities', 'badges')); 179 } 180 181 /** 182 * Review this criteria and decide if it has been completed 183 * 184 * @param int $userid User whose criteria completion needs to be reviewed. 185 * @param bool $filtered An additional parameter indicating that user list 186 * has been reduced and some expensive checks can be skipped. 187 * 188 * @return bool Whether criteria is complete 189 */ 190 public function review($userid, $filtered = false) { 191 if ($this->course->startdate > time()) { 192 return false; 193 } 194 195 $info = new completion_info($this->course); 196 197 $overall = false; 198 foreach ($this->params as $param) { 199 $cm = new stdClass(); 200 $cm->id = $param['module']; 201 202 $data = $info->get_data($cm, false, $userid); 203 $check_date = true; 204 205 if (isset($param['bydate'])) { 206 $date = $data->timemodified; 207 $check_date = ($date <= $param['bydate']); 208 } 209 210 // Successfull completion states depend on the completion settings. 211 if (isset($data->passgrade)) { 212 // Passing grade is required. Don't issue a badge when state is COMPLETION_COMPLETE_FAIL. 213 $completionstates = [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS]; 214 } else { 215 // Any grade is required. Issue a badge even when state is COMPLETION_COMPLETE_FAIL. 216 $completionstates = [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS, COMPLETION_COMPLETE_FAIL]; 217 } 218 219 if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) { 220 if (in_array($data->completionstate, $completionstates) && $check_date) { 221 $overall = true; 222 continue; 223 } else { 224 return false; 225 } 226 } else { 227 if (in_array($data->completionstate, $completionstates) && $check_date) { 228 return true; 229 } else { 230 $overall = false; 231 continue; 232 } 233 } 234 } 235 236 return $overall; 237 } 238 239 /** 240 * Returns array with sql code and parameters returning all ids 241 * of users who meet this particular criterion. 242 * 243 * @return array list($join, $where, $params) 244 */ 245 public function get_completed_criteria_sql() { 246 global $DB; 247 $join = ''; 248 $where = ''; 249 $params = array(); 250 251 if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) { 252 foreach ($this->params as $param) { 253 $moduledata[] = " cmc.coursemoduleid = :completedmodule{$param['module']} "; 254 $params["completedmodule{$param['module']}"] = $param['module']; 255 } 256 if (!empty($moduledata)) { 257 $extraon = implode(' OR ', $moduledata); 258 $join = " JOIN {course_modules_completion} cmc ON cmc.userid = u.id AND 259 ( cmc.completionstate = :completionfail OR 260 cmc.completionstate = :completionpass OR 261 cmc.completionstate = :completioncomplete ) AND ({$extraon})"; 262 $params["completionpass"] = COMPLETION_COMPLETE_PASS; 263 $params["completionfail"] = COMPLETION_COMPLETE_FAIL; 264 $params["completioncomplete"] = COMPLETION_COMPLETE; 265 } 266 return array($join, $where, $params); 267 } else { 268 // Get all cmids of modules related to the criteria. 269 $cmids = array_map(fn ($x) => $x['module'], $this->params); 270 list($cmcmodulessql, $paramscmc) = $DB->get_in_or_equal($cmids, SQL_PARAMS_NAMED); 271 272 // Create a sql query to get all users who have worked on these course modules. 273 $sql = "SELECT DISTINCT userid FROM {course_modules_completion} cmc " 274 . "WHERE coursemoduleid " . $cmcmodulessql . " AND " 275 . "( cmc.completionstate IN ( :completionpass, :completionfail, :completioncomplete ) )"; 276 $paramscmcs = [ 277 'completionpass' => COMPLETION_COMPLETE_PASS, 278 'completionfail' => COMPLETION_COMPLETE_FAIL, 279 'completioncomplete' => COMPLETION_COMPLETE 280 ]; 281 $paramscmc = array_merge($paramscmc, $paramscmcs); 282 $userids = $DB->get_records_sql($sql, $paramscmc); 283 284 // Now check each user if the user has a completion of each module. 285 $useridsbadgeable = array_keys(array_filter( 286 $userids, 287 function ($user) use ($cmcmodulessql, $paramscmc, $cmids) { 288 global $DB; 289 $params = array_merge($paramscmc, ['userid' => $user->userid]); 290 $select = "coursemoduleid " . $cmcmodulessql . " AND userid = :userid"; 291 $cmidsuser = $DB->get_fieldset_select('course_modules_completion', 'coursemoduleid', $select, $params); 292 return empty(array_diff($cmidsuser, $cmids)); 293 } 294 )); 295 296 // Finally create a where statement (if neccessary) with all userids who are allowed to get the badge. 297 // This list also includes all users who have previously received the badge. These are filtered out in the badge.php. 298 $join = ""; 299 $where = ""; 300 if (!empty($useridsbadgeable)) { 301 list($wherepart, $params) = $DB->get_in_or_equal($useridsbadgeable, SQL_PARAMS_NAMED); 302 $where = " AND u.id " . $wherepart; 303 } 304 return array($join, $where, $params); 305 } 306 } 307 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body