Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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 profile 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($CFG->dirroot . "/user/lib.php");
  29  
  30  /**
  31   * Profile completion badge award criteria
  32   *
  33   */
  34  class award_criteria_profile extends award_criteria {
  35  
  36      /* @var int Criteria [BADGE_CRITERIA_TYPE_PROFILE] */
  37      public $criteriatype = BADGE_CRITERIA_TYPE_PROFILE;
  38  
  39      public $required_param = 'field';
  40      public $optional_params = array();
  41  
  42      /* @var array The default profile fields allowed to be used as award criteria.
  43       *
  44       * Note: This is used instead of user_get_default_fields(), because it is not possible to
  45       * determine which fields the user can modify.
  46       */
  47      protected $allowed_default_fields = [
  48          'firstname',
  49          'lastname',
  50          'email',
  51          'address',
  52          'phone1',
  53          'phone2',
  54          'icq',
  55          'skype',
  56          'yahoo',
  57          'aim',
  58          'msn',
  59          'department',
  60          'institution',
  61          'description',
  62          'picture',
  63          'city',
  64          'url',
  65          'country',
  66      ];
  67  
  68      /**
  69       * Add appropriate new criteria options to the form
  70       *
  71       */
  72      public function get_options(&$mform) {
  73          global $DB;
  74  
  75          $none = true;
  76          $existing = array();
  77          $missing = array();
  78          $dfields = $this->allowed_default_fields;
  79  
  80          $sql = "SELECT uf.id as fieldid, uf.name as name, ic.id as categoryid, ic.name as categoryname, uf.datatype
  81                  FROM {user_info_field} uf
  82                  JOIN {user_info_category} ic
  83                  ON uf.categoryid = ic.id AND uf.visible <> 0
  84                  ORDER BY ic.sortorder ASC, uf.sortorder ASC";
  85  
  86          // Get custom fields.
  87          $cfields = $DB->get_records_sql($sql);
  88          $cfids = array_map(function($o) {
  89              return $o->fieldid;
  90          }, $cfields);
  91  
  92          if ($this->id !== 0) {
  93              $existing = array_keys($this->params);
  94              $missing = array_diff($existing, array_merge($dfields, $cfids));
  95          }
  96  
  97          if (!empty($missing)) {
  98              $mform->addElement('header', 'category_errors', get_string('criterror', 'badges'));
  99              $mform->addHelpButton('category_errors', 'criterror', 'badges');
 100              foreach ($missing as $m) {
 101                  $this->config_options($mform, array('id' => $m, 'checked' => true, 'name' => get_string('error:nosuchfield', 'badges'), 'error' => true));
 102                  $none = false;
 103              }
 104          }
 105  
 106          if (!empty($dfields)) {
 107              $mform->addElement('header', 'first_header', $this->get_title());
 108              $mform->addHelpButton('first_header', 'criteria_' . $this->criteriatype, 'badges');
 109              foreach ($dfields as $field) {
 110                  $checked = false;
 111                  if (in_array($field, $existing)) {
 112                      $checked = true;
 113                  }
 114                  $this->config_options($mform, array('id' => $field, 'checked' => $checked, 'name' => get_user_field_name($field), 'error' => false));
 115                  $none = false;
 116              }
 117          }
 118  
 119          if (!empty($cfields)) {
 120              foreach ($cfields as $field) {
 121                  if (!isset($currentcat) || $currentcat != $field->categoryid) {
 122                      $currentcat = $field->categoryid;
 123                      $mform->addElement('header', 'category_' . $currentcat, format_string($field->categoryname));
 124                  }
 125                  $checked = false;
 126                  if (in_array($field->fieldid, $existing)) {
 127                      $checked = true;
 128                  }
 129                  $this->config_options($mform, array('id' => $field->fieldid, 'checked' => $checked, 'name' => $field->name, 'error' => false));
 130                  $none = false;
 131              }
 132          }
 133  
 134          // Add aggregation.
 135          if (!$none) {
 136              $mform->addElement('header', 'aggregation', get_string('method', 'badges'));
 137              $agg = array();
 138              $agg[] =& $mform->createElement('radio', 'agg', '', get_string('allmethodprofile', 'badges'), 1);
 139              $agg[] =& $mform->createElement('static', 'none_break', null, '<br/>');
 140              $agg[] =& $mform->createElement('radio', 'agg', '', get_string('anymethodprofile', 'badges'), 2);
 141              $mform->addGroup($agg, 'methodgr', '', array(' '), false);
 142              if ($this->id !== 0) {
 143                  $mform->setDefault('agg', $this->method);
 144              } else {
 145                  $mform->setDefault('agg', BADGE_CRITERIA_AGGREGATION_ANY);
 146              }
 147          }
 148  
 149          return array($none, get_string('noparamstoadd', 'badges'));
 150      }
 151  
 152      /**
 153       * Get criteria details for displaying to users
 154       *
 155       * @return string
 156       */
 157      public function get_details($short = '') {
 158          global $DB, $OUTPUT;
 159          $output = array();
 160          foreach ($this->params as $p) {
 161              if (is_numeric($p['field'])) {
 162                  $str = $DB->get_field('user_info_field', 'name', array('id' => $p['field']));
 163              } else {
 164                  $str = get_user_field_name($p['field']);
 165              }
 166              if (!$str) {
 167                  $output[] = $OUTPUT->error_text(get_string('error:nosuchfield', 'badges'));
 168              } else {
 169                  $output[] = $str;
 170              }
 171          }
 172  
 173          if ($short) {
 174              return implode(', ', $output);
 175          } else {
 176              return html_writer::alist($output, array(), 'ul');
 177          }
 178      }
 179  
 180      /**
 181       * Review this criteria and decide if it has been completed
 182       *
 183       * @param int $userid User whose criteria completion needs to be reviewed.
 184       * @param bool $filtered An additional parameter indicating that user list
 185       *        has been reduced and some expensive checks can be skipped.
 186       *
 187       * @return bool Whether criteria is complete
 188       */
 189      public function review($userid, $filtered = false) {
 190          global $DB;
 191  
 192          // Users were already filtered by criteria completion, no checks required.
 193          if ($filtered) {
 194              return true;
 195          }
 196  
 197          $join = '';
 198          $whereparts = array();
 199          $sqlparams = array();
 200          $rule = ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) ? ' OR ' : ' AND ';
 201  
 202          foreach ($this->params as $param) {
 203              if (is_numeric($param['field'])) {
 204                  // This is a custom field.
 205                  $idx = count($whereparts) + 1;
 206                  $join .= " LEFT JOIN {user_info_data} uid{$idx} ON uid{$idx}.userid = u.id AND uid{$idx}.fieldid = :fieldid{$idx} ";
 207                  $sqlparams["fieldid{$idx}"] = $param['field'];
 208                  $whereparts[] = "uid{$idx}.id IS NOT NULL";
 209              } else if (in_array($param['field'], $this->allowed_default_fields)) {
 210                  // This is a valid field from {user} table.
 211                  if ($param['field'] == 'picture') {
 212                      // The picture field is numeric and requires special handling.
 213                      $whereparts[] = "u.{$param['field']} != 0";
 214                  } else {
 215                      $whereparts[] = $DB->sql_isnotempty('u', "u.{$param['field']}", false, true);
 216                  }
 217              }
 218          }
 219  
 220          $sqlparams['userid'] = $userid;
 221  
 222          if ($whereparts) {
 223              $where = " AND (" . implode($rule, $whereparts) . ")";
 224          } else {
 225              $where = '';
 226          }
 227          $sql = "SELECT 1 FROM {user} u " . $join . " WHERE u.id = :userid $where";
 228          $overall = $DB->record_exists_sql($sql, $sqlparams);
 229  
 230          return $overall;
 231      }
 232  
 233      /**
 234       * Returns array with sql code and parameters returning all ids
 235       * of users who meet this particular criterion.
 236       *
 237       * @return array list($join, $where, $params)
 238       */
 239      public function get_completed_criteria_sql() {
 240          global $DB;
 241  
 242          $join = '';
 243          $whereparts = array();
 244          $params = array();
 245          $rule = ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) ? ' OR ' : ' AND ';
 246  
 247          foreach ($this->params as $param) {
 248              if (is_numeric($param['field'])) {
 249                  // This is a custom field.
 250                  $idx = count($whereparts);
 251                  $join .= " LEFT JOIN {user_info_data} uid{$idx} ON uid{$idx}.userid = u.id AND uid{$idx}.fieldid = :fieldid{$idx} ";
 252                  $params["fieldid{$idx}"] = $param['field'];
 253                  $whereparts[] = "uid{$idx}.id IS NOT NULL";
 254              } else if (in_array($param['field'], $this->allowed_default_fields)) {
 255                  // This is a valid field from {user} table.
 256                  if ($param['field'] == 'picture') {
 257                      // The picture field is numeric and requires special handling.
 258                      $whereparts[] = "u.{$param['field']} != 0";
 259                  } else {
 260                      $whereparts[] = $DB->sql_isnotempty('u', "u.{$param['field']}", false, true);
 261                  }
 262              }
 263          }
 264  
 265          if ($whereparts) {
 266              $where = " AND (" . implode($rule, $whereparts) . ")";
 267          } else {
 268              $where = '';
 269          }
 270          return array($join, $where, $params);
 271      }
 272  }