Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
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 * Badge award criteria 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 29 /* 30 * Role completion criteria type 31 * Criteria type constant, primarily for storing criteria type in the database. 32 */ 33 define('BADGE_CRITERIA_TYPE_OVERALL', 0); 34 35 /* 36 * Activity completion criteria type 37 * Criteria type constant, primarily for storing criteria type in the database. 38 */ 39 define('BADGE_CRITERIA_TYPE_ACTIVITY', 1); 40 41 /* 42 * Duration completion criteria type 43 * Criteria type constant, primarily for storing criteria type in the database. 44 */ 45 define('BADGE_CRITERIA_TYPE_MANUAL', 2); 46 47 /* 48 * Grade completion criteria type 49 * Criteria type constant, primarily for storing criteria type in the database. 50 */ 51 define('BADGE_CRITERIA_TYPE_SOCIAL', 3); 52 53 /* 54 * Course completion criteria type 55 * Criteria type constant, primarily for storing criteria type in the database. 56 */ 57 define('BADGE_CRITERIA_TYPE_COURSE', 4); 58 59 /* 60 * Courseset completion criteria type 61 * Criteria type constant, primarily for storing criteria type in the database. 62 */ 63 define('BADGE_CRITERIA_TYPE_COURSESET', 5); 64 65 /* 66 * Course completion criteria type 67 * Criteria type constant, primarily for storing criteria type in the database. 68 */ 69 define('BADGE_CRITERIA_TYPE_PROFILE', 6); 70 71 /* 72 * Badge completion criteria type 73 * Criteria type constant, primarily for storing criteria type in the database. 74 */ 75 define('BADGE_CRITERIA_TYPE_BADGE', 7); 76 77 /* 78 * Cohort criteria type 79 * Criteria type constant, primarily for storing criteria type in the database. 80 */ 81 define('BADGE_CRITERIA_TYPE_COHORT', 8); 82 83 /* 84 * Competency criteria type 85 * Criteria type constant, primarily for storing criteria type in the database. 86 */ 87 define('BADGE_CRITERIA_TYPE_COMPETENCY', 9); 88 89 /** 90 * Award criteria abstract definition 91 * 92 */ 93 abstract class award_criteria { 94 95 /** 96 * ID of the criterion. 97 * @var integer 98 */ 99 public $id; 100 101 /** 102 * Aggregation method [BADGE_CRITERIA_AGGREGATION_ANY, BADGE_CRITERIA_AGGREGATION_ALL]. 103 * @var integer 104 */ 105 public $method; 106 107 /** 108 * ID of a badge this criterion belongs to. 109 * @var integer 110 */ 111 public $badgeid; 112 113 /** 114 * Criterion HTML/plain text description. 115 * @var string 116 */ 117 public $description; 118 119 /** 120 * Format of the criterion description. 121 * @var integer 122 */ 123 public $descriptionformat; 124 125 /** 126 * Any additional parameters. 127 * @var array 128 */ 129 public $params = array(); 130 131 /** 132 * Criteria type. 133 * @var string 134 */ 135 public $criteriatype; 136 137 /** 138 * Required parameters. 139 * @var string 140 */ 141 public $required_param = ''; 142 143 /** 144 * Optional parameters. 145 * @var array 146 */ 147 public $optional_params = []; 148 149 /** 150 * The base constructor 151 * 152 * @param array $params 153 */ 154 public function __construct($params) { 155 $this->id = isset($params['id']) ? $params['id'] : 0; 156 $this->method = isset($params['method']) ? $params['method'] : BADGE_CRITERIA_AGGREGATION_ANY; 157 $this->badgeid = $params['badgeid']; 158 $this->description = isset($params['description']) ? $params['description'] : ''; 159 $this->descriptionformat = isset($params['descriptionformat']) ? $params['descriptionformat'] : FORMAT_HTML; 160 if (isset($params['id'])) { 161 $this->params = $this->get_params($params['id']); 162 } 163 } 164 165 /** 166 * Factory method for creating criteria class object 167 * 168 * @param array $params associative arrays varname => value 169 * @return award_criteria 170 */ 171 public static function build($params) { 172 global $CFG; 173 174 require_once($CFG->libdir . '/badgeslib.php'); 175 176 $types = badges_list_criteria(false); 177 178 if (!isset($params['criteriatype']) || !isset($types[$params['criteriatype']])) { 179 throw new \moodle_exception('error:invalidcriteriatype', 'badges'); 180 } 181 182 $class = 'award_criteria_' . $types[$params['criteriatype']]; 183 require_once($CFG->dirroot . '/badges/criteria/' . $class . '.php'); 184 185 return new $class($params); 186 } 187 188 /** 189 * Return criteria title 190 * 191 * @return string 192 */ 193 public function get_title() { 194 return get_string('criteria_' . $this->criteriatype, 'badges'); 195 } 196 197 /** 198 * Get criteria details for displaying to users 199 * 200 * @param string $short Print short version of criteria 201 * @return string 202 */ 203 abstract public function get_details($short = ''); 204 205 /** 206 * Add appropriate criteria options to the form 207 * 208 */ 209 abstract public function get_options(&$mform); 210 211 /** 212 * Add appropriate parameter elements to the criteria form 213 * 214 */ 215 public function config_options(&$mform, $param) { 216 global $OUTPUT; 217 $prefix = $this->required_param . '_'; 218 219 if ($param['error']) { 220 $parameter[] =& $mform->createElement('advcheckbox', $prefix . $param['id'], '', 221 $OUTPUT->error_text($param['name']), null, array(0, $param['id'])); 222 $mform->addGroup($parameter, 'param_' . $prefix . $param['id'], '', array(' '), false); 223 } else { 224 $parameter[] =& $mform->createElement('advcheckbox', $prefix . $param['id'], '', $param['name'], null, array(0, $param['id'])); 225 $parameter[] =& $mform->createElement('static', 'break_start_' . $param['id'], null, 226 '<div class="ml-3 mt-1 w-100 align-items-center">'); 227 228 if (in_array('grade', $this->optional_params)) { 229 $parameter[] =& $mform->createElement('static', 'mgrade_' . $param['id'], null, get_string('mingrade', 'badges')); 230 $parameter[] =& $mform->createElement('text', 'grade_' . $param['id'], '', array('size' => '5')); 231 $mform->setType('grade_' . $param['id'], PARAM_INT); 232 } 233 234 if (in_array('bydate', $this->optional_params)) { 235 $parameter[] =& $mform->createElement('static', 'complby_' . $param['id'], null, get_string('bydate', 'badges')); 236 $parameter[] =& $mform->createElement('date_selector', 'bydate_' . $param['id'], "", array('optional' => true)); 237 } 238 239 $parameter[] =& $mform->createElement('static', 'break_end_' . $param['id'], null, '</div>'); 240 $mform->addGroup($parameter, 'param_' . $prefix . $param['id'], '', array(' '), false); 241 if (in_array('grade', $this->optional_params)) { 242 $mform->addGroupRule('param_' . $prefix . $param['id'], array( 243 'grade_' . $param['id'] => array(array(get_string('err_numeric', 'form'), 'numeric', '', 'client')))); 244 } 245 $mform->disabledIf('bydate_' . $param['id'] . '[day]', 'bydate_' . $param['id'] . '[enabled]', 'notchecked'); 246 $mform->disabledIf('bydate_' . $param['id'] . '[month]', 'bydate_' . $param['id'] . '[enabled]', 'notchecked'); 247 $mform->disabledIf('bydate_' . $param['id'] . '[year]', 'bydate_' . $param['id'] . '[enabled]', 'notchecked'); 248 $mform->disabledIf('param_' . $prefix . $param['id'], $prefix . $param['id'], 'notchecked'); 249 } 250 251 // Set default values. 252 $mform->setDefault($prefix . $param['id'], $param['checked']); 253 if (isset($param['bydate'])) { 254 $mform->setDefault('bydate_' . $param['id'], $param['bydate']); 255 } 256 if (isset($param['grade'])) { 257 $mform->setDefault('grade_' . $param['id'], $param['grade']); 258 } 259 } 260 261 /** 262 * Add appropriate criteria elements 263 * 264 * @param stdClass $data details of various criteria 265 */ 266 public function config_form_criteria($data) { 267 global $OUTPUT; 268 $agg = $data->get_aggregation_methods(); 269 270 $editurl = new moodle_url('/badges/criteria_settings.php', 271 array('badgeid' => $this->badgeid, 'edit' => true, 'type' => $this->criteriatype, 'crit' => $this->id)); 272 $deleteurl = new moodle_url('/badges/criteria_action.php', 273 array('badgeid' => $this->badgeid, 'delete' => true, 'type' => $this->criteriatype)); 274 $editaction = $OUTPUT->action_icon($editurl, new pix_icon('t/edit', get_string('edit')), null, array('class' => 'criteria-action')); 275 $deleteaction = $OUTPUT->action_icon($deleteurl, new pix_icon('t/delete', get_string('delete')), null, array('class' => 'criteria-action')); 276 277 echo $OUTPUT->box_start(); 278 if (!$data->is_locked() && !$data->is_active()) { 279 echo $OUTPUT->box($deleteaction . $editaction, array('criteria-header')); 280 } 281 echo $OUTPUT->heading($this->get_title() . $OUTPUT->help_icon('criteria_' . $this->criteriatype, 'badges'), 3, 'main help'); 282 283 if (!empty($this->description)) { 284 $badge = new badge($this->badgeid); 285 echo $OUTPUT->box( 286 format_text($this->description, $this->descriptionformat, array('context' => $badge->get_context())), 287 'criteria-description' 288 ); 289 } 290 291 if (!empty($this->params)) { 292 if (count($this->params) > 1) { 293 echo $OUTPUT->box(get_string('criteria_descr_' . $this->criteriatype, 'badges', 294 core_text::strtoupper($agg[$data->get_aggregation_method($this->criteriatype)])), array('clearfix')); 295 } else { 296 echo $OUTPUT->box(get_string('criteria_descr_single_' . $this->criteriatype , 'badges'), array('clearfix')); 297 } 298 echo $OUTPUT->box($this->get_details(), array('clearfix')); 299 } 300 echo $OUTPUT->box_end(); 301 } 302 303 /** 304 * Review this criteria and decide if the user has completed 305 * 306 * @param int $userid User whose criteria completion needs to be reviewed. 307 * @param bool $filtered An additional parameter indicating that user list 308 * has been reduced and some expensive checks can be skipped. 309 * 310 * @return bool Whether criteria is complete 311 */ 312 abstract public function review($userid, $filtered = false); 313 314 /** 315 * Returns array with sql code and parameters returning all ids 316 * of users who meet this particular criterion. 317 * 318 * @return array list($join, $where, $params) 319 */ 320 abstract public function get_completed_criteria_sql(); 321 322 /** 323 * Mark this criteria as complete for a user 324 * 325 * @param int $userid User whose criteria is completed. 326 */ 327 public function mark_complete($userid) { 328 global $DB; 329 $obj = array(); 330 $obj['critid'] = $this->id; 331 $obj['userid'] = $userid; 332 $obj['datemet'] = time(); 333 if (!$DB->record_exists('badge_criteria_met', array('critid' => $this->id, 'userid' => $userid))) { 334 $DB->insert_record('badge_criteria_met', $obj); 335 } 336 } 337 338 /** 339 * Return criteria parameters 340 * 341 * @param int $critid Criterion ID 342 * @return array 343 */ 344 public function get_params($cid) { 345 global $DB; 346 $params = array(); 347 348 $records = $DB->get_records('badge_criteria_param', array('critid' => $cid)); 349 foreach ($records as $rec) { 350 $arr = explode('_', $rec->name); 351 $params[$arr[1]][$arr[0]] = $rec->value; 352 } 353 354 return $params; 355 } 356 357 /** 358 * Delete this criterion 359 * 360 */ 361 public function delete() { 362 global $DB, $PAGE; 363 364 // Remove any records if it has already been met. 365 $DB->delete_records('badge_criteria_met', array('critid' => $this->id)); 366 367 // Remove all parameters records. 368 $DB->delete_records('badge_criteria_param', array('critid' => $this->id)); 369 370 // Finally remove criterion itself. 371 $DB->delete_records('badge_criteria', array('id' => $this->id)); 372 373 // Trigger event, badge criteria deleted. 374 $eventparams = array('objectid' => $this->id, 375 'context' => $PAGE->context, 376 'other' => array('badgeid' => $this->badgeid)); 377 $event = \core\event\badge_criteria_deleted::create($eventparams); 378 $event->trigger(); 379 } 380 381 /** 382 * Saves intial criteria records with required parameters set up. 383 * 384 * @param array $params Values from the form or any other array. 385 */ 386 public function save($params = array()) { 387 global $DB, $PAGE; 388 389 // Figure out criteria description. 390 // If it is coming from the form editor, it is an array(text, format). 391 $description = ''; 392 $descriptionformat = FORMAT_HTML; 393 if (isset($params['description']['text'])) { 394 $description = $params['description']['text']; 395 $descriptionformat = $params['description']['format']; 396 } else if (isset($params['description'])) { 397 $description = $params['description']; 398 } 399 400 $fordb = new stdClass(); 401 $fordb->criteriatype = $this->criteriatype; 402 $fordb->method = isset($params['agg']) ? $params['agg'] : BADGE_CRITERIA_AGGREGATION_ALL; 403 $fordb->badgeid = $this->badgeid; 404 $fordb->description = $description; 405 $fordb->descriptionformat = $descriptionformat; 406 $t = $DB->start_delegated_transaction(); 407 408 // Pick only params that are required by this criterion. 409 // Filter out empty values first. 410 $params = array_filter($params); 411 // Find out which param matches optional and required ones. 412 $match = array_merge($this->optional_params, array($this->required_param)); 413 $regex = implode('|', array_map(function($a) { 414 return $a . "_"; 415 }, $match)); 416 $requiredkeys = preg_grep('/^(' . $regex . ').*$/', array_keys($params)); 417 418 if ($this->id !== 0) { 419 $cid = $this->id; 420 421 // Update criteria before doing anything with parameters. 422 $fordb->id = $cid; 423 $DB->update_record('badge_criteria', $fordb, true); 424 425 // Trigger event: badge_criteria_updated. 426 $eventparams = array('objectid' => $this->id, 427 'context' => $PAGE->context, 428 'other' => array('badgeid' => $this->badgeid)); 429 $event = \core\event\badge_criteria_updated::create($eventparams); 430 $event->trigger(); 431 432 $existing = $DB->get_fieldset_select('badge_criteria_param', 'name', 'critid = ?', array($cid)); 433 $todelete = array_diff($existing, $requiredkeys); 434 435 if (!empty($todelete)) { 436 // A workaround to add some disabled elements that are still being submitted from the form. 437 foreach ($todelete as $del) { 438 $name = explode('_', $del); 439 if ($name[0] == $this->required_param) { 440 foreach ($this->optional_params as $opt) { 441 $todelete[] = $opt . '_' . $name[1]; 442 } 443 } 444 } 445 $todelete = array_unique($todelete); 446 list($sql, $sqlparams) = $DB->get_in_or_equal($todelete, SQL_PARAMS_NAMED, 'd', true); 447 $sqlparams = array_merge(array('critid' => $cid), $sqlparams); 448 $DB->delete_records_select('badge_criteria_param', 'critid = :critid AND name ' . $sql, $sqlparams); 449 } 450 451 foreach ($requiredkeys as $key) { 452 if (in_array($key, $existing)) { 453 $updp = $DB->get_record('badge_criteria_param', array('name' => $key, 'critid' => $cid)); 454 $updp->value = $params[$key]; 455 $DB->update_record('badge_criteria_param', $updp, true); 456 } else { 457 $newp = new stdClass(); 458 $newp->critid = $cid; 459 $newp->name = $key; 460 $newp->value = $params[$key]; 461 $DB->insert_record('badge_criteria_param', $newp); 462 } 463 } 464 } else { 465 $cid = $DB->insert_record('badge_criteria', $fordb, true); 466 if ($cid) { 467 foreach ($requiredkeys as $key) { 468 $newp = new stdClass(); 469 $newp->critid = $cid; 470 $newp->name = $key; 471 $newp->value = $params[$key]; 472 $DB->insert_record('badge_criteria_param', $newp, false, true); 473 } 474 } 475 // Trigger event: badge_criteria_created. 476 $eventparams = array('objectid' => $this->id, 477 'context' => $PAGE->context, 478 'other' => array('badgeid' => $this->badgeid)); 479 $event = \core\event\badge_criteria_created::create($eventparams); 480 $event->trigger(); 481 } 482 $t->allow_commit(); 483 } 484 485 /** 486 * Saves intial criteria records with required parameters set up. 487 */ 488 public function make_clone($newbadgeid) { 489 global $DB; 490 491 $fordb = new stdClass(); 492 $fordb->criteriatype = $this->criteriatype; 493 $fordb->method = $this->method; 494 $fordb->badgeid = $newbadgeid; 495 $fordb->description = $this->description; 496 $fordb->descriptionformat = $this->descriptionformat; 497 if (($newcrit = $DB->insert_record('badge_criteria', $fordb, true)) && isset($this->params)) { 498 foreach ($this->params as $k => $param) { 499 foreach ($param as $key => $value) { 500 $paramdb = new stdClass(); 501 $paramdb->critid = $newcrit; 502 $paramdb->name = $key . '_' . $k; 503 $paramdb->value = $value; 504 $DB->insert_record('badge_criteria_param', $paramdb); 505 } 506 } 507 } 508 } 509 510 /** 511 * Allow some specific criteria types to be disabled based on config. 512 * 513 * @return boolean 514 */ 515 public static function is_enabled() { 516 return true; 517 } 518 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body