Merge branch 'MDL-40572_master' of https://github.com/totara/openbadges

This commit is contained in:
Sam Hemelryk 2013-10-01 11:05:59 +13:00
commit 7da590c11b
8 changed files with 250 additions and 110 deletions

View File

@ -25,16 +25,24 @@
*/
require_once(dirname(dirname(__FILE__)) . '/config.php');
require_once($CFG->libdir . '/badgeslib.php');
if (empty($CFG->enablebadges)) {
print_error('badgesdisabled', 'badges');
}
$hash = required_param('b', PARAM_ALPHANUM);
$hash = required_param('b', PARAM_ALPHANUM); // Issued badge unique hash for badge assertion.
$action = optional_param('action', null, PARAM_BOOL); // Generates badge class if true.
$badge = badges_get_issued_badge_info($hash);
$assertion = new core_badges_assertion($hash);
if (!is_null($action)) {
// Get badge class or issuer information depending on $action.
$json = ($action) ? $assertion->get_badge_class() : $assertion->get_issuer();
} else {
// Otherwise, get badge assertion.
$json = $assertion->get_badge_assertion();
}
header('Content-type: application/json; charset=utf-8');
echo json_encode($badge);
echo json_encode($json);

View File

@ -35,8 +35,8 @@ $output = $PAGE->get_renderer('core', 'badges');
$badge = new issued_badge($id);
if ($bake && ($badge->recipient == $USER->id)) {
$name = str_replace(' ', '_', $badge->issued['badge']['name']) . '.png';
if ($bake && ($badge->recipient->id == $USER->id)) {
$name = str_replace(' ', '_', $badge->badgeclass['name']) . '.png';
ob_start();
$file = badges_bake($id, $badge->badgeid);
header('Content-Type: image/png');
@ -50,8 +50,8 @@ $PAGE->set_pagelayout('base');
$PAGE->set_title(get_string('issuedbadge', 'badges'));
if (isloggedin()) {
$PAGE->set_heading($badge->issued['badge']['name']);
$PAGE->navbar->add($badge->issued['badge']['name']);
$PAGE->set_heading($badge->badgeclass['name']);
$PAGE->navbar->add($badge->badgeclass['name']);
$url = new moodle_url('/badges/mybadges.php');
navigation_node::override_active_url($url);
}

View File

@ -0,0 +1,158 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Badge assertion library.
*
* @package core
* @subpackage badges
* @copyright 2012 onwards Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @author Yuliya Bozhko <yuliya.bozhko@totaralms.com>
*/
defined('MOODLE_INTERNAL') || die();
/**
* Open Badges Assertions specification 1.0 {@link https://github.com/mozilla/openbadges/wiki/Assertions}
*
* Badge asserion is defined by three parts:
* - Badge Assertion (information regarding a specific badge that was awarded to a badge earner)
* - Badge Class (general information about a badge and what it is intended to represent)
* - Issuer Class (general information of an issuing organisation)
*/
/**
* Class that represents badge assertion.
*
*/
class core_badges_assertion {
/** @var object Issued badge information from database */
private $_data;
/** @var moodle_url Issued badge url */
private $_url;
/**
* Constructs with issued badge unique hash.
*
* @param string $hash Badge unique hash from badge_issued table.
*/
public function __construct($hash) {
global $DB;
$this->_data = $DB->get_record_sql('
SELECT
bi.dateissued,
bi.dateexpire,
bi.uniquehash,
u.email,
b.*,
bb.email as backpackemail
FROM
{badge} b
JOIN {badge_issued} bi
ON b.id = bi.badgeid
JOIN {user} u
ON u.id = bi.userid
LEFT JOIN {badge_backpack} bb
ON bb.userid = bi.userid
WHERE ' . $DB->sql_compare_text('bi.uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40),
array('hash' => $hash), IGNORE_MISSING);
$this->_url = new moodle_url('/badges/badge.php', array('hash' => $this->_data->uniquehash));
}
/**
* Get badge assertion.
*
* @return array Badge assertion.
*/
public function get_badge_assertion() {
global $CFG;
$assertion = array();
if ($this->_data) {
$hash = $this->_data->uniquehash;
$email = empty($this->_data->backpackemail) ? $this->_data->email : $this->_data->backpackemail;
$assertionurl = new moodle_url('/badges/assertion.php', array('b' => $hash));
$classurl = new moodle_url('/badges/assertion.php', array('b' => $hash, 'action' => 1));
// Required.
$assertion['uid'] = $hash;
$assertion['recipient'] = array();
$assertion['recipient']['identity'] = 'sha256$' . hash('sha256', $email . $CFG->badges_badgesalt);
$assertion['recipient']['type'] = 'email'; // Currently the only supported type.
$assertion['recipient']['hashed'] = true; // We are always hashing recipient.
$assertion['recipient']['salt'] = $CFG->badges_badgesalt;
$assertion['badge'] = $classurl->out(false);
$assertion['verify'] = array();
$assertion['verify']['type'] = 'hosted'; // 'Signed' is not implemented yet.
$assertion['verify']['url'] = $assertionurl->out(false);
$assertion['issuedOn'] = $this->_data->dateissued;
// Optional.
$assertion['evidence'] = $this->_url->out(false); // Currently issued badge URL.
if (!empty($this->_data->dateexpire)) {
$assertion['expires'] = $this->_data->dateexpire;
}
}
return $assertion;
}
/**
* Get badge class information.
*
* @return array Badge Class information.
*/
public function get_badge_class() {
$class = array();
if ($this->_data) {
if (empty($this->_data->courseid)) {
$context = context_system::instance();
} else {
$context = context_course::instance($this->_data->courseid);
}
$issuerurl = new moodle_url('/badges/assertion.php', array('b' => $this->_data->uniquehash, 'action' => 0));
// Required.
$class['name'] = $this->_data->name;
$class['description'] = $this->_data->description;
$class['image'] = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $this->_data->id, '/', 'f1')->out(false);
$class['criteria'] = $this->_url->out(false); // Currently issued badge URL.
$class['issuer'] = $issuerurl->out(false);
}
return $class;
}
/**
* Get badge issuer information.
*
* @return array Issuer information.
*/
public function get_issuer() {
$issuer = array();
if ($this->_data) {
// Required.
$issuer['name'] = $this->_data->issuername;
$issuer['url'] = $this->_data->issuerurl;
// Optional.
if (!empty($this->_data->issuercontact)) {
$issuer['email'] = $this->_data->issuercontact;
}
}
return $issuer;
}
}

