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 badge earned badge award criteria type class
  19   *
  20   * @package    core
  21   * @subpackage badges
  22   * @copyright  2017 Stephen Bourget
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * Badge award criteria -- award on badge completion
  30   *
  31   * @package    core
  32   * @subpackage badges
  33   * @copyright  2017 Stephen Bourget
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class award_criteria_badge extends award_criteria {
  37  
  38      /* @var int Criteria [BADGE_CRITERIA_TYPE_BADGE] */
  39      public $criteriatype = BADGE_CRITERIA_TYPE_BADGE;
  40  
  41      public $required_param = 'badge';
  42      public $optional_params = array();
  43  
  44      /**
  45       * Get criteria details for displaying to users
  46       * @param string $short Print short version of criteria
  47       * @return string
  48       */
  49      public function get_details($short = '') {
  50          global $DB, $OUTPUT;
  51          $output = array();
  52          foreach ($this->params as $p) {
  53              $badgename = $DB->get_field('badge', 'name', array('id' => $p['badge']));
  54              if (!$badgename) {
  55                  $str = $OUTPUT->error_text(get_string('error:nosuchbadge', 'badges'));
  56              } else {
  57                  $str = html_writer::tag('b', '"' . $badgename . '"');
  58              }
  59              $output[] = $str;
  60          }
  61  
  62          if ($short) {
  63              return implode(', ', $output);
  64          } else {
  65              return html_writer::alist($output, array(), 'ul');
  66          }
  67      }
  68  
  69      /**
  70       * Add appropriate new criteria options to the form
  71       * @param object $mform moodle form
  72       */
  73      public function get_options(&$mform) {
  74          global $DB;
  75          $none = false;
  76          $availablebadges = null;
  77  
  78          $mform->addElement('header', 'first_header', $this->get_title());
  79          $mform->addHelpButton('first_header', 'criteria_' . $this->criteriatype, 'badges');
  80  
  81          // Determine if this badge is a course badge or a site badge.
  82          $thisbadge = $DB->get_record('badge', array('id' => $this->badgeid));
  83  
  84          if ($thisbadge->type == BADGE_TYPE_SITE) {
  85              // Only list site badges that are enabled.
  86              $select = " type = :site AND (status = :status1 OR status = :status2)";
  87              $params = array('site' => BADGE_TYPE_SITE,
  88                              'status1' => BADGE_STATUS_ACTIVE,
  89                              'status2' => BADGE_STATUS_ACTIVE_LOCKED);
  90              $availablebadges = $DB->get_records_select_menu('badge', $select, $params, 'name ASC', 'id, name');
  91  
  92          } else if ($thisbadge->type == BADGE_TYPE_COURSE) {
  93              // List both site badges and course badges belonging to this course.
  94              $select = " (type = :site OR (type = :course AND courseid = :courseid)) AND (status = :status1 OR status = :status2)";
  95              $params = array('site' => BADGE_TYPE_SITE,
  96                              'course' => BADGE_TYPE_COURSE,
  97                              'courseid' => $thisbadge->courseid,
  98                              'status1' => BADGE_STATUS_ACTIVE,
  99                              'status2' => BADGE_STATUS_ACTIVE_LOCKED);
 100              $availablebadges = $DB->get_records_select_menu('badge', $select, $params, 'name ASC', 'id, name');
 101          }
 102          if (!empty($availablebadges)) {
 103              $select = array();
 104              $selected = array();
 105              foreach ($availablebadges as $bid => $badgename) {
 106                  if ($bid != $this->badgeid) {
 107                      // Do not let it use itself as criteria.
 108                      $select[$bid] = format_string($badgename, true);
 109                  }
 110              }
 111  
 112              if ($this->id !== 0) {
 113                  $selected = array_keys($this->params);
 114              }
 115              $settings = array('multiple' => 'multiple', 'size' => 20, 'class' => 'selectbadge', 'required' => 'required');
 116              $mform->addElement('select', 'badge_badges', get_string('addbadge', 'badges'), $select, $settings);
 117              $mform->addRule('badge_badges', get_string('requiredbadge', 'badges'), 'required');
 118              $mform->addHelpButton('badge_badges', 'addbadge', 'badges');
 119  
 120              if ($this->id !== 0) {
 121                  $mform->setDefault('badge_badges', $selected);
 122              }
 123          } else {
 124              $mform->addElement('static', 'nobadges', '', get_string('error:nobadges', 'badges'));
 125              $none = true;
 126          }
 127  
 128          // Add aggregation.
 129          if (!$none) {
 130              $mform->addElement('header', 'aggregation', get_string('method', 'badges'));
 131              $agg = array();
 132              $agg[] =& $mform->createElement('radio', 'agg', '', get_string('allmethodbadges', 'badges'), 1);
 133              $agg[] =& $mform->createElement('radio', 'agg', '', get_string('anymethodbadges', 'badges'), 2);
 134              $mform->addGroup($agg, 'methodgr', '', array('<br/>'), false);
 135              if ($this->id !== 0) {
 136                  $mform->setDefault('agg', $this->method);
 137              } else {
 138                  $mform->setDefault('agg', BADGE_CRITERIA_AGGREGATION_ANY);
 139              }
 140          }
 141  
 142          return array($none, get_string('noparamstoadd', 'badges'));
 143      }
 144  
 145      /**
 146       * Save criteria records
 147       *
 148       * @param array $params Values from the form or any other array.
 149       */
 150      public function save($params = array()) {
 151          $badges = $params['badge_badges'];
 152          unset($params['badge_badges']);
 153          foreach ($badges as $badgeid) {
 154              $params["badge_{$badgeid}"] = $badgeid;
 155          }
 156  
 157          parent::save($params);
 158      }
 159  
 160      /**
 161       * Review this criteria and decide if it has been completed
 162       *
 163       * @param int $userid User whose criteria completion needs to be reviewed.
 164       * @param bool $filtered An additional parameter indicating that user list
 165       *        has been reduced and some expensive checks can be skipped.
 166       *
 167       * @return bool Whether criteria is complete.
 168       */
 169      public function review($userid, $filtered = false) {
 170  
 171          global $DB;
 172          $overall = false;
 173  
 174          foreach ($this->params as $param) {
 175              $badge = $DB->get_record('badge', array('id' => $param['badge']));
 176              // See if the user has earned this badge.
 177              $awarded = $DB->get_record('badge_issued', array('badgeid' => $param['badge'], 'userid' => $userid));
 178  
 179              // Extra check in case a badge was deleted while this badge is still active.
 180              if (!$badge) {
 181                  if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) {
 182                      return false;
 183                  } else {
 184                      continue;
 185                  }
 186              }
 187  
 188              if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) {
 189  
 190                  if ($awarded) {
 191                      $overall = true;
 192                      continue;
 193                  } else {
 194                      return false;
 195                  }
 196              } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
 197                  if ($awarded) {
 198                      return true;
 199                  } else {
 200                      $overall = false;
 201                      continue;
 202                  }
 203              }
 204          }
 205  
 206          return $overall;
 207      }
 208  
 209      /**
 210       * Checks criteria for any major problems.
 211       *
 212       * @return array A list containing status and an error message (if any).
 213       */
 214      public function validate() {
 215          global $DB;
 216          $params = array_keys($this->params);
 217          $method = ($this->method == BADGE_CRITERIA_AGGREGATION_ALL);
 218          $singleparam = (count($params) == 1);
 219  
 220          foreach ($params as $param) {
 221              // Perform check if there only one parameter with any type of aggregation,
 222              // Or there are more than one parameter with aggregation ALL.
 223  
 224              if (($singleparam || $method) && !$DB->record_exists('badge', array('id' => $param))) {
 225                  return array(false, get_string('error:invalidparambadge', 'badges'));
 226              }
 227          }
 228  
 229          return array(true, '');
 230      }
 231  
 232      /**
 233       * Returns array with sql code and parameters returning all ids
 234       * of users who meet this particular criterion.
 235       *
 236       * @return array list($join, $where, $params)
 237       */
 238      public function get_completed_criteria_sql() {
 239          $join = '';
 240          $where = '';
 241          $params = array();
 242  
 243          if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
 244              // User has received ANY of the required badges.
 245              $join = " LEFT JOIN {badge_issued} bi2 ON bi2.userid = u.id";
 246              $i = 0;
 247              foreach ($this->params as $param) {
 248                  if ($i == 0) {
 249                      $where .= ' bi2.badgeid = :badgeid'.$i;
 250                  } else {
 251                      $where .= ' OR bi2.badgeid = :badgeid'.$i;
 252                  }
 253                  $params['badgeid'.$i] = $param['badge'];
 254                  $i++;
 255              }
 256              // MDL-66032 Do not create expression if there are no badges in criteria.
 257              if (!empty($where)) {
 258                  $where = ' AND (' . $where . ') ';
 259              }
 260              return array($join, $where, $params);
 261          } else {
 262              // User has received ALL of the required badges.
 263              $join = " LEFT JOIN {badge_issued} bi2 ON bi2.userid = u.id";
 264              $i = 0;
 265              foreach ($this->params as $param) {
 266                  $i++;
 267                  $where = ' AND bi2.badgeid = :badgeid'.$i;
 268                  $params['badgeid'.$i] = $param['badge'];
 269              }
 270              return array($join, $where, $params);
 271          }
 272      }
 273  }