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   * Classes to manage manual badge award.
  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  
  29  require_once($CFG->libdir . '/badgeslib.php');
  30  require_once($CFG->dirroot . '/user/selector/lib.php');
  31  
  32  abstract class badge_award_selector_base extends user_selector_base {
  33  
  34      /**
  35       * The id of the badge this selector is being used for
  36       * @var int
  37       */
  38      protected $badgeid = null;
  39      /**
  40       * The context of the badge this selector is being used for
  41       * @var object
  42       */
  43      protected $context = null;
  44      /**
  45       * The id of the role of badge issuer in current context
  46       * @var int
  47       */
  48      protected $issuerrole = null;
  49      /**
  50       * The id of badge issuer
  51       * @var int
  52       */
  53      protected $issuerid = null;
  54  
  55      /**
  56       * The return address. Accepts either a string or a moodle_url.
  57       * @var string $url
  58       */
  59      public $url;
  60  
  61      /**
  62       * The current group being displayed.
  63       * @var int $currentgroup
  64       */
  65      public $currentgroup;
  66  
  67      /**
  68       * Constructor method
  69       * @param string $name
  70       * @param array $options
  71       */
  72      public function __construct($name, array $options) {
  73          $options['accesscontext'] = $options['context'];
  74          parent::__construct($name, $options);
  75          if (isset($options['context'])) {
  76              if ($options['context'] instanceof context_system) {
  77                  // If it is a site badge, we need to get context of frontpage.
  78                  $this->context = context_course::instance(SITEID);
  79              } else {
  80                  $this->context = $options['context'];
  81              }
  82          }
  83          if (isset($options['badgeid'])) {
  84              $this->badgeid = $options['badgeid'];
  85          }
  86          if (isset($options['issuerid'])) {
  87              $this->issuerid = $options['issuerid'];
  88          }
  89          if (isset($options['issuerrole'])) {
  90              $this->issuerrole = $options['issuerrole'];
  91          }
  92          if (isset($options['url'])) {
  93              $this->url = $options['url'];
  94          }
  95          if (isset($options['currentgroup'])) {
  96              $this->currentgroup = $options['currentgroup'];
  97          } else {
  98              // Returns group active in course, changes the group by default if 'group' page param present.
  99              $this->currentgroup = groups_get_course_group($COURSE, true);
 100          }
 101      }
 102  
 103      /**
 104       * Returns an array of options to seralise and store for searches
 105       *
 106       * @return array
 107       */
 108      protected function get_options() {
 109          global $CFG;
 110          $options = parent::get_options();
 111          $options['file'] =  'badges/lib/awardlib.php';
 112          $options['context'] = $this->context;
 113          $options['badgeid'] = $this->badgeid;
 114          $options['issuerid'] = $this->issuerid;
 115          $options['issuerrole'] = $this->issuerrole;
 116          // These will be used to filter potential badge recipients when searching.
 117          $options['currentgroup'] = $this->currentgroup;
 118          return $options;
 119      }
 120  
 121      /**
 122       * Restricts the selection of users to display, according to the groups they belong.
 123       *
 124       * @return array
 125       */
 126      protected function get_groups_sql() {
 127          $groupsql = '';
 128          $groupwheresql = '';
 129          $groupwheresqlparams = array();
 130          if ($this->currentgroup) {
 131              $groupsql = ' JOIN {groups_members} gm ON gm.userid = u.id ';
 132              $groupwheresql = ' AND gm.groupid = :gr_grpid ';
 133              $groupwheresqlparams = array('gr_grpid' => $this->currentgroup);
 134          }
 135          return array($groupsql, $groupwheresql, $groupwheresqlparams);
 136      }
 137  }
 138  
 139  /**
 140   * A user selector control for potential users to award badge
 141   */
 142  class badge_potential_users_selector extends badge_award_selector_base {
 143      const MAX_USERS_PER_PAGE = 100;
 144  
 145      /**
 146       * Existing recipients
 147       */
 148      protected $existingrecipients = array();
 149  
 150      /**
 151       * Finds all potential badge recipients
 152       *
 153       * Potential badge recipients are all enroled users
 154       * who haven't got a badge from current issuer role.
 155       *
 156       * @param string $search
 157       * @return array
 158       */
 159      public function find_users($search) {
 160          global $DB;
 161  
 162          $whereconditions = array();
 163          list($wherecondition, $params) = $this->search_sql($search, 'u');
 164          if ($wherecondition) {
 165              $whereconditions[] = $wherecondition;
 166          }
 167  
 168          $existingids = array();
 169          foreach ($this->existingrecipients as $group) {
 170              foreach ($group as $user) {
 171                  $existingids[] = $user->id;
 172              }
 173          }
 174          if ($existingids) {
 175              list($usertest, $userparams) = $DB->get_in_or_equal($existingids, SQL_PARAMS_NAMED, 'ex', false);
 176              $whereconditions[] = 'u.id ' . $usertest;
 177              $params = array_merge($params, $userparams);
 178          }
 179  
 180          if ($whereconditions) {
 181              $wherecondition = ' WHERE ' . implode(' AND ', $whereconditions);
 182          }
 183  
 184          list($groupsql, $groupwheresql, $groupwheresqlparams) = $this->get_groups_sql();
 185  
 186          list($esql, $eparams) = get_enrolled_sql($this->context, 'moodle/badges:earnbadge', 0, true);
 187          $params = array_merge($params, $eparams, $groupwheresqlparams);
 188  
 189          $fields      = 'SELECT ' . $this->required_fields_sql('u');
 190          $countfields = 'SELECT COUNT(u.id)';
 191  
 192          $params['badgeid'] = $this->badgeid;
 193          $params['issuerrole'] = $this->issuerrole;
 194  
 195          $sql = " FROM {user} u JOIN ($esql) je ON je.id = u.id
 196                   LEFT JOIN {badge_manual_award} bm
 197                       ON (bm.recipientid = u.id AND bm.badgeid = :badgeid AND bm.issuerrole = :issuerrole)
 198                   $groupsql
 199                   $wherecondition AND bm.id IS NULL
 200                   $groupwheresql";
 201  
 202          list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext);
 203          $order = ' ORDER BY ' . $sort;
 204  
 205          if (!$this->is_validating()) {
 206              $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
 207              if ($potentialmemberscount > self::MAX_USERS_PER_PAGE) {
 208                  return $this->too_many_results($search, $potentialmemberscount);
 209              }
 210          }
 211  
 212          $availableusers = $DB->get_records_sql($fields . $sql . $order, array_merge($params, $sortparams));
 213  
 214          if (empty($availableusers)) {
 215              return array();
 216          }
 217  
 218          return array(get_string('potentialrecipients', 'badges') => $availableusers);
 219      }
 220  
 221      /**
 222       * Sets the existing recipients
 223       * @param array $users
 224       */
 225      public function set_existing_recipients(array $users) {
 226          $this->existingrecipients = $users;
 227      }
 228  }
 229  
 230  /**
 231   * A user selector control for existing users to award badge
 232   */
 233  class badge_existing_users_selector extends badge_award_selector_base {
 234  
 235      /**
 236       * Finds all users who already have been awarded a badge by current role
 237       *
 238       * @param string $search
 239       * @return array
 240       */
 241      public function find_users($search) {
 242          global $DB;
 243          list($wherecondition, $params) = $this->search_sql($search, 'u');
 244          $params['badgeid'] = $this->badgeid;
 245          $params['issuerrole'] = $this->issuerrole;
 246  
 247          list($esql, $eparams) = get_enrolled_sql($this->context, 'moodle/badges:earnbadge', 0, true);
 248          $fields = $this->required_fields_sql('u');
 249          list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext);
 250  
 251          list($groupsql, $groupwheresql, $groupwheresqlparams) = $this->get_groups_sql();
 252  
 253          $params = array_merge($params, $eparams, $sortparams, $groupwheresqlparams);
 254          $recipients = $DB->get_records_sql("SELECT $fields
 255                  FROM {user} u
 256                  JOIN ($esql) je ON je.id = u.id
 257                  JOIN {badge_manual_award} s ON s.recipientid = u.id
 258                  $groupsql
 259                  WHERE $wherecondition AND s.badgeid = :badgeid AND s.issuerrole = :issuerrole
 260                  $groupwheresql
 261                  ORDER BY $sort", $params);
 262  
 263          return array(get_string('existingrecipients', 'badges') => $recipients);
 264      }
 265  }
 266  
 267  function process_manual_award($recipientid, $issuerid, $issuerrole, $badgeid) {
 268      global $DB;
 269      $params = array(
 270                  'badgeid' => $badgeid,
 271                  'issuerid' => $issuerid,
 272                  'issuerrole' => $issuerrole,
 273                  'recipientid' => $recipientid
 274              );
 275  
 276      if (!$DB->record_exists('badge_manual_award', $params)) {
 277          $award = new stdClass();
 278          $award->badgeid = $badgeid;
 279          $award->issuerid = $issuerid;
 280          $award->issuerrole = $issuerrole;
 281          $award->recipientid = $recipientid;
 282          $award->datemet = time();
 283          if ($DB->insert_record('badge_manual_award', $award)) {
 284              return true;
 285          }
 286      }
 287      return false;
 288  }
 289  
 290  /**
 291   * Manually revoke awarded badges.
 292   *
 293   * @param int $recipientid
 294   * @param int $issuerid
 295   * @param int $issuerrole
 296   * @param int $badgeid
 297   * @return bool
 298   */
 299  function process_manual_revoke($recipientid, $issuerid, $issuerrole, $badgeid) {
 300      global $DB;
 301      $params = array(
 302                  'badgeid' => $badgeid,
 303                  'issuerid' => $issuerid,
 304                  'issuerrole' => $issuerrole,
 305                  'recipientid' => $recipientid
 306              );
 307      if ($DB->record_exists('badge_manual_award', $params)) {
 308          if ($DB->delete_records('badge_manual_award', array('badgeid' => $badgeid,
 309                                                              'issuerid' => $issuerid,
 310                                                              'recipientid' => $recipientid))
 311              && $DB->delete_records('badge_issued', array('badgeid' => $badgeid,
 312                                                        'userid' => $recipientid))) {
 313  
 314              // Trigger event, badge revoked.
 315              $badge = new \badge($badgeid);
 316              $eventparams = array(
 317                  'objectid' => $badgeid,
 318                  'relateduserid' => $recipientid,
 319                  'context' => $badge->get_context()
 320              );
 321              $event = \core\event\badge_revoked::create($eventparams);
 322              $event->trigger();
 323  
 324              return true;
 325          }
 326      } else {
 327          throw new moodle_exception('error:badgenotfound', 'badges');
 328      }
 329      return false;
 330  }