Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 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   * External badge renderable.
  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  namespace core_badges\output;
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  
  31  require_once($CFG->libdir . '/badgeslib.php');
  32  
  33  use renderable;
  34  use renderer_base;
  35  use stdClass;
  36  
  37  /**
  38   * An external badges for external.php page
  39   *
  40   * @copyright  2012 onwards Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
  41   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42   */
  43  class external_badge implements renderable {
  44      /** @var stdClass Issued badge */
  45      public $issued;
  46  
  47      /** @var int User ID */
  48      public $recipient;
  49  
  50      /** @var bool Validation of external badge */
  51      public $valid = true;
  52  
  53      /**
  54       * Initializes the badge to display
  55       *
  56       * @param stdClass $badge External badge information.
  57       * @param int $recipient User id.
  58       */
  59      public function __construct($badge, $recipient) {
  60          global $DB;
  61          // At this point a user has connected a backpack. So, we are going to get
  62          // their backpack email rather than their account email.
  63          $userfieldsapi = \core_user\fields::for_name();
  64          $namefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
  65          $user = $DB->get_record_sql("SELECT {$namefields}, b.email
  66                      FROM {user} u INNER JOIN {badge_backpack} b ON u.id = b.userid
  67                      WHERE b.userid = :userid", array('userid' => $recipient), IGNORE_MISSING);
  68  
  69          $this->issued = $badge;
  70          $this->recipient = $user;
  71  
  72          // Check if recipient is valid.
  73          // There is no way to be 100% sure that a badge belongs to a user.
  74          // Backpack does not return any recipient information.
  75          // All we can do is compare that backpack email hashed using salt
  76          // provided in the assertion matches a badge recipient from the assertion.
  77          if ($user) {
  78              if (isset($badge->assertion->recipient->identity)) {
  79                  $badge->assertion->salt = $badge->assertion->recipient->salt;
  80                  $badge->assertion->recipient = $badge->assertion->recipient->identity;
  81              }
  82              // Open Badges V2 does not even include a recipient.
  83              if (!isset($badge->assertion->recipient)) {
  84                  $this->valid = false;
  85              } else if (validate_email($badge->assertion->recipient) && $badge->assertion->recipient == $user->email) {
  86                  // If we have email, compare emails.
  87                  $this->valid = true;
  88              } else if ($badge->assertion->recipient == 'sha256$' . hash('sha256', $user->email)) {
  89                  // If recipient is hashed, but no salt, compare hashes without salt.
  90                  $this->valid = true;
  91              } else if ($badge->assertion->recipient == 'sha256$' . hash('sha256', $user->email . $badge->assertion->salt)) {
  92                  // If recipient is hashed, compare hashes.
  93                  $this->valid = true;
  94              } else {
  95                  // Otherwise, we cannot be sure that this user is a recipient.
  96                  $this->valid = false;
  97              }
  98          } else {
  99              $this->valid = false;
 100          }
 101      }
 102  
 103      /**
 104       * Export this data so it can be used as the context for a mustache template.
 105       *
 106       * @param renderer_base $output Renderer base.
 107       * @return stdClass
 108       */
 109      public function export_for_template(renderer_base $output): stdClass {
 110          $data = new stdClass();
 111  
 112          $now = time();
 113          if (isset($this->issued->assertion->expires)) {
 114              if (!is_numeric($this->issued->assertion->expires)) {
 115                  $this->issued->assertion->expires = strtotime($this->issued->assertion->expires);
 116              }
 117              $expiration = $this->issued->assertion->expires;
 118          } else {
 119              $expiration = $now + 86400;
 120          }
 121  
 122          // Field: Image.
 123          if (isset($this->issued->imageUrl)) {
 124              $this->issued->image = $this->issued->imageUrl;
 125          }
 126          $data->badgeimage = $this->issued->image;
 127          if (is_object($data->badgeimage)) {
 128              if (!empty($data->badgeimage->author)) {
 129                  $data->hasotherfields = true;
 130                  $data->imageauthorname = $data->badgeimage->author;
 131              }
 132              if (!empty($data->badgeimage->caption)) {
 133                  $data->hasotherfields = true;
 134                  $data->imagecaption = $data->badgeimage->caption;
 135              }
 136              $data->badgeimage = $data->badgeimage->id;
 137          }
 138  
 139          // Field: Expiration date.
 140          if (isset($this->issued->assertion->expires)) {
 141              if ($expiration < $now) {
 142                  $data->expireddate = $this->issued->assertion->expires;
 143                  $data->expireddateformatted = userdate(
 144                      $this->issued->assertion->expires,
 145                      get_string('strftimedatetime', 'langconfig')
 146                  );
 147              } else {
 148                  $data->expiredate = $this->issued->assertion->expires;
 149              }
 150          }
 151  
 152          // Fields: Name, description, issuedOn.
 153          $data->badgename = $this->issued->assertion->badge->name;
 154          $data->badgedescription = $this->issued->assertion->badge->description;
 155          if (isset($this->issued->assertion->issued_on)) {
 156              if (!is_numeric($this->issued->assertion->issued_on)) {
 157                  $this->issued->assertion->issued_on = strtotime($this->issued->assertion->issued_on);
 158              }
 159              $data->badgeissuedon = $this->issued->assertion->issued_on;
 160          }
 161  
 162          // Field: Recipient (the badge was awarded to this person).
 163          $data->recipientname = fullname($this->recipient);
 164          if (!$this->valid) {
 165              $data->recipientnotification = new stdClass();
 166              $data->recipientnotification->message = get_string('recipientvalidationproblem', 'badges');
 167          }
 168  
 169          // Field: Criteria.
 170          if (isset($this->issued->assertion->badgeclass->criteria->narrative)) {
 171              $data->criteria = $this->issued->assertion->badgeclass->criteria->narrative;
 172          }
 173  
 174          // Field: Issuer.
 175          $data->issuedby = $this->issued->issuer->name;
 176          if (isset($this->issued->issuer->contact) && !empty($this->issued->issuer->contact)) {
 177              $data->issuedbyemailobfuscated = obfuscate_mailto($this->issued->issuer->contact, $data->issuedby);
 178          }
 179  
 180          // Field: Hosted URL.
 181          if (isset($this->issued->hostedUrl) && !empty($this->issued->hostedUrl)) {
 182              $data->hostedurl = $this->issued->hostedUrl;
 183          }
 184  
 185          return $data;
 186      }
 187  }