View File

@ -120,25 +120,11 @@ class PNG_MetaDataHandler
debugging('Key is too big');
}
if ($type == 'iTXt') {
// iTXt International textual data.
// Keyword: 1-79 bytes (character string)
// Null separator: 1 byte
// Compression flag: 1 byte
// Compression method: 1 byte
// Language tag: 0 or more bytes (character string)
// Null separator: 1 byte
// Translated keyword: 0 or more bytes
// Null separator: 1 byte
// Text: 0 or more bytes
$data = $key . "\000'json'\0''\0\"{'method': 'hosted', 'assertionUrl': '" . $value . "'}\"";
} else {
// tEXt Textual data.
// Keyword: 1-79 bytes (character string)
// Null separator: 1 byte
// Text: n bytes (character string)
$data = $key . "\0" . $value;
}
// tEXt Textual data.
// Keyword: 1-79 bytes (character string)
// Null separator: 1 byte
// Text: n bytes (character string)
$data = $key . "\0" . $value;
$crc = pack("N", crc32($type . $data));
$len = pack("N", strlen($data));

View File

@ -282,24 +282,24 @@ class core_badges_renderer extends plugin_renderer_base {
global $USER, $CFG, $DB;
$issued = $ibadge->issued;
$userinfo = $ibadge->recipient;
$badgeclass = $ibadge->badgeclass;
$badge = new badge($ibadge->badgeid);
$today_date = date('Y-m-d');
$today = strtotime($today_date);
$now = time();
$table = new html_table();
$table->id = 'issued-badge-table';
$imagetable = new html_table();
$imagetable->attributes = array('class' => 'clearfix badgeissuedimage');
$imagetable->data[] = array(html_writer::empty_tag('img', array('src' => $issued['badge']['image'])));
$imagetable->data[] = array(html_writer::empty_tag('img', array('src' => $badgeclass['image'])));
if ($USER->id == $userinfo->id && !empty($CFG->enablebadges)) {
$imagetable->data[] = array($this->output->single_button(
new moodle_url('/badges/badge.php', array('hash' => $ibadge->hash, 'bake' => true)),
new moodle_url('/badges/badge.php', array('hash' => $issued['uid'], 'bake' => true)),
get_string('download'),
'POST'));
$expiration = isset($issued['expires']) ? strtotime($issued['expires']) : $today + 1;
if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $today) && badges_user_has_backpack($USER->id)) {
$assertion = new moodle_url('/badges/assertion.php', array('b' => $ibadge->hash));
$expiration = isset($issued['expires']) ? $issued['expires'] : $now + 86400;
if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $now) && badges_user_has_backpack($USER->id)) {
$assertion = new moodle_url('/badges/assertion.php', array('b' => $issued['uid']));
$action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
$attributes = array(
'type' => 'button',
@ -339,24 +339,23 @@ class core_badges_renderer extends plugin_renderer_base {
$datatable->data[] = array(get_string('bcriteria', 'badges'), self::print_badge_criteria($badge));
$datatable->data[] = array($this->output->heading(get_string('issuancedetails', 'badges'), 3), '');
$datatable->data[] = array(get_string('dateawarded', 'badges'), $issued['issued_on']);
$datatable->data[] = array(get_string('dateawarded', 'badges'), userdate($issued['issuedOn']));
if (isset($issued['expires'])) {
$expiration = strtotime($issued['expires']);
if ($expiration < $today) {
$cell = new html_table_cell($issued['expires'] . get_string('warnexpired', 'badges'));
if ($issued['expires'] < $now) {
$cell = new html_table_cell(userdate($issued['expires']) . get_string('warnexpired', 'badges'));
$cell->attributes = array('class' => 'notifyproblem warning');
$datatable->data[] = array(get_string('expirydate', 'badges'), $cell);
$image = html_writer::start_tag('div', array('class' => 'badge'));
$image .= html_writer::empty_tag('img', array('src' => $issued['badge']['image']));
$image .= html_writer::empty_tag('img', array('src' => $badgeclass['image']));
$image .= $this->output->pix_icon('i/expired',
get_string('expireddate', 'badges', $issued['expires']),
get_string('expireddate', 'badges', userdate($issued['expires'])),
'moodle',
array('class' => 'expireimage'));
$image .= html_writer::end_tag('div');
$imagetable->data[0] = array($image);
} else {
$datatable->data[] = array(get_string('expirydate', 'badges'), $issued['expires']);
$datatable->data[] = array(get_string('expirydate', 'badges'), userdate($issued['expires']));
}
}
@ -909,15 +908,15 @@ class issued_badge implements renderable {
/** @var badge recipient */
public $recipient;
/** @var badge class */
public $badgeclass;
/** @var badge visibility to others */
public $visible = 0;
/** @var badge class */
public $badgeid = 0;
/** @var issued badge unique hash */
public $hash = "";
/**
* Initializes the badge to display
*
@ -925,8 +924,10 @@ class issued_badge implements renderable {
*/
public function __construct($hash) {
global $DB;
$this->issued = badges_get_issued_badge_info($hash);
$this->hash = $hash;
$assertion = new core_badges_assertion($hash);
$this->issued = $assertion->get_badge_assertion();
$this->badgeclass = $assertion->get_badge_class();
$rec = $DB->get_record_sql('SELECT userid, visible, badgeid
FROM {badge_issued}

View File

@ -35,6 +35,7 @@ class core_badgeslib_testcase extends advanced_testcase {
protected $user;
protected $module;
protected $coursebadge;
protected $assertion;
protected function setUp() {
global $DB, $CFG;
@ -52,6 +53,7 @@ class core_badgeslib_testcase extends advanced_testcase {
$fordb->usermodified = $user->id;
$fordb->issuername = "Test issuer";
$fordb->issuerurl = "http://issuer-url.domain.co.nz";
$fordb->issuercontact = "issuer@example.com";
$fordb->expiredate = null;
$fordb->expireperiod = null;
$fordb->type = BADGE_TYPE_SITE;
@ -86,6 +88,10 @@ class core_badgeslib_testcase extends advanced_testcase {
$fordb->status = BADGE_STATUS_ACTIVE;
$this->coursebadge = $DB->insert_record('badge', $fordb, true);
$this->assertion = new stdClass();
$this->assertion->badge = '{"uid":"%s","recipient":{"identity":"%s","type":"email","hashed":true,"salt":"%s"},"badge":"%s","verify":{"type":"hosted","url":"%s"},"issuedOn":"%d","evidence":"%s"}';
$this->assertion->class = '{"name":"%s","description":"%s","image":"%s","criteria":"%s","issuer":"%s"}';
$this->assertion->issuer = '{"name":"%s","url":"%s","email":"%s"}';
}
public function test_create_badge() {
@ -103,6 +109,7 @@ class core_badgeslib_testcase extends advanced_testcase {
$this->assertEquals($badge->description, $cloned_badge->description);
$this->assertEquals($badge->issuercontact, $cloned_badge->issuercontact);
$this->assertEquals($badge->issuername, $cloned_badge->issuername);
$this->assertEquals($badge->issuercontact, $cloned_badge->issuercontact);
$this->assertEquals($badge->issuerurl, $cloned_badge->issuerurl);
$this->assertEquals($badge->expiredate, $cloned_badge->expiredate);
$this->assertEquals($badge->expireperiod, $cloned_badge->expireperiod);
@ -279,4 +286,34 @@ class core_badgeslib_testcase extends advanced_testcase {
$this->assertDebuggingCalled('Error baking badge image!');
$this->assertTrue($badge->is_issued($this->user->id));
}
/**
* Test badges assertion generated when a badge is issued.
*/
public function test_badges_assertion() {
$badge = new badge($this->coursebadge);
$this->assertFalse($badge->is_issued($this->user->id));
$criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
$criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
$criteria_overall1 = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_PROFILE, 'badgeid' => $badge->id));
$criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL, 'field_address' => 'address'));
$this->user->address = 'Test address';
user_update_user($this->user, false);
// Check if badge is awarded.
$this->assertDebuggingCalled('Error baking badge image!');
$awards = $badge->get_awards();
$this->assertCount(1, $awards);
// Get assertion.
$award = reset($awards);
$assertion = new core_badges_assertion($award->uniquehash);
$testassertion = $this->assertion;
// Make sure JSON strings have the same structure.
$this->assertStringMatchesFormat($testassertion->badge, json_encode($assertion->get_badge_assertion()));
$this->assertStringMatchesFormat($testassertion->class, json_encode($assertion->get_badge_class()));
$this->assertStringMatchesFormat($testassertion->issuer, json_encode($assertion->get_issuer()));
}
}

View File

@ -821,69 +821,6 @@ function badges_get_user_badges($userid, $courseid = 0, $page = 0, $perpage = 0,
return $badges;
}
/**
* Get issued badge details for assertion URL
*
* @param string $hash
*/
function badges_get_issued_badge_info($hash) {
global $DB, $CFG;
$a = array();
$record = $DB->get_record_sql('
SELECT
bi.dateissued,
bi.dateexpire,
u.email,
b.*,
bb.email as backpackemail
FROM
{badge} b
JOIN {badge_issued} bi
ON b.id = bi.badgeid
JOIN {user} u
ON u.id = bi.userid
LEFT JOIN {badge_backpack} bb
ON bb.userid = bi.userid
WHERE ' . $DB->sql_compare_text('bi.uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40),
array('hash' => $hash), IGNORE_MISSING);
if ($record) {
if ($record->type == BADGE_TYPE_SITE) {
$context = context_system::instance();
} else {
$context = context_course::instance($record->courseid);
}
$url = new moodle_url('/badges/badge.php', array('hash' => $hash));
$email = empty($record->backpackemail) ? $record->email : $record->backpackemail;
// Recipient's email is hashed: <algorithm>$<hash(email + salt)>.
$a['recipient'] = 'sha256$' . hash('sha256', $email . $CFG->badges_badgesalt);
$a['salt'] = $CFG->badges_badgesalt;
if ($record->dateexpire) {
$a['expires'] = date('Y-m-d', $record->dateexpire);
}
$a['issued_on'] = date('Y-m-d', $record->dateissued);
$a['evidence'] = $url->out(); // Issued badge URL.
$a['badge'] = array();
$a['badge']['version'] = '0.5.0'; // Version of OBI specification, 0.5.0 - current beta.
$a['badge']['name'] = $record->name;
$a['badge']['image'] = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $record->id, '/', 'f1')->out();
$a['badge']['description'] = $record->description;
$a['badge']['criteria'] = $url->out(); // Issued badge URL.
$a['badge']['issuer'] = array();
$a['badge']['issuer']['origin'] = $record->issuerurl;
$a['badge']['issuer']['name'] = $record->issuername;
$a['badge']['issuer']['contact'] = $record->issuercontact;
}
return $a;
}
/**
* Extends the course administration navigation with the Badges page
*

View File

@ -4678,4 +4678,17 @@ function get_browser_version_classes() {
function generate_email_supportuser() {
debugging('generate_email_supportuser is deprecated, please use core_user::get_support_user');
return core_user::get_support_user();
}
}
/**
* Get issued badge details for assertion URL
*
* @deprecated since Moodle 2.6
* @param string $hash Unique hash of a badge
* @return array Information about issued badge.
*/
function badges_get_issued_badge_info($hash) {
debugging('Function badges_get_issued_badge_info() is deprecated. Please use core_badges_assertion class and methods to generate badge assertion.', DEBUG_DEVELOPER);
$assertion = new core_badges_assertion($hash);
return $assertion->get_badge_assertion();
}