Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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 courseset completion 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 ('award_criteria_course.php');
  29  require_once($CFG->libdir . '/completionlib.php');
  30  require_once($CFG->dirroot . '/grade/querylib.php');
  31  require_once($CFG->libdir . '/gradelib.php');
  32  
  33  /**
  34   * Badge award criteria -- award on courseset completion
  35   *
  36   */
  37  class award_criteria_courseset extends award_criteria {
  38  
  39      /* @var int Criteria [BADGE_CRITERIA_TYPE_COURSESET] */
  40      public $criteriatype = BADGE_CRITERIA_TYPE_COURSESET;
  41  
  42      public $required_param = 'course';
  43      public $optional_params = array('grade', 'bydate');
  44  
  45      /**
  46       * Get criteria details for displaying to users
  47       *
  48       * @return string
  49       */
  50      public function get_details($short = '') {
  51          global $DB, $OUTPUT;
  52          $output = array();
  53          foreach ($this->params as $p) {
  54              $coursename = $DB->get_field('course', 'fullname', array('id' => $p['course']));
  55              if (!$coursename) {
  56                  $str = $OUTPUT->error_text(get_string('error:nosuchcourse', 'badges'));
  57              } else {
  58                  $str = html_writer::tag('b', '"' . $coursename . '"');
  59                  if (isset($p['bydate'])) {
  60                      $str .= get_string('criteria_descr_bydate', 'badges', userdate($p['bydate'], get_string('strftimedate', 'core_langconfig')));
  61                  }
  62                  if (isset($p['grade'])) {
  63                      $str .= get_string('criteria_descr_grade', 'badges', $p['grade']);
  64                  }
  65              }
  66              $output[] = $str;
  67          }
  68  
  69          if ($short) {
  70              return implode(', ', $output);
  71          } else {
  72              return html_writer::alist($output, array(), 'ul');
  73          }
  74      }
  75  
  76      public function get_courses(&$mform) {
  77          global $DB, $CFG;
  78          require_once($CFG->dirroot . '/course/lib.php');
  79          $buttonarray = array();
  80  
  81          $hasselectablecourses = core_course_category::search_courses(['onlywithcompletion' => true], ['limit' => 1]);
  82          if ($hasselectablecourses) {
  83              $settings = array('multiple' => 'multiple', 'onlywithcompletion' => 1);
  84              $mform->addElement('course', 'courses', get_string('addcourse', 'badges'), $settings);
  85              $mform->addRule('courses', get_string('requiredcourse', 'badges'), 'required');
  86              $mform->addHelpButton('courses', 'addcourse', 'badges');
  87  
  88              $buttonarray[] =& $mform->createElement('submit', 'submitcourse', get_string('addcourse', 'badges'));
  89              $buttonarray[] =& $mform->createElement('submit', 'cancel', get_string('cancel'));
  90              $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
  91  
  92              $mform->addElement('hidden', 'addcourse', 'addcourse');
  93              $mform->setType('addcourse', PARAM_TEXT);
  94              if ($this->id !== 0) {
  95                  $selected = array_keys($this->params);
  96                  $mform->setDefault('courses', $selected);
  97              }
  98              $mform->setType('agg', PARAM_INT);
  99          } else {
 100              $mform->addElement('static', 'nocourses', '', get_string('error:nocourses', 'badges'));
 101              $buttonarray[] =& $mform->createElement('submit', 'cancel', get_string('continue'));
 102              $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
 103          }
 104      }
 105  
 106      public function add_courses($params = array()) {
 107          global $DB;
 108          $t = $DB->start_delegated_transaction();
 109          if ($this->id !== 0) {
 110              $critid = $this->id;
 111          } else {
 112              $fordb = new stdClass();
 113              $fordb->criteriatype = $this->criteriatype;
 114              $fordb->method = BADGE_CRITERIA_AGGREGATION_ALL;
 115              $fordb->badgeid = $this->badgeid;
 116              $critid = $DB->insert_record('badge_criteria', $fordb, true, true);
 117          }
 118          if ($critid) {
 119              foreach ($params as $p) {
 120                  $newp = new stdClass();
 121                  $newp->critid = $critid;
 122                  $newp->name = 'course_' . $p;
 123                  $newp->value = $p;
 124                  if (!$DB->record_exists('badge_criteria_param', array('critid' => $critid, 'name' => $newp->name))) {
 125                      $DB->insert_record('badge_criteria_param', $newp, false, true);
 126                  }
 127              }
 128          }
 129          $t->allow_commit();
 130          return $critid;
 131      }
 132  
 133      /**
 134       * Add appropriate new criteria options to the form
 135       *
 136       */
 137      public function get_options(&$mform) {
 138          global $DB;
 139          $none = true;
 140  
 141          $mform->addElement('header', 'first_header', $this->get_title());
 142          $mform->addHelpButton('first_header', 'criteria_' . $this->criteriatype, 'badges');
 143  
 144          if ($courses = $DB->get_records('course', array('enablecompletion' => COMPLETION_ENABLED))) {
 145              $mform->addElement('submit', 'addcourse', get_string('addcourse', 'badges'), array('class' => 'addcourse'));
 146          }
 147  
 148          // In courseset, print out only the ones that were already selected.
 149          foreach ($this->params as $p) {
 150              if ($course = $DB->get_record('course', array('id' => $p['course']))) {
 151                  $coursecontext = context_course::instance($course->id);
 152                  $param = array(
 153                          'id' => $course->id,
 154                          'checked' => true,
 155                          'name' => format_string($course->fullname, true, array('context' => $coursecontext)),
 156                          'error' => false
 157                  );
 158  
 159                  if (isset($p['bydate'])) {
 160                      $param['bydate'] = $p['bydate'];
 161                  }
 162                  if (isset($p['grade'])) {
 163                      $param['grade'] = $p['grade'];
 164                  }
 165                  $this->config_options($mform, $param);
 166                  $none = false;
 167              } else {
 168                  $this->config_options($mform, array('id' => $p['course'], 'checked' => true,
 169                          'name' => get_string('error:nosuchcourse', 'badges'), 'error' => true));
 170              }
 171          }
 172  
 173          // Add aggregation.
 174          if (!$none) {
 175              $mform->addElement('header', 'aggregation', get_string('method', 'badges'));
 176              $agg = array();
 177              $agg[] =& $mform->createElement('radio', 'agg', '', get_string('allmethodcourseset', 'badges'), 1);
 178              $agg[] =& $mform->createElement('radio', 'agg', '', get_string('anymethodcourseset', 'badges'), 2);
 179              $mform->addGroup($agg, 'methodgr', '', array('<br/>'), false);
 180              if ($this->id !== 0) {
 181                  $mform->setDefault('agg', $this->method);
 182              } else {
 183                  $mform->setDefault('agg', BADGE_CRITERIA_AGGREGATION_ANY);
 184              }
 185          }
 186  
 187          return array($none, get_string('noparamstoadd', 'badges'));
 188      }
 189  
 190      /**
 191       * Review this criteria and decide if it has been completed
 192       *
 193       * @param int $userid User whose criteria completion needs to be reviewed.
 194       * @param bool $filtered An additional parameter indicating that user list
 195       *        has been reduced and some expensive checks can be skipped.
 196       *
 197       * @return bool Whether criteria is complete
 198       */
 199      public function review($userid, $filtered = false) {
 200          foreach ($this->params as $param) {
 201              $course =  new stdClass();
 202              $course->id = $param['course'];
 203  
 204              $info = new completion_info($course);
 205              $check_grade = true;
 206              $check_date = true;
 207  
 208              if (isset($param['grade'])) {
 209                  $grade = grade_get_course_grade($userid, $course->id);
 210                  $check_grade = ($grade->grade >= $param['grade']);
 211              }
 212  
 213              if (!$filtered && isset($param['bydate'])) {
 214                  $cparams = array(
 215                          'userid' => $userid,
 216                          'course' => $course->id,
 217                  );
 218                  $completion = new completion_completion($cparams);
 219                  $date = $completion->timecompleted;
 220                  $check_date = ($date <= $param['bydate']);
 221              }
 222  
 223              $overall = false;
 224              if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) {
 225                  if ($info->is_course_complete($userid) && $check_grade && $check_date) {
 226                      $overall = true;
 227                      continue;
 228                  } else {
 229                      return false;
 230                  }
 231              } else {
 232                  if ($info->is_course_complete($userid) && $check_grade && $check_date) {
 233                      return true;
 234                  } else {
 235                      $overall = false;
 236                      continue;
 237                  }
 238              }
 239          }
 240  
 241          return $overall;
 242      }
 243  
 244      /**
 245       * Returns array with sql code and parameters returning all ids
 246       * of users who meet this particular criterion.
 247       *
 248       * @return array list($join, $where, $params)
 249       */
 250      public function get_completed_criteria_sql() {
 251          $join = '';
 252          $where = '';
 253          $params = array();
 254  
 255          if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
 256              foreach ($this->params as $param) {
 257                  $coursedata[] = " cc.course = :completedcourse{$param['course']} ";
 258                  $params["completedcourse{$param['course']}"] = $param['course'];
 259              }
 260              if (!empty($coursedata)) {
 261                  $extraon = implode(' OR ', $coursedata);
 262                  $join = " JOIN {course_completions} cc ON cc.userid = u.id AND
 263                            cc.timecompleted > 0 AND ({$extraon})";
 264              }
 265              return array($join, $where, $params);
 266          } else {
 267              foreach ($this->params as $param) {
 268                  $join .= " LEFT JOIN {course_completions} cc{$param['course']} ON
 269                            cc{$param['course']}.userid = u.id AND
 270                            cc{$param['course']}.course = :completedcourse{$param['course']} AND
 271                            cc{$param['course']}.timecompleted > 0 ";
 272                  $where .= " AND cc{$param['course']}.course IS NOT NULL ";
 273                  $params["completedcourse{$param['course']}"] = $param['course'];
 274              }
 275              return array($join, $where, $params);
 276          }
 277      }
 278  }