diff --git a/badges/badge_json.php b/badges/badge_json.php index b7526d5ec76..60f25a589f9 100644 --- a/badges/badge_json.php +++ b/badges/badge_json.php @@ -66,7 +66,9 @@ if ($badge->status != BADGE_STATUS_INACTIVE) { $json['image'] = $urlimage; } - $json['criteria']['id'] = $url->out(false); + $params = ['id' => $badge->id]; + $badgeurl = new moodle_url('/badges/badgeclass.php', $params); + $json['criteria']['id'] = $badgeurl->out(false); $json['criteria']['narrative'] = $badge->markdown_badge_criteria(); $json['issuer'] = $badge->get_badge_issuer(); $json['@context'] = OPEN_BADGES_V2_CONTEXT; diff --git a/badges/badgeclass.php b/badges/badgeclass.php new file mode 100644 index 00000000000..c71b07df896 --- /dev/null +++ b/badges/badgeclass.php @@ -0,0 +1,57 @@ +. + +/** + * Display details of a badge. + * + * @package core_badges + * @copyright 2022 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../config.php'); +require_once($CFG->libdir . '/badgeslib.php'); + +$badgeid = required_param('id', PARAM_ALPHANUM); +$badgeclass = new \core_badges\output\badgeclass($badgeid); + +$context = !empty($badgeclass) ? $badgeclass->context : \context_system::instance(); +$PAGE->set_context($context); +$output = $PAGE->get_renderer('core', 'badges'); +$PAGE->set_url('/badges/badgeclass.php', ['id' => $badgeid]); +$PAGE->set_pagelayout('base'); +$PAGE->set_title(get_string('badgedetails', 'badges')); + +if (!empty($badgeclass->badge)) { + $PAGE->navbar->add($badgeclass->badge->name); + $url = new moodle_url($CFG->wwwroot); + navigation_node::override_active_url($url); + + echo $OUTPUT->header(); + echo $output->render($badgeclass); +} else { + echo $OUTPUT->header(); + echo $OUTPUT->notification(get_string('error:relatedbadgedoesntexist', 'badges')); +} + +// Trigger event, badge viewed. +$other = ['badgeid' => $badgeclass->badgeid]; +$eventparams = ['context' => $PAGE->context, 'other' => $other]; + +$event = \core\event\badge_viewed::create($eventparams); +$event->trigger(); + +echo $OUTPUT->footer(); diff --git a/badges/classes/assertion.php b/badges/classes/assertion.php index bbfa50b4f0c..6565493c7c3 100644 --- a/badges/classes/assertion.php +++ b/badges/classes/assertion.php @@ -196,7 +196,10 @@ class core_badges_assertion { } } $class['image'] = 'data:image/png;base64,' . $imagedata; - $class['criteria'] = $this->_url->out(false); // Currently issued badge URL. + + $params = ['id' => $this->get_badge_id()]; + $badgeurl = new moodle_url('/badges/badgeclass.php', $params); + $class['criteria'] = $badgeurl->out(false); // Currently badge URL. if ($issued) { $params = ['id' => $this->get_badge_id(), 'obversion' => $this->_obversion]; $issuerurl = new moodle_url('/badges/issuer_json.php', $params); @@ -281,13 +284,15 @@ class core_badges_assertion { public function get_criteria_badge_class() { $badge = new badge($this->_data->id); $narrative = $badge->markdown_badge_criteria(); + $params = ['id' => $this->get_badge_id()]; + $badgeurl = new moodle_url('/badges/badgeclass.php', $params); if (!empty($narrative)) { - $criteria = array(); - $criteria['id'] = $this->_url->out(false); + $criteria = []; + $criteria['id'] = $badgeurl->out(false); $criteria['narrative'] = $narrative; return $criteria; } else { - return $this->_url->out(false); + return $badgeurl->out(false); } } diff --git a/badges/classes/badge.php b/badges/classes/badge.php index 6bae4e781cd..a5186070291 100644 --- a/badges/classes/badge.php +++ b/badges/classes/badge.php @@ -141,7 +141,7 @@ class badge { $data = $DB->get_record('badge', array('id' => $badgeid)); if (empty($data)) { - print_error('error:nosuchbadge', 'badges', $badgeid); + throw new moodle_exception('error:nosuchbadge', 'badges', '', $badgeid); } foreach ((array)$data as $field => $value) { diff --git a/badges/classes/output/badgeclass.php b/badges/classes/output/badgeclass.php new file mode 100644 index 00000000000..d66b37fd0c0 --- /dev/null +++ b/badges/classes/output/badgeclass.php @@ -0,0 +1,175 @@ +. + +namespace core_badges\output; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/badgeslib.php'); + +use coding_exception; +use context_course; +use stdClass; +use renderable; +use core_badges\badge; +use moodle_url; +use renderer_base; + +/** + * Page to display badge information, such as name, description or criteria. This information is unrelated to assertions. + * + * @package core_badges + * @copyright 2022 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class badgeclass implements renderable { + + /** @var badge class */ + public $badge; + + /** @var badge class */ + public $badgeid = 0; + + /** @var \context The badge context*/ + public $context; + + /** + * Initializes the badge to display. + * + * @param int $id Id of the badge to display. + */ + public function __construct(int $id) { + $this->badgeid = $id; + $this->badge = new badge($this->badgeid); + if ($this->badge->status == BADGE_STATUS_INACTIVE) { + // Inactive badges that haven't been published previously can't be displayed. + $this->badge = null; + } else { + $this->context = $this->badge->get_context(); + } + } + + /** + * Export this data so it can be used as the context for a mustache template. + * + * @param renderer_base $output Renderer base. + * @return stdClass + */ + public function export_for_template(renderer_base $output): stdClass { + global $DB, $SITE; + + $data = new stdClass(); + if ($this->context instanceof context_course) { + $data->coursefullname = format_string($DB->get_field('course', 'fullname', ['id' => $this->badge->courseid]), + true, ['context' => $this->context]); + } else { + $data->sitefullname = format_string($SITE->fullname, true, ['context' => $this->context]); + } + + // Field: Image. + $storage = get_file_storage(); + $imagefile = $storage->get_file($this->context->id, 'badges', 'badgeimage', $this->badgeid, '/', 'f3.png'); + if ($imagefile) { + $imagedata = base64_encode($imagefile->get_content()); + } else { + if (defined('PHPUNIT_TEST') && PHPUNIT_TEST) { + // Unit tests the file might not exist yet. + $imagedata = ''; + } else { + throw new coding_exception('Image file does not exist.'); + } + } + $data->badgeimage = 'data:image/png;base64,' . $imagedata; + + // Fields: Name, description. + $data->badgename = $this->badge->name; + $data->badgedescription = $this->badge->description; + + // Field: Criteria. + // This method will return the HTML with the badge criteria. + $data->criteria = $output->print_badge_criteria($this->badge); + + // Field: Issuer. + $data->issuedby = format_string($this->badge->issuername, true, ['context' => $this->context]); + if (isset($this->badge->issuercontact) && !empty($this->badge->issuercontact)) { + $data->issuedbyemailobfuscated = obfuscate_mailto($this->badge->issuercontact, $data->issuedby); + } + + // Fields: Other details, such as language or version. + $data->hasotherfields = false; + if (!empty($this->badge->language)) { + $data->hasotherfields = true; + $languages = get_string_manager()->get_list_of_languages(); + $data->language = $languages[$this->badge->language]; + } + if (!empty($this->badge->version)) { + $data->hasotherfields = true; + $data->version = $this->badge->version; + } + if (!empty($this->badge->imageauthorname)) { + $data->hasotherfields = true; + $data->imageauthorname = $this->badge->imageauthorname; + } + if (!empty($this->badge->imageauthoremail)) { + $data->hasotherfields = true; + $data->imageauthoremail = obfuscate_mailto($this->badge->imageauthoremail, $this->badge->imageauthoremail); + } + if (!empty($this->badge->imageauthorurl)) { + $data->hasotherfields = true; + $data->imageauthorurl = $this->badge->imageauthorurl; + } + if (!empty($this->badge->imagecaption)) { + $data->hasotherfields = true; + $data->imagecaption = $this->badge->imagecaption; + } + + // Field: Endorsement. + $endorsement = $this->badge->get_endorsement(); + if (!empty($endorsement)) { + $data->hasotherfields = true; + $endorsement = $this->badge->get_endorsement(); + $endorsement->issueremail = obfuscate_mailto($endorsement->issueremail, $endorsement->issueremail); + $data->endorsement = (array) $endorsement; + } + + // Field: Related badges. + $relatedbadges = $this->badge->get_related_badges(true); + if (!empty($relatedbadges)) { + $data->hasotherfields = true; + $data->hasrelatedbadges = true; + $data->relatedbadges = []; + foreach ($relatedbadges as $related) { + if (isloggedin() && !is_guest($this->context)) { + $related->url = (new moodle_url('/badges/overview.php', ['id' => $related->id]))->out(false); + } + $data->relatedbadges[] = (array)$related; + } + } + + // Field: Alignments. + $alignments = $this->badge->get_alignments(); + if (!empty($alignments)) { + $data->hasotherfields = true; + $data->hasalignments = true; + $data->alignments = []; + foreach ($alignments as $alignment) { + $data->alignments[] = (array)$alignment; + } + } + + return $data; + } +} diff --git a/badges/renderer.php b/badges/renderer.php index 8b789061356..57cc70a7312 100644 --- a/badges/renderer.php +++ b/badges/renderer.php @@ -327,6 +327,17 @@ class core_badges_renderer extends plugin_renderer_base { return parent::render_from_template('core_badges/issued_badge', $data); } + /** + * Render an issued badge. + * + * @param \core_badges\output\badgeclass $badge + * @return string + */ + protected function render_badgeclass(\core_badges\output\badgeclass $badge) { + $data = $badge->export_for_template($this); + return parent::render_from_template('core_badges/issued_badge', $data); + } + /** * Render an external badge. * diff --git a/badges/templates/issued_badge.mustache b/badges/templates/issued_badge.mustache index 6d57d37814c..57077ab49c2 100644 --- a/badges/templates/issued_badge.mustache +++ b/badges/templates/issued_badge.mustache @@ -23,14 +23,14 @@ * coursefullname - Course name (only available if it's a course badge). * sitefullname - Site name (only available if it's a site badge). * badgeimage - Badge image. - * expireddate - Date (in the past) when the badge expired (if defined). If expiredate is defined, this field will be empty. - * expireddateformatted - Formatted expired date. - * expiredate - Date (in the future) when the badge will expire (if defined). If expireddate is defined, this field will be empty. + * expireddate - Date (in the past) when the badge expired. If expiredate is defined, this field will be empty [optional]. + * expireddateformatted - Formatted expired date [optional]. + * expiredate - Date (in the future) when the badge will expire. If expireddate is defined, this field will be empty [optional]. * badgename - Badge name. * badgedescription - Badge description. - * badgeissuedon - Date where the badge was issued on by the user. - * recipientname - User awarded with the badge. - * recipientnotification.message - Message to be displayed if there is some issue with the recipient. + * badgeissuedon - Date where the badge was issued on by the user [optional]. + * recipientname - User awarded with the badge [optional]. + * recipientnotification.message - Message to be displayed if there is some issue with the recipient [optional]. * criteria - HTML code with the criteria to display. * issuedby - Badge issuer. * issuedbyemailobfuscated - Badge issuer email link obfuscated. @@ -139,22 +139,27 @@