Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 39 and 311]

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