Merge branch 'MDL-78684-402' of https://github.com/andelacruz/moodle into MOODLE_402_...
[moodle.git] / badges / criteria / award_criteria_competency.php
blob288f7a22979fa4d3be7692b2720476642c699e0e
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.
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/>.
17 /**
18 * This file contains the badge earned badge award criteria type class
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
26 defined('MOODLE_INTERNAL') || die();
28 /**
29 * Badge award criteria -- award on competency completion
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
36 class award_criteria_competency extends award_criteria {
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 = [];
46 /**
47 * Get criteria details for displaying to users
48 * @param string $short Print short version of criteria
49 * @return string
51 public function get_details($short = '') {
52 $output = array();
54 foreach ($this->params as $p) {
55 $competency = new \core_competency\competency($p['competency']);
56 if ($short) {
57 $competency->set('description', '');
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);
68 \core_competency\api::check_enabled();
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>';
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.
81 public function get_options(&$mform) {
82 global $DB;
83 $none = false;
84 $availablebadges = null;
86 $mform->addElement('header', 'first_header', $this->get_title());
87 $mform->addHelpButton('first_header', 'criteria_' . $this->criteriatype, 'badges');
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));
94 $badge = $DB->get_record('badge', array('id' => $this->badgeid));
95 $context = null;
96 $courseid = 0;
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;
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');
112 $mform->getElement('competency_competencies')->setValue($competencies);
113 $mform->addRule('competency_competencies', get_string('requiredcompetency', 'badges'), 'required');
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);
129 return array($none, get_string('noparamstoadd', 'badges'));
133 * Save criteria records
135 * @param array $params Values from the form or any other array.
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);
143 foreach ($competencies as $competencyid) {
144 $params["competency_{$competencyid}"] = $competencyid;
146 parent::save($params);
150 * Review this criteria and decide if it has been completed
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.
156 * @return bool Whether criteria is complete.
158 public function review($userid, $filtered = false) {
159 global $DB;
161 $overall = false;
162 $competencyids = [];
164 if (!self::is_enabled()) {
165 return false;
167 foreach ($this->params as $param) {
168 $competencyids[] = $param['competency'];
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);
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;
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');
192 if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) {
193 if (!$proficiency) {
194 return false;
196 } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
197 if ($proficiency) {
198 return true;
203 return $overall;
207 * Returns array with sql code and parameters returning all ids
208 * of users who meet this particular criterion.
210 * @return array list($join, $where, $params)
212 public function get_completed_criteria_sql() {
213 global $DB;
215 $join = '';
216 $where = '';
217 $params = [];
218 $competencyids = [];
220 $badge = $DB->get_record('badge', array('id' => $this->badgeid));
222 if (!self::is_enabled()) {
223 return array($join, $where, $params);
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'];
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;
241 $where .= ' AND uc2.proficiency = :isproficient ';
242 $params['isproficient'] = true;
243 } else {
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'];
253 $where .= ' AND uc' . $joincount . '.userid = u.id';
254 $where .= ' AND uc' . $joincount . '.proficiency = :isproficient' . $joincount;
255 $params['isproficient' . $joincount] = true;
259 return array($join, $where, $params);
263 * Hide this criteria when competencies are disabled.
265 * @return boolean
267 public static function is_enabled() {
268 return \core_competency\api::is_enabled();
272 * Check if any badge has records for competencies.
274 * @param array $competencyids Array of competencies ids.
275 * @return boolean Return true if competencies were found in any badge.
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;
286 return self::record_exists_sql($sql, $params);