PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/badges/criteria/award_criteria.php

